二分

对于升序序列,有常见这样几种二分问题(假设要查的数是v):

  1. 第一个 >= v的位置
  2. 第一个 > v的位置
  3. 最后一个 <=v 的位置
  4. 最后一个 <v 的位置

个人认为:
前两个问题,是从前往后找,而后两个问题是从后往前找。
所以前两个在二分的时候中间值 m 的取法是向下取整,而后两个是上取整。
前两个左闭右开,后两个左开右闭。

#include <cstdio>

const int N = 10;
int A[N+1] = { 1,2,2,2,4,8,12,13,13,16 };

// [L,R)下的普通二分查找 v
int bsearch( int *A, int L, int R, int v ) {
	int mid;
	while( L<R ) {
		mid = (R+L)/2;
		if( A[mid]==v ) return mid;
		else if( v<A[mid] ) R = mid;
		else L = mid+1;
	} 
	return -1;
}

// ********** [L,R)左闭右开  ***********//

// 1. 第一个 >=v
// [L,R)下 v 的区间下界: 当 v 存在时返回它出现的第一个位置的下标。
// 如果不存在,返回这样一个下标:在此处插入 v (原来的元素后移)后序列仍然有序 
int Lower_bound( int *A, int L, int R, int v ) {
	while( L<R ){
		int m = (L+R)/2; // 向下取整 
		if( v<=A[m] ) R = m;
		else L = m+1;
	}
	return L;
}

// 2. 第一个 >v
// [L,R)下 v 的区间上界 : 当 v 存在时返回它出现的最后一个位置的后一个位置下标。
// 如果不存在,返回这样一个下标:在此处插入 v (原来的元素后移)后序列仍然有序 
int Upper_bound( int *A, int L, int R, int v ) {
	while( L<R ){
		int m = (L+R)/2;
		if( v<A[m] ) R = m;
		else L = m+1;
	}
	return L;
}

// ******** (L,R] 左开右闭  ******** // 

// 1. 最后一个 <= v
int Last_less_equal( int *A, int L, int R, int v ){
	while( L<R ){
		int m = (L+R+1)/2; // 向上取整 
		if( v>=A[m] ) L = m;
		else R = m-1;
	}
	return L;
}
// 2. 最后一个 < v  
int Last_less( int *A, int L, int R, int v ){
	while( L<R ){
		int m = (L+R+1)/2; // 向上取整 
		if( v>A[m] ) L = m;
		else R = m-1;
	}
	return L;
}

int main()
{
	int v;
	while( scanf("%d",&v)==1 ) {
		
		int pos = bsearch( A,0,N,v );
		printf("%d is located at %d\n",v,pos);
		
		printf("%d's Lower bound is %d\n",v,Lower_bound(A,0,N,v));
		
		printf("%d's Upper bound is %d\n",v,Upper_bound(A,0,N,v));
		
		printf("last <=%d located %d\n",v,Last_less_equal(A,-1,N-1,v));

		printf("last <%d located %d\n",v,Last_less(A,-1,N-1,v));
		
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值