Data Structure-Bidirectional Loop Chain List

1. The structure of a two-way circular chain table

1.1 Definition of Two-way Circular Chain List

Two-way circular chain table Each node has two pointer domains, one pointing to the precursor node and the other pointing to the successor node, and the precursor pointer of the head node points to the end node, and the next pointer of the end node points to the head node. The two-way chain table with the head is used in practice.

1.2 Two-way Circulating Chain List

  • Defines a DualCircleList through a template class and inherits the DualLinkList class from the two-way chain table
  • Using Linux Kernel Chain List in DualCircleList, obtained in the previous article
  • Using struct list_head Definition for DualCircleList Head Node Definition
  • Ignore header nodes during loop traversal

1.3 Implementing Key Points of Two-way Circulating Chain List

  • Through list_head locating target node
  • Through list_entry will list_head Target Node Pointer Converted to Target Node
  • Through list_for_each to implement int find (const T&e)
  • next and Preto in traversal functions skip header nodes

2. Interface implementation of two-way circular chain table

2.1 Bidirectional Circulating Chain List Structure

struct Node
 {
       list_head head;
      T value;
 };

list_head m_header;//Define Head Node
list_head* m_current;//Define Header Pointer

Initialization of 2.2 Bidirectional Loop Chain List

Implemented in a constructor

DualCircleList()
{
		this->m_length = 0;
		this->m_step = 1;
		m_current = NULL;
		INIT_LIST_HEAD(&m_header);
}

2.3 Bidirectional Circulating Chain List Node Location

It is the same as traversing node locations in single-chain lists. To find the previous node inserted into a node, it is important to note that the const-modified member function should not modify any member variables of the object. In order to get the first node, a type conversion is required, that is, when the member function of the const attribute calls a member function of a non-const attribute, const_is used Cast<>operator removes const attribute

	//1. Implement positioning functions
	list_head* postion(int i)const
	{
		//The address of a member variable cannot be used directly in a const-modified member function, and a type conversion is required.
		list_head* ret = const_cast<list_head*>(&m_header);
		for (int p = 0; p < i; p++)
		{
			ret = ret->next;
		}

		return ret;
	}

2.4 Insert nodes based on specified locations

  • Find the insertion position first
  • Be careful not to disconnect the original list first; the new node will establish two relationships with the precursor and subsequent nodes, respectively.
	bool insert(int i, const T& e)
	{
		bool ret    = true;
		Node* node	= new Node();
		i = i % (this->m_length + 1);//Determine the value of i

		if (node != NULL)
		{
			node->value = e;
			list_add_tail(&node->head, postion(i)->next);
			this->m_length++;
		}
		else
		{
			cout << "new Node failed\n<<endl;
		}
		
		return ret;
	}

	//4. Tail Insert
	bool insert(const T& e)
	{
		return insert(this->m_length, e);
	}

2.5 Delete nodes at specified locations


Pseudocode implementation:

static void __list_del(struct list_head* prev, struct list_head* next)
{
    next->prev = prev;
    prev->next = next;
}

static void list_del(struct list_head* entry)
{
    __list_del(toDel->prev, toDel->next);
    toDel->next = NULL;
    toDel->prev = NULL;
}

Implemented in cpp as:
First determine if the deleted location is legal, find the deleted node, determine if the current deleted node is the location pointed to by the cursor, if so, move the cursor to the next location, and call the deletion function of the Linux kernel chain table, while reducing the length by 1, freeing up memory of the deleted node

bool remove(int i)
	{
		bool ret = true;

		i = mod(i);//Determine if deleted location is legal
		ret = ((i >= 0) && (i < this->m_length));

		if (ret)
		{
			//Find deleted nodes
			list_head* toDel = postion(i)->next;

			//Determines whether the currently deleted node is the location pointed to by the cursor
			//If so, move the cursor one position
			if (m_current == toDel)
			{
				m_current = toDel->next;
			}

			list_del(toDel);
			this->m_length--;

			delete list_entry(toDel, Node, head);
		}

		return ret;
	}

2.6 Modify the value of a node

  • First determine if the location of the settings is legal
  • Perform pointer conversion
	bool set(int i, const T& e)
	{
		bool ret = true;

		i = mod(i);

		if (ret)
		{
			list_entry(postion(i)->next, Node, head)->value = e;
		}

		return ret;
	}

2.7 Get the value of a node

	T get(int i) const
	{
		bool ret;
		i = mod(i);
		ret = ((i >= 0) && (i < this->m_length));

		if (ret)
		{
			return list_entry(postion(i)->next, Node, head)->value;
		}

		return ret;
	}

2.8 Find subscripts from node values

int find(const T& e)const
	{
		int ret = -1;
		int i = 0;
		list_head* slider = NULL;

		list_for_each(slider, &m_header)
		{
			if (list_entry(postion(i)->next, Node, head)->value == e)
			{
				ret = i;
				break;
			}

			i++;
		}
		return ret;
	}

2.9 Getting the Length of a Two-way Chain List

	int length()const
	{
		return this->m_length;
	}

2.10 Empty Chain List

void clear()
	{
		while (this->m_length > 0)
		{
			remove(0);
		}
	}

2.11 Destructor Implementation

	~DualCircleList()
	{
		clear();
	}

3. Complete Code

Code may not inherit a two-way Chain table, but you need to add some variables

#ifndef DUALCIRCLELIST_H
#define DUALCIRCLELIST_H

#include "DualLinkList.h"
#include "LinuxList.h"


template<class T>
class DualCircleList : public DualLinkList<T>
{
protected:
	struct Node
	{
		list_head head;
		T value;
	};

	list_head m_header;//Define Head Node
	list_head* m_current;//Define Header Pointer

