Leetcode算法训练日记 | day27

一、组合总和

1.题目

Leetcode:第 39 题

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

2.解题思路

使用回溯算法来解决组合求和问题。backtracking 函数是一个递归函数,它尝试将每个可能的数字添加到当前路径中,并递归地继续添加下一个数字,直到找到所有可能的组合。每次递归调用时,都会检查当前路径是否满足条件(即当前和是否等于目标和),如果满足,则将其添加到结果中。combinationSum 函数是公共接口,它初始化结果和路径,然后开始递归过程。

3.实现代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;// 一、组合总和
class Solution1 {
public:vector<vector<int>> result; // 用于存储所有可能组合的结果集vector<int> path; // 用于记录当前组合的路径// 递归函数,用于生成所有可能的组合void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {if (sum > target) { // 如果当前和已经超过目标和,递归返回,不再继续扩展当前路径return;}if (sum == target) { // 如果当前和等于目标和,表示找到了一个有效的组合result.push_back(path); // 将当前路径添加到结果集中return; // 递归返回,不再继续扩展当前路径}// 从startIndex开始遍历候选数字数组for (int i = startIndex; i < candidates.size(); i++) {sum += candidates[i]; // 将当前数字添加到当前和中path.push_back(candidates[i]); // 将当前数字添加到当前路径中// 递归调用backtracking函数,尝试添加下一个数字backtracking(candidates, target, sum, i);// 回溯,从当前和中移除最后一个数字,并从当前路径中移除最后一个数字sum -= candidates[i];path.pop_back();}}// 主函数,用于生成给定目标和的所有可能组合vector<vector<int>> combinationSum(vector<int>& candidates, int target) {result.clear(); // 清空之前的组合结果集path.clear(); // 清空当前路径backtracking(candidates, target, 0, 0); // 调用递归函数,从索引0开始生成组合return result; // 返回所有可能的组合结果集}
};// 二、组合总和(剪枝优化)
class Solution2 {
public:vector<vector<int>> result; // 用于存储所有可能组合的结果集vector<int> path; // 用于记录当前组合的路径// 递归函数,用于生成所有可能的组合void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {if (sum == target) { // 如果当前和等于目标和,表示找到了一个有效的组合result.push_back(path); // 将当前路径添加到结果集中return; // 递归返回,不再继续扩展当前路径}// 遍历候选数字数组,从startIndex开始,直到不能形成有效组合为止for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {//剪枝优化sum += candidates[i]; // 将当前数字添加到当前和中path.push_back(candidates[i]); // 将当前数字添加到当前路径中// 递归调用backtracking函数,尝试添加下一个数字backtracking(candidates, target, sum, i);// 回溯,从当前和中移除最后一个数字,并从当前路径中移除最后一个数字sum -= candidates[i];path.pop_back();}}// 主函数,用于生成给定目标和的所有可能组合vector<vector<int>> combinationSum(vector<int>& candidates, int target) {result.clear(); // 清空之前的组合结果集path.clear(); // 清空当前路径sort(candidates.begin(), candidates.end()); // 对候选数字数组进行排序,以便使用剪枝backtracking(candidates, target, 0, 0); // 调用递归函数,从索引0开始生成组合return result; // 返回所有可能的组合结果集}
};//测试
int main()
{Solution1 s;vector<vector<int>> result;vector<int>candidates = { 2, 3, 6, 7 };int target = 7;result = s.combinationSum(candidates, target);cout << "所有的组合有:" << endl;for (auto & ans :result) {for (auto& i : ans) {cout << i << "  ";}cout << endl;}cout << endl;return 0;
}

 

二、组合Ⅱ

1.题目

Leetcode:第 40 题

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
2.解题思路

