在一堆数中寻找一个值的位置
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;
}