剑指offer -- 二叉树的最近公共祖先(二叉搜索树+有指向父亲结点指针的二叉树+普通二叉树)

给你一棵二叉树及二叉树中的两个结点,找出这两个结点的最近公共祖先。最近公共祖先:“ 对于有根树 root 的两个结点 p、q,最近公共祖先表示为一个结点 a,满足 a是 p、q 的祖先且 a的深度尽可能大(一个节点也可以是它自己的祖先)。”如结点6和结点0的最近公共祖先为结点3,结点5和结点4的最近公共祖先为结点5。
在这里插入图片描述

题目一:如果这棵树为二叉搜索树。
二叉搜索树中的结点都是按照顺序排列的,左子树结点的值<根节点<右子树结点的值,我们可以从根节点开始遍历每个结点,如果当前结点大于这两个结点,那么公共祖先一定在当前结点的左子树中,如果当前结点小于这两个结点那么公共祖先一定在当前节点的右子树中。否则当前的这个结点就是我们要找的最近公共祖先。

 TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    
    
        if(root == NULL) return NULL;

        if(root -> val > p-> val && root -> val > q -> val)
        return lowestCommonAncestor(root -> left,p,q);

        if(root -> val < p -> val && root -> val < q -> val)
        return lowestCommonAncestor(root -> right,p,q);

        return root;
    }

题目二:这是一棵普通的二叉树,但是它有指向父亲结点的指针。(除根节点外)
下图中左图红色和绿色的结点为我们所要求最近公共祖先的两个结点。蓝色线为指向父亲结点的指针。在这里插入图片描述
我们如果把从两个当前结点出发一直到根节点的路径抽象出来为两条链表,那么最近公共祖先就是这两条相交链表的第一个相交的结点。

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q){
    
    
	if(root == NULL) return NULL;
	
	stack<TreeNode*>s1;
	stack<TreeNode*>s2;
	TreeNode* p;
	
	while(p -> parent != root){
    
    
		s1.push(p);
		p = p -> parent;
	}
	s1.push(p);
	s1.push(p -> parent);

	while(q -> parent != root){
    
    
		s2.push(q);
		q = q -> parent;
	}
	s2.push(q);
	s2.push(q -> parent);

	while(!s1.empty() && !s2.empty()){
    
    
		if(s1.top() == s2.top()){
    
    
			p = s1.top();
			s1.pop();
			s2.pop();
		}
		else
			break;
	}
	return p;
}

题目三:这是一棵普通的二叉树。
如果这是一棵普通的二叉树,虽然我们没有了指向父节点的指针,但是我们可以沿用题目二的思路,我们从根节点开始获取到达指定结点的路径,然后再转换成寻找链表的相交结点,需要注意的是这次的相交结点会被存在栈底,我们可能需要先弹出一些结点来保证两个栈内元素相等,然后再寻找相交结点。

下述代码中的第一个和第三个函数都比较容易写出来,最核心的代码是如何保存从根节点出发到指定结点的路径,也就是第二个函数。

 TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    
    
        if(root == NULL || p == NULL || q == NULL)
        return NULL;

        stack<TreeNode*>path1;
        GetNodePath(root,p,path1);

        stack<TreeNode*>path2;
        GetNodePath(root,q,path2);

        return GetLastCommonNode(path1,path2);
        
  }
  
bool GetNodePath(TreeNode* root,TreeNode* pNode,stack<TreeNode*> &path){
    
    
        if(root == NULL)
        return false;

        path.push(root); //保存当前遍历到的结点
        
        if(pNode == root)
        return true;

        if(GetNodePath(root -> left,pNode,path))//到当前遍历结点的左子树寻找指定结点
        return true;

        if(GetNodePath(root -> right,pNode,path))//到当前遍历结点的右子树寻找指定结点
        return true;

        path.pop(); //当前结点的左右子树都没有寻找到指定结点,则该结点不可能存在于路径中
        return false;
 }
 
TreeNode* GetLastCommonNode(stack<TreeNode*>&path1,stack<TreeNode*>&path2){
    
    
       int a = path1.size();
       int b = path2.size();

       if(a > b){
    
    
           int c = a-b;
           while(c!= 0){
    
    
               path1.pop();
               c--;
           }
       }

        if(a<b){
    
    
           int c = b-a;
           while(c != 0){
    
    
               path2.pop();
               c--;
           }
       }

       while(!path1.empty() && !path2.empty()){
    
    
           if(path1.top() != path2.top()){
    
    
               path1.pop();
               path2.pop();
           }
           else
           break;
       }
       return path1.top();
  }

おすすめ

転載: blog.csdn.net/scarificed/article/details/120814988