力扣100题解及笔记 回溯

 目录

1.力扣100题解及笔记 哈希-CSDN博客

2.力扣100题解及笔记 堆-CSDN博客

3.力扣100题解及笔记 栈-CSDN博客

4.力扣100题解及笔记 双指针-CSDN博客

5.力扣100题解及笔记 链表-CSDN博客

6.力扣100题解及笔记 二叉树-CSDN博客

7.力扣100题解及笔记 二分查找-CSDN博客

8.力扣100及题解 滑动窗口&子串-CSDN博客

9.力扣100题解及笔记 回溯-CSDN博客

10.力扣100题解及笔记 dp&多维dp-CSDN博客

11.力扣100题解及笔记 贪心-CSDN博客

12.力扣100题解及笔记 数组-CSDN博客

13.力扣100题解及笔记 技巧-CSDN博客

14.力扣100题解及笔记 矩阵-CSDN博客

15.力扣100题解及笔记 图论-CSDN博客

理论基础

通用

子集

组合

        子集的基础上,长度判定+剪枝

排列

        通用的基础上,加一个Boolean判定选过没

路径搜索

        对访问过的网格单元会进行临时标记,并在回溯时恢复状态

17.电话号码

17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:

输入:digits = ""
输出:[]
示例 3:

输入:digits = "2"
输出:["a","b","c"]

基础题

class Solution {
    private static final String[] MAPPING = new String[]{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    private final List<String> ans = new ArrayList<>();
    private char[] digits;
    private char[] path;

    public List<String> letterCombinations(String digits) {
        if ( digits.length()== 0)  return List.of();//空字符串,返回一个空列表结果
        this.digits = digits.toCharArray();
        path = new char[digits.length()]; //长度固定
        dfs(0);
        return ans;
    }

    private void dfs(int i) {
        if (i == digits.length) {
            ans.add(new String(path));
            return;
        }
        for (char c : MAPPING[digits[i] - '0'].toCharArray()) {//遍历当前数字对应的字母组合中的每一个字母
            //digits[i] - '0',字符 '2' 变为整数 2,用作索引查找 MAPPING 数组中的对应项
            //.toCharArray()"abc"转换['a', 'b', 'c']
            path[i] = c; // 直接覆盖
            dfs(i + 1);
        }
    }
}

78.子集

78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的
子集
(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

 

示例 1:

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

输入:nums = [0]
输出:[[],[0]]
 

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同
class Solution {
    private final List<List<Integer>> ans = new ArrayList<>();
    private final List<Integer> path = new ArrayList<>();
    private int[] nums;

    public List<List<Integer>> subsets(int[] nums) {
        this.nums = nums;
        dfs(0);
        return ans;
    }

    private void dfs(int i) { 
        ans.add(new ArrayList<>(path));
        for (int j = i; j < nums.length; j++) {
            path.add(nums[j]);
            dfs(j + 1);
            path.remove(path.size() - 1);
        }
    }
}

131.分割回文串

131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 
回文串
 。返回 s 所有可能的分割方案。

 

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]
示例 2:

输入:s = "a"
输出:[["a"]]
 

提示:

1 <= s.length <= 16
s 仅由小写英文字母组成

子集题

class Solution {
    private final List<List<String>> ans = new ArrayList<>();
    private final List<String> path = new ArrayList<>();
    private String s;
    public List<List<String>> partition(String s) {
        this.s = s;
        dfs(0);
        return ans;
    }

    private void dfs(int i) {
        if (i == s.length()) {
            ans.add(new ArrayList<>(path)); // 复制 path
            return;
        }
        for (int j = i; j < s.length(); j++) { // 枚举子串的结束位置
            if (isPalindrome(i, j)) {
                path.add(s.substring(i, j + 1));//s.substring(i, j + 1):从字符串 s 中获取从索引 i 到 j(包含 j)的子串
                dfs(j + 1);
                path.remove(path.size() - 1); //移除列表的末尾 //每次偶要回溯
            }
        }
    }
    
    private boolean isPalindrome(int left, int right) {
        while (left < right) {
            if (s.charAt(left++) != s.charAt(right--)) {
                return false;
            }
        }
        return true;
    }
}

39.组合总和

39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

 

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:

输入: candidates = [2], target = 1
输出: []
 

提示:

1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates 的所有元素 互不相同
1 <= target <= 40

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        dfs(0, target, candidates, ans, path);
        return ans;
    }

    private void dfs(int i, int left, int[] candidates, List<List<Integer>> ans, List<Integer> path) {
        //left 是目标和 target 减去当前组合中的数之后剩余的差值
        if (left == 0) {
            ans.add(new ArrayList<>(path));
            return;
        }
        if (left < candidates[i])  return; 
        //减去则会变成负数
        //已经排序,后续的候选数只会更大
        for (int j = i; j < candidates.length; j++) {
            path.add(candidates[j]);
            dfs(j, left - candidates[j], candidates, ans, path);
            path.remove(path.size() - 1); // 恢复现场
        }
    }
}

22.括号生成

22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

 

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:

输入:n = 1
输出:["()"]
 

提示:

1 <= n <= 8
class Solution {
    private int n;
    private final List<Integer> path = new ArrayList<>();
    private final List<String> ans = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        this.n = n;
        dfs(0, 0);
        return ans;
    }
    private void dfs(int i, int b) {
        if (path.size() == n) {//左括号满,生成s存入
            char[] s = new char[n * 2];
            Arrays.fill(s, ')');
            for (int j : path) s[j] = '(';
            ans.add(new String(s));
            return;
        }

        for (int close = 0; close <= b; close++) { //填 close填充的右括号的个数
            path.add(i + close); //位置 i + close 处放置一个左括号
            dfs(i + close + 1, b - close + 1);//下一个位置是 i + close + 1 //更新括号的平衡状态
            path.remove(path.size() - 1);
        }
    }
}

46.全排列

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

 

示例 1:

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

输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:

输入:nums = [1]
输出:[[1]]
 

提示:

1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> path = Arrays.asList(new Integer[nums.length]);
        boolean[] is = new boolean[nums.length]; // 标记每个数字是否已经选过
        dfs(0, nums, ans, path, is);
        return ans;
    }

    private void dfs(int i, int[] nums, List<List<Integer>> ans, List<Integer> path, boolean[] is) {
        if (i == nums.length) {
            ans.add(new ArrayList<>(path));
            return;
        }
        for (int j = 0; j < nums.length; j++) {
            if (!is[j]) {
                path.set(i, nums[j]); // 将第 i 个位置设置为 nums[j]
                is[j] = true;
                dfs(i + 1, nums, ans, path, is);
                is[j] = false; // 恢复现场,将 nums[j] 设为未选状态
                // path长度在初始化时固定为 nums.length,因此不需要在每次递归时进行恢复操作,直接覆盖即可
            }
        }
    }
}

51.N皇后h

51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。


提示:

1 <= n <= 9

说人话:不能同行同列同斜线

多了行列问题+打印棋盘的步骤

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> ans = new ArrayList<>(); 
        int[] col = new int[n];  // 行列对应,例如 col[r] = c 表示第 r 行的皇后在第 c 列
        boolean[] onPath = new boolean[n];  // 标记每列是否已有皇后
        boolean[] diag1 = new boolean[n * 2 - 1];  // 标记主对角线(左上到右下)是否已有皇后,主对角线编号为 r + c
        boolean[] diag2 = new boolean[n * 2 - 1];  // 标记副对角线(右上到左下)是否已有皇后,副对角线编号为 r - c + n - 1
        dfs(0, n, col, onPath, diag1, diag2, ans);  // 从第 0 行开始放置皇后
        return ans; 
    }

    private void dfs(int r, int n, int[] col, boolean[] onPath, boolean[] diag1, boolean[] diag2, List<List<String>> ans) {
        if (r == n) {  // 当 r == n 时,表示已经成功在所有行放置了皇后
            List<String> board = new ArrayList<>(n);
            for (int c : col) {  // 根据 col 数组构建棋盘
                char[] row = new char[n];
                Arrays.fill(row, '.'); 
                row[c] = 'Q'; 
                board.add(new String(row));
            }
            ans.add(board); 
            return; 
        }

        for (int c = 0; c < n; c++) {  // 尝试在当前行 r 的每一列 c 放置皇后
            int rc = r - c + n - 1;  // 计算当前列 c 对应的副对角线编号
            if (!onPath[c] && !diag1[r + c] && !diag2[rc]) {  // 如果当前列、主对角线、副对角线都没有皇后
                col[r] = c;  // 记录当前行 r 的皇后放在第 c 列
                onPath[c] = diag1[r + c] = diag2[rc] = true;
                dfs(r + 1, n, col, onPath, diag1, diag2, ans);  
                onPath[c] = diag1[r + c] = diag2[rc] = false;
            }
        }
    }
}

  79.单词

79. 单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

 

提示:

m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board 和 word 仅由大小写英文字母组成
 

路径搜索问题

class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (dfs(board, words, i, j, 0)) return true;//从每行第一个开始遍历
            }
        }
        return false;
    }

    boolean dfs(char[][] board, char[] word, int i, int j, int k) {
        if (i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) 
            return false;// 越界    
        if (k == word.length - 1) //匹配到了单词的最后一个字符,返回 true
            return true;
        board[i][j] = '\0';//标记
        boolean res = dfs(board, word, i + 1, j, k + 1) ||  // 向下走
                      dfs(board, word, i - 1, j, k + 1) ||  // 向上走
                      dfs(board, word, i, j + 1, k + 1) ||  // 向右走
                      dfs(board, word, i, j - 1, k + 1);    // 向左走
        board[i][j] = word[k];// 恢复标记
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值