【演算法】尋找第k大的數
目錄:
1、引子
2、排序解決法
3、類快排解法
4、最小堆解法
1、引子
日常編碼中,常見遇到這樣的問題,“尋找最大的數”,此問題非常容易,可暴力直接遍歷找出,也可使用分冶策略找出最大值(詳見分冶演算法)。
本文中需要尋找第k大的數,筆者目前想到3個方法可解決它。
2、排序解決法
如果是一個有序陣列,那麼尋找第k的大數則相當簡單了,且效率為1。陣列排序演算法中相對較優的演算法為快速排序,效率為N*lgN,將陣列從到到小排列,第k大的數則為array[k-1]。
快排的思想為,從陣列中取任意一個值key,將大於key的值放在key右邊,小於key的值放在key左邊。key的左邊和右邊則都是有序的了,然後遞迴key左邊的子陣列和key右邊的子陣列,直到每個子陣列長度為1,此時,整個陣列均有序了。
程式碼如下
public static int partition(int[] array, int left, int right) { int k = array[left]; int i = left; int j = right; while (j > i) { while (array[j] < k && j > i) { j--; } if (j > i) { array[i] = array[j]; i++; } while (array[i] > k && j > i) { i++; } if (j > i) { array[j] = array[i]; j--; } } array[i] = k; return i; } public static void quickSort(int[] array, int left, int right) { if (left >= right) { return; } int i = partition(array, left, right); quickSort(array, left, i - 1); quickSort(array, i + 1, right); }
本文中快排略有差異,是按從大到小順序排列。
快排的partition演算法有兩種寫法,具體可檢視快速排序及主定理。此解法效率為N*lgN
3、類快排解法
由於只要求找出第k大的數,沒必要將陣列中所有值都排序。
快排中的partition演算法,返回key在陣列中的位置,如果key的位置正好等於k-1,那麼問題則得到解決,如果key的位置不等於k-1,可使用遞迴查詢對應子陣列。直到key的位置等於k-1,則找對問題的解。
public static int findK(int[] array, int left, int right, int k) { int i = partition(array, left, right); if (i == k - 1) { return array[k - 1]; } else if (i > k - 1) { return findK(array, left, i - 1, k); } else if (i < k - 1) { return findK(array, i + 1, right, k); } return 0; }
此解法的效率值為N*lgK,由於K是常數,所以此解法效率值為N,優於排序解法
4、最小堆解法
最小堆是一種特殊的陣列結構,它實質是一個完全二叉樹,且樹中子節點的值均大於父節點的值,詳見 堆排序及優先佇列。
考慮到只需要找到第k大的數,構造一個大小為k的最小堆,堆中根節點為最小值。如果陣列中最大的幾個數均在堆中,那麼堆中根節點的值就是問題的解。
構造最小堆
public static void maxHeapify(int[] array, int size, int i) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int small = i;
if (left < size) {
if (array[small] > array[left]) {
small = left;
}
}
if (right < size) {
if (array[small] > array[right]) {
small = right;
}
}
if (small != i) {
int temp = array[small];
array[small] = array[i];
array[i] = temp;
maxHeapify(array, size, small);
}
}
public static void buildHeap(int[] array, int size) {
for (int i = size - 1; i >= 0; i--) {
maxHeapify(array, size, i);
}
}
最小堆已構造完成,將陣列中剩餘的值與根節點相比,大於根節點的值則將根節點的值與之交換,同時維護最小堆的特性,遍歷結束,則根結點即為問題的解。
public static int findKByHeap(int[] array, int k) {
buildHeap(array, k);
for (int i = k + 1; i < array.length; i++) {
if (array[i] > array[0]) {
int temp = array[i];
array[i] = array[0];
array[0] = temp;
maxHeapify(array, k, 0);
}
}
return array[0];
}
程式碼地址:https://github.com/okunu/DataStructure ,歡迎訪問本人的github
作者:某昆
連結:https://www.jianshu.com/p/33ee33ce8699
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。