C++:STL中的棧和隊列的適配器deque

學習完string類、容器vector和容器list,再去學習其他容器的學習成本就非常低,容器的使用方法都大差不差,而棧和隊列的底層使用了適配器,去模擬實現就沒有那么麻煩,適配器也是一種容器,但是這種容器兼備棧和隊列需要的特性,通過觀察棧和隊列的碼源更能體會到適配器的方便和厲害之處。雖然stack和queue中也可以存放元素,但在STL中并沒有將其劃分在容器的行列,而是將其稱為容器適配 器,這是因為stack和隊列只是對其他容器的接口進行了包裝,STL中stack和queue默認使用deque。

目錄

雙端隊列:deque

deque的優缺點

棧和隊列的模擬實現

?優先級隊列的模擬實現

priority_queue初始化

向下調整和向上調整算法

priority_queue插入和刪除

?priority_queue其它接口

對比總結


雙端隊列:deque

如果熟悉一個容器的使用對于其他容器也能很快上手,所以小編在這里就不講棧和隊列的使用了,在還未學習c++之前,小編學習了數據結構雖然是C語言版的,也是初步了解了棧和隊列。在C語言中棧和隊列的實現,棧的特點是先進后出,是用數組來實現的,棧需要pop和top來刪除數據和去數據但是在尾部,所以用一個數組就能很好的解決。隊列的特點是先進先出,需要頻繁的頭刪,如果使用數組來實現效率不高,數組頭刪的時間復雜度為O(N),所以鏈表就是一個很好的選擇。在c++中究竟是什么樣的容器既能符合棧的特征,又能符合隊列的特征,讓我們一起往下瞧瞧吧!

在棧和隊列中哪里可以用到deque呢?deque在模版中,在棧和隊列顯示實例化中默認的適配器是deque,當然也可以手動傳vector或者list。

?雙端隊列原理:deque(雙端隊列):是一種雙開口的"連續"空間的數據結構,雙開口的含義是:可以在頭尾兩端進行插入和刪除操作,且時間復雜度為O(1),與vector比較,頭插效率高,不需要搬移元素;與list比較,空間利用率比較高。

但是雙端隊列并不是這樣連續的空間,而是像二維數組一樣,一個指針數組,每個指針都對應著一塊空間。

?從上面的圖中可以看出迭代器中node是當前結點,cur指向當前數組位置,用來迭代器訪問元素,first和last分別指向每個節點對應的數組的起始位置和末尾。當cur等于last,node就要指向下一個節點,cur又在起始位置了,雖然這些空間不連續,但是可以重載一些函數來定義其行為以及封裝看似空間時連續的。

deque的優缺點

優點:與vector比較,deque的優勢是:頭部插入和刪除時,不需要搬移元素,效率特別高,而且在擴容時,也不需要搬移大量的元素,因此其效率是必vector高的。

與list比較,其底層是連續空間,空間利用率比較高,不需要存儲額外字段。

缺點:deque有一個致命缺陷:不適合遍歷,因為在遍歷時,deque的迭代器要頻繁的去檢測其是否移動到某段小空間的邊界,導致效率低下,而序列式場景中,可能需要經常遍歷,因此在實際中,需要線性結構

時,大多數情況下優先考慮vector和list,deque的應用并不多,而目前能看到的一個應用就是,STL用其作為stack和queue的底層數據結構。

那么為什么要用deque來作為stack和Queue的默認底層容器呢??

首先stack是一種后進先出的特殊線性數據結構,因此只要具有push_back()和pop_back()操作的線性結構,都可以作為stack的底層容器,比如vector和list都可以;queue是先進先出的特殊線性數據結構,只要具有 push_back和pop_front操作的線性結構,都可以作為queue的底層容器,比如list。

但是STL中對stack和 queue默認選擇deque作為其底層容器,主要是因為: 1. stack和queue不需要遍歷(因此stack和queue沒有迭代器),只需要在固定的一端或者兩端進行操作。 2. 在stack中元素增長時,deque比vector的效率高(擴容時不需要搬移大量數據);queue中的元素增長 時,deque不僅效率高,而且內存使用率高。 結合了deque的優點,而完美的避開了其缺陷。

棧和隊列的模擬實現

