刷题:backtrace

  • 排列用visited数组,每次从[0, len)
  • 组合用start控制重复
  • 子集问题和组合问题类似,去重方式类似,只不过添加结果的时机不一致
    77. 组合
    // 注意start的含义,以及剪枝处理
    class Solution {
        private List<List<Integer>> res = new ArrayList<>();
        private int n;
        private int k;
        private List<Integer> trace = new ArrayList<>();

        public List<List<Integer>> combine(int n, int k) {
            this.n = n;
            this.k = k;
            dfs(1);
            return res;
        }

        private void dfs(int start) {
            if (trace.size() == k) {
                res.add(new ArrayList<>(trace));
                return;
            }
            for (int i = start; i <= n - (k - trace.size()) + 1; i++) {
                trace.add(i);
                dfs(i+1);
                trace.remove(trace.size() - 1);
            }
        }
    }

39. 组合总和

    // 注意可以去重复的处理,以及求和的处理
    class Solution {
        private List<List<Integer>> res = new ArrayList<>();
        private List<Integer> path = new ArrayList<>();
        private int target;
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            this.target = target;
            dfs(candidates, 0, 0);
            return res;
        }

        private void dfs(int[] nums, int sum, int start) {
            if (sum > target) return;

            if (sum == target) {
                res.add(new ArrayList<>(path));
                return;
            }

            for (int i = start; i < nums.length; i++) {    // 不能从0开始
                path.add(nums[i]);
                sum += nums[i];
                dfs(nums, sum, i);
                path.remove(path.size()-1);
                sum -= nums[i];
            }
        }
    }

40. 组合总和 II

    // 排序后用set也可以去重,但是会超时,最后几个用例通不过
    class Solution {
        private List<List<Integer>> res = new ArrayList<>();
        private List<Integer> path = new ArrayList<>();
        private int target;
        public List<List<Integer>> combinationSum2(int[] candidates, int target) {
            this.target = target;
            Arrays.sort(candidates);
            dfs(0, 0, candidates);
            return res;
        }

        private void dfs(int start, int sum, int[] nums) {
            if (sum > target) {
                return;
            }
            if (sum == target) {
                res.add(new ArrayList<>(path));
                return;
            }
            for (int i = start; i < nums.length; i++) {
                if (i > start && nums[i] == nums[i - 1]) continue; // 此题去重的精髓所在

                path.add(nums[i]);
                sum += nums[i];
                dfs(i + 1, sum, nums);
                path.remove(path.size() - 1);
                sum -= nums[i];
            }
        }
    }

216. 组合总和 III

    class Solution {
        private List<Integer> path = new ArrayList<>();
        private List<List<Integer>> res = new ArrayList<>();
        private int k;
        private int n;
        public List<List<Integer>> combinationSum3(int k, int n) {
            this.k = k;
            this.n = n;
            dfs(1, 0);
            return res;
        }
        private void dfs(int start, int sum) {
            if (path.size() > k || sum > n) {
                return;
            }
            if (path.size() == k && sum == n) {
                res.add(new ArrayList<>(path));
                return;
            }
            for (int i = start; i <= 9 - (k - path.size()) + 1; i++) {
                path.add(i);
                sum += i;
                dfs(i+1, sum);
                path.remove(path.size()-1);
                sum -= i;
            }
        }
    }

46. 全排列

    class Solution {
        private List<List<Integer>> res =  new ArrayList<>();
        private List<Integer> path = new ArrayList<>();
        private boolean[] flag = new boolean[7];
        public List<List<Integer>> permute(int[] nums) {
            dfs(nums);
            return res;
        }
        private void dfs(int[] nums) {
            if (path.size() == nums.length) {
                res.add(new ArrayList<>(path));
            }

            for (int i = 0; i < nums.length; i++) {    // 每次都是从0开始,在path中的打标记
                if (flag[i]) continue;

                path.add(nums[i]);
                flag[i] = true;
                dfs(nums);
                path.remove(path.size()-1);
                flag[i] = false;
            }
        }
    }

