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

代码随想录25 回溯算法

目录

回溯算法:

(1)回溯算法可以解决以下的问题:

(2)一般模板:

77.组合

216.组合总和lll

17.电话号码的字母组合

106.从中序与后序遍历序列构造二叉树

(1) 前序 + 中序 或 后序 + 中序

(2) 前序 + 后序


回溯算法:

回溯和递归是相辅相成的,回溯常常是写在递归函数里的

递归其实是一种暴力搜索

(1)回溯算法可以解决以下的问题:

1.组合

2.切割

3.子集

4.排列

5.棋盘

(2)一般模板:

void backtracking(参数){
if(终止条件){
收集结果;
return;
}
for(集合元素){
处理结点;
递归函数;
回溯操作;
}
return
}

77.组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4], 
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> result; // 存储最终结果
        vector<int> path;           // 当前路径
        backtracking(n, k, 1, path, result); // 从 1 开始遍历
        return result;
    }

private:
    void backtracking(int n, int k, int startIndex, vector<int>& path, vector<vector<int>>& result) {
        // 判断终止条件
        if (path.size() == k) {
            result.push_back(path); // 保存当前路径到结果集
            return;
        }

        for (int i = startIndex; i <= n; i++) { // 遍历从 startIndex 到 n
            path.push_back(i);                 // 选择当前数字
            backtracking(n, k, i + 1, path, result); // 递归到下一层
            path.pop_back();                   // 回溯,撤销选择
        }
    }
};
优化,剪枝后

剪枝就是将后面无法实现的情况直接不用考虑了

一般是在i的循环条件中改变最大的取值条件

 for (int i = startIndex; i <= n-(k-path.size())+1; i++)

216.组合总和lll

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次 

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
class Solution {
public:
    vector<vector<int>> combinationSum3(int k, int n) {
        vector<vector<int>> result; // 存储最终结果
        vector<int> path;           // 当前路径
        backtracking(k, n, 1, 0, path, result); // 从 1 开始遍历
        return result;
    }

private:
    void backtracking(int k, int n, int startIndex, int sum, vector<int>& path, vector<vector<int>>& result) {
        // 判断终止条件
        if (path.size() == k) {
            if (sum == n) { // 如果路径长度为 k 且当前和等于目标值
                result.push_back(path);
            }
            return;
        }

        // 剪枝优化:如果剩余的数字不够填满路径,直接返回
        for (int i = startIndex; i <= 9 ; i++) { 
            path.push_back(i);       // 选择当前数字
            backtracking(k, n, i + 1, sum + i, path, result); // 递归到下一层,在这里进行sum与startindex的操作
            path.pop_back();         // 回溯,撤销选择
        }
    }
};

剪枝优化后:

   for (int i = startIndex; i <= 9 - (k - path.size()) + 1 ; i++) 

17.电话号码的字母组合

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

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

示例 1:

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

代码如下:

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {}; // 输入为空,直接返回空结果
        
        // 数字到字母的映射表
        vector<string> letterMap = {
            "",    // 0
            "",    // 1
            "abc", // 2
            "def", // 3
            "ghi", // 4
            "jkl", // 5
            "mno", // 6
            "pqrs",// 7
            "tuv", // 8
            "wxyz" // 9
        };

        vector<string> result; // 存储结果
        string path;           // 当前路径
        backtracking(digits, 0, path, result, letterMap);
        return result;
    }

private:
    void backtracking(string digits, int index, string& path, vector<string>& result, const vector<string>& letterMap) {
        // 终止条件
        if (index == digits.size()) {
            result.push_back(path); // 保存当前路径到结果集
            return;
        }

        int digit = digits[index] - '0'; // 将字符转换为整数
        const string& letter = letterMap[digit]; // 获取对应的字母字符串

        // 遍历字母
        for (char ch : letter) {
            path.push_back(ch); // 选择当前字母
            backtracking(digits, index + 1, path, result, letterMap); // 递归下一层
            path.pop_back(); // 回溯,撤销选择
        }
    }
};

106.从中序与后序遍历序列构造二叉树

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

注:仅知道前序、中序或后序中的任何一个遍历结果,无法唯一确定一棵二叉树的结构。

(1) 前序 + 中序 或 后序 + 中序

这些组合可以唯一确定树的结构,因为:

  • 中序遍历给出了左右子树的分界点。
  • 前序或后序遍历给出了根节点和访问顺序。
(2) 前序 + 后序

单独的 前序 + 后序 也无法唯一确定树的结构,因为左右子树的分界点未知。

思路:

1.后序数组为0,空结点

2.后序数组最后一个元素为根节点元素

3.寻找中序数组位置作切割点

4.切中序数组

5.切后序数组

6.递归处理

class Solution {
private:
    TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
        if (postorder.size() == 0) return NULL;

        // 后序遍历数组最后一个元素,就是当前的中间节点
        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

        // 叶子节点
        if (postorder.size() == 1) return root;

        // 找到中序遍历的切割点
        int delimiterIndex;
        for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }

        // 切割中序数组
        // 左闭右开区间:[0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        // [delimiterIndex + 1, end)
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );

        // postorder 舍弃末尾元素
        postorder.resize(postorder.size() - 1);

        // 切割后序数组
        // 依然左闭右开,注意这里使用了左中序数组大小作为切割点
        // [0, leftInorder.size)
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        // [leftInorder.size(), end)
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());

        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);

        return root;
    }
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0 || postorder.size() == 0) return NULL;
        return traversal(inorder, postorder);
    }
};


http://www.kler.cn/a/507204.html

相关文章:

  • springcloud中的Feign调用
  • 2025第3周 | json-server的基本使用
  • 关于jwt和security
  • vue3+vite+ts+router4+Pinia+Axios+sass 从0到1搭建
  • 读书笔记~管理修炼-风险性决策:学会缩小风险阈值
  • 4.Proto 3 语法详解
  • 78_Redis网络模型
  • K8S--边车容器
  • 如何Python机器学习、深度学习技术提升气象、海洋、水文?
  • 2025第3周 | json-server的基本使用
  • Linux下使用MySql数据库
  • 采用海豚调度器+Doris开发数仓保姆级教程(满满是踩坑干货细节,持续更新)
  • 浏览器中的Markdown编辑器
  • 【2024年华为OD机试】(B卷,100分)- 相对开音节 (Java JS PythonC/C++)
  • java常用开发工具类
  • uniapp 自定义日历组件 源码
  • Spring Boot中的自动配置原理是什么
  • 2025智能网联汽车数据分类分级白皮书
  • redis(2:数据结构)
  • 【云岚到家】-day03-门户缓存方案选择
  • 策略模式详解与应用
  • npm : 无法加载文件 D:\phpdev\nodejs\npm.ps1
  • 【数模学习笔记】插值算法和拟合算法
  • springBoot项目使用Elasticsearch教程
  • docker hello world
  • leetcode 115. 不同的子序列