在學習C語言階段,模擬實現了棧和隊列,需要的代碼量需要幾百行,自己造輪子還是比較麻煩的,而在C++中有現成的不需要我們去造輪子,模擬實現棧和隊列就輕松多了,deque作為默認的底層容器。

stack代碼示例:

template<class T, class Con = deque<T>>class stack{public:void push(const T& x){_c.push_back(x);}void pop(){_c.pop_back();}T& top(){return _c.back();}const T& top()const{return _c.back();}size_t size()const{return _c.size();}bool empty()const{return _c.empty();}private:Con _c;};

Queue代碼示例:

template<class T, class Con = deque<T>>class queue{public:void push(const T& x){_c.push_back(x);}void pop(){_c.pop_front();}T& back(){return _c.pop_back();}const T& back()const{return _c.pop_back();}T& front(){return _c.front();}const T& front()const{return _c.pop_front();}size_t size()const{return _c.size();}bool empty()const{return _c.empty();}private:Con _c;};

?優先級隊列的模擬實現

優先級隊列的底層結構是堆,還是又vector作為容器來實現,而模擬實現優先級隊列就需要建堆,在STL中優先級隊列默認是大堆,所以在模擬實現中我們也是大堆。在模擬實現的過程中可以發現自己的對知識的疏漏,在建堆和什么時候使用向下調整和向上調整出現了問題,也導致了花費有些時間去復習以前的知識。優先級隊列畢竟是隊列,還有先進先出的特性,在隊列插入元素實在末尾插入,需要根據大小堆來進行向上調整,刪除頭元素如果直接進行刪除就會大大降低效率,可以先和末尾元素交換,接著刪除末尾元素,在從根結點開始向下調整,使堆保持大堆或者小堆。

在實現之前需要三個模版參數:

 template <class T, class Container = vector<T>, class Compare = less<T> >

?T是數據的類型,Container是適配器,也就是優先級隊列的底層容器,默認容器是vector,compare是仿函數,來控制大堆或者小堆也就是數據的排升降序。注意:這里的less用的是庫里面的

變量:

private:Container c;Compare comp;

priority_queue初始化

 priority_queue(){}template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){c.push_back(*first);++first;}for (int i = (size()-2)/2; i >= 0; i--){Adupdown(i);}}

構造函數對內置類型不做處理,對于自定義類型會調用自己的構造函數,所以默認構造不需要寫,而用迭代器初始化需要建堆,堆的特性使得它能在O(1)時間內獲取最高(或最低)優先級的元素,并在O(log n)時間內完成插入和刪除操作,這與優先級隊列的需求高度契合。堆的結構保證了根節點始終是最大(或最小)元素,每次插入或刪除后,堆會通過“上浮”或“下沉”操作重新調整結構,確保優先級順序始終正確。?

向下調整和向上調整算法

void Adupdown(int parent)//向下調整{int child = parent * 2 + 1;while (child < c.size()){if (child + 1 < c.size() && comp(c[child],c[child+1])){child++;}if (comp(c[parent], c[child])){swap(c[parent], c[child]);parent = child;child = parent * 2 + 1;}else{break;}}}
 void Adjustup(int child)//向上調整{int parent = (child - 1) / 2;while (child > 0){if (comp(c[parent] , c[child])){swap(c[parent], c[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}

向下調整通常用于刪除堆頂元素或構建堆時,向下調整從根節點開始。向上調整通常用于插入新元素時。將新元素添加到堆的末尾,然后通過向上調整來恢復堆的性質,向上調整從新插入的節點開始。?

priority_queue插入和刪除

每次插入一個元素就需要向上調整來保持大堆或者小堆,而刪除元素也是方便,首位元素進行交換,再刪除末尾元素,比直接刪除頭元素效率更高。

代碼示例:

void push(const T& x){c.push_back(x);Adjustup(size() - 1);}void pop(){swap(c[0], c[c.size() - 1]);c.pop_back();Adupdown(0);}

?priority_queue其它接口

 bool empty() const{return c.empty();}size_t size() const{return c.size();}const T& top() const{return c[0];}

對比總結

