无处不在的二分搜索
2012-05-14 10:21
113 查看
二分搜索想必学计算机的人都很熟了,它是很多算法里的都会用到的,编程珠玑里有专门的介绍。二分搜索的框架大家都很熟悉,但要完完整整写对也不是那么容易的事情。这很考验一个人的基本功的,这也是面试官常考的一个问题。下面就对其进行一下总结。
1. 常规写法
int BinarySearch(int *a, int l, int r, int z){ if(l > r) return -1; else if(l == r) if(a[l] == z) return l; else return -1; else{ int mid; while(l <= r){ mid = (l + r)/2; if(a[mid] < z) l = mid +1; else if(a[mid] > z) r = mid - 1; else return mid; } return -1; } }
这里需要注意几点:
(1)mid的取法,是取上整(mid = (l + r +1)/2)还是取下整(mid = (l + r )/2)。这里是取下整,这意味着mid总是倾向于偏小的索引;否则,mid
倾向于偏大的索引 。
(2)l 或 r 的更新。当mid倾向于偏小值时(取下整),当查找值比中间值还大时( a[mid] < z ),l 要倾向于偏大值,所以 l = mid + 1,不然就可能出现mid索引值不变导致死循环;相反,当mid倾向于偏大值时(取上整),当查找值比中间值还小时(
a[mid] > z ),r 要倾向于偏小值,所以 r = mid - 1。
现在再说说二分搜索算法的改进。使内层循环更紧凑。这里内层用的是三路比较,现在改进为二路比较。
int BinarySearch(int *a, int l, int r, int z){ if(l > r) return -1; else if(l == r) if(a[l] == z) return l; else return -1; else{ int mid; while(l < r){ mid = (l + r)/2; if(a[mid] < z) l = mid +1; else r = mid; } if(a[l] == z) return l; return -1; } }
同样,这里还是要注意前面提到的那两点。不同的是,(1)如果查找值不比中间值大,即 if(a[mid] < z) 不成立的话,r = mid ,包含了<=两个情况;(2)while循环的条件变成了 l < r ,l == r 变成了终止条件,放到外面;(3)while循环外面的 if 判断,当 l == r 时,a[l] 和 a[r] 是否都指向要查找的值。
二分查找改进版
当数组中有重复元素时,返回满足条件的最右元素或最左元素。我自己写了个java实现版
class BinarySearch { //在大小为n的数组a中查找值为n的元素,返回满足条件的最右元素 public static int binarySearchR(int[] a, int x, int n) { if(n > 0 && x >= a[0]) { int left = 0; int right = n-1; while(left < right) { int middle = (left + right + 1)/2; if(x < a[middle]) right = middle - 1; else left = middle; } if(x == a[left]) return left; } return -1; } //在大小为n的数组a中查找值为n的元素,返回满足条件的最左元素 public static int binarySearchL(int[] a, int x, int n) { if(n > 0 && x >= a[0]) { int left = 0; int right = n-1; while(left < right) { int middle = (left + right)/2; if(x > a[middle]) left = middle + 1; else right = middle; } if(x == a[left]) return left; } return -1; } } public class BinarySearchTest { /** * @param args */ public static void main(String[] args) { int[] a = {1,1,6,6,6,6,6,8,8}; System.out.println(BinarySearch.binarySearchL(a, 1, 9)); System.out.println(BinarySearch.binarySearchR(a, 1, 9)); System.out.println(BinarySearch.binarySearchL(a, 6, 9)); System.out.println(BinarySearch.binarySearchR(a, 6, 9)); System.out.println(BinarySearch.binarySearchL(a, 8, 9)); System.out.println(BinarySearch.binarySearchR(a, 8, 9)); } }
近日看过一篇博问,里面讲到二分查找中容易忽略的一个错误——数组下标上溢。当给middle赋值时,middle = (left + right)/2 或 middle = (left + right + 1)/2,如果数组很大的话,left + right可能上溢。
所以建议在给给middle赋值时,采用 middle = left + ( right - left )/2 或
middle = left + ( right - left + 1)/2。
相关文章推荐
- nyoj914(二分搜索+贪心)
- hdu 5432 Pyramid Split(二分搜索)
- HDU-4033 Fruit Ninja 几何 二分搜索
- POJ 2976 - Dropping tests(二分搜索)
- poj2456 二分搜索
- 一步一步学习java数组学习(二分搜索)
- 二分搜索
- 二分搜索问题详解(java)
- “玲珑杯”线上赛 Round #15 咸鱼商店(二分搜索+01背包)
- 数据结构与算法以及leetcode刷题 012 二分搜索树
- 二分搜索专题1-在非递减数组中寻找满足A[i]=i的i
- 34. Search for a Range-二分搜索
- 二分搜索——在有序数组中找到目标函数出来的最左端的位置
- POJ 2723 Get Luffy Out(图论-2SAT,搜索-二分)
- 特定字符输出及二分搜索 简单面试编程题
- 基本算法连载(1)-顺序搜索与二分搜索
- POJ 1631-Bridging signals(最长上升子序列+二分搜索)
- 二分搜索
- 二分搜索大法
- 2144 砝码称重 2 用map离散化hasi+二分搜索