拜托,面試別再問我跳錶了!

来源:https://www.cnblogs.com/tong-yuan/archive/2019/04/12/skiplist.html
-Advertisement-
Play Games

拜托,面試別再問我跳錶了! 何為跳錶? 跳錶使用什麼樣的存儲結構? 為何Redis選擇用跳錶來實現有序集合? ...


何為跳錶?

跳錶是一個隨機化的數據結構,實質就是一種可以進行二分查找的有序鏈表

跳錶在原有的有序鏈表上面增加了多級索引,通過索引來實現快速查找。

跳錶不僅能提高搜索性能,同時也可以提高插入和刪除操作的性能。

跳錶詳解

有序鏈表

skiplist1

考慮一個有序鏈表,我們要查找3、7、17這幾個元素,我們只能從頭開始遍歷鏈表,直到查找到元素為止。

上述這個鏈表是有序的,但是不能使用二分查找,是不是很捉急?(P.S.數組可以實現二分查找)

那麼,有沒有什麼方法可以實現有序鏈表的二分查找呢?

答案是肯定的,那就是我們將要介紹的這種數據結構——跳錶。

跳錶的演進

我們把一些節點從有序表中提取出來,緩存一級索引,就組成了下麵這樣的結構:

skiplist2

現在,我們要查找17這個元素是不是要快很多呢?

我們只要從一級索引往後遍歷即可,只需要經過1、6、15、17這幾個元素就可以找到17了。

那麼,我們要查找11這個元素呢?

我們從一級索引的1開始,向右到6,再向右發現是15,它比11大,此路不通,從6往下走,再從下麵的6往右走,到7,再到11。

同樣地,一級索引也可以往上再提取一層,組成二級索引,如下:

skiplist3

這時候我們再查找17這個元素呢?

只需要經過6、15、17這幾個元素就可以找到17了。

這基本上就是跳錶的核心思想了,其實這也是一個“空間換時間”的演算法,通過向上提取索引增加了查找的效率。

跳錶的插入

上面講的都是跳錶的查詢,那麼,該如何向跳錶中插入元素呢?

比如,我們要向上面這個跳錶添加一個元素8。

首先,我們先根據投硬幣的方式,決定8這個元素要占據的層數,沒錯就是扔硬幣,是不是很好玩兒^^

比如,層數level=2。

然後,找到8這個元素在下麵兩層的前置節點。

接著,就是鏈表的插入元素操作了,比較簡單。

最後,就像下麵這樣:

skiplist4

跳錶的刪除

查詢、插入元素都講了,下麵我們就來說說怎麼刪除元素。

首先,找到各層中包含元素x的節點。

然後,使用標準的鏈表刪除元素的方法刪除即可。

比如,要刪除17這個元素。

skiplist5

標準化的跳錶

上面舉的例子是完全隨機的跳錶,那麼,如果我們每兩個元素提取一個元素作為上一級的索引會怎麼樣呢?

skiplist6

這是不是很像平衡二叉樹,現在這顆樹元素比較少,可能不太明顯,我們來看個元素個數多的情況。

skiplist6

可以看到,上一級元素的個數是下一級的一半,這樣每次減少一半,就很接近平衡二叉樹了。

時間複雜度

我們知道單鏈表查詢的時間複雜度為O(n),而插入、刪除操作需要先找到對應的位置,所以插入、刪除的時間複雜度也是O(n)。

那麼,跳錶的時間複雜度是多少呢?

如果按照標準的跳錶來看的話,每一級索引減少k/2個元素(k為其下麵一級索引的個數),那麼整個跳錶的高度就是(log n)。

學習過平衡二叉樹的同學都知道,它的時間複雜度與樹的高度成正比,即O(log n)。

所以,這裡跳錶的時間複雜度也是O(log n)。(這裡不一步步推倒了,只要記住,查詢時每次減少一半的元素的時間複雜度都是O(log n),比如二叉樹的查找、二分法查找、歸併排序、快速排序)

空間複雜度

我們還是以標準的跳錶來分析,每兩個元素向上提取一個元素,那麼,最後額外需要的空間就是:

n/2 + (n/2)^2 + (n/2)^3 + ... + 8 + 4 + 2 = n - 2

