题目思路:
这道题目就是很典型的回溯类题目。
回溯其实也是暴力解法,但是又一些题目可以通过剪枝对算法进行优化,这道题目要找出所有的排列,其实还是比较简单的。
算法的思路主要就是:选择与撤销
例如:1开头的有,[1,2,3],接着3撤销,2撤销,然后选择3,再选择2,就有了[1,3,2]。
整体用一个图来观看整个过程
图片说明

方法一:递归

import java.util.*;
public class Solution {
    // 存所有排列的集合
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    public ArrayList<ArrayList<Integer>> permute(int[] num) {
        // 存一种排列
        LinkedList<Integer> list = new LinkedList<>();
        // 递归进行
        backTrack(num,list);
        return res;
    }

    public void backTrack(int[] num, LinkedList<Integer> list){
        // 当list中的长度等于数组的长度,则证明此时已经找到一种排列了
        if(list.size() == num.length){
            // add进返回结果集中
            res.add(new ArrayList<>(list));
            return;
        }
        // 遍历num数组
        for(int i = 0; i < num.length; i++){
            // 若当前位置中的数已经添加过了则跳过
            if(list.contains(num[i]))
                continue;
            // 选择该数
            list.add(num[i]);
            // 继续寻找
            backTrack(num,list);
            // 撤销最后一个
            list.removeLast();
        }
    }
}

复杂度分析:
时间复杂度:。n为num数组的长度。
空间复杂度:。返回的结果的空间。

方法二:不递归版
这种方法不使用递归,其实也是一个选择和撤销的过程,只是不使用递归来完成。
通过插入的方式,一次性找到所有的情况。
例如:第一次选择1,接着可以在1前面和后面插入2,则变为 1,2 和 2,1;接着可选择3,3插入到1,2中有三种分别为 3,1,2;1,3,2;1,2,3;然后3插入2,1也有三种。
其实就是找到能插的位置,同一个数可以插在不同的位置,则构成了另外的排列。
图片说明

public class Solution {
    // 所有的排列结果集
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    public ArrayList<ArrayList<Integer>> permute(int[] num) {
        ArrayList<Integer> list = new ArrayList<>();
        // 先对res中加入一个空的list,给第一次插入制造一个空间。
        res.add(list);
        // 整个循环的次数为num的元素个数
        for(int i = 0; i < num.length; i++){

            ArrayList<ArrayList<Integer>> tmp = new ArrayList<>();
            // 遍历此时的排列结果
            for(ArrayList<Integer> r:res){
                // 根据集合的大小,使用for循环在可插入的位置进行插入
                for(int j = 0; j < r.size()+1; j++){
                    // 在第j个位置插入
                    r.add(j,num[i]);
                    // 此时构成新的排列集合,可能是不完整的排列集合(例如:[1,2];[2,1]这类)
                    ArrayList<Integer> temp = new ArrayList<>(r);
                    // 放进去tmp临时集合中
                    tmp.add(temp);
                    // 将刚插入的数移除掉,为了将同样的这个插入不同的位置
                    r.remove(j);
                }
            }
            // 最后赋给res进行返回
            res = new ArrayList<>(tmp);
        } 
        return res;
    }
}

复杂度分析:
时间复杂度:。n为num数组的长度。
空间复杂度:。返回的结果的空间。