日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

线索二叉树原理及前序、中序线索化(Java版)

發布時間:2025/3/21 java 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线索二叉树原理及前序、中序线索化(Java版) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載

原文地址:https://blog.csdn.net/UncleMing5371/article/details/54176252

一、線索二叉樹原理

??????前面介紹二叉樹原理及特殊二叉樹文章中提到,二叉樹可以使用兩種存儲結構:順序存儲二叉鏈表。在使用二叉鏈表的存儲結構的過程中,會存在大量的空指針域,為了充分利用這些空指針域,引申出了“線索二叉樹”。回顧一下二叉鏈表存儲結構,如下圖:?




??????通過觀察上面的二叉鏈表,存在著若干個沒有指向的空指針域。對于一個有n個節點的二叉鏈表,每個節點有指向左右節點的2個指針域,整個二叉鏈表存在2n個指針域。而n個節點的二叉鏈表有n-1條分支線,那么空指針域的個數=2n-(n-1) = n+1個空指針域,從存儲空間的角度來看,這n+1個空指針域浪費了內存資源。?
??????從另外一個角度來分析,如果我們想知道按中序方式遍歷二叉鏈表時B節點的前驅節點或者后繼節點時,必須要按中序方式遍歷二叉鏈表才能夠知道結果,每次需要結果時都需要進行一次遍歷,是否可以考慮提前存儲這種前驅和后繼的關系來提高時間效率呢??
??????綜合以上兩方面的分析,可以通過充分利用二叉鏈表中的空指針域,存放節點在某種遍歷方式下的前驅和后繼節點的指針。 我們把這種指向前驅和后繼的指針成為線索,加上線索的二叉鏈表成為線索鏈表,對應的二叉樹就成為“線索二叉樹(Threaded Binary Tree) ?。


二、構建線索二叉樹過程

1、我們對二叉樹進行中序遍歷(不了解二叉樹遍歷請參考二叉樹及特殊二叉樹介紹),將所有的節點右子節點為空的指針域指向它的后繼節點。如下圖:?




??????通過中序遍歷我們知道H的right指針為空,并且H的后繼節點為D(如上圖第1步),I的right指針為空,并且I的后繼節點為B(如上圖第2步),以此類推,知道G的后繼節點為null,則G的right指針指向null。

2、接下來將這顆二叉樹的所有節點左指針域為空的指針域指向它的前驅節點。如下圖:?




??????如上圖,H的left指針域指向Null(如第1步),I的前驅節點是D,則I的left指針指向D,以此類推。

??????通過上面兩步完成了整個二叉樹的線索化,最后結果如下圖:?




??????通過觀察上圖(藍色虛線代表后繼、綠色虛線代表前驅),可以看出,線索二叉樹,等于是把一棵二叉樹轉變成了一個“ 特殊的雙向鏈表 “(后面會解釋為什么叫特殊的雙向鏈表),這樣對于我們的新增、刪除、查找節點帶來了方便。所以我們對二叉樹以某種次序遍歷使其變為線索二叉樹的過程稱做是線索化。如下圖:?

?

??????仔細分析上面的雙向鏈表,與線索化之后的二叉樹相比,比如節點D與后繼節點I,在完成線索化之后,并沒有直接線索指針,而是存在父子節點的指針;節點A與節點F,在線索化完成之后,節點A并沒有直接指向后繼節點F的線索指針,而是通過父子節點遍歷可以找到最終的節點F,前驅節點也存在同樣的問題,正因為很多節點之間不存在直接的線索,所以我將此雙向鏈表稱做“ 特殊的雙向鏈表 ”,再使用過程中根據指針是線索指針還是子節點指針來分別處理,所以在每個節點需要標明當前的左右指針是線索指針還是子節點指針,這就需要修改節點的數據結構。修改后的數據結構如下:

