Skip to content

Commit e704585

Browse files
committed
Merge branch 'master' into day41
2 parents 82cc632 + f41d339 commit e704585

File tree

2 files changed

+165
-39
lines changed

2 files changed

+165
-39
lines changed

day38/README.md

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ push - takes an integer as an argument and adds it into the heap, returning
6262
nothing. Should complete in `O(logn)` time.
6363

6464
heapify - takes an array of integers and forms a heap out of them. Should run in
65-
`O(nlogn)` (?) time.
65+
`O(n)` time.
6666

6767
size - takes no arguments and returns the number of integers in the heap `n`
6868
in `O(1)` time.
@@ -82,3 +82,83 @@ If some kind of ordering is required, i.e. max or min ordering, a heap is an
8282
efficient way to maintain that ordering even when elements are being deleted
8383
and added. The tree structure of the heap allows you to find elements in
8484
`O(logn)` time rather than `O(n)` time like in a normal array or linked list.
85+
86+
---
87+
88+
Some more thinking around heapify, aka just cram an unsorted array into
89+
a binary tree, and then compare and swap unordered parent-children pairs,
90+
from the bottom up:
91+
92+
arr = [7, 6, 5, 4, 3, 2, 1]
93+
94+
1
95+
/ \
96+
3 2
97+
/ \ / \
98+
4 6 7 5
99+
100+
101+
arr = [4, 5, 7, 3, 9, 6, 1]
102+
103+
4
104+
/ \
105+
5 7
106+
/ \ / \
107+
3 9 6 1
108+
109+
len = 7
110+
first parent = 7/2 = 3
111+
index = 3-1
112+
arr[index] == 7
113+
114+
7's children: arr[2*index + 1], arr[2*index + 2] aka 6, 1
115+
1 < 6
116+
117+
4
118+
/ \
119+
5 1
120+
/ \ / \
121+
3 9 6 7
122+
123+
next parent -> index -= 1
124+
arr[index] = 5
125+
126+
5's children: 3, 9
127+
3 < 9
128+
129+
4
130+
/ \
131+
3 1
132+
/ \ / \
133+
5 9 6 7
134+
135+
next parent -> index -= 1
136+
arr[index] = 4
137+
138+
4's children: 3, 1
139+
1 < 3
140+
141+
1
142+
/ \
143+
3 4
144+
/ \ / \
145+
5 9 6 7
146+
147+
done.
148+
149+
Need to do one bubbleDown each parent node from the bottom up, and then done.
150+
On the bottom-most level, `n/2` nodes can move at most 0 levels down.
151+
Next level, `n/4` nodes can move at most 1 level down.
152+
etc.
153+
154+
So it's a summation on i from 0 to logn:
155+
156+
`n/(2**i) * i`
157+
158+
which turns out to be `O(n)` work!
159+
160+
Edit: fix `heapify` code.
161+
162+
For more information:
163+
- https://www.cs.umd.edu/~meesh/351/mount/lectures/lect14-heapsort-analysis-part.pdf
164+
- https://www.youtube.com/watch?v=MiyLo8adrWw

day38/heap.py

Lines changed: 84 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,79 @@
1+
from collections import deque
2+
13
class MinHeap:
24
def __init__(self, arr=[]):
3-
if len(arr) > 0:
4-
self.heap = self.heapify(arr)
5-
else:
6-
self.heap = []
7-
5+
self.heap = deque()
86
self.size = 0
7+
if len(arr) > 0:
8+
self.size = len(arr)
9+
self.heapify(arr)
910

11+
# runtime: O(logn) aka the height of the heap
1012
def getMin(self):
11-
if self.size > 0
12-
ret = self.heap[0] #[1]
13-
self.heap[0] = self.heap[-1] #[1]
14-
self.heap.pop() #[]
13+
if self.size > 0:
14+
ret = self.heap.popleft()
1515
self.size -= 1
16-
self.bubbleDown() #[]
16+
if self.size > 0:
17+
self.heap.appendleft(self.heap.pop())
18+
self.bubbleDown(0)
1719
return ret
1820

21+
# runtime: O(1)
1922
def peek(self):
20-
if self.size > 0
23+
if self.size > 0:
2124
return self.heap[0]
2225

26+
# runtime: O(logn) aka the height of the heap
2327
def push(self, val):
28+
self.size += 1
2429
self.heap.append(val)
2530
self.bubbleUp()
26-
self.size += 1
2731

32+
# runtime: O(nlogn)
2833
def heapify(self, arr):
29-
# TODO
30-
return
31-
32-
def size(self):
33-
return self.size
34+
self.heap = deque(arr)
35+
for i in xrange(self.size-1, -1, -1):
36+
self.bubbleDown(i)
3437

38+
# runtime: O(1)
3539
def isEmpty(self):
3640
return self.size == 0
3741

