二分法(手写和STL自带)

这篇博客详细介绍了二分查找算法,包括其基本原理、实现代码以及在有序数组中的应用。文中提供了两种二分查找的实现,一种是传统的循环实现,另一种是使用STL自带的lower_bound和upper_bound函数。此外,还给出了一种利用二分查找解决求连续子序列最短长度问题的解决方案,其时间复杂度为O(nlogn)。
摘要由CSDN通过智能技术生成

手写二分

题目如下:

给定一个 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

  1. 循环条件时left <= right,因为搜索范围是左闭右闭,left == right时也可能包含最终的解,left==right时不能结束循环
  2. 新的搜索范围不包括middle中间结点,因为搜索范围是左闭右闭,解的范围肯定不包括middlle结点
  3. 适用条件是排好序的数组,升序降序都可以,灵活应用,不要学死了。而且从某种程度来说,二分只需要会升序就可以了,降序是同理的,对升序的程序稍作修改即可
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 );
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值