缘起

最近阅读<<我的第一本算法书>>(【日】石田保辉;宫崎修一)
本系列笔记拟采用golang练习之

  1. 堆是一种图的树形结构,
  2. 被用于实现“优先队列”(priority queues)。
  3. 优先队列是一种数据结构,
  4. 可以自由添加数据,
  5. 但取出数据时要从最小值开始按顺序取出。
  6. 在堆中存储数据时必须遵守这样一条规则:子结点必定大于父结点。
  7. 摘自 <<我的第一本算法书>> 【日】石田保辉;宫崎修一

补充知识

  • 堆又名二叉堆, 是一种无序完全二叉树
  • 所谓完全, 是指节点从上至下, 从左至右填充, 中间不会留有空隙
  • 完全二叉树可以使用数组存放, 因为其父节点与左右子节点的索引存在线性关系, 以0下标为例
    • parent.index = (child.index - 1) / 2, 如: 0 = (1 - 1) / 2, 0 = (2 - 1) / 2
    • left.index = parent.index 2 + 1, 如: 1 = 0 2 + 1
    • right.index = left.index + 1
  • 添加数据时
    • 先把数据添加到最末尾, 也就是堆的右下角
    • 然后跟父节点比较大小, 如果小于父节点, 则交换之, 又名上浮
    • 重复上浮的过程, 直到满足堆的规则: 父节点总是小于子节点
  • 弹出数据时
    • 总是弹出堆顶, 也就是最小值
    • 然后把堆的末尾值, 放到堆顶. 移动末尾值, 不会留下中间间隙, 保持完全二叉树的状态
    • 如果堆顶值大于某个子节点的值, 则与最小值节点交换, 又名下沉
    • 重复下沉的过程, 直到满足堆的规则: 父节点总是小于子节点

目标

  • 基于数组实现二叉堆, 并验证堆排序的功能

设计

  • IComparator: 值大小比较器接口. 通过翻转比较函数, 也可以创建最大堆(顶点值最大).
  • IHeap: 定义堆的接口
  • IIterator: 堆迭代器接口
  • tComparator: 值大小比较器, 实现IComparator接口, 具体比较函数由外部传入
  • tArrayHeap: 二叉堆, 实现IHeap接口
  • tHeapIterator: 堆迭代器, 实现IIterator接口

单元测试

heap_test.go, 验证二叉堆的基本功能, 并通过随机Push和有序Pop, 观察堆排序的效果

  1. package data_structure
  2. import (
  3. "fmt"
  4. "learning/gooop/data_structure/heap"
  5. "math/rand"
  6. "strings"
  7. "testing"
  8. "time"
  9. )
  10. func Test_Heap(t *testing.T) {
  11. fnAssertTrue := func(b bool, msg string) {
  12. if !b {
  13. panic(msg)
  14. }
  15. }
  16. fnLess := func(a interface{}, b interface{}) bool {
  17. i1 := a.(int)
  18. i2 := b.(int)
  19. return i1 < i2
  20. }
  21. comparator := heap.NewComparator(fnLess)
  22. // test empty heap
  23. hp := heap.NewArrayHeap(comparator)
  24. fnAssertTrue(hp.Size() == 0, "expecting size == 0")
  25. fnAssertTrue(hp.IsEmpty(), "expecting empty")
  26. fnAssertTrue(hp.Iterator().More() == false, "expecting !more")
  27. e,_ := hp.Pop()
  28. fnAssertTrue(e != nil, "expecting e != nil")
  29. // push random samples
  30. rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
  31. samples := 13
  32. for i := 0;i < samples;i++ {
  33. hp.Push(rnd.Intn(100))
  34. }
  35. t.Log(hp)
  36. // test iterator
  37. iter := hp.Iterator()
  38. itemStrings := make([]string, 0)
  39. for i := 0;i < samples;i++ {
  40. e, v := iter.Next()
  41. fnAssertTrue(e == nil, "expecting e == nil")
  42. itemStrings = append(itemStrings, fmt.Sprintf("%v", v))
  43. }
  44. fnAssertTrue(iter.More() == false, "expecting !more")
  45. e,_ = iter.Next()
  46. fnAssertTrue(e != nil, "expecting e != nil")
  47. t.Log(strings.Join(itemStrings, ","))
  48. // test heap sort
  49. prev := -1
  50. size := hp.Size()
  51. for i := 0;i < size;i++ {
  52. e,v := hp.Pop()
  53. fnAssertTrue(e == nil, "expecting e == nil")
  54. n := v.(int)
  55. t.Logf("%d = %d", i, n)
  56. if prev >= 0 {
  57. fnAssertTrue(prev <= n, "expecting prev <= n")
  58. prev = n
  59. }
  60. prev = n
  61. }
  62. fnAssertTrue(hp.IsEmpty(), "expecting empty")
  63. // test 0-9
  64. for i := 0;i < 10;i++ {
  65. hp.Push(i)
  66. }
  67. itemStrings = make([]string, 0)
  68. for ;hp.IsNotEmpty(); {
  69. e,v := hp.Pop()
  70. fnAssertTrue(e == nil, "expecting e == nil")
  71. itemStrings = append(itemStrings, fmt.Sprintf("%v", v))
  72. }
  73. s := strings.Join(itemStrings, ",")
  74. t.Log(s)
  75. fnAssertTrue(s == "0,1,2,3,4,5,6,7,8,9", "expecting 0-9")
  76. }

