LC46-全排列

160 阅读2分钟

题目名称:全排列

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

示例 1:

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

示例 2:

输入: nums = [0,1]
输出: [[0,1],[1,0]]

示例 3:

输入: nums = [1]
输出: [[1]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

思路分析

回溯就是不断抉择再撤销决策,再讲这两个大动作交给递归函数去做 和一般的递归一样,首先去明确递归函数需要做的是什么:

判断当前结果是否满足条件,若满足则将其加入结果list并返回 针对所有可选择的数,进入循环开始选择 判读当前数是否已经选过 若没选过,加入path并将新path带入下个递归 撤销选择,进入下一次循环

根据排列组合的性质进行解题,假设 List<List> subList为已求出的前n个数字的所有排列组合,那么里面的每个组合的list长度为n; 则当第n+1个数字(设为current)))加入的时候,此时的排列组合就是尝试将current加入到之前已有组合的0-n的位置,这样每个新生成的组合都是加入current之后的排列组合。

对于已有组合[1,2],当加入3的时候

  • 首先,将3放置在第0位,也就是[1,2]的前面,生成新的组合[3,1,2];
  • 其次,将3放置在第1位,也就是[1,2]的中间,生成新的组合[1,3,2];
  • 之后,将3放置在第2位,也就是[1,2]的后面,生成新的组合[1,2,3]。
  • 这样,对于已有组合[1,2]和3组成的新组合就获取到了,为[3,1,2],[1,3,2],[1,2,3];

对于每个已有的组合,将新加入的元素依次加入到组合的0-n位,这样,就能获取到全部的新组合。

Code实现

public List < List < Integer >> permute(int[] nums) {
    if (nums == null || nums.length == 0) {
        return new ArrayList < > ();
    }
    numbers = nums;
    return helper(nums.length - 1);
}

private int[] numbers;

private List < List < Integer >> helper(int index) {
    if (index == 0) {
        // index为0,那么此时的组合只有第一位数字
        List < Integer > list = Collections.singletonList(numbers[0]);
        return Collections.singletonList(list);
    }
    // 获取已有组合
    List < List < Integer >> subList = helper(index - 1);
    // 当前待加入的数字
    int currentNum = numbers[index];

    List < List < Integer >> ans = new ArrayList < > ();
    for (int i = 0; i <= index; i++) {
        for (List < Integer > currentSub: subList) {
            // 对于每个已有组合,将待加入数字插入到第i位,生成新的组合
            List < Integer > current = new ArrayList < > (currentSub.subList(0, i));
            current.add(currentNum);
            current.addAll(currentSub.subList(i, index));
            ans.add(current);
        }
    }
    return ans;
}

结果

Snipaste_2023-05-03_21-50-10.png

算法复杂度分析

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(n)O(n)