您好,登錄后才能下訂單哦!
比如創建一個二叉樹
1
/ \
3 6
/ \ /
8 10 14
線索化二叉樹幾個概念:
//創建樹的節點
/**
* 〈節點定義〉
*
* @author nick
* @create 2019/9/17
* @since 1.0.0
*/
@Data
public class HeroNode {
private int no;
private String name;
/**
* //默認null
*/
private HeroNode left;
/**
* //默認null
*/
private HeroNode right;
/**
* //父節點的指針(為了后序線索化使用)
*/
private HeroNode parent;
/**
* //說明
* //1. 如果leftType == 0 表示指向的是左子樹, 如果 1 則表示指向前驅結點
* //2. 如果rightType == 0 表示指向是右子樹, 如果 1表示指向后繼結點
*/
private int leftType;
private int rightType;
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + "]";
}
}
/**
* 〈線索化二叉樹〉
* 1
* / \
* 3 6
* / \ /
* 8 10 14
*
* @author nick
* @create 2019/9/17
* @since 1.0.0
*/
public class ThreadedBinaryTree {
private HeroNode root;
/**
* 為了實現線索化,需要創建要給指向當前結點的前驅結點的指針
* 在遞歸進行線索化時,pre 總是保留前一個結點
*/
private HeroNode pre = null;
public void setRoot(HeroNode root) {
this.root = root;
}
/**
* 重載一把threadedNodes方法
*/
public void threadedNodes() {
this.threadedNodes(root);
}
/**
* 重載一把threadedNodesPre方法
*/
public void threadedNodesPre() {
this.threadedNodesPre(root);
}
/**
* 重載一把threadedNodesAfter方法
*/
public void threadedNodesAfter() {
this.threadedNodesAfter(root);
}
/***********************遍歷線索化二叉樹開始**********************/
/**
* 中序遍歷線索化二叉樹的方法
* <p>
*/
public void threadedList() {
//定義一個變量,存儲當前遍歷的結點,從root開始
HeroNode node = root;
while ( node != null ) {
//循環的找到leftType == 1的結點,第一個找到就是8結點
//后面隨著遍歷而變化,因為當leftType==1時,說明該結點是按照線索化
//處理后的有效結點
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();
}
}
/**
* 前序線索化二叉樹遍歷方法
* 1
* / \
* 3 6
* / \ /
* 8 10 14
* <p>
* {1,3,8,10,6,14}
*/
public void threadedListPre() {
//定義一個變量,存儲當前遍歷的結點,從root開始
HeroNode node = root;
while ( node != null ) {
while ( node.getLeftType() == 0 ) {
//如果是葉子節點,非前驅節點,打印當前這個結點
System.out.print(node + ",");
node = node.getLeft();
}
System.out.print(node + ",");
//替換這個遍歷的結點
node = node.getRight();
}
}
/**
* 后序線索化二叉樹遍歷方法
* <p>
* 注意后序有點復雜,需要建立二叉樹的時候,將節點的parent進行賦值,否則不能遍歷成功
* 1
* / \
* 3 6
* / \ /
* 8 10 14
* <p>
* {8,10,3,1,14,6}
* 1. 如果leftType == 0 表示指向的是左子樹, 如果 1 則表示指向前驅結點
* 2. 如果rightType == 0 表示指向是右子樹, 如果 1表示指向后繼結點
*/
public void threadedListAfter() {
//1、找后序遍歷方式開始的節點
HeroNode node = root;
while ( node != null && node.getLeftType() == 0 ) {
node = node.getLeft();
}
while ( node != null ) {
//右節點是線索
if (node.getRightType() == 1) {
System.out.print(node + ", ");
pre = node;
node = node.getRight();
} else {
//如果上個處理的節點是當前節點的右節點
if (node.getRight() == pre) {
System.out.print(node + ", ");
if (node == root) {
return;
}
pre = node;
node = node.getParent();
} else { //如果從左節點的進入則找到有子樹的最左節點
node = node.getRight();
while ( node != null && node.getLeftType() == 0 ) {
node = node.getLeft();
}
}
}
}
}
/***********************遍歷線索化二叉樹結束**********************/
/****************線索化二叉樹開始********************************/
/**
* 中序線索化
* 得到的數組{8, 3, 10, 1, 14, 6}
* 1
* / \
* 3 6
* / \ /
* 8 10 14
*
* @param node 就是當前需要線索化的結點
*/
public void threadedNodes(HeroNode node) {
//如果node==null, 不能線索化
if (node == null) {
return;
}
//(一)先線索化左子樹
threadedNodes(node.getLeft());
//(二)線索化當前結點[有難度]
//處理當前結點的前驅結點
//以8結點來理解
//8結點的.left = null , 8結點的.leftType = 1
if (null == node.getLeft()) {
//讓當前結點的左指針指向前驅結點
node.setLeft(pre);
//修改當前結點的左指針的類型,指向前驅結點
node.setLeftType(1);
}
//處理后繼結點,是下一次進行處理,有點不好理解
if (pre != null && pre.getRight() == null) {
//讓前驅結點的右指針指向當前結點
pre.setRight(node);
//修改前驅結點的右指針類型
pre.setRightType(1);
}
//!!! 每處理一個結點后,讓當前結點是下一個結點的前驅結點
pre = node;
//(三)在線索化右子樹
threadedNodes(node.getRight());
}
/**
* 前序線索化
* 變成數組后{1,3,8,10,6,14}
* 1
* / \
* 3 6
* / \ /
* 8 10 14
*
* @param node 就是當前需要線索化的結點
*/
public void threadedNodesPre(HeroNode node) {
//如果node==null, 不能線索化
if (node == null) {
return;
}
//左指針為空,將左指針指向前驅節點
//8結點的.left = 上一個節點 , 8結點的.leftType = 1
if (node.getLeft() == null) {
//讓當前結點的左指針指向前驅結點
node.setLeft(pre);
//修改當前結點的左指針的類型,指向前驅結點
node.setLeftType(1);
}
//處理后繼結點,是下一次進行處理,有點不好理解
if (pre != null && pre.getRight() == null) {
//讓前驅結點的右指針指向當前結點
pre.setRight(node);
//修改前驅結點的右指針類型
pre.setRightType(1);
}
//!!! 每處理一個結點后,讓當前結點是下一個結點的前驅結點
pre = node;
//(一)先線索化左子樹
if (node.getLeftType() != 1) {
threadedNodesPre(node.getLeft());
}
//(三)再線索化右子樹
if (node.getRightType() != 1) {
threadedNodesPre(node.getRight());
}
}
/**
* 后序線索化
* 變成數組后{8,10,3,1,14,6}
*
* @param node
*/
public void threadedNodesAfter(HeroNode node) {
//如果node==null, 不能線索化
if (node == null) {
return;
}
//(一)先線索化左子樹
threadedNodesAfter(node.getLeft());
//(三)再線索化右子樹
threadedNodesAfter(node.getRight());
//左指針為空,將左指針指向前驅節點
//8結點的.left = 上一個節點 , 8結點的.leftType = 1
if (node.getLeft() == null) {
//讓當前結點的左指針指向前驅結點
node.setLeft(pre);
//修改當前結點的左指針的類型,指向前驅結點
node.setLeftType(1);
}
//處理后繼結點,是下一次進行處理,有點不好理解
if (pre != null && pre.getRight() == null) {
//讓前驅結點的右指針指向當前結點
pre.setRight(node);
//修改前驅結點的右指針類型
pre.setRightType(1);
}
//!!! 每處理一個結點后,讓當前結點是下一個結點的前驅結點
pre = node;
}
/*********************線索化結束*********************************/
}
//測試二叉樹的遍歷
/**
* 〈線索化二叉樹測試〉
*
* @author nick
* @create 2019/9/17
* @since 1.0.0
*/
public class ThreadedBinaryTreeTest extends Tester {
@Test
public void testPolandNotation() throws Exception {
//測試一把中序線索二叉樹的功能 以數組{8, 3, 10, 1, 14, 6}為例
/**
* 1
* / \
* 3 6
* / \ /
* 8 10 14
*/
HeroNode root = new HeroNode(1, "java");
HeroNode node2 = new HeroNode(3, "C#");
HeroNode node3 = new HeroNode(6, "Python");
HeroNode node4 = new HeroNode(8, "C++");
HeroNode node5 = new HeroNode(10, "GO");
HeroNode node6 = new HeroNode(14, "Dephi");
//二叉樹,后面我們要遞歸創建, 現在簡單處理使用手動創建
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
//*************測試中序線索化***************//
System.out.println("==========中序線索化開始=============");
System.out.println("{8, 3, 10, 1, 14, 6}");
ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
threadedBinaryTree.setRoot(root);
threadedBinaryTree.threadedNodes();
//測試: 以10號節點測試
HeroNode leftNode = node5.getLeft();
HeroNode rightNode = node5.getRight();
System.out.println("10號結點的前驅結點是 =" + leftNode); //3
System.out.println("10號結點的后繼結點是=" + rightNode); //1
//當線索化二叉樹后,能在使用原來的遍歷方法
//threadedBinaryTree.infixOrder();
System.out.println("中序使用線索化的方式遍歷 線索化二叉樹");
threadedBinaryTree.threadedList(); // 8, 3, 10, 1, 14, 6
//********************中序結束******************//
//******************前序*****************//
System.out.println("==========前序線索化開始=============");
System.out.println("{1,3,8,10,6,14}");
//前序:{1,3,8,10,6,14}
ThreadedBinaryTree threadedBinaryTreePre = new ThreadedBinaryTree();
threadedBinaryTreePre.setRoot(root);
threadedBinaryTreePre.threadedNodesPre();
//測試: 以10號節點測試
HeroNode leftNodePre = node4.getLeft();
HeroNode rightNodePre = node4.getRight();
System.out.println("8號結點的前驅結點是 =" + leftNodePre); //3
System.out.println("8號結點的后繼結點是=" + rightNodePre); //10
HeroNode leftNodetenPre = node5.getLeft();
HeroNode rightNodetenPre = node5.getRight();
System.out.println("10號結點的前驅結點是 =" + leftNodetenPre); //8
System.out.println("10號結點的后繼結點是=" + rightNodetenPre); //6
System.out.println("前序使用線索化的方式遍歷 線索化二叉樹");
threadedBinaryTreePre.threadedListPre();//{1,3,8,10,6,14}
//******************前序結束*****************//
//******************后序*****************//
//如果是后序,需要創建二叉樹的時候,將parent進行保存。這個是用于后續二叉樹的遍歷的
node2.setParent(root);
node3.setParent(root);
node4.setParent(node2);
node5.setParent(node2);
node6.setParent(node3);
System.out.println("==========后序線索化開始=============");
System.out.println("{8,10,3,14,6,1}");
//后序:{8,10,3,14,6,1}
ThreadedBinaryTree threadedBinaryTreeAfter = new ThreadedBinaryTree();
threadedBinaryTreeAfter.setRoot(root);
threadedBinaryTreeAfter.threadedNodesAfter();
HeroNode leftNodeAfter = node4.getLeft();
HeroNode rightNodeAfter = node4.getRight();
System.out.println("8號結點的前驅結點是 =" + leftNodeAfter); //null
System.out.println("8號結點的后繼結點是=" + rightNodeAfter); //10
HeroNode leftNodetenAfter = node5.getLeft();
HeroNode rightNodetenAfter = node5.getRight();
System.out.println("10號結點的前驅結點是 =" + leftNodetenAfter); //8
System.out.println("10號結點的后繼結點是=" + rightNodetenAfter); //3
System.out.println("后序使用線索化的方式遍歷 線索化二叉樹");
threadedBinaryTreeAfter.threadedListAfter();//{8,10,3,14,6,1}
}
}
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。