Skip to content

Commit 6f40198

Browse files
author
Isaac Ramirez
committed
- implement IndexMinPQ
- implement Multiway client
1 parent 25c1bcc commit 6f40198

File tree

3 files changed

+587
-0
lines changed

3 files changed

+587
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/** @module ADTs */
2+
3+
const { newArrayOf } = require('../../utils')
4+
const { compare } = require('../../common')
5+
6+
/**
7+
* Generic Minimum Priority Queue implementation with associated indices.
8+
* @memberof module:ADTs
9+
* @see pages: 320, 323, 333, 334.
10+
* @see [edu.princeton.cs.algs4.IndexMinPQ.java]{@link https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/IndexMinPQ.java.html}
11+
*/
12+
class IndexMinPQ {
13+
/**
14+
* Creates a PQ of capacity `maxN` with possible indices between `0` an `maxN - 1`.
15+
* @param {number} [maxN] - Maximum fixed size for the Priority Queue.
16+
*/
17+
constructor (maxN) {
18+
/**
19+
* Number of elements on PQ.
20+
* @private
21+
* @type {number}
22+
*/
23+
this._n = 0
24+
25+
/**
26+
* Binary Heap using 1-based indexing.
27+
* @private
28+
* @type {Array}
29+
*/
30+
this._pq = [] // default initialization
31+
32+
/**
33+
* Inverse of `_pq`: `_qp[_pq[i]] = _pq[_qp[i]] = i`.<br>
34+
* `_qp[i]` gives the position of `i` in `_pq[]`, the index `j` such that `_pq[j]` is `i`.
35+
* @private
36+
* @type {Array}
37+
*/
38+
this._qp = [] // default initialization
39+
40+
/**
41+
* The array that holds the keys with priorities.
42+
* @private
43+
* @type {Array}
44+
*/
45+
this._keys = [] // default initialization
46+
47+
// constructor initialization
48+
if (typeof maxN === 'number' && Number.isInteger(maxN) && maxN > 0) {
49+
this._pq = newArrayOf(maxN + 1, undefined)
50+
this._qp = newArrayOf(maxN + 1, -1) // -1 indicates that key is not present
51+
this._keys = newArrayOf(maxN + 1, undefined)
52+
}
53+
54+
Object.seal(this)
55+
}
56+
57+
// Private instance methods
58+
// ==================================================
59+
60+
/**
61+
* Compares if the key at index `i` is greater than the key at index `j`.
62+
* @private
63+
* @param {number} i - The inverse index of the first key.
64+
* @param {number} j - The inverse index of the second key.
65+
* @returns {boolean} If the key at index `i` is greater than the key at index`j`.
66+
*/
67+
_greater (i, j) {
68+
return compare(this._keys[this._pq[i]], this._keys[this._pq[j]]) > 0
69+
}
70+
71+
/**
72+
* Exchanges the indexes `i` and `j`.
73+
* Updates the inverse array `_qp`.
74+
* @private
75+
* @param {number} i - The index of the first key.
76+
* @param {number} j - The index of the second key.
77+
*/
78+
_exch (i, j) {
79+
const prevI = this._pq[i]
80+
const prevJ = this._pq[j]
81+
82+
this._pq[i] = prevJ
83+
this._pq[j] = prevI
84+
85+
const temp = this._qp[prevI]
86+
87+
this._qp[prevI] = this._qp[prevJ]
88+
this._qp[prevJ] = temp
89+
}
90+
91+
/**
92+
* @summary Bottom-Up reheapify (min version).
93+
* @description Algorithm to fix the heap order when
94+
* a key becomes **smaller** than its parent.
95+
* @private
96+
* @param {number} k - The index of the key to *swim*.
97+
*/
98+
_swim (k) {
99+
// While the current index 'k' is not the root (k > 1)
100+
// and while the parent node (at k / 2) is greater than
101+
// the current node (at k), exchange both nodes.
102+
while (k > 1 && this._greater(Math.floor(k / 2), k)) {
103+
this._exch(Math.floor(k / 2), k)
104+
k = Math.floor(k / 2)
105+
}
106+
}
107+
108+
/**
109+
* @summary Top-Down reheapify (min version).
110+
* @description Algorithm to fix the heap order when
111+
* a key becomes **greater** than a child.
112+
* @private
113+
* @param {number} k - Index of the key to *sink*.
114+
*/
115+
_sink (k) {
116+
// While 'k' is still having a next child
117+
// that is in bounds with the PQ size (_n) ...
118+
while (2 * k <= this._n) {
119+
// let 'j' be the next left child of 'k' (2 * k)
120+
let j = 2 * k
121+
122+
// if the left child (j) is greater than the right child (j + 1)
123+
// then choose the right child (j++)
124+
if (j < this._n && this._greater(j, j + 1)) {
125+
j++
126+
}
127+
128+
// if parent node (at k) is NOT greater than
129+
// the child node (at j), then we have found
130+
// its final position.
131+
if (!this._greater(k, j)) {
132+
break
133+
}
134+
135+
this._exch(k, j)
136+
137+
k = j
138+
}
139+
}
140+
141+
// Public instance methods
142+
// ==================================================
143+
144+
/**
145+
* Inserts a new key to the PQ and fixes the heap order.
146+
* @param {number} i - The index for the key `k`.
147+
* @param {*} k - The key to be inserted.
148+
*/
149+
insert (i, k) {
150+
this._n++
151+
this._qp[i] = this._n
152+
this._pq[this._n] = i
153+
this._keys[i] = k
154+
this._swim(this._n)
155+
}
156+
157+
/**
158+
* Changes the associated key with index `i` to be the new key `k`.
159+
* @param {number} i - The index that will get the new key.
160+
* @param {*} k - The new key for index `i`.
161+
*/
162+
changeKey (i, k) {
163+
this._keys[i] = k
164+
this._swim(this._qp[i])
165+
this._sink(this._qp[i])
166+
}
167+
168+
/**
169+
* Returns if index `i` is associated with some key.
170+
* @param {number} i - The index to be inspected.
171+
* @returns {boolean} If the index contains an associated key.
172+
*/
173+
contains (i) {
174+
return this._qp[i] !== -1
175+
}
176+
177+
/**
178+
* Removes the key associated with index `i`.
179+
* @param {number} i - The index of the key that should be deleted.
180+
*/
181+
delete (i) {
182+
if (!this.contains(i)) return
183+
184+
const index = this._qp[i]
185+
186+
this._exch(index, this._n--)
187+
this._swim(index)
188+
this._sink(index)
189+
190+
this._pq[this._n + 1] = undefined
191+
this._keys[i] = undefined
192+
this._qp[i] = -1
193+
}
194+
195+
/**
196+
* Returns the minimum key in the PQ.
197+
* @returns {*} The minimum key in the PQ.
198+
*/
199+
minKey () {
200+
return this._keys[this.minIndex()]
201+
}
202+
203+
/**
204+
* Returns the index of the minimum key in the PQ.
205+
* @returns {number} The index of the minimum key in the PQ.
206+
* @throws {ReferenceError} If the PQ is empty.
207+
*/
208+
minIndex () {
209+
if (this.isEmpty()) {
210+
throw new ReferenceError('IndexMinPQ is empty.')
211+
}
212+
213+
return this._pq[1]
214+
}
215+
216+
/**
217+
* Removes the minimum key in the PQ and returns its index.
218+
* @returns {*} The minimum key.
219+
*/
220+
delMin () {
221+
const minIndex = this.minIndex()
222+
223+
this._exch(1, this._n--)
224+
this._sink(1)
225+
226+
const i = this._pq[this._n + 1]
227+
228+
this._pq[this._n + 1] = undefined
229+
this._keys[i] = undefined
230+
this._qp[i] = -1
231+
232+
return minIndex
233+
}
234+
235+
/**
236+
* Returns if the PQ is empty.
237+
* @returns {boolean} If the PQ is empty.
238+
*/
239+
isEmpty () {
240+
return this._n === 0
241+
}
242+
243+
/**
244+
* Returns the size of the PQ.
245+
* @returns {number} The size of the PQ (total nodes).
246+
*/
247+
size () {
248+
return this._n
249+
}
250+
251+
/**
252+
* Returns the key associated with index `i` in `_pq`.
253+
* @param {number} i - The index of the key.
254+
* @returns {*} The key located at index `i`.
255+
*/
256+
keyOf (i) {
257+
return this._keys[i]
258+
}
259+
}
260+
261+
module.exports = IndexMinPQ

0 commit comments

Comments
 (0)