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