二分(折半)查找算法 收藏

本文详细分析了折半查找法在处理有序数组中的边界情况时的特殊需求,包括如何找到数组中等于给定元素的最小和最大序号,以及如何处理数组中元素重复的问题。通过具体的代码示例,展示了不同场景下如何优化查找算法以避免无限循环,确保算法的正确性和效率。


折半查找法是对有序数组进行查找的高效算法,本文主要对一些边界情况做一些分析,结合《编程之美》上的讨论,给出实际的代码。

基本的折半查找的代码如下:(假设数组有序且没有重复元素)

view plaincopy to clipboardprint?
int bin_search(char *s,char v,int n)
{
int max = n-1,min = 0,mid = 0;
while(min<=max)
{
mid = min+(max-min)/2;//防止加法溢出
if(s[mid]<v) min = mid + 1;
else if(s[mid]>v) max = mid - 1;
else return mid;
}
return -1;
}
int bin_search(char *s,char v,int n)
{
int max = n-1,min = 0,mid = 0;
while(min<=max)
{
mid = min+(max-min)/2;//防止加法溢出
if(s[mid]<v) min = mid + 1;
else if(s[mid]>v) max = mid - 1;
else return mid;
}
return -1;
}

但是当有序数组中有重复元素时,就需要考虑特殊情况:

1,找到数组中等于v的元素的最大序号。实现如下:

对于退出条件:min<max && min+1 != max,而不是上述的min<=max,是为了保证循环的正常退出。

因为上述中每次循环min或者max都会改变,能够保证循环的正常退出,而这里当s[mid]<=v时,min的值不变,可能出现无限循环的情况。

例如,对于数组a[]={2},寻找2最后一次次出现情况时,第一次循环,min=0,min=0,max=0,满足s[mid]<=v条件,此时min=mid=max=0,如果循环退出条件仍然是min<=max,则会出现无限循环。

view plaincopy to clipboardprint?
int bin_search1(char *s,char v,int n)
14 {
15 int min = 0,max = n-1,mid = 0;
16 while(min < max && min+1 != max)
17 {
18 mid = min+(max-min)/2;//防止加法溢出
19 if(s[mid] <= v)
20 {
21 min = mid;
22 }
23 else
24 {
25 max = mid - 1;
26 }
27 }
28 if(s[max] == v)
29 {
30 return max;
31 }
32 else if(s[min] == v)
33 {
34 return min;
35 }
36 else if(min + 1 == max)
37 {
38 return -1;
39 }
40 }
int bin_search1(char *s,char v,int n)
14 {
15 int min = 0,max = n-1,mid = 0;
16 while(min < max && min+1 != max)
17 {
18 mid = min+(max-min)/2;//防止加法溢出
19 if(s[mid] <= v)
20 {
21 min = mid;
22 }
23 else
24 {
25 max = mid - 1;
26 }
27 }
28 if(s[max] == v)
29 {
30 return max;
31 }
32 else if(s[min] == v)
33 {
34 return min;
35 }
36 else if(min + 1 == max)
37 {
38 return -1;
39 }
40 }

2,找到数组中等于v的元素的最小序号。实现如下:

view plaincopy to clipboardprint?
int bin_search2(char *s,char v,int n)
42 {
43 int min = 0,max = n-1,mid = 0;
44 while(min < max && min+1 != max)
45 {
46 mid = min + (max - min)/2;
47 if(s[mid] >= v)
48 {
49 max = mid;
50 }
51 else
52 {
53 min = mid + 1;
54 }
55 }
56 if(s[min] == v)
57 {
58 return min;
59 }
60 else if(s[max] == v)
61 {
62 return max;
63 }
64 else if(min + 1 == max)
65 {
66 return -1;
67 }
68 }
int bin_search2(char *s,char v,int n)
42 {
43 int min = 0,max = n-1,mid = 0;
44 while(min < max && min+1 != max)
45 {
46 mid = min + (max - min)/2;
47 if(s[mid] >= v)
48 {
49 max = mid;
50 }
51 else
52 {
53 min = mid + 1;
54 }
55 }
56 if(s[min] == v)
57 {
58 return min;
59 }
60 else if(s[max] == v)
61 {
62 return max;
63 }
64 else if(min + 1 == max)
65 {
66 return -1;
67 }
68 }

3,找到数组中小于v的元素的最大序号。实现如下:首先根据1,找到等于v的最小序号,然后-1(如果合法)。

4,找到数组中大于v的元素的最小序号。实现如下:首先根据2,找到等于v的最大序号,然后+1(如果合法)。

在这几种情况中,关键点是“结束循环的边界条件”。

想到的测试例子如下:

s="abbcdef" 奇数个

s="abbcdf"偶数个

s="aaaaaaa" 奇数个

s="a"一个

s="aaaaaa"偶数个

s="abbbbb"

s="aaaab"

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值