數據結構(二)之二叉樹

基礎概念  

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

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

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

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

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

 

二叉樹的遍歷spa

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

前序遍歷blog

  前序遍歷的規則是:若二叉樹爲空,則空操做返回,不然先訪問根節點,而後前序遍歷左子樹,再前序遍歷右子樹排序

 

中序遍歷get

   中序遍歷的規則是:若樹爲空,則空操做返回;不然從根節點開始(注意並非先訪問根節點),中序遍歷根節點的左子樹,而後是訪問根節點,最後中序遍歷右子樹。能夠看到,若是是二叉排序樹,中序遍歷的結果就是個有序序列。

 

 

後序遍歷

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

 

刪除結點

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

    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 
相關文章
相關標籤/搜索