CPP學習之list使用及模擬實現

一、list簡介及用法

1. list簡介
list是可以在常數范圍內任意位置進行插入、刪除、修改操作的有順序性的容器,而且支持雙向迭代,其底層是雙鏈表結構,邏輯上連續但物理空間上不連續,只能通過指針來進行元素訪問,無法使用下標隨機訪問。在任意位置的增刪查改的實現上,list效率比vector高;在快排中,因為要取中的原因,vector支持下標隨機訪問,而list只能從開頭或者尾部逐步迭代到對應位置,而且還需要額外的空間開銷來存儲節點的信息,這使得list的排序效率比vector要低。
在這里插入圖片描述
2. 重要接口及使用
構造函數:
在這里插入圖片描述

迭代器iterator
在這里插入圖片描述

capacity
在這里插入圖片描述

元素訪問
在這里插入圖片描述

增刪查改
在這里插入圖片描述

注意:在vector中進行增刪操作會面臨迭代器失效的風險,而在list中增加元素操作不會造成迭代器失效問題,只有刪除操作會影響指向被刪除元素的迭代器,并且其它的迭代器不會被影響。我們可以通過接收erase函數返回值的辦法使迭代器仍然有效。

二、模擬實現

list的模擬實現有幾個要點:

  1. 需要模擬出雙鏈表的節點,所以我們需要構建節點結構體,這個結構體要包含節點的元素,指向前一個和后一個節點的該結構體的指針;
  2. 我們需要構建list的迭代器模板,這個迭代器要有非const及const類型,所以我們要用到模板,模板要有元素類型自定義,元素的const和非const的引用和指針,下面的代碼中可以看到T代表元素類型,Ref代表元素引用,Ptr代表指向元素的指針;
  3. 開始構建list類,其成員變量要包含哨兵位節點和代表鏈表內元素個數的size,成員函數則是實現list功能的重要接口。

list的模擬實現如下:

#pragma once
#include<iostream>
#include<list>
#include<vector>
#include<algorithm>
#include<assert.h>
#include"my_reverse_iterator.h"using namespace std;// 注意:因為物理上結構的差異,list的迭代器與vector的不一樣,需要封裝成一個結構體并且重載引用等運算符
// 通過迭代器封裝來改變迭代器的行為
// 單參數類型構造函數支持隱式類型轉換
// 迭代器結構體內部不能處理節點的創建和釋放template<class T>
struct _list_node
{typedef struct _list_node<T> list_node;list_node* _prev;list_node* _next;T _val;_list_node(const T& val = T()):_prev(nullptr),_next(nullptr),_val(val){}};template<class T, class Ref, class Ptr > //T代表數據類型,Ref用于區分const與非const迭代器,Ptr用于箭頭運算符重載返回_val
struct _list_iterator
{typedef _list_node<T> node;typedef _list_iterator<T, Ref, Ptr> self; //self代表iterator自身node* _node;_list_iterator(node* new_node):_node(new_node){}Ref operator*(){return _node->_val;}bool operator==(const self& it){return (_node == it._node);}bool operator!=(const self& it) const{return (_node != it._node);}self operator++(int) //這里不能加引用,因為返回的是temp,{self temp(*this); //采用了拷貝構造函數_node = _node->_next;return temp;}self& operator++(){_node = _node->_next;return *this;}self operator--(int) //這里不能加引用,因為返回的是temp,{self temp(*this); //采用了拷貝構造函數_node = _node->_prev;return temp;}self& operator--(){_node = _node->_prev;return *this;}Ptr operator->() //注意:這里經過特殊處理(C++標準)it->_val即可獲得值,不用it->->_val//(正常情況下返回指針得再經過解引用才能得到值){return &_node->_val; //返回地址}};template<class T>
class my_list
{
public:typedef _list_node<T> node;typedef _list_iterator<T, T&, T*> iterator;typedef _list_iterator<T, const T&, T*> const_iterator;typedef my_reverse_iterator<iterator, T&, T*> reverse_iterator;typedef my_reverse_iterator<const_iterator, T&, T*> const_reverse_iterator;private:node* _head;size_t _size;public:void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;_size = 0;}my_list(){empty_init();}void clear() //刪除鏈表除哨兵位外所有元素{iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}~my_list(){clear();delete _head;_head = nullptr;}my_list(const my_list<T>& list) //形參必須是引用才是拷貝構造函數{empty_init();for (auto& e : list) //節省棧幀空間{push_back(e);}}void swap(my_list<T>& list){std::swap(_head, list._head);std::swap(_size, list._size);}my_list<T>& operator=(my_list<T> list){swap(list);return *this;}iterator insert(iterator pos, const T& val) //默認在pos位置前插入數據{node* new_node = new node(val);node* prev = (pos._node)->_prev;new_node->_next = pos._node;new_node->_prev = prev;(pos._node)->_prev = new_node;prev->_next = new_node;_size++;return --pos; //這里return new_node; 也可以}void push_back(const T& val){//node* new_node = new node(val); //使用了默認構造函數(系統生成,無參,全缺省)//new_node->_next = _head; //使用了賦值運算符重載//new_node->_prev = _head->_prev;//_head->_prev->_next = new_node;//_head->_prev = new_node;insert(end(), val);}void push_front(const T& val){insert(begin(), val);}iterator erase(iterator pos) //返回刪除元素的后一個元素的位置{assert(pos != end()); //注意:這里要檢查鏈表內是否只有哨兵位,不然會出問題!!!node* next = (pos._node)->_next;node* prev = (pos._node)->_prev;next->_prev = prev;prev->_next = next;//iterator temp = ++pos; //注意:pos也改變了!!!delete pos._node;pos = nullptr;_size--;return next; //函數返回值是iterator類型,為什么這里可以返回node*類型?//因為這里隱式調用了iterator的構造函數,即iterator(next)!!!//如果不希望被隱式類型轉換,那么就在構造函數前用explicit修飾//return temp;}void pop_back(){iterator temp = --end();erase(temp);}void pop_front(){erase(begin());}size_t size(){return _size;}iterator begin(){//return _head->_next; //注意,返回值的類型是iterator,用node*返回是錯誤的!return iterator(_head->_next); //采用迭代器的構造函數來返回}iterator end(){return iterator(_head);}const_iterator begin() const{//return _head->_next; //注意,返回值的類型是iterator,用node*返回是錯誤的!return const_iterator(_head->_next); //采用迭代器的構造函數來返回}const_iterator end() const{return const_iterator(_head);}reverse_iterator rbegin(){return end();}const_reverse_iterator rbegin() const{return end();}reverse_iterator rend(){return begin();}const_reverse_iterator rend() const{return begin();}void print(){for (auto e : *this){cout << e << " ";}cout << endl;}
};

我們知道,list支持反向迭代,所以我們也需要模擬實現反向迭代器,以下是實現反向迭代器的一些要點:

  1. 構建反向迭代器只需復用正向迭代器即可,正向迭代器自加那么反向迭代器就自減;
  2. 在設計中,反向迭代器是正向迭代器的鏡像,rbegin指向end,rend指向begin,所以在反向迭代器解引用操作符重載中,需要讓指針自減再解引用;
  3. 同樣地,反向迭代器模擬實現也需要用到模板,模板要實現可以自定義是哪種迭代器(vector or list?),const還是非const類型。類的成員變量僅有iterator _it。

如下是反向迭代器的模擬實現:

#pragma once
#include"my_list.h"template<class iterator, class ref, class ptr> //用普通迭代器來做反向迭代器,ref為類型引用,ptr是指針
class my_reverse_iterator
{
public:iterator _it;typedef my_reverse_iterator<iterator, ref, ptr> self;my_reverse_iterator(iterator it):_it(it){}ref operator*(){self temp = *this;return *(--temp._it);}self& operator++() //前置++{_it--;return *this;}self operator++(int) //后置++{iterator temp = _it;_it--;return temp;}self& operator--(){_it++;return *this;}self operator--(int){iterator temp = _it;_it++;return temp;}ptr operator->(){return &(operator*()); //直接返回解引用后內容的地址即可}bool operator!=(const self& it) const{return (_it != it._it);}
};

三、與vector對比

這里是引用

四、小結

