二分
int l = 0, r = nums.length - 1; while (l <= r) { int mid = (r - l) / 2 + l; if (nums[l] <= nums[mid]) { //ans = mid; r = mid - 1; } else { l = mid + 1; } } //这是我习惯的二分写法,求的是左边界。这里的结果是 l (ans)。因为结束时是区间[l, r]。l = r + 1。
二分的种类也可以有很多,一般要求时间复杂度带有logn的话,除了排序就要想到二分。然后找哪里是有序的,再考虑能不能用上二分。
譬如nlogn复杂度的最长上升子序列。
将数组分成三个子数组的方案数
前缀和和二分的思想,第一个二分是找左边界,第二个是找右边界
class Solution { public int waysToSplit(int[] nums) { int[] pre = new int[nums.length]; pre[0] = nums[0]; for (int i = 1; i < nums.length; i++) { pre[i] = pre[i - 1] + nums[i]; } int res = 0, n = pre.length; for (int i = 0; i < n - 2; i++) { if (3 * pre[i] > pre[n - 1]) break; int l = i + 1, r = n - 2, j = 0; while (l < r) { int mid = (r - l) / 2 + l; if (pre[mid] - pre[i] >= pre[i]) { r = mid; } else { l = mid + 1; } } j = r; if (pre[j] - pre[i] > pre[n - 1] - pre[j]) continue; int ll = j, rr = n - 2, k = 0; while (ll < rr) { int mid = (rr - ll) / 2 + ll; if (pre[n - 1] - pre[mid] >= pre[mid] - pre[i]) { ll = mid; } else { rr = mid - 1; } } k = ll; res += k - j + 1; res %= 1000000007; } return res; } }
寻找两个正序数组的中位数
十分阴间的好题,可以升化为第K大的数。因为第一个数组可以一个也不取,所以这里特别的从-1开始,nums1[-1] = Integer.MIN_VALUE;
class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { if (nums1.length > nums2.length) { int[] temp = nums1; nums1 = nums2; nums2 = temp; } int m = nums1.length; int n = nums2.length; int len = (m + n + 1) / 2; int l = -1, r = m - 1, index = 0; //定义一个[-1]极小哨兵 while (l <= r) { int mid = (r - l) / 2 + l; int nums1L = mid == -1 ? Integer.MIN_VALUE : nums1[mid]; int nums2R = len - mid - 1 == n ? Integer.MAX_VALUE : nums2[len - mid - 1]; // num1[mid] <= num2[len- mid - 1] // num1[mid+1] >= num2[len - mid - 2]; if (nums1L > nums2R) { r = mid - 1; } else { index = mid; l = mid + 1; } } int nums1L = index == -1 ? Integer.MIN_VALUE : nums1[index]; int nums1R = index + 1 == m ? Integer.MAX_VALUE : nums1[index + 1]; int nums2L = len - index - 2 == -1 ? Integer.MIN_VALUE : nums2[len - index - 2]; int nums2R = len - index - 1 == n ? Integer.MAX_VALUE : nums2[len - index - 1]; if ((m + n) % 2 == 1) { return Math.max(nums1L, nums2L); } else return (double)(Math.max(nums1L, nums2L) + Math.min(nums1R, nums2R)) / 2; } }
水位上升的泳池中游泳
DFS和二分的思路,因为是按顺序查找,故而想到DFS。
class Solution { public int swimInWater(int[][] grid) { int max = 0; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { max = Math.max(max, grid[i][j]); } } int l = 0, r = max; while (l <= r) { int mid = (r - l) / 2 + l; boolean [][]mark = new boolean[grid.length][grid[0].length]; dfs(mid, 0, 0, grid, mark); if (mark[grid.length - 1][grid[0].length - 1]) { // ans = mid; r = mid - 1; } else { l = mid + 1; } } return l; } private void dfs(int x, int i, int j, int[][] grid, boolean [][] mark) { if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length) return; if (grid[i][j] > x || mark[i][j]) return; mark[i][j] = true; dfs(x, i + 1, j, grid, mark); dfs(x, i - 1, j, grid, mark); dfs(x, i, j + 1, grid, mark); dfs(x, i, j - 1, grid, mark); } }
搜索旋转排序数组 II
分情况的二分,根据拐点的位置不同进行不同的二分。
class Solution { public boolean search(int[] nums, int target) { int l = 0, r = nums.length - 1; while (l <= r) { int mid = (l + r) / 2; if (nums[mid] == target) return true; if (nums[mid] > nums[l]) {//拐点在右边 if (nums[l] <= target && target < nums[mid]) { r = mid - 1; } else { l = mid + 1; } } else if (nums[mid] < nums[l]) {//拐点在左边 if (nums[mid] < target && target <= nums[r]) { l = mid + 1; } else { r = mid - 1; } } else l++;//拐点与左边相同 } return false; } }
有序矩阵中第K小的元素
这里的有有序性比较难找,是一个数字的左边和上边的区域小于该数字。而且不是用下标二分,而是用值二分。
class Solution { public int kthSmallest(int[][] matrix, int k) { int n = matrix.length; if (n == 0) return 0; int left = matrix[0][0], right = matrix[n-1][n-1], ans = 0; while (left <= right) { int mid = (right - left) / 2 + left; if (get(matrix, mid) >= k) { //因为是小于等于说有mid的有多少个 ans = mid; right = mid - 1; } else { left = mid + 1; } } return ans; } //小于等于x的有多少个 private int get(int[][] matrix, int x) { int res = 0, i = 0, j = matrix.length - 1; while (i < matrix.length && j >= 0) { if (matrix[i][j] <= x) { res += j + 1; i++; } else { j--; } } return res; } }