LeetCode之回溯

17. 电话号码的字母组合

class Solution {

    // 定义一个公共方法,用于获取给定数字字符串的字母组合,并以列表形式返回
    public List<String> letterCombinations(String digits) {
        // 创建一个字符串列表来存储最终的组合结果
        List<String> ans = new ArrayList<>();
        // 如果输入的数字字符串长度为 0 ,直接返回空列表
        if (digits.length() == 0) {
            return ans;
        }
        // 创建一个映射,将数字字符与对应的字母字符串关联起来
        Map<Character, String> map = new HashMap<>();
        map.put('2', "abc");
        map.put('3', "def");
        map.put('4', "ghi");
        map.put('5', "jkl");
        map.put('6', "mno");
        map.put('7', "pqrs");
        map.put('8', "tuv");
        map.put('9', "wxyz");
        // 创建一个可变字符串缓冲区,用于构建当前的组合
        StringBuffer sb = new StringBuffer();

        // 调用回溯方法来生成字母组合
        dfs(digits, ans, 0, sb, map);

        // 返回生成的组合列表
        return ans;
    }

    // 定义回溯方法
    private void dfs(String digits, List<String> ans, int index, StringBuffer sb, Map<Character, String> map) {
        // 如果当前索引达到数字字符串的长度,说明生成了一个完整的组合,将其添加到结果列表中
        if (index == digits.length()) {
            ans.add(sb.toString());
            return;
        }
        // 获取当前索引处的数字字符
        char digit = digits.charAt(index);
        // 根据数字字符从映射中获取对应的字母字符串
        String letters = map.get(digit);

        // 遍历字母字符串中的每个字母
        for (int i = 0; i < letters.length(); i++) {
            // 将当前字母添加到组合中
            sb.append(letters.charAt(i));
            // 递归调用回溯方法,处理下一个数字字符
            dfs(digits, ans, index + 1, sb, map);
            // 回溯,删除当前添加的字母,尝试其他字母
            sb.deleteCharAt(index);
        }
    }
}

77. 组合

class Solution {
    List<List<Integer>> listTotal = new ArrayList<>(); // 存储所有的组合结果

    // 回溯函数
    private void backtrack(int n, int k, List<Integer> list, int index) {
        // 当当前组合的大小达到 k 时,将其添加到结果中
        if (list.size() == k) {
            listTotal.add(new ArrayList<>(list)); // 深拷贝当前组合并添加到结果
            return; // 结束当前回溯
        }

        // 从 index 开始遍历到 n
        for (int i = index; i <= n; i++) {
            list.add(i); // 将当前元素 i 添加到组合中

            // 递归调用,继续选择下一个元素
            // 注意:i + 1 确保不会重新选择已经添加的元素
            backtrack(n, k, list, i + 1);

            // 回溯,移除最后添加的元素,准备尝试下一个元素
            list.remove(list.size() - 1);
        }
    }

    // 主函数,启动回溯过程
    public List<List<Integer>> combine(int n, int k) {
        backtrack(n, k, new ArrayList<Integer>(), 1); // 从 1 开始选择
        return listTotal; // 返回最终结果
    }
}

46. 全排列

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        // 创建一个空的列表 res,用于存储所有的排列结果
        List<List<Integer>> res = new ArrayList<>();
        // 如果输入数组 nums 的长度为 0,则直接返回结果列表(空列表)
        if (nums.length == 0) {
            return res;
        }
        // 创建一个布尔数组 used,用于记录哪些数字已经被使用。其长度与 nums 相同
        boolean[] used = new boolean[nums.length];
        // 创建一个双端队列 path,用于保存当前排列的数字
        Deque<Integer> path = new LinkedList<>();
        // 调用名为 dfs 的辅助方法,开始深度优先搜索。初始深度为 0
        dfs(nums, 0, path, used, res);
        // 最后返回包含所有排列结果的列表 res
        return res;
    }

    private void dfs(int[] nums, int depth, Deque<Integer> path, boolean[] used, List<List<Integer>> res) {
        // 递归终止条件
        if (depth == nums.length) {
            // 当前深度等于 nums 的长度时,表示当前的排列已经完成。将 path 中的当前排列添加到结果列表中
            res.add(new ArrayList<>(path));
        }
        // 通过一个循环,遍历所有的数组元素
        for (int i = 0; i < nums.length; i++) {
            // 如果当前元素 nums[i] 已经在当前排列中使用,则跳过该元素,以避免重复排列
            if (used[i]) {
                continue;
            }
            // 将 nums[i] 添加到 path 的末尾,表示当前元素已被选择
            path.addLast(nums[i]);
            // 将 used[i] 标记为 true,表示当前元素已被使用
            used[i] = true;
            // 递归调用 dfs 方法,进行下一层深度的搜索
            dfs(nums, depth + 1, path, used, res);
            // 撤销操作,即是回溯
            // 撤销上一步的选择,移除 path 中的最后一个元素
            path.removeLast();
            // 将 used[i] 标记为 false,表示当前元素可以被下一个排列使用
            used[i] = false;
        }
    }
}

39. 组合总和

