劍指Offer:面試題30——最小的k個數(java實現)
阿新 • • 發佈:2019-01-02
問題描述:
輸入n個整數,找出其中最小的k個數
思路1:
先排序,再取前k個
時間複雜度O(nlogn)
下面給出快排序的程式碼(基於下面Partition函式的方法)
public void QuickSort(int[] arr, int start, int end){
if(start == end){
return;
}
int index = Partition(arr, start, end);
if(index > start){
QuickSort(arr, start, index - 1);
}
if(index < end){
QuickSort(arr, index + 1, end);
}
}
相應的求最小的k個數的程式碼:
static boolean InvalidInput = false;
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<Integer>();
if (input == null || input.length == 0 || k > input.length || k <= 0){
InvalidInput = true;
return result;
}
int start = 0;
int end = input.length - 1;
QuickSort(input, start, end);
for(int i = 0; i < k; i++){
result.add(input[i]);
}
return result;
}
思路2:(適合海量資料的輸入)
—維護一個大小為k的容器。
—每次從輸入整數讀取一個數,如果容器中的數字少於k個,則直接加入,否則,找出容器中的最大值,與當前數字比較,若大於最大值,則不管,若小於,則替換最大值。
—時間複雜度O(n*log*k)
關於容器的資料結構的選擇:
- 陣列:查詢最大值需要O(k)
- 最大堆:查詢最大值需要O(1),但需要O(logk)時間完成刪除及插入操作。
- 紅黑樹
思路3:基於Partition,會改變原始陣列
- 首先來看看Partition的原理與具體實現以及結果
Partition:思想是隨機選擇一個數,調整陣列使得比所選數小的數字移動到陣列的左邊,比選擇的數字大的數字大的數移到陣列的右邊。上一文中也用到了Partition的思想,但是並沒有弄懂。下面對程式碼進行了詳細的註釋。
基於上述Partition函式的解析,我們能用它來實現本文中的求最小的k個數
思想:如果基於陣列的第k個數字來調整,使得比第k個數字小的所有數字都位於陣列的左邊,比第k個數字大的數字都位於陣列的右邊。這樣調整後,位於陣列中左邊的k個數字就是最小的k個數字。
缺陷:會改變原始陣列;所得到的k個最小的數字不是有序的。
程式碼:
import java.util.ArrayList;
public class Solution {
static boolean InvalidInput = false;
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<Integer>();
if(input == null || input.length == 0 || k > input.length || k <= 0){
InvalidInput = true;
return result;
}
int start = 0;
int end = input.length - 1;
int index = Partition(input, start ,end);
while(index != k-1){
if(index > k - 1){
end = index - 1;
index = Partition(input, start ,end);
}else{
start = index + 1;
index = Partition(input, start, end);
}
}
for(int i = 0; i < k; i++){
result.add(input[i]);
}
return result;
}
public int Partition(int[] arr, int start, int end){
if(arr == null || arr.length == 0 || start < 0 || end < 0){
return -1;
}
int index = start + (int)(Math.random() * ((end - start) + 1));//隨機選擇一個作為標杆的數字
//將標杆放在陣列最後一位
int tmp = arr[index]; arr[index] = arr[end]; arr[end] = tmp;
int small = start - 1;//small用來儲存從右到左第一個小於標杆的數字的下標
for(index = start; index < end; index++){
if(arr[index] < arr[end]){//如果小於標杆
small++;//更新第一個小的
if(small != index){//如果當前遍歷的不是第一個小的
tmp = arr[index];arr[index] = arr[small];arr[small] = tmp;//將當前遍歷的數字放在第一個小的位置上
}
}
}
//由於small指示的是從右到左第一個小於標杆的,而此時標杆還放在陣列最後,因此,應該將標杆放在small後面一位。
small++;
tmp = arr[small];arr[small] = arr[end]; arr[end] = tmp;
return small;//返回位置為所選擇的標杆最後的位置
}
}