[回溯]代码随想录总结

本文详细介绍了在IT技术中,如何使用递归遍历解决组合问题(如77/216/39/40/17题)、排列问题(46/47题)、子集问题(78/90/491题)、切割问题(131/93题)以及棋盘问题(51题,如N皇后)。着重展示了回溯算法在这些问题中的应用。
摘要由CSDN通过智能技术生成


组合问题

力扣相关题目:
77.组合
216.组合总和III
39. 组合总和(hot100)
40.组合总和II
17.电话号码的字母组合(hot100)

递归遍历(Java代码):

class Solution {
	// 77.组合
	// 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合
    List<List<Integer>> res;
    List<Integer> path;

    public void backtracking(int n, int k, int index)
    {
        if (path.size() == k)
        {
            res.add(new ArrayList<>(path));
            return;
        }
        // 剪枝
        for (int i = index; i <= n - (k - path.size()) + 1; i ++)
        {
            path.add(i);
            backtracking(n,k,i + 1);
            path.remove(path.size() - 1);
        }
    }
    public List<List<Integer>> combine(int n, int k) {
        res = new ArrayList<>();
        path = new ArrayList<>();
        backtracking(n,k,1);
        return res;

    }
    // 216.组合总和III
    // 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public void backtracking(int n, int k, int sum, int index)
    {
        if (sum > n) return;
        if (path.size() == k)
        {
            if (sum == n) res.add(new ArrayList<>(path));
            return;
        }
        for (int i = index; i <= 9 - (k - path.size()) + 1; i ++)
        {
            path.add(i);
            sum += i;
            backtracking(n,k,sum,i + 1);
            path.remove(path.size() - 1);
            sum -= i;
        }
    }
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtracking(n,k,0,1);
        return res;
    }
    // 39. 组合总和
    // 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public void backtracking(int[] candidates, int target,int sum,int index)
    {
        if (target == sum)
        {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = index; i < candidates.length; i ++)
        {
            if (sum + candidates[i] > target) break;
            path.add(candidates[i]);
            sum += candidates[i];
            backtracking(candidates,target,sum,i);
            path.remove(path.size() - 1);
            sum -= candidates[i];
        }

    }
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtracking(candidates,target,0,0);
        return res;
    }
    // 40.组合总和II
    // 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public void backtracking(int[] candidates, int target, int sum, int index)
    {
        if (sum == target)
        {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = index; i < candidates.length; i ++)
        {
            if (sum + candidates[i] > target) break;
            if (i > index && candidates[i - 1] == candidates[i]) continue;

            path.add(candidates[i]);
            sum += candidates[i];
            // 和39.组合总和的区别1:这里是i+1,每个数字在每个组合中只能使用一次
            backtracking(candidates,target,sum,i + 1);
            path.remove(path.size() - 1);
            sum -= candidates[i];
        }
    }
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtracking(candidates,target,0,0);
        return res;
    }
    // 17.电话号码的字母组合
    // 本题每一个数字代表的是不同集合,也就是求不同集合之间的组合
    List<String> res = new ArrayList<>();
    StringBuffer tmp = new StringBuffer();

    public void backtracking(String digits, String[] numbers, int index)
    {
        if (digits.length() == index)
        {
            res.add(tmp.toString());
            return;
        }
        String digit = numbers[digits.charAt(index) - '0'];

        for (int i = 0; i < digit.length(); i ++)
        {
            tmp.append(digit.charAt(i));
            backtracking(digits,numbers,index + 1);
            tmp.deleteCharAt(tmp.length() - 1);
        }
    }
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) return res;
        String[] numbers = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        backtracking(digits,numbers,0);
        return res;
    }

}

切割问题

力扣相关题目:
131.分割回文串(hot100)
93.复原IP地址

递归遍历(Java代码):

class Solution {
	// 131.分割回文串
	// 切割问题类似组合问题
    List<List<String>> res = new ArrayList<>();
    List<String> path = new ArrayList<>();

