题干
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
这道题跟 46.全排列 不同之处在于序列可能包含重复数字的,但是返回的全排列是不重复。
我们看下全排列的代码:
List<List<Integer>> res = new LinkedList<>();
/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {
// 记录「路径」
LinkedList<Integer> track = new LinkedList<>();
backtrack(nums, track);
return res;
}
// 路径:记录在 track 中
// 选择列表:nums 中不存在于 track 的那些元素
// 结束条件:nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track) {
// 触发结束条件
if (track.size() == nums.length) {
res.add(new LinkedList(track));
return;
}
for (int i = 0; i < nums.length; i++) {
// 排除不合法的选择
if (track.contains(nums[i]))
continue;
// 做选择
track.add(nums[i]);
// 进入下一层决策树
backtrack(nums, track);
// 取消选择
track.removeLast();
}
}
我们排除不合法的选择的操作是:
// 排除不合法的选择
if (track.contains(nums[i]))
continue;
在这个题目的条件下显然是不可行的,因为我可能有重复的元素,比如[1,1,3],第二个1就放不进去。
对[1,1,3]这个组合进行排列,对第二个1
要整个减去。
先对数组进行排序,当搜索到和上一次搜索一样的数时,减去。 排序是剪枝的基础,比如3已经遇到一个1,得出结果311,再遇到1就跳出。
在全排列的基础上添加一句:
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
used数组记录哪些元素被使用过。nums[i] == nums[i - 1] && !used[i - 1]
表示如果遇到相同元素,且它前一个未被使用过,则跳出。其实写成nums[i] == nums[i - 1] && used[i - 1]
也是可以的,不这样写是为了剪枝更彻底,剪到根上。详见:回溯搜索 + 剪枝
全部代码:
class Solution(){
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
// 首先是特判
int len = nums.length;
if (len == 0) {
return new LinkedList<>();
}
Arrays.sort(nums);
backtrack( nums, new LinkedList<>(), new boolean[nums.length]);
return result;
}
void backtrack(int[] nums, LinkedList<Integer> track, boolean[] used) {
// 触发结束条件
if (track.size() == nums.length) {
result.add(new LinkedList(track));
return;
}
for (int i = 0; i < nums.length; i++) {
// 跳过同一个元素
if (used[i])
continue;
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
// 做选择
track.add(nums[i]);
// 进入下一层决策树
used[i] = true;
backtrack(nums, track, used);
// 取消选择
used[i] = false;
track.removeLast();
}
}
}
参考文章:回溯搜索 + 剪枝