輸入n個整數,找出其中最小的K個數
問題:輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
分析:
同樣最直接的簡單方法就是先給這n個整數先排序,排序之後位於最前面的K個數就是最小的K個數,結合考慮到的邊界條件和錯誤處理。簡單編碼如下:
importjava.util.ArrayList;
importjava.util.Collections;
publicclass Solution {
public ArrayListGetLeastNumbers_Solution(int [] input, int k) {
if(input.length<=0||k<=0||input.length
return newArrayList();
ArrayList list=newArrayList();
ArrayList newlist=newArrayList();
for(int i=0;i
list.add(input[i]);
}
if(input.length==k)
return list;
Collections.sort(list);
for(int i=0;i<=k-1;i++){
newlist.add(list.get(i));
}
return newlist;
}
}
但是算法複雜度爲O(nlogn),試着優化,提出更快的算法,如果基於數組的第K個數字來調整,使得比第K個數字小的所有數字位於數組的左邊,比第K個數字大的所有數字位於數組的右邊。這樣調整後。位於數組左邊的K個數字就是最小的K個數字(這K個數字不一定是排序的)。基於這個思路,算法複雜度降爲了O(n),編碼如下:
importjava.util.ArrayList;
publicclass Solution {
public ArrayListGetLeastNumbers_Solution(int [] input, int k) {
if(input.length<=0||k<=0||input.length
return newArrayList();
int start=0,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);
}
}
ArrayList list=newArrayList();
for(int i=0;i
list.add(input[i]);
return list;
}
public static int Partition(int[] input,intstart,int end){
int pivot=(int)(Math.random()*(end-start)+start);
swap(input[pivot],input[end]);
int small=start-1;
for(pivot=start;pivot
if(input[pivot]
small++;
if(small!=pivot)
swap(input[pivot],input[small]);
}
}
small++;
swap(input[small],input[end]);
return small;
}
private static void swap(int x,int y){
int temp=x;
x=y;
y=temp;
}
}
但是在通過時有錯誤,不明白錯在哪裏,正確運行無錯的代碼如下:
importjava.util.ArrayList;
publicclass Solution {
public ArrayListGetLeastNumbers_Solution(int [] input, int k) {
ArrayList aList = new ArrayList();
if(input.length == 0 || k >input.length || k <= 0)
return aList;
int low = 0;
int high = input.length-1;
int index =Partition(input,k,low,high);
while(index != k-1){
if (index > k-1) {
high = index-1;
index =Partition(input,k,low,high);
}else{
low = index+1;
index =Partition(input,k,low,high);
}
}
for (int i = 0; i < k; i++)
aList.add(input[i]);
return aList;
}
int Partition(int[] input,int k,int low,inthigh){
int pivotkey = input[k-1];
swap(input,k-1,low);
while(low < high){
while(low < high &&input[high] >= pivotkey)
high--;
swap(input,low,high);
while(low < high &&input[low] <= pivotkey)
low++;
swap(input,low,high);
}
return low;
}
private void swap(int[] input, int low, inthigh) {
int temp = input[high];
input[high] = input[low];
input[low] = temp;
}
}
進一步思考,如果數據很大,面對海量數據時,能不能使得時間複雜度較低且快速完成任務?我們可以先創建一個大小爲K的數據容器來存儲最小的K個數字,接下來我們每次從輸入的n個整數中讀入一個數。如果容器中已有的數字少於K個,則直接把這次讀入的整數放入到容器中;如果容器中已有K個數字了,就說明容器已滿,此時我們不能再插入新的數字而只能替換已有的數字。當容器滿了,我們要做三件事:一是在K個整數中找到最大數;二是有可能在這個容器中刪除最大數;三是有可能插入一個新的數。找到已有的K個數中的最大值,然後拿這次待插入的整數和最大值進行比較。如果待插入的值比當前已有的最大值小,則用這個數替換當前已有的最大值;如果待插入的值比當前已有的最大值還要大,那麼這個數不可能是最小的K個整數之一,我們拋棄這個整數。選擇不同的二叉樹來實現這個容器,自然想到的就是最大堆或紅黑樹。按照該方法編寫代碼如下:
importjava.util.ArrayList;
importjava.util.Iterator;
importjava.util.TreeSet;
publicclass Solution {
public static ArrayListGetLeastNumbers_Solution(int[] input, int k) {
ArrayList leastNum = newArrayList();
if (input == null || input.length <1 || k < 1 || k > input.length)
return leastNum;
TreeSet kSet = newTreeSet();
for (int i = 0; i < input.length;i++) {
if (kSet.size() < k) {
kSet.add(input[i]);
} else {
if (input[i] < kSet.last()){
kSet.remove(kSet.last());
kSet.add(input[i]);
}
}
}
Iterator it =kSet.iterator();
while (it.hasNext()) {
leastNum.add(it.next());
}
return leastNum;
}
}
比較後兩種改進的算法,如下: