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

Go數(shù)據(jù)結(jié)構(gòu)之HeapMap實(shí)現(xiàn)指定Key刪除堆

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

堆(Heap)

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

問(wèn)題背景

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

二叉堆的概念

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

完全二叉樹(shù)的定義如下:

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

比如下圖就是一顆完全二叉樹(shù):

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

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

優(yōu)先級(jí)隊(duì)列的實(shí)現(xiàn)

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

Item

通過(guò)定義Item結(jié)構(gòu)體來(lái)表示優(yōu)先級(jí)隊(duì)列中的項(xiàng)。每個(gè)項(xiàng)具有值(value)和優(yōu)先級(jí)(priority)。index表示項(xiàng)在優(yōu)先級(jí)隊(duì)列中的索引。

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

PriorityQueue

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

  • Len方法返回優(yōu)先級(jí)隊(duì)列的長(zhǎng)度。

  • Less方法比較兩個(gè)項(xiàng)的優(yōu)先級(jí)。

  • Swap方法交換兩個(gè)項(xiàng)在優(yōu)先級(jí)隊(duì)列中的位置。

  • Push方法向優(yōu)先級(jí)隊(duì)列中添加一個(gè)項(xiàng)。

  • Pop方法移除并返回優(yōu)先級(jí)隊(duì)列中的最小項(xiàng)。

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

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

改進(jìn)

但是我們經(jīng)常有一種場(chǎng)景,需要堆的快速求最值的性質(zhì),又需要能夠支持快速的訪問(wèn)元素,特別是刪除元素。 如果我們要查找堆中的某個(gè)元素,需要遍歷一遍。非常麻煩。

比如延遲任務(wù)的場(chǎng)景,我們可以使用堆對(duì)任務(wù)的到期時(shí)間戳進(jìn)行排序,從而實(shí)現(xiàn)到期任務(wù)自動(dòng)執(zhí)行,但是它沒(méi)辦法支持刪除一個(gè)延遲任務(wù)的需求。

HeapMap

一種能夠快速隨機(jī)訪問(wèn)元素的數(shù)據(jù)結(jié)構(gòu)是哈希表。使用哈希表實(shí)現(xiàn)的map可以在O(1)的時(shí)間復(fù)雜度下進(jìn)行隨機(jī)訪問(wèn)。

另外,堆結(jié)構(gòu)可以在O(log(n))的時(shí)間復(fù)雜度下刪除元素,前提是知道要?jiǎng)h除的元素的下標(biāo)。因此,我們可以將這兩個(gè)數(shù)據(jù)結(jié)構(gòu)結(jié)合起來(lái)使用。使用哈希表記錄堆中每個(gè)元素的下標(biāo),同時(shí)使用堆來(lái)獲取最值元素。

// 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中定義一個(gè)dataMap

dataMap是一個(gè)用于存儲(chǔ)隊(duì)列中的項(xiàng)的映射表,它的好處是可以根據(jù)項(xiàng)的鍵快速地查找到對(duì)應(yīng)的項(xiàng)。 在PriorityQueue中,有一個(gè)數(shù)據(jù)切片data,用于存儲(chǔ)隊(duì)列中的項(xiàng),并且用一個(gè)索引值index來(lái)表示項(xiàng)在切片中的位置。

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

使用dataMap的好處是可以快速地根據(jù)鍵找到對(duì)應(yīng)的項(xiàng),而不需要遍歷整個(gè)切片。這對(duì)于需要頻繁查找和修改項(xiàng)的場(chǎng)景非常重要,可以提高代碼的效率。

如果沒(méi)有dataMap,想要根據(jù)鍵找到對(duì)應(yīng)的項(xiàng)則需要遍歷整個(gè)切片進(jìn)行查找,時(shí)間復(fù)雜度將為O(n)。而使用dataMap可以將查找的時(shí)間復(fù)雜度降低到O(1),提高代碼的性能。

另外,需要注意的是dataMap必須與data切片保持同步,即在對(duì)切片進(jìn)行修改時(shí),需要同時(shí)更新dataMap,保持兩者的一致性。否則,在使用dataMap時(shí)會(huì)出現(xiàn)不一致的情況,導(dǎo)致錯(cuò)誤的結(jié)果。因此,在使用PriorityQueue時(shí),需要確保維護(hù)dataMap和data切片的一致性。

push

在Push時(shí)需要保證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方法可以根據(jù)Key查找并移除對(duì)應(yīng)的元素

// 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
}

測(cè)試用例

