線索二叉樹的前序中序後序線索化
阿新 • • 發佈:2021-08-10
13.2 線索化二叉樹
在我們構建成一個二叉樹的時候,我們發現葉子節點左右節點的指標並沒有完全利用上。
如果我們希望充分利用各個節點的左右指標,讓各個節點可以指向自己前驅,即父節點,於是線索化二叉樹就應運而生
特點
- n個節點的二叉連結串列中含有
n + 1
公式2*n-(n-1) = n + 1
個空指標域。利用二叉連結串列中的空指標域,存放指向結點在某種遍歷次序下的前驅和後繼結點的指標(這種附加的指標稱為“線索”) - 這種加上了線索的二叉連結串列稱為 線索連結串列,相應的二叉樹稱為 線索二叉樹(Threaded Binary Tree), 根據線索性質的不同,線索二叉樹可分為前序線索二叉樹、中序線索二叉樹、和後續線索二叉樹
- 一個結點的前一個結點,稱為前驅結點
- 一個結點的後一個結點,稱為後繼結點
需求:
將下面的二叉樹進行中序線索二叉樹。中序遍歷的數列為{8, 3, 10, 1, 14, 6}
說明:當線索化二叉樹後,Node結點的屬性 left 和 right , 存在一下情況:
- left 指向的是左子樹,也可能是指向的前驅結點,比如 1 結點的 left 指向的左子樹,而 10 結點的 left 指向的就是前驅結點
- right 指向的是右子樹,也可能指向的是後繼結點,比如 1 節點的 right 指向的是右子樹,而 10 節點的 right 指向的就是後繼結點
package tree.threadedbinarytree; public class ThreadedBinaryTreeDemo { public static void main(String[] args) { // 測試 把中序線索二叉樹 Node1 root = new Node1(1, "12"); Node1 node2 = new Node1(3, "13"); Node1 node3 = new Node1(6, "16"); Node1 node4 = new Node1(8, "18"); Node1 node5 = new Node1(10, "114"); Node1 node6 = new Node1(14, "114"); // 二叉樹後面要遞迴建立,現在手動建立 root.setLeft(node2); root.setRight(node3); node2.setLeft(node4); node2.setRight(node5); node3.setLeft(node6); // 測試線索化 infixThreadedBinaryTree infixThreadedBinaryTree = new infixThreadedBinaryTree(); infixThreadedBinaryTree.setRoot(root); infixThreadedBinaryTree.threadeNodes(); // 測試:以10號結點為測試 Node1 leftNode = node5.getLeft(); Node1 rightNode = node5.getRight(); System.out.println("10號結點的前驅結點時:" + leftNode); System.out.println("10號結點的後繼結點" + rightNode); } } // 線索化二叉樹 實現了線索化功能的二叉樹 class infixThreadedBinaryTree{ private Node1 root; // 為了實現線索化,需要建立要給指向當前節點的前驅結點的指標 // 在遞迴的進行線索時,pre 總是保留一個結點 private Node1 pre = null; // 過載一把threadeNodes方法 public void threadeNodes(){ this.threadedNodes(root); } // 編寫二叉樹進行中序線索化的方法 /** * * @param node 就是當前需要線索化的結點 */ public void threadedNodes(Node1 node){ // 如果 node == null,不能進行線索化 if (node == null){ return; } // 1. 先線索化左子樹 threadedNodes(node.getLeft()); // 2. 線索化當前結點 // 2.1 先處理當前節點的前驅結點 if (node.getLeft() == null){ // 讓當前節點的左指標指向前驅結點 node.setLeft(pre); // 修改當前節點的左指標的型別,指向前驅結點 node.setLeftType(1); } // 2.2 處理當前節點的後繼結點 if (pre != null && pre.getRight() == null){ // 讓前驅結點的右指標指向當前結點 pre.setRight(node); // 修改前驅結點的右指標型別 pre.setLeftType(1); } // 沒處理一個節點後,讓當前結點時下一個節點的前驅結點 pre = node; // 3. 再線索化右子樹 threadedNodes(node.getRight()); } public Node1 getRoot() { return root; } public void setRoot(Node1 root) { this.root = root; } } // 先建立節點 class Node1{ private int id; private String name; private Node1 left; private Node1 right; // 說明 // 1. 如果leftType == 0 表示的指向的是左子樹,如果1則表示指向前驅結點 // 2. 如果rightType == 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 Node1(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Node1 getLeft() { return left; } public void setLeft(Node1 left) { this.left = left; } public Node1 getRight() { return right; } public void setRight(Node1 right) { this.right = right; } @Override public String toString() { return "Node1{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
前序線索化,後序線索化
注意,前序線索化要新增判斷條件,否則就會陷入無限迭代,無法跳出導致棧滿,判斷條件就是我們宣告的 leftType
和rightType
的值,如果為1,則我們進行遞迴,如果不為1,則我們不進行遞迴,否則會進入死迴圈。
package tree.threadedbinarytree; public class ThreadedBinaryTreeDemo { public static void main(String[] args) { // 測試 把中序線索二叉樹 Node1 root = new Node1(1, "12"); Node1 node2 = new Node1(3, "13"); Node1 node3 = new Node1(6, "16"); Node1 node4 = new Node1(8, "18"); Node1 node5 = new Node1(10, "114"); Node1 node6 = new Node1(14, "114"); // 二叉樹後面要遞迴建立,現在手動建立 root.setLeft(node2); root.setRight(node3); node2.setLeft(node4); node2.setRight(node5); node3.setLeft(node6); // 測試中序線索化 infixThreadedBinaryTree infixThreadedBinaryTree = new infixThreadedBinaryTree(); // infixThreadedBinaryTree.setRoot(root); // infixThreadedBinaryTree.threadeNodes(); // // // 測試:以10號結點為測試 // Node1 leftNode = node5.getLeft(); // Node1 rightNode = node5.getRight(); // System.out.println("10號結點的前驅結點時:" + leftNode); // System.out.println("10號結點的後繼結點" + rightNode); // 測試前序線索化 infixThreadedBinaryTree.setRoot(root); infixThreadedBinaryTree.postNodes(); // 測試:以10號結點為測試 Node1 leftNode = node5.getLeft(); Node1 rightNode = node5.getRight(); System.out.println("10號結點的前驅結點時:" + leftNode); System.out.println("10號結點的後繼結點" + rightNode); } } // 線索化二叉樹 實現了線索化功能的二叉樹 class infixThreadedBinaryTree{ private Node1 root; // 為了實現線索化,需要建立要給指向當前節點的前驅結點的指標 // 在遞迴的進行線索時,pre 總是保留一個結點 private Node1 pre = null; // 過載一把threadeNodes方法 public void threadeNodes(){ this.threadedNodes(root); } // 編寫二叉樹進行中序線索化的方法 /** * * @param node 就是當前需要線索化的結點 */ public void threadedNodes(Node1 node){ // 如果 node == null,不能進行線索化 if (node == null){ return; } // 1. 先線索化左子樹 threadedNodes(node.getLeft()); // 2. 線索化當前結點 // 2.1 先處理當前節點的前驅結點 if (node.getLeft() == null){ // 讓當前節點的左指標指向前驅結點 node.setLeft(pre); // 修改當前節點的左指標的型別,指向前驅結點 node.setLeftType(1); } // 2.2 處理當前節點的後繼結點 if (pre != null && pre.getRight() == null){ // 讓前驅結點的右指標指向當前結點 pre.setRight(node); // 修改前驅結點的右指標型別 pre.setLeftType(1); } // 沒處理一個節點後,讓當前結點時下一個節點的前驅結點 pre = node; // 3. 再線索化右子樹 threadedNodes(node.getRight()); } public void preNodes(){ this.preNodes(root); } // 前序線索化 public void preNodes(Node1 node){ if (node == null){ return; } 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() == 0){ preNodes(node.getLeft()); } if (node.getRightType() == 0){ preNodes(node.getRight()); } } // 後序線索化 public void postNodes(){ this.postNodes(root); } public void postNodes(Node1 node){ if (node == null){ return; } postNodes(node.getLeft()); postNodes(node.getRight()); if (node.getLeft() == null){ node.setLeft(pre); node.setLeftType(1); } if ( pre != null && pre.getRight() == null){ pre.setRight(node); pre.setRightType(1); } pre = node; } public void setRoot(Node1 root) { this.root = root; } } // 先建立節點 class Node1{ private int id; private String name; private Node1 left; private Node1 right; // 說明 // 1. 如果leftType == 0 表示的指向的是左子樹,如果1則表示指向前驅結點 // 2. 如果rightType == 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 Node1(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Node1 getLeft() { return left; } public void setLeft(Node1 left) { this.left = left; } public Node1 getRight() { return right; } public void setRight(Node1 right) { this.right = right; } @Override public String toString() { return "Node1{" + "id=" + id + ", name='" + name + '\'' + '}'; } }