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

c++如何實現(xiàn)跳表(skiplist)

 更新時間:2020年08月12日 14:45:58   作者:evenleo  
這篇文章主要介紹了c++如何實現(xiàn)跳表,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下

引言

二分查找底層依賴的是數(shù)組隨機訪問的特性,所以只能用數(shù)組來實現(xiàn)。如果數(shù)據(jù)存儲在鏈表中,就真的沒法用二分查找算法了嗎?實際上,只需要對鏈表稍加改造,就可以支持類似“二分”的查找算法。改造之后的數(shù)據(jù)結(jié)構(gòu)叫作跳表。

定義

跳表是一個隨機化的數(shù)據(jù)結(jié)構(gòu)。它允許快速查詢一個有序連續(xù)元素的數(shù)據(jù)鏈表。跳躍列表的平均查找和插入時間復(fù)雜度都是O(log n),優(yōu)于普通隊列的O(n)。性能上和紅黑樹,AVL樹不相上下,但跳表的原理非常簡單,目前Redis和LevelDB中都有用到。
跳表是一種可以替代平衡樹的數(shù)據(jù)結(jié)構(gòu)。跳表追求的是概率性平衡,而不是嚴格平衡。因此,跟平衡二叉樹相比,跳表的插入和刪除操作要簡單得多,執(zhí)行也更快。

C++簡單實現(xiàn)

下面實現(xiàn)過程主要是簡單實現(xiàn)跳表的過程,不是多線程安全的,LevelDB實現(xiàn)的跳表支持多線程安全,用了std::atomic原子操作,本文主要是為了理解跳表的原理,所以采用最簡單的實現(xiàn)。

#ifndef SKIPLIST_H
#define SKIPLIST_H

#include <ctime>
#include <initializer_list>
#include <iostream>
#include <random>

template <typename Key>
class Skiplist {
public:
 struct Node {
 Node(Key k) : key(k) {}
 Key key;
 Node* next[1]; // C語言中的柔性數(shù)組技巧
 };

private:
 int maxLevel;
 Node* head;

 enum { kMaxLevel = 12 };

public:
 Skiplist() : maxLevel(1)
 {
 head = newNode(0, kMaxLevel);
 }

 Skiplist(std::initializer_list<Key> init) : Skiplist()
 {
 for (const Key& k : init)
 {
  insert(k);
 }
 }

 ~Skiplist()
 {
 Node* pNode = head;
 Node* delNode;
 while (nullptr != pNode)
 {
  delNode = pNode;
  pNode = pNode->next[0];
  free(delNode); // 對應(yīng)malloc
 }
 }

 // 禁止拷貝構(gòu)造和賦值
 Skiplist(const Skiplist&) = delete;
 Skiplist& operator=(const Skiplist&) = delete;
 Skiplist& operator=(Skiplist&&) = delete;

private:
 Node* newNode(const Key& key, int level)
 {
 /*
 * 開辟sizeof(Node) + sizeof(Node*) * (level - 1)大小的空間
 * sizeof(Node*) * (level - 1)大小的空間是給Node.next[1]指針數(shù)組用的
 * 為什么是level-1而不是level,因為sizeof(Node)已包含一個Node*指針的空間
 */ 
 void* node_memory = malloc(sizeof(Node) + sizeof(Node*) * (level - 1));
 Node* node = new (node_memory) Node(key);
 for (int i = 0; i < level; ++i)
  node->next[i] = nullptr;

 return node;
 }
 /*
 * 隨機函數(shù),范圍[1, kMaxLevel],越小概率越大
 */ 
 static int randomLevel()
 {
 int level = 1;
 while (rand() % 2 && level < kMaxLevel)
  level++;

 return level;
 }

public:
 Node* find(const Key& key)
 {
 // 從最高層開始查找,每層查找最后一個小于key的前繼節(jié)點,不斷縮小范圍
 Node* pNode = head;
 for (int i = maxLevel - 1; i >= 0; --i)
 {
  while (pNode->next[i] != nullptr && pNode->next[i]->key < key)
  {
  pNode = pNode->next[i];
  }
 }

 // 如果第一層的pNode[0]->key == key,則返回pNode->next[0],即找到key
 if (nullptr != pNode->next[0] && pNode->next[0]->key == key)
  return pNode->next[0];

 return nullptr;
 }

 void insert(const Key& key)
 {
 int level = randomLevel();
 Node* new_node = newNode(key, level);
 Node* prev[kMaxLevel];
 Node* pNode = head;
 // 從最高層開始查找,每層查找最后一個小于key的前繼節(jié)點
 for (int i = level - 1; i >= 0; --i)
 {
  while (pNode->next[i] != nullptr && pNode->next[i]->key < key)
  {
  pNode = pNode->next[i];
  }
  prev[i] = pNode;
 }
 // 然后每層將新節(jié)點插入到前繼節(jié)點后面
 for (int i = 0; i < level; ++i)
 {
  new_node->next[i] = prev[i]->next[i];
  prev[i]->next[i] = new_node;
 }

 if (maxLevel < level) // 層數(shù)大于最大層數(shù),更新最大層數(shù)
  maxLevel = level;
 }

