二叉树非递归遍历和层序遍历(各种版本)

先说下递归遍历吧,特别简单,交换三者的顺序,就能得到三种递归方式。

以先序遍历为例:

void preOrder1(BinTree *root)     //递归前序遍历 
{
    if(root!=NULL)
    {
        cout<<root->data<<" ";
        preOrder1(root->lchild);
        preOrder1(root->rchild);
    }
}

一:先序遍历非递归方式-----其实也可以看做深度优先算法

1、用栈实现

要设置一个栈,来模拟向上回溯的方式。

(1)如果左节点不空,就一直进行“访问该结点,入栈,p指向左子树”

(2)当p指向的结点为空的时候,就开始出栈了,出栈的是p的父节点pp,那么将p = pp->right,使得p指向其兄弟结点,再返回(1)进行递归访问。

(3)每次出栈的结点的右结点肯定没有被访问过,因此让p指向其右节点就行了。

void preOrder2(BinTree *root)     //非递归前序遍历 
{
    stack<BinTree*> s;
    BinTree *p=root;
    while(p!=NULL||!s.empty())
    {
        while(p!=NULL)
        {
            cout<<p->data<<" ";
            s.push(p);
            p=p->lchild;
        }
        if(!s.empty())
        {
            p=s.top();
            s.pop();
            p=p->rchild;
        }
    }
}


2、用栈实现

改进一:在方法一中,我们让根结点入栈,是为了方便找到根结点的右节点。这里我们可以直接让右节点入栈。

改进二:为了省去上面的不规则代码,先while循环,再if判断。我们可以直接先让右结点入栈,再让左结点入栈,这样的话,下次循环开始,就直接出栈就行了。相当于没入栈的时候,让p = p->left。

代码如下:

void preOrder(Node *p) //非递归
{
    if(!p) return;
    stack<Node*> s;
    Node *t;
    s.push(p);

    while(!s.empty()){
        t=s.top();
        cout << t.data << " ";
        s.pop();

        if(t->right) s.push(t->right);
        if(t->left) s.push(t->left);
    }
}

3、不用栈,但需要额外添加父节点指针和访问标记

先判断左孩子的访问标记,再判断右孩子的访问标记。如果左右孩子都被访问过,就让root指向root->parent。

// 先序遍历伪代码:非递归版本,不用栈,增加指向父节点的指针
void preOrder3(TNode* root)
{
    while ( root != NULL ){ // 回溯到根节点时为NULL,退出
        if( !root->bVisited ){   // 判定是否已被访问
            Visit(root);
            root->bVisited = true;
        }
        if ( root->left != NULL && !root->left->bVisited ){      // 访问左子树
            root = root->left;
        }
        else if( root->right != NULL && !root->right->bVisited ){ // 访问右子树
            root = root->right;
        }
        else{   // 回溯
            root = root->parent;
        }
    }
}

这个方法不太好,不能直接找到要被访问的右结点,需要一层一层向上走。也需要了额外的空间来存储父节点指针和访问标记。但有点是没有用到栈。


二:中序遍历非递归方式

1、用栈实现

参照先序遍历的方案一,我们直接将访问的方式,放到出栈的时候,因为这个时候出栈的是父节点。利用这个代码最简洁。

void InOrderTraverse1(BiTree T)   // 中序遍历的非递归    
{    
    if(!T)    
        return ;    
    BiTree curr = T;    // 指向当前要检查的节点    
    stack<BiTree> s;  
    while(curr != NULL || !s.empty())  
    {  
        while(curr != NULL)  
        {  
            s.push(curr);  
            curr = curr->lchild;  
        }//while  
        if(!s.empty())  
        {  
            curr = s.top();  
            s.pop();  
            cout<<curr->data<<"  ";  
            curr = curr->rchild;  
        }  
    }  
}  

2、用栈实现

如果参照先序遍历的方案二的话,就不能实现了,因为那里的栈中只保存了左右子树,没有跟结点信息。导致了没有办法实现中序遍历。

因此三个节点都要入栈,而且入栈的先后顺序必须为:右节点,根节点,左节点。但是,当入栈以后,根节点与其左右子树的节点就分不清楚了。因此必须引入一个标志位,表示 是否已经将该节点的左右子树入栈了。每次入栈时,根节点标志位为true,左右子树标志位为false。

下面用c++来实现,就用到pair类型。

