1. 二分法查找算法,判断一个数是否在一个有序数组中存在,最快的方法就是二分法,时间复杂度为O(Logn)
2. 利用二分思想,每次与中间数值比较,缩小查找范围。
3. 写好二分查找需要注意,循环条件是low <= high; mid取值最好不用mid=(low+high)/2, 而是用low + ((high-low) >>1),位运算效率高,且避免溢出。
应用:
a. 查找第一个(最后一个)值等于给定值的元素,数组可能存在重复的元素。对于我们做工程的人来说,代码易懂,bug free,其实最重要。
public int bsearch(int[] a, int n, int value) { int low = 0; int high = n - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (a[mid] > value) { high = mid - 1; } else if (a[mid] < value) { low = mid + 1; } else { if ((mid == 0) || (a[mid - 1] != value)) return mid; // 第一个
// if ((mid == n - 1) || (a[mid + 1] != value)) return mid; //最后一个 else high = mid - 1; } } return -1; }
b. 查找第一个大于等于给定值的元素 / 查找最后一个小于等于给定值的元素
public int bsearch(int[] a, int n, int value) { int low = 0; int high = n - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (a[mid] >= value) { if ((mid == 0) || (a[mid - 1] < value)) return mid; else high = mid - 1; } else { low = mid + 1; } } return -1; }
public int bsearch(int[] a, int n, int value) { int low = 0; int high = n - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (a[mid] > value) { high = mid - 1; } else { if ((mid == n - 1) || (a[mid + 1] > value)) return mid; else low = mid + 1; } } return -1; }
c.根据一个IP地址查询归属地,ip地址是可以转换为一个数值的, 把原来的区间起始地址构造成一个有序数组,然后对应查找最后一个最后一个小于等于给定值的元素,找到后在到对应区间去看是否在区间内,在则显示,不在就显示未找到。
d.求平方根
class Solution { public int mySqrt(int x) { int l = 0, r = x, ans = -1; while (l <= r) { int mid = l + (r - l) / 2; if ((long)mid * mid <= x) { ans = mid; l = mid + 1; } else { r = mid - 1; } } return ans; } }
e.二分查找的优势在于近似查找,存在重复元素,这是散列表和二叉查找树没法实现的。