欢迎回到:遇见蓝桥遇见你,不负代码不负卿!

目录

一、引入:深度优先搜索(DFS)

二、经典例题

例题1.二叉搜索树的范围和

题目描述

题解

代码执行

例题2.岛屿数量

题目描述

题解

代码执行

例题3.背包问题

题目描述

题解

代码执行

三、思考题

四、蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!

你好,我是安然无虞。

面试利器&算法学习:牛客网

风趣幽默的学习人工智能:人工智能学习

文章前言:

提到深度优先搜索(DFS),我们很容易就会想到广度优先搜索(BFS),它们俩合在一起称为一个搜索专题,今天笔者先把DFS讲清楚,BFS的内容留在下一章详细讲解。

OK,废话不多说,走着...先送你一朵小红花...

一、引入:深度优先搜索(DFS)

这块内容很重要哦,为了方便大家理解,先举一个(来自胡凡、曾磊老师编写的《算法笔记》一书)的栗子。

举个栗子:

设想我们现在以第一视角身处一个巨大的迷宫当中,没有上帝视角,没有通信设施,更没有热血动漫里的奇迹,有的只是四周长得一样的墙壁。于是我们只能自己想办法走出去。如果迷失了内心,随便乱走,那么很可能会被四周完全相同的景色绕晕在其中,这时只能放弃所谓的侥幸,而去采取下面这种看上去很盲目但实际上会很有效的方法。

以当前所在位置为起点,沿着一条路向前走,当碰到岔道口时,选择其中一条岔道前进。如果岔路中存在新的岔道口,那么仍然按照上面的方法枚举新岔道口的每一条岔路。这样,只要迷宫存在出口,那么这个方法一定能找到它。

可能有铁汁会问,如果在第一个岔道口选择了没有出路的分支,而这个分支比较深,并且路上多次出现了新的岔道口,那么当发现这个分支是个死分支之后,如何退回到最初的这个岔道口?其实方法很简单,只要让自己的右手,始终贴着右边的墙壁一路往前走,那么自动会执行上面这个走法,并且最终一定能找到出口。

你看下图:

由图可知,从起点开始前进,当碰到岔道口时,总是选择其中一条岔路前进(注意哦,这里总是选择最右手边的岔路),在岔路上如果又遇到新的岔道口,仍然选择新岔道口的其中一条岔路前进,直到碰到死胡同才回退到最近的岔道口选择另一条岔路。也就是说,当碰到岔道口时,总是以“深度”作为前进的关键词,不碰到死胡同就不回头,因此把这种搜索的方式称为深度优先搜索。

从这个迷宫的例子还可以注意到,深度优先搜索会走遍所有路径,并且每次走到死胡同就代表一条完整路径的形成。这就是说,深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法。

总结何为深度优先搜索:

从根节点开始,尽可能深的搜索每一个分支,把一个分支的结果搜索完,再去看另一个分支。形象来说:“一条路走到底,不撞南墙不回头”。

【敲黑板】:上面的内容可能有点绕,所以建议铁汁们最好认真看个三遍这样子,好好去理解,毕竟这一章属于拔高部分,可能稍微难理解些,但做题目后会发现其实不难,不信的话,往后看。不对不对,先把上面弄得差不多了再朝后看...

问:用什么方法能更好的实现深度优先搜索呢?

其实要想既容易理解又容易实现深度优先搜索非递归莫属

这里就不用俺再强调递归的重要了吧,后面很多地方都要用到递归,尤其是数据结构中的二叉树。

这里再温习一下上篇的递归部分:

蓝桥杯算法竞赛系列第二章——深入理解重难点之递归(上)_安然无虞的博客-CSDN博客

回顾一下对于斐波那契数列的定义:F(0) = 1, F(1) = 1, F(n) = F(n - 1) + F(n - 2) (n >= 2)。可以从这个定义挖掘到,每当将F(n)分为两部分F(n - 1)与F(n - 2)时,就可以把F(n)看做迷宫的岔道口,由它可以到达两个新的关键节点F(n - 1) 和 F(n - 2)。而之后计算F(n - 1)时,又可以把F(n - 1)当作在岔道口F(n)之下的岔道口。

既然有岔道口,那么一定有死胡同。很容易想象,当访问到F(0)和F(1)时,就无法向下递归下去,因此F(0)、F(1)在这里就是死胡同。这样说来,递归中的递归式就是岔道口,而递归边界就是死胡同,这样一来,就可以把如何用递归实现深度优先搜索的过程理解的很清楚。

因此,使用递归可以很好地实现深度优先搜索。这个说法并不是说深度优先搜索就是递归,只能说深度优先搜索是递归的一种实现方式,因为使用非递归也能实现DFS的思想,但是一般情况下会比递归麻烦。不过,使用递归时,系统会调用一个叫系统栈的东西来存放递归中每一层的状态,因此使用递归来实现DFS的本质其实还是栈。

DFS主要应用场景:

  • 二叉树搜索
  • 图搜索

好咯,前面简单引入了一下DFS,下面准备要上硬菜咯...

二、经典例题

例题1.二叉搜索树的范围和

题目描述

给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和。

注意哦,根节点的值大于左子树,并且根节点的值小于右子树

示例1:

输入:root = [10,5,15,3,7,null,18], low = 7, high = 15
输出:32

示例2:

输入:root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10
输出:23

题解

按深度优先搜索的顺序计算范围和。记当前子树根节点为root,分以下四种情况讨论:

1.root 节点为空,返回0;

2.root 节点的值大于high,由于二叉搜索树右子树上所有节点的值均大于根节点的值,即均大于 high,故无需考虑右子树,返回左子树的范围和;

3.root 节点的值小于low,由于二叉搜索树左子树上所有节点的值均小于根节点的值,即均小于 low,故无需考虑左子树,返回右子树的范围和;

4.root 节点的值在[low,high] 范围内,此时应返回root 节点的值、左子树的范围和、右子树的范围和这三者之和。

代码执行

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/int rangeSumBST(struct TreeNode* root, int low, int high){//方法1:DFS//找递归边界(死胡同)if(root == NULL){return 0;}//岔道口//如果根节点的值大于high,那么右子树的值一定不满足,此时只需要判断左子树即可if(root->val > high){return rangeSumBST(root->left, low, high);}//如果根节点的值小于low,那么左子树的值一定不满足,此时只需要判断右子树即可if(root->val < low){return rangeSumBST(root->right, low, high);}//否则根节点的值在low和high之间,则根节点、左子树、右子树这三者都要判断return root->val + rangeSumBST(root->left, low, high) + rangeSumBST(root->right, low, high);
}

是不是感觉和递归别无二致,毕竟就是利用递归这个解题思想的嘛,那就来看看用纯递归思想该怎么解决本题。

int rangeSumBST(struct TreeNode* root, int low, int high){//方法2:递归法//找边界if(root == NULL){return 0;}//左子树int leftSum = rangeSumBST(root->left, low, high);//右子树int rightSum = rangeSumBST(root->right, low, high);int result = leftSum + rightSum;//判断根节点是否满足if(root->val >= low && root->val <= high){result += root->val;}return result;
}

OK,其实感觉还好吧,也不是很难,而且做多了会发现,什么递归呀,分治呀都很有趣,很妙。

例题2.岛屿数量

题目描述

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例1:

输入:grid = [["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]
]
输出:1

示例2:

输入:grid = [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]
]
输出:3

题解

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0,也就是说将1周围出现(上下左右)的1全部同化成0

代码执行

int numIslands(char** grid, int gridSize, int* gridColSize){//考虑特殊情况if(grid == NULL || gridSize == 0){return 0;}int row = gridSize;//行数int col = *gridColSize;//列数int count = 0;//用于计数for(int i = 0; i < row; i++){for(int j = 0; j < col; j++){if(grid[i][j] == '1'){count++;}dfs(grid, i, j, row, col);}}return count;
}void dfs(char** grid, int x, int y, int row, int col){//找递归边界(死胡同)if(x < 0 || x >= row || y < 0 || y >= col || grid[x][y] == '0'){return;}grid[x][y] = '0';//岔道口dfs(grid, x - 1, y, row, col);dfs(grid, x + 1, y, row, col);dfs(grid, x, y - 1, row, col);dfs(grid, x, y + 1, row, col);
}

例题3.背包问题

题目描述

有n件物品,每件物品的重量为w[i], 价值为c[i]。现在需要选出若干件物品放入一个容量为v的背包中,使得在选入背包的物品重量和不超过容量v的前提下,让背包中物品的价格之和最大,求最大价值。

