个人主页
🎄一、媒介
list是一个双向链表的容器,它可以在其内部中存储各种范例的元素,而且支持动态地添加、删除和修改元素。
🏠二、根本框架
list可以分为三部门:一个是list节点类,一个是迭代器类,尚有一个是list类自己。
它们三个类底层的成员变量又分别代表差异的功能。
此中list节点类封装了list的元素以及前后指针,且完成了节点的初始化。
迭代器类封装了指向节点的指针,还负责重载++、–等运算符。
list类自己负责初始化以及负责插入和删除等功能。
🎡三、list节点类的实现
链表中数据以及前后指针的范例都是由模板主动天生的,可以为内置范例或自界说范例。
- template<class T>
- struct list_node
- {
- T _data;
- list_node<T>* _next;
- list_node<T>* _prev;
- //构造
- list_node(const T& data = T())
- :_data(data)
- , _next(nullptr)
- , _prev(nullptr)
- {};
- };
复制代码 🎉四、list迭代器类
由于迭代器涉及平凡迭代器和const迭代器,因此我们可以利用模板,在模板内里设置三个差异的范例,分别为节点的数据范例、引用范例和指针范例。
- template<class T,class Ref,class Ptr>
- struct list_iterator
- {
- typedef list_node<T> Node;
- typedef list_iterator<T, Ref, Ptr> Self;
- //用来访问Node类型的成员变量
- Node* _node;
- };
复制代码 迭代器的默认构造函数由于支持根本的隐式范例转换,因此实现起来也很简单。
- list_iterator(Node* node)
- :_node(node)
- {}
复制代码 1.Ref operator*()
负责重载迭代器的解引用,直接返回当前该迭代器指向的节点数据即可。
- Ref operator*()
- {
- return _node->_data;
- }
复制代码 2.Ptr operator->()
由于我们想把迭代器当成指针利用,因此重载->是须要的,其返回值为节点元素的地点。为什么是返回地点呢?这是由于在利用迭代器->时,编译器会主动优化成->->。
- Ptr operator->()
- {
- return &_node->_data;
- }
复制代码 3.Self& operator++()前置和Self& operator–()前置
直接让节点指向下一个和前一个即可。
- //前置++
- Self& operator++()
- {
- _node = _node->_next;
- return *this;
- }
- //前置--
- Self& operator--()
- {
- _node = _node->_prev;
- return *this;
- }
复制代码 4.Self operator++(int)后置和Self operator–(int)后置
我们只需把当前迭代器实例化的对象拷贝给一个新的迭代器对象tmp,然后把当前的数据举行处理处罚,末了将tmp举行返回即可。
- //后置++
- Self operator++(int)
- {
- Self tmp(*this);
- _node = _node->_next;
- return tmp;
- }
- //后置--
- Self& operator--(int)
- {
- Self tmp(*this);
- _node = _node->_prev;
- return tmp;
- }
复制代码 5.bool operator!=(const Self& s) const和bool operator==(const Self& s) const
判定节点是否相称不能只判定值是否相称,由于差异的节点可以存放类似的值,因此必要比力其节点是否相称即可。
- bool operator!=(const Self& s) const
- {
- return _node != s._node;
- }
- bool operator==(const Self& s) const
- {
- return _node == s._node;
- }
复制代码 ⭐五、list类
1.初始化
初始化只需初始化头结点即可。
- //无参初始化
- list()
- {
- //通过调用这一函数来创建头节点
- empty_init();
- }
- //空初始化
- void empty_init()
- {
- _head = new Node;
- _head->_next = _head;
- _head->_prev = _head;
- _size = 0;
- }
复制代码 2.构造函数
当链表为空时,_head节点的头和尾都指向它自己,因此在后续有节点插入时,只需改变一下prev和next指向的位置即可。
- list()
- {
- _head = new Node<T>();
- _head->next = _head;
- _head->prev = _head;
- }
复制代码 4.析构函数和clear函数
析构函数是用来开释节点空间的,因此必要先界说一个clear函数用来开释全部的有效节点,确保没有有效节点后再把哨兵位的_head举行删除,然后将_head置为空。
- //析构
- ~list()
- {
- clear();
- delete _head;
- _head = nullptr;
- }
- void clear()
- {
- auto it = begin();
- while (it != end())
- {
- it = erase(it);
- }
- }
复制代码 5.深拷贝
先对其举行空初始化利用,然后用迭代器依次对其举行访问然后插入即可。
- //深拷贝
- list(const list<T>& lt)
- {
- empty_init();
- for (auto& e : lt)
- {
- push_back(e);
- }
- }
复制代码 6.深赋值
我们可以调用swap函数将两者数据举行交换即可。
- //深赋值
- void swap(list<int>& lt)
- {
- std::swap(_head, lt._head);
- std::swap(_size, lt._size);
- }
- list<T>& operator=(list<T> lt)
- {
- swap(lt);
- return *this;
- }
复制代码 7.头插和头删函数
头插函数就是每次在begin位置之进步行插入,由于begin是第一个有效的元素;而头删就是每次在begin位置举行删除。
- void push_front(const T& x)
- {
- Insert(begin(), x);
- }
- void pop_front()
- {
- erase(--begin());
复制代码 8.尾插和尾删函数
尾插就是每次在end()位置举行插入,由于end是末了一个有效的元素;而尾删则是在end()的位置上举行删除。
由于我们实现了insert函数,因此就可以调用insert函数在其尾部插入数据即可。
- void push_back(const T& x)
- {
- Insert(end(), x);
- ++_size;
- }
- void pop_back()
- {
- erase(--end());
- }
复制代码 9.insert和erase函数
insert函数就是在pos位置插入值。
- iterator Insert(iterator pos, const T& x)
- {
- //取到pos位置的节点
- Node* cur = pos._node;
- //保留之前的节点
- Node* prev = cur->_prev;
- //创建新节点
- Node* newnode = new Node(x);
- //改变指向,最后更新一下_size
- newnode->_next = cur;
- cur->_prev = newnode;
- newnode->_prev = prev;
- prev->_next = newnode;
- ++_size;
- return newnode;
- }
复制代码 erase函数起首要举行断言,防止删除哨兵位。然后将pos位置节点的前和后举行毗连,末了删除pos位置的节点,更新一下_size的巨细。
- 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;
- --_size;
- return next;
- }
复制代码 10.迭代器的实现(平凡和const)
由于存在平凡成员变量和const成员变量的调用,因此必要实现两个迭代器。
begin迭代器是返回第一个有效位置,由于哨兵位_head并没有数据,因此返回哨兵位的下一个位置。end迭代器是返回末了一个元素的下一个位置,而这个位置是无效的,双向链表无效的位置就只有哨兵位这一个位置,因此返回哨兵位即可。
- iterator begin()
- {
- return _head->_next;
- }
- iterator end()
- {
- return _head;
- }
- //const迭代器
- const_iterator begin() const
- {
- return _head->_next;
- }
- const_iterator end() const
- {
- return _head;
- }
复制代码 11.判空
直接判定_size是否为空即可。
- //判空
- bool empty() const
- {
- return _size == 0;
- }
复制代码 12.size()函数
直接返回_size即可。
- size_t size() const
- {
- return _size;
- }
复制代码 🚀六、vector和list函数的区别
起首,vector是一个动态数组,在插入和删除数据的时间会挪动背面的元素,这就有大概会导致迭代器失效;而list则为链表,它可以在恣意位置举行插入和删除,因此迭代器的稳固性就更高。
其次,vector的迭代器通常为随机访问迭代器,允许向前向后访问元素,而list迭代器大概为双向迭代器,只能向前或向后移动。
末了,vector的随机访问速率快,因此在查找元素时的服从高,但假如频仍的插入大概删除元素时,list通常会更快,由于它不必要移动大量的元素。
🏝️七、源代码
- #include<iostream>
- #include<assert.h>
- #include<list>
- using namespace std;
- namespace bit
- {
- template<class T>
- struct list_node
- {
- T _data;
- list_node<T>* _next;
- list_node<T>* _prev;
- //构造
- list_node(const T& data = T())
- :_data(data)
- , _next(nullptr)
- , _prev(nullptr)
- {};
- };
- //const迭代器
- 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->_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) const
- {
- return _node != s._node;
- }
- bool operator==(const Self& s) const
- {
- return _node == s._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;
- iterator begin()
- {
- return _head->_next;
- }
- iterator end()
- {
- return _head;
- }
- //const迭代器
- const_iterator begin() const
- {
- return _head->_next;
- }
- const_iterator end() const
- {
- return _head;
- }
- //空初始化
- void empty_init()
- {
- _head = new Node;
- _head->_next = _head;
- _head->_prev = _head;
- _size = 0;
- }
- //无参初始化
- list()
- {
- empty_init();
- }
- //析构
- ~list()
- {
- clear();
- delete _head;
- _head = nullptr;
- }
- void clear()
- {
- auto it = begin();
- while (it != end())
- {
- it = erase(it);
- }
- }
- //深拷贝
- list(const list<T>& lt)
- {
- empty_init();
- for (auto& e : lt)
- {
- push_back(e);
- }
- }
- //深赋值
- void swap(list<int>& lt)
- {
- std::swap(_head, lt._head);
- std::swap(_size, lt._size);
- }
- list<T>& operator=(list<T> lt)
- {
- swap(lt);
- return *this;
- }
- size_t size() const
- {
- return _size;
- }
- void push_back(const T& x)
- {
- Insert(end(), x);
- ++_size;
- }
- iterator Insert(iterator pos, const T& x)
- {
- Node* cur = pos._node;
- Node* prev = cur->_prev;
- Node* newnode = new Node(x);
- newnode->_next = cur;
- cur->_prev = newnode;
- newnode->_prev = prev;
- prev->_next = newnode;
- ++_size;
- return newnode;
- }
-
- void pop_back()
- {
- erase(--end());
- }
- void pop_front()
- {
- erase(--begin());
- }
- 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;
- --_size;
- return next;
- }
- void push_front(const T& x)
- {
- Insert(begin(), x);
- }
- //判空
- bool empty() const
- {
- return _size == 0;
- }
-
- private:
- Node* _head;
- size_t _size;
- };
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |