Skip to content

Commit 6abea57

Browse files
committed
Added written description of Heap
Added a written description for Heap. Small terminology changes in Heap.swift
1 parent e349363 commit 6abea57

File tree

3 files changed

+171
-6
lines changed

3 files changed

+171
-6
lines changed

Heap/Heap.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public struct MaxHeap<T : Comparable> : Heap {
3838

3939
init(array: [T]) {
4040
self.init()
41+
//This could be optimized into O(n) time using the Floyd algorithm instead of O(nlog(n))
4142
mem.reserveCapacity(array.count)
4243
for value in array {
4344
insert(value)
@@ -57,7 +58,7 @@ public struct MaxHeap<T : Comparable> : Heap {
5758
*/
5859
public mutating func insert(value: T) {
5960
mem.append(value)
60-
upShift(index: mem.count - 1)
61+
shiftUp(index: mem.count - 1)
6162
}
6263

6364
public mutating func insert<S : SequenceType where S.Generator.Element == T>(sequence: S) {
@@ -85,7 +86,7 @@ public struct MaxHeap<T : Comparable> : Heap {
8586
mem[0] = last
8687

8788
//Downshift the new top value
88-
downShift()
89+
shiftDown()
8990

9091
return value
9192
}
@@ -114,7 +115,7 @@ public struct MaxHeap<T : Comparable> : Heap {
114115
/**
115116
* Restore the heap property above a given index.
116117
*/
117-
private mutating func upShift(index index: Int) {
118+
private mutating func shiftUp(index index: Int) {
118119
var childIndex = index
119120
let child = mem[childIndex]
120121
while childIndex != 0 {
@@ -134,9 +135,9 @@ public struct MaxHeap<T : Comparable> : Heap {
134135
}
135136

136137
/**
137-
* Maintains the heap property of parents > both children
138+
* Maintains the heap property of parent > both children
138139
*/
139-
private mutating func downShift(index index: Int = 0) {
140+
private mutating func shiftDown(index index: Int = 0) {
140141
var parentIndex = index
141142
var leftChildIndex = firstChildIndex(parentIndex)
142143

Heap/README.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Heap
2+
3+
Written for the Swift Algorithm Club by Kevin Randrup.
4+
5+
### Intro
6+
7+
A heap is a specialized type of tree with only two operations, `insert()` and `remove()`. Heaps are usually implemented using an array.
8+
9+
In Swift, a Heap protocol would look something like this:
10+
11+
```
12+
protocol Heap {
13+
typealias Value
14+
func insert(value: Value)
15+
func remove() -> Value?
16+
}
17+
```
18+
19+
### Heap Property
20+
21+
In any given heap, ether every parent node is greater than its child nodes or every parent is less than its child nodes. This "heap property" is true for every single node in the tree.
22+
23+
An example:
24+
25+
```
26+
(10)
27+
/ \
28+
(7) (2)
29+
/ \
30+
(5) (1)
31+
```
32+
The heap property for this tree is satisfied because every parent node is greater than its children node. `(10)` is greater than `(7)` and `(3)`. `(7)` is greater than `(3)` and `(1)`.
33+
34+
### So what's the problem with trees?
35+
36+
#### The balancing issue
37+
If you randomly add and remove data, trees will have `O(log(n))` performance. Unfortunatly our data ususally isn't perfectly random so tree can become unbalanced and take more than `O(log(n))` time. There are a lot of ways to combat this issue in trees (see Binary Trees, AVL tree, Red-Black Tree) but with heaps we don't actually need the entire tree to be sorted. We just want the heap property to be fullfilled.
38+
39+
#### The memory issue
40+
Trees also take up more memory than they data they store. You need to allocate memory for nodes and pointers to the left/right child nodes. Compare this to an [array](../Fixed Size Array).
41+
42+
```
43+
(10)
44+
/ \
45+
(7) (2)
46+
/ \
47+
(5) (1)
48+
```
49+
Notice that if you go horizontally accross the tree shown above, the values are in no particular order; it does not matter whether `(7)` is greater than or less that `(2)`, as long as they are both less than `(10)`. The lack of order means we can fill out our tree one row at a time, as long as we maintain the heap property.
50+
51+
#### The solution
52+
53+
Adding only to the end of the heap allows us to implement the Heap with an array; this may seem like an odd way to implement a tree-like structure but it is very efficient in both time and space. This is how we're going to store the array shown above:
54+
55+
```
56+
[10|7|2|5|1]
57+
```
58+
59+
We can use simple path to find the parent or child node for a node at a particular index. Let's create a mapping of the array indexes to figure out how they relate:
60+
61+
|Child Node|Parent Node|
62+
|----------|-----------|
63+
| 1,2 | 0 |
64+
| 3,4 | 1 |
65+
| 5,6 | 2 |
66+
| 7,8 | 3 |
67+
68+
Using integer arithmetic and division the solution is a trivial computation:
69+
70+
```
71+
childOne = (parent * 2) + 1
72+
childTwo = childOne + 1 = (parent * 2) + 2
73+
parent = (child - 1) / 2
74+
```
75+
76+
These equations let us find the parent or child index for any node in `O(1)` time without using nodes; this means we don't need to use extra memory to allocate nodes that contain data and references to the left and right children. We can also append a node to the end of the array in `O(1)` time.
77+
78+
### Insertion
79+
80+
Lets go through an example insertion. Lets insert `(6)` into this heap:
81+
82+
```
83+
(10)
84+
/ \
85+
(7) (2)
86+
/ \
87+
(5) (1)
88+
```
89+
Lets add `(6)` to the last available space on the last row.
90+
91+
```
92+
(10)
93+
/ \
94+
(7) (2)
95+
/ \ /
96+
(5) (1) (6)
97+
```
98+
99+
Unfortunately, the heap property is no longer satisfied because `(2)` is above `(6)` and we want higher numbers above lower numbers. We're just going to swap `(6)` and `(2)` to restore the heap property. We keep swapping our inserted value with its parent until the parent is larger or until we get to the top of the tree. This is called **shift-up** or **sifting** and is done after every insertion. The time required for shifting up is proportional to the height of the tree so it takes `O(log(n))` time.
100+
101+
```
102+
(10)
103+
/ \
104+
(7) (6)
105+
/ \ /
106+
(5) (1) (2)
107+
```
108+
109+
110+
### Removal
111+
112+
The `remove()` method is implemented similar to `insert()`. Lets remove 10 from the previous tree:
113+
114+
```
115+
(10)
116+
/ \
117+
(7) (2)
118+
/ \
119+
(5) (1)
120+
```
121+
122+
So what happens to the empty spot at the top?
123+
124+
```
125+
(??) (10)
126+
/ \
127+
(7) (2)
128+
/ \
129+
(5) (1)
130+
```
131+
Like `insert()`, we're going to take the last object we have, stick it up on top of the tree, and restore the heap property.
132+
133+
```
134+
(1) (10)
135+
/ \
136+
(7) (2)
137+
/
138+
(5)
139+
```
140+
141+
Let's look at how to **shift-down** `(1)`. To maintain the heap property, we want to the highest number of top. We have 2 candidates for swapping places with `(1)`: `(7)` and `(2)`. Choose the highest number between these three nodes to be on top; this is `(7)`, so swapping `(1)` and `(7)` gives us the following tree.
142+
143+
```
144+
(7) (10)
145+
/ \
146+
(1) (2)
147+
/
148+
(5)
149+
```
150+
151+
Keep shifting down until the node doesn't have any children or it's largest than both its children. The time required for shifting all the way down is proportional to the height of the tree so it takes `O(log(n))` time. For our heap we only need 1 more swap to restore the heap property.
152+
153+
```
154+
(7)
155+
/ \
156+
(5) (2)
157+
/
158+
(1)
159+
```
160+
Finally we can return `(10)`.
161+
162+
### Sources
163+
164+
[Heap on Wikipedia](https://en.wikipedia.org/wiki/Heap_%28data_structure%29)

README.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ Lists:
123123
- Splay Tree
124124
- Threaded Binary Tree
125125
- kd-Tree
126-
- Heap
126+
- [Heap](Heap/)
127127
- Fibonacci Heap
128128
- Trie
129129

0 commit comments

Comments
 (0)