線索化二叉樹
阿新 • • 發佈:2021-02-02
技術標籤:演算法與資料結構二叉樹線索化二叉樹資料結構java
一、線索二叉樹概述
在二叉樹的節點上加上線索的二叉樹稱為線索二叉樹。對二叉樹以某種遍歷方式(如先序、中序、後序或層次等)進行遍歷,使其變為線索二叉樹的過程稱為對二叉樹進行線索化。
通過考察各種二叉連結串列,不管二叉樹的形態如何,空鏈域的個數總是多過非空鏈域的個數。準確的說,n 個節點的二叉連結串列共有 2n 個鏈域,其中非空鏈域為 n-1 個,但空鏈域卻有 n+1 個。如下圖所示,二叉樹有 6 個節點,其中非空鏈域的個數為 7 個。
利用這些空鏈域存放在某種遍歷次序下該結點的前驅結點和後繼結點的指標,這些指標稱為線索,加上線索的二叉樹稱為線索二叉樹。根據線索性質的不同,線索二叉樹可分為前序線索二叉樹、中序線索二叉樹和後序線索二叉樹三種。
上圖的二叉樹的中序遍歷結果是 {8, 3, 10, 1, 14, 6},但是 8, 10, 14, 6 這四個節點的左右指標並沒有完全利用上。其實我們可以利用它們的左右指標,讓各個節點可以指向自己的前後節點。如下圖所示就是原二叉樹線索化後生成的中序線索二叉樹。
【注意】
當線索化二叉樹後,節點的屬性 left
和 right
,有如下情況:
left
可能指向的是左子樹,也可能是指向的前驅節點。比如 ① 節點的left
指向的是左子樹,而 ⑩ 節點的left
指向的就是它的前驅節點;right
可能指向的是右子樹,也可能是指向後繼節點。比如 ① 節點的right
指向的是右子樹,而 ⑩ 節點的right
二、線索化二叉樹實現
中序線索化二叉樹程式碼實現如下:
public class No3_ThreadedBinaryTree {
public static void main(String[] args) {
// 構造結點用於測試
NumNode node_1 = new NumNode(1);
NumNode node_2 = new NumNode(3);
NumNode node_3 = new NumNode(6);
NumNode node_4 = new NumNode (8);
NumNode node_5 = new NumNode(10);
NumNode node_6 = new NumNode(14);
// 手動構造二叉樹
node_1.left = node_2; // 根節點的左子樹
node_2.left = node_4;
node_2.right = node_5;
node_1.right = node_3; // 根節點的右子樹
node_3.left = node_6;
ThreadedBinaryTree tree = new ThreadedBinaryTree(node_1);
tree.threadedNodes(); // 中序線索化
tree.listNodes(); // 遍歷列印
}
}
/**
* 線索化二叉樹
*/
class ThreadedBinaryTree{
private NumNode root; // 根節點
private NumNode pre = null; // 當前節點的前驅
public ThreadedBinaryTree(NumNode node){
this.root = node;
}
// 遍歷列印中序線索化的樹
public void listNodes(){
NumNode node = root;
while (node != null){
// 首先到左樹葉
while (node.leftType == 0){
node = node.left;
}
System.out.println(node); // 列印樹葉
while (node.rightType == 1){ // 判斷右指標指向是否為後繼結點
node = node.right; // 如果是直接列印
System.out.println(node);
}
node = node.right; // 如果是右子樹,就先過去,不列印
}
}
// 中序線索化二叉樹的過載方法
public void threadedNodes(){
threadedNodes(root);
}
// 中序線索化二叉樹
public void threadedNodes(NumNode node){
if (node == null){ // 根節點為空則直接返回
return;
}
// 先線索化左子樹
threadedNodes(node.left);
// ==========> 線索化
// 處理當前節點的左指標
if (node.left == null){ // 如果左指標指向為空,說明到了左葉子節點
node.left = pre; // 讓當前節點的左指標指向前驅節點
node.leftType = 1; // 設定左指標指向的型別為前驅
}
// 處理前驅的右指標
if (pre != null && pre.right == null){
pre.right = node; // 讓前驅節點的右指標指向當前節點
pre.rightType = 1; // 設定前驅節點的右指標指向型別為後繼節點
}
// <========== 線索化
pre = node; // 處理完後,前驅指標要指向當前節點,接下來指向當前節點的指標也要向右移動一位
// 最後線索化右子樹
threadedNodes(node.right);
}
}
/**
* 二叉樹節點
*/
class NumNode{
int num;
NumNode left; // 左指標
NumNode right; // 右指標
int leftType; // 左指標型別標誌位:0-指向子樹 1-指向前驅
int rightType; // 右指標型別標誌位:0-指向子樹 1-指向後繼
public NumNode(int num){
this.num = num;
}
@Override
public String toString() {
return "NumNode{" +
"num=" + num +
'}';
}
}