ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

二分与前缀和

2022-06-15 09:33:46  阅读:151  来源: 互联网

标签:二分 r1 int mid while l1 边界 前缀


有一个已经按升序排好序的数组,求某个数在数组中出现的下标区间。即,若这个数在数组中出现一次或多次,就给出这个范围。

#include <iostream>
using namespace std;
const int maxn = 100005;
int n, q, x, a[maxn];
int main() {
	scanf("%d%d", &n, &q);
	for (int i = 0; i < n; i++)    scanf("%d", &a[i]);
	while (q--) {
		scanf("%d", &x);
		int l = 0, r = n - 1;
		while (l < r) {
			int mid = l + r >> 1;
			if (a[mid] < x)  l = mid + 1;
			else    r = mid;
		}
		if (a[l] != x) {
			printf("-1 -1\n");
			continue;
		}
		int l1 = l, r1 = n;
		while (l1 + 1 < r1) {
			int mid = l1 + r1 >> 1;
			if (a[mid] <= x)  l1 = mid;
			else    r1 = mid;
		}
		printf("%d %d\n", l, l1);
	}
	return 0;
}

设区间为 [left,right]。
首先用二分法求区间 left。对应代码:

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

初始时 l 和 r 处都有值。mid 的值靠近 l。

  • 如果 a[mid] 小于目标值 x,则说明 l 小了,此时调整 l 到 mid+1。这里是想求出下界,所以要保证所有小于 x 的数也一定小于 a[l]。
  • 如果 a[mid] 等于 x,则说明已经探测到区间内部了,但是不知道边界在哪里。注意!这一步只是要求左边界,所以需要极力保证左边,对右边可以随意。因此,将 r 移到 mid 处,缩小了查找的范围。由于已经保证了 l 处的值一定大于数组中所有小于 x 的数,因此当 x 存在时,l 就是 x 的左边界;当 x 不存在时,l 是数组中第一个大于 x 的数所在的位置。此时 l 就不会动了,r 会不停地变小,直到等于 l,循环终止。

    这里证明一下,当 x 存在时 l 一定是左边界。如果 l 是 x 出现区间中除左边界以外的某个点,由于 l 的变化规则是 mid0+1,这就说明上一步的 mid0=l-1。而 l 不是左边界(l>left),因此 mid0 一定满足 left<=mid0<l,这就说明当时的情况是 a[mid0]==x,那这怎么会让 l 变化呢……矛盾了。

  • 如果 a[mid] 大于 x,显然是因为 r 大了,就要让 r 减小到 mid。这里其实也可以减小到 mid-1,但是就不能和 a[mid]==x 的情况写在一起了。
    else if(a[mid]==k) r=mid;
    else r=mid-1;
    
  • 循环终止条件:a[l]<=x, a[r]>=x,因此 l 和 r 有可能相等。

求右边界:

int l1 = l, r1 = n;
while (l1 + 1 < r1) {
	int mid = l1 + r1 >> 1;
	if (a[mid] <= x) l1 = mid;
	else r1 = mid;
}

直接在刚才的基础上,用 l 作为左边界(l 本来就是左边界嘛,如果要一般化就还是 l1=0),r1 从没有值的 n 处开始(总范围是 [0,n-1])。
我们的目标是让 l1 成为右边界。同时,我们要保证 r1 永远是大于 x 的。

  • 当 a[mid]==x 时,我们并不知道此时的 mid 到底是内部点还是什么,所以只敢动 l1,不能动 r1。因此 l1=mid。
  • 当 a[mid]<x 时,显然还是要增大 l1,同理这里也可以是 l1=mid+1,但是不能和 a[mid]==x 的情况写在一起。
  • 当 a[mid]>x 时,要减小 r1。如果 r1=mid-1,则 r1 有可能变成区间右边界了,这不行,因为我们想让 r1 大于 x。因此 r1=mid。
  • 再说说循环终止条件。a[l1] 永远是 <=x 的,a[r1] 永远是 >x 的。最后的关系一定是 l1=r1-1。如果 x 不存在,则 a[l1]<x, a[r1]>x,最后也是 l1=r1-1 的关系。

标签:二分,r1,int,mid,while,l1,边界,前缀
来源: https://www.cnblogs.com/preccrep/p/16377190.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有