47. 全排列 II

    // 用set去重也可以,能通过,但是效率较低
    class Solution {
        private List<List<Integer>> res = new ArrayList<>();
        private List<Integer> path = new ArrayList<>();

        public List<List<Integer>> permuteUnique(int[] nums) {
            boolean[] flag = new boolean[nums.length];
            Arrays.sort(nums);
            dfs(nums, flag);
            return res;
        }

        private void dfs(int[] nums, boolean[] flag) {
            if (path.size() == nums.length) {
                res.add(new ArrayList<>(path));
                return;
            }

            for (int i = 0; i < nums.length; i++) {
                if (flag[i]) continue;
                if (i > 0 && nums[i-1] == nums[i] && !flag[i-1]) continue;    // 注意这里的判断,相比组合多了一个flag判断
                path.add(nums[i]);
                flag[i] = true;
                dfs(nums, flag);
                path.remove(path.size()-1);
                flag[i] = false;
            }
        }
    }

784. 字母大小写全排列,*

// 每个dfs函数完成一定的任务,不一定都有for循环
class Solution {
    private List<String> res = new ArrayList<>();
    public List<String> letterCasePermutation(String s) {
        char[] chs = s.toCharArray();
        bfs(chs, 0);
        return res;
    }

    private void dfs(char[] chs, int pos) {
        if (pos == chs.length) {
            res.add(new String(chs));
            return;
        }
        if (Character.isDigit(chs[pos])) {
            dfs(chs, pos+1);
            return;
        }
        chs[pos] = Character.toLowerCase(chs[pos]);
        dfs(chs, pos+1);

        chs[pos] = Character.toUpperCase(chs[pos]);
        dfs(chs, pos+1);
    }
}

78. 子集

// 和组合问题一样,要有个start,注意start的含义
class Solution {
    private List<List<Integer>> res = new ArrayList<>();
    private List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        dfs(nums, 0);
        return res;
    }

    private void dfs(int[] nums, int start) {
        res.add(new ArrayList<>(path));    // 注意要在return之前加入集合
        if (start == nums.length) return;

        for (int i = start; i < nums.length; i++) {
            path.add(nums[i]);
            dfs(nums, i+1);
            path.remove(path.size()-1);
        }
    }
}

90. 子集 II

// 注意排序+去重的方式
class Solution {
    private List<List<Integer>> res =  new ArrayList<>();
    private List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        dfs(nums, 0);
        return res;
    }

    private void dfs(int[] nums, int start) {
        res.add(new ArrayList<>(path));
        if (start == nums.length) return;

        for (int i = start; i < nums.length; i++) {
            if (i > start && nums[i] == nums[i-1]) continue;  // 和组合问题的去重一致
            path.add(nums[i]);
            dfs(nums, i+1);
            path.remove(path.size()-1);
        }
    }
}

17. 电话号码的字母组合

    // 典型回溯法,排列问题
    class Solution {
        private String[] mapArr = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        private List<String> res = new ArrayList<>();
        private StringBuilder path = new StringBuilder();

        public List<String> letterCombinations(String digits) {
            if (digits.length() == 0) return res;
            dfs(digits.toCharArray(), 0);
            return res;
        }

        private void dfs(char[] digits, int pos) {
            if (pos == digits.length) {
                res.add(path.toString());
                return;
            }
            String curStr = mapArr[digits[pos] - '0'];

            for (char curChar : curStr.toCharArray()) {
                path.append(curChar);
                dfs(digits, pos+1);
                path.deleteCharAt(path.length()-1);
            }
        }
    }

93. 复原 IP 地址,*

    // 还可以用三重循环去搜索这个问题
    class Solution {
        List<String> res = new ArrayList<>();

        public List<String> restoreIpAddresses(String s) {
            if (s.length() > 12) return res;
            backtrace(new StringBuilder(), s, 0, 0);
            return res;
        }

        public void backtrace(StringBuilder path, String s, int start, int index) {
            if (index == 4) {
                if (path.length() - 3 == s.length()) res.add(path.toString());
                return;
            }
            for (int i = 1; i <= 3 && i <= s.length() - start; i++) {
                String sub = s.substring(start, start + i);
                if (!valid(sub)) {
                    continue;
                }
                if (path.length() != 0) {
                    sub = "." + sub;
                }
                path.append(sub);
                backtrace(path, s, start + i, index + 1);
                path.delete(path.length() - sub.length(), path.length());  // 值得注意
            }
        }

        public boolean valid(String s) {
            if (s.length() > 1 && s.charAt(0) == '0') return false;
            if (Integer.valueOf(s) > 255) return false;
            return true;
        }
    }

