再探二分搜索

来源:互联网 发布:linux远程访问数据库 编辑:程序博客网 时间:2024/05/22 01:27

1基本的二分搜索, 在没有重复元素的数组中查找一个元素

#include <stdio.h>int binsearch(int a[], int t, int l, int h){int p;while (1){if (l > h){p = -1;break;}int m = (l + h) / 2;if (a[m] < t)l = m + 1;else if (a[m] > t)h = m - 1;else {p = m;break;}}return p;}int main(){int a[] = {0, 1, 2, 3, 4, 5, 7};printf("%d\n", binsearch(a, 2, 0, 6));printf("%d\n", binsearch(a, 8, 0, 6));}
关键有以下几点:

1)循环的不变性,查找元素必须在[l, h]之间,包括l和h,而且l <= h,这个条件是进入循环就需要一直成立的

2)第一个if条件中,由于不满足第二个循环不变性,退出循环;

 第二个if条件中,a[m] < t,则t 位于 [m+1(l), h]之间,满足不变性,继续循环,第三个同理;

最后一个找到目标,退出循环

3)l 和h的范围是 l [0, n]:  0 表示l的下界, h 是 [-1, n), n-1是h的上界;而l的n和h的-1,则表示,当n =0 时,l取得0, h取得-1, 由于不满足循环的不变性,在第一个if中退出循环, l + h的范围是 [-1, 2n - 1]



2有重复元素的数组中查找重复元素的最小下标

#include <stdio.h>int bs_duplicate(int a[], int n, int t, int l, int u){while (l + 1 != u){int m = (l + u) / 2;if (a[m] < t)l = m;elseu = m;}int p = u;if (p >= n || a[p] != t)p = -1;return p;}int main(){int a[10] = {1,1,3,4,5,5};printf("%d", bs_duplicate(a, 6, 5, -1, 6));}
1)循环的不变性 : t 属于 (l, u]中,且l < u, 这样的不变性是因为,我们需要找到仅仅有两个元素的时候,判断前一个元素比t小,后一个元素=t或者 >t, 进而找到第一次出现t的位置,否则,若第一个不变性与常规的二分查找相同,则有可能 l = t = u, 第二个不变性是要保证数组中至少有一个元素,之所以不是l = u表示数组中至少有一个元素,是因为l此时表示的不是查找范围内的下标

2)可以看到进入循环的条件比单独的l < u这一循环不变性条件更强,目的是为了定位到最终的唯一一个元素中,因此, 可以说进入循环必然满足循环不变性,而循环条件应该是某个循环不变性的子集,对于常规的二分搜索来说,循环条件与某一循环不变性相同

条件中的 循环不变性,进入循环的条件是至少有一个元素, 之后,在第一个条件中, 由于a[m] < t, 则, l = m, 满足循环不变性,而第二个条件同理

退出循环后,仍然满足循环不变性, 但是不满足循环条件,循环条件和循环不变性不同, 此时可知我们定位到唯一的一个元素啊a[u]上,由于仍然满足循环不变性, 则有 t > a[l], t <= a[ l + 1],  此时两个相邻元素a[l] 和a[l + 1] 就是我们想找到第一次出现t的方法(就是找到比t小的最大的元素), 那么此时又两种情况, 或者下一个找到比t小的之后的元素= t, 或者,之后的元素 > t,就是数组中没有t

3)l的范围是[-1, n), u的范围是[0, n]。 l + h 的范围是 [ -1, 2n - 1]。 l 的范围表示中 -1表明,因为t可能是a[0], 所以比a[0]小的下标就是-1; u的范围中, n 表示, 若t= a[n-1],则比a[n-1]大的下标就是n, 之所以有n,是因为数组中有重复元素, 所以会有 t <= a[n], 而不是常规的t < a[n].而此处,-1和n都是假想的元素下标,目的是为了与数组中元素的循环不变性相一致。当n =0 时, l 取得 -1, u取得 0, 可以看到它将由于不满足进入循环的条件而直接进入判断。

4)可以看到,以上两个二分查找的l + h的范围相同, 也就是说,求得的m的范围相同,而且[0, n - 1],  仍然满足l和h的范围。




0 0
原创粉丝点击