【数据结构】求二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

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

例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

      _______3______
       /              \
    ___5__          ___1__
   /      \        /      \
   6      _2       0       8
         /  \
         7   4
示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点3。
示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点5 和节点4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉树中。
  • 思路分析:

通过对题目的分析,我们发现,如果两个节点是一个在左,一个在右,最近祖先就是root,如果不是一左一右,依旧是递归子问题,两个都是左,就去左子树里面找,两个都在右,就去右子树里面去找。

  • 具体代码如下:


 struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
 };


bool FindNode(struct TreeNode* root,struct TreeNode* node)
{
   if(root==NULL)
   {
       return false; 
   }
   if(root==node)
    {
       return true;
    }
    
    return FindNode(root->left,node)
        || FindNode(root->right,node);
}


struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    if(root==NULL)
    {
        return NULL;
    }
    if(root==p||root==q)
    {
        return root;
    }
    bool pInLeft,pInRight,qInLeft,qInRight;
    if(FindNode(root->left,p))
    {
        pInLeft= true;
        pInRight= false;
    }
    else
    {
        pInRight=true;
        pInLeft= false;
    }
    
     if(FindNode(root->left,q))
    {
        qInLeft= true;
        qInRight= false;
    }
    else
    {
        qInRight=true;
        qInLeft= false;
    }
    if(pInLeft&&qInLeft)
    {
        return lowestCommonAncestor( root->left,p,q);
    }
    else if(pInRight&&qInRight)
    {
       return lowestCommonAncestor( root->right,p,q);
    }
    else
    {
        return root;
    }
}

上面代码使用递归时,时间复杂度为O(N^2),下面代码利用栈,把时间复杂度优化到O(N),具体代码如下:



  struct TreeNode {
     int val;
     struct TreeNode *left;
     struct TreeNode *right;
 };

typedef struct TreeNode* STDataType;
typedef struct Stack
{
	STDataType* _a;
    int _top;//栈顶
	int _capacity;

}Stack;


void StackInit(Stack* ps, int n)
{
	assert(ps);
	ps->_a = (STDataType*)malloc(sizeof(STDataType)*n);
	ps->_top = 0;
	ps->_capacity = n;
}

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_top = ps->_capacity = 0;
}

void StackPush(Stack* ps, STDataType x)//在栈顶入数据
{
	assert(ps);
	if (ps->_top == ps->_capacity)//容量检测
	{
		ps->_a = (STDataType*)realloc(ps->_a, ps->_capacity * 2 * sizeof(STDataType));
		ps->_capacity *= 2;
	}
	ps->_a[ps->_top] = x;
	ps->_top++;

}

void StackPop(Stack* ps)//在栈顶出数据
{
	assert(ps);
	if (ps->_top > 0)
	{
		ps->_top--;
	}
}

STDataType StackTop(Stack* ps)//取出栈顶的数据
{
	assert(ps);
	return ps->_a[ps->_top - 1];
}

int StackSize(Stack* ps)//返回数据个数
{
	assert(ps);
	return ps->_top;//top其实就是链表中的size

}

int StackEmpty(Stack* ps)
{
	assert(ps);
	if (ps->_top == 0)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

void  Find(Stack* st, struct TreeNode* root, struct TreeNode* p)
{
    if(root == NULL)
        return ;
    struct TreeNode* cur = root;
     struct TreeNode* prev;
    while(cur != NULL || StackEmpty(st) != 0)
    {
        while(cur)//遍历左路节点
        {
            
            if(cur->val == p->val)//若是p就是cur,把cur入栈后,直接返回
            {
                StackPush(st,cur);
                return;
            }
            else//遍历左路节点,找p的路径
            {
                StackPush(st, cur);
                cur = cur->left;
            }
        }
        //访问右路节点
        struct TreeNode* top = StackTop(st);  
       
        if(top->right == NULL|| top->right==prev)
        {
             prev=top;
            StackPop(st);
            cur= NULL;
        }
        else
            cur = top->right;
    }
}

struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) 
{
    Stack st_p;
    Stack st_q;
    StackInit(&st_p,10);
    StackInit(&st_q,10);
    if(root==NULL)
        return NULL;
    
    //找p的路径
    Find(&st_p, root, p);
                
      //找q的那条路径
    Find(&st_q, root, q);
    int sz1=StackSize(&st_p);
    int sz2=StackSize(&st_q);  
    int gap=abs(sz1-sz2);                                                                        
    if(sz1<sz2)
    {
        while(gap--)
        {
            StackPop(&st_q);
        }
    }
    else
    {
        while(gap--)
        {
            StackPop(&st_p);
        } 
    }
    //同时pop
    if(sz1 ==0)
        return root;
    struct TreeNode* top;
    while(StackEmpty(&st_p)!=0&&StackEmpty(&st_q)!=0)                                                
    {
        struct TreeNode* top1=StackTop(&st_p);
        struct TreeNode* top2=StackTop(&st_q);
         if(top1->val!=top2->val) 
         {
              StackPop(&st_p);
              StackPop(&st_q);
         }
        else
        {
            top = top1;
            break;
        }
     }      

    return top;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值