堆的结构,堆的创建,节点添加与删除

题目来源:好未来

答案:

堆就是用数组实现的二叉树,所以它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置.

如下图这是一个最大堆,,因为每一个父节点的值都比其子节点要大。10 比 7 和 2 都大。7 比 5 和 1都大。

最大堆

根据这一属性,那么最大堆总是将其中的最大值存放在树的根节点。而对于最小堆,根节点中的元素总是树中的最小值。堆属性非常有用,因为堆常常被当做优先队列使用,因为可以快速地访问到“最重要”的元素。

在Go中,堆结构的实现依赖于使用者对于Heap接口进行实现,更加灵活,但也需要额外的工作量。Go 提供了container/heap这个包来实现堆的操作。
堆中元素的类型需要实现 heap.Interface 这个接口:

  1. type Interface interface {
  2. sort.Interface
  3. Push(x interface{}) // 将x添加至元素Len()的位置
  4. Pop() interface{} // 移除并且返回元素Len()-1
  5. }

其中 sort.Interface 包括 Len(), Less, Swap 方法

以一个int类型的堆来举例,在使用堆之前,我们需要定义堆的底层数组:type IntHeap []int
然后,我们需要实现heap.Interface接口的五个函数:

  1. func (h IntHeap) Len() int { return len(h) }
  2. func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
  3. func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
  4. func (h *IntHeap) Push(x interface{}) {
  5. *h = append(*h, x.(int))
  6. }
  7. func (h *IntHeap) Pop() interface{} {
  8. old := *h
  9. n := len(old)
  10. x := old[n-1]
  11. *h = old[0 : n-1]
  12. return x
  13. }

需要注意的是,在实现Push()和Pop()的时候,需要使用指针接收,而不能简单的使用对象。因为Go语言结构体传参默认是传值,而在修改切片长度的时候有可能导致切片的引用发生改变,所以需要使用指针接收。

然后,我们就可以使用这个堆了:

  1. func Example_intHeap() {
  2. h := &IntHeap{2, 1, 5}
  3. heap.Init(h)
  4. heap.Push(h, 3)
  5. fmt.Printf("堆顶元素: %d
  6. ", (*h)[0])
  7. for h.Len() > 0 {
  8. fmt.Printf("%d ", heap.Pop(h))
  9. }
  10. // 输出:
  11. // 堆顶元素: 1
  12. // 1 2 3 5
  13. }

关于节点的增加和删除
可参考这三个内置函数:
Push():

Push()将元素x插入到堆中。

Push()的时间复杂度是O(log n),n为堆中的元素个数。

  1. func Push(h Interface, x interface{}) {
  2. h.Push(x)
  3. up(h, h.Len()-1)
  4. }

从源码可以看出,实际的插入步骤是先调用heap.Interface接口的Push()函数将待插入的元素x放到堆中的最后一个位置,然后对其进行上浮操作。

Pop():
Pop()从堆中移除并返回最小元素(根据Less)。也就是堆顶元素。

Pop()的时间复杂度是O(log n),n为堆中的元素个数。

Pop()等价于Remove(h, 0)。

  1. func Pop(h Interface) interface{} {
  2. n := h.Len() - 1
  3. h.Swap(0, n)
  4. down(h, 0, n)
  5. return h.Pop()
  6. }

从源码可以看出,实际的弹出步骤是将第一个元素与最后一个元素进行互换,然后对一个元素进行下沉,最后移除并返回最后一个元素。

Remove:
Remove()从堆中移除并返回第i个元素。

Remove()的时间复杂度是O(log n),n为堆中的元素个数。

  1. func Remove(h Interface, i int) interface{} {
  2. n := h.Len() - 1
  3. if n != i {
  4. h.Swap(i, n)
  5. if !down(h, i, n) {
  6. up(h, i)
  7. }
  8. }
  9. return h.Pop()
  10. }