二分查找
二分查找需要找到序列中存在的元素的下标,如果值不存在就返回-1,因此循环的条件需要设置为left<=right,当left>right说明所查找的元素不存在,循环中止。
int binarySearch(int a[],int left,int right,int target) {
//当左端点小于等于右端点时进行循环
while(left<=right) {
//防止超过int范围
int mid = left+(right-left)/2;
//当中点的值为搜索目标,返回中点下标
if(a[mid]==target) return mid;
//当中点的值小于目标,说明目标在中点右边
else if(a[mid]<target) left=mid+1;
//当中点的值大于目标,说明目标在中点左边
else right=mid-1;
}
//当左端点大于右端点说明没有找到
return -1;
}
搜索必须要找到点或者点不存在时返回-1,所以右端点是n-1而不是n(数组的大小为n因此取不到)
//设n为数组a的大小
//设x为要查找的元素
binarySearch(a,0,n-1,x);
序列中第一个大于等于x元素的位置
如果序列中没有要找的x,也可以理解为如果存在x,那么x的位置应该是哪里。比如要找出往这个序列中插入x的位置,就可以用如下的方法。
int lower_bound(int a[],int left,int right,int x) {
//找出第一个(可能有多个相同的点)大于等于x的元素的位置
while(left<right) {
int mid = left+(right-left)/2;
//当中点的值大于等于x,说明目标在中点处或者中点的左边
if(a[mid]>=x) right = mid;
//当中点的值小于x,说明目标在中点的右边
else left = mid + 1;
}
//当left==right时说明只有一个点,返回left和right都可以
return left;
}
查找位置时不一定存在符合条件的元素,所以右端点可能在最大的元素后面,所以取得到n(数组的大小为n,即可以插到数组的后一个位置上)
//设n为数组a的大小
//设x为要查找的元素
lower_bound(a,0,n,x);
序列中第一个大于x元素的位置
这个的做法和上述的类似,只需要改变等号的位置即可。
int upper_bound(int a[],int left,int right,int x) {
//找出第一个大于x的元素的位置
while(left<right) {
int mid = left+(right-left)/2;
//当中点的值大于x,说明目标在中点处或者中点的左边
if(a[mid]>x) right=mid;
//当中点的值小于等于x,说明目标在中点的右边
else left = mid + 1;
}
return left;
}
同理,此处也不一定存在符合条件的元素,所以右端点可能在最大的元素后面,所以取得到n。
//设n为数组a的大小
//设x为要查找的元素
upper_bound(a,0,n,x);
木棒切割问题
木棒切割问题是二分问题的一个变种:给出N根木棒,长度均已知,希望能够通过切割它们来得到至少K段等长的木棒,问这些木棒最长能够切成多长?
int slice(int a[],int left,int right,int k) {
while(left<right) {
int mid = left + (right-left)/2;
//计算按当前的中间长度切割可以得到多少根木棒
int cnt=0;
for(int i=0; i<3; i++) {
cnt+=a[i]/mid;
}
//如果cnt大于k,说明切的太短,可以增长每段的长度
if(cnt>k) {
left=mid+1;
}
//如果cnt小于等于k,说明切的太长或者正好合适
else {
right=mid;
}
}
return left;
}
这里可以确定最长的长度不会超过最长那段木棒的长度,最短的长度一定大于0
//输入有序数组并且记录下最长的木棒长度为max_len
//设k为所需要得到的木棒数量
slice(a,0,max_len,k);
编程实现的一个重要细节
当使用int类型的数据时,由于C++的语言特性,mid求出来会自动向下取整,这就会引发一个问题!
举一个例子,当left为5,right为6时,mid会得到5,此时在遇到某些特殊的情况后,假设条件执行了改变left的那组代码,那么当代码写为
if(条件)
left=mid;
else
right=mid-1;
这样的形式后,就会导致left被赋值为mid,但是mid已经和left相等,导致循环while(left<right)
永远成立。
所以当使用会向下取整的编程语言进行int型代码的编写时,必须写成如下的形式。(注意=的情况总是和+1的代码在相反的代码段里)
if(条件)
left=mid+1;
else
right=mid;