题目描述
输入整数数组 arr
,找出其中最小的 k
个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
题目解答
解法1:
利用快速排序,将数组arr进行从小到大排序,再依次输出前k个数。这种算法的复杂度是O(nlogn)
快排的解释:http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html(阮一峰)
var getLeastNumbers = function(arr, k) {
var newArr = [];
newArr = quickSort(arr);
var result = [];
// 依次输出前k个数
for(var i=0;i<k;i++){
result.push(newArr[i]);
}
return result;
};
// 快排
var quickSort = function(arr){
if(arr.length<=1){
return arr;
}
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex,1)[0];
var left = [];
var right = [];
for(var i=0;i<arr.length;i++){
if(arr[i] < pivot){
left.push(arr[i]);
}
else{
right.push(arr[i]);
}
}
return quickSort(left).concat([pivot],quickSort(right));
}
解法2:
由于题目要求输出前k个最小数,则利用前面快排思路,但并不需要对所有元素进行排序。
在快排中,通过partition(分片)可以将对应基准元素放入数组排序后的正确位置。
function partition(arr, start, end) {
const k = arr[start];
let left = start + 1,
right = end;
while (1) {
while (left <= end && arr[left] <= k) ++left;
while (right >= start + 1 && arr[right] >= k) --right;
if (left >= right) {
break;
}
[arr[left], arr[right]] = [arr[right], arr[left]];
++left;
--right;
}
[arr[right], arr[start]] = [arr[start], arr[right]];
// 最后返回排序后标准值的索引值
return right;
}
基于上述函数,设计前k个元素的求解方法
var getLeastNumbers = function(arr,start,end){
var length = arr.length;
if(k>length){
return arr;
}
var start = 0;
var end = length-1;
var index = partition(arr, start, end);
while(1){
if(k<=index+1){
return arr.slice(0,k);
}else{
index = partition(arr,index+1,end);
}
}
}
上面while循环写的不正确。当k>index+1时,再次以index+1为起始位置进行分片,但不保证小于新基准值的元素按从小到大排序。最终可能导致出错。
正确解法:一定要确定某元素排序后在索引为k(k-1)的位置才能结束,小于或大于都会出问题。这样可以真正确保在该基准元素之前有k个最小元素。若结束条件定为(index == k),则输出前k个元素里不包括基准元素;若定为(index == k-1),则输出的前k个元素里包括基准元素。
var getLeastNumbers = function(arr, k) {
const length = arr.length;
if (k >= length) return arr;
let left = 0,
right = length - 1;
let index = partition(arr, left, right);
while (index !== k) {
if (index < k) {
left = index + 1;
index = partition(arr, left, right);
} else if (index > k) {
right = index - 1;
index = partition(arr, left, right);
}
}
return arr.slice(0, k);
};