使用回溯算法来解决组合求和问题。combinationSum2首先对候选数字进行排序,然后初始化路径和结果集,并开始回溯过程。backtracking函数是回溯算法的核心,它尝试将每个候选数字添加到当前路径中,并递归地继续寻找满足条件的组合。为了避免重复使用相同的数字,它会跳过与前一个元素相同的候选数字。当找到一个满足条件的组合时,它会将其添加到结果集中。通过这种方式,backtracking函数能够找到所有可能的组合。

3.实现代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;// 一、组合总和II
class Solution {
public:// 定义一个二维数组result,用于存储所有满足条件的组合vector<vector<int>> result;// 定义一个一维数组path,用于存储当前的组合路径vector<int> path;// 定义backtracking函数,用于实现回溯算法void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {// 如果当前和sum等于目标值target,则将当前路径path添加到结果集result中if (sum == target) {result.push_back(path);return;}// 遍历候选数字candidates,从startIndex开始,直到遍历完或者当前和超过目标值for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {// 如果当前元素与前一个元素相同,跳过当前循环,避免重复使用相同的数字if (i > startIndex && candidates[i] == candidates[i - 1]) {continue;}// 将当前候选数字加入到当前和sum中,并将其加入到当前路径path中sum += candidates[i];path.push_back(candidates[i]);// 递归调用backtracking函数,尝试使用下一个候选数字backtracking(candidates, target, sum, i + 1);// 回溯:从当前和中减去当前候选数字,并从当前路径中移除它sum -= candidates[i];path.pop_back();}}// 定义combinationSum2函数,用于求解组合求和问题vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {// 清空当前路径path和结果集resultpath.clear();result.clear();// 将候选数字candidates按升序排序,以便在回溯过程中可以跳过重复的数字sort(candidates.begin(), candidates.end());backtracking(candidates, target, 0, 0); // 调用backtracking函数,开始回溯过程return result;// 返回结果集result,其中包含了所有满足条件的组合}
};//测试
int main()
{Solution s;vector<vector<int>> result;vector<int>candidates = { 10,1,2,7,6,1,5 };int target = 8;result = s.combinationSum2(candidates, target);cout << "所有的组合有:" << endl;for (auto& ans : result) {for (auto& i : ans) {cout << i << "  ";}cout << endl;}cout << endl;return 0;
}

 

三、分割回文串

1.题目

Leetcode:第 131 题

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 

回文串

 。返回 s 所有可能的分割方案。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]
2.解题思路

使用回溯算法来解决分割回文串问题。partition是主函数,它初始化路径和结果集,并开始回溯过程。backtracking函数是回溯算法的核心,它尝试将字符串分割为多个子字符串,并递归地继续寻找满足条件的分区。isPalindrome函数用于检查一个子字符串是否是回文串。通过这种方式,backtracking函数能够找到所有可能的满足条件的分区。

