從n個無序數中找出第K大的數

 

  •  遇到題目爲從n個無序數組中找出第K大的數,最開始想到的就是冒泡排序、選擇排序等,每次找到一個最大(或最小)的,可是很明顯須要時間複雜度爲O(n*k)!具體代碼細節參考findK_2
  • 改進一點的算法有根據快速排序的思想,時間複雜度達到O(n)。想象一下,第k大,說明前面有k-1個元素是比第k個元素大,那麼利用快速排序的思想,將大於軸值(暫且固定第一個元素)的放在左邊,小於軸值的放在右邊。那麼一趟劃分下來,若是左邊的元素數目大於k,那麼則說明下一趟繼續在左邊進行劃分;相反,則一趟繼續在右邊進行劃分...直到劃分軸索引等於k。具體代碼細節參考findK_1(想想,劃分軸索引爲k的時候,根據快速排序劃分的思想,是否是前面有k-1個比它大的數,那麼是否是找到了第k大的數?)
  • 當內存中不能一次性存下不少數的時候,O(n)的算法就不能知足需求了,這時候有O(nlogk)的算法,即根據堆排序的思想,創建一個k的元素的大根堆,遍歷整個數組,不斷調整並輸出。具體代碼細節參考findK_3
/***************** n個無序數中求第K大數 *****************/ 
#include<iostream>
#include<cstdio> 
#include<algorithm> 
using namespace std;

/*冒泡排序思想,時間複雜度:O(n*k)*/
int findK_2(int a[], int k){
    bool flag = true;
    for(int i=0;i<k && flag;i++){
        flag=false;
        for(int j=8;j>i;j--){
            if(a[j]>a[j-1]){
                swap(a[j],a[j-1]);
                flag=true;
            }
        }
    }
    return a[k-1];
}

/*堆排序思想,時間複雜度:O(nlogk) */
//將a[s...m]調正爲堆,其中除s位置值都符合堆定義 
void AdjustHeap(int a[], int s, int m){
    int j, tmp=a[s];
    for(j=s*2;j<=m;j*=2){
        if(j<m && a[j]<a[j+1]){
            ++j;
        }
        if(a[j]<=tmp){
            break;
        }
        a[s]=a[j];
        s=j;
    }
    a[s]=tmp;
}

int findK_3(int a[], int k){
    int len = 9;
    //建堆 
    for(int i=(len-1)/2;i>=0;i--){
        AdjustHeap(a, i, len-1);
    }
     
    //堆排序
    for(int i=len-1;i>=len-k;i--){
        swap(a[i], a[0]);
        AdjustHeap(a, 0, i-1);
    }
    
    return a[len-k]; 
}


/*快速排序思想,時間複雜度:O(n) */
int Partition(int low, int high, int a[]){
    int p = a[low]; //哨兵 
    while(low<high){
        while(low<high && a[high]<=p){
            high--;
        }
        a[low]=a[high];
        while(low<high && a[low]>p){
            low++;
        } 
        a[high] = a[low];
    }
    a[low] = p;
    return low;
} 

int findK_1(int a[], int k, int low, int high){
    int tmp = Partition(low, high, a);
    if(tmp==k-1){
        return a[tmp];
    }else if(tmp>k-1){
        findK_1(a, k, low, tmp-1);
    }else{
        findK_1(a, k, tmp+1, high);
    }
}

int QuickSort(int a[], int low, int high){
    if(low<high){
        int tmp = Partition(low, high, a);
        QuickSort(a, low, tmp-1);
        QuickSort(a, tmp+1, high);
    }
}


int main(){
    int a[] = {15,3,2,6,1,10,13,11,8};
    int b[] = {15,3,2,6,1,10,13,11,8};
    int c[] = {15,3,2,6,1,10,13,11,8};
    int d[] = {15,3,2,6,1,10,13,11,8};
    int k;
    cout<<"輸入k: ";
    cin>>k;
    QuickSort(a, 0, 8);
    cout<<"快速排序後:"<<endl;
    
    for(int i=0;i<=8;i++){
        printf("%d%c",a[i],i!=8?' ':'\n');
    } 
    
    int big_k_1 = findK_1(b, k, 0, 8);
    printf("利用快速排序思想O(n),第%d大的數爲:%d\n", k, big_k_1);

    int big_k_2 = findK_2(c, k);
    printf("利用冒泡排序思想O(n*k),第%d大的數爲:%d\n", k, big_k_2);

    int big_k_3 = findK_3(d, k);
    printf("利用堆排序思想O(nlogk),第%d大的數爲:%d\n", k, big_k_3);
    return 0;
}

 


代碼運行以下:

輸入k: 3 快速排序後: 15 13 11 10 8 6 3 2 1 利用快速排序思想O(n),第3大的數爲:11 利用冒泡排序思想O(n*k),第3大的數爲:11 利用堆排序思想O(nlogk),第3大的數爲:11
相關文章
相關標籤/搜索