class Solution {

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        // 创建一个列表用于存储最终的组合结果
        List<List<Integer>> result = new ArrayList<>();
        // 创建一个列表用于存储当前的组合路径
        List<Integer> path = new ArrayList<>();
        // 调用深度优先搜索方法开始生成组合
        dfs(candidates, target, path, 0, result);
        // 返回最终的组合结果列表
        return result;
    }

    private void dfs(int[] candidates, int target, List<Integer> path, int depth, List<List<Integer>> result) {
        // 如果深度达到数组长度,说明已经遍历完所有元素,直接返回
        if (depth == candidates.length) {
            return;
        }
        // 如果目标值为 0,说明找到了一个满足条件的组合,将当前路径加入结果列表并返回
        if (target == 0) {
            result.add(new ArrayList<>(path));
            return;
        }
        // 不选择当前元素,继续递归下一个位置
        dfs(candidates, target, path, depth + 1, result);
        // 如果目标值大于等于当前元素,可以选择当前元素
        if (target >= candidates[depth]) {
            // 将当前元素加入路径列表
            path.add(candidates[depth]);
            // 递归调用,目标值减去当前元素,继续在当前位置探索
            dfs(candidates, target - candidates[depth], path, depth, result);
            // 回溯,将当前元素从路径列表中移除
            path.remove(path.size() - 1);
        }
    }
}

22. 括号生成

class Solution {

    public List<String> generateParenthesis(int n) {
        // 创建一个列表 result,用于存储所有生成的合法括号组合
        List<String> result = new ArrayList<>();

        // 创建一个 StringBuilder 对象 path,用于构建当前的括号组合
        StringBuilder path = new StringBuilder();

        // 调用深度优先搜索方法 dfs 开始生成括号组合
        dfs(result, n, 0, 0, path);

        // 返回包含所有合法括号组合的列表 result
        return result;
    }

    private void dfs(List<String> result, int n, int open, int close, StringBuilder path) {
        // 如果当前括号组合的长度等于 n * 2,说明已经生成了一个完整的合法组合
        if (path.length() == n * 2) {
            // 将当前组合添加到结果列表中
            result.add(path.toString());
            // 返回,结束当前递归调用
            return;
        }

        // 如果打开的括号数量小于 n,可以添加一个左括号
        if (open < n) {
            // 将左括号添加到当前组合中
            path.append("(");
            // 递归调用 dfs 方法,打开括号数量加 1,继续生成组合
            dfs(result, n, open + 1, close, path);
            // 回溯,删除刚才添加的左括号,尝试其他可能的组合
            path.deleteCharAt(path.length() - 1);
        }

        // 如果关闭的括号数量小于打开的括号数量,可以添加一个右括号
        if (close < open) {
            // 将右括号添加到当前组合中
            path.append(")");
            // 递归调用 dfs 方法,关闭括号数量加 1,继续生成组合
            dfs(result, n, open, close + 1, path);
            // 回溯,删除刚才添加的右括号,尝试其他可能的组合
            path.deleteCharAt(path.length() - 1);
        }
    }

}

79. 单词搜索

class Solution {

    // 接受一个字符二维数组 board 和一个字符串 word 作为参数,返回一个布尔值,表示 word 是否可以在 board 中找到
    public boolean exist(char[][] board, String word) {
        // 获取字符网格 board 的高度 m 和宽度 n。board.length 表示行数,board[0].length 表示列数
        int m = board.length;
        int n = board[0].length;

        // 创建一个与 board 同样大小的布尔二维数组 visited,用于记录哪些字符已经被访问过
        boolean[][] visited = new boolean[m][n];

        // 使用嵌套循环遍历 board 中的每个字符。外层循环遍历行,内层循环遍历列
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 调用 dfs 方法,尝试从当前位置 (i, j) 开始查找 word 的字符,初始字符的索引为 0
                boolean result = dfs(board, word, visited, i, j, 0);
                if (result) {
                    // 如果找到结果,立即返回 true
                    return true;
                }
            }
        }
        // 如果遍历完 board 中所有位置,仍未找到 word,返回 false
        return false;
    }

    /**
     * 定义深度优先搜索方法 dfs,负责在 board 中查找单词的字符。
     * 该方法接受以下参数:
     * board:字符网格。
     * word:要查找的单词。
     * visited:用于记录访问状态的数组。
     * i 和 j:当前查找的位置。
     * k:当前字符在单词中的索引
     */
    private boolean dfs(char[][] board, String word, boolean[][] visited, int i, int j, int k) {
        // 如果当前位置的字符与单词中对应的字符不匹配,则返回 false
        if (board[i][j]!= word.charAt(k)) {
            return false;
        } else if (k == word.length() - 1) {
            // 如果已经匹配到单词的最后一个字符,返回 true
            return true;
        }

        // 将当前位置标记为已访问,以避免在同一路径中重复访问
        visited[i][j] = true;

        // 创建一个二维数组 directions,表示四个可能的移动方向(这里的方向数组似乎不太准确,{0, -1}应该是向左,{0, 1}向右,{1, 0}向下,{-1, 0}向上)
        int[][] directions = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}};

        // 初始化变量 result 为 false,用于存储找到单词的结果
        boolean result = false;
        for (int[] direction : directions) {
            // 遍历每个方向,并计算新的位置 (newi, newj)
            int newi = i + direction[0];
            int newj = j + direction[1];
            // 确保新的位置在网格范围内
            if (newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length) {
                // 确保新的位置未被访问过
                if (!visited[newi][newj]) {
                    // 如果符合条件,递归调用 dfs 方法,查找下一个字符(k + 1)
                    boolean flag = dfs(board, word, visited, newi, newj, k + 1);
                    // 如果找到匹配(即 flag 为 true),设置 result 为 true 并跳出循环
                    if (flag) {
                        result = true;
                        break;
                    }
                }
            }
        }
        // 在所有方向检查完成后,将当前位置标记为未访问(回溯),以允许其他路径访问此位置
        visited[i][j] = false;

        // 返回找到单词的结果(true 或 false)
        return result;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值