完整教程:【C++】--list的使用和模拟实现

一、list的介绍和利用

1、认识list

List和vector一样也是C++标准模板库中的一个容器,其中文名就是我们前面数据结构学习的链表,那么其和我们前面学习的string类和vector类最大的不同就是其物理结构是不连续的,其是一个一个的节点构成的,接着list是一个双向带头循环链表:

上面就是list的简单图示了,可以看到其一个结点4主要是三个部分,一个是存储的数据,然后一个是指向后一个节点,一个是指向前一个节点,因而我们也说在逻辑上我们的list是连续的。

下面我们来看看其启用


2、list的使用

(1)list对象的实例化

其语法和vector的一样:list<类型>+对象名。

其实数据类型能够是内置类型也可以是自定义类型

(2)list对象的初始化

库中的构造函数就是这个

第一个就是默认构造一个空的list,然后其有一个默认值。

第二个就是给构造的list对象构造为n给第二个参数的值。

第三个构造就是赋值运算符重载构造

第四个构造就是利用我们的迭代器的方式进行构造

拷贝构造了就是第五个

可以看到list不仅支持迭代器,还承受其他类型数据的迭代器进行初始化

那么说完材料的初始化,我们的list主要使用:增删改查这四种作用

(3)尾插元素

应用push_back函数进行尾插:就是其尾插函数的采用和前面学习的string和vector的尾插是一样的,也

如上,这是其库中的原型。

  (4)尾删

我们的list的尾删函数和前面学习的vector和string类也是一样的名称:

(5)获取list中的元素

前面我们学习string和vector容器的时候,基于这两个容器的物理结构都是连续的,那么其就支持[]访问元素的方式,那么我们的list的结构是不连续的,所以就没有支持这种方式进行访问元素,我们前面学习数据结构的时候,我们对于元素的访问都是通过指向下一个结点的指针进行访问。

那么我们在list中是否也只能这样进行数据的访问呢?

其实不然,我们的C++容器,大部分都承受采用迭代器的方式进行数据的访问,于是list中其也拥护这个方式:

那么我们就许可使用++的方式对list的元素进行访问了。

那么支持迭代器就支持范围for:

那么就有同学会很疑惑,lits的空间都不是连续的,那么其怎么通过++就允许找到下一个结点的位置的呢?

这个我们后面模拟实现的时候会进行讲解。

(6)指定结点前插入

该功能在vector和string类中也都有提供

(7)删除指定位置的元素

我们的list中也献出了指定位置删除的操控:

可以看到其还支持迭代器的方式进行数据的删除。

随后就是对于我们要连续对内容进行删除,然后利用迭代器的障碍,前面我们学习vector类的时候讲过迭代器失效的难题,如下面这种情况就会出现:

上面的情况,没有启用接收值来接收返回值,那么就会造成迭代器失效,所以当我们要使用迭代器进行连续的删除的时候,为了避免迭代器失效的问题,我们要使用一个来接收其返回值。就是如果

(8)排序sort

在我们的库中还提供了排序的接口,我们许可使用其对数据进行排序

然后我们对于list的排序的话,在数据量少的时候没啥影响,但是当数据很多的时候,其效率就不是很高了,虽然其时间复杂度是O(n log n)。

上面这个是升序的排法,然后我们的sort还支持降序的排序:

(9)链接

将两个原本独立的list拼在一起成为一个新的list,不过其支持很多种情况。就是这个的话就

第一个就是两个list进行拼接,然后就是单个元素进行拼接,指定范围拼接等

下面我演示一下整体拼接和单个元素拼接:

(10)去重

要注意的是去重的话要注意我们的链表是有序的:

(11)逆置

这个接口我们在string和vector的时候也遇到了:

注意:

大家前面已经提到了,我们的list链表其在逻辑上是连续的,然而其实际物理结构上不是连续的,所以我们在运用上是不行通过+n的方式寻找元素。

二、list的模拟实现

(1)整体实现结构

首先大家知道的是我们的库中的list是双向循环带头链表,然后其是由一个一个的结点组成的,然后我们的结点里是由一个存储数据,一个指向下一个结点,一个指向前一个结点的指针,一共三个成员组成的。

随后和string和vector类的建立一样,为了避免和库中的起冲突,我们将其放置在一个命名空间中。

该时候就会有同学会发出疑问了,为啥我们这个时候去启用struct来定义list类结点,这是因为我们的链表三个成员都要经常被进行访问,然后要是我们使用class进行定义,然后其被私有,那么我们就还是要将其友元化,那么我们不如直接将其公开即可。

然后我们的list是带头的链表,那么我们还需要一个头结点

我们整个list的初始实现的结构。就是那么上面就

