给定一个可包含重复数字的序列,返回所有不重复的全排列
这题与上题的全排列问题类似,问题在于如何将重复序列排除在外。
这里就需要剪枝思想,即去掉多余的子树。
我们先看图,令nums=[1,1,2] ![](https://img2020.cnblogs.com/blog/1809280/202004/1809280-20200430115337390-1498940799.png) 图中可以看出,[1
]节点与[1]节点一致,当[1`]节点选择子节点时与[1]节点选择时一致,故应该去除。类推。
再看如何判断剪枝:
nums[0]=1,nums[1]=1.我们需要将nums[i-1]与nums[i]进行数值判断。同时在nums[i]为父节点进行遍历时,判断nums[i-1]是否已被调用。同时注意边界i>0;
代码在全排列问题上添加一个剪枝语句。
public List<List<Integer>> permute(int[] nums) {
int numsLength = nums.length; // numsLength为nums长度
List<List<Integer>> res = new ArrayList<>(); // 返回值
Array.sort(nums); // 排序是剪枝的前提
Deque<Integer> path = new ArrayDeque<>(); // path类型为Stack类型,系统推荐使用Deque。
// path用来存放每一次遍历结果
boolean[] tOf = new boolean[numsLength]; // boolean数组对应nums中数字是否被遍历
dfs(nums,numsLength,0,path,tOf,res);
return res;
}
private void dfs(int[] nums, int numsLength, int depth, Deque<Integer> path, boolean[] tOf, List<List<Integer>> res) {
// 遍历结束条件
if (numsLength == depth) {
res.add(new ArrayList<>(path));
return;
}
// for循环遍历
for (int i = 0; i < numsLength; i++) {
// 判断nums中数字是否被遍历到
if (tOf[i]) {
continue;
}
// 进行剪枝判断
if (i>0 && nums[i] == nums[i -1] && !tOf[i-1]) {
continue;
}
// 若未被遍历
path.addLast(nums[i]); // 将遍历到的nums[i]放入path尾部
tOf[i] = true; // 将对应tof置为true,表示已遍历
dfs(nums,numsLength,depth+1,path,tOf,res); // 继续向下搜索
path.removeLast(); // 假设[1][2][3]回溯,返回上一层[1][2]。但是如何继续返回[1]节点呢
// 这时候tof[1]为true,代表nums[1]已被遍历,继续回撤。
tOf[i] = false;
}
}
有错误或有其他想法的朋友们欢迎评论。