Leetcode面T8(1-9)树

Q8.1  三步问题

三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。

示例1:

 输入:n = 3 
 输出:4
 说明: 有四种走法
示例2:

 输入:n = 5
 输出:13
提示:

n范围在[1, 1000000]之间

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/three-steps-problem-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

知识点动态规划,跟青蛙跳台类似,坑点: 因为这能条三个台阶,所以我们的动态方程中,有三项d[i-1] + d[i-2] + d[i-3],前两项相加的时候,会存在溢出的情况,因此此时前两项也需要进行取模1000000007操作。

class Solution {
    public int waysToStep(int n) {
        if(n <= 2) return n;
        if (n == 3) return 4;
        int[] d = new int[n + 1];

        d[1] = 1;
        d[2] = 2;
        d[3] = 4;
        for (int i = 4; i <= n; i++){
            d[i] = (d[i-1] + d[i-2]) % 1000000007 +d[i-3];
            d[i] %= 1000000007;
        }
        return d[n];
    }
}

Q8.3 魔术索引

魔术索引。 在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。

示例1:

 输入:nums = [0, 2, 3, 4, 5]
 输出:0
 说明: 0下标的元素为0
示例2:

 输入:nums = [1, 1, 1]
 输出:1
说明:

nums长度在[1, 1000000]之间
此题为原书中的 Follow-up,即数组中可能包含重复元素的版本

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/magic-index-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二分法,如果找到中点满足,还需在左半部分继续找
中点不满足,两侧查找
当找到了一个idx以后,区间的端点需要小于idx,才继续找

class Solution {
    public int findMagicIndex(int[] nums) {
        for(int i=0;i<nums.length;i=Math.max(i+1,nums[i])){
            if(nums[i]==i)
            return i;
        }
        return -1;
    }
}

Q8.4 幂集

幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。

说明:解集不能包含重复的子集。

示例:

 输入: nums = [1,2,3]
 输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/power-set-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<Integer> list = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<>();
        backtrack(nums, 0, list, res);
        
        return res;
    }
    
    
    private void backtrack(int[] nums, int cur, List<Integer> list, List<List<Integer>> res) {
        res.add(new ArrayList<>(list));
        for (int i = cur; i < nums.length; ++i) {
            list.add(nums[i]);
            backtrack(nums, i + 1, list, res);
            list.remove(list.size() - 1);
        }
    }
}

Q8.5 递归乘法

递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。

示例1:

 输入:A = 1, B = 10
 输出:10
示例2:

 输入:A = 3, B = 4
 输出:12
提示:

保证乘法范围不会溢出

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/recursive-mulitply-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

右移1位 == 除以2
左移1位 == 乘以2

class Solution {
public:
    int multiply(int A, int B) {
    	if(A < B)
    		A^=B^=A^=B;//swap大的在前,少递归几次
        if(B==1)
    		return A;
    	if((B&1)==0)//B是偶数
    		return multiply(A,B>>1)<<1;
    	else
    		return A + (multiply(A,B>>1)<<1);
    }
};

Q8.6 汉诺塔问题

在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。

请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。

你需要原地修改栈。

示例1:

 输入:A = [2, 1, 0], B = [], C = []
 输出:C = [2, 1, 0]
示例2:

 输入:A = [1, 0], B = [], C = []
 输出:C = [1, 0]
提示:

A中盘子的数目不大于14个。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/hanota-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
        move(A.size(), A, B, C);
    }
    
    void move(int n, List<Integer> A, List<Integer> B, List<Integer> C) {
        if (n == 1) {
            C.add(A.remove(A.size() - 1));
        } else {
            B.add(A.remove(A.size() - 1));
            move(n - 1, A, B, C); // 一处递归即可
            C.add(B.remove(B.size() - 1));
        }
    }
}

Q8.7 无重复字符串的排列组合

无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。

示例1:

 输入:S = "qwe"
 输出:["qwe", "qew", "wqe", "weq", "ewq", "eqw"]
示例2:

 输入:S = "ab"
 输出:["ab", "ba"]
提示:

字符都是英文字母。
字符串长度在[1, 9]之间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-i-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

回溯模板题

class Solution {
    private boolean[] used;
    /**
        如果字符串有重复字母,最简单的去重是使用Set,也可以排序后下标剪枝(相对比较难)
        重复字母的全排列可去做《全排列》系列题目。这道题好像和全排列I差不多。
        接着要做的就是回溯模板啦,选了的跳过,每次选或不选即可爆搜到所有解。
    */
    private List<String> res;
    private int len;

    public String[] permutation(String S) {
        len = S.length();
        used = new boolean[len];
        res = new ArrayList<>();

        dfs(S, new StringBuilder(), 0);

        return res.toArray(new String[0]);
    }
    
