回溯方法总结

简介: 回溯方法总结

回溯模板

关于构造回溯抽象树

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }
    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}
  • for循环中嵌套递归,这就是回溯的基本格式,最重要的就是将其抽象为一颗树,固定一个根节点,就拿leetcode46全排列来说
// 全排列核心代码
void backtracking(vector<int> nums, vector<bool> used){
    if (path.size() == nums.size()) {
        result.push_back(path);
        return;
    }
    for (int i = 0; i < nums.size(); i++) {
        if (used[i] == true) continue;
        used[i] = true;
        path.push_back(nums[i]);  // 加入path数组
        backtracking(nums, used);
        path.pop_back();
        used[i] = false;
    }
}
  • 假设输入样例是[1, 2, 3],那么根节点就是[1, 2, 3],也就是nums数组,回溯操作就是在其中取值加入另一设好的path集合,将其作为节点,令节点的内容就是path组合的内容,没有进入递归的最外层的for循环成为外层for循环,假设只看外层for循环不管递归部分则有下图

可以察觉到的是,只关注外层for循环的话就发现他遍历了树的宽度,我们可以猜测,递归则就是遍历数的深度,接下来我将对当最外层for循环i=0时的操作细画图

其余两个节点的详细图类似

这个图的对应代码为

void backtracking(vector<int> nums, vector<bool> used){
    if (path.size() == nums.size()) {
        result.push_back(path);
        return;
    }
    for (int i = 0; i < nums.size(); i++) {
        path.push_back(nums[i]);
        backtracking(nums, used);
        path.pop_back();
    }
}

这就是最原始的回溯,在for循环内部没有任何筛选条件,所有回溯的题目都是在此基础上加条件进行节点的删除,全排列这道题就是加了used数组,目的在于去除同一路径下的相同位置的节点,删除的节点如下图所示

那许多组合相关的题目中的startIndex参数是拿来干什么的呢,其实也是删除一些节点,他就是令每个父节点下遍历子节点时index索引不重启,如下图所示

可以看出从上随着深度的增加i的值只增不减,这就是startIndex的作用,由于大量节点被删除,因此画出整棵树

可以直观的感受到startIndex的作用,利用伪代码表示startIndex

void backtracking(vector<int> nums, int startIndex){
    // ... 终止条件
    for(int i = startIndex; i < nums.size(); i++){
        // ...
        backtracking(nums, startIndex + 1);
        // 对应回溯操作
    }
}

一些题目举例

各种去重条件

  • 全排列1
  • 去掉同一树枝下的相同结点
  • 全局的集合查找
  • 全局的bool数组标记
  • 全排列2
  • 去掉同一树枝下的相同结点
  • 只能用全局的bool数组标记
  • 去掉同一层下的相同结点
  • 局部的集合查找
  • 。。。以后再来加
相关文章
BUUCTF [UTCTF2020]docx 1
BUUCTF [UTCTF2020]docx 1
457 0
|
编解码 前端开发 UED
解密CSS单位:px、em、vh的区别与应用
解密CSS单位:px、em、vh的区别与应用
385 0
|
11月前
|
运维 Prometheus 监控
基于阿里云可观测产品构建企业级告警体系的通用路径与最佳实践
本文围绕企业级告警体系构建展开,探讨了监控与告警在系统稳定性中的重要作用。通过梳理监控对象、分析指标、采集数据及配置规则等环节,提出告警体系建设的通用流程,并针对多平台告警、误报、告警风暴等问题提供解决思路。结合阿里云可观测产品,分享了某电商企业的实践案例,展示了如何通过标签规范、日志标准和统一管理平台实现高效告警处置,为构建全面且实用的告警体系提供了参考指南。
1041 1
|
设计模式 JavaScript 开发工具
Vue开发中使用好钩子方法(hook method)可以使你的代码更加模块化和可维护
Vue开发中使用好钩子方法(hook method)可以使你的代码更加模块化和可维护
228 0
|
分布式计算 资源调度 Hadoop
Java大数据处理:Spark与Hadoop整合
Java大数据处理:Spark与Hadoop整合
|
NoSQL Java 调度
订单超时取消的11种方法(下)
大家好,我是三友~~ 延迟任务在我们日常生活中比较常见,比如订单支付超时取消订单功能,又比如自动确定收货的功能等等。 所以本篇文章就来从实现到原理来盘点延迟任务的11种实现方式,这些方式并没有绝对的好坏之分,只是适用场景的不大相同。
订单超时取消的11种方法(下)
|
消息中间件 运维 监控
Linux命令ipcs详解:IPC对象的全面洞察
`ipcs`命令详解:Linux下用于洞察IPC(消息队列、信号量、共享内存)对象的工具。它列出系统中的IPC资源,显示详细信息,如ID、所有者、权限等。参数如`-m`、`-q`、`-s`分别显示共享内存、消息队列和信号量信息。结合`-l`或`-c`可调整输出格式。定期检查IPC状态有助于系统管理和性能优化。需注意权限和谨慎操作。
|
测试技术 Linux 虚拟化
Docker与虚拟机的区别
概要 Docker是近年来新兴的虚拟化工具,它可以和虚拟机一样实现资源和系统环境的隔离。本文将主要根据IBM发表的研究报告,论述docker与传统虚拟化方式的不同之处,并比较物理机、docker容器、虚拟机三者的性能差异及差异产生的原理。
2901 1
|
网络协议 安全 Ubuntu
如何使用JuiceSSH实现手机端远程连接Linux服务器
如何使用JuiceSSH实现手机端远程连接Linux服务器
485 1
|
小程序 JavaScript 关系型数据库
实习生管理|基于SprinBoot+vue的微信小程序的实习生管理系统(源码+数据库+文档)
实习生管理|基于SprinBoot+vue的微信小程序的实习生管理系统(源码+数据库+文档)
268 0