问题描述:给出一个个元素的序列,求其中的第k小元素(即序列按升序排序后的第k个元素)。
中项(中间元素):一个序列的中项为该序列的第「n/2 小元素。
快速选择
基本思路:将一个数组的第一个元素为基准元素(pivotIndex),将数组中小于基准元素的,放在左边,大于的放在右边,当基准元素的索引=k-1的时候,此时基准元素的值就是第k小元素。
如果pivotIndex>k-1 就在右边递归找 如果pivotIndex<k-1 就在左边递归找
递归出口为数组长度为1
const arr = [5, 3, 8, 4, 2];
function quickSelect(arr, k) {
// 增加边界检查
if (k < 1 || k > arr.length) {
return null;
}
return _quickSelect(arr, k);
}
function _quickSelect(arr, k) {
// 递归的终止条件
if (arr.length === 1) {
return arr[0];
}
const { result, pivotIndex } = partition(arr);
const targetIndex = k - 1; // 目标索引
if (pivotIndex === targetIndex) {
return result[pivotIndex];
} else if (pivotIndex > targetIndex) {
// 目标在左边
return _quickSelect(result.slice(0, pivotIndex), k);
} else { // pivotIndex < targetIndex
// 目标在右边,更新 k 值
const newK = k - (pivotIndex + 1);
return _quickSelect(result.slice(pivotIndex + 1), newK);
}
}
function partition(arr) {
const pivot = arr[0];
const left = [];
const right = [];
const mid = [];
for (let i = 1; i < arr.length; i++) {
const e = arr[i];
if (e < pivot) {
left.push(e);
} else if (e > pivot) {
right.push(e);
} else {
mid.push(e);
}
}
// 修正:将 pivot 自身放回数组
const result = [...left, pivot, ...mid, ...right];
const pivotIndex = left.length; // pivot 的最终索引
return { result, pivotIndex };
}
const result = quickSelect(arr, 2);
console.log(result); // 正确输出: 3