目录
一:题目
二:分析
题目要求我们在给定的有序的整型数组元素中找到某一个数的区间,如果没有该数就输出-1.
这就是一个很典型的整数二分查找的题目:元素排列具有单调性!!!
所以我们就可以使用整数二分查找的两个模板来进行作答。(第三点是二分算法的分析,会的大佬请移步到第四点)
三:二分算法分析(整数的二分,浮点数类似)
二分的本质就是根据某个性质,把元素分为满足该性质的和不满足该性质的两部分,我还是直接上图:
图中元素分为两个部分,那么二分的最后的实现效果就是将箭头所指向的性质的边界点二分出来(注意两个箭头所指的位置没有相交,因为是整数二分)
每次进行二分的时候我们都取中点,判断中点是否满足性质,然后根据结果将边界往里缩小,直到找到边界。
四:二分算法代码示例
以题目为例,假设数组元素为1 2 2 3 3 4,假设我们要找3.
先找左区间:很明显我们要划分的性质是和3有关的,那么要把左边界的3划入左边区间还是右边区间呢?
答案是右边区间。
(重点)为什么呢:要找到左边界的3,其性质满足:其左边的数都是小于3的,其右边的数都是大于等于3的,所以3就应该归属于右边的区间。
明白了这一点就能够看懂代码了:
int i=0,j=n-1;//i和j分别为数组的左边和右边界
int mid;
while(i<j)
{
mid=i+j>>1;
if(arr[mid]>=3)
j=mid;
else i=mid+1;
}
当mid满足>=3性质的时候,那么要找的3可能就是mid或者在mid的左边(为什么,这里建议画图深刻理解,这里用文字说明),因为要找的是左边界,左边界的左边都小于三,而mid大于等于三(你品你细品),我还是给图吧:
所以要把j往左边移动到mid(mid可能是边界,要包括)。
同理,当mid不满足>=3性质时,i就往右移动到mid+1(道理自己思考,很容易的)。
由此可以退出找右边界的情况,然后根据题目进行代码的完善。
五:代码实现:
#include<stdio.h>
int main()
{
int n,q,k;
int arr[(int)1e6+10];
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++)
{
scanf("%d",arr+i);
}
while(q--)
{
int left,right;
scanf("%d",&k);
int i=0,j=n-1;
int mid;
while(i<j)
{
mid=i+j>>1;
if(arr[mid]>=k)
j=mid;
else i=mid+1;
}
if(arr[i]!=k)//如果左边界都找不到肯定没有右边界,所以直接进行下一次循环
{
printf("-1 -1\n");
continue;
}
left=i;
i=0; j=n-1;
while(i<j)
{
mid=i+j+1>>1;
if(arr[mid]<=k)
i=mid;
else j=mid-1;
}
right=i;
printf("%d %d\n",left,right);
}
}
这里一个很重要的mid 的取值问题:
你有没有注意到求右边界的mid为什么要+1再除以二吗?
这样是为了mid不会取到i:
当代码进行到这个阶段的时候,如果mid取到i,那么当mid满足<=k的性质时,i=mid,你就发现边界i和j根本没有变化,所以会一直执行下去,造成死循环。
同理:求左边界的mid不能取到j。
写在最后:
(这是毒鸡汤):人生路漫漫,当我们摆烂的时候有人说我们无所事事,当我们努力的时候有人在嘲讽我们内卷,但是不要忘了:我们是自己,不必在意外界的东西,努力的意义从来都是为了自己,希望看到这里的你能够忘记过去的种种烦恼,继续加油!