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

穷举 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;
    }
};

结尾

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

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

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

相关推荐
core51215 小时前
PageRank 算法:互联网的“人气投票”
算法·pagerank
小白菜又菜15 小时前
Leetcode 1523. Count Odd Numbers in an Interval Range
算法·leetcode
你们补药再卷啦15 小时前
人工智能算法概览
人工智能·算法
cnxy18815 小时前
围棋对弈Python程序开发完整指南:步骤3 - 气(Liberties)的计算算法设计
python·算法·深度优先
AndrewHZ15 小时前
【图像处理基石】什么是光栅化?
图像处理·人工智能·算法·计算机视觉·3d·图形渲染·光栅化
小白菜又菜16 小时前
Leetcode 944. Delete Columns to Make Sorted
算法·leetcode
我找到地球的支点啦16 小时前
Matlab系列(006) 一利用matlab保存txt文件和读取txt文件
开发语言·算法·matlab
Dev7z16 小时前
基于Matlab实现GRACE卫星重力数据的全球水储量变化估算与分析
人工智能·算法·matlab
爱喝热水的呀哈喽17 小时前
11题目汇总
算法
三斗米17 小时前
Transformer入门:一文读懂《Attention Is All You Need》
算法·架构