中序線索二叉樹

  雖說對於二叉樹的遍歷操作來說非遞歸法使用用戶自定義的棧來代替遞歸使用時的系統棧,可以得到不小的效率提升,但將二叉樹線索化時能將用戶棧也省略掉進一步提高了效率。

  對於二叉樹的鏈表結構,n個結點的二叉樹有n+1個空鏈域(每個葉節點都有兩個空鏈域),而線索二叉樹就把這些空鏈域有效的利用了起來,在一般的二叉樹中,我們只知道每個結點的左右孩子,並不知道某個結點在某種遍歷方式下的直接前驅和直接後繼,如果能夠知道前驅和後繼信息,就可以把二叉樹看作一個鏈表結構,從而可以像遍歷鏈表那樣來遍歷二叉樹,進而提高效率。這對經常需要進行遍歷操作的二叉樹而言,無疑是很有用的。

  (1)中序線索二叉樹的構造

  線索二叉樹的結點結構如下:

lchild ltag data rtag rchild

  在二叉樹線索化的過程中會把原有的空指針利用起來作爲尋找當前結點的前驅或者後繼的線索,但是這樣的話,線索和樹中原有指向孩子結點的指針分不清,於是給左右孩子結點添加標記(itag、rtag),即:

  如果ltag=0,表示lchild爲指向孩子結點的指針,如果ltag=1,則表示lchild爲線索,指向結點的前驅;

  如果rtag=0,表示rchild爲指向孩子結點的指針,如果rtag=1,則表示rchild爲線索,指向結點的後繼;

  結點定義如下:

typedef struct TBTNode{
    char data;
    int ltag,rtag;
    struct TBTNode *lchild;
    struct TBTNode *rchild;
}TBTNode;

  線索化的規則是:左線索指針指向當前結點在中序遍歷序列中的前驅結點,右線索指針指向後繼結點。因此我們需要一個指針p指向當前正在訪問的結點,pre指向p的前驅結點,p的左線索指針如果存在的話直接指向pre(也就是前驅結點),pre的右線索如果存在則指向p(也就是直接後繼)。

  如圖,中序遍歷序列爲DBHEIAFCG,所以從序列中我們可以直接得出H的前驅爲B後繼爲E,I的前驅爲E後繼爲A等等。

void InThread(TBTNode *p, TBTNode *&pre){
    if (p != null)
    {
        InThread(p->lchild, pre);    //對左孩子遞歸,自然第一個操作的結點爲最左下結點(不一定爲葉結點),如上圖爲D
        if (p->lchild == NULL)
        {
            p->lchild = pre;
            p->ltag = 1;
        }
        if (pre != NULL && pre->rchild == NULL)
        {
            pre->rchild = p;
            pre->rtag = 1;
        }
        pre = p; //當p將要離開一個訪問過的結點時,pre指向p,所以p指向新結點時,pre顯然是此時p的直接前驅
        p = p->rchild; //p指向右孩子,準備將右子樹線索化
        InThread(p, pre);
    }
}

  (2)遍歷中序線索二叉樹

TBTNode *First(TBTNode *p){//尋找中序序列下第一個結點
    while(p->ltag == 0)
        p=p->lchild;
    return p; //尋找樹的最左下角結點(不一定是葉節點,如上圖樹中是D
}

TBTNode *Next(TBTNode *p){
    if (p->tag == 0)
        return First(p->rchild); //中序遍歷下,p結點的下一個結點自然是p的右子樹中First()求出來的結點
    else
        return p->rchild; //rtag==1,此時rchild爲線索,指向結點的直接後繼
}

void InOrder(TBTNode *root){//遍歷
    for (TBTNode *p = First(root) ; p !=NULL ; p = Next(p))
    {
        //對結點操作
    }
}

 

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