欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go數據結構之HeapMap實現指定Key刪除堆

 更新時間:2023年07月30日 08:38:01   作者:Goland貓  
這篇文章主要給大家介紹了Go語言數據結構之HeapMap實現指定Key刪除堆,通過使用Go語言中的container/heap包,我們可以輕松地實現一個優(yōu)先級隊列,文中有詳細的代碼示例講解,需要的朋友可以參考下

堆(Heap)

堆(Heap),又稱為優(yōu)先隊列(Priority Queue)。盡管名為優(yōu)先隊列,但堆并不是隊列。在隊列中,我們可以進行的操作是向隊列中添加元素和按照元素進入隊列的順序取出元素。而在堆中,我們不是按照元素進入隊列的先后順序,而是按照元素的優(yōu)先級取出元素。

問題背景

在Linux內核中,調度器根據各個進程的優(yōu)先級來進行程序的執(zhí)行調度。在操作系統(tǒng)運行時,通常會有很多個不同的進程,各自優(yōu)先級也不相同。調度器的作用是讓優(yōu)先級高的進程得到優(yōu)先執(zhí)行,而優(yōu)先級較低的則需要等待。堆是一種適用于實現這種調度器的數據結構。需要提一下,現在Linux內核的調度器使用的是基于紅黑樹的CFS(Completely Fair Scheduler)。

二叉堆的概念

我們常用的二叉堆是一顆任意節(jié)點的優(yōu)先級不小于其子節(jié)點的完全二叉樹。

完全二叉樹的定義如下:

  • 若設二叉樹的高度為h,除第h層外,其它各層(1~h-1)的結點數都達到最大個數,第h層從右向左連續(xù)缺若干結點,這就是完全二叉樹。

比如下圖就是一顆完全二叉樹:

            10
         /     \            
      15        30  
     /  \      /  \
   40    50  100   40

現在假設保存的數值越小的節(jié)點的優(yōu)先級越高,那么上圖就是一個堆。我們將任意節(jié)點不大于其子節(jié)點的堆叫做最小堆或小根堆,將任意節(jié)點不小于其子節(jié)點的堆叫做最大堆或大根堆。因此,上圖就是一個小根堆。

優(yōu)先級隊列的實現

通過使用Go語言中的container/heap包,我們可以輕松地實現一個優(yōu)先級隊列。這個隊列可以用于解決許多問題,如任務調度、事件處理等。通過設置每個項的優(yōu)先級,我們可以確保在處理隊列時按照指定的順序進行操作。

Item

通過定義Item結構體來表示優(yōu)先級隊列中的項。每個項具有值(value)和優(yōu)先級(priority)。index表示項在優(yōu)先級隊列中的索引。

// Item represents an item in the priority queue.
type Item struct {
 value    int // 項的值。
 priority int // 項的優(yōu)先級。
 index    int // 項在隊列中的索引。
}

PriorityQueue

  • PriorityQueue是一個切片類型,實現了heap.Interface接口。它提供了用于操作優(yōu)先級隊列的方法,如插入、刪除和修改。

  • Len方法返回優(yōu)先級隊列的長度。

  • Less方法比較兩個項的優(yōu)先級。

  • Swap方法交換兩個項在優(yōu)先級隊列中的位置。

  • Push方法向優(yōu)先級隊列中添加一個項。

  • Pop方法移除并返回優(yōu)先級隊列中的最小項。

  • Update方法用于修改項的優(yōu)先級并更新其在優(yōu)先級隊列中的位置。

// PriorityQueue 實現了 heap.Interface 接口。
type PriorityQueue []*Item
// Len 返回優(yōu)先級隊列的長度。
func (pq PriorityQueue) Len() int {
     return len(pq)
}
// Less 比較優(yōu)先級隊列中的兩個項。
func (pq PriorityQueue) Less(i, j int) bool {
     return pq[i].priority < pq[j].priority
}
// Swap 交換優(yōu)先級隊列中的兩個項。
func (pq PriorityQueue) Swap(i, j int) {
     pq[i], pq[j] = pq[j], pq[i]
     pq[i].index = i
     pq[j].index = j
}
// Push 向優(yōu)先級隊列中添加一個項。
func (pq *PriorityQueue) Push(x interface{}) {
     item := x.(*Item)
     item.index = len(*pq)
     *pq = append(*pq, item)
}
// Pop 移除并返回優(yōu)先級隊列中的最小項。
func (pq *PriorityQueue) Pop() interface{} {
     old := *pq
     n := len(old)
     item := old[n-1]
     old[n-1] = nil // 避免內存泄漏
     item.index = -1
     *pq = old[0 : n-1]
     return item
}
// Update 修改項的優(yōu)先級并更新其在優(yōu)先級隊列中的位置。
func (pq *PriorityQueue) Update(item *Item, value, priority int) {
 item.value = value
 item.priority = priority
 heap.Fix(pq, item.index)
}

