輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。html
顯然最簡答作法就是對原數組排序,取前k個就行。java
Note: 這裏能夠分狀況的:面試
/** * 排序的作法 * @param input * @param k * @return */
public static ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input == null || k <= 0 || k > input.length) {
return result;
}
Arrays.sort(input);
for(int i = 0; i < k; i++) {
result.add(input[i]);
}
return result;
}
複製代碼
咱們注意到題目並無要求輸出的最小k個數必須是有序的,因此咱們能夠利用快排中partion函數的思想來作作題。 由於partion可使得序列分爲2部分:左邊的值都小於哨兵,右邊的值都大於哨兵。因此咱們只要找處處於第k位置的哨兵便可,也就是說找到第k大的值所在的位置便可,那麼它的左邊的k-1值都小於等於第k大值。顯然,前k個值即爲咱們所求的最小k個數。在咱們的劃分過程有3種狀況:算法
/** * 基於快排的劃分函數的思想來作的。 * @param input * @param k * @return */
public static ArrayList<Integer> GetLeastNumbers_Solution2(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input == null || k <= 0 || k > input.length) {
return result;
}
findKthValue(input, 0, input.length - 1, k - 1);
for(int i = 0; i < k; i++) {
result.add(input[i]);
}
return result;
}
public static void findKthValue(int[] input, int low, int high, int k) {
if(low < high) {
int pivot = new Random().nextInt(high - low + 1) + low;
swap(input, pivot, high);
int index = low;
for(int i = low; i < high; i++) {
if(input[i] < input[high]) {
swap(input, i, index);
index++;
}
}
swap(input, index, high);
if(index > k) {
findKthValue(input, low, index - 1, k);
}else if(index < k) {
findKthValue(input, index + 1, high, k);
}
}
}
複製代碼
這是典型的Top-K問題,即從n個數中找出最小的k個數或者最大的k個數問題。
咱們一般的作法用一個容量爲k的容器來存放這k個最小的值。咱們只需遍歷一遍原數組,就能獲得最小的k個數。數組
問題轉化爲如何高效率獲得容器中的最大值。一個優雅的數據結構完美的解決此題,即堆結構,分爲大根堆或者小根堆。顯然這裏應該選擇大根堆。在大根堆中,根節點大於左子樹和右子樹中全部點,因此咱們只需訪問根節點便可獲得k容量的最大值,且數據結構能夠對插入的值進行動態調整堆結構,使得知足大根堆。關於堆的具體代碼,之後我單獨寫一個博客,這裏再也不累述了。
在Java中,沒有專門的堆數據結果,不過有基於堆結構的優先隊列,因此這裏採用優先隊列並自定義比較器,來知足大根堆的需求。數據結構
/** * Topk問題 * @param input * @param k * @return */
public static ArrayList<Integer> GetLeastNumbers_Solution3(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input == null || k <= 0 || k > input.length) {
return result;
}
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, new Comparator<Integer>() {
//由於要知足大根堆需求,因此使用自定義比較器,比較策略爲o1大於o2時,o1放o2的前面
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for(int i = 0; i < input.length; i++) {
if(i < k) {
priorityQueue.add(input[i]);
} else if(input[i] < priorityQueue.peek()) {
priorityQueue.poll();
priorityQueue.add(input[i]);
}
}
result.addAll(priorityQueue);
return result;
}
複製代碼
多結合排序算法和常見的數據結構來簡化題目。dom
文章收錄在[我的專欄(upadating)](https://blog.csdn.net/column/details/23876.html),期待與你一塊兒KO常見面試題。