List

欢迎来到Cefler的博客😁

🕌博客主页:那个传说中的man的主页

🏠个人专栏:题目解析

🌎推荐文章:题目大解析2


目录

👉🏻List概念

C++中的list是一种双向链表数据结构。它可以存储任意类型的元素,并且可以在常数时间内在任意位置插入或删除元素。list提供了许多操作,如在开头或结尾插入/删除元素,访问元素,以及在列表中搜索元素。与向量(vector)相比,list的插入和删除操作更高效,但是访问元素的效率较低。

👉🏻List constructor

和vector差不多,list的构造函数中,有:

  • 无参默认构造
  • n个val值构造
  • 模板函数,迭代器区间初始化构造
  • 拷贝构造

List官方文档

👉🏻Iterators

👉🏻Capacity

👉🏻Element access

👉🏻Modifiers

👉🏻Operations

sort

可能有人会问,std中不是有提供一个全局的sort函数,为什么list内部还要再支持一个sort接口?

我们看下原因

噢,原来是因为list的迭代器是双向迭代器,而std的sort只接收随机迭代器类型,因为双向迭代器不支持+/-功能,所以list用不了std的sort,只能自己造轮子了。

list的sort默认情况下是升序,list的排序有:

  • 升序:< less
  • 降序:> greater
c 复制代码
	list<int> l;
	for (int i = 1; i <= 4; i++)
	{
		l.push_back(i);
	}
	//我们想要个降序
	greater<int> gt;
	//	list<int> l;
	for (int i = 1; i <= 4; i++)
	{
		l.push_back(i);
	}
	//我们想要个降序
	greater<int> gt;
	//l.sort(greater<int> ());匿名对象也可以
	l.sort(gt);

list的sort底层是实现是用归并排序

std的sort底层实现是快速排序,要比list的排序快,所以有时候,如果数据量比较少一般直接用list的排序,

但如果数据量大的话,我们可以先将list里面的数据拷贝到vector上,然后借助vector用std的排序进行快排,排序完之后,用assign的迭代器区间再拷贝回list中,这样就可以偷梁换柱了。

c 复制代码
#include <iostream>
#include <vector>
#include <list>
#include <algorithm> //使用sort排序记得包含头文件
using namespace std;
void Test1()
{
	list<int> l;
	for (int i = 10; i >=0; i--)
	{
		l.push_back(i);
	}
	vector<int> v(l.begin(), l.end());//拷贝进vector
	
	sort(v.begin(), v.end());

	l.assign(v.begin(), v.end());//再拷贝回去
	list<int>::iterator it = l.begin();

	while (it != l.end())
	{
		cout << *it << " ";
		it++;
	}
}

merge

合并两个链表,但是前提是这两个链表是有序的。

c 复制代码
// list::merge
#include <iostream>
#include <list>

// compare only integral part:
bool mycomparison (double first, double second)
{ return ( int(first)<int(second) ); }

