缘起

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

队列

  1. 队列中的数据也呈线性排列。
  2. 队列中添加和删除数据的操作分别是在两端进行的。
  3. 就和“队列”这个名字一样,
  4. 把它想象成排成一队的人更容易理解。
  5. 在队列中,
  6. 处理总是从第一名开始往后进行,
  7. 而新来的人只能排在队尾。
  8. 像队列这种最先进去的数据最先被取来,
  9. 即“先进先出”的结构,
  10. 我们称为First In First Out,简称FIFO
  11. 摘自 <<我的第一本算法书>>(【日】石田保辉;宫崎修一)

目标

  • 以数组+读写双指针为基础, 实现一个FIFO队列
  • 可以初始指定期望容量大小
  • 当元素数量超过容量/2时, 自动以2倍率速度扩容
  • 提供免拷贝的迭代器, 并能检测迭代版本错误(Concurrent Modification Error)

设计

  • IQueue: 队列的接口
  • IListIterator: 迭代器接口
  • tArrayQueue: 以数组+读写双指针为基础的FIFO队列, 实现IQueue接口.
  • tQueueIterator: 免拷贝的队列迭代器, 通过版本号检测迭代中的Concurrent Modification Error

单元测试

queue_test.go

  1. package data_structure
  2. import (
  3. "fmt"
  4. qu "learning/gooop/data_structure/queue"
  5. "strings"
  6. "testing"
  7. )
  8. func Test_Queue(t *testing.T) {
  9. fnAssertTrue := func(b bool, msg string) {
  10. if !b {
  11. t.Fatal(msg)
  12. }
  13. }
  14. queue := qu.NewArrayQueue(1)
  15. state := queue.String()
  16. t.Log(state)
  17. fnAssertTrue(state == "c=2,r=0,w=0,s=0,v=0 []", "expecting []")
  18. fnAssertTrue(queue.IsEmpty(), "expecting IsEmpty()")
  19. queue.Push(10)
  20. state = queue.String()
  21. t.Log(state)
  22. fnAssertTrue(state == "c=2,r=0,w=1,s=1,v=1 [10]", "expecting [10]")
  23. fnAssertTrue(queue.IsNotEmpty(), "expecting IsNotEmpty()")
  24. queue.Push(20)
  25. state = queue.String()
  26. t.Log(state)
  27. fnAssertTrue(state == "c=2,r=0,w=2,s=2,v=2 [10,20]", "expecting [10,20]")
  28. queue.Push(30)
  29. state = queue.String()
  30. t.Log(state)
  31. fnAssertTrue(state == "c=4,r=0,w=3,s=3,v=3 [10,20,30]", "expecting [10,20,30]")
  32. e,v := queue.Peek()
  33. fnAssertTrue(e == nil, "expecting e == nil")
  34. fnAssertTrue(v == 10, "expecting Peek() = 10")
  35. e,v = queue.Poll()
  36. fnAssertTrue(e == nil, "expecting e == nil")
  37. fnAssertTrue(v == 10, "expecting Peek() = 10")
  38. state = queue.String()
  39. t.Log(state)
  40. fnAssertTrue(state == "c=4,r=1,w=3,s=2,v=4 [20,30]", "expecting [20,30]")
  41. e,v = queue.Poll()
  42. fnAssertTrue(e == nil, "expecting e == nil")
  43. fnAssertTrue(v == 20, "expecting Peek() = 20")
  44. state = queue.String()
  45. t.Log(state)
  46. fnAssertTrue(state == "c=4,r=0,w=1,s=1,v=5 [30]", "expecting [30]")
  47. e,v = queue.Poll()
  48. fnAssertTrue(e == nil, "expecting e == nil")
  49. fnAssertTrue(v == 30, "expecting Peek() = 30")
  50. state = queue.String()
  51. t.Log(state)
  52. fnAssertTrue(state == "c=4,r=1,w=1,s=0,v=6 []", "expecting []")
  53. queue.Push(40)
  54. queue.Push(50)
  55. queue.Push(60)
  56. queue.Push(70)
  57. queue.Push(80)
  58. queue.Push(90)
  59. e,v = queue.Poll()
  60. fnAssertTrue(e == nil, "expecting e == nil")
  61. fnAssertTrue(v == 40, "expecting Peek() = 40")
  62. state = queue.String()
  63. t.Log(state)
  64. fnAssertTrue(state == "c=8,r=1,w=6,s=5,v=13 [50,60,70,80,90]", "expecting [50,60,70,80,90]")
  65. items := make([]string, 0)
  66. for iter := queue.Iterator();iter.More(); {
  67. e,v := iter.Next()
  68. fnAssertTrue(e == nil, "expecting e == nil")
  69. items = append(items, fmt.Sprintf("%v", v))
  70. }
  71. itemString := strings.Join(items, ",")
  72. t.Log(itemString)
  73. fnAssertTrue(itemString == "50,60,70,80,90", "expecting [50,60,70,80,90]")
  74. }