示例:

输入:物品重量:3 5 1 2 2  物品价值:4 5 2 1 3输出:10

题解

在这个问题中,需要从n件物品中选择若干件物品放入背包,使它们的价值之和最大。这样的话,对每件物品都有选和不选两种选择,而这就是所谓的“岔道口”。而当完成对于n件物品的选择之后就到达了“死胡同”,不过本题需要额外再多考虑一种情况,题目要求选择的物品总和不能超过v,因此一旦选择的物品重量总和超过v,也就会到达“死胡同”,所以这道题目需要多考虑一下,铁汁们要小心哦,具体的看代码实现。

代码执行

//题目:有n件物品,每件物品的重量为w[i],价值为c[i](由于每件都不同,所以采用i表示变化的意思)。现在需要选出若干件物品放入一个
//容量为V的背包中,使得在选入背包的物品重量和不超过V的前提下,让背包中物品的价值之
//和最大,求最大价值(1 <= n <= 20)#include<stdio.h>int maxValue = 0;//最大价值
//下面四组数据可以自己设定,由于想简化题目所以在这里直接以全局变量的形式给出
int n = 5;//物品数目
int v = 8;//背包容量
int w[] = { 3,5,1,2,2 };//w[i]为每件物品重量
int c[] = { 4,5,2,1,3 };//c[i]为每件物品价值//index为当前处理物品的下标(物品下标范围是0~n - 1)
//sumW和sumC分别为当前总重量和当前总价值
void DFS(int index, int sumW, int sumC)
{//已经完成了对n件物品的选择(递归边界--死胡同)if (index == n){return;}//岔道口DFS(index + 1, sumW, sumC);//不选第index件物品//只有加入第index件物品后未超出容量v,才能继续执行(注意这个限制条件)if (sumW + w[index] <= v){//注意哦,如果加入第index件物品后总价值大于最大价值maxValue时记得要更新最大价值if (sumC + c[index] > maxValue){maxValue = sumC + c[index];//更新最大价值maxValue}DFS(index + 1, sumW + w[index], sumC + c[index]);//选择第index件物品}
}
int main()
{DFS(0, 0, 0);//初始时为第0件物品、当前总重量和总价值均为0printf("满足条件的最大价值为:%d\n", maxValue);return 0;
}

三、思考题

今天的思考题部分是要求铁汁再把之前的有关递归的内容再看一下,多找些经典的递归题目练练手。加油哦

蓝桥杯算法竞赛系列第二章——深入理解重难点之递归(上)_安然无虞的博客-CSDN博客

四、蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!

希望以上能帮助到铁汁们哦,也不枉俺挤时间去更新这块内容,如果感觉有所收获,可不可以给俺个三连加关注呢,让这篇文章可以被更多铁汁们看见,嘿嘿,拜托各位啦,笔者会再接再厉滴,886,咱们下次再见。

