github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/minheap/heap.go (about) 1 // go sdk中的container包下已经有了heap堆的结构,此处实现按照java的思路实现 2 package minheap 3 4 //比较大小 相等返回0 , 当前这个数小返回负数 ,当前数大返回正数 5 type CompareFunc func(v1, v2 interface{}) int 6 7 type Heap struct { 8 data []interface{} //存储的数据 9 cmpFunc CompareFunc // 比较函数 10 } 11 12 func NewHeap(f CompareFunc) *Heap { 13 return &Heap{cmpFunc: f} 14 } 15 16 /* 17 heapifyd 是时间复杂度是 O(n) 级别 18 排序复杂度是 O(nlogn) , 非稳定性排序 19 */ 20 func (h *Heap) Heapify(d ...interface{}) { 21 // 思路: 跳过叶子节点,对最小的父节点进行下沉操作,一直到根部 22 // 最小的叶子节点的服节点就 parent(len()-1) 23 if d == nil || len(d) == 0 { 24 return 25 } 26 h.data = make([]interface{}, len(d)) 27 copy(h.data, d) 28 for i := parent(h.Len() - 1); i >= 0; i-- { 29 h.siftDown(i) 30 } 31 } 32 33 func (h *Heap) Len() int { 34 if h.data == nil { 35 return 0 36 } 37 return len(h.data) 38 } 39 40 func (h *Heap) Poll() interface{} { 41 //1. 取出队头元素 42 //2. 将对尾元素,移到顶部 43 //3. 移除尾部 44 //4. 对头部下沉 45 if h.Len() == 0 { 46 return nil 47 } 48 ret := h.Peek() 49 h.swap(0, h.Len()-1) 50 h.data = h.data[:h.Len()-1] 51 h.siftDown(0) 52 return ret 53 } 54 55 // 移除对应的元素 56 func (h *Heap) Remove(e interface{}, eqFunc func(e, b interface{}) bool) interface{} { 57 // 1 找到对应元素 58 var find interface{} 59 var fi int 60 for i := 0; i < h.Len(); i++ { 61 if eqFunc(e, h.data[i]) { 62 find = h.data[i] 63 fi = i 64 break 65 } 66 } 67 if find == nil { 68 //没有找到 69 return nil 70 } else { 71 // 与最后一个值进行替换 72 h.swap(fi, h.Len()-1) 73 h.data = h.data[:h.Len()-1] //移除最后一个 74 // 下沉下标 75 h.siftDown(fi) 76 return find 77 } 78 } 79 80 func (h *Heap) Peek() interface{} { 81 if h.Len() == 0 { 82 return nil 83 } 84 return h.data[0] 85 } 86 87 func (h *Heap) Add(e interface{}) { 88 h.data = append(h.data, e) 89 h.siftUp(h.Len() - 1) 90 } 91 92 // 上浮 93 func (h *Heap) siftUp(i int) { 94 ci := i 95 pi := parent(ci) 96 for ci > 0 && h.cmpFunc(h.data[ci], h.data[pi]) < 0 { 97 h.swap(pi, ci) 98 ci = pi 99 pi = parent(ci) 100 } 101 } 102 103 // 下沉 104 func (h *Heap) siftDown(i int) { 105 ci := i 106 dataLen := h.Len() // 数据大小 107 for leftChild(ci) < dataLen { 108 mi := leftChild(ci) // 较小值的孩子的下标 109 if mi+1 < dataLen && h.cmpFunc(h.data[mi], h.data[mi+1]) > 0 { // mi + 1 表示右边下标 110 // 右孩子的值小些 111 mi += 1 112 } 113 if h.cmpFunc(h.data[ci], h.data[mi]) <= 0 { 114 // 已经比孩子小了不用下沉 115 break 116 } 117 h.swap(mi, ci) 118 ci = mi 119 } 120 } 121 122 func (h *Heap) swap(i, j int) { 123 h.data[i], h.data[j] = h.data[j], h.data[i] 124 } 125 126 // 返回完全二叉堆的数组表示中,一个索引所表示的元素的父亲节点的索引 127 func parent(index int) int { 128 if index <= 0 { 129 return -1 //表示没有父节点索引 130 } 131 return (index - 1) / 2 132 } 133 134 // 左孩子下标 135 func leftChild(index int) int { 136 return index*2 + 1 137 } 138 139 // 右孩子下标 140 func rightChild(index int) int { 141 return index*2 + 2 142 }