遍歷二叉樹是按必定的規則將樹中的結點排列成一個線性序列,便是對非線性結構的線性化操做。如何找到遍歷過程當中動態獲得的每一個結點的直接前驅和直接後繼(第一個和最後一個除外)?如何保存這些信息?算法
設一棵二叉樹有n個結點,則有n-1條邊(指針連線) , 而n個結點共有2n個指針域(Lchild和Rchild) ,顯然有n+1個空閒指針域未用。則能夠利用這些空閒的指針域來存放結點的直接前驅和直接後繼信息。this
對結點的指針域作以下規定:spa
1.若結點有左子樹,則其leftChild域指示其左孩子,不然令leftChild域指示其前驅。prototype
2.若結點有右子樹,則其rightChild域指示其右孩子,不然令其rightChild域指示其後繼。指針
爲了不混淆,尚需改變結點結構,增長兩個標誌域(leftTag, rightTag),其中:code
-- 0 leftChild域指示結點的左孩子
leftTag --
-- 1 rightChild域指示結點的前驅blog
-- 0 rightChild域指示結點的右孩子
rightTag --
-- 1 rightChild域指示結點的後繼it
以這種結點結構構成的二叉鏈表做爲二叉樹的存儲結構,叫作線索鏈表,其中指向結點前驅和後繼的指針,叫作線索。io
加上線索的二叉樹稱之爲線索二叉樹(Threaded Binary Tree)。function
對二叉樹以某種次序遍歷使其變爲線索二叉樹的過程叫作線索化。
說明:畫線索二叉樹時,實線表示指針,指向其左、右孩子;虛線表示線索,指向其直接前驅或直接後繼。
在線索樹上進行遍歷,只要先找到序列中的第一個結點,而後就能夠依次找結點的直接後繼結點直到後繼爲空爲止。
如何在線索樹中找結點的直接後繼?
以圖(d) ,(e)所示的中序線索樹爲例:
◆ 樹中全部葉子結點的右鏈都是線索。右鏈直接指示告終點的直接後繼,如結點G的直接後繼是結點E。
◆ 樹中全部非葉子結點的右鏈都是指針。根據中序遍歷的規律,非葉子結點的直接後繼是遍歷其右子樹時訪問的第一個結點,即右子樹中最左下的(葉子)結點。如結點C的直接後繼:沿右指針找到右子樹的根結點F,而後沿左鏈往下直到Ltag=1的結點即爲C的直接後繼結點H。
如何在線索樹中找結點的直接前驅?
若結點的Ltag=1,則左鏈是線索,指示其直接前驅;不然,遍歷左子樹時訪問的最後一個結點(即沿左子樹中最右往下的結點) 爲其直接前驅結點。
對於後序遍歷的線索樹中找結點的直接後繼比較複雜,可分如下三種狀況:
◆ 若結點是二叉樹的根結點:其直接後繼爲空;
◆ 若結點是其父結點的左孩子或右孩子且其父結點沒有右子樹:直接後繼爲其父結點;
◆ 若結點是其父結點的左孩子且其父結點有右子樹:直接後繼是對其父結點的右子樹按後序遍歷的第一個結點。
線索化二叉樹
二叉樹的線索化指的是依照某種遍歷次序使二叉樹成爲線索二叉樹的過程。
線索化的過程就是在遍歷過程當中修改空指針使其指向直接前驅或直接後繼的過程。
仿照線性表的存儲結構,在二叉樹的線索鏈表上也添加一個頭結點head,頭結點的指針域的安排是:
◆ Lchild域:指向二叉樹的根結點;
◆ Rchild域:指向中序遍歷時的最後一個結點;
◆ 二叉樹中序序列中的第一個結點Lchild指針域和最後一個結點Rchild指針域均指向頭結點head。
如同爲二叉樹創建了一個雙向線索鏈表,對一棵線索二叉樹既可從頭結點也可從最後一個結點開始按尋找直接後繼進行遍歷。顯然,這種遍歷不須要堆棧。
線索二叉樹的遍歷
在線索二叉樹中,因爲有線索存在,在某些狀況下能夠方便地找到指定結點在某種遍歷序列中的直接前驅或直接後繼。此外,在線索二叉樹上進行某種遍歷比在通常的二叉樹上進行這種遍歷要容易得多,不須要設置堆棧,且算法十分簡潔。
線索二叉樹的相關代碼實現:
1 var LINK = 0; 2 var THREAD = 1; 3 4 function BinaryThreadTree_inOrder(data, leftChild, rightChild) { 5 this.data = data; 6 this.leftChild = leftChild || null; 7 this.rightChild = rightChild || null; 8 // 左右標記 9 this.leftTag = this.rightTag = undefined; 10 } 11 BinaryThreadTree_inOrder.prototype = { 12 constructor: BinaryThreadTree_inOrder, 13 // 中序線索二叉樹的遍歷 14 inOrderTraverse_thread: function (visit) { 15 var p = this.leftChild; 16 17 while (p != this) { 18 while (p.leftTag === LINK) p = p.leftChild; 19 20 if (visit(p.data) === false) return; 21 22 while (p.rightTag == THREAD && p.rightChild != this) { 23 p = p.rightChild; 24 visit(p.data); 25 } 26 p = p.rightChild; 27 } 28 }, 29 // 中序線索化 30 inOrderThreading: function () { 31 return inOrderThreading(this); 32 }, 33 // 在當前結點插入子樹x,p表明當前結點 34 insertSubTree: function (xTree) { 35 var s, q; 36 // x做爲p的左子樹 37 if (this.leftTag === THREAD) { 38 s = this.leftChild; // s爲p的前驅 39 this.leftTag = LINK; 40 this.leftChild = xTree; 41 q = xTree; 42 43 while (q.leftChild && q.leftTag === LINK) q = q.leftChild; 44 // 找到子樹中的最左結點,並修改其前驅指向s 45 q.leftChild = s; 46 xTree.rightTag = THREAD; 47 // x的後繼指向p 48 xTree.rightChild = this; 49 } 50 // x做爲p的右子樹 51 else if (this.rightTag === THREAD) { 52 // s爲p的後繼 53 s = this.rightChild; 54 this.rightTag = LINK; 55 this.rightChild = xTree; 56 q = xTree; 57 58 while (q.leftChild && q.leftTag === LINK) q = q.leftChild; 59 // 找到子樹中的最左結點,並修改其前驅指向p 60 q.leftChild = this; 61 xTree.rightTag = THREAD; 62 // x的後繼指向p的後繼 63 xTree.rightChild = s; 64 } 65 // x做爲p的左子樹,p的左子樹做爲x的右子樹 66 else { 67 s = this.leftChild; 68 var t = s; 69 70 while (t.leftChild && t.leftTag === LINK) t = t.leftChild; 71 // 找到p的左子樹的最左結點t和前驅u 72 var u = t.leftChild; 73 this.leftChild = xTree; 74 xTree.rightTag = LINK; 75 // x做爲p的左子樹,p的左子樹做爲x的右子樹 76 xTree.rightChild = s; 77 t.leftChild = xTree; 78 q = xTree; 79 80 while (q.leftChild && q.leftTag === LINK) q = q.leftChild; 81 // 找到子樹中的最左結點,並修改其前驅指向u 82 q.leftChild = u; 83 } 84 } 85 }; 86 87 // 二叉樹中序線索化 88 function inOrderThreading(tree) { 89 var threadTree = new BinaryThreadTree(); 90 threadTree.leftTag = LINK; 91 threadTree.rightTag = THREAD; 92 // 右指針回指 93 threadTree.rightChild = threadTree; 94 95 var pre; 96 // 若二叉樹爲空,左指針回指 97 if (!tree) threadTree.leftChild = threadTree; 98 else { 99 threadTree.leftChild = tree; 100 pre = threadTree; 101 inThreading(tree); // 中序遍歷進行中序線索化 102 // 最後一個結點線索化 103 pre.rightChild = threadTree; 104 pre.rightTag = THREAD; 105 threadTree.rightChild = pre; 106 } 107 108 return threadTree; 109 110 function inThreading(p) { 111 if (!p) return; 112 113 inThreading(p.leftChild); // 左子樹線索化 114 // 前驅線索 115 if (!p.leftChild) { 116 p.leftTag = THREAD; 117 p.leftChild = pre; 118 } 119 // 後繼線索 120 if (!pre.rightChild) { 121 pre.rightTag = THREAD; 122 pre.rightChild = p; 123 } 124 pre = p; 125 inThreading(p.rightChild); // 右子樹線索化 126 } 127 }