dfs刷题排列问题 + 子集问题 + 组和问题总结

news2025/3/22 11:08:16

文章目录

  • 一、排列问题
  • 全排列II
    • 题解
    • 代码
  • 优美的排列
    • 题解
    • 代码
  • 二、子集问题
  • 字母大小写全排列
    • 题解
    • 代码
  • 找出所有子集的异或总和再求和
    • 题解
    • 代码
  • 三、组合问题
  • 电话号码的字母组合
    • 题解
    • 代码
  • 括号生成
    • 题解
    • 代码
  • 组合
    • 题解
    • 代码
  • 目标和
    • 题解
    • 代码
  • 组合总和
    • 题解
    • 代码
  • 总结

一、排列问题

全排列II

题目链接
在这里插入图片描述

题解

1. 这题和全排列那题框架是一样的,就是剪枝操作不一样
2. 同一节点出现相同元素肯定会重复,所以把同一节点的相同元素剪掉
3. 同一个数只能出现一次,用check数组剪枝
分为两种情况进行剪枝:
1、只关心不合法的分支:
不合法的进行跳过(剪枝)
check[i] == true || ( i != 0 &&nums[i] == nums[i-1] && check[i-1] == false)
这个点是已经使用过的,或者是这个点和前一个点是相同的并且前一个点没有使用过,i != 0保证不越界
2、只关心合法的分支:
合法的分支才进行dfs
check[i] == false && (i == 0 || nums[i] != nums[i-1] || check[i] == true)
这个点没有被使用过并且该点为第一个点肯定可以进行dfs,或者是该点和前一个点不相同也可以dfs,或者是该点和前一个点相同,但是前一个点上一层已经使用过了,这个点这层可以继续使用,因为它们是用的不同位置

在这里插入图片描述

代码

class Solution 
{
public:
    vector<vector<int>> ret;
    vector<int> path;
    bool check[9];
    vector<vector<int>> permuteUnique(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());
        dfs(nums);
        return ret;
    }

    void dfs(vector<int> nums)
    {
        if(path.size() == nums.size())
        {
            ret.push_back(path);
            return;
        }

        // 如何把重复的数字剪掉
        for(int i = 0;i < nums.size();i++)
        {
            // 合法的剪枝,不合法就不进行dfs
            // if((check[i] == false)&&
            // (i == 0||nums[i] != nums[i-1]||check[i-1] == true))
            // {
            //     check[i] = true;
            //     path.push_back(nums[i]);
            //     dfs(nums);
            //     // 恢复现场
            //     check[i] = false;
            //     path.pop_back();
            // }

            // 考虑不合法的剪枝,跳过不合法的剪枝
            if((check[i] == true)||
            (i != 0&&nums[i] == nums[i-1]&&check[i-1] == false))
            continue;
        
                check[i] = true;
                path.push_back(nums[i]);
                dfs(nums);
                // 恢复现场
                check[i] = false;
                path.pop_back();
        }
    }
};

优美的排列

题目链接
在这里插入图片描述

题解

1. 画出决策树
2. 全局变量的设计:ret用来记录优美排列的个数,check数组检查是否可以剪枝,n设计成全局变量就不需要进行传参了
3. 剪枝:第一种剪枝不能出现重复的数,第二种剪枝不满足整除条件的
4. 回溯:如图我们每个位置都要进行判断,每个位置都会走一遍,递归完后进行恢复现场,把最后一位pop_back
5. 递归出口:当path路径的长度等于n时为递归出口
6. for循环的i = 1开始是因为要遍历所有的路径,dfs中pos+1是因为此位置遍历完会来到下一个位置进行遍历,画出决策树就很清晰了

在这里插入图片描述

代码

class Solution
{
public:
    int n;
    int ret;
    bool check[16];
    vector<int> path;
    int countArrangement(int _n) 
    {
        n = _n;
        dfs(1);
        return ret;
    }

    void dfs(int pos)
    {
        if(path.size() == n)
        {
            ret++;
            return;
        }

        for(int i = 1;i <= n;i++)
        {
            if(pos % i == 0 || i % pos == 0)
            {
                if(check[i] == false)
                {
                    check[i] = true;
                    path.push_back(i);
                    dfs(pos+1);
                    // 恢复现场
                    path.pop_back();
                    check[i] = false;
                }
            } 
        }
    }
};

二、子集问题

字母大小写全排列

题目链接
在这里插入图片描述

题解

