詳細講解設計跳表的三個步驟(查找、插入、刪除)

目錄

    • 寫在前面
    • 跳表概要
    • 查找步驟
    • 插入步驟
    • 刪除步驟
    • 完整代碼

寫在前面

關于跳表的一些知識可以參考這篇文章,最好是先看完這篇文章再看詳細的思路->代碼的復現步驟:
Redis內部數據結構詳解(6)——skiplist
關于跳表的插入、刪除基本操作其實也就是鏈表的插入和刪除,所以如果不熟悉的話還得先回顧鏈表的插入以及刪除是怎樣的,可以參考:
【數據結構基礎筆記】【鏈表】
相關代碼以及其他語言的寫法或者其他思路可以參考:
1206. 設計跳表
代碼參考鏈接:
https://leetcode-cn.com/problems/design-skiplist/solution/can-kao-redisshi-xian-by-bakezq/
https://www.iteye.com/blog/kenby-1187303

跳表概要

跳表采用隨機法決定鏈表中哪些節點應該增加向前指針以及在該節點中應增加多少個指針。
跳表結構的頭節點需要有足夠的指針域,以滿足可能構造最大級數的需求,而尾節點不需要指針域。
跳表在原有的有序鏈表上增加了多級索引,通過索引來實現快速查找。
1、首先在最高級索引上查找最后一個小于當前查找元素的位置
2、調到次高級索引繼續查找,直到調到最底層為止,如果查找元素存在的話,此刻已經十分接近要查找的元素的位置了。

查找步驟

你需要了解的內容:

1、指定head為左上角
2、初始化prevs數組(轉折點數組)
3、查找只有 right 和 down 兩個方向
4、maxlevel是當前跳表的最大層數,并非一開始人為限制的MAXLEVEL。maxlevel可能會隨著插入新的結點時的產生隨機層數而更新。
但是總是有:maxlevel <= MAXLEVEL

以下面的圖為例:從中你應該能清楚了解到prevs數組的含義。
在這里插入圖片描述
從從頂層向下遍歷到最底層:
在這里插入圖片描述
在這里插入圖片描述
根據這樣的思路可以寫出這樣的代碼:

vector<Node*> _search(int key)
{Node* cur = &head;vector<Node*> prevs(MAX_LEVEL);//從頂層向下遍歷,注意這里的maxlevel是當前跳表的最大層數,并非一開始人為限制的MAXLEVEL。//maxlevel可能會隨著插入新的結點時的產生隨機層數而更新。for(int i = maxlevel- 1;i >= 0;i--){//在當前層中比較,直到查找元素小于keywhile(cur->level[i] && cur->level[i]->val < key)cur = cur->level[i];//每層找到一個最靠近的點,作為向下的轉折點prevs[i] = cur;}return prevs;
}bool search(int target)
{//獲取第一層(最底層)最靠近key且val小于key的節點Node* prev = _search(target)[0];return prev->level[0] && prev->level[0]->val == target;
}

插入步驟

插入步驟:
1、通過查找子函數獲取最底層鏈表中<=num值的最近的節點,賦給prevs
2、隨機產生該節點的層數
首先,每個節點肯定都有第1層指針(每個節點都在第1層鏈表里)。
如果一個節點有第i層(i>=1)指針(即節點已經在第1層到第i層鏈表中),那么它有第(i+1)層指針的概率為p。
節點最大的層數不允許超過一個最大值,記為MaxLevel。

static int random_level() {int level = 1;while (rand() < SKIPLIST_P_VAL && level < MAX_LEVEL) level++;return level;
}

randomLevel()的偽碼中包含兩個參數,一個是p,一個是MaxLevel。在Redis的skiplist實現中,這兩個參數的取值為:

p = 1/4
MaxLevel = 32

