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

回溯算法中关于剪枝的一些应用

在这里插入图片描述
衔接上篇( ^ _ ^ )
剪枝优化是回溯算法中一种重要的优化手段,其核心思想是 提前终止无效的递归分支,避免无意义的搜索,从而大幅减少计算量。通过合理剪枝,可以将指数级的时间复杂度降低到更优的水平。


一、剪枝的核心思想

在递归遍历决策树时,提前判断当前路径是否可能得到有效解

  • 不可能得到解 → 直接终止当前分支的递归(“剪掉”这个分支)
  • 可能得到解 → 继续递归探索

二、剪枝的常见类型及示例

1. 可行性剪枝

在每一步判断当前路径是否满足问题的约束条件,若不满足则提前返回。
示例:组合总和问题

void backtrack(vector<int>& nums, int remain, int start, vector<int>& path, vector<vector<int>>& res) {
    if (remain < 0) return;  // ✂️剪枝:剩余值为负数时直接返回
    if (remain == 0) {
        res.push_back(path);
        return;
    }
    
    for (int i = start; i < nums.size(); ++i) {
        if (nums[i] > remain) break;  // ✂️剪枝:已排序,后续元素更大,无需尝试
        path.push_back(nums[i]);
        backtrack(nums, remain - nums[i], i, path, res);
        path.pop_back();
    }
}

关键点

  • 先对数组排序(sort),使后续元素递增
  • nums[i] > remain 时,后续元素必然更大,不可能满足条件 → 直接break

2. 重复性剪枝

避免生成重复的解(常见于元素有重复值的问题)。
示例:全排列II(含重复元素)

void backtrack(vector<int>& nums, vector<bool>& used, vector<int>& path, vector<vector<int>>& res) {
    if (path.size() == nums.size()) {
        res.push_back(path);
        return;
    }
    
    for (int i = 0; i < nums.size(); ++i) {
        // ✂️剪枝条件1:跳过已使用的元素
        if (used[i]) continue;  
        // ✂️剪枝条件2:跳过重复元素(需先排序)
        if (i > 0 && nums[i] == nums[i-1] && !used[i-1]) continue;
        
        used[i] = true;
        path.push_back(nums[i]);
        backtrack(nums, used, path, res);
        path.pop_back();
        used[i] = false;
    }
}

关键点

  • 先对数组排序(sort),使相同元素相邻
  • nums[i] == nums[i-1]!used[i-1] 时,说明前一个相同元素未被使用 → 当前分支会产生重复解 → 跳过

3. 对称性剪枝

利用问题本身的对称性,避免重复搜索对称解。
示例:生成回文数
在生成回文数时,只需构造前半部分,后半部分对称生成即可。


三、剪枝的实现技巧

技巧适用场景示例
排序 + 提前终止循环组合问题、子集问题if (nums[i] > remain) break
索引标记(start避免重复选择元素组合问题中的 for (i = start)
哈希表记录已访问状态棋盘类问题(如数独)记录某行/列/宫是否包含某数字
数学性质推导利用数学公式提前排除不可能的情况N皇后问题的斜线检查

四、剪枝的意义

  1. 时间复杂度优化:将指数级复杂度降低到更优水平(例如从 O(n!) 优化到 O(n×n!))
  2. 空间优化:减少递归深度和内存占用
  3. 实际效率提升:对于大规模输入,剪枝可能将不可行的问题变为可解

五、经典剪枝练习

  1. 组合总和II(不可重复选相同元素):if (i > start && nums[i] == nums[i-1]) continue
  2. 子集II(含重复元素):if (i > start && nums[i] == nums[i-1]) continue
  3. 数独求解:预先记录每行/列/宫已使用的数字,快速判断是否可填

六、总结

剪枝是回溯算法的灵魂,核心在于通过问题特性提前终止无效递归
关键步骤

  1. 明确问题的约束条件
  2. 分析决策树中哪些分支必然无效
  3. 设计剪枝条件,用代码实现提前终止
  4. 通过排序、哈希表等工具辅助剪枝判断

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

相关文章:

  • Baumer工业相机堡盟相机的相机传感器芯片清洁指南
  • [论文阅读] Knowledge Fusion of Large Language Models
  • windows 蓝牙驱动开发-传输总线驱动程序常见问题
  • 私有化部署DeepSeek并SpringBoot集成使用(附UI界面使用教程-支持语音、图片)
  • Maven 依赖管理全面解析
  • Linux 内核模块 | 加载 / 添加 / 删除 / 优先级
  • PHP PDO 教程
  • 【Elasticsearch】date range聚合
  • 安卓/ios脚本开发按键精灵经验小分享
  • .net一些知识点5
  • VMware下Linux和macOS安装VSCode一些总结
  • 2025 IT职业发展方向及推荐
  • 基于SpringBoot养老院平台系统功能实现六
  • 【SpringBoot篇】基于Redis分布式锁的 误删问题 和 原子性问题
  • log4j2日志配置文件
  • DeepSeek 引领的 AI 范式转变与存储架构的演进
  • pring MVC 中的 `@RequestParam` 注解
  • Vue2:通过inject在子组件中使用父组件通过mixin引入的公用方法
  • 常见数据库对象与视图VIEW
  • 力扣.623. 在二叉树中增加一行(链式结构的插入操作)
  • LeetCode--279. 完全平方数【动态规划】
  • 深度学习模型格式解析:PyTorch、AWQ 和 GPTQ
  • @RequestBody与@ResponseBody:Spring数据处理的“翻译官”
  • 基于PSO粒子群优化和Voronoi图的配电网电动汽车充电站最优选址matlab仿真
  • error: externally-managed-environment
  • 【网络安全学习笔记】传输层协议 UDP 与 TCP