【算法专题】二叉树中的深搜(DFS)

这篇具有很好参考价值的文章主要介绍了【算法专题】二叉树中的深搜(DFS)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

深搜

深度优先遍历(DFS,全称为 Depth First Traversal),是我们树或者图这样的数据结构中常用的⼀种遍历算法。这个算法会尽可能深的搜索树或者图的分支,直到一条路径上的所有节点都被遍历完毕,然后再回溯到上一层,继续找⼀条路遍历。

在二叉树中,常见的深度优先遍历为:前序遍历、中序遍历以及后序遍历。因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁。并且前中后序三种遍历的唯一区别就是访问根节点的时机不同,在做题的时候,选择一个适当的遍历顺序,对于算法的理解是非常有帮助的。

1. 计算布尔二叉树的值

题目链接 -> Leetcode -2331.计算布尔二叉树的值

Leetcode -2331.计算布尔二叉树的值

题目:给你一棵 完整二叉树 的根,这棵树有以下特征:

叶子节点 要么值为 0 要么值为 1 ,其中 0 表示 False ,1 表示 True 。
非叶子节点 要么值为 2 要么值为 3 ,其中 2 表示逻辑或 OR ,3 表示逻辑与 AND 。
计算 一个节点的值方式如下:

如果节点是个叶子节点,那么节点的 值 为它本身,即 True 或者 False 。
否则,计算 两个孩子的节点值,然后将该节点的运算符对两个孩子值进行 运算 。
返回根节点 root 的布尔运算值。
完整二叉树 是每个节点有 0 个或者 2 个孩子的二叉树。
叶子节点 是没有孩子的节点。

示例 1:
输入:root = [2, 1, 3, null, null, 0, 1]
输出:true
解释:上图展示了计算过程。
AND 与运算节点的值为 False AND True = False 。
OR 运算节点的值为 True OR False = True 。
根节点的值为 True ,所以我们返回 true 。

示例 2:
输入:root = [0]
输出:false
解释:根节点是叶子节点,且值为 false,所以我们返回 false 。

提示:

  • 树中节点数目在[1, 1000] 之间。
  • 0 <= Node.val <= 3
  • 每个节点的孩子数为 0 或 2 。
  • 叶子节点的值为 0 或 1 。
  • 非叶子节点的值为 2 或 3 。

思路:通过判断节点的值来做不同的操作,如果节点的值为1或0,说明是叶子节点,返回true/false;如果节点值是2,那么返回它的左子树 || 右子树;如果节点值是3,返回它的左子树 && 右子树。

代码如下:文章来源地址https://www.toymoban.com/news/detail-776598.html

		/**
		 * Definition for a binary tree node.
		 * struct TreeNode {
		 *     int val;
		 *     TreeNode *left;
		 *     TreeNode *right;
		 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
		 * };
		 */
		class Solution {
		public:
		    bool evaluateTree(TreeNode* root)
		    {
		        if (root->val == 1) return true;
		        if (root->val == 0) return false;
		
		        if (root->val == 2) return evaluateTree(root->left) || evaluateTree(root->right);
		        return evaluateTree(root->left) && evaluateTree(root->right);
		    }
		};

2. 求根节点到叶节点数字之和

题目链接 -> Leetcode -129.求根节点到叶节点数字之和

Leetcode -129.求根节点到叶节点数字之和

题目:给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。

叶节点 是指没有子节点的节点。

示例 1:
输入:root = [1, 2, 3]
输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25

示例 2:
输入:root = [4, 9, 0, 5, 1]
输出:1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495
从根到叶子节点路径 4->9->1 代表数字 491
从根到叶子节点路径 4->0 代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026

提示:

  • 树中节点的数目在范围[1, 1000] 内
  • 0 <= Node.val <= 9
  • 树的深度不超过 10

思路:
在前序遍历的过程中,我们可以往左右子树传递信息,并且在回溯时得到左右子树的返回值。递归函数可以帮我们完成两件事:

  1. 将父节点的数字与当前节点的信息整合到⼀起,计算出当前节点的数字,然后传递到下⼀层进行递归;
  2. 当遇到叶子节点的时候,就不再向下传递信息,而是将整合的结果向上一直回溯到根节点。

在递归结束时,根节点需要返回的值也就被更新为了整棵树的数字和。

