bfprt演算法求最小的k個數

來源:互聯網
上載者:User

bfprt演算法是一種經典的線性時間內求最小的第k個數或者最小的k個數的演算法。

比如我們要知道銷量是前幾名商品或者是瀏覽量最多的前幾個網站,我們不需要排序,利用bfprt演算法就可以完成。

bfprt演算法又稱快速選擇演算法,其思想是利用快速排序的劃分來確定位置。此演算法經典而優雅,值得我們學習。 步驟

        1.將輸入的元素劃分為5個元素一組,至多有一組不足5個元素。

        2.利用插入排序尋找每組5個元素的中位元,然後再遞迴找所有中位元的中位元。

        3.利用快排的劃分方法,以第二步所找的中位元為哨兵劃分。返回最後劃分的位置i,

          如果i+1 = k,則返回i,

          如果i+1<k,說明找到的數小了,在i+1到數組末遞迴尋找k-i-1小的數,

          同理,i+1>k,在數組開始到i-1的部分遞迴尋找第i小的元素。 c語言代碼

#include <stdio.h>#define N 8 void swap(int* a, int* b) {    int t = *a;    *a = *b;    *b = t;}//插入排序void InsertSort(int a[], int l, int r){    for(int i = l + 1; i <= r; i++)    {        if(a[i - 1] > a[i])        {            int t = a[i];            int j = i;            while(j > l && a[j - 1] > t)            {                a[j] = a[j - 1];                j--;            }            a[j] = t;        }    }} //尋找中位元的中位元,利用插入排序對每5個元素排序,再把每組的中位元依次交換到數組的前面,//最後再遞迴對數組前面的中位元排序,找中位元int FindMid(int a[], int l, int r){    if(l == r) return a[l];    int i = 0;    int n = 0;    for(i = l; i < r - 5; i += 5)    {        InsertSort(a, i, i + 4);        n = i - l;        swap(a+l + n / 5, a+i + 2);    }      //處理剩餘元素    int num = r - i + 1;//剩餘元素的個數    if(num > 0)    {        InsertSort(a, i, i + num - 1);        n = i - l;        swap(a+l + n / 5, a+i + num / 2);    }    n /= 5;//有幾組5個的數       if(n == l) return a[l];    return FindMid(a, l, l + n);} //尋找中位元的所在位置int FindId(int a[], int l, int r, int num){    for(int i = l; i <= r; i++)        if(a[i] == num)            return i;    return -1;} //進行劃分過程int Partion(int a[], int l, int r, int p){    swap(a+p, a+l);    int i = l;    int j = r;    int pivot = a[l];    while(i < j)    {        while(a[j] >= pivot && i < j)            j--;        a[i] = a[j];        while(a[i] <= pivot && i < j)            i++;        a[j] = a[i];    }      a[i] = pivot;    return i;} int BFPTR(int a[], int l, int r, int k){if (k<1 || k>r-l+1)return -1;    int num = FindMid(a, l, r);    //尋找中位元的中位元        int p =  FindId(a, l, r, num); //找到中位元的中位元對應的id       int i = Partion(a, l, r, p);     int m = i - l + 1;    if(m == k) return a[i];    if(m > k)  return BFPTR(a, l, i - 1, k);    return BFPTR(a, i + 1, r, k - m);} int main(){    int i, k;    int a[N] = {72, 6, 57, 88, 60, 42, 83, 73};     scanf("%d", &k);    printf("The %d th number is : %d\n", k, BFPTR(a, 0, N - 1, k));    for(i = 0; i < N; i++)        printf("%d ", a[i]);    getchar();    getchar();    return 0;}



時間複雜度證明

       第一步劃分,複雜度為O(n),

       第二步求中位元,對大小為O(1)的數組進行O(n)次插入排序的複雜度為O(n),遞迴求取中位元的複雜度T(n/5).

       第三步,得到的中位元x作為哨兵進行劃分,在n/5個中位元中,哨兵x大於其中1/2*n/5=n/10的中位元,而每個中位元在其本來的5個數的小組中又大於或等於其中的3個數,所以哨兵x至少大於所有數中的n/10*3=3/10*n個。同理,哨兵x至少小於所有數中的3/10*n個。即劃分之後,任意一邊的長度至少為3/10*n,在最壞情況下,每次選擇都選到了7/10*n的那一部分,則遞迴的複雜度為T(7/10*n)。劃分的時間複雜度為O(n).
  我們假設在每5個數求中位元和劃分的函數中,進行若干個次線性掃描,其時間複雜度為c*n,其中c為常數。
  其總的時間複雜度滿足 T(n)<=T(n/5)+T(7/10*n)+c*n。

  我們假設T(n)=x*n,其中x不一定是常數(比如x可以為n的倍數,則對應的T(n)=O(n^2))。則有 x*n <= x*n/5 + x*7/10*n + c*n
  得到 x<=10*c
  於是可以知道x與n無關,T(n)<=10*c*n,為線性時間複雜度演算法。

”為什麼要分成5個元素一組。“的證明和時間複雜度的證明一樣,假設分成k組就行了。下面看演算法導論的證明

k必須要大於4才能保證時間是線性。

由於劃分的性質,當我們找到了第k小的數時,它前面的數小於等於它,後面的數都大於等於它。

這樣我們就找到了最小的k個數。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.