二叉树中的深搜

我们这次来解决一些二叉数类型的深度优先搜索。

计算布尔二叉树的值

题目描述

计算布尔二叉树的值

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

叶子节点 要么值为 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 。

算法原理和实现

根据题目描述和我们递归缩减规模的思想。

如果是叶子结点直接返回,否则返回左右子树的或/与:

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:
    bool evaluateTree(TreeNode* root) {
        if(root->val<=1)return root->val;
        else if(root->val==2)return evaluateTree(root->left)||evaluateTree(root->right);
        else return evaluateTree(root->left)&&evaluateTree(root->right);
    }
};

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

题目描述

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

给你一个二叉树的根节点 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

算法原理和实现

首先根据二叉树的拆解,如果他是叶子结点,那么返回当前结点值,拼接上前缀值即可。

没错我们还需要一个前缀数值:

可以看到,这个叶子结点我们理应返回491,这个49该如何来的,自然需要9传递下来:

这是叶子结点。如果不是叶子结点,自然就是返回左右结点的路径数字之和。并且我们要将前缀和当前结点的值拼接,并传递下去:

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:
    int dfs(TreeNode*root,int prev){
        if(!root)return 0;
        else if(!root->left&&!root->right){
            return 10*prev+root->val;
        }
        else return dfs(root->left,10*prev+root->val)+dfs(root->right,10*prev+root->val);
    }
    int sumNumbers(TreeNode* root) {
        return dfs(root,0);
    }
};

二叉树剪枝

题目描述

二叉树剪枝

给你二叉树的根结点 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就需要剪掉:

cpp 复制代码
class Solution {
public:
    TreeNode* pruneTree(TreeNode* root) {
        if(root->left)root->left=pruneTree(root->left);
        if(root->right)root->right=pruneTree(root->right);
        if(!root->left&&!root->right&&!root->val)return nullptr;
        return root;
    }
};

因为我们这里不知道TreeNode是怎么的得到的是栈上还是堆上,是malloc还是new?因此这里不做释放空间处理。

验证二叉搜索树

题目描述

验证二叉搜索树

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

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

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

递归

首先我们根据定义,判断当前左子树结点是否严格小于当前结点,右子树结点是否严格大于当前结点。

不过这样判断略显麻烦,我们可以将当前结点传给左右子树,让他们自行判断。

随后再判断左右子树是不是二叉搜索树:

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:
    bool isValidBST(TreeNode* root) {
        return dfs(root,LLONG_MIN,LLONG_MAX);
    }
    bool dfs(TreeNode*root,long long floor,long long ceiling){
        if(!root)return true;
        else if(root->val<=floor||root->val>=ceiling)return false;
        else return dfs(root->left,floor,root->val)&&dfs(root->right,root->val,ceiling);
    }
};

如此便是标准递归做法,这里实际上可以做一些剪枝操作,我们只做了一小部分。下一种实现方式再完成右子树剪枝:

中序遍历

事实上中序遍历本身也是递归的一种。我们这里利用二叉搜索树中序遍历是有序的这个性质,每个结点判断是否严格大于中序遍历的前驱结点:

cpp 复制代码
class Solution {
public:
    long long prev=LLONG_MIN;
    bool isValidBST(TreeNode* root) {
        if(!root)return true;
        bool ret=true;
        ret=isValidBST(root->left);
        if(!ret)return ret;
        if(root->val<=prev)return false;
        prev=root->val;
        return isValidBST(root->right);
    }
};

当左子树或者当前结点不满足时,我们执行剪枝操作。

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

题目描述

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

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 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 <= 104
  • 0 <= Node.val <= 104

算法原理和实现

根据上一题的思路,我们返回他中序的第k个结点即可,结合剪枝策略:

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:
    int step=0;
    int kthSmallest(TreeNode* root, int k) {
        int num;
        if(root->left)num=kthSmallest(root->left,k);//还是不要走到空结点为好。
        if(step==k)return num;
        if(++step==k)return root->val;
        if(root->right)return kthSmallest(root->right,k);
        return root->val;
    }
};

二叉树的所有路径

题目描述

二叉树的所有路径

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

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

示例 1:

输入:root = [1,2,3,null,5]

输出:["1->2->5","1->3"]

算法原理和实现

这是一个简单的回溯,我们判断当前是否为叶子结点,如果是将当前路径加入返回数组中。如果不是向左右子树传递。最后要回退当前值:

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:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> ret;
        string tmp;
        function<void(TreeNode*)>dfs=[&](TreeNode* root){
            int len=tmp.size();
            tmp+=to_string(root->val);
            if(!root->left&&!root->right){
                ret.emplace_back(tmp);
            }else{
                tmp+="->";
            }
            if(root->left)dfs(root->left);
            if(root->right)dfs(root->right);
            tmp.resize(len);
        };
        dfs(root);
        return ret;
    }
};
相关推荐
锅包一切2 小时前
PART17 一维动态规划
c++·学习·算法·leetcode·动态规划·力扣·刷题
Polaris北2 小时前
第二十六天打卡
c++·算法·动态规划
罗湖老棍子3 小时前
【例 2】选课(信息学奥赛一本通- P1576)
算法·树上背包·树型动态规划
每天要多喝水3 小时前
动态规划Day33:编辑距离
算法·动态规划
每天要多喝水3 小时前
动态规划Day34:回文
算法·动态规划
weixin_477271693 小时前
马王堆帛书《周易》系统性解读(《函谷门》原创)
算法·图搜索算法
AomanHao4 小时前
【ISP】基于暗通道先验改进的红外图像透雾
图像处理·人工智能·算法·计算机视觉·图像增强·红外图像
We་ct4 小时前
LeetCode 226. 翻转二叉树:两种解法(递归+迭代)详解
前端·算法·leetcode·链表·typescript
大熊背4 小时前
APEX系统中为什么 不用与EV0的差值计算曝光参数调整量
人工智能·算法·apex·自动曝光