目录
遍历的基本概念-必读
二叉树由三个元素组成,分别是根节点D,左子树L,右子树R。理论上存在六种遍历方法,分别是:DLR(先序
)、LDR(中序
)、LRD(后序
)、DRL、RDL、RLD。实际中只有前三种遍历算法,特点是:
- L在R之前
- 根据D的前、中、后三种位置分为三种
遍历可以借助递归调用实现,方法简单便捷,本文不再介绍。
本文利用栈实现遍历,栈的特点:先进后出。利用栈主要是将树的关联性存储在栈中,通过栈可以返回到正确的结点位置处,实现对树的整体遍历。
遍历示意图-必看
图中虚线箭头所示是这三种遍历的递归执行过程,图中三角形、圆形、方形
分别是先序、中序、后序
在遍历过程中依次访问结点输出的信息。
先序:-*abc
中序:a*b-c
后序:ab*c-
先序遍历:先打印父结点,然后遍历左结点,再遍历右结点。
中序遍历:先打印最左的结点,然后打印父节点,再遍历右结点。
后序遍历:先打印最左结点,再打印兄弟右结点,最后打印父结点。
先序遍历
先序遍历:先打印父结点,然后遍历左结点,再遍历右结点。
思路:
- 获得栈顶结点,就打印数值,实际就是再打印父节点。然后先入栈右结点,再入栈左结点。
这样深度越低的右结点就越在栈底。我们保留了树的关联信息。
深度:树结构中的层数,根是第一层,深度为1,以此类推)
/BitTree:结点指针
void preOrderTraverse(BitTree Tree){
stack<BitTree> st;
st.push(Tree);
BitTree p ;
while(!st.empty()){
//弹出栈顶
p = st.top();st.pop();
cout << p->data;
//先入栈右结点
if(p->right){
st.push(p->right);
}
//先入栈左结点
if(p->left){
st.push(p->left);
}
}
}
中序遍历
中序遍历:先打印最左的结点,然后打印父节点,再遍历右结点。
思路:
- 遍历到最左子树
- 某结点若左子树为空,则退栈,输出此节点的数值
- 某结点若右子树为空,表明当前层遍历完毕(当前层指的是父结点,左子树,右子树),则继续退栈
- 隐晦信息:能访问右子树则左子树和父节点一定已经访问结束。故第三步成立。
void InOrderTraverse(BitTree Tree){
stack<BitTree> st;
BitTree p = Tree,p1;
while(p || !st.empty()){
if (p){
st.push(p);
p = p->left;
}
else{
p1 = st.top();st.pop();
cout << p1->data;
p = p1->right;
}
}
}
后序遍历
后序遍历:先打印最左结点,再打印兄弟右结点,最后打印父结点。
后序遍历可以只用一个栈实现,但需要一个参考量flag,flag指向最近访问的结点:
思路:
- 若是左右子树都未访问,才入栈左子树。
- 若是左子树已访问,右子树未访问,才进入入栈右子树。
- 不满足上述信息代表左右子树都已访问,则出栈打印当前结点信息。
隐晦信息
- 首先能访问到最左,同中序遍历。
- 左右都已访问,才访问父结点。
void InOrderTraverse(BitTree Tree){
stack<BitTree> st;
BitTree p;
st.push(Tree);
while(!st.empty()){
p = st.top();
if (p->left and p->left != flag and p->right != right){
st.push(p->left)
}else if(p->right and p->right != flag ){
st.push(p->left)
}else{
p = st.top();st.pop();
cout << p.data;
flag = h;
}
}
}