首頁 後端開發 Golang golang 實作跳表

golang 實作跳表

May 21, 2023 pm 02:15 PM

跳錶是一種基於鍊錶的資料結構,它透過鍊錶中加入一些額外的指針,使得資料的尋找和操作效率相較於普通鍊錶有大幅提升。跳表最初是由William Pugh於1990年提出的,並被廣泛應用於資料庫、搜尋引擎等領域。本文將介紹如何使用Go語言實作跳表資料結構。

一、跳表概述

跳表是一種多層鍊錶結構,每一級鍊錶的資料節點分佈在下一級鍊錶的若干個節點中。跳表中的每個節點都有一個包含多個指標的數組,這些指標指向根節點和下一級鍊錶中同一位置的節點。這些指標是隨機設定或依照一定規則設定的,若設定不當則會導致跳錶退化成為單鍊錶,因此需要合理設定指標的分佈。

跳表支援新增、刪除、尋找等基本操作,其時間複雜度為O(log n),與二元樹的時間複雜度相當。由於跳表結構是基於鍊錶,因此跳表需要使用一定量的額外儲存空間來儲存指標資訊。

二、跳表實作

首先,我們需要定義跳表的節點結構體:

type skipListNode struct {
    Val       int                            // 节点值
    next      []*skipListNode               // 指向下一层节点的指针数组
}
登入後複製

節點結構體中定義了節點的值和指向下一層節點的指標數組next。下一層節點的數量隨機設置,並透過rand.Intn()函數產生。

func newNode(val int, level int) *skipListNode {
    node := &skipListNode{Val: val, next: make([]*skipListNode, level+1)}
    return node
}

func randLevel() int {
    level := 1
    for rand.Float32() < 0.5 {
        level++
    }
    return level
}
登入後複製

在定義完節點結構體和產生隨機層數的函數之後,我們可以定義跳表的結構體:

type skipList struct {
    head   []*skipListNode              // 指向跳表头节点的指针数组
    level  int                           // 当前跳表深度
    length int                          // 跳表节点数量
}
登入後複製

跳表結構體中包含了指向跳表頭節點的指標數組head、目前跳表深度level和跳表節點數量length。跳表的初始深度為1,新增節點時根據隨機數產生的層數來改變深度。

在定義跳表結構體之後,我們可以開始實作跳表的基本運算。首先是插入操作:

func (sl *skipList) insert(val int) {
    level := randLevel()                   // 生成随机层数
    node := newNode(val, level)            // 创建新节点
    update := make([]*skipListNode, level+1) // 用于更新每层跳表的节点指针
    cur := sl.head[sl.level]
    for i := sl.level; i >= 0; i-- {       // 从最高层开始向下查找
        for cur.next[i] != nil && cur.next[i].Val < val { // 查找插入位置
            cur = cur.next[i]
        }
        update[i] = cur                      // 更新每层跳表要插入的位置
    }
    for i := 0; i <= level; i++ {            // 更新每层跳表插入节点
        node.next[i] = update[i].next[i]
        update[i].next[i] = node
    }
    // 更新跳表深度和节点数
    if level > sl.level {
        sl.level = level
    }
    sl.length++
}
登入後複製

插入作業先產生隨機層數,建立新節點,使用update陣列記錄插入每一層跳表時的位置。然後從最高層開始向下尋找要插入的位置,記錄要插入位置的前一個節點,然後更新每層跳表中插入節點和前一個節點的指向。最後更新跳表的深度和節點數量。

接下來是刪除操作:

func (sl *skipList) delete(val int) {
    update := make([]*skipListNode, sl.level+1) // 用于更新每层跳表的节点指针
    cur := sl.head[sl.level]
    for i := sl.level; i >= 0; i-- {
        for cur.next[i] != nil && cur.next[i].Val < val { // 查找要删除的节点位置
            cur = cur.next[i]
        }
        if cur.next[i] != nil && cur.next[i].Val == val { // 找到要删除的节点
            update[i] = cur
        } else {
            update[i] = nil
        }
    }
    if update[0] != nil && update[0].next[0].Val == val { // 更新节点指针
        node := update[0].next[0]
        for i := 0; i <= sl.level && update[i].next[i] == node; i++ {
            update[i].next[i] = node.next[i]
        }
        // 更新跳表深度和节点数
        for sl.level > 0 && len(sl.head[sl.level].next) == 0 {
            sl.level--
        }
        sl.length--
    }
}
登入後複製

刪除操作首先尋找要刪除的節點,記錄其位置和前一個節點的位置。如果找到要刪除的節點,則更新節點指針,更新跳表的深度和節點數量。

最後是查找操作:

