数据结构:对于代码的理解要能够用语言去表达出来,那才叫真的理解。
目前对于树的遍历只停留在知道应该怎样去遍历的阶段。
以下所有观点仅是个人看法,欢迎补充和评论,共同进步。
问题一:
树(二叉树)的前序和后序非递归遍历到底有何区别
答:
前序:先根后孩子 后序:先孩子后跟
其实,我们观察代码,可以发现,树的前序和后序(二叉树的前序和中序)的非递归代码可以非常相似,当初刚刚再网上看到这个代码时,其实我也是感到十分地惊讶。
以下代码都是使用了栈,只是一个是顺序栈,一个是链表栈
树的后序遍历的代码,借鉴与二叉树的中序遍历
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 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;
}
}
}
}
问题二:树的后序遍历为什么和对应二叉树前序遍历一致
答:对于树来说,中的概念并不好定义,比如,如果有一个结点有三个孩子,那么按照二叉树的中序,我们又该在什么时候去遍历位于“中间”的根呢。对于用二叉树表示的树来说,其所有孩子就是二叉树的左子树,所以先遍历其左子树就是先遍历了树的孩子,就是树的后序遍历了。