最近学习了树,和大家分享一下,有不对的地方欢迎指正。
首先,讲递归遍历前,我们需要有一种递归的思维,一开始可能很难理解,不理解的可以去学习一下经典的汉诺塔。
在我的理解中,递归就是自己调用自己的一种方式,我们需要确定递归结束的条件,以及每一次递归之间的关系。递归的功能非常强大。
前序遍历:
根左右
递归:先判断根结点是否空,空直接返回,否则将根输出,之后寻找根的左子树,以其为新根,继续上述操作,直到所有与当前根结点向对应的左子树都遍历完输出了,就进入最后这个结点的右子树并以其为根,重复递归即可。
void preorder(TreeNode* root)//递归遍历
{
if (root == NULL)
return;
visit(root);
preorder(root->child);
preorder(root->brother);
}
中序遍历:
左根右
递归:依然先判断根是否空,空则返回,否则进入左子树并以其为根,直到来到二叉树的最左侧,输出,然后进入右子树继续操作。
void midtorder(TreeNode* root)//递归
{
if (root == NULL)
return;
postorder(root->child);
visit(root);
postorder(root->brother);
}
后序遍历:
递归:依旧判断是否非空,不空先进入其左子树,左子树遍历完再遍历右子树,最后输出。
void postorder(TreeNode* root)//递归
{
if (root == NULL)
return;
postorder(root->child);
postorder(root->brother);
visit(root);
}
非递归:
这是我做用二叉树的遍历方法遍历树结构的,child相当于左子树,brother相当于右子树
前序:
若当前结点非空,就一直访问其左孩子,输出并入栈,直到来到树的最左侧底部,从栈里抛出栈顶元素,这是如果该结点有右子树,我们就进入右子树并再将其当做根结点遍历到其最左侧,如果右子树是空的,说明这个结点的左右子树都已经遍历完了,因为这个结点已经输出过了,继续进行循环。
void Preorder(TreeNode* root)//非递归遍历
{
if (root == NULL)
return;
Stack* S = InitStack();
TreeNode* p;
p = root;
while (p)
{
visit(p);
Push(S, p);
p = p->child;
}
while (!EmptyStack(S))
{
p = Pop(S);
if (p->brother)
{
p = p->brother;
while (p)
{
visit(p);
Push(S, p);
p = p->child;
}
}
}
}
以下是我用孩子链表法来实现借用二叉树的遍历方式对树进行操作的过程:
void OrderFirst(CTree* tree)
{
ChildPtr* stack[MAX_TREE_SIZE];
int top = -1;
ChildPtr* p;
printf("%c", tree->nodes[tree->r].data);
ChildPtr* root = tree->nodes[tree->r].firstchild;
if (root == NULL)
return;
else
{
top++;
stack[top] = root;
while (top > -1)//栈非空
{
p = stack[top];
top--;
printf("%c", p->child);
if (p->next != NULL)//next对于p来说是兄弟关系,所以先入栈,后输出
{
top++;
stack[top] = p->next;
}
if ((p = search(tree, p->child)) != NULL)//先对p的孩子进行操作
{
top++;
stack[top] = p;
}
}
}
}
中序:
对于树来说,其后序遍历和二叉树的中序遍历是一致的。
思路和前序类似,先遍历至最左侧并沿路进栈,但是不输出,之后将栈顶元素抛出并输出,之后访问其右结点,重复上述操作
void Postorder(TreeNode* root)//非递归
{
if (root == NULL)
return;
Stack* S = InitStack();
TreeNode* p;
p = root->child;
Push(S, root);
while (!EmptyStack(S)||p)
{
while (p)
{
Push(S, p);
p = p->child;
}//这边到了左树的最下面
if (!EmptyStack(S))
{
p = Pop(S);
visit(p);
//if (p->brother)
//{
// p = p->brother;
//}
//else
//p = NULL;//需要把p置空
p = p->brother;
}
}
}
void OrderLast(CTree* tree)
{
ChildPtr* stack[MAX_TREE_SIZE];
int top = -1;
ChildPtr* p;
ChildPtr* root = tree->nodes[tree->r].firstchild;
if (root == NULL)
return;
else
{
p = root;
while (top > -1||p)//栈非空
{
while (p!=NULL)//先对p的孩子进行操作
{
top++;
stack[top] = p;
p = search(tree, p->child);
}
if (top>-1)//next对于p来说是兄弟关系,所以先入栈,后输出
{
p = stack[top];
top--;
printf("%c", p->child);
p = p->next;
}
}
}
printf("%c", tree->nodes[tree->r].data);
}
后序的非递归是最麻烦的,需要判断右子树是否已经遍历过了,需要打标签。
这是参考别人的:
void PostOrder(TreeNode* root)//非递归
{
if (root == NULL)
return;
Stack* S = InitStack();//建立栈
TreeNode* Pcur, * Plast;//当前遍历的结点和前一次遍历的结点
Pcur = root;
Plast = NULL;
while (Pcur)
{
Push(S, Pcur);
Pcur = Pcur->left;
}
//这里我们已经遍历到了左子树最下面的一个元素
//遍历到这里Pcur都是空
while (!EmptyStack(S))
{
Pcur=Pop(S);
if (Pcur->right == NULL || Pcur->right == Plast)//输出结果的条件是要么右子树为空,要么已经遍历过了
{
printf("%d", Pcur->data);
Plast = Pcur;
}
else
{
Push(S, Pcur);//重新入栈,遍历右子树
Pcur = Pcur->right;//进入右子树
while (Pcur)//重新开始遍历左子树
{
Push(S, Pcur);
Pcur = Pcur->left;
}
}
}
}