combination sum Ipermutation Isubsets  I 是组合和、全排列、子集的第一种情况,给定数组中没有重复的元素。

combination sum IIpermutation IIsubsets  II 是组合和、全排列、子集的第而种情况,给定数组中有重复元素。

combination sum I中元素每个可以被多次使用,所以每次遍历都是从当前元素开始,然后往后面遍历。

combination sum II中元素只能被使用一次,所以算下个元素时,只能从当前元素后面的元素查找。

全排列每次都要从头开始遍历,既然从头遍历,就要考虑前面元素是否使用过。对于permutation I,每次从头遍历只要判断当前元素是否使用过,这个可以使用list.contains判断,也可以使用combination sum II而中的数组来表示是否使用过。因为全排列每次从头遍历,而且添加时不能添加那些已经在集合中的元素(使用过的),所以用一个数组。而从当前元素下一个开始遍历的情况(求子集),则不需要考虑前面的元素,只考虑后面的,而后面的元素都是没有使用过的,所以不需要再判断是否使用过,只需要考虑跳过重复元素就行。

求子集是每次要从当前元素后面的元素添加,也就是遍历当前元素后面的元素。subsets  I不需要判断是否重复,也不需要排序。判断条件只是list中元素个数小于nums长度即可,也就是是集合之一,但是判断完了不返回,因为还有继续添加。subsets  II则要排序,还有去除重复元素,这里跳过重复元素并不需要额外数组,因为不是从头开始,只要跳过相同的即可。

注意这几个处理重复的方法:全排列是用到所有元素,每次从头遍历,所以它处理重复的方法就是使用一个数组标记是否使用过该元素。子集和组合不是从头开始遍历,每次只遍历后面的元素,它的处理方式就是直接用if跳过相同元素。

代码:

combination sum I

class Solution {
public List<List<Integer>> combinationSum(int[] nums, int target) {
/**
这个题是穷举所有情况,回溯的可能性比较大
*/
Arrays.sort(nums);
List<List<Integer>> result=new ArrayList<List<Integer>>();
backtracking(result,new ArrayList<Integer>(),nums,target,0,0);
return result;
} public void backtracking(List<List<Integer>> result,List<Integer> tmp,int[] nums,int target,int sum,int index){
if(sum>target) return ;
if(sum==target) {
result.add(new ArrayList<Integer>(tmp));
return ;
} if(index>nums.length-1) return ; for(int i=index;i<nums.length;i++){
tmp.add(nums[i]);
backtracking(result,tmp,nums,target,sum+nums[i],i);
tmp.remove(tmp.size()-1);
}
}
}

combination sum II

class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result=new ArrayList<List<Integer>>();
if(candidates==null||candidates.length==0) return result;
Arrays.sort(candidates);
backtracking(result,new ArrayList<Integer>(),candidates,target,0,0);
return result;
} public void backtracking(List<List<Integer>> result,List<Integer> list,int[] nums,int target,int sum,int index){
if(sum>target) return ;
if(sum==target){
result.add(new ArrayList<Integer>(list));
return ;
}
if(index>nums.length-1) return ;
/*
这一题相比combination sum I,做了两点修改,1、数组中每个元素只能被使用一次;2、数组中有重复元素。针对1,每次添加list从下一个元素开始(i+1);针对2,每次遍历的时候,跳过重复的元素。
*/
for(int i=index;i<nums.length;i++){
//每次遍历时,跳过重复元素,这样就不会出现重复的两个list
if(i>index&&nums[i]==nums[i-1]) continue;
list.add(nums[i]);
backtracking(result,list,nums,target,sum+nums[i],i+1);
list.remove(list.size()-1);
}
}
}

permination I

