github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/coding/container/00-heap-pq.go (about)

     1  package main
     2  
     3  import (
     4  	"container/heap"
     5  	"fmt"
     6  )
     7  
     8  // Item 优先级队列项Item
     9  type Item struct {
    10  	value    string // 数据
    11  	priority int    // 优先级
    12  	index    int    // 堆数组中的索引
    13  }
    14  
    15  // PriorityQueue  优先级队列
    16  type PriorityQueue []*Item
    17  
    18  // 接着heap 接口实现优先级队列, 需要实现 heap 的Push 和Pop
    19  // 及其继承sort.Interface 的 Len, Less, Swap 这五个接口
    20  
    21  // Len 不涉及修改pq 操作的 可以不需要使用指针
    22  func (p PriorityQueue) Len() int {
    23  	return len(p)
    24  }
    25  
    26  // Less 业务定义: 优先级大的 表示优先级高 这里用 > 大于号
    27  func (p PriorityQueue) Less(i, j int) bool {
    28  	return p[i].priority > p[j].priority
    29  }
    30  
    31  // Swap 交换i和j的位置
    32  func (p PriorityQueue) Swap(i, j int) {
    33  	p[i], p[j] = p[j], p[i]
    34  	p[i].index = i
    35  	p[j].index = j
    36  }
    37  
    38  // 涉及到修改堆数组的时候需要用指针
    39  // Push 加入新节点, 注意 堆接口Push 的入口参数是interface{}
    40  func (p *PriorityQueue) Push(x interface{}) {
    41  	// 获得加入新节点的下标位置
    42  	n := len(*p)
    43  	item := x.(*Item)
    44  	item.index = n          // 保存数组索引
    45  	(*p) = append(*p, item) // 添加item
    46  }
    47  
    48  // Pop 出口参数也是interface
    49  func (p *PriorityQueue) Pop() interface{} {
    50  	old := *p        // 用临时变量old 指向优先级队列p
    51  	n := len(old)    // 获取队列长度
    52  	item := old[n-1] // 获取最后一个参数
    53  	// 注意事项
    54  	// 防止内存泄露
    55  	old[n-1] = nil
    56  	// 防止索引被引用
    57  	item.index = -1
    58  	*p = old[:n-1] // 修改队列长度
    59  	return item    // 返回堆顶元素
    60  }
    61  
    62  // update  更新索引和值
    63  func (p *PriorityQueue) update(item *Item, value string, priority int) {
    64  	item.value = value
    65  	item.priority = priority
    66  	heap.Fix(p, item.index) // 索引在更新堆化这里用到, 因为重新调整堆时 需要知道是哪个节点要调整
    67  }
    68  
    69  func main() {
    70  	items := map[string]int{
    71  		"banana": 3, "apple": 2, "pear": 4,
    72  	}
    73  	pq := make(PriorityQueue, len(items)) // 初始化优先级队列, 注意这里填写了len 后面就不要用append 了否则前面len个数据是Nil
    74  	i := 0
    75  	for item, priority := range items {
    76  		pq[i] = &Item{
    77  			value:    item,
    78  			priority: priority,
    79  			index:    i,
    80  		}
    81  		i++
    82  	}
    83  	heap.Init(&pq) // 构建最小堆
    84  	// 插入一个新项,并修改其优先级
    85  	item := &Item{
    86  		value:    "orange",
    87  		priority: 1,
    88  	}
    89  	heap.Push(&pq, item)
    90  	pq.update(item, item.value, 5) // 修改优先级
    91  
    92  	// 读取优先级队列
    93  	for pq.Len() > 0 {
    94  		item := heap.Pop(&pq).(*Item)
    95  		fmt.Printf("%.2d:%s ", item.priority, item.value)
    96  	}
    97  	// 05:orange 04:pear 03:banana 02:apple
    98  }