天天看点

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

二分查找:

引用网上大神的一句话:

 Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky...

 这句话可以这样理解:思路很简单,细节是魔鬼。

所以本文中讲述的都是二分查找的细节问题。

定义

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

算法要求

1.必须采用顺序存储结构。

2.必须按关键字大小有序排列。

查找条件

查找区域的左边界,小于等于查找区域的右边界

查找过程

 1. 循环条件 == 查找条件  

 2.计算序列中间下标位置

 3.如果待查找值value == 中间位置的元素的值 返回当前中间位置下标

 4.如果待查找值value   >   序列中间位置元素的值  左边界等于中间位置+1

 5.如果带查找值value    <  序列中间位置元素的值   右边界等于中间位置-1

 6.如果循环结束 返回 -1   (也就是没查找到,返回-1)

用英语作文的写作方式总领一下流程:

First,来说明一下二分查找的具体流程,先说明的是mid中间值的问题:

为什么不用mid = (left+right)/2 ?

因为我们假设定义的类型是int型,查找的是[0,65535]数字中的60000,第一查找mid = (left+right)/2,就会超出int型的数据范围,这样mid值就是内存中的一个不确定数字,但这个数字和我们原来实际查找的中间数是不同的,所以说,这种写法有问题。

正确写法:

int mid = left + ((right-left)>>1);

其中((right-left)>>1)是右值减去左值除2的意思,猛的一看不好理解,举个例子,假设right-left的值为4,二进制就是0100,右移一个就是010,也就是除2的后的值,但为什么是这样写呢?如下,3和5都是不同的形状大小,相减后在平分,也就是中间值的思想。画图理解:

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

Second,说明的是while()循环括号中两种情况,因为while循环中不同条件影响后面的代码:

这里面的size是数组元素的总大小 。

情况一 int left = 0; int right = size - 1;

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

这种情况左右都是闭区间,left <= right ,原因如下:

假设我们要找的数字是9,第一次二分查找后,9比5大left = ++mid,right不变,结果是:

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

注意,我们要找的数字是9,如果left < right,此时循环会跳出,找不到数字9,所以这种情况下是while(left <= right)

Third,关于中间值arr[mid]和要查找的值大小相关的细节问题。

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

if第一次查找的值比中间值5大,那下一次的查找区间到底要不要包含5呢?答案是不需要。因为第一次查找的时候包含5了,5不是我们想要的查找的数,为了避免重复查找, left =  mid + 1

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

else第一次查找的值比中间值5小,这次变的是右边,right = mid - 1

Finally,情况一 代码如下:

int binarysearch(int arr[], int data, int size)
{
	int left = 0;
	int right = size - 1;

	while (left <= right)
	{
		int mid = left + ((right - left) >> 1);
		if (data == arr[mid])
			return mid;
		else if (data < arr[mid])
			right = mid - 1;
		else
			left = mid + 1;
	}
	return -1;
}
           

情况二 int left = 0; int right = size;

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

First,开始还是讨论while(),中括号里面的左右比较,这次范围是left < right,因为这次右边的范围是开区间,所以不需要再加上等号,否则在查找的过程中会产生越界的情况。

Secong,中间值的情况和情况一样,都是mid = left + ((right - left) >> 1)

Third,关于中间值arr[mid]和要查找的值大小相关的细节问题。

if查找的值比中间值小,因为右边是开区间,所以这次right = mid

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

而不是right = mid - 1,如下图所示,如果是mid -1,则在下一次查找中就会漏掉4这个值,因为右边是开区间。

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

else查找的值比中间值大,因为左侧是闭区间,所以left = mid + 1

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

Finally,情况二代码如下

int binarysearch(int arr[], int data, int size)
{
	int left = 0;
	int right = size;

	while (left < right)
	{
		int mid = left + ((right - left) >> 1);
		if (data == arr[mid])
			return mid;
		else if (data < arr[mid])
			right = mid;
		else
			left = mid + 1;
	}
	return -1;
}

           

二分查找的复杂度:

总体思路就是每次减半,如图:

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

计算过程:

二分查找画图详述细节问题(边界值问题+中间值溢出+复杂度问题手写的照片)二分查找:二分查找的复杂度:

继续阅读