題目描述
輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
解法一:根據快速排序,基於數組的第k個數字來調整,使得k左邊的數字都小於k,k右邊的數字都大於k。
調整後,左邊的k個數字就是最小的k個數字,時間複雜度O(n)。
/**
* @author yuan
* @date 2019/2/19
* @description 基於partition,時間複雜度O(n)
*/
public class 最小的K個數 {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>(input.length);
if (k < 1 || k > input.length) {
return result;
}
int l = 0, r = input.length - 1;
int index = partition(input, l, r);
while (index != k - 1) {
if (index > k - 1) {
r = index - 1;
index = partition(input, l, r);
} else {
l = index + 1;
index = partition(input, l, r);
}
}
for (int i = 0; i < k; i++) {
result.add(input[i]);
}
return result;
}
/**
* 切片
* @param a
* @param left
* @param right
* @return
*/
private int partition(int[] a, int left, int right) {
int i = left, j = right;
int base = a[left];
while (i < j) {
while (i < j && a[j] >= base) {
--j;
}
while (i < j && a[i] <= base) {
++i;
}
swap(a, i, j);
}
swap(a, left, i);
return i;
}
private void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
解法二:用最大堆,時間複雜度O(nlgk)
先創建一個大小爲k的堆,接下來每次讀取一個整數,當堆的數字小於k,直接放入堆中。
若堆已經有k個數字,將堆中最大的值和當前整數比較,如果當前整數小於堆的最大值,則將堆中的最大值替換爲當前整數。
如果當前整數比堆的最大值大,則不可能是最小的k個整數之一,直接拋棄。
/**
* @author yuan
* @date 2019/2/19
* @description 最大堆,時間複雜度O(nlgk)
* 用最大堆保存這k個數,每次只和堆頂比,如果比堆頂(堆中最大的元素)小,刪除堆頂,新數入堆。
* 否則拋棄
*/
public class 最小的K個數 {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>(input.length);
int len = input.length;
if (k > len || k < 1) {
return result;
}
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, Comparator.reverseOrder());
for (int i = 0; i < len; i++) {
if (maxHeap.size() < k) {
maxHeap.offer(input[i]);
} else if (input[i] < maxHeap.peek()) {
// 如果當前整數比堆中最大的元素還小,用這個整數替換已有的最大值
maxHeap.poll();
maxHeap.offer(input[i]);
}
}
result.addAll(maxHeap);
return result;
}
}