List列表

1. List的介绍及使用

1.1 list的介绍

List的使用文档: list - C++ Reference

list可以看作是一个带头结点的双向循环链表

1.2 list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已

达到可扩展的能力。以下为list中一些常见的重要接口。

1.2.1 list的构造

上面定义了三个类:本文介绍的List类,List的组成元素结点list_node以及List的迭代器list_iterator,后续它们的用法本文详细补充

list的构造:

list类

cpp 复制代码
void empty_init()
{
	_head = new Node;
	_head->_next = _head;
	_head->prev = _head;
}

list()
{
	empty_init();   // 空表初始化
}
cpp 复制代码
// initializer_list直接{}初始化
list(initializer_list<T> il)
{
	empty_init();
	for (auto& e : v)
	{
		push_back(e);
	}
}

// 迭代器区间初始化
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
	empty_init();
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

// n个元素初始化
list(size_t n, T val = T())
{
	empty_init();
	for (size_t i = 0; i < n; ++i)
	{
		push_back(val);
	}
}

push_back函数和insert函数

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

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

	// prev newnode cur
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = cur;
	cur->prev = newnode;

	++size;
}

list拷贝构造和赋值拷贝的传统和现代写法:

list类:

cpp 复制代码
// 传统写法
// 拷贝构造
// lt2(lt1)
list(const list<T>& lt)
{
	empty_init();
	for (auto& e : lt)
	{
		push_back(e);
	}
}

// 赋值拷贝
list<T>& operator=(const list<T>& lt)
{
	if (this != &lt)
	{
		clear();
		for (auto& e : lt)
		{
			push_back(e);
		}
	}

	return *this;
}

// 现代写法
//list(list<T>& lt),拷贝构造
list(const list<T>& lt)
{
	empty_init();

	list tmp(lt.begin(), lt.end());
	swap(tmp);
}

// list<T>& operator=(list<T> tmp),赋值拷贝
list& operator=(list<T> tmp)
{
	swap(tmp);

	return *this;
}

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

1.2.2 list iterator的使用(重点)

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

迭代器的设计是一种封装,封装隐藏底层的结构差异提供类似的统一方式访问容器

cpp 复制代码
template<class T,class Ref>
struct list_iterator
{
	using Self = list_iterator<T, Ref>; // 迭代器
	using Node = list_node<T>;  // 结点
	Node* _node;  // 结点的指针
	list_iterator(Node* node)
		:_node(node)
	{}

	// *it = 1
	Ref operator*()
	{
		return _node->_data;
	}

	// ++it
	Self& operator++()
	{
		_node = _node->_next;
		return *this;
	}

	//it++
	Self operator++(int)  //为了与后置++做区分
	{
		Self tmp(*this);  // 1. 先保存当前状态的副本
		_node = _node->next;  //2. 再移动指针
		return tmp;   //3. 返回副本(注意:不是引用!)
	}

	// --it
	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}

	//it--
	Self operator--(int)  //为了与后置++做区分
	{
		Self tmp(*this);  // 1. 先保存当前状态的副本
		_node = _node->prev;  //2. 再移动指针
		return tmp;   //3. 返回副本(注意:不是引用!)
	}

	bool operator!=(const Self& s) const
	{
		return _node != s._node;
	}

	bool operator==(const Self& s) const
	{
		return _node == s._node;
	}


};

2. List的模拟实现

2.1 list类

结点:

cpp 复制代码
template<class T>
struct list_node
{
	list_node<T>* _next;
	list_node<T>* _prev;
	T _data;

	list_node(const T& x = T()) // 匿名对象初始化
		:_next(nullptr)
		,_prev(nullptr)
		,_data(x)
	{}
};

list类:

cpp 复制代码
template<class T>
class list
{
	using Node = list_node<T>;
public:
	using iterator = list_iterator<T, T&>;
	using const_iterator = list_iterator<T, const T&>;

	// 之前的写法
/*	using iterator = list_iterator<T, T&>;
	using const_iterator = list_iterator<T, const T&>;*/

	iterator begin()
	{
		return iterator(_head->_next);
	}

