剑指 Offer -40 最小的k个数c++

------------------------------2021/2/2------------------------------------------
二刷
两种办法都需要掌握;
两种方法的优劣性比较
看起来分治法的快速选择算法的时间、空间复杂度都优于使用堆的方法,但是要注意到快速选择算法的几点局限性:

一、算法需要修改原数组,如果原数组不能修改的话,还需要拷贝一份数组,空间复杂度就上去了。
二、 算法需要保存所有的数据。如果把数据看成输入流的话,使用堆的方法是来一个处理一个,不需要保存数据,只需要保存 k 个元素的最大堆。而快速选择的方法需要先保存下来所有的数据,再运行算法。当数据量非常大的时候,甚至内存都放不下的时候,就麻烦了。所以当数据量大的时候还是用基于堆的方法比较好。

快排的partition函数有很多细节,一定要记住模板
++l和–r对应初始值i / j + 1 、 l >= r结束 、最后交换i和r


    int partition(vector<int> &arr, int i, int j) {
        int l = i, r = j + 1;
        int mark = arr[l];
        while(1) {
            while(++l <= j && arr[l] < mark);
            while(--r >= i && arr[r] > mark);
            if(l >= r) break;
            swap(arr[l], arr[r]); 
        }
        swap(arr[i],arr[r]);
    }
    return r;
};

许久没有写快排,之前默写过但细节已经忘记了,借这个机会再写了一次

题目描述

在这里插入图片描述

解法一、堆排序

建立优先队列,也就是c++的大根堆,先填充k个数进去,然后对剩下元素,如果它小于优先队列队首就删除队首放入元素,直到放完剩下所有元素

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if(arr.empty() || k == 0) return {};
        vector<int> ans;
        priority_queue<int> pq;
        for(int i = 0; i < k; i++) pq.push(arr[i]);
        for(int i = k; i < arr.size(); i++) {
            if(pq.top() > arr[i]) {
                pq.pop();
                pq.push(arr[i]);
            }
        }
        while(k --) {
            ans.push_back(pq.top());
            pq.pop();
        }
        return ans;
    }
};

在这里插入图片描述
时间复杂度O(nlogn)
空间复杂度O(k)

解法二、快排

实际是快排的改进版本,
保留快排的partition函数,得到基准元素的下标m

  • m == k -1

即包括下标内有k个数,直接返回数组前k个数。

  • m < k - 1

第k小的元素大于基准元素,所以将基准元素右边的数组内的元素作为新的基准元素继续partition

  • m > k - 1

第k小的元素小于基准元素,所以将基准元素左边的数组内的元素作为新的基准元素继续partition

//快排解法
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if(arr.empty() || k == 0) return {};
        int len = arr.size() - 1;
        return FindLeast(arr, 0, len, k - 1);//第k个数下标为k-1
    }
    vector<int> FindLeast(vector<int>& arr, int l, int h, int k) {
        int m = partition(arr, l, h);
        if(m == k) {
            vector <int> ans (arr.begin(),arr.begin() + k + 1);
            return ans;
        }
        if(m < k) return FindLeast(arr, m + 1, h, k);
        return FindLeast(arr, l, m - 1, k);
    }
    int partition(vector<int> &arr, int l, int h) {
    //l作为基准元素留在原地所以i设为l,++i从i+1开始
    //j设为h+1,保证--h从h开始
        int i = l, j = h + 1;
        int pNum = arr[l];
        while(true) {
            while(++ i <= h && arr[i] < pNum);
            while(--j >= l && arr[j] > pNum);
            if(i >= j) break;
            swap(arr[i], arr[j]);
        }
        swap(arr[l], arr[j]);
        return j;
    }
};

在这里插入图片描述
时间复杂度趋近O(n)
空间复杂度O(1)
快排的partition函数加深一下记忆
要注意l和r的起始元素 避免掉坑

int partition(vector<int> &arr, int l, int h) {
    //l作为基准元素留在原地所以i设为l,++i从i+1开始
    //j设为h+1,保证--h从h开始
        int i = l, j = h + 1;
        int pNum = arr[l];
        while(true) {
            while(++ i <= h && arr[i] < pNum);
            while(--j >= l && arr[j] > pNum);
            if(i >= j) break;
            swap(arr[i], arr[j]);
        }
        swap(arr[l], arr[j]);
        return j;
    }
    ```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值