一、list的介绍和使用
1.1 list的介绍
- list 是一个允许在常数时间( O(1) )内进行任意位置插入/删除操作的顺序容器,并且该容器可以前后双向迭代。
- list 的实现方法类似于双向链表:双向链表在不同的且没有关联的存储空间存储每一个元素,但是每个元素保持链接着它的前一个和后一个元素。
- list 的实现方法也类似于双向链表:他们最大的不同是单链表是单向的链表,每一个元素只链接着其后一个元素。单链表只能朝前迭代,所以它规模更小也更高效。
- 相较于其它基础的标准顺序容器(array, vector and deque),list 在任意位置的插入/删除/移动元素效率更高
- list 相较于其他顺序容器而言,最主要的缺点就是它不支持任意位置的随机访问。例如去访问list的第六个元素,就必须从一个已知的位置(例如开始或结尾)迭代到这个位置。
不仅查找元素需要线性的时间,list 也需要消耗一些额外的空间去保证每个元素与它相邻元素的链接。
1.2 list的使用
由于C++引入了STL(标准模板库)的概念,list、vector这些模板类提供了类似的接口,通过之前对vector的学习,相信你也会很快地学会list的使用和其背后的原理。
1.2.1 构造函数、析构函数和赋值重载
std::list::list
- list() 无参构造,构造空的list。
- list (size_type n, const value_type& val = value_type()) 用n个val填充构造的list
- list (const list& x) 拷贝构造
- list (InputIterator first, InputIterator last)
用两个迭代器(first, last)区间之间的值填充一个构造的list。(区间左闭右开)
std::list::~list
- ~list(); 销毁容器对象
std::list::operator=
- list& operator= (const list& x);
分配一个新容器(新容器对x拷贝构造),替换当前的容器,相应地修改容器的大小
1.2.2 Iterators:
- begin 返回指向list第一个元素的迭代器。
iterator begin(); const_iterator begin() const;- end 返回指向list最后一个元素下一个位置(即end位置)的迭代器。
iterator end(); const_iterator end() const;- rbegin 返回指向list最后一个元素的迭代器。
reverse_iterator rbegin(); const_reverse_iterator rbegin() const;- rend 返回指向list第一个元素前一个位置(即end位置)的迭代器。
reverse_iterator rend(); const_reverse_iterator rend() const;
注: end() 和 rend() 指向的是同一个位置,因为list是有头节点的双向环型链表。
1.2.3 Capacity:
- size 返回容器元素个数
size_type size() const;- empty 判断容器是否为空
bool empty() const;
1.2.4 Element access:
- front 访问第一个元素
reference front(); const_reference front() const;- back 访问最后一个元素
reference back(); const_reference back() const;
1.2.5 Modifiers:
- push_front 在list首元素前插入值为val的元素
void push_front (const value_type& val);- pop_front 删除list中第一个元素
void pop_front();- push_back 在list尾部插入值为val的元素
void push_back (const value_type& val);- pop_back 删除list中最后一个元素
void pop_back();- insert 在list position 位置中插入值为val的元素
iterator insert (iterator position, const value_type& val);- erase 删除list position位置的元素
iterator erase (iterator position); iterator erase (iterator first, iterator last);- swap 交换两个list中的元素
void swap (list& x);- clear 清空list中的有效元素
void clear();
1.2.6 Operations:
- unique 移除重复的元素
void unique();- sort 对容器内元素排序
void sort();
注:
- unique() 移除list中每一组相等的连续元素,只留下各组的第一个元素,而不是移除所有相等元素只留下一个。
例如122333444221,unique()之后为123421。 - sort() 排序效率不是很高。
二、list与vector的对比
2.1 迭代器的种类
iterator | definition |
list | a random access iterator to value_type |
vector | a bidirectional iterator to value_type |
- 迭代器从功能上分类,可以分成正向迭代器和反向迭代器,又能根据const再次分成四种 :iterator、const_iterator、reverse_iterator、const_reverse_iterator。
- 迭代器从性质上分类,可以分成单向迭代器、双向迭代器和随机迭代器。
- 单向迭代器(forward iterator)支持++操作,例如forward_list;
双向迭代器(bidirectional iterator)支持++、--操作,例如list;
随机迭代器(random access iterator)支持+、-、++、--操作,例如vector;
2.2 list与vector的迭代器失效
vector使用随机迭代器,其底层结构为动态顺序表,有一段连续的存储空间
迭代器失效原因:
- 会引起其底层空间改变的操作,都有可能使迭代器失效(例如扩容)。
- 指定位置元素的插入或删除操作。
list使用的是双向迭代器,其底层结构为带头结点双向循环链表。
因此list中不存在扩容,在进行插入时不会导致list的迭代器失效,只有在删除时迭代器才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
void Test1()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> ll(arr, arr + sizeof(arr) / sizeof(arr[0]));
list<int>::iterator it = ll.begin();
while (it != ll.end())
{
if (*it % 2 == 0)
{
it = ll.erase(it);
}
else
{
it++;
}
}
for (auto x : ll)
{
cout << x << " ";
}
cout << endl;
}
注:
erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值。