面試題40:最小的k個數
題目要求:
找出n個整數中最小的k個數。例如輸入4,5,1,6,2,7,3,8,則最小的4個數字是1,2,3,4。
解題思路:
經典的topK問題,網上有很多種思路,在此僅介紹我能想到的幾種:
解法 | 介紹 | 時間 | 空間 | 是否修改原數組 |
---|---|---|---|---|
1 | 排序后,前k個即為所求 | o(nlogn) | o(1) | 是 |
2 | 執行k次直接選擇排序 | o(n*k) | o(1) | 是 |
3 | 使用快排的分區函數求出第k小的元素 | o(n) | o(1) | 是 |
4 | 維護一個長度為k的升序數組,用二分法更新元素 | o(nlogk) | o(k) | 否 |
5 | 創建并維護一個長度為k的最大堆 | o(nlogk) | o(k) | 否 |
package chapter5;
/**
* Created with IntelliJ IDEA.
* Author: ryder
* Date : 2017/7/31
* Time : 18:14
* Description: 最小的k個數
**/
public class P209_KLeastNumbers {
//選擇排序,時間復雜度o(N*k),適合k較小的情況
public static int getLeastNumbers1(int[] data,int k){
if(data==null||data.length==0||k>data.length)
return 0;
for(int i=0;i<k;i++){
int minIndex = i;
for(int j=i+1;j<data.length;j++){
if(data[j]<data[minIndex])
minIndex = j;
}
if(minIndex!=i){
int temp = data[minIndex];
data[minIndex] = data[i];
data[i] = temp;
}
}
//第k小,也就是排序后下標為k-1的元素。
return data[k-1];
}
//使用分區函數解決,時間復雜度o(n)(不確定),會修改原數組
public static int getLeastNumbers2(int[] data,int k){
if(data==null || data.length==0 || k>data.length)
return 0;
int left=0,right=data.length-1;
int index = partition(data,left,right);
while(index!=k-1){
if(index<k-1)
index = partition(data,index+1,right);
else
index = partition(data,left,index-1);
}
return data[k-1];
}
public static int partition(int[] data,int left,int right){
int pivot = data[left];
while(left<right){
while (left<right && data[right]>=pivot)
right--;
if(left<right)
data[left] = data[right];
while (left<right && data[left]<pivot)
left++;
if(left<right)
data[right] = data[left];
}
data[left] = pivot;
return left;
}
//使用最大堆解決,不會修改原數組,適合處理海量數據
//k個元素的最大堆調整時間復雜度為o(logk),所以總的時間復雜度為o(nlogk)
public static int getLeastNumbers3(int[] data,int k){
if(data==null || data.length==0 || k>data.length)
return 0;
//最大堆,0號元素不用,因此長度需k+1
int[] heap = new int[k+1];
int i = 0;
while (i<k){
heap[i+1] = data[i];
i++;
}
//初始化最大堆
buildMaxHeap(heap);
//調整最大堆
while (i<data.length){
if(data[i]<heap[k]) {
heap[1] = data[i];
adjustMaxHeap(heap, 1);
}
i++;
}
//長度為k的最大堆中下標為1的元素就是data數組中第k小的數據值
return heap[1];
}
//0號元素不用,創建一個長度為k+1的堆
public static void buildMaxHeap(int[] heap){
for(int i = heap.length>>>1;i>0;i--)
adjustMaxHeap(heap,i);
}
//調整最大堆,i為待調整的下標
public static void adjustMaxHeap(int[] heap,int i){
int left = 2*i,right = left+1;
int max = i;
if(left<heap.length && heap[left]>heap[max])
max = left;
if(right<heap.length && heap[right]>heap[max])
max = right;
if(max!=i){
int temp = heap[i];
heap[i] = heap[max];
heap[max] = temp;
adjustMaxHeap(heap,max);
}
}
public static void main(String[] args){
int[] data1 = {6,1,3,5,4,2};
System.out.println(getLeastNumbers1(data1,5));
int[] data2 = {6,1,3,5,4,2};
System.out.println(getLeastNumbers2(data2,5));
int[] data3 = {6,1,3,5,4,2};
System.out.println(getLeastNumbers3(data3,5));
}
}
運行結果
5
5
5