    private void dfs(String s, StringBuilder sb, int cnt) {
        // end —— 当sb长度与s长度一致时结束,存储答案
        if (cnt == len) {
            res.add(sb.toString());
            return;
        }

        // 回溯模板
        for (int i = 0; i < len; i++) {
            if (!used[i]) {
                used[i] = true;
                sb.append(s.charAt(i));
                dfs(s, sb, cnt + 1);
                used[i] = false;
                sb.deleteCharAt(cnt);
            }
        }
    }
}

Q8.8 有重复字符串的排列组合

有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。

示例1:

 输入:S = "qqe"
 输出:["eqq","qeq","qqe"]
示例2:

 输入:S = "ab"
 输出:["ab", "ba"]
提示:

字符都是英文字母。
字符串长度在[1, 9]之间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-ii-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这个就是基本的dfs, 不过要注意去重,如果单纯用set则复杂度过高,这里可以直接写规则来过滤,下面简单说一下两种过滤方式,假设数据是aab(这里注意要给字符数组先排序再dfs)

1.arr[i] == arr[i - 1] && book[i - 1]: 该种情况是优先取右,举个简单例子,第一个我们从左到右是a,a,b,这种情况是不可取的,因为当到第二个a时候,第一个a已经用过了,正相反,当我们从第二个a开始的时候,取第一个字符也就是arr[0]=a还没用过,符合条件,故两个a,a,b只会存下来1个。
2.arr[i] == arr[i - 1] && !book[i - 1]: 这个跟第一种过滤方式刚好相反,不过多解释。
还有一种方法可以免去去重步骤,就是可以把字符装桶,然后对桶dfs,只要这个字符还没用完就继续递归下去,有兴趣的可以实现一下。

public String[] permutation(String S) {

        List<String> list = new ArrayList<>();
        char[] arr = S.toCharArray();
        Arrays.sort(arr);
        boolean[] book = new boolean[arr.length];
        dfs(list, new StringBuilder(), book, arr);

        String[] res = new String[list.size()];
        for (int i = 0; i < res.length; i++)
            res[i] = list.get(i);

        return res;
    }
    
    public void dfs(List<String> res, StringBuilder sb, boolean[] book, char[] arr) {
        
        if (sb.length() == arr.length) {
            res.add(sb.toString());
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            if (!book[i]) {
                if (i > 0 && arr[i] == arr[i - 1] && !book[i - 1])
                    continue;
                else {
                    sb.append(arr[i]);
                    book[i] = true;
                    dfs(res, sb, book, arr);
                    book[i] = false;
                    sb.deleteCharAt(sb.length() - 1);
                }
            }
        }
    }

Q8.9 括号

括号。设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。

说明:解集不能包含重复的子集。

例如,给出 n = 3,生成结果为:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bracket-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
    List<String> result = new ArrayList<String>();
    public List<String> generateParenthesis(int n) {
        getAllResult(0, 0, n, new StringBuilder());
        return result;
    }

    public void getAllResult(int pre, int last, int n, StringBuilder sb){
        if(pre == n && last == n){
            result.add(sb.toString());
            return;
        }
        if(pre < n){
            sb.append('(');
            getAllResult(pre + 1, last, n, sb);
            sb.delete(sb.length() - 1, sb.length());
        }
        if(last < pre){
            sb.append(')');
            getAllResult(pre, last + 1, n, sb);
            sb.delete(sb.length() - 1, sb.length());
        }
    }
}

Q8.10  颜色填充

编写函数,实现许多图片编辑软件都支持的「颜色填充」功能。

待填充的图像用二维数组 image 表示,元素为初始颜色值。初始坐标点的横坐标为 sr 纵坐标为 sc。需要填充的新颜色为 newColor 。

「周围区域」是指颜色相同且在上、下、左、右四个方向上存在相连情况的若干元素。

请用新颜色填充初始坐标点的周围区域,并返回填充后的图像。

 

示例:

输入:
image = [[1,1,1],[1,1,0],[1,0,1]] 
sr = 1, sc = 1, newColor = 2
输出:[[2,2,2],[2,2,0],[2,0,1]]
解释: 
初始坐标点位于图像的正中间,坐标 (sr,sc)=(1,1) 。
初始坐标点周围区域上所有符合条件的像素点的颜色都被更改成 2 。
注意,右下角的像素没有更改为 2 ,因为它不属于初始坐标点的周围区域。
 

提示:

image 和 image[0] 的长度均在范围 [1, 50] 内。
初始坐标点 (sr,sc) 满足 0 <= sr < image.length 和 0 <= sc < image[0].length 。
image[i][j] 和 newColor 表示的颜色值在范围 [0, 65535] 内。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/color-fill-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

r代表row,c代表col,理解为x行x列

