classbinary{ publicintbinarySearch(int[] nums,int key){ int l =0,h = nums.length - 1; while(l<=h){ int m = l + (h - l) / 2; if(nums[m] == key){ return m; }elseif(nums[m] < key){ l = m + 1; }else{ h = m - 1; } } return -1; } }
时间复杂度
二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度为 O(logN)。
计算m
有两种计算中值 m 的方式:
m = (l + h) / 2
m = l + (h - l) / 2
l + h 可能出现加法溢出,也就是说加法的结果大于整型能够表示的范围。但是 l 和 h 都为正数,因此 h - l 不会出现加法溢出问题。所以,最好使用第二种计算法方法。
publicintbinarySearch2(int[] nums, int key){ int l =0,h = nums.length - 1; while(l<h){ int m = l + (h - l) / 2; if(nums[m] >= key){ h = m; }else{ l = m + 1; } } return l; }
该实现和正常实现有以下不同:
h 的赋值表达式为 h = m
循环条件为 l < h
最后返回 l 而不是 -1
在 nums[m] >= key 的情况下,可以推导出最左 key 位于 [l, m] 区间中,这是一个闭区间。h 的赋值表达式为 h = m,因为 m 位置也可能是解。
在 h 的赋值表达式为 h = m 的情况下,如果循环条件为 l <= h,那么会出现循环无法退出的情况,因此循环条件只能是 l < h。以下演示了循环条件为 l <= h 时循环无法退出的情况:
classSolution{ publicintmySqrt(int x){ if (x <= 1) { return x; } int l = 1, h = x; while (l <= h) { int m = l + (h - l) / 2; int sqrt = x / m; if (sqrt == m) { return m; } elseif (sqrt < m) { h = m - 1; } else { l = m + 1; } } return h; } }
classSolution{ publiccharnextGreatestLetter(char[] letters, char target){ int n = letters.length; int l = 0, h = n - 1; while (l <= h) { int m = l + (h - l) / 2; if (letters[m] <= target) { l = m + 1; } else { h = m - 1; } } return l < n ? letters[l] : letters[0]; } }
classSolution{ publicintsingleNonDuplicate(int[] nums){ int n = nums.length; int l = 0, h = n - 1; while (l < h) { int m = l + (h - l) / 2; if (m % 2 == 1) { m = m - 1; } if (nums[m] == nums[m + 1]) { l = m + 2; } else { h = m; } } return nums[l]; } }
publicclassSolutionextendsVersionControl{ publicintfirstBadVersion(int n){ int l = 1, h = n; while (l < h) { int m = l + (h - l) / 2; if (isBadVersion(m)) { h = m; } else { l = m + 1; } } return l; } }
classSolution{ publicintfindMin(int[] nums){ int l = 0, h = nums.length - 1; while (l < h) { int m = l + (h - l) / 2; if (nums[m] < nums[h]) { h = m; } else { l = m + 1; } } return nums[l]; } }
classSolution{ publicint[] searchRange(int[] nums, int target) { int first = findFirst(nums, target); int last = findFirst(nums, target + 1) - 1; if (first == nums.length || nums[first] != target) { returnnewint[]{-1, -1}; } else { returnnewint[]{first, Math.max(first, last)}; } }
privateintfindFirst(int[] nums, int target){ int l = 0, h = nums.length; while (l < h) { int m = l + (h - l) / 2; if (nums[m] >= target) { h = m; } else { l = m + 1; } } return l; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution{ publicint[] searchRange(int[] nums, int target) { int left=0,right=nums.length-1; while (left<=right){ int mid = left + (right-left)/2; if(target>nums[mid]){ left=mid+1; }elseif(target<nums[mid]){ right=mid-1; }else {//target==nums[mid] while (nums[left]!=target) left++; while (nums[right]!=target) right--; returnnewint[]{left,right}; } } returnnewint[]{-1,-1}; } }
classSolution{ publicintminEatingSpeed(int[] piles, int H){ int l = 1, h = Arrays.stream(piles).max().getAsInt(); while (l < h) { int m = l + (h - l) / 2; if (canEat(piles, m, H)) { h = m; } else { l = m + 1; }
} return l; }
privatebooleancanEat(int[] piles, int speed, int H){ int sum = 0; for (int pile : piles) { if (pile % speed > 0) { sum += pile / speed + 1; } else { sum += pile / speed; } } return sum <= H; } }
寻找旋转排序数组中的最小值Ⅱ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution{ publicintfindMin(int[] nums){ int l = 0, h = nums.length - 1; while (l < h) { int m = l + (h - l) / 2; if (nums[m] > nums[h]) { l = m + 1; } elseif (nums[m] < nums[h]) { h = m; } else {//key position h--; } } return nums[l];
classSolution{ publicintfindRadius(int[] houses, int[] heaters){ int res = 0; int n = heaters.length; Arrays.sort(heaters); for (int house : houses) { int l = 0, h = n - 1; while (l < h) { int m = l + (h - l) / 2; if (heaters[m] < house) { l = m + 1; } else { h = m; } } int dist1 = h == n ? Integer.MAX_VALUE : Math.abs(house - heaters[h]); int dist2 = h == 0 ? Integer.MAX_VALUE : Math.abs(house - heaters[h - 1]); res = Math.max(res, Math.min(dist1, dist2)); } return res; } }