	//1. Implement positioning functions
	list_head* postion(int i)const
	{
		//The address of a member variable cannot be used directly in a const-modified member function, and a type conversion is required.
		list_head* ret = const_cast<list_head*>(&m_header);
		for (int p = 0; p < i; p++)
		{
			ret = ret->next;
		}

		return ret;
	}

	//2.
	int mod(int i)const
	{
		return ((this->m_length == 0) ? 0 : (i % this->m_length));
	}

public:
	DualCircleList()
	{
		this->m_length = 0;
		this->m_step = 1;
		m_current = NULL;
		INIT_LIST_HEAD(&m_header);
	}

	//3.
	bool insert(int i, const T& e)
	{
		bool ret    = true;
		Node* node	= new Node();
		i = i % (this->m_length + 1);//Determine the value of i

		if (node != NULL)
		{
			node->value = e;
			list_add_tail(&node->head, postion(i)->next);
			this->m_length++;
		}
		else
		{
			cout << "new Node failed"<<endl;
		}
		
		return ret;
	}

	//4. Tail Insert
	bool insert(const T& e)
	{
		return insert(this->m_length, e);
	}

	//5. Delete Nodes
	bool remove(int i)
	{
		bool ret = true;

		i = mod(i);//Determine if deleted location is legal
		ret = ((i >= 0) && (i < this->m_length));

		if (ret)
		{
			//Find deleted nodes
			list_head* toDel = postion(i)->next;

			//Determines whether the currently deleted node is the location pointed to by the cursor
			//If so, move the cursor one position
			if (m_current == toDel)
			{
				m_current = toDel->next;
			}

			list_del(toDel);
			this->m_length--;

			delete list_entry(toDel, Node, head);
		}

		return ret;
	}

	bool set(int i, const T& e)
	{
		bool ret = true;

		i = mod(i);
		ret = ((i >= 0) && (i < this->m_length));

		if (ret)
		{
			list_entry(postion(i)->next, Node, head)->value = e;
		}

		return ret;
	}

	T get(int i) const
	{
		bool ret;
		i = mod(i);
		ret = ((i >= 0) && (i < this->m_length));

		if (ret)
		{
			return list_entry(postion(i)->next, Node, head)->value;
		}

		return ret;
	}

	virtual int find(const T& e)const
	{
		int ret = -1;
		int i = 0;
		list_head* slider = NULL;

		list_for_each(slider, &m_header)
		{
			if (list_entry(postion(i)->next, Node, head)->value == e)
			{
				ret = i;
				break;
			}

			i++;
		}
		return ret;
	}

	int length()const
	{
		return this->m_length;
	}

	void clear()
	{
		while (this->m_length > 0)
		{
			remove(0);
		}
	}

	bool move(int i, int step = 1)
	{
		bool ret = (step > 0);

		i = mod(i);

		ret = ret && ((i >= 0) && (i < this->m_length));

		if (ret)
		{
			m_current = postion(i)->next;
			this->m_step = step;
		}

		return ret;
	}

	bool end()
	{
		return ((m_current == NULL) || (this->m_length == 0));
	}

	
	virtual T current()
	{
		if (!end())
		{
			return list_entry(m_current, Node, head)->value;
		}
		else
		{
			cout << "list is empty" << endl;
			return NULL;
		}

	}

	bool next()
	{
		int i = 0;

		//
		while ((i < this->m_step) && !end())
		{
			//Not equal to header node
			if (m_current != &m_header)
			{
				//Point to Next Node
				m_current = m_current->next;
				//count
				i++;
			}
			else
			{
				//Skip header nodes directly
				m_current = m_current->next;
			}
		}

		//If equal to header node
		if (m_current == &m_header)
		{
			m_current = m_current->next;
		}

		return (i == this->m_step);
	}

	bool pre()
	{
		int i = 0;

		//
		while ((i < this->m_step) && !end())
		{
			//Not equal to header node
			if (m_current != &m_header)
			{
				//Point to the previous node
				m_current = m_current->prev;
				//count
				i++;
			}
			else
			{
				//Skip header nodes directly
				m_current = m_current->prev;
			}
		}

		//If equal to header node
		if (m_current == &m_header)
		{
			m_current = m_current->prev;
		}

		return (i == this->m_step);
	}

	~DualCircleList()
	{
		clear();
	}
};



#endif

4. Test Code

Insert 10 elements and delete elements with an element value of 5

#include "DualCircleList.h"
#include <iostream>

int main()
{
	DualCircleList<int> dl;
	int j = 5;

	for (int i = 0; i < 5; i++)
	{
		dl.insert(0, i);
		dl.insert(0, j);
	}

	for (int i = 0; i < dl.length(); i++)
	{
		cout << dl.get(i) << endl;
	}

	//Navigate to the last element
	dl.move(dl.length() - 1);
	cout << " delete begin()" << endl;

	//Find out if this element is in the list and if it is, delete it
	while (dl.find(5)!= -1)
	{
		if (dl.current() == 5)
		{
			cout << dl.current() << endl;
			dl.remove(dl.find(dl.current()));
		}
		else
		{
			dl.pre(); //Locate to a precursor node
		}
	}
	cout << "delete end()" << endl;
	cout << "for_each begin" << endl;

	for (int i = 0; i < dl.length(); i++)
	{
		cout << dl.get(i) << endl;
	}


	return 0;
}

Result:

5
4
5
3
5
2
5
1
5
0
 delete begin()
5
5
5
5
5
delete end()
for_each begin
4
3
2
1
0

Tags: C++ data structure

Posted on Tue, 30 Nov 2021 15:27:39 -0500 by redsox8185