68剑指offer leetcode 88. 最近公共祖先

 

给定一棵二叉树,找到两个节点的最近公共父节点(LCA)。

最近公共祖先是两个节点的公共的祖先节点且具有最大深度。

样例

对于下面这棵二叉树

  4
 / \
3   7
   / \
  5   6

LCA(3, 5) = 4

LCA(5, 6) = 7

LCA(6, 7) = 7

一、树为二叉查找树

二叉查找树的创建: 

//创建二叉搜索树
struct TreeNode
{
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode(val)
	{
		this->val = val;
		this->left = this->right = NULL;
	}
};
//插入
void Treeinsert(TreeNode* &root, int val)
{   
	TreeNode*node = new TreeNode(val);
	if (root == NULL)
	{
		root->val = val;
		return;
	}
	TreeNode*pre = NULL;
	TreeNode*p = root;
	while (p)
	{
		pre = p;
		if (val < p->val)
			p = p->left;
		else
			p = p->right;
	}
	if (val<pre->val)
	 pre->left=node;
	else 
	 pre->right=node;
	}
}

思路:

1、二叉搜索树是排过序的,位于左子树的结点都比父节点小,位于右子树的结点都比父节点大。

2、从树的根结点开始和输入的两个结点比较。

如果当前结点比两个结点的值都大,则最低的共同父节点一定在当前结点的左子树中

如果当前结点比两个结点的值都小,则最低共同父节点一定在当前结点的右子树中。

这样,在树中从上到下找到的第一个在两个输入结点值之间的结点即最低公共祖先。

代码:

自己写的代码,非递归

TreeNode* LCA(TreeNode* root, TreeNode*A, TreeNode*B)
{
	if (root == NULL)
		return NULL;
	while (root != NULL)
	{
		if (root->val < min(A->val,B->val))
			root = root->right;
		if (root->val > max(A->val,B->val))
			root = root->left;
		else
			return root;
	}
	return NULL;
}

参考他人的代码:

// 最近公共祖先  
	TreeNode *LCA(TreeNode *root, TreeNode *u, TreeNode *v) {
		// 空树  
		if (root == NULL || u == NULL || v == NULL) {
			return NULL;
		} 
		 // u < t < v  or v < t < u  
		if ((u->val < root->val && root->val < v->val) ||
			(v->val < root->val && root->val < u->val)) {
			return root;
		} 
		 // u < root and v < root  left sub tree  
		if (u->val < root->val && v->val < root->val) {
			// u是v祖先 or v是u的祖先  
			if (root->left == u || root->left == v) {
				return root;
			}/ 
			return LCA(root->left, u, v);
		} 
		 // u > root and v > root  right sub tree  
		if (u->val > root->val && v->val > root->val) {
			// u是v祖先 or v是u的祖先  
			if (root->right == u || root->right == v) {
				return root;
			} 
			return LCA(root->right, u, v);
		}  
		return  NULL;
	}

二叉树,但每个结点都有指向父节点的指针next.

思路:分别从给定的两个节点出发上溯到根节点,形成两条相交的链表,问题转化为求这两个相交链表的第一个交点。

即传统方法:求出linkedList A的长度lengthA, linkedList B的长度LengthB。然后让长的那个链表走过abs(lengthA-lengthB)步之后,齐头并进,就能解决了。

代码:

int  getlength(TreeNode*node)
{
	int length = 0;
	TreeNode*temp = node;
	while (temp)
	{
		length++;
		temp = temp->next;
	}
	return length;
}
TreeNode* LCA(TreeNode*node1, TreeNode* node2)
	{
	int length1 = getlength(node1);
	int length2 = getlength(node2);
	TreeNode* pt1 = NULL;
	TreeNode* pt2 = NULL;
	int k = 0;
	if (length1 >= length2)
	{
		TreeNode*temp = node1;
		while (k++ < (length1 - length2))
		{
			temp = temp->next;
		}
		pt1 = temp;
		pt2 = node2;
	}
	else
		if (length1 < length2)
		{
			TreeNode*temp = node2;
			while (k++ < (length2 - length1))
			{
				temp = temp->next;
			}
			pt1 = temp;
			pt2 = node1;
		}
	while(pt1&&pt2&&pt1 != pt2)
	{
	pt1 = pt1->next;
	pt2 = pt2->next;
    }
	return pt1;

普通二叉树:无父节点指针

思路:


记录从根找到node1和node2的路径,然后再把它们的路径用类似的情况一来做分析,比如还是node1=3,node2=8这个case.我们肯定可以从根节点开始找到3这个节点,同时记录下路径3,4,6,10,类似的我们也可以找到8,6,10。我们把这样的信息存储到两个vector里面,把长的vector开始的多余节点3扔掉,从相同剩余长度开始比较,4!=8, 6==6,我们找到了我们的答案。

写的很好,代码:

 //找到root到结点n的路径path
   bool nodepath(TreeNode*root, TreeNode* value, vector<TreeNode*>&path)
	{
		if (root == NULL)
			return false;
		if (root==value)
		{
		    path.push_back(root);
			return true;
		}
		if (nodepath(root->left, value, path))
			{
			    path.push_back(root);
                return true;}
		 else  if (nodepath(root->right, value, path))
		    {
			    path.push_back(root);
			 	return true;}
	     else
		    	return false;
	}
	//LCA函数寻找最近公共祖先
		TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* A,TreeNode* B)
		{
			vector<TreeNode*>path1;
			vector<TreeNode*>path2;
			bool find = false;
			find|= nodepath(root, A, path1);
			find&= nodepath(root, B, path2);
			TreeNode*p = NULL;
		if(find)
		{
		int minsize = path1.size() > path2.size() ? path2.size() : path1.size();
		int it1 = path1.size() - minsize;
		int it2 = path2.size() - minsize;
		for (; it1 < path1.size(), it2 < path2.size(); it1++, it2++)
		{
			if (path1[it1] == path2[it2])
			{
				p = path1[it1];
				break;
			}
		}
    	}
		return p;
   }
};



 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值