1、排序解決法
如果是乙個有序陣列,那麼尋找第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)
if (j >i)
while (array[i] > k && j >i)
if (j >i)
}array[i] =k;
returni;}
public
static
void quicksort(int array, int left, int
right)
int i =partition(array, left, right);
quicksort(array, left, i - 1
); quicksort(array, i + 1
, right);
}
快排的partition演算法有兩種寫法,具體可檢視快速排序及主定理。此解法效率為n*lgn
2、類快排解法
由於只要求找出第k大的數,沒必要將陣列中所有值都排序。
快排中的partition演算法,返回key在陣列中的位置,如果key的位置正好等於k-1,那麼問題則得到解決,如果key的位置不等於k-1,可使用遞迴查詢對應子陣列。直到key的位置等於k-1,則找對問題的解。
public此解法的效率值為n*lgk,由於k是常數,所以此解法效率值為n,優於排序解法static
int findk(int array, int left, int right, int
k)
else
if (i > k - 1
) else
if (i < k - 1
)
return0;
}
3、最小堆解法
最小堆是一種特殊的陣列結構,它實質是乙個完全二叉樹,且樹中子節點的值均大於父節點的值,詳見 堆排序及優先佇列。
考慮到只需要找到第k大的數,構造乙個大小為k的最小堆,堆中根節點為最小值。如果陣列中最大的幾個數均在堆中,那麼堆中根節點的值就是問題的解。
構造最小堆
void minheapfix(int a, int i, int最小堆已構造完成,將陣列中剩餘的值與根節點相比,大於根節點的值則將根節點的值與之交換,同時維護最小堆的特性,遍歷結束,則根結點即為問題的解。n)
if (a[j] >temp)
break
; a[i] =a[j];
i =j;
j = i * 2 + 1
; }
a[i] =temp;
}void buildheap(int a, int
n)}
int findk1(int a, int l, int r, int4、bfprt演算法面說了快排法能夠使得其時間複雜度到達期望為o(n),那能不能使得時間複雜度嚴格為o(n)呢?可以的,這裡就引出了bfprt演算法。k) }
return a[0
];}
首先我們可以來看一下bfprt演算法的步驟:
分組(5個一組)
小組內排序(對於每一組的排序的時間複雜度是o(1))
把每個組的中位數拿出來組成乙個新的陣列
使用bfprt遞迴算出這個中位數組的中位數
用這個當做劃分值進行劃分
劃分成兩邊以後遞迴呼叫直到找到第k個數
原理:通過這樣選到的劃分數一定在0.3n-0.7n之間,如圖所示
每次選取到的劃分數一定都比紅色的數要大,比藍色的數要小
這樣時間複雜度的迭代公式就是:
通過推導不難算出其時間複雜度嚴格為o(n)
#include #includeusing
namespace
std;
int insertsort(int array, int left, int
right);
int getpivotindex(int array, int left, int
right);
int partition(int array, int left, int right, int
pivot_index);
int bfprt(int array, int left, int right, int
k);int
main()
; cout
<< "
原陣列:";
for (int i = 0; i < 20; i++)
cout
<< array[i] << "";
cout
<
//因為是以 k 為劃分,所以還可以求出第 k 小值
cout << "第 "
<< k << "
小值為:
"<< array[bfprt(array, 0, 19, k)] <
cout
<< "
變換後的陣列:";
for (int i = 0; i < 20; i++)
cout
<< array[i] << "";
cout
<
return0;
}/** * 對陣列 array[left, right] 進行插入排序,並返回 [left, right]
* 的中位數。 */
int insertsort(int array, int left, int
right)
array[j + 1] =temp;
}return ((right - left) >> 1) +left;}/*
* * 陣列 array[left, right] 每五個元素作為一組,並計算每組的中位數,
* 最後返回這些中位數的中位數下標(即主元下標)。
* * @attention 末尾返回語句最後乙個引數多加乙個 1 的作用其實就是向上取整的意思,
* 這樣可以始終保持 k 大於 0。 */
int getpivotindex(int array, int left, int
right)
//利用 bfprt 得到這些中位數的中位數下標(即主元下標)
return bfprt(array, left, sub_right, ((sub_right - left + 1) >> 1) + 1);}
/*** 利用主元下標 pivot_index 進行對陣列 array[left, right] 劃分,並返回
* 劃分後的分界線下標。 */
int partition(int array, int left, int right, int
pivot_index)
}swap(array[partition_index], array[right]);
//最後把主元換回來
return
partition_index;}/*
* * 返回陣列 array[left, right] 的第 k 小數的下標 */
int bfprt(int array, int left, int right, int
k)
原陣列:11910**:113815
016217
51436
1812719
4第 8 小值為:7
變換後的陣列:40
1325
6789
1012
1314
1715
1611
1819
LeetCode 215 陣列中的第K個最大元素
在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。示例 1 輸入 3,2,1,5,6,4 和 k 2 輸出 5 示例 2 輸入 3,2,3,1,2,4,5,5,6 和 k 4 輸出 4 說明 你可以假設 k 總是有效的,且 1...
LeetCode 215 陣列中的第K個最大元素
在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。示例 1 輸入 3,2,1,5,6,4 和 k 2 輸出 5 示例 2 輸入 3,2,3,1,2,4,5,5,6 和 k 4 輸出 4 說明 你可以假設 k 總是有效的,且 1...
Leetcode215 陣列中的第K個最大元素
在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。示例 1 輸入 3,2,1,5,6,4 和 k 2 輸出 5 示例 2 輸入 3,2,3,1,2,4,5,5,6 和 k 4 輸出 4 說明 你可以假設 k 總是有效的,且 1...