题目:
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]
.
分析:
LeetCode47 与 LeetCode46 的区别就是提供的数字 nums[] 有重复,但是最后的排列方式不能有重复。
比如 nums = [1,1,2],如果按照LeetCode46的程序,结果为[1,1,2], [1,2,1], [1,1,2], [1,2,1], [2,1,1], [2,1,1],显然是不符合要求的。
如果对最后一步的results进行去重,逻辑是上可以的,但是已经得到了很多组没有用的结果,在LeetCode上也会因为Time Limit Exceed而不能通过。
所以LeetCode46在LeetCode47的递归基础上,需要使用一种重要的编程技巧:剪枝,即在递归过程中尽量省去没有必要的步骤。
代码:
public class LeetCode47 {
public static void main(String[] args) {
int[] nums = {1, 1, 2};
System.out.println(permuteUnique(nums));
}
public static List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> results = new ArrayList<List<Integer>>();
permuteUnique(results, new ArrayList<Integer>(), nums, new boolean[nums.length]);
return results;
}
private static void permuteUnique(List<List<Integer>> results, List<Integer> cur, int[] nums, boolean[] visited) {
if (cur.size() == nums.length) {
results.add(new ArrayList<Integer>(cur));
return;
}
for (int i = 0; i < nums.length; i++) {
if (visited[i] || i > 0 && nums[i] == nums[i-1] && !visited[i-1])
continue;
cur.add(nums[i]);
visited[i] = true;
permuteUnique(results, cur, nums, visited);
cur.remove(cur.size()-1);
visited[i] = false;
}
}
}
if (visited[i] || i >0 && nums[i] == nums[i-1] && !visited[i-1])
visited[i]:如果该数已经被选过,则直接跳过该数,相当于LeetCode46中的条件判断cur.contains(nums[i])。由于LeetCode47中有重复的数字,所以不能通过cur.contains(nums[i])判断结果中是否有这个数值,只能判断该数是否被选过。
i > 0 && nums[i] == nums[i-1] && !visited[i-1]:对一个nums 中 index 大于0 的数,如果它和前面的一个数相等,前面的数字如果没被选,该数也不能被选;如果前面的数被选过了,该数可以考虑选。这样永远保证了相同数字只能按照nums中的顺序出现,就直接避免了 前1-后1 与 后1-前1 这类型的重复。