1. 标准库中的list类
list 类 的介绍:
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代
2. list与forward_list非常相似:最主要的不同在于forward_list是单链表
3. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好,只是不支持任意位置的随机访问
a. list 的构造函数
- list() (无参构造函数)
- list (const list& x) (拷贝构造)
- list (InputIterator first, InputIterator last)
( 用[first, last)区间中的元素构造list )
- list (size_type n, const value_type& val = value_type())
( 构造的list中包含n个值为val的元素 )
注意:
list 的迭代器是双向迭代器(完成 ++ , --),可以支持传单向迭代器( 完成 ++ ) 和双向迭代器
b. list 增删查改
- push_back (尾插)
- push_front (头插)
- pop_back (尾删)
- pop_front (头删)
- insert (在某一位置前增加新节点)
代码举例1
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(0); lt.push_back(10); lt.push_back(20); lt.push_back(30); list<int> ::iterator it = lt.begin(); ++it; lt.insert(it,70); it = lt.begin(); while(it != lt.end()) { cout << *it << endl; ++it; } }
运行结果:
- earse (删除某一位置的节点)
代码举例2
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(0); lt.push_back(10); lt.push_back(20); lt.push_back(30); list<int> ::iterator it = lt.begin(); ++it; lt.erase(it); it = lt.begin(); while(it != lt.end()) { cout << *it << endl; ++it; } }
运行结果:
- swap ( 交换两个list中的元素 )
代码举例3
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(10); lt.push_back(20); lt.push_back(30); list<int> llt; llt.push_back(4); llt.push_back(5); llt.push_back(6); lt.swap(llt); auto it = lt.begin(); while(it != lt.end()) { cout << *it << " "; ++it; } cout << endl; for (auto i : llt) { cout << i << " "; } }
运行结果:
- clear (清除有效节点,即不包括哨兵位)
c. list 容量
d. list 获取元素
e. list 迭代器
- begin + end ( 返回第一个元素的迭代器+ 返回最后一个元素下一个位置的迭代器 )
画图分析
- rbegin + rend ( 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 )
注意:
反向迭代器的模拟实现和我们理解的有偏差,图上为了理解,我们可以认为rbegin是最后一个元素,rend是第一个元素的前一个位置
但是实际上,rbegin指向的位置就是end的位置,rend指向的位置就是rbegin的位置,但是在解引用时,会运算符重载 *,得到该位置的上一个位置 (详情看 list 模拟实现)
2. 迭代器失效
list 迭代器类似一个指针,指向节点的地址 (具体详情看 list 的模拟实现)
所以在发生 erase 的时候容易造成迭代器失效(即野指针)
3. list 类的模拟实现
代码
namespace lhy { template<class T> struct ListNode { public: ListNode* prev; ListNode* next; T val; ListNode(const T& t = T()) { prev = next = nullptr; val = t; } }; template<class T,class Ref,class Ptr> class list_iterator { public: typedef list_iterator<T,Ref,Ptr> self; list_iterator(ListNode<T>* n) :_node(n) {} Ptr operator->() { return &_node->val; } Ref operator*() { return _node->val; } self& operator++() { _node = _node->next; return *this; } self operator++(int) { self tmp = *this; _node = _node->next; return tmp; } self& operator--() { _node = _node->prev; return *this; } self operator--(int) { self tmp = *this; _node = _node->prev; return tmp; } bool operator!=(const list_iterator& t) { return _node != t._node; } bool operator==(const list_iterator& t) { return _node == t._node; } ListNode<T>* _node; }; template<class iterator,class Ref,class Ptr> class list_converse_iterator { private: iterator com; public: typedef list_converse_iterator self; list_converse_iterator(iterator& it) :com(it) {} Ptr operator->() { return &(*com); } Ref operator*() { iterator tmp = com; --tmp; return *tmp; } self& operator++() { --com; return *this; } self operator++(int) { self tmp = *this; --*this; return tmp; } self& operator--() { ++com; return *this; } self operator--(int) { self tmp = *this; ++*this; return tmp; } bool operator!=(const self& t) { return com != t.com; } bool operator==(const self& t) { return com == t.com; } }; template<class T> class List { public: typedef list_iterator<T, T&, T*> iterator; typedef list_iterator<T, const T&, const T*> const_iterator; typedef list_converse_iterator<iterator, T&, T*> converse_iterator; typedef list_converse_iterator<iterator, const T&,const T*> const_converse_iterator; List() { node = new ListNode<T>; node->next = node; node->prev = node; } iterator begin() { return iterator(node->next); } const const_iterator begin() const { return const_iterator(node->next); } iterator end() { return iterator(node); } const const_iterator end() const { return const_iterator(node); } converse_iterator rbegin() { return converse_iterator(end()); } const_converse_iterator rbegin() const { return const_converse_iterator(end()); } converse_iterator rend() { return converse_iterator(begin()); } const_converse_iterator rend() { return const_converse_iterator(begin()); } void push_back(const T& val) { ListNode<T>* ptail = node->prev; ListNode<T>* newnode = new ListNode<T>(val); ptail->next = newnode; newnode->next = node; newnode->prev = ptail; node->prev = newnode; } void push_front(const T& x) { insert(begin(), x); } void insert(iterator pos,const T &x) { ListNode<T>* cur = pos._node; ListNode<T>* newnode = new ListNode<T>(x); newnode->next = cur; newnode->prev = cur->prev; cur->prev->next = newnode; cur->prev = newnode; } void earse(iterator pos) { ListNode<T>* cur = pos._node; assert(cur != node); ListNode<T>* _prev = cur->prev; ListNode<T>* _next = cur->next; _prev->next = _next; _next->prev = _prev; delete cur; } private: ListNode<T>* node; }; }
list 迭代器的实现
单看这一个类的实现,可能会疑惑,已经有一个 List 类了,为什么还要加一个 list_iterator 类,并且很容易发现,两个类的成员变量是一样的
如:list<int> :: iterator it;
我们希望 *it 得到的是T类型的变量(这里是int 类型)
而 it++ 得到的是下一个节点的地址
如果是只有 List 类,无法实现
因为如果 typedef ListNode* iterator
那么 *it 的类型就是 ListNode;
it++ 也不是下一个结点的地址(这是链表,开辟的空间不是连续的)
所以这里的 list_iterator 类是为了运算符重载 *和++
代码注意事项
可以和下面的对应
注意:
const iterator 修饰的是 (ListNode<T>*),即指针不可以更改,但是指针所指向的内容可以更改
const_iterator 修饰的是 const ListNode<T>* ,即指针所指向的内容不可更改
注意:
对于这个运算符重载,实际写的时候只要写一个 -> 就行,编译器简化两个 ->
模板第一个传的是 正向迭代器,利用正向迭代器来实现反向迭代器的功能
这里 运算符重载* 让正向迭代器--再解引用,是为了得到 原先T 类型的数据的前一个数据,原因如下:
这里传递是 end() ,但是 对应的数据不是我们想要的
才会在解引用得到前一个数据的值