int main ()
{
  std::list<double> first, second;

  first.push_back (3.1);
  first.push_back (2.2);
  first.push_back (2.9);

  second.push_back (3.7);
  second.push_back (7.1);
  second.push_back (1.4);

  first.sort();
  second.sort();

  first.merge(second);

  // (second is now empty)

  second.push_back (2.1);

  first.merge(second,mycomparison);

  std::cout << "first contains:";
  for (std::list<double>::iterator it=first.begin(); it!=first.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

输出:

first contains: 1.4 2.2 2.9 2.1 3.1 3.7 7.1

请注意,在第二次合并中,函数 mycompare(仅比较整数部分)没有考虑 2.1 低于 2.2 或 2.9,因此它入到它们之后,在 3.1 之前

unique

unique 函数的作用是将容器中相邻的重复元素保留一个,并删除其余的重复元素。具体而言,它会遍历容器中的元素,将相邻的重复元素中的后一个元素删除。

以下是 std::list 的 unique 函数的用法示例:

c 复制代码
#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 2, 3, 3, 3, 4, 5, 5};

    myList.unique();

    for (const auto& element : myList) {
        std::cout << element << " ";
    }

    return 0;
}

输出结果为:1 2 3 4 5。

注意,unique 函数只能去除相邻的重复元素,如果容器中存在非相邻的重复元素,它们不会被去除。如果需要去除所有重复元素,可以先使用 std::sort 函数对容器进行排序,然后再使用 unique 函数

remove

remove 函数用于删除容器中指定的元素。

remove 函数的工作原理是遍历容器,找到所有与指定值相等的元素,并将它们从容器中删除。

remove 函数的语法如下:

c 复制代码
void remove(const T& value);

splice

splice 是 std::list 类提供的一个成员函数,用于将另一个 std::list 的元素插入到当前 std::list 中的指定位置。

splice 函数的语法如下:👇🏻

c 复制代码
void splice (iterator position, list& x);
void splice (iterator position, list& x, iterator i);
void splice (iterator position, list& x, iterator first, iterator last);
  • 第一个版本将 x 中的所有元素插入到当前 list 的 position 之前。
  • 第二个版本将 x 中第i个元素开始之后的元素插入到当前 list 的 position 之前。
  • 第三个版本将 x 中的 [first, last) 范围内的元素插入到当前 list 的 position 之前。

注意,splice 操作会改变被插入的 list,并且被插入的元素会从原来的 list 中移除。

以下是一个示例代码,演示了如何使用 splice 函数:👇🏻

c 复制代码
#include <iostream>
#include <list>

int main() {
    std::list<int> list1 = {1, 2, 3};
    std::list<int> list2 = {4, 5, 6};

    auto it = list1.begin();
    std::advance(it, 2); // 将迭代器移动到第三个元素的位置

    list1.splice(it, list2); // 将 list2 中的所有元素插入到 list1 的第三个元素之前

    // 输出 list1 的元素
    for (const auto& num : list1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // 输出 list2 的元素
    for (const auto& num : list2) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出结果为:

1 2 4 5 6 3

👉🏻list的模拟实现

list_node(链表结点)

c 复制代码
template <class T>//模板结构体,为了存储可能不同的数据类型
		struct list_node   //这边用strcut主要是想开放所有的成员变量,所以不用class
		{
			T _data;
			list_node<T>* _next;
			list_node<T>* _prev;
			list_node(const T& val = T())
				:_data(val)
				, _next(nullptr)
				, _prev(nullptr)
			{

			}
		};

封装迭代器

c 复制代码
template <class T>
		struct __list_iterator//封装一个迭代器,实现迭代器的++/--等等功能,解引用
			//迭代器牛逼之处
			//封装屏蔽底层差异和实现细节
			//提供统一的访问修改方式
		{
			typedef list_node<T>   Node;
			typedef __list_iterator<T>  self;
			Node* _node;
			__list_iterator(Node* node)//传参进来
				:_node(node)
			{

			}
			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;
			}
			T& operator*()
			{
				return _node->_data;
			}
				T* operator->() //指针访问结构体
			{
				return &(_node->_data);
			}
			bool operator!=(const self& s)
			{
				return _node != s._node;
			}
			bool operator==(const self& s)
			{
				return _node == s._node;
			}
		};

insert

c 复制代码
iterator insert(iterator pos, const T& x)
			{
				Node* newnode = new Node(x);//先开辟新空间
				Node* cur = pos._node;

				Node* prev = cur->_prev;

				prev->_next = newnode;
				newnode->_prev = prev;

				newnode->_next = cur;
				cur->_prev = newnode;
				//insert后pos仍指向原空间,迭代器没失效

				++_size;
				return iterator(newnode);
			}

erase

c 复制代码
iterator erase(iterator pos)
			{
				Node* cur = pos._node;
				Node* prev = cur->_prev;//上一个结点
				Node* next = cur->_prev;//下一个结点

				prev->_next = next;
				next->_prev = prev;

				delete cur;
				//delete之后,cur即pos指向的空间已经不是原空间了,迭代器妥妥失效了!
				//所以需要返回删除位置的下一位置
				--_size;
				return iterator(next);
			}

push_back

c 复制代码
void push_back(const T& x)
			{
				//先建新空间
				Node* tail = _head->_prev;
				Node* newnode = new Node(x);
				
				
				tail->_next = newnode;
				newnode->_prev = tail;

				newnode->_next = _head;
				_head->_prev = newnode;
				

			}

insert进行尾插

c 复制代码
void push_back(const T& x)
			{
				insert(end(), x);
			}

头插/删,尾插/删

c 复制代码
           void push_back(const T& x)
			{
				insert(end(), x);
			}
			void push_front(const T& x)
			{
				insert(begin(), x);
			}
			void pop_back()
			{
				erase(--end());
			}
			void pop_front()
			{
				erase(begin());
			}

empty_init函数

c 复制代码
void empty_init()
			{
				_head = new Node;
				_head->_next  = _head;
				_head->_prev = _head;

				_size = 0;
			}

无参构造函数

c 复制代码
void empty_init()
			{
				_head = new Node;
				_head->_next  = _head;
				_head->_prev = _head;
			}
			list()
			{
				empty_init();

			}

swap函数、clear函数、拷贝构造函数、析构函数

c 复制代码
void swap(list<T>& lt)
			{
				std::swap(this->_head, lt._head);
				std::swap(this->_size, lt._size);

			}
			void clear()
			{
				_head->_next = _head;
				_head->_prev = _head;
			}
				/*	void clear()  //全部清除版本
			{
				iterator it = begin();
				while (it != end())
				{
					it = erase(it);
				}
			}*/
c 复制代码
list( list<T>& lt)//拷贝构造
			{
				empty_init();
				for (auto e : lt)
				{
					push_back(e);
				}
			}
			~list()
			{
				//clear();
				delete _head;
				_head = nullptr;
			}

迭代器的begin和end() 【非const】

c 复制代码
typedef __list_iterator<T>  iterator;
			iterator begin()  //返回值会将_head->_next的值拷入进行构造
			{
				//return (iterator)_head;这边无需强转
				return _head->_next;
			  }
			iterator end()
			{
				return _head;
			}

🌛插入自定义类型,编译器优化书写格式->

c 复制代码
struct AA
		{
			AA(int a1 = 0, int a2 = 0)
				:_a1(a1)
				, _a2(a2)
			{}

			int _a1;
			int _a2;
		};

		void test_list3()
		{
			list<AA> lt;
			lt.push_back(AA(1, 1));
			lt.push_back(AA(2, 2));
			lt.push_back(AA(3, 3));

			list<AA>::iterator it = lt.begin();
			while (it != lt.end())
			{
				//cout << (*it)._a1<<" "<<(*it)._a2 << endl;
				cout << it->_a1 << " " << it->_a2 << endl;
				cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;


				++it;
			}
			cout << endl;

		/*	int* p = new int;
			*p = 1;

			AA* ptr = new AA;
			ptr->_a1 = 1;*/

		}

const 迭代器

这不简单?我们已经写好了一个迭代器,直接const iterator不就好了。
但是list说 :在哥的地盘上,你在string和vector使的那招不管用,我的规矩才是规矩。

这是为什么呢,list的规矩是什么呢?
首先为什么说直接const iterator不行呢?

list中的迭代器很特殊,它不像string和vector一样能直接实现++/--等功能。

所以我们要专门去封装一个迭代器类型,在内部自己造轮子,自己动手,丰衣足食。

而问题也出在这里,如果说我一旦const iterator,这一个const直接让整个迭代器瘫痪 了,何出此言?我们定义一个const迭代器,本意上是让数据无法被更改,

结果好家伙,你不仅封印了const _data,还封印了++/--等功能,这不是扯淡吗,

现在连遍历都不得行了。

所以list的规矩就来了

既然这么直接const iterator这么难办,那我们只能再专门写一个const_iterator类型了,单独对其中的数据进行const这样就行了。

实现如下:👇🏻

c 复制代码
template <class T>
		struct __list_const_iterator//封装一个const_iterator迭代器,
		
		{
			typedef list_node<T>   Node;
			typedef __list_iterator<T>  self;
			Node* _node;
			__list_iterator(Node* node)//传参进来
				:_node(node)
			{

			}
			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;
			}
		const 	T& operator*() //const一下
			{
				return _node->_data;
			}
		const	T* operator->()//const一下
			{
				return &(_node->_data);
			}
			bool operator!=(const self& s)
			{
				return _node != s._node;
			}
			bool operator==(const self& s)
			{
				return _node == s._node;
			}
		};

实现const 迭代器优化(增加模板参数)

我们上述的实现const 迭代器的方法虽然可行。

但问题是代码太过冗余,我只是修改了operator*()和operator->(),而其它处的代码和iterator的都一模一样。

而解决办法也有,此时模板就开始展示它的威力了,

我们可以直接增加Ref(引用)、Ptr(指针)两个参数,然后分别tyepedef const和非const 的iterator,这样编译器就会自动匹配最适合的那个类模板。

c 复制代码
template <class T,class Ref,class Ptr>
		struct __list_iterator
		{
			typedef list_node<T>   Node;
			typedef __list_iterator<T,Ref,Ptr>  self;
			Node* _node;
			__list_iterator(Node* node)//传参进来
				:_node(node)
			{

			}
			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;
			}
			Ref operator*()
			{
				return _node->_data;
			}
			Ptr operator->() //指针访问结构体
			{
				return &(_node->_data);
			}
			bool operator!=(const self& s)
			{
				return _node != s._node;
			}
			bool operator==(const self& s)
			{
				return _node == s._node;
			}
		};
