【算法】dfs

news2024/11/26 22:30:28



快乐的流畅:个人主页


个人专栏:《算法神殿》《数据结构世界》《进击的C++》

远方有一堆篝火,在为久候之人燃烧!

文章目录

  • 引言
  • 一、全排列
    • 1. 决策树
    • 2. 设计代码
      • 1. 全局变量
      • 2. dfs函数
      • 3. 细节问题
  • 二、子集
    • 解法一
    • 1. 决策树
    • 2. 设计代码
      • 1. 全局变量
      • 2. dfs函数
      • 3. 细节问题
    • 解法二
    • 1. 决策树
    • 2. 设计代码
      • 1. 全局变量
      • 2. dfs函数
      • 3. 细节问题
  • 三、子集的异或总和之和
  • 四、全排列 ||
  • 五、电话号码的字母组合
  • 六、括号生成
  • 七、组合
  • 八、目标和
  • 九、组合总和
  • 十、字母大小写全排列
  • 十一、优美的排列
  • 总结

引言

在实际的dfs问题中,大多时候并不会直接告诉你,而是需要自己发现可以使用dfs来解决。而是否能用dfs解决的关键,就是画出决策树!同时,不同的决策树代表不同的解决方式,对于同一问题,好的决策树往往能节省时间,提高效率。

一、全排列

1. 决策树


绿色部分,就是剪枝,因为全排列不能重复枚举。

2. 设计代码

1. 全局变量

  • vector<vector< int >> ret:用来保存最终所有的结果
  • vector< int > path:用来保存单一路径的结果
  • bool check[6]:用来实现剪枝

2. dfs函数

  • 将数组中所有数枚举一遍,如果没有枚举过,则将其加入path

3. 细节问题

  • 回溯
    • 清除path中最后一个数
    • 更改check中的标记
  • 剪枝:根据check的标记,去除重复枚举的情况
  • 递归出口:当path路径长度等于枚举数组长度,则将其加入ret,返回