	iterator end()
	{
		return iterator(_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;
	}

	list()
	{
		empty_init();   // 空表初始化
	}

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

	// 迭代器区间初始化
	template<class InputIterator>
	list(InputIterator first, InputIterator last)
	{
		empty_init();
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

	// n个元素初始化
	list(size_t n, T val = T())
	{
		empty_init();
		for (size_t i = 0; i < n; ++i)
		{
			push_back(val);
		}
	}

	list(int n, T val = T())
	{
		empty_init();
		for (size_t i = 0; i < n; ++i)
		{
			push_back(val);
		}
	}

	~list()
	{
		clear();

		delete _head; 
		_head = nullptr;  // 为了防止访问野指针
		_size = 0;
	}

	// 传统写法
	// 拷贝构造
	// lt2(lt1)
	list(const list<T>& lt)
	{
		empty_init();
		for (auto& e : lt)
		{
			push_back(e);
		}
	}

	// 赋值拷贝
	list<T>& operator=(const list<T>& lt)
	{
		if (this != &lt)
		{
			clear();
			for (auto& e : lt)
			{
				push_back(e);
			}
		}

		return *this;
	}

	//// 现代写法
	////list(list<T>& lt),拷贝构造
	//list(const list<T>& lt)
	//{
	//	empty_init();

	//	list tmp(lt.begin(), lt.end());
	//	swap(tmp);
	//}

	//// list<T>& operator=(list<T> tmp),赋值拷贝
	//list& operator=(list<T> tmp)
	//{
	//	swap(tmp);

	//	return *this;
	//}
	
	void swap(list<T>& lt)
	{
		std::swap(_head, lt._head);
		std::swap(_size, lt._size);
	}

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

	//void push_back(const T& x)
	//{
	//	Node* newnode = new Node(x);
	//	Node* tail = _head->_prev;
	//	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 erase(iterator pos)
	{
		Node* cur = pos._node;
		Node* prev = cur->_prev;
		Node* next = cur->_next;

		prev->_next = next;
		next->_prev = prev;
		delete cur;
		--_size;

		//return iterator(next); 
		return next;  //结点的指针可以隐式类型转换成迭代器
	}

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

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

		++_size;
	}

	size_t size() const
	{
		/*size_t n = 0;
		for (auto& e : *this)
		{
			++n;
		}
		return n;*/
		return _size;
	}

private:
	Node* _head;
	size_t _size = 0;
};

3. List与vector的对比

两者的迭代器类型不同,

list:

vector:

只支持单向访问的迭代器:

迭代器从功能的角度可以分为:

(1)单向:++

(2)双向:++/--

(3)随机:++/--/+/-

因为sort排序函数只支持随机迭代器,所以list无法用sort函数,但是sort内部自己实现了sort排序函数,但是效率较于vector低很多

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);   // 输出 vector 排序耗时
	printf("list sort : %d\n", end2 - begin2);   // 输出 list 排序耗时

}

vector更适合排序

示例代码:

cpp 复制代码
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);
}
相关推荐
无双@2 小时前
高并发内存池9 —— Page Cache 回收
c++·线程·秋招·项目·高并发内存池·c++项目·内存池
卡提西亚2 小时前
C++笔记-26-类模板
c++·笔记
沐怡旸2 小时前
【穿越Effective C++】条款19:设计class犹如设计type——用户定义类型的艺术与科学
c++·面试
一个不知名程序员www2 小时前
算法学习入门---模拟(C++)
c++·算法
夜月yeyue2 小时前
嵌入式开发中的 Git CI/CD
c++·git·单片机·嵌入式硬件·ci/cd·硬件架构
编程之路,妙趣横生3 小时前
类和对象(下)
c++
Q741_1474 小时前
C++ 面试高频考点 链表 迭代 递归 力扣 25. K 个一组翻转链表 每日一题 题解
c++·算法·链表·面试·递归·迭代
syker4 小时前
手搓UEFI.h
c++
LIZhang20164 小时前
基于ffmpeg8.0录制mp4文件
开发语言·c++