(2)尾插

尾插就是在链表的尾部插入一个结点,那么我们就要先找到当前链表的尾结点,那么我们头结点的前一个结点就是当前链表的尾结点,那么我们就可以进行插入了,不过要注意的是我们要先将插入前的尾结点的地址记录下来,然后再进行修改头结点指向前一个结点的指针,将其指向我们要插入的结点,然后我们插入的新的结点的指向下一个结点的指针指向头结点,接着原来的尾部结点的指向下一个结点的指针指向新插入的结点。

(3)尾删

尾删很简单的,就找到尾部结点然后删除即可,可是也要注意的是,当我们该list只有头结点的时候是不可以进行删除操作的。

(4)构造函数

首先就是我们普通结点的构造:

然后我们的头结点部分也要写一下其构造函数:

(5)运算符重载

实现的迭代器上的。就是该我们

1、解引用

解引用就是返回这个位置存储的素材即可:

2、->运算符重载

其写法如下:

3、前置++
通过这个就是将我们的迭代器移动到下一个结点,那么我们就能够使用++的方式进行数据的遍历和访问了:

4、后置++
后置++的话,其返回的还是当前的位置,但是使用完后,指针的要指向下一个结点的位置,那么我们可能使用一个临时变量存储当前的位置,然后再将指针 往下一个结点移动,然后返回记录好的原来的结点的位置:

这里大家会发现我们此时没有使用引用返回,这是因为我们的tmp是函数中创建的一个临时变量,那么出了这个函数后,这个变量就会被销毁了,那么就会造成野引用的问题。

5、前置--

我们的list库中,其是一个双向的迭代器,那么其就允许向前--的操作:

6、后置--

该和前面的后置++一个道理:

7、==

这个就很简便了:

8、!=

(6)const迭代器

我们上面对于迭代器的内容的完成都是对于普通的变量进行的,那么对于const对象,其和普通对象的迭代器的区别就是,要求其指向的内容不可以被进行修改,而迭代器本身是可以进行修改的。

修饰迭代器本身的。所以大家针对这个情况,我们可以单独搭建一个const迭代器的类。就是所以我们有的同学一开始会想到使用const iterationor的方式来表示const迭代器,这个const

我们会发现,除了解引用和->的部分,其他的代码和普通迭代器没有区别。

这样我们的代码的复用率就比较低了,所以我们考虑另外一种方式来实现。

我们采用模板来进行优化,提高代码的复用率:
由于普通迭代器和const的迭代器其实际上只是返回类型不同,那么我们不妨增加两个模板参数

(7)迭代器begin和end

这两个就很容易了,就是其有两个重载,一个是普通的迭代器,一个是const对象的迭代器:

(8)链表的其他操作

1、insert

该函数的作用是在pos的位置之前插入一个结点:

2、push_back

通过这个函数我们上面是按照尾插的逻辑写的,然后我们也能够和下面这种方式写:

3、push_front

这个函数是使用头插的,那么我们和上面的push_back一样复用insert即可:

4、erase

该函数是删除指定位置的结点:

上面是按照arase的逻辑来实现的,不过我们前面学习vector提到的迭代器失效问题,其主要是出现在删除数据移动数据和扩容上,我们这边删除内容,那么就会使得原来的迭代器变成了一个野指针,那么后面我们进行访问数据的操作的时候 ,编译器就会报错。

所以我们这边对于areas的话,我们对其弄一个返回值,然后对原来的迭代器进行赋值,那么就不会出现这个难题了。

5、swap

交换两个链表,继而我们只得将链表中的成员变量进行交换即可,我们在函数中调用库中的swap函数对其一个一个进行交换即可:就是这个函数的作用

(9)容量

我们在设计链表的时候可以添加一个成员变量,用来记录结点个数,因此我们这个函数直接返回这个成员变量即可:

(10)clear

这个函数的作用是用来直接清空链表的结点的:

(11)拷贝构造

我们的拷贝构造核心是两个,一个是普通的拷贝构造,然后还有一个就是赋值运算符拷贝:


(12)析构函数

这个大家可以先清空链表,然后将头结点释放:

三、完整代码

