框架

回溯的核心特征,做选择。

回溯主要基于递归,它的特点是选择以及撤销选择,非常经典。子问题性质不是很明显。算法复杂度可以从子集数考虑,空间复杂度则一般为递归深度N。

回溯法框架如下:

result = []
def backtrack(路径, 选择列表):if 满足结束条件:result.add(路径)returnfor 选择 in 选择列表:剪枝做选择backtrack(路径, 选择列表)撤销选择

集合选择(子集数,全排列)是典型的回溯法应用,而且它们的算法非常规则,简洁。

以40,求子集组合为target的所有可能,元素不可重复,但可选集合有重复。

public class Solution {public List<List<Integer>> combinationSum2(int[] candidates, int target) {List<List<Integer>> res = new ArrayList<>();// 排序,方便去重Arrays.sort(candidates);backtrack(candidates, target, 0, new ArrayList<>(), res);return res;}private void backtrack(int[] nums, int sum, int k, ArrayList<Integer> cur, List<List<Integer>> res) {// 满足条件加入集合if(sum == 0) res.add(new ArrayList<>(cur));else if (sum < 0) return;else {// 选择所有状态for (int i = k; i < nums.length; i++) {// 可选集合去重if (i > k && nums[i] == nums[i-1])continue;// 回溯cur.add(nums[i]);backtrack(nums, sum - nums[i], i + 1, cur, res);cur.remove(cur.size() - 1);}}}
}

上述算法包含了集合选择题目的所有核心要点(注释)。

  • 为何是子集数

    事实上这个算法不算直观,递归树也不算直观。但观察状态选择部分,可以发现,对于每个状态,它都有两个分支,选择(加入),或者不选,直接i++。因而本质就是子集数。

  • **全排列为何是子集数?**其实唯一的区别就是,全排列每次都考虑所有状态,而子集数选过的之前的状态不再考虑(关键是上述回溯调用的k值)。另外全排列需要标记。

  • 子集数去重,这是个很抽象的点,理解请参考 47 有重复的全排列。它的本质是,对可选集合去重,实际操作就是上述代码的sort和for中的if部分。

  • 考虑空集,请参考题目78,90.

问题

78. Subsets

子集数

https://leetcode.com/problems/subsets/

/*
* Runtime: 0 ms, faster than 100.00% of Java online submissions for Subsets.
* Memory Usage: 39.7 MB, less than 16.56% of Java online submissions for Subsets.
* */
public class SolutionV2 {public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> res = new ArrayList<>();backtrack(nums, 0, new ArrayList<>(), res);return res;}private void backtrack(int[] nums, int k, ArrayList<Integer> cur, List<List<Integer>> res) {// 包括空的简洁实现方法,相当于在nums后加上null元素res.add(new ArrayList<>(cur));for (int i = k; i < nums.length; i++) {cur.add(nums[i]);backtrack(nums, i + 1, cur, res);cur.remove(cur.size() - 1);}}
}

90. Subsets II

子集数,集合有重复元素,求出子集不得重复

https://leetcode.com/problems/subsets-ii/

/*
Runtime: 1 ms, faster than 99.52% of Java online submissions for Subsets II.Memory Usage: 39.5 MB, less than 33.78% of Java online submissions for Subsets II.
*/class Solution {public List<List<Integer>> subsetsWithDup(int[] nums) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(nums);backtrack(nums, 0, new ArrayList<>(), res);return res;}private void backtrack(int[] nums, int k, ArrayList<Integer> cur, List<List<Integer>> res) {// 包括空的简洁实现方法,相当于在nums后加上null元素res.add(new ArrayList<>(cur));for (int i = k; i < nums.length; i++) {if (k < i && nums[i] == nums[i-1]) continue;cur.add(nums[i]);backtrack(nums, i + 1, cur, res);cur.remove(cur.size() - 1);}}
}

46. Permutations

全排列
https://leetcode.com/problems/permutations/

/*Runtime: 2 ms, faster than 49.39% of Java online submissions for Permutations.Memory Usage: 39.1 MB, less than 82.56% of Java online submissions for Permutations.
*/class Solution {public List<List<Integer>> permute(int[] nums) {List<List<Integer>> res = new ArrayList<>();Map<Integer, Boolean> flag = new HashMap<>();backtrack(nums,  new ArrayList<>(), res, flag);return res;}private void backtrack(int[] nums,ArrayList<Integer> cur, List<List<Integer>> res, Map<Integer, Boolean> flag) {if (cur.size() == nums.length) {res.add(new ArrayList<>(cur));} else {for (int i = 0; i < nums.length; i++) {if (flag.getOrDefault(i, false)) continue;cur.add(nums[i]);flag.put(i, true);backtrack(nums, cur, res, flag);cur.remove(cur.size() - 1);flag.put(i, false);}}}
}

47 Permutations II

全排列,元素有重复

https://leetcode.com/problems/permutations-ii/

/** 47. Permutations II* 全排列* https://leetcode.com/problems/permutations-ii/* *//*
(i > 0 && nums[i] == nums[i - 1] && !flag[i-1])该条件是去重的关键
按照类似的调节,排序后的数组重复元素优先只会选择第一个,以[1 1 2]为例子,该递归树被去重为如下,这也是在子集数去重中的原理——每次可选集合没有重复元素/ | \1  1  2/|\1 1 2/|\1 1 2
>>>>>>/ \1   2/ \1   2/ \1   2
Runtime: 1 ms, faster than 98.93% of Java online submissions for Permutations II.
Memory Usage: 39.8 MB, less than 38.95% of Java online submissions for Permutations II.*/class Solution {public List<List<Integer>> permuteUnique(int[] nums) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(nums);backtrack(nums, new ArrayList<>(), res, new boolean[nums.length]);return res;}private void backtrack(int[] nums, ArrayList<Integer> cur, List<List<Integer>> res, boolean[] flag) {if (cur.size() == nums.length) {res.add(new ArrayList<>(cur));} else {for (int i = 0; i < nums.length; i++) {if (flag[i] || (i > 0 && nums[i] == nums[i - 1] && !flag[i-1])) continue;cur.add(nums[i]);flag[i] = true;backtrack(nums, cur, res, flag);cur.remove(cur.size() - 1);flag[i] = false;}}}public static void main(String[] args) {int[] candidates = {1, 1, 5};System.out.println(new Solution().permuteUnique(candidates));}
}

