最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c++ 隊列相關(guān)知識總結(jié)

 更新時間:2021年03月03日 09:18:25   作者:hedzr  
這篇文章主要介紹了c++ 隊列相關(guān)知識總結(jié),幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下

預(yù)備

隊列,是一種先進先出(FIFO)的線性表,一般來說會使用鏈表或者數(shù)組來實現(xiàn)它。

隊列被允許從后端(rear)插入(insert)新元素,稱作入列(push,enqueue);而從前端(front)彈出(pop)元素,稱作出列(pop,dequeue)。

學(xué)術(shù)上說它和堆棧常常被同時提起,因為堆棧與隊列幾乎一摸一樣,除了出棧時也在后端彈出元素,從而構(gòu)成了后進先出(LIFO)的數(shù)據(jù)結(jié)構(gòu)。

古典的單向鏈表/單向隊列

單向鏈表表示的隊列,出列時必須遍歷整個鏈表,直至鏈尾的前一位,然后摘除鏈尾來實現(xiàn)出列操作。

毫無疑問,這一定是代價很高的。

但這種方案的優(yōu)勢在于任何時候任何人都能夠隨手寫得出來。也就是說,它是最具備可手擼性的一種實現(xiàn)方案,雖然出列代價較高,但對于相當(dāng)多的場景來說那點代價 CPU 根本不當(dāng)作是事。

雙向鏈表/雙向隊列

雙向鏈表的好處是在鏈表頭尾的操作都是 O(1) 的,代價極低,可以視作毫無代價。但這是用額外的兩個指針的空間代價來達成的。對于像 std::deque<long> 這樣的雙向鏈表來說,每個鏈表元素的數(shù)據(jù)部分(payload)需要 8 bytes,而附著在 payload 上的指針則需要 16 bytes(典型的 64bit 計算時),相當(dāng)于說超出數(shù)據(jù)實體兩倍的代價,不可謂不昂貴。幸而通常的實踐場景中很少會有這樣的實踐需求,往往更多的是一個 payload 的實體 struct 可能需要數(shù)百 bytes 甚至更多,這時候 16 bytes 就不算太起眼了。

雙向鏈表結(jié)構(gòu)的手寫,難點在于指針的同步修改,正確指向。

對于高頻交易場景來說,雙向鏈表的指針維護操作是導(dǎo)致鎖開銷的典型瓶頸,所以需要下力氣研究和解決鎖問題。

一般來說在現(xiàn)實世界中大家都是借助于 STL 來實現(xiàn)隊列算法及其應(yīng)用的。典型的例子是通過 deque:

#include <iostream>
#include <deque>
 
int main()
{
  // 創(chuàng)建容納整數(shù)的 deque
  std::deque<int> d{};
 
  // 約定 push_back 為入列操作
  d.push_back(13);
  d.push_back(25);
  d.push_back(7);
  d.push_back(2);
 
  // 約定 pop_front 為出列操作
  auto tmp = d.pop_front();
  std::cout << "FRONT: " << tmp << '\n';
 
  // 迭代并打印 deque 的值
  for(int n : d) {
    std::cout << n << '\n';
  }
}

得益于 deque 的強健的基礎(chǔ)能力,我們實際上可以得到一個工程強度的有效的隊列模板類。

同時,deque 也可以被毫無代價地實現(xiàn)強健的 Stack 數(shù)據(jù)結(jié)構(gòu),僅僅是將出棧操作從上文的 pop_front 改為 pop_back() 即可。

所以,deque 的強大可見一斑。

不過,無論如何,我們有必要手寫一份來完成自己的理解。在下面我們簡單地實現(xiàn)了一份理論上的典型隊列方案:

template<class T>
 class ti_queue {
  public:
  struct element {
   T _data;
   element *_last;
   element *_next;
  };

  public:
  ti_queue() {
   _head = new element;
   _tail = new element;
   _head->_next = _tail;
   _tail->_last = _head;
  }
  ~ti_queue() {
   auto *p = _head;
   while (p != nullptr) {
    auto *t = p;
    p = p->_next;
    delete t;
   }
  }

  void push(T const &data) {
   auto h = _tail->_last;
   auto *ptr = new element{data, h, _tail};
   _tail->_last = ptr;
   h->_next = ptr;
  }

  T pop() {
   auto p = _head->_next;
   if (p == _tail) {
    return T{};
   }
   p->_last->_next = p->_next;
   p->_next->_last = p->_last;
   T t = p->_data;
   delete p;
   return t;
  }

  bool empty() const { return _tail->_last == _head; }

  T const &peek() const {
   if (empty()) return _tail->_data;
   return _tail->_last->_data;
  }

  private:
  element *_head{};
  element *_tail{};
 };

