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

回溯算法详解:从原理到实战(C++代码实现)


前言

回溯算法是基于**深度优先搜索(DFS)**的经典算法思想,核心是“尝试-回退”,通过遍历解空间找到所有符合条件的解。它广泛应用于排列、组合、子集等问题,本文将结合LeetCode经典例题,用C++实现回溯算法,讲解核心思路与实战技巧。

一、回溯算法通用框架(C++)

回溯的核心是递归函数中完成选择-递归-撤销选择的循环,C++通用框架模板如下:

cpp

#include <vector>
using namespace std;

vector<vector<int>> result; // 存储最终结果

void backtrack(/* 路径,选择列表,其他参数 */) {
if (/* 结束条件 */) {
result.push_back(/* 当前路径 */);
return;
}
for (/* 遍历选择列表 */) {
// 做选择
// 递归
backtrack(/* 新的路径和选择列表 */);
// 撤销选择
}
}


二、经典例题C++实现

2.1 组合问题(LeetCode 77. 组合)

题目:给定 n 和 k ,返回 [1,n] 中所有 k 个数的组合。
思路:用 start 控制选择起点避免重复,路径长度等于 k 时保存结果,同时剪枝优化。

cpp

#include <vector>
using namespace std;

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
void backtrack(int n, int k, int start) {
if (path.size() == k) {
res.push_back(path);
return;
}
// 剪枝:剩余数字不足时直接退出
for (int i = start; i <= n - (k - path.size()) + 1; ++i) {
path.push_back(i); // 做选择
backtrack(n, k, i + 1); // 递归
path.pop_back(); // 撤销选择
}
}
public:
vector<vector<int>> combine(int n, int k) {
backtrack(n, k, 1);
return res;
}
};


2.2 全排列问题(LeetCode 46. 全排列)

题目:给定无重复数字的数组 nums ,返回所有全排列。
思路:用 used 数组标记已选元素,路径长度等于数组长度时保存结果。

cpp

#include <vector>
using namespace std;

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
void backtrack(vector<int>& nums, vector<bool>& used) {
if (path.size() == nums.size()) {
res.push_back(path);
return;
}
for (int i = 0; i < nums.size(); ++i) {
if (used[i]) continue; // 跳过已选元素
used[i] = true; // 做选择
path.push_back(nums[i]);
backtrack(nums, used); // 递归
path.pop_back(); // 撤销选择
used[i] = false;
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(), false);
backtrack(nums, used);
return res;
}
};


2.3 子集问题(LeetCode 78. 子集)

题目:给定数组 nums ,返回所有可能的子集(幂集)。
思路:遍历过程中每一步的路径都是一个子集,直接保存,用 start 控制选择起点避免重复。

cpp

#include <vector>
using namespace std;

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
void backtrack(vector<int>& nums, int start) {
res.push_back(path); // 每一步都保存子集
for (int i = start; i < nums.size(); ++i) {
path.push_back(nums[i]); // 做选择
backtrack(nums, i + 1); // 递归
path.pop_back(); // 撤销选择
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
backtrack(nums, 0);
return res;
}
};


三、回溯算法优化技巧

1. 剪枝:提前判断当前路径是否无法得到有效解,直接跳过分支(如组合问题中 i <= n - (k - path.size()) + 1 )。
2. 状态标记:用 bool 数组/哈希表标记已使用元素,避免重复选择(如全排列的 used 数组)。
3. 排序去重:若数组含重复元素(如LeetCode 47. 全排列 II),先排序再跳过重复元素,避免生成重复解。

四、应用场景

回溯算法适用于需要穷举所有可能解的场景:

- 排列/组合/子集类问题;
- N皇后、数独等棋盘问题;
- 单词搜索、迷宫路径等搜索问题;
- 分割回文串、复原IP地址等分割问题。

http://www.cnnetsun.cn/news/91808.html

相关文章:

  • 执行 install.sh 报错 `env: ‘bash\r‘: No such file or directory` 怎么解决?
  • Part 10|我给这套系统划的第一个边界
  • agent-zh.md
  • 为什么过滤 rtmpt 而不是 rtmp?
  • Navicat x 达梦技术指引 | 启用和配置AI助手
  • Transformer的注意力权重的理解
  • 解构 Codigger:从内核到无限生态的“进化阶梯”
  • 基于Python的高考志愿报名推荐系统源码设计与文档
  • 飞桨PaddlePaddle入门与核心实践
  • 使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第四十讲)
  • 热销榜单:2025年高口碑数字人推荐,解决你的选择难题!
  • 应“双碳”考核!安科瑞通信机房能耗监测方案,让PUE管控精准落地
  • 1天净流入10亿!A500ETF南方凭什么成为布局中国核心资产的优选?
  • Android 基础入门教程之RelativeLayout(相对布局)
  • 基于微信小程序的跑腿系统的设计与实现毕业设计项目源码
  • 基于SpringBoot的社区老年人健康知识阅读分享管理系统毕业设计项目源码
  • MySQL迁移达梦数据库,Quartz报错“无效的表或视图名”
  • Dify入门:搭建一个文件翻译智能体
  • 基于SpringBoot的金丰旺零售商经营平台系统毕业设计项目源码
  • Git:分布式版本控制的哲学、理论与创新
  • 农业产量预测的终极方案:R语言中XGBoost+随机森林+ARIMA融合技巧
  • 为什么90%的团队都选错了Dify排序算法?真相在这里!
  • 揭秘云原生Agent网络难题:如何高效配置Docker容器通信
  • 基于Python的电商用户购买行为数据分析系统设计与实现(源代码+文档+PPT+调试+讲解)
  • 为什么你的Dify模型加载总失败?这3个坑90%的人都踩过
  • ClaudeCode 实战指南(五):SubAgent 深度解析与专家团队构建
  • 【干货收藏】从零开始构建知识图谱:9大核心技术详解!
  • 智能算法与边缘计算融合:驱动下一代实时决策系统的技术范式革新
  • 为什么顶尖团队都在用Dify 1.7.0做音频转换?真相令人震惊
  • 【Dify 1.7.0音频转文字黑科技】:3大核心升级揭秘,效率提升90%的秘诀