二分算法总结

在一堆数中寻找一个值的位置

1.左闭右闭[l,r]


int search(int nums[], int size, int target) //nums是数组,size是数组的大小,target是需要查找的值
{
    int left = 0;
    int right = size - 1;	// 定义了target在左闭右闭的区间内,[left, right]
    while (left <= right) {	//当left == right时,区间[left, right]仍然有效
        int middle = left + ((right - left) / 2);//等同于 (left + right) / 2,防止溢出
        if (nums[middle] > target) {
            right = middle - 1;	//target在左区间,所以[left, middle - 1]
        } else if (nums[middle] < target) {
            left = middle + 1;	//target在右区间,所以[middle + 1, right]
        } else {	//既不在左边,也不在右边,那就是找到答案了
            return middle;
        }
    }
    //没有找到目标值
    return -1;
}

注意能写成一下格式

if(num[mid]<=target)r=mid-1;
else l=mid+1;


if(num[mid]<target)r=mid-1;
else l=mid+1;

也就是说当num[mid]的值等于target的值的时候,我们不能把它放入小于或大于的条件中进行运算,很好理解,因为小于或大于的条件下l或r的值会发生改变,会使找找到的答案进行偏移,也可能TLE。

2.左闭右开[l,r)


int search(int nums[], int size, int target)
{
	int left = 0;
	int right = size; //定义target在左闭右开的区间里,即[left, right)
	while (left < right) {	//因为left = right的时候,在[left, right)区间上无意义
		int middle = left + ((right - left) / 2);
		if (nums[middle] > target) {
			right = middle; //target 在左区间,在[left, middle)中 
		} else if (nums[middle] < target) {
			left = middle + 1;
		} else {
			return middle;
		}
	} 
    // 没找到就返回-1
	return -1;
}

注意此时下面的代码也可以

while(l<r)
		{
			int mid=(l+r)/2;
			if(nums[mid]>=target)r=mid;
			else l=mid+1;
		}

而下方代码就不行

while(l<r)
		{
			int mid=(l+r)/2;
			if(nums[mid]>target)r=mid;
			else l=mid+1;
		}

最简单的理解就是当num[mid]==target的时候,第二块代码中r的值不会改变,因此当r的值就是答案的值的时候,l会不断往r方向递进,知道l==r,最后l的值就是答案的值。而第三块中当num[mid]==target的时候,此时l就是答案,但其会进行+1操作,所以不行。

以上代码粘贴别人的代码,自己太菜,写不出优美的代码。

在一堆数中找的一个值的区间(数的区间问题)

这个就是acwing讲的数的范围

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int q[N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

    while (m -- )
    {
        int x;
        scanf("%d", &x);

        int l = 0, r = n - 1;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (q[mid] >= x) r = mid;
            else l = mid + 1;
        }

        if (q[l] != x) cout << "-1 -1" << endl;
        else
        {
            cout << l << ' ';

            int l = 0, r = n - 1;
            while (l < r)
            {
                int mid = l + r + 1 >> 1;
                if (q[mid] <= x) l = mid;
                else r = mid - 1;
            }

            cout << l << endl;
        }
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/39787/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

狠狠记住这个模板就行。

求左区间的时候,mid=(l+r)/2,这样写是防止当l恰好是左边界,且r=l+1的时候mid是r而不是l,为了防止死循环,因此l要+1。

同理,再求右区间的时候,mid=(l+r+1),这样写是防止当r恰好是右边界且r=l+1的时候mid是l而不是r,并且此式子可以不用担心死循环,所以l不+1。

发现还可以这样写

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int q[N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

    while (m -- )
    {
        int x;
        scanf("%d", &x);

        int l = 0, r = n - 1;
        while (l <= r)
        {
            int mid = l + r >> 1;
            if (q[mid] >= x) r = mid-1;
            else l = mid + 1;
        }

        if (q[r+1] != x) cout << "-1 -1" << endl;
        else
        {
            cout << l << ' ';

            int l = 0, r = n - 1;
            while (l <= r)
            {
                int mid = l + r + 1 >> 1;
                if (q[mid] <= x) l = mid+1;
                else r = mid - 1;
            }

            cout << l-1 << endl;
        }
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值