快速選擇算法(找到第k個數字)


  在一個給定的亂序的序列中找到第k個數字,可能會想到先排序,然后輸出第k個數。這種方法簡單粗暴,時間復雜度為O(nlogn)。

  還有一種方法是快速選擇,它的思想和快速排序很相似。就是先選擇一個數x,然后把這個序列分成左右兩邊,其中左邊的所有的數都<=x,右邊的數都>=x。然后比較左邊數字的個數leftNum與k的大小。如果k <= leftNum,說明我們要找到第k個數在調整后的序列的左邊那部分;否則就是在右邊的那部分。然后再用相同的方法遞歸那部分來找到第k個數。

  我們在這個序列中隨便找到一個數x,接下來和快速排序一樣,把這個序列調整為左邊的部分的數都<=x,右邊部分的數都>=x。

  其中指針j就為划分左右兩邊的下標位置,j的左邊(包括j這個位置)就是左部分,右邊就是右部分。

  接下來計算左邊部分元素的個數leftNum = j - left + 1,與k進行比較:

  • 如果k <= leftNum,說明我們要找的第k個數就在左邊部分,遞歸去在左邊部分找第k個數,尋找序列的下標范圍是[left, j];
  • 否則如果k > leftNum,說明我們要找的第k個數在右邊部分,這時應該是遞歸去在右邊部分找第k - leftNum個數,相應的尋找序列的下標范圍應該為[j + 1, right]。

  快速選擇算法的時間復雜度為O(n)。

  可以試想假設序列有n個元素,開始的第一次尋找次數為n,然后把序列分成左右兩部分,有相關證明兩邊的期望大小各為為n/2,我們要在其中的一部分找。然后同樣的方法又分成兩部分,為n/4。以此類推,所以有

\[n + \frac{n}{2} + \frac{n}{4} + ... = n (1 + \frac{1}{2} + \frac{1}{4} + ...) = n\lim_{m->\infty }\sum_{i = 0}^{m} \frac{1}{2^{i}} = 2n\]

  所以時間復雜度就為O(n)了。

  相應的代碼就通過一道模板題來給出吧。

 

786. 第k個數

給定一個長度為 n 的整數數列,以及一個整數 k,請用快速選擇算法求出數列從小到大排序后的第 k 個數。

輸入格式

第一行包含兩個整數 n 和 k。

第二行包含 n 個整數(所有整數均在 1∼109 范圍內),表示整數數列。

輸出格式

輸出一個整數,表示數列的第 k 小數。

數據范圍

1≤n≤100000,
1≤k≤n

輸入樣例:

5 3
2 4 1 5 3

輸出樣例:

3

   AC代碼如下:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 int solve(int *a, int left, int right, int k) {
 6     if (left == right) return a[left];      // left == right,說明只有一個數字,這個數字就是我們要找到的第k個數
 7     int x = a[left + right >> 1], i = left - 1, j = right + 1;
 8     while (i < j) {
 9         while (a[++i] < x);
10         while (a[--j] > x);
11         if (i < j) swap(a[i], a[j]);
12     }
13     
14     int leftNum = j - left + 1;
15     if (k <= leftNum) return solve(a, left, j, k);
16     else return solve(a, j + 1, right, k - leftNum);
17 }
18 
19 int main() {
20     int n, k;
21     scanf("%d %d", &n, &k);
22     int a[n];
23     for (int i = 0; i < n; i++) {
24         scanf("%d", a + i);
25     }
26     printf("%d", solve(a, 0, n - 1, k));
27     
28     return 0;
29 }

 

參考資料

  AcWing 786. 第k個數:https://www.acwing.com/video/228/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM