简单却总是需要debug的二分(C/C++)

2021/4/19更新
今天在小组讲了二分,突然感觉有种二分很简单的错觉,copy一下labuladong总结的模板:

第一种:最简单的二分查找
因为我们初始化 right = nums.length - 1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid+1 和 right = mid-1
因为我们只需找到⼀个 target 的索引即可
所以当 nums[mid] == target 时可以⽴即返回

第二种:寻找左边界
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧右侧边界以锁定左侧边界

因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧左侧边界以锁定右侧边界
⼜因为收紧左侧边界时必须 left = mid + 1
所以最后⽆论返回 left 还是 right,必须减⼀

至于代码,在leetcode704, 34中看吧。

另外,今天发现了C的标准库中有bsearch可以用。(感觉还不如自己写一个,懒得记23333)

int cmpfunc(const void * a, const void * b)
{
   return ( *(int*)a - *(int*)b );
}

int search(int* nums, int numsSize, int target){
    int *item= (int*) bsearch(&target, nums, numsSize, sizeof(int),cmpfunc);
    if (item!=NULL)
        return item-nums;
    else
        return -1;
}

二分搜索不难,但总是会有些“等号”“+1”“-1”的问题想的人抓耳挠腮

但是不要怕!二分真的不难!
写代码之前一定要打草稿!先写好伪码(一定不能怕麻烦边写代码边想)!

然后再考虑全测试用例!
考虑:

  1. 只有一个元素
  2. 二分时奇数偶数两种情况
  3. target在左/右边界,target不存在

想练手?安排。

23. Search in Rotated Sorted Array

链接: https://leetcode.com/problems/search-in-rotated-sorted-array/.

·

·

·

·

·
至少得先写伪码才能看!
不自觉警告⚠️
·

class Solution {
public:
    int search(vector<int>& nums, int target) {

        vector<int>::size_type N=nums.size();
        int l=0;
        int r=N-1;
        int m;
        
        while (l<=r){  //⚠️
            m=(l+r)/2;
            if (nums[m]==target)
                return m;
            else if (nums[l]<=nums[m]){  //⚠️
                if (nums[l]<=target && target<=nums[m])  //⚠️
                    r=m-1;
                else
                    l=m+1;
            }
            else{
                if (nums[m]<=target && target<=nums[r])  //⚠️
                    l=m+1;
                else
                    r=m-1;
            }
        }
        
        return -1;
    }
};

参考: https://blog.csdn.net/fuxuemingzhu/article/details/79534213.

34. Find First and Last Position of Element in Sorted Array

emmmmm这题,我先是想在一次二分中同时找上下bound,找一个是没问题,但再遍历找边界岂不是O(n)了咋都想不出来,一查就给跪了, 可以两次二分,分别找上下bound,合起来是O(logn)+O(logn),不还是O(logn)吗,哭泣。

c++有3个二分查找的函数:
lower_bound(起始地址,结束地址,要查找的数值) 返回的是数值 第一个 出现的位置。
upper_bound(起始地址,结束地址,要查找的数值) 返回的是 第一个大于待查找数值 出现的位置。
binary_search(起始地址,结束地址,要查找的数值) 返回的是是否存在这么一个数,是一个bool值。
注意:使用二分查找的前提是数组有序。函数返回值是vector::iterator

然鹅,upper_bound的返回值很容易出现越界的情况,搞不定的我决定还是自己写函数,越界的话high就为N.

菜鸟不易,自己写函数又有一堆问题,整了半天还是各种有问题,最后发现还是伪码写的不对,主要还是停下来跳出循环的问题,刚开始还行写着写着忽略了target不存在的问题。

思维还是很乱。下面直接用大佬的了,多学学吧。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int low = lower_index(nums, target);
        int high = upper_index(nums, target);
        
        if (low == high) return {-1, -1};
        return {low, high-1};
    }
    
    /*  // BF O(n)
    int lower_index(vector<int>& nums,int target){
        // return the index of the first number which is not less than target
        vector<int>::size_type N=nums.size();
        for (int i=0;i<N;++i){
            if (nums[i]>=target)
                return i;
        }
        return int(N);
    }
    
    int upper_index(vector<int>& nums,int target){
        // return the index of the first number which is greater than target
        vector<int>::size_type N=nums.size();
        for (int i=0;i<N;++i){
            if (nums[i]>target)
                return i;
        }
        return int(N);
    }
     */
    
    // binary search O(logn)
    int lower_index(vector<int>& nums, int target) {
        const int N = nums.size();
        // [l, r) ⚠️
        int l = 0, r = N; //⚠️
        while (l < r) { //⚠️
            int mid = l + (r - l) / 2;
            if (nums[mid] >= target) {
                r = mid;
            } else  {
                l = mid + 1;
            }
        }
        return l;
    }
    int upper_index(vector<int>& nums, int target) {
        const int N = nums.size();
        // [l, r)
        int l = 0, r = N;
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] <= target) {
                l = mid + 1;
            } else {
                r = mid;
            }
        }
        return l;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值