class Solution {
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        helper(image, sr, sc, image[sr][sc], newColor);
        return image;
    }
    private void helper(int[][] image, int i, int j, int oldColor, int newColor){
        if(i < 0 || i >= image.length || j < 0 || j >= image[0].length || image[i][j] != oldColor || image[i][j] == newColor) return;
        image[i][j] = newColor;
        helper(image, i+1, j, oldColor, newColor);
        helper(image, i-1, j, oldColor, newColor);
        helper(image, i, j+1, oldColor, newColor);
        helper(image, i, j-1, oldColor, newColor);
    }
}

Q8.11 硬币

硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)

示例1:

 输入: n = 5
 输出:2
 解释: 有两种方式可以凑成总金额:
5=5
5=1+1+1+1+1
示例2:

 输入: n = 10
 输出:4
 解释: 有四种方式可以凑成总金额:
10=10
10=5+5
10=5+1+1+1+1+1
10=1+1+1+1+1+1+1+1+1+1
说明:

注意:

你可以假设:

0 <= n (总金额) <= 1000000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

动态规划,每次小循环只用一种硬币。

若在一次for循环中处理四种情况(一个for里带四个硬币的处理情况),每次计算新一项时,由于每次取的硬币是任意的,会出现对于不同的硬币取法,情况重复的现象。 例如:n=15时,res[15] = 1(全1) + res[15 - 5] + res[15 - 10] = 7,但10 + 5和5 + 10是重复的。

每次小循环只用一种硬币可以避免重复,因为每次小循环中选用的硬币是固定的,在没有到对应硬币的循环前,表内记录对应的解必然不包含该硬币。 例如:n=15时,四次:res[15]=0 -> res[15] = 0 -> res[15] = 2 -> res[15] = 6

实际上coins数组升序也不会影响结果。

class Solution {
    private final int mod = 1000000007;
    private final int[] coins = {25,10,5,1};

    public int waysToChange(int n) {
        int[] res = new int[n + 1];
        res[0] = 1;
        for(int coin : coins){
            for(int i = coin;i <= n;i++){
                res[i] = (res[i] + res[i - coin]) % mod;
            }
        }
        return res[n];
    }
}

Q8.12 八皇后

设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。

注意:本题相对原题做了扩展

示例:

 输入:4
 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
 解释: 4 皇后问题存在如下两个不同的解法。
[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/eight-queens-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

N皇后本质上就是一个枚举问题,和全排列、组合什么的一个道理。枚举问题都可以考虑使用DFS来解决

class Solution {
    private List<List<String>> res = new ArrayList<>();

    public List<List<String>> solveNQueens(int n) {
        char[][] grid = new char[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = '.';
            }
        }
        boolean[] col = new boolean[n];
        boolean[] dg = new boolean[n + n];
        boolean[] udg = new boolean[n + n];
        dfs(0, n, grid, col, dg, udg);

        return res;
    }

    private void dfs(int h, int n, char[][] grid, boolean[] col, boolean[] dg, boolean[] udg) {
        if (h == n) {
            List<String> list = new ArrayList<>();
            for (int i = 0; i < grid.length; i++) {
                list.add(new String(grid[i]));
            }
            res.add(list);
            return;
        }
        for (int j = 0; j < n; j++) {
            if (!col[j] && !dg[n - h + j] && !udg[h + j]) {
                grid[h][j] = 'Q';
                col[j] = dg[n - h + j] = udg[h + j] = true;
                dfs(h + 1, n, grid, col, dg, udg);
                grid[h][j] = '.';
                col[j] = dg[n - h + j] = udg[h + j] = false;
            }
        }
    }
}

Q8.13 堆箱子

堆箱子。给你一堆n个箱子,箱子宽 wi、深 di、高 hi。箱子不能翻转,将箱子堆起来时,下面箱子的宽度、高度和深度必须大于上面的箱子。实现一种方法,搭出最高的一堆箱子。箱堆的高度为每个箱子高度的总和。

输入使用数组[wi, di, hi]表示每个箱子。

示例1:

 输入:box = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
 输出:6
示例2:

 输入:box = [[1, 1, 1], [2, 3, 4], [2, 6, 7], [3, 4, 5]]
 输出:10
提示:

箱子的数目不大于3000个。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/pile-box-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

将数组按照某一维排序,最大高度是排序后的某一子序列。用dp[i]标记以第n个箱子为结尾的最大高度和

class Solution {
    public int pileBox(int[][] box) {
        int m = box.length;
        if (m == 0) {
            return 0;
        }
        Arrays.sort(box, (o1, o2) -> o1[0] - o2[0]);
        int[] dp = new int[m];
        int res = 0;
        for (int i = 0; i < m; i++) {
            dp[i] = box[i][2];
            for (int j = 0; j < i; j++) {
                if (box[i][0] > box[j][0] && box[i][1] > box[j][1] && box[i][2] > box[j][2]) {
                    dp[i] = Math.max(dp[i], dp[j] + box[i][2]);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

 

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页