推荐使用闭区间的方式去做二分查找的题目
如果数量比较少,那么建议使用顺序遍历的方式
因此二分结束时一定有: i指向首个大于 target 的元素,j指向首个小于 target 的元素。易得当数组不包含 target 时,插入索引为
162. 寻找峰值
寻找最大值,这个也可以理解,嗯
大的一侧为什么一定有峰值?注意题目条件,在题目描述中出现了 nums[-1] = nums[n] = -∞,这就代表着 只要数组中存在一个元素比相邻元素大,那么沿着它一定可以找到一个峰值
沿着大的方向走,肯定会存在一个峰值
class Solution { public int findPeakElement(int[] nums) { int l = 0, r = nums.length - 1; while (l < r) { int mid = l + ((r - l) >> 1); if (nums[mid] < nums[mid + 1]) { l = mid + 1; } else { r = mid; } } return l; } }
33. 搜索旋转排序数组
我们将数组从中间分开成左右两部分的时候,一定有一部分的数组是有序的
当前 mid 为分割位置分割出来的两个部分 [l, mid] 和 [mid + 1, r] 哪个部分是有序的,并根据有序的那个部分确定我们该如何改变二分查找的上下界,因为我们能够根据有序的那部分判断出 target 在不在这个部分:
- 如果 [l, mid - 1] 是有序数组,且 target 的大小满足 [nums[l],nums[mid]),则我们应该将搜索范围缩小至 [l, mid - 1],否则在 [mid + 1, r] 中寻找。
- 如果 [mid, r] 是有序数组,且 target 的大小满足 [nums[mid+1],nums[r]],则我们应该将搜索范围缩小至 [mid + 1, r],否则在 [l, mid - 1] 中寻找。
public int search(int[] nums, int target) { int lo = 0, hi = nums.length - 1, mid = 0; while (lo <= hi) { mid = lo + (hi - lo) / 2; if (nums[mid] == target) { return mid; } if (nums[mid] >= nums[lo]) { // left到mid是有序数据 if (target >= nums[lo] && target < nums[mid]) { hi = mid - 1; } else { lo = mid + 1; } } else { // mid --> right是有序数组 if (target > nums[mid] && target <= nums[hi]) { lo = mid + 1; } else { hi = mid - 1; } } } return -1; }
34. 在排序数组中查找元素的第一个和最后一个位置
ACE了
class Solution { public int[] searchRange(int[] nums, int target) { return new int[] { left(nums, target), right(nums, target) }; } public int left(int[] nums, int target) { int i = binarySearch(nums, target); if (i == nums.length || nums[i] != target) { return -1; } return i; } public int right(int[] nums, int target) { int i = binarySearch(nums, target + 1); int j = i - 1; if (j == -1 || nums[j] != target) { return -1; } return j; } public int binarySearch(int[] nums, int target) { int left = 0; int right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else { right = mid - 1; } } return left; } }
153. 寻找旋转排序数组中的最小值
解题总结:
1、边界条件判断
2、如果单调的,直接返回第一个元素即可
3、动态调整small,当前最small为nums[0]
4、如果大于=small,那么缩小左边的边界
5、如果小于small,那么缩小右边的边界,然后更新small的值
6、最后循环结束的时候,l就指向第一个大于=small的元素。
class Solution { public int findMin(int[] nums) { int l = 0; int r = nums.length - 1; if (nums.length == 1) { return nums[0]; } if (nums[0] < nums[nums.length - 1]) { return nums[0]; } int small = nums[0]; while(l <= r) { int m = (l + r) / 2; if (nums[m] >= small) { l = m + 1; } else { r = m - 1; small = nums[m]; } } return nums[l]; } }