1. 画出决策树
2. 全局变量:ret记录最终的结果,path记录每次的路径
3. 剪枝:没有剪枝
4. 回溯:到达叶子节点的时候记录完进行回溯,pop_back最后一个位置的数来到上一层
5. 递归出口:pos位置为n时,是最后一个数据的下一个位置,为递归出口
6. 这题和选和不选基本上是一样的,子集问题,pos这个位置变或者不变然后来到下一个位置,所以dfs(pos+1),变的情况为小写字母时转为大写字母,大写转为小写,不变就直接push

在这里插入图片描述

代码

class Solution 
{
public:
    vector<string> ret;
    string path;
    vector<string> letterCasePermutation(string s) 
    {
        dfs(s,0);
        return ret;
    }

    void dfs(string s,int pos)
    {
        // 为什么不能写pos == s.size()
        if(pos == s.size())
        {
            ret.push_back(path);
            return;
        }
        
        char ch = s[pos];
        // 不变
        path.push_back(ch);
        dfs(s,pos+1);
        path.pop_back();

        // 变
        if(ch < '0' || ch > '9')
        {
            ch = change(ch);
            path.push_back(ch);
            dfs(s,pos+1);
            path.pop_back();
        }
    }
    char change(char ch)
    {
        if(ch >= 'a' && ch <= 'z') ch -= 32;
        else ch += 32;
        return ch;
    }
};

找出所有子集的异或总和再求和

题目链接
在这里插入图片描述

题解

1. 画出决策树
2. 全局变量:用sum记录最终的结果,用path记录一个集合的异或和
3. 剪枝:没有剪枝
4. 回溯:每次异或当前元素就抵消掉这个元素了,然后回到上一层
5. 递归出口:没有递归出口,每次把path加到sum中即可
6. for循环中每次从pos位置开始向后枚举,避免重复,dfs(i+1),i+1就是数组中下一个位置的数,相当于剪枝了

在这里插入图片描述

代码

class Solution 
{
public:
    long long sum = 0;// 记录全部路径的异或和
    long long path = 0;// 记录一条路径的异或和
    int subsetXORSum(vector<int>& nums) 
    {
        dfs(nums,0);
        return sum;
    }

    void dfs(vector<int> nums,int pos)
    {
        sum += path;
        for(int i = pos;i < nums.size();i++)
        {
            path ^= nums[i];
            dfs(nums,i+1);
            path ^= nums[i];
            // 不能使用pos代替i,如果进行回溯回来,
            // 进行循环,path始终异或nums[pos]
        }
    }
};

三、组合问题

电话号码的字母组合

题目链接
在这里插入图片描述

题解

1. 画出决策树
2. 全局变量:ret记录最终的结果,path记录每次的路径,哈希表记录下标的映射关系
3. 剪枝:没有剪枝
4. 回溯:到达叶子节点时,pop_back最后一个元素,恢复现场
5. 递归出口:path的长度和给定的数组的长度相同
6. for循环把所有的数都枚举出来了,所以i下标从0开始,dfs(pos+1),每个位置枚举完跳到下一个位置继续枚举,这题主要是建立一个hash表记录每次的电话号码的数字对应的映射字符串

在这里插入图片描述

代码

class Solution 
{
public:
    vector<string> ret;
    string path;
    string hash[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    vector<string> letterCombinations(string digits) 
    {
        if(digits.size() == 0) return ret;
        dfs(digits,0);   
        return ret;
    }

    void dfs(string digits,int pos)
    {
        if(pos == digits.size())
        {
            ret.push_back(path);
            return;
        }

        for(int i = 0;i < hash[digits[pos] - '0'].size();i++)
        {
            path.push_back(hash[digits[pos] - '0'][i]);
            dfs(digits,pos+1);
            // 恢复现场
            path.pop_back();
        }
    }
};

括号生成

题目链接
在这里插入图片描述

题解

1. 画出决策树
2. 全局变量:path记录每次的路径,ret记录最终的结果,left记录左括号的数量,right记录右括号的数量
3. 剪枝:第一种右括号的数量大于等于左括号的数量,第二中左括号的数量大于等于n的数量,开始的时候只能给左扩号,右括号剪枝,左右括号相等时,不能加入右括号,左括号大于n不符合结果,等于n时,再往下加就大于n,需要剪枝
4. 回溯:如果是左括号回溯,pop_back最后一个元素,left–,如果是右括号回溯,pop_back最后一个元素,right–
5. 递归出口:右括号的数量等于n时,为左右括号相等的最终结果

在这里插入图片描述

代码

class Solution 
{
public:
    vector<string> ret;
    string path;
    int left,right;
    vector<string> generateParenthesis(int n) 
    {
        dfs(n);
        return ret;
    }
    
