《算法题讲解指南:递归,搜索与回溯算法--二叉树中的深搜》--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的叶子节点进行删除;验证二叉搜索树 则利用中序遍历特性,通过维护前驱节点判断序列是否严格递增。提供了两种解法:完整遍历版和剪枝优化版,后者在发现不符合条件时提前终止递归。**希望大家能有所收获!

相关推荐
雪度娃娃15 小时前
ASIO异步通信——多线程模型
开发语言·网络·c++·php
嵌入式老牛15 小时前
液晶段码(米/日字格)识别—倾斜校正
opencv·算法·仿射变换
luj_176815 小时前
残熵算法:风险缓冲与效率优化的融合
c语言·开发语言·网络·经验分享·算法
oddsand116 小时前
pgvector 三大相似度算法
人工智能·算法·机器学习
运筹vivo@16 小时前
LeetCode 2574. 左右元素和的差值
算法·leetcode·职场和发展·每日一题
计算机安禾16 小时前
【数据库系统原理】第4篇:关系数据结构的形式化定义:域、笛卡尔积与关系模式
数据结构·数据库·算法
手写码匠16 小时前
手写 DeepSeek 推理引擎优化:从 FP16 到 INT4 的量化加速实战
人工智能·深度学习·算法·aigc
GuWenyue17 小时前
LeetCode 76 最小覆盖子串|JS 滑动窗口标准解法
前端·算法·面试
一只齐刘海的猫17 小时前
【Leetcode】移动零
算法·leetcode·职场和发展
王老师青少年编程17 小时前
信奥赛C++提高组csp-s之搜索进阶(迭代加深IDDFS)
c++·csp·信奥赛·csp-s·提高组·iddfs·埃及分数