先看一段代码(本以为看了代码就能理解怎么写这么个循环遍历):
Status InOrderTraverse(BiTree T, Status ( *Visit)(TElemType e)){
InitStack(S); p = T;
while(p || !StackEmpty(S)){
if (p) { Push(S, p); p = p->lchild; }
else {
Pop(S, p);
if(!Visit(p->data)) return ERROR;
p = p->rchild;
}
}
return OK
}
应该是一段很简洁高效的C/C++代码,但是看得我瞬间晕倒。我根本没看懂是怎么回事情。
再回头想一想什么才算利用栈写循环遍历呢?
首先,作为中序遍历,必然总要走所有的左子树。
Stack< Node > stack = new Stack< Node >();
Node n = this.root;
while (n != null)
{
stack.Push(n);
n = n.LChild;
}
这一步必然是这样的。
其次,当栈找完所有的左子树的时候,就要打印出该节点的值(这个没问题)。这个时候其实相当于在访问中间节点,因为如果把空的左节点看成非空,那么其实就是打印一个空值,也就是说正真的打印其实是中节点。那么此时只需要遍历该节点的右子树就可以了。
while (stack.Count != 0)
{
n = stack.Pop();
_visit(n);
stack.Push(n);
}
但是,光这段代码不可能继续去遍历这颗右子树啊!怎么办?这个时候看看原来的C/C++代码,有点感觉了。只要把这两个循环结合在一起就可以解决这个问题。灵感来自最后那句stack.Push,只要重新复制n就解决了,不过这其实是一个揣测的操作,我是一点理论根据也没有,就算对了,我感觉依然没有完全搞懂的样子。
while (n != null || stack.Count != 0)
{
if (n != null)
{
stack.Push(n);
n = n.LChild;
}
else
{
n = stack.Pop();
_visit(n);
n = n.RChild;
}
}
No comments:
Post a Comment