数据结构二叉树 2

第二弹 二叉树中各式各样的找祖先问题

1 寻找某一特定节点值的所有祖先(默认特点节点只有一个)。

  • 第一种方法整体采用层次遍历+递归的思想,一个一个找出目标元素的祖先。这种方法比较简单,不过空间复杂度比较高。
//递归寻找特定节点值的所有祖宗(假设该节点只有一个)
void FindAncestor(BiTree T,char x)
{
    if(!T || T->data==x) //树为空或节点值匹配时作为递归结束条件
        return;
    queue<BiTree> que;
    que.push(T); //根节点入队
    while(!que.empty())
    {
        BiTree quefront = que.front();
        if(quefront->lchild)
        {
            que.push(quefront->lchild); //有左孩子入队
            if(quefront->lchild->data == x) //如果左孩子的值为目标值
            {
                printdata(quefront); //把它本身输出,作为目标节点的祖先
                FindAncestor(T,quefront->data);  //再去递归的寻找该节点的祖先
            }
        }
        if(quefront->rchild)
        {
            que.push(quefront->rchild); //有右孩子入队
            if(quefront->rchild->data == x) //如果右孩子的值为目标值
            {
                printdata(quefront); //把它本身输出,作为目标节点的祖先
                FindAncestor(T,quefront->data);  //再去递归的寻找该节点的祖先
            }
        }
        que.pop(); //进行到这里说明本次判断的节点左右孩子中没有目标节点元素
    }
}
  • 第二种方法是采用非递归+后序遍历的思想。后序遍历顺序为“左右根”,也就是说在判断完左右后才会去谈论根的情况,这个访问顺序用来找祖先很合适。
//非递归寻找特定节点值的所有祖先(假设该节点只有一个)
void FindAncestor(BiTree T,char x)
{
    stack sta[10]; //用于存放后序遍历的结果,栈的大小与树的深度有关
    int top=0; //可以理解为栈顶指针
    while(T || top>0) //栈内还有数据或者节点不为空时循环
    {
        while(T && T->data!=x) //节点不为空且其数据不为目标元素时,把它入栈
        {
            sta[++top].t=T;
            sta[top].tag=0; //标记这个是左孩子被访问
            T=T->lchild; //沿左分支往下
        }  //这一步是在往左走到底,把所有非目标最左节点都入栈,循环若执行完,T=null

        if(T && T->data==x) //这个条件实际上是上面循环的退出条件之一:找到了目标值
        {
            for(int i=1;i<=top;i++)
                printdata(sta[i].t); //将栈内元素全部输出作为祖先
            return; //输出完后就结束了
        }

        while(top!=0 && sta[top].tag==1)
        /*上一次循环将栈顶指针tag置为1表示处理右孩子,而在这个循环
        的前一个循环说明只要处理的节点(如a->rchlid)有左孩子,该
        孩子就会入队,栈顶元素tag就会为0;那么,当sta[top].tag==1
        在这里成立时,说明a->rchild没有左孩子,那么这个节点就访问
        完成(实现了“左右根”的依次处理),且保证了以此栈顶元素为根
        的子树中一定没有目标节点,那就把他出栈表示访问完成*/
            top--;  //退栈

        if(top!=0)
        /*执行到这了且栈非空,说明进行后序遍历找到的第一个元素a(a
        一定没有左孩子),在其前访问过一次的元素中都没有目标元素,
        根据后序遍历的特点“左右根”,此时应该去处理a的右孩子*/
        {
            sta[top].tag=1; //表示栈顶元素,即a,现在在处理右孩子
            T=sta[top].t->rchild;   //沿右分支往下,后续仍然处理左孩子
        }
    }
}

2 寻找指定两个节点的最近公共祖先。(假设p在q的左边)

  • 利用递归的思想解决问题(还可以更完善)
//递归实现两个节点之间最近公共祖先查找
BiTree ReFindPQAncestor(BiTree T,BiTree p,BiTree q)
{
    if(T==p || T==q || !T) //递归的结束条件
        return T;

    BiTree left=ReFindPQAncestor(T->lchild,p,q);
    BiTree right=ReFindPQAncestor(T->rchild,p,q);
    
    /*   
		对于一个根节点:
		其左、右孩子均不为p,q时,说明根节点不会为其公共祖先
		其左孩子为p/q,右孩子不为时,说明其可能的公共祖先为左孩子节点
		其右孩子为p/q,左孩子不为时,说明其可能的公共祖先为右孩子节点
		其左孩子为p/q,右孩子为q/p时,最近公共祖先即为根节点
		
		
	*/
    if(!left && !right) 
        return NULL;
    if(left && !right)
        return left;
    if(!left && right)
        return right;
    if(left && right)
        return T;
}
//这种方法还可以设置bool变量,实时更新p、q是否找到,减少遍历次数
  • 借鉴非递归寻找一个元素的所有祖先的思想,分别用两个栈存放树的祖先以及找到p后p的祖先;找到q时将这两个栈匹配,最先匹配上的即为最近公共。(有疑问)
// p,q指针分别指向二叉树中不同节点,找出其最近公共祖先节点
BiTree FindPQAncestor(BiTree T,BiTree p,BiTree q) //设p在q左边,即后序遍历中p先被访问到
{
    stack sta[10];
    stack sta_p[10];
    int top=0,top_p;

    while( T || top>0)
    {
        while(T)
        {
            sta[++top].t=T;
            sta[top].tag=0;
            T=T->lchild;
        }//沿左分支往下
        while(top!=0 && sta[top].tag==1)
        {
            if(sta[top].t==p)
            //找到了p节点,把p节点栈中的祖先都放到另一个栈中,方便等下比较
            {
                for(int i=1;i<=top;i++)
                {
                    sta_p[i]=sta[i];
                    top_p=top;
                }
            }
            if(sta[top].t==q)
            //找到了q节点,开始比较两个栈最近的相同元素
            {
                for(int i=top;i>0;i--)
                {
                    for(int j=top_p;j>0;j--)
                    {
                        if(sta[i].t==sta_p[j].t)
                            return sta[i].t;
                    }
                }
            }
            top--; //退栈
        }
        if(top!=0)
        {
            sta[top].tag=1;
            T=sta[top].t->rchild; //沿右分支往下
        }
    }
    return NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值