這是一份不采用智能指針的實現(xiàn)。因為智能指針在這里只有壞處沒有好處。然而這就會使得這份實現(xiàn)顯得低級,很有趣吧,現(xiàn)代 C++ 在很多時候其實是扭曲了的,它雖然很現(xiàn)代,很時髦,但也很脫了褲子放屁。

在這份實現(xiàn)中有一個重要的約定:我們認為你在提供 T 的具現(xiàn)類型時,你會隱含地約束零值初始化的 T 示例代表著空元素。所以當(dāng)你 pop/peek 得到一個 T 實例時,如果它是零值將代表著隊列為空。

所以我們的測試片段為:

int main() {
 std::cout << '\n' << "queue: ";
 bet::ti_queue<int> tiq;
 tiq.push(31);
 tiq.push(17);
 tiq.push(19);
 tiq.push(73);

 auto tmp = tiq.pop();
 std::cout << tmp;

 while (!tiq.empty()) {
  tmp = tiq.pop();
  std::cout << ',' << tmp;
 }

 std::cout << '\n';
}

其結(jié)果應(yīng)該形如這樣:

queue: 31,17,19,73

OK,繼續(xù)我們的思考線路:

檢測隊列有否為空很重要,你不可能無代價地避免空訪問,因為這是數(shù)據(jù)結(jié)構(gòu)本身的特性決定的。而在具體實現(xiàn)方面,上面之所以要做出那樣的約定,是因為我們沒有使用指針方案來存儲 T,所以你需要檢查 pop/peek 得到的 T 實例是否為空元素?;蛘?,你在 pop/peek() 之前首先采用 empty() 進行隊列非空判斷。

如果采用指針存儲方案的話,你可以順理成章地檢查 pop/peek 返回值是否 nullptr,這其實是 C/C++98 時代的典型實現(xiàn)方案。

如果是在 C++11 之后,盡管我們的實現(xiàn)有上述約定,但你也可以通過給 T 實例化為 std::shared_ptr<Real> 的方式來建立一個智能指針的緩沖層,因此你可以判斷該指針的非空性來鑒別返回值的非空性。

借助于共享指針的強力特性,很多時候我們無需在做容器實現(xiàn)時考慮太多的元素如何存儲的問題,因為模板的一個重要能力就是透明地引入中間層來提供額外的供給能力。這種時候,在這個例子里,中間層的智能指針包裝實際上提供了一種裝飾器的設(shè)計模式運用機制。

所以,

我們認為,在容器等數(shù)據(jù)結(jié)構(gòu)等實現(xiàn)部分,如果有可能,你應(yīng)該負起責(zé)來,自行處理 C++/C 指針的各種運算,保證你的實現(xiàn)邊界之內(nèi)沒有泄漏問題。這樣做的好處在于你的實現(xiàn)將會非??勺x可維護,因為它通常能夠和理論上的數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)近似于等價,如同我們上面的實現(xiàn)一樣。

然而,如果你的目標(biāo)是在面向兩個目標(biāo)時則例外了:第一個目標(biāo)是工程性的高性能線程安全性要求,此時應(yīng)該加上的鎖、優(yōu)化則取決于具體實現(xiàn);第二個目標(biāo)是用戶的使用簡便性,很顯然讓用戶直接具現(xiàn)他們的 T 而不是 shared_ptr<T> 是絕對有助于用戶的使用簡便性的,那么你就可能要在精打細算和借助智能指針方面做個選擇了。

環(huán)形隊列

此前,我其實在 Golang 針對高頻交易提出了一種無鎖環(huán)形隊列的實現(xiàn)方案,在那里使用了一種程序員直覺化的代碼編寫方法及直覺化的算法設(shè)計思路。事實上我個人偏好于那種直覺化的算法設(shè)計思路,我討厭搞委托交易,像 Spring framework 這樣的框架,在做無論什么的集成的時候如同脫了褲子放屁一般的老奶奶裹腳布似的做法,我鄙視之,所以我現(xiàn)在根本沒興趣討論 Java 的任何事情,這是被我判定了不值得的一種技術(shù)體系。

