二分算法 再次理解

二分算法 再次理解

详解二分查找算法

这篇博客很详细介绍了二分算法的一些细节问题

寻找一个数,也是最基本的二分搜索

//代码示例如下
int bsearch(int []nums, int target)
{
    int left=0, right=nums.length-1;//这里的数组长度用法可以是其他的形式
    while(left<right)
    {
        int mid = left + (right - left) / 2; //注意这是地板除
        if(num[mid] == target)
            return mid;
        else if(num[mid] > target)
            right = mid - 1;
        else left = mid + 1;
	}
    return -1;//没有找到
}

寻找左侧边界

这里的代码实现和lower_bound(begin,end+1, target)相同,都是找到第一个大于等于target的数组下标。

假设数组中的排列顺序是按照从小到大的。

int left_bound(int[] nums, int target)
{
    if(nums.length == 0) return -1;
    int left = 0, right = nums.length;
    //注意这里的边界包含了最右边不存在的一个位置,因此最后的结果也是可以返回这个位置的
    while(left < right)
    {
        int mid = left + (right - left) / 2; //地板除法
        if(nums[mid] >= target)
            right = mid; //[left, mid]
        else left = mid+1;//[mid+1, right]
	}
    return left; //返回的结果肯定是第一个大于或是等于target的下标,
    //需要注意的是,因为右侧多包含了一个边界外位置,因此,也是可能返回的。
    //当返回右侧边界外位置的时候,表明数组内没有元素大于等于target值
}
nums: 1 2 2 4
index: 0 1 2 3

如果使用这个左侧二分搜索2,那么将会返回1,表示在nums数组中有一个元素小于2,其他的类似。但是这样也存在一些问题,如果我们搜索1或者-1的时候,返回的值都是0,但是两者的含义是不同的,所以再收到结果后需要进行判断,以免出错。当然这里如果搜索的值为8的时候返回为值为4,也就是数组最后一个元素的下一个元素,这里也需要进行注意。

寻找右侧边界的二分查找

这个题目的要求是寻找最后一个小于target的下标,实际上我们可以对文件进行转化,我们只需要找到第一个大于等于target的下标,然后减一,就是所需要的答案了。

当前,我们也可以不用转变思维,直接求取最后一个小于target的下标。

int right_bound(int[] nums, int target)
{
    if(nums.length==0) return -1;
    int left = 0, right = nums.length;
    whlie(left < right)
    {
        int mid = left + (right - left + 1) / 2; //注意,这里需要向上除
        if(nums[mid] < target)
            left = mid; // [mid, right],left=mid是需要向上除的原因
        else right=mid-1; //[left, mid-1]
	}
    return left;
}

这里可以转变思维,也可以直接求取,但是注意地板除法和向上出发的原因:

如果left=mid需要向上除法,如果是left=mid+1,使用地板除法即可。

需要求一个边界,但是不知道是左边界还是右边界的情况

这个是自己在做一个题的时候遇到的,详情参看这里:POJ 3685 Matrix

//这里的nums元素也是有序的,但是nums可能不全是有效的,这时就需要我们使用这种方法
int both_bound(int[] nums, int target)
{
    int left=-1, right=nums.length, ans; //左右都开的区间内进行查找
    while(left+1 < right)
    {
        int mid = left + (right - left) / 2;
        //下面的两句话需要根据实际情况耳边
        //不变的是left和right都要直接等于mid
        if(nums[mid] >= target)		
        {						
            ans = mid;			
            left=mid;			
		}
        else right=mid;
	}
    return ans;
}

2023年5月15日更新:

发现这里有点问题,满足二分的算法其实是需要知道左边界还是右边界的,不可能出现不知道边界的情况。因为二分做法的前提是题目满足一定的性质:单调性(我自己起的)。我们可以根据这个单调性来二分排除不满足我们需求的范围。

上面的题目,仔细阅读,其实是还是知道边界的,我们枚举的值越小,那么就越不可能是第m小,等等。

这篇文章是自己看到那篇博客的一些领会,详细参阅还是需要看看原文章,链接再最上面

posted @ 2020-01-30 11:54  ALKING1001  阅读(238)  评论(0编辑  收藏  举报