[C++] List interface and simulation implementation

1. Introduction to the list class

  • The bottom layer of the list uses a two-way circular linked list with a leading node, so its characteristics:
    • Insertion and deletion at any position is more efficient, compared with other sequential containers
    • Random access of elements is not supported
    • Time complexity of finding elements O(n)

Insert picture description here

2. List common interfaces

#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main()
{
    
    
	list<int> l1;						// 空的构造
	list<int> l2(4, 5);					// 4个元素值为100
	list<int> l3(l2.begin(), l2.end());	// 迭代器构造
	list<int> l4(l3);					// 拷贝构造

	// 用数组构造
	int arr[] = {
    
     1,2,3,4,5 };
	list<int> l5(arr, arr + sizeof(arr) / sizeof(arr[0]));

	// 迭代器
	for (auto it = l5.begin(); it != l5.end(); ++it)
		cout << *it << " ";
	cout << endl;
	// 范围for遍历,要实现范围for遍历,应该在容器中增加begin()、end()还有迭代器比较方法
	for (auto& e : l5)
		cout << e << " ";
	cout << endl;
	
	// 容量
	l5.empty();		// 判空
	l5.size();		// 返回list 有效元素

	// 元素访问
	l5.front();		// 返回首元素
	l5.back();		// 返回尾元素
	
	l5.push_back(9);	// 尾插9
	l5.push_front(8);	// 头插8
	l5.pop_back();		// 尾删
	l5.pop_front();		// 头删

	l5.insert(l5.end(), 20);	  // 在尾部插入20
	l5.insert(l5.begin(), 2, 3);  // 插入2个3 
	l5.erase(--l5.end());		  // 尾部删除
	
	auto it = find(l5.begin(), l5.end(), 5);	// 查找元素5返回迭代器
	
	l5.clear();			// 清空
	return 0;
}

Third, the cognition and failure of iterators

  • Awareness about iterators:
    • Concept: is a design pattern used to traverse the data in the container without exposing the container implementation details
    • Function: It is one of the six major components of STL, the link between the container and the algorithm. The generalization of the algorithm must be used through the iterator, which has nothing to do with the data type and data structure.
    • Essence: the original ecological indicator or the encapsulation of the original ecological indicator
    • Methods that need to be provided to encapsulate iterators:
      • Constructor: Construct an iterator to combine the object and element
      • Similar operations for adding pointers: operator*(), operator->()
      • Iterator movement: operator++(), operator- -(), operator++(int), operator- -(int)
      • Iterator comparison: operator!=(), operator==()
    • Add an iterator to the container:
      • Alias: iterator
      • Add begin(), end() interfaces

The iterator of the list is not the original pointer, but the encapsulation of the pointer, because the iterator must be able to reach the next node. The
root cause: the bottom layer of the linked list is not a continuous space, but a encapsulated node, which is linked by the pointer field.

  • Cognition about iterator failure: the space pointed to by the underlying pointer has been released, and continued use will cause the program to crash
    • Iterator failure in vector: insert/delete/expand
    • The iterator in the list is invalid: it will only appear in the deletion, and will only affect the iterator of the deleted node, and has no effect on other iterators

Fourth, the simulation implementation list