代码如下:

		/**
		 * Definition for a binary tree node.
		 * struct TreeNode {
		 *     int val;
		 *     TreeNode *left;
		 *     TreeNode *right;
		 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
		 * };
		 */
		class Solution {
		public:
		    int ret = 0;
		
		    void dfs(TreeNode* root, int sum)
		    {
		        if(root->left == nullptr && root->right == nullptr) 
		        {
		            ret += sum;
		            return;
		        }
		
		        if(root->left) dfs(root->left, sum * 10 + root->left->val);
		        if(root->right) dfs(root->right, sum * 10 + root->right->val);
		    }
		
		    int sumNumbers(TreeNode* root) 
		    {
		        dfs(root, root->val);
		        return ret;
		    }
		};

3. 二叉树剪枝

题目链接 -> Leetcode -814.二叉树剪枝

Leetcode -814.二叉树剪枝

题目:给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1 。

返回移除了所有不包含 1 的子树的原二叉树。

节点 node 的子树为 node 本身加上所有 node 的后代。

示例 1:
输入:root = [1, null, 0, 0, 1]
输出:[1, null, 0, null, 1]
解释:
只有红色节点满足条件“所有不包含 1 的子树”。 右图为返回的答案。

示例 2:
输入:root = [1, 0, 1, 0, 0, 0, 1]
输出:[1, null, 1, null, 1]

示例 3:
输入:root = [1, 1, 0, 1, 1, 0, 1, 0]
输出:[1, 1, 0, 1, 1, null, 1]

提示:

  • 树中节点的数目在范围[1, 200] 内
  • Node.val 为 0 或 1

思路:
如果我们选择从上往下删除,我们需要收集左右子树的信息,这可能导致代码编写相对困难。然而,通过观察我们可以发现,如果我们先删除最底部的叶子节点,然后再处理删除后的节点,最终的结果并不会受到影响。因此,我们可以采用后序遍历的方式来解决这个问题。在后序遍历中,我们先处理左子树,然后处理右子树,最后再处理当前节点。在处理当前节点时,我们可以判断其是否为叶子节点且其值是否为 0,如果满足条件,我们可以删除当前节点。

  • 需要注意的是,在删除叶子节点时,其父节点很可能会成为新的叶子节点。因此,在处理完子节点后,我们仍然需要处理当前节点。这也是为什么选择后序遍历的原因(后序遍历首先遍历到的一定是叶子节点)。
  • 通过使用后序遍历,我们可以逐步删除叶子节点,并且保证删除后的节点仍然满足删除操作的要求。这样,我们可以较为方便地实现删除操作,而不会影响最终的结果。
  • 若在处理结束后所有叶子节点的值均为 1,则所有子树均包含 1,此时可以返回。

代码如下:

		/**
		 * Definition for a binary tree node.
		 * struct TreeNode {
		 *     int val;
		 *     TreeNode *left;
		 *     TreeNode *right;
		 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
		 * };
		 */
		class Solution 
		{
		public:
		    TreeNode* pruneTree(TreeNode* root) 
		    {
		        if(root == nullptr) return nullptr;
		        
		        root->left = pruneTree(root->left);
		        root->right = pruneTree(root->right);
		
		        if(root->val == 0 && root->left == nullptr && root->right == nullptr) 
		        {
		            delete root;
		            root = nullptr;
		        }
		
		        return root;
		    }
		};

4. 验证二叉搜索树

题目链接 -> Leetcode -98.验证二叉搜索树

Leetcode -98.验证二叉搜索树

题目:给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

示例 1:
输入:root = [2, 1, 3]
输出:true

