剑指 Offer 68 - II. 二叉树的最近公共祖先

本文介绍两种寻找二叉树中两个节点的最小公共祖先的方法:递归和使用哈希表记录父节点。递归方法通过判断当前节点的子树是否包含目标节点来确定公共祖先;哈希表方法则先存储每个节点的父节点,再从目标节点向上查找共同路径。

题目链接:leetcode.

递归,返回值为当前节点的子树是否包含p或q

1.当前节点为空,则一定不包含,返回false
2.当前节点的左右子树都为true,或者,当前节点是p或q 且左右子树有一个为true,则当前节点就是最小公共子节点
3.当前节点的子树有true,则返回true,或者当前节点是p或q,也返回true

注意:找到的ans是最小公共祖先,因为从叶子节点往上找的,一定是深度最大的,而且找到之后,不会被别的节点刷新,因为ans的子树已经包括了p和q,而ans只能提供一个true值,所以再也满足不了if条件了
时间复杂度 O(n)每个节点都遍历一次
空间复杂度 O(n)递归调用栈的深度,最差条件下是一个链

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
/*
/*
执行用时:20 ms, 在所有 C++ 提交中击败了72.05%的用户
内存消耗:13.8 MB, 在所有 C++ 提交中击败了96.35%的用户
*/
class Solution {
public:
	TreeNode* ans;
	bool dfs(TreeNode* root, TreeNode* p, TreeNode* q)
	{
		if(root == nullptr)
			return false;
		bool lchild = dfs(root->left, p, q);
		bool rchild = dfs(root->right, p, q);
		if((lchild && rchild) || ((root == q || root == p) && (lchild || rchild)))
			ans = root;
		return lchild || rchild || (root == p) || (root == q);
	} 
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        dfs(root, p, q);
        return ans;
    }
};

使用哈希表存储每个节点的父节点地址(因为每个节点的值仅出现一次)

1.遍历树,对于碰到的每一个节点,存储<value, &parent>
2.搜索p的parent,直到root,将访问过的节点置为true
3.搜索q的parent,碰到第一个被访问过的节点,就是最小公共祖先

O(n) O(n)

/*
执行用时:32 ms, 在所有 C++ 提交中击败了16.34%的用户
内存消耗:17.4 MB, 在所有 C++ 提交中击败了5.89%的用户
*/
class Solution {
public:
	unordered_map<int, TreeNode*> map;
	void dfs(TreeNode* root)
	{
		if(root == nullptr)
		{
			return;
		}
		if(root->left)
		{
			map[root->left->val] = root;
			dfs(root->left);
		}
		if(root->right)
		{
			map[root->right->val] = root;
			dfs(root->right);
		}
	} 
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        map[root->val] = NULL;
		dfs(root);
        set<int> visit;
        while(p)
        {
        	visit.insert(p->val);
        	p = map[p->val];
        }
        while(q)
        {
        	if(visit.count(q -> val))
        	{
        		return q;
        	}
        	q = map[q -> val];
        }
        return NULL;
    }
};
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值