數據結構(二)之二叉樹


基礎概念  

  二叉樹(binary tree)是一棵樹,其中每個結點都不能有多於兩個兒子。

  二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹:

    (1)若左子樹不空,則左子樹上所有結點的值均小於或等於它的根結點的值;

    (2)若右子樹不空,則右子樹上所有結點的值均大於或等於它的根結點的值;

    (3)左、右子樹也分別為二叉排序樹;

 

二叉樹的遍歷

  二叉樹的遍歷是指從根節點出發,按照某種次序依次訪問二叉樹中所有結點,使得每個結點被訪問一次且僅被訪問一次。二叉樹的遍歷方式有很多,主要有前序遍歷,中序遍歷,后序遍歷。

前序遍歷

  前序遍歷的規則是:若二叉樹為空,則空操作返回,否則先訪問根節點,然后前序遍歷左子樹,再前序遍歷右子樹

 

中序遍歷

   中序遍歷的規則是:若樹為空,則空操作返回;否則從根節點開始(注意並不是先訪問根節點),中序遍歷根節點的左子樹,然后是訪問根節點,最后中序遍歷右子樹。可以看到,如果是二叉排序樹,中序遍歷的結果就是個有序序列。

 

 

后序遍歷

  后序遍歷的規則是:若樹為空,則空操作返回;然后先遍歷左子樹,再遍歷右子樹,最后訪問根結點,在遍歷左、右子樹時,仍然先遍歷左子樹,然后遍歷右子樹,最后遍歷根結點。

 

刪除結點

  對於二叉排序樹的其他操作,比如插入,遍歷等,比較容易理解;而刪除操作相對復雜些。對於要刪除的結點,有以下三種情況:

    1.葉子結點;

    2.僅有左子樹或右子樹的結點;

    3.左右子樹都有結點;

  對於1(要刪除結點為葉子結點)直接刪除,即直接解除父節點的引用即可,對於第2種情況(要刪除的結點僅有一個兒子),只需用子結點替換掉父節點即可;而對於要刪除的結點有兩個兒子的情況,比較常用處理邏輯為,在其子樹中找尋一個結點來替換,而這個結點我們成為中序后繼結點。

 

 

  可以看到,我們找到的這個用來替換的結點,可以是刪除結點的右子樹的最小結點(6),也可以是其左子樹的最大結點(4),這樣可以保證替換后樹的整體結構不用發生變化。為什么稱為中序后繼結點呢?我們來看下這棵樹的中序遍歷結果 1-2-3-4-5-6-7-8-9可以很清晰的看到,其實要找的這個結點,可以是結點5的前驅或者后繼。

