您的位置:首页 > 其它

折半查找的多种写法

2016-05-12 13:48 459 查看
首先我们来看一下折半查找正确的代码:
int bin_search(int arr[], int sz, int num)
{
int left = 0;
int right = sz - 1; //注意点一(传入数组的元素个数-1之后可以得到下标的最大值)
int mid = 0;
while (left<=right) //注意点二
{
mid = (right - left) / 2 + left; //注意点三
if (arr[mid]<num)
{
left = mid + 1;
}
else if (arr[mid]>num)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
上面这是一个基本的折半查找函数,但是我们必须要这样写么,答案当然是否定的,写法可以有很多种,但是不同的写法都有需要注意的地方,下面我们来分情况来讨论下:

其实这里写法多的原因是righ初始值不同的影响。
假设我们就直接使用了传进来的数组的个数,即我们right的初始值始为数组元素下标的最大值再加1,那么需要怎么修改代码呢?
首先是while循环条件需不需要改呢,一般来说,折半查找的条件都是left<=right,因为如果改成left<right,那么你会发现你要查找的元素有时候可以查到,有时候不行,结果取决于你要查找的数字,如果你要查找的数字(如图所示的情况)。



图一



[align=left] 图二[/align]



[align=left] 图三[/align]
[align=left]这两种情况就要特别注意了如果你要找的数字都最后归结到这三种情况,先来看情况一:如果你要找的数字正好位于你的left和right之间,那么如果你不写等号,(写成left<right结果也可以找到这个元素),但是如果你要找的数字最后可以归结为图二和图三的那两种情况,那么你就会发现查找不到你想要的数字。因为再作一次循环就会发现left与right循环会出现相等的情况,此时while循环又进不去,所以你会发现你要找的数字“丢了”。[/align]
[align=left]所以综上所述,while循环中的条件还是写成left<=right比较好。[/align]

[align=left]但是如果你要说写成left<right,改下面的条件,能不能是结果成立呢?那我们就来试一试:[/align]
[align=left]稍微思考一下就会发现,刚刚元素找不到是因为你left不能与right相等,而你要查找的元素有正好在left或者right所指向的内容上,那么是不是可以每次让right的值不变成mid-1,而是每次让他变成mid,这样刚刚的元素似乎不会“丢掉了”,这样想似乎有道理,那么我们来实现一下,看看这样写是不是有问题。[/align]

[align=left]int bin_search(int arr[], int sz, int num)[/align]
[align=left]{[/align]
[align=left] int left = 0;[/align]
[align=left] int right = sz;[/align]
[align=left] int mid = 0;[/align]
[align=left] while (left<right)[/align]
[align=left] {[/align]
[align=left] mid = (right - left) / 2 + left;[/align]
[align=left] if (arr[mid]<num)[/align]
[align=left] {[/align]
[align=left] left = mid + 1;[/align]
[align=left] }[/align]
[align=left] else if (arr[mid]>num)[/align]
[align=left] {[/align]
[align=left] right = mid ;[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
[align=left] return mid;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] return -1;[/align]
[align=left]}[/align]

[align=left]如果我们把代码改成上述所示,测试起来似乎也没什么问题,只是逻辑条理没有之前最初的版本清晰。[/align]

那么我们再来看看righ如果初始化为sz - 1,那么又会有什么另外的写法呢?最初的版本就是用sz-1写的,那种当然可以查找,所以不做讨论了,那么我们看看改变其他条件行不行,首先看看while的条件,还是应写成left<=right,原因上面已经讲到过了,不这样写,有些元素会找不到。但是上面的解决方法可不可以用到下面来呢?
我们如果写成下面的形式可不可以呢?
[align=left]int bin_search(int arr[], int sz, int num)[/align]
[align=left]{[/align]
[align=left] int left = 0;[/align]
[align=left] int right = sz - 1;[/align]
[align=left] int mid = 0;[/align]
[align=left] while (left<right)[/align]
[align=left] {[/align]
[align=left] mid = (right - left) / 2 + left;[/align]
[align=left] if (arr[mid]<num)[/align]
[align=left] {[/align]
[align=left] left = mid + 1;[/align]
[align=left] }[/align]
[align=left] else if (arr[mid]>num)[/align]
[align=left] {[/align]
[align=left] right = mid ;[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
[align=left] return mid;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] return -1;[/align]
[align=left]}[/align]

[align=left]这次在测试的过程中似乎没什么问题,但是,如果你测试最后一个元素,你就会发现最后一个元素找不到了, 原因就是left最后一次要与right相等,但是循环又进不去,该元素就丢了,但是之前的为什么可以,之前的right是最后的下标+1得到的,即使得不到这个下标也没事,那个下标上没有对应的数字,但是这个就不一样了,这个有对应的数字。[/align]

最后总结一下就是:while的循环条件最好写成left<=right,这样逻辑也比较好理解,right的初始值可以是元素的个数,也可以是元素的下标的最大值,具体写成什么就看你了,不过如果写的不同,那么下面的代码也会有所不同。还有一点就是mid的求取,一般来说平时我们在测试过程中写成(left + right)/2 也没什么问题,但是如果left与right的下标都比较大了之后可能会发生溢出状况,这样就不太好了。所以还是写成(right
- left)/2 + left的形式比较好一点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: