forked from TheAlgorithms/TypeScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathheap.ts
More file actions
202 lines (175 loc) · 5.59 KB
/
Copy pathheap.ts
File metadata and controls
202 lines (175 loc) · 5.59 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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/**
* A heap is a complete binary tree
* In a complete binary tree each level is filled before lower levels are added
* Each level is filled from left to right
*
* In a (min|max) heap the value of every node is (less|greater) than that of its children
*
* The heap is often implemented using an array structure.
* In the array implementation, the relationship between a parent index and its two children
* are ((parentindex * 2) + 1) and ((parentindex * 2) + 2)
*/
export abstract class Heap<T> {
protected heap: T[]
// A comparison function. Returns true if a should be the parent of b.
protected compare: (a: T, b: T) => boolean
constructor(compare: (a: T, b: T) => boolean) {
this.heap = []
this.compare = compare
}
/**
* Compares the value at parentIndex with the value at childIndex
* In a maxHeap, the value at parentIndex should be larger than the value at childIndex
* In a minHeap, the value at parentIndex should be smaller than the value at childIndex
*/
private isRightlyPlaced(childIndex: number, parentIndex: number): boolean {
return this.compare(this.heap[parentIndex], this.heap[childIndex])
}
/**
* In a maxHeap, the index with the larger value is returned
* In a minHeap, the index with the smaller value is returned
*/
private getChildIndexToSwap(
leftChildIndex: number,
rightChildIndex: number
): number {
if (rightChildIndex >= this.size()) {
return leftChildIndex
}
return this.compare(this.heap[leftChildIndex], this.heap[rightChildIndex])
? leftChildIndex
: rightChildIndex
}
public insert(value: T): void {
this.heap.push(value)
this.bubbleUp()
}
public extract(): T {
const maxElement = this.heap[0]
this.heap[0] = this.heap[this.size() - 1]
this.heap.pop()
this.sinkDown()
return maxElement
}
public size(): number {
return this.heap.length
}
public isEmpty(): boolean {
return this.size() === 0
}
protected swap(a: number, b: number): void {
;[this.heap[a], this.heap[b]] = [this.heap[b], this.heap[a]]
}
protected bubbleUp(index: number = this.size() - 1): void {
let parentIndex
while (index > 0) {
parentIndex = Math.floor((index - 1) / 2)
if (this.isRightlyPlaced(index, parentIndex)) break
this.swap(parentIndex, index)
index = parentIndex
}
}
private sinkDown(): void {
let index = 0
let leftChildIndex = this.getLeftChildIndex(index)
let rightChildIndex = this.getRightChildIndex(index)
let childIndexToSwap
while (this.heap[leftChildIndex] || this.heap[rightChildIndex]) {
childIndexToSwap = this.getChildIndexToSwap(
leftChildIndex,
rightChildIndex
)
if (this.isRightlyPlaced(childIndexToSwap, index)) break
this.swap(childIndexToSwap, index)
index = childIndexToSwap
leftChildIndex = this.getLeftChildIndex(index)
rightChildIndex = this.getRightChildIndex(index)
}
}
private getLeftChildIndex(index: number): number {
return index * 2 + 1
}
private getRightChildIndex(index: number): number {
return index * 2 + 2
}
public check(): void {
this._check()
}
private _check(index: number = 0): void {
if (!this.heap[index]) return
const leftChildIndex = this.getLeftChildIndex(index)
const rightChildIndex = this.getRightChildIndex(index)
if (
this.heap[leftChildIndex] &&
!this.isRightlyPlaced(leftChildIndex, index)
) {
throw new Error('Heap does not adhere to heap invariant')
}
if (
this.heap[rightChildIndex] &&
!this.isRightlyPlaced(rightChildIndex, index)
) {
throw new Error('Heap does not adhere to heap invariant')
}
this._check(leftChildIndex)
this._check(rightChildIndex)
}
}
export class MinHeap<T> extends Heap<T> {
constructor(compare: (a: T, b: T) => boolean = (a: T, b: T) => a < b) {
super(compare)
}
}
export class MaxHeap<T> extends Heap<T> {
constructor(compare: (a: T, b: T) => boolean = (a: T, b: T) => a > b) {
super(compare)
}
}
export class PriorityQueue<T> extends MinHeap<T> {
// Maps from the n'th node to its index within the heap.
private keys: number[]
// Maps from element to its index with keys.
private keys_index: (a: T) => number
constructor(
keys_index: (a: T) => number,
num_keys: number,
compare: (a: T, b: T) => boolean = (a: T, b: T) => a < b
) {
super(compare)
this.keys = Array(num_keys).fill(-1)
this.keys_index = keys_index
}
protected swap(a: number, b: number): void {
const akey = this.keys_index(this.heap[a])
const bkey = this.keys_index(this.heap[b])
;[this.keys[akey], this.keys[bkey]] = [this.keys[bkey], this.keys[akey]]
super.swap(a, b)
}
public insert(value: T): void {
this.keys[this.keys_index(value)] = this.size()
super.insert(value)
}
public extract(): T {
// Unmark the highest priority element and set key to zero for the last element in the heap.
this.keys[this.keys_index(this.heap[0])] = -1
if (this.size() > 1) {
this.keys[this.keys_index(this.heap[this.size() - 1])] = 0
}
return super.extract()
}
public increasePriority(idx: number, value: T): void {
if (this.keys[idx] === -1) {
// If the key does not exist, insert the value.
this.insert(value)
return
}
const key = this.keys[idx]
if (this.compare(this.heap[key], value)) {
// Do not do anything if the value in the heap already has a higher priority.
return
}
// Increase the priority and bubble it up the heap.
this.heap[key] = value
this.bubbleUp(key)
}
}