二叉树的遍历

来源:互联网 发布:微信支付java案例demo 编辑:程序博客网 时间:2024/05/16 10:21

一、先定义二叉树的数据结构

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);        }    }}
0 0