-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathheap.py
More file actions
186 lines (161 loc) · 5.55 KB
/
heap.py
File metadata and controls
186 lines (161 loc) · 5.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# Heap
from math import log, ceil, floor
from collections import defaultdict, deque
from operator import le, lt, ge, gt
from copy import deepcopy as dcopy, copy
class Heap:
NAME = 'Heap'
def __init__(self, *things):
self.data = deque()
self.dict = {}
self += things
def __iadd__(self, iterable):
for x in iterable:
self.append(x)
return self
def copy(self):
return copy(self)
def deepcopy(self):
return dcopy(self)
def __len__(self):
return self.data.__len__()
def __iter__(self):
new = self.copy()
while new:
yield new.popleft()
return None
def __repr__(self):
return self.__str__()
def __str__(self):
def wraping(a_deque):
a_deque.appendleft(f"{self.NAME} {'v'*12}")
a_deque.append('^'*20)
return a_deque
data = self.data
n_layers = ceil( log( len(data)+1, 2 ) )
if n_layers < 1:
return '\n'.join(wraping(deque()))
elif n_layers == 1:
stack = deque([str(data[0])])
stack = wraping(stack)
return '\n'.join(stack)
else:
stack = deque()
ele_per_layer = [2**x for x in range(n_layers)]
# +2 account for space encompass each element
span_per_ele = max(len(str(x)) for x in data) + 1
# The real number of element for the last layer is in [-1]
graph_width = ele_per_layer[-1] * (span_per_ele)
start = 0
for epl in ele_per_layer:
end = start + epl
ele_span = graph_width // epl
# every element is center-aligned
this_layer = ''.join( f"{str(x):^{ele_span}}" for x in list(data)[start:end] )
stack.append(this_layer)
start = end
stack = wraping(stack)
# right-align all layers
return '\n'.join(f"{row:<{graph_width}}" for row in stack)
def append(self, x):
self.data.append(x)
idx = self.data.__len__() - 1
self.dict[x] = idx
self.up(idx)
def appendleft(self, x):
self.data.appendleft(x)
self.dict[x] = 0
self.down([0])
def pop(self):
x = self.data.pop()
del self.dict[x]
# TODO: potential bug if there are multiple entries for the same key
return x
def popleft(self):
# Must have at least one element to pop()
data = self.data
dic = self.dict
if len(data) >= 1:
data[0], data[-1] = data[-1], data[0]
dic[data[0]] = 0
del dic[data[-1]]
out = data.pop()
if len(data) > 0:
self.down(0)
return out
else:
raise IndexError("Heap is empty.")
def up(self, child_i):
data = self.data
dic = self.dict
compare = self.UP_COMPARE
child_v = data[child_i]
# If index i == 0, no need to do anthing.
while child_i > 0:
parent_i = (child_i-1)//2
parent_v = data[parent_i]
if compare(child_v, parent_v):
data[child_i] = parent_v
dic[parent_v] = child_i
child_i = parent_i
else:
# Can no longer move upwards
break
data[child_i] = child_v
dic[child_v] = child_i
def down(self, parent_i):
compare = self.DOWN_COMPARE
data = self.data
dic = self.dict
end = len(data)-1
if parent_i == end:
return None
if parent_i > end:
raise IndexError(f"This index exceed the length: {parent_i}")
parent_v = data[parent_i]
child_i = parent_i
while True:
lchild_i = parent_i*2+1
rchild_i = lchild_i+1
# Child may not exist for one node, check before getting the value
# If Right Exist, this means that Left exist.
# Reversely if Left do not exist, there is no Right.
if lchild_i <= end:
lchild_v = data[lchild_i]
# We do not sure to swap with L or R
if rchild_i <= end:
rchild_v = data[rchild_i]
if compare(lchild_v, rchild_v) and compare(parent_v, rchild_v):
child_i = rchild_i
data[parent_i] = rchild_v
dic[rchild_v] = parent_i
parent_i = child_i
continue
# Can only swap with L
if compare(parent_v, lchild_v):
child_i = lchild_i
data[parent_i] = lchild_v
dic[lchild_v] = parent_i
parent_i = child_i
continue
break
data[child_i] = parent_v
dic[parent_v] = child_i
def update(self, old_v, new_v):
dic = self.dict
old_i = dic[old_v]
del dic[old_v]
dic[new_v] = old_i
self.data[old_i] = new_v
if self.DOWN_COMPARE(new_v, old_v):
self.down(old_i)
else:
self.up(old_i)
class MaxHeap(Heap):
NAME = "MaxHeap"
UP_COMPARE = gt
DOWN_COMPARE = lt
class MinHeap(Heap):
NAME = "MinHeap"
UP_COMPARE = lt
DOWN_COMPARE = gt