3、更新當前的跳表中的最大層數maxlevel,在新生成的層里,將prevs[]初始化為head結點:
如下圖:當我們插入的數為2,隨機出來的層數為5,那么新的一層中小于這個num的節點一定是頭結點。
在這里插入圖片描述
4、生成一個新的結點,值為num,層數為level
5、對于每一層來說,由于新插入了一個結點,于是原本的索引關系就得發生改變:
將原本prevs的在本層的后繼作為cur在本層的后繼,再將cur作為prevs的后繼:
以第0層為例
在這里插入圖片描述
由于這里的prevs[i]都是head結點,所以可以比較方便的看出關系,整理之后就是下面的樣子:在這里插入圖片描述
代碼:

void add(int num)
{//1、通過查找子函數獲取最底層鏈表中<=num值的最近的節點。auto prevs = _search(num);//2、隨機產生該節點的層數int level = random_level();//3、更新當前的跳表中的最大層數maxlevel,并且if(level > maxlevel){for(int i = maxlevel; i < level; i++)prevs[i] = &head;maxlevel = level;}Node* cur = new Node(num,level);//for(int i = level -1; i >= 0; i--){cur->level[i] = prevs[i]->level[i];prevs[i]->level[i] = cur;}
}

刪除步驟

1、通過查找子函數獲取最底層鏈表中<= num值的最近的節點,賦給prevs
2、特殊情況處理:如果prevs在原鏈表中不存在(是指向空的節點) 或者 最近的節點的值不等于num(沒有值為num的節點),返回錯誤
3、否則,說明存在值為nums的結點,且為prevs[0]->level[0]
4、接下來就是要通過修改節點之間的后繼關系將值為num的前繼后繼節點相連。
prevs的后繼為需要刪除的del節點,則將del的后繼連接到prevs后面,作為新的prevs的后繼
5、將空間釋放
6、刪除掉一個結點可能需要更新當前最大層數(如果刪除的結點是之前層數最多的話)
判斷方法:如果頭結點maxlevel-1層的結點沒有后繼,說明本層已經沒有其他節點了

bool erase(int num) 
{//1、通過查找子函數獲取最底層鏈表中<= num值的最近的節點,賦給prevsauto prevs = _search(num);//2、特殊情況處理:如果prevs在原鏈表中不存在(是指向空的節點) 或者 最近的節點的值不等于num(沒有值為num的節點),返回錯誤if (!prevs[0]->level[0] || prevs[0]->level[0]->val != num)return false;//3、否則,說明存在值為nums的結點,且為prevs[0]->level[0]   Node * del = prevs[0]->level[0];//4、接下來就是要通過修改節點之間的后繼關系將值為num的前繼后繼節點相連。for (int i = 0; i < maxlevel; i++)//prevs的后繼為需要刪除的del節點,則將del的后繼連接到prevs后面,作為新的prevs的后溪if (prevs[i]->level[i] == del)prevs[i]->level[i] = del->level[i];//將空間釋放delete del;//刪除掉一個結點可能需要更新當前最大層數(如果刪除的結點是之前層數最多的話)//如果頭結點maxlevel-1層的結點沒有后繼,說明本層已經沒有其他節點了。while (maxlevel > 1 && !head.level[maxlevel - 1])maxlevel--;return true;
}

完整代碼

將成員數據和函數進行了私有公有的劃分:

class Skiplist {
private://定義結點struct Node {int val;vector<Node *> level;Node(int val, int size = MAX_LEVEL) : val(val), level(size) {}};//定義隨機結點層數的參數static const int SKIPLIST_P_VAL = RAND_MAX / 4, MAX_LEVEL = 32;//定義頭結點Node head;//定義當前最大層數int maxlevel = 1;//定義search子函數vector<Node*> _search(int key) {Node* cur = &head;vector<Node *> prevs(MAX_LEVEL);// through every level, from top to bottomfor (int i = maxlevel - 1; i >= 0; i--) {// through elements in the current level with smaller valuewhile (cur->level[i] && cur->level[i]->val < key)cur = cur->level[i];prevs[i] = cur;}return prevs;}//定義隨機產生層數子函數static int random_level() {int level = 1;while (rand() < SKIPLIST_P_VAL && level < MAX_LEVEL) level++;return level;}public://初始化跳表的時候也需要初始化headSkiplist() : head(INT_MIN, MAX_LEVEL) {}bool search(int target) {Node* prev = _search(target)[0];return prev->level[0] && prev->level[0]->val == target;}void add(int num) {auto prevs = _search(num);int level = random_level();if (level > maxlevel) {for (int i = maxlevel; i < level; i++)prevs[i] = &head;maxlevel = level;}Node * cur = new Node(num, level);for (int i = level - 1; i >= 0; i--) {cur->level[i] = prevs[i]->level[i];prevs[i]->level[i] = cur;}}bool erase(int num) {auto prevs = _search(num);if (!prevs[0]->level[0] || prevs[0]->level[0]->val != num)return false;Node * del = prevs[0]->level[0];for (int i = 0; i < maxlevel; i++)if (prevs[i]->level[i] == del)prevs[i]->level[i] = del->level[i];delete del;while (maxlevel > 1 && !head.level[maxlevel - 1])maxlevel--;return true;}
};/*** Your Skiplist object will be instantiated and called as such:* Skiplist* obj = new Skiplist();* bool param_1 = obj->search(target);* obj->add(num);* bool param_3 = obj->erase(num);*/

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

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

相關文章

php 類靜態變量 和 常量消耗內存及時間對比

在對類執行100w次循環后&#xff0c; 常量最快&#xff0c;變量其次&#xff0c;靜態變量消耗時間最高 其中&#xff1a; 常量消耗&#xff1a;101.1739毫秒 變量消耗&#xff1a;2039.7689毫秒 靜態變量消耗&#xff1a;4084.8911毫秒 測試代碼&#xff1a; class Timer_profi…

一個機器周期 計算機_計算機科學組織| 機器周期

一個機器周期 計算機機器周期 (Machine Cycle) The cycle during which a machine language instruction is executed by the processor of the computer system is known as the machine cycle. If a program contains 10 machine language instruction, 10 separate machine …

四、Transforms

transform是torchvision下的一個.py文件&#xff0c;這個python文件中定義了很多的類和方法&#xff0c;主要實現對圖片進行一些變換操作 一、Transforms講解 from torchvision import transforms#按著Ctrl&#xff0c;點擊transforms進入到__init__.py文件中 from .transfo…

leetcode 134. 加油站 思考分析

目錄題目1、暴力法&#xff0c;雙層遍歷2、貪心題目 在一條環路上有 N 個加油站&#xff0c;其中第 i 個加油站有汽油 gas[i] 升。 你有一輛油箱容量無限的的汽車&#xff0c;從第 i 個加油站開往第 i1 個加油站需要消耗汽油 cost[i] 升。你從其中的一個加油站出發&#xff0…

單鏈線性表的實現

//函數結果狀態代碼#define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2 //Status是函數的類型&#xff0c;其值是函數結果狀態代碼 typedef int Status; typedef int ElemType;…

時間模塊,帶Python示例

Python時間模塊 (Python time Module) The time module is a built-in module in Python and it has various functions that require to perform more operations on time. This is one of the best modules in Python that used to solve various real-life time-related pro…

五、torchvision

一、下載CIFAR-10數據集 CIFAR-10數據集官網 通過閱讀官網給的解釋可以大概了解到&#xff0c;一共6w張圖片&#xff0c;每張圖片大小為3232&#xff0c;5w張訓練圖像&#xff0c;1w張測試圖像&#xff0c;一共由十大類圖像。 CIFAR10官網使用文檔 torchvision.datasets.CIF…

leetcode 69. x 的平方根 思考分析

題目 實現 int sqrt(int x) 函數。 計算并返回 x 的平方根&#xff0c;其中 x 是非負整數。 由于返回類型是整數&#xff0c;結果只保留整數的部分&#xff0c;小數部分將被舍去。 示例 1: 輸入: 4 輸出: 2 示例 2: 輸入: 8 輸出: 2 說明: 8 的平方根是 2.82842…, 由于返回…

背包問題 小灰_小背包問題

背包問題 小灰Prerequisites: Algorithm for fractional knapsack problem 先決條件&#xff1a; 分數背包問題算法 Here, we are discussing the practical implementation of the fractional knapsack problem. It can be solved using the greedy approach and in fraction…

