Skip to content

Commit df6f6c4

Browse files
Merge pull request #96 from ichinaski/master
Implement heap-based priority queue
2 parents 630fae4 + 684bdac commit df6f6c4

File tree

1 file changed

+62
-35
lines changed

1 file changed

+62
-35
lines changed

queue/priority_queue.go

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ would be easier to solve.
2424

2525
package queue
2626

27-
import (
28-
"sort"
29-
"sync"
30-
)
27+
import "sync"
3128

3229
// Item is an item that can be added to the priority queue.
3330
type Item interface {
@@ -42,50 +39,66 @@ type Item interface {
4239

4340
type priorityItems []Item
4441

42+
func (items *priorityItems) swap(i, j int) {
43+
(*items)[i], (*items)[j] = (*items)[j], (*items)[i]
44+
}
45+
46+
func (items *priorityItems) pop() Item {
47+
size := len(*items)
48+
49+
// Move last leaf to root, and 'pop' the last item.
50+
items.swap(size-1, 0)
51+
item := (*items)[size-1] // Item to return.
52+
(*items)[size-1], *items = nil, (*items)[:size-1]
53+
54+
// 'Bubble down' to restore heap property.
55+
index := 0
56+
childL, childR := 2*index+1, 2*index+2
57+
for len(*items) > childL {
58+
child := childL
59+
if len(*items) > childR && (*items)[childR].Compare((*items)[childL]) < 0 {
60+
child = childR
61+
}
62+
63+
if (*items)[child].Compare((*items)[index]) < 0 {
64+
items.swap(index, child)
65+
66+
index = child
67+
childL, childR = 2*index+1, 2*index+2
68+
} else {
69+
break
70+
}
71+
}
72+
73+
return item
74+
}
75+
4576
func (items *priorityItems) get(number int) []Item {
4677
returnItems := make([]Item, 0, number)
47-
index := 0
4878
for i := 0; i < number; i++ {
4979
if i >= len(*items) {
5080
break
5181
}
5282

53-
returnItems = append(returnItems, (*items)[i])
54-
(*items)[i] = nil
55-
index++
83+
returnItems = append(returnItems, items.pop())
5684
}
5785

58-
*items = (*items)[index:]
5986
return returnItems
6087
}
6188

62-
func (items *priorityItems) insert(item Item) {
63-
if len(*items) == 0 {
64-
*items = append(*items, item)
65-
return
66-
}
67-
68-
equalFound := false
69-
i := sort.Search(len(*items), func(i int) bool {
70-
result := (*items)[i].Compare(item)
71-
if result == 0 {
72-
equalFound = true
73-
}
74-
return result >= 0
75-
})
89+
func (items *priorityItems) push(item Item) {
90+
// Stick the item as the end of the last level.
91+
*items = append(*items, item)
7692

77-
if equalFound {
78-
return
79-
}
93+
// 'Bubble up' to restore heap property.
94+
index := len(*items) - 1
95+
parent := int((index - 1) / 2)
96+
for parent >= 0 && (*items)[parent].Compare(item) > 0 {
97+
items.swap(index, parent)
8098

81-
if i == len(*items) {
82-
*items = append(*items, item)
83-
return
99+
index = parent
100+
parent = int((index - 1) / 2)
84101
}
85-
86-
*items = append(*items, nil)
87-
copy((*items)[i+1:], (*items)[i:])
88-
(*items)[i] = item
89102
}
90103

91104
// PriorityQueue is similar to queue except that it takes
@@ -94,6 +107,7 @@ func (items *priorityItems) insert(item Item) {
94107
type PriorityQueue struct {
95108
waiters waiters
96109
items priorityItems
110+
itemMap map[Item]struct{}
97111
lock sync.Mutex
98112
disposeLock sync.Mutex
99113
disposed bool
@@ -112,7 +126,10 @@ func (pq *PriorityQueue) Put(items ...Item) error {
112126
}
113127

114128
for _, item := range items {
115-
pq.items.insert(item)
129+
if _, ok := pq.itemMap[item]; !ok {
130+
pq.itemMap[item] = struct{}{}
131+
pq.items.push(item)
132+
}
116133
}
117134

118135
for {
@@ -150,6 +167,13 @@ func (pq *PriorityQueue) Get(number int) ([]Item, error) {
150167

151168
var items []Item
152169

170+
// Remove references to popped items.
171+
deleteItems := func(items []Item) {
172+
for _, item := range items {
173+
delete(pq.itemMap, item)
174+
}
175+
}
176+
153177
if len(pq.items) == 0 {
154178
sema := newSema()
155179
pq.waiters.put(sema)
@@ -164,11 +188,13 @@ func (pq *PriorityQueue) Get(number int) ([]Item, error) {
164188
pq.disposeLock.Unlock()
165189

166190
items = pq.items.get(number)
191+
deleteItems(items)
167192
sema.response.Done()
168193
return items, nil
169194
}
170195

171196
items = pq.items.get(number)
197+
deleteItems(items)
172198
pq.lock.Unlock()
173199
return items, nil
174200
}
@@ -230,6 +256,7 @@ func (pq *PriorityQueue) Dispose() {
230256
// NewPriorityQueue is the constructor for a priority queue.
231257
func NewPriorityQueue(hint int) *PriorityQueue {
232258
return &PriorityQueue{
233-
items: make(priorityItems, 0, hint),
259+
items: make(priorityItems, 0, hint),
260+
itemMap: make(map[Item]struct{}, hint),
234261
}
235262
}

0 commit comments

Comments
 (0)