最近刷了leetcode上的树相关的题,对一些知识点做一下总结。首先就是最基本的三种遍历方式的非递归写法。源代码来自于王道的《数据结构》,个人觉得写的还是很简洁的。
先序遍历
void PreOrderNonRecursion(node *root) {
stack<node*> s;
while (!s.empty() || root) {
if (root) {
printf("%d ", root->data);
s.push(root);
root = root->lchild;
}else {
root = s.top();
s.pop();
root = root->rchild;
}
}
}
中序遍历
void InOrderNonRecursion(node *root) {
stack<node*> s;
while (!s.empty() || root) {
if (root) {
s.push(root);
root = root->lchild;
}else {
root = s.top();
printf("%d ", root->data);
s.pop();
root = root->rchild;
}
}
}
后序遍历
void PostOrderNonRecursion(node *root) {
node* preVist = NULL;
while (!s.empty() || root) {
if (root) {
s.push(root);
root = root->lchild;
}else {
root = s.top();
if (root->rchild != NULL && root->rchild != preVist) {
root = root->rchild;
} else {
s.pop();
printf("%d ", root->data); // 访问一个结点的时候栈中保存的是从根结点到该结点的父结点的所有结点
preVist = root;
root = NULL;
}
}
}
}
后序遍历无疑是最复杂的一个,相比之下先序和中序的逻辑都很相似。而后序要解决的一个额外的问题是当root
为空时,如何判断是从左子树返回的还是从右子树返回的。如果是从左子树返回,则不能直接让栈顶指针出栈。因此我们用preVist
指针来记录我们访问的上一个结点。这样当栈顶结点右孩子不为空且右孩子还没有访问过的时候,就说明是从左指针返回的。