前言
欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
今天是五月集训第十八天:树🔥🔥🔥🔥
一、练习题目
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、然后去后序递归遍历,如果遇到一个结点在删除的列表中,则需要断绝父节点和自己的关系,并且左右子树存在的话塞入结果列表中。