改進

但是我們經常有一種場景,需要堆的快速求最值的性質,又需要能夠支持快速的訪問元素,特別是刪除元素。 如果我們要查找堆中的某個元素,需要遍歷一遍。非常麻煩。

比如延遲任務的場景,我們可以使用堆對任務的到期時間戳進行排序,從而實現到期任務自動執(zhí)行,但是它沒辦法支持刪除一個延遲任務的需求。

HeapMap

一種能夠快速隨機訪問元素的數據結構是哈希表。使用哈希表實現的map可以在O(1)的時間復雜度下進行隨機訪問。

另外,堆結構可以在O(log(n))的時間復雜度下刪除元素,前提是知道要刪除的元素的下標。因此,我們可以將這兩個數據結構結合起來使用。使用哈希表記錄堆中每個元素的下標,同時使用堆來獲取最值元素。

// PriorityQueue facilitates queue of Items, providing Push, Pop, and
// PopByKey convenience methods. The ordering (priority) is an int64 value
// with the smallest value is the highest priority. PriorityQueue maintains both
// an internal slice for the queue as well as a map of the same items with their
// keys as the index. This enables users to find specific items by key. The map
// must be kept in sync with the data slice.
// See https://golang.org/pkg/container/heap/#example__priorityQueue
type PriorityQueue struct {
   // data is the internal structure that holds the queue, and is operated on by
   // heap functions
   data queue
   // dataMap represents all the items in the queue, with unique indexes, used
   // for finding specific items. dataMap is kept in sync with the data slice
   dataMap map[string]*Item
   // lock is a read/write mutex, and used to facilitate read/write locks on the
   // data and dataMap fields
   lock sync.RWMutex
}
// queue is the internal data structure used to satisfy heap.Interface. This
// prevents users from calling Pop and Push heap methods directly
type queue []*Item
// Item is something managed in the priority queue
type Item struct {
   // Key is a unique string used to identify items in the internal data map
   Key string
   // Value is an unspecified type that implementations can use to store
   // information
   Value interface{}
   // Priority determines ordering in the queue, with the lowest value being the
   // highest priority
   Priority int64
   // index is an internal value used by the heap package, and should not be
   // modified by any consumer of the priority queue
   index int
}

PriorityQueue中定義一個dataMap

dataMap是一個用于存儲隊列中的項的映射表,它的好處是可以根據項的鍵快速地查找到對應的項。 在PriorityQueue中,有一個數據切片data,用于存儲隊列中的項,并且用一個索引值index來表示項在切片中的位置。

dataMap則以項的鍵作為索引,將項的指針映射到該鍵上。

使用dataMap的好處是可以快速地根據鍵找到對應的項,而不需要遍歷整個切片。這對于需要頻繁查找和修改項的場景非常重要,可以提高代碼的效率。

如果沒有dataMap,想要根據鍵找到對應的項則需要遍歷整個切片進行查找,時間復雜度將為O(n)。而使用dataMap可以將查找的時間復雜度降低到O(1),提高代碼的性能。

另外,需要注意的是dataMap必須與data切片保持同步,即在對切片進行修改時,需要同時更新dataMap,保持兩者的一致性。否則,在使用dataMap時會出現不一致的情況,導致錯誤的結果。因此,在使用PriorityQueue時,需要確保維護dataMap和data切片的一致性。

push

在Push時需要保證Key值唯一

func (pq *PriorityQueue) Push(i *Item) error {
   if i == nil || i.Key == "" {
      return errors.New("error adding item: Item Key is required")
   }
   pq.lock.Lock()
   defer pq.lock.Unlock()
   if _, ok := pq.dataMap[i.Key]; ok {
      return ErrDuplicateItem
   }
   // Copy the item value(s) so that modifications to the source item does not
   // affect the item on the queue
   clone, err := copystructure.Copy(i)
   if err != nil {
      return err
   }
   pq.dataMap[i.Key] = clone.(*Item)
   heap.Push(&pq.data, clone)
   return nil
}

pop

PopByKey方法可以根據Key查找并移除對應的元素

// PopByKey searches the queue for an item with the given key and removes it
// from the queue if found. Returns nil if not found. This method must fix the
// queue after removing any key.
func (pq *PriorityQueue) PopByKey(key string) (*Item, error) {
   pq.lock.Lock()
   defer pq.lock.Unlock()
   item, ok := pq.dataMap[key]
   if !ok {
      return nil, nil
   }
   // Remove the item the heap and delete it from the dataMap
   itemRaw := heap.Remove(&pq.data, item.index)
   delete(pq.dataMap, key)
   if itemRaw != nil {
      if i, ok := itemRaw.(*Item); ok {
         return i, nil
      }
   }
   return nil, nil
}

