第二弹 二叉树中各式各样的找祖先问题
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;
}