    public boolean isPalindrome(String s, int start, int end)
    {
        for (int i = start, j = end; i < j; i ++, j --)
        {
            if (s.charAt(i) != s.charAt(j)) return false;
        }
        return true;
    }
    // index表示切割线
    public void backtracking(String s, int index)
    {
        if (index >= s.length())
        {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = index; i < s.length(); i ++)
        {
            if (isPalindrome(s,index,i))
            {
                path.add(s.substring(index,i + 1));
            }
            else continue;

            backtracking(s,i + 1);
            path.remove(path.size() - 1);
        }
    }
    public List<List<String>> partition(String s) {
        backtracking(s,0);
        return res;
    }
    // 93.复原IP地址
    List<String> res = new ArrayList<>();
    public void backtracking(StringBuilder s, int index, int dot)
    {
        if (dot == 3)
        {
            if (valid(s,index,s.length() - 1)) res.add(s.toString());
            return;

        }
        for (int i = index; i < s.length(); i ++)
        {
             if (valid(s,index,i))
             {
                s.insert(i + 1, '.');
                backtracking(s, i + 2, dot + 1);
                s.deleteCharAt(i + 1);
             } 
             else break;  
        }
    }
    public boolean valid(StringBuilder s, int start, int end)
    {
        if (start > end) return false;

        if (s.charAt(start) == '0' && start != end) return false;
        int num = 0;

        for (int i = start; i <= end; i ++)
        {
            if (s.charAt(i) > '9' || s.charAt(i) < '0') return false;
            num = num*10 + (s.charAt(i) - '0');
            if (num > 255) return false;

        }
        return true;
    }
    public List<String> restoreIpAddresses(String s) {
        StringBuilder sb = new StringBuilder(s);
        backtracking(sb, 0, 0);
        return res;
    }

}

子集问题

力扣相关题目:
78.子集(hot100)
90.子集II
491.递增子序列

递归遍历(Java代码):

class Solution {
	// 78.子集
	// 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public void backtracking(int[] nums, int index)
    {
        res.add(new ArrayList<>(path));
        if (index >= nums.length) return;

        for (int i = index; i < nums.length; i ++)
        {
            path.add(nums[i]);
            backtracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
    public List<List<Integer>> subsets(int[] nums) {
        backtracking(nums,0);
        return res;
    }
    // 90.子集II
    // 这道题目和78.子集 (opens new window)区别就是集合里有重复元素了,而且求取的子集要去重
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public void backtracking(int[] nums, int index)
    {
        res.add(new ArrayList<>(path));
        if (index >= nums.length) return;

        for (int i = index; i < nums.length; i ++)
        {
            if (i > index && nums[i] == nums[i - 1]) continue;
            path.add(nums[i]);
            backtracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        backtracking(nums,0);
        return res;
    }
    // 491.递增子序列
    // 不能进行排序去重,所以使用集合的方法
    // 但子集问题一定要排序
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public void backtracking(int[] nums, int index)
    {
        if (path.size() >= 2)
        {
            res.add(new ArrayList<>(path));
        }

        Set<Integer> set = new HashSet<>();

        for (int i = index; i < nums.length; i ++)
        {
            if (!path.isEmpty() && path.get(path.size() - 1) > nums[i] || set.contains(nums[i])) continue;

            set.add(nums[i]);
            path.add(nums[i]);
            backtracking(nums,i + 1);
            path.remove(path.size() - 1);
        }
    }
    public List<List<Integer>> findSubsequences(int[] nums) {
        backtracking(nums,0);
        return res;
    }
    
}

排列问题

力扣相关题目:
46.全排列(hot100)
47.全排列 II
491.递增子序列

递归遍历(Java代码):

class Solution {
	// 46.全排列
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] used;

    public void backtracking(int[] nums)
    {
        if (path.size() == nums.length)
        {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < nums.length; i ++)
        {
            if (used[i]) continue;

            used[i] = true;
            path.add(nums[i]);
            backtracking(nums);
            path.remove(path.size() - 1);
            used[i] = false;
        }
    }
    public List<List<Integer>> permute(int[] nums) {
        if (nums.length == 0) return res;
        used = new boolean[nums.length];
        backtracking(nums);
        return res;
    }
    // 47.全排列 II
    // 可包含重复数字的序列,所以要排序去重
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] used;

    public void backtracing(int[] nums)
    {
        if (path.size() == nums.length)
        {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i ++)
        {
            if (i >0 && nums[i - 1] == nums[i] && used[i - 1] == false) continue;
            if (used[i] != false) continue;

            used[i] = true;
            path.add(nums[i]);
            backtracing(nums);
            path.remove(path.size() - 1);
            used[i] = false;
        }
    }
    public List<List<Integer>> permuteUnique(int[] nums) {
        if (nums.length == 0) return res;
        used = new boolean[nums.length];
        Arrays.sort(nums);
        backtracing(nums);
        return res;
    }
    
}

图论额外拓展

力扣相关题目:
332.重新安排行程

递归遍历(Java代码):

class Solution {
	// 332.重新安排行程
    private Deque<String> res;
    private Map<String, Map<String, Integer>> map;

