【算法打卡day25(2026-03-17 周二)今日算法:「回溯算法」】1-力扣17-电话号码的字母组合 2-力扣39-组合总和 3-力扣40-组合总和II

- 第 191 篇 -
Date: 2026 - 03- 17| 周二
Author: 郑龙浩(仟墨)
今日算法or技巧:回溯算法

2026-03-17-算法刷题day25

今日算法:回溯算法

本来今天是想把「回溯算法」的所有题都做完的,但是我发现我低估了回溯算法题的难度了

如果纯套模板的可能还能写出来,但凡有变动或者某些特殊条件的,我就会写不出来,很难理解透彻

总体看下来,今天我可以说是什么也没学到:上午满课,然后下午开始搞回溯,真的是把我头整大了,太难理解了

我放弃搞「回溯算法」了,明天开始,做一些「蓝桥杯」真题吧,然后再刷一些基础算法题,不能因为扣这种难以理解的题太长时间,把那些基础的简单题给忘记了,这就得不偿失了,最终再落一个什么也没学到的结果,不值得,还有25天蓝桥杯,希望接下来的25天,我都能把握好每天的学习进度吧。

文章目录

1-力扣17-电话号码的字母组合

【题目】

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入: digits = "23"
输出: ["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入: digits = "2"
输出: ["a","b","c"]

提示:

  • 1 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

【思路】

【代码】

cpp 复制代码
/* 2026-03-17-算法打卡day25
 * 1-力扣17-电话号码的字母组合
 * Author:郑龙浩
 * Date:2026-03-16
 * 算法/技巧:回溯算法 & 递归
 * 用时:
 */

#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    vector<string> results;  // 存储所有字母组合结果的容器
    string path;             // 当前正在构建的字母组合路径
    
    // 数字到字母的映射表
    // 索引0-9对应数字0-9,其中数字2-9对应电话键盘上的字母
    string litters[10] = {
        "",       // 数字0 - 无对应字母
        "",       // 数字1 - 无对应字母
        "abc",    // 数字2 - 对应字母abc
        "def",    // 数字3 - 对应字母def
        "ghi",    // 数字4 - 对应字母ghi
        "jkl",    // 数字5 - 对应字母jkl
        "mno",    // 数字6 - 对应字母mno
        "pqrs",   // 数字7 - 对应字母pqrs(注意4个字母)
        "tuv",    // 数字8 - 对应字母tuv
        "wxyz"    // 数字9 - 对应字母wxyz(注意4个字母)
    };
    
    // 主函数:返回所有可能的字母组合
    vector<string> letterCombinations(string digits) {
        // 处理边界情况:输入为空字符串
        if (digits.size() == 0) return results;
        
        // 从第0个数字开始回溯
        backtracking(digits, 0);
        return results;
    }
    
    // 回溯函数
    // index: 当前处理到digits中的第几个数字(从0开始)
    // 也可以理解为递归树的当前深度
    void backtracking(string& digits, int index) {
        // 终止条件:已经处理完所有数字
        // 当index等于digits长度时,说明已经为每个数字都选了一个字母
        if (index == digits.size()) {
            results.push_back(path);  // 保存当前完整的字母组合
            return;                    // 结束当前递归分支
        }
        
        // 获取当前数字对应的字母串
        // digits[index]是字符,如'2',减去'0'得到整数2
        // 用这个整数作为索引访问litters数组
        string s = litters[digits[index] - '0'];
        
        // 遍历当前数字的所有可能字母
        for (char ch : s) {
            // 做出选择:将当前字母加入路径
            path.push_back(ch);
            
            // 递归:处理下一个数字
            // index+1表示进入下一层递归,处理digits中的下一个数字
            backtracking(digits, index + 1);
            
            // 回溯:撤销选择,回到当前状态
            // 为尝试当前数字的下一个字母做准备
            path.pop_back();
        }
    }
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); 
    cout.tie(0);
    return 0;
}

2-力扣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
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

【思路】

【代码】

cpp 复制代码
/* 2026-03-17-算法打卡day25
 * 2-力扣39-组合总和
 * Author:郑龙浩
 * Date:2026-03-16
 * 算法/技巧:回溯算法 & 递归
 * 用时:17 min
 * 说实话做的模模糊糊的,主要是套模板
 */