特性雙端隊列優先級隊列
操作位置頭部和尾部均支持操作僅支持從優先級最高端操作
底層實現數組或鏈表通常為堆(Heap)
時間復雜度插入/刪除:O(1)(均攤)插入/刪除:O(log n)
典型用途雙向數據處理、滑動窗口動態優先級任務處理

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

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

相關文章

9類主流數據庫 - 幫你更好地進行數據庫選型!

作者&#xff1a;唐叔在學習 專欄&#xff1a;數據庫學習 標簽&#xff1a;數據庫選型、MySQL、Redis、MongoDB、大數據存儲、NoSQL、數據庫優化、數據架構、AI數據庫 大家好&#xff0c;我是你們的老朋友唐叔&#xff01;今天咱們來聊聊程序員吃飯的家伙之一 —— 數據庫。在這…

推送本地項目到Gitee遠程倉庫

文章目錄前言前面已加學習了下載gitee軟件&#xff0c;網址在上一篇文章。在gitee創建賬號與倉庫。現在來學習如何講本地項目推送到Gitee遠程倉庫一、流程總結前言 前面已加學習了下載gitee軟件&#xff0c;網址在上一篇文章。在gitee創建賬號與倉庫。現在來學習如何講本地項目…

CMake 命令行參數完全指南(5)

?**40. --version**? ?解釋?&#xff1a;顯示CMake版本 ?示例?&#xff1a; cmake --version # 輸出&#xff1a;cmake version 3.25.2?**41. --warn-uninitialized**? ?解釋?&#xff1a;警告未初始化的變量 ?適用場景?&#xff1a;檢測腳本錯誤 ?示例?&#xf…

基于Python實現生產者—消費者分布式消息隊列:構建高可用異步通信系統

深入剖析分布式消息隊列的核心原理與Python實現&#xff0c;附完整架構設計和代碼實現引言&#xff1a;分布式系統的通信基石在微服務架構和云原生應用普及的今天&#xff0c;服務間的異步通信成為系統設計的核心挑戰。當單體應用拆分為數十個微服務后&#xff0c;服務間通信呈…

【大模型核心技術】Agent 理論與實戰

一、基本概念 LLM 特性&#xff1a;擅長理解和生成文本&#xff0c;但采用 “一次性” 響應模式&#xff0c;本質上是無記憶的生成模型。Agent 本質&#xff1a;包含 LLM 的系統應用&#xff0c;具備自主規劃、工具調用和環境反饋能力&#xff0c;是將 LLM 從 “聊天機器人” 升…

Maven - 依賴的生命周期詳解

作者&#xff1a;唐叔在學習 專欄&#xff1a;唐叔的Java實踐 標簽&#xff1a;Maven依賴管理、Java項目構建、依賴傳遞性、Spring Boot依賴、Maven最佳實踐、項目構建工具、依賴沖突解決、POM文件詳解 文章目錄一、開篇二、Maven依賴生命周期2.1 依賴聲明階段&#xff1a;POM文…

從零打造大語言模型--處理文本數據

從零打造大語言模型 第 1 章&#xff1a;處理文本數據 章節導讀 在把文本投喂進 Transformer 之前&#xff0c;需要兩步&#xff1a;① 將字符流切分成離散 Token&#xff1b;② 把 Token 映射成連續向量。 1.1 理解詞嵌入&#xff08;Word Embedding&#xff09; 嵌入向量 一…

【Spring】Bean的生命周期,部分源碼解釋

文章目錄Bean 的生命周期執行流程代碼演示執行結果源碼閱讀AbstractAutowireCapableBeanFactorydoCreateBeaninitializeBeanBean 的生命周期 生命周期指的是一個對象從誕生到銷毀的整個生命過程&#xff0c;我們把這個過程就叫做一個對象的聲明周期 Bean 的聲明周期分為以下 …

[spring-cloud: 服務發現]-源碼解析

DiscoveryClient DiscoveryClient 接口定義了常見的服務發現操作&#xff0c;如獲取服務實例、獲取所有服務ID、驗證客戶端可用性等&#xff0c;通常用于 Eureka 或 Consul 等服務發現框架。 public interface DiscoveryClient extends Ordered {/*** Default order of the dis…

QML 基礎語法與對象模型