#pragma once
#include
using namespace std;
namespace cyy
{
	//链表结点
   template
   struct list_node
   {
	   list_node(const T &x= T())
		   :_next(nullptr)
		   ,_prev(nullptr)
		   ,_date(x)
	   {
	   }
	   //节点成员
	   list_node _date;
	   list_node* _next;
	   list_node* _prev;
   };
   //迭代器
   template
   struct _list_iterator
   {
	   typedef list_node Node;
	   typedef list_iterator Self;
	   Node* _node;
	   Ref operator*()
	   {
		   return _node->_date;
	   }
	   Ptr operator->()
	   {
		   return &_node->_date;
	   }
	   Self& operator++()
	   {
		   _node = _node->_next;
		   return *this;
	   }
	   Self operator++(int)
	   {
		   Self tmp(*this);
		   _node = _node->_next;
		   return tmp;
	   }
	   Self& operator--()
	   {
		   _node = _node->_prev;
		   return *this;
	   }
	   Self operator--(int)
	   {
		   Self tmp(*this);
		   _node = _node->_prev;
		   return tmp;
	   }
	   bool operator==(const Self& s) const
	   {
		   return _node == s._node;
	   }
	   bool operator!=(const Self& s)const
	   {
		   return _node != s._node;
	   }
   };
	   //const迭代器
 //template
	//struct __list_const_iterator
	//{
	//	typedef list_node Node;
	//	Node* _node;
	//	__list_const_iterator(Node* node)
	//		:_node(node)
	//	{}
	//	const T& operator*()
	//	{
	//		return _node->_data;
	//	}
	//	__list_const_iterator& operator++()
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}
	//	__list_const_iterator operator++(int)
	//	{
	//		__list_iterator tmp(*this);
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//	__list_const_iterator& operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//	__list_const_iterator operator--(int)
	//	{
	//		__list_const_iterator tmp(*this);
	//		_node = _node->_prev;
	//		return tmp;
	//	}
	//	bool operator!=(const __list_const_iterator& it) const
	//	{
	//		return _node != it._node;
	//	}
	//	bool operator==(const __list_const_iterator& it) const
	//	{
	//		return _node == it._node;
	//	}
	//};
   //链表
   template
   class list
   {
	   typedef list_node	Node;
   public:
	   typedef _list_iterator iterator;
	   typedef _list_iterator const_iterator;
	   list()
		   :_head(nullptr)
	   {
		   Head = new Node;
		   Head->_next = _head;
		   Head->_prev = _head;
	    }
	   void empty_init()
	   {
		   _head = new Node;
		   _head->_next = _head;
		   _head->_prev = _head;
	   }
	   list(const list& it)
	   {
		   empty_init();
		   for (const auto& e : it)
		   {
			   push_back(e);
		   }
	   }
	   list& operator=(list& it)
	   {
		   swap(it);
		   return *this;
	   }
	   ~list()
	   {
		   clear();
		   delete _head;
		   _head = nullptr;
	   }
	   iterator begin()
	   {
		   return iterator(_head->_next);
	   }
	   const_iterator begin()
	   {
		   return const_iterator(_head->_next);
	   }
	   iterator end()
	   {
		   return iterator(_head);
	   }
	   const_iterator end()
	   {
		   return const_iterator(_head);
	   }
	   void pop_back()
	   {
		   assert(_head->next != Head);
		   Node* pp = _head->_prev;
		   Node* pur = pp->_prev;
		   head->_prev = pur;
		   pur->_next = _head;
		   delete pp;
	   }
	   void insert(iterator pos, const T& x)
	   {
		   Node* cur = pos._node;
		   Node* prev = cur->_prev;
		   Node* newnode = new Node(x);
		   prev->_next = newnode;
		   newnode->_next = cur;
		   newnode->_prev = prev;
		   cur->_prev = newnode;
		   ++_size;
	   }
	   // void push_back(const T& _date)
	   //{
		  // //开辟结点空间
		  // Node* tmp = new Node(_date);
		  // Node* pp = _head->_prev;
		  // tmp->_date = _date;
		  // tmp->_next = _head;
		  // tmp->_prev = pp;
		  // pp->_next = tmp;
		  // _head->_prev = tmp;
	   //}
	   void push_back(const T& x)
	   {
		   insert(end(), x);
	   }
	   void push_front(const T& x)
	   {
		   insert(begin(), x);
	   }
	   iterator erase(iterator pos, const T& x)
	   {
		   assert(pos != end());//方式删了哨兵位头结点
		   Node* cur = pos._node;
		   Node* next = cur->_next;
		   Node* prev = cur->_prev;
		   prev->_next = next;
		   next->_prev = prev;
		   delete cur;
		   --_size;
		   return iterator(next);
	   }
	   void swap(list& it)
	   {
		   std::swap(_head, it._head);
		   std::swap(_size, it._size);
	   }
	   size_t size()
	   {
		   return _size;
	   }
	   void clear()
	   {
		   auto it = begin();
		   while (it != end())
		   {
			   it = erase(it);
		   }
		   _size = 0;
	   }
   private:
	   Node* _head;
	   size_t _size = 0;
   };
}

posted @ 2025-11-26 11:40  yangykaifa  阅读(13)  评论(0)    收藏  举报