-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbinary-heap.cpp
211 lines (164 loc) · 6.54 KB
/
binary-heap.cpp
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include <cassert>
#include <algorithm>
#include <deque>
#include <unordered_map>
// extract_keys gets keys from a map
template<typename TK, typename TV>
std::deque<TK> extract_keys(std::unordered_map<TK, TV> const& input_map) {
std::deque<TK> retval;
for (auto const& element : input_map) {
retval.push_back(element.first);
}
return retval;
}
// Implements a max heap
class BinaryHeap {
public:
// Puts keys into a binary heap with priority equal to the key's value
BinaryHeap(std::unordered_map<int,int> unsorted_priorities) :
priorities(unsorted_priorities), indices({})
{
elements = extract_keys(unsorted_priorities);
for (int i=0; i<elements.size(); i++) {
indices[elements[i]]=i;
}
heapify();
};
std::deque<int> get_heap_elements() const {
return elements;
}
bool empty() const {
return (elements.size()==0);
};
int get_priority(int nodeName) const {
return priorities.at(nodeName);
}
void insert_with_priority(int nodeName,int priority) {
priorities[nodeName]=priority;
elements.push_back(nodeName);
indices[nodeName]=elements.size()-1;
siftUp(elements.size()-1);
};
int peek_max_element() const {
return elements[0];
};
int pop_max_element() {
int max_element = elements.front();
//erase max element
priorities.erase(max_element);
indices.erase(max_element);
elements.pop_front();
// edge case: heap had only one element
if (elements.size()==0) {
return max_element;
}
// put a leaf in root position, and correct:
int temp_last_element = elements.back();
elements.pop_back();
indices[temp_last_element] = 0;
elements.push_front(temp_last_element);
siftDown(0);
assert(priorities.size()==indices.size()&&priorities.size()==elements.size());
return max_element;
};
void increase_priority_by(int nodeName,int amount_of_increase) {
if (priorities.count(nodeName) ) {
priorities[nodeName]+=amount_of_increase;
siftUp(indices[nodeName]);
}
};
private:
/* The flattened binary tree sorted as an array. Will be kept in heap order.
A deque is used instead of a vector because extract_max operation will pop_front from elements. */
std::deque<int> elements;
//holds current priority of each element
std::unordered_map<int,int> priorities;
/* while the existence of an indices map might seem extraneous,
it is necessary since sifting operations use the index of the element being sifted */
std::unordered_map<int,int> indices;
void siftUp(int index) {
int parentIndex = (index-1)/2;
while (parentIndex>=0) {
auto currentPriority = priorities[elements[index]];
auto parentPriority = priorities[elements[parentIndex]];
bool isHeap = (currentPriority<=parentPriority);
if (isHeap) break;
heap_swap(index,parentIndex);
index = parentIndex;
parentIndex = (index-1)/2;
}
};
void siftDown(int index) {
/* swaps an element with bigger child until heap property is restored */
int leftChildIndex = 2*index+1;
int rightChildIndex = 2*index+2;
while (leftChildIndex<elements.size()) {
// i.e. while the current node has at least one child
auto currentPriority = priorities[elements[index]];
auto leftChildPriority = priorities[elements[leftChildIndex]];
if (rightChildIndex<elements.size()) {
// Case where current node has both left and right children
auto rightChildPriority = priorities[elements[rightChildIndex]];
bool isHeap = (currentPriority>=leftChildPriority) && (currentPriority>=rightChildPriority);
if (isHeap) break;
if (leftChildPriority>=rightChildPriority) {
heap_swap(leftChildIndex,index);
index = leftChildIndex;
} else {
heap_swap(rightChildIndex,index);
index = rightChildIndex;
}
} else {
/* Case where current node has only a left child - e.g., a complete tree with 4 elements
has an element with a left child but no right child. */
bool isHeap = (currentPriority>=leftChildPriority);
if (isHeap) break;
heap_swap(leftChildIndex,index);
index = leftChildIndex;
}
leftChildIndex = 2*index+1;
rightChildIndex = 2*index+2;
}
};
/*It is possible to turn a set into a heap by inserting one-by-one;
but because of siftUp subroutine, this would take O(n log n) time.
By putting everything into the tree at once, and sifting down,
we exploit the fact that most elements are in the bottom half of the tree
and can only sift down so far.
Note the reverse order in which we apply siftDown is due to the following property of siftDown:
If both subtrees beneath x satisfy the Heap property, then sifting x down will restore the Heap property
to the subtree with root x. */
void heapify() {
for (int i=(elements.size()-1); i>=0; --i) {
siftDown(i);
}
};
void heap_swap(const int index1,const int index2) {
assert (index1<elements.size() && index2<elements.size());
// swap in the indices map:
indices[elements[index1]]=index2;
indices[elements[index2]]=index1;
// then swap in the tree representation
std::swap(elements[index1],elements[index2]) ;
}
};
// int main()
// {
// std::unordered_map<int,int> m;
// std::cout<<"enter some unique ints; their priorities will be their values: \n";
// auto v = readVector<int>();
// for (auto el: v) {
// m[el]=el;
// }
// auto b = BinaryHeap(m);
// std::cout<<"enter another node to insert: \n";
// int n;
// std::cin>>n;
// b.insert_with_priority(n,n);
// std::cout<<"tree is now: ";
// for (auto x: b.display() ) {
// std::cout<<x<<", ";
// }
// std::cout<<std::endl;
// return 0;
// }