線索化二叉樹詳解
說明
- 線索化二叉樹,由字面意思,就是將二叉樹的節點拿線索連接起來
- 實質上,也就是將二叉樹的葉子節點左右指針域彼此連接一個節點
- 二叉樹的非葉子節點的左右指針域都各自連接了一個節點,但是葉子節點的左右指針域是空的,因此考慮將葉子節點的左右指針域按照某種遍歷次序連接起來
- 按照二叉樹的遍歷方式,有前序中序后續三種遍歷方式,因此可以形成三種鏈式結構
- 每個葉子節點前一個節點稱為前驅節點,后一個節點稱為后繼節點,如果當前節點沒有前驅或者后繼節點,則直接置為空
- 以中序線索化二叉樹為例,編寫中序線索化二叉樹的方法
- 先判斷當前節點是否為空,如果為空,則直接返回
- 否則先向左遞歸線索化二叉樹的左子樹
- 然后再線索化當前節點,定義屬性pre保存當前節點的前一個節點,因此當前節點的前一個節點置為pre
- 注意當前節點的后一個節點,需要用pre保存當前節點,然后遍歷到后一個節點,然后用pre指向
- 注意第一個節點和最后一個節點
- 中序線索化如下,前序和后續類似
源碼及分析
節點類
//創建節點
class HeroNode{
//編號
private int no;
//姓名
private String name;
//左子樹
private HeroNode left;
//右子樹
private HeroNode right;
//線索化的前驅節點類型,是節點還是樹,假定 0 為樹 , 1 為節點
private int leftType;
//線索化的后繼節點類型
private int rightType;
public int getLeftType() {
return leftType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public int getRightType() {
return rightType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
//構造器,左子樹和右子樹默認為空
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
//刪除節點
/**
*
* @param no 要刪除的節點編號
*/
public void delNode(int no){
//判斷當前節點的左子樹是否為空,如果不為空,再判斷是否為要刪除的節點
if (this.left != null && this.left.no == no){
this.left = null;
}
//判斷當前節點的右子樹是否為空,如果不為空,再判斷是否為要刪除的節點
if (this.right != null && this.right.no == no){
this.right = null;
}
//否則向左向右遞歸
if (this.left != null){
this.left.delNode(no);
}
if (this.right != null){
this.right.delNode(no);
}
}
//前序中序后序遍歷主要區別在於父節點的輸出位置不同,
/**
* 前序遍歷先輸出父節點信息,然后判斷左子樹是否為空,如果不為空,則遞歸前序遍歷
* 然后再判斷右子樹是否為空,如果不為空,則遞歸遍歷前序遍歷
*/
//前序遍歷
public void preOrder(){
//先輸入當前節點信息
System.out.println(this);
//然后判斷當前節點的左子樹是否為空
if (this.left != null){
this.left.preOrder();
}
//再判斷當前節點的右子樹是否為空
if (this.right != null){
this.right.preOrder();
}
}
//中序遍歷
public void infixOrder(){
//先判斷當前節點的左子樹是否為空
if (this.left != null){
this.left.infixOrder();
}
//再輸出當前節點的信息
System.out.println(this);
//然后再判斷當前節點的右子樹是否為空
if (this.right != null){
this.right.infixOrder();
}
}
//后序遍歷
public void postOrder(){
//先判斷當前節點的左子樹是否為空
if (this.left != null){
this.left.postOrder();
}
//再判斷當前節點的右子樹是否為空
if (this.right != null){
this.right.postOrder();
}
//最后輸出當前節點的信息
System.out.println(this);
}
//前序查找
/**
* 前序遍歷查找
* @param no 要查找的節點編號
* @return 返回查找的結果
*/
public HeroNode preOrderSearch(int no){
//先判斷當前節點是不是要查找的節點
if (this.no == no){
return this;
}
//如果當前節點不是要查找的節點,則判斷左子樹是否為空,若不為空,則遞歸前序查找
HeroNode resNode = null;
if (this.left != null){
resNode = this.left.preOrderSearch(no);
}
//如果在左子樹找到,則直接返回
if (resNode != null){
return resNode;
}
//如果左子樹也沒有找到,則判斷右子樹是否為空,並遞歸
if (this.right != null){
resNode = this.right.preOrderSearch(no);
}
return resNode;
}
//中序查找
/**
* 中序遍歷查找
* @param no 要查找的節點編號
* @return 返回查找的結果
*/
public HeroNode infixOrderSearch(int no){
//先判斷當前節點左子樹是否為空,如果不為空則遞歸中序查找
//定義變量保存查找的結果
HeroNode resNode = null;
if (this.left != null){
resNode = this.left.preOrderSearch(no);
}
//如果查找到,則直接返回
if (resNode != null){
return resNode;
}
//如果沒有找到,判斷當前節點是不是要查找的節點
if (this.no == no){
return this;
}
//如果還沒有找到,則判斷右子樹是否為空,不為空則遞歸中序查找
if (this.right != null){
resNode = this.right.infixOrderSearch(no);
}
return resNode;
}
//后序查找
/**
* 后續遍歷查找
* @param no 要查找的節點編號
* @return 返回查找的結果
*/
public HeroNode postOrderSearch(int no){
//判斷當前節點的左子樹是否為空,如果不為空,則遞歸后續查找
HeroNode resNode = null;
if (this.left != null){
resNode = this.left.postOrderSearch(no);
}
if (resNode != null){
return resNode;
}
if (this.right != null){
resNode = this.right.postOrderSearch(no);
}
if (resNode != null){
return resNode;
}
if (this.no == no){
return this;
}
return resNode;
}
}
線索化二叉樹類
//創建一顆線索化二叉樹
class ThreaderBinaryTree{
//二叉樹必有根節點
private HeroNode root;
//定義變量指向前驅節點,默認為空
private HeroNode pre = null;
public void setRoot(HeroNode root) {
this.root = root;
}
//編寫中序線索化二叉樹的方法
/**
*
* @param node node為當前要中序線索化的節點
*/
public void infixThreadedBinaryTree(HeroNode node){
//先判斷當前節點是否為空
if (node == null){
return;
}
//如果不為空,先線索化左子樹
infixThreadedBinaryTree(node.getLeft());
//再線索化當前節點
//當前節點的前驅節點為pre,后繼節點需要在下一個節點連通,因為是單向的
//設置當前節點的前驅節點,並設置前驅節點類型
if (node.getLeft() == null){
node.setLeft(pre);
node.setLeftType(1);
}
//設置當前節點的后繼節點及其類型
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setRightType(1);
}
//讓pre指向當前節點
pre = node;
//最后再線索化右子樹
infixThreadedBinaryTree(node.getRight());
}
//重載線索化的方法
public void infixThreadedBinaryTree(){
this.infixThreadedBinaryTree(root);
}
//刪除節點
/**
*
* @param no 要刪除的節點編號
*/
public void delNode(int no){
//先判斷二叉樹是否為空
if (this.root != null){
//再判斷當前root節點是不是要刪除的節點
if (this.root.getNo() == no){
root = null;
}else {
this.root.delNode(no);
}
}else {
System.out.println("二叉樹為空,不能刪除...");
}
}
//前序遍歷
public void preOrder(){
if (this.root != null){
this.root.preOrder();
}else {
System.out.println("二叉樹為空...");
}
}
//中序遍歷
public void infixOrder(){
if (this.root != null){
this.root.infixOrder();
}else {
System.out.println("二叉樹為空...");
}
}
//后續遍歷
public void postOrder(){
if (this.root != null){
this.root.postOrder();
}else {
System.out.println("二叉樹為空...");
}
}
//前序查找
public HeroNode preOrderSearch(int no){
if (this.root != null){
return this.root.preOrderSearch(no);
}else {
return null;
}
}
//中序查找
public HeroNode infixOrderSearch(int no){
if (this.root != null){
return this.root.infixOrderSearch(no);
}else {
return null;
}
}
//后續查找
public HeroNode postOrderSearch(int no){
if (this.root != null){
return this.root.postOrderSearch(no);
}else {
return null;
}
}
}
測試類
public static void main(String[] args) {
ThreaderBinaryTree threaderBinaryTree = new ThreaderBinaryTree();
HeroNode root = new HeroNode(1,"tom");
HeroNode node2 = new HeroNode(3,"jack");
HeroNode node3 = new HeroNode(6,"smith");
HeroNode node4 = new HeroNode(8,"king");
HeroNode node5 = new HeroNode(10,"mary");
HeroNode node6 = new HeroNode(14,"dop");
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
threaderBinaryTree.setRoot(root);
//進行線索化
threaderBinaryTree.infixThreadedBinaryTree();
//測試線索化的結果
System.out.println("node5的前一個節點 = " + node5.getLeft());
System.out.println("node5的后一個節點 = " + node5.getRight());
}