寻找第k小的元素或者前k小的所有元素 -- O(n)

参考:https://blog.csdn.net/njust_ecjtu/article/details/27491573 

此题第一思路肯定是,排序后,直接输出,但是时间复杂 度O(nlgn);那么有没有更快的呢?答案是肯定的。

在我们将利用快排的划分法进行。其中的思路可参见算法导论120页,具体证明步骤也有。具体代码在下面:

再者此题也适合求最小的k个数,最大的k个数(只不过相当于求n-k个小的数,在转化下)。

把此题归类为线性查找算法!

  1. #include <iostream>  
  2. #include <cassert>  
  3. using namespace std;  
  4.   
  5. int partition(int *A,int s,int e);//划分  
  6. int select_k(int * A,int n,int k);  
  7. int select_k_recursive(int * A,int s,int e,int k);//选出第k个大的数  
  8.   
  9.   
  10. int main()  
  11. {  
  12.     int A[10] = {3,2,6,4,9,8,7,1,5,0};  
  13.     //下标!  
  14.     cout<<select_k(A,10,3)<<endl ;  
  15.   
  16.     return 0;  
  17. }  
  18.   
  19. int select_k(int * A,int n,int k)//选出第k个小的数  
  20. {  
  21.     assert(A&& n > 0&& k <= n);  
  22.   
  23.     return select_k_recursive(A,0,n-1,k);//只需输出前k个元素即可,即最小的k个  
  24. }  
  25. int select_k_recursive(int * A,int s,int e,int k)  
  26. {  
  27.       
  28.     if (s == e)  
  29.     {  
  30.         return A[s];  
  31.     }  
  32.       
  33.     int index = partition(A,s,e);  
  34.   
  35.     if (index == k-1)//此处为第k个转化为下标为k-1  
  36.     {  
  37.         return A[index];  
  38.     }  
  39.     else if(index > k-1)  
  40.     {  
  41.         return select_k_recursive(A,s,index-1,k);// 大于则,在左边  
  42.     }  
  43.     else  
  44.     {  
  45.         return select_k_recursive(A,index+1,e,k);//小于则,在右边  
  46.     }  
  47. }  
  48.   
  49. int partition(int *A,int s,int e)//快排中的划分  
  50. {  
  51.     assert(A&& s <= e);  
  52.   
  53.     int pivot = A[s];  
  54.   
  55.     while(s < e)  
  56.     {  
  57.         while(s < e&&A[e] > pivot)e--;  
  58.         A[s] = A[e];  
  59.         while(s < e&&A[s] < pivot)s++;  
  60.         A[e] = A[s];  
  61.     }  
  62.     A[s] = pivot;  
  63.   
  64.     return s;  
  65. }


(输出前k小的所有元素。初始版本,后期会优化,也望读者批评指正。后期我会并更新其他关联问题)

#include <iostream>  
#include <cassert>  
using namespace std;

int partition(int *A, int s, int e);//划分  
int select_k(int * A, int n, int k);
int select_k_recursive(int * A, int s, int e, int k);//选出第k个大的数  


void main()
{
    int A[10] = {3,5,1,4,2,9,6,7,8,10};
    //下标!  
    select_k(A, 10,7);

    //cout << A[0] << " " << A[1] << " " << A[2] << " " << A[3] << " " << A[4] << " " << endl;

    //return 0;
}

int select_k(int * A, int n, int k)//选出第k个小的数  
{
    assert(A&& n > 0 && k <= n);

    select_k_recursive(A, 0, n - 1, k);//只需输出前k个元素即可,即最小的k个  

    return 0;
}
int select_k_recursive(int * A, int s, int e, int k)
{

    if (s == e)
    {
        return A[s];
    }


    int index = partition(A, s, e);

    if (index == k - 1)//此处为第k个转化为下标为k-1  
    {    
        cout << "前k小的数是:";
        for (int p = 0; p <= index; ++p)    //利用了快速排序每次划分结束后返回s,A[s]左边永远小于A[s]
                                            //右边永远大于A[s]
        {
            cout << A[p] << " ";
        }
        cout << endl;
        //return A[index];
        return 0;
    }
    else if (index > k - 1)
    {
        return select_k_recursive(A, s, index - 1, k);// 大于则,在左边  
    }
    else
    {
        return select_k_recursive(A, index + 1, e, k);//小于则,在右边  
    }
}

int partition(int *A, int s, int e)//快排中的划分 s左边数都比A[s]大,右边的都比A[s]小
{
    assert(A&& s <= e);

    int pivot = A[s];

    while (s < e)
    {
        while (s < e&&A[e] > pivot)e--;
        A[s] = A[e];
        while (s < e&&A[s] < pivot)s++;
        A[e] = A[s];
    }
    A[s] = pivot;

    return s;

}


注意这个输出,数组顺序已经被打乱,我会更新不改变输入数组的算法实现


转载需要私信博主征得同意,并指定出处:https://blog.csdn.net/qq_34793133/article/details/80607743

未经博主允许不得转载

猜你喜欢

转载自blog.csdn.net/qq_34793133/article/details/80607743