/**
* 重新排列 `arr` 中的元素,使得 `[left, k]` 范围内的所有元素都是最小的。
* 第 `k` 个元素将是 `[left, right]` 范围内第 `(k - left + 1)` 小的值。
*
* 实现 Floyd-Rivest 选择算法 https://en.wikipedia.org/wiki/Floyd-Rivest_algorithm
*
* @param {Array<number>} arr 输入数组
* @param {number} k 枢轴索引
* @param {number} [left] 左索引
* @param {number} [right] 右索引
* @returns {void} 会修改输入数组
* @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 和 0.5 是原始论文中选定的任意常数,用于最小化执行时间
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;