手撸golang 根本数据结构与算法 队列
缘起
最近浏览<<我的第一本算法书>>(【日】石田保辉;宫崎修一)
本系列笔记拟采纳golang练习之
队列
队列中的数据也呈线性排列。队列中增加和删除数据的操作别离是在两端进行的。就和“队列”这个名字一样,把它设想成排成一队的人更容易了解。在队列中,解决总是从第一名开始往后进行,而新来的人只能排在队尾。像队列这种最先进去的数据最先被取来,即“先进先出”的构造,咱们称为First In First Out,简称FIFO。摘自 <<我的第一本算法书>>(【日】石田保辉;宫崎修一)
指标
- 以数组+读写双指针为根底, 实现一个FIFO队列
- 能够初始指定冀望容量大小
- 当元素数量超过容量/2时, 主动以2倍率速度扩容
- 提供免拷贝的迭代器, 并能检测迭代版本谬误(Concurrent Modification Error)
设计
- IQueue: 队列的接口
- IListIterator: 迭代器接口
- tArrayQueue: 以数组+读写双指针为根底的FIFO队列, 实现IQueue接口.
- tQueueIterator: 免拷贝的队列迭代器, 通过版本号检测迭代中的Concurrent Modification Error
单元测试
queue_test.go
package data_structureimport ( "fmt" qu "learning/gooop/data_structure/queue" "strings" "testing")func Test_Queue(t *testing.T) { fnAssertTrue := func(b bool, msg string) { if !b { t.Fatal(msg) } } queue := qu.NewArrayQueue(1) state := queue.String() t.Log(state) fnAssertTrue(state == "c=2,r=0,w=0,s=0,v=0 []", "expecting []") fnAssertTrue(queue.IsEmpty(), "expecting IsEmpty()") queue.Push(10) state = queue.String() t.Log(state) fnAssertTrue(state == "c=2,r=0,w=1,s=1,v=1 [10]", "expecting [10]") fnAssertTrue(queue.IsNotEmpty(), "expecting IsNotEmpty()") queue.Push(20) state = queue.String() t.Log(state) fnAssertTrue(state == "c=2,r=0,w=2,s=2,v=2 [10,20]", "expecting [10,20]") queue.Push(30) state = queue.String() t.Log(state) fnAssertTrue(state == "c=4,r=0,w=3,s=3,v=3 [10,20,30]", "expecting [10,20,30]") e,v := queue.Peek() fnAssertTrue(e == nil, "expecting e == nil") fnAssertTrue(v == 10, "expecting Peek() = 10") e,v = queue.Poll() fnAssertTrue(e == nil, "expecting e == nil") fnAssertTrue(v == 10, "expecting Peek() = 10") state = queue.String() t.Log(state) fnAssertTrue(state == "c=4,r=1,w=3,s=2,v=4 [20,30]", "expecting [20,30]") e,v = queue.Poll() fnAssertTrue(e == nil, "expecting e == nil") fnAssertTrue(v == 20, "expecting Peek() = 20") state = queue.String() t.Log(state) fnAssertTrue(state == "c=4,r=0,w=1,s=1,v=5 [30]", "expecting [30]") e,v = queue.Poll() fnAssertTrue(e == nil, "expecting e == nil") fnAssertTrue(v == 30, "expecting Peek() = 30") state = queue.String() t.Log(state) fnAssertTrue(state == "c=4,r=1,w=1,s=0,v=6 []", "expecting []") queue.Push(40) queue.Push(50) queue.Push(60) queue.Push(70) queue.Push(80) queue.Push(90) e,v = queue.Poll() fnAssertTrue(e == nil, "expecting e == nil") fnAssertTrue(v == 40, "expecting Peek() = 40") state = queue.String() t.Log(state) fnAssertTrue(state == "c=8,r=1,w=6,s=5,v=13 [50,60,70,80,90]", "expecting [50,60,70,80,90]") items := make([]string, 0) for iter := queue.Iterator();iter.More(); { e,v := iter.Next() fnAssertTrue(e == nil, "expecting e == nil") items = append(items, fmt.Sprintf("%v", v)) } itemString := strings.Join(items, ",") t.Log(itemString) fnAssertTrue(itemString == "50,60,70,80,90", "expecting [50,60,70,80,90]")}
测试输入
$ go test -v queue_test.go === RUN Test_Queue queue_test.go:19: c=2,r=0,w=0,s=0,v=0 [] queue_test.go:25: c=2,r=0,w=1,s=1,v=1 [10] queue_test.go:31: c=2,r=0,w=2,s=2,v=2 [10,20] queue_test.go:36: c=4,r=0,w=3,s=3,v=3 [10,20,30] queue_test.go:47: c=4,r=1,w=3,s=2,v=4 [20,30] queue_test.go:54: c=4,r=0,w=1,s=1,v=5 [30] queue_test.go:61: c=4,r=1,w=1,s=0,v=6 [] queue_test.go:74: c=8,r=1,w=6,s=5,v=13 [50,60,70,80,90] queue_test.go:84: 50,60,70,80,90--- PASS: Test_Queue (0.00s)PASSok command-line-arguments 0.003s
IQueue.go
队列的接口
package queuetype IQueue interface { Size() int IsEmpty() bool IsNotEmpty() bool Push(value interface{}) Poll() (error, interface{}) Peek() (error, interface{}) Clear() Iterator() IListIterator String() string}
IListIterator.go
迭代器接口
package queuetype IListIterator interface { More() bool Next() (error,interface{})}
tArrayQueue.go
以数组+读写双指针为根底的FIFO队列, 实现IQueue接口.
package queueimport ( "errors" "fmt" "strings")type tArrayQueue struct { items []interface{} capacity int rindex int windex int version int64}var gEmptyQueueError = errors.New("empty queue")func NewArrayQueue(initSpace int) IQueue { if initSpace < 0 { initSpace = 0 } c := initSpace*2 return &tArrayQueue{ items: make([]interface{}, c), capacity: c, rindex: 0, windex: 0, version: 0, }}func (me *tArrayQueue) Size() int { return me.windex - me.rindex}func (me *tArrayQueue) IsEmpty() bool { return me.Size() <= 0}func (me *tArrayQueue) IsNotEmpty() bool { return !me.IsEmpty()}func (me *tArrayQueue) Push(value interface{}) { me.ensureSpace(1) me.items[me.windex] = value me.windex++ me.version++}func (me *tArrayQueue) ensureSpace(space int) { if me.remainingSpace() >= space { return } for ;me.remainingSpace()<space; { me.capacity = maxInt(me.capacity*2, me.capacity + 1) } newItems := make([]interface{}, me.capacity) p := 0 for i := me.rindex;i < me.windex;i++ { newItems[p] = me.items[i] p++ } me.items = newItems me.windex -= me.rindex me.rindex = 0}func maxInt(x,y int) int { if x >= y { return x } return y}func (me *tArrayQueue) remainingSpace() int { return me.capacity - me.windex}func (me *tArrayQueue) Poll() (error, interface{}) { if me.IsEmpty() { return gEmptyQueueError, nil } it := me.items[me.rindex] me.items[me.rindex] = nil me.rindex++ if me.rindex >= me.capacity / 2 { p := 0 for i := me.rindex;i < me.windex;i++ { me.items[p] = me.items[i] me.items[i] = nil p++ } me.windex -= me.rindex me.rindex = 0 } me.version++ return nil, it}func (me *tArrayQueue) Peek() (error, interface{}) { if me.IsEmpty() { return gEmptyQueueError, nil } return nil, me.items[me.rindex]}func (me *tArrayQueue) Clear() { for i := me.rindex;i < me.windex;i++ { me.items[i] = nil } me.rindex = 0 me.windex = 0 me.version++}func (me *tArrayQueue) Iterator() IListIterator { return newArrayQueueIterator(me)}func (me *tArrayQueue) String() string { itemStrings := make([]string, me.Size()) p := 0 for i := me.rindex;i < me.windex;i++ { itemStrings[p] = fmt.Sprintf("%v", me.items[i]) p++ } 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, ","))}
tQueueIterator.go
免拷贝的队列迭代器, 通过版本号检测迭代中的Concurrent Modification Error
package queueimport "errors"type tQueueIterator struct { queue *tArrayQueue pos int version int64}var gConcurrentModificationError = errors.New("concurrent modification error")var gNoMoreElementsError = errors.New("no more elements")func newArrayQueueIterator(queue *tArrayQueue) IListIterator { return &tQueueIterator{ queue: queue, pos : queue.rindex, version: queue.version, }}func (me *tQueueIterator) More() bool { q := me.queue if q == nil { return false } if q.version != me.version { return false } return me.pos < q.windex}func (me *tQueueIterator) Next() (error, interface{}) { q := me.queue if q == nil { return gEmptyQueueError, nil } if q.version != me.version { return gConcurrentModificationError, nil } if me.pos >= q.windex { return gNoMoreElementsError, nil } it := q.items[me.pos] me.pos++ return nil, it}
(end)