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

相关推荐
承渊政道2 小时前
C++学习之旅【异常相关内容以及类型转换介绍】
c语言·c++·笔记·vscode·学习·macos·visual studio
liulilittle2 小时前
Debian/Ubuntu 18.04 上安装 GLIBC 2.28 (2026)
linux·运维·服务器·开发语言·c++·ubuntu·debian
承渊政道2 小时前
C++学习之旅【深入回溯C++11的发展历程】
c语言·c++·笔记·vscode·学习·macos·visual studio
像素猎人3 小时前
数据结构之——图论中常用的方向数组是如何定义的
算法
卷福同学10 小时前
QClaw内测体验,能用微信指挥AI干活了
人工智能·算法·ai编程
sali-tec10 小时前
C# 基于OpenCv的视觉工作流-章34-投影向量
图像处理·人工智能·opencv·算法·计算机视觉
xiaoye-duck10 小时前
《算法题讲解指南:递归,搜索与回溯算法--递归》--3.反转链表,4.两两交换链表中的节点,5.快速幂
数据结构·c++·算法·递归
Eward-an11 小时前
【算法竞赛/大厂面试】盛最多水容器的最大面积解析
python·算法·leetcode·面试·职场和发展
山栀shanzhi11 小时前
归并排序(Merge Sort)原理与实现
数据结构·c++·算法·排序算法