蓝桥杯算法竞赛系列第五章——拔高篇之深度优先搜索(DFS)相关推荐

  1. 蓝桥杯算法竞赛系列第0章——蓝桥必考点及标准模板库STL(上)(万字博文,建议抱走)

    欢迎来到:遇见蓝桥遇见你,不负代码不负卿! 目录 ​ 一.蓝桥必考点剖析 二.什么是STL 三.vector的常见用法详解 1.vector的定义 2.vector容器内元素的访问 (1).通过下标访 ...

  2. 蓝桥杯算法竞赛系列第二章——深入理解重难点之递归(上)

    铁汁们,递归(下)已经更新咯,欢迎铁汁们批评指正. 蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(下)_安然无虞的博客-CSDN博客 目录 一.递归是什么? 二.如何理解"递归" ...

  3. 蓝桥杯算法竞赛系列第一章——位运算的奇巧淫技及其实战

    遇见蓝桥遇见你,不负代码不负卿! 第二章"递归"已将更新咯,欢迎铁汁们点评!蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(上)_安然无虞的博客-CSDN博客 目录 一.位运算符 ...

  4. 蓝桥杯算法竞赛系列第八章——提高篇之广度优先搜索(BFS)

    欢迎回到:遇见蓝桥遇见你,不负代码不负卿! 目录 一.广度优先搜索算法(BFS) 典例一:二叉搜索树的范围和 方法一:DFS解法 方法二:BFS解法 典例二:二叉树的层序遍历 典例三:二叉树的层序遍历 ...

  5. 【蓝桥杯】2015决赛A组 5 穿越雷区(深度优先搜索dfs、广度优先搜索bfs)

    历届试题 穿越雷区 问题描述 X星的坦克战车很奇怪,它必须交替地穿越正能量辐射区和负能量辐射区才能保持正常运转,否则将报废. 某坦克需要从A区到B区去(A,B区本身是安全区,没有正能量或负能量特征), ...

  6. [蓝桥杯][算法提高VIP]夺宝奇兵-递推+记忆化搜索

    题目描述 在一座山上,有很多很多珠宝,它们散落在山底通往山顶的每条道路上,不同道路上的珠宝的数目也各不相同.下图为一张藏宝地图: 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 " ...

  7. 【算法很美】深入递归 (下)深度优先搜索DFS问题

    深搜.回溯.剪枝 深度优先搜索DFS 2.1 无死角搜索I 数独游戏 部分和 水洼数目 2.2 回溯和剪枝 n皇后问题 素数环 困难的串 小结 一些使用 2.1 无死角搜索I 数独游戏 你一定听说过& ...

  8. 蓝桥杯算法竞赛备考算法归纳总结

    蓝桥备考基础算法归纳 暴力.贪心 递归.递推 二分.快排 深度优先搜索.广度优先搜索.回溯 字符串处理 双指针 动态规划 各类背包问题 数论 全排列.组合 素数.最大公约数.最小公倍数.欧几里得gcd ...

  9. [蓝桥杯]算法提高 第二点五个不高兴的小明(记忆化搜索||动态规划)

    问题描述 有一条长为n的走廊,小明站在走廊的一端,每次可以跳过不超过p格,每格都有一个权值wi. 小明要从一端跳到另一端,不能回跳,正好跳t次,请问他跳过的方格的权值和最大是多少? 输入格式 输入的第 ...

  10. ((蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第6天(动态规划 专题)】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有61天

最新文章

  1. 修改class文件_VM实战(六) - 通过案例深入学习class文件结构原理
  2. linux 占用缓存前10_Linux 中的零拷贝技术
  3. 高级 Angular 组件模式 (3a)
  4. python 有趣的变量_Python进阶之路 3.2有趣的赋值操作
  5. AUP2敏捷统一过程之一:序言及降低过程的总体拥有成本
  6. php event loop,理解javascript中的事件循环(Event Loop)
  7. 面向对象(匿名内部类重写多个方法调用)
  8. [Editor][001][vim]VIM的辅助工具们
  9. 2.4. myisamchk — MyISAM Table-Maintenance Utility
  10. LeetCode——二叉树的最近公共祖先
  11. nginx启动只有master没有worker_深入探索Nginx工作原理
  12. innodb_flush_log_at_trx_commit和sync_binlog参数详解
  13. plsql导出oracle数据库表结构及表数据
  14. 计算机考研作息时间表,2016考研人:牛人的考研作息时间表
  15. m132nw与m132snw差异_最新评测揭秘m132nw与m132snw差异哪个好?各个型号有什么区别?用户实话实说...
  16. 定制LK阶段开机LOGO
  17. 中国科学院深圳先进技术研究院合成所赵国屏研究员课题组2022年招聘启事
  18. Mac 终端 oh-my-zsh 配置,内含解决oh-my-zsh 下载不下来的方法
  19. sentos7查看网络配置_怎么查看centos7的网络配置
  20. apache 问题 You don't have permission to access /test.php on this server 解决方法

热门文章

  1. 35岁仍然落魄,有这3个苗头将大器晚成,你要刮目相看,主动结交
  2. 直播APP软件开发,直播系统开发的技术架构揭秘
  3. httpd安装、配置、编译三种访问模式控制https证书的安装访问实例及排错
  4. 楚门的世界/The Truman Show (1998)
  5. 微信公众号开发之微信测试账号申请
  6. Unity 球面行走
  7. 在CENTOS 7上安装SNIPE-IT进行资产管理
  8. HTML <meta> http-equiv 属性
  9. 港科资讯 | 北京海外高层次人才协会与香港科技大学创业中心合作谅解备忘录签约...
  10. 问request.getRequestDispatcher(“url“).forward(request.response)是什么意思