不過,RxJava 的思路是符合我的審美觀的。這才是對世界做抽象的正確態(tài)度。

那種頑固地拿著 XML 搞出一個龐大的 shit 堆的方法,我只能是表示敬而遠之。不但是 Java 的多數(shù)玩意如此地垃圾,.NET 畫界面 XAML,macOS XCode 畫界面的 Storyboard 統(tǒng)統(tǒng)都是毫無疑問的垃圾。任何和 XML 緊密捆綁的東西,沒說的,都是在創(chuàng)造垃圾。

那是一種將任何事情復(fù)雜化的思維模式,我拒絕的正是如此這般明顯愚蠢的模式。

所以我這樣的人,一邊不爽 Golang 的簡陋,一邊不也還是投入其懷抱了嗎——就因為它能夠極低代價的 ELF 化啊。程序員要干的事情就是簡化這個世界。創(chuàng)造垃圾這樣的事是干不得的。

所以,環(huán)形隊列在理論研究層面看,實際上就是一個數(shù)組,它是固定大小的,而后我們將其首尾相接(通過 put-pointer 和 get-pointer 自動回繞的方式),形成了抽象層面上的環(huán)形狀態(tài)。它的特點在于,當(dāng)排除了分配每個元素的代價之后,它可以通過自己的固定緩沖區(qū)的特點來去除對象入列和出列時的潛在可能的復(fù)制開銷。

關(guān)于這一點,即使是充分利用 C++11 甚至 C++17 的現(xiàn)代語法,在借用 STL 來實現(xiàn)隊列的時候,你可能也往往無法避免出列時的復(fù)制開銷。如果考慮到高頻交易場景下想要避免鎖代價的影響的話,往往你可能連入列時的額外開銷也無法避免——只不過,很多時候鎖的代價更高過內(nèi)存分配已經(jīng)內(nèi)存復(fù)制的總線級代價,所以我們不得不兩害相權(quán)取其輕罷了。

為了解決這種特定的問題,一種方案是通過環(huán)形隊列來避免內(nèi)存分配問題;另一種則是自行構(gòu)造自己的隊列模板,通過專門的設(shè)計和實現(xiàn)來取得內(nèi)存分配、內(nèi)存復(fù)制與讀寫訪問、CPU Cache 等層面的鎖的問題等等的平衡。

再有一種方案,那就簡單了:用指針吧。鑒于 C++11 之后用裸指針被認為是跟不上時代的表現(xiàn),所以:用智能指針吧。借助智能指針的移動語義或共享計數(shù)值技術(shù),通常可以較低代價地避免內(nèi)存反復(fù)頻繁分配以及內(nèi)存復(fù)制帶來的額外開銷問題。這種方案是有一點投機取巧,但是語言的基礎(chǔ)設(shè)施本就應(yīng)該提供給程序員以這樣的能力。反過來思考,如果一個程序員根本經(jīng)受不了 C 指針的蹂躪的話,他也不配被稱作程序員。話說今天這話也很沒意思,阿貓阿狗都是程序員的——劣幣正在驅(qū)逐良幣。

應(yīng)用

由于環(huán)形隊列是固定大小的,所以它極其適合這樣的場景:數(shù)據(jù)采樣工具,或者遙測場景。在數(shù)據(jù)采樣的場景,常常需要的是最最近幾十條記錄進行數(shù)學(xué)分析。當(dāng)使用環(huán)形隊列來做相應(yīng)的數(shù)據(jù)緩沖時,老的數(shù)據(jù)不斷隨著插入指針的推進而被覆蓋掉,所以在取出指針一側(cè)總是最新的 N 條記錄,N 是環(huán)形隊列的預(yù)設(shè)大小。于是分析器可以面對這個環(huán)形隊列順利地提取到最新 N 條記錄用于計算和分析。

除此而外,我們還可以讓環(huán)形隊列的插入行為是非覆蓋的,于是當(dāng)隊列滿時,插入操作就會在隊尾被阻塞,這是另一種環(huán)形隊列的應(yīng)用方式。

下幾期我將可能會考慮實現(xiàn)一份 C++ 版本的環(huán)形隊列,但這次可能不會做高頻版了,大約只會做適合毫秒級例如典型工業(yè) TCP 交易的版本。

