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的使用  

函数声明

接口说明

返回第一个元素的迭代器 + 返回最后一个元素下一个位置的迭代器
返回第一个元素的 reverse_iterator, end 位置 返回最后一个元素下一个位 置的 reverse_iterator, begin 位置

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

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的迭代器的实现就不再是原生指针了:(不支持+/-)

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

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

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

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:

函数声明

接口说明

检测 list 是否为空,是返回 true ,否则返回 false
返回 list 中有效节点的个数

4.list element access:

函数声明

接口说明

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

5.list modifiers:

函数声明

接口说明

list 首元素前插入值为 val 的元素
删除 list 中第一个元素
list 尾部插入值为 val 的元素
删除 list 中最后一个元素
list position 位置中插入值为 val 的元素
删除 list position 位置的元素
交换两个 list 中的元素
清空 list 中的有效元素

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

	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这个模板):

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没有优化,可能起不到公平的对比:

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);
}
int main()
{
	test_op1();
	test_op2();
	return 0;
}

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

lt.reverse();
reverse(lt.begin(), lt.end());

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

// 一个链表节点转移给另一个链表
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 的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭 代器,其他迭代器不会受到影响:
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的底层的简单实现:

实现代码+测试代码:

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

  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STL(Standard Template Library)是C++的一个标准库,其包含了许多常用的数据结构和算法。其list是一种常用的数据结构,它是一个双向链表,可以实现快速的插入和删除操作。 下面是C++STL list简单实现: ```cpp template <typename T> class List { private: struct Node { T data; Node* prev; Node* next; Node(const T& d = T(), Node* p = nullptr, Node* n = nullptr) : data(d), prev(p), next(n) {} }; Node* head; Node* tail; int size; public: List() : head(nullptr), tail(nullptr), size(0) {} List(const List& other) : head(nullptr), tail(nullptr), size(0) { for (const auto& item : other) { push_back(item); } } ~List() { clear(); } List& operator=(const List& other) { if (this != &other) { List tmp(other); swap(tmp); } return *this; } void push_back(const T& item) { Node* new_node = new Node(item, tail, nullptr); if (tail) { tail->next = new_node; } else { head = new_node; } tail = new_node; ++size; } void push_front(const T& item) { Node* new_node = new Node(item, nullptr, head); if (head) { head->prev = new_node; } else { tail = new_node; } head = new_node; ++size; } void pop_back() { if (tail) { Node* tmp = tail; tail = tail->prev; if (tail) { tail->next = nullptr; } else { head = nullptr; } delete tmp; --size; } } void pop_front() { if (head) { Node* tmp = head; head = head->next; if (head) { head->prev = nullptr; } else { tail = nullptr; } delete tmp; --size; } } T& front() { return head->data; } T& back() { return tail->data; } bool empty() const { return size == 0; } int size() const { return size; } void clear() { while (!empty()) { pop_back(); } } class iterator { private: Node* ptr; public: iterator(Node* p = nullptr) : ptr(p) {} iterator(const iterator& other) : ptr(other.ptr) {} iterator& operator++() { ptr = ptr->next; return *this; } iterator operator++(int) { iterator tmp(*this); ptr = ptr->next; return tmp; } iterator& operator--() { ptr = ptr->prev; return *this; } iterator operator--(int) { iterator tmp(*this); ptr = ptr->prev; return tmp; } T& operator*() const { return ptr->data; } T* operator->() const { return &(ptr->data); } bool operator==(const iterator& other) const { return ptr == other.ptr; } bool operator!=(const iterator& other) const { return ptr != other.ptr; } }; iterator begin() const { return iterator(head); } iterator end() const { return iterator(nullptr); } iterator rbegin() const { return iterator(tail); } iterator rend() const { return iterator(nullptr); } void swap(List& other) { std::swap(head, other.head); std::swap(tail, other.tail); std::swap(size, other.size); } }; ``` 上述代码List定义了一个私有结构体Node,它表示双向链表的每个节点。Node包含了数据成员data、指向前一个节点的指针prev和指向后一个节点的指针next。List还包含了头指针head、尾指针tail和大小size等数据成员。 List类提供了许多操作,包括push_back、push_front、pop_back、pop_front、front、back、empty、size、clear等。其,push_back和push_front分别表示在双向链表的末尾和开头插入一个元素,pop_back和pop_front分别表示删除双向链表的末尾和开头的元素,front和back分别表示获取双向链表的第一个和最后一个元素,empty表示判断双向链表是否为空,size表示获取双向链表元素的个数,clear表示清空双向链表的所有元素。 List类还定义了一个迭代器iterator,它可以用于遍历双向链表的所有元素。迭代器包含了一个指向Node的指针ptr,它可以指向双向链表的任意一个节点。迭代器提供了许多操作,包括++、--、*、->、==、!=等。其,++和--分别表示迭代器向前和向后移动一个位置,*表示获取迭代器所指向节点的数据成员data,->表示获取迭代器所指向节点的数据成员data的指针,==和!=分别表示判断两个迭代器是否相等和不相等。 List还提供了一些其他操作,比如拷贝构造函数、析构函数、赋值运算符、begin、end、rbegin、rend和swap等。其,拷贝构造函数用于复制另一个List对象,析构函数用于释放所有节点的内存,赋值运算符用于将一个List对象赋值给另一个List对象,begin和end分别返回指向双向链表第一个元素和最后一个元素的迭代器,rbegin和rend分别返回指向双向链表最后一个元素和第一个元素的迭代器,swap用于交换两个List对象的所有数据成员。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值