示例 2:
输入:root = [5, 1, 4, null, null, 3, 6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:
树中节点数目范围在[1, 10^4] 内

  • -2^31 <= Node.val <= 2^31 - 1

思路:如果一棵树是二叉搜索树,那么它的中序遍历的结果一定是一个严格递增的序列。因此,我们可以初始化一个无穷小的全区变量,用来记录中序遍历过程中的前驱结点。那么就可以在中序遍历的过程中,先判断是否和前驱结点构成递增序列,然后修改前驱结点为当前结点,传入下一层的递归中。

		/**
		 * Definition for a binary tree node.
		 * struct TreeNode {
		 *     int val;
		 *     TreeNode *left;
		 *     TreeNode *right;
		 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
		 * };
		 */
		class Solution {
		    long prev = LONG_MIN;
		
		public:
		    bool isValidBST(TreeNode* root) 
		    {
		        if(root == nullptr) return true;
		
		        bool Left = isValidBST(root->left);
		
		        if(!Left) return false; // 剪枝
		
		        if(root->val <= prev) return false; //剪枝
		        prev = root->val;  
		
		        bool Right = isValidBST(root->right);
		
		        return Left && Right;
		    }
		};

5. 二叉搜索树中第K小的元素

题目链接 -> Leetcode -230.二叉搜索树中第K小的元素

Leetcode -230.二叉搜索树中第K小的元素

题目:给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

示例 1:
输入:root = [3, 1, 4, null, 2], k = 1
输出:1

示例 2:
输入:root = [5, 3, 6, 2, 4, null, null, 1], k = 3
输出:3

提示:
树中的节点数为 n 。

  • 1 <= k <= n <= 10^4
  • 0 <= Node.val <= 10^4

思路:我们可以根据中序遍历的过程,只需扫描前 k 个结点即可;因此,我们可以创建一个全局的计数器 count,将其初始化为 k,每遍历一个节点就将 count- -;直到某次递归的时候,count 的值等于 1,说明此时的结点就是我们要找的结果。

		/**
		 * Definition for a binary tree node.
		 * struct TreeNode {
		 *     int val;
		 *     TreeNode *left;
		 *     TreeNode *right;
		 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
		 * };
		 */
		class Solution 
		{
		    int ret, count;
		public:
		    void dfs(TreeNode* root)
		    {
		        if(root == nullptr || count == 0) return;
		        dfs(root->left);
		
		        count--;
		        if(count == 0) ret = root->val;
		
		        dfs(root->right);
		    }
		
		    int kthSmallest(TreeNode* root, int k) 
		    {
		        count = k, ret = 0;
		        dfs(root);
		        return ret;
		    }
		};

6. 二叉树的所有路径

题目链接 -> 添加链接描述

Leetcode -257.二叉树的所有路径

题目:给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例 1:
输入:root = [1, 2, 3, null, 5]
输出:[“1->2->5”, “1->3”]

示例 2:
输入:root = [1]
输出:[“1”]

提示:
树中节点的数目在范围[1, 100] 内

  • 100 <= Node.val <= 100

思路:路径以字符串形式存储,从根节点开始遍历,每次遍历时将当前节点的值加入到路径中,如果该节点为叶子节点,将路径存储到结果中。否则,将 “->” 加入到路径中并递归遍历该节点的左右子树。定义一个结果数组,进行递归。递归具体实现方法如下:

  1. 如果当前节点不为空,就将当前节点的值加入路径 str 中,否则直接返回;
  2. 判断当前节点是否为叶子节点,如果是,则将当前路径加入到所有路径的存储数组 ret 中;
  3. 否则,将当前节点值加上 “->” 作为路径的分隔符,继续递归遍历当前节点的左右子节点。
  4. 返回结果数组

注意:我们可以只使用一个字符串存储每个状态的字符串,在递归回溯的过程中,需要将路径中的当前节点移除,以回到上一个节点。

代码如下:

		/**
		 * Definition for a binary tree node.
		 * struct TreeNode {
		 *     int val;
		 *     TreeNode *left;
		 *     TreeNode *right;
		 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
		 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
		 * };
		 */
		class Solution 
		{
		    vector<string> ret;
		public:
		    void dfs(TreeNode* root, string str)
		    {
		        if(root->left == nullptr && root->right == nullptr) ret.push_back(str);
		
		        if(root->left)  dfs(root->left, str + ("->" + (to_string(root->left->val))));
		        if(root->right) dfs(root->right, str + ("->" + (to_string(root->right->val))));
		    }
		    
		    vector<string> binaryTreePaths(TreeNode* root) 
		    {
		        string str = to_string(root->val);
		        dfs(root, str);
		
		        return ret;
		    }
		};

到了这里,关于【算法专题】二叉树中的深搜(DFS)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LeetCode算法递归类—二叉树中的最大路径和

    目录 124. 二叉树中的最大路径和 - 力扣(LeetCode) 题解: 代码: 运行结果: 二叉树中的  路径  被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中  至多出现一次  。该路径  至少包含一个  节点,且不一定经过根节点。 路径和

    2024年02月12日
    浏览(38)
  • 设计一个求结点x在二叉树中的双亲结点算法

    要设计一个求二叉树中指定节点 x 的双亲节点的算法,可以按照以下步骤进行: 创建一个递归函数  findParent(root, x) ,其中  root  是当前子树的根节点, x  是要查找其双亲节点的节点。 首先检查根节点是否为空或者根节点是否就是要查找的节点 x,若是,则说明 x 没有双亲

    2024年02月04日
    浏览(43)
  • 算法刷题Day 20 最大二叉树+合并二叉树+二叉搜索树中的搜索+验证二叉搜索树

    递归 递归 迭代 递归 迭代 遇到两个坑的地方: 递归的时候,不能光验证左子树的根节点小于当前根节点,右子树的根节点大于但当前根节点,别忘了左子树上的每一个节点都要求比根节点要小,右子树上的每一个节点都要求比根节点大。 根节点跟左(右)子树中的某一个节

    2024年02月16日
    浏览(44)
  • 算法训练day20Leetcode654最大二叉树617合并二叉树700二叉树中的1搜索98验证二叉搜索树

    https://leetcode.cn/problems/maximum-binary-tree/description/ 中序遍历递归,找到最大值然后作为根节点 凡是构造二叉树的题目都用前序遍历 (中左右) 为先构造中间节点,然后递归构造左子树和右子树。 确定递归函数的参数和返回值 参数传入的是存放元素的数组,返回该数组构造的二

    2024年01月21日
    浏览(42)
  • 【算法刷题day20】Leetcode:654. 最大二叉树、617.合并二叉树、700. 二叉搜索树中的搜索、98.验证二叉搜索树

    草稿图网站 java的Deque 题目: 654. 最大二叉树 解析: 代码随想录解析 解题思路 NLR的建树 代码 总结 暂无 题目: 617.合并二叉树 解析: 代码随想录解析 解题思路 如果都为root1, root2都为空,返回null;如果root1为空,root2不为空,返回root2;如果roo1不为空,root2为空,返回root

    2024年04月10日
    浏览(40)
  • 算法刷题Day 21 二叉搜索树的最小绝对差+二叉搜索树中的众数+二叉树的最近公共祖先

    递归 递归中保存前一个节点的指针的方法,需要再学习,巩固一下 迭代 一下子就想到的方法:遍历一遍二叉搜索树,把各个节点的值存进哈希表里(节点值——键,出现频率——值),再遍历哈希表找众数 一般方法 既然是二叉搜索树,中序遍历就是有序的。有序的话,我

    2024年02月16日
    浏览(45)
  • 【算法第十七天8.1】530.二叉搜索树的最小绝对差 501.二叉搜索树中的众数 236. 二叉树的最近公共祖先

    链接 力扣530-二叉搜索树的最小绝对差 思路 链接 力扣501-二叉搜索树中的众数 思路 链接 力扣236.二叉树的最近公共祖先 思路

    2024年02月14日
    浏览(48)
  • 每日一题 1372二叉树中的最长交错路径

    给你一棵以  root  为根的二叉树,二叉树中的交错路径定义如下: 选择二叉树中  任意  节点和一个方向(左或者右)。 如果前进方向为右,那么移动到当前节点的的右子节点,否则移动到它的左子节点。 改变前进方向:左变右或者右变左。 重复第二步和第三步,直到你

    2024年02月10日
    浏览(46)
  • 力扣hot100 二叉树中的最大路径和 递归

    Problem: 124. 二叉树中的最大路径和 👨‍🏫 参考思路 时间复杂度: O ( n ) O(n) O ( n ) 空间复杂度: O ( n ) O(n) O ( n )

    2024年01月18日
    浏览(37)
  • 【数据结构】二叉树中的递归问题(超详细画图~)

    和光同尘_我的个人主页 不管风吹浪打,胜似闲庭信步。 --毛泽东 我本来还说上节难来着,没想到这节更难🥲 不过我既然会了保证xdm也能看懂👍 首先回顾下二叉树的概念 二叉树是由:空树 或者非空树(根节点,根节点的左子树、根节点的右子树)组成的 从概念中可以看出

    2024年02月07日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包