class Solution
{
    vector<vector<int>> ret;
    vector<int> path;
    bool check[6];//实现剪枝
public:
    void dfs(vector<int>& nums)
    {
        if(path.size() == nums.size())
        {
            ret.push_back(path);
            return;
        }

        for(int i=0; i<nums.size(); ++i)
        {
            if(!check[i])//剪枝
            {
                path.push_back(nums[i]);
                check[i] = true;
                dfs(nums);
                //回溯 - 恢复现场
                path.pop_back();
                check[i] = false;
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums)
    {
        dfs(nums);
        return ret;
    }
};

二、子集

解法一

1. 决策树

2. 设计代码

1. 全局变量

  • vector<vector< int >> ret:用来保存最终所有的结果
  • vector< int > path:用来保存单一路径的结果

2. dfs函数

  • 对于数组中的每一个数,都遵循选或不选两种方式
    • 不选:直接dfs下一层
    • 选:先将其加入path,再dfs下一层
  • dfs(nums, i):增加参数i作为当前数的下标

3. 细节问题

  • 回溯:删除path中最后一个数
  • 递归出口:当i枚举完所有数,来到数组末尾,则将path加入ret,返回
class Solution
{
    vector<vector<int>> ret;
    vector<int> path;
public:
    void dfs(vector<int>& nums, int i)
    {
        if(i == nums.size())
        {
            ret.push_back(path);
            return;
        }
        //不选
        dfs(nums, i+1);
        //选
        path.push_back(nums[i]);
        dfs(nums, i+1);
        path.pop_back();
    }

    vector<vector<int>> subsets(vector<int>& nums)
    {
        dfs(nums, 0);
        return ret;
    }
};

解法二

1. 决策树


由于决策树不同的选取,解法二要优于解法一

2. 设计代码

1. 全局变量

  • vector<vector< int >> ret:用来保存最终所有的结果
  • vector< int > path:用来保存单一路径的结果

2. dfs函数

  • 按照集合中元素的个数进行分类
  • 依据集合的互异性,每层dfs只能选取下标i后面的数
  • dfs(nums, i):增加参数i作为当前数的下标

3. 细节问题

  • 回溯:删除path中最后一个数
  • 递归出口:每一层path都是结果,都需要添加到ret
class Solution
{
    vector<vector<int>> ret;
    vector<int> path;
public:
    void dfs(vector<int>& nums, int i)
    {
        ret.push_back(path);

        for(int j=i; j<nums.size(); ++j)
        {
            path.push_back(nums[j]);
            dfs(nums, j+1);
            path.pop_back();
        }
    }

    vector<vector<int>> subsets(vector<int>& nums)
    {
        dfs(nums, 0);
        return ret;
    }
};

三、子集的异或总和之和


思路:子集的变式题(按照集合元素个数分类)

class Solution
{
    int ret = 0;
    int path = 0;
public:
    void dfs(vector<int>& nums, int pos)
    {
        ret += path;

        for(int i=pos; i<nums.size(); ++i)
        {
            path ^= nums[i];
            dfs(nums, i + 1);
            path ^= nums[i];
        }
    }

    int subsetXORSum(vector<int>& nums)
    {
        dfs(nums, 0);
        return ret;
    }
};

四、全排列 ||


思路:本题是全排列的进阶版,存在重复元素,所以剪枝是关键。

  • 前提:先对数组排序
  • 同一个元素只能使用一次(check)
  • 对于每一个节点,相同的元素只能选一次
class Solution
{
    vector<vector<int>> ret;
    vector<int> path;
    bool check[8];
public:
    void dfs(vector<int>& nums)
    {
        if(path.size() == nums.size())
        {
            ret.push_back(path);
            return;
        }

        for(int i=0; i<nums.size(); ++i)
        {
            if(!check[i] && (i == 0 || nums[i] != nums[i-1] || check[i-1]))
            {
                path.push_back(nums[i]);
                check[i] = true;
                dfs(nums);
                check[i] = false;
                path.pop_back();
            }
        }
    }

    vector<vector<int>> permuteUnique(vector<int>& nums)
    {
        sort(nums.begin(), nums.end());
        dfs(nums);
        return ret;
    }
};

五、电话号码的字母组合


细节:用哈希表存储数字与字符串的映射关系

class Solution
{
    vector<string> ret;
    string path;
    vector<string> hash = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
    void dfs(string& digits, int pos)
    {
        if(path.size() == digits.size())
        {
            ret.push_back(path);
            return;
        }

        int n = digits[pos] - '0';
        string s = hash[n];
        for(int i=0; i<s.size(); ++i)
        {
            path.push_back(s[i]);
            dfs(digits, pos + 1);
            path.pop_back();
        }
    }

    vector<string> letterCombinations(string& digits)
    {
        if(digits.size() == 0) return ret;
        dfs(digits, 0);
        return ret;
    }
};

六、括号生成



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

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

        if(right < left)
        {
            path.push_back(')');
            dfs(left, right + 1);
            path.pop_back();
        }
    }

    vector<string> generateParenthesis(int _n)
    {
        n = _n;
        dfs(0, 0);
        return ret;
    }
};

七、组合


class Solution
{
    vector<vector<int>> ret;
    vector<int> path;
    int n, k;
public:
    void dfs(int pos)
    {
        if(path.size() == k)
        {
            ret.push_back(path);
            return;
        }

        for(int i=pos; i<=n; ++i)
        {
            path.push_back(i);
            dfs(i + 1);
            path.pop_back();
        }
    }

    vector<vector<int>> combine(int _n, int _k)
    {
        n = _n, k = _k;
        dfs(1);
        return ret;
    }
};

八、目标和


class Solution
{
    int ret = 0;
    int t;
public:
    void dfs(vector<int>& nums, int pos, int path)
    {
        if(pos == nums.size())
        {
            if(path == t) ++ret;
            return;
        }

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

    int findTargetSumWays(vector<int>& nums, int target)
    {
        t = target;
        dfs(nums, 0, 0);
        return ret;
    }
};

九、组合总和


class Solution
{
    vector<vector<int>> ret;
    vector<int> path;
    int t;
public:
    void dfs(vector<int>& candidates, int pos, int sum)
    {
        if(sum >= t)
        {
            if(sum == t) ret.push_back(path);
            return;
        }

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

    vector<vector<int>> combinationSum(vector<int>& candidates, int target)
    {
        t = target;
        dfs(candidates, 0, 0);
        return ret;
    }
};

十、字母大小写全排列


class Solution
{
    vector<string> ret;
public:
    void dfs(string& s, int pos, string path)
    {
        if(path.size() == s.size())
        {
            ret.push_back(path);
            return;
        }

        if(isalpha(s[pos]))
        {
            dfs(s, pos + 1, path + (char)tolower(s[pos]));
            dfs(s, pos + 1, path + (char)toupper(s[pos]));
        }
        else dfs(s, pos + 1, path + s[pos]);
    }

