基础算法之二分算法

发布于:2024-04-24 ⋅ 阅读:(25) ⋅ 点赞:(0)

前言

本次博客,将要介绍二分算法的基本原理以及如何使用,深入浅出

二分可以针对整型以及浮点型接下来对其讲解希望对小白有所帮助吧

整型的二分法

一般要在一个数组中猜出一个数是否存在我们可以遍历一遍整个数组,判断是否存在,

看看遍历的简单代码

#include<stdio.h>
int main()
{
int arr[]={5,6,8,1,2,3,10,20,50};
int find=1;
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if(arr[i]==find)
{
printf("找到了\n");
break;
}
}
return 0;
}

但是如果这个数组的数是有序的,从大到小或从小到大的话

比如 1 2 3 4 5 6 7 8 9 10

我们可以从中间的数开始与要查找的数相比

如果中间的数大于查找的数 那么要查找的数在整个数组的左边,那么右边的数可以摒弃,右边界往左一半

如果中间的数小于查找的数 那么查找的数在整个数组的右边,那么左边的数可以摒弃,左边界往右一半

最终 左边和右边会相等或者是左边会大于右边此时循环结束

当然有两种情况是因为二分的范围不同,按照数学的集合的概念可以是 左闭右闭 

也可以是左闭右开 以及左开右开

闭就是取得到,开就是取不到

前两种用的更多,最后一种比较少

我们现在举一个例子,模拟一下在一个有序数组里中查找数的过程,简单的画个图以便理解

下面的图是左闭右闭为例

 

接下来还是看代码吧

先敲个左闭右闭

//左闭右闭
int main()
{
	int arr[10] = { 5,9,10,20,30,99,123,145,199,200 };
	int find;
	printf("请输入要找到的数");
	scanf("%d", &find);
	int left = 0;
	int right = sizeof(arr) / sizeof(arr[0])-1;
	//这里必须要写等于,不写会错,很多人在这里会犯错
	while (left <= right)
	{
		int mid = (left + right) / 2;
		if (arr[mid] > find)//因为mid不等于find所以-1缩小范围
			right = mid - 1;
		else if (arr[mid] < find)
			left = mid + 1;
		else
		{
			printf("找到了该数的下标为%d", mid);
			break;//这里要写break不然是死循环
		}
	}
	if (left > right)
	printf("没找到\n");
	return 0;
}

注意左闭右闭是要取到left==right的值比如 如果循环中没有等号

当范围为[1,1]且要查找的数为 1时,此时就会出现找不到的bug

这个很重要

接下来可以写一写左闭右开,其实也差不多

看看

//左闭右开
int main()
{
	int arr[] = { 20,36,55,69,100,300,500,1000,1002,1225 };
	int left = 0;
	int right = sizeof(arr) / sizeof(arr[0]);//这里取不到
	int find;
	printf("请输入要查找的数");
	scanf("%d", &find);
	while (left < right)//由于右边是取不到的所以不加等号
	{
		int mid = (left + right) / 2;
		if (arr[mid] > find)
			right = mid;//mid所指向的值不等于find,但是右边是开取不到的所以不减1
		else if (arr[mid] < find)
			left = mid + 1;//左边取得到 arr[mid]可以排除在外
		else
		{
			printf("找到了该数的下标为%d", mid);
			break;//不写可能死循环
		}
	}
	if (left >= right)
		printf("找不到\n");
	return 0;
}

这样我们就解决了,很多初学者不知道什么时候加一什么时候减一

非常nice

整型的二分到此就结束了

希望大家注意

1 二分是要有序的

2 二分可按区间判断左右边结束条件 以及调整方法

3找到后记得跳出循环

4注意它的时间复杂是log2n

浮点型的二分

浮点型的数据,在内存中往往是不精确的,那么为了解决这个问题,我们可以通过夹逼的方式

来处理问题

那么执行次数越多精确值越大,如果执行100次二分 那么就是2的100次方分之一的精确度

非常精确

此时的left right 代表的是这个数的范围,一直二分,每次将范围缩小二分之一,可以吧

比如

求一个数的立方根,并且精确到第6位小数

加设这个数的立方根的范围为-100~100

我们本次以此为例

通过浮点数的二分来解决问题,在此之前可以画图让大家了解

看代码吧

int main()
{
	double input;
	scanf("%lf", &input);
	double right=100;
	double left=-100;
	double ans = 0;
	for (int i = 0; i < 100; i++)
	{
		double mid = (right + left)/2;
		if (mid * mid * mid <= input)
			left = mid;
		else
			right = mid;
	}
	ans = left;
	printf("%lf", ans);
	return 0;
}

浮点数的精确除了直接使用for循环直接执行100外,也可以用right-left<精确度

比如保留6为小数就可以 right-left<1e-6

至此二分法的基础也算是讲解完了,这个算法还算是比较简单

还是要多多练习

祝大家开心,睡个好觉