【算法笔记】关于二分法边界问题。

文章介绍了二分法在有序列表中的应用,强调了边界问题对算法效率的影响。提供了两种二分查找的代码模板,分别讨论了加等于号和不加等于号在查找存在与不存在元素时的区别,以及它们对查找区间的影响。总结了如何根据需求选择是否添加等于号以及如何理解两个模板的查找特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

最近在学习算法,二分法是一个非常好的优化查找时间的方法,在列表有序的情况下,我们可以使用二分法加快查抄速率。虽然思路很简单,但是二分法很容易遇到边界问题,一不小心就会被边界卡住,做题的时候非常头疼。这里做一下小总结。PS:以下均为整数二分。

一:算法思路

二分法思路其实很简单,我们设置一个low指针指向有序数组的最低位置,high指针指向最高位置,然后将待查找的元素与(low+high)/2的位置比较。如果待查元素小于中间位置,那么就去右半边查找,否则就去左半边查找。(这是以为右半边元素肯定是大于这个中间元素的,以为数组元素有序,左半边亦然)

二:算法代码

2.1: 模板1

ps:完整代码会在最后面附上,这里只是代码片段。

		while(low<high){
			
			int mid = (low+high)/2;
			if(q[mid]>=a[i]){
				high = mid;
			}else{
				low = mid+1;
			}
		}

2.2: 模板2

		while(low<high){
			int mid = (low+high+1)/2;
			if(q[mid]<=x){
				low = mid;
			}else{
				high = mid-1;
			}
			
		}

关于上述代码的注意事项:

  • 如果我们在修改的过程中,让low = mid那么一定要让low+high这里再加上一个1,则mid = (low+high+1)/2; 否则可能会导致死循环。这里可以自己推导以下,或者直接记住拉到。
  • if(q[mid]>=a[i])这里的等于号,是我们可以自己修改的,有些题中这个等于号需要,有些题不需要。这也就变成了我们自己做题的时候需要注意的地方。下面讨论一下等于号加与不加的含义。

上面只是简单的给了俩个模板,下面分析一下这俩个模板的具体含义。包括等号的作用进行总结。

三: 算法演示

下这段代码是一段演示代码。就是定义了一个长度为12的数组,元素有序,但是有多个重复的5,来讨论一下等号的具体含义。

#include<iostream>
using namespace std;
int q[]={1,2,3,4,5,5,5,5,6,7,8,9,10};
int main(){
	int low = 0;
	int high = 9;
	
	while(1){
		float x;
		cin>>x;
		int low = 0;
		int high = 12;
		while(low<high){
			int mid = (low+high)/2;
			if(q[mid]>=x){
				high = mid;
			}else{
				low = mid+1;
			}
			
		}
		
		cout<<"low:"<<low<<"high:"<<high<<endl;
		
		 low = 0;
		high = 12;
		while(low<high){
			int mid = (low+high+1)/2;
			if(q[mid]<=x){
				low = mid;
			}else{
				high = mid-1;
			}
			
		}
		
		cout<<"low:"<<low<<"high:"<<high<<endl;
	}
	return 0;
}

3.1 加等号查找

我们将俩个模板都加上等号,查找5和5.5。运行看一下结果。(5为数组中存在的数,5.5不存在)
数组为:

int q[]={1,2,3,4,5,5,5,5,6,7,8,9,10};

在这里插入图片描述

  • 对于查找到数组中存在的数来说:

    可以看到,第一段代码查找的是左边界。且是闭区间。第二段代码查找到的是最右边界。也是闭区间。

  • 对于查找数组中原本不存在的数来说:

    可以看到,第一个模板查到的是最右边界,第二段代码查找的是最左边界。

3.2 不加等号查找

我们将俩个模板都去掉等号,查找5和5.5。运行看一下结果。(5为数组中存在的数,5.5不存在)
在这里插入图片描述

  • 对于查找到数组中存在的数来说:

    可以看到,第一段代码查找的是右边界。且是开区间。第二段代码查找到的是最左边界。也是开区间。

  • 对于查找数组中原本不存在的数来说:

    可以看到,第一个模板查到的是最右边界,第二段代码查找的是最左边界。

3.3 总结

可以看到,对于二分法而言,如果我们查找的是数组中本来就不存在的数字,那么加不加等号不影响结果。并且查找的结果永远都是,第一个模板是查找右开区间,第二个模板是左开区间。这里有一个自己的记忆方法:
如果是low = mid,那就是左开区间。
如果是high = mid,那就是右开区间。
(看最左边是low还是high)
但是如果我们要查找的数是数组中本来就有的,那么
加等号和不加等号中俩个模板的功能反过来了。首先可以确定的是,加等于号一定是尽可能的找到闭区间,而不加找到的是开区间。如果查找找的元素数组中原来就有且带等号,那么
low = mid找到的是右闭区间,high = mid是左开区间。

所以,在做二分的题目的时候,关键就是要取舍自己是否要加等号。加找到的是小于等于或者大于等于边界的位置,不加找到的一定是大于或者小于边界的位置。加等号后,俩个模板查找的功能就相反了。
low = mid变成右边界,high= mid是左边界。

### C语言实现二分法 在C语言中,二分法是一种高效的算法用于在一个有序数组中查找特定元素的位置。该方法通过反复将待查序列折半来减少搜索范围,从而显著提高效率。 #### 函数定义与参数说明 为了实现这一功能,可以创建名为`binary_search`的函数[^1]: ```c int binary_search(int arr[], int size, int target); ``` 此函数接收三个参数:指向整数数组的第一个元素指针`arr[]`;表示数组长度的正整数值`size`;以及要寻找的目标值`target`。返回值为找到目标位置的索引,如果未发现则返回-1作为标志。 #### 逻辑流程解析 以下是具体的实现过程: 1. 初始化两个边界变量left和right分别代表当前考虑区间的左端点(0)和右端点(size - 1),并计算中间位置mid。 2. 当left不超过right时循环执行以下操作: * 计算新的mid=(left+right)/2; * 如果arr[mid]==target,则已定位到目标项,立即退出循环; * 若arr[mid]<target,则更新left=mid+1缩小左侧区间; * 否则调整右侧边界right=mid-1。 3. 结束条件满足后检查是否成功匹配到了目标值。如果没有找到对应的键值对,就给出提示信息指出不存在这样的记录。 #### 完整代码实例 下面是一个完整的例子展示了如何利用上述思路编写一个简单的二分查找程序: ```c #include <stdio.h> // 声明二分查找函数原型 int binary_search(int arr[], int size, int target); int main() { // 测试数据集 int numbers[] = {1, 3, 5, 7, 9}; int n = sizeof(numbers) / sizeof(numbers[0]); printf("请输入想要查询的数据:\n"); int key; scanf("%d", &key); // 调用二分查找函数 int result = binary_search(numbers, n, key); if (result != -1){ printf("找到了! %d 的下标是:%d\n", key, result); }else{ printf("抱歉,%d 不在这个列表里。\n", key); } return 0; } // 定义二分查找的具体实现 int binary_search(int arr[], int size, int target) { int left = 0; /* 左边界的初始设置 */ int right = size - 1;/* 右边界的初始设置 */ while (left <= right) { int mid = left + (right - left) / 2; // 检测中间值是否等于目标值 if (arr[mid] == target) return mid; // 如果目标大于位于中间的元素,则忽略左边部分 if (arr[mid] < target) left = mid + 1; // 如果目标小于位于中间的元素,则忽略右边部分 else right = mid - 1; } // 如果我们到达这里,那么元素不在数组中 return -1; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值