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

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package heap provides heap operations for any type that implements
     6  // heap.Interface. A heap is a tree with the property that each node is the
     7  // minimum-valued node in its subtree.
     8  //
     9  // The minimum element in the tree is the root, at index 0.
    10  //
    11  // A heap is a common way to implement a priority queue. To build a priority
    12  // queue, implement the Heap interface with the (negative) priority as the
    13  // ordering for the Less method, so Push adds items while Pop removes the
    14  // highest-priority item from the queue. The Examples include such an
    15  // implementation; the file example_pq_test.go has the complete source.
    16  // 堆是实现优先级队列的常用方法
    17  package heap
    18  
    19  import "sort"
    20  
    21  // The Interface type describes the requirements
    22  // for a type using the routines in this package.
    23  // Any type that implements it may be used as a
    24  // min-heap with the following invariants (established after
    25  // Init has been called or if the data is empty or sorted):
    26  //
    27  //	!h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len()
    28  //
    29  // Note that Push and Pop in this interface are for package heap's
    30  // implementation to call. To add and remove things from the heap,
    31  // use heap.Push and heap.Pop.
    32  /*
    33  根据上面interface的定义,这个堆结构继承自sort.Interface,
    34  而sort.Interface,须要实现三个方法:Len(), Less() , Swap()
    35  
    36  此外还需实现堆接口定义的两个方法:Push(x interface{})   /  Pop() interface{},
    37  因此想使用heap定义一个堆, 只须要定义实现了这五个方法结构就能够了
    38  
    39  任何实现了本接口的类型均可以用于构建最小堆。最小堆能够经过heap.Init创建,数
    40  据是递增顺序或者空的话也是最小堆。
    41  最小堆的约束条件
    42  !h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len()
    43  */
    44  type Interface interface {
    45  	sort.Interface
    46  	Push(x interface{}) // add x as element Len()
    47  	Pop() interface{}   // remove and return element Len() - 1.
    48  }
    49  
    50  // Init establishes the heap invariants required by the other routines in this package.
    51  // Init is idempotent with respect to the heap invariants
    52  // and may be called whenever the heap invariants may have been invalidated.
    53  // The complexity is O(n) where n = h.Len().
    54  // 时间复杂度 O(n)
    55  // 构建一个最小堆
    56  // 初始化一个堆。一个堆在使用任何堆操做以前应先初始化。
    57  // Init函数对于堆的约束性是幂等的(屡次执行无心义),并可能在任什么时候候堆的约束性被破坏时被调用。
    58  // 本函数复杂度为O(n),其中n等于h.Len()。
    59  func Init(h Interface) {
    60  	// heapify
    61  	n := h.Len()
    62  	// i 从 n/2 -1 开始, 然后i-- 到 i>=0
    63  	for i := n/2 - 1; i >= 0; i-- {
    64  		down(h, i, n)
    65  	}
    66  }
    67  
    68  // Push pushes the element x onto the heap.
    69  // The complexity is O(log n) where n = h.Len().
    70  // 加一个数据到堆中, 从尾部加入, 然后向上调整 达到最小堆
    71  func Push(h Interface, x interface{}) {
    72  	h.Push(x)
    73  	up(h, h.Len()-1)
    74  }
    75  
    76  // Pop removes and returns the minimum element (according to Less) from the heap.
    77  // The complexity is O(log n) where n = h.Len().
    78  // Pop is equivalent to Remove(h, 0).
    79  // 返回顶部的最小元素, 并删除它
    80  // 然后将最后一个元素挪到顶部,以保证为完全二叉树
    81  // 然后向下比较左右子节点,调整为满足最小堆
    82  // //删除并返回堆h中的最小元素(不影响约束性)。复杂度O(log(n)),其中n等于h.Len()。该函数等价于Remove(h, 0)。
    83  func Pop(h Interface) interface{} {
    84  	n := h.Len() - 1
    85  	h.Swap(0, n)   // 交换了堆顶和堆尾元素
    86  	down(h, 0, n)  // 调整最小堆
    87  	return h.Pop() // 是的这里返回的数组最后一个元素其实就是堆顶元素
    88  }
    89  
    90  // Remove removes and returns the element at index i from the heap.
    91  // The complexity is O(log n) where n = h.Len().
    92  // 删除第i个元素 操作复杂度 O(log n)
    93  func Remove(h Interface, i int) interface{} {
    94  	n := h.Len() - 1 // 堆最后一个元素的下标
    95  	if n != i {      //如果要移除的元素不是最后一个
    96  		h.Swap(i, n)        // 交换移除的元素和最后一个元素
    97  		if !down(h, i, n) { // 从交换之后的元素到堆最后进行堆化调整,如果不需要调整
    98  			up(h, i) // 则向上调整
    99  			// 这里要注意, 如果down 向下调整了, 说明这个i元素一定大于其左右子节点元素
   100  			// 那么也大于其父节点元素, 因为是最小堆,所以只需要向下down即可;
   101  			// 如果i 被它左右节点都小,那么不需要down, 有可能比它的父节点也小,所以需要up调整
   102  		}
   103  	}
   104  	// pop最后元素, 堆顶元素
   105  	return h.Pop()
   106  }
   107  
   108  // Fix re-establishes the heap ordering after the element at index i has changed its value.
   109  // Changing the value of the element at index i and then calling Fix is equivalent to,
   110  // but less expensive than, calling Remove(h, i) followed by a Push of the new value.
   111  // The complexity is O(log n) where n = h.Len().
   112  // Fix 表示在修改第i节点值后 调整最小堆
   113  // 在修改第i个元素后,调用本函数修复堆,比删除第i个元素后插入新元素更有效率。复杂度O(log(n)),其中n等于h.Len()。
   114  func Fix(h Interface, i int) {
   115  	if !down(h, i, h.Len()) {
   116  		up(h, i)
   117  	}
   118  }
   119  
   120  func up(h Interface, j int) {
   121  	for {
   122  		i := (j - 1) / 2 // parent
   123  		// 如果父节点与插入节点相同,表示时第一个节点
   124  		// 因为golang 是向0取整  所以 (0-1)/2 = 0
   125  		// h.Less(j,i) 表示插入节点比父节点小
   126  		// !h.Less(j, i) 表示父节点比插入节点小, 那么符合最小堆,则不需要调整
   127  		if i == j || !h.Less(j, i) {
   128  			break
   129  		}
   130  		// 插入值比父节点值小, 则需要交换,是的父节点值最小 才满足最小堆
   131  		h.Swap(i, j)
   132  		// 更新插入值待插入的索引位置, 为其当前的父节点位置
   133  		j = i
   134  	}
   135  }
   136  
   137  // 构建最小堆时, 待插入位置是堆顶,所以是与其左右子节点比较, 即向下调整堆 down
   138  func down(h Interface, i0, n int) bool {
   139  	i := i0 // 父节点
   140  	for {
   141  		j1 := 2*i + 1 // 左节点
   142  		// 左节点 >= 堆大小,表示当前节点已经是叶子节点了
   143  		// j1 考虑到溢出异常 < 0
   144  		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
   145  			break
   146  		}
   147  		j := j1 // left child
   148  		// j2 = j1 + 1 右节点
   149  		// j2 < n 表示右节点存在
   150  		// h.Less(j2, j1) 为true 表示 j2 < j1
   151  		// 取最小值 用于构建最小堆
   152  		if j2 := j1 + 1; j2 < n && h.Less(j2, j1) {
   153  			j = j2 // = 2*i + 2  // right child
   154  		}
   155  		// h.Less(j, i) 表示子节点值小于父节点值 j < i
   156  		// !h.Less(j, i) 表示父节点值小于子节点值 i < j
   157  		// 最小堆构建时, 如果父节点值小于子节点值 则直接退出
   158  		if !h.Less(j, i) {
   159  			break
   160  		}
   161  		// 否则交换节点值
   162  		h.Swap(i, j)
   163  		// 更换新的父节点索引位置, 继续与其左右子节点比较
   164  		i = j
   165  	}
   166  	// i > i0 表示 发生了down 堆化交换
   167  	return i > i0
   168  }