测试输出

  1. $ go test -v queue_test.go
  2. === RUN Test_Queue
  3. queue_test.go:19: c=2,r=0,w=0,s=0,v=0 []
  4. queue_test.go:25: c=2,r=0,w=1,s=1,v=1 [10]
  5. queue_test.go:31: c=2,r=0,w=2,s=2,v=2 [10,20]
  6. queue_test.go:36: c=4,r=0,w=3,s=3,v=3 [10,20,30]
  7. queue_test.go:47: c=4,r=1,w=3,s=2,v=4 [20,30]
  8. queue_test.go:54: c=4,r=0,w=1,s=1,v=5 [30]
  9. queue_test.go:61: c=4,r=1,w=1,s=0,v=6 []
  10. queue_test.go:74: c=8,r=1,w=6,s=5,v=13 [50,60,70,80,90]
  11. queue_test.go:84: 50,60,70,80,90
  12. --- PASS: Test_Queue (0.00s)
  13. PASS
  14. ok command-line-arguments 0.003s

IQueue.go

队列的接口

  1. package queue
  2. type IQueue interface {
  3. Size() int
  4. IsEmpty() bool
  5. IsNotEmpty() bool
  6. Push(value interface{})
  7. Poll() (error, interface{})
  8. Peek() (error, interface{})
  9. Clear()
  10. Iterator() IListIterator
  11. String() string
  12. }

IListIterator.go

迭代器接口

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

tArrayQueue.go

