Skip to content

Commit e349363

Browse files
committed
Added Heap
Created Heap protocol and MaxHeap. I also included some basic tests.
1 parent 501f0ed commit e349363

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

Heap/Heap.swift

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//
2+
// Heap.swift
3+
// Written for the Swift Algorithm Club by Kevin Randrup
4+
//
5+
6+
/**
7+
* A heap is a type of tree data structure with 2 charactersitics:
8+
* 1. Parent nodes are either greater or less than each one of their children (called max heaps and min heaps respectively)
9+
* 2. Only the top item is accessible (greatest or smallest)
10+
*
11+
* This results in a data structure that stores n items in O(n) space. Both insertion and deletion take O(log(n)) time (amortized).
12+
*/
13+
protocol Heap {
14+
typealias Value
15+
mutating func insert(value: Value)
16+
mutating func remove() -> Value?
17+
var count: Int { get }
18+
var isEmpty: Bool { get }
19+
}
20+
21+
/**
22+
* A MaxHeap stores the highest items at the top. Calling remove() will return the highest item in the heap.
23+
*/
24+
public struct MaxHeap<T : Comparable> : Heap {
25+
26+
typealias Value = T
27+
28+
/** 10
29+
* 7 5
30+
* 1 2 3
31+
* Will be represented as [10, 7, 5, 1, 2, 3]
32+
*/
33+
private var mem: [T]
34+
35+
init() {
36+
mem = [T]()
37+
}
38+
39+
init(array: [T]) {
40+
self.init()
41+
mem.reserveCapacity(array.count)
42+
for value in array {
43+
insert(value)
44+
}
45+
}
46+
47+
public var isEmpty: Bool {
48+
return mem.isEmpty
49+
}
50+
51+
public var count: Int {
52+
return mem.count
53+
}
54+
55+
/**
56+
* Inserts the value into the Heap in O(log(n)) time
57+
*/
58+
public mutating func insert(value: T) {
59+
mem.append(value)
60+
upShift(index: mem.count - 1)
61+
}
62+
63+
public mutating func insert<S : SequenceType where S.Generator.Element == T>(sequence: S) {
64+
for value in sequence {
65+
insert(value)
66+
}
67+
}
68+
69+
/**
70+
* Removes the max value from the heap in O(logn)
71+
*/
72+
public mutating func remove() -> T? {
73+
//Handle empty/1 element cases.
74+
if mem.isEmpty {
75+
return nil
76+
}
77+
else if mem.count == 1 {
78+
return mem.removeLast()
79+
}
80+
81+
82+
// Pull the last element up to replace the first one
83+
let value = mem[0]
84+
let last = mem.removeLast()
85+
mem[0] = last
86+
87+
//Downshift the new top value
88+
downShift()
89+
90+
return value
91+
}
92+
93+
//MARK: Private implmentation
94+
95+
/**
96+
* Returns the parent's index given the child's index.
97+
* 1,2 -> 0
98+
* 3,4 -> 1
99+
* 5,6 -> 2
100+
* 7,8 -> 3
101+
*/
102+
private func parentIndex(childIndex childIndex: Int) -> Int {
103+
return (childIndex - 1) / 2
104+
}
105+
106+
private func firstChildIndex(index: Int) -> Int {
107+
return index * 2 + 1
108+
}
109+
110+
@inline(__always) private func validIndex(index: Int) -> Bool {
111+
return index < mem.endIndex
112+
}
113+
114+
/**
115+
* Restore the heap property above a given index.
116+
*/
117+
private mutating func upShift(index index: Int) {
118+
var childIndex = index
119+
let child = mem[childIndex]
120+
while childIndex != 0 {
121+
let parentIdx = parentIndex(childIndex: childIndex)
122+
let parent = mem[parentIdx]
123+
//If the child doesn't need to be swapped up, return
124+
if child <= parent {
125+
return
126+
}
127+
//Otherwise, swap the child up the tree
128+
mem[parentIdx] = child
129+
mem[childIndex] = parent
130+
131+
//Update childIdx
132+
childIndex = parentIdx
133+
}
134+
}
135+
136+
/**
137+
* Maintains the heap property of parents > both children
138+
*/
139+
private mutating func downShift(index index: Int = 0) {
140+
var parentIndex = index
141+
var leftChildIndex = firstChildIndex(parentIndex)
142+
143+
//Loop preconditions: parentIndex and left child index are set
144+
while (validIndex(leftChildIndex)) {
145+
let rightChildIndex = leftChildIndex + 1
146+
let highestIndex: Int
147+
148+
//If we have valid right and left indexes, choose the highest one
149+
if (validIndex(rightChildIndex)) {
150+
let left = mem[leftChildIndex]
151+
let right = mem[rightChildIndex]
152+
highestIndex = (left > right) ? leftChildIndex : rightChildIndex
153+
} else {
154+
highestIndex = leftChildIndex
155+
}
156+
157+
//If the child > parent, swap them
158+
let parent = mem[parentIndex]
159+
let highestChild = mem[highestIndex]
160+
if highestChild <= parent { return }
161+
162+
mem[parentIndex] = highestChild
163+
mem[highestIndex] = parent
164+
165+
//Set the loop preconditions
166+
parentIndex = highestIndex
167+
leftChildIndex = firstChildIndex(parentIndex)
168+
}
169+
}
170+
}

Heap/HeapTests.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// HeapTests.swift
3+
// Written for the Swift Algorithm Club by Kevin Randrup
4+
//
5+
6+
import XCTest
7+
@testable import Test
8+
9+
class HeapTests: XCTestCase {
10+
11+
func testIsEmpty() {
12+
var heap = MaxHeap<Int>()
13+
XCTAssertTrue(heap.isEmpty)
14+
heap.insert(1)
15+
XCTAssertFalse(heap.isEmpty)
16+
heap.remove()
17+
XCTAssertTrue(heap.isEmpty)
18+
}
19+
20+
func testInsertRemove() {
21+
/** 9
22+
* 7 5
23+
* 1 2 3
24+
* Should be represented in memory as [9, 5, 7, 1, 3, 2] though we are just testing the effects.
25+
*/
26+
var heap = MaxHeap<Int>()
27+
heap.insert([1, 3, 2, 7, 5, 9])
28+
29+
//Should be removed in order
30+
XCTAssertEqual(9, heap.remove())
31+
XCTAssertEqual(7, heap.remove())
32+
XCTAssertEqual(5, heap.remove())
33+
XCTAssertEqual(3, heap.remove())
34+
XCTAssertEqual(2, heap.remove())
35+
XCTAssertEqual(1, heap.remove())
36+
XCTAssertNil(heap.remove())
37+
}
38+
39+
func testCount() {
40+
var heap = MaxHeap<Int>()
41+
XCTAssertEqual(0, heap.count)
42+
heap.insert(1)
43+
XCTAssertEqual(1, heap.count)
44+
}
45+
46+
func testRemoveEmpty() {
47+
var heap = MaxHeap<Int>()
48+
let removed = heap.remove()
49+
XCTAssertNil(removed)
50+
}
51+
}

0 commit comments

Comments
 (0)