测试输出

  1. $ go test -v heap_test.go
  2. === RUN Test_Heap
  3. heap_test.go:41:
  4. 0
  5. 12, 36
  6. 36, 17, 37, 53
  7. 69, 37, 79, 22, 69, 37
  8. heap_test.go:54: 0,12,36,36,17,37,53,69,37,79,22,69,37
  9. heap_test.go:64: 0 = 0
  10. heap_test.go:64: 1 = 12
  11. heap_test.go:64: 2 = 17
  12. heap_test.go:64: 3 = 22
  13. heap_test.go:64: 4 = 36
  14. heap_test.go:64: 5 = 36
  15. heap_test.go:64: 6 = 37
  16. heap_test.go:64: 7 = 37
  17. heap_test.go:64: 8 = 37
  18. heap_test.go:64: 9 = 53
  19. heap_test.go:64: 10 = 69
  20. heap_test.go:64: 11 = 69
  21. heap_test.go:64: 12 = 79
  22. heap_test.go:84: 0,1,2,3,4,5,6,7,8,9
  23. --- PASS: Test_Heap (0.00s)
  24. PASS
  25. ok command-line-arguments 0.002s

IComparator.go

值大小比较器接口. 通过翻转比较函数, 也可以创建最大堆(顶点值最大).

  1. package heap
  2. type IComparator interface {
  3. Less(a interface{}, b interface{}) bool
  4. }

IHeap.go

定义堆的接口

  1. package heap
  2. type IHeap interface {
  3. Size() int
  4. IsEmpty() bool
  5. IsNotEmpty() bool
  6. Push(value interface{})
  7. Pop() (error, interface{})
  8. Iterator() IIterator
  9. String() string
  10. }

IIterator.go

堆迭代器接口

  1. package heap
  2. type IIterator interface {
  3. More() bool
  4. Next() (error,interface{})
  5. }

tComparator.go

值大小比较器, 实现IComparator接口, 具体比较函数由外部传入

  1. package heap
  2. import "errors"
  3. type FnLess func(a interface{}, b interface{}) bool
  4. type tComparator struct {
  5. fnLess FnLess
  6. }
  7. func NewComparator(fn FnLess) IComparator {
  8. if fn == nil {
  9. panic(gNullArgumentError)
  10. }
  11. return &tComparator{
  12. fnLess: fn,
  13. }
  14. }
  15. func (me *tComparator) Less(a interface{}, b interface{}) bool {
  16. if a == nil || b == nil {
  17. panic(gNullArgumentError)
  18. }
  19. return me.fnLess(a, b)
  20. }
  21. var gNullArgumentError = errors.New("null argument error")

tArrayHeap.go

