《五月集训》(第十八天)——树

前言

        欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
        今天是五月集训第十八天:🔥🔥🔥🔥

一、练习题目

        2236. 判断根结点是否等于子结点之和
        面试题 04.10. 检查子树
        面试题 04.06. 后继者
        1110. 删点成林

二、算法思路

树遍历图解
在这里插入图片描述

  • 1、2236. 判断根结点是否等于子结点之和:😄水题
  • 2、面试题 04.10. 检查子树:🐮🍺后续遍历,然后算哈希值这一招我做的话肯定做不了这么妙。
  • 3、面试题 04.06. 后继者:😄根据提示做一个中序遍历。
  • 4、1110. 删点成林:😄也是一个后序遍历

三、源码剖析

// 2236. 判断根结点是否等于子结点之和
class Solution {
public:
    bool checkTree(TreeNode* root) {
        return root->val == (root->left->val + root->right->val);
    }
};
  • 1、简简单单大喊一声过啦。
// 面试题 04.10. 检查子树
class Solution {
public:
    // 后序遍历,先左 再右 后根
    void calcHash(TreeNode *t) {
        if(t == nullptr) {
            return;
        }
        calcHash(t->left);
        calcHash(t->right); //(3)
        unsigned long long l = t->left ? t->left->val : 17201;
        unsigned long long r = t->right ? t->right->val : 89201;
        t->val = (int)((unsigned long long)t->val *1120102 + l * 23007 + r * 1); //(2)
    }
    bool find(TreeNode *root, int val) {//(4)
        if(root == nullptr) {
            return false;
        }
        return (root->val == val || find(root->left, val) || find(root->right, val));
    }
    bool checkSubTree(TreeNode* t1, TreeNode* t2) {
        calcHash(t1);
        calcHash(t2); //(1)
        if(t2 == nullptr) {
            return true;
        }
        return find(t1, t2->val);
    }
};
  • 1、利用树的哈希值来求解;
  • 2、树的哈希值可以理解为自己的值、左子树的哈希值、右子树的哈希值乘上不同的权重(权重可以理解为编译期的随机数,非运行时的随机数)后的累加和;(妙解by:英雄哥)
  • 3、后序遍历来计算树的哈希值(我画了个草图说明后续遍历);
  • 4、对于这道题,可以计算t1和t2的哈希值,然后去看t1这棵树上找是否有哈希值为t2的哈希值,如果有则表明存在。
// 面试题 04.06. 后继者
class Solution {
    bool flag;
    TreeNode *ret;
public:
    void dfs(TreeNode* root, TreeNode* p) {
        if(root == nullptr) {
            return;
        }
        // 中序遍历,先左 再根 后右
        dfs(root->left, p); //(1)
        if(flag && ret == nullptr) {
            ret = root;
        }

        if(root == p) {
            flag = true;
        }

        dfs(root->right, p);
    };
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        flag = false;
        ret = nullptr;
        dfs(root, p);
        return ret;
    }
};
  • 1、根据题目意思要返回上一个结点其实就是对树进行中序遍历;
  • 2、定义一个flag和返回的结点ret,并初始化flag为false,ret为nullptr,然后中序遍历树,如果发现当前树的根节点的为p,flag置为true,否则如果flag置为true,ret还是空,说明我们找到了p的上一个结点放入ret中。
// 1110. 删点成林
class Solution {
    int hash[1010];
    vector<TreeNode*> ret;
public:
    void dfs(TreeNode* parent, int isLeft, TreeNode* root) { //(1)
        if(root == nullptr) {
            return;
        }
        dfs(root, true, root->left);
        dfs(root, false, root->right);

        if(hash[root->val] == 1) {
            if(parent) {
                if(isLeft) {
                    parent->left = nullptr;
                } else
                    parent->right = nullptr;
            }
            if(root->left) ret.push_back(root->left);
            if(root->right) ret.push_back(root->right);
        }
    }
    vector<TreeNode*> delNodes(TreeNode* root, vector<int>& to_delete) {
        if(root == nullptr) {
            return {};
        }
        ret.clear();
        memset(hash, 0, sizeof(hash));
        for(int i = 0; i < to_delete.size(); ++i) {
            hash[to_delete[i]] = 1;
        } //(2)
        
        if(hash[root->val] == 0) {
            ret.push_back(root);
        } //(3)
        dfs(nullptr, false, root);
        return ret;
    }
};
  • 1、这也是个后序遍历的过程,需要记录父节点,目的是如果子节点被删了,父亲要和孩子断绝关系的;
  • 2、利用一个哈希表给要删除的结点置为1;
  • 3、这一步是判断一下根节点在不在删除列表中,不在的话加进来;
  • 4、然后去后序递归遍历,如果遇到一个结点在删除的列表中,则需要断绝父节点和自己的关系,并且左右子树存在的话塞入结果列表中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值