class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums.length<=0) return result;
backtracking(result,new ArrayList<Integer>(),nums);
return result;
} public void backtracking(List<List<Integer>> result, List<Integer> list,int[] nums){
if(list.size()==nums.length){
result.add(new ArrayList<Integer>(list));
return ;
} for(int i=0;i<nums.length;i++){
if(list.contains(nums[i])) continue;//每次都是遍历数组中的每个元素,然后添加不一样的元素(保证不重复)
list.add(nums[i]); backtracking(result,list,nums);
list.remove(list.size()-1); }
}
}

permination II

class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
if(nums==null||nums.length==0) return res;
//该数组用来标记每个位置的元素是否使用以保证每个位置的元素只能使用一次。控制递归遍历(往list后面添加元素)中该元素是否已经使用。
boolean[] used=new boolean[nums.length];
Arrays.sort(nums);
List<Integer> list=new ArrayList<>();
helper(nums,res,list,used);
return res;
} public void helper(int[] nums,List<List<Integer>> res,List<Integer> list,boolean[] used){
if(nums.length==list.size()){
res.add(new ArrayList<Integer>(list));
return ;
}
for(int i=0;i<nums.length;i++){
if(used[i]) continue; //该元素使用过了。
//下一个重复值只有在前一个重复值被使用的情况下才可以使用。
/*
    这个!used[i-1]条件是为深度遍历准备的。
对于当前层遍历时,每遍历一个元素结束后就会设为false然后遍历下一个,所以可以跳过重复元素,避免相同元素出现在同一个位置。
对于深度遍历(递归,往结果集中继续添加元素),需要使用!used[i-1]来保证后面的相同元素可以往后面的结果集中添加。
      比如[1,1,2].第一层遍历第一个1时,设为true后,递归往后面添加第二个元素,此时也是从第一个元素开始遍历,
      因为此时used[0]为true,所以跳过,i=1,此时如果没有!used[i-1],也会跳过第二个1了,所以需要加上这个条件。
      
      */ if(i>0&&nums[i]==nums[i-1]&&!used[i-1]) continue;
used[i]=true;
list.add(nums[i]);
helper(nums,res,list,used);
//下面这两步,为了同层遍历,同层遍历是给一个位置轮流添加元素。所以需要将上一个添加的元素删了,然后删除标记,遍历下一个元素
used[i]=false;
list.remove(list.size()-1);
}
}
}

subsets I

class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
if(nums==null||nums.length==0) return res;
helper(res,new ArrayList<Integer>(),nums,0);
return res;
} public void helper(List<List<Integer>> res,List<Integer> list,int[] nums,int index){
if(list.size()<=nums.length){
res.add(new ArrayList<Integer>(list));
}
for(int i=index;i<nums.length;i++){
list.add(nums[i]);
helper(res,list,nums,i+1);
list.remove(list.size()-1);
}
}
}

subsets II

class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
if(nums==null||nums.length==0) return res;
Arrays.sort(nums); //有相同元素,所以要排序
helper(res,new ArrayList<Integer>(),nums,0);
return res;
}
public void helper(List<List<Integer>> res,List<Integer> list,int[] nums,int index){
//求子集,所以判断条件为.而且满足条件后还要继续向下进行,而不是返回
if(list.size()<=nums.length)
res.add(new ArrayList<Integer>(list));
for(int i=index;i<nums.length;i++){
//跳过相同的元素。因为是从后面的元素中选元素,不是从头开始,所以不需要再创建是否使用的数组了。注意:下面是i>index,而不是i>0.
if(i>index&&nums[i]==nums[i-1]) continue;
list.add(nums[i]);
helper(res,list,nums,i+1);
list.remove(list.size()-1);
}
}
}

