輸入整數陣列 arr ,找出其中最小的 k 個數。例如,輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4。這道題應該是比較常見的乙個問題了,還有乙個映象問題是最大的k個數。總結之後,大概有如下幾種解題思路:示例1:
輸入:arr = [3,2,1], k = 2
輸出:[1,2] 或者 [2,1]
示例2:
輸入:arr = [0,1,2,1], k = 1
輸出:[0]
限制:0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
最容易想到的方法,直接按公升序對原陣列排序,然後輸出前k個數即可。時間複雜度依賴於所使用的排序演算法,快排為o(nlogn)。
class
solution
};
只需要前k個最小的數,而不需要對全部陣列排序,因此自然而然就可以想到建立乙個最小堆,並且取k次堆頂的元素,最終就得到了最小的前k個數。建堆的時間複雜度為o(n),插入即取出堆元素後堆化的時間複雜度為o(logn),原則上這個演算法的時間複雜度也是o(nlogn)(o(klogn)),但是當k比較小時,時間複雜度會想o(logn)靠近,所以相比於o(nlogn)的排序演算法,效率還是會高一些。
class
solution
return ans;
}void
buildheap
(vector<
int>
&arr,
int heapsize)}}
void
heapify
(vector<
int>
&arr,
int heapsize)if(
(index +1)
*2< heapsize && arr[minpos]
> arr[
(index +1)
*2])
if(minpos == index)
int temp = arr[index]
; arr[index]
= arr[minpos]
; arr[minpos]
= temp;
index = minpos;}}
};
思路與第二種方法類似,只不過是變成了維護乙個k個元素的最大堆。將剩餘的每個元素都和堆頂的元素進行比較,如果小於堆頂元素,就和堆頂元素替換,這樣最終這個堆裡的元素就是最小的k個數。這種方法相比於第二種方法,減少了一次堆化操作的消耗,但增大了堆化操作的次數,總的來說時間複雜度還是o(nlogn)(或者說是o(nlogk)),只不過當k變化時實際的消耗會有所不同。
我們還可以採用紅黑樹來實現這個容器,紅黑樹通過把節點分為紅、黑兩種顏色並根據一些規則確保樹在一定程度上是平衡的,從而保證在紅黑樹中的查詢、刪除和插入操作都只需要o(logk)時間。在stl中,set和multiset都是基於紅黑樹實現的。
這個演算法適合海量資料的輸入。因為只需在記憶體中維護最小的k個數字即可,其餘的資料可以放在輔助記憶體(如硬碟中)。
class
solution
}return ans;
}void
buildheap
(vector<
int>
&arr,
int heapsize)}}
void
heapify
(vector<
int>
&arr,
int heapsize)if(
(index +1)
*2< heapsize && arr[maxpos]
< arr[
(index +1)
*2])
if(maxpos == index)
int temp = arr[index]
; arr[index]
= arr[maxpos]
; arr[maxpos]
= temp;
index = maxpos;}}
};
今天看面經看到這個問題,面試官問除了堆之外有沒有可以達到o(n)的演算法,問的就是基於快速排序的這個操作。
(佛了本來寫了挺多的結果一斷網沒了)
先簡單複習一下快排:
兩種partition寫法:
int
partition1
(vector<
int>
&arr,
int low,
int high)
arr[low]
= arr[high]
;while
(low < high && arr[low]
<= pivot)
arr[high]
= arr[low];}
}int
partition2
(vector<
int>
&arr,
int low,
int high)
}swap
(arr[high]
, arr[
++location]);
return location;
}
快速排序主程式:
void
quicksort
(vector<
int>
&arr,
int low,
int high)
}
這道題使用快排中的partition函式,思路是,每次選擇乙個pivot元素,如果這個元素恰好是第k個,則左邊的元素就是最小的k個數,如果這個pivot元素不是第k個,則縮小範圍,在它的左邊或右邊繼續查詢。採用這種思路是有限制的。我們需要修改輸入的陣列,因為函式partition會調整陣列中數字的順序。如果要求不能修改輸入陣列,則只能使用堆的方法。
class
solution
else
}for
(int i =
0; i < k; i++
)return ans;
}int
mypartition
(vector<
int>
&arr,
int low,
int high)
}swap
(arr[high]
, arr[
++location]);
return location;}}
;
最小的K個數
問題描述 給定的n個整數,計算其中最小的k個數。最直觀的解法莫過於將n個數按公升序排列後輸出前k個。但是就效率來看,這種方法並不是最理想的。一種改進方法是借助快速排序中對陣列的劃分,以第k個元素對陣列進行劃分,使得比第k個數字小的數字都在其左邊,比其大的數字都在它的右邊。void swap int ...
最小的K個數
從 陣列中出現次數超過一半的數字 得到啟發,同樣可以基於partition函式來解決。一 o n 演算法 void getleastnumbers int input,int n,int output,int k else for int i 0 i k i output i input i 二 o...
最小的K個數
輸入n個整數,找出其中最小的k個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,如果不讓使用sort的話,自己實現乙個,或者依次選取最小的 class solution public vectorgetleastnumbers solution vectori...