一、基本介紹
-
n個結點的二叉鏈表中含有n+1 【公式 2n-(n-1)=n+1】 個空指針域。利用二叉鏈表中的空指針域,存放指向該結點在某種遍歷次序下的前驅和後繼結點的指針(這種附加的指針稱爲"線索")
-
這種加上了線索的二叉鏈表稱爲線索鏈表,相應的二叉樹稱爲線索二叉樹(Threaded BinaryTree)。根據線索性質的不同,線索二叉樹可分爲前序線索二叉樹、中序線索二叉樹和後序線索二叉樹三種
-
一個結點的前一個結點,稱爲前驅結點
-
一個結點的後一個結點,稱爲後繼結點
二、應用案例
思路分析:
節點8
的前驅節點爲null,後繼節點爲節點3
節點10
的前驅節點爲節點3
,後繼節點爲節點1
節點14
的前驅節點爲節點1
,後繼節點爲節點6
中序遍歷的結果:{8, 3, 10, 1, 14, 6}
說明:
當線索化二叉樹後,Node節點的 屬性 left 和 right ,有如下情況:
- left 指向的是左子樹,也可能是指向的前驅節點. 比如 ① 節點 left 指向的左子樹, 而 ⑩ 節點的 left 指向的就是前驅節點.
- right指向的是右子樹,也可能是指向後繼節點,比如 ① 節點right 指向的是右子樹,而⑩ 節點的right 指向的是後繼節點.
三、線索化代碼實現
節點定義
/**
* 學生節點
*/
class StudentNode {
private int id;
private String name;
private StudentNode left;
private StudentNode right;
//父節點指針(後序線索化使用)
public StudentNode parent;
//若leftType=0表示當前指向的是左子樹,如果是1則說明指向的是前驅節點
private int leftType;
//rightType=0表示當前指向的是右子樹,如果是1則說明指向的是後繼節點
private int rightType;
public StudentNode(int id, String name) {
this.id = id;
this.name = name;
}
// Get And Set
@Override
public String toString() {
return "StudentNode{id=" + id + ", name='" + name + "'}";
}
}
中序線索化
/**
* 線索化二叉樹
*/
class ThreadedBinaryTree {
//根節點
private StudentNode root;
//輔助指針指向當前節點的前驅節點
private StudentNode pre = null;
public void setRoot(StudentNode root) {
this.root = root;
}
/**
* 遍歷中序線索化二叉樹入口
*/
public void infixThreadedNode() {
infixThreadedNode(root);
}
/**
* 對二叉樹進行中序線索化
*/
private void infixThreadedNode(StudentNode node) {
//若傳入的node爲null則不能線索化
if (node == null) {
return;
}
// 一. 先線索化左子樹
infixThreadedNode(node.getLeft());
// 二. 線索化當前節點
// 1. 處理當前節點的前驅節點
if (node.getLeft() == null) {
//讓當前節點的左指針指向前驅節點
node.setLeft(pre);
//修改當前節點的左指針類型爲前驅節點類型,這裏用1代表
node.setLeftType(1);
}
// 2. 處理當前節點的後繼節點 ==> 理解:因爲當前節點的後繼節點需要連接到當前節點的前驅節點,
// 所以可以理解爲用 pre代替原本的node 從當前節點的前驅節點獲取到當前節點再將當前節點的右指針指向下一個節點
if (pre != null && pre.getRight() == null) {
//讓前驅節點的右指針指向當前節點
pre.setRight(node);
//修改前驅節點的右指針類型
pre.setRightType(1);
}
// 3. 處理好一個節點後,讓當前節點的下一個節點是前驅節點
pre = node;
// 三.線索化右子樹
infixThreadedNode(node.getRight());
}
}
前序線索化
/**
* 線索化二叉樹
*/
class ThreadedBinaryTree {
//根節點
private StudentNode root;
//輔助指針指向當前節點的前驅節點
private StudentNode pre = null;
public void setRoot(StudentNode root) {
this.root = root;
}
/**
* 遍歷前序線索化二叉樹入口
*/
public void preThreadedNode() {
preThreadedNode(root);
}
/**
* 對二叉樹進行前序線索化
*/
private void preThreadedNode(StudentNode node) {
//若傳入的node爲null則不能線索化
if (node == null) {
return;
}
// 一. 線索化當前節點
// 1. 處理當前節點的前驅節點
if (node.getLeft() == null) {
//讓當前節點的左指針指向前驅節點
node.setLeft(pre);
//修改當前節點的左指針類型爲前驅節點類型,這裏用1代表
node.setLeftType(1);
}
// 2. 處理當前節點的後繼節點 ==> 理解:因爲當前節點的後繼節點需要連接到當前節點的前驅節點,
// 所以可以理解爲用 pre代替原本的node 從當前節點的前驅節點獲取到當前節點再將當前節點的右指針指向下一個節點
if (pre != null && pre.getRight() == null) {
//讓前驅節點的右指針指向當前節點
pre.setRight(node);
//修改前驅節點的右指針類型
pre.setRightType(1);
}
// 3. 處理好一個節點後,讓當前節點的下一個節點是前驅節點
pre = node;
// 二. 先線索化左子樹
if (node.getLeftType() == 0) {
preThreadedNode(node.getLeft());
}
// 三.線索化右子樹
if (node.getRightType() == 0) {
preThreadedNode(node.getRight());
}
}
}
後序線索化
/**
* 線索化二叉樹
*/
class ThreadedBinaryTree {
//根節點
private StudentNode root;
//輔助指針指向當前節點的前驅節點
private StudentNode pre = null;
public void setRoot(StudentNode root) {
this.root = root;
}
/**
* 遍歷後序線索化二叉樹入口
*/
public void postThreadedNode() {
postThreadedNode(root);
}
/**
* 對二叉樹進行後序線索化
*/
private void postThreadedNode(StudentNode node) {
//若傳入的node爲null則不能線索化
if (node == null) {
return;
}
// 一. 先線索化左子樹
postThreadedNode(node.getLeft());
// 二.線索化右子樹
postThreadedNode(node.getRight());
// 三. 線索化當前節點
// 1. 處理當前節點的前驅節點
if (node.getLeft() == null) {
//讓當前節點的左指針指向前驅節點
node.setLeft(pre);
//修改當前節點的左指針類型爲前驅節點類型,這裏用1代表
node.setLeftType(1);
}
// 2. 處理當前節點的後繼節點 ==> 理解:因爲當前節點的後繼節點需要連接到當前節點的前驅節點,
// 所以可以理解爲用 pre代替原本的node 從當前節點的前驅節點獲取到當前節點再將當前節點的右指針指向下一個節點
if (pre != null && pre.getRight() == null) {
//讓前驅節點的右指針指向當前節點
pre.setRight(node);
//修改前驅節點的右指針類型
pre.setRightType(1);
}
// 3. 處理好一個節點後,讓當前節點的下一個節點是前驅節點
pre = node;
}
}
四、線索化後輸出二叉樹代碼實現
中序線索化後輸出二叉樹
/**
* 線索化二叉樹
*/
class ThreadedBinaryTree {
//根節點
private StudentNode root;
/**
* 中序遍歷輸出
*/
public void infixOrder() {
//定義一個變量,存儲當前遍歷的節點
StudentNode node = root;
while (node != null) {
//找到左子樹最底節點非前驅節點
while (node.getLeftType() == 0) {
node = node.getLeft();
}
System.out.println(node);
//如果當前節點的右指針指向後繼節點,就一直輸出
while (node.getRightType() == 1) {
//獲取到當前節點的後繼節點
node = node.getRight();
System.out.println(node);
}
node = node.getRight();
}
}
}
前序線索化後輸出二叉樹
/**
* 線索化二叉樹
*/
class ThreadedBinaryTree {
//根節點
private StudentNode root;
/**
* 前序遍歷輸出
*/
public void preOrder() {
//定義一個變量,存儲當前遍歷的節點
StudentNode node = root;
while (node != null) {
//找到左子樹最底節點非前驅節點
while (node.getLeftType() == 0) {
System.out.println(node);
node = node.getLeft();
}
//如果當前節點的右指針指向後繼節點,就一直輸出
System.out.println(node);
node = node.getRight();
}
}
}
後序線索化後輸出二叉樹
/**
* 線索化二叉樹
*/
class ThreadedBinaryTree {
//根節點
private StudentNode root;
/**
* 後序遍歷輸出
*/
public void postOrder() {
StudentNode node = root;
StudentNode preNode = null;
//1.找到後序遍歷的開始節點 -> 最左子節點
while (node != null && node.getLeftType() == 0) {
node = node.getLeft();
}
while (node != null) {
//右節點是後繼結點
if (node.getRightType() == 1) {
System.out.println(node);
//用臨時指針preNode保存當前結點
preNode = node;
//指向下一個後繼節點
node = node.getRight();
} else {
//若上一個結點是當前節點的右節點
if (preNode == node.getRight()) {
System.out.println(node);
//若當前結點是根節點則退出
if (node == root) {
return;
}
preNode = node;
//當前結點指向當前節點的父級節點
node = node.parent;
} else {
//上一個結點是當前節點的左節點,則找到當前子樹的最左節點
node = node.getRight();
while (node != null && node.getLeftType() == 0) {
node = node.getLeft();
}
}
}
}
}
}
附 完整代碼