剑指offer68 树中两个节点的最低公共祖先

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

题目一:这棵树是一颗二叉搜索树的情况

二叉搜索树的性质:二叉搜索树是排序的

从当前树的根节点开始和两个输入的节点进行比较:

  • 如果当前节点的值比两个节点的值都大,那么最低的共同父节点一定在当前节点的左子树,于是下一步遍历当前节点的左子树节点
  • 如果当前节点的值比两个节点的值都小,那么最低的共同父节点一定在当前节点的右子树,于是下一步遍历当前节点的右子树节点
  • 这样,从树中从上到下找到的第一个在两个输入节点值之间的节点就是最低的公共祖先
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    if( (root->val > p->val) && (root->val > q->val))
        return lowestCommonAncestor(root->left, p, q);
    else if((root->val < p->val) && (root->val < q->val))
        return lowestCommonAncestor(root->right, p, q);
    else
        return root;
}

题目二:这棵树是一颗普通树的情况

在这里插入图片描述
思路一:用递归

三种情况:

1、p q 一个在左子树 一个在右子树 那么当前节点即是最近公共祖先
2、p q 都在左子树
3、p q 都在右子树

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       
        if(root == nullptr) return nullptr;
        if(root == p || root == q) return root;

        //判断p q 是否在左 右子树中
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        
        //p q 一个在左子树 一个在右子树 返回他俩的根节点
        if(left != nullptr && right != nullptr) {
            return root;
        }
        //右子树里面没有p q
        if(left != nullptr) {
            return left;
        }
        //左子树没有p q
        if(right != nullptr) {
            return right;
        }
        //左 右子树都返回null
        return nullptr;
    }
};

思路二:剑指offer思路

  • 找到从 root 到 p 的路径,以及从 root 到 q 的路径,然后找出两条路径上最后一个相同的节点。
/**
 * 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 || !p || !q || p ==root || q == root) {
            return root;
        }

        vector<TreeNode*> pPath;
        vector<TreeNode*> qPath;

        getNodePath(root, p, pPath); // 找到从 root 到 p 的路径
        getNodePath(root, q, qPath); // 找到从 root 到 q 的路径

        return getlowestCommonAncestor(pPath, qPath); // 返回两条路径上最后一个相同的节点
    }

    void getNodePath(TreeNode* root, TreeNode* node, vector<TreeNode*>& path) { // 注意传引用
        if (!root || !node) {
            return;
        }

        TreeNode* temp = root, *prev = nullptr;
        deque<TreeNode*> store;

        while (temp || !store.empty()) {
            while (temp) {
                store.push_back(temp);

                if (temp == node) { // 中
                    while (!store.empty()) {
                        // 如果 root 匹配到了 node,填充 path 并退出函数
                        TreeNode* t = store.front();
                        path.push_back(t);
                        store.pop_front();
                    }
                    return;
                }

                temp = temp -> left; // 左
            }

            temp = store.back();

            if (!temp -> right || temp -> right == prev) {
                // 如果 temp 没有右子节点,或者我们之前已经访问过其右子节点了
                store.pop_back();
                prev = temp;
                temp = nullptr; // 这样就可以不进入上面那个 "while (temp)" 的子循环了
            } else {
                temp = temp -> right; // 右
            }
        }
    }

    TreeNode* getlowestCommonAncestor(vector<TreeNode*>& path1, vector<TreeNode*>& path2) { // 注意传引用
        if (path1.empty() || path2.empty()) {
            return nullptr;
        }

        int size = min(path1.size(), path2.size());
        int i = 0;

        for (; i < size; ++i) {
            if (path1[i] == path2[i]) {
                continue;
            } else {
                break; // 两条路径上的节点第一次不相同时,退出
            }
        }

        return path1[i - 1]; // 返回两条路径上最后一次相同的节点
    }
};

学习链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值