查看: 3314| 回复: 4
跳转到指定楼层
上一主题 下一主题
收起左侧

[Leetcode] Backtracking类型题的经验和攻略整理

全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
刚开始接触backtracking的时候,感到十分困惑,总觉得看到这种类型的题的时候没有思路。在leetcode上面刷题的时候看到了一个大神分享的模板,针对subset,permutations,和combination sum这几种类型的模板,基础的code其实是非常类似的,但是根据每道题要求不一样,做了一些小细节的变化。我用了这个模板又解了几题,目前可以应用到9道题上面。希望可以给和我一样的小白一些思路。如果大家觉得有用,求一些大米看面经。

以下几点可以每次做题之前先思考一下
3 keys to consider
  • Our choice - What choice to make at each call of the function?
  • Our constraints - When do we stop following a certain path? / When do we not even go one way?
  • Our goal - What is our target? / Base Case


其实backtracking里面的subset,permutations,和combination sum这几种类型题里面的核心思想就是choose,explore,unchoose

General Idea
  • Choose // 先做一个选择
  • Explore // 在这个选择里面探索所有的可能性,call自己
  • Unchoose // 通常如果我们用list来存结果的话,是需要unchoose,比如把刚才选择的从list里面删除掉,这样才不会影响做下一个选择的结果

  1. private void backtrack(List<List<Integer>> list , List<Integer> tempList, int [] nums, int start){
  2.     list.add(new ArrayList<>(tempList));
  3.     for(int i = start; i < nums.length; i++){
  4.         tempList.add(nums[i]); // Choose
  5.         backtrack(list, tempList, nums, i + 1); // Explore
  6.         tempList.remove(tempList.size() - 1);  // Unchoose
  7.     }
  8. }
复制代码



以下是9道leetcode题的写法。基本可以看出来,大致的code和思路是一模一样的,每次只是改了一到两行的写法,或者加了一些小细节。最主要的是需要自己去吃透这些细节的变化,去实际在leetcode上面写几次,之后可以灵活运用。


78. Subsets : https://leetcode.com/problems/subsets/
  1. public List<List<Integer>> subsets(int[] nums) {
  2.     List<List<Integer>> list = new ArrayList<>();
  3.     Arrays.sort(nums); // not necessary here
  4.     backtrack(list, new ArrayList<>(), nums, 0);
  5.     return list;
  6. }

  7. private void backtrack(List<List<Integer>> list , List<Integer> tempList, int [] nums, int start){
  8.     list.add(new ArrayList<>(tempList));
  9.     for(int i = start; i < nums.length; i++){
  10.         tempList.add(nums[i]);
  11.         backtrack(list, tempList, nums, i + 1);
  12.         tempList.remove(tempList.size() - 1);
  13.     }
  14. }
复制代码



90. Subsets II (contains duplicates) : https://leetcode.com/problems/subsets-ii/
  1. public List<List<Integer>> subsetsWithDup(int[] nums) {
  2.     List<List<Integer>> list = new ArrayList<>();
  3.     Arrays.sort(nums); //skip duplicates
  4.     backtrack(list, new ArrayList<>(), nums, 0);
  5.     return list;
  6. }

  7. private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int start){
  8.     list.add(new ArrayList<>(tempList));
  9.     for(int i = start; i < nums.length; i++){
  10.         if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates
  11.         tempList.add(nums[i]);
  12.         backtrack(list, tempList, nums, i + 1);
  13.         tempList.remove(tempList.size() - 1);
  14.     }
  15. }
复制代码



46. Permutations : https://leetcode.com/problems/permutations/
  1. public List<List<Integer>> permute(int[] nums) {
  2.    List<List<Integer>> list = new ArrayList<>();
  3.    // Arrays.sort(nums); // not necessary
  4.    backtrack(list, new ArrayList<>(), nums);
  5.    return list;
  6. }

  7. private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){
  8.    if(tempList.size() == nums.length){
  9.       list.add(new ArrayList<>(tempList));
  10.    } else{
  11.       for(int i = 0; i < nums.length; i++){
  12.          if(tempList.contains(nums[i])) continue; // element already exists, skip
  13.          tempList.add(nums[i]);
  14.          backtrack(list, tempList, nums);
  15.          tempList.remove(tempList.size() - 1);
  16.       }
  17.    }
  18. }
