46.Permutations

题目链接:https://leetcode.com/problems/permutations/description/

题目大意:给出一串数组进行全排列(数组中数字唯一)。

法一:模板全排列代码标记数组版。代码如下(耗时5ms):

 1     public List<List<Integer>> permute(int[] nums) {
 2         List<List<Integer>> list = new ArrayList<List<Integer>>();
 3         int[] vis = new int[nums.length];
 4         Arrays.fill(vis, 0);//初始化全为0
 5         List<Integer> tmp = new ArrayList<Integer>();
 6         dfs(list, nums, vis, tmp);
 7         return list;
 8     }
 9     public static void dfs(List<List<Integer>> list , int[] nums, int[] vis, List<Integer> tmp) {
10     //    System.out.println(tmp);
11         if(tmp.size() == nums.length) {
12             list.add(new ArrayList<Integer>(tmp));//这里一定要用new之后再加入到list中,否则list集合会是空值
13             return;
14         }
15         for(int i =0 ; i < nums.length; i++) {
16             if(vis[i] == 0) {
17                 vis[i] = 1;
18                 tmp.add(nums[i]);
19                 dfs(list, nums, vis, tmp);
20                 tmp.remove(tmp.size() - 1);//删除最后一个元素
21                 vis[i] = 0;
22             }
23         }
24     }
View Code

另一种实现方式:模板java版,利用list属性来进行if条件判断,省去vis标记数组。代码如下(耗时26ms):

 1     public List<List<Integer>> permute(int[] nums) {
 2         List<List<Integer>> list = new ArrayList<List<Integer>>();
 3         List<Integer> tmp = new ArrayList<Integer>();
 4         dfs(list, nums, tmp);
 5         return list;
 6     }
 7     public static void dfs(List<List<Integer>> list , int[] nums, List<Integer> tmp) {
 8     //    System.out.println(tmp);
 9         if(tmp.size() == nums.length) {
10             if(!list.contains(tmp))
11                 list.add(new ArrayList<Integer>(tmp));//这里一定要用new之后再加入到list中,否则list集合会是空值
12             return;
13         }
14         for(int i =0 ; i < nums.length; i++) {
15             if(!tmp.contains(nums[i])) {
16                 tmp.add(nums[i]);
17                 dfs(list, nums, tmp);
18                 tmp.remove(tmp.size() - 1);//删除最后一个元素
19             }
20         }
21     }
View Code

当序列是{1,2,3}时运行结果如下:

[]
[1]
[1, 2]--->因为这里有if条件判断是否已经选1,所以这里不会再将第二个1加入到序列当中,而是跳过if条件直接执行i++操作,在第77题组合数中,这里的i不是从0开始,而是每次dfs时,传入一个起始下标,这样就可以不用if条件判断起始下标前面的数值是否已经存在,因为肯定不会重复选择。在排列中,不能用传入起始下标的办法,因为有可能当前要选的数的下标是在起始下标之前的,如果用起始下标的话,前面的数就没法选到。
[1, 2, 3]--->两次跳过if条件,执行i++
[1, 3]--->return执行remove,这里remove的是2,不是3,因为return回来的时候第三个数起始还没有加进去
[1, 3, 2]--->两次跳过if条件,执行i++
[2]--->return执行remove,这里remove的是1,原因同上
[2, 1]
[2, 1, 3]
[2, 3]
[2, 3, 1]
[3]
[3, 1]
[3, 1, 2]
[3, 2]
[3, 2, 1]
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

