javascript實現資料結構:線索二叉樹
遍曆二叉樹是按一定的規則將樹中的結點排列成一個線性序列,即是對非線性結構的線性化操作。如何找到遍曆過程中動態得到的每個結點的直接前驅和直接後繼(第一個和最後一個除外)?如何儲存這些資訊? 設一棵二叉樹有n個結點,則有n-1條邊(指標連線) , 而n個結點共有2n個指標域(Lchild和Rchild) ,顯然有n+1個空閑指標域未用。則可以利用這些閒置指標域來存放結點的直接前驅和直接後繼資訊。 對結點的指標域做如下規定: 1.若結點有左子樹,則其leftChild域指示其左孩子,否則令leftChild域指示其前驅。 2.若結點有右子樹,則其rightChild域指示其右孩子,否則令其rightChild域指示其後繼。 為了避免混淆,尚需改變結點結構,增加兩個標誌域(leftTag, rightTag),其中: -- 0 leftChild域指示結點的左孩子 leftTag -- -- 1 rightChild域指示結點的前驅 -- 0 rightChild域指示結點的右孩子 rightTag -- -- 1 rightChild域指示結點的後繼 以這種結點結構構成的二叉鏈表作為二叉樹的儲存結構,叫做線索鏈表,其中指向結點前驅和後繼的指標,叫做線索。 加上線索的二叉樹稱之為線索二叉樹(Threaded Binary Tree)。 對二叉樹以某種次序遍曆使其變為線索二叉樹的過程叫做線索化。 說明:畫線索二叉樹時,實線表示指標,指向其左、右孩子;虛線表示線索,指向其直接前驅或直接後繼。 線上索樹上進行遍曆,只要先找到序列中的第一個結點,然後就可以依次找結點的直接後繼結點直到後繼為空白為止。 如何線上索樹中找結點的直接後繼? 以圖(d) ,(e)所示的中序線索樹為例: ◆ 樹中所有葉子結點的右鏈都是線索。右鏈直接指示了結點的直接後繼,如結點G的直接後繼是結點E。 ◆ 樹中所有非葉子結點的右鏈都是指標。根據中序遍曆的規律,非葉子結點的直接後繼是遍曆其右子樹時訪問的第一個結點,即右子樹中最左下的(葉子)結點。如結點C的直接後繼:沿右指標找到右子樹的根結點F,然後沿左鏈往下直到Ltag=1的結點即為C的直接後繼結點H。 如何線上索樹中找結點的直接前驅? 若結點的Ltag=1,則左鏈是線索,指示其直接前驅;否則,遍曆左子樹時訪問的最後一個結點(即沿左子樹中最右往下的結點) 為其直接前驅結點。 對於後序遍曆的線索樹中找結點的直接後繼比較複雜,可分以下三種情況: ◆ 若結點是二叉樹的根結點:其直接後繼為空白; ◆ 若結點是其父結點的左孩子或右孩子且其父結點沒有右子樹:直接後繼為其父結點; ◆ 若結點是其父結點的左孩子且其父結點有右子樹:直接後繼是對其父結點的右子樹按後序遍曆的第一個結點。 線索化二叉樹 二叉樹的線索化指的是依照某種遍曆次序使二叉樹成為線索二叉樹的過程。 線索化的過程就是在遍曆過程中修改null 指標使其指向直接前驅或直接後繼的過程。 仿照線性表的儲存結構,在二叉樹的線索鏈表上也添加一個頭結點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 }複製代碼