复制代码



47. Permutations II (contains duplicates) : https://leetcode.com/problems/permutations-ii/
  1. public List<List<Integer>> permuteUnique(int[] nums) {
  2.     List<List<Integer>> list = new ArrayList<>();
  3.     Arrays.sort(nums);
  4.     backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
  5.     return list;
  6. }

  7. private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
  8.     if(tempList.size() == nums.length){
  9.         list.add(new ArrayList<>(tempList));
  10.     } else{
  11.         for(int i = 0; i < nums.length; i++){
  12.             if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
  13.             used[i] = true;
  14.             tempList.add(nums[i]);
  15.             backtrack(list, tempList, nums, used);
  16.             used[i] = false;
  17.             tempList.remove(tempList.size() - 1);
  18.         }
  19.     }
  20. }
复制代码



77. Combinations :
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
  1. public List<List<Integer>> combine(int n, int k) {
  2.         List<List<Integer>> list = new ArrayList<>();
  3.         backtrack(list, new ArrayList(), n, k, 1);
  4.         return list;
  5.     }
  6.    
  7.     private void backtrack(List<List<Integer>> list, List<Integer> sublist, int n, int k, int start) {
  8.         if(sublist.size() == k) {
  9.             list.add(new ArrayList<>(sublist));
  10.         } else {
  11.             for(int i = start; i <= n; i++) {
  12.                 if(sublist.contains(i)) {
  13.                     continue;
  14.                 }
  15.                 sublist.add(i);
  16.                 backtrack(list, sublist, n, k, i + 1);
  17.                 sublist.remove(sublist.size() - 1);
  18.             }
  19.         }
  20.     }
复制代码



39. Combination Sum : https://leetcode.com/problems/combination-sum/
  1. public List<List<Integer>> combinationSum(int[] nums, int target) {
  2.     List<List<Integer>> list = new ArrayList<>();
  3.     Arrays.sort(nums);
  4.     backtrack(list, new ArrayList<>(), nums, target, 0);
  5.     return list;
  6. }

  7. private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
  8.     if(remain < 0) return;
  9.     else if(remain == 0) list.add(new ArrayList<>(tempList));
  10.     else{
  11.         for(int i = start; i < nums.length; i++){
  12.             tempList.add(nums[i]);
  13.             backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements
  14.             tempList.remove(tempList.size() - 1);
  15.         }
  16.     }
  17. }
复制代码



40. Combination Sum II (can't reuse same element) : https://leetcode.com/problems/combination-sum-ii/
  1. public List<List<Integer>> combinationSum2(int[] nums, int target) {
  2.     List<List<Integer>> list = new ArrayList<>();
  3.     Arrays.sort(nums);
  4.     backtrack(list, new ArrayList<>(), nums, target, 0);
  5.     return list;
  6.    
  7. }

  8. private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
  9.     if(remain < 0) return;
  10.     else if(remain == 0) list.add(new ArrayList<>(tempList));
  11.     else{
  12.         for(int i = start; i < nums.length; i++){
  13.             if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates
  14.             tempList.add(nums[i]);
  15.             backtrack(list, tempList, nums, remain - nums[i], i + 1);
  16.             tempList.remove(tempList.size() - 1);
  17.         }
  18.     }
  19. }
复制代码



216. Combination Sum III (unique set of numbers && combination of k numbers add to n, and the range is 1 to 9) : https://leetcode.com/problems/combination-sum-iii/
  1. public List<List<Integer>> combinationSum3(int k, int n) {
  2.         List<List<Integer>> list = new ArrayList<>();
  3.         backtrack(k, n, list, new ArrayList(), 1);
  4.         return list;
  5.     }
  6.    
  7.     private void backtrack(int k, int n, List<List<Integer>> list, List<Integer> sublist, int start) {
  8.         if(n < 0 || sublist.size() > k) {
  9.             return;
  10.         } else if(sublist.size() == k && n == 0) {
  11.             list.add(new ArrayList<>(sublist));
  12.         } else {
  13.             for(int i = start; i <= 9; i++) {
  14.                 sublist.add(i);
  15.                 backtrack(k, n - i, list, sublist, i + 1);
  16.                 sublist.remove(sublist.size() - 1);
  17.             }
  18.         }
  19.     }
