1.Description
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
2.Example
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
3.Solution
1.我的题解(麻烦,时间复杂度高)
在31题思路的基础上加了一点,用contains判断是否全部添加到list里了。
LeetCode 31.Next Permutation Medium/Array
class Solution {
public List<List<Integer>> permute(int[] nums) {
int l = nums.length;
int i = l-1;
int j = l-1;
List<List<Integer>> list1 = new ArrayList<List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
list = getList(nums,i,j);
do{
i = l-1;
j = l-1;
list1.add(list);
list = getList(nums,i,j);
}while(!list1.contains(list));
return list1;
}
public static void reverse(int[] nums,int i){//从i位置开始到最
//后进行从大到小排列
int l = nums.length-1;
while(i<l){
swap(nums,i,l);
i++;
l--;
}
}
public static void swap(int[] nums,int i,int j){
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
public static List<Integer> getList(int[] nums,int i,int j){
int l = nums.length;
List<Integer> list = new ArrayList<Integer>();
for(i = l-2;i>=0;--i){
if(nums[i]<nums[i+1])
break;
}
if(i>=0){
for(j = l-1;j>0;--j){
if(nums[i]<nums[j]){
swap(nums,i,j);
break;
}
}
}
reverse(nums,i+1);
for (int k = 0; k < nums.length; k++) {
list.add(nums[k]);
}
return list;
}
}
2.DFS(深度优先搜索)/回溯
DFS详细讲解:dfs介绍
定义一个布尔数组used来保存深度优先遍历时每一层的数是否被使用过,一开始都是null,代表都没有用过,每一层在用过之后给他赋值为true。
链表path代表每次走到树的底部的路线,在走的过程中不断将上一层没使用过的数加到链表path中,走到底部后path的值就是一个所求的结果。
在for循环中,for循环的次数是总共的深度(数组有多长就有几层),if条件语句判断是在递归中跳过数字已经被使用过的情况,遇到未使用过的说明到了递归的这一层了。
实现回溯是根据将使用过的boolean数组的元素重新设置为false,并且将path中添加进去的元素移除从而使状态回到之前(return的时候已经将path的记录给加到了res中,所以可以回去移除path中的元素了)。
dfs函数中返回的是new的对象而不是直接add(path)的原因:(传进去了一个拷贝)变量 path 所指向的列表 在深度优先遍历的过程中只有一份 ,深度优先遍历完成以后,回到了根结点,成为空列表。
在 Java 中,参数传递是 值传递,对象类型变量在传参的过程中,复制的是变量的地址。这些地址被添加到 res 变量,但实际上指向的是同一块内存地址,因此我们会看到 66 个空的列表对象。解决的方法很简单,在 res.add(path); 这里做一次拷贝即可。
public class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
// 使用一个动态数组保存所有可能的全排列
List<List<Integer>> res = new ArrayList<>();
if (len == 0) {
return res;
}
boolean[] used = new boolean[len];
List<Integer> path = new ArrayList<>();
dfs(nums, len, 0, path, used, res);
return res;
}
private void dfs(int[] nums, int len, int depth,
List<Integer> path, boolean[] used,
List<List<Integer>> res) {
if (depth == len) {
res.add(new ArrayList<>(path));//注意这里直接add(path)的话不对,因为在回溯的时候将path中的元素都remove了,
//到最后都是空的,所以需要记录下走到底后path的值,再回溯
return;
}
// 在非叶子结点处,产生不同的分支,这一操作的语义是:在还未选择的数中依次选择一个元素作为下一个位置的元素,这显然得通过一个循环实现。
for (int i = 0; i < len; i++) {
if (!used[i]) {
path.add(nums[i]);
used[i] = true;
dfs(nums, len, depth + 1, path, used, res);
// 注意:下面这两行代码发生 「回溯」,回溯发生在从 深层结点 回到 浅层结点 的过程,代码在形式上和递归之前是对称的
used[i] = false;
path.remove(path.size() - 1);
}
}
}
public static void main(String[] args) {
int[] nums = {1, 2, 3};
Solution solution = new Solution();
List<List<Integer>> lists = solution.permute(nums);
System.out.println(lists);
}
}