《算法题讲解指南:递归,搜索与回溯算法--二叉树中的深搜》--8.二叉树剪枝,9.验证二叉搜索树

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》

《C++入门到进阶&自我学习过程记录》

《算法题讲解指南》--优选算法

《算法题讲解指南》--递归、搜索与回溯算法

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

8.⼆叉树剪枝

题目链接:

题目描述:

题目示例:

解法(dfs-后序遍历):

算法思路:

C++算法代码:

算法总结及流程解析:

9.验证二叉搜索树

题目链接:

题目描述:

题目示例:

解法(利用中序遍历):

算法思路:

C++算法代码(无剪枝版本):

C++算法代码(剪枝版本):

算法总结及流程解析:

结束语


8.⼆叉树剪枝

题目链接:

814. 二叉树剪枝 - 力扣(LeetCode)

题目描述:

题目示例:

解法(dfs-后序遍历):

后序遍历按照左子树、右子树、根节点的顺序遍历二叉树的所有节点,通常用于父节点的状态依赖于子节点状态的题目。

算法思路:

如果我们选择从上往下删除,我们需要收集左右子树的信息,这可能导致代码编写相对困难。然而,通过观察我们可以发现,如果我们先删除最底部的叶子节点,然后再处理删除后的节点,最终的结果并不会受到影响。

因此,我们可以采用后序遍历的方式来解决这个问题。在后序遍历中,我们先处理左子树,然后处理右子树,最后再处理当前节点 。在处理当前节点时,我们可以判断其是否为叶子节点且其值是否为0 ,如果满足条件,我们可以删除当前节点(置空)

需要注意的是,在删除叶子节点时,其父节点很可能会成为新的叶子节点。因此,在处理完子节点后,我们仍然需要处理当前节点。这也是为什么选择后序遍历的原因(后序遍历首先遍历到的一定是叶子节点)。

通过使用后序遍历,我们可以逐步删除叶子节点,并且保证删除后的节点仍然满足删除操作的要求。这样,我们可以较为方便地实现删除操作,而不会影响最终的结果。

若在处理结束后所有叶子节点的值均为1,则所有子树均包含1,此时可以返回。

C++算法代码:

cpp 复制代码
/**
 * 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);
        //左右子树完成剪枝后,还需要判断当前结点本身值是否为0
        //如果结点本身为0且左右子树全为空说明需要删除(结点置为nullptr)
        if(root->val == 0 && root->left == nullptr && root->right == nullptr)
        {
            root = nullptr;
        }
        //如果左右子树其中一个不是空(存在结点值为1)或者当前结点值为1,则不需要删除
        return root;
    }
};

算法总结及流程解析:

9.验证二叉搜索树

题目链接:

98. 验证二叉搜索树 - 力扣(LeetCode)

题目描述:

题目示例:

解法(利用中序遍历):

中序遍历 按照左子树、根节点、右子树的顺序遍历二叉树的所有节点,通常用于二叉搜索树相关题目

算法思路:

如果一棵树是二叉搜索树 ,那么它的中序遍历的结果一定是一个严格递增的序列

因此,我们可以初始化一个无穷小的全区变量prev ,用来记录中序遍历过程中的前驱结点 。那么就可以在中序遍历的过程中,先判断是否和前驱结点构成递增序列 ,然后修改前驱结点为当前结点,传入下一层的递归中。

C++算法代码(无剪枝版本):

cpp 复制代码
/**
 * 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:
    //用一个全局变量来记录中序遍历过程中是否有序
    long prev = LONG_MIN;
    bool isValidBST(TreeNode* root) 
    {
        //判断是否为二叉搜索树方法:利用中序遍历判断是否有序
        //递归结束条件:当前结点为空则也是二叉搜索树,返回true
        if(root == nullptr)
        {
            return true;
        }

        bool left = isValidBST(root->left);
        //此时左子树已经判断完了,则轮到当前结点的值和prev进行判断
        bool cur = false;
        if(prev < root->val)
        {
            prev = root->val;
            cur = true;
        }
        bool right = isValidBST(root->right);
        //此时当前结点的右子树已经判断完了,则判断以当前结点为头是否为二叉搜索树
        if(left && cur && right)
        {
            return true;
        }
        return false;
    }
};

C++算法代码(剪枝版本):

cpp 复制代码
/**
 * 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:
    //用一个全局变量来记录中序遍历过程中是否有序
    long prev = LONG_MIN;
    bool isValidBST(TreeNode* root) 
    {
        if(root == nullptr)
        {
            return true;
        }

        bool left = isValidBST(root->left);
        //此时当前结点的左子树已经判断完了
        //但如果判断完左子树发现已经不是二叉搜索树了,
        //则无需再将结点的值和prev比较,以及判断结点的右子树了(剪枝)
        if(left == false)
        {
            return false;
        }
        bool cur = false;
        if(prev < root->val)
        {
            prev = root->val;
            cur = true;
        }
        else
        {
            //如果当前结点值大于等于prev,说明不是有序的
            //就不符合二叉搜索树,则无需再判断结点的右子树了(剪枝)
            return false;
        }
        bool right = isValidBST(root->right);
        if(left && cur && right)
        {
            return true;
        }
        return false;
    }
};

算法总结及流程解析:

结束语

到此,8.二叉树剪枝,9.验证二叉搜索树 这两道算法题就讲解完了。**二叉树剪枝 采用后序遍历方法,先处理左右子树再判断当前节点是否为值为0的叶子节点进行删除;验证二叉搜索树 则利用中序遍历特性,通过维护前驱节点判断序列是否严格递增。提供了两种解法:完整遍历版和剪枝优化版,后者在发现不符合条件时提前终止递归。**希望大家能有所收获!

相关推荐
青瓦梦滋5 小时前
C++的IO流与STL的空间配置器
开发语言·c++
鱼很腾apoc7 小时前
【学习篇】第20期 超详解 C++ 多态:从语法规则到底层原理
java·c语言·开发语言·c++·学习·算法·青少年编程
不吃土豆的马铃薯8 小时前
4.SGI STL 二级空间配置器 allocate 与_S_refill 源码解析
c语言·开发语言·c++·dreamweaver·内存池
小许同学记录成长8 小时前
三维重建技术文档
算法·无人机
fufu03118 小时前
vscode配置C/C++环境,用GDB调试简单程序分享
开发语言·c++
水云桐程序员10 小时前
C++变量的概念及用法
开发语言·c++
小O的算法实验室10 小时前
2026年ASOC,基于多目标优化去噪双存档进化算法+路径规划,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
水饺编程10 小时前
第5章,[Win32 章节] :几种典型的颜色
c语言·c++·windows·visual studio
2601_9545267510 小时前
逆向解析Temu底层动销算法:基于API高并发轮询与全域存量透视的自动化架构重构
算法·架构·自动化