美文网首页
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