131. 分割回文串

    // 搜索算法,搜索到终止条件即满足题意
    class Solution {
        List<List<String>> res = new ArrayList<>();
        public List<List<String>> partition(String s) {
            backtrace(s, 0, new ArrayList<String>());
            return res;
        }

        public void backtrace(String s, int start, ArrayList<String> path) {
            if (start == s.length()) {
                res.add(new ArrayList<>(path));
                return;
            }

            for (int i = 1; i <= s.length() - start; i++) {
                String subStr = s.substring(start, start+i);
                if (!valid(subStr)) {
                    continue;
                }
                path.add(subStr);
                backtrace(s, start+i, path);
                path.remove(path.size()-1);
            }
        }

        public boolean valid(String s) {
            if (s.length() == 0) return false;
            int begin = 0;
            int end = s.length() - 1;
            while (begin < end) {
                if (s.charAt(begin) != s.charAt(end)) {
                    return false;
                }
                begin++;
                end--;
            }
            return true;
        }
    }

51. N 皇后

    // 按步骤来其实不难,怎么校验是关键
    class Solution {
        List<List<String>> res = new ArrayList<>();
        public List<List<String>> solveNQueens(int n) {
            char[][] board = new char[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    board[i][j] = '.';
                }
            }
            dfs(board, 0);
            return res;
        }

        private void dfs(char[][] board, int colIndex) {
            if (colIndex == board.length) {
                res.add(constructStr(board));
                return;
            }

            for (int i = 0; i < board.length; i++) {
                if (isValid(board, i, colIndex)) {
                    board[i][colIndex] = 'Q';
                    dfs(board, colIndex + 1);
                    board[i][colIndex] = '.';
                }
            }
        }
        // 怎么校验是关键
        private boolean isValid(char[][] board, int x, int y) {
            for (int i = 0; i < board.length; i++) {
                for (int j = 0; j < y; j++) {
                    if (board[i][j] == 'Q' && (x == i || x + j == y + i || x + y == i + j))
                        return false;
                }
            }
            return true;
        }

        private List<String> constructStr(char[][] board) {
            List<String> str = new ArrayList<>();
            for (int i = 0; i < board.length; i++) {
                str.add(new String(board[i]));
            }
            return str;
        }
    }

22. 括号生成

    // 自己想的,超过100%
    class Solution {
        private int left;
        private int right;
        private List<String> res = new ArrayList<>();
        private StringBuilder path = new StringBuilder();
        public List<String> generateParenthesis(int n) {
            this.left = n;
            this.right = n;
            dfs();
            return res;
        }

        private void dfs() {
            if (left > right) return;
            if (left == 0) {
                int tmp = right;
                while (tmp-- > 0) {
                    path.append(')');
                }
                res.add(path.toString());
                return;
            }
            int len = path.length();
            path.append('(');
            left--;
            dfs();
            path.delete(len, path.length());
            left++;

            path.append(')');
            right--;
            dfs();
            path.delete(len, path.length());
            right++;
        }
    }

37. 解数独

    // 注意递归函数的结构,结束的判断条件,怎么递归,有效值的判断。
    class Solution {
        public void solveSudoku(char[][] board) {
            dfs(board, 0, 0);
        }

        private boolean dfs(char[][] board, int row, int col) {
            if (col == board[0].length) {
                col = 0;
                row += 1;
            }
            if (row == board.length) return true;

            if (board[row][col] != '.') return dfs(board, row, col + 1);

            for (int i = '1'; i <= '9'; i++) {
                if (isValid(board, row, col, (char)i)) {
                    board[row][col] = (char)i;
                    if (dfs(board, row, col+1)) return true;
                    board[row][col] = '.';
                }
            }
            return false;
        }

        private boolean isValid(char[][] board, int row, int col, char value) {
            for (int i = 0; i < 9; i++) {
                if (board[row][i] != '.' && board[row][i] == value) return false;
                if (board[i][col] != '.' && board[i][col] == value) return false;
            }
            int boxRow = row / 3;
            int boxCol = col / 3;
            for (int i = boxRow * 3; i < (boxRow+1) * 3; i++) {
                for (int j = boxCol * 3; j < (boxCol+1) * 3; j++) {
                    if (board[i][j] != '.' && board[i][j] == value) return false;
                }
            }
            return true;
        }
    }

