zoukankan      html  css  js  c++  java
  • Lowest Common Ancestor of a Binary Tree题解

    这是LeetCode上的一道题,让我们先来看一看题目:

    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.

    简而言之就是已知二叉树,已知二叉树中的两个节点,求这两个节点最近的公共祖先。

    同时在代码部分,题目给出了所要求的数据结构,我们可以注意到这一数据结构中,我们只能直接找到这一个节点的左右孩子,但是并不能直接找到他的双亲。

    /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     * };
     */
    

      

    基于这样的数据结构,对于没有学过算法或者学过一些算法不过比较渣的同学(比如我。。。),可能的第一反应就是从根节点开始逐层向下遍历,判断每一个节点是否是所求的公共祖先,然后再找到这些符合条件的节点中最低层的,这种方法显然是十分低效的,因为以每一个节点为根节点的子树都被遍历了很多很多遍。于是为了优化这种算法,我们很自然地想到能否通过调用递归函数来储存中间信息,只对树进行一次遍历解决这个问题。使用这种方法时我们首先需要考虑两个问题:树的遍历方法是先根遍历中根遍历还是什么别的?递归函数的返回值应该储存什么信息?

    为了解决这两个问题,我们回归到最近共同祖先这个概念本身。若所求节点是已知节点的最近共同祖先,则说明两点,首先这个节点一定是已知节点的共同祖先,同时这个节点的所有后代(例如左孩子,右孩子)都不是已知节点的共同祖先。因此我们可以总结出该问题的两种不同情况,第一种情况是p,q两个节点分别在所求节点的左子树,右子树中;第二种情况是所求节点本身就是p或q节点。

    应对第一种情况,我们发现递归函数的返回值需要能够反映一个节点是否是p或q节点的祖先即可,若一个节点的左右孩子都是p或q节点的祖先,则该节点就是p和q的最近共同祖先。同时我们还需要先对根节点进行特判,判断其是否就是p或q节点,则这时不用再遍历该节点的子树了。经过这些分析,我们决定采取先根遍历的形式,先判断某节点是否是p或q节点,至于递归函数的返回值,如果某节点是p或q的祖先,返回该节点指针,否则返回NULL。

    实现这种思路的代码如下:

    /**
     * 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
                    if(!root) 
    			return NULL;      
    		if(root == p || root == q)
    			return root;
    		TreeNode* L = lowestCommonAncestor(root->left, p, q);
    		TreeNode* R = lowestCommonAncestor(root->right, p, q);
    		if(L && R)
    			return root;
    		return L ? L : R;
        }
    };
    

      下面我们来分析这个代码的正确性。首先,若p,q两个节点分别在所求节点的左子树,右子树中,则该所求节点是整个二叉树中唯一一个左右节点返回值都不是NULL的节点;若所求节点本身即是p或q节点,则在该节点处会返回该节点的指针,而该遍历该节点的所有祖先节点的返回值都是该节点指针。这确保了这种算法的正确性。

      最后总结一下,这段代码应用了深度搜索的思想和先根遍历的遍历方法,时间复杂度为O(n),形式上也很有美感。感觉Lowest Common Ancestor of a Binary Tree这道题目本身虽然不复杂,但却很能说明一些问题。

  • 相关阅读:
    最长回文子串 leetcode
    leetcode Plus one
    n的阶乘末尾有几个0?
    求两数的最大公约数和最小公倍数
    汉诺塔
    求n的阶乘
    svn book
    求斐波那契数列第n项
    判断一个数是否是素数
    <C Traps and Pitfalls>笔记
  • 原文地址:https://www.cnblogs.com/tiezhibieek/p/4985913.html
Copyright © 2011-2022 走看看