【樹】線索二叉樹的基本概念及遍歷

基本概念

我們知道,在有 n 個結點的二叉鏈表中共有 2n 個鏈域,但只有 n-1 個有用的非空鏈域,其餘 n+1 個鏈域是空的。可以利用剩下的 n+1 個空鏈域來存放遍歷過程中結點的前驅和後繼信息。

現作如下規定:

  • 若結點有左子樹,則其 LChild 域指向其左孩子,否則 LChild 域指向其前驅結點;
  • 若結點有右子樹,則其 RChild 域指向其右孩子,否則 RChild 域指向其後繼結點。

爲了區分孩子結點和前驅、後繼結點,爲結點結構增設兩個標誌域,如下圖所示:
在這裏插入圖片描述

在這裏插入圖片描述

二叉樹的線索化

線索化實質是將二叉鏈表中的空指針域,填上相應結點的遍歷前驅或後繼結點的地址,而前驅和後繼的地址只能在動態的遍歷過程中才能得到。因此線索化的過程即爲在遍歷過程中修改空指針域的過程。

對二叉樹按照不同的遍歷次序進行線索化,可以得到不同的線索二叉樹,包括先序線索二叉樹、中序線索二叉樹和後序線索二叉樹。這裏重點介紹中序線索化的算法。

【算法思想】
(1)中序線索化採用中序遞歸遍歷算法框架。
(2)加線索操作就是訪問結點操作。
(3)加線索操作需要利用剛訪問過結點與當前結點的關係,因此設置一個指針 pre,始終記錄剛訪問過的結點,其操作如下:
①如果當前遍歷結點 root 的左子域爲空,則讓左子域指向 pre ;
②如果前驅 pre 的右子域爲空,則讓右子域指向當前遍歷結點 root;
③爲下次做準備,當前訪問結點 root 作爲下一個訪問結點的前驅 pre。
【算法描述】

/* 對 root 所指的二叉樹進行中序線索化,其中 pre 始終指向剛訪問過的結點,其初值爲NULL*/
void Inthread(BiTree root)
{
    if (root != NULL)
    {
        Inthread(root->LChild); /* 線索化左子樹 */
        if (root->LChild == NULL)
        {
            root->Ltag = 1;
            root->LChild = pre;
            / *置前驅線索 * /
        }
        if (pre != NULL && pre->RChild == NULL) /* 置後繼線索 */
        {
            pre->RChild = root;
            pre->Rtag = 1;
        }
        pre = root;                /*當前訪問結點爲下一個訪問結點的前驅*/
            Inthread(root->RChild); /*線索化右子樹*/
    }
}
在線索二叉樹中找前驅,後繼結點

以中序線索二叉樹爲例,來討論如何在線索二叉樹中查找結點的前驅和後繼。
(1)找結點的中序前驅結點
根據線索二叉樹的基本概念和存儲結構可知,對於結點 p,

  • 當 p->Ltag=1 時,p->LChild 指 向 p 的前驅。
  • 當 p->Ltag=0 時,p->LChild 指向 p 的左孩子。由中序遍歷的規律可知,作爲根 p 的前驅結點,它是中序遍歷 p 的左子樹時訪問的最後一個結點,即左子樹的“最右下端”結點。

【算法描述】

BiTNode *InPre(BiTNode *p)
/* 在中序線索二叉樹中查找 p 的中序前驅, 並用 pre 指針返回結果 */
{
    if (p->Ltag == 1)
        pre = p->LChild; /*直接利用線索*/
    else
    { /* 在 p 的左子樹中查找“最右下端”結點 */
        for (q = p->LChild; q->Rtag == 0; q = q->RChild)
            ;
        pre = q;
    }
    return (pre);
}

在這裏插入圖片描述
(2)在中序線索樹中找結點後繼對於結點 p,若要找其後繼結點,

  • 當 p->Rtag=1 時,p->RChild 即爲 p 的後繼結點;
  • 當 p->Rtag=0時,說明 p 有右子樹,此時 p 的中序後繼結點即爲其右子樹的“最左下端”的結點。

【算法描述】

BiTNode *InNext(BiTNode *p)
/*在中序線索二叉樹中查找 p 的中序後繼結點,並用 Next 指針返回結果*/
{
    if (p->Rtag == 1)
        Next = p->RChild; /*直接利用線索*/
    else
    { /*在 p 的右子樹中查找“最左下端”結點*/
        if (p->RChild != NULL)
        {
            for (q = p->RChild; q->Ltag == 0; q = q->LChild)
                ;
            Next = q;
        }
        else
            Next = NULL;
    }
    return (Next)
}

在先序線索樹中找結點的後繼比較容易,根據先序線索樹的遍歷過程可知,

  • 若結點 p 存在左子樹,則 p 的左孩子結點即爲 p 的後繼;
  • 若結點 p 沒有左子樹,但有右子樹,則 p 的右孩子結點即爲 p 的後繼;
  • 若結點 p 既沒有左子樹,也沒有右子樹,則結點 p 的 RChild 指針域所指的結點即爲 p 的後繼。

用語句表示則爲

if (p->Ltag==0) Next=p->Lchild; 
else Next =p->RChild;

同樣,在後序線索樹中查找結點 p 的前驅也很方便。

在先序線索樹中找結點的前驅比較困難。

  • 若結點 p 是二叉樹的根,則 p 的前驅爲空;
  • 若 p是其雙親的左孩子,或者 p 是其雙親的右孩子並且其雙親無左孩子,則 p 的前驅是 p 的雙親
    結點
  • 若 p 是雙親的右孩子且雙親有左孩子,則 p 的前驅是其雙親的左子樹中按先序遍歷時最後訪問的那個結點。
遍歷中序線索樹

遍歷線索樹的問題可以分解成兩步,第一步是求出某種遍歷次序下第一個被訪問結點;然後連續求出剛訪問結點的後繼結點,直至所有的結點均被訪問。以遍歷中序線索樹爲例。
(1)在中序線索樹上求中序遍歷的第一個結點
【算法描述】

BiTNode *InFirst(BiTree Bt)
{
    BiTNode *p = Bt;
    If(!p) return (NULL);
    while (p->LTag == 0)
        p = p->Lchild;
    return p;
}

(2)遍歷中序二叉線索樹:
通過調用 InFirst 和 InNext ,可以實現對中序線索樹的中序遍歷,且不需要使用遞歸棧。函數 TInOrder 實現了這種遍歷

void TInOrder(BiTree Bt)
{
    BITNode *p;
    P = InFirst(Bt);
    While(p)
    {
        Visit(p);
        p = InNext(p);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章