???學習的道路很枯燥,希望我們能并肩走下來!
文章目錄
目錄
文章目錄
前言
一 list的介紹及使用
1.1 list的介紹
1.2 list的使用?
1.2.1?list的構造
1.2.2 list iterator的使用
1.2.3 list capacity?
1.2.4 list element access?
?1.2.5 list modifiers
1.2.6 list的迭代器失效?
?1.2.7?List中sort的效率測試
二、list的模擬實現
?2.1 正向迭代器的實現
2.1.1 正向迭代器的封裝?
?2.1.2 迭代器的使用
2.2?list相關的成員函數
?2.2.1?構造函數
2.2.1.1 默認構造函數
?2.2.1.2 有參構造函數
?2.2.1.3 迭代器區間構造函數?
2.2.1.4 拷貝構造?
1.傳統?
2.現代?
2.2.2 析構函數和clear
2.2.2.1 析構函數
2.2.2.2 clear()
?2.2.3 賦值重載
2.2.4.1?empty、size
?2.2.4.2 insert
2.2.4.3 erase?
2.2.4.4 尾插尾刪頭插頭刪?
2.3 反向迭代器的實現?
?
三 list模擬實現的全部代碼
總
前言
本篇詳細介紹了list的使用和模擬實現,讓使用者了解list,而不是僅僅停留在表面,更好的模擬,為了更好的使用. 文章可能出現錯誤,如有請在評論區指正,讓我們一起交流,共同進步!
一 list的介紹及使用
1.1 list的介紹
list文本介紹
1. list是可以在常數范圍內在任意位置進行插入和刪除的序列式容器,并且該容器可以前后雙向迭代。
2. list的底層是雙向鏈表結構,雙向鏈表中每個元素存儲在互不相關的獨立節點中,在節點中通過指針指向其前一個元素和后一個元素。
3. list與forward_list非常相似:最主要的不同在于forward_list是單鏈表,只能朝前迭代,已讓其更簡單高效。
4. 與其他的序列式容器相比(array,vector,deque),list通常在任意位置進行插入、移除元素的執行效率更好。
5. 與其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的隨機訪問,比如:要訪問list 的第6個元素,必須從已知的位置(比如頭部或者尾部)迭代到該位置,在這段位置上迭代需要線性的時間開銷;list還需要一些額外的空間,以保存每個節點的相關聯信息(對于存儲類型較小元素的大list來說這可能是一個重要的因素)
1.2 list的使用?
list中的接口比較多,此處類似,只需要掌握如何正確的使用,然后再去深入研究背后的原理,已達到可擴展 的能力。以下為list中一些常見的重要接口。
1.2.1?list的構造
構造函數( (constructor)) | 接口說明 |
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 |
1.2.2 list iterator的使用
此處,大家可暫時將迭代器理解成一個指針,該指針指向list中的某個節點
函數聲明 | 接口說明 |
begin + end | 返回第一個元素的迭代器+返回最后一個元素下一個位置的迭代器 |
rbegin + rend | 返回第一個元素的reverse_iterator,即end位置,返回最后一個元素下一個位置的 reverse_iterator,即begin位置 |
【注意】
1. begin與end為正向迭代器,對迭代器執行++操作,迭代器向后移動
2. rbegin(end)與rend(begin)為反向迭代器,對迭代器執行++操作,迭代器向前移動?
1.2.3 list capacity?
函數聲明 | 接口說明 |
empty | 檢測list是否為空,是返回true,否則返回false |
size | 返回list中有效節點的個數 |
1.2.4 list element access?
函數聲明 | 接口說明 |
front | 返回list的第一個節點中值的引用 |
back | 返回list的最后一個節點中值的引用 |
?1.2.5 list modifiers
函數聲明 | 接口說明 |
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中的有效元素 |
list中還有一些操作,需要用到時大家可參閱list的文檔說明。
1.2.6 list的迭代器失效?
前面說過,此處大家可將迭代器暫時理解成類似于指針
迭代器失效即迭代器所指向的節點的無效,即該節點被刪除了。
因為list的底層結構為帶頭結點的雙向循環鏈表,因此在list中進行插入時是不會導致list的迭代器失效的,只有在刪除時才會失效,并且失效的只是指向被刪除節點的迭代器,其他迭代器不會受到影響。
void TestListIterator1(){int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase()函數執行后,it所指向的節點已被刪除,因此it無效,在下一次使用it時,必須先給
其賦值l.erase(it); ++it;}}// 改正
void TestListIterator(){int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){l.erase(it++); }}
?1.2.7?List中sort的效率測試
我們用一段代碼來測試一下list中sort的性能
#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
using namespace std;
void test_op()
{srand((unsigned int)time(NULL));const int N = 1000000;vector<int> v;v.reserve(N);list<int> lt1;list<int> lt2;for (int i = 0; i < N; ++i){int e = rand();lt1.push_back(e);lt2.push_back(e);}// 拷貝到vector排序,排完以后再拷貝回來int begin1 = clock();for (auto e : lt1){v.push_back(e);}sort(v.begin(), v.end());size_t i = 0;for (auto& e : lt1){e = v[i++];}int end1 = clock();//list調用自己的sortint begin2 = clock();lt2.sort();int end2 = clock();printf("vector sort:%d\n", end1 - begin1);printf("list sort:%d\n", end2 - begin2);
}
?
會發現哪怕我先拷貝到vector排完再拷貝回去效率都比list的sort效率高,所以list的sort實際中意義不是很大!!?
二、list的模擬實現
?2.1 正向迭代器的實現
2.1.1 正向迭代器的封裝?
我們在學習vector的時候,發現vector的迭代器就是一個原生指針T*,這得益于vector的空間的連續性
那我們還能像vector一樣用原生指針去修飾迭代器嗎?
不能,鏈表空間上是不連續的,那我們對一個節點指針進行加減,就很難說能不能找到下一個節點,更多的是找不到的情況
我們在數據結構中訪問下一個節點,往往是利用當前節點存的下一節點的地址來進行訪問的
所以我們可以將迭代器單獨封裝成一個類去管理節點
template<class T, class Ref, class Ptr> //Ref == T& Ptr == T*
struct ListIterator //這里使用struct是因為我們要多次訪問成員,也可使用class+public
{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(PNode pNode = nullptr) :_pNode(pNode){}ListIterator(const Self& l) :_pNode(l._pNode){}T& operator*() //解引用{return _pNode->_val;}T* operator->() {return &_pNode->_val;}Self& operator++() //前置++{_pNode = _pNode->_pNext;return *this;}Self operator++(int) //后置++{Self temp(*this);_pNode = _pNode->_pNext;return temp;}Self& operator--() //前置--{_pNode = _pNode->_pPre;return *this;}Self& operator--(int) //后置--{Self temp(*this);_pNode = _pNode->_pPre;return temp;}bool operator!=(const Self& l) //不相等判斷{return _pNode != l._pNode;}bool operator==(const Self& l) //相等判斷{return !(*this != l);}PNode _pNode;
};
?T* operator->()? 大家可能有疑惑
?當我們的節點里存的是內置類型或者STL容器是,庫里面的<<重載了這些來讀取數據
但如果是我們自己寫的類型(如class CH),<<并不能讀取該類型的數據
因此我們要取來節點自定義類的指針,來訪問該類的內部的數據(因為最底層都是內置類型
?2.1.2 迭代器的使用
template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;public:iterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin()const{return const_iterator(_pHead->_pNext);}const_iterator end()const{return const_iterator(_pHead);}
private:PNode _pHead;
}
這邊我們用到了匿名對象。?
這里的const迭代器為什么不能直接用const修飾普通迭代器??
?因為typedef碰到const的話,就不是簡單的字符串替換? 實際上你以為的const T*?,在這里變成了T*const ,因為迭代器我們是希望他可以進行++和--的,而我們只是不希望他指向的內容給改變,所以我們的const要修飾的是指針的內容,而不是修飾指針。
2.2?list相關的成員函數
?2.2.1?構造函數
2.2.1.1 默認構造函數
list()
{_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;
}
因為無論如何都要有哨兵節點(方便我們不用判空,所以我們直接封裝一個?
void CreateHead()
{_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;
}
?2.2.1.2 有參構造函數
list(int n, const T& value = T()){CreateHead();for (int i = 0; i < n; ++i)push_back(value);}
?2.2.1.3 迭代器區間構造函數?
template <class Iterator>
list(Iterator first, Iterator last)
{CreateHead();while (first != last){push_back(*first);++first;}
}
2.2.1.4 拷貝構造?
1.傳統?
//拷貝構造函數傳統寫法
list(const list<T>& l)
{CreateHead();for (auto e : l)push_back(e);
}
2.現代?
void swap(list<T>& l)
{std::swap(_pHead, l._pHead);
}list(const list<T>& l)
{CreateHead();// 用l中的元素構造臨時的temp,然后與當前對象交換list<T> temp(l.begin(), l.end());swap(temp);
}
2.2.2 析構函數和clear
2.2.2.1 析構函數
~list()
{clear();delete _pHead;_pHead = nullptr;
}
2.2.2.2 clear()
void clear()
{iterator p = begin();while (p != end()){p = erase(p);}_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;
}
?2.2.3 賦值重載
list<T>& operator=(const list<T> l)
{swap(l);return *this;
}
2.2.4?修改相關函數(Modifiers)
2.2.4.1?empty、size
size_t size()const
{size_t size = 0;ListNode* p = _pHead->_pNext;while (p != _pHead){size++;p = p->_pNext;}return size;
}
bool empty()const
{return size() == 0;
}
?2.2.4.2 insert
// 在pos位置前插入值為val的節點
iterator insert(iterator pos, const T& val)
{PNode newnode = new Node(val);PNode cur = pos._pNode;PNode prev = cur->_pPre;newnode->_pPre = prev;newnode->_pNext = cur;prev->_pNext = newnode;cur->_pPre = newnode;return iterator(newnode);
}
2.2.4.3 erase?
// 刪除pos位置的節點,返回該節點的下一個位置
iterator erase(iterator pos)
{assert(pos != end());PNode prev = pos._pNode->_pPre;PNode next = pos._pNode->_pNext;prev->_pNext = next;next->_pPre = prev;delete pos._pNode;return iterator(next);//利用匿名對象返回
}
2.2.4.4 尾插尾刪頭插頭刪?
// List Modifyvoid push_back(const T& val){insert(end(), val);}void pop_back(){erase(--end());}void push_front(const T& val){insert(begin(), val);}void pop_front(){erase(begin());}
2.3 反向迭代器的實現?
?sgi版本下的反向迭代器,其實就是將構建一個反向迭代器的類將正向迭代器封裝起來,這個時候正向迭代器的++就是反向迭代器的--
namespace bit
{// 適配器 -- 復用template<class Iterator, class Ref, class Ptr> //Ref == T& Ptr == T*struct Reverse_iterator{typedef Reverse_iterator<Iterator, Ref, Ptr> Self;Reverse_iterator(Iterator it):_it(it){}Ref operator*(){Iterator temp = _it;--temp;return *temp;}Self& operator++(){--_it;return *this;}Self operator++(int){Iterator tmp = _it;--_it;return tmp;}Self& operator--(){++_it;return *this;}Self operator--(int){iterator temp = _it;++_it;return temp;}bool operator!=(const Self& s){return _it != s._it;}bool operator==(const Self& s){return _it == s._it;}Iterator _it;};
}
?為什么解引用的是前一個位置的元素???
?
?from?C++:List的使用和模擬實現-CSDN博客?圖來源
?
三 list模擬實現的全部代碼
#pragma once
namespace ch
{// List的節點類template<class T>struct ListNode{ListNode(const T& val = T()) :_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};//List的迭代器類template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(PNode pNode = nullptr) :_pNode(pNode){}ListIterator(const Self& l) :_pNode(l._pNode){}T& operator*(){return _pNode->_val;}T* operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self temp(*this);_pNode = _pNode->_pNext;return temp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self temp(*this);_pNode = _pNode->_pPre;return temp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return !(*this != l);}PNode _pNode;};template<class Iterator, class Ref, class Ptr> //Ref == T& Ptr == T*struct Reverse_iterator{typedef Reverse_iterator<Iterator, Ref, Ptr> Self;Reverse_iterator(Iterator it):_it(it){}Ref operator*(){Iterator temp = _it;--temp;return *temp;}Self& operator++(){--_it;return *this;}Self operator++(int){Iterator tmp = _it;--_it;return tmp;}Self& operator--(){++_it;return *this;}Self operator--(int){iterator temp = _it;++_it;return temp;}bool operator!=(const Self& s){return _it != s._it;}bool operator==(const Self& s){return _it == s._it;}Iterator _it;};//list類template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public://正向迭代器typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;//反向迭代器typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;typedef Reverse_iterator<iterator, const T&, const T*> const_reverse_iterator;public:///// List的構造list(){CreateHead();}list(int n, const T& value = T()){CreateHead();for (int i = 0; i < n; ++i)push_back(value);}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*first);++first;}}list(const list<T>& l){CreateHead();// 用l中的元素構造臨時的temp,然后與當前對象交換list<T> temp(l.begin(), l.end());swap(temp);}list<T>& operator=(const list<T> l){swap(l);return *this;}~list(){clear();delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin()const{return const_iterator(_pHead->_pNext);}const_iterator end()const{return const_iterator(_pHead);}//反向迭代器(可讀可寫)reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}//反向迭代器(可讀不可寫)const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}///// List Capacitysize_t size()const{size_t size = 0;ListNode* p = _pHead->_pNext;while (p != _pHead){size++;p = p->_pNext;}return size;}bool empty()const{return size() == 0;}// List AccessT& front(){assert(!empty());return _pHead->_pNext->_val;}const T& front()const{assert(!empty());return _pHead->_pNext->_val;}T& back(){assert(!empty());return _pHead->_pPre->_val;}const T& back()const{assert(!empty());return _pHead->_pPre->_val;}// List Modifyvoid push_back(const T& val){insert(end(), val);}void pop_back(){erase(--end());}void push_front(const T& val){insert(begin(), val);}void pop_front(){erase(begin());}// 在pos位置前插入值為val的節點iterator insert(iterator pos, const T& val){PNode newnode = new Node(val);PNode cur = pos._pNode;PNode prev = cur->_pPre;newnode->_pPre = prev;newnode->_pNext = cur;prev->_pNext = newnode;cur->_pPre = newnode;return iterator(newnode);}// 刪除pos位置的節點,返回該節點的下一個位置iterator erase(iterator pos){assert(pos != end());PNode prev = pos._pNode->_pPre;PNode next = pos._pNode->_pNext;prev->_pNext = next;next->_pPre = prev;delete pos._pNode;return iterator(next);//利用匿名對象返回}void clear(){iterator p = begin();while (p != end()){p = erase(p);}_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}void swap(list<T>& l){std::swap(_pHead, l._pHead);}private:void CreateHead(){_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}PNode _pHead;};
}
總結
???各位讀友,本篇分享到內容是否更好的讓你理解了C++的list,如果對你有幫助給個👍贊鼓勵一下吧!!
🎉🎉🎉世上沒有絕望的處境,只有對處境絕望的人。
感謝每一位一起走到這的伙伴,我們可以一起交流進步!!!一起加油吧!!。