void inOrder(Node *p)
{
    if(!p)
        return;
    stack< pair<Node*,int> > s;
    Node *t;
    int unUsed;

    s.push(make_pair(p,1));
    while(!s.empty()){
        t=s.top().first;
        unUsed = s.top().second;
        s.pop();//退出栈顶结点

        if(unUsed){//如果其左右子树都没有放入栈中
            if(t->right)            //按照右子树,根结点,左子树的顺序放入
                s.push( make_pair(t->right,1) );
            s.push( make_pair(t,0) );   //只有放入根结点的时候,要设置标记为0
            if(t->left)
                s.push( make_pair(t->left,1));
        }
        else printf("%d\n",t->data);
    }
}

3、不用栈

这个方法比较绕,参考下就行

中序遍历的第三个非递归版本:采用指向父节点的指针回溯。这个与先序遍历是非常类似的,不同之处在于,先序遍历只要一遇到节点,那么没有被访问那么立即访问,访问完毕后尝试向左走,如果左孩子补课访问,则尝试右边走,如果左右皆不可访问,则回溯;中序遍历是先尝试向左走,一直到左边不通后访问当前节点,然后尝试向右走,右边不通,则回溯。(这里不通的意思是:节点不为空,且没有被访问过)

// 中序遍历伪代码:非递归版本,不用栈,增加指向父节点的指针
void InOrder3(TNode* root)
{
    while ( root != NULL ) // 回溯到根节点时为NULL,退出
    {
        while ( root->left != NULL && !root->left->bVisited )
        {                  // 沿左子树向下搜索当前子树尚未访问的最左节点           
            root = root->left;
        }
        if ( !root->bVisited )
        {                  // 访问尚未访问的最左节点
            Visit(root);
            root->bVisited=true;
        }
        if ( root->right != NULL && !root->right->bVisited )
        {                  // 遍历当前节点的右子树  
            root = root->right;
        }
        else
        {                 // 回溯至父节点
            root = root->parent;
        }
    }
}


三、后序遍历非递归方式

1、用栈

(1)

void PostOrder_Nonrecursive1(BiTree T)  // 后序遍历的非递归      
{      
    stack<BiTree> S;      
    BiTree curr = T ;           // 指向当前要检查的节点    
    BiTree previsited = NULL;    // 指向前一个被访问的节点    
    while(curr != NULL || !S.empty())  // 栈空时结束      
    {      
        while(curr != NULL)            // 一直向左走直到为空    
        {      
            S.push(curr);      
            curr = curr->lchild;      
        }      
        curr = S.top();    
        // 当前节点的右孩子如果为空或者已经被访问,则访问当前节点    
        if(curr->rchild == NULL || curr->rchild == previsited)      
        {      
            cout<<curr->data<<"  ";      
            previsited = curr;      
            S.pop();      
            curr = NULL;      
        }      
        else    
            curr = curr->rchild;      // 否则访问右孩子    
    }      
}  

(2)

void PostOrder_Nonrecursive(BiTree T)  // 后序遍历的非递归     双栈法    
{      
    stack<BiTree> s1 , s2;      
    BiTree curr ;           // 指向当前要检查的节点    
    s1.push(T);    
    while(!s1.empty())  // 栈空时结束      
    {    
        curr = s1.top();    
        s1.pop();    
        s2.push(curr);    
        if(curr->lchild)    
            s1.push(curr->lchild);    
        if(curr->rchild)    
            s1.push(curr->rchild);    
    }    
    while(!s2.empty())    
    {    
        printf("%c ", s2.top()->data);    
        s2.pop();    
    }    
}

2、用栈

这个参考了中序遍历的第二种方法,入栈的顺序是,根结点、右子树、左子树。

void postOrder(Node *p)
{
    if(!p) return;
    stack<pair<Node*,int> > s;
    Node *t;

    int unUsed;
    s.push(make_pair(p,1));
    while(!s.empty()){
        t=s.top().first;
        unUsed=s.top().second;
        s.pop();

        if(unUsed){
            s.push(make_pair(t,0);

            if(t->right)
                s.push(make_pair(t->right,1));
            if(t->left)
                s.push(make_pair(t->left,1));
        }
        else printf("%d\n",t->data);
    }
}


四、层序遍历

利用队列,很简单

void levelOrderTraverse(const BiTree& T)  
{  
    queue<BiTree> q;  
    BiTree p = NULL;  
      
    if(T)//若根结点非空,则入队列   
    {  
        q.push(T);  
    }  
    while(!q.empty())//队列非空   
    {  
        p = q.front();  
        q.pop();  
        cout<<p->data<<" ";  
        if(p->lchild)//左孩子不空,入队列   
        {  
            q.push(p->lchild);  
        }  
        if(p->rchild)//右孩子不空,入队列   
        {  
            q.push(p->rchild);  
        }  
    }   
}  





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值