手写二分
题目如下:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例一:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例二:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
- 循环条件时left <= right,因为搜索范围是左闭右闭,left == right时也可能包含最终的解,left==right时不能结束循环
- 新的搜索范围不包括middle中间结点,因为搜索范围是左闭右闭,解的范围肯定不包括middlle结点
- 适用条件是排好序的数组,升序降序都可以,灵活应用,不要学死了。而且从某种程度来说,二分只需要会升序就可以了,降序是同理的,对升序的程序稍作修改即可
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;
}
STL自带二分函数
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
应用
给定长度为n的数列整数a0,a1,…,an-1以及整数S。求出总和不小于S的连续子序列的长度的最小值。如果解不存在,则输出0。
限制条件:
- 10 < n < 10^5
- 10 < ai <= 10^4
- S < 10^8
a是原数组,sum是a的累加和数组,一共n轮循环,每轮循环对序列进行二分查找,时间复杂度为O(logn),所以该算法的时间复杂度为O(nlogn)
// 输入
int n, S;
int a[ MAX_N ];
int sum[ MAX_n + 1 ];
// O( nlogn )
void solve() {
// 计算sum
for ( int i = 0; i < n; i++ ) {
sum[ i + 1 ] = sum[ i ] + a[ i ];
}
if ( sum[ n ] < S ) {
// 解不存在
printf( "0\n" );
return;
}
int res = n;
for ( int s = 0; sum[ s ] + S <= sum[ n ]; s++ ) {
// 二分法查找t
int t = lower_bound( sum + s, sum + n, sum[ s ] + S ) - sum;
res = min( res, t -s );
}
printf( "%d\n", res );
}