class Node {String data; //數據域Node left; //左指針域Node right; //右指針域byte leftType; //左指針域類型 0:指向子節點、1:前驅或后繼線索byte rightType; //右指針域類型 0:指向子節點、1:前驅或后繼線索}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

最終的二叉鏈表修改為如下圖的樣子:?




三、線索二叉樹的代碼(Java版)

下面是中序線索化二叉樹的實現代碼:

/*** @Title: 二叉樹相關操作* @Description:* @Author: Uncle Ming* @Date:2017年1月6日 下午2:49:14* @Version V1.0*/ public class ThreadBinaryTree {private Node preNode; //線索化時記錄前一個節點//節點存儲結構static class Node {String data; //數據域Node left; //左指針域Node right; //右指針域boolean isLeftThread = false; //左指針域類型 false:指向子節點、true:前驅或后繼線索boolean isRightThread = false; //右指針域類型 false:指向子節點、true:前驅或后繼線索Node(String data) {this.data = data;}}/*** 通過數組構造一個二叉樹(完全二叉樹)* @param array* @param index* @return*/static Node createBinaryTree(String[] array, int index) {Node node = null;if(index < array.length) {node = new Node(array[index]);node.left = createBinaryTree(array, index * 2 + 1);node.right = createBinaryTree(array, index * 2 + 2);}return node;}/*** 中序線索化二叉樹* @param node 節點*/void inThreadOrder(Node node) {if(node == null) {return;}//處理左子樹inThreadOrder(node.left);//左指針為空,將左指針指向前驅節點if(node.left == null) {node.left = preNode;node.isLeftThread = true;}//前一個節點的后繼節點指向當前節點if(preNode != null && preNode.right == null) {preNode.right = node;preNode.isRightThread = true;}preNode = node;//處理右子樹inThreadOrder(node.right);}/*** 中序遍歷線索二叉樹,按照后繼方式遍歷(思路:找到最左子節點開始)* @param node*/void inThreadList(Node node) {//1、找中序遍歷方式開始的節點while(node != null && !node.isLeftThread) {node = node.left;}while(node != null) {System.out.print(node.data + ", ");//如果右指針是線索if(node.isRightThread) {node = node.right;} else { //如果右指針不是線索,找到右子樹開始的節點node = node.right;while(node != null && !node.isLeftThread) {node = node.left;}}}}/*** 中序遍歷線索二叉樹,按照前驅方式遍歷(思路:找到最右子節點開始倒序遍歷)* @param node*/void inPreThreadList(Node node) {//1、找最后一個節點while(node.right != null && !node.isRightThread) {node = node.right;}while(node != null) {System.out.print(node.data + ", ");//如果左指針是線索if(node.isLeftThread) {node = node.left;} else { //如果左指針不是線索,找到左子樹開始的節點node = node.left;while(node.right != null && !node.isRightThread) {node = node.right;}}}}/*** 前序線索化二叉樹* @param node*/void preThreadOrder(Node node) {if(node == null) {return;}//左指針為空,將左指針指向前驅節點if(node.left == null) {node.left = preNode;node.isLeftThread = true;}//前一個節點的后繼節點指向當前節點if(preNode != null && preNode.right == null) {preNode.right = node;preNode.isRightThread = true;}preNode = node;//處理左子樹if(!node.isLeftThread) {preThreadOrder(node.left);}//處理右子樹if(!node.isRightThread) {preThreadOrder(node.right);}}/*** 前序遍歷線索二叉樹(按照后繼線索遍歷)* @param node*/void preThreadList(Node node) {while(node != null) {while(!node.isLeftThread) {System.out.print(node.data + ", ");node = node.left;}System.out.print(node.data + ", ");node = node.right;}}public static void main(String[] args) {String[] array = {"A", "B", "C", "D", "E", "F", "G", "H"};Node root = createBinaryTree(array, 0);ThreadBinaryTree tree = new ThreadBinaryTree();tree.inThreadOrder(root);System.out.println("中序按后繼節點遍歷線索二叉樹結果:");tree.inThreadList(root);System.out.println("\n中序按后繼節點遍歷線索二叉樹結果:");tree.inPreThreadList(root);Node root2 = createBinaryTree(array, 0);ThreadBinaryTree tree2 = new ThreadBinaryTree();tree2.preThreadOrder(root2);tree2.preNode = null;System.out.println("\n前序按后繼節點遍歷線索二叉樹結果:");tree.preThreadList(root2);} }

四、小結

  • 線索化的實質就是將二叉鏈表中的空指針改為指向前驅節點或后繼節點的線索;
  • 線索化的過程就是修改二叉鏈表中空指針的過程,可以按照前序、中序、后序的方式進行遍歷,分別生成不同的線索二叉樹;
  • 有了線索二叉樹之后,我們再次遍歷時,就相當于操作一個雙向鏈表。
  • 使用場景:如果我們在使用二叉樹過程中經常需要遍歷二叉樹或者查找節點的前驅節點和后繼節點,可以考慮采用線索二叉樹存儲結構。
  • 總結

    以上是生活随笔為你收集整理的线索二叉树原理及前序、中序线索化(Java版)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。