打印值为x的节点的所有祖先
在二叉树中查找值为x的节点,试编写算法打印值为x的节点的所有祖先,假设值为x的节点不多于一个
算法思想
采用非递归后序遍历,最后访问根节点,访问到值为x的节点时,栈中所有元素均为该节点的祖先,依次出栈打印即可。
假设x是5,T入栈,沿左向下遍历,直到T指向NULL
依次将2,4也入栈,tag置为0,表示沿左子树访问
如果top不为0,将栈顶的节点的右孩子,并将tag置为1
由于4为NULL,没有右子树,将4出栈,接着访问2
将5入栈,找到值为x的节点,从栈底开始输出栈内的元素
typedef struct
{
BTNode root;
// tag=0表示左子女被访问,tag=1表示右子女被访问
int tag;
}stack;
// 在二叉树T中,查找值为x的节点,并打印其所有祖先
void Search(BTNode T, ElemType x)
{
// 栈的容量足够大
ST s[];
top = 0;
while (T != NULL || top > 0)
{
// 节点入栈
while (T != NULL && T->data != x)
{
s[++top].root = T;
s[top].tag = 0;
// 沿左分支向下
T = T->left;
}
if (T != NULL && T->data == x)
{
// 找到x
printf("所查节点的所有祖先节点的值为:\n");
for (i = 1; i <= top; i++)
{
// 遍历输出栈中的节点的值
printf("%d", s[i].root->data);
}
exit(1);
}
while (top != 0 && s[top].tag == 1)
// 退栈
top--;
if (top != 0)
{
s[top].tag = 1;
// 沿右分支向下遍历
T = s[top].root->right;
}
}
}
- 栈的结构定义:
- 使用一个栈s来存储节点信息,每个元素包含:
- 节点指针BTNode t。
- 标志位tag:
- tag = 0:表示从左子树访问。
- tag = 1:表示从右子树访问。
- 使用一个栈s来存储节点信息,每个元素包含:
- 从根节点开始遍历:
- 遇到一个节点时,将其入栈并沿左子树向下遍历。
- 找到目标节点:
- 如果当前节点 T 的值为 x,遍历栈中的所有节点,打印其值(这些节点即为目标节点的祖先)。
- 回溯过程:
- 如果当前节点没有右子树或者右子树已经被访问过(tag == 1),弹出栈顶节点。
- 如果当前节点的右子树尚未访问,访问右子树并继续遍历。
- 结束条件:
- 找到目标节点后直接退出程序。
- 如果遍历完整棵树未找到目标节点,循环退出。
注意点
- 栈的实现
- 栈的容量应足够大以存储整棵树的所有节点(通常不会超过树的深度)。
- 栈指针top用于操作栈。
- 边界处理:
- 如果T为NULL或遍历完成后未找到x,则程序结束。
- 遍历方式:
- 代码中采用非递归后序遍历:
- 沿左子树向下直到找到目标节点或遍历完成。
- 回溯并访问右子树,直至找到目标节点。
- 代码中采用非递归后序遍历:
最近公共祖先节点
设一棵二叉树的结点结构为(LLINK,INFO,RLINK),ROOT为指向该二叉树根结点的指针,P和分别为指向该二叉树中任意两个结点的指针,试编写算法ANCESTOR(ROOT,P,a,r),找到p和q的最近公共祖先结点r。
12
算法思想
后序遍历最后访问根节点,在递归算法中,根是压在栈底的。设p在q的左边
采用后序非递归算法,栈中存放二叉树节点的指针,当访问到某节点时,栈中所有元素均为该节点的祖先。后序遍历必然先遍历到节点p,栈中元素均为p的祖先。
先将栈复制到另一个辅助栈中。继续遍历到节点q时,将栈中元素从栈顶开始逐个到辅助栈中去匹配,第一个匹配的元素就是节点p和q的最近公共祖先。
typedef struct
{
BTNode root;
//tag=0表示左子女已被访问,tag=1表示右子女已被访问
int tag;
}stack;
// 栈,容量足够大
ST s[], s1[];
// 求二叉树中p和q指向节点的最近公共节点
BTNode Ancestor(BTNode root, BTNode *p, BTNode *q)
{
top = 0; top1 = 0; BTNode cur = root;
while (cur != NULL || top > 0)
{
// 沿左分支向下
while (cur != NULL)
{
s[++top].root = cur;
s[top].tag = 0;
cur = cur->left;
}
// 假定p在q左侧,遇到p时,栈中元素均为p的祖先
while (top != 0 && s[top].tag == 1)
{
// 将栈s中的元素转入辅助栈s1中保存
if (s[top].root == p)
{
for (i = 1; i <= top; i++)
{
s1[i] = s[i];
}
top1 = top;
}
// 找到q节点
if (s[top].root == q)
{
// 将栈s中元素和s1中的去匹配
for (i = top; i > 0; i--)
{
for (j = top1; j > 0; j--)
{
// 找到p和q的最近公共祖先
if (s1[j].root == s[i].root)
return s[i].root;
}
}
}
// 退栈
top--;
}
// 沿右分支向下遍历
if (top != 0)
{
s[top].tag = 1;
cur = s[top].root->right;
}
}
// p和q无公共祖先
return NULL;
}
- 核心思想:
- 使用一个栈 s 进行非递归遍历,记录遍历过程中每个节点的路径。
- 当遇到节点 p 时,将当前路径保存到辅助栈 s1。
- 当遇到节点 q 时,将当前路径与 s1 中的路径匹配,找到最近的公共祖先节点。
- 主要流程:
- 沿左分支向下遍历:不断压入节点及其状态(左子女是否访问)。
- 遇到节点 p:保存路径到 s1。
- 遇到节点 q:将当前路径与 s1 中的路径对比,找到最近的公共祖先。
- 右子树遍历和回溯:继续沿右子树遍历,或回溯到父节点。