-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
BinaryHeap.cs
236 lines (210 loc) · 7.54 KB
/
BinaryHeap.cs
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
using System;
using System.Collections.Generic;
namespace DataStructures.Heap;
/// <summary>
/// A generic implementation of a binary heap.
/// </summary>
/// <remarks>
/// A binary heap is a complete binary tree that satisfies the heap property;
/// that is every node in the tree compares greater/less than or equal to its left and right
/// child nodes. Note that this is different from a binary search tree, where every node
/// must be the largest/smallest node of all of its children.
/// Although binary heaps are not very efficient, they are (probably) the simpliest heaps
/// to understand and implement.
/// More information: https://en.wikipedia.org/wiki/Binary_heap .
/// </remarks>
/// <typeparam name="T">Type of elements in binary heap.</typeparam>
public class BinaryHeap<T>
{
/// <summary>
/// Comparer to use when comparing elements.
/// </summary>
private readonly Comparer<T> comparer;
/// <summary>
/// List to hold the elements of the heap.
/// </summary>
private readonly List<T> data;
/// <summary>
/// Initializes a new instance of the <see cref="BinaryHeap{T}" /> class.
/// </summary>
public BinaryHeap()
{
data = new List<T>();
comparer = Comparer<T>.Default;
}
/// <summary>
/// Initializes a new instance of the <see cref="BinaryHeap{T}" /> class with a custom comparision function.
/// </summary>
/// <param name="customComparer">The custom comparing function to use to compare elements.</param>
public BinaryHeap(Comparer<T> customComparer)
{
data = new List<T>();
comparer = customComparer;
}
/// <summary>
/// Gets the number of elements in the heap.
/// </summary>
public int Count => data.Count;
/// <summary>
/// Add an element to the binary heap.
/// </summary>
/// <remarks>
/// Adding to the heap is done by append the element to the end of the backing list,
/// and pushing the added element up until the heap property is restored.
/// </remarks>
/// <param name="element">The element to add to the heap.</param>
/// <exception cref="ArgumentException">Thrown if element is already in heap.</exception>
public void Push(T element)
{
data.Add(element);
HeapifyUp(data.Count - 1);
}
/// <summary>
/// Remove the top/root of the binary heap (ie: the largest/smallest element).
/// </summary>
/// <remarks>
/// Removing from the heap is done by swapping the top/root with the last element in
/// the backing list, removing the last element, and pushing the new root down
/// until the heap property is restored.
/// </remarks>
/// <returns>The top/root of the heap.</returns>
/// <exception cref="InvalidOperationException">Thrown if heap is empty.</exception>
public T Pop()
{
if (Count == 0)
{
throw new InvalidOperationException("Heap is empty!");
}
var elem = data[0];
data[0] = data[^1];
data.RemoveAt(data.Count - 1);
HeapifyDown(0);
return elem;
}
/// <summary>
/// Return the top/root of the heap without removing it.
/// </summary>
/// <returns>The top/root of the heap.</returns>
/// <exception cref="InvalidOperationException">Thrown if heap is empty.</exception>
public T Peek()
{
if (Count == 0)
{
throw new InvalidOperationException("Heap is empty!");
}
return data[0];
}
/// <summary>
/// Returns element if it compares larger to the top/root of the heap, else
/// inserts element into the heap and returns the top/root of the heap.
/// </summary>
/// <param name="element">The element to check/insert.</param>
/// <returns>element if element compares larger than top/root of heap, top/root of heap otherwise.</returns>
public T PushPop(T element)
{
if (Count == 0)
{
return element;
}
if (comparer.Compare(element, data[0]) < 0)
{
var tmp = data[0];
data[0] = element;
HeapifyDown(0);
return tmp;
}
return element;
}
/// <summary>
/// Check if element is in the heap.
/// </summary>
/// <param name="element">The element to check for.</param>
/// <returns>true if element is in the heap, false otherwise.</returns>
public bool Contains(T element) => data.Contains(element);
/// <summary>
/// Remove an element from the heap.
/// </summary>
/// <remarks>
/// In removing an element from anywhere in the heap, we only need to push down or up
/// the replacement value depending on how the removed value compares to its
/// replacement value.
/// </remarks>
/// <param name="element">The element to remove from the heap.</param>
/// <exception cref="ArgumentException">Thrown if element is not in heap.</exception>
public void Remove(T element)
{
var idx = data.IndexOf(element);
if (idx == -1)
{
throw new ArgumentException($"{element} not in heap!");
}
Swap(idx, data.Count - 1);
var tmp = data[^1];
data.RemoveAt(data.Count - 1);
if (idx < data.Count)
{
if (comparer.Compare(tmp, data[idx]) > 0)
{
HeapifyDown(idx);
}
else
{
HeapifyUp(idx);
}
}
}
/// <summary>
/// Swap the elements in the heap array at the given indices.
/// </summary>
/// <param name="idx1">First index.</param>
/// <param name="idx2">Second index.</param>
private void Swap(int idx1, int idx2)
{
var tmp = data[idx1];
data[idx1] = data[idx2];
data[idx2] = tmp;
}
/// <summary>
/// Recursive function to restore heap properties.
/// </summary>
/// <remarks>
/// Restores heap property by swapping the element at <paramref name="elemIdx" />
/// with its parent if the element compares greater than its parent.
/// </remarks>
/// <param name="elemIdx">The element to check with its parent.</param>
private void HeapifyUp(int elemIdx)
{
var parent = (elemIdx - 1) / 2;
if (parent >= 0 && comparer.Compare(data[elemIdx], data[parent]) > 0)
{
Swap(elemIdx, parent);
HeapifyUp(parent);
}
}
/// <summary>
/// Recursive function to restore heap properties.
/// </summary>
/// <remarks>
/// Restores heap property by swapping the element at <paramref name="elemIdx" />
/// with the larger of its children.
/// </remarks>
/// <param name="elemIdx">The element to check with its children.</param>
private void HeapifyDown(int elemIdx)
{
var left = 2 * elemIdx + 1;
var right = 2 * elemIdx + 2;
var leftLargerThanElem = left < Count && comparer.Compare(data[elemIdx], data[left]) < 0;
var rightLargerThanElem = right < Count && comparer.Compare(data[elemIdx], data[right]) < 0;
var leftLargerThanRight = left < Count && right < Count && comparer.Compare(data[left], data[right]) > 0;
if (leftLargerThanElem && leftLargerThanRight)
{
Swap(elemIdx, left);
HeapifyDown(left);
}
else if (rightLargerThanElem && !leftLargerThanRight)
{
Swap(elemIdx, right);
HeapifyDown(right);
}
}
}