39. Combination Sum

组合,元素无限制,和等于目标值

https://leetcode.com/problems/combination-sum/

/*
更好的形式
Runtime: 3 ms, faster than 78.07% of Java online submissions for Combination Sum.Memory Usage: 39.4 MB, less than 44.99% of Java online submissions for Combination Sum.
*/
public class SolutionV2 {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> r;r = new ArrayList<>();backtrack(target, 0, new ArrayList<>(), r, candidates);return r;}private void backtrack(int sum, int k, ArrayList<Object> cur, List<List<Integer>> result, int[] nums) {if(sum == 0) result.add(new ArrayList(cur));else if(sum < 0) return;else{for (int i = k; i < nums.length; i++) {cur.add(nums[i]);backtrack(sum - nums[i], i, cur, result, nums);cur.remove(cur.size() - 1);}}}
}

40. Combination Sum II

组合,求目标和,元素不重复

/*
集合选择框架版本
非常好的子集数实现。
算法复杂度,它的递归树是一个倾斜的递归树,实际上不好直接考虑,一个宽松的上界为元组可重复的版本,即使递归深度为target,
每个节点能选择的为N,因而为O(target ^ N)。从子集数来考虑,该问题的状态空间等于子集数数量,也就是O(2^N).
空间复杂度,递归深度等于O(N),N为集合大小
Runtime: 3 ms, faster than 81.28% of Java online submissions for Combination Sum II.Memory Usage: 39.5 MB, less than 41.18% of Java online submissions for Combination Sum II.
*/public class SolutionV2 {public List<List<Integer>> combinationSum2(int[] candidates, int target) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(candidates);backtrack(candidates, target, 0, new ArrayList<>(), res);return res;}private void backtrack(int[] nums, int sum, int k, ArrayList<Integer> cur, List<List<Integer>> res) {if(sum == 0) res.add(new ArrayList<>(cur));else if (sum < 0) return;else {for (int i = k; i < nums.length; i++) {// 防止组合重复的要点if (i > k && nums[i] == nums[i-1])continue;cur.add(nums[i]);backtrack(nums, sum - nums[i], i + 1, cur, res);cur.remove(cur.size() - 1);}}}
}

131 Palindrome Partitioning

切割字符串,使得所有切分都是回文,求所有可能

https://leetcode.com/problems/palindrome-partitioning/

/*
算法复杂度,这个问题本质上是子集数的问题,所以总共有O(2^N)个节点,每个节点需要O(N)
来判断是否是回文,所以时间复杂度为O(N 2^N)
空间复杂度为递归深度,O(N)
Runtime: 7 ms, faster than 85.32% of Java online submissions for Palindrome Partitioning.Memory Usage: 52.9 MB, less than 53.17% of Java online submissions for Palindrome Partitioning.
*/public class SolutionV2 {public List<List<String>> partition(String s) {List<List<String>> res = new ArrayList<>();backtrack(0, s, new ArrayList<String>(), res);return res;}private void backtrack(int k, String s, ArrayList<String> cur, List<List<String>> res) {if (k == s.length()) res.add(new ArrayList<>(cur));else {for (int i = k; i < s.length(); i++) {if (!isPalindrome(s, k, i)) continue;cur.add(s.substring(k, i+1));backtrack(i + 1, s, cur, res);cur.remove(cur.size() - 1);}}}private boolean isPalindrome(String sub, int low, int high) {for (; low < high; low++, high--)if (sub.charAt(low) != sub.charAt(high)) return false;return true;}
}

Ref

  • 全部灵感来自 https://leetcode.com/problems/combination-sum/discuss/16502/A-general-approach-to-backtracking-questions-in-Java-(Subsets-Permutations-Combination-Sum-Palindrome-Partitioning)

回溯法,子集选择合集相关推荐

  1. python 回溯法 子集树模板 系列 —— 3、0-1背包问题

    问题 给定N个物品和一个背包.物品i的重量是Wi,其价值位Vi ,背包的容量为C.问应该如何选择装入背包的物品,使得放入背包的物品的总价值为最大? 分析 显然,放入背包的物品,是N个物品的所有子集的其 ...

  2. python回溯方法的模板_Python基于回溯法子集树模板解决0-1背包问题实例

    本文实例讲述了Python基于回溯法子集树模板解决0-1背包问题.分享给大家供大家参考,具体如下: 问题 给定N个物品和一个背包.物品i的重量是Wi,其价值位Vi ,背包的容量为C.问应该如何选择装入 ...

  3. python找零钱问题_Python基于回溯法子集树模板解决找零问题示例

    本文实例讲述了Python基于回溯法子集树模板解决找零问题.分享给大家供大家参考,具体如下: 问题 有面额10元.5元.2元.1元的硬币,数量分别为3个.5个.7个.12个.现在需要给顾客找零16元, ...

  4. python全排列问题_Python基于回溯法子集树模板解决全排列问题示例

    本文实例讲述了Python基于回溯法子集树模板解决全排列问题.分享给大家供大家参考,具体如下: 问题 实现 'a', 'b', 'c', 'd' 四个元素的全排列. 分析 这个问题可以直接套用排列树模 ...

  5. python棋盘放米问题_Python基于回溯法子集树模板解决马踏棋盘问题示例

    本文实例讲述了Python基于回溯法子集树模板解决马踏棋盘问题.分享给大家供大家参考,具体如下: 问题 将马放到国际象棋的8*8棋盘board上的某个方格中,马按走棋规则进行移动,走遍棋盘上的64个方 ...

  6. 八皇后问题python回溯_解决Python基于回溯法子集树模板实现8皇后问题

    这篇文章主要介绍了Python基于回溯法子集树模板实现8皇后问题,简单说明了8皇后问题的原理并结合实例形式分析了Python回溯法子集树模板解决8皇后问题的具体实现技巧,需要的朋友可以参考下 本文实例 ...

  7. python回溯方法的模板_实例讲解Python基于回溯法子集树模板实现图的遍历功能

    这篇文章主要介绍了Python基于回溯法子集树模板实现图的遍历功能,结合实例形式分析了Python使用回溯法子集树模板针对图形遍历问题的相关操作技巧与注意事项,需要的朋友可以参考下 本文实例讲述了Py ...

  8. python 回溯法 子集树模板

    回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足 ...

  9. 回溯法-子集树排序树满m叉树

    回溯法是在仅给出初始节点.目标节点及产生子节点的条件的情况下,构造出一个图,然后按照深度优先搜索的思想,在有关条件的约束下扩展到目标节点,从而找到问题的解. 子集树 当所给的问题是从n个元素组成的集合 ...

最新文章

  1. 全文翻译(三) TVM An Automated End-to-End Optimizing Compiler
  2. 现在,Serverless 真的已经成熟了吗?
  3. 叉乘(三)——线段与线段相交吗?
  4. [机器学习笔记] Note3--多变量线性回归
  5. python汉化之后好用吗_Python官方中文文档上线了:各种教程已汉化,不用再苦等野生翻译...
  6. Cloudera迁移scm数据库
  7. 【从零开始学架构-李运华】01|架构到底是指什么?
  8. python中单引号、双引号、三单引号、三双引号用法详解
  9. Last-Modified和Etag
  10. AE 制作一个简单的动画
  11. 32位cpu和64位cpu对内存地址和内存单元的差别
  12. 理解以太坊 Serenity - 第二部分: Casper
  13. 有一种风险让期权交易员如坐针毡——谨慎管理 “大头针风险”
  14. 三星临时取消Galaxy Fold中国发布会;视觉中国网站部分恢复上线?官方回应:并未恢复;特斯拉周一发布完全自动驾驶将网络直播 | 雷锋早报...
  15. Sherman-Morrison-Woodbury,SMW恒等式
  16. 【新媒体】现阶段新闻聚合的玩法 1
  17. 不平衡样本的分类实践---Credit-Card-Fraud-Detection
  18. 立冬穿短袖,生活不将就 2020.11.18日记
  19. kafka 报错: LEADER_NOT_AVAILABLE
  20. 计算机登录忘记密码怎么办,电脑登录密码忘记了怎么办

热门文章

  1. SpringCloud学习笔记(十二)基于Hystrix解决雪崩效应
  2. 【附源码】Python计算机毕业设计人脸识别考勤系统
  3. 23、LZ77压缩和解压
  4. 深度学习下运维日志分析的趋势解读与应用实践
  5. 为什么深层神经网络难以训练_深度学习与统计力学(III) :神经网络的误差曲面...
  6. 5G网络能力开放部署及关键技术方案
  7. C# web references 调用出错 The underlying connection was closed: An unexpected error occurred on a send.
  8. 设计,让交叉口更安全
  9. 一个简单的python爬虫程序
  10. ANSYS_APDL在绘制 vonMises(等效)应力云图报错:The requested data S is not available.The PLES command is ignored.