二分

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;
    }
}

 

posted @ 2021-01-12 15:32  CPJ31415  阅读(163)  评论(0)    收藏  举报