#include <iostream>
using namespace std;
namespace my_list
{
    
    
	template <class T>
	struct ListNode
	{
    
    
		ListNode(const T& val = T())
			: val_(val)
			, pre_(nullptr)
			, next_(nullptr)
		{
    
    }
		T val_;
		ListNode* pre_;
		ListNode* next_;
	};
	template <class T>
	struct ListIterator
	{
    
    
		typedef ListNode<T> Node;
		typedef ListIterator<T> Self;
		ListIterator(Node* node = nullptr)
			:_pNode(node)
		{
    
    }
		T& operator*()
		{
    
    
			return _pNode->val_;
		}
		T* operator->()
		{
    
    
			return &(operator*());
		}
		Self& operator++()	// 前置++
		{
    
    
			_pNode = _pNode->next_;
			return *this;
		}
		Self operator++(int)	// 后置++
		{
    
    
			Self temp(_pNode);
			_pNode = _pNode->next_;
			return temp;
		}
		Self& operator--()	// 前置--
		{
    
    
			_pNode = _pNode->pre_;
			return *this;
		}
		Self operator--(int)	// 后置--
		{
    
    
			Self temp(_pNode);
			_pNode = _pNode->pre_;
			return temp;
		}
		bool operator!=(const Self& it)const 
		{
    
    
			return _pNode != it._pNode;
		}
		bool operator==(const Self& it)const 
		{
    
    
			return _pNode == it._pNode;
		}
		Node* _pNode;
	};
	template <class T>
	class list
	{
    
    
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T> iterator;
		list()
		{
    
    
			CreateHead();
		}
		list(size_t n, const T& data = T())
		{
    
    
			CreateHead();
			for(int i = 0; i < n; ++i)
				push_back(data);
		}
		//template <class Iterator>
		list(int* first, int* last)	// 迭代器构造必须采用模板,因为不同容器的迭代器实现可能不同
		{
    
    
			CreateHead();
			while (first != last)
			{
    
    
				push_back(*first);
				++first;
			}
		}
		list(const list<T>& node)
		{
    
    
			CreateHead();
			Node* cur = node.head_->next_;
			while (cur != node.head_)
			{
    
    
				push_back(cur->val_);
				cur = cur->next_;
			}
		}
		~list()
		{
    
    
			Clear();
			delete head_;
			head_ = nullptr;
		}
		list<T>& operator=(list<T> node)
		{
    
    
			swap(head_, node.head_);
			return *this;
		}
		iterator begin()
		{
    
    
			return iterator(head_->next_);
		}
		iterator end()
		{
    
    
			return iterator(head_);
		}
		size_t size()const
		{
    
    
			size_t count = 0;
			Node* cur = head_->next_;
			while (cur != head_)
			{
    
    
				cur = cur->next_;
				count++;
			}
			return count;
		}
		bool empty()const
		{
    
    
			return head_->next_ == head_;
		}
		void resize(size_t n, const T& data = T())
		{
    
    
			size_t oldsize = size();
			if (n > oldsize)
			{
    
    
				for (size_t i = oldsize; i < n; ++i)
				{
    
    
					push_back(data);
				}
			}
			else
			{
    
    
				for (size_t i = n; i < oldsize; ++i)
				{
    
    
					pop_back();
				}
			}
		}
		void push_back(const T& data)
		{
    
    
			insert(end(), data);
		}
		void pop_back()
		{
    
    
			erase(--end());
		}
		void push_front(const T& data)
		{
    
    
			insert(begin(), data);
		}
		void pop_front()
		{
    
    
			erase(begin());
		}
		iterator insert(iterator pos, const T& data)
		{
    
    
			Node* node = new Node(data);
			Node* posNode = pos._pNode;
			
			node->pre_ = posNode->pre_;
			node->next_ = posNode;
			posNode->pre_->next_ = node;
			posNode->pre_ = node;
			return iterator(node);
		}
		iterator erase(iterator pos)
		{
    
    
			if (pos == end())
				return pos;
			Node* node = pos._pNode;
			Node* pRet = node->next_;
			node->pre_->next_ = node->next_;
			node->next_->pre_ = node->pre_;
			delete node;
			node = nullptr;
			return iterator(pRet);
		}
		iterator erase(iterator begin, iterator end)
		{
    
    
			while (begin != end)
			{
    
    
				begin = erase(begin);
			}
			return begin;
		}
		void Clear()
		{
    
    
			erase(begin(), end());
		}
	private:
		void CreateHead()
		{
    
    
			head_ = new Node;
			head_->pre_ = head_;
			head_->next_ = head_;
		}
	private:
		Node* head_;
	};
}
void Test1()
{
    
    
	my_list::list<int> l1;
	my_list::list<int> l2(10, 5);
	my_list::list<int> l3(l1);

	int array[] = {
    
     1,2,3,4,5 };
	my_list::list<int> l4{
    
     array, array + sizeof(array) / sizeof(array[0]) };

	//auto it = l2.begin();
	my_list::ListIterator<int> it = l4.begin();
	while (it != l4.end())
	{
    
    
		cout << *it << " ";
		it++;
	}
	cout << endl;
	
	// 如果想要让容器支持范围for循环,则必须实现begin/end的方法,以及迭代器++操作
	for (auto e : l2)
		cout << e << " ";
	cout << endl;
}
void Test2()
{
    
    
	my_list::list<int> L;
	L.push_back(1);
	L.push_back(2);
	L.push_back(3);
	L.push_back(4);
	L.push_back(5);

	cout << L.size() << endl;
	L.resize(10, 6);
	for (auto e : L)
		cout << e << " ";
	cout << endl;

	L.resize(4);
	for (auto e : L)
		cout << e << " ";
	cout << endl;

	L.push_front(0);
	for (auto e : L)
		cout << e << " ";
	cout << endl;

	L.pop_front();
	for (auto e : L)
		cout << e << " ";
	cout << endl;

	L.erase(L.begin());
	for (auto e : L)
		cout << e << " ";
	cout << endl;

	L.Clear();
	if (L.empty())
	{
    
    
		cout << "ok" << endl;
	}
}
int main()
{
    
    
	// 迭代器是对原生态指针的封装
	Test1();
	return 0;
}

Five, the difference between vector and list

Similarity: Both are serial containers provided by STL and are included in the std namespace

vector list
Underlying structure Dynamically typed sequence table Two-way circular linked list with head node
Random access Support, access element time complexity O(1) Not supported, access element O(n)
Insert/delete efficiency Inserting and deleting elements at any position is inefficient (moving data) Insert and delete at any position with high efficiency
Iterator Original ecological pointer Encapsulation of the original ecological pointer
Iterator failure Insert/delete/expand delete
Application scenarios More access elements, but fewer insertions and deletions Insert and delete more at any position

Guess you like

Origin blog.csdn.net/qq_45691748/article/details/113915474