STL中的list以及简单实现

STL的list的底层结构其实就是带头双向循环双向链表

带头双向循环双向链表又简单又好用,效率又高,所以其结构是完美的(对于链表而言):

其中一个原因:有哨兵位的头节点,又循环,找尾很方便,也就是有着O(1)的时间复杂度的插入删除

1.list的构造:

| (constructor) 构造函数声明 | 接口说明 |
| list (size_type n, const value_type& val = value_type()) | 构造的 list 中包含 n 个值为 val 元素 |
| list() | 构造空的 list |
| list (const list& x) | 拷贝构造函数 |

list (InputIterator first, InputIterator last) [first, last) 区间中的元素构造 list

2.list iterator的使用

| 函数声明 | 接口说明 |
| begin + end | 返回第一个元素的迭代器 + 返回最后一个元素下一个位置的迭代器 |

rbegin + rend 返回第一个元素的 reverse_iterator, end 位置返回最后一个元素下一个位 置的 reverse_iterator, begin 位置

说到迭代器遍历的话,与string,vector都一样;

cpp 复制代码
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);

list<int>::iterator it = lt.begin();
while (it != lt.end())
{
	cout << *it << " ";
}
cout << endl;

for (auto e : lt)
{
	cout << e << " ";
}
cout << endl;

是否还支持下标+【】? list就没有支持方括号了:

比如说从第一个开始到size,下标倒是能想办法获取,但是方括号不支持了,支持 operator [ ] 的话,成本会很高,比如说我要获取我的第3个数据,第5个数据,我是不是得从头开始挨着挨着往后才能遍历获取到第3个,第5个?从功能上来说,从可行性上来说,可以实现的。这个角度来说,语法支持,那它是可以实现 operator [ ] 的。只不过 list 的 operator [ ] 是O(N),但是之前像string,vector 的 operator [ ] 是O(1),因为之前像数组这种结构呢,它是连续的物理空间,想获取第几个的话,它的物理空间是连续的,我有指向,你开始的指针,你想获取第二个我是不是加a就过去了,对不对?但是这个地方这些链表呢,它的底层的这些节点呢都是不连续的,你加I你就加不过去,在这个地方,所以我们在这呢是不能这样玩的,不支持 operator [ ] 的。

链表:(空间展示)

数组: (空间展示)

可以看出:list的迭代器的实现就不再是原生指针了:(不支持+/-)

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

using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);

	list<int>::iterator it = lt.begin();
	lt.erase(it + 3);//二进制"+":"std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>>"不定义该运算符或到预定义运算符可接收的类型的转换

	return 0;
}

扩展:不同结构带来的各自不同所属迭代器的性质区别

决定了可以使用那些算法 :

cpp 复制代码
void test_list1()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	list<int>::iterator it = lt.begin();
	// 不支持,算法中的sort要求随机迭代器
	//sort(lt.begin(), lt.end());



	string s("dadawdfadsa");
	cout << s << endl;
	//string的迭代器就能被支持
	sort(s.begin(), s.end());
	cout << s << endl;
}

【注意】

  1. begin end 为正向迭代器,对迭代器执行 ++ 操作,迭代器向后移动
  2. rbegin(end) rend(begin) 为反向迭代器,对迭代器执行 ++ 操作,迭代器向前移动

3.list capacity:

| 函数声明 | 接口说明 |
| empty | 检测 list 是否为空,是返回 true ,否则返回 false |

size 返回 list 中有效节点的个数

4.list element access:

| 函数声明 | 接口说明 |
| front | 返回 list 的第一个节点中值的引用 |

back 返回 list 的最后一个节点中值的引用

5.list modifiers:

| 函数声明 | 接口说明 |
| push_front | list 首元素前插入值为 val 的元素 |
| pop_front | 删除 list 中第一个元素 |
| push_back | list 尾部插入值为 val 的元素 |
| pop_back | 删除 list 中最后一个元素 |
| insert | list position 位置中插入值为 val 的元素 |
| erase | 删除 list position 位置的元素 |
| swap | 交换两个 list 中的元素 |