c 复制代码
typedef __list_iterator<T,T&,T*>  iterator;
typedef __list_iterator<T,const T&,const T*>  const_iterator;//const迭代器

实例化模板

我们来看个例子:

c 复制代码
	template <typename T>
		void Print_list(const list<T>& lt)
		{
			
			 list<T>::const_iterator it = lt.begin();
			 while (it != lt.end())
			 {
				 cout << *it << " ";
				 ++it;
			 }
		}
		void test_list4()
		{
		 list<string> lt;
			for (int i = 0; i < 4; i++)
			{
				lt.push_back("123");
			}
			Print_list(lt);

		}

当我们在list< string >类型前加上typename后就可以了。

可是为什么模板函数下为什么要这样多次一举呢?

原因如下

typename 这里就是具体实例化了一个模板对象 ,有了实例化对象后,编译器才可以去访问对象内部的情况,而这时,编译器才能知道如const_iterator是一个类型,

才可以初始化,那不然编译器判断不了到底是内嵌类型还是静态成员变量

🌠list.h

c 复制代码
#pragma once
#include<iostream>
#include<list>
#include <assert.h>
using namespace std;
namespace Space3
{
		template <class T>//模板结构体,为了存储可能不同的数据类型
		struct list_node   //这边用strcut主要是想开放所有的成员变量,所以不用class
		{
			T _data;
			list_node<T>* _next;
			list_node<T>* _prev;
			list_node(const T& val = T())
				:_data(val)
				, _next(nullptr)
				, _prev(nullptr)
			{

			}

		};
		template <class T,class Ref,class Ptr>
		struct __list_iterator//封装一个迭代器,实现迭代器的++/--功能,解引用
			//迭代器牛逼之处
			//封装屏蔽底层差异和实现细节
			//提供统一的访问修改方式
		{
			typedef list_node<T>   Node;
			typedef __list_iterator<T,Ref,Ptr>  self;
			Node* _node;
			__list_iterator(Node* node)//传参进来
				:_node(node)
			{

			}
			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;
			}
			Ref operator*()
			{
				return _node->_data;
			}
			Ptr operator->() //指针访问结构体
			{
				return &(_node->_data);
			}
			bool operator!=(const self& s)
			{
				return _node != s._node;
			}
			bool operator==(const self& s)
			{
				return _node == s._node;
			}
		};
	
		template <class T>
		class list
		{
			typedef list_node<T>   Node;

		public:
			typedef __list_iterator<T,T&,T*>  iterator;
			typedef __list_iterator<T,const T&,const T*>  const_iterator;//const迭代器
			iterator begin()  //返回值会将_head->_next的值拷入进行构造
			{
				
				return _head->_next;
			  }
			iterator end()
			{
				return _head;
			}
			const_iterator begin() const
			{
				return const_iterator(_head->_next);
		      }
			const_iterator end()const
			{
				return const_iterator(_head);
			}
			void empty_init()
			{
				_head = new Node;
				_head->_next  = _head;
				_head->_prev = _head;

				_size = 0;
			}
			void swap(list<T>& lt)
			{
				std::swap(this->_head, lt._head);
				std::swap(this->_size, lt._size);

			}
			void clear()
			{
				_head->_next = _head;
				_head->_prev = _head;
			}
		
		/*	void clear()  //全部清除版本
			{
				iterator it = begin();
				while (it != end())
				{
					it = erase(it);
				}
			}*/
			list()
			{
				empty_init();

			}
			list( const list<T>& lt)//拷贝构造
			{
				empty_init();
				for (auto e : lt)
				{
					push_back(e);
				}
			}
			~list()
			{
				//clear();
				delete _head;
				_head = nullptr;
			}
			//void push_back(const T& x)
			//{
			//	//先建新空间
			//	Node* tail = _head->_prev;
			//	Node* newnode = new Node(x);
			//	
			//	
			//	tail->_next = newnode;
			//	newnode->_prev = tail;

			//	newnode->_next = _head;
			//	_head->_prev = newnode;
			//	
			//}
			void push_back(const T& x)
			{
				insert(end(), x);
			}
			void push_front(const T& x)
			{
				insert(begin(), x);
			}
			void pop_back()
			{
				erase(--end());
			}
			void pop_front()
			{
				erase(begin());
			}
			iterator insert(iterator pos, const T& x)
			{
				Node* newnode = new Node(x);//先开辟新空间
				Node* cur = pos._node;

				Node* prev = cur->_prev;

				prev->_next = newnode;
				newnode->_prev = prev;

				newnode->_next = cur;
				cur->_prev = newnode;
				//insert后pos仍指向原空间,迭代器没失效

				++_size;
				return iterator(newnode);
			}
			iterator erase(iterator pos)
			{
				Node* cur = pos._node;
				Node* prev = cur->_prev;//上一个结点
				Node* next = cur->_prev;//下一个结点

				prev->_next = next;
				next->_prev = prev;

				delete cur;
				//delete之后,cur即pos指向的空间已经不是原空间了,迭代器妥妥失效了!
				//所以需要返回删除位置的下一位置
				--_size;
				return iterator(next);
			}
			list<T>& operator=(list<T>& lt)
			{
				swap(lt);
				return *this;
			}
		
		private:
			Node* _head;//头节点(哨兵卫)
			size_t _size;
		};
		void test_list1()
		{
			list<int> l;
			for (int i = 1; i<=5; i++)
			{
				l.push_back(i);
			}
			l.clear();
			for (int i = 1; i <= 5; i++)
			{
				l.push_back(i);
			}
			list<int>::iterator it = l.begin();
			while (it != l.end())
			{
				cout << *it << " ";
				++it;
			}
			cout << endl;
			for (auto e : l)
			{
				cout << e << " ";
			}
		}
		void test_list2()
		{
			list<int> lt1;
			for (int i = 1; i <= 4; i++)
			{
				lt1.push_back(i);
			}
			list<int> lt2(lt1);
			lt2.insert(lt2.begin(), 0);
			for (auto e : lt2)
			{
				cout << e << " ";
			}
		}
	
		struct AA
		{
			AA(int a1 = 0, int a2 = 0)
				:_a1(a1)
				, _a2(a2)
			{}

			int _a1;
			int _a2;
		};

		void test_list3()
		{
			list<AA> lt;
			lt.push_back(AA(1, 1));
			lt.push_back(AA(2, 2));
			lt.push_back(AA(3, 3));

			list<AA>::iterator it = lt.begin();
			while (it != lt.end())
			{
				//cout << (*it)._a1<<" "<<(*it)._a2 << endl;
				cout << it->_a1 << " " << it->_a2 << endl;
				cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;


				++it;
			}
			cout << endl;

		/*	int* p = new int;
			*p = 1;

			AA* ptr = new AA;
			ptr->_a1 = 1;*/

		}
		template <typename T>
		void Print_list(const list<T>& lt)
		{
			typename list<T>::const_iterator it = lt.begin();
			 while (it != lt.end())
			 {
				 cout << *it << " ";
				 ++it;
			 }
		}
		void test_list4()
		{
		 list<string> lt;
			for (int i = 0; i < 4; i++)
			{
				lt.push_back("123");
			}
			Print_list(lt);

		}
}

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

相关推荐
火云洞红孩儿7 分钟前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
FeboReigns1 小时前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns1 小时前
C++简明教程(10)(初识类)
c语言·开发语言·c++
zh路西法1 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
.Vcoistnt2 小时前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
小k_不小2 小时前
C++面试八股文:指针与引用的区别
c++·面试
沐泽Mu2 小时前
嵌入式学习-QT-Day07
c++·qt·学习·命令模式
ALISHENGYA2 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战训练三)
数据结构·c++·算法·图论
GOATLong3 小时前
c++智能指针
开发语言·c++