LeetCode回溯问题合集(general solution)
来源:互联网 发布:不要去淘宝搜血滴子 编辑:程序博客网 时间:2024/05/22 00:33
全排列问题,子集问题,组合和问题都是经典的回溯问题。
对于子集问题,若无重复元素情况下,可以有下面的通用解法,这个解法的关键在于:第一层递归,得出单个元素集合,第二层递归得到两个元素集合,以此类推。
public List<List<Integer>> subsets(int[] nums) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, 0); return list;}private void backtrack(List<List<Integer>> list , List<Integer> tempList, int [] nums, int start){ list.add(new ArrayList<>(tempList)); for(int i = start; i < nums.length; i++){ tempList.add(nums[i]); backtrack(list, tempList, nums, i + 1); tempList.remove(tempList.size() - 1); }}对于带有重复元素的子集问题,也是同样的思路,不同之处在于我们需要过滤掉重复的集合。
首先对数组进行排序,来方便后续过滤重复时候的操作。
接着来分析,按照上边的思路,什么时候会出现重复的集合:对于单个元素集合,我们只需要其出现一次,因此有了skip duplicates的操作;那么,对于两个元素的集合是否也是如此呢?答案是肯定的,因为后续的递归可以看作是没有第一个元素以后的子问题。
public List<List<Integer>> subsetsWithDup(int[] nums) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, 0); return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int start){ list.add(new ArrayList<>(tempList)); for(int i = start; i < nums.length; i++){ if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates tempList.add(nums[i]); backtrack(list, tempList, nums, i + 1); tempList.remove(tempList.size() - 1); }}对于全排列问题,同理,在没有重复元素时,第一层递归枚举所有长度为1的templist,第二层递归再往上加没有用过的元素。但是。。。那个每次检查是否重复使用的操作,貌似很费时间啊。
public List<List<Integer>> permute(int[] nums) { List<List<Integer>> list = new ArrayList<>(); // Arrays.sort(nums); // not necessary backtrack(list, new ArrayList<>(), nums); return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){ if(tempList.size() == nums.length){ list.add(new ArrayList<>(tempList)); } else{ for(int i = 0; i < nums.length; i++){ if(tempList.contains(nums[i])) continue; // element already exists, skip tempList.add(nums[i]); backtrack(list, tempList, nums); tempList.remove(tempList.size() - 1); } }}如果有重复元素时,该怎么办呢?
还是先分析,上边那个做法为什么不可行呢。因为数据有重复,不可能使用contains查重了。如何判断重复数据是否加入templist呢?
对于重复数据连在一起的情况,我们只需要其在最后结果中出现一次,如何保证其只出现一次呢,就是在每次递归中保证templist中的数据次序是唯一的。因此需要一个used数组来指示这个次序,只有在前一个数used情况下,后一个重复数才可能接在其后。
public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]); return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){ if(tempList.size() == nums.length){ list.add(new ArrayList<>(tempList)); } else{ for(int i = 0; i < nums.length; i++){ if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue; used[i] = true; tempList.add(nums[i]); backtrack(list, tempList, nums, used); used[i] = false; tempList.remove(tempList.size() - 1); } }}
还有Combination Sum未完待续。。。
续上。
Combination Sum问题是指从某一数组中找出某几个数使其和为定值Target。
第一种情况是可以重复利用元素,这种情况最为简单,套用模板即可解决。
public List<List<Integer>> combinationSum(int[] nums, int target) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, target, 0); return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){ if(remain < 0) return; else if(remain == 0) list.add(new ArrayList<>(tempList)); else{ for(int i = start; i < nums.length; i++){ tempList.add(nums[i]); backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements tempList.remove(tempList.size() - 1); } }}
public List<List<Integer>> combinationSum2(int[] nums, int target) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, target, 0); return list; }private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){ if(remain < 0) return; else if(remain == 0) list.add(new ArrayList<>(tempList)); else{ for(int i = start; i < nums.length; i++){ if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates tempList.add(nums[i]); backtrack(list, tempList, nums, remain - nums[i], i + 1); //i+1很关键 tempList.remove(tempList.size() - 1); } }}
另外的几道Combination Sum的题目与上边两种大同小异,都可以沿用这种思路进行求解。
0 0
- LeetCode回溯问题合集(general solution)
- LeetCode排列组合问题合集
- Leetcode Tree Problem 树问题合集
- leetcode二叉树问题合集
- PDE9 wave equation: general solution
- Leetcode 22, 77: 回溯问题
- LeetCode合集
- 回溯法实现求解子集合和问题
- LeetCode: Buy and Sell Stock相关问题合集
- Leetcode Linked List Problem 链表问题合集
- PDE3&4 Transport equation: derivation & general solution
- solution Of 1019. General Palindromic Number (20)
- leetcode Combination Sum II回溯问题
- [leetcode] 回溯法 Combination Sum 系列问题
- js问题合集
- session问题合集
- 小问题合集
- js问题合集
- 区间动规经典题——石子合并
- CVTE笔试题---字符串的全排列
- Eclipse-source folder文件夹和folder文件夹的区别
- Mybatis+mysql动态分页查询数据案例——房屋信息的接口(IHouseDao)
- 全排列算法(c语言实现)acm练习
- LeetCode回溯问题合集(general solution)
- floyd hdu 1874
- CCF CSP试题 201403-2 窗口
- 蓝桥杯-猜算式
- AWS 遇到的问题
- Mybatis+mysql动态分页查询数据案例——房屋信息的实现类(HouseDaoMybatisImpl)
- 有一个长为n的数组A,求满足0≤a≤b<n的A[b]-A[a]的最大值。 给定数组A及它的大小n,请返回最大差值。 测试样例: [10,5],2 返回:0
- 互联网公司估值
- Windows下配置Shadowsocks