leetcode | 分类整理2

二分查找

69. x的平方根(e)

重点:1. 用x/m<m而不是m*m>x防止溢出;2. 用右中位数防止无限循环

744. 寻找比目标字母大的最小字母(e)

重点:字母顺序可以循环(a>z),且letters数组是有序的。因此,最后退出循环的时候还要判断:

return letters[l] > target ? letters[l] : letters[0];

540. 有序数组的单一元素(m)

重点:题目中要求要O(logn)的复杂度,所以应该用二分;如果不要求,用位运算(O(n))。

1) 二分中的判断,主要是判断的是num[mid]和num[mid+1]是否相等,就可以判断单个数字已经出现在前部分还是还没有出现(前提就是一定保证mid始终指向的是索引为偶数的位)

public int singleNonDuplicate(int[] nums) {
        if (nums.length <= 0 || nums == null) {
            return -1;
        }
        if (nums.length == 1) {
            return nums[0];
        }

        int l = 0, r = nums.length-1;

//        有要求的数字的一方一定是奇数个
        while (l < r) {
//            mid最开始肯定是第偶数个,所以判断mid和mid+1
//            在只出现一次的数字之前,两个相同数字的下标一定是{偶数,奇数};在一次的数字之后,下标一定是{奇数,偶数}
            int mid = l + (r - l) / 2;
//            保证mid只指向偶数,这样就只需要判断相同数的位置,就可以判断单个数字在前部分还是后部分
            if (mid % 2 == 1) mid--;
            if (nums[mid] == nums[mid + 1]) {
                l = mid + 2;
            }
            else {
                r = mid;
            }
        }

        return nums[l];
    }

278. 第一个错误的版本(e)

153. 寻找旋转排序数组中的最小值

重点:循环内的判断条件

if (nums[mid] > nums[r]) {
    l = mid + 1;
}
else {
    r = mid;
}

34. 在排序数组中查找元素的第一个和最后一个位置(m)

重点:两次二分查找。分别查找该数第一次出现的位置和target+1应该出现的位置(该数-1就是所求的end)

public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        if (nums.length <= 0 || target < nums[0] || target > nums[nums.length-1]) {
            res[0] = -1;
            res[1] = -1;
            return res;
        }

        int first = findIndex(nums,target);
        int last = findIndex(nums,target+1);

        if (first == nums.length || nums[first] != target) {
            return new int[]{-1,-1};
        }
        return new int[]{first,last-1};
    }

    /**
     * 求target第一次出现的位置
     * @param nums
     * @param target
     * @return
     */
    private int findIndex(int[] nums, int target) {
//        r的初始值不是length-1,而是length,因为若不存在可能会返回最后一个
        int l = 0 , r = nums.length;
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] < target) {
                l = mid + 1;
            }
            else {
                r = mid;
            }
        }

        return l;

    }

分治:

分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

可以分成i的左边和i的右边这种子问题的用分治。

参数中一定要能表示开始和结束(比如241题,substring函数可以截取字符串,潜在的表明开始和结束;但是95题不行,所以需要一个helper函数用来递归)。

241. 为表达式设计优先级(m)(记住!!!!!)

思路:

带备忘录(HashMap)的分治算法,提高对重复子问题的运算效率。

例如2*3-4*5

1. 遇到第一个符号*:左[2],右[3-4*5]

此时左边直接返回,右边(3-4*5)递归:

2. 遇到第一个符号-:左[3],右[4*5]

左边直接返回,右边(4*5)递归:

3. 遇到第一个符号*,左4右5,结果20,直接返回到2,(3-4*5)结果【1】-17;

4. 此时2有了第一个结果结果【1】,继续遍历,遇到第二个符号*:左[3-4],右[5]

左边(3-4)递归,右边直接返回:

5. 遇到第一个符号-,左3右4,结果-1,直接返回到4,(3-4*5)结果【2】-5;

6. 结果【1】和结果【2】返回给1,得到对应的结果【1】和结果【2】-34和-10;

7. 继续遍历(2*3-4*5),遇到第二个符号-:左[2*3],右[4*5],结果【3】-12;

8. 继续遍历(2*3-4*5),遇到第三个符号*:左[2*3-4],右[5]

右边直接返回,左边(2*3-4)递归。。。同上