360瀏覽器兼容問題

360瀏覽器兼容問題 360瀏覽器又是一大奇葩&#xff0c;市場份額大&#xff0c;讓我們不得不也對他做些兼容性處理。 360瀏覽器提供了兩種瀏覽模式&#xff0c;極速模式和兼容模式&#xff0c;極速模式下是webkit內核的處理模式&#xff0c;兼容模式下是與IE內核相同的處理模式。…

轉 設計師也需要了解的一些前端知識

一、常見視覺效果是如何實現的 一些事 關于文字效果 互聯網的一些事 文字自身屬性相關的效果css中都是有相對應的樣式的&#xff0c;如字號、行高、加粗、傾斜、下劃線等&#xff0c;但是一些特殊的效果&#xff0c;主要表現為ps中圖層樣式中的效果&#xff0c;css是無能為力的…

六、DataLoader

一、DataLoader參數解析 DataLoader官網使用手冊 參數描述dataset說明數據集所在的位置、數據總數等batch_size每次取多少張圖片shuffleTrue亂序、False順序(默認)samplerbatch_samplernum_workers多進程&#xff0c;默認為0采用主進程加載數據collate_fnpin_memorydrop_las…

單調棧 leetcode整理(一)

目錄單調棧知識402. 移掉K位數字1673. 找出最具競爭力的子序列316. 去除重復字母&#xff08;1081. 不同字符的最小子序列&#xff09;321. 拼接最大數單調棧知識 單調棧就是一個內部元素有序的棧&#xff08;大->小 or 小->大&#xff09;&#xff0c;但是只用到它的一…

數字簽名 那些密碼技術_密碼學中的數字簽名

數字簽名 那些密碼技術A signature is usually used to bind signatory to the message. The digital signature is thus a technique that binds a person or the entity to the digital data. This binding ensures that the person sending the data is solely responsible …

七、torch.nn

一、神經網絡模塊 進入到PyTorch的torch.nnAPI學習頁面 PyTorch提供了很多的神經網絡方面的模塊&#xff0c;NN就是Neural Networks的簡稱 二、Containers torch.nn下的Containers 一共有六個模塊&#xff0c;最常用的就是Module模塊&#xff0c;看解釋可以知道&#xff0c…

Java多線程初學者指南(8):從線程返回數據的兩種方法

本文介紹學習Java多線程中需要學習的從線程返回數據的兩種方法。從線程中返回數據和向線程傳遞數據類似。也可以通過類成員以及回調函數來返回數據。原文鏈接 從線程中返回數據和向線程傳遞數據類似。也可以通過類成員以及回調函數來返回數據。但類成員在返回數據和傳遞數據時有…

【C++進階】 遵循TDD原則,實現平面向量類(Vec2D)

目錄1、明確要實現的類的方法以及成員函數2、假設已經編寫Vec2D&#xff0c;根據要求&#xff0c;寫出測試代碼3、編寫平面向量類Vec2D,并進行測試4、完整代碼5、最終結果1、明確要實現的類的方法以及成員函數 考慮到效率問題&#xff0c;我們一般將函數的參數設置為引用類型。…

Keilc的中斷號計算方法

中斷號碼 (中斷向量-3)/8轉載于:https://www.cnblogs.com/yuqilihualuo/p/3423634.html

md5模式 簽名_MD的完整形式是什么?

md5模式 簽名醫師&#xff1a;醫學博士/常務董事 (MD: Doctor of Medicine / Managing Director) 1)醫學博士&#xff1a;醫學博士 (1) MD: Doctor of Medicine) MD is an abbreviation of a Doctor of Medicine degree. In the field of Medicine, it is the main academic de…

八、卷積層

一、Conv2d torch.nn.Conv2d官網文檔 torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride1, padding0, dilation1, groups1, biasTrue, padding_modezeros, deviceNone, dtypeNone) 參數解釋官網詳情說明in_channels輸入的通道數&#xff0c;如果是彩色照片通道…