#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    vector <vector <int>> results;
    vector <int> path;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtracking(candidates, target, 0);
        return results;
    }
    int sum (vector <int>& nums) {
        int Sum = 0;
        for (int it : nums) Sum += it;
        return Sum;
    }

    void backtracking(vector <int>& candidates, int target, int startIndex) {
        // 如果sum超过了target继续执行下去,只会无限递归,所以为了避免,直接return
        if (sum(path) > target) return; // 刚开始没写这个

        if (sum(path) == target) {
            results.push_back(path);
            return;
        }
        for (int i = startIndex; i < candidates.size(); i++) {
            path.push_back(candidates[i]);
            backtracking(candidates, target, i); // 因为可以重复当前数字,所以下一次也从i开始
            path.pop_back();
        }
        return;
    }
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); 
    cout.tie(0);
    return 0;
}

3-力扣40-组合总和II

【题目】

给定一个候选人编号的集合 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]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

【思路】

【代码】

cpp 复制代码
/* 2026-03-17-算法打卡day25
 * 3-力扣40-组合总和II
 * Author:郑龙浩
 * Date:2026-03-16
 * 算法/技巧:回溯算法 & 递归
 * 用时:1 小时
 * 我依然没明白
 * 
 */

#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    vector <vector <int>> results;
    vector <int> path;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end()); // 如果要去重,首先要给数组进行排序
        vector <bool> used(candidates.size(), false); // 存储遇到过的元素
        backtracking(candidates, target, 0, used);
        return results;
    }
    int SUM(vector <int> nums) {
        int sum = 0;
        for (int it : nums) sum += it;
        return sum;
    }
    void backtracking(vector<int>& candidates, int target, int StartIndex, vector <bool> used) {
        int sum = SUM(path);
        if (sum > target) return;

        if (sum == target) {
            results.push_back(path);
            return;
        }
        /* 错误代码:我第一次这么写的
            错误原因:没有去重(因为每个数字在每个组合中只能使用一次)
            就是使用过的元素,不能在重复选取了
            "使用过"有两个维度:1 同一树枝上使用过 2 同一树层上使用过

            那么应该去重 同一树枝使用过,还是同一树层使用过?

            应该要去重同一树层的"使用过",同一树枝上都是组合中的元素,不需要去重

        for (int i = StartIndex; i < candidates.size(); i++) {
            path.push_back(candidates[i]);
            backtracking(candidates, target, i + 1);
            path.pop_back();
        }
        */

        for (int i = StartIndex; i < candidates.size(); i++) {
            // 对于排序后相同的数字,在同一层遍历中,我们只选第一个,跳过后面的相同数字,避免重复组合
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) continue;
            path.push_back(candidates[i]);
            used[i] = true;
            backtracking(candidates, target, i + 1, used);
            used[i] = false; // 回溯
            path.pop_back();
        }
    }
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); 
    cout.tie(0);
    return 0;
}
相关推荐
Storynone2 小时前
【Day26】LeetCode:452. 用最少数量的箭引爆气球,435. 无重叠区间,763. 划分字母区间
python·算法·leetcode
月明长歌2 小时前
【码道初阶-Hot100】LeetCode 3. 无重复字符的最长子串:从错误直觉到滑动窗口,彻底讲透为什么必须判断 `map.get(c) >= left`
java·算法·leetcode·哈希算法
junnhwan2 小时前
LeetCode Hot 100——贪心算法
java·算法·leetcode
魑魅魍魉都是鬼2 小时前
java 的排序算法
java·算法·排序算法
2401_853576502 小时前
并行算法在STL中的应用
开发语言·c++·算法
晓纪同学2 小时前
ROS2 -06-动作
java·数据库·python·算法·机器人·ros·ros2
无限进步_2 小时前
【C++】字符串中的字母反转算法详解
开发语言·c++·ide·git·算法·github·visual studio
qyzm2 小时前
Codeforces Round 927 (Div. 3)
数据结构·python·算法
2401_891482172 小时前
C++中的状态模式实战
开发语言·c++·算法