以数组+读写双指针为基础的FIFO队列, 实现IQueue接口.

  1. package queue
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. )
  7. type tArrayQueue struct {
  8. items []interface{}
  9. capacity int
  10. rindex int
  11. windex int
  12. version int64
  13. }
  14. var gEmptyQueueError = errors.New("empty queue")
  15. func NewArrayQueue(initSpace int) IQueue {
  16. if initSpace < 0 {
  17. initSpace = 0
  18. }
  19. c := initSpace*2
  20. return &tArrayQueue{
  21. items: make([]interface{}, c),
  22. capacity: c,
  23. rindex: 0,
  24. windex: 0,
  25. version: 0,
  26. }
  27. }
  28. func (me *tArrayQueue) Size() int {
  29. return me.windex - me.rindex
  30. }
  31. func (me *tArrayQueue) IsEmpty() bool {
  32. return me.Size() <= 0
  33. }
  34. func (me *tArrayQueue) IsNotEmpty() bool {
  35. return !me.IsEmpty()
  36. }
  37. func (me *tArrayQueue) Push(value interface{}) {
  38. me.ensureSpace(1)
  39. me.items[me.windex] = value
  40. me.windex++
  41. me.version++
  42. }
  43. func (me *tArrayQueue) ensureSpace(space int) {
  44. if me.remainingSpace() >= space {
  45. return
  46. }
  47. for ;me.remainingSpace()<space; {
  48. me.capacity = maxInt(me.capacity*2, me.capacity + 1)
  49. }
  50. newItems := make([]interface{}, me.capacity)
  51. p := 0
  52. for i := me.rindex;i < me.windex;i++ {
  53. newItems[p] = me.items[i]
  54. p++
  55. }
  56. me.items = newItems
  57. me.windex -= me.rindex
  58. me.rindex = 0
  59. }
  60. func maxInt(x,y int) int {
  61. if x >= y {
  62. return x
  63. }
  64. return y
  65. }
  66. func (me *tArrayQueue) remainingSpace() int {
  67. return me.capacity - me.windex
  68. }
  69. func (me *tArrayQueue) Poll() (error, interface{}) {
  70. if me.IsEmpty() {
  71. return gEmptyQueueError, nil
  72. }
  73. it := me.items[me.rindex]
  74. me.items[me.rindex] = nil
  75. me.rindex++
  76. if me.rindex >= me.capacity / 2 {
  77. p := 0
  78. for i := me.rindex;i < me.windex;i++ {
  79. me.items[p] = me.items[i]
  80. me.items[i] = nil
  81. p++
  82. }
  83. me.windex -= me.rindex
  84. me.rindex = 0
  85. }
  86. me.version++
  87. return nil, it
  88. }
  89. func (me *tArrayQueue) Peek() (error, interface{}) {
  90. if me.IsEmpty() {
  91. return gEmptyQueueError, nil
  92. }
  93. return nil, me.items[me.rindex]
  94. }
  95. func (me *tArrayQueue) Clear() {
  96. for i := me.rindex;i < me.windex;i++ {
  97. me.items[i] = nil
  98. }
  99. me.rindex = 0
  100. me.windex = 0
  101. me.version++
  102. }
  103. func (me *tArrayQueue) Iterator() IListIterator {
  104. return newArrayQueueIterator(me)
  105. }
  106. func (me *tArrayQueue) String() string {
  107. itemStrings := make([]string, me.Size())
  108. p := 0
  109. for i := me.rindex;i < me.windex;i++ {
  110. itemStrings[p] = fmt.Sprintf("%v", me.items[i])
  111. p++
  112. }
  113. return fmt.Sprintf("c=%v,r=%v,w=%v,s=%v,v=%v [%s]", me.capacity, me.rindex, me.windex, me.Size(), me.version, strings.Join(itemStrings, ","))
  114. }

tQueueIterator.go

免拷贝的队列迭代器, 通过版本号检测迭代中的Concurrent Modification Error

  1. package queue
  2. import "errors"
  3. type tQueueIterator struct {
  4. queue *tArrayQueue
  5. pos int
  6. version int64
  7. }
  8. var gConcurrentModificationError = errors.New("concurrent modification error")
  9. var gNoMoreElementsError = errors.New("no more elements")
  10. func newArrayQueueIterator(queue *tArrayQueue) IListIterator {
  11. return &tQueueIterator{
  12. queue: queue,
  13. pos : queue.rindex,
  14. version: queue.version,
  15. }
  16. }
  17. func (me *tQueueIterator) More() bool {
  18. q := me.queue
  19. if q == nil {
  20. return false
  21. }
  22. if q.version != me.version {
  23. return false
  24. }
  25. return me.pos < q.windex
  26. }
  27. func (me *tQueueIterator) Next() (error, interface{}) {
  28. q := me.queue
  29. if q == nil {
  30. return gEmptyQueueError, nil
  31. }
  32. if q.version != me.version {
  33. return gConcurrentModificationError, nil
  34. }
  35. if me.pos >= q.windex {
  36. return gNoMoreElementsError, nil
  37. }
  38. it := q.items[me.pos]
  39. me.pos++
  40. return nil, it
  41. }

(end)