    void dfs(int n)
    {
        if(right == n)
        {
            ret.push_back(path);
            return ;
        }

        if(left < n)
        {
            path.push_back('(');
            left++;
            dfs(n);
            path.pop_back();
            left--;
        }
        if(left > right)
        {
            path.push_back(')');
            right++;
            dfs(n);
            path.pop_back();
            right--;
        }

    }
};

组合

题目链接
在这里插入图片描述

题解

1. 画出决策树
2. 全局变量:path记录每次的路径,ret记录最终的结果,k变为全局变量,不需要多传一个参数
3. 剪枝:不能重复出现相同的数剪枝,长度不够k也剪枝,这题可以从pos位置开始向后枚举,dfs(i+1),i+1表示每次枚举当前数的下一个位置的数,相当于进行了剪枝
4. 回溯:到达叶子节点后,每次pop_back最后一个数
5. 递归出口:path的长度等于k的大小

在这里插入图片描述

代码

class Solution 
{
public:
    vector<vector<int>> ret;
    vector<int> path;
    int k;
    vector<vector<int>> combine(int n, int _k) 
    {
        k = _k;
        dfs(n,1);
        return ret;
    }

    void dfs(int n,int pos)
    {
        if(path.size() == k)
        {
            ret.push_back(path);
            return ;
        }

        for(int i = pos;i <= n;i++)
        {
            path.push_back(i);
            dfs(n,i+1);
            path.pop_back();// 恢复现场
        }
    }
};

目标和

题目链接
在这里插入图片描述

题解

1. 画出决策树
2. 全局变量:ret记录最终的结果,target变为全局变量就少传一个参数
3. 剪枝:没有剪枝
4. 回溯:函数自动回溯,将path作为参数,记录每条路径的大小,回到上一层,path变为了没有加减之前的path
5. 递归出口:最终的path等于target的大小,ret++
6. 这题就是每个数两种情况,要么加,要么减

在这里插入图片描述

代码

class Solution 
{
public:
    int ret,target;
    int findTargetSumWays(vector<int>& nums, int _target) 
    {
        target = _target;
        dfs(nums,0,0);
        return ret;
    }

    void dfs(vector<int>& nums,int pos,int path)
    {
        if(pos == nums.size())
        {
            if(target == path)
            ret++;
            return;
        }

        // 加法,放在参数中函数帮我们自动恢复现场了
        dfs(nums,pos+1,path + nums[pos]);

        // 减法
        dfs(nums,pos+1, path - nums[pos]);
    }
};

组合总和

题目链接
在这里插入图片描述

题解

解法一:每个pos位置填
1. 画出决策树
2. 全局变量:ret记录最终的结果,path记录每条路径,target变为全局变量就不需要传参target了
3. 剪枝:如果这条路径的和大于target剪枝,如果选择的路径重复了剪枝,这种可以使用i = pos位置开始向后枚举,dfs(i),i表示每次从当前这个数向后枚举,不需要重复枚举这个数前面的数,就避免了重复的路径,达到了剪枝的效果
4. 回溯:sum在函数的参数中自动进行了回溯,pop_back最后一个元素,回到上一层
5. 递归出口:目标值等于记录的路径总和就找到一条路径,加入ret中,并返回给上一层

在这里插入图片描述

代码

class Solution 
{
public:
    vector<vector<int>> ret;
    vector<int> path;
    int target;
    vector<vector<int>> combinationSum(vector<int>& candidates, int _target) 
    {
        target = _target;
        dfs(candidates,0,0);
        return ret;
    }

    void dfs(vector<int> candidates,int pos,int sum)
    {
        if(sum == target)
        {
            ret.push_back(path);
            return;
        }
        // 枚举完了所有的位置也不是target,这个总和比较小
        if(pos == candidates.size()) return;

        for(int i = pos;i < candidates.size();i++)
        {
            if(sum > target) continue;
            
            path.push_back(candidates[i]);
            dfs(candidates,i,sum + candidates[i]);
            path.pop_back();
        }
    }
};

解法二:枚举每个数的数量
1. 和解法一不一样的地方就是dfs函数的实现不一样

在这里插入图片描述

class Solution 
{
public:
    vector<vector<int>> ret;
    vector<int> path;
    int target;
    vector<vector<int>> combinationSum(vector<int>& candidates, int _target) 
    {
        target = _target;
        dfs(candidates,0,0);
        return ret;
    }

