目录
前言
前面在数据结构中我们已经了解到,单链表,带头双向循环链表,那么好!C++中的容器也存在这一些链表,下面来了解了解list,以及它常用的接口,完整文档可看list文档!(PC端打开哦!)使用时一定要记得包对应容器的头文件哦!
一、list的介绍
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是带头双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素 。
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
5.与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问!要访问某一位置,需要从已知位置开始遍历到该位置,时间上存在一笔大的开销!
二、list的使用
注意:list 中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list 中一些 常见的重要接口
Ⅰ.默认成员函数
1、构造函数
和前面的vector类似,常见的就四个:
cpplist(); //无参构造 list(size_type n, const value_type& val = value_type()); //构造并初始化n个val list (const list& x);//拷贝构造 list (InputIterator first, InputIterator last);//迭代器区间构造
具体使用:
cpplist<int> l1;//无参 list<int> l2(10, 1);//初始化元素为10个1 list<int> l3(l2);//将l2的内容拷贝给l3,并初始化l3 list<int> l4(l2.begin(), l2.begin() + 5);//迭代器区间构造
2、赋值重载
cpplist& operator= (const list& x); //使用 list<int> l2(10, 1);//初始化元素为10个1 list<int> l5=l2;//赋值
3、析构函数
cpp~list();
这个也是使用自带的即可,实践在底层实现时还是要自己写,因为在底层涉及到空间的申请,需要手动释放!现在使用的容器是直接封装好的,给用户带来我们带来良好的体验!
Ⅱ、容量
1.size()
cpp//原型,获取数据个数 size_type size() const; //使用 list<int> l2(10,1); cout << l2.size();
2.empty()
cppl2.empty();//判空
你没看错,这里的list常用就这两个,库里面还有一个叫max_size(),但用得不多!
Ⅲ、迭代器与遍历
1.begin+end (正向迭代器)
begin():返回第一个元素的迭代器
iterator begin();
const_iterator begin() const;//为const对象提供的
end(): 返回****最后一个元素下一个位置的迭代器
iterator end();
const_iterator end() const;
看代码:
值得注意的是:list的begin和end,不能像vector那样直接begin()+5,而是只能++,实际上是因为底层结构的原因,vector底层是个连续的空间,而list不是!
2.rbegin+rend (反向迭代器)
rbegin(): 返回****第一个元素的reverse_iterator(反向迭代器),即end位置
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;//为const对象提供的
rend(): 返回****最后一个元素下一个位置的 reverse_iterator,即begin位置
reverse_iterator rend();
const_reverse_iterator rend() const;
看代码:
还是要注意反向迭代器是反向++,向前走!
3.front
front: 返回list的第一个节点中值的引用
reference front();
const_reference front() const;
4.back
back: 返回list的最后一个节点中值的引用
reference back);
const_reference back() const;
Ⅳ、增删查改
1.push_front
头插操作。
void push_front(const value_type & val);
使用范围for遍历是因为有迭代器的存在,这一点不过多赘婿!
2.pop_front
头删操作。
void pop_front();
3.push_back
尾插操作。
void push_back(const value_type & val);
**4.**pop_back
尾删操作。
void pop_back();
5.insert
插入操作:在pos位置前插入一个值,同样需要结合算法库里的find函数去使用!
①特定位置前插入一个值
②特定位置前插入n个val
③在特定位置前插入一段迭代区间(左闭右开)
6.erase
删除操作:删除特定位置或者区间的值!并且返回一个迭代器,这个迭代器指向被删除元素的后一个元素的位置
iterator erase(iterator position);
iterator erase(iterator first, iterator last);
来吧,展示!同样也需要配合find()使用
①删除特定位置的值
②删除特定区间的值(左闭右开)
一定要注意这里是传进去两个迭代器,并且是左闭右开区间,这里写法上和vector一定要注意区分,原因还是因为底层结构的不同!!!
7.swap
主要是交换两个list中的元素 !这个是list内部的成员函数,不是那个算法库里面的全局的swap
void swap(list & x);
8.clear()
clear:清空 list 中的有效元素
void clear();
三、细节问题-迭代器失效
这个问题也在vector中涉及,但是对于list而言,它的insert不会存在迭代器失效的问题,因为他并没有引起底层空间的变化!那么它是在哪里会失效呢?就是在删除操作上list迭代器会失效。
原因:
因为list的底层结构为带头结点的双向循环链表 ,因此在list中进行插入时是不会导致list的迭代 器失效的,只有在删除时才会失效,并且 失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
看代码:
解决方案还是一样:如果还是要使用就更新;
这里能这样写是因为erase它是由返回值的, 返回一个迭代器,这个迭代器指向被删除元素的后一个元素的位置
今天的分享就到这里!感谢你的观看与指导!