二分模板
普通二分查找模板
public int search(int[] nums, int target) {int left = 0;int right = nums.length - 1;while (left <= right) {int mid = (left + right) / 2;if (nums[mid] == target) return mid;else if (nums[mid] < target) left = mid +1;else right = mid-1;}return -1;}
lower_bound
查找lower_bound(第一个>=target的数),不存在返回nums.length
public int search02(int[] nums, int target) {int left = 0;int right = nums.length;while (left < right) {int mid = (left + right) / 2;if (nums[mid] >= target) {right = mid;} else {left = mid + 1;}}return right;}
另外一种写法
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 n;}
upper_bound
查找最后一个<=target的数,不存在返回-1
// 查找最后一个小于等于target的元素public int search03(int[] nums, int target) {int left = -1;int right = nums.length - 1;while (left < right) {int mid = (left + right + 1) / 2;if (nums[mid] <= target) {left = mid;} else {right = mid - 1;}}return right;}
模板二
// 二分变体4:查找最后一个小于等于给定值的元素public int bsearch7(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;}
实战
153. 寻找旋转排序数组中的最小值

二分条件,数组整体不是递增的,可以分为递增的二分条件,参考lower_bound
public int findMin(int[] nums) {int left = 0;int right = nums.length - 1;// 查找第一个小于等于right的数字while (left < right) {int mid = (left + right) / 2;if (nums[mid] <= nums[right]) {right = mid; // 答案是mid或者在mid左边} else {left = mid + 1; // 答案在mid右边}}return nums[right];}
34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。
public int[] searchRange(int[] nums, int target) {// 第一个比target - 1大的下标也就是第一个target的下标int left = search(nums, target - 1);// 第一个比target大的下标-1,也就是最后一个target的下标int right = search(nums, target) - 1;if (left <= right && nums[left] == target) {return new int[]{left, right};}return new int[]{-1, -1};}// 查找第一个比target大的下标private int search(int[] nums, int target) {int left = 0;int right = nums.length;while (left < right) {int mid = (left + right) / 2;if (nums[mid] > target) {right = mid; // 答案是right或者在right左边} else {left = mid + 1; // 答案在mid右边}}// 如果找不到比target大的下标,说明数组元素都比target小,将nums.length返回return right;}
69. x 的平方根
public int mySqrt(int x) {int left = 1;int right = x;while (left < right) {int mid = (left + right + 1) / 2;if (mid * mid <= x) {// 答案是mid或者mid的右边left = mid;} else {right = mid - 1;}}return right;}
三分查找
162. 寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。 给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
public int findPeakElement(int[] nums) {int left = 0;int right = nums.length - 1;while (left < right) {int lmid = (left + right) / 2;int rmid = lmid + 1;if (nums[lmid] <= nums[rmid]) { // 峰值一定在lmid的右边left = lmid + 1;} else { // 峰值一定在rmid的左边right = rmid - 1;}}// 这里返回left或者right都行,循环的终止条件是left == rightreturn right;}
二分答案
410. 分割数组的最大值
给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。 设计一个算法使得这 m 个子数组各自和的最大值最小。
class Solution {public int splitArray(int[] nums, int m) {int left = 0;int right = 0;for (int num : nums) {left = Math.max(left, num);right += num;}while (left < right) {int mid = left + (right - left) / 2;if (validate(nums, m, mid)) { // 满足条件,size为mid能满足分成m段right = mid;} else {left = mid +1;}}return right;}// 对于一个数字size,判断是否数字每段和不超过size的情况下,能分成m段private boolean validate(int[] nums, int m, int size) {int box = 0;int count = 1;for (int num : nums) {if (box + num <= size) { // 这一段还可以继续放box += num;} else { // 需要开启新的一段count++;box = num;}}return count <= m;}}
实战
剑指 Offer II 068. 查找插入位置
给定一个排序的整数数组 nums 和一个整数目标值 target ,请在数组中找到 target ,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
- 查找第一个大于target的元素