clear 清空 list 中的有效元素

其实list库中还有一个接口:emplace_back 与push_back功能类似,但是emplace_back的效率相对较高,而且,emplace_back支持直接传构造对象的参数:

cpp 复制代码
	list<A> lt;
	A aa1(1, 1);
	lt.push_back(aa1);
	lt.push_back(A(2, 2));
	//lt.push_back(3, 3);不支持直接传构造A对象的参数

	lt.emplace_back(aa1);
	lt.emplace_back(A(2, 2));
	cout << endl;
	//支持直接传构造A对象的参数emplace_back
	lt.emplace_back(3, 3);

算法库中还有自己定义实现的sort(底层是归并){因为算法库的sort(底层主要是快排,递归深度过大才会选择堆排)不支持list结构(于swap基本一样的处境)},而且sort都是默认升序 。如果想实现降序的话要用到仿函数(greater这个模板):

cpp 复制代码
list<int> lt;
lt.push_back(1);
lt.push_back(20);
lt.push_back(3);
lt.push_back(5);
lt.push_back(4);
lt.push_back(5);
lt.push_back(6);

for (auto e : lt)
{
	cout << e << " ";
}
cout << endl;

// 升序
lt.sort();

// 降序 - 仿函数
// less<int> ls;
// greater<int> gt;
// lt.sort(gt);
lt.sort(greater<int>());//使用匿名对象

但是,list中的sort的效率并不是很高,下面我们拿vector与其对比:

注意:要在release版本,不要在debug版本下对比两者,因为debug没有优化,可能起不到公平的对比:

cpp 复制代码
void test_op1()
{
	srand(time(0));
	const int N = 1000000;

	list<int> lt1;
	vector<int> v;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand() + i;
		lt1.push_back(e);
		v.push_back(e);
	}

	int begin1 = clock();
	// 排序
	sort(v.begin(), v.end());
	int end1 = clock();

	int begin2 = clock();
	lt1.sort();
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

void test_op2()
{
	srand(time(0));
	const int N = 1000000;

	list<int> lt1;
	list<int> lt2;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand()+i;
		lt1.push_back(e);
		lt2.push_back(e);
	}

	int begin1 = clock();
	// 拷贝vector
	vector<int> v(lt2.begin(), lt2.end());

	// 排序
	sort(v.begin(), v.end());

	// 拷贝回lt2
	lt2.assign(v.begin(), v.end());

	int end1 = clock();

	int begin2 = clock();
	lt1.sort();
	int end2 = clock();

	printf("list copy vector sort copy list sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}
cpp 复制代码
int main()
{
	test_op1();
	test_op2();
	return 0;
}

其实list库中还有reverse(),但其实和算法中的reverse一样:(历史原因)

cpp 复制代码
lt.reverse();
reverse(lt.begin(), lt.end());

splice也是list库中的接口,可以剪切自己或别人,将剪切的片段或元素添加到自己本身位置,它实现的是一种粘接,与复制粘贴不一样,他会影响对方,会夺取对方(从对方身上拔下来)

cpp 复制代码
// 一个链表节点转移给另一个链表
std::list<int> mylist1, mylist2;
std::list<int>::iterator it;

// set some initial values:
for (int i = 1; i <= 4; ++i)
	mylist1.push_back(i);      // mylist1: 1 2 3 4

for (int i = 1; i <= 3; ++i)
	mylist2.push_back(i * 10);   // mylist2: 10 20 30

it = mylist1.begin();
++it;                         // points to 2

mylist1.splice(it, mylist2); 
// mylist1: 1 10 20 30 2 3 4
// mylist2 (empty)
// "it" still points to 2 (the 5th element

6.list的迭代器失效:

可将迭代器暂时理解成类似于指针, 迭代器失效即迭代器所指向的节点的无 效,即该节点被删除了 。因为 list 的底层结构为带头结点的双向循环链表 ,因此 list 中进行插入 时是不会导致 list 的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭 代器,其他迭代器不会受到影响:

cpp 复制代码
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);

