目录
5、增删查改(insert、erase、pop_back、pop_front)
| 构造函数 | 接口说明 |
| list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的元素 |
| list() | 构造空的list |
| list (const list& x) | 拷贝构造函数 |
| list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
- int main()
- {
- // 默认构造
- list<int> lt;
- lt.push_back(1);
- lt.push_back(2);
- lt.push_back(3);
- lt.push_back(4);
- // 拷贝构造
- list<int> lt2(lt);
- // 构造 n 个节点
- list<int> lt3(5, 1);
- // 迭代器区间构造
- list<int> lt4(lt.begin(), lt.end());
-
- return 0;
- }
| 函数声明 | 接口说明 |
| begin + end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
| rbegin + rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 |
- int main()
- {
- int a[] = { 1,2,3,4,5,6,7,8,9 };
- list<int> lt(a, a + 9);
- auto it = lt.begin();
- while (it != lt.end())
- {
- cout << *it << " ";
- it++;
- }
- cout << endl;
- return 0;
- }
迭代器一般是用来遍历和查找的;
而反向迭代器的使用是类似的,只不过调用的函数换成了 rbegin 和 rend 。
注意:反向迭代器的迭代使用的也是++。但迭代器区间一样是[rbegin, rend);
| 函数声明 | 接口说明 |
| push_front | 在list首元素前插入值为 val 的元素 |
| pop_front | 删除 list 中第一个元素 |
| push_back | 在list尾部插入值为 val 的元素 |
| pop_back | 删除 list 中最后一个元素 |
| insert | 在list position 位置中插入值为 val 的元素 |
| erase | 删除list position 位置的元素 |
| swap | 交换两个 list 中的元素 |
| clear | 清空 list 中的有效元素 |
- int main()
- {
- vector<int> v = { 1,2,3,4,5,6,7,8,9 };
- list<int> lt(v.begin(), v.end());
- for (auto e : lt) cout << e << " ";
- cout << endl;
-
- lt.push_front(10);
- lt.push_back(20);
- for (auto e : lt) cout << e << " ";
- cout << endl;
-
- lt.pop_front();
- lt.pop_back();
- for (auto e : lt) cout << e << " ";
- cout << endl;
-
- auto pos = find(lt.begin(), lt.end(), 5);
- lt.insert(pos, 50);
- for (auto e : lt) cout << e << " ";
- cout << endl;
-
- pos = find(lt.begin(), lt.end(), 8);
- lt.erase(pos);
- for (auto e : lt) cout << e << " ";
- cout << endl;
-
- return 0;
- }
| 函数声明 | 接口说明 |
| empty | 检测 list 是否为空,是返回 true ,否则返回 false |
| size | 返回 list 中有效节点的个数 |
| front | 返回 list 的第一个节点中值的引用 |
| back | 返回 list 的最后一个节点中值的引用 |
- template<class T>
- struct list_node//节点
- {
- list_node
* _next; - list_node
* _prev; - T _data;
- // 构造函数
- list_node(const T& x = T())
- :_next(nullptr)
- , _prev(nullptr)
- , _data(x)
- {}
- };
由于节点存储的数据可能是任意类型,所以我们需要将将节点定义为模板类。这里我们需要写一个给缺省值的默认构造函数,便于之后在主类中new一个新节点时直接初始化,同时将两个指针置为空,将数据写入数据域中。
- class list
- {
- public:
- typedef list_node
node; -
- private:
- node* _head;
- }
- //尾插
- void push_back(const T& x) const
- {
- node* new_node = new node(x);
- node* tail = _head->_prev;
- //链接节点之间的关系
- tail->_next = new_node;
- new_node->_prev = tail;
- new_node->_next = _head;
- _head->_prev = new_node;
- }
- //头插
- void push_front(const T& x)
- {
- node* head = _head->_next;
- node* new_node = new node(x);
-
- _head->_next = new_node;
- new_node->_prev = _head;
- new_node->_next = head;
- head->_prev = new_node;
- }
这里模拟的头插和尾插也很简单,因为和我们之前在数据结构时候的双向循环链表是一样的,只需要找到头或者尾,然后链接四个节点间的关系即可。
注意:list 的迭代器是自定义类型,不是原生指针node*。
迭代器为自定义类型,其中*,++等都是通过运算符重载来完成的。
所以我们需要重载的符号:*,->,前置++,后置++,前置--,后置--,!=,==;
- template<class T>
- struct __list_iterator
- {
- typedef list_node
node; - typedef __list_iterator
self; - node* _node;
-
- //构造函数
- __list_iterator(node* n)
- :_node(n)
- {}
- //重载*运算符
- T& operator*()
- {
- return _node->_val;
- }
- T* operator->()
- {
- return &_node->_data;
- }
- //重载前置++运算符
- 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 self& s)
- {
- return _node != s._node;
- }
- //重载==运算符
- bool operator==(const self& s)
- {
- return _node == s._node;
- }
- };
此处我实现了一个简单的正向迭代器,使用一个模板参数T表示类型。
当普通迭代器封装好了之后,我们需要在list类中来实现它的 begin() 和 end() 方法。由于迭代器的名字一般都是 iterator,而且对于范围for来说,也只能通过 iterator 来转换为迭代器进行遍历。所以这里我们将其typedef为iterator。
- template<class T>
- class list//链表
- {
- typedef list_node
node; - public:
- typedef __list_iterator
iterator; -
- iterator begin()
- {
- return iterator(_head->_next);
- }
-
- iterator end()
- {
- return iterator(_head);
- }
- private:
- node* _head;
- };
const迭代器与普通迭代器的区别在于const迭代器指向的内容是不能修改的,但是它的指向是可以修改的。
- template<class T>
- class list//链表
- {
- typedef list_node
node; - public:
- typedef __list_const_iterator
const_iterator; -
- const_iterator begin()
- {
- return const_iterator(_head->_next);
- }
-
- const_iterator end()
- {
- return const_iterator(_head);
- }
- private:
- node* _head;
- };
我们最好的做法就是在__list_iterator 的类模板中的添加两个模板参数,然后再 list 类中 typedef 两份分别将第二个参数分别改成 T& 和 const T& 的类型,本质上就是让编译器根据传入的 Ref 的不同来自动示例化出 const 迭代器类,而我们还需要重载一个->运算符,因为list中可能存储的是自定义类型,这个自定义类型如果要是有多个成员变量的话,我们就需要使用->来解引用访问成员变量,同样还是要区分普通迭代器和const 迭代器,所以就增加了另一个模版参数 Ptr。具体的解决做法如下:
- template<class T, class Ref, class Ptr>
- struct __list_iterator
- {
- typedef list_node
node; - typedef __list_iterator
self; - node* _node;
-
- __list_iterator(node* n)
- :_node(n)
- {}
-
- Ref operator*()//解引用
- {
- return _node->_data;
- }
- Ptr operator->()
- {
- return &_node->_data;
- }
- ...
- };
然后,最终在链表类中使用如下:
- template<class T>
- class list//链表
- {
- typedef list_node
node; - public:
- typedef __list_iterator
iterator;//普通迭代器 - typedef __list_iterator
const T&, const T*> const_iterator;//const迭代器 -
- iterator begin()
- {
- return iterator(_head->_next);//匿名对象的返回
- }
- const_iterator begin() const
- {
- return const_iterator(_head->_next);
- }
- iterator end()
- {
- return iterator(_head);
- }
- const_iterator end() const
- {
- return const_iterator(_head);
- }
- private:
- node* _head;
- };
- // 指定位置插入
- void insert(iterator pos, const T& x)
- {
- node* cur = pos._node;
- node* prev = cur->_prev;
- node* new_node = new node(x);
-
- prev->_next = new_node;
- new_node->_prev = prev;
- new_node->_next = cur;
- cur->_prev = new_node;
- }
- // 指定位置删除
- iterator erase(iterator pos)
- {
- assert(pos != end());
-
- node* prev = pos._node->_prev;
- node* next = pos._node->_next;
-
- prev->_next = next;
- next->_prev = prev;
- delete pos._node;
-
- return iterator(next);
- }
- // 尾删
- void pop_back()
- {
- erase(--end());
- }
- // 头删
- void pop_front()
- {
- erase(begin());
- }
由于后面会频繁对空进行初始化,所以在这里对它进行了封装,方便后面的调用。
- void empty_init()//空初始化
- {
- _head = new node;
- _head->_next = _head;
- _head->_prev = _head;
- }
- list()
- {
- empty_init();
- }
- //用n个val构造对象
- list(int n, const T& val = T())
- {
- empty_init();
- for (int i = 0; i < n; i++)
- {
- push_back(val);
- }
- }
- //拷贝构造传统写法
- list(const list
& lt) - {
- empty_init();
- for (auto e : lt)
- {
- push_back(e);
- }
- }
- //拷贝构造现代写法
- list(const list
& lt) - {
- empty_init();
- list
tmp(lt.begin(), lt.end()) ; - swap(tmp);
- }
- template <class Iterator>
- list(Iterator first, Iterator last)
- {
- empty_init();
- while (first != last)
- {
- push_back(*first);
- ++first;
- }
- }
- //赋值运算符重载
- list
& operator=(list lt)//注意这里不能用引用 - {
- swap(lt);
- return *this;
- }
- //要全部清理掉
- ~list()
- {
- clear();
- delete _head;
- _head = nullptr;
- }
- //不释放头结点
- void clear()
- {
- iterator it = begin();
- while (it != end())
- {
- it = erase(it);
- //这样也可以
- //erase(it++);
- }
- }
- template<class T>
- struct list_node//节点
- {
- list_node
* _next; - list_node
* _prev; - T _data;
-
- list_node(const T& x = T())
- :_next(nullptr)
- , _prev(nullptr)
- , _data(x)
- {}
- };
- template<class T, class Ref, class Ptr>
- struct __list_iterator
- {
- typedef list_node
node; - typedef __list_iterator
self; - node* _node;
-
- __list_iterator(node* n)
- :_node(n)
- {}
-
- Ref operator*()//解引用
- {
- return _node->_data;
- }
- Ptr operator->()
- {
- return &_node->_data;
- }
- //前置++
- 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 self& s)
- {
- return _node != s._node;
- }
- bool operator==(const self& s)
- {
- return _node == s._node;
- }
- };
- template<class T>
- class list//链表
- {
- typedef list_node
node; - public:
- typedef __list_iterator
iterator;//普通迭代器 - typedef __list_iterator
const T&, const T*> const_iterator;//const迭代器 -
- iterator begin()
- {
- return iterator(_head->_next);//匿名对象的返回
- }
- const_iterator begin() const
- {
- return const_iterator(_head->_next);
- }
- iterator end()
- {
- return iterator(_head);
- }
- const_iterator end() const
- {
- return const_iterator(_head);
- }
- void empty_init()//空初始化
- {
- _head = new node;
- _head->_next = _head;
- _head->_prev = _head;
- }
- list()
- {
- empty_init();
- }
- //迭代器区间构造
- template <class Iterator>
- list(Iterator first, Iterator last)
- {
- empty_init();
- while (first != last)
- {
- push_back(*first);//push_back使用的前提是要有哨兵位的头结点
- ++first;
- }
- }
- // 交换函数
- void swap(list
& tmp) - {
- std::swap(_head, tmp._head);
- }
- //现代拷贝构造
- list(const list
& lt) - {
- list
tmp(lt.begin(), lt.end()) ; - swap(tmp);
- }
- //现代赋值写法
- list
& operator=(list lt) - {
- swap(lt);
- return *this;
- }
- ~list()//要全部清理掉
- {
- clear();
- delete _head;
- _head = nullptr;
- }
- void clear()//不释放头结点
- {
- iterator it = begin();
- while (it != end())
- {
- it = erase(it);
- //这样也可以
- //erase(it++);
- }
- }
- void insert(iterator pos, const T& x)
- {
- node* cur = pos._node;
- node* prev = cur->_prev;
- node* new_node = new node(x);
-
- prev->_next = new_node;
- new_node->_prev = prev;
- new_node->_next = cur;
- cur->_prev = new_node;
- }
- iterator erase(iterator pos)
- {
- assert(pos != end());
-
- node* prev = pos._node->_prev;
- node* next = pos._node->_next;
-
- prev->_next = next;
- next->_prev = prev;
- delete pos._node;
-
- return iterator(next);
- }
- //尾插
- void push_back(const T& x) const
- {
- //node* new_node = new node(x);
- //node* tail = _head->_prev;
- 链接节点之间的关系
- //tail->_next = new_node;
- //new_node->_prev = tail;
- //new_node->_next = _head;
- //_head->_prev = new_node;
- insert(end(), x);
- }
- //头插
- void push_front(const T& x)
- {
- //node* head = _head->_next;
- //node* new_node = new node(x);
-
- //_head->_next = new_node;
- //new_node->_prev = _head;
- //new_node->_next = head;
- //head->_prev = new_node;
- insert(begin(), x);
- }
- //尾删
- void pop_back()
- {
- erase(--end());
- }
- //头删
- void pop_front()
- {
- erase(begin());
- }
- private:
- node* _head;
- };
当我们使用 erase 进行删除后,此时指向删除位置的迭代器就失效了,再次使用就会令程序崩溃。
因此若要多次删除,则需要在使用后利用 erase 的返回值更新迭代器,这样使用才不会出现错误。
- int main()
- {
- vector<int> v = { 1, 2,3,5,4,6 };
- list<int> lt(v.begin(), v.end());
- list<int>::iterator pos = find(lt.begin(), lt.end(), 3);
- for (int i = 0; i < 3; i++)
- {
- pos = lt.erase(pos); //利用erase的返回值更新迭代器
- }
- for (auto e : lt) cout << e << " ";
- cout << endl;
- return 0;
- }
| vector | list | |
| 底 层 结 构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
| 随 机 访 问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素 效率O(N) |
| 插 入 和 删 除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂 度为O(N),插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1) |
| 空 间 利 用 率 | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低 |
| 迭 代 器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
| 迭 代 器 失 效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响 |
| 使 用 场 景 | 需要高效存储,支持随机访问,不关心插入删除效率 | 大量插入和删除操作,不关心随机访问 |
本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。
