目录
一,题目描述
英文描述
Design an algorithm to find the smallest K numbers in an array.
中文描述
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例与说明
来源:力扣(LeetCode)
链接:力扣
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二,解题思路
借助快速排序的partation方法,这样就成了寻找partation后返回值为k的过程。
然后就可以按照二分法的技巧,改变待排序区间范围了。
三,AC代码
Java
class Solution {
public void swap (int[] arr, int a, int b) {
int tem = arr[a];
arr[a] = arr[b];
arr[b] = tem;
}
public int partion (int[] arr, int left, int right) {
int index = (int) Math.random() * (right - left) + left;
int pivot = arr[index];
swap(arr, left, index); // 将哨兵放在排序区间的左边界
index = left; // index指向排好序的数组的右边界
// 将所有小于pivot的元素放到数组的前面,类似于插入排序
for (int i = left + 1; i <= right; i++) {
if (arr[i] < pivot) {
index++;
swap(arr, index, i);
}
}
swap(arr, left, index); // 将哨兵换回到合适的位置
return index;
}
public void quickSort(int[] arr, int left, int right, int k) {
if (k < left || k > right) return;
int index = partion(arr, left, right);
if (index < k - 1) {
quickSort(arr, index + 1, right, k);
} else if (index > k - 1) {
quickSort(arr, left, index - 1, k);
} else {
return;
}
}
public int[] smallestK(int[] arr, int k) {
int[] ans = new int[k];
if (k == 0 || arr == null) return ans;
quickSort(arr, 0, arr.length - 1, k);
// System.arraycopy(arr, 0, ans, 0, k);
for (int i = 0; i < k; i++) {
ans[i] = arr[i];
}
return ans;
}
}
四,解题过程
第一博
我一眼就看出这一题需要用快速排序(不要求前k个数据的顺序)。快排可以很好的解决这个问题,只不过选择k这个过程可能比较魔幻(如果第一次随机就找到了第k小的数,那么当场算法结束,反之如果运气不太好,那就emmm)
有点类似于二分法的感觉。
class Solution {
public void swap (int[] arr, int a, int b) {
int tem = arr[a];
arr[a] = arr[b];
arr[b] = tem;
}
public int partion (int[] arr, int left, int right) {
int index = (int) Math.random() * (right - left) + left;
int pivot = arr[index];
swap(arr, left++, index);
index = left - 1;
while (left < right) {
while (left < right && arr[left] < pivot) left++;
while (left < right && arr[right] > pivot) right--;
if (left < right) swap(arr, left, right);
}
if (arr[left] > pivot) left--;
swap(arr, left, index);
return left;
}
public void quickSort(int[] arr, int left, int right, int k) {
if (k < left || k > right) return;
int index = partion(arr, left, right);
if (index < k - 1) {
quickSort(arr, index + 1, right, k);
} else if (index > k - 1) {
quickSort(arr, left, index - 1, k);
} else {
return;
}
}
public int[] smallestK(int[] arr, int k) {
int[] ans = new int[k];
if (k == 0 || arr == null) return ans;
quickSort(arr, 0, arr.length - 1, k);
for (int i = 0; i < k; i++) {
ans[i] = arr[i];
}
return ans;
}
}
第二搏
超时?为啥会超时???
经过和大佬的代码仔细对比后,终于发现了问题。。。
真相只有一个:partation算法的实现
之前partation算法的实现(分别从左/右边界开始,定位大于/小于pivot的元素位置,然后交换位置)
优化后的partation算法实现(顺序遍历排序区间的元素,类似于插入排序的方法,维护一个已排序区间,将小于pivot的元素插入到这个区间中)
简单分析一下:
- 从整体上来看,两种解法都是遍历一次数组,将元素进行划分。
- 从细节上来看,优化后的方法内部只有一个简单的if分支判断,而之前的方法中额外包含了两个while循环(虽然看上去可以快速定位需要交换的元素,一次将两个元素放到正确的位置,但实际上还是对数组进行了一次遍历)
将这两个while放到大数据的范围上来看,它的开销确实不容小觑。。。