【C++】list接口及模拟实现

一、list 类介绍

  • list 底层使用了带头结点的双向循环链表,因此其特点:
    • 在任意位置的插入和删除效率较高,其他序列式容器相比
    • 不支持元素的随机访问
    • 查找元素的时间复杂度O(n)

在这里插入图片描述

二、list 常用接口

#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;
}

三、迭代器的认知及失效

  • 关于迭代器的认知:
    • 概念:是一种设计模式,用于遍历容器中的数据,而不暴露容器实现细节
    • 作用:是STL六大组件之一,容器和算法之间的纽带,算法实现通用化必须通过迭代器使用,与数据类型和数据结构都无关
    • 本质:原生态指针或者是对原生态指针的封装
    • 封装迭代器需要提供的方法:
      • 构造函数:构造一个迭代器的对象与元素结合
      • 添加指针类似操作:operator*()、operator->()
      • 迭代器移动:operator++()、operator- -()、operator++(int)、operator- -(int)
      • 迭代器比较:operator!=()、operator==()
    • 给容器增加迭代器:
      • 取别名:iterator
      • 增加 begin()、end() 接口

list 的迭代器不是原生态指针,而是对指针的封装,因为迭代器要实现能到达下一个节点
根本原因:链表底层不是一段连续的空间,而是一个个封装的节点,通过指针域链接起来

  • 关于迭代器失效的认知:底层指针所指空间已被释放,继续使用会造成程序崩溃
    • vector中迭代器失效:插入/删除/扩容
    • list 中迭代器失效:只会出现在删除中,并且只会影响删除节点的迭代器,对其他迭代器没有影响

四、模拟实现 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;
}

五、vector 和 list 的区别

相同点:都是STL提供的序列式容器,包含在 std 的命名空间中

vector list
底层结构 动态类型的顺序表 带头节点的双向循环链表
随机访问 支持,访问元素时间复杂度O(1) 不支持,访问元素O(n)
插入/删除效率 任意位置插入删除元素效率低 (搬移数据) 任意位置插入删除效率高
迭代器 原生态指针 对原生态指针进行的封装
迭代器失效 插入/删除/扩容 删除
应用场景 访问元素较多,而插入删除较少 任意位置插入删除较多

猜你喜欢

转载自blog.csdn.net/qq_45691748/article/details/113915474