// insert以后迭代器不失效
list<int>::iterator it = lt.begin();
lt.insert(it, 10);
*it += 100;

print_container(lt);

// erase以后迭代器失效
// 我们应该将erase后,保留迭代器
// 删除所有的偶数
it = lt.begin();
while (it != lt.end())
{
	if (*it % 2 == 0)
	{
		it = lt.erase(it);
	}
	++it;
}

print_container(lt);

7.list的底层的简单实现:

实现代码+测试代码:

cpp 复制代码
#pragma once
#include<assert.h>

namespace home
{
	template<class T>
	struct list_node
	{
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;

		list_node(const T& data = T())
			:_data(data)
		    ,_next(nullptr)
			,_prev(nullptr)
		{}
	};


	封装
	//template<class T>
	//struct list_iterator
	//{
	//	typedef list_node<T> Node;
	//	typedef list_iterator<T> Self;
	//	Node* _node;
	//	list_iterator(Node* node)
	//		:_node(node)
	//	{}
	//	T& operator*()
	//	{
	//		return _node->_data
	//	}
	//	T* operator->()
	//	{
	//		//返回地址
	//		return &(_node->_data);
	//	}
	//	Self& operator++()
	//	{
	//		//我多写this->是为了方便理解
	//		this->_node = _node->_next;
	//		return *this;
	//	}
	//	Self operator++(T)
	//	{
	//		Self tmp(*this);
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//	Self& operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//	Self operator--(T)
	//	{
	//		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;
	//	}
	//};
	//template<class T>
	//struct list_const_iterator
	//{
	//	typedef list_node<T> Node;
	//	typedef list_const_iterator<T> Self;
	//	Node* _node;
	//	list_const_iterator(Node* node)
	//		:_node(node)
	//	{}
	//	const T& operator*()
	//	{
	//		return _node->_data;
	//	}
	//	const T* operator->()
	//	{
	//		//返回地址
	//		return &(_node->_data);
	//	}
	//	Self& operator++()
	//	{
	//		//我多写this->是为了方便理解
	//		this->_node = _node->_next;
	//		return *this;
	//	}
	//	Self operator++(T)
	//	{
	//		Self tmp(*this);
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//	Self& operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//	Self operator--(T)
	//	{
	//		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_iteerator与iterator基本相似,可以用两个模板进行封装
    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)
		{}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &(_node->_data);
		}
		Self& operator++()
		{
			this->_node = _node->_next;
			return *this;
		}
		Self operator++(int)
		{
			Self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		Self& operator--()
		{
			_node = _node->_next;
			return *this;
		}
		Self operator--(int)
		{
			Self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		bool operator!=(const Self& s)const
		{
			return _node != s._node;
		}
		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};


	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		/*typedef list_iterator<T> iterator;
		typedef list_const_iterator<T> const_iterator;*/
		
		//两个模板
		typedef list_iterator<T, T&, T*> iterator;
		typedef list_iterator<T, const T&, const T*> const_iterator;

		iterator begin()
		{
			//匿名+隐式类型转换
			return _head->_next;
		}
		
		iterator end()
		{
			return  _head;
		}

		const_iterator begin()const
		{
			return _head->_next;
		}

		const_iterator end()const
		{
			return _head;
		}

		//空初始化
		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		//默认构造
		list()
		{
			empty_init();
		}

		//{.....}初始化
		list(initializer_list<T> il)
		{
			empty_init();
			for (auto& e : il)
			{
				push_back(e);
			}
		}

		//拷贝构造
		list(const list<T>& lt)
		{
			empty_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
		}

		//赋值拷贝
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		void swap(list<T> lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		void push_back(const T& x)
		{
			insert(end(), x);
		}
		void front_back(const T& x)
		{
			insert(begin(), x);
		}
	    iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

			newnode->_prev = prev;
			prev->_next = newnode;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;

			return newnode;
		}
		void pop_back()
		{
			erase(--end());
		}
		void pop_front()
		{
			erase(begin());
		}
		iterator erase(iterator pos)
		{
			assert(pos != end());//不可以删头节点
			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;

			prev->_next = next;
			prev->_prev = prev;
			delete pos._node;
			--_size;

			return next;
		}
		size_t size()const
		{
			return _size;
		}
		bool empty()const
		{
			return _size == 0;
		}
	private:
		Node* _head;
		size_t _size;
	};
	struct AA
	{
		int _a1 = 1;
		int _a2 = 1;
	};

	// 按需实例化
	// T* const ptr1
	// const T* ptr2
	template<class Container>
	void print_container(const Container& con)
	{
		// const iterator -> 迭代器本身不能修改
		// const_iterator -> 指向内容不能修改
		typename Container::const_iterator it = con.begin();
		//auto it = con.begin();
		while (it != con.end())
		{
			//*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : con)
		{
			cout << e << " ";
		}
		cout << endl;
	}
	void test_list1()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;
		print_container(lt);

		list<AA> lta;
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		list<AA>::iterator ita = lta.begin();
		while (ita != lta.end())
		{
			//cout << (*ita)._a1 << ":" << (*ita)._a2 << endl;
			// 特殊处理,本来应该是两个->才合理,为了可读性,省略了一个->
			cout << ita->_a1 << ":" << ita->_a2 << endl;
			cout << ita.operator->()->_a1 << ":" << ita.operator->()->_a2 << endl;

			++ita;
		}
		cout << endl;
	}
	void test_list2()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		// insert以后迭代器不失效
		list<int>::iterator it = lt.begin();
		lt.insert(it, 10);
		*it += 100;

		print_container(lt);

		// erase以后迭代器失效
		// 删除所有的偶数
		it = lt.begin();
		while (it != lt.end())
		{
			if (*it % 2 == 0)
			{
				it = lt.erase(it);
			}
			it++;
		}

		print_container(lt);
	}

	void test_list3()
	{
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);

		list<int> lt2(lt1);

		print_container(lt1);
		print_container(lt2);

		list<int> lt3;
		lt3.push_back(10);
		lt3.push_back(20);
		lt3.push_back(30);
		lt3.push_back(40);

		lt1 = lt3;
		print_container(lt1);
		print_container(lt3);
	}

	void func(const list<int>& lt)
	{
		print_container(lt);
	}

	void test_list4()
	{
		// 直接构造
		list<int> lt0({ 1,2,3,4,5,6 });
		// 隐式类型转换
		list<int> lt1 = { 1,2,3,4,5,6,7,8 };
		const list<int>& lt3 = { 1,2,3,4,5,6,7,8 };

		func(lt0);
		func({ 1,2,3,4,5,6 });

		print_container(lt1);

		//auto il = { 10, 20, 30 };
	/*	initializer_list<int> il = { 10, 20, 30 };
		cout << typeid(il).name() << endl;
		cout << sizeof(il) << endl;*/
	}
}
相关推荐
Charles Ray22 分钟前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码22 分钟前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
爱上语文24 分钟前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
编程零零七3 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
2401_858286113 小时前
52.【C语言】 字符函数和字符串函数(strcat函数)
c语言·开发语言
铁松溜达py4 小时前
编译器/工具链环境:GCC vs LLVM/Clang,MSVCRT vs UCRT
开发语言·网络
everyStudy4 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript
C-SDN花园GGbond5 小时前
【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)
c语言·开发语言·数据结构·排序算法
迷迭所归处6 小时前
C++ —— 关于vector
开发语言·c++·算法
架构文摘JGWZ6 小时前
Java 23 的12 个新特性!!
java·开发语言·学习