尋找單個無序陣列中第K小的數字

2021-06-23 03:53:55 字數 4534 閱讀 3204

1、排序

對陣列進行排序

,然後前k個元素就是需要查詢的元素,排序的方法可以採用快速排序,但是我們知道在快速

排序中如果已經是有序的陣列,採用快速排序的時間複雜度是o(n^2),為了解決這種問題,通常選擇隨機選擇乙個

陣列值pivot作為基準,將陣列分為s1 =< pivot和s2 > pivot,這樣就能避免快速排序中存在的問題,或者採

用隨機選擇三個元素,然後取中間值作為基準就能避免快速演算法的最差時間複雜度,這種方法的前k個數字是有序的。

2、利用快排中的pivot特性

既然是選擇前k個物件,那麼就沒必要對所有的物件進行排序,可以採用快速選擇的思想獲得前k個物件,

比如首先採用快速排序的集合劃分方法劃分集合:s1,pivot,s2,然後比較k是否小於s1的個數,如何小於,則

直接對s1進行快速排序,如果k的個數超過s1,那麼對s2進行快速排序,排序完成之後,取陣列的前k個元素就是數

組的前k個最小值。這種實現方法肯定比第一種的全快速排序要更快速。

3、將陣列轉換為最小堆的情況

根據最小堆的特性,第乙個元素肯定就是陣列中的最小值,這時候我們可以

將元素儲存起來,然後將最後乙個元素提公升到第乙個元素,重新構建最小堆,這樣進行k次的最小堆建立,就找到

了前k個最小值,這是運用了最小堆的特性,實質上是最小堆的刪除實現方法。這種演算法的好處是實現了陣列的原

地排序,並不需要額外的記憶體空間。

4、接下來的這種思想有點類似桶排序

首先給定乙個k個大小的陣列b,然後複製陣列a中的前k個數到陣列b

中,將這k個數當成陣列a的前k個最小值,對陣列b建立最大堆,這時候再次比較陣列a中的其他元素,如果其他元

素小於陣列b的最大值(堆頂),則將堆頂的值進行替換,並重新建立最大堆。這樣遍歷一次陣列就找到了前k個

最小元素。這種方法運用了額外的記憶體空間,特別當選擇的k值比較大時,這種方法有待於權衡一下。

這種方法對於海量資料來說是有較好的作用,對於海量資料不能全部存放在記憶體中,這時候建立乙個較小的

陣列空間,然後建立最大堆,從硬碟中讀取其他的資料,進而實現前k個資料的查詢。

#include#include#include#include#include#define len 500000

#define k 100

/*堆的性質*/

#define leftson(i) (2*(i)+1)

#define rightson(i) (2*((i)+1))

#define parent(i) (((i)-1)/2)

void swap(int *a, int *b)

}int partition(int *a, int left, int right)

}swap(&a[j + 1],&a[right]);

return (j + 1);

}void quicksort(int *a, int left, int right)

}int quicksort(int *a, int size)

void quickselect(int *a, int left, int right, int k)

}void quickselect(int *a, int size, int k)

/*最大堆*/

void max_heapify(int *a, int left, int right)

a[parent] = tmp;

}/*建立最大堆*/

void build_maxheap(int *a, int size)

/*最小堆的實現*/

void min_heapify(int *a, int left, int right)

a[parent] = tmp;

}/*建立最小堆*/

void build_minheap(int *a, int size)

/*採用快速排序查詢*/

void find_kmin_num_1(int *a , int size, int k)

/*採用快速選擇實現*/

void find_kmin_num_2(int *a, int size, int k)

/*採用最大堆實現*/

void find_kmin_num_3(int *a, int size, int k)

}#if 0

for(i = 0; i < k ; ++ i)

printf("%d\t",b[i]);

printf("\n");

#endif

}/*採用最小堆刪除元素的方式實現*/

void find_kmin_num_4(int *a ,int size, int k)

// printf("\n");

}int main()

// printf("\n");

_start = clock();

find_kmin_num_1(a,len,k);

times = (double)(clock() - _start)/clocks_per_sec;

printf("快速排序的查詢需要:%f\n",times);

_start = clock();

find_kmin_num_2(b,len,k);

times = (double)(clock() - _start)/clocks_per_sec;