本文一開始簡要介紹了list的定義和重要接口,接著進行list的模擬實現,在模擬實現內容中列出了實現的數個要點,這些要點是關于list和反向迭代器的構建思路的,最后則是總結了list和vector的不同點,使讀者可以根據需求來選擇容器。
如有錯誤,請批評指正,謝謝。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91871.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91871.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91871.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Boot 參數校驗:@Valid 與 @Validated

在日常開發中&#xff0c;參數校驗是保障接口健壯性與數據安全的第一道防線。Spring Boot 為我們提供了基于 JSR-303/JSR-380 的強大校驗機制&#xff0c;通過注解與 AOP 實現了靈活且高效的數據校驗方式。本篇博客將詳細介紹 Spring Boot 中 Valid、Validated 注解的使用方法&…

linux看門狗重啟定位思路總結

1&#xff0c;看門狗定位思路&#xff08;1&#xff09;是否是死鎖導致查看日志查看是否有RCU install或者deadlock相關打印&#xff0c;如果有的話可以考慮使用lockdep死鎖檢測工具&#xff08;2&#xff09;中斷風暴查看中斷&#xff0c;抓中斷打印&#xff0c;可以查看/proc…

基于單片機直流電機測速中文液晶顯示設計

摘 要 在現在工業自動化高度發展的時期&#xff0c;幾乎所有的工業設備都離不開旋轉設備&#xff0c;形形色色的電機在不同領域發揮著很重要的作用。不同場合對電機控制要求是不同的&#xff0c;但大部分都會涉及到旋轉設備的轉速測量&#xff0c;從而利用轉速來實施對旋轉設備…

c# sqlsugar 主子表明細 查詢

在使用 SqlSugar ORM 進行數據庫操作時&#xff0c;特別是在處理主子表關系時&#xff0c;通常需要執行關聯查詢來獲取主表和其子表的數據。SqlSugar 提供了強大的查詢能力&#xff0c;支持多種方式的關聯查詢&#xff0c;包括左連接&#xff08;Left Join&#xff09;、內連接…

研華PCI-1285/1285E 系列------(一概述)

PCI-1285/1285E 系列是基于 DSP 的 SoftMotion PCI 總線控制器卡,專為各種電機自動 化和其它機器自動化的廣泛應用設計。板卡配有高性能 DSP,其中包括 SoftMotion算法,能夠實現運動軌跡和時間控制,以滿足精確運動中的同步應用需求。 研華 SoftMotion 支持以下特性:龍門…

二代身份證識別技術的發展:從機器學習到深度學習

一、技術發展歷程1. 傳統機器學習時代&#xff08;2000-2012&#xff09;特征工程方法&#xff1a;主要依賴手工設計的特征&#xff08;HOG、SIFT、LBP等&#xff09;分類器技術&#xff1a;支持向量機(SVM)、隨機森林、AdaBoost等OCR技術&#xff1a;基于模板匹配和連通區域分…

云服務器如何設置防火墻和安全組規則?

一、安全組&#xff08;Security Group&#xff09;設置安全組是云平臺提供的虛擬防火墻&#xff0c;用于控制 入站&#xff08;Ingress&#xff09;和出站&#xff08;Egress&#xff09;流量。1. 基本安全組規則&#xff08;推薦&#xff09;協議端口源IP用途是否必需TCP22你…

排序【各種題型+對應LeetCode習題練習】

