算法——LeetCode46. 全排列

46. 全排列

原题链接

题目:

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

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

题解1:回溯法(递归DFS)

主要流程:

  • 每个状态结点的信息包含 输入数组,结果集,使用标记数组,当前列表

  • 这里注意在每次递归搜索完成后需要恢复状态,撤销之前的操作,避免影响其他结点

  • 具体搜素过程图参考如下:
    在这里插入图片描述

  • 在具体代码中体现的状态重置,就是在递归后加上对状态的恢复。由于是求全排列,每个数只能使用一次,所以这里主要涉及到的状态恢复是每次递归完成后对标记数组 boolean[] used 的状态恢复,即回退操作。

代码:

    class Solution {
        List<List<Integer>> ans;// 定义结果集

        public List<List<Integer>> permute(int[] nums) {
            this.ans = new ArrayList<>();
            boolean[] used = new boolean[nums.length];
            Arrays.fill(used, false);
            dfs(nums, used, new ArrayList<>());
            return ans;
        }

        // 参数为输入数组, 使用标记数组, 当前路径列表
        public void dfs(int[] nums, boolean[] used, List<Integer> paths) {
            if (paths.size() == nums.length) {
                ans.add(new ArrayList(paths));
                return;
            }
            for (int i = 0; i < nums.length; i++) {
                if (!used[i]) {//未被搜索过
                    used[i] = true;
                    paths.add(nums[i]);
                    dfs(nums, used, paths);
                    paths.remove(paths.size() - 1);// 恢复状态, 回溯
                    used[i] = false;
                }
            }
        }
    }

或每次多复制一个数组, 代码如下:

    class Solution {
        public List<List<Integer>> permute(int[] nums) {
            List<List<Integer>> res = new ArrayList<>();
            boolean[] used = new boolean[nums.length];
            Arrays.fill(used, false);
            findArrange(nums, res, used, new ArrayList<>());
            return res;
        }

        //递归函数,参数有输入数组,结果集,使用标记数组,当前列表
        public void findArrange(int[] nums, List<List<Integer>> res, boolean[] used, List<Integer> list) {
            //递归终止判断
            if (list.size() == nums.length) {
                res.add(list);
                return;
            } else {
                for (int i = 0; i < nums.length; i++) {
                    if (!used[i]) {//未被搜索过
                        used[i] = true;
                        List<Integer> tempList = new ArrayList<>(list);
                        tempList.add(nums[i]);
                        findArrange(nums, res, used, tempList);
                        used[i] = false;//最后需要将状态改为原来状态回溯
                    }
                }
            }

        }

    }

题解2:题解1的不用状态恢复版本

主要思路:

  • 每次都创建一个新的数组来保存遍历过的数,但这样空间开销会更大
  • 该方法每一次尝试都「复制」,则不需要回溯,即不需要状态恢复
  • 大部分代码类似题解1

代码:

    class Solution {
        public List<List<Integer>> permute(int[] nums) {
            List<List<Integer>> res = new ArrayList<>();
            boolean[] used = new boolean[nums.length];
            Arrays.fill(used, false);
            findArrange(nums, res, used, new ArrayList<>());
            return res;
        }

        //递归函数,参数有输入数组,结果集,使用标记数组,当前列表
        public void findArrange(int[] nums, List<List<Integer>> res, boolean[] used, List<Integer> list) {
            //递归终止判断
            if (list.size() == nums.length) {
                res.add(list);
                return;
            } else {
                for (int i = 0; i < nums.length; i++) {
                    if (!used[i]) {//未被搜索过
                        //在每个结点状态都创建一个状态数组,这样不用在递归结束后恢复状态
                        boolean[] tempUsed = new boolean[used.length];
                        System.arraycopy(used, 0, tempUsed, 0, used.length);
                        tempUsed[i] = true;
                        List<Integer> tempList = new ArrayList<>(list);
                        tempList.add(nums[i]);
                        findArrange(nums, res, tempUsed, tempList);
                    }
                }
            }

        }

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值