 void erase(const Key& key)
 {
 Node* prev[maxLevel];
 Node* pNode = head;
 // 從最高層開始查找,每層查找最后一個小于key的前繼節(jié)點
 for (int i = maxLevel - 1; i >= 0; --i)
 {
  while (pNode->next[i] != nullptr && pNode->next[i]->key < key)
  pNode = pNode->next[i];
  prev[i] = pNode;
 }
 
 // 如果找到key,
 if (pNode->next[0] != nullptr && pNode->next[0]->key == key)
 {
  Node *delNode = pNode->next[0];
  // 從最高層開始,如果當(dāng)前層的next節(jié)點的值等于key,則刪除next節(jié)點
  for (int i = maxLevel - 1; i >= 0; --i)
  {
  if (prev[i]->next[i] != nullptr && key == prev[i]->next[i]->key)
   prev[i]->next[i] = prev[i]->next[i]->next[i];
  }
  free(delNode); // 最后銷毀pNode->next[0]節(jié)點
 }
 
 // 如果max_level>1且頭結(jié)點的next指針為空,則該層已無數(shù)據(jù),max_level減一
 while (maxLevel > 1 && head->next[maxLevel] == nullptr)
 {
  maxLevel--;
 }
 }
};

#endif

Redis和LevelDB選用跳表而棄用紅黑樹的原因

  1. Skiplist的復(fù)雜度和紅黑樹一樣,而且實現(xiàn)起來更簡單。
  2. 在并發(fā)環(huán)境下Skiplist有另外一個優(yōu)勢,紅黑樹在插入和刪除的時候可能需要做一些rebalance的操作,這樣的操作可能會涉及到整個樹的其他部分,而skiplist的操作顯然更加局部性一些,鎖需要盯住的節(jié)點更少,因此在這樣的情況下性能好一些。

以上就是c++如何實現(xiàn)跳表的詳細內(nèi)容,更多關(guān)于c++ 跳表的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一篇文章帶你了解C語言的選擇結(jié)構(gòu)

    一篇文章帶你了解C語言的選擇結(jié)構(gòu)

    這篇文章主要為大家介紹了C語言的選擇結(jié)構(gòu),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • C++中基類和派生類之間的轉(zhuǎn)換實例教程

    C++中基類和派生類之間的轉(zhuǎn)換實例教程

    這篇文章主要介紹了C++中基類和派生類之間的轉(zhuǎn)換,有助于深入理解C++面向?qū)ο蟪绦蛟O(shè)計,需要的朋友可以參考下
    2014-08-08
  • C++11中union的使用方法示例

    C++11中union的使用方法示例

    這篇文章主要給大家介紹了關(guān)于C++11中union的使用方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • 八皇后問題的相關(guān)C++代碼解答示例

    八皇后問題的相關(guān)C++代碼解答示例

    這篇文章主要介紹了八皇后問題的相關(guān)C++代碼解答示例,文中包括ACM競賽的八皇后相關(guān)知識的練習(xí)實例,需要的朋友可以參考下
    2015-08-08
  • C語言實現(xiàn)2048小游戲

    C語言實現(xiàn)2048小游戲

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)2048小游戲,注釋清晰,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • C++?Boost?Spirit精通教程

    C++?Boost?Spirit精通教程

    Boost是為C++語言標準庫提供擴展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標準庫的后備,是C++標準化進程的開發(fā)引擎之一,是為C++語言標準庫提供擴展的一些C++程序庫的總稱
    2022-11-11
  • C++多線程實現(xiàn)電子詞典

    C++多線程實現(xiàn)電子詞典

    這篇文章主要為大家詳細介紹了C++多線程實現(xiàn)電子詞典,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • C語言深入探索之單鏈表與typedef的用法

    C語言深入探索之單鏈表與typedef的用法

    typedef為C語言的關(guān)鍵字,作用是為一種數(shù)據(jù)類型定義一個新名字,單鏈表是后面要學(xué)的雙鏈表以及循環(huán)鏈表的基礎(chǔ),要想繼續(xù)深入了解數(shù)據(jù)結(jié)構(gòu)以及C語言,我們就要奠定好這塊基石!接下來就和我一起學(xué)習(xí)吧
    2022-05-05
  • C語言使用ffmpeg和sdl實現(xiàn)多路音頻播放

    C語言使用ffmpeg和sdl實現(xiàn)多路音頻播放

    這篇文章主要為大家詳細介紹了一種基于ffmpeg和sdl實現(xiàn)的音頻多路混合的方法,文中的示例代碼講解詳細,感興趣的小伙伴可以參考一下
    2023-06-06
  • C++歸并算法實例

    C++歸并算法實例

    這篇文章主要介紹了C++歸并算法,實例分析了C++實現(xiàn)基于歸并算法合并線性表的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07

最新評論

民乐县| 乾安县| 韶关市| 攀枝花市| 仪征市| 肥西县| 厦门市| 民权县| 东兰县| 肥乡县| 鄱阳县| 保德县| 德清县| 搜索| 凤台县| 临潭县| 兰考县| 永兴县| 慈利县| 南漳县| 漯河市| 鹤峰县| 博野县| 安仁县| 玉屏| 石首市| 耿马| 当涂县| 宾阳县| 绍兴市| 东明县| 无极县| 翁牛特旗| 莱西市| 萝北县| 平江县| 铜鼓县| 孟津县| 枞阳县| 垦利县| 晋城|