3.实现代码
#include <iostream>
#include <vector>
using namespace std;// 一、分割回文串
class Solution1 {
public:// 定义一个二维字符串数组result,用于存储所有满足条件的分区结果vector<vector<string>> result;// 定义一个一维字符串数组path,用于存储当前的分区路径vector<string> path;// 定义backtracking函数,用于实现回溯算法void backtracking(const string& s, int startIndex) {// 如果当前起始索引startIndex已经到达字符串s的末尾,说明已经处理完字符串if (startIndex >= s.size()) {result.push_back(path);// 将当前路径path添加到结果集result中return;}// 遍历字符串s,从startIndex开始,直到遍历完字符串for (int i = startIndex; i < s.size(); i++) {// 检查从startIndex到i的子字符串是否是回文串if (isPalindrome(s, startIndex, i)) {// 从字符串s中截取从startIndex到i的子字符串,并保存到局部变量str中string str = s.substr(startIndex, i - startIndex + 1);path.push_back(str);// 将子字符串str添加到当前路径path中}else {continue;}backtracking(s, i + 1);// 递归调用backtracking函数,尝试使用下一个分区path.pop_back(); // 回溯:从当前路径中移除最后一个元素}}// 定义isPalindrome函数,用于检查一个子字符串是否是回文串bool isPalindrome(const string& s, int start, int end) {// 使用双指针方法,从字符串的两端开始向中间遍历for (int i = start, j = end; i < j; i++, j--) {// 如果在遍历过程中发现字符不相等,则该子字符串不是回文串if (s[i] != s[j]) {return false;}}return true;// 如果所有字符都相等,则该子字符串是回文串}// 定义partition函数,用于求解字符串分区问题vector<vector<string>> partition(string s) {// 清空当前路径path和结果集resultresult.clear();path.clear();backtracking(s, 0); // 调用backtracking函数,开始回溯过程return result; // 返回结果集result,其中包含了所有满足条件的分区结果}
};// 二、分割回文串(优化)
class Solution2 {
public:// 定义一个二维字符串数组result,用于存储所有满足条件的分区结果vector<vector<string>> result;// 定义一个一维字符串数组path,用于存储当前的分区路径,即已经找到的回文子串vector<string> path;// 定义一个二维布尔数组isPalindrome,用于存储字符串中每个子串是否是回文串的预先计算结果vector<vector<bool>> isPalindrome;// 定义backtracking函数,用于实现回溯算法void backtracking(const string& s, int startIndex) {// 如果当前起始索引startIndex已经到达或超过字符串s的末尾,说明已经找到了一组分区方案if (startIndex >= s.size()) {// 将当前路径path添加到结果集result中result.push_back(path);return;}// 遍历字符串s,从startIndex开始,直到遍历完字符串for (int i = startIndex; i < s.size(); i++) {// 如果从startIndex到i的子串是回文串(根据预先计算的isPalindrome向量)if (isPalindrome[startIndex][i]) {// 从字符串s中截取从startIndex到i的子串,并保存到局部变量str中string str = s.substr(startIndex, i - startIndex + 1);// 将子串str添加到当前路径path中path.push_back(str);} else {continue;}backtracking(s, i + 1);// 递归调用backtracking函数,尝试使用下一个起始位置i+1进行分区path.pop_back();// 回溯过程,从当前路径中移除最后一个元素}}// 定义computePalindrome函数,用于预先计算字符串中所有子串是否是回文串void computePalindrome(const string& s) {// 根据字符串s的大小,初始化isPalindrome二维向量,所有元素初始为falseisPalindrome.resize(s.size(), vector<bool>(s.size(), false));// 从后向前遍历字符串s,计算每个子串是否是回文串for (int i = s.size() - 1; i >= 0; i--) {// 由于是检查子串,所以需要从后向前计算,以确保在计算i行时,i+1行已经计算好了for (int j = i; j < s.size(); j++) {// 如果子串的长度为1,则它是回文串if (j == i) {isPalindrome[i][j] = true;}// 如果子串的长度为2,则检查两个字符是否相等else if (j - i == 1) {isPalindrome[i][j] = (s[i] == s[j]);}// 如果子串的长度大于2,则检查首尾字符是否相等,并且中间子串是否是回文串else {isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i + 1][j - 1]);}}}}// 定义partition函数,用于求解字符串分区问题vector<vector<string>> partition(string s) {// 清空当前路径path和结果集resultresult.clear();path.clear();computePalindrome(s);// 预先计算字符串s中所有子串是否是回文串backtracking(s, 0); // 调用backtracking函数,开始回溯过程,以0作为起始位置return result;// 返回结果集result,其中包含了所有满足条件的分区结果}
};//测试
int main()
{Solution1 p;vector<vector<string>> result;string s = "aab";result = p.partition(s);cout << "所有的组合有:" << endl;for (auto& ans : result) {for (auto& i : ans) {cout << i << "  ";}cout << endl;}cout << endl;return 0;
}

ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/630465.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 红黑树

01.红黑树的定义&#xff1a; 每一个结点有五个属性&#xff1a;

SQL Server Management Studio 显示行号

