二分关键——边界判断(是否满足条件)

二分非常重要的关键就是判断边界是否满足所需的条件!

以下是我常用的两套模板
模板一

//假设需要二分的区间是 [a, b]
int l = a, r = b; 
while(l <= r){
	mid = l + (r - l) / 2;
	if(check()){
		r = mid - 1; 
	  //ans = mid;  保留中间值
	}else{
		l = mid + 1;
	}
}

模板二(有两种用法)

//假设需要二分的区间是 [a, b]

//找左边界
int l = a, r = b; 
while(l < r){
	mid = l + (r - l) / 2;  
	//把 [a, b] 分为 [a, mid] 和 [mid + 1, r] 两个区间
	if(check()){
		r = mid; //收缩区间为 [a, mid]
	}else{
		l = mid + 1; //收缩区间为 [mid + 1, r]
	}
}

//找右边界
int l = a, r = b; 
while(l < r){
	mid = l + (r - l + 1) / 2;
	//把 [a, b] 分为 [a, mid +1] 和 [mid, r] 两个区间
	if(check()){
		l = mid;//收缩区间为 [a, mid]
	}else{
		r = mid - 1;//收缩区间为 [mid + 1, r]
	}
}

H 指数 II 来源:力扣(LeetCode)

给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数,citations 已经按照 升序排列 。计算并返回该研究者的 h 指数。h 指数的定义:h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (n 篇论文中)总共有 h 篇论文分别被引用了至少 h 次。且其余的 n - h 篇论文每篇被引用次数 不超过 h 次。提示:如果 h 有多种可能的值,h 指数 是其中最大的那个。请你设计并实现对数时间复杂度的算法解决此问题。

输入:citations = [0,1,3,5,6]
输出:3
解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 0, 1, 3, 5, 6 次。
由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3 。

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int n = citations.size();
        int l = 0, r = n - 1;
        while(l <= r){
            int mid = l + (r - l) / 2;
            if(citations[mid] >= n - mid){ // 答案存在
                r = mid - 1; 
            }else{
                l = mid + 1;
            }
        }
        // 如果不存在答案 
        // l = mid + 1 
        //   = r + 1 = n - 1 + 1 
        // l = n 
        return n - l;
    }
};

模板一的边界符合题目要求,而接下来的模板二就是笔者想讲的边界判断是否满足条件

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int n = citations.size();
        int l = 0, r = n - 1;
        while(l < r){
            int mid = l + (r - l) / 2;
            if(citations[mid] >= n - mid){
                r = mid;
            }else{
                l = mid + 1;
            }
        }
        if(citations[l] < n - l) //这一步很重要,判断边界是否是想要的
            l ++; //	答案不存在 l = n
        return n - l;
    }
};

这只是千万二分类型的题目之一,二分的核心——边界判断掌握才是关键。

找到 K 个最接近的元素 来源:力扣(LeetCode)

给定一个 排序好 的数组 arr ,两个整数 k 和 x ,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。
整数 a 比整数 b 更接近 x 需要满足:
|a - x| < |b - x| 或者
|a - x| == |b - x| 且 a < b

解法一:二分查找

class Solution {
public:
    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
        int l = 0, r = arr.size() - k;
        while(l < r){
            int mid = l + (r - l) / 2;
            if(x - arr[mid] <= arr[mid + k] - x) r = mid;
             // arr[mid] 和 arr[(mid+k-1)+1] 比较才会更新
            else l = mid + 1;
        }
        vector<int> p;
        for(int i = 0; i < k; i ++) p.push_back(arr[i + l]);
        return p;
    }
};

有人对于if(x - arr[mid] <= arr[mid + k] - x)arr[mid + k] 有疑问,为什么是 midmid + k 比较而不是和 mid + k - 1 比较,还有 mid + k 会不会超边界
首先,arr[mid+k-1] 是相对于 mid 的第 k 个数, 必然是符合题目条件的,不需要比较,也不会更新,只有与第 k+1 个数比较才会更新
其次,这个二分是查找左边界,mid 的最大值是 arr.size() - k -1 ,加 k 后正好是最后一个元素,所以不会超边界

解法二:滑动窗口

class Solution {
public:
    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
        int l = 0, r = arr.size() - 1;
        while(r - l + 1 > k){
            if(abs(x - arr[l]) <= abs(arr[r] - x)) r --;
            else l ++;
        }
        vector<int> p;
        for(int i = l; i <= r; i ++) p.push_back(arr[i]);
        return p;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值