片头
嗨!小伙伴们,大家好!今天,我们一起来学习list类,准备好了吗?咱们开始咯~
一、list类的介绍和使用
1.1 list类的介绍
- (1)list类是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代
- (2)list类的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素
- (3)list类和forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效
- (4)与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好
- (5)与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保护每个节点的相关联信息(对于存储类型较小的元素的大list来说这可能是一个重要的因素)
list是一个模板类,在使用的时候我们需要给出元素的类型
使用list类时,需要包含头文件<list>
1.2 list类的使用
list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后原理,已达到可扩展的能力。以下为list中一些常见的重要接口。
1.2.1 list的构造
构造函数(constructor) | 接口说明 |
list(); | 构造空的list |
list(size_type n,const value_type& val = value_type()) | 构造的list中包含n个值为val的元素 |
list(const list& x) | 拷贝构造函数 |
list(InputIterator first,InputIterator last); | 用[fist,last)区间中的元素构造list |
(1)list();
list类的默认构造函数,构造一个空链表
例如:
(2)list(size_type n,const value_type& val = value_type());
构造一个list对象并用n个val去初始化
例如:
(3)list(const list& x);
list类的拷贝构造函数
(4)Template<class InputIterator>
list(InputIterator first,InputIterator last);
用[first,last)区间中的元素构造list
1.2.2 容器操作接口
函数声明 | 接口说明 |
size_type size() const; | 获取链表节点个数 |
bool empty() const; | 判断链表是否为空 |
void resize(size_type n,value_type val = value_type()); | 增加或减少节点个数为n |
(1)size_type size() const;
获取链表节点个数
例如:
(2)bool empty() const;
判断链表是否为空
例如:
(3)void resize(size_type n,value_type val = value_type());
增加或者减少节点个数为n
如果没有给出val,就用0作为元素
例如:
1.2.3 访问和遍历
函数声明 | 接口说明 |
begin + end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
rbegin+rend | 返回第一个元素的reverse_iterator,即end位置;返回最后一个元素下一个位置的reverse_iterator,即begin位置 |
front | 返回list的第一个节点中值的引用 |
back | 返回list的最后一个节点中值的引用 |
(1)迭代器
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
迭代器(正向),用于获取链表中第一个节点的位置和最后一个节点的下一个位置(即哨兵位)
例如:
9
(2)反向迭代器
reverse_iterator begin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
反向迭代器,rbegin获取容器中最后一个节点的位置,rend获取容器中哨兵位的位置
例如:
注意:
①begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
②rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
(3)front
reference front();
const_reference front() const;
返回链表中第一个节点存储的数据的引用
例如:
(4)back
reference back();
const_reference back const;
返回链表中最后一个节点存储的数据的引用
例如:
1.2.4 list的增删查改
函数声明 | 接口说明 |
push_front | 在list首元素前插入值为val的元素 |
pop_front | 删除list中第一个元素 |
push_back | 在list尾部插入值为val的元素 |
pop_back | 删除list中最后一个元素 |
find | 在2个迭代器区间寻找val并返回其所在处的迭代器 |
insert | 在pos位置插入1个或多个元素 |
erase | 删除pos位置的元素或者 [first,las) 区间的所有元素 |
swap | 交换2个list对象 |
assign | 为list指定新内容,替换其当前内容并修改节点个数 |
clear | 清空list中的有效元素 |
(1)push_front
void push_front(const value_type& val);
在list首元素前插入值为val的元素
例如:
(2)pop_front
void pop_front();
删除list中的第一个元素
例如:
(3)push_back
void push_back(const value_type& val);
从list尾部插入值为val的元素
例如:
(4)pop_back
void pop_back();
删除list中最后一个元素
例如:
(5)find
template<class InputIterator,class T>
InputIterator find(InputIterator first,InputIterator last,const T& val);
在2个迭代器区间寻找val并返回其所在处的迭代器
例如:
注意:
①该函数并非list的成员函数,是标准库中的函数,多个容器共用该find函数
可以看到,我们可以直接对list的迭代器进行解引用并修改其内容,但是迭代器不应该指向节点吗?为什么对其解引用能修改数据呢?
实际上,list的迭代器并不是用原生态指针进行模拟实现的,需要进行底层的封装,这里会在list的模拟实现的源代码中体现
(6)insert
Iterator insert(iterator position,const value_type& val);
void insert(iterator position,size_type n,const value_type& val);
——————————————————————————————————————
template<class InputIterator>
void insert(iterator position,InputIterator first,InputIterator last);
在position位置插入1个或者多个元素
例如:
哈哈,小伙伴可能已经发现了:
list的insert操作是不会导致迭代器失效的,因为pos指向的节点不变,相对位置也不变
但是list的删除操作一定会导致迭代器失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响
(7)erase
iterator erase(iterator position);
iterator erase(iterator first,iterator last);
删除position位置的元素或者 [first,last) 区间的所有元素
例如:
(8) swap
void swap(list& x);
交换2个list对象
例如:
(9)assign
tepmplate<class InputIterator>
void assign(InputIterator first,InputIterator last);
void assign(size_type n,const value_type& val);
为list指定新内容,替换其当前内容并修改节点个数
例如:
(10)clear
void clear();
清空list中的有效元素
例如:
1.2.5 list的顺序修改接口
(1)sort
void sort();
template<class Compare>
void sort(Compare comp);
list由于结构是双向链表,是无法使用标准库中的sort函数的,因为迭代器无法进行相减操作
并且在C++文档中,我们可以看到标准库中的sort也限定了迭代器的类型必须是可以进行随机访问(RandomAccess)的
迭代器有几个功能分类:
- 单向迭代器,只能进行++
- 双向迭代器,可以++和--
- 随机迭代器,可以++,--,+和-
但是list的sort函数也是没什么必要的,因为直接对链表排序的效率比拷贝到vector进行排序再拷贝回链表要慢很多
void test22() {
list<int> l1 = { 4,1,10,3,6,2 };
list<int>::iterator it1 = l1.begin();
while (it1 != l1.end()) {
cout << *it1 << " ";
it1++;
}
cout << endl;
//默认排升序
l1.sort();
for (auto e : l1) {
cout << e << " ";
}
cout << endl;
//排降序,传递仿函数(匿名对象)
l1.sort(greater<int>());
for (auto e : l1) {
cout << e << " ";
}
cout << endl;
}
(2) unique
void unique();
去掉list容器中重复的元素
例如:
注意:使用unique函数之前,必须先进行排序(升序或降序),不然运行不正确。
(3)reverse
void reverse();
对链表进行逆置
例如:
片尾
今天,我们学习了list类的相关接口,在下一篇中,我们将学习如何模拟实现list类,希望看完这篇文章对友友们有所帮助!!!
求点赞收藏加关注!!!
谢谢大家!!!