前言 在使用 SQL Server Management Studio (SSMS) 进行数据库管理和查询时&#xff0c;能够看到代码的行号是非常有用的。这可以帮助您更容易地定位代码错误、讨论特定的代码行&#xff0c;或者在执行长查询时快速找到特定行。在本文中&#xff0c;我将向您展示如何在 SSMS 中…

Redis中的Lua脚本(二)

Lua脚本 创建排序辅助函数 为了防止带有副作用的函数令脚本产生不一致的数据&#xff0c;Redis对math库的math.random函数和math.randomseed函数进行了替换。对于Lua脚本来说&#xff0c;另一个可能产生不一致数据的地方是哪些带有不确定性质的命令&#xff0c;比如对于一个集…

面试八股——线程

进程与线程的对比 线程的创建方式⭐ 继承Thread类 实现Runnable接口 实现Callable类 适用于需要获取返回值的线程。 线程池创建线程 Callable与Runnable的区别 1. 首先Runnable的run方法是没有返回值的&#xff0c;Callable的call方法有返回值。 2. Runnable不能向外抛出异常…

混合云构建-如何创建一个高可用的Site to Site VPN 连接 Azure 和GCP云

在现代云计算环境中,企业通常会采用多云战略,将工作负载分布在不同的云服务提供商上。这种方式可以提高可用性、降低供应商锁定风险,并利用每个云提供商的独特优势。然而,在这种情况下,需要确保不同云环境之间的互联互通,以实现无缝的数据传输和应用程序集成。 本文将详细介绍…

Python根据公募基金在一定时期内持有的股票数据进行社会网络分析

【背景】根据提供的公募基金在一定时期内持有的股票数据&#xff0c;构建一个社会网络分析框架&#xff0c;度量每个基金在每年的度中心度、介数中心度和特征向量中心度&#xff0c;并对相关数据做出简要说明。 【代码】 import networkx as nx import pandas as pd import n…

SpringBoot整合Mybatis实现从数据库中读取blob属性的图片在html页面中无法显示并且出现乱码实体类,如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

大屏数字字体+渐变色

vue数据大屏使用数字字体_vue数字字体-CSDN博客 用css实现文字字体颜色渐变的三种方法_css 字体颜色渐变-CSDN博客

华为OD技术面试-有序数组第K最小值

背景 2024-03-15华为od 二面&#xff0c;记录结题过程 有序矩阵中第 K 小的元素 - 力扣&#xff08;LeetCode&#xff09; https://leetcode.cn/problems/kth-smallest-element-in-a-sorted-matrix/submissions/512483717/ 题目 给你一个 n x n 矩阵 matrix &#xff0c;其…

HackMyVM-Gift

目录 信息收集 arp nmap WEB dirsearch hydra ssh连接 get root 信息收集 arp ┌─[rootparrot]─[~] └──╼ #arp-scan -l Interface: enp0s3, type: EN10MB, MAC: 08:00:27:16:3d:f8, IPv4: 192.168.9.102 Starting arp-scan 1.10.0 with 256 hosts (https://git…

SpringBoot框架——7.整合MybatisPlus

这篇主要介绍Springboot整合MybatisPlus&#xff0c;另外介绍一个插件JBLSpringbootAppGen,以及一个经常用于测试的基于内存的h2数据库。 Mybatisplus是mybatis的增强工具&#xff0c;和tk-mybatis相似&#xff0c;但功能更强大&#xff0c;可避免重复CRUD语句&#xff0c;先来…

#无FIFO驱动OV7670基于cubemx(草稿)

1.前言 之前在淘宝买了一个不带FIFO的OV7670&#xff0c;由于比赛和其他事一直搁置&#xff0c;现在有时间于是想玩一玩。我发现网上这个的教程多为标准库&#xff0c;有些甚至利用了DCMI&#xff08;数字摄像头接口&#xff0c;目前已知F4系列是有这个外设的&#xff09;。标…