目錄 常用排序 快速排序 LeetCode 912 排序數組 歸并排序 LeetCode 912 排序數組 常用排序 名稱排序方式時間復雜度是否穩定快速排序分治O(n log n)否歸并排序分治O(n log n)是冒泡排序交換O(n)是插入排序插入O(n)是選擇排序選擇最值O(n)否C STL sort快排內省排序O(n log…

鴻蒙與web混合開發雙向通信

鴻蒙與web混合開發雙向通信用runJavaScript和registerJavaScriptProxy web entry/src/main/resources/rawfile/1.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content&q…

unity Physics.RaycastNonAlloc

Physics.RaycastNonAlloc 是 Unity 中用于 3D 物理射線檢測的高性能方法&#xff0c;它是 Physics.Raycast 的非分配版本。 方法簽名 public static int RaycastNonAlloc(Ray ray, RaycastHit[] results, float maxDistance Mathf.Infinity, int layerMask DefaultRaycastLay…

數據庫(five day finally)——物物而不物于物,念念而不念于念。(數據庫到此結束!祝世間美好與各位不期而遇,善意常伴汝身!)

1.子查詢&#xff08;1&#xff09;where 子查詢①多行單列配合in和not in操作&#xff08;類似于數據范圍查詢&#xff09;例&#xff1a;顯示工資與各個經理相同的雇員信息&#xff08;包含經理本身&#xff09;。select * from empwhere sal(select sal from emp where jobM…

【甲烷數據集】Sentinel-5P 衛星獲取的全球甲烷數據集-TROPOMI L2 CH?

目錄 數據概述 傳感器 & 衛星信息 監測目標:甲烷(CH?) 數據產品內容 空間與時間覆蓋 云篩選與協同觀測 技術文檔資源 數據下載 Python 代碼繪制 CH4 數據 參考 數據概述 Sentinel-5 Precursor Level 2 Methane (TROPOMI L2 CH?) 數據集是由歐洲哥白尼計劃的 Sentinel…

【數據結構】單鏈表練習(有環)

1.判斷是否是環形鏈表 141. 環形鏈表 - 力扣&#xff08;LeetCode&#xff09; bool hasCycle(struct ListNode *head) {struct ListNode *fast,*slow;fastslowhead;while(fast&&fast->next){fastfast->next->next;slowslow->next;if(fastslow)return tr…

VR 污水廠初體驗:顛覆傳統認知?

第一次戴上 VR 設備走進 VR 污水廠時&#xff0c;那種震撼的感覺至今難以忘懷。仿佛一瞬間&#xff0c;我被傳送到了一個全新的世界&#xff0c;平日里只能在圖紙或實地看到的污水廠&#xff0c;此刻就立體地呈現在眼前。腳下是縱橫交錯的管道&#xff0c;頭頂巨大的處理設備有…

父類 div 自適應高度 子類如何撐滿其高度

使用絕對定位 如果你想要子元素完全撐滿父元素的高度&#xff0c;可以使用絕對定位。這種方法適用于當子元素需要完全覆蓋父元素時。<div class"parent"><div class"child"><!-- 子類內容 --></div> </div>.parent {positio…

從0開始學習R語言--Day51--PH檢驗

在用cox回歸做分析時&#xff0c;我們一般會得出各種變量在結局的風險影響&#xff08;HR大于1&#xff0c;就代表變量值增大&#xff0c;對應結局影響的風險就隨之增大&#xff09;&#xff0c;但是這里有個壞處是&#xff0c;cox回歸得到的是瞬時風險值&#xff0c;我們最多得…

Docker 網絡原理

Linux 常見網絡虛擬化 虛擬網卡:tun/tap虛擬網卡&#xff08;又稱虛擬網絡適配器&#xff09;&#xff0c;即用軟件模擬網絡環境&#xff0c;模擬網絡適配器。在計算機網絡中&#xff0c;tun 與 tap 是操作系統內核中的虛擬網絡設備。不同于普通靠硬件網絡適配器實現的設備&…

【通識】PCB文件

1. PCB文件的導入 在PORTEL99 PCB編輯器的文件菜單中選擇導入先前繪制的CAD文件。導入成功后&#xff0c;編輯器將顯示出元件封裝的基本圖形&#xff0c;為后續操作奠定基礎。將需要抄板的PCB放置于掃描儀中隨后啟動掃描儀&#xff0c;之后啟動AUTO CAD軟件&#xff0c;之后插入…

分布式彈性故障處理框架——Polly(1)

1 前言之服務雪崩 在我們實施微服務之后&#xff0c;服務間的調用變得異常頻繁&#xff0c;多個服務之前可能存在互相依賴的關系&#xff0c;當某個服務出現故障或者是因為服務間的網絡出現故障&#xff0c;導致服務調用的失敗&#xff0c;進而影響到某個業務服務處理失敗&…

【機器學習深度學習】大模型推理速度與私有化部署的價值分析

目錄 前言 一、主流推理框架速度對比 二、為什么 HuggingFace 框架更適合微調驗證&#xff1f; 三、大模型私有化部署的必要性分析 ? 私有化部署的主要動因 1. 數據隱私與業務安全 2. 可控性與性能保障 ? 哪些情況不建議私有部署&#xff1f; 四、總結與選型建議 &…