复制代码



131. Palindrome Partitioning : https://leetcode.com/problems/palindrome-partitioning/
  1. public List<List<String>> partition(String s) {
  2.    List<List<String>> list = new ArrayList<>();
  3.    backtrack(list, new ArrayList<>(), s, 0);
  4.    return list;
  5. }

  6. public void backtrack(List<List<String>> list, List<String> tempList, String s, int start){
  7.    if(start == s.length())
  8.       list.add(new ArrayList<>(tempList));
  9.    else{
  10.       for(int i = start; i < s.length(); i++){
  11.          if(isPalindrome(s, start, i)){
  12.             tempList.add(s.substring(start, i + 1));
  13.             backtrack(list, tempList, s, i + 1);
  14.             tempList.remove(tempList.size() - 1);
  15.          }
  16.       }
  17.    }
  18. }

  19. public boolean isPalindrome(String s, int low, int high){
  20.    while(low < high)
  21.       if(s.charAt(low++) != s.charAt(high--)) return false;
  22.    return true;
  23. }
复制代码






评分

参与人数 7大米 +10 收起 理由
哎哎哎echo + 1 给你点个赞!
LeoSun + 2 给你点个赞!
mxzhang + 1 给你点个赞!
cherry19880908 + 1 很有用的信息!
yeetatbig4 + 3 谢谢整理到一起,只做过前4道

查看全部评分


上一篇:为啥studentComparator class不能写在里面...
下一篇:对LeetCode里频率的疑问
推荐
akdhfikbk 2020-2-3 12:10:04 | 只看该作者
全局:
为啥backtracking的攻略这么多...

补充内容 (2020-2-12 08:18):
补充下,这不就是lc原贴吗?https://leetcode.com/problems/su ... -questions-in-Java-(Subsets-Permutations-Combination-Sum-Palindrome-Partitioning)
回复

使用道具 举报

🔗
 楼主| xwang139 2020-2-13 01:21:11 来自APP | 只看该作者
全局:
akdhfikbk 发表于 2020/02/03 12:10:04
为啥backtracking的攻略这么多...

补充内容 (2020-2-12 08:18):
补充下,这不就是lc原...
是的,我上面写了来源,原帖大概有5题,我又加了几题。第一段都有写。

补充内容 (2020-2-12 09:36):
这算是一个整理贴,前面加了一些想法,后面加了一些可以用这个模版写的题,如果不符合规定,就请管理员把这个帖子移除吧。
回复

使用道具 举报

🔗
yeetatbig4 2020-2-13 01:41:35 | 只看该作者
全局:
谢谢整理到一起,只做过前4道
回复

使用道具 举报

🔗
mikezy 2020-2-13 16:02:34 | 只看该作者
全局:
我的理解是:
//压栈
//backtracking/dfs
//出栈

仅供参考
回复

使用道具 举报

🔗
 楼主| xwang139 2020-2-14 01:50:56 来自APP | 只看该作者
全局:
mikezy 发表于 2020/02/13 16:02:34
我的理解是:
//压栈
//backtracking/dfs
//出栈

仅供参考
栈应该是说stack,不过bracktrack的这个模版和栈应该没什么关系。或者你指的是add 和 remove的部分。这些类型的题一般要返回的最终结果是List&lt;List&lt;Integer&gt;&gt;,因为我们一直在用list来记录每一次的选择,然后dfs把这个list传进去,由于java里面list是pass by reference,所以指的还是原来这个list,对原来list进行的操作都会保存下来。所以每次返回上一层去探索另一种可能性之前需要remove刚才的选择。但是如果最后要的结果是一个数字或者string,而不是一个list,就不用add 和 remove,因为int 和 string在每次重新pass进dfs的时候是copy of a value。目前写法都是没有return值,而是pass in。所以下面一层对数字和string的操作并不会影响上一层。

欢迎讨论,这也只是我的理解,还是刷题路上的小白一枚。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表