47. Permutations II ?

题目:

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:
[1,1,2][1,2,1], and [2,1,1].

链接: http://leetcode.com/problems/permutations-ii/

4/15/2017

自己的做法是错的,但是按照别人的改一下就对了。

自己的错误做法,一直到[1,0,2,0,1,-1,-1]才有test case挂的情况,正确是有630个解,我的算法有696个解。

 1 public class Solution {
 2     public List<List<Integer>> permuteUnique(int[] nums) {
 3         List<List<Integer>> ret = new ArrayList<>();
 4         if (nums.length == 0) return ret;
 5         Arrays.sort(nums);
 6         enumerate(nums, ret, 0);
 7         return ret;
 8     }
 9     private void enumerate(int[] nums, List<List<Integer>> ret, int pos) {
10         if (pos == nums.length) {
11             ArrayList<Integer> list = new ArrayList<Integer>();
12             for (int i = 0; i < nums.length; i++) {
13                 list.add(nums[i]);
14             }
15             ret.add(list);
16             return;
17         }
18         for (int i = pos; i < nums.length; i++) {
19             if (i != pos && (nums[i] == nums[pos] || nums[i] == nums[i - 1])) continue;
20             exchange(nums, pos, i);
21             enumerate(nums, ret, pos + 1);
22             exchange(nums, i, pos);
23         }
24     }
25     private void exchange(int[] nums, int i, int j) {
26         int tmp = nums[i];
27         nums[i] = nums[j];
28         nums[j] = tmp;
29     }
30 }

按照讨论修改了几句,也不需要sort,10ms, 45%

https://discuss.leetcode.com/topic/36221/share-my-java-code-with-detailed-explanantion

我不能解释为什么会这样,不过我的猜想是即使最开始已经sort过了,在中间的各种交换之后也许不能保证在recursive function之内相同元素是相邻的了。

很多算法题有去重复的要求,有时候需要判断相邻元素,有时候要用set。什么情况用啥呢

这道题让我意识到需要理解其他更普遍的backtracking permutation的算法

 1 public class Solution {
 2     public List<List<Integer>> permuteUnique(int[] nums) {
 3         List<List<Integer>> ret = new ArrayList<>();
 4         if (nums.length == 0) return ret;
 5         enumerate(nums, ret, 0);
 6         return ret;
 7     }
 8     private void enumerate(int[] nums, List<List<Integer>> ret, int pos) {
 9         if (pos == nums.length) {
10             ArrayList<Integer> list = new ArrayList<Integer>();
11             for (int i = 0; i < nums.length; i++) {
12                 list.add(nums[i]);
13             }
14             ret.add(list);
15             return;
16         }
17         Set<Integer> appeared = new HashSet<>();
18         for (int i = pos; i < nums.length; i++) {
19             if (!appeared.add(nums[i])) continue;
20             exchange(nums, pos, i);
21             enumerate(nums, ret, pos + 1);
22             exchange(nums, i, pos);
23         }
24     }
25     private void exchange(int[] nums, int i, int j) {
26         int tmp = nums[i];
27         nums[i] = nums[j];
28         nums[j] = tmp;
29     }
30 }

Princeton Introduction to Programming in Java里对permutation有很多介绍,可惜没有跟这道题一样的。有机会一定看看:

http://introcs.cs.princeton.edu/java/23recursion/

别人的做法:

visited的意义?在当前层还是递归层?

 1 public class Solution {
 2     public List<List<Integer>> permuteUnique(int[] nums) {
 3         List<List<Integer>> res = new ArrayList<>();
 4         if (nums == null || nums.length == 0) {
 5             return res;
 6         }
 7         Arrays.sort(nums);
 8         boolean[] visited = new boolean[nums.length];
 9         permuteUnique(res, new ArrayList<Integer>(), visited, nums);
10         return res;
11     }
12     
13     private void permuteUnique(List<List<Integer>> res, List<Integer> onePerm, boolean[] visited, int[] nums) {
14         if (onePerm.size() == nums.length) {
15             res.add(new ArrayList<>(onePerm));
16             return;
17         }
18         for (int i = 0; i < nums.length; i++) {
19             if (visited[i] || (i > 0 && nums[i] == nums[i - 1] && visited[i - 1])) {
20                 continue;
21             }
22             visited[i] = true;
23             onePerm.add(nums[i]);
24             permuteUnique(res, onePerm, visited, nums); 
25             onePerm.remove(onePerm.size() - 1);
26             visited[i] = false;
27         }
28     }
29 }

更多讨论:

https://discuss.leetcode.com/category/55/permutations-ii 

 

4/22/2017

算法班

跟前面别人的算法一样,注意第26行的!visited[i - 1]是一定要有的,为什么,当相同值的时候如果想要加入当前元素,必须保证前面相同值已经在permutation里面了,这样,他们的相对位置才能始终一致。

如果没有这个条件,碰到相同元素后面的就加不进去了,所以permutation永远不会到permutation.size() == nums.length,结果为空list

 1 class Solution {
 2     /**
 3      * @param nums: A list of integers.
 4      * @return: A list of unique permutations.
 5      */
 6     public List<List<Integer>> permuteUnique(int[] nums) {
 7         List<List<Integer>> ret = new ArrayList<>();
 8         if (nums == null) return ret;
 9         boolean[] visited = new boolean[nums.length];
10 
11         Arrays.sort(nums);
12         ArrayList<Integer> permutation = new ArrayList<Integer>();
13         helper(ret, permutation, nums, visited);
14         
15         return ret;
16     }
17     private void helper(List<List<Integer>> ret, 
18                         ArrayList<Integer> permutation,
19                         int[] nums,
20                         boolean[] visited) {
21         if (permutation.size() == nums.length) {
22             ret.add(new ArrayList<Integer>(permutation));
23             return;
24         }
25         for (int i = 0; i < nums.length; i++) {
26             if (visited[i] || (i > 0 && !visited[i - 1] && nums[i] == nums[i - 1])) continue;
27             visited[i] = true;
28             permutation.add(nums[i]);
29             helper(ret, permutation, nums, visited);
30             permutation.remove(permutation.size() - 1);
31             visited[i] = false;
32         }
33     }
34 }

 

posted @ 2017-04-16 07:57  panini  阅读(180)  评论(0编辑  收藏  举报