算法通关村第十八关 | 白银 | 回溯热门问题

1.组合总和问题

原题:力扣39.

元素可以重复拿取,且题目的测试用例保证了组合数少于 150 个。

class CombinationSum {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        dfs(candidates, 0, target);
        return res;
    }

    // u 是开始遍历的索引
    public void dfs(int[] c, int u, int target) {
        if (target < 0) {
            return;
        }
        if (target == 0) {
            res.add(new ArrayList(path));
            return;
        }
        // 由于元素可以重复,所以 i 从传入的 i 开始继续遍历
        for (int i = u; i < c.length; i++) {
            if (c[i] <= target) {
                path.add(c[i]);
                dfs(c, i, target - c[i]);
                path.remove(path.size() - 1);
            }
        }
    }
}

2.分割回文串

原题:力扣131.

先取第一个数与后边分开,再取前两个数与后边分开,以此类推…

class Pritition {
    List<List<String>> lists = new ArrayList<>();
    Deque<String> deque = new LinkedList<>();

    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return lists;
    }

    // 回溯算法
    private void backTracking(String s, int startIndex) {
        // 已经遍历完成,可以作为一组方案添加到 lists 里面
        if (startIndex >= s.length()) {
            lists.add(new ArrayList(deque));
            return;
        }
        
        for (int i = startIndex; i < s.length(); i++) {
            if (isPalindrome(s, startIndex, i)) {
                String str = s.substring(startIndex, i + 1);
                deque.addLast(str);
            } else {
                continue;
            }
            backTracking(s, i + 1);
            deque.removeLast();
        }
    }

    // 判断回文串
    private boolean isPalindrome(String s, int startIndex, int end) {
        for (int i = startIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }
}

3.子集问题

原题:力扣78.

子集问题往往是要取得所有结果,所以不需要找到满足条件的结果或者剪枝。

class Subsets {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();

    public List<List<Integer>> subsets(int[] nums) {
        // 空集合
        if (nums.length == 0) {
            result.add(new ArrayList<>());
            return result;
        }
        subsetsHelper(nums, 0);
        return result;
    }
    private void subsetsHelper(int[] nums, int startIndex) {
        // 之前的可以作为一个子集
        result.add(new ArrayList<>(path));
        if (startIndex >= nums.length) {
            return;
        }
        for (int i = startIndex; i < nums.length; i++) {
            path.add(nums[i]);
            subsetsHelper(nums, i + 1);
            path.removeLast();
        }
    }
}

4.排列问题

原题:力扣46.

class Permute {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
        if (nums.length == 0) {
            return result;
        }
        used = new boolean[nums.length];
        permuteHelper(nums);
        return result;
    }
    private void permuteHelper(int [] nums) {
        // 排列完了,加入结果
        if (path.size() == nums.length) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (used[i]) {
                continue;
            }
            used[i] = true;
            path.add(nums[i]);
            permuteHelper(nums);
            path.removeLast();
            used[i] = false;
        }
    }
}

5.字母大小写全排列

原题:力扣784.

数字是干扰项,排除就好。字母的大小写转换用异或 32 来操作。

class LetterCasePermutation {
    public List<String> letterCasePermutation(String s) {
        List<String> ans = new ArrayList<String>();
        dfs(s.toCharArray(), 0, ans);
        return ans;
    }
    public void dfs(char[] arr, int pos, List<String> res) {
        while (pos < arr.length && Character.isDigit(arr[pos])) {
            pos++;
        }
        if (pos == arr.length) {
            res.add(new String(arr));
            return;
        }
        arr[pos] ^= 32;
        dfs(arr, pos + 1, res);
        arr[pos] ^= 32;
        dfs(arr, pos + 1, res);
    }
}

6.单词搜索

原题:力扣79.

class Exist {
    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;
    }
    // k 代表了 word 取到了第几个字符
    boolean dfs(char[][] board, char[] words, int i, int j, int k) {
        if (i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != words[k]) {
            return false;
        }
        // 终止条件
        if (k == words.length - 1) {
            return true;
        }
        // 改成终止符,不允许再次访问
        board[i][j] = '\0';
        boolean res = dfs(board, words, i + 1, j, k + 1) || 
        				dfs(board, words, i - 1, j, k + 1) ||
                        dfs(board, words, i, j + 1, k + 1) || 
        				dfs(board, words, i, j - 1, k + 1);
        // 使用后再改回来,即前面题目的 remove 操作
        board[i][j] = words[k];
        return res;
    }
}

如果对您有帮助,请点赞关注支持我,谢谢!
如有错误或者不足之处,敬请指正!
个人主页:星不易
算法通关村专栏:不易|算法通关村

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值