栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基础算法:递归/回溯相关题目

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基础算法:递归/回溯相关题目

回溯

递归、回溯思想类似bfs,通常需要考虑触底条件+回溯后处理(例如题目常有将临时结果记录的最后一项删除)

子集

在给定数组中做出所有子集。

public List> subsets(int[] nums) {
        List> res = new LinkedList<>();
        List temp = new LinkedList<>();
        backTrack(0, nums, res, temp);
        return res;
    }

    private void backTrack(int i, int[] nums, List> res, List temp) {
        res.add(new LinkedList<>(temp));
        for (int j = i; j < nums.length; j++) {
            temp.add(nums[j]);
            backTrack(j + 1, nums, res, temp);
            temp.remove(temp.size() - 1);
        }
    }

该题用递归回溯类似dfs,只不过是在一维数组内进行dfs,每次遇到新数加入结果,递归探到底,到底后回溯需要将结果数列最后一个数删掉。

子集 II
public List> subsetsWithDup(int[] nums) {
        List> res = new LinkedList<>();
        List temp = new LinkedList<>();
        boolean[] visited = new boolean[nums.length];
        if (nums.length == 0) {
            res.add(temp);
            return res;
        }
        Arrays.sort(nums);
        subSets(nums, 0, res, temp, visited);
        return res;
    }
    
    private void subSets(int[] nums, int index, List> res, List temp, boolean[] visited) {
        res.add(new LinkedList<>(temp));
        if (index >= nums.length) {
            return;
        }
        for (int i = index; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
                continue;
            }
            temp.add(nums[i]);
            visited[i] = true;
            subSets(nums, i + 1, res, temp, visited);
            visited[i] = false;
            temp.remove(temp.size() - 1);
        }
    }

相比于之前子集,添加了重复内容,所以需要进行重复性判断。
即使用visited进行是否遍历过的记录,当遇到新值nums[i] == nums[i - 1](前提需要数组排序)说明遇到两个相同值中的第二个,那么有两种情况:
①若i-1数值被用过,说明在重复的数据中,i下标值并不是重复数字中第一次出现,这次使用会出现类似12(i-1)2(i)这种情况,这样不会产生重复数据
②若i-1数值没被用,说明当前i下标值是重复数字中第一次出现,但在数组中并不是重复数字的头部,类似数组122,当第二个2想开始构造,就会与第一个2构造出的数据发生重复
所以由!visited[i - 1]划分出两个情况进行处理。

全排列 II

目的得出给定数组的全部排列组合可能。

public List> permuteUnique(int[] nums) {
        List> res = new LinkedList<>();
        if (nums.length == 0) {
            return res;
        }
        Arrays.sort(nums);

        boolean[] visited = new boolean[nums.length];
        dfs(nums, visited, nums.length, 0, new LinkedList<>(), res);
        return res;
    }

    private void dfs(int[] nums, boolean[] visited, int len, int size, List path, List> res) {
        if (size == len) {
            res.add(new LinkedList<>(path));
            return;
        }

        for (int i = 0; i < len; i++) {
            if (visited[i]) {
                continue;
            }
            if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
                continue;
            }

            path.add(nums[i]);
            visited[i] = true;

            dfs(nums, visited, len, size + 1, path, res);
            visited[i] = false;
            path.remove(path.size() - 1);
        }
    }

相比于子集的区别就是每次以新下标进行遍历时,仍要从头开始遍历,相关注意和子集II相同(相同字符判断等)

组合求和

给定数据数组和目标值,需要得出所有可以相加为目标值的组合。

public List> combinationSum(int[] candidates, int target) {
        List> res = new LinkedList<>();
        if (candidates.length == 0) {
            return res;
        }
        Arrays.sort(candidates);

        dfs(candidates, 0, target, new LinkedList<>(), res);

        return res;
    }

    private void dfs(int[] candidates, int begin, int target, List path, List> res) {
        if (target == 0) {
            res.add(new LinkedList<>(path));
            return;
        }

        for (int i = begin; i < candidates.length; i++) {
            if (target - candidates[i] < 0) {
                break;
            }

            path.add(candidates[i]);
            dfs(candidates, i, target - candidates[i], path, res);
            path.remove(path.size() - 1);
        }
    }