combination sum、permutation、subset(组合和、全排列、子集)的更多相关文章

  1. 【LeetCode每天一题】Combination Sum II(组合和II)

    Given a collection of candidate numbers (candidates) and a target number (target), find all unique c ...

  2. 子集系列(二) 满足特定要求的子集,例 [LeetCode] Combination, Combination Sum I, II

    引言 既上一篇 子集系列(一) 后,这里我们接着讨论带有附加条件的子集求解方法. 这类题目也是求子集,只不过不是返回所有的自己,而往往是要求返回满足一定要求的子集. 解这种类型的题目,其思路可以在上一 ...

  3. [Leetcode 40]组合数和II Combination Sum II

    [题目] Given a collection of candidate numbers (candidates) and a target number (target), find all uni ...

  4. [Leetcode 39]组合数的和Combination Sum

    [题目] Given a set of candidate numbers (candidates) (without duplicates) and a target number (target) ...

  5. [Leetcode 216]求给定和的数集合 Combination Sum III

    [题目] Find all possible combinations of k numbers that add up to a number n, given that only numbers ...

  6. 【LeetCode练习题】Combination Sum

    Combination Sum Given a set of candidate numbers (C) and a target number (T), find all unique combin ...

  7. LeetCode题解39.Combination Sum

    39. Combination Sum Given a set of candidate numbers (C) (without duplicates) and a target number (T ...

  8. leetcode 39. Combination Sum 、40. Combination Sum II 、216. Combination Sum III

    39. Combination Sum 依旧与subsets问题相似,每次选择这个数是否参加到求和中 因为是可以重复的,所以每次递归还是在i上,如果不能重复,就可以变成i+1 class Soluti ...

  9. 【LeetCode】40. Combination Sum II (2 solutions)

    Combination Sum II Given a collection of candidate numbers (C) and a target number (T), find all uni ...

随机推荐

  1. javaRMI详解

    前几天在阿里内推一面的时候,面试官问到了一个关于java中RMI(Remote Method Invocation)的问题,当时感觉自己回答的还比较好,他比较满意,但那是因为他问的比较浅,所以自己看了 ...

  2. UNIX环境高级编程——线程属性之分离属性

    说到线程的分离状态,我认为,之所以会有这个状态,是因为系统对某些线程的终止状态根本不感兴趣导致的. 我们知道,进程中的线程可以调用: int pthread_join(pthread_t tid, v ...

  3. 轻松学习Asp.net中的控件

    C/S 结构,即大家熟知的客户机和服务器结构.它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销.目前大多数应用软件 ...

  4. 【Unity Shaders】Lighting Models —— 光照模型之Lit Sphere

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  5. 关于C语言中输出格式

    刷OJ题的时候,经常发现题目的输入输出会有相应的特殊要求,像位宽和位域输出就比较常见. 位宽:如果长度不足位宽大小,则默认情况下前面补空格. 标志位:有+有- 默认格式为左对齐. 在位宽前面加0,补全 ...

  6. C语言中的内存分配

    对于一个C语言程序而言,内存空间主要由以下几个部分组成: 1)程序代码区:用来存储程序的二进制代码 2)全局区/静态存储区 3)BSS段:用来存储未初始化的全局变量和静态变量. 4)栈区:存储局部变量 ...

  7. 调用sed命令的三种方式

    调用sed命令的三种方式 调用sed有三种方式,一种为Shell命令行方式,另外两种是将sed命令写入脚本文件,然后执行该脚本文件. 三种方式的命令格式归纳如下: 一.在Shell命令行输入命令调用s ...

  8. 安装mysql到服务器的linux环境下

    1·安装mysql 命令:yum -y install httpd php mysql mysql-server 2·配置mysql 配置开机启动服务 /sbin/chkconfig --add my ...

  9. 迭代器模式之看芒果台还是央视nie?

    "大风车吱呀吱悠悠的转,这里的风景啊真好看,天好看,地好看,还有一群快乐的小伙伴,大风车转呀转悠悠,快乐的伙伴手牵着手,牵着你的手,牵着我的手......"童年的美好时光因为有了& ...

  10. jquery sortable的拖动方法内容说明和示例详解(转载http://www.jb51.net/article/45803.htm)

     所有的事件回调函数都有两个参数:event和ui,浏览器自有event对象,和经过封装的ui对象 ui.helper - 表示sortable元素的JQuery对象,通常是当前元素的克隆对象 u ...