没有重复 数字
的序列,返回其所有可能的全排列。包含重复数字
的序列,返回所有不重复的全排列。
- 全排列
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 全排列 II
这一题是在「力扣」第 46 题: “全排列” 的基础上增加了“序列中的元素可重复”这一条件
,但要求返回的结果又不能有重复元素。
如果要比较两个列表是否一样,一个很显然的办法是分别排序,然后逐个比对。既然要排序,我们可以在搜索之前就对候选数组排序
,一旦发现这一支搜索下去可能搜索到重复的元素就停止搜索,这样结果集中不会包含重复元素
这个题目相比于46题,要多做的工作就是去重
可以先排序,这个方便后面剪枝操作,所以要增加排序的代码
然后就是判断剪枝的条件,if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])
这里,i>0 是为了为了保证 nums[i - 1] 有意义,
nums[i] == nums[i - 1],就是说该元素与前一个元素相等,这就是剪枝的关键,要被剪掉
写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
还有就是循环题的第一个判断条件,
在46题中,是if(!used[i]),但是在47题中,是if(used[i])
这也是46 和 47题的区别~~~
新加的代码,如果已经被使用,那么继续下一步continue
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 全排列
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
//特例:如果为空,返回的是空数组
if(len == 0){
return res;
}
//used布尔类型的数组,表示有没有使用过
//当检查元素是否使用的时候时间复杂度为1, 用空间换取时间
boolean[] used = new boolean[len];
List<Integer> path = new ArrayList<>();
dfs(nums, len, 0, path, used, res);
return res;
}
public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res){
//已经找到满足长度的数组,可以进行返回
if(depth == len){
//这个地方要进行数据的复制,否则返回的是空
res.add(new ArrayList<>(path));
return;
}
//循环实现在,还未选择的数中依次选择一个元素作为下一个位置的元素
for(int i = 0; i < len; i++){
if(!used[i]){
//没有被用过加入到path路径之中,把used状态设为true表示已使用
path.add(nums[i]);
used[i] = true;
//递归
dfs(nums, len, depth + 1, path, used,res);
//这一步就是完成状态重制工作,从深层次节点回到浅层次节点
//use的状态设为false,表示没有使用,并且将最后加入的那个元素移除
used[i] = false;
path.remove(path.size() - 1);
}
}
}
}
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def dfs(nums, size, depth, path, used, res):
if depth == size:
#这个地方实现的也是元素的复制
res.append(path[:])
return
for i in range(size):
if not used[i]:
used[i] = True
path.append(nums[i])
dfs(nums, size, depth + 1, path, used, res)
used[i] = False
path.pop()
size = len(nums)
if len(nums) == 0:
return []
#布尔类型的数组used
used = [False for _ in range(size)]
res = []
# path = []
dfs(nums, size, 0, [], used,res)
return res
- 全排列II
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
//特例:如果为空,返回的是空数组
if(len == 0){
return res;
}
//-------新加的代码,排序剪枝,方便去重-----
Arrays.sort(nums);
//used布尔类型的数组,表示有没有使用过
//当检查元素是否使用的时候时间复杂度为1, 用空间换取时间
boolean[] used = new boolean[len];
List<Integer> path = new ArrayList<>();
dfs(nums, len, 0, path, used, res);
return res;
}
public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res){
//已经找到满足长度的数组,可以进行返回
if(depth == len){
//这个地方要进行数据的复制,否则返回的是空
res.add(new ArrayList<>(path));
return;
}
//循环实现在,还未选择的数中依次选择一个元素作为下一个位置的元素
for(int i = 0; i < len; i++){
//-------新加的代码,如果已经被使用,那么继续下一步-----
if(used[i]){
continue;
}
//-------新加的代码,剪枝条件,-----
//剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义
// 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
//没有被用过加入到path路径之中,把used状态设为true表示已使用
used[i] = true;
path.add(nums[i]);
//递归
dfs(nums, len, depth + 1, path, used,res);
//这一步就是完成状态重制工作,从深层次节点回到浅层次节点
//use的状态设为false,表示没有使用,并且将最后加入的那个元素移除
used[i] = false;
path.remove(path.size() - 1);
}
}
}