-
Notifications
You must be signed in to change notification settings - Fork 224
/
quickselect.js
70 lines (62 loc) · 2.06 KB
/
quickselect.js
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
/**
* Rearrange items in `arr` so that all items in `[left, k]` range are the smallest.
* The `k`-th element will have the `(k - left + 1)`-th smallest value in `[left, right]`.
*
* Implements Floyd-Rivest selection algorithm https://en.wikipedia.org/wiki/Floyd-Rivest_algorithm
*
* @param {Array<number>} arr input array
* @param {number} k pivot index
* @param {number} [left] left index
* @param {number} [right] right index
* @returns {void} mutates input array
* @example
* var arr = [65, 28, 59, 33, 21, 56, 22, 95, 50, 12, 90, 53, 28, 77, 39];
* quickselect(arr, 8);
* // = [39, 28, 28, 33, 21, 12, 22, 50, 53, 56, 59, 65, 90, 77, 95]
*/
function quickselect(arr, k, left, right) {
left = left || 0;
right = right || arr.length - 1;
while (right > left) {
// 600 and 0.5 are arbitrary constants chosen in the original paper to minimize execution time
if (right - left > 600) {
const n = right - left + 1;
const m = k - left + 1;
const z = Math.log(n);
const s = 0.5 * Math.exp((2 * z) / 3);
let sd = 0.5 * Math.sqrt((z * s * (n - s)) / n);
if (m - n / 2 < 0) sd *= -1;
const newLeft = Math.max(left, Math.floor(k - (m * s) / n + sd));
const newRight = Math.min(
right,
Math.floor(k + ((n - m) * s) / n + sd)
);
quickselect(arr, k, newLeft, newRight);
}
const t = arr[k];
let i = left;
let j = right;
swap(arr, left, k);
if (arr[right] > t) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (arr[i] < t) i++;
while (arr[j] > t) j--;
}
if (arr[left] === t) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
export default quickselect;