回溯法练习

全排列

给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

  1. 采用回溯法求解,采用深度遍历,递归方式
  2. 当递归的深度到达nums数组的长度时,说明所有的元素都被选择了一遍了,此时生成了一个排列。递归退出
  3. 遍历过程中需要记录哪些元素被访问过,使用 boolean[] used 数组记录。刚好 used 数组 能和 nums 数组一一对应。
  4. 遍历过程中需要记录遍历的路径,path
  5. 回溯,在进入时添加元素,在退出时(回溯)删除元素,采用:ArrayDeque 能方便地在末尾删除元素。
    代码如下:
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> resultList = new LinkedList<>();
        if (null == nums || nums.length == 0) {
            //边界条件
            return resultList;
        }

        //default false
        boolean[] used = new boolean[nums.length];

        //想象成一棵树,记录树的遍历路径,也即记录将nums中的哪些元素加入到了path
        List<Integer> path = new ArrayList<>(nums.length);
        dfs(nums, resultList, 0, new ArrayDeque<>(nums.length), used);

        return resultList;
    }

    private void dfs(int[] nums, List<List<Integer>> resultList, int depth, ArrayDeque<Integer> path, boolean[] used) {
        //递归退出条件,数组中所有的元素都选取了一遍之后,depth就等于nums.length
        if (depth == nums.length) {
            resultList.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            if (!used[i]) {
                path.addLast(nums[i]);
                used[i] = true;
                //递归遍历,深度加1
                dfs(nums, resultList, depth + 1, path, used);
                used[i] = false;
                path.removeLast();
            }
        }
    }    
}

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
https://leetcode.cn/problems/binary-tree-paths/description/
做这类题目的共同点:

  1. 递归函数怎么写,思考递归退出条件
  2. 是否需要记录某个节点已经被访问了
  3. 如何构造路径List,记录遍历的每个节点。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> resultList = new LinkedList<>();
        if (null == root) {
            //考虑边界条件
            return resultList;
        }
        dfs(root, resultList, new ArrayDeque<>());
        return resultList;
    }

    private void dfs(TreeNode root, List<String> resultList, ArrayDeque<Integer> pathList) {
        //递归退出条件
        if (root == null) {
            //当某个节点,它只有一个孩子时,比如 root.left为null, root.right不为null,但是root.right为叶子结点了。
            // 那么 dfs(root.left,resultList,pathList) 就会从这里返回,由于 root.right不为null, 因此这里不添加 root.val,而是直接 return
            //只有遍历到 dfs(root.right,resultList,pathList)时,此时 root.right 为叶子结点,在下面if中就会生成路径。
            return;
        }

        //递归退出条件,只有当左右孩子都为空时,该节点才能加入到路径。
        if (root.left == null && root.right == null) {
            //----------入-------------//
            //将叶子结点加入path
            pathList.addLast(root.val);
            StringBuilder sb = new StringBuilder();
            for (Integer value : pathList) {
                sb.append(value);
                sb.append("->");
            }
            //生成一条路径:比如:"1->3"
            resultList.add(sb.substring(0, sb.length() - 2));

            //----------出------------//
            pathList.removeLast();
            return;
        }

        //----------入-------------//
        pathList.addLast(root.val);//非叶子结点加入路径

        //递归遍历
        dfs(root.left, resultList, pathList);
        dfs(root.right, resultList, pathList);
        //----------出------------//
        pathList.removeLast();//非叶子结点出路径,回溯时避免有重复的元素。
    }  
}
posted @ 2023-08-05 23:09  大熊猫同学  阅读(17)  评论(0编辑  收藏  举报