79. 单词搜索

class Solution {
    private char[][] board;
    private String word;
    private int m;
    private int n;
    public boolean exist(char[][] board, String word) {
        this.board = board;
        this.word = word;
        this.m = board.length;
        this.n = board[0].length;
        boolean[][] visit = new boolean[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dfs(i, j, 0, visit)) return true;
            }
        }
        return false;
    }

    private boolean dfs(int x, int y, int index, boolean[][] visit) {
        if (index == word.length()) return true;
        if (x < 0 || x >= m || y < 0 || y >= n || visit[x][y]) return false;
        if (board[x][y] != word.charAt(index)) return false;
        visit[x][y] = true;
        
        if (dfs(x+1, y, index+1, visit) || dfs(x-1, y, index+1, visit) || dfs(x, y+1, index+1, visit) || dfs(x, y-1, index+1, visit))
            return true;
        visit[x][y] = false;
        return false;
    }
}

89. 格雷编码

95. 不同的二叉搜索树 II

113. 路径总和 II

126. 单词接龙 II

140. 单词拆分 II

212. 单词搜索 II

254. 因子的组合

    class Solution {
        public List<List<Integer>> getFactors(int n) {
            return dfs(n, 2);
        }

        private List<List<Integer>> dfs(int n, int start) {

            List<List<Integer>> res = new ArrayList<>();

            for (int i = start; i * i <= n; i++) {
                if (n % i == 0) {
                    List<Integer> cur = new ArrayList<>();
                    cur.add(i);
                    cur.add(n / i);
                    res.add(cur);
                    for (List<Integer> list : dfs(n / i, i)) {
                        list.add(i);
                        res.add(list);
                    }
                }
            }
            return res;
        }
    }

267. 回文排列 II

    // 哭了,忍着焦躁的心情AC这题,超过100%。这其实是个全排列问题,排列好前面的n/2个字符,注意排列问题的去重
    class Solution {
        private List<String> res = new ArrayList<>();
        private char[] halfChs;
        private char single;
        public List<String> generatePalindromes(String s) {
            int[] count = new int[26];
            for (char c : s.toCharArray()) count[c-'a']++;
            boolean flag = s.length() % 2 == 0;
            char[] chs = new char[s.length() / 2];
            char single = ' ';
            int k = 0;
            for (int i = 0; i < 26; i++) {
                if (count[i] % 2 == 1) {
                    single = (char)(i + 'a');
                    if (flag) return res;
                    flag = true;
                }
                for (int j = 0; j < count[i] / 2; j++) {
                    chs[k++] = (char)(i + 'a');
                }
            }
            halfChs = chs;
            this.single = single;
            dfs(new char[s.length()], 0, new boolean[s.length()/2]);
            return res;
        }

        private void dfs(char[] path, int start, boolean[] visit) {
            if (start == path.length / 2) {
                res.add(conStr(path));
                return;
            }
            for (int i = 0; i < halfChs.length; i++) {
                if (visit[i]) continue;              // 选过的不选
                if (i > 0 && halfChs[i] == halfChs[i-1] && !visit[i-1]) continue;  // 前一个没选且等于前一个才continue,注意!!!
                path[start] = halfChs[i];
                visit[i] = true;
                dfs(path, start+1, visit);
                visit[i] = false;
            }
        }

        private String conStr(char[] path) {
            for (int i = 0; i < path.length / 2; i++) {
                path[path.length - i - 1] = path[i];
            }
            if (path.length % 2 == 1) {
                path[path.length / 2] = single;
            }
            return new String(path);
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容