生活随笔
收集整理的這篇文章主要介紹了
线索二叉树(基于链表存储树结点)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
有以下場景
如果使用中序遍歷,那么得到的順序是:HDIBEAFCG,可以得知A的前驅結點為E,后繼結點為F。但是,這種關系的獲得是建立在完成遍歷后得到的。如果我們每次想得到某個節點的前驅或者后繼,都要先遍歷整棵樹,未免太過于麻煩了!我們為何不在生成該樹的時候,就直接將結點的前驅后后繼寫入結點的屬性中,提高在后續尋找前驅結點和后繼結點的效率?這就是線索二叉樹的由來。
首先,得先明確以下概念:
1.n個結點的二叉鏈表中含有n
+1個空指針域。利用二叉鏈表中的空指針域,存放指向該結點在某種遍歷次序下的前驅和后繼結
占的指針(這種附加的指針稱為
"線索")
2.這種加上了線索的二叉鏈表稱為線索鏈表,相應的二叉樹稱為線索二叉
(ThreadedBinaryTree
)。根據線索性質的不同,線索二叉樹可分為前序線索二叉樹、中序線索二叉樹和后序線索二叉樹三種。
3.一個結點的前一個結點,稱為前驅結點
4.一個結點的后一個結點,稱為后繼結點
5.二叉樹線索化時,通常規定,若左子樹為空,令其左子節點
=其前驅結點,若右子樹為空,令其右子結點
==其后繼結點。
6.指向結點得前驅后者后繼指針,便稱為線索。
7.將二叉樹以某種次序遍歷使其變為線索二叉樹的過程成為線索化
圖中的虛線為線索
出現問題:
上面的雙向鏈表,與線索化之后的二叉樹相比,比如節點D與后繼節點I,在完成線索化之后,并沒有直接線索指針,而是存在父子節點的指針;節點A與節點F,在線索化完成之后,節點A并沒有直接指向后繼節點F的線索指針,而是通過父子節點遍歷可以找到最終的節點F,前驅節點也存在同樣的問題,正因為很多節點之間不存在直接的線索,所以我將此雙向鏈表稱做“特殊的雙向鏈表”,再使用過程中根據指針是線索指針還是子節點指針來分別處理,所以在每個節點需要標明當前的左右指針是線索指針還是子節點指針,這就需要修改節點的數據結構,增加標志位。
isLeftThread為false時,指向左孩子,為ture時指向前驅
isRightThread為false時,指向右孩子,為ture時指向后繼
添加后結點結構如下:
節點類:
static class Node {String data
; Node left
; Node right
; Node parent
; boolean isLeftThread
= false; boolean isRightThread
= false; Node(String data
) {this.data
= data
;}
}
添加標志位后的二叉樹:
線索樹代碼:
public class ThreadBinaryTree {private Node preNode
; static class Node {String data
; Node left
; Node right
; boolean isLeftThread
= false; boolean isRightThread
= false; Node(String data
) {this.data
= data
;}}static Node
createBinaryTree(String
[] array
, int index
) {Node node
= null
;if(index
< array
.length
) {node
= new Node(array
[index
]);node
.left
= createBinaryTree(array
, index
* 2 + 1);node
.right
= createBinaryTree(array
, index
* 2 + 2);}return node
;}void inThreadOrder(Node node
) {if(node
== null
) {return;}inThreadOrder(node
.left
);if(node
.left
== null
) {node
.left
= preNode
;node
.isLeftThread
= true;}if(preNode
!= null
&& preNode
.right
== null
) {preNode
.right
= node
;preNode
.isRightThread
= true;}preNode
= node
;inThreadOrder(node
.right
);}void inThreadList(Node node
) {while(node
!= null
&& !node
.isLeftThread
) {node
= node
.left
;}while(node
!= null
) {System
.out
.print(node
.data
+ ", ");if(node
.isRightThread
) {node
= node
.right
;} else { node
= node
.right
;while(node
!= null
&& !node
.isLeftThread
) {node
= node
.left
;}}}}void inPreThreadList(Node node
) {while(node
.right
!= null
&& !node
.isRightThread
) {node
= node
.right
;}while(node
!= null
) {System
.out
.print(node
.data
+ ", ");if(node
.isLeftThread
) {node
= node
.left
;} else { node
= node
.left
;while(node
.right
!= null
&& !node
.isRightThread
) {node
= node
.right
;}}}}void preThreadOrder(Node node
) {if(node
== null
) {return;}if(node
.left
== null
) {node
.left
= preNode
;node
.isLeftThread
= true;}if(preNode
!= null
&& preNode
.right
== null
) {preNode
.right
= node
;preNode
.isRightThread
= true;}preNode
= node
;if(!node
.isLeftThread
) {preThreadOrder(node
.left
);}if(!node
.isRightThread
) {preThreadOrder(node
.right
);}}void preThreadList(Node node
) {while(node
!= null
) {while(!node
.isLeftThread
) {System
.out
.print(node
.data
+ ", ");node
= node
.left
;}System
.out
.print(node
.data
+ ", ");node
= node
.right
;}}public static void main(String
[] args
) {String
[] array
= {"A", "B", "C", "D", "E", "F", "G", "H"};Node root
= createBinaryTree(array
, 0);ThreadBinaryTree tree
= new ThreadBinaryTree();tree
.inThreadOrder(root
);System
.out
.println("中序按后繼節點遍歷線索二叉樹結果:");tree
.inThreadList(root
);System
.out
.println("\n中序按后繼節點遍歷線索二叉樹結果:");tree
.inPreThreadList(root
);Node root2
= createBinaryTree(array
, 0);ThreadBinaryTree tree2
= new ThreadBinaryTree();tree2
.preThreadOrder(root2
);tree2
.preNode
= null
;System
.out
.println("\n前序按后繼節點遍歷線索二叉樹結果:");tree
.preThreadList(root2
);}
}
加上頭結點
有時為了方便,模仿線性表的存儲結構為了便于遍歷線索二叉樹,我們為其添加一個頭結點,頭結點左孩子指向原二叉樹的根結點,右孩子指針指向中序遍歷的最后一個結點。同時,將第一個結點左孩子指針指向頭結點,最后一個結點的右孩子指針指向頭結點。這好比為二叉樹建立了一個雙向線索鏈表,既可以從第一個結點起順后繼進行遍歷,又可以從最后一個結點起順前驅進行遍歷。
小結
線索化的實質就是將二叉鏈表中的空指針改為指向前驅節點或后繼節點的線索;線索化的過程就是修改二叉鏈表中空指針的過程,可以按照前序、中序、后序的方式進行遍歷,分別生成不同的線索二叉樹;有了線索二叉樹之后,我們再次遍歷時,就相當于操作一個雙向鏈表。使用場景:如果我們在使用二叉樹過程中經常需要遍歷二叉樹或者查找節點的前驅節點和后繼節點,可以考慮采用線索二叉樹存儲結構。
本文轉載于
與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的线索二叉树(基于链表存储树结点)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。