【递归、搜索与回溯算法】穷举、暴搜、深搜、回溯、剪枝

穷举 vs 暴搜 vs 深搜 vs 回溯 vs 剪枝

  1. 穷举 :最基础的思想
    • 定义:从所有可能的解中,逐一检查每个候选解是否满足条件,直到找到答案(或确认无解)。
    • 特点:不依赖任何策略,纯粹 "地毯式" 覆盖所有可能性,是最朴素的搜索思想。
  2. 暴搜 :穷举的 "实现方式"
    • 定义:"暴力搜索" 的简称,是穷举思想的具体实现,通常指无优化地遍历所有可能解(本质上是 "未优化的穷举")。
    • 与穷举的关系:穷举是思想,暴搜是这种思想的直接落地方式,两者常被混用,但暴搜更强调 "不做任何优化,硬刚所有可能"。
  3. 深搜 :暴搜的 "遍历策略"
    • 定义:深度优先搜索,是暴搜的一种具体遍历方式 ------ 优先沿着一条路径 "走到底",直到无法继续再回溯,换另一条路径探索。
    • 与暴搜的关系:深搜是暴搜的 "优化形式" 之一,它通过明确的遍历顺序避免重复探索同一路径,比 "无规律的暴搜" 更有序。
  4. 回溯 :深搜的 "改进版"
    • 定义:在深搜的基础上,增加 "撤销选择" 的操作 ------ 当探索到某一步发现当前路径不可能通向解时,不仅退回上一步,还会 "清理当前步骤的影响",以便重新尝试其他路径。
    • 与深搜的关系:回溯是 "带状态重置的深搜",是深搜的精细化实现,解决了深搜中 "状态污染" 的问题。
  5. 剪枝 :回溯 / 深搜的 "优化手段"
    • 定义:在回溯或深搜过程中,通过提前判断 "当前路径不可能通向解",直接终止该路径的探索,避免无效分支消耗资源。
    • 与回溯 / 深搜的关系:剪枝是对回溯或深搜的 "效率优化",不改变其核心逻辑,只减少不必要的计算。

一、全排列

题目描述:

思路讲解:

本道题需要我们从给定一个不含重复数字的数组 nums ,返回其所有可能的全排列。通过递归尝试所有可能的元素排列,在每一步选择一个未使用的元素加入当前排列,完成选择后回溯并尝试下一个元素,直到生成所有完整排列。以下是具体的思路:

  • 核心逻辑
    • 全排列的本质是从数组中依次选择元素,每个元素在排列中只能出现一次。通过递归逐层确定排列的每个位置,使用 "选择 - 递归 - 回溯" 的流程遍历所有可能的组合
  • 全局变量
    • path:当前正在构建的排列
    • isuse:记录元素是否已被使用的布尔数组(避免重复选择)
    • ans:存储所有完整排列的结果集(引用传递)
  • 终止条件
    • 当 path 的长度等于 nums 的长度时,说明已生成一个完整排列,将其加入 ans 并返回
  • 递归逻辑
    • 遍历数组中的每个元素,若元素未被使用(isuse[i] == false):
      • 将元素加入 path,标记 isuse[i] = true
      • 继续递归构建下一个位置的元素
      • 递归返回后,将元素从 path 中移除,标记 isuse[i] = false,以便其他分支使用该元素

编写代码:

cpp 复制代码
class Solution {
    vector<vector<int>> ans;
    bool isuse[7];
    vector<int> path;
    int len;
public:
    void _permute(vector<int>& nums) 
    {
        if(path.size() == len)
        {
            ans.push_back(path);
            return;
        }

        for(int i = 0 ; i < len ; i++)
        {
            if(isuse[i] == false)
            {
                path.push_back(nums[i]);
                isuse[i] = true;
                _permute(nums);
                path.pop_back();
                isuse[i] = false;
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        len = nums.size();
        memset(isuse,false,sizeof(isuse));
        _permute(nums);

        return ans;
    }
};

二、子集

题目描述:

思路讲解:

本道题需要我们根据整数数组 nums ,返回该数组所有可能的子集。逐步选择元素,构建所有可能的子集,通过回溯法在每一步决定是否选择当前元素,最终生成所有不重复的子集。

  • 核心逻辑
    • 子集问题的本质是从数组中选择任意个元素(包括 0 个),形成不重复的集合。递归过程中,对于每个元素,有两种选择:加入当前子集或不加入当前子集,通过遍历所有选择组合生成完整解集。
  • 全局变量
    • path:当前正在构建的子集
    • ans:存储所有子集的结果集
  • 终止条件
    • 当遍历完数组所有元素(pos == nums.size())时,将当前子集 path 加入 ans 并返回。
  • 递归逻辑
    • 不选择当前元素:直接递归处理下一个元素,当前子集不变。
    • 选择当前元素:将 nums[pos] 加入 path,递归处理下一个元素,之后回溯(从 path 中移除该元素)。

编写代码:

cpp 复制代码
class Solution {
    vector<vector<int>> ans;
    vector<int> path;
    int len;
public:
    void _subsets(vector<int>& nums , int pos) {
        if(pos == len)
        {
            ans.push_back(path);
            return;
        }

        path.push_back(nums[pos]);
        _subsets(nums,pos+1);
        path.pop_back();

        _subsets(nums,pos+1);
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        len = nums.size();
        _subsets(nums,0);

        return ans;
    }
};

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。

希望大家以后也能和我一起进步!!🌹🌹

如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

相关推荐
菜就多练,以前是以前,现在是现在3 分钟前
Codeforces Round 1042 (Div. 3)
c++·算法
数据智能老司机2 小时前
图算法趣味学——图遍历
数据结构·算法·云计算
范特西_2 小时前
交错字符串-二维dp
算法·动态规划
菜鸟555552 小时前
河南萌新联赛2025第五场 - 信息工程大学
c++·算法·思维·河南萌新联赛
楽码3 小时前
在RestFul接口应用Hmac算法
后端·算法·restful
用户7143109838854 小时前
Claude Code VSCode集成开发指南:AI编程助手完整配置
算法
CoovallyAIHub4 小时前
SOD-YOLO:基于YOLO的无人机图像小目标检测增强方法
深度学习·算法·计算机视觉
许小燚5 小时前
LeetCode——456. 132 模式
算法·leetcode·职场和发展
sali-tec9 小时前
C# 基于halcon的视觉工作流-章29-边缘提取-亚像素
开发语言·图像处理·算法·计算机视觉·c#