二分查找

寻找有序序列中第一个满足某条件的元素的位置

  1. 二分的区间是答案可能出现的区间,而不是(不一定是)有序序列的区间,比如说,对于“寻找序列中第一个大于等于x的元素的位置(lower_bound)”这个问题,如果给定有序序列的范围是 [ 0 , n − 1 ] [0,n-1] [0,n1],那么考虑到x有可能比这个序列中所有的数都要大,实际上答案的范围是 [ 0 , n ] [0,n] [0,n]
  2. 不管是什么形式的区间,while循环进行的条件永远都是是否找到了唯一的位置,也就是待二分的区间是否可以夹出唯一的位置。
  3. rightleft赋值,实际上是选择左区间或者右区间,然后继续二分。要注意,赋值之后区间的形式要保持统一,比如一开始区间是左闭右开,那么赋值之后仍应该是左闭右开的。

这类问题有几个常见的算法:

  • lower_bound 寻找第一个大于等于x的元素的位置
  • upper_bound 寻找第一个大于x的元素的位置

这两个函数在中均有实现,且参数区间范围均是左闭右开,也就是说,答案的范围相当于同一个区间的左闭右闭。

// 寻找有序序列第一个满足某条件的元素的位置
int solve (int left, int right) {
	int mid;
	while (没有找到唯一位置) {
		mid = (left + right) / 2  // 取中点
		// mid = left + (right - left) / 2  // 等价写法避免溢出
		if (A[mid]满足条件) {  // 找第一个满足条件的位置
			给 right 赋值
		} else {
			给 left 赋值
		}
	}
	
	return 唯一的位置
}

寻找序列中是否存在满足某条件的元素

  1. 这个问题完整的描述是“寻找序列中是否存在满足某条件的元素,如果存在则返回元素的位置,否则返回-1”。
  2. 对于这类问题,序列中的元素最好不要重复,否则答案位置不唯一,这时应该使用lower_boundupper_bound找到答案的上下边界。
  3. 如果只是想要知道是否存在,可以直接使用中的binary_search。在已知存在的情况下,如果还想进一步知道位置,可以使用lower_bound,也就是说,同时使用标准库函数binary_searchlower_bound,可以达到与下面的代码一样的目的,而且查找到的一定是第一个可能的位置。
int binary_search (int left, int right) {
	int mid;
	while (没有找到唯一位置) {
		mid = (left + right) / 2  // 取中点
		// mid = left + (right - left) / 2  // 等价写法避免溢出
		if(A[mid]满足条件) return mid;
		if (待找元素在左子区间) {
			给 right 赋值
		} else {
			给 left 赋值
		}
	}
	
	return -1;  // 查找失败
}

你可能感兴趣的:(算法模板)