题目链接:https://leetcode-cn.com/problems/smallest-subtree-with-all-the-deepest-nodes/
根据题意及几个示例分析:分几种情况:
- 如果最深节点只有一个那么该节点就是所求的最小子树,独点成树。
- 如果有多个节点,目的就是求包含这多个节点的最小子树,那么转变一下思路,是不是求这多个节点的以LCA(最近公共祖先)为根的子树。所以问题转变为求LCA问题。
- 当时想到一个问题,怎么去求解很多个,超过2个节点的LCA呢,后来想明白,其实这个问题是可以变化为求所得所得节点数组
midres
的第一个和最后一个节点的LCA即可。
对于3种想法的证实,如下图:
显然图中的7,4节点是最深的,那么此时midres
中保存的是7和4节点的地址,我们求这两者的LCA就是答案2的地址。有假设原树中没有7和4节点,那么这时求到的midres
中的内容就为[6,2,0,8],那么我们求6和8节点的LCA是不是等价于求这四者的LCA。
有了这些思路那么编写代码就比较容易了,下述代码是可以AC的。
主要包括三个辅助函数:void helper1(TreeNode* rt,int dep,int &maxdep)
来获取树的最深节点的深度和每个叶节点和其深度的映射关系;bool helper(TreeNode*rt,int val)
判断rt子树中是否包含val值,也可以理解为rt子树中是否包括val的子节点;TreeNode* LCA(TreeNode* root, int p, int q)
求两个节点的最近公共祖先。
class Solution {
public:
unordered_map<TreeNode*,int> mirror;//映射节点地址或值对应的深度
void helper1(TreeNode* rt,int dep,int &maxdep){
if(rt){
if(!rt->left && !rt->right){
maxdep = maxdep > dep ? maxdep : dep;
mirror[rt] = dep;
}
if(rt->left) helper1(rt->left,dep+1,maxdep);
if(rt->right) helper1(rt->right,dep+1,maxdep);
}
}
bool helper(TreeNode*rt,int val){
/*
@ 判断rt树中是否包含val
*/
if(!rt) return false; //如果树为空,返回false
if(rt->val == val) return true;//包含就返回true
return helper(rt->left,val) || helper(rt->right,val);//只要包含在左子树或右子树就返回true
}
TreeNode* LCA(TreeNode* root, int p, int q) {
if(!root)
return root;
if(root->val == p || root->val == q)
return root;
bool left = helper(root->left,p);//p是否在左子树
bool right = helper(root->right,q);//q是否在右子树
if(left == right) return root;//一个在左一个在右
/*
p没在左,即left=false,则p在右,而right为true,即q在右,所以在右子树找即可
同理,left为真时,则right为假,说明q没在右子树中,在左子树,此时p也在左子树,所以在左子树找
*/
if(!left) return LCA(root->right,p,q);
else return LCA(root->left,p,q);
}
TreeNode* subtreeWithAllDeepest(TreeNode* root) {
int maxdep = INT_MIN;
helper1(root,1,maxdep);
vector<TreeNode*> midres;
for(auto [k,v] : mirror)
if(v == maxdep) midres.push_back(k);
if(midres.size() == 1) return midres[0];
return LCA(root,midres[0]->val,midres.back()->val);
}
};
本题涉及到的值得学习的知识点有LCA求法,每个节点对应的深度求法。