剑指 Offer 40. 最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]


思路:我们的目标是找到递增排序后的第k个数。可以采用快排的思想,快排的partition操作会确定pivot在排序后的位置,将这个位置index与k进行比较,如果等于k,则返回[0, index]的数;如果index < k,则在右半边继续找;如果index > k,则在左半边找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Solution {

private static final Random random = new Random(System.currentTimeMillis());

public int[] getLeastNumbers(int[] arr, int k) {
if (k < 1) {
return new int[0];
}
int left = 0;
int right = arr.length - 1;
while (true) {
int pivotIdx = partition(arr, left, right);
if (pivotIdx == k - 1) {
return Arrays.copyOf(arr, pivotIdx + 1);
} else if (pivotIdx < k - 1) {
left = pivotIdx + 1;
} else {
right = pivotIdx - 1;
}
}
}

private int partition(int[] arr, int left, int right) {
int randomIdx = random.nextInt(right - left + 1) + left;
swap(arr, left, randomIdx);
int pivot = arr[left];
int le = left + 1;
int ge = right;
while (true) {
while (le <= ge && arr[le] < pivot) {
le++;
}
while (le <= ge && arr[ge] > pivot) {
ge--;
}
if (le >= ge) {
break;
}
swap(arr, le, ge);
le++;
ge--;
}
swap(arr, left, ge);
return ge;
}

private void swap(int[] arr, int a, int b) {
int c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
}