法二(借鉴):非递归,假设我们有了当前前 i 个元素的组合,当第 i+1个元素加入时,我们需要做的是将这个元素加入之前的每一个结果,并且放在每个结果的每个位置,因为之前的结果没有重复,所以加入新元素的结果也不会有重复,比如当前i个元素组合是1,当加入第2个元素的时候,可以选择加在1之前,也可以选择加在1之后,这样就可以得到两个不同序列,依次类推。代码如下(耗时7ms):

 1 public List<List<Integer>> permute(int[] nums) {
 2         LinkedList<List<Integer>> res = new LinkedList<List<Integer>>();//这里利用LinkedList,而不用ArrayList,因为这样可以利用LinkedList.add(i,n)在第i位加入数值
 3         res.add(new ArrayList<Integer>());//加一个空的list进去,以保证下面size在第一次的时候就为1,从而保证后面的计算
 4         for(int n : nums) {//遍历nums数组值
 5             for(int size = res.size(); size > 0; size--) {//查看结果集res中已经包含几个序列
 6                 List<Integer> tmp = res.pollFirst();//取出结果集res中的每个序列
 7                 for(int i = 0; i <= tmp.size(); i++) {//对于取出的序列,在每一个位置都尝试加入当前nums数组值,这里的i表示当前取出序列的第i位,比如取出的序列是1,接下来就要在1之前及1之后加入2
 8                     List<Integer> resIn = new ArrayList<Integer>(tmp);//每一个位置都新初始化刚取出的序列,这样保证能在原始取出序列的基础上增添数值
 9                     resIn.add(i, n);//在第i位加入数值n
10                     res.add(resIn);//这里因为每从结果集中取出一个序列就是清空一个序列,所以这里add的时候就是一个全新的序列
11                 }
12             }
13         }
14         return res;
15     }
View Code

当数组是{1,2,3}时,运行结果如下:

[1]
第一次取到{1}
[2, 1]
[1, 2]
第二次取到{2,1}
[3, 2, 1]
[2, 3, 1]
[2, 1, 3]
第三次取到{1,2}
[3, 1, 2]
[1, 3, 2]
[1, 2, 3]
[[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]

 法三:由31题http://www.cnblogs.com/cing/p/8001308.html,求解下一个排列所引发的这题的题解,也就是C++里STL所封装的nextPermutation函数用java实现后,其他的代码跟C++调用类似。注意在求解排列之前,数组要先升序排序,因为nextPermutation的false结果就是在数组是降序时结束。否则会导致求出的全排列不完全。代码如下(耗时6ms):

 1     public List<List<Integer>> permute(int[] nums) {
 2         List<List<Integer>> res = new ArrayList<List<Integer>>();
 3         Arrays.sort(nums);
 4         do {
 5             ArrayList<Integer> listIn = new ArrayList<Integer>();
 6             for(int i = 0; i < nums.length; i++) {
 7                 listIn.add(nums[i]);
 8             }
 9             //每求出一个全排列就加入res结果集中
10             //由下面的尝试得知,必须要new一个新的,原因不是新开辟了一个地址空间,而是为了让原地址空间不被垃圾回收器回收,而导致原数据无法读取的情况
11             //listIn和a的地址空间相同
12 /*            System.out.println(listIn.hashCode());
13             List<Integer> a = new ArrayList<Integer>(listIn);
14             System.out.println(a.hashCode());
15             res.add(a);*/
16             res.add(new ArrayList<Integer>(listIn));
17         } while(nextPermutation(nums) == true);
18         return res;
19     }
20     public static boolean nextPermutation(int[] nums) {
21         int length = nums.length;
22         int pre = 0, post = 0;//pre标记前面的第一个小数,即nums[pre]<nums[pre+1],post标记后面的第一个大数,即nums[post]>nums[pre]
23         boolean mark = false;//标记是否是降序序列
24         //从后往前找,找到第一个前面的数小于后面的数的下标
25         for(int i = length - 1; i > 0; i--) {
26             if(nums[i - 1] < nums[i]) {
27                 mark = true;
28                 pre = i - 1;
29                 break;
30             }
31         }
32         //从后往前找,找到第一个比前面标记的数大的数的下标
33         for(int i = length - 1; i > 0; i--) {
34             if(nums[i] > nums[pre]) {
35                 post = i;
36                 break;
37             }
38         }
39         int mid = (length - pre - 1) / 2;
40         //如果直接是降序,直接反转即可
41         if(mark == false) {
42             return false;
43         }
44         int tmp = nums[pre];
45         nums[pre] = nums[post];
46         nums[post] = tmp;
47         //反转后面的降序序列为升序序列
48         for(int i = pre + 1; i <= (pre + mid); i++) {
49             int t = nums[i];
50             nums[i] = nums[--length];
51             nums[length] = t;
52         }
53         return true;
54     }
View Code
原文地址:https://www.cnblogs.com/cing/p/7857730.html