    vector<string> letterCasePermutation(string& s)
    {
        dfs(s, 0, "");
        return ret;
    }
};

十一、优美的排列


class Solution
{
    int ret = 0;
    bool check[20];
    int n;
public:
    void dfs(int pos, int path)
    {
        if(path == n)
        {
            ++ret;
            return;
        }

        for(int i=1; i<=n; ++i)
        {
            if(!check[i] && (i % pos == 0 || pos % i == 0))
            {
                check[i] = true;
                dfs(pos + 1, path + 1);
                check[i] = false;
            }
        }
    }

    int countArrangement(int _n)
    {
        n = _n;
        dfs(1, 0);
        return ret;
    }
};

总结

  • 决策树
  • 设计代码
    • 全局变量
    • dfs函数
    • 细节问题
      • 回溯
      • 剪枝
      • 递归出口

真诚点赞,手有余香

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

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

相关文章

Spring Boot:让微服务开发像搭积木一样简单!

带你一探 Spring Boot 的自动配置和 Starter POMs 的神奇之处&#xff0c;展示如何通过几个简单的步骤就能让你的微服务应用在云端翱翔&#xff01; 文章目录 1. 引言1.1 简述Spring框架的起源与重要性1.2 阐述文章目的&#xff1a;深入解析Spring核心功能与应用实践2. 背景介绍…

C语言 | Leetcode C语言题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize) {int m matrixSize;if (m 0) {return 0;}int n matrixColSize[0];int left[m][n];memset(left, 0, sizeof(left));for (int i 0; i < m; i) {for (int j …

251 基于matlab的动态粒子群算法

基于matlab的动态粒子群算法。普通粒子群算法无法感知外界环境的变化&#xff0c;在外界环境发生改变时无法实时进行响应&#xff0c;因而缺乏动态环境寻优能力。在普通粒子群算法基本上通过增加敏感粒子得到一种动态粒子群算法&#xff0c;该算法通过实时计算敏感粒子的适应度…

Redis集群安装

将Redis安装包分别上传到3个文件夹&#xff0c;并解压缩 #编译并指定安装目录 cd /root/usr/local/redis-cluster/redis-7001/redis-6.2.6/ make make PREFIX/root/usr/local/redis-cluster/redis-7001 install # cd /root/usr/local/redis-cluster/redis-7002/redis-6.2.6/ m…

iZotope RX 11 for Mac 激活版:让您的音频焕发生机!

在追求音频完美的道路上&#xff0c;iZotope RX 11 for Mac是您的得力助手。它凭借先进的音频修复技术和丰富的音频增强工具&#xff0c;让您的音频作品焕发出前所未有的生机与活力。无论您是专业的音频工程师&#xff0c;还是业余的音乐爱好者&#xff0c;都能在这款工具中找到…

栈的讲解

栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底&#xff08;因为先进后出&#xff09;。栈中的数据元素遵守后进先出LIFO&#xff08;Last In Firs…

【Threejs进阶教程-算法篇】1.常用坐标系介绍与2d/3d随机点位算法

2d/3d随机算法 学习ThreeJS的捷径坐标系简介平面直角坐标系和极坐标系空间直角坐标系圆柱坐标系球坐标系球坐标系与直角坐标系的转换 基于坐标系系统的随机点位算法平面直角坐标系随机平面直角坐标系随机的变形 空间直角坐标系随机二维极坐标系随机圆柱坐标系随机基于Cylinderc…

Python 整数类型(int)详解:无限范围与多种进制

引言 在编程中&#xff0c;整数是最基本的数据类型之一。不同编程语言对整数的处理方式各不相同&#xff0c;这往往影响到程序的性能和开发者的选择。本文将深入探讨 Python 中的整数类型&#xff08;int&#xff09;&#xff0c;其独特的处理方式&#xff0c;以及它在日常编程…

Lambda表达式 怎么debug调试

前言 Lambda 表达式是 JDK8 的一个重要新特性&#xff0c;可以取代大部分的匿名内部类&#xff0c;写出更优雅的 Java 代码&#xff0c;尤其在集合的遍历和其他集合操作中&#xff0c;可以极大地优化代码结构。JDK 也提供了大量的内置函数式接口供我们使用&#xff0c;使得 La…

【Chrome实用命令笔记】

文章目录 Chrome实用命令笔记1、chrome基本介绍2. 打开开发者工具&#xff08;DevTools&#xff09;方法一&#xff1a;快捷键方法二&#xff1a;右键菜单方法三&#xff1a;浏览器设置 2. 开发者工具面板Elements面板Console面板Sources面板Network面板Performance面板Memory面…

C++ 数据结构算法 学习笔记(25) - 图及其企业级应用

C 数据结构算法 学习笔记(25) - 图及其企业级应用 图的故事导入 故事情节 Jack 自从买车后&#xff0c;交通出行方便了&#xff0c;心自然就野了&#xff01;身边的各种朋友自然就多了起来&#xff01; 有一天晚上&#xff0c;一个年轻漂亮的女同事生日&#xff0c;Jack 受邀…

python 自定义包的实现

1. 代码目录 创建自定义包的时候&#xff0c;原理是当 python 检测到一个目录下存在 __init__.py 文件时&#xff0c;python 就会把它当成一个模块(module)。 下面这个例子是网上整理的代码&#xff0c;但是有些小改动&#xff0c;可以直接拿来就用。 看代码结构&#xff1a;…

springboot+vue+mybatis社交网络平台+PPT+论文+讲解+售后

近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;社交网络平台利用计算机网络实现信息化管理&#xff0c;使整个社交网络管理的发展和服务水平有显著提升。 本文拟采用Eclipse开发工具…

免费证件照一键换底色

最近星期天在家搞了一个小工具&#xff0c;在这里分享下! 废话不多说看看效果&#xff1a; 效果还不错&#xff0c;需要的可以联系我!!!!!!!!! 别的网上可都是一次五块钱这种。太贵了。。&#xff01;&#xff01;

Python - 深度学习系列33 - ollama_langchain_ppt生成

说明 只是为了速记一下这个实践过程。整体上说&#xff0c;这个结果并不是那么好用&#xff0c;但有一些可以借鉴的地方。 先看结果&#xff1a; 生成的PPT 说的直白点&#xff0c;就是用大模型生成了一堆没太有意义的文字&#xff0c;然后做成ppt。所以实用是不成的&#…

Golang | Leetcode Golang题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; func maximalRectangle(matrix [][]byte) (ans int) {if len(matrix) 0 {return}m, n : len(matrix), len(matrix[0])left : make([][]int, m)for i, row : range matrix {left[i] make([]int, n)for j, v : range row {if v 0 {continu…

【Spring Boot】Spring Boot 中的 Starter

Spring Boot 中的 Starter 1.常用 Starter2.为什么要用 Starter3.Starter 有哪些要素 我们都知道&#xff0c;Spring 的功能非常强大&#xff0c;但也有些弊端。比如&#xff1a;我们需要手动去配置大量的参数&#xff0c;没有默认值&#xff0c;需要我们管理大量的 jar 包和它…

SAP-CentralFinance - 学习心得2

过账总账中的交易 业务示例 创建大量日记账分录是会计日常工作的一部分。在SAP,会计可以使用不同的输入屏幕。使用所有方法,总账科目过账会自动列在损益表报表中(如果财务报表版本中包含科目)。查询已过账科目时还可显示对应的过账。 贵公司计划通过企业基金增加资本。在…

kafka安装配置及集成springboot

1. 安装 单机安装kafka Kafka对于zookeeper是强依赖&#xff0c;保存kafka相关的节点数据&#xff0c;所以安装Kafka之前必须先安装zookeeper dockerhub网址: https://hub.docker.com Docker安装zookeeper 下载镜像&#xff1a; docker pull zookeeper:3.4.14创建容器 doc…

《C++学习笔记---初阶篇6》---string类 上

目录 1. 为什么要学习string类 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明 2.2.1. string类对象的常见构造 2.2.2. string类对象的容量操作 2.2.3.再次探讨reserve与resize 2.2.4.string类对象的访问及遍历操作 2.2.5…