二叉樹(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