第K小/大问题【二分法】

  • 对于第K小、第K大的问题,通常使用的解法是优先队列,优先队列的求解方法可以查看我的文章:优先队列解决多个升序列表的第K小但是对于优先队列枚举会超时的情况,我们一般采用二分法来求解。
  • 我们考虑题目如果要求:求出数组/二维数组中的第k小的数/数对,我们可以考虑另一个相同的解法:对于数组/二维数组中的数/数组 x,他是第几小的数字?
  • 上面的描述可以看出是很明显的二分的问法,可以防止对数组的全部遍历,对于二分查找有疑惑的可以查看我的文章:二分法题解合集以及模板【第一部分】

目录

  • 乘法表中第k小的数
  • 找出第k小的距离对

乘法表中第k小的数

第K小/大问题【二分法】_第1张图片

  • 根据上面的分析,我们把此题等价成对于乘法表中的数字x,他是乘法表中的第几小的数字
  • 对于乘法表中的第 i i i行,其数字均为 i i i的倍数,这一行一共有 n n n个数字,因此不超过 x x x的数字有 m i n ( min( min( x a \frac xa ax⌋, n ) n) n),对每一行求和,即可得到矩阵中小于 x x x的个数。

代码如下:

bool check(int m,int n,int k,int mid){
    int cnt=0;
    for(int i=1;i<=m;i++){
        cnt+=min(mid/i,n); //计算一共有多小小于mid的数字
    }
    return cnt>=k; //如果大于等于k,有边界过大,right右移
}
int findKthNumber(int m, int n, int k) {
    int left=1;
    int right=m*n;
    while(left<right){ //二分查找
        int mid=(left+right)>>1;
        if(check(m,n,k,mid)) right=mid;
        else left=mid+1;
    }
    return left;
}

找出第k小的距离对

第K小/大问题【二分法】_第2张图片

  • 根据分析,我们可以把题目等价成:对于某个距离x,他是数组中的第几小距离,很明显可以用二分求解距离,左端点left=0,最小为0,右端点right=最大值-最小值。
  • 考虑对于某个距离值x,如何求出数组中有多少个比他小的距离值?可以对数组进行排序,再采用双指针的思想,对于右指针r的每一个位置,求解左指针l能指向的最左的位置,由于数组是排好序的,因此对于上一个满足条件的[l,r],需要判断[l,r+1]是否满足小于距离x,如果不满足,右移左端点直到满足条件为止

代码如下:

bool check(vector<int>& nums,int k,int mid){
    int l=0;
    int r=1;
    int cnt=0;
    for(r=1;r<nums.size();r++){ //双指针对排序数组进行查找
        while(nums[r]-nums[l]>mid) l++; //距离大于x了,需要增大l使距离减小
        cnt+=r-l; //[l,r]中共有数对r-l
    }
    return cnt>=k; //大于k说明距离太大,需要减小right
}
int smallestDistancePair(vector<int>& nums, int k) {
    sort(nums.begin(),nums.end());
    int left=0;
    int right=(*max_element(nums.begin(),nums.end())-*min_element(nums.begin(),nums.end())); //right为最大值和最小值之差
    while(left<right){ //二分查找
        int mid=(left+right)>>1;
        if(check(nums,k,mid)) right=mid;
        else left=mid+1;
    }
    return left;
}

你可能感兴趣的:(算法,leetcode,数据结构,二分,优先队列)