    void dfs(vector<int> candidates,int pos,int sum)
    {
        if(sum == target)
        {
            ret.push_back(path);
            return;
        }
        // 枚举完了所有的位置也不是target,这个总和比较小
        if(pos == candidates.size() || sum > target) return;
         
        // 枚举个数
        for(int k = 0;sum + k * candidates[pos] <= target;k++)
        {
            if(k) path.push_back(candidates[pos]);
            dfs(candidates,pos+1,sum + k*candidates[pos]);
        }
        
        // 整块恢复现场
        for(int k = 1;sum + k * candidates[pos] <= target;k++)
        {
            path.pop_back();
        }
    }
};

总结

1. 最重要的就是画出决策树
2. 全局变量:一般是path记录路径,ret记录各个路径的结果
3. 剪枝:看题目分析和看决策树
4. 回溯:一般是pop_back最后一个元素
5. 递归出口:看题目条件,或者是叶子节点

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2319523.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

996引擎-接口测试:消息Tips

996引擎-接口测试:消息Tips 发送视野内广播消息 sendrefluamsg发送聊天框消息 sendmsg发送地图消息 sendmapmsg打印消息到控制台 release_print发送自定义颜色的文字信息 guildnoticemsg测试NPC参考资料发送视野内广播消息 sendrefluamsg function npc_test_onclick1(player)-…

JavaWeb之WebSocket

目录 一、 websocket 概念二、WebSocket原理三、WebSocket特点四、WebSocket应用场景五、Websocket基本使用1、创建Websocket对象2、Websocket事件3、Websocket方法4、前端服务程序 六、聊天室案例1、Tomcat版本&#xff1a;8.0.442、Maven 依赖&#xff1a;3、前端代码4、后端…

算法2--两数相加

题目描述 解题思路 题目说的很详细了&#xff0c;也就是把每个数倒序写成链表进行输入&#xff0c;然后让你计算两个倒序数组的和&#xff0c;要保证跟预期的结果一样。 首先应该考虑的是两个数组的长度问题&#xff0c;对于链表的每一位进行加法运算&#xff0c;如果两个列表…

Springboot的jak安装与配置教程

目录 Windows系统 macOS系统 Linux系统 Windows系统 下载JDK&#xff1a; 访问Oracle官网或其他JDK提供商网站&#xff0c;下载适合Windows系统的JDK版本。网站地址&#xff1a;Oracle 甲骨文中国 | 云应用和云平台点击进入下滑&#xff0c;点击进入下载根据自己的系统选择&…

Axure大屏可视化模板:赋能多领域,开启数据展示新篇章

在当今这个数据爆炸的时代&#xff0c;数据已经成为各行各业的核心资产。然而&#xff0c;如何高效、直观地展示数据&#xff0c;并将其转化为有价值的决策依据&#xff0c;成为了许多企业和组织面临的共同挑战。Axure大屏可视化模板&#xff0c;作为一款强大的数据展示工具&am…

区块链学习总结

Hardhat 是一个用于 Ethereum 智能合约开发 的开发环境&#xff0c;专为 Solidity 语言编写的智能合约提供工具支持。它能够帮助开发者 编译、部署、测试和调试 智能合约&#xff0c;并提供一个本地的以太坊测试网络。 Hardhat 的核心功能 本地开发网络&#xff08;Hardhat Ne…

识别并脱敏上传到deepseek/chatgpt的文本文件中的身份证/手机号

本文将介绍一种简单高效的方法解决用户在上传文件到DeepSeek、ChatGPT,文心一言,AI等大语言模型平台过程中的身份证号以及手机号等敏感数据识别和脱敏问题。 DeepSeek、ChatGPT,Qwen,Claude等AI平台工具快速的被接受和使用,用户每天上传的文本数据中潜藏着大量敏感信息,…

ruoyi-vue部署4

1.jdk-linux安装 2.tomcat-linux安装 3.ruoy后台部署 4.nginx-linux安装5.ruoyi前端部署​​​​​​​

【秣厉科技】LabVIEW工具包——OpenCV 教程(12):机器学习

文章目录 前言机器学习例1&#xff1a;支持向量机&#xff08;SVM&#xff09;做平面向量二分类例2&#xff1a; K邻近算法&#xff08;KNearest&#xff09;实现分类 总结 前言 需要下载安装OpenCV工具包的朋友&#xff0c;请前往 此处 &#xff1b;系统要求&#xff1a;Wind…

UI数据处理新隐私保护:确保用户新信息安全

hello宝子们...我们是艾斯视觉擅长ui设计和前端数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 在这个数字时代&#xff0c;我们的个人信息似乎无处不在。从社交媒体上的点滴分享&#xff0c;到在线…

【Javascrip】Javascript练习01 REST API using Express.js.

针对该问题的项目路径 要求部分 what you need to doReview the tasks provided in the section below.Obtain the boilerplate code.Use your local development environment to implement a solution.Upload your solution for marking via Gradescope. There is no attempt…

小样本学习综述

小样本学习综述 &#x1f4d5;[1]潘雪玲,李国和,郑艺峰. 面向深度网络的小样本学习综述 [J]. 计算机应用研究, 2023, 40 (10): 2881-28882895. DOI:10.19734/j.issn.1001-3695.2023.02.0074. 主要是该论文的一些摘要。 小样本学习旨在利用较少目标数据训练模型快速学习的。 …

挂谷问题与挂谷猜想:从平面转针到高维拓扑

挂谷问题与挂谷猜想&#xff1a;从平面转针到高维拓扑 目录 挂谷问题的起源数学定义与基本性质研究进展挂谷集合与挂谷猜想王虹与Joshua Zahl的突破意义与影响 挂谷问题的起源 1917年&#xff0c;日本数学家挂谷宗一(かけや そういち Soichi Kakeya&#xff0c;1886-1947)提…

火语言RPA--表格数据导出

表格数据导出 &#x1f6a9;【组件功能】&#xff1a;导出表格内数据到指定的文件 配置预览 配置说明 导出格式 Excel&#xff1a;导出Excel文档格式&#xff0c;CSV:导出CSV数据格式。 导出文件夹 支持T或# 导出文件需要保存的文件夹路径。 导出文件名支持T或# 导出文…

数学建模:MATLAB卷积神经网络

一、简述 卷积神经网络是一种处理具有网格结构数据的深度学习模型&#xff0c;由输入层、卷积层、池化层、全连接层、输出层组成。 输出层&#xff1a;将图像转换为其对应的由像素值构成的二维矩阵&#xff0c;并存储二维矩阵 卷积层&#xff1a;提取图像的底层特征&#xf…

xLua_003 Lua访问C#

1、new C# 对象&#xff08;创建游戏物体&#xff09; LuaCallCSharp.cs using UnityEngine; using XLua;public class LuaCallCSharp : MonoBehaviour {public LuaEnv env null;void Start(){LuaEnv env new LuaEnv();env.DoString("requireLuaCallCSharp");}pr…

mysql 磐维(opengauss)tidb误删数据之高级恢复

Mysql参考&#xff1a; Mysql 8.0 XtraBackupMysqlbinlog 完全恢复 - 墨天轮 Mysql 8.0 XtraBackupMysqlbinlog 完全恢复[TOC]# 一、安装mysql 8.0.19## 1.1https://www.modb.pro/db/509223MySQL 的全量备份、增量备份与 Binlog 时间点恢复_mysqlbinlog自动备份吗-CSDN博客文章…

字符指针的三道例题+算法改进

目录 一.杨氏矩阵 1.初级 2.想把下标带回来 二.字符串左旋 算法改进 三.判断是否为字符串旋转结果 算法改进 四. 3个字符函数 1.strcat 2.strncat 3.strstr 一.杨氏矩阵 数字矩阵&#xff0c;每行从左到右递增&#xff0c;每列从上到下递增&#xff0c;编写程序在矩…

如何构建简单有效的AI Agents代理?

工程技术 在过去的一年里&#xff0c;我们与数十个跨行业的团队合作&#xff0c;构建基于大型语言模型&#xff08;LLM&#xff09;的代理。我们发现&#xff0c;最成功的实现并不是使用复杂的框架或专门的库&#xff0c;而是采用简单、可组合的模式。 在本文中&#xff0c;我…

【虚幻引擎UE5】SpawnActor生成Character实例不执行AI Move To,未初始化AIController的原因和解决方法

虚幻引擎版本&#xff1a;5.5.4 问题描述 刚创建的Third Person项目里&#xff0c;定义一个BP_Enemy蓝图&#xff0c;拖拽到场景中产生的实例会追随玩家&#xff0c;但SpawnActor产生的实例会固定不动。BP_Enemy蓝图具体设计如下&#xff1a; BP_Enemy的Event Graph ​​ 又定义…