printf("快速選擇的查詢需要:%f\n",times);

_start = clock();

find_kmin_num_3(c,len,k);

times = (double)(clock() - _start)/clocks_per_sec;

printf("最大堆的查詢需要:%f\n",times);

_start = clock();

find_kmin_num_4(d,len,k);

times = (double)(clock() - _start)/clocks_per_sec;

printf("最小堆的查詢需要:%f\n",times);

return 0;

}5、select

演算法,它能在時間複雜度為

o(n)

的情況下找出第

k大的數

第一步:把陣列分成

n/5這麼多子陣列,每個子陣列裡包含

5個數,因為會有無法整出的可能,所以最後乙個子陣列會小於5.

第二步:用

insertionsorting把這5

個數排序,然後找出中位數,也就是第3個。

第三步:把獲得的中位數又排序,找出中位數的中位數。如果中位數的個數是偶數,那麼取排好序的第

m/2個數,

m指的是中位數的個數。

第四步:然後呢,把原來的陣列分成兩個部分,一部分比那個

「中位數的中位數

」大,一部分比那個

「中位數的中位數

」小。我們可以假設左邊的數大,右邊的數小。然後我們可以得到

「中位數的中位數

」的位置i.

第五步:如果

i = k,

那麼那個

「中位數的中位數

」就是第

k大的數。如果

i < k

,不用說,第

k大的在

「中位數的中位數

」的右邊,否則就在左邊。我們一直

recursely

這麼做,那麼就一定能夠找到第

k大的值了。

其實,演算法還是比較容易懂得,關鍵的關鍵,是複雜度的分析。如果能夠知道複雜度如何求出來的,那麼,對演算法本身就了解得更清楚。

要講複雜度,首先看乙個圖。

圖中的x 就是「中位數的中位數」, 而且箭頭的方向是從大數指到小數。所以,我們可以知道,至少灰色區域的都比x大,這是整個複雜度分析的關鍵,而,其它點能否說它比x大,我們不能保證。而灰色區域裡最多有多少個數呢?因為x是中位數的中位數,所以,比x大的中位數最少有 [(n/5l)* (1/2) - 2] 個(這個值也是關鍵),這裡減2是因為要去除x本身,第二呢,還要去除乙個中位數---這個中位數所在的子陣列個數小於5.  所以,最壞最壞的情況,第k大的值不在灰色區域裡,那麼我們就要對剩下部分進行不斷的select。剩餘部分就是n -3 [(\lfloor n/5 \rfool) * (1/2) - 2] = o(7n/10) .

整個過程中,第1,2,4步所需時間為o(n), 注意第2步的複雜度不為o(n^2),第3步的複雜度為 t(n/5),第五步的複雜度為 t(7n/10)。

所以,複雜度的遞迴公式為: t(n)=  t(n/5) + t(7n/10) + o(n), 算出來以後t(n) =o(n).

無序數字中位數 如何在無序陣列中查詢第K小的值

如題 給定乙個無序陣列,如何查詢第k小的值。例子如下 在乙個無序陣列,查詢 k 3 小的數 輸入 arr 輸出 7在乙個無序陣列,查詢 k 4 小的數 輸入 arr 輸出 10幾種思路如下和複雜度分析如下 1 最簡單的思路直接使用快排,堆排或者歸併排,排序之後取陣列的k 1索引的值即可,時間複雜度為...

尋找無序陣列的第K項(分治)

尋找n個數的第k項只需要對它們進行排序,然後找到即可。但是耗時o nlongn 在書上看到了很好的方法,想著實現一下 輸入 乙個數列a,乙個整數k 輸出 數列a中第k小的數 我們假定陣列的乙個數v,現在把陣列a分成三份。a2 a3 例如任意乙個陣列a 假設v等於5 a1 2 4 1 a2 5 5 a...

快速排序 尋找無序陣列中的第k大的數

思路是利用快速排序 因為快速排序的分治思想可以將查詢的範圍縮小 快速排序的思想 low為陣列的起始點,high為陣列的尾部點。交替掃瞄 1.固定陣列的第乙個數為定點,從陣列的尾部high開始往左查詢,直到第乙個比定點小的數,和定點交換,因此當前點為空 high 2.從陣列的起始處,low找到第乙個比...