QML (Qt Meta-Object Language) 是一種聲明式語言&#xff0c;專為創建流暢的用戶界面和應用程序邏輯而設計。作為 Qt 框架的一部分&#xff0c;QML 提供了簡潔、直觀的語法來描述 UI 組件及其交互方式。本文將深入解析 QML 的基礎語法和對象模型。 一、QML 基礎語法 1. 基本對…

HTTPS的概念和工作過程

一.HTTPS是什么HTTPS也是一個應用層協議&#xff0c;是在HTTP協議的基礎上引入了一個加密層&#xff08;SSL&#xff09;HTTP協議內容都是按照文本的方式明文傳輸的&#xff0c;這就導致傳輸過程中可能出現被篡改的情況最著名的就是十多年前網絡剛發展的時期&#xff0c;出現“…

Unity —— Android 應用構建與發布?

文章目錄1 ?Gradle模板??&#xff1a;了解Gradle模板的作用及使用方法&#xff0c;以增強對構建流程的控制。?2 ?Gradle模板變量??&#xff1a;參考文檔——自定義Gradle模板文件中可用的變量列表。2.1 修改Unity應用的Gradle工程文件2.1.1 通過Gradle模板文件2.1.2 導出…

【iOS】strong和copy工作流程探尋、OC屬性關鍵字復習

文章目錄前言strong和copy的區別為什么要用copy&#xff1f;什么時候用什么修飾&#xff1f;strong&#xff08;ARC自動管理&#xff09;strong修飾變量的底層流程圖底層代碼核心實現小結copy底層流程圖對比與strong的關鍵不同之處內部調用關系&#xff08;偽代碼&#xff09;小…

程序代碼篇---多循環串口程序切換

上位機版&#xff08;Python&#xff09;要實現根據串口接收結果高效切換四個 while 循環函數&#xff0c;我們可以采用狀態機模式&#xff0c;配合非阻塞串口讀取來設計程序結構。這種方式可以實現快速切換&#xff0c;避免不必要的資源消耗。下面是一個高效的實現方案&#x…

rk3568上,實現ota,計算hash,驗證簽名,判斷激活分區,并通過dd命令,寫入對應AB分區

通過自定義升級程序&#xff0c;更直觀的理解ota升級原理。 一、模擬計算hash&#xff0c;驗證簽名&#xff0c;判斷激活分區&#xff0c;并通過dd命令&#xff0c;寫入對應分區 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <u…

數據分析—numpy庫

numpy庫NumPy 庫全面指南NumPy (Numerical Python) 是 Python 科學計算的基礎庫&#xff0c;提供了高性能的多維數組對象和工具。以下是 NumPy 的核心功能和使用方法。一、安裝與基礎1. 安裝 NumPypip install numpy2. 導入 NumPyimport numpy as np # 標準導入方式二、數組創建…

Vue3 setup、ref和reactive函數

一、setup函數1.理解&#xff1a;Vue3.0中一個新的配置項&#xff0c;值為一個函數。2.setup是所有Composition API(組合API)的“表演舞臺”。3.組件中用到的&#xff1a;數據、方法等等&#xff0c;均要配置在setup中。4.setup函數的兩種返回值&#xff1a;(1).若返回一個對象…

python中appium 的NoSuchElementException錯誤 原因以及解決辦法

錯誤收集D:\Program\Util\python.exe "D:/Program/myUtil/PyCharm 2024.3.5/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --target demo.py::TestAppium Testing started at 15:57 ... Launching pytest with arguments demo.py::TestAppium --no-hea…

mybatis-plus從入門到入土(四):持久層接口之BaseMapper和選裝件

大家好&#xff0c;今天繼續更新MybatisPlus從入門到入土系列&#xff0c;上一次的持久層接口還沒講完&#xff0c;只講了IService接口&#xff0c;今天我們繼續來講一下。 BaseMapper BaseMapper中的方法也比較簡單&#xff0c;都是增刪改查的基礎API&#xff0c;不知道大家還…

數組和指針的關系

在 C 語言中&#xff0c;?指針和數組有著非常緊密的聯系&#xff0c;但它們本質上是 ?不同的概念。理解它們的關系是掌握 C 語言內存操作的關鍵。下面我會從多個角度幫你梳理 ?指針和數組的直接聯系&#xff0c;并解釋它們的異同點。1. 數組和指針的本質區別?概念本質存儲方…