排序-top-k问题

例题: 最小K个数
网址: https://leetcode-cn.com/problems/smallest-k-lcci/
前沿: 寻找一组任意排序数的最小数问题,一般处理办法有下面三种。但是最近做了好几题这种了,推荐采用快排的思路,堆排序需要不断去存数据和取数据,尤其是存储的数据是非基本数据类型很麻烦,推荐快排的方法,快排相对堆排序既节约时间,也能够加速。

  • 全排列,但是时间复杂度较高
  • 堆排序,对数据采用堆存储k个数,控制最大堆或者最小堆,那么最后堆顶元素就是寻找元素
  • 快速排序思想,
    随便找个数,进行一轮快排
    快排结束,进行数据划分,假设这个数属于第t个
    t==k,那么返回前t个;
    t > k,那么问题规模缩小为在前面t-1个数找k个;
    t < k,说明已经确定前t个数属于前k个,但是第t+1到k这些数还没确定,那么问题规模缩小为从t+1到右边界找k-t个数

方法1:直接调用sort排序函数即可实现,话说C++第层的sort排序函数具体是什么排序算法呢?这一篇还可以:
https://blog.csdn.net/qq_35440678/article/details/80147601

方法2:堆排序

class Solution {
public:
    vector<int> smallestK(vector<int>& arr, int k) {
        //使用最大堆是k*logn,否则是k*n
        priority_queue<int> p;
        vector<int> res;
        if(k==0){
            return res;
        }
        for(int i=0;i<k;++i)
            p.push(arr[i]);
        
        for(int i=k;i<arr.size();++i){
            if(p.top()>arr[i]){
                p.pop();
                p.push(arr[i]);
            }
        }
        while(!p.empty()){
            res.push_back(p.top());
            p.pop();
        }
        return res;
    }
};

注意点:对于堆排序解决这个问题,需要先将k个数据存储在堆中,然后在进行比较,注意理解逻辑。

方法3:快速排序思想

class Solution {
public:
    vector<int> smallestK(vector<int>& arr, int k) {
        vector<int> ans;
        quickSort(arr, ans, k, 0, arr.size() - 1);
        return ans;
}

    void quickSort(vector<int>& arr, vector<int>& res, int k, int left, int right) {
        //快排的实现方式有多种,我们选择了其中的一种
        int start = left;
        int end = right;
        while (left < right) {
            while (left < right && arr[right] >= arr[start]) {
                right--;
            }
            while (left < right && arr[left] <= arr[start]) {
                left++;
            }
            swap(arr, left, right);
        }
        swap(arr, left, start);
        //注意这里,start是数组中元素的下标。在start之前的元素都是比start指向的元素小,
        //后面的都是比他大。如果k==start,正好start之前的k个元素是我们要找的,也就是
        //数组中最小的k个,如果k>start,说明前k个元素不够,我们还要往后再找找。如果
        //k<start,说明前k个足够了,我们只需要在start之前找k个即可。
        if (left > k) {
            quickSort(arr, res, k, start, left - 1);
        } else if (left < k) {
            quickSort(arr, res, k, left + 1, end);
        } else {
            //取前面的k个即可
            for (int m = 0; m < k; ++m) {
                res.push_back(arr[m]);
            }
        }
}

//交换数组中两个元素的值
    void swap(vector<int>& arr, int i, int j) {
        if (i == j)
            return;
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
};

下面给出一个方法三的快排的非递归实现版本,然后给出自己总是出错的地方,引以为戒。

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        //两种实现方法:分别是快排思路和map映射的思路
        if(input.size()<k || k==0 || input.size()==0) return vector<int>();
        int n = input.size();
        int l = 0;
        int r = n - 1;
        while(l!=k){
//             cout<<l<<" "<<r<<endl;
            int start = l;
            int end = r;
            while(l<r){
                while(l<r && input[r]>=input[start]) r--;
                while(l<r && input[l]<=input[start]) l++;
                swap(input[l], input[r]);
            }
            swap(input[l], input[start]);
            if(l>k){
                r = l-1;   //这一行包括下面一行是给下次的左右边界进行赋值,但是刚开始的时候写反了,先进行l赋值,导致r一直出错
                l = start;  //后面谨记这个,对于需要修改的部分,一定先用值,再修改。
            }else if(l<k){
                r = end;
                l = l+1;
            }
        }
        vector<int> res(k);
        for(int i=0;i<k;i++) res[i]= input[i];
        return res;
    }
};

这个是常用快速排序的模板

//快速排序
void quick_sort(int s[], int l, int r)
{
    if (l < r)
    {
        //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
                j--;  
            if(i < j) 
                s[i++] = s[j];
            
            while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
                i++;  
            if(i < j) 
                s[j--] = s[i];
        }
        s[i] = x;
        quick_sort(s, l, i - 1); // 递归调用 
        quick_sort(s, i + 1, r);
    }
}

虽然上面给出了快排的常用模板,但是我个人还是喜欢我给的方法三中的快排实现,确实精巧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值