代碼實現

 1 package treeDemo;  2 
 3 /**
 4  * Created by chengxiao on 2017/02/12.  5  */
 6 public class BinaryTree {  7     //根節點
 8     private Node root;  9     /**
 10  * 樹的結點  11      */
 12     private static class Node{  13         //數據域
 14         private long data;  15         //左子結點
 16         private Node leftChild;  17         //右子結點
 18         private Node rightChild;  19         Node(long data){  20             this.data = data;  21  }  22  }  23 
 24     /**
 25  * 插入結點  26  * @param data  27      */
 28     public void insert(long data){  29         Node newNode = new Node(data);  30         Node currNode = root;  31  Node parentNode;  32         //如果是空樹
 33         if(root == null){  34             root = newNode;  35             return;  36  }  37         while(true){  38             parentNode = currNode;  39             //向右搜尋
 40             if(data > currNode.data){  41                 currNode = currNode.rightChild;  42                 if(currNode == null){  43                     parentNode.rightChild = newNode;  44                     return;  45  }  46             }else{  47                 //向左搜尋
 48                 currNode = currNode.leftChild;  49                 if(currNode == null){  50                     parentNode.leftChild = newNode;  51                     return;  52  }  53  }  54  }  55 
 56  }  57 
 58     /**
 59  * 前序遍歷  60  * @param currNode  61      */
 62     public void preOrder(Node currNode){  63         if(currNode == null){  64             return;  65  }  66         System.out.print(currNode.data+" ");  67  preOrder(currNode.leftChild);  68  preOrder(currNode.rightChild);  69  }  70 
 71     /**
 72  * 中序遍歷  73  * @param currNode  74      */
 75     public void inOrder(Node currNode){  76         if(currNode == null){  77             return;  78  }  79  inOrder(currNode.leftChild);  80         System.out.print(currNode.data+" ");  81  inOrder(currNode.rightChild);  82 
 83  }  84 
 85     /**
 86  * 后序遍歷  87  * @param currNode  88      */
 89     public void postOrder(Node currNode){  90         if(currNode == null){  91             return;  92  }  93  postOrder(currNode.leftChild);  94  postOrder(currNode.rightChild);  95         System.out.print(currNode.data+" ");  96  }  97 
 98     /**
 99  * 查找結點 100  * @param data 101  * @return
102      */
103     public Node find(long data){ 104         Node currNode = root; 105         while(currNode!=null){ 106             if(data>currNode.data){ 107                 currNode = currNode.rightChild; 108             }else if(data<currNode.data){ 109                 currNode = currNode.leftChild; 110             }else{ 111                 return currNode; 112  } 113  } 114         return null; 115  } 116 
117     /**
118  * 刪除結點 分為3種情況 119  * 1.葉子結點 120  * 2.該節點有一個子節點 121  * 3.該節點有二個子節點 122  * @param data 123      */
124     public boolean delete(long data) throws Exception { 125         Node curr = root; 126         //保持一個父節點的引用
127         Node parent = curr; 128         //刪除結點是左子結點還是右子結點,
129         boolean isLeft = true; 130         while(curr != null && curr.data!=data){ 131             parent = curr; 132             if(data > curr.data){ 133                 curr = curr.rightChild; 134                 isLeft = false; 135             }else{ 136                 curr = curr.leftChild; 137                 isLeft = true; 138  } 139  } 140         if(curr==null){ 141             throw new Exception("要刪除的結點不存在"); 142  } 143         //第一種情況,要刪除的結點為葉子結點
144         if(curr.leftChild == null && curr.rightChild == null){ 145             if(curr == root){ 146                 root = null; 147                 return true; 148  } 149             if(isLeft){ 150                 parent.leftChild = null; 151             }else{ 152                 parent.rightChild = null; 153  } 154         }else if(curr.leftChild == null){ 155             //第二種情況,要刪除的結點有一個子節點且是右子結點
156             if(curr == root){ 157                 root = curr.rightChild; 158                 return true; 159  } 160             if(isLeft){ 161                 parent.leftChild = curr.rightChild; 162             }else{ 163                 parent.rightChild = curr.rightChild; 164  } 165         }else if(curr.rightChild == null){ 166             //第二種情況,要刪除的結點有一個子節點且是左子結點
167             if(curr == root){ 168                 root = curr.leftChild; 169                 return true; 170  } 171             if(isLeft){ 172                 parent.leftChild = curr.leftChild; 173             }else{ 174                 parent.rightChild = curr.leftChild; 175  } 176         }else{ 177             //第三種情況,也是最復雜的一種情況,要刪除的結點有兩個子節點,需要找尋中序后繼結點
178             Node succeeder = getSucceeder(curr); 179             if(curr == root){ 180                 root = succeeder; 181                 return  true; 182  } 183             if(isLeft){ 184                 parent.leftChild = succeeder; 185             }else{ 186                 parent.rightChild = succeeder; 187  } 188             //當后繼結點為刪除結點的右子結點
189             succeeder.leftChild = curr.leftChild; 190 
191  } 192         return true; 193  } 194     public Node getSucceeder(Node delNode){ 195         Node succeeder = delNode; 196         Node parent = delNode; 197         Node currNode = delNode.rightChild; 198         //尋找后繼結點
199         while(currNode != null){ 200             parent = succeeder; 201             succeeder = currNode; 202             currNode = currNode.leftChild; 203  } 204         //如果后繼結點不是要刪除結點的右子結點
205         if(succeeder != delNode.rightChild){ 206             parent.leftChild = succeeder.rightChild; 207             //將后繼結點的左右子結點分別指向要刪除結點的左右子節點
208             succeeder.leftChild = delNode.leftChild; 209             succeeder.rightChild = delNode.rightChild; 210  } 211         return succeeder; 212 
213  } 214     public static void main(String []args) throws Exception { 215         BinaryTree binaryTree = new BinaryTree(); 216         //插入操作
217         binaryTree.insert(5); 218         binaryTree.insert(2); 219         binaryTree.insert(8); 220         binaryTree.insert(1); 221         binaryTree.insert(3); 222         binaryTree.insert(6); 223         binaryTree.insert(10); 224         //前序遍歷
225         System.out.println("前序遍歷:"); 226  binaryTree.preOrder(binaryTree.root); 227  System.out.println(); 228         //中序遍歷
229         System.out.println("中序遍歷:"); 230  binaryTree.inOrder(binaryTree.root); 231  System.out.println(); 232         //后序遍歷
233         System.out.println("后序遍歷:"); 234  binaryTree.postOrder(binaryTree.root); 235  System.out.println(); 236         //查找結點
237         Node node = binaryTree.find(10); 238         System.out.println("找到結點,其值為:"+node.data); 239         //刪除結點
240         binaryTree.delete(8); 241         System.out.print("刪除結點8,中序遍歷:"); 242  binaryTree.preOrder(binaryTree.root); 243  } 244 }
二叉樹的基本操作

執行結果

前序遍歷: 5 2 1 3 8 6 10 中序遍歷: 1 2 3 5 6 8 10 后序遍歷: 1 3 2 6 10 8 5 找到結點,其值為:10 刪除結點8,中序遍歷:5 2 1 3 10 6 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM