Skip to content

Commit fea0d54

Browse files
committed
Merge pull request kodecocodes#124 from chris-pilcher/bounded-priority-queue-updated-implementation
Bounded Priority Queue - adding updated implementation
2 parents b408b50 + 636fee8 commit fea0d54

File tree

7 files changed

+528
-208
lines changed

7 files changed

+528
-208
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
struct Message: Comparable, CustomStringConvertible {
2+
let name: String
3+
let priority: Int
4+
5+
var description: String {
6+
return "\(name):\(priority)"
7+
}
8+
}
9+
10+
func ==(m1: Message, m2: Message) -> Bool {
11+
return m1.priority == m2.priority
12+
}
13+
14+
func <(m1: Message, m2: Message) -> Bool {
15+
return m1.priority < m2.priority
16+
}
17+
18+
19+
20+
let queue = BoundedPriorityQueue<Message>(maxElements: 5)
21+
queue.count
22+
23+
queue.enqueue(Message(name: "hello", priority: 100))
24+
queue.count
25+
queue.peek()
26+
print(queue)
27+
28+
queue.enqueue(Message(name: "there", priority: 99))
29+
queue.count
30+
queue.peek()
31+
print(queue)
32+
33+
queue.enqueue(Message(name: "world", priority: 150))
34+
queue.count
35+
queue.peek()
36+
print(queue)
37+
38+
queue.enqueue(Message(name: "swift", priority: 110))
39+
queue.count
40+
queue.peek()
41+
print(queue)
42+
43+
queue.enqueue(Message(name: "is", priority: 30))
44+
queue.count
45+
queue.peek()
46+
print(queue)
47+
48+
// At this point, the queue is:
49+
// <world:150, swift:110, hello:100, there:99, is:30, >
50+
51+
52+
53+
// Try to insert an item with a really low priority. This should not get added.
54+
queue.enqueue(Message(name: "very", priority: -1))
55+
queue.count // 5
56+
queue.peek()
57+
print(queue) // still same as before
58+
59+
// Try to insert an item with medium priority. This gets added and the lowest
60+
// priority item is removed.
61+
queue.enqueue(Message(name: "cool", priority: 120))
62+
queue.count
63+
queue.peek()
64+
print(queue)
65+
66+
// Try to insert an item with very high priority. This gets added and the
67+
// lowest priority item is removed.
68+
queue.enqueue(Message(name: "!!!", priority: 500))
69+
queue.count
70+
queue.peek()
71+
print(queue)
72+
73+
74+
75+
// Test dequeuing
76+
queue.dequeue()
77+
queue.count
78+
queue.peek()
79+
print(queue)
80+
81+
queue.dequeue()
82+
queue.count
83+
queue.peek()
84+
print(queue)
85+
86+
queue.dequeue()
87+
queue.count
88+
queue.peek()
89+
print(queue)
90+
91+
queue.dequeue()
92+
queue.count
93+
queue.peek()
94+
print(queue)
95+
96+
queue.dequeue()
97+
queue.count
98+
queue.peek()
99+
print(queue)
100+
101+
queue.dequeue()
102+
queue.count
103+
queue.peek()
104+
print(queue)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
public class LinkedListNode<T: Comparable> {
2+
var value: T
3+
var next: LinkedListNode?
4+
var previous: LinkedListNode?
5+
6+
public init(value: T) {
7+
self.value = value
8+
}
9+
}
10+
11+
public class BoundedPriorityQueue<T: Comparable> {
12+
private typealias Node = LinkedListNode<T>
13+
14+
private(set) public var count = 0
15+
private var head: Node?
16+
private var tail: Node?
17+
private var maxElements: Int
18+
19+
public init(maxElements: Int) {
20+
self.maxElements = maxElements
21+
}
22+
23+
public var isEmpty: Bool {
24+
return count == 0
25+
}
26+
27+
public func peek() -> T? {
28+
return head?.value
29+
}
30+
31+
public func enqueue(value: T) {
32+
if let node = insert(value, after: findInsertionPoint(value)) {
33+
// If the newly inserted node is the last one in the list, then update
34+
// the tail pointer.
35+
if node.next == nil {
36+
tail = node
37+
}
38+
39+
// If the queue is full, then remove an element from the back.
40+
count += 1
41+
if count > maxElements {
42+
removeLeastImportantElement()
43+
}
44+
}
45+
}
46+
47+
private func insert(value: T, after: Node?) -> Node? {
48+
if let previous = after {
49+
50+
// If the queue is full and we have to insert at the end of the list,
51+
// then there's no reason to insert the new value.
52+
if count == maxElements && previous.next == nil {
53+
print("Queue is full and priority of new object is too small")
54+
return nil
55+
}
56+
57+
// Put the new node in between previous and previous.next (if exists).
58+
let node = Node(value: value)
59+
node.next = previous.next
60+
previous.next?.previous = node
61+
previous.next = node
62+
node.previous = previous
63+
return node
64+
65+
} else if let first = head {
66+
// Have to insert at the head, so shift the existing head up once place.
67+
head = Node(value: value)
68+
head!.next = first
69+
first.previous = head
70+
return head
71+
72+
} else {
73+
// This is the very first item in the queue.
74+
head = Node(value: value)
75+
return head
76+
}
77+
}
78+
79+
/* Find the node after which to insert the new value. If this returns nil,
80+
the new value should be inserted at the head of the list. */
81+
private func findInsertionPoint(value: T) -> Node? {
82+
var node = head
83+
var prev: Node? = nil
84+
85+
while let current = node where value < current.value {
86+
prev = node
87+
node = current.next
88+
}
89+
return prev
90+
}
91+
92+
private func removeLeastImportantElement() {
93+
if let last = tail {
94+
tail = last.previous
95+
tail?.next = nil
96+
count -= 1
97+
}
98+
99+
// Note: Instead of using a tail pointer, we could just scan from the new
100+
// node until the end. Then nodes also don't need a previous pointer. But
101+
// this is much slower on large lists.
102+
}
103+
104+
public func dequeue() -> T? {
105+
if let first = head {
106+
count -= 1
107+
if count == 0 {
108+
head = nil
109+
tail = nil
110+
} else {
111+
head = first.next
112+
head!.previous = nil
113+
}
114+
return first.value
115+
} else {
116+
return nil
117+
}
118+
}
119+
}
120+
121+
extension LinkedListNode: CustomStringConvertible {
122+
public var description: String {
123+
return "\(value)"
124+
}
125+
}
126+
127+
extension BoundedPriorityQueue: CustomStringConvertible {
128+
public var description: String {
129+
var s = "<"
130+
var node = head
131+
while let current = node {
132+
s += "\(current), "
133+
node = current.next
134+
}
135+
return s + ">"
136+
}
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='osx'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

Bounded Priority Queue/BoundedPriorityQueue.playground/playground.xcworkspace/contents.xcworkspacedata

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)