您的位置:首页 > 理论基础 > 数据结构算法

二叉树的遍历

2015-11-28 00:39 190 查看
一、先定义二叉树的数据结构

typedef struct TreeNode *BinTree;

struct TreeNode
{
ElementType data;
BinTree left;
BinTree right;
};


二、基于DFS的递归遍历
       1. 先序遍历(PreOrder)
void PreOrder(BinTree T)
{
if(!T) return;
Visit(T);
PreOrder(T->left);
PreOrder(T->right);
}


      2. 中序遍历(InOrder)
void InOrder(BinTree T)
{
if(!T) return;
InOrder(T->left);
Visit(T);
InOrder(T->right);
}


      3. 后序遍历(PostOrder)
void PostOrder(BinTree T)
{
if(!T) return;
InOrder(T->left);
InOrder(T->right);
Visit(T);
}


      Summarize: 这三种遍历过程,经过结点的路线是一样的,只是访问各结点的时机不同。

三、利用堆栈实现非递归遍历
       借助堆栈实现实际上就是模拟递归函数调用时的入栈和出栈过程,先序遍历是结点第一次入栈即访问,然后分别对该结点的左右子树遍历;中序遍历是当结点出栈时,表明刚完成对该结点左子树的访问,这时访问该结点。
      1. 先序非递归
          1) 遇到一个结点,访问后入栈,并遍历其左子树;
          2) 当左子树遍历完后,从栈顶弹出一个结点;
          3) 按该结点的右指针遍历其右子树;
          4) 当p为NULL且栈空时,整个遍历过程结束。
void PreOrder(BinTree T)
{
stack<BinTree> S;
BinTree p = T;

while(p || !S.empty())
{
while(p){
Visit(p);
S.push(p);
p = p->left;
}
if(!S.empty()){
p = S.top();
S.pop();
p = p->right;
}
}
}


      2. 中序非递归
          同先序基本一样,不同之处在于访问结点的时机,是在结点出栈后访问。
void InOrder(BinTree T)
{
stack<BinTree> S;
BinTree p = T;

while(p || !S.empty())
{
while(p){
S.push(p);
p = p->left;
}
if(!S.empty()){
p = S.top();
Visit(p);
S.pop();
p = p->right;
}
}
}


      3. 后序非递归
          与前两个非递归的过程不同,后序遍历必须在左右子树均访问后才访问根结点,关键是要确定什么时候右子树遍历结束。一种思路是在某一结点的左子树遍历完后将其再次入栈,等到再次出栈就说明其右子树已经遍历结束,这时候就可以访问该结点了。也就是说,在整个后序非递归过程中,每个结点出入栈两次,第一次出栈说明其左子树遍历完成,第二次出栈说明其右子树也遍历完成,而为了区分是第几次出栈,每个结点需要添加一个变量标记。
void postOrder(BinTree T)
{
if(!T) return;

stack<BinTree> S;
BinTree p = T;
while(p || !S.empty())
{
if(p){
S.push(p);
p = p->left;
}
else{
p = S.top();
S.pop();
p->flag++;
if(p->flag == 2){
Visit(p);
p = NULL;
}
else{
S.push(p);
p = p->right;
}
}
}
}


四、基于BFS的层序遍历
       1. 层序遍历用队列实现,从根节点开始,首先将根节点入队,然后执行循环:结点出队并访问,左右结点入队,直到队列为空,形成按广度优先搜索的遍历方式。
       基本过程:
       1) 根节点入队;
       2) 从队列中出队一个结点;
       3) 访问该结点;
       4) 如果该结点左右子结点非空,依次入队;
       5) 队列为空,遍历结束。
void LevelOrder(BinTree T)
{
if(!T) return;

queue<BinTree> Q;
BinTree p = T;
Q.push(p);
while(!Q.empty())
{
p = Q.front();
Visit(p);
Q.pop();

if(p->left)
Q.push(p->left);
if(p->right)
Q.push(p->right);
}
}


     2. 有意思的是,基于层序遍历的思想也可以实现先序、中序和后序三种遍历。只要将队列改为栈,同时改变结点的的入栈顺序即可。
        1) 先序:入栈顺序为右结点再左结点。
void LevelPreOrder(BinTree T)
{
if(!T) return;
stack<BinTree> S;
BinTree p = T;
S.push(p);
while(!S.empty())
{
p = S.top();
Visit(p);
S.pop();

if(p->right)
S.push(p->right);
if(p->left)
S.push(p->left);
}
}


       2) 中序:为了知道左右子树什么时候遍历结束,结点需要出入栈两次,第一次出栈后在左右结点间再次入栈,第二次出栈时即可访问该结点。
           入栈顺序:右结点->当前结点->左结点
void LevelInOrder(BinTree T)
{
if(!T) return;
stack<BinTree> S;
BinTree p = T;
S.push(p);
while(!S.empty())
{
p = S.top();
p->flag++;
S.pop();
if(p->flag == 2){
Visit(p);
}
else{
if(p->right)
S.push(p->right);
S.push(p);
if(p->left)
S.push(p->left);
}
}
}


        3) 后序:与中序类似,结点第一次出栈后在左右结点之前再次入栈,第二次出栈后访问。入栈顺序:当前结点->右结点->左结点
void LevelPostOrder(BinTree T)
{
if(!T) return;
stack<BinTree> S;
BinTree p = T;
S.push(p);
while(!S.empty())
{
p = S.top();
p->flag++;
S.pop();
if(p->flag == 2){
Visit(p);
}
else{
S.push(p);
if(p->right)
S.push(p->right);
if(p->left)
S.push(p->left);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息