因为该题数字可以重复使用(即目标值为6,数组2 3,结果可以有{2,2,2}和{3,3})且没有重复元素,所以不用进行相同值判断的处理。
因为每次遍历由当前位置起始,所以遍历过的元素不会再次遍历,不用visited记录。

组合总和 II

相比上面题目,该题数字只能用一次

public List> combinationSum2(int[] candidates, int target) {
        List> res = new ArrayList<>();
        if (candidates.length == 0) {
            return res;
        }

        Arrays.sort(candidates);

        List path = new LinkedList<>();
        dfs(candidates, 0, target, path, res);
        return res;
    }

    private void dfs(int[] candidates, int begin, int target, List path, List> res) {
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = begin; i < candidates.length; i++) {
            if (target - candidates[i] < 0) {
                break;
            }

            if (i > begin && candidates[i] == candidates[i - 1]) {
                continue;
            }

            path.add(candidates[i]);

            dfs(candidates, i + 1, target - candidates[i], path, res);

            path.remove(path.size() - 1);
        }
    }

因为数字只能用一次且有重复数据,就要防止重复组合情况出现i > begin && candidates[i] == candidates[i - 1]。

电话号码的字母组合

给一串数字,得到其9键可组成的所有字串

public List letterCombinations(String digits) {
        List res = new LinkedList<>();
        if (digits.length() == 0) {
            return null;
        }
        String[] map = new String[] {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};

        backTrack(map, res, new StringBuilder(), digits, 0);

        return res;
    }

    private void backTrack(String[] map, List res, StringBuilder stringBuilder, String digits, int index) {
        if (stringBuilder.length() == digits.length()) {
            res.add(stringBuilder.toString());
            return;
        }

        for (char word : map[digits.charAt(index) - '2'].toCharArray()) {
            stringBuilder.append(word);
            backTrack(map, res, stringBuilder, digits, index + 1);
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        }
    }

该题目和之前求全排列类似,只是取字符换了一种方式,由String[] map = new String[] {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};方便后续依据数字取可能字符。

括号生成

给定n组左右括号,生成所有组合并且有效

public List generateParenthesis(int n) {
        List res = new LinkedList<>();
        StringBuilder temp = new StringBuilder();

        backTrack(res, temp, n, n);

        return res;
    }

    private void backTrack(List res, StringBuilder temp, int left, int right) {
        if (left == 0 && right == 0) {
            res.add(temp.toString());
            return;
        }

        if (left > right) {
            return;
        }

        if (left > 0) {
            backTrack(res, temp.append("("), left - 1, right);
            temp.deleteCharAt(temp.length() - 1);
        }
        if (right > 0) {
            backTrack(res, temp.append(")"), left, right - 1);
            temp.deleteCharAt(temp.length() - 1);
        }
    }

记录左右括号可用数,思路很简单,只需要注意剪枝条件:左括号可用大于右括号(其后无法完成匹配)

单词搜索

给定二维数组,查找其内部是否包含目标单词

public boolean exist(char[][] board, String word) {
        boolean[][] visited = new boolean[board.length][board[0].length];
        int[][] dir = new int[][] {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (backTrack(board, word, 0, visited, i, j, dir)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean backTrack(char[][] board, String word, int pos, boolean[][] visited, int row, int col, int[][] dir) {
        if (row < 0 || col < 0 || row >= board.length || col >= board[0].length || visited[row][col]) {
            return false;
        }

        if (board[row][col] != word.charAt(pos)) {
            return false;
        }

        if (pos == word.length() - 1) {
            return true;
        }
        visited[row][col] = true;

        for (int[] ints : dir) {
            if (backTrack(board, word, pos + 1, visited, row + ints[0], col + ints[1], dir)) {
                return true;
            }
        }
        visited[row][col] = false;

        return false;
    }

这个题就可以看出经典bfs思路,到新节点,四方向遍历查找。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/874533.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号