树层:没有记入到path的所有元素。
树深:排列的个数要求。
与以上的问题不一样,函数参数不需要startIdx,而是需要一个used数组记录哪些元素在这条树枝上使用过了。
树层去重:(一般放在Backtracing的函数内。可以用hashset和map,甚至可以用used;更简单的是排序后用数组本身跳过);不用在回溯后复原
排序:
Arrays.sort(nums);
i > 0 && nums[i] == nums[i - 1]
数组:
int[] used = new int[201];
|| (used[nums[i] + 100] == 1)
used[nums[i] + 100] = 1;
HashSet:
//创建
HashSet<Integer> hs = new HashSet<>();
//判断
|| hs.contains(nums[i])
//修改
hs.add(nums[i]);
Map:
//创建
HashMap<Integer,Integer> map = new HashMap<>();
//判断
if ( map.getOrDefault( nums[i],0 ) >=1 ){
continue;
}
//修改
map.put(nums[i],map.getOrDefault( nums[i],0 )+1);
树枝去重:在函数参数内体现,boolean[] used。
boolean[] used = new boolean[nums.length];//树枝去重
Arrays.fill(used, false);
Backtracing(nums, used);
used[i] = true;
Backtracing(nums, used);
used[i] = false;
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
private void Backtracing(int[] nums, boolean[] used){
if(path.size()==nums.length){
ans.add(new ArrayList<>(path));
}
for(int i=0; i<nums.length; i++){
if(used[i]==true){
continue;
}
path.add(nums[i]);
used[i] = true;
Backtracing(nums, used);
used[i] = false;
path.removeLast();
}
}
public List<List<Integer>> permute(int[] nums) {
ans.clear();
path.clear();
boolean[] used = new boolean[nums.length];
Backtracing(nums, used);
return ans;
}
}
代码随想录里使用到path的类型是LinkedList。
// 解法2:通过判断path中是否存在数字,排除已经选择的数字
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>(); //好奇怪,为什么要用这个?
public List<List<Integer>> permute(int[] nums) {
if (nums.length == 0) return result;
backtrack(nums, path);
return result;
}
public void backtrack(int[] nums, LinkedList<Integer> path) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
}
for (int i =0; i < nums.length; i++) {
// 如果path中已有,则跳过
if (path.contains(nums[i])) {//因为题目上说了nums不存在重复的元素。
continue;
}
path.add(nums[i]);
backtrack(nums, path);
path.removeLast();
}
}
}
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
本题就涉及了树层去重和树枝去重的所有要求。(整理到最上面了。)
class Solution {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
private void Backtracing(int[] nums, boolean[] used){
if(path.size()==nums.length){
ans.add(new ArrayList<>(path));
}
HashSet<Integer> hs = new HashSet<>();//树层去重
for(int i=0; i<nums.length; i++){
if(used[i]==true || hs.contains(nums[i])){
continue;
}
path.add(nums[i]);
used[i] = true;
hs.add(nums[i]);
Backtracing(nums, used);
used[i] = false;
path.removeLast();
}
}
public List<List<Integer>> permuteUnique(int[] nums) {
ans.clear();
path.clear();
boolean[] used = new boolean[nums.length];//树枝去重
Arrays.sort(nums);
Backtracing(nums, used);
return ans;
}
}
代码随想录选择排序+used(这里涉及了used[i-1]的用法,还是很有趣的)。
// used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过
// used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过
// 如果同⼀树层nums[i - 1]使⽤过则直接跳过
class Solution {
//存放结果
List<List<Integer>> result = new ArrayList<>();
//暂存结果
List<Integer> path = new ArrayList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
boolean[] used = new boolean[nums.length];
Arrays.fill(used, false);
Arrays.sort(nums);
backTrack(nums, used);
return result;
}
private void backTrack(int[] nums, boolean[] used) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
// used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过
// used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过
// 如果同⼀树层nums[i - 1]使⽤过则直接跳过
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
continue;
}
//如果同⼀树⽀nums[i]没使⽤过开始处理
if (used[i] == false) {
used[i] = true;//标记同⼀树⽀nums[i]使⽤过,防止同一树枝重复使用
path.add(nums[i]);
backTrack(nums, used);
path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复
used[i] = false;//回溯
}
}
}
}