private HashMap<String, List<Integer>> hm = new HashMap<String, List<Integer>>();
     public List<Integer> diffWaysToCompute(String input) {
         if(hm.containsKey(input))
             return hm.get(input);
         List<Integer> res = new ArrayList<Integer>();
         for (int i=0; i<input.length(); i++) {
            char ch = input.charAt(i);
            if (ch=='+' || ch=='-' || ch=='*') {
                for (Integer l : diffWaysToCompute(input.substring(0, i)))
                    for (Integer r : diffWaysToCompute(input.substring(i + 1))) {
                        if (ch == '+')
                            res.add(l + r);
                        else if (ch == '-')
                            res.add(l - r);
                        else
                            res.add(l * r);
                    }
            }
         }
         if (res.size() == 0) res.add(Integer.valueOf(input));
         hm.put(input, res);
         return res;
    }

96. 不同的二叉搜索树(m)

作者:LeetCode
链接:https://leetcode-cn.com/problems/unique-binary-search-trees/solution/bu-tong-de-er-cha-sou-suo-shu-by-leetcode/

给定一个有序序列 1 ... n,为了根据序列构建一棵二叉搜索树。我们可以遍历每个数字 i,将该数字作为树根,1 ... (i-1) 序列将成为左子树,(i+1) ... n 序列将成为右子树。于是,我们可以递归地从子序列构建子树。
在上述方法中,由于根各自不同,每棵二叉树都保证是独特的。

问题是计算不同二叉搜索树的个数。为此,我们可以定义两个函数:

G(n): 长度为n的序列的不同二叉搜索树个数。

F(i, n): 以i为根的不同二叉搜索树个数。

可见, G(n) 是我们解决问题需要的函数。

举例而言,F(3, 7),以 3 为根的不同二叉搜索树个数。为了以 3 为根从序列 [1, 2, 3, 4, 5, 6, 7] 构建二叉搜索树,我们需要从左子序列 [1, 2] 构建左子树,从右子序列 [4, 5, 6, 7] 构建右子树,然后将它们组合(即笛卡尔积)。
巧妙之处在于,我们可以将 [1,2] 构建不同左子树的数量表示为 G(2), 从 [4, 5, 6, 7]` 构建不同右子树的数量表示为 G(4)。这是由于 G(n) 和序列的内容无关,只和序列的长度有关。于是,F(3,7) =G(2)⋅G(4)。

public int numTrees(int n) {
        if (n <= 1) {
            return 1;
        }

        int[] G = new int[n+1];
        G[0] = 1;
        G[1] = 1;

        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                G[i] += G[j-1] * G[i-j];
            }
        }
        
        return G[n];
    }

95. 不同的二叉搜索树II(m)

思路:由上!!

我们从序列 1 ..n 中取出数字 i,作为当前树的树根。于是,剩余 i - 1 个元素可用于左子树,n - i 个元素用于右子树。

现在,我们对序列 1 ... i - 1 重复上述过程,以构建所有的左子树;然后对 i + 1 ... n 重复,以构建所有的右子树。

这样,我们就有了树根 i 和可能的左子树、右子树的列表。

public List<TreeNode> generateTrees(int n) {
        if (n <= 0) {
            return new LinkedList<TreeNode>();
        }
        return geneHelper(1,n);
    }

    private LinkedList<TreeNode> geneHelper(int start, int end) {
        LinkedList<TreeNode> res = new LinkedList<>();
        if (start > end) {
            res.add(null);
            return res;
        }

        for (int i = start; i <= end; i++) {
            LinkedList<TreeNode> left = geneHelper(start,i-1);
            LinkedList<TreeNode> right = geneHelper(i+1,end);
            // connect left and right trees to the root i
//            其实是一种笛卡尔乘积,G(i-1)*G(n-i)
            for (TreeNode lnode : left) {
                for (TreeNode rnode : right) {
                    TreeNode currentRoot = new TreeNode(i);
                    currentRoot.left = lnode;
                    currentRoot.right = rnode;
                    res.add(currentRoot);
                }
            }
        }

        return res;
    }
发布了53 篇原创文章 · 获赞 5 · 访问量 1513

猜你喜欢

转载自blog.csdn.net/zhicheshu4749/article/details/104180903