list的基本使用
本质为双向带头循环列表
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向带头循环链表,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
1 | list<int> lst; |
list迭代器在插入元素之后==不会失效==
删除元素会导致迭代器失效,调用删除接口之后,需要重新更新迭代器
- 获取erase返回值,其指向下一个元素的位置
- 调用迭代器接口
remove
:删除指定值,如果有多个,全部删除,如果没有,不做操作
splice
:拼接,拼接时,被拼接的元素直接存入第一个list,第二个list中不再保留被拼接的元素
unique
:去重,使用之前需要lst元素有序,先调sort,再unique,如果自定义类型需要排序,则自定义类型需要支持比较运算
list迭代器
list迭代器不是原生态指针实现的,一般通过封装节点,定义一个自定义类型,通过运算符重载函数完成迭代器规定的操作,例如:
*iterator
:operator*( )
—-> _node->_val++iterator
:operator++( )
—-> _node = _node->_nextiterator->
:operator->( )
—-> &_node->_val!=iterator
:operator!=( )
—-> _node != iterator2._node;
即list迭代器不是一个指针,而是一个对象,以下为一个模拟实现
1 | template<class T, class Ref, class Ptr>//迭代器 |
list与vector的差异
- list底层结构为带头双向循环链表,vector则为顺序表
- 由第一条,vector支持随机访问,访问某个元素效率为O(1),而list则不支持,访问某个元素效率为O(n)
- 在插入或删除时,vector的效率为O(n),并且可能涉及搬移元素、增容的问题,而list插入删除不需要搬移元素或增容等,效率为O(1)
- 由于vector低层为顺序表,所以内存上是连续空间,不容易造成内存碎片,缓存利用率低,list恰恰相反
- vector的迭代器是原生态指针实现,而list则对原生态指针进行了封装
- 对于vector来说,插入元素时,要给所有迭代器重新赋值,因为插入元素可能导致增容,使原来的迭代器失效,删除时,当前迭代器需要重新赋值,否则会失效;对于list来说,插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
- 使用场景:vector适合需要高效存储,随机访问,不关心插入删除效率;list适合需要大量插入删除操作,不进行随机访问的场景