線索二叉樹
1、線索二叉樹概述
1.1 線索二叉樹的使用背景
對於一顆二叉樹而言(如下圖所示),每次我們想要查找二叉樹裏面的一個節點的時候,我們都必須遍歷這棵樹,以中序遍歷爲例,遍歷的順序爲4251637,當我們遍歷到標號爲5這個節點的時候,如果有個指針指向5的前一個節點2或者後一個節點1的話,那麼我們跳轉到2或者1的效率肯定會大大的提升,那我們在哪裏去創建這種指針呢?剛好我們的二叉樹除了父節點以外,其它的每一個節點都有一個節點指向它,意味着二叉樹有(n-1)條鏈被使用,而我們的二叉樹一共有2N條鏈,所以二叉樹會有 2n-(n-1) = n+1 條空鏈,這n+1條空鏈存在這空間浪費的情況。所以線索二叉樹就是爲了合理使用這n+1條空鏈域而產生的一種數據結構。
1.2線索二叉樹創建原理
1、線索二叉樹將二叉樹中 n+1條空鏈域用來線索化二叉樹,在線索二叉樹中,我們將一個節點的前一個節點叫做前驅節點,一個節點的後一個節點叫做後繼節點。
2、在線索化二叉樹的過程中我們會遇到這樣一個問題,有些指針本來就是指向我們的下一個節點的,這樣的話我們是分不清楚到底這個指針是指向它的前後驅節點還是子節點的。爲了解決這個問題,我們將指針分成兩個部分,一個布爾值,一個指向下一個節點的地址,如下圖所示:
當LFlag爲true時,表示LChild指向這個節點的前驅節點,爲False時,LChild指向左節點
同理當RFlag爲true時,表示RChild指向該節點的後繼節點,爲False時,RChild指向右節點
2、線索二叉樹代碼實現
2.1 線索二叉樹創建
線索二叉樹的創建
package ClueBinary;
/**
* 二叉樹對象
*/
public class ClueNode<T> {
// 用於存放節點的權
private T value;
private boolean lflag;
private boolean rflag;
// 左節點
private ClueNode<T> leftNode;
// 右節點
private ClueNode<T> rightNode;
public ClueNode(T value) {
this.value = value;
}
public ClueNode<T> getLeftNode() {
return leftNode;
}
public boolean isLflag() {
return lflag;
}
public void setLflag(boolean lflag) {
this.lflag = lflag;
}
public boolean isRflag() {
return rflag;
}
public void setRflag(boolean rflag) {
this.rflag = rflag;
}
public void setLeftNode(ClueNode<T> leftNode) {
this.leftNode = leftNode;
}
public ClueNode<T> getRightNode() {
return rightNode;
}
public void setRightNode(ClueNode<T> rightNode) {
this.rightNode = rightNode;
}
/**
* 前序遍歷
*/
public void frontShow() {
if (this == null) {
return;
}
//打印根節點
System.out.println(this.value);
if (this.leftNode != null) {
this.leftNode.frontShow();
}
if (this.rightNode != null) {
this.rightNode.frontShow();
}
}
/**
* 中序遍歷
*/
public void middelShow() {
if (this == null) {
return;
}
if (this.leftNode != null) {
this.leftNode.middelShow();
}
//打印根節點
System.out.println(this.value);
if (this.rightNode != null) {
this.rightNode.middelShow();
}
}
/**
* 後續遍歷
*/
public void afterShow() {
if (this == null) {
return;
}
if (this.leftNode != null) {
this.leftNode.afterShow();
}
if (this.rightNode != null) {
this.rightNode.afterShow();
}
//打印根節點
System.out.println(this.value);
}
/**
* 前序查找
*
* @param value
*/
public ClueNode frontSearch(T value) {
ClueNode treeNode = null;
if (this.value == value) {
return this;
}
if (this.leftNode != null) {
treeNode = this.leftNode.frontSearch(value);
}
if (treeNode != null) {
return treeNode;
}
if (this.rightNode != null) {
treeNode = this.rightNode.frontSearch(value);
}
return treeNode;
}
/**
* 前序查找
*
* @param value
*/
public ClueNode middleSearch(T value) {
ClueNode treeNode = null;
if (this.leftNode != null) {
treeNode = this.leftNode.frontSearch(value);
}
if (treeNode != null) {
return treeNode;
}
if (this.value == value) {
return this;
}
if (this.rightNode != null) {
treeNode = this.rightNode.frontSearch(value);
}
return treeNode;
}
/**
* 前序查找
*
* @param value
*/
public ClueNode afterSearch(T value) {
ClueNode treeNode = null;
if (this.leftNode != null) {
treeNode = this.leftNode.frontSearch(value);
}
if (treeNode != null) {
return treeNode;
}
if (this.rightNode != null) {
treeNode = this.rightNode.frontSearch(value);
}
if (treeNode != null) {
return treeNode;
}
if (this.value == value) {
return this;
}
return treeNode;
}
}
線索二叉樹的線索化,和遍歷方法,代碼以中序遍歷爲例,二叉樹線索化後,調用中序遍歷與調用線索化threadNodes()後調用遍歷線索二叉樹方法threadIterate()的結果是一樣的。
package ClueBinary;
/**
* 線索二叉樹
*/
public class ClueBinaryTree<T> {
private ClueNode<T> root;
private ClueNode<T> pre;
public ClueBinaryTree(ClueNode<T> root) {
this.root = root;
}
/**
* 遍歷線索二叉樹
*/
public void threadIterate(){
//臨時存儲當前遍歷節點
ClueNode<T> node = root;
while (node!=null) {
if (root != null) {
//循環找到最開始的節點
while (!node.isLflag()) {
node = node.getLeftNode();
}
// 打印當前節點
System.out.println(node.getValue());
//如果當前節點的右指針指向的的是後繼節點,循環找到最後一個
while (node.isRflag()) {
node = node.getRightNode();
System.out.println(node.getValue());
}
//替換遍歷的節點
node = node.getRightNode();
}
}
}
/**
* 中序線索化二叉樹
*/
public void threadNodes(){
threadNodes(root);
}
/**
* 遍歷線索二叉樹
* @param node
*/
public void threadNodes(ClueNode<T> node){
if (node == null){
return;
}
// 處理左子樹
threadNodes(node.getLeftNode());
//處理前驅節點
if (node.getLeftNode() == null){
node.setLeftNode(pre);
node.setLflag(true);
}
//處理前驅節點的右指針
if (pre!=null && pre.getRightNode() == null){
pre.setRightNode(node);
pre.setRflag(true);
}
pre = node;
//處理右子樹
threadNodes(node.getRightNode());
}
/**
* 中序遍歷
*/
public void middleShow(){
root.middelShow();
}
/**
* 前序遍歷
*/
public void frontShow(){
if (root == null){
return;
}
root.frontShow();
}
}