五月集训(第18天)—树
树
1. 2236. 判断根结点是否等于子结点之和
思路:
超级大水题
bool checkTree(TreeNode* root) {
if (root->left->val + root->right->val == root->val) return true;
return false;
}
2. 面试题 04.10. 检查子树
思路1:
分类讨论所有情况,递归求解。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool compare(TreeNode *t1, TreeNode *t2) {
// t1和t2不同则返回false
if (t1 != NULL && t2 == NULL) return false;
if (t1 == NULL && t2 != NULL) return false;
// t1和t2相同则返回true
if (t1 == NULL && t2 == NULL) return true;
if (t1->val == t2->val) { /* t1与t2相同,继续比较左右子树是否相同 */
return compare(t1->left, t2->left) && compare(t1->right, t2->right);
} else return false; /* 出现不同则t2不是t1的子树 */
}
bool checkSubTree(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL && t2 == NULL) return true; /* 两棵空树 */
if (t1 != NULL && t2 == NULL) return true; /* t1不空,t2空,t2必为t1子树 */
if (t1 == NULL && t2 != NULL) return false; /* t1空,t2不空,t2一定不是t1子树 */
// t1等于t2,t2是t1左边子树,t2是t1右边子树,满足其一即可
return compare(t1, t2) || checkSubTree(t1->left, t2) || checkSubTree(t1->right, t2);
}
};
思路2:
对树及其左右子树分别建立hash映射,根据hash值判断。
注意:递归建立hash的过程中,由于数值的累积,会导致爆int,可以先开long long
强转一下,然后转回int
。如果觉得强转int
有问题,可以直接使用一个hash函数,每次调用时都重新递归的计算hash值,不过复杂度会高很多(因为多次遍历二叉树)。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void cal_hash(TreeNode *root) {
if (root == nullptr) return ;
// 递归的将左右子树建立hash映射
if (root->left) cal_hash(root->left);
if (root->right) cal_hash(root->right);
// 建立映射的方法
long long l = root->left ? root->left->val : 32;
long long r = root->right ? root->right->val : 33;
root->val = (int)((long long)root->val + l * 31 + r * 131);
}
bool find_child(TreeNode *root, int target) {
if (root == nullptr) return false;
if (root->val == target) return true; /* 找到目标结点 */
// 在左右子树中寻找目标结点
return find_child(root->left, target) || find_child(root->right, target);
}
bool checkSubTree(TreeNode* t1, TreeNode* t2) {
if (t2 == nullptr) return true;
// 对以某个结点做为根节点的子树建立hash映射,将hash值直接存入结点的val中
cal_hash(t1);
cal_hash(t2);
// 当t2与t1相同,或为其左右子树时,满足条件
return (t1->val == t2->val) || find_child(t1->left, t2->val) || find_child(t1->right, t2->val);
}
};
3. 面试题 04.06. 后继者
思路:
根据题目提示,使用树的中序遍历寻找目标点的下一个点。遇到目标点时进行标记,中序遍历到其后一个结点时返回结果。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
int flag;
TreeNode *ret;
public:
void dfs(TreeNode *root, TreeNode *P) {
if (root == NULL) return ;
dfs(root->left, P);
if (flag && ret == NULL) { /* 返回目标点的下一个点 */
ret = root;
return ;
}
if (root == P) { /* 找到目标点 */
flag = 1;
}
dfs(root->right, P);
}
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
flag = false;
ret = NULL;
dfs(root, p);
return ret;
}
};
4. 1110. 删点成林
思路:
根据题目中结点的标号方式可知应该使用先序遍历,为了保证树的先序遍历能够正常执行,需要等到每个结点的左右子树遍历之后再删除结点。
/**
* 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 {
int hash[1010];
vector<TreeNode*> ret;
public:
void dfs(TreeNode *parent, int isleft, TreeNode *root) {
if (root == nullptr) return ;
// 先序遍历
dfs(root, true, root->left);
dfs(root, false, root->right);
// 遍历之后完成删除操作,否则无法完成遍历
if (hash[root->val] == 1) {
if (parent) { /* 将父节点指向nullptr,完成删除 */
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 {};
int n = to_delete.size();
int i;
// 初始化容器
ret.clear();
memset(hash, 0, sizeof(hash));
for (i = 0; i < n; i++) {
hash[to_delete[i]] = 1;
}
// ret中添加删除后剩下的每棵子树
dfs(nullptr, false, root);
// 单独考虑root结点
if (hash[root->val] == 0) ret.push_back(root); /* 根节点未被删除,则加入ret */
return ret;
}
};
东方欲晓,莫道君行早。