有重复元素的求子集问题和全排列问题,两者都要去重剪枝,全排列是定义一个数组,来标记是否访问过。而子集没有用到数组。两者的共同点都是要先对题目给的数组排序。
求重复元素的子集问题:
代码:
class Solution {
List<List<Integer>> res=new ArrayList<List<Integer>>();
List<Integer> List=new ArrayList<Integer>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
//添加空集合
res.add(new ArrayList<>());
//先对数组排好序,以便去重
Arrays.sort(nums);
//递归回溯
dfs(nums,0);
return res;
}
public void dfs(int[] nums,int start){
//递归结束条件
if(start>=nums.length) return;
for(int i=start;i<nums.length;i++){
//剪枝去重
if( i>start && nums[i]==nums[i-1]) continue;
list.add(nums[i]);
res.add(new ArrayList<>(list)); //注意这里是新创建集合,再复制,否则为空集合
dfs(nums,i+1);
list.remove(list.size()-1);
}
}
}
求重复元素的全排列问题
代码
class Solution {
List<List<Integer>> res=new ArrayList<List<Integer>>();
List<Integer> curr=new ArrayList<Integer>();
//标记元素是否被访问,0未,1是
int[] visited;
public List<List<Integer>> permuteUnique(int[] nums) {
if(nums==null || nums.length==0) return res;
this.visited=new int[nums.length];//标记数组的长度和数组序列长度相等
Arrays.sort(nums);//对数组排好序,以便减枝
def(nums);
return res;
}
public void def(int[] nums){
//说明遍历完一个分支
if(curr.size()==nums.length){
//因为java中的参数传递是值传递,如果直接传curr,得到的是地址,,需要创建一个新的数组为其复制即可
res.add(new ArrayList<Integer>(curr));
return;
}
for(int i=0;i<nums.length;i++){
//说明这个元素被访问过了,跳过这次循环,
if(visited[i]==1) continue;
//减枝(当遇到和之前元素相等的数值,并且之前已经访问过了,就直接跳过本次循环,不再构建访问以这个数值作为节点的树)
if(i>0 && nums[i]==nums[i-1] && visited[i-1]==1) continue;
//上面i>0防止数组-1越界
visited[i]=1;
curr.add(nums[i]); //先置为1,再添加到小集合中
def(nums);
curr.remove(curr.size()-1); //回溯
visited[i]=0;
}
}
}