初夏小谈:模拟实现list以及list与vector的区别
list容器在底层使用双向链表实现的。数据存在每个节点中,并且每个结点还有两个空间分别存放两个指针用来找寻它的前后节点。
在C+11中还引入了forward_list它的底层是单链表实现的。在只实现尾插,头插头删时比list更加高效。
在list还会分配一些额外空间来保存结点的相关联信息。
list基本接口:
一、在list中构造有四种分别是:1.构造空list。2.构造N个值为data的list。3.拷贝构造函数。4.用区间来构造。
- list()
- list(size_type n, const value_type& val = value_type())
- list(const list& L)
- list(InputIterator first, InputIterator last)
二、list中常用容量操作有两种:1.判断list是否为空。2.计算list的有效数据个数。由于list是链表实现所以不需对容量操作。
- bool empty() const
- size_t size() const
三、元素访问操作:由于list是链表实现。不支持随机访问操作。两种操作:1.访问第一个元素。2.访问最后一个元素。
- reference front()
- const_reference front() const
- reference back()
- const_reference back() const
四、元素修改操作:1.头插。2.头删。3.尾插。4.尾删。5.在某位置插入。6.在某位置插入N个值为data的数据。7.在某个位置插入一块区间内的所有数据。8.删除某个位置上的元素。9.删除list中某块区域中的所有结点。10.交换两个数据。11.修改有效结点个数,倘若不够用data填充。12.清空所有数据。
- void push_front(const value_type& val)
- void pop_front()
- void push_back(const value_type& val)
- void pop_back()
- iterator insert(iterator position, const value_type& val)
- void insert(iterator position, size_type n,const value_type& val)
- void insert(iterator position, InputIterator first, InputerIterator last)
- iterator erase(iterator position)
- iterator erase(iterator first, iterator last)
- void swap(list& x)
- void resize(size_type n,value_type val = value_type())
- void clear()
五、迭代器操作:1.返回第一个元素的迭代器。2.返回最后一个元素的下一个位置的迭代器
- begin()
- end()
- rbegin()
- rend()
六、list中迭代器失效问题:
迭代器失效就是迭代器所指向的空间无效。就是迭代器所指向的结点被删除了。所以删除结点时会导致指向删除结点的迭代器失效,而其它迭代器不会失效。
在list中插入数据不会导致迭代器失效,是因为迭代器所指向的数据不会被搬移。这就是链表的好处。
七模拟实现list:
在实现普通类型的函数操作时和链表实现相似。但是在实现迭代器操作时,就不能像vector那样直接用原生态指针来模拟。因为在list中数据不连续。所以对原生态指针的操作必须封装来模拟实现。
#include<iostream> #include<list> using namespace std; //list底层结构:带头节点的双向循环链表 //为什么带头节点 //1.头部插入和删除比较简单 //2.区分begin(),和end() namespace ListS { template<class T> struct ListNode { ListNode(const T& data = T()) :Prev(nullptr) , Next(nullptr) , data(data) {} ListNode<T>* Prev; ListNode<T>* Next; T data; }; //迭代器当成像指针一样的方式使用:指针具有的操作 迭代器都必须支持 //operator*() //operator->() //operator++() //operator--():视具体结构对待 //operator!=() //operator==() template<class T, class Ref, class Ptr> //三个参数分别是什么意思各自的作用??? //template<class T, class Ref, class Ptr> //T 元素类型 //普通类型: //Ref代表T& Ptr代表T* //const时 //Ref代表const T&, Ptr代表const T* //为了统一普通类型和const类型 //1.实现这个迭代器类是因为它不是原生态指针,底层是双向链表,空间不一定连续。 //2.在这个类中实现++,--,==,!=,*,->。操作(指针操作,迭代器都可以支持) //3.直接在list类中内嵌对应迭代器类型使用。为简单取别名 //typedef ListIterator<> iterator struct ListIterator { typedef ListNode<T>* PNode; typedef ListIterator<T, Ref, Ptr> Self; typedef Ref Ref; typedef Ptr Ptr; public: ListIterator(PNode pNode = nullptr) :NodePtr(pNode) {} ListIterator(const Self& s) :NodePtr(s.NodePtr) {} Ref operator*() { return NodePtr->data; } Ptr operator->() { return &(operator*()); } Self& operator++() { NodePtr = NodePtr->Next; return *this; } Self operator++(int) { Self temp(*this); NodePtr = NodePtr->Next; return temp; } Self& operator--() { NodePtr = NodePtr->Prev; return *this; } Self operator--(int) { Self temp(*this); NodePtr = NodePtr->Prev; return temp; } bool operator!=(const Self& s) { return NodePtr != s.NodePtr; } bool operator==(const Self& s) { return NodePtr == s.NodePtr; } //private: PNode NodePtr; }; //反向迭代器 //对正向迭代器的封装,迭代器适配器 template<class Iterator> class ReverseIterator { //typedef ListIterator<T, T&, T*> iterator; typedef ReverseIterator<Iterator> Self; //typename : 告诉编译器Ref是Iterator类型中的内嵌类型,而不是静态的成员变量 typename typedef Iterator::Ref Ref; typename typedef Iterator::Ptr Ptr; public: //构造函数 ReverseIterator(Iterator it) :_it(it) {} //拷贝构造函数 ReverseIterator(const Self& rit) :_it(rit._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& s) { return _it != s._it; } bool operator==(const Self& s) { return _it == s._it; } public: Iterator _it; }; template<class T> class list { typedef ListNode<T> Node; public: typedef ListIterator<T, T&, T*> iterator; typedef ListIterator<T, const T&, const T*> const_iterator; typedef ReverseIterator<iterator> reverse_iterator; public: //********************************************************************** //1.链表创建操作 list() { CreatHeadNode(); } list(size_t n, const T& data = T()) { CreatHeadNode(); //Node* ptr = HeadPtr; for (size_t i = 0; i < n; ++i) { push_back(data); } } list(T* first, T* last) { CreatHeadNode(); while (first != last) { push_back(*first); ++first; } } //拷贝构造 list(list<T>& L) { CreatHeadNode(); for (const auto& e : L) { push_back(e); } } //运算符重载 list<T>& operator=(const list<T>& L) { if (HeadPtr != &l) { for (const auto& e : L) { push_back(e); } } } ~list() { clear(); delete HeadPtr; HeadPtr = nullptr; } //********************************************************************** //2.容量操作 //1.有效结点的个数 size_t size()const { size_t count = 0; Node* ptr = HeadPtr->Next; while (ptr != HeadPtr) { count++; ptr = ptr->Next; } return count; } //2.判断是否为空 bool empty()const { return HeadPtr == HeadPtr->Next; } //3.设置有效链表的长度 void resize(size_t NewSize, const T& data =T()) { size_t OldSize = size(); if (NewSize > OldSize) { for (size_t i = OldSize; i < NewSize; i++) { push_back(data); } } else { for (size_t i = OldSize; i > NewSize; i--) { pop_back(); } } } //********************************************************************** //3.元素访问操作 //1.访问首元素 T& front() { return HeadPtr->Next->data; } //1.1 const T& front()const { return HeadPtr->Next->data; } //2.访问尾元素 T& back() { return HeadPtr->Prev->data; } //2.1 const T& back()const { return HeadPtr->Prev->data; } //*************************************************************** //4.元素修改操作 //1.尾插 void push_back(const T& data) { insert(end(), data); } //2.尾删 void pop_back() { erase(--end()); } //3.头插 void push_front(const T& data) { insert(begin(), data); } //4.头删 void pop_front() { erase(begin()); } //5.任意位置插入 iterator insert(iterator position, const T& data) { Node* pos = position.NodePtr; Node* NewNode = new Node(data); NewNode->Next = pos; NewNode->Prev = pos->Prev; pos->Prev->Next = NewNode; pos->Prev = NewNode; return iterator(NewNode); } //6.任意位置删除 iterator erase(iterator position) { Node* DelNode = position.NodePtr; if (DelNode == HeadPtr) { return iterator(HeadPtr); } Node* pRet = DelNode->Next; DelNode->Next->Prev = DelNode->Prev; DelNode->Prev->Next = DelNode->Next; delete DelNode; return iterator(pRet); } //7.清空list void clear() { Node* ptr = HeadPtr->Next; while (ptr != HeadPtr) { HeadPtr->Next = ptr->Next; delete ptr; ptr = HeadPtr->Next; } HeadPtr->Next = HeadPtr; HeadPtr->Prev = HeadPtr; } //*********************************************************************** //5.迭代器操作 //1. iterator begin() { return iterator(HeadPtr->Next); } iterator end() { return iterator(HeadPtr); } const const_iterator cbegin()const { return const_iterator(HeadPtr->Next); } const const_iterator cend()const { return const_iterator(HeadPtr); } reverse_iterator rbegin() { return reverse_iterator(iterator(HeadPtr)); } reverse_iterator rend() { return reverse_iterator(iterator(HeadPtr->Next)); } private: void CreatHeadNode() { HeadPtr = new Node; HeadPtr->Next = HeadPtr; HeadPtr->Prev = HeadPtr; } private: Node* HeadPtr; }; } template<class T> void PrintList(ListS::list<T>& L) { auto it = L.begin(); while (it != L.end()) { cout << *it << "--->"; ++it; } cout << "NULL" << endl; } int main() { int array[] = { 1,2,3,4,5 }; ListS::list<int> L(array, array + sizeof(array) / sizeof(array[0])); L.push_back(6); L.push_front(0); PrintList(L); //ListS::list<int>::reverse_iterator it = L.rbegin(); auto it = L.rbegin();// while (it != L.rend()) { cout << *it << "--->"; //++it; } cout << "NULL" << endl; cout << L.front() << endl; cout << L.back() << endl; cout << L.size() << endl; L.pop_front(); L.pop_back(); PrintList(L); L.resize(10, 6); PrintList(L); L.resize(5); PrintList(L); L.clear(); cout << L.size() << endl; system("pause"); return 0; }
运行:
八、list与vector区别:
- vector是动态顺序表实现,是一块连续的空间,而list是带头结点的双向循环链表。
- vector可以随机访问元素,list不支持
- vector的插入元素效率低,因为可能需要扩容和搬移数据。而list任意位置插入删除都只需要更改要插入的数据指向和它前后结点的指向。
- vector底层不易造成内存碎片,空间和缓存利用率高。list每次都要开辟结点,内存容易碎片化,利用率低。
- vector迭代器是原生态指针。list迭代器是对原生态指针的一层封装。
- vector的插入和删除都可以造成迭代器失效,list删除数据可以造成迭代器失效插入不会导致迭代器失效。
珍&源码