目录
一、list的介绍及使用
1.1 list的介绍
1.2 list的使用
1.2.1 构造、析构与赋值操作符重载
1.2.2 Iterators
1.2.3 Capacity
1.2.4 Element access:
1.2.5 Modifiers
1.2.6 Operations
1.2.7 非成员函数
二、list的深度剖析及模拟实现
2.1 list的深度剖析
2.1.1 迭代器与const迭代器模板化
2.1.2 反向迭代器
2.2 list的模拟实现
2.3 list与vector的对比
1.1 list的介绍
英文文档原文
list - C++ Reference (cplusplus.com)https://legacy.cplusplus.com/reference/list/list/?kw=list中文介绍(翻译不准,请结合文档理解)
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素(详细介绍可以参考其他大佬相关blog)
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能单向迭代,更加简单高效
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更优
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,这个过程需要线性的时间开销
成员类型一览1.2 list的使用
1.2.1 构造、析构与赋值操作符重载
(1) empty container constructor (default constructor) 无参构造
Constructs an empty container, with no elements.
(2) fill constructor 填充构造
Constructs a container with n elements. Each element is a copy of val.
(3) range constructor 范围构造
Constructs a container with as many elements as the range [first,last), with each element constructed from its corresponding element in that range, in the same order.
(4) copy constructor 拷贝构造
Constructs a container with a copy of each of the elements in x, in the same order.
注意:所有测试均在 VS2022 下进行 // constructing lists #include
#include using namespace std; int main() { // constructors used in the same order as described above: list
lt1; // empty list of ints list lt2(4, 100); // four ints with value 100 list lt3(lt2.begin(), lt2.end()); // iterating through second list lt4(lt3); // a copy of third // list lt4 = lt3; int array[] = { 16,9,7,49,55,248 }; list lt5(array, array + sizeof(array) / sizeof(int)); cout << "The contents of lt5 are: "; list ::iterator it = lt5.begin(); while (it != lt5.end()) { cout << *it << ' '; it++; } cout << endl; return 0; } output: The contents of lt5 are: 16 9 7 49 55 248 1.2.2 Iterators
1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
主要学会使用这4个迭代器函数即可
1.2.3 Capacity
1.2.4 Element access:
int main() { // list front list
list1; list1.push_back(13); list1.push_back(14); // now front equals 13, and back 14 list1.front() -= list1.back(); cout << "list1.front() is now " << list1.front() << endl; // // list back list list2; list2.push_back(10); while (list2.back() != 0) { list2.push_back(list2.back() - 1); } cout << "list2 contains:"; list ::iterator it = list2.begin(); while (it != list2.end()) { cout << ' ' << *it; ++it; } cout << endl; return 0; } output: list1.front() is now -1 list2 contains: 10 9 8 7 6 5 4 3 2 1 0
1.2.5 Modifiers
int main() { list
lt1; lt1.push_back(1); lt1.push_back(2); lt1.push_back(3); lt1.push_front(4); lt1.push_front(5); lt1.push_front(6); list ::iterator it = lt1.begin(); it++; lt1.insert(it, 10); lt1.pop_back(); lt1.pop_front(); it++; lt1.erase(it); for (auto e : lt1) { cout << e << " "; } cout << endl; return 0; } output: 10 5 1 2 注意函数接口比较复杂的几个函数即可
1.2.6 Operations
int main() { list
lt1; lt1.push_back(1); lt1.push_back(3); lt1.push_back(2); lt1.push_back(5); lt1.push_back(4); list lt2; list ::iterator it2 = lt2.begin(); lt2.splice(it2, lt1); // 此时lt1已经为空了 for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.reverse(); for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.sort(); for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.remove(5); for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.push_back(4); lt2.push_back(4); lt2.push_back(4); lt2.unique(); for (auto e : lt2) { cout << e << " "; } cout << endl; list lt3; lt3.push_back(9); lt3.push_back(8); lt3.push_back(7); lt3.push_back(6); lt2.sort(), lt3.sort(); // 注意先排序再合并 lt3.merge(lt2); // 此时lt2已经为空了 for (auto e : lt3) { cout << e << " "; } cout << endl; return 0; } output: 1 3 2 5 4 4 5 2 3 1 1 2 3 4 5 1 2 3 4 1 2 3 4 1 2 3 4 6 7 8 9
1.2.7 非成员函数
2.1 list的深度剖析
2.1.1 迭代器与const迭代器模板化
template
//普通正向迭代器 //const正向迭代器 struct __list_iterator { typedef list_node Node; typedef __list_iterator itor; Node* _node; __list_iterator(Node* node) :_node(node) {} itor& operator++() { _node = _node->_next; return *this; } itor& operator--() { _node = _node->_prev; return *this; } itor operator++(int) // 前置++ { itor tmp(*this); _node = _node->_next; return tmp; } itor operator--(int) // 前置-- { itor tmp(*this); _node = _node->_prev; return tmp; } Ref operator*() // 引用 { return _node->_data; } Ptr operator->() // 指针 { return &_node->_data; } bool operator==(const itor& it) { return _node == it._node; } bool operator!=(const itor& it) { return _node != it._node; } };
2.1.2 反向迭代器
template
class ReverseListIterator { // 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的类型,而不是静态成员变量 // 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量 // 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的 public: typedef typename Iterator::Ref Ref; typedef typename Iterator::Ptr Ptr; typedef ReverseListIterator Self; public: // // 构造 ReverseListIterator(Iterator it) : _it(it) {} // // 具有指针类似行为 Ref operator*() { Iterator temp(_it); --temp; return *temp; } Ptr operator->() { return &(operator*()); } // // 迭代器支持移动 Self& operator++() { --_it; return *this; } Self operator++(int) { Self temp(*this); --_it; return temp; } Self& operator--() { ++_it; return *this; } Self operator--(int) { Self temp(*this); ++_it; return temp; } // // 迭代器支持比较 bool operator!=(const Self& l)const { return _it != l._it; } bool operator==(const Self& l)const { return _it != l._it; } Iterator _it; };
2.2 list的模拟实现
namespace mylist { template
struct list_node { T _data; list_node * _prev; list_node * _next; list_node(const T& x = T()) :_data(x) ,_prev(nullptr) ,_next(nullptr) {} }; // 迭代器 与 const迭代器 //template template struct __list_iterator { typedef list_node Node; typedef __list_iterator itor; Node* _node; __list_iterator(Node* node) :_node(node) {} itor& operator++() { _node = _node->_next; return *this; } itor& operator--() { _node = _node->_prev; return *this; } itor operator++(int) // 前置++ { itor tmp(*this); _node = _node->_next; return tmp; } itor operator--(int) // 前置-- { itor tmp(*this); _node = _node->_prev; return tmp; } Ref operator*() { return _node->_data; } Ptr operator->() { return &_node->_data; } bool operator==(const itor& it) { return _node == it._node; } bool operator!=(const itor& it) { return _node != it._node; } }; // 反向迭代器 template struct Reverse_iterator { Iterator _it; typedef Reverse_iterator Self; Reverse_iterator(Iterator it) :_it(it) {} Ref operator*() { Iterator tmp = _it; return *(--tmp); } Ptr operator->() { return &(operator*()); } Self& operator++() { --_it; return *this; } Self& operator--() { ++_it; return *this; } bool operator!=(const Self& s) { return _it != s._it; } }; template class list { typedef list_node Node; public: typedef __list_iterator iterator; typedef __list_iterator const_iterator; typedef Reverse_iterator reverse_iterator; typedef Reverse_iterator const_reverse_iterator; iterator begin() { return _head->_next; } iterator end() { return _head; } const_iterator begin() const { return const_iterator(_head->_next); } const_iterator end() const { return const_iterator(_head); } const_reverse_iterator rbegin() const { // list_node * return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } void empty_init() { _head = new Node; _head->_next = _head; _head->_prev = _head; _size = 0; } list() { empty_init(); } // list(list & lt) list(const list & lt) { empty_init(); for (auto e : lt) { push_back(e); } } /*list & operator=(const list & lt) { if (this != <) { clear(); for (auto e : lt) { push_back(e); } } return *this; }*/ void swap(list & lt) { std::swap(_head, lt._head); std::swap(_size, lt._size); } list & operator=(list lt) { swap(lt); return *this; } ~list() { clear(); delete _head; _head = nullptr; _size = 0; } void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } } 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()); } T& front() { return _head->_next->_data; } const T& front()const { return _head->_next->_data; } T& back() { return _head->_prev->_data; } const T& back()const { return _head->_prev->_data; } size_t size() const { return _size; } bool empty() const { return _size == 0; } iterator insert(iterator pos, const T& x) { Node* cur = pos._node; Node* newnode = new Node(x); Node* prev = cur->_prev; prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode; ++_size; return iterator(newnode); } iterator erase(iterator pos) { Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; delete cur; prev->_next = next; next->_prev = prev; --_size; return iterator(next); } private: Node* _head; size_t _size; }; }
2.3 list与vector的对比
vector list
底 层 结 构 动态顺序表,一段连续空间 带头结点的双向循环链表 随 机 访 问 支持随机访问,访问某个元素效率O(1) 不支持随机访问,访问某个元素
效率O(N)插 入 和 删 除 任意位置插入和删除效率低,需要搬移元素,时间复杂
度为O(N),插入时有可能需要增容,增容:开辟新空
间,拷贝元素,释放旧空间,导致效率更低任意位置插入和删除效率高,不
需要搬移元素,时间复杂度为
O(1)空 间 利 用 率 底层为连续空间,不容易造成内存碎片,空间利用率
高,缓存利用率高底层节点动态开辟,小节点容易
造成内存碎片,空间利用率低,
缓存利用率低迭 代 器 原生态指针 对原生态指针(节点指针)进行封装 迭 代 器 失 效 在插入元素时,要给所有的迭代器重新赋值,因为插入
元素有可能会导致重新扩容,致使原来迭代器失效,删
除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,
删除元素时,只会导致当前迭代
器失效,其他迭代器不受影响使 用 场 景 需要高效存储,支持随机访问,不关心插入删除效率 大量插入和删除操作,不关心随
机访问