func main() {
    // 測(cè)試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")
    }
    // 測(cè)試Pop方法
    poppedItem, err := pq.Pop()
    if err != nil {
        fmt.Println("Pop error:", err)
    } else {
        fmt.Println("Popped item key:", poppedItem.Key)
    }
    // 測(cè)試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)
    }
    // 測(cè)試Len方法
    item3 := &Item{
        Key:      "key3",
        Value:    "value3",
        Priority: 3,
        index:    2,
    }
    pq.Push(item3)
    length := pq.Len()
    fmt.Println("Queue length:", length)
}

以上就是Go數(shù)據(jù)結(jié)構(gòu)之HeapMap實(shí)現(xiàn)指定Key刪除堆的詳細(xì)內(nèi)容,更多關(guān)于Go HeapMap指定Key刪除堆的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go slice 擴(kuò)容實(shí)現(xiàn)原理源碼解析

    go slice 擴(kuò)容實(shí)現(xiàn)原理源碼解析

    這篇文章主要為大家介紹了go slice 擴(kuò)容實(shí)現(xiàn)原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • golang 熔斷限流降級(jí)實(shí)踐

    golang 熔斷限流降級(jí)實(shí)踐

    本文主要介紹了golang 熔斷限流降級(jí)實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • Go來(lái)合并兩個(gè)csv的實(shí)現(xiàn)示例

    Go來(lái)合并兩個(gè)csv的實(shí)現(xiàn)示例

    本文主要介紹了Go來(lái)合并兩個(gè)csv的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • golang連接mysql數(shù)據(jù)庫(kù)操作使用示例

    golang連接mysql數(shù)據(jù)庫(kù)操作使用示例

    這篇文章主要為大家介紹了golang連接mysql數(shù)據(jù)庫(kù)操作使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • Goland 斷點(diǎn)調(diào)試Debug的操作

    Goland 斷點(diǎn)調(diào)試Debug的操作

    這篇文章主要介紹了Goland 斷點(diǎn)調(diào)試Debug的操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Gin的中間件執(zhí)行流程與用法詳解

    Gin的中間件執(zhí)行流程與用法詳解

    我們?cè)谑褂肎in框架進(jìn)行Web開(kāi)發(fā)的時(shí)候,基本上都會(huì)遇到登錄攔截的場(chǎng)景,在Gin當(dāng)中,?中間件和業(yè)務(wù)處理函數(shù)都是一樣的類型,都是一種函數(shù),本文給大家介紹了Gin的中間件執(zhí)行流程與用法,需要的朋友可以參考下
    2024-04-04
  • 一文帶你掌握Golang中的類型斷言

    一文帶你掌握Golang中的類型斷言

    類型斷言是?Golang?中的一個(gè)非常重要的特性,使用類型斷言可以判斷一個(gè)接口的實(shí)際類型是否是預(yù)期的類型,以便進(jìn)行對(duì)應(yīng)的處理,下面就跟隨小編一起深入了解一下Golang中的類型斷言吧
    2024-01-01
  • Go語(yǔ)言繼承功能使用結(jié)構(gòu)體實(shí)現(xiàn)代碼重用

    Go語(yǔ)言繼承功能使用結(jié)構(gòu)體實(shí)現(xiàn)代碼重用

    今天我來(lái)給大家介紹一下在?Go?語(yǔ)言中如何實(shí)現(xiàn)類似于繼承的功能,讓我們的代碼更加簡(jiǎn)潔和可重用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • 深入了解Golang的map增量擴(kuò)容

    深入了解Golang的map增量擴(kuò)容

    這篇文章主要介紹了深入了解Golang的map增量擴(kuò)容,擴(kuò)容的主要目的是為了縮短map容器的響應(yīng)時(shí)間。增量擴(kuò)容的本質(zhì)其實(shí)就是將總的擴(kuò)容時(shí)間分?jǐn)偟搅嗣恳淮蝖ash操作上,更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-06-06
  • Go語(yǔ)言實(shí)現(xiàn)熱更新具體步驟

    Go語(yǔ)言實(shí)現(xiàn)熱更新具體步驟

    這篇文章主要為大家介紹了Go語(yǔ)言實(shí)現(xiàn)熱更新具體步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01

最新評(píng)論

六枝特区| 石柱| 彩票| 图木舒克市| 浦城县| 五常市| 光山县| 苍山县| 江山市| 雷州市| 西丰县| 蓝田县| 临安市| 厦门市| 古蔺县| 顺昌县| 东丽区| 赤壁市| 静乐县| 海安县| 武穴市| 凤山市| 巴林右旗| 虎林市| 鱼台县| 五指山市| 涞水县| 务川| 常宁市| 汽车| 苏尼特左旗| 邹平县| 克什克腾旗| 揭阳市| 容城县| 亚东县| 青田县| 三明市| 格尔木市| 剑阁县| 峨山|