二分搜索(c++,模板,详细解析)

二分搜索(c++,模板,详细解析),第1张

二分搜索(c++,模板,详细解析)

普通二分搜索

while(l

循环条件                初始化                循环终止                查询区间

 while(l

while(l<=r)               l=0,r=n-1            l==r+1                    [l,r]

int binary_search(int x)
{//普通二叉搜索,查找某个元素,返回下标
	int l = 0;
	int r = n;			//数组长度
	//l> 1;		//中点
		if (a[mid] == x)
			return mid;
		else if (a[mid] < x)				//x在mid右边
			l = mid + 1;				//去区间[mid+1,r)寻找
		else if (a[mid] > x)			//x在mid左边
			r = mid;					//去区间[l,mid)寻找
	}
	//最后出来时l==r,我们少检查一个元素mid,此时还要检查
	return a[l]==x?l:-1;							//找不到

	
	 l = 0;
	 r = n - 1;
	//l<=r,循环终止条件是l==r+1,可遍历区间是[l,r]
	//由于右边会取交集,所以r==n-1,而不是取n
	while (l <= r)
	{
		int mid = l + r >> 1;
		if (a[mid] == x)
			return mid;
		else if (a[mid] < x)		//right
			l = mid + 1;			//去区间[mid+1,r]寻找
		else if (a[mid] > x)			//left
			r = mid - 1;			//去区间[l,mid-1]寻找
	}
	return -1;
	//l<=r退出的循环条件是l==r+1,等价于[r+1,r],所以是不会漏的
	//那么如果while循环找不到,就说明没有
}
如果一个x有多个,二分搜索分为两种情况:

1.寻找第一个满足条件的元素

2.寻找最后一个满足条件的元素

1.寻找第一个满足条件的元素

 

int left_bound(int x)
{//寻找x的第一个出现的下标
//也可以理解为:数组中有几个小于x的数字,可能的返回值取值为[0,n]

	int l = 0, r = n;
	//l> 1;
		if (a[mid] < x)	//元素在右边
			l = mid + 1;	//去[mid+1,r)区间寻找
		else if (a[mid] > x)	//元素在左边
			r = mid;	//去[l,mid)区间寻找
		else if (a[mid] == x)	//这也是为什么能搜查左边界的原因
			//遇到x时不是立即返回,而是继续向左搜索,减小右边边界
			r = mid;
	}
	//循环终止条件是l==r,可能取值是[0,n],left满足时返回l,当搜寻不到时返回-1
	//可能越界,先检查是否越界
	if (l == n) return -1;
	return a[l] == x ? l : -1;

	//方法二,while(l<=r)的情况
	 l = 0, r = n - 1;
	//l<=r  搜索区间
	 while (l <= r)
	 {
		 int mid = l + r >> 1;
		 if (a[mid] < x)		//x在右边
			 l = mid + 1;		//去[mid+1,r]区间查找
		 else if (a[mid] > x)	//x在左边
			 r = mid - 1;		//去[l,mid-1]区间查找
		 else if (a[mid] == x)
			 r = mid - 1;		//去[l,mid-1]区间查找
	 }
	 //由于循环终止条件是  l==r+1  l可能取值是[0,n]
	 //由于l可能取到n,下标会越界,所以需要检查
	 if (l >= n || a[l] != x)
		 return -1;
	 return l;
}

2.寻找最后一个满足条件的值

 

int right_bound(int x)
{
	int l = 0, r = n;
	
	while (l < r)
	{
		int mid = l + r >> 1;
		if (a[mid] < x)		//x在右边
			l = mid + 1;	//去区间[mid+1,r)寻找
		else if (a[mid] > x)	//x在左边
			r = mid;		//去区间[l,mid)寻找
		else if (a[mid] == x)	//找到了x不是立即返回,而是去右边继续寻找
			l = mid + 1;
	}
	if (l == 0)return -1;
	return a[l - 1] == x ? l - 1 : -1;
	
	l = 0, r = n - 1;
	//while(l<=r)	循环终止l==r+1,循环区间[l,r)
	while (l <= r)
	{
		int mid = l + r >> 1;
		if (a[mid] < x)	//x在右边
			l = mid + 1;	//去区间[mid+1,r]寻找
		else if (a[mid] > x)
			r = mid - 1;	//去区间[l,mid-1]寻找
		else if (a[mid] == x)	//继续寻找右侧端点
			l = mid + 1;	//去区间[mid+1,r]寻找
	}
	//循环终止条件为l==r+1,所以只要r不越界,那么r的值就是x的右侧边界,不需要l-1
	if (r < 0 || a[l] != x)
		return -1;
	return r;
}

y总的记忆法

int acwing(int x)
{
	//搜索左侧边界
	//[l,r]分为[l,mid],[mid+1,r]
	//[l,mid]保证了不漏

	int l = 0, r = n - 1;
	while (l < r) {
			int mid = (l + r) / 2;
			if (a[mid]>=x)	//x在左边,去左边寻找左侧边界 
				r = mid; 
			else l = mid + 1;
		}
		return l;
	
	//搜索右侧边界
	//[l,r]分为[l,mid-1][mid,r]
	//[mid,r]保证了不漏
		l = 0, r = n - 1;
	while (l < r)
	{
		int mid = (l + r + 1) >> 1;		//写了l=mid,mid就+1
		if (a[mid] <= x) l = mid;
		else r = mid - 1;
	}
	return l;
	//p.s.mid = (l + r + 1) / 2这里,+1是因为除法下取整,
	//在r = l + 1时更新l = mid时会出现l = mid = l的死循环。+1则相当于上取整,解决了这个隐患。
}
//写成l=mid mid=(l+r+1)/2
//写成l=mid+1 mid=(l+r)/2

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5636521.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存