二叉树(4)非递归法遍历二叉树      1.非递归前序遍历 2.非递归中序遍历 3.非递归后序遍历 4.非递归层序遍历

      首先递归法是操作二叉树的首选方法。非递归法也分前序、中序、后序三种。后序比较特殊,我们在二叉树的节点中设置了成员变量flag,约定创建节点时候初始化为false。定义如下:


typedef struct BinaryTreeNode  
{  
    int data;  
    BinaryTreeNode * leftchild;  
    BinaryTreeNode * rightchild; 
    bool flag;//一定初始化为0
}Node,*NodePoint;

1.非递归前序遍历

void NonRecursivePreOrder(NodePoint Root)
{
    stack<NodePoint> s;    
    NodePoint p = Root;
    while(p)
    {
      cout<<p ->data<<"  ";  
      if(p->rightchild)  
	 s.push(p->rightchild);//注意此处的顺序,先压入右孩子再压入左孩子
      if(p->leftchild)
         s.push(p->leftchild);
      if(s.empty()) 
         break;
      else 
        {p = s.top();s.pop();}
    }
}


         前序=根左右。对于任一个节点,先输出节点值,然后再操作左子树,因此右子树先压入stack。紧接着的是弹出top顶端的指针进行操作,若上一步左子树为空,则弹出的指针指向右子树。如果上一级的左右孩子均为空,则弹出的为指向上2级的右子树的指针,以此完成递归操作。这里需要控制的压入stack的当前子树先后,大范围来说,操作完某一个节点的左子树,再操作其右子树。

          使用栈的非递归前序遍历的另外一种形式:

void NonRecursivePreOrder(NodePoint Root)
{
    stack<NodePoint> s;    
  
    while(Root||!s.empty())
    {  
	if(Root){
           cout<<Root->data;
           s.push(Root);
           Root=Root->leftchild;         
        }
        else{
          Root=s.top();
          s.pop();
          Root=Root->rightchild;
        }
    }
}



2.非递归中序遍历

void NonRecursiveInOrder(NodePoint Root)
{
    stack<NodePoint> s;
    NodePoint p = Root;
    while(p||!s.empty())
    {
        while(p) 
           {s.push(p);           
            p = p->leftchild;} 
        p = s.top();
        s.pop();//弹出的根节点
        cout<<p ->data<<" ";
        p = p->rightchild; }
}


               中序=左根右。首先找到某个节点的最左边的节点,以此控制向后退和向右转。找到最左边的节点后,接着做的是判断其右子树是否空。倘若为空,则弹出指向上两级节点的指针。倘若不为空,则遍历以右孩子为根节点的子树的最左边的节点。节点一旦输出,马上弹出。

      另外一种非递归中序遍历:

void NonRecursiveInOrder(NodePoint Root)
{
    stack<NodePoint> s;    
    while(Root||!s.empty())
    {  
	   if(Root){           
	        s.push(Root);
            Root=Root->leftchild; }
	    else{ 
	        Root=s.top(); 
	        s.pop(); 
            cout<<Root->data; 
	        Root=Root->rightchild; } 
	    }
	}
}

3.非递归后序遍历

void NonRecursivePostOrder(NodePoint Root)
{
     stack<NodePoint>  s;
     NodePoint p=Root;
     while(p||!s.empty()){
	while(p){
          s.push(p);
          p=p->leftchild;//遍历至最左根节点
	}
        p=s.top();
        if(p->flag||!p->rightchild)//p尚且不能输出,或者右子树为NULL
          {cout<<p->data<<" ";
	   s.pop();
	   p=NULL;}//先赋值,仅仅在满足条件的时候,再将元素弹出stack,此处弹出的为子‘根’节点
       else
      	  {p->flag=true;
	  p=p->rightchild;}//顺序不能换 
   }
}





           后序=左右根。先输出左子树。倘若右子树为空,或者后退的时候已经显示遍历过了,那么输出当前节点数据并弹出stack即可;倘若不为空,则对以右子树为根节点的子树遍历左节点压栈。

4.非递归层序遍历

       需要使用队列,满足先遍历的节点。且子节点也要先遍历。

void NonRecursivelevelOrder(Tree Root){
	queue<Node> q;
	Tree temp;
	q.push(Root);
	while(!q.empty()){
	  temp=q.front();
	  q.pop();
	  cout<<Root->data;
         if(Root->leftchild)
	    q.push(Root->leftchild);
	 if(Root->rightchild)
	   q.push(Root->rightchild);
	}
}



     非递归算法的时间复杂度和空间复杂度都是O(n)。二叉树的遍历算法中,每个算法都访问了节点的每一个域,其每个域仅被访问一次,故时间复杂度为O(n),三种非递归遍历中,栈的最大空间为深度k倍的节点元素存储空间,最坏的情况,空间复杂度也为O(n)。层序遍历时,队列的最大长度不会超过二叉树中某一层最多的节点数,故最坏的情况空间复杂度也是O(n)。

    不论是那种遍历方式,仅仅考虑叶子节点,排序方式都是从左到右。





部分参考修改自:二叉树常用算法总结