二分查找的前提
网络异常,图片无法展示
|
使用递归实现二分查找
public static <E extends Comparable<E>> int search(E[] arr, E target) { return search(arr, 0, arr.length - 1, target); } private static <E extends Comparable<E>> int search(E[] arr, int l, int r, E target) { if(l > r) { return -1; } int mid = l + (r - l) / 2; if(arr[mid].compareTo(target) == 0) { // 刚好找到中间的位置 return mid; }else if (arr[mid].compareTo(target) > 0) { // 查找mid前面的元素 return search(arr, l, mid - 1, target); }else { //(arr[mid].compareTo(target) < 0) return search(arr, mid + 1, r, target); } }
使用非递归实现二分查找
private static <E extends Comparable<E>> int search1(E[] arr, E target) { int l = 0, r = arr.length - 1; while(l <= r) { // 数组中存在元素时 int mid = l + ( r - l) / 2; if(arr[mid].compareTo(target) == 0) { // 刚好中间的元素就是 return mid; } if(arr[mid].compareTo(target) > 0) { // 中间的元素小于target,接着查找右边的列表 r = mid - 1; }else { // 中间的元素大于target, 接着查找左边的列表 l = mid + 1; } } return -1; }
二分查找法的变种
查找大于target的最小值
- 当未查找到这样的元素,那么将返回数组长度。
- 当中间值大于等于目标值,那么查询返回下一边界需要加上上一次的中间值,所以
r = mid
。
网络异常,图片无法展示
|
/** * 查找大于target的最小值 * @param arr * @param target */ public static <E extends Comparable<E>> int upper(E[] arr, E target) { int l = 0, r = arr.length; while(l < r) { int mid = l + (r - l) / 2; if(arr[mid].compareTo(target) == 0) { return mid + 1; }else if(arr[mid].compareTo(target) > 0) { // 这个r = mid是因为mid的位置可能是目标值 r = mid; }else { l = mid + 1; } } // l和r最后都都指向同一个位置。没找到则返回arr.length return l; }
查找目标元素最大的下标元素 ceil
- 如果有等于target的元素就返回最大的下标元素。
- 如果没有等于target的元素,那么就返回大于target的最小元素。即上面实现的upper函数。
网络异常,图片无法展示
|
public static <E extends Comparable<E>> int ceil(E[] arr, E target) { int index = upper(arr, target); if(arr[index - 1].compareTo(target) == 0) { return index - 1; } return index; }
查找目标元素最小的小标元素 lower_ceil
- 如果有等于target的元素就返回最小的下标元素。
- 如果没有等于target的元素,那么就返回大于target的最小元素。即上面实现的upper函数。
网络异常,图片无法展示
|
查找小于target的最大值
网络异常,图片无法展示
|
按照上面的逻辑写成的程序会出现死循环
网络异常,图片无法展示
|
网络异常,图片无法展示
|
LeetCode相关习题
LeetCode 875
网络异常,图片无法展示
|
/** * 使用二分法,解决leetCode875 * @param piles * @return */ public static int leetCode875(int[] piles, int h) { // 假设他先吃二分法中间的那个根数 int l = 1; // 最少吃一根 int r = Arrays.stream(piles).max().getAsInt(); // 假设吃数组中最大数量的根数 while(l < r) { int mid = l + (r - l) / 2; if(eatingTime(piles, mid) <= h) { // 此时mid可能会目标值 r = mid; }else { l = mid + 1; } } return l; } public static int eatingTime(int[] arr, int k) { int res = 0; for(int pile: arr) { res = res + pile / k + (pile % k > 0 ? 1 : 0); } return res; }
LeetCode 1011
网络异常,图片无法展示
|