樹(2)--二叉樹的遍歷(非遞迴)+線索二叉樹

fan_rockrock發表於2014-02-15

一:二叉樹的遍歷.

     由於遞迴演算法很簡單,在這裡就不例舉了,主要看一下非遞迴演算法(其實也就是用棧實現,因為遞迴本身就是一種棧)

     1.先序遍歷:

          思想:(1)從根節點依次遍歷當前節點的左子樹,邊遍歷訪問,並且壓入棧

                   (2).再訪問當前棧頂結點的右子樹,然後再返回到(1)執行,直至棧空        

#define maxsize 100
typedef struct
{
    Bitree Elem[maxsize];
    int base,top;
}SqStack;

void PreOrderUnrec(Bitree t)
{
    SqStack s;
    StackInit(s);
    p=t;
    
    while (p!=null || !StackEmpty(s))
    {
        while (p!=null)             //遍歷左子樹
        {
            visite(p->data);
            push(s,p);
            p=p->lchild;       
        }//endwhile
        
        if (!StackEmpty(s))         //通過下一次迴圈中的內嵌while實現右子樹遍歷
        {
            p=pop(s);
            p=p->rchild;        
        }//endif                
    }//endwhile 
    
}//PreOrderUnrec
   2.中序遍歷:

      思想:(1)從根節點遍歷左子樹,邊遍歷邊入棧

               (2)彈出棧頂元素,並訪問,然後訪問當前棧頂的右子樹,回到(1)

#define maxsize 100
typedef struct
{
    Bitree Elem[maxsize];
    int base,top;
}SqStack;

void InOrderUnrec(Bitree t)
{
    SqStack s;
    StackInit(s);
    p=t;
    while (p!=null || !StackEmpty(s))
    {
        while (p!=null)             //遍歷左子樹
        {
            push(s,p);
            p=p->lchild;
        }//endwhile
        
        if (!StackEmpty(s))
        {
            p=pop(s);
            visite(p->data);        //訪問根結點
            p=p->rchild;            //通過下一次迴圈實現右子樹遍歷
        }//endif   
    
    }//endwhile
}//InOrderUnrec
 

3.後序遍歷

在後序遍歷中,要保證左孩子和右孩子都已被訪問並且左孩子在右孩子前訪問才能訪問根結點對於任一結點P,將其入棧,然後沿其左子樹一直往下搜尋,直到搜尋到沒有左孩子的結點,此時該結點出現在棧頂,但是此時不能將其出棧並訪問,因此其右孩子還為被訪問。所以接下來按照相同的規則對其右子樹進行相同的處理,當訪問完其右孩子時,該結點又出現在棧頂,此時可以將其出棧並訪問。這樣就保證了正確的訪問順序。可以看出,在這個過程中,每個結點都兩次出現在棧頂,只有在第二次出現在棧頂時,才能訪問它。因此需要多設定一個變數標識該結點是否是第一次出現在棧頂

while(p!=NULL||!s.empty())
    {
        while(p!=NULL)              //沿左子樹一直往下搜尋,直至出現沒有左子樹的結點 
        {
            BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
            btn->btnode=p;
            btn->isFirst=true;
            s.push(btn);
            p=p->lchild;
        }
        if(!s.empty())
        {
            temp=s.top();
            s.pop();
            if(temp->isFirst==true)     //表示是第一次出現在棧頂 
             {
                temp->isFirst=false;
                s.push(temp);
                p=temp->btnode->rchild;    
            }
            else                        //第二次出現在棧頂 
             {
                cout<<temp->btnode->data<<" ";
                p=NULL;
            }
        }
    }


二.線索二叉樹:

             含有n個結點的二叉樹,一共有2n個指標域,有n+1個處於Null狀態,為了使空間不浪費,可以讓這些空的指標域指向二叉樹各種遍歷的前驅或後繼結點,這樣又可以方便查詢每一個元素,而不必採用遍歷,節省了時間

1.儲存結構:

typedef enum { Link, Thread } PointerThr; 
    // Link==0:指標,Thread==1:線索
typedef struct BiThrNode{
    TElemType data;
    Struct BiThrNode *lchild, *rchild;    // 左右孩子指標
    PointerThr LTag, RTag;   // 左右標誌,當LTag=Thread時,表示線索,為Link時表示指向下一結點
} BiThrNode, *BiThrTree;

1)若結點有左子樹,則lchild指向其左孩子;
       否則, lchild指向其直接前驅(即線索);

2).若結點有右子樹,則rchild指向其右孩子;

     否則,rchild指向其後繼(即線索);

例:


2.線索二叉樹的中序遍歷演算法:

Status IOTraver_T( BiThrTree T,Status (*Visit)(TElemType e) )
{ //T指向頭結點,頭結點的左鏈lchild指向根結點,中序遍歷 //二叉線索樹T的非遞迴演算法,對每個資料元素呼叫函式Visit。
 p = T->lchild;  //p指向根結點     
 while (p != T) {     //空樹或遍歷結束時,p = = T
    while (p->LTag==Link) p = p->lchild;  
    if (!Visit(p->data)) return ERROR;  //訪問其左子樹為空的結點
    while (p->RTag==Thread && p->rchild!=T) 
       { p = p->rchild; Visit(p->data);  } //訪問後繼結點
    p = p->rchild; 
    }
 return OK;  
} // IOTraver_T

3.線索二叉樹的生成演算法:

void InThreading (BiThrTree p)//中序併線索化
 {
    if (p)
    {  
       InThreading( p->lchild );  // 左子樹線索化
        if ( !p->lchild ) 
         { p->LTag=Thread; p->lchild=pre; }  // 前驅線索

        if ( !pre->rchild )
         { pre->RTag=Thread; pre->rchild=p; }  //後繼線索
        pre = p;                         // 保持pre指向p的前驅
      InThreading(p->rchild);      //右子樹線索化
     }
  } // InThreading

Status InorderThreading(BiThrTree  & Thrt, BiThrTree  T)
{ //中序遍歷二叉樹T,並將其中序線索化, Thrt 指向頭結點.
   if ( ! (Thrt = (BiThrTree) malloc ( sizeof (BiThrnode) ) )  exit  ( OVERFLOW ) ;
   Thrt ->LTag = Link;   Thrt ->RTag = Thead;   // 建頭結點
   Thrt ->rchild = Thrt ;                                       //右指標回指
   if ( !T ) Thrt ->lchild = Thrt ;    // 若二叉樹空,則左指標回指
   else {
             Thrt ->lchild = T;     pre = Thrt; //將頭結點與樹相連
             <strong>InThreading(T);  </strong>        // 中序遍歷進行中序線索化,呼叫上面的函式
             pre ->rchild = Thrt;   
             pre ->RTag = Thread;     //最後一個結點線索化
             Thrt ->rchild = pre;
            }
    return OK;
 } // InOrderThreading







相關文章