题目

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

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

思路

排列类型的题目,暴力穷举所有排列,想到用回溯。

回溯模板

  1. def backtrack(nums, path){
  2. // base case
  3. if (路径走完) {
  4. 讲路劲加入结果集;
  5. return;
  6. }
  7. 遍历所有选择
  8. 选择1加入path
  9. backtrack(nums, path)
  10. 选择1移出path
  11. }

代码

  1. class Solution {
  2. List<List<Integer>> res = new ArrayList<>();
  3. public List<List<Integer>> permute(int[] nums) {
  4. backtrack(nums, new ArrayList<Integer>());
  5. return res;
  6. }
  7. public void backtrack(int[] nums, List<Integer> path) {
  8. // base case
  9. if (path.size() == nums.length) {
  10. res.add(new ArrayList<>(path));
  11. return;
  12. }
  13. // 遍历所有选择
  14. for (int n : nums) {
  15. if (path.contains(n)) { // 检查path中是否有n话费O(n)时间,耗时,可以优化
  16. continue;
  17. }
  18. path.add(n);
  19. backtrack(nums, path);
  20. path.remove(path.size() - 1);
  21. }
  22. }
  23. }

优化

上面的代码用arrayList.contains(n)检查path中是否已经有当前元素n,耗时O(n)。
如果改用visited数组,用空间换时间,可以优化这一部分的时间为O(1)。

  1. class Solution {
  2. List<List<Integer>> res = new ArrayList<>();
  3. public List<List<Integer>> permute(int[] nums) {
  4. backtrack(nums, new ArrayList<Integer>(), new boolean[nums.length]);
  5. return res;
  6. }
  7. public void backtrack(int[] nums, List<Integer> path, boolean[] visited) {
  8. // base case
  9. if (path.size() == nums.length) {
  10. res.add(new ArrayList<>(path));
  11. return;
  12. }
  13. // 遍历所有选择
  14. for (int i = 0; i < nums.length; i++) {
  15. if (visited[i]) { // 用visited数组判断是否用过当前选择,O(1)
  16. continue;
  17. }
  18. path.add(nums[i]);
  19. visited[i] = true;
  20. backtrack(nums, path, visited);
  21. path.remove(path.size() - 1);
  22. visited[i] = false;
  23. }
  24. }
  25. }