Leetcode 215 陣列中的第K個最大元素

2022-06-09 14:39:12 字數 4260 閱讀 3941

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

static

int findk(int array, int left, int right, int

k)

else

if (i > k - 1

) else

if (i < k - 1

)

return0;

}

此解法的效率值為n*lgk,由於k是常數,所以此解法效率值為n,優於排序解法

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, int

k) }

return a[0

];}

4、bfprt演算法面說了快排法能夠使得其時間複雜度到達期望為o(n),那能不能使得時間複雜度嚴格為o(n)呢?可以的,這裡就引出了bfprt演算法。

首先我們可以來看一下bfprt演算法的步驟:

分組(5個一組)

小組內排序(對於每一組的排序的時間複雜度是o(1))

把每個組的中位數拿出來組成乙個新的陣列

使用bfprt遞迴算出這個中位數組的中位數

用這個當做劃分值進行劃分

劃分成兩邊以後遞迴呼叫直到找到第k個數

原理:通過這樣選到的劃分數一定在0.3n-0.7n之間,如圖所示

每次選取到的劃分數一定都比紅色的數要大,比藍色的數要小

這樣時間複雜度的迭代公式就是:

通過推導不難算出其時間複雜度嚴格為o(n)

#include #include 

using

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...