func (sl *skipList) search(val int) *skipListNode {
    cur := sl.head[sl.level]
    for i := sl.level; i >= 0; i-- {
        for cur.next[i] != nil && cur.next[i].Val < val { // 查找要查找的节点位置
            cur = cur.next[i]
        }
    }
    if cur.next[0] != nil && cur.next[0].Val == val { // 找到要查找的节点
        return cur.next[0]
    }
    return nil // 没有找到节点,返回nil
}
登入後複製

查找操作基本上與插入和刪除操作類似,從最高層開始向下查找要查找的節點,記錄其位置,如果找到則返回該節點。

三、跳表分析

跳表是一種基於鍊錶的高效資料結構,與平衡二元樹相比,其插入和刪除操作的時間複雜度相同(O(log n )),但查找操作的時間複雜度為O(log n),相較於二元樹的查找時間複雜度O(h)較為高效,其中h為樹的高度。由於隨機層數的設置,跳表的高度也隨機,插入、刪除和查找的效率也更高。

跳表還可以透過合理設定節點數量和指標數量來控制其空間複雜度。在跳表中設定多個指標並消耗更多的儲存空間是一個權衡,為了獲得更好的效能,在一些特定的場景下這些額外的空間開銷是比較合理的。

四、總結

跳表是一種高效的鍊錶資料結構,可以用來取代平衡樹以應對大規模快取資料儲存和搜尋的問題。 Go語言的並發特性和扁平化包結構使得跳表在Go應用程式中非常實用。實現跳表的關鍵在於節點的層數隨機產生、尋找要插入和刪除的位置以及更新節點指標。跳表透過這些基本操作使得其效率與普通鍊錶相比更高,並且可以根據特定的應用場景來合理地設定節點數量和指標數量。

以上是golang 實作跳表的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1660
14
CakePHP 教程
1417
52
Laravel 教程
1311
25
PHP教程
1261
29
C# 教程
1234
24
Golang的目的:建立高效且可擴展的系統 Golang的目的:建立高效且可擴展的系統 Apr 09, 2025 pm 05:17 PM

Go語言在構建高效且可擴展的系統中表現出色,其優勢包括:1.高性能:編譯成機器碼,運行速度快;2.並發編程:通過goroutines和channels簡化多任務處理;3.簡潔性:語法簡潔,降低學習和維護成本;4.跨平台:支持跨平台編譯,方便部署。

Golang和C:並發與原始速度 Golang和C:並發與原始速度 Apr 21, 2025 am 12:16 AM

Golang在並發性上優於C ,而C 在原始速度上優於Golang。 1)Golang通過goroutine和channel實現高效並發,適合處理大量並發任務。 2)C 通過編譯器優化和標準庫,提供接近硬件的高性能,適合需要極致優化的應用。

Golang vs. Python:主要差異和相似之處 Golang vs. Python:主要差異和相似之處 Apr 17, 2025 am 12:15 AM

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。Golang以其并发模型和高效性能著称,Python则以简洁语法和丰富库生态系统著称。

Golang vs. Python:性能和可伸縮性 Golang vs. Python:性能和可伸縮性 Apr 19, 2025 am 12:18 AM

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

Golang的影響:速度,效率和簡單性 Golang的影響:速度,效率和簡單性 Apr 14, 2025 am 12:11 AM

goimpactsdevelopmentpositationality throughspeed,效率和模擬性。 1)速度:gocompilesquicklyandrunseff,IdealforlargeProjects.2)效率:效率:ITScomprehenSevestAndardArdardArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdEcceSteral Depentencies,增強的Depleflovelmentimency.3)簡單性。

表演競賽:Golang vs.C 表演競賽:Golang vs.C Apr 16, 2025 am 12:07 AM

Golang和C 在性能競賽中的表現各有優勢:1)Golang適合高並發和快速開發,2)C 提供更高性能和細粒度控制。選擇應基於項目需求和團隊技術棧。

C和Golang:表演至關重要時 C和Golang:表演至關重要時 Apr 13, 2025 am 12:11 AM

C 更適合需要直接控制硬件資源和高性能優化的場景,而Golang更適合需要快速開發和高並發處理的場景。 1.C 的優勢在於其接近硬件的特性和高度的優化能力,適合遊戲開發等高性能需求。 2.Golang的優勢在於其簡潔的語法和天然的並發支持,適合高並發服務開發。

Golang和C:性能的權衡 Golang和C:性能的權衡 Apr 17, 2025 am 12:18 AM

Golang和C 在性能上的差異主要體現在內存管理、編譯優化和運行時效率等方面。 1)Golang的垃圾回收機制方便但可能影響性能,2)C 的手動內存管理和編譯器優化在遞歸計算中表現更為高效。

See all articles