题目思路:
这道题目就是很典型的回溯类题目。
回溯其实也是暴力解法,但是又一些题目可以通过剪枝对算法进行优化,这道题目要找出所有的排列,其实还是比较简单的。
算法的思路主要就是:选择与撤销
例如: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数组的长度。
空间复杂度:。返回的结果的空间。

京公网安备 11010502036488号