38-
def bubbleDown(self):
39-
# TODO
40-
return
42+
def bubbleDown(self, index):
43+
if self.size > 0:
44+
i = index
45+
h = self.heap
46+
withinBounds = 2*i + 2 < self.size
47+
while withinBounds and (h[i] > h[2*i + 1] or h[i] > h[2*i + 2]):
48+
if h[i] > h[2*i + 1] and h[i] > h[2*i + 2]:
49+
if h[2*i + 1] < h[2*i + 2]:
50+
h[i], h[2*i + 1] = h[2*i + 1], h[i]
51+
i = 2*i + 1
52+
else:
53+
h[i], h[2*i + 2] = h[2*i + 2], h[i]
54+
i = 2*i + 2
55+
elif h[i] > h[2*i + 1]:
56+
h[i], h[2*i + 1] = h[2*i + 1], h[i]
57+
i = 2*i + 1
58+
elif h[i] > h[2*i + 2]:
59+
h[i], h[2*i + 2] = h[2*i + 2], h[i]
60+
i = 2*i + 2
61+
withinBounds = 2*i + 2 < self.size
62+
63+
if 2*i + 1 < self.size and h[i] > h[2*i + 1]:
64+
h[i], h[2*i + 1] = h[2*i + 1], h[i]
65+
elif 2*i + 2 < self.size and h[i] > h[2*i + 2]:
66+
h[i], h[2*i + 2] = h[2*i + 2], h[i]
4167

4268
def bubbleUp(self):
43-
# TODO
44-
return
69+
if self.size > 0:
70+
i = self.size-1
71+
h = self.heap
72+
withinBounds = i/2 >= 0
73+
while withinBounds and (h[i] < h[i/2]):
74+
h[i/2], h[i] = h[i], h[i/2]
75+
i /= 2
76+
withinBounds = i/2 >= 0
4577

4678
def testPush():
4779
h = MinHeap()
@@ -61,34 +93,35 @@ def testPush():
6193
assert h.heap[0] == -1
6294

6395
def testHeapify():
64-
# heap1 = MinHeap([1, 2, 3, 4, 5])
65-
# heap2 = MinHeap([])
66-
# heap3 = MinHeap([5, 4, 3, 2, 1])
67-
# heap4 = MinHeap([3, 1])
68-
# heap5 = MinHeap([0, -1, 1, -2, 2])
69-
# assert isMinHeap(heap1)
70-
# assert isMinHeap(heap2)
71-
# assert isMinHeap(heap3)
72-
# assert isMinHeap(heap4)
73-
# assert isMinHeap(heap5)
96+
# TODO
7497
assert True
7598

7699
def testGetMin():
77100
heap = MinHeap([2, 5, 20])
78101
assert heap.getMin() == 2
79102
assert heap.getMin() == 5
103+
assert heap.getMin() == 20
80104

81105
heap1 = MinHeap([1, 2, 3, 4, 5])
82106
assert heap1.getMin() == 1
83107
assert heap1.getMin() == 2
108+
assert heap1.getMin() == 3
109+
assert heap1.getMin() == 4
110+
assert heap1.getMin() == 5
84111

85112
heap2 = MinHeap([5, 4, 3, 2, 1])
86113
assert heap2.getMin() == 1
87114
assert heap2.getMin() == 2
115+
assert heap2.getMin() == 3
116+
assert heap2.getMin() == 4
117+
assert heap2.getMin() == 5
88118

89119
heap3 = MinHeap([2, 5, 3, 7, -1])
90120
assert heap3.getMin() == -1
91121
assert heap3.getMin() == 2
122+
assert heap3.getMin() == 3
123+
assert heap3.getMin() == 5
124+
assert heap3.getMin() == 7
92125

93126
def testPeek():
94127
heap = MinHeap([2, 5, 20])
@@ -109,26 +142,39 @@ def testPeek():
109142

110143
def testSize():
111144
heap = MinHeap()
112-
assert heap.size() == 0
145+
assert heap.size == 0
113146

114147
heap2 = MinHeap([1])
115-
assert heap.size() == 1
148+
assert heap2.size == 1
116149

117150
heap3 = MinHeap([1, 2, 3, 4])
118-
assert heap.size() == 4
151+
assert heap3.size == 4
119152

120153
def testIsEmpty():
121154
heap = MinHeap()
122155
assert heap.isEmpty()
123156

124157
heap2 = MinHeap([1])
125-
assert not heap.isEmpty()
158+
assert not heap2.isEmpty()
126159

127160
heap3 = MinHeap([1, 2, 3])
128-
assert not heap.isEmpty()
161+
assert not heap3.isEmpty()
129162

130163
def testEverything():
131-
assert True
164+
heap = MinHeap([1, 2, 3, 4, -1, -2, -7, 6, -3, -1 , -1])
165+
assert not heap.isEmpty()
166+
assert heap.size == 11
167+
assert heap.peek() == -7
168+
assert heap.getMin() == -7
169+
assert heap.getMin() == -3
170+
171+
heap.push(-10)
172+
assert heap.peek() == -10
173+
assert heap.size == 10
174+
assert heap.getMin() == -10
175+
assert heap.getMin() == -2
176+
assert heap.getMin() == -1
177+
assert heap.getMin() == -1
132178

133179
def tests():
134180
testPush()

0 commit comments

Comments
 (0)