寻找第K大(借用快排思想)

1.题目:牛客网 NC88 (寻找第K大)

描述
有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。
给定一个整数数组a,同时给定它的大小n和要找的K(1<K<n),请返回第k大的数(包括重复的元素,不用去重),保证答案存在。

要求时间复杂度O(n)

示例1
输入:
[1,3,5,2,2],5,3
返回值:
2 

2.解题思路
这题给了提示,可以借用快排的思想。同时要注意对事件复杂度的要求是O(n)。
对于快排不熟悉的,建议看下我的这篇博文:快速排序详细讲解和代码实现
先给大家介绍第一种思路:先对A进行快速排序(从小到大),然后返回第K大的数

class Solution {
    
    
public:
    int findKth(vector<int> a, int n, int K) {
    
    
        // write code here
        quickSort(a,0,n-1);
        return a[n-K];
    }
    void quickSort(vector<int> &A,int low,int high)
    {
    
    
        if(low<high)
        {
    
    
            int pivotpos=Partition(A,low,high);
            quickSort(A,low,pivotpos-1);
            quickSort(A,pivotpos+1,high);
        }
    }
    int Partition(vector<int> &A,int low,int high){
    
    
        int privot=A[low];//取第一个作为基准
        while(low<high)
        {
    
    
            while(low<high && A[high]>=privot)
                high--;
            A[low]=A[high];
            while(low<high && A[low]<=privot)
                low++;
            A[high]=A[low];
        }
        A[low]=privot;
        return low;
    }
};

这里自己实现了快速排序算法,在排序时,调用了快排。时间复杂度O(nlogn)
因题目要求是O(n),所以必须对代码做优化。
显而易见,上述解法没有充分利用有效信息。题目中只要求返回第k大的值,并没有要求要对整个数组排序。
在这里有优化的地方。

在快排中,我们只要一趟快排,就使得这趟快排基准privot的位置得以确定。下一趟快排,使得重新确定的基准的位置又得以确定。
因此,每一趟快排后,比如急转的位置为pivotpos,就比较pivotpos和n-K的关系(从小到大的快排):
1.pivotpos=n-K,找到该元素。此元素就是数组A中第k大的数。
2.pivotpos<n-K,往右边的划分继续寻找quickSort(A,pivotpos+1,high)。
3.pivotpos>n-K,往左边的划分继续寻找quickSort(A,low,pivotpos-1)。

代码实现:

class Solution {
    
    
public:
    int findKth(vector<int> a, int n, int K) {
    
    
        // write code here
        return quickSort(a,0,n-1,K,n);
        //return a[n-K];
    }
    int quickSort(vector<int> &A,int low,int high,int k,int n)
    {
    
    
        if(1)
        {
    
    
            int pivotpos=Partition(A,low,high);
            if(pivotpos==k) 
                return A[n-pivotpos];
            else if(pivotpos<k)
                return quickSort(A,pivotpos+1,high,k,n);
            else
                return quickSort(A,low,pivotpos-1,k,n);
        }
        //return -1;//表示有误
    }
    int Partition(vector<int> &A,int low,int high){
    
    
        int privot=A[low];//取第一个作为基准
        while(low<high)
        {
    
    
            while(low<high && A[high]>=privot)
                high--;
            A[low]=A[high];
            while(low<high && A[low]<=privot)
                low++;
            A[high]=A[low];
        }
        A[low]=privot;
        return low;
    }
};

这样改写后,时间复杂度满足了要求,但使用到了递归,测试用例只通过过了几组。可能未通过的测试用例中,A相对大,超出了递归栈的深度。有可能代码有点问题。读者觉得呢?

继续改写,不使用递归

class Solution {
    
    
public:
    int findKth(vector<int> a, int n, int K) {
    
    
        // write code here
        return quickSort(a,0,n-1,K,n);
        //return a[n-K];
    }
    int quickSort(vector<int> &A,int low,int high,int k,int n)
    {
    
    
        while(1)
        {
    
    
            int pivotpos=Partition(A,low,high);
            if(pivotpos==n-k) 
                return A[pivotpos];
            else if(pivotpos<n-k)
                //return quickSort(A,pivotpos+1,high,k,n);
                low=pivotpos+1;
            else
                //return quickSort(A,low,pivotpos-1,k,n);
                high=pivotpos-1;
        }
    }
    int Partition(vector<int> &A,int low,int high){
    
    
        int privot=A[low];//取第一个作为基准
        while(low<high)
        {
    
    
            while(low<high && A[high]>=privot)
                high--;
            A[low]=A[high];
            while(low<high && A[low]<=privot)
                low++;
            A[high]=A[low];
        }
        A[low]=privot;
        return low;
    }
};

读者有什么想法,可留言评论。

おすすめ

転載: blog.csdn.net/t18438605018/article/details/119870473