測試用例

func main() {
    // 測試Push方法
    pq := &PriorityQueue{
        data:    queue{},
        dataMap: make(map[string]*Item),
    }
    item := &Item{
        Key:      "key1",
        Value:    "value1",
        Priority: 1,
        index:    0,
    }
    err := pq.Push(item)
    if err != nil {
        fmt.Println("Push error:", err)
    } else {
        fmt.Println("Push success")
    }
    // 測試Pop方法
    poppedItem, err := pq.Pop()
    if err != nil {
        fmt.Println("Pop error:", err)
    } else {
        fmt.Println("Popped item key:", poppedItem.Key)
    }
    // 測試PopByKey方法
    item2 := &Item{
        Key:      "key2",
        Value:    "value2",
        Priority: 2,
        index:    1,
    }
    pq.Push(item2)
    poppedItemByKey, err := pq.PopByKey("key2")
    if err != nil {
        fmt.Println("PopByKey error:", err)
    } else if poppedItemByKey == nil {
        fmt.Println("Item not found")
    } else {
        fmt.Println("Popped item by key:", poppedItemByKey.Key)
    }
    // 測試Len方法
    item3 := &Item{
        Key:      "key3",
        Value:    "value3",
        Priority: 3,
        index:    2,
    }
    pq.Push(item3)
    length := pq.Len()
    fmt.Println("Queue length:", length)
}

以上就是Go數據結構之HeapMap實現指定Key刪除堆的詳細內容,更多關于Go HeapMap指定Key刪除堆的資料請關注腳本之家其它相關文章!

相關文章

  • Golang合并yaml文件過程逐步講解

    Golang合并yaml文件過程逐步講解

    之前一直從事java開發(fā),習慣了使用yaml文件的格式,尤其是清晰的層次結構、注釋,下面這篇文章主要給大家介紹了關于Golang合并yaml文件的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-01-01
  • 利用dep代替go get獲取私有庫的方法教程

    利用dep代替go get獲取私有庫的方法教程

    go get 從指定源上面下載或者更新指定的代碼和依賴,并對他們進行編譯和安裝,但go get功能比較差,所以下面這篇文章主要給大家介紹了關于利用dep代替go get獲取私有庫的相關資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-11-11
  • Go語言學習之JSON編碼解析與使用

    Go語言學習之JSON編碼解析與使用

    這篇文章主要為大家詳細介紹了Go語言中JSON編碼的解析與使用已經JSON與Map、結構體的互相轉化,文中的示例代碼講解詳細,需要的可以參考一下
    2023-02-02
  • Go如何優(yōu)雅的關閉goroutine協(xié)程

    Go如何優(yōu)雅的關閉goroutine協(xié)程

    本文將介紹首先為什么需要主動關閉goroutine,并介紹如何在Go語言中關閉goroutine的常見套路,包括傳遞終止信號和協(xié)程內部捕捉終止信號,之后,文章列舉了需要主動關閉協(xié)程運行的常見場景,希望通過本文的介紹,讀者能夠掌握如何在適當的時候關閉goroutine
    2023-05-05
  • Golang實現帶優(yōu)先級的select

    Golang實現帶優(yōu)先級的select

    這篇文章主要為大家詳細介紹了如何在Golang中實現帶優(yōu)先級的select,文中的示例代碼講解詳細,對我們學習Golang有一定的幫助,需要的可以參考一下
    2023-04-04
  • Golang創(chuàng)建第一個web項目(Gin+Gorm)

    Golang創(chuàng)建第一個web項目(Gin+Gorm)

    本文主要介紹了Golang創(chuàng)建第一個web項目(Gin+Gorm),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-06-06
  • 詳解Golang中日志庫glog的使用

    詳解Golang中日志庫glog的使用

    golang/glog?是?C++?版本?google/glog?的?Go?版本實現,基本實現了原生?glog?的日志格式,下面大家就跟隨小編一起了解一下glog的具體使用吧
    2023-09-09
  • golang協(xié)程設計及調度原理

    golang協(xié)程設計及調度原理

    這篇文章主要介紹了golang協(xié)程設計及調度原理,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下
    2022-06-06
  • golang使用redis實現全文搜索功能詳解

    golang使用redis實現全文搜索功能詳解

    這篇文章主要為大家詳細介紹了golang如何使用redis實現全文搜索功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-02-02
  • 在Go程序中實現服務器重啟的方法

    在Go程序中實現服務器重啟的方法

    這篇文章主要介紹了在Go程序中實現服務器重啟的方法,由于很多人盲目崇拜谷歌"親爹",Go語言在國內有著不尋常的人氣,需要的朋友可以參考下
    2015-06-06

最新評論