目錄:
1、引子
2、排序解決法
3、類快排解法
4、最小堆解法
1、引子
日常編碼中,常見遇到這樣的問題,“尋找最大的數(shù)”,此問題非常容易,可暴力直接遍歷找出,也可使用分冶策略找出最大值(詳見分冶算法)。
本文中需要尋找第k大的數(shù),筆者目前想到3個(gè)方法可解決它。
2、排序解決法
如果是一個(gè)有序數(shù)組,那么尋找第k的大數(shù)則相當(dāng)簡(jiǎn)單了,且效率為1。數(shù)組排序算法中相對(duì)較優(yōu)的算法為快速排序,效率為N*lgN,將數(shù)組從到到小排列,第k大的數(shù)則為array[k-1]。
快排的思想為,從數(shù)組中取任意一個(gè)值key,將大于key的值放在key右邊,小于key的值放在key左邊。key的左邊和右邊則都是有序的了,然后遞歸key左邊的子數(shù)組和key右邊的子數(shù)組,直到每個(gè)子數(shù)組長度為1,此時(shí),整個(gè)數(shù)組均有序了。
代碼如下
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大的數(shù),沒必要將數(shù)組中所有值都排序。
快排中的partition算法,返回key在數(shù)組中的位置,如果key的位置正好等于k-1,那么問題則得到解決,如果key的位置不等于k-1,可使用遞歸查找對(duì)應(yīng)子數(shù)組。直到key的位置等于k-1,則找對(duì)問題的解。
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是常數(shù),所以此解法效率值為N,優(yōu)于排序解法
4、最小堆解法
最小堆是一種特殊的數(shù)組結(jié)構(gòu),它實(shí)質(zhì)是一個(gè)完全二叉樹,且樹中子節(jié)點(diǎn)的值均大于父節(jié)點(diǎn)的值,詳見 堆排序及優(yōu)先隊(duì)列。
考慮到只需要找到第k大的數(shù),構(gòu)造一個(gè)大小為k的最小堆,堆中根節(jié)點(diǎn)為最小值。如果數(shù)組中最大的幾個(gè)數(shù)均在堆中,那么堆中根節(jié)點(diǎn)的值就是問題的解。
構(gòu)造最小堆
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);
}
}
最小堆已構(gòu)造完成,將數(shù)組中剩余的值與根節(jié)點(diǎn)相比,大于根節(jié)點(diǎn)的值則將根節(jié)點(diǎn)的值與之交換,同時(shí)維護(hù)最小堆的特性,遍歷結(jié)束,則根結(jié)點(diǎn)即為問題的解。
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