题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
题解一
最直观的解法就是对数组进行排序,然后输出前k个数。
时间复杂度为O(nlogn),空间复杂度为O(1)。
public ArrayList GetLeastNumbers_Solution(int[] input, int k) {
ArrayList res = new ArrayList<>();
if (k > input.length)
return res;
Arrays.sort(input);
for (int i = 0; i < k; i++)
res.add(input[i]);
return res;
}
题解二
和上一题类似,也可以利用快排的思想。当pivot的数组下标为k时,pivot左边的k个数字都小于pivot,pivot右边的n-k个数字都大于或等于pivot。若找到这与的pivot,直接输出数组的前k个元素即可。
时间复杂度为O(n),空间复杂度为O(1)。
public ArrayList GetLeastNumbers_Solution2(int[] input, int k) {
ArrayList res = new ArrayList<>();
if (k > input.length || input.length == 0)
return res;
int pivotPos = Partition(input, 0, input.length-1);
while (k < input.length && pivotPos != k) {
if (pivotPos > k)
pivotPos = Partition(input, 0, pivotPos-1);
else pivotPos = Partition(input, pivotPos+1, input.length-1);
}
for (int i = 0; i < k; i++)
res.add(input[i]);
return res;
}
private int Partition(int[] array, int left, int right) {
int pivot = array[left];
while (left < right) {
while (left < right && array[right] >= pivot) right--;
array[left] = array[right];
while (left < right && array[left] <= pivot) left++;
array[right] = array[left];
}
array[left] = pivot;
return left;
}
题解三
利用最大堆的思想,维护一个大小为k的最大堆,用于存储这最小的k个数。
遍历input,与最大堆中的最大值进行比较。当输入的数字比这k个数中的最大值还小时,则用输入的数字替换这k个数中的最大值。
尽管这种算法的时间复杂度为O(nlogn),比题解二慢一些,但它有两个优点:
- 没有修改输入数组的顺序。(所有操作都是在最大堆中完成的)
- 适合海量输入数据。(尤其是当输入数组很大而k很小时,这时候只需要维护一个大小为k的最大堆即可)
时间复杂度为O(nlogn),空间复杂度为O(1)。
public ArrayList GetLeastNumbers_Solution3(int[] input, int k) {
ArrayList res = new ArrayList<>();
if (k <= 0 || k > input.length)
return res;
// 构建最大堆
PriorityQueue maxHeap = new PriorityQueue<>(k, new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
// 遍历input, 并与最大堆中的最大值进行比较
for (int num : input) {
if (maxHeap.size() < k)
maxHeap.offer(num);
else if (!maxHeap.isEmpty() && maxHeap.peek() > num) {
maxHeap.poll();
maxHeap.offer(num);
}
}
res.addAll(maxHeap);
return res;
}