所以,跳錶的空間複雜度是O(n)。

總結

(1)跳錶是可以實現二分查找的有序鏈表;

(2)每個元素插入時隨機生成它的level;

(3)最低層包含所有的元素;

(4)如果一個元素出現在level(x),那麼它肯定出現在x以下的level中;

(5)每個索引節點包含兩個指針,一個向下,一個向右;

(6)跳錶查詢、插入、刪除的時間複雜度為O(log n),與平衡二叉樹接近;

彩蛋

為什麼Redis選擇使用跳錶而不是紅黑樹來實現有序集合?

首先,我們來分析下Redis的有序集合支持的操作:

1)插入元素

2)刪除元素

3)查找元素

4)有序輸出所有元素

5)查找區間內所有元素

其中,前4項紅黑樹都可以完成,且時間複雜度與跳錶一致。

但是,最後一項,紅黑樹的效率就沒有跳錶高了。

在跳錶中,要查找區間的元素,我們只要定位到兩個區間端點在最低層級的位置,然後按順序遍歷元素就可以了,非常高效。

而紅黑樹只能定位到端點後,再從首位置開始每次都要查找後繼節點,相對來說是比較耗時的。

此外,跳錶實現起來很容易且易讀,紅黑樹實現起來相對困難,所以Redis選擇使用跳錶來實現有序集合。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 又有好長時間沒有寫博客了,今天想起來之前的那篇博客還沒有寫完,然後就開始接著寫,本來想把《高性能JavaScript》這本書的知識都羅列進來的,但是......太多了,哎,還是慢慢來,於是就打算分開來寫。 本人JavaScript水平並不是特別高,也只是把自己閱讀《高性能JavaScript》的部分 ...
  • 現在應用都是前後端分離,這也造成前端在調用介面時出現跨域問題,在控制台會這樣提示 ,如果有類似於此圖的提示,就已經表明你的介面調用出現了跨域問題,此文章是我對於vue跨域其中一種方式的一些經驗,如果錯誤,謝謝諒解!!! 首先對於vue跨域,我們可以用代理:在 config --> index.js里 ...
  • 這是我第一次寫博客,主要是記錄下自己解決問題的過程和知識的總結,如有不對的地方歡迎指出來! 需求:點擊btn,彈出modal顯示圖表(以折現圖為例) 這應該是很基本的需求也是很容易實現的,代碼和效果如下: 代碼解釋:setTem是一個方法,改變modal為true,預設為false ; chart- ...
  • 首先我們來瞭解一下什麼是文檔聲明: 文檔聲明就是文檔告訴游覽器該以什麼樣的標準去解析它。游覽器可以解析的文檔可不止html,還有xhtml,xml...當然在這裡我們並不需要知道xhtml、xml是什麼以及和html的區別,我們只需要知道,游覽器可以解析的文檔不止html ,所以文檔聲明是必須的,為 ...
  • 目的:將多個子系統的認證體系打通,實現一個入口多處使用 共用session最簡單最直接。以session存儲的值為用戶憑證,在用戶信息驗證用戶信息管理與業務應用分離的場景下會遇到單點登錄問題,適用體系簡單,考慮基於redis的session共用方案,將整個系統全局cookiesdomain設置於頂級 ...
  • 前言: Iterator翻譯過來就是迭代器的意思。在前面的工廠模式中就介紹過了iterator,不過當時介紹的是方法,現在從Iterator介面的設計來看,似乎又是一種設計模式,下麵我們就來講講迭代器模式到底是怎麼實現的。 一、定義 提供一種方法,順序訪問一個集合對象中的各個元素,而又不暴露該對象的 ...
  • 一、認識多線程中的 start() 和 run() 1。start(): 先來看看Java API中對於該方法的介紹: 使該線程開始執行;Java 虛擬機調用該線程的 run 方法。 結果是兩個線程併發地運行;當前線程(從調用返回給 start 方法)和另一個線程(執行其 run 方法)。 多次啟動 ...
  • springboot項目里怎麼使用swagger2?1.maven依賴 io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2 2.配置 @Configuration @EnableSwagger2 ... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...