美文网首页
container目录 heap 包

container目录 heap 包

作者: one_zheng | 来源:发表于2019-05-07 20:40 被阅读0次

    go版本:go1.12 windows/amd64

    container目录结构如下:

    ├─heap
        ├─heap.go
        ├─heap_test.go
        ├─example_intheap_test.g
        └─example_pq_test.go
    ├─list
        ├─list.go
        ├─list_test.go
        ├─example_test.go
    └─ring
        ├─ring.go
        ├─ring_test.go
        └─example_test.go
    

    1. heap包介绍
      为所有继承Interface的结构体提供操作,heap是一个根节点为最小值的二叉树(最小堆)
      heap包对任意实现了heap接口的类型提供堆操作。
      树的最小元素在根部,为index 0.
      heap是常用的优先队列的方法,要创建一个优先队列,要实现一个具有负优先级队列,可以通过Less方法的Heap接口,如此一来可用Push添加item,而用Pop取出队列最高优先级的item。(后面解释

    type Interface interface {
        sort.Interface
        Push(x interface{}) // add x as element Len()
        Pop() interface{}   // remove and return element Len() - 1.
    }
    
    // sort.Interface如下:
    // A type, typically a collection, that satisfies sort.Interface can be
    // sorted by the routines in this package. The methods require that the
    // elements of the collection be enumerated by an integer index.
    type Interface interface {
        // Len is the number of elements in the collection.
        Len() int
        // Less reports whether the element with
        // index i should sort before the element with index j.
        Less(i, j int) bool
        // Swap swaps the elements with indexes i and j.
        Swap(i, j int)
    }
    

      其中sort.Interface用于排序,heap.Interface中Push和Pop方法分别用于插入和取出元素的操作

      heap中的方法:

    func Init(h Interface)  // h初始化,按最小堆排序,时间复杂度O(n) ,n等于h.Len()
    func Pop(h Interface) interface{} // 删除并返回堆h中的最小元素(不影响约束性)。复杂度O(log(n)),n等于h.Len()。该函数等价于Remove(h, 0)。
    func Push(h Interface, x interface{})  // 向堆h中插入元素x,并保持堆的约束性。复杂度O(log(n)),n等于h.Len()。
    func Remove(h Interface, i int) interface{}  // 删除堆中的第i个元素,并保持堆的约束性。复杂度O(log(n)),n等于h.Len()。
    func Fix(h Interface, i int)  // 在修改第i个元素后,调用本函数修复堆,比删除第i个元素后插入新元素更有效率。复杂度O(log(n)),n等于h.Len()。
    

    2. heap(小顶堆) 构造原理:
      小顶堆实际上是一个二叉树,满足:Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]规则,即根节点既小于或等于左子树的关键字值,又小于或等于右子树的关键字值。
      由此可知, n/2 - 1之前的节点都为父节点,n/2-1之后的节点都为叶子节点,我们可以从 n/2 - 1节点开始,依次与其左叶子节点h[2(n/2-1)+1]和右叶子节点h[2(n/2-1)+2]比较,取三者最小值做为父节点

    func Init(h Interface) {
        // heapify
        n := h.Len()
        for i := n/2 - 1; i >= 0; i-- {
            down(h, i, n)
        }
    }
    
    func down(h Interface, i0, n int) bool {
        i := i0
        for {
            j1 := 2*i + 1
            if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
                break
            }
            j := j1 // left child
            if j2 := j1 + 1; j2 < n && h.Less(j2, j1) {
                j = j2 // = 2*i + 2  // right child
            }
            if !h.Less(j, i) {
                break
            }
            h.Swap(i, j)
            i = j
        }
        return i > i0
    } 
    //  将末尾元素移跟根节点元素交换,然后使用 down 方法来修复堆。最后Pop()取出末尾元素
    func Pop(h Interface) interface{} {
        n := h.Len() - 1
        h.Swap(0, n)
        down(h, 0, n)
        return h.Pop()
    }
    
    
    // 每次添加新元素,若存在父节点则跟父节点比较,若小于父节点,则交换
    func Push(h Interface, x interface{}) {
        h.Push(x)
        up(h, h.Len()-1)
    }
    func up(h Interface, j int) {
        for {
            i := (j - 1) / 2 // parent
            if i == j || !h.Less(j, i) {
                break
            }
            h.Swap(i, j)
            j = i
        }
    }
    

    3.使用heap构造优先级队列 (nsq源码的优先级队列实现原理)
    思路:通过重写Less()方法,取相反的优先级比较,这样Pop()函数每次取出的即是优先级最高的Item.

    // An Item is something we manage in a priority queue.
    type Item struct {
        value    string // The value of the item; arbitrary.
        priority int    // The priority of the item in the queue.
        // The index is needed by update and is maintained by the heap.Interface methods.
        index int // The index of the item in the heap.
    }
    
    // A PriorityQueue implements heap.Interface and holds Items.
    type PriorityQueue []*Item
    
    func (pq PriorityQueue) Len() int { return len(pq) }
    
    func (pq PriorityQueue) Less(i, j int) bool {
        // We want Pop to give us the highest, not lowest, priority so we use greater than here.
        return pq[i].priority > pq[j].priority
    }
    
    func (pq PriorityQueue) Swap(i, j int) {
        pq[i], pq[j] = pq[j], pq[i]
        pq[i].index = i
        pq[j].index = j
    }
    
    func (pq *PriorityQueue) Push(x interface{}) {
        n := len(*pq)
        item := x.(*Item)
        item.index = n
        *pq = append(*pq, item)
    }
    
    func (pq *PriorityQueue) Pop() interface{} {
        old := *pq
        n := len(old)
        item := old[n-1]
        item.index = -1 // for safety
        *pq = old[0 : n-1]
        return item
    }
    
    
    // update modifies the priority and value of an Item in the queue.
    func (pq *PriorityQueue) update(item *Item, value string, priority int) {
        item.value = value
        item.priority = priority
        heap.Fix(pq, item.index)
    }
    
    // This example creates a PriorityQueue with some items, adds and manipulates an item,
    // and then removes the items in priority order.
    func Example_priorityQueue() {
        // Some items and their priorities.
        items := map[string]int{
            "banana": 3, "apple": 2, "pear": 4,
        }
    
        // Create a priority queue, put the items in it, and
        // establish the priority queue (heap) invariants.
        pq := make(PriorityQueue, len(items))
        i := 0
        for value, priority := range items {
            pq[i] = &Item{
                value:    value,
                priority: priority,
                index:    i,
            }
            i++
        }
        heap.Init(&pq)
    
        // Insert a new item and then modify its priority.
        item := &Item{
            value:    "orange",
            priority: 1,
        }
        heap.Push(&pq, item)
        pq.update(item, item.value, 5)
    
        // Take the items out; they arrive in decreasing priority order.
        for pq.Len() > 0 {
            item := heap.Pop(&pq).(*Item)
            fmt.Printf("%.2d:%s ", item.priority, item.value)
        }
        // Output:
        // 05:orange 04:pear 03:banana 02:apple
    }
    

    相关文章

      网友评论

          本文标题:container目录 heap 包

          本文链接:https://www.haomeiwen.com/subject/dsjjoqtx.html