LeetCode刷题日志(三)

分手后的思念是犯贱 2022-02-27 12:52 210阅读 0赞

之前看的是链表的问题,现在主要是研究关于递归的问题,对初学者来说递归问题的理解是比较困难的。我也是通过一些问题的逐步分析来进行判断和理解。

这篇博客主要是针对leetcode的某一类问题进行分析。这些是关于组合求和的问题

目录

  1. 1.组合总和

2.组合总和 II

3.电话号码的字母组合


1.组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

这道问题,首先来看解集不能包含重复的组合而且每个数字可以重复使用,题目给了一个示例。

  1. 输入: candidates = [2,3,5], target = 8,
  2. 所求解集为:
  3. [
  4. [2,2,2,2],
  5. [2,3,3],
  6. [3,5]
  7. ]

首先,我们将这个数组进行排序。为了保证不出现重复的组合,我们在以2为首元素的时候,只考虑2,3和,5。同理向后递推,以3为首元素的时候,只考虑3,5.。每次将数组中的数字放入栈中,如果发现当前的和与target相同,就将栈中的元素进行遍历。

  1. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  2. List<List<Integer>> result = new ArrayList<List<Integer>>();
  3. if(candidates.length == 0)
  4. return result;
  5. Arrays.sort(candidates);
  6. Stack<Integer> stack = new Stack<Integer>();
  7. generate(0,result,stack,0,target,candidates);
  8. return result;
  9. }

主函数如上,如果数组为空,返回一个空的链表。

算法的核心是generate方法。

  1. private void generate(int i, List<List<Integer>> result, Stack<Integer> stack, int sum, int target,int[] candidates) {
  2. if(i==candidates.length || sum > target)//不符合条件循环结束
  3. return ;
  4. if(sum == target){
  5. List<Integer> list = new ArrayList<Integer>();
  6. for(Integer num : stack){
  7. list.add(num);
  8. }
  9. result.add(list);
  10. }
  11. for(int index = i;index<candidates.length;index++){
  12. stack.push(candidates[index]);
  13. generate(index,result,stack,sum+candidates[index],target,candidates);
  14. stack.pop();
  15. }
  16. }

这个函数也是调用了递归的思想,sum用来记录当前栈中的数值总和。如果发现遍历到了数组的末尾或者发现和超过了target,那么此次方法调用返回。接下来。如果sum和target相同,那么直接将栈中的值遍历插入链表中。

可以看到接下里的for循环就是将数组中index位置的数字放入栈中,然后递归下一个数字(因为可以重复,所以下一次递归调用也是从index开始的)。如果跳出了递归函数(generate方法)。说明放入的数字导致sum值过大,所以将栈顶元素进行弹出。

2.组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

与上题不同,数组中包含重复数字,同样解集不可以重复。所以在递归调用的时候需要对传入的index值进行判断。这道题和上一道题的思路相同,所以把代码先写出来。

  1. public List<List<Integer>> combinationSum2(int[] candidates, int target) {
  2. List<List<Integer>> result = new ArrayList<List<Integer>>();
  3. if(candidates.length == 0)
  4. return result;
  5. Arrays.sort(candidates);
  6. Stack<Integer> stack = new Stack<Integer>();
  7. backTrace(0,candidates,target,0,result,stack);
  8. return result;
  9. }
  10. private void backTrace(int index, int[] candidates, int target, int sum, List<List<Integer>> result,Stack<Integer> stack) {
  11. if(index >candidates.length || sum>target)
  12. return ;
  13. if(sum == target){
  14. List<Integer> list = new ArrayList<Integer>();
  15. for(Integer num : stack){
  16. list.add(num);
  17. }
  18. result.add(list);
  19. System.out.println(list);
  20. }
  21. for(int i = index;i<candidates.length;i++){
  22. if(sum+candidates[i] > target)
  23. break;
  24. if(i > index && candidates[i] == candidates[i-1])
  25. continue;
  26. stack.push(candidates[i]);
  27. backTrace(i+1, candidates, target, sum+candidates[i], result, stack);
  28. stack.pop();
  29. }
  30. }