Conclusion

隊列這種數(shù)據(jù)結(jié)構(gòu),總體上來說非常簡單,也不會牽涉到高深的數(shù)學(xué)證明,所以實現(xiàn)一個隊列往往會是初學(xué)者上手編程的好題目。

以上就是c++ 隊列相關(guān)知識總結(jié)的詳細內(nèi)容,更多關(guān)于c++ 隊列的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言中大小端問題實例探索解決方法

    C語言中大小端問題實例探索解決方法

    這篇文章主要介紹了C語言中大小端問題實例,總的來說這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過程。希望通過這道題能給你帶來一種解題優(yōu)化的思路
    2023-02-02
  • C語言大小端字節(jié)序存儲模式深入解讀

    C語言大小端字節(jié)序存儲模式深入解讀

    我們知道,當(dāng)編譯器執(zhí)行 “創(chuàng)建變量” 這一代碼時,會在內(nèi)存中開辟空間相應(yīng)的空間來存儲變量值。而對于整型變量而言,變量值又是以二進制補碼的形式存放的
    2022-09-09
  • 用while判斷輸入的數(shù)字是否回文數(shù)的簡單實現(xiàn)

    用while判斷輸入的數(shù)字是否回文數(shù)的簡單實現(xiàn)

    這篇文章主要介紹了用while判斷輸入的數(shù)字是否回文數(shù)的簡單實現(xiàn),需要的朋友可以參考下
    2014-02-02
  • QT使用canon sdk拍照并保存到本機的方法示例

    QT使用canon sdk拍照并保存到本機的方法示例

    這篇文章主要介紹了QT使用canon sdk拍照并保存到本機的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • 詳解C++編程中的析構(gòu)函數(shù)

    詳解C++編程中的析構(gòu)函數(shù)

    這篇文章主要介紹了C++編程中的析構(gòu)函數(shù),是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • C++中的強制類型轉(zhuǎn)換操作詳解

    C++中的強制類型轉(zhuǎn)換操作詳解

    C++中提供了四種強制類型轉(zhuǎn)換技術(shù):static_cast、dynamic_cast、reinterpret_cast和const_cast。這些技術(shù)能夠在需要時將一種類型轉(zhuǎn)換為另一種類型,但需要注意它們的適用條件和安全性。程序員需要根據(jù)具體情況選擇合適的強制類型轉(zhuǎn)換方式,以確保程序的正確性和可靠性
    2023-04-04
  • 用C語言實現(xiàn)排雷游戲

    用C語言實現(xiàn)排雷游戲

    大家好,本篇文章主要講的是用C語言實現(xiàn)排雷游戲,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • C++中實現(xiàn)保存數(shù)據(jù)到CSV文件

    C++中實現(xiàn)保存數(shù)據(jù)到CSV文件

    這篇文章主要介紹了C++中實現(xiàn)保存數(shù)據(jù)到CSV文件方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • C語言數(shù)據(jù)結(jié)構(gòu)之隊列的定義與實現(xiàn)

    C語言數(shù)據(jù)結(jié)構(gòu)之隊列的定義與實現(xiàn)

    隊列是一種特殊的線性表,特殊之處在于它只允許在表的前端(head)進行刪除操作,而在表的后端(tail)進行插入操作。本文將詳細講講C語言中隊列的定義與實現(xiàn),感興趣的可以了解一下
    2022-07-07
  • C 語言基礎(chǔ)教程(我的C之旅開始了)[六]

    C 語言基礎(chǔ)教程(我的C之旅開始了)[六]

    C 語言基礎(chǔ)教程(我的C之旅開始了)[六]...
    2007-02-02

最新評論

宝兴县| 樟树市| 东港市| 连山| 那曲县| 双流县| 屯门区| 靖江市| 旬邑县| 荆州市| 徐水县| 多伦县| 漾濞| 毕节市| 呼伦贝尔市| 松江区| 乌拉特前旗| 庆阳市| 嵊泗县| 苍山县| 丹巴县| 耿马| 都江堰市| 当涂县| 游戏| 沙田区| 什邡市| 吉林市| 隆昌县| 财经| 西乌| 绍兴市| 灵石县| 德昌县| 富民县| 渝北区| 会同县| 岳西县| 富阳市| 雷波县| 油尖旺区|