-
Notifications
You must be signed in to change notification settings - Fork 1
/
Queue.go
189 lines (168 loc) · 4.8 KB
/
Queue.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright (c) 2013, Peter H. Froehlich. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.
// Package queue implements a double-ended queue abstraction on
// top of a slice/array. All operations are constant time except
// for PushFront and PushBack which are amortized constant time.
//
// We are about 60%-90% faster than container/list would be at
// the price of potentially wasting some memory because we grow
// our slice by amortized doubling.
package main
import "fmt"
// Queue represents a double-ended queue.
// The zero value for Queue is an empty queue ready to use.
type Queue struct {
// PushBack writes to rep[back] and then increments
// back; PushFront decrements front and then writes
// to rep[front]; gotta love those invariants.
rep []interface{}
front int
back int
length int
}
// New returns an initialized empty queue.
func NewQueue() *Queue {
return new(Queue).Init()
}
// Init initializes or clears queue q.
func (q *Queue) Init() *Queue {
// start with a slice of length 2 even if that "wastes"
// some memory; we do front/back arithmetic modulo the
// length, so starting at 1 would require special cases
q.rep = make([]interface{}, 2)
// for some time I considered reusing the existing slice
// if all a client does is re-initialize the queue; the
// big problem with that is that the previous queue might
// have been huge while the current queue doesn't grow
// much at all; if that were to happen we'd hold on to a
// huge chunk of memory for just a few elements and nobody
// could do anything about it; so instead I decided to
// just allocate a new slice and let the GC take care of
// the previous one; seems a better tradeoff all around
q.front, q.back, q.length = 0, 0, 0
return q
}
// lazyInit lazily initializes a zero Queue value.
//
// I am mostly doing this because container/list does the same thing.
// Personally I think it's a little wasteful because every single
// PushFront/PushBack is going to pay the overhead of calling this.
// But that's the price for making zero values useful immediately,
// something Go apparently likes a lot.
func (q *Queue) lazyInit() {
if q.rep == nil {
q.Init()
}
}
// Len returns the number of elements of queue q.
func (q *Queue) Len() int {
return q.length
}
// empty returns true if the queue q has no elements.
func (q *Queue) empty() bool {
return q.length == 0
}
// full returns true if the queue q is at capacity.
func (q *Queue) full() bool {
return q.length == len(q.rep)
}
// grow doubles the size of queue q's underlying slice/array.
func (q *Queue) grow() {
big := make([]interface{}, q.length*2)
j := q.front
for i := 0; i < q.length; i++ {
big[i] = q.rep[j]
j = q.inc(j)
}
q.rep = big
q.front = 0
q.back = q.length
}
// lazyGrow grows the underlying slice/array if necessary.
func (q *Queue) lazyGrow() {
if q.full() {
q.grow()
}
}
// String returns a string representation of queue q formatted
// from front to back.
func (q *Queue) String() string {
result := ""
result = result + "["
j := q.front
for i := 0; i < q.length; i++ {
if i == q.length-1 {
result = result + fmt.Sprintf("%v", q.rep[j])
} else {
result = result + fmt.Sprintf("%v, ", q.rep[j])
}
j = q.inc(j)
}
result = result + "]"
return result
}
// inc returns the next integer position wrapping around queue q.
func (q *Queue) inc(i int) int {
l := len(q.rep)
return (i + 1 + l) % l
}
// dec returns the previous integer position wrapping around queue q.
func (q *Queue) dec(i int) int {
l := len(q.rep)
return (i - 1 + l) % l
}
// Front returns the first element of queue q or nil.
func (q *Queue) Front() interface{} {
if q.empty() {
return nil
}
return q.rep[q.front]
}
// Back returns the last element of queue q or nil.
func (q *Queue) Back() interface{} {
if q.empty() {
return nil
}
return q.rep[q.dec(q.back)]
}
// PushFront inserts a new value v at the front of queue q.
func (q *Queue) PushFront(v interface{}) {
q.lazyInit()
q.lazyGrow()
q.front = q.dec(q.front)
q.rep[q.front] = v
q.length++
}
// PushBack inserts a new value v at the back of queue q.
func (q *Queue) PushBack(v interface{}) {
q.lazyInit()
q.lazyGrow()
q.rep[q.back] = v
q.back = q.inc(q.back)
q.length++
}
// Both PopFront and PopBack set the newly free slot to nil
// in an attempt to be nice to the garbage collector.
// PopFront removes and returns the first element of queue q or nil.
func (q *Queue) PopFront() interface{} {
if q.empty() {
return nil
}
v := q.rep[q.front]
q.rep[q.front] = nil
q.front = q.inc(q.front)
q.length--
return v
}
// PopBack removes and returns the last element of queue q or nil.
func (q *Queue) PopBack() interface{} {
if q.empty() {
return nil
}
q.back = q.dec(q.back)
v := q.rep[q.back]
q.rep[q.back] = nil
q.length--
return v
}