支线任务-4
Lowest Common Ancestor of a Binary Tree
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”
_______3______ / \ ___5__ ___1__ / \ / \ 6 _2 0 8 / \ 7 4
For example, the lowest common ancestor (LCA) of nodes 5
and 1
is 3
. Another example is LCA of nodes 5
and 4
is 5
, since a node can be a descendant of itself according to the LCA definition.
按照题目的说法,这就是一个LCA(最小公共祖先)的裸题。可以算是经典题目了。
抛开那些套路般的算法(下面会详细介绍)模板,这个题目也是可以做的。注意到它只是进行了一次询问,所以会比较好考虑。
注意到这样几点(假设所求节点为p和q):
- 如果节点node为p和q的LCA的话,node的所有祖先都会是p和q的公共祖先
- 对于节点p和q来说,设他们的LCA为r,则要么p和q其中一个为r,要么他们分别处于r的左子树和右子树。
所以如果采用DFS(后序遍历)的搜索方式,一旦判断到某个节点符合上面第二条的性质,那么就能马上肯定这个节点就是所求LCA。复杂度不难分析,O(N)
illustrate:
比如上面的样例我们要找的是4和8的LCA
DFS开始
6这里什么都没发生
7这里什么都没发生
找到了我们要的一个节点4
把4顺着传上去
同理
0这里什么都没发生
找到了另一个节点8
把8传上去
在3处达到了所要条件之一,4和8分别属于他的左右子树。所以3这里就是答案了。
对应代码如下:
/** * 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: TreeNode *pp = NULL; TreeNode *qq = NULL; TreeNode *lca = NULL; void dfs(TreeNode *root, TreeNode *p, TreeNode *q) { if (lca != NULL) return; if (root->left != NULL) { dfs(root->left, p, q); if (pp == root->left) pp = root; if (qq == root->left) qq = root; } if (root->right != NULL) { dfs(root->right, p, q); if (pp == root->right) pp = root; if (qq == root->right) qq = root; } if (lca != NULL) return; if (root == p) pp = root; if (root == q) qq = root; if (pp != NULL && qq != NULL && pp == qq) lca = root; } TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { dfs(root, p, q); return lca; } };
当然还有很多方法,求解LCA问题本来应该是用
Tarjan算法
ST算法
等其他正规方法。
但是就像之前一样,写进去觉得这样会需要读者有一些先验知识,读起来比较困难,然而不写又感觉对不起读者。
比如第三次任务的目的是写的尽量精简,形象,然后我给了一堆奇怪的式子,还没有自己画(或者是随便从其他的博客复制粘贴的)一系列图片(再配上点随便点句子就算是illustrate了)而是自己手打了一串计算过程,确实一点都不形象,是我的问题,说明我的叙述的方式有待改善。
所以这次任务既然写明了同样要平易近人,那么我就顺着任务的要求,采用了很多illustrate(绝对自己画的),绝口不提那些并查集、倍增法等需要读者有先验知识的词汇,这并不代表我不了解这些算法,而是为了让读者读起来不困难,能够在一分钟之内理解博客的思路。所以我依然敢说我没有敷衍对待任务。
尽管我从不认为自己写的博客质量高,但是我更不会认为连对问题的一点拓展都没有的博客能说是有质量,那样还不如随便看了看别人的然后照着添油加醋地码一遍来的好,而且我认为希望通过博客来传达自己对一个问题真正思考的人都会有一样的想法;
而从希望读技术博客的读者的角度来讲,该读者定是希望通过阅读博客来接受新的思想,也能预料得到一些理解起来可能有困难的东西,更会希望在看到一道题目的博客的同时,能够了解更多与这道题目相关的题目,触类旁通,举一反三,而不是只是做一个题目,为了做一个题目而做一个题目,也就是硬着头皮为了完成任务而完成的吧,没有更多的思考。
这样的为了任务而看博客,为了任务而写博客,表面上看来我写你读,皆大欢喜,而静下心来看,谁又能得到真正的提高呢?
要说所谓的人生思考,写博客也是能带来人生思考的,那就是,有思想的东西(并没有说我自己,而是其他一些被忽略的存在),往往都被其他的表面浮华的东西掩藏住了,而有能力发现这些思想的人少之又少。
不过说真的,
裸题真是太没意思了。