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

来源:互联网 发布:有哪些的网络推广方法 编辑:程序博客网 时间:2024/05/22 03:14

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

以先序遍历为例:

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 0
原创粉丝点击