1.组合总和
题目链接/文章讲解: 代码随想录视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_bilibili
代码:(未剪枝版 )
class Solution { private: vectorpath; vector > result; void backtracking(vector & candidates,int target,int startIndex){ // 递归出口 if(target < 0){ return; } if(target == 0){ result.push_back(path); return; } // 处理结点 for(int i = startIndex;i < candidates.size();i++){ target -= candidates[i]; path.push_back(candidates[i]); backtracking(candidates,target,i); path.pop_back(); target += candidates[i]; } } public: vector > combinationSum(vector & candidates, int target) { if(candidates.size() == 0) return result; backtracking(candidates,target,0); return result; } };
代码:(剪枝版)
class Solution { private: vectorpath; vector > result; void backtracking(vector & candidates,int target,int startIndex){ // 递归出口 if(target < 0){ return; } if(target == 0){ result.push_back(path); return; } // 处理结点 // 这里加了判断:如果下一轮的target已经小于0了,就没有必要递归了 for(int i = startIndex;i < candidates.size() && target - candidates[i] >= 0;i++){ target -= candidates[i]; path.push_back(candidates[i]); backtracking(candidates,target,i); path.pop_back(); target += candidates[i]; } } public: vector > combinationSum(vector & candidates, int target) { if(candidates.size() == 0) return result; sort(candidates.begin(), candidates.end()); // 涉及大小的剪枝必排序 backtracking(candidates,target,0); return result; } };
思路:学到了这种元素数量不限制的取的解法
其实startIndex变量还是要有,因为我们求的是组合,要数层去重;并且这是在同一个数组里取数。
而数量不限,体现在我在递归里传入的starIndex的值是i,而不是i+1了。之前说过for循环管控每一层数的宽度;递归则管控数的深度。这样更改后,就可以在取完一个数的情况后,继续取这个数。实现如下图所示的效果:
关于剪枝的操作。这种涉及到总和大小的取值时。剪枝就要将给定的数组排序! 而且要注意给定的数组元素里全是正数,只有正数才越加越大。
这道题,我直接用目标总和target取减去每一个结点的数值了,看最后是否被减为0了。
2.组合总和2
题目链接/文章讲解:代码随想录
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
代码:去重且剪枝过的
class Solution { private: vectorpath; vector > result; void backtracking(vector & candidates,int target,int startIndex,vector & used){ if(target < 0){ return; } if(target == 0){ result.push_back(path); return; } for(int i = startIndex;i < candidates.size() && target - candidates[i] >= 0;i++){ // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过 // used[i - 1] == false,说明同一树层candidates[i - 1]使用过 // 要对同一树层使用过的元素进行跳过 if(i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false){ continue; } // 数层去重 used[i] = true; target -= candidates[i]; path.push_back(candidates[i]); backtracking(candidates,target,i + 1,used); path.pop_back(); target += candidates[i]; used[i] = false; } } public: vector > combinationSum2(vector & candidates, int target) { vector used(candidates.size(),false); sort(candidates.begin(),candidates.end()); backtracking(candidates,target,0,used); return result; } };
思路:
这道题和上题的区别是:
每个元素只能使用一次——startIndex为i+1;
给定的元素有重复值,但是要去重后的结果:这里就是难点。首先去重是有两个维度的。
一个组合里可以有相同大小的不同元素,所以递归操作里不需要去重,即上图的同一个子树的数枝上不需要去重;不同组合不能相同,所以要在数层去重。
这里用一个数组used来记录,遇到的和前一个元素相同的情况(这里去重先去把给定数组排序)时,到底是和前一个相同元素是在同一个树枝里的,还是同一个树层里的。
在同一个树枝里:前一个元素已经加入到了path里,已经使用过了。
在同一个树层里:前一个元素没有加入到path里,没有被使用,这是独立的两个分支。
所以如果遇到这种树层里重复的情况,加个判断语句,直接continue(因为接下来的情况还有我们要收集的,所以只是跳过这一种情况,用continue)
3.分割回文串
代码随想录
视频讲解:带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!_哔哩哔哩_bilibili
代码:
class Solution { private: vectorpath; vector > result; bool isPalindrome(string& s,int start,int end){ while(start <= end){ if(s[start] != s[end]){ return false; } start++; end--; } return true; } void backtracking(string& s,int startIndex){ // 递归出口 if(startIndex >= s.size()){ result.push_back(path); return; } // 遍历结点 // 回文子串的范围是[startIndex,i] for(int i = startIndex;i < s.size();i++){ // 在这里直接判断是否子串满足回文子串 if(isPalindrome(s,startIndex,i)){ string str = s.substr(startIndex,i - startIndex + 1); path.push_back(str); }else{ continue; } backtracking(s,i + 1); path.pop_back(); } } public: vector > partition(string s) { if(s.size() == 0) return result; backtracking(s,0); return result; } };
思路:
关于substr函数:
这道题很巧妙地将startIndex作为了子串的开始下标,子串的范围可以表示为[startIndex,i]。
同时,本题将判断条件放在了for循环的里面来判断,来决定是否要执行下面的递归回溯操作。判断用了一个额外的函数来判断回文子串。
细节问题其实就这些。
还没有评论,来说两句吧...