1. 程式人生 > >線索二叉樹實現

線索二叉樹實現

遍歷二叉樹是以一定規則將二叉樹中結點排列成一個線性序列,得到二叉樹中結點的先序序列或中序序列或後序序列。這實際上是對一個非線性結構進行線性化操作,使得每個結點(除第一個和最後一個外)在這些線性序列中有且僅有一個直接前驅和直接後繼。

當以二叉連結串列作為儲存結構時,只能找到結點的左右孩子的資訊,而不能直接得到結點在任一序列中的前驅和後繼資訊,這種資訊只有在遍歷的動態過程中才能得到。

那麼,如果有一個需要大量查詢前驅後繼的程式,對於每次都遍歷,時間成本過大,我們便需要能快速查詢前驅後繼的辦法。最簡單的方法,每個結點加兩個儲存前驅後繼的變數,但是這樣卻浪費大量的空間。

另一種方法,對於n個結點的二叉連結串列中必定存在n+1個空鏈域,由此,可以利用這些空鏈域來存放結點的前驅和後繼的資訊。

規定如下:若結點有左子樹,則其lchild域指示其左孩子。否則令lchild域指示其前驅;若結點有右子樹,則其rchild域指示其右孩子,否則令rchild域指示其後繼。(為了避免混淆,尚需改變結點結構,增加兩個標誌域,用來區分其左右孩子指標到底指向的是子樹還是前驅後繼)。

更改為這種結構的二叉連結串列作為二叉樹的儲存結構叫做線索連結串列,其中指向結點前驅和後繼的指標叫做線索。加上線索的二叉樹稱之為線索二叉樹。對二叉樹以某種次序遍歷使其變為線索二叉樹的過程叫做線索化

線上索樹上進行遍歷,只要先找到序列中的第一個結點,然後依次找結點後繼直至其後繼為空時為止。

在後序線索樹中招結點後繼較為複雜些,可分為3種情況:

  • 若結點x是二叉樹的根,則其後繼為空
  • 若結點x使其雙親的右孩子或左孩子且其雙親沒有右子樹,則其後繼即為雙親結點
  • 若結點x是其雙親的最孩子,且其雙親有右子樹,則其後繼為雙親的右子樹上按後序遍歷列出的第一個結點

為方便起見,仿照線性表的儲存結構,在二叉樹的線索連結串列上也新增一個頭結點,病令其lchild域的指標指向二叉樹的根結點,其rchild域的指標指向中序遍歷時訪問的最後一個結點;反之,靈感二叉樹中序序列中的第一個結點的l遲來的域指標和最後一個結點rchild域的指標均指向頭結點。

這好比為二叉樹建立了一個雙向線索連結串列,既可從第一個結點起順後繼進行遍歷,也可以從最後一個結點起順前驅進行遍歷。

以雙向線索連結串列為儲存結構是對二叉樹進行遍歷的演算法:

Status InOrderTraverse_Thr(BiThrTree T, Status (*Visit)(ElemType)) {
   // T指向頭結點,頭結點的左鏈lchild指向根結點,頭結點的右鏈lchild指向
   // 中序遍歷的最後一個結點。中序遍歷二叉線索連結串列表示的二叉樹T,
   // 對每個資料元素呼叫函式Visit。
   BiThrTree p;
   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;  // p進至其右子樹根
   }
   return OK;
} // InOrderTraverse_Thr

線索化

由於線索化的實質是將二叉連結串列中的空指標改為指向前驅或後繼的線索,而前驅或後繼是資訊是隻有在遍歷的時候才能得到,因此,線索化的過程即為在遍歷的過程中修改空指標的過程。

為記下遍歷過程中訪問結點的先後關係,附設一個指標pre始終指向剛剛訪問過的結點,若指標p指向當前訪問的結點,則per指向他的前驅。由此,可得中序遍歷建立中序線索化連結串列的演算法:

Status InOrderThreading(BiThrTree &Thrt, BiThrTree T) {  // 演算法6.6
   // 中序遍歷二叉樹T,並將其中序線索化,Thrt指向頭結點。
   if (!(Thrt = (BiThrTree)malloc(sizeof(BiThrNode)))) exit(OVERFLOW);
   Thrt->LTag = Link;  Thrt->RTag =Thread;  // 建頭結點
   Thrt->rchild = Thrt;              // 右指標回指
   if (!T) Thrt->lchild = Thrt;      // 若二叉樹空,則左指標回指
   else {
      Thrt->lchild = T;    pre = Thrt;
      InThreading(T);  // 演算法6.7:中序遍歷進行中序線索化
      pre->rchild = Thrt;  pre->RTag = Thread; // 最後一個結點線索化
      Thrt->rchild = pre;
   }
   return OK;
} // InOrderThreading

歡迎到微信裡去當吃瓜群眾