二叉堆, 实现IHeap接口

  1. package heap
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. )
  7. type tArrayHeap struct {
  8. comparator IComparator
  9. items []interface{}
  10. size int
  11. version int64
  12. }
  13. func NewArrayHeap(comparator IComparator) IHeap {
  14. return &tArrayHeap{
  15. comparator: comparator,
  16. items: make([]interface{}, 0),
  17. size: 0,
  18. version: 0,
  19. }
  20. }
  21. func (me *tArrayHeap) Size() int {
  22. return me.size
  23. }
  24. func (me *tArrayHeap) IsEmpty() bool {
  25. return me.size <= 0
  26. }
  27. func (me *tArrayHeap) IsNotEmpty() bool {
  28. return !me.IsEmpty()
  29. }
  30. func (me *tArrayHeap) Push(value interface{}) {
  31. me.version++
  32. me.ensureSize(me.size + 1)
  33. me.items[me.size] = value
  34. me.size++
  35. me.shiftUp(me.size - 1)
  36. me.version++
  37. }
  38. func (me *tArrayHeap) ensureSize(size int) {
  39. for ;len(me.items) < size; {
  40. me.items = append(me.items, nil)
  41. }
  42. }
  43. func (me *tArrayHeap) parentOf(i int) int {
  44. return (i - 1) / 2
  45. }
  46. func (me *tArrayHeap) leftChildOf(i int) int {
  47. return i*2 + 1
  48. }
  49. func (me *tArrayHeap) rightChildOf(i int) int {
  50. return me.leftChildOf(i) + 1
  51. }
  52. func (me *tArrayHeap) last() (i int, v interface{}) {
  53. if me.IsEmpty() {
  54. return -1, nil
  55. }
  56. i = me.size - 1
  57. v = me.items[i]
  58. return i,v
  59. }
  60. func (me *tArrayHeap) shiftUp(i int) {
  61. if i <= 0 {
  62. return
  63. }
  64. v := me.items[i]
  65. pi := me.parentOf(i)
  66. pv := me.items[pi]
  67. if me.comparator.Less(v, pv) {
  68. me.items[pi], me.items[i] = v, pv
  69. me.shiftUp(pi)
  70. }
  71. }
  72. func (me *tArrayHeap) Pop() (error, interface{}) {
  73. if me.IsEmpty() {
  74. return gNoMoreElementsError, nil
  75. }
  76. me.version++
  77. top := me.items[0]
  78. li, lv := me.last()
  79. me.items[0] = nil
  80. me.size--
  81. if me.IsEmpty() {
  82. return nil, top
  83. }
  84. me.items[0] = lv
  85. me.items[li] = nil
  86. me.shiftDown(0)
  87. me.version++
  88. return nil, top
  89. }
  90. func (me *tArrayHeap) shiftDown(i int) {
  91. pv := me.items[i]
  92. ok, ci, cv := me.minChildOf(i)
  93. if ok && me.comparator.Less(cv, pv) {
  94. me.items[i], me.items[ci] = cv, pv
  95. me.shiftDown(ci)
  96. }
  97. }
  98. func (me *tArrayHeap) minChildOf(p int) (ok bool, i int, v interface{}) {
  99. li := me.leftChildOf(p)
  100. if li >= me.size {
  101. return false, 0, nil
  102. }
  103. lv := me.items[li]
  104. ri := me.rightChildOf(p)
  105. if ri >= me.size {
  106. return true, li, lv
  107. }
  108. rv := me.items[ri]
  109. if me.comparator.Less(lv, rv) {
  110. return true, li, lv
  111. } else {
  112. return true, ri, rv
  113. }
  114. }
  115. func (me *tArrayHeap) Iterator() IIterator {
  116. return newHeapIterator(me)
  117. }
  118. func (me *tArrayHeap) String() string {
  119. level := 0
  120. lines := make([]string, 0)
  121. lines = append(lines, "")
  122. for {
  123. n := 1<<level
  124. min := n - 1
  125. max := n + min - 1
  126. if min >= me.size {
  127. break
  128. }
  129. line := make([]string, 0)
  130. for i := min;i <= max;i++ {
  131. if i >= me.size {
  132. break
  133. }
  134. line = append(line, fmt.Sprintf("%4d", me.items[i]))
  135. }
  136. lines = append(lines, strings.Join(line, ","))
  137. level++
  138. }
  139. return strings.Join(lines, "\n")
  140. }
  141. var gNoMoreElementsError = errors.New("no more elements")

tHeapIterator.go

堆迭代器, 实现IIterator接口

  1. package heap
  2. import "errors"
  3. type tHeapIterator struct {
  4. heap *tArrayHeap
  5. pos int
  6. version int64
  7. }
  8. func newHeapIterator(heap *tArrayHeap) IIterator {
  9. return &tHeapIterator{
  10. heap: heap,
  11. pos: 0,
  12. version: heap.version,
  13. }
  14. }
  15. func (me *tHeapIterator) More() bool {
  16. if me.version != me.heap.version {
  17. return false
  18. }
  19. return me.pos < me.heap.size
  20. }
  21. func (me *tHeapIterator) Next() (error, interface{}) {
  22. if me.version != me.heap.version {
  23. return gConcurrentModificationError, nil
  24. }
  25. if me.pos >= me.heap.size {
  26. return gNoMoreElementsError, nil
  27. }
  28. v := me.heap.items[me.pos]
  29. me.pos++
  30. return nil, v
  31. }
  32. var gConcurrentModificationError = errors.New("concurrent modification error")

(end)