二叉樹的遍歷-先序、中序、後序、層次

       二叉樹的遍歷,就是指按某條搜索路徑訪問樹中的每個結點,使得每個結點均被訪問一次,而且僅被訪問一次。

       遍歷一棵二叉樹便要決定對根結點N,左子樹L和右子樹R的訪問順序。按照先遍歷左子樹再遍歷右子樹的原則,常見的遍歷次序有先序(NLR)、中序(LNR)、後序(LRN)三種遍歷算法,其中的序是指根結點在何時被訪問。

一、先序遍歷

先序遍歷(PreOrder)的操作過程如下:

若二叉樹爲空,則什麼也不做;否則:

1)訪問根結點;

2)先序遍歷左子樹;

3)先序遍歷右子樹。

 

對應的遞歸算法如下:      

void PreOrder(BiTree T)
{
    if(T!=NULL)
    {
        visit(T);                      //訪問根結點
        PreOrder(T->lchild);           //遞歸遍歷左子樹
        PreOrder(T->rchild);           //遞歸遍歷右子樹
    }
}

 

上圖先序遍歷得到的結果爲1,2,4,6,3,5。

注意:這裏提供的是僞代碼的形式,主要是明白遍歷思想,具體要結合實際問題實際分析,下同。

 

二、中序遍歷

中序遍歷(InOrder)的操作過程如下:

若二叉樹爲空,則什麼也不做;否則,

1)中序遍歷左子樹;

2)訪問根結點;

3)中序遍歷右子樹。

 

對應的遞歸算法如下:    

void InOrder(BiTree T)
{
    if(T!=NULL)
    {
        InOrder(T->lchild);  //遞歸遍歷左子樹
        visit(T);            //訪問根結點
        InOrder(T->rchild);  //遞歸遍歷右子樹
    }
}

又是這個圖,其中序遍歷得到的結果爲2,6,4,1,3,5。

這裏可能不太好理解,分步列一下:

(1)進入結點1左子樹;

(2)進入結點2左子樹,爲空,結點2左子樹訪問完畢,返回結點2;

(3)訪問結點2,訪問順序爲2;

(4)進入結點2右子樹;

(5)進入結點4左子樹;

(6)進入結點6左子樹,爲空,返回結點6;

(7)訪問結點6,訪問順序爲2,6;

(8)進入結點6右子樹,爲空,返回結點6,結點4左子樹訪問完畢,返回結點4;

(9)訪問結點4,訪問順序爲2,6,4;

(10)進入結點4右子樹,爲空,返回結點4,結點2右子樹訪問完畢,返回結點1;

(11)訪問根結點1,訪問順序爲2,6,4,1;

右子樹的訪問思路同上。

 

三、後序遍歷

後序遍歷(PostOrder)的操作過程如下:

若二叉樹爲空,則什麼也不做;否則,

1)後序遍歷左子樹;

2)後序遍歷右子樹;

3)訪問根結點。

 

對應的遞歸算法如下:    

void PostOrder(BiTree T)
{
    if(T!=NULL)
    {
        PostOrder(T->lchild);         //遞歸遍歷左子樹
        PostOrder(T->rchild);         //遞歸遍歷右子樹
        visit(T);                     //訪問根結點
    }
}

還是這個圖,後序遍歷得到的結果爲6,4,2,5,3,1

這個應該還是比較好理解的。

       三種遍歷算法中,遞歸遍歷左、右子樹的順序都是固定的,只是訪問根結點的順序不同。不管採用哪種遍歷算法,每個結點都訪問一次且僅訪問一次,故時間複雜度都是O(n)。

 

四、遞歸算法和非遞歸算法的轉換

       藉助棧,我們可以將二叉樹的遞歸遍歷算法轉換爲非遞歸算法。以中序遍歷爲例,先掃描根結點的所有左結點並將它們一一進棧,然後出棧一個結點*p,訪問它,然後掃描該結點的右孩子結點,將其進棧,再掃描該右孩子結點的所有左結點並一一進棧,如此繼續,直到棧空爲止。

       中序遍歷的非遞歸算法如下:   

void InOrder2(BiTree T)
{                                //二叉樹中序遍歷的非遞歸算法需要藉助一個棧
    InitStack(S);                       
    BiTree p=T;                  //初始化棧;p是遍歷指針
    while(p||!IsEmpty(S))        //棧不空或p不空時循環
    {
        if(P)                    //根指針進棧,遍歷左子樹
        {
            Push(S,p);           //每遇到非空二叉樹先向左走
            p=p->lchild;        
        }
        else                     //根指針退棧,訪問根結點,遍歷右子樹
        {
            Pop(S,p);            //退棧,訪問根結點
            visit(p);                
            p=p->rchild;         //再向右子樹走
        }
    }
}

非遞歸算法的效率肯定比遞歸算法的效率高滴!

 

 

五、層次遍歷

       層次遍歷顧名思義就是對每一層都進行遍歷,如下圖所示:

 

       要進行層次遍歷需要一個隊列。先將二叉樹根結點入隊,然後出隊,訪問該結點,若它有左子樹,則將左子樹根結點入隊;若它有右子樹,則將右子樹根結點入隊。然後出隊,對出隊結點訪問,如此反覆,直到隊列空。

       二叉樹層次遍歷算法如下:     

void LevelOrder(BiTree T)
{
    InitQueue(Q);                     //初始化輔助隊列
    BiTree p;       
    EnQueue(Q,T);                     //將根結點入隊
    while(!IsEmpty(Q))                //隊列不空循環
    {
        DeQueue(Q,p);                 //隊頭元素出隊
        visit(p);                     //訪問當前p所指向結點
        if(p->lchild!=NULL)
            EnQueue(Q,p->lchild);     //左子樹不空,則左子樹入隊列
        if(p->rchild!=NULL)
            EnQueue(Q,p->rchild);     //右子樹不空,則右子樹入隊列
    }
}

 

六、由遍歷序列構造二叉樹

       由二叉樹的先序序列和中序序列可以唯一地確定一棵二叉樹。

       由二叉樹的後序序列和中序序列也可以唯一地確定一棵二叉樹。

       由二叉樹的層次序列和中序序列也可以唯一地確定一棵二叉樹。

       這告訴我們一個道理:想要利用遍歷序列構造二叉樹,中序序列必不可少!!

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章