    private boolean backTracking(int ticketNum){
        if(res.size() == ticketNum + 1){
            return true;
        }
        String last = res.getLast();
        if(map.containsKey(last)){//防止出现null
            for(Map.Entry<String, Integer> target : map.get(last).entrySet()){
                int count = target.getValue();
                if(count > 0){
                    res.add(target.getKey());
                    target.setValue(count - 1);
                    if(backTracking(ticketNum)) return true;
                    res.removeLast();
                    target.setValue(count);
                }
            }
        }
        return false;
    }

    public List<String> findItinerary(List<List<String>> tickets) {
        map = new HashMap<String, Map<String, Integer>>();
        res = new LinkedList<>();
        for(List<String> t : tickets){
            Map<String, Integer> temp;
            if(map.containsKey(t.get(0))){
                temp = map.get(t.get(0));
                temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);
            }else{
                temp = new TreeMap<>();//升序Map
                temp.put(t.get(1), 1);
            }
            map.put(t.get(0), temp);

        }
        res.add("JFK");
        backTracking(tickets.size());
        return new ArrayList<>(res);
    }
    
}

棋盘问题

力扣相关题目:
51. N皇后(hot100)

递归遍历(Java代码):

class Solution {
	// 51. N皇后
    List<List<String>> res = new ArrayList<>();

    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        for (char[] c : chessboard) {
            Arrays.fill(c, '.');
        }
        backTrack(n, 0, chessboard);
        return res;
    }


    public void backTrack(int n, int row, char[][] chessboard) {
        if (row == n) {
            res.add(Array2List(chessboard));
            return;
        }

        for (int col = 0;col < n; ++col) {
            if (isValid (row, col, n, chessboard)) {
                chessboard[row][col] = 'Q';
                backTrack(n, row+1, chessboard);
                chessboard[row][col] = '.';
            }
        }

    }
    public List Array2List(char[][] chessboard) {
        List<String> list = new ArrayList<>();

        for (char[] c : chessboard) {
            list.add(String.copyValueOf(c));
        }
        return list;
    }


    public boolean isValid(int row, int col, int n, char[][] chessboard) {
        // 检查列
        for (int i=0; i<row; ++i) { // 相当于剪枝
            if (chessboard[i][col] == 'Q') {
                return false;
            }
        }

        // 检查45度对角线
        for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }

        // 检查135度对角线
        for (int i=row-1, j=col+1; i>=0 && j<=n-1; i--, j++) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }
    
}

hot100其他问题

22. 括号生成

递归遍历(Java代码):

class Solution {
	public void dfs_generateParenthesis(List<String> ans, StringBuffer curr, int open, int close, int max)
    {
        if (curr.length() == max*2)
        {
            ans.add(curr.toString());
            return;
        }
        if (open < max)
        {
            curr.append('(');
            dfs_generateParenthesis(ans, curr, open + 1, close, max);
            curr.deleteCharAt(curr.length() - 1);
        }
        if (close < open)
        {
            curr.append(')');
            dfs_generateParenthesis(ans, curr, open, close + 1, max);
            curr.deleteCharAt(curr.length() - 1);
        }
    }

    public List<String> generateParenthesis(int n) {

        List<String> ans = new ArrayList<>();
        dfs_generateParenthesis(ans,new StringBuffer(),0,0,n);
        return ans;
    }  
}

79. 单词搜索

递归遍历(Java代码):

class Solution {
	boolean[][] visited;
    public boolean traverse(char[][] board, String word, int i, int j, int index) {
        if (board[i][j] != word.charAt(index)) {
            
            return false;
        }
        if (index == word.length() - 1) {
            
            return true;
        }
        int[][] directions = {{0,1}, {0,-1}, {1,0}, {-1,0}};
        visited[i][j] = true;
        boolean result = false;
        for (int[] direction : directions) {
            int newi = i + direction[0], newj = j + direction[1];
            if (newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length) {
                if (! visited[newi][newj]) {
                    boolean flag = traverse(board, word, newi, newj, index + 1);
                    if (flag) {
                        result = true;
                    }
                }
            }
        }
        visited[i][j] = false;
        return result;
    }
    public boolean exist(char[][] board, String word) {
        int m = board.length, n = board[0].length;
        visited = new boolean[m][n];
        boolean result = false;
        for (int i = 0; i < m; i ++) {
            for (int j = 0; j < n; j ++) {
                boolean flag = traverse(board, word, i, j, 0);
                if (flag) {
                    return flag;
                }
            }
        }
        return result;

    } 
}

回溯问题总结

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 棋盘问题:N皇后,解数独等等
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值