基礎概念
二叉樹(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