list列表
list 容器的优点
- 高效的插入和删除:由于std::list是基于带头双向循环链表实现的,插入和删除操作复杂度O(1)
- 稳定的迭代器:相对于vector这种,list迭代器失效的情况比较少
- 动态内存管理:std::list可以动态调整大小,根据需要分配和释放内存。能够有效地处理大量元素
但因为是链表结构,空间不连续,占用了更多的内存。在list 的迭代器当中也就没有实现 operator+ 这个函数。
定义私有成员:
private:Node* _head;size_t _size;
在官方的List源代码当中,List容器的结点定义在一个结构体当中。
链表节点
template<class T> //模板声明struct list_node{list_node<T>* _next; //成员变量list_node<T>* _prev;T _val;list_node(const T& val = T()) //构造函数:_next(nullptr), _prev(nullptr), _val(val){}};
使用 struct
而不是 class
,意味着所有成员默认是 public
的。
定义正向迭代器
通过模板参数来同时实现 普通迭代器 和 const 迭代器,避免代码冗余。
// T: 数据类型, Ref: 引用类型(T&/const T&), Ptr: 指针类型(T*/const T*)template<class T, class Ref, class Ptr>struct __list_iterator //定义一个类来实现迭代起的行为{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self; //给自己类型起个别名,简化代码Node* _node; //节点指针__list_iterator(Node* node) //构造函数 :_node(node){}// 运算符重载Ref operator*() // 解引用{return _node->_val;}Ptr 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 self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}
};
举例使用
定义反向迭代器
核心思想:
rbegin()
对应end()
(即最后一个元素的下一个),rend()
对应begin()
。操作:对反向迭代执行
++
操作,实际是调用其内部封装的正向迭代器的--
操作。
template<class T, class Ref, class Ptr>struct __list_reverse_iterator{typedef list_node<T> Node;typedef __list_reverse_iterator<T, Ref, Ptr> self;Node* _node;__list_reverse_iterator(Node* node):_node(node){}Ref operator*(){return _node->_val;}Ptr operator->(){return &_node->_val;}// 注意:反向迭代器的++是正向的--self& operator++(){_node = _node->_prev;return *this;}self operator++(int){self tmp(*this);_node = _node->_prev;return tmp;}// 反向迭代器的--是正向的++self& operator--(){_node = _node->_next;return *this;}self operator--(int){self tmp(*this);_node = _node->_next;return tmp;}bool operator!=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}};
定义一个list类
对之前
- list_node结构体 重命名为 Node
- 正向迭代器重命名为 iterator 和 const_iterator
- 反向迭代器重命名为 reverse_iterator 和 const_reverse_iterator
template<class T>class list{typedef list_node<T> Node; //重命名public://正向迭代器typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;//反向迭代器typedef __list_reverse_iterator<T, T&, T*> reverse_iterator;typedef __list_reverse_iterator<T, const T&, const T*> const_reverse_iterator;......};
构造函数
void empty_init() //创建一个空指针{_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}list() //构造函数 {empty_init();}
析构函数
~list() {clear(); //析构函数delete _head;_head = nullptr;}void clear() //清理{iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}
拷贝构造函数
// lt2(lt1)list(const list<T>& lt)//list(const list& lt){empty_init();for (auto& e : lt){push_back(e);}}
push_back
void push_back(const T& x){insert(end(), x);}
push_front
void push_front(const T& x){insert(begin(), x);}
pop_back
void pop_back(){erase(--end());}
pop_front
void pop_front(){erase(begin());}
insert
iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;++_size;return newnode;}
要返回新插入的元素的位置,防止外部迭代器失效。
erase
iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;return next;}
size()
size_t size() { return _size; }
empty
bool empty() const { return _size == 0; }
front()
T& front(){assert(_size > 0);return _head->_next->_val;}
back()
T& back(){assert(_size > 0);return _head->_prev->_val;}
赋值运算符重载函数和swap()函数
void swap(list<T>& lt) {std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt)//list& operator=(list lt){swap(lt);return *this;}
其实上述的 赋值重载运算符函数当中的模板类的类型名可以不用 List<T>,我们知道List<T>是类型名,List是类名,对于模版类,类型名不是 List,而是List<T>,但是如果是在类模板当中写,可以写类名也可以写类型名(下述写法也可以):
list& operator=(list lt)
总代码
#pragma once
#include<assert.h>
#include <iostream>using namespace std;namespace bit
{template<class T>struct list_node //底层是结构体 {list_node<T>* _next;list_node<T>* _prev;T _val;list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}};// typedef __list_iterator<T, T&, T*> iterator;// typedef __list_iterator<T, const T&, const T*> const_iterator;// T: 数据类型, Ref: 引用类型(T&/const T&), Ptr: 指针类型(T*/const T*)template<class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self; // 简化类型名Node* _node;// 核心:持有一个指向节点的指针__list_iterator(Node* node):_node(node){}// 运算符重载Ref operator*()// 解引用{return _node->_val;}Ptr 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 self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}};// 反向迭代器template<class T, class Ref, class Ptr>struct __list_reverse_iterator{typedef list_node<T> Node;typedef __list_reverse_iterator<T, Ref, Ptr> self;Node* _node;__list_reverse_iterator(Node* node):_node(node){}Ref operator*(){return _node->_val;}Ptr operator->(){return &_node->_val;}// 注意:反向迭代器的++是正向的--self& operator++(){_node = _node->_prev;return *this;}self operator++(int){self tmp(*this);_node = _node->_prev;return tmp;}// 反向迭代器的--是正向的++self& operator--(){_node = _node->_next;return *this;}self operator--(int){self tmp(*this);_node = _node->_next;return tmp;}bool operator!=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}};/*template<class T>struct __list_const_iterator{typedef list_node<T> Node;Node* _node;__list_const_iterator(Node* node):_node(node){}const T& operator*(){return _node->_val;}__list_const_iterator<T>& operator++(){_node = _node->_next;return *this;}__list_const_iterator<T> operator++(int){__list_const_iterator<T> tmp(*this);_node = _node->_next;return tmp;}bool operator!=(const __list_const_iterator<T>& it){return _node != it._node;}bool operator==(const __list_const_iterator<T>& it){return _node == it._node;}};*/template<class T>class list{typedef list_node<T> Node; //重命名public://正向迭代器typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;//反向迭代器typedef __list_reverse_iterator<T, T&, T*> reverse_iterator;typedef __list_reverse_iterator<T, const T&, const T*> const_reverse_iterator;// 这么设计太冗余了,看看库里面如何设计的//typedef __list_const_iterator<T> const_iterator;// 这样设计const迭代器是不行的,因为const迭代器期望指向内容不能修改// 这样设计是迭代器本身不能修改// typedef const __list_iterator<T> const_iterator;// const T* ptr1;// T* const ptr2;// 如何设计const迭代器?iterator begin(){//return _head->_next;return iterator(_head->_next);}iterator end(){return _head;//return iterator(_head);}const_iterator begin() const{//return _head->_next;return const_iterator(_head->_next);}const_iterator end() const{return _head;//return const_iterator(_head);}// 反向迭代器的起始是最后一个元素reverse_iterator rbegin(){return reverse_iterator(_head->_prev);}// 反向迭代器的结束是头节点(第一个元素的前一个位置)reverse_iterator rend(){return reverse_iterator(_head);}const_reverse_iterator rbegin() const{return const_reverse_iterator(_head->_prev);}const_reverse_iterator rend() const{return const_reverse_iterator(_head);}void empty_init() //ok{_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}list() //构造函数{empty_init();}// lt2(lt1)list(const list<T>& lt) //拷贝构造函数//list(const list& lt){empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T>& lt) //swap{std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt) //赋值运算符重载//list& operator=(list lt){swap(lt);return *this;}~list() //析构函数{clear();delete _head;_head = nullptr;}void clear() // ok{iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}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());}// pos位置之前插入iterator insert(iterator pos, const T& x) //insert{Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;++_size;return newnode;}iterator erase(iterator pos) //erase{assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;return next;}size_t size() //ok {/*size_t sz = 0;iterator it = begin();while (it != end()){++sz;++it;}return sz;*/return _size;}bool empty() const { return _size == 0; }T& front(){assert(_size > 0);return _head->_next->_val;}T& back(){assert(_size > 0);return _head->_prev->_val;}private:Node* _head;size_t _size;};
list与vector的区别
vector | list | |
底 层 结 构 | 动态顺序表,是一段连续空间 | 带头结点的双向循环链表 |
随 机 访 问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素 效率O(N) |
插 入 和 删 除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂 度为O(N),插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1) |
空 间 利 用 率 | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低 |
迭 代 器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
迭 代 器 失 效 | 失效情况相对较多 | 失效情况相对较少 |
使 用 场 景 | 需要随机访问,不关心插入删除 | 大量插入和删除操作,不关心随 机访问 |