1、中序遍历
(1)递归中序遍历
void OrderTree(BinTree T){
if(T){
cout<<T->key;
OrderTree(T->lchild);
OrderTree(T->rchild);
}
}
(2)非递归中叙遍历
根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。
算法步骤:
对于任一结点P:
1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
3)直到P为NULL并且栈为空则遍历结束。
void OrderTree(BinTree T){
stack<Binree> s;
BinTree p=T;
while(p!=NULL||!s.empty()) { //如果当前结点不为空或栈不为空
while(p!=NULL){
//如果当前结点不为空则将当前结点压入栈,循环访问当前结点的左孩子,直到当前结点为NULL
//当前结点为NULL,说明他的父节点左孩子为空,应该输出他的的父结点了。所以跳出循环
s.push(p);
p=p->lchild;
}
if(!s.empty()){
//如果栈不为空进入
p=s.top(); //取其栈顶结点
cout<<p->key; //输出
s.pop(); //弹出栈顶结点
p=p->rchild; //访问栈顶结点的右孩子
}
}
}
上面的算法是先当前结点不为空就先压入栈,然后循环访问他的左孩子。直到当前结点为空。
然后从栈中取栈顶结点并弹出该结点,输出该结点,并将当前结点指向该结点的右孩子。
然后对当前结点再使用上面的套路。
2、先序遍历
(1)递归先序遍历
void OrderTree(BinTree T){
if(T){
cout<<T->key;
OrderTree(T->lchild);
OrderTree(T->rchild);
}
}
(2)非递归先序遍历
void OrderTree(BinTree T){
stack<Binree> s;
BinTree p=T;
while(p!=NULL||!s.empty()) { //如果当前结点不为空或栈不为空
while(p!=NULL){
//如果当前结点不为空,则输当前结点,并将当前结点压入栈中
//并且循环访问当前结点的左孩子,直到为空。
cout<<p->key; //输出
s.push(p); //
p=p->lchild;
}
if(!s.empty()){
//如果栈不为空进入
p=s.top(); //取其栈顶结点
s.pop(); //弹出栈顶结点
p=p->rchild; //访问栈顶结点的右孩子
}
}
}
我们可以发现,如果去掉输出语句,先序和后序的非递归遍历是完全相同。
void OrderTree(BinTree T){
stack<Binree> s;
BinTree p=T;
while(p!=NULL||!s.empty()) {
while(p!=NULL){
s.push(p);
p=p->lchild;
}
if(!s.empty()){
p=s.top();
s.pop();
p=p->rchild;
}
}
}
这是因为去掉输出函数后都是对二叉树进行访问,只是输出的时候不一样就形成了不同的遍历。
先序遍历是在第一次访问的时候就输出,中序遍历是在其左孩子访问完之后再输出。
3、后序遍历
(1)递归后序遍历
(2)非递归后序遍历
非递归后序遍历是三种非递归遍历里面最难的。