当前位置: 首页 > news >正文

代码随想录算法训练营 Day20

LeetCode 39. 组合总和

链接:39. 组合总和 - 力扣(LeetCode)

思路:

        回溯法:先把回溯法的模板写出来,再一步步考虑终止条件和剪枝。回溯的终止比较好想:当路径和(pathSum)等于target时和路径和大于target(并非所有路径和都等于target,题目已经说明每个元素都大于0),路径和等于target时将path加入到结果数组result中。时间复杂度为:O(N * 2^{N}) ,空间复杂度为 O(N) 。

        剪枝优化:先将candidates数组进行排序(先选择比target小得多的元素进行组合,大于或等于target的元素直接剪枝),再进行回溯,在for循环中再添加一个if判断:当pathSum + candidates[posIdx] > target时直接break退出循环,因为这个部分的组合已经不可能在组合成等于target的组合了。

代码:

回溯法:

class Solution {
public:vector<vector<int>> result;vector<int> path;int pathSum = 0;void backtracking(vector<int> &candidates, int posIdx, int target){if(pathSum == target){result.push_back(path);return;} else if(pathSum > target){return;}for(int idx = posIdx; idx < candidates.size(); idx++){path.push_back(candidates[idx]);pathSum += candidates[idx];backtracking(candidates, idx, target);int popVal = path.back();path.pop_back();pathSum -= popVal;}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {backtracking(candidates, 0, target);return result;}
};

剪枝优化:

class Solution {
public:vector<vector<int>> result;vector<int> path;int pathSum = 0;void backtracking(vector<int> &candidates, int posIdx, int target){if(pathSum == target){result.push_back(path);return;} else if(pathSum > target){return;}for(int idx = posIdx; idx < candidates.size(); idx++){if(pathSum + candidates[idx] > target) break;path.push_back(candidates[idx]);pathSum += candidates[idx];backtracking(candidates, idx, target);int popVal = path.back();path.pop_back();pathSum -= popVal;}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {sort(candidates.begin(), candidates.end());backtracking(candidates, 0, target);return result;}
};

LeetCode 40. 组合总和II

链接:40. 组合总和 II - 力扣(LeetCode)

思路:

        回溯法:思路和39题大致上相同,回溯终止的条件和39题一样,区别就是39题是允许元素的位置重复(即这个位置的元素使用过后还可用继续使用)的,本题的组合是不允许元素位置重复的,那么回溯中的for循环遍历就需要重新修改。因为元素位置不允许重复,所有可以设置一个数组来记录对应元素下标位置的使用情况,再往下层递归时,used数组是用于回溯for循环遍历中树的一层中需要跳过的下标(并不是向下递归需要跳过的下标,因为本题的回溯递归的开始位置每次都会加1,不会出现重复使用上一层使用过的位置),可以这样来理解:将初始传入数组排完序后,尽量在数组靠前部分找出所有的符合的组合,每个不同元素的第一个出现的元素开始组合,其后相同于第一个元素的元素再进行组合时必定会和第一个元素的组合结果重复,所以需要去重:candidates[idx] == candidates[i - 1] && used[idx - 1] = false 这一句表示的是在同一树层上的相同元素存在使用过的元素(使用过的元素再进行使用组合必定会重复)。时间复杂度为 O(N * 2^{N}) ,空间复杂度为 O(N) 。

代码:
 

class Solution {
public:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& candidates, int target, int sum, int startIdx, vector<bool>used){if(sum == target){result.push_back(path);return;} else if(sum > target){return;}for(int idx = startIdx; idx < candidates.size(); idx++){if(idx > 0 && candidates[idx - 1] == candidates [idx] && used[idx - 1] == false){continue;}path.push_back(candidates[idx]);sum += candidates[idx];used[idx] = true;backtracking(candidates, target, sum, idx + 1, used);path.pop_back();sum -= candidates[idx];used[idx] = false;}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {sort(candidates.begin(), candidates.end());vector<bool>used(candidates.size(), false);int sum = 0;backtracking(candidates, target, sum, 0, used);return result;}
};

LeetCode 131. 分割回文串

链接:131. 分割回文串 - 力扣(LeetCode)

思路:

        回溯法:这题使用回溯法和以往几题有所不同,本题是选取分割线的位置进行分割子串,并判断该子串是否是回文串(从前往后和从后往前的元素全部一致),将符合条件的这些子串加入到结果数组中。关键就是如何划分分割线形成一个子串:请参考下图。很明显下标区间就是对应分割线划分的子串,比如:下标0划分的子串就是a,下标0到1划分的子串就是aa,下标0到2划分的子串就是aab。因此代码就不难写了,在回溯for循环中先判断本次传入子串是否满足回文,若满足,则将该子串加入到路径path中继续递归,若不满足则继续开始下一个位置划分;当当前path的长度已经等于candidates主串的长度说明形成一组满足要求划分的子串,将其加入到结果数组中,继续递归直至找出所有满足的子串。时间复杂度为 O(N * 2^{N}) ,空间复杂度为 O(N^{2}) 。

代码:

回溯法:

class Solution {
public:vector<vector<string>> result;vector<string> path;bool isPalindrome(string &s, int begin, int end){while(begin < end){if(s[begin] != s[end]) return false;begin++;end--;}return true;}void backtracking(string &s, int startIdx){if(startIdx == s.size()){result.push_back(path);return;}for(int idx = startIdx; idx < s.size(); idx++){if(isPalindrome(s, startIdx, idx)){path.push_back(s.substr(startIdx, idx - startIdx + 1));backtracking(s, idx + 1);path.pop_back();} else {continue;}}}vector<vector<string>> partition(string s) {backtracking(s, 0);return result;}
};

http://www.lryc.cn/news/613558.html

相关文章:

  • Redis面试精讲 Day 13:Redis Cluster集群设计与原理
  • P1037 [NOIP 2002 普及组] 产生数
  • NFS 服务器
  • Docker容器强制删除及文件系统修复完整指南
  • mysql的InnoDB索引总结
  • 传统防火墙与下一代防火墙
  • 中介效应分析 原理解释 实例分析
  • python中的集合
  • 移动端录屏需求调研:以小熊录屏为例的轻量级实现方案
  • 线程池创建线程
  • jmeter要如何做接口测试?
  • Jmeter使用第一节-认识面板(Mac版)
  • 【线性代数】5特征值和特征向量
  • Vue3获取当前页面相对路径
  • 站在Vue的角度,对比鸿蒙开发中的状态管理
  • Casrel关系抽取
  • vue3 el-select 加载触发
  • AI绘画:生成唐初李世民全身像提示词
  • 【unity实战】使用Unity程序化生成3D随机地牢(附项目源码)
  • 8.3.1 注册服务中心Etcd
  • 【感知机】感知机(perceptron)学习算法的对偶形式
  • Java包装类详解与应用指南
  • Caffeine 三种过期策略详解
  • Day 6: CNN卷积神经网络 - 计算机视觉的核心引擎
  • MCU中的USB
  • 论文解读:单个标点符号如何欺骗LLM,攻破AI评判系统
  • Linux总线,设备和驱动关系以及匹配机制解析
  • vue打包号的文件如何快速查找文件打包后的位置
  • Redis 编译错误:缺少静态库文件,如何解决?
  • 在NVIDIA Orin上用TensorRT对YOLO12进行多路加速并行推理时内存泄漏 (中)