我将核心的for循环部分粘贴出来

  1. for(int i = index;i<candidates.length;i++){
  2. if(sum+candidates[i] > target)
  3. break;
  4. if(i > index && candidates[i] == candidates[i-1])
  5. continue;
  6. stack.push(candidates[i]);
  7. backTrace(i+1, candidates, target, sum+candidates[i], result, stack);
  8. stack.pop();
  9. }

首先这个数组是经过排序的,所以发现sum+candidates[i]超过target,那么就没有必要去遍历i之后的元素了。因为一定会只有的值和sum相加也一定会超过target(整个数组是递增的)。

其次,关于第二个if条件是为了防止重复。参考实例

  1. 输入: candidates = [10,1,2,7,6,1,5], target = 8,
  2. 所求解集为:
  3. [
  4. [1, 7],
  5. [1, 2, 5],
  6. [2, 6],
  7. [1, 1, 6]
  8. ]

这个数组排序后位1,1,2,5,6,7,10。target为8.当我们判断了1(index=0)和7 为结果之后。i=index=0时的backTrace方法执行完毕。此时i++为1,这个时候i>index且candidates[1] = candidates[0]。这个时候如果考虑这个1,那么就会和之后的7再次组成结果。为了防止重复,使用了这个if判断。其他的就和上道题完全相同了。

3.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

200px-Telephone-keypad2.svg.png

示例:

  1. 输入:"23"
  2. 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

这道题和上面两道题的思路是相通的,只不过这道题很类似于掷骰子的问题

就像将A数组中n个数和B数组中的m个数还有C数组中的j个数进行全排列得到n*m*j个结果。

  1. public List<String> letterCombinations(String digits) {
  2. List<String> result = new ArrayList<String>();
  3. if(digits == null||"".equals(digits))
  4. return result;
  5. char[][] dictionary = {
  6. {},{},
  7. {'a','b','c'},{'d','e','f'},{'g','h','i',}
  8. ,{'j','k','l'},{'m','n','o'},{'p','q','r','s'},{'t','u','v'},{'w','x','y','z'}
  9. };
  10. char[] digs = digits.toCharArray();
  11. Set<String> set = new HashSet<String>();
  12. Stack<Character> item = new Stack<Character>();
  13. generate(0,dictionary,digs,result,set,item);
  14. return result;
  15. }
  16. private void generate(int i,char[][] dictionary, char[] digs, List<String> result, Set<String> set, Stack<Character> item) {
  17. if(i>=digs.length){
  18. if(!set.contains(item)){
  19. String s = "";
  20. for(Character c : item){
  21. s+=c;
  22. }
  23. set.add(s);
  24. result.add(s);
  25. }
  26. return ;
  27. }
  28. int num = Integer.valueOf(digs[i])-'0';//找到对应字典中的位置
  29. for(int j = 0;j<dictionary[num].length;j++){
  30. item.push(dictionary[num][j]);
  31. generate(i+1, dictionary, digs, result, set, item);
  32. item.pop();
  33. }
  34. }

主要的方法依旧是generate方法。其中的i表示第i个数字,也就是将第i个数字从字典中找到合适的字符数组进行遍历。

j就是从第i个数字对应的字典中的数组进行遍历。思路和上面两道题完全相同,只不过这道题在加入结果的时候需要去重,因为给定的digits可能会出现重复的数字,因此我采用了一个set进行了去重。

发表评论

表情:
评论列表 (有 0 条评论,210人围观)

还没有评论,来说两句吧...

相关阅读

    相关 LeetCode日志(一)

    虽然之前对java的各方面应用有了拓展的了解,但是发现自己的短板依旧很明显就是算法。之前虽然有看过算法课,但是实际的coding却没有做太多。所以从今天起,每天都要ac一些题目