<<编程珠玑>>看到一篇关于二分查找算法的分析, 觉得说得特别好,以前数据结构也学过这个算法,下面就把书上的大致意思加上自己的一些研究和看法做个总结.
二分查找的算法可以分为递归与非递归算法.下面的这两种算法的实现形式.
- //递归算法
- int BinarySearch(int l, int u)
- {
- int m = 0;
- if(l > u)
- return -1;
- m = (l + u) / 2;
- if(key == array[m])
- return m;
- else if(key < array[m])
- {
- u = m - 1;
- BinarySearch(l, u);
- }
- else if(key > array[m])
- {
- l = m + 1;
- BinarySearch(l, u);
- }
- }
- //非递归算法
- int BinarySearch(int l, int u)
- {
- int m = 0;
- while(l <= u)
- {
- m = (l + u) / 2;
- if(key == array[m])
- return m;
- else if(key < array[m])
- u = m - 1;
- else
- l = m + 1;
- }
- return (-1);
- }
递归的算法比较容易理解,但执行速度稍慢,我在vc环境下profile过,性能确实比非递归稍差.
<<编程珠玑>>上说90%的程序员写不出无bug的二分查找程序,作者主要基于测试用例来讲的,意思是说测试的情况有很多,要考虑到很多情况. 作者是专门研究算法的,在我看来是有点”钻牛角尖”, 但是如果在实际开发中,应该由此想到代码的健壮性.尤其是嵌入式领域, 一个数据溢出错误就可能导致一台医疗仪器害死一个病人,或者航天飞机上不了天.
就拿上面的例子说明代码的健壮性,
- m = (l+u)/2
这一行代码就有问题, 如果l+u溢出怎么办(l, u两个数假定都不会溢出) ? 网上看到有人说把l和u声明为无符号数,这显然不行, 因为无符号数也会溢出, 尽管溢出的标志位与有符号位不同. 不管溢出的结果是不是不可预知的,对于二分查找,有无符号数计论下去都是没意义的. 也看到另外一种方法就是
- m = l + ((u-l)/2).
这的确是一个不错的方法. 在l和u保证不溢出的情况下, m肯定不会溢出的. 另外如果把(u-l)/2 改成(u-l)>>1 就更好了, 因为了u-l肯定是大于等于零的, 右移和除法的效果是一样的(如果是负数的话,除法和右移有一点区别,这里不说明).
还有就是如果用了计算机所能支持的最大的数据长度还是会有溢出的可能,就干脆加assert, 毕竟一个明确的错误的提示也是代码健壮性的体现.