java實現二叉樹查找樹

二叉樹(binary)是一種特殊的樹。二叉樹的每個節點最多只能有2個子節點:

二叉樹

由於二叉樹的子節點數目確定,所以可以直接採用上圖方式在內存中實現。每個節點有一個左子節點(left children)和右子節點(right children)。左子節點是左子樹的根節點,右子節點是右子樹的根節點。

 

如果我們給二叉樹加一個額外的條件,就可以得到一種被稱作二叉搜索樹(binary search tree)的特殊二叉樹。二叉搜索樹要求:每個節點都不比它左子樹的任意元素小,而且不比它的右子樹的任意元素大。

(如果我們假設樹中沒有重複的元素,那麼上述要求可以寫成:每個節點比它左子樹的任意節點大,而且比它右子樹的任意節點小)

二叉搜索樹,注意樹中元素的大小

二叉搜索樹可以方便的實現搜索算法。在搜索元素x的時候,我們可以將x和根節點比較:

1. 如果x等於根節點,那麼找到x,停止搜索 (終止條件)

2. 如果x小於根節點,那麼搜索左子樹

3. 如果x大於根節點,那麼搜索右子樹

二叉搜索樹所需要進行的操作次數最多與樹的深度相等。n個節點的二叉搜索樹的深度最多爲n,最少爲log(n)。

 

下面是用java實現的二叉搜索樹,並有搜索,插入,刪除,尋找最大最小節點的操作。

刪除節點相對比較複雜。刪除節點後,有時需要進行一定的調整,以恢復二叉搜索樹的性質(每個節點都不比它左子樹的任意元素小,而且不比它的右子樹的任意元素大)。

  • 葉節點可以直接刪除。
  • 刪除非葉節點時,比如下圖中的節點8,我們可以刪除左子樹中最大的元素(或者右樹中最大的元素),用刪除的節點來補充元素8產生的空缺。但該元素可能也不是葉節點,所以它所產生的空缺需要其他元素補充…… 直到最後刪除一個葉節點。上述過程可以遞歸實現。

刪除節點

刪除節點後的二叉搜索樹

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
import java.util.ArrayList;
import java.util.List;
 
public class BinarySearchTree {
 
    // 樹的根結點
    private TreeNode root = null;
 
    // 遍歷結點列表
    private List<TreeNode> nodelist = new ArrayList<TreeNode>();
 
    private class TreeNode {
 
        private int key;
        private TreeNode leftChild;
        private TreeNode rightChild;
        private TreeNode parent;
 
        public TreeNode(int key, TreeNode leftChild, TreeNode rightChild,
                TreeNode parent) {
            this.key = key;
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.parent = parent;
        }
 
        public int getKey() {
            return key;
        }
 
        public String toString() {
            String leftkey = (leftChild == null "" : String
                    .valueOf(leftChild.key));
            String rightkey = (rightChild == null "" : String
                    .valueOf(rightChild.key));
            return "(" + leftkey + " , " + key + " , " + rightkey + ")";
        }
 
    }
 
    /**
     * isEmpty: 判斷二叉查找樹是否爲空;若爲空,返回 true ,否則返回 false .
     *
     */
    public boolean isEmpty() {
        if (root == null) {
            return true;
        else {
            return false;
        }
    }
 
    /**
     * TreeEmpty: 對於某些二叉查找樹操作(比如刪除關鍵字)來說,若樹爲空,則拋出異常。
     */
    public void TreeEmpty() throws Exception {
        if (isEmpty()) {
            throw new Exception("樹爲空!");
        }
    }
 
    /**
     * search: 在二叉查找樹中查詢給定關鍵字
     *
     * @param key
     *            給定關鍵字
     * @return 匹配給定關鍵字的樹結點
     */
    public TreeNode search(int key) {
        TreeNode pNode = root;
        while (pNode != null && pNode.key != key) {
            if (key < pNode.key) {
                pNode = pNode.leftChild;
            else {
                pNode = pNode.rightChild;
            }
        }
        return pNode;
    }
 
    /**
     * minElemNode: 獲取二叉查找樹中的最小關鍵字結點
     *
     * @return 二叉查找樹的最小關鍵字結點
     * @throws Exception
     *             若樹爲空,則拋出異常
     */
    public TreeNode minElemNode(TreeNode node) throws Exception {
        if (node == null) {
            throw new Exception("樹爲空!");
        }
        TreeNode pNode = node;
        while (pNode.leftChild != null) {
            pNode = pNode.leftChild;
        }
        return pNode;
    }
 
    /**
     * maxElemNode: 獲取二叉查找樹中的最大關鍵字結點
     *
     * @return 二叉查找樹的最大關鍵字結點
     * @throws Exception
     *             若樹爲空,則拋出異常
     */
    public TreeNode maxElemNode(TreeNode node) throws Exception {
        if (node == null) {
            throw new Exception("樹爲空!");
        }
        TreeNode pNode = node;
        while (pNode.rightChild != null) {
            pNode = pNode.rightChild;
        }
        return pNode;
    }
 
    /**
     * successor: 獲取給定結點在中序遍歷順序下的後繼結點
     *
     * @param node
     *            給定樹中的結點
     * @return 若該結點存在中序遍歷順序下的後繼結點,則返回其後繼結點;否則返回 null
     * @throws Exception
     */
    public TreeNode successor(TreeNode node) throws Exception {
        if (node == null) {
            return null;
        }
 
        // 若該結點的右子樹不爲空,則其後繼結點就是右子樹中的最小關鍵字結點
        if (node.rightChild != null) {
            return minElemNode(node.rightChild);
        }
        // 若該結點右子樹爲空
        TreeNode parentNode = node.parent;
        while (parentNode != null && node == parentNode.rightChild) {
            node = parentNode;
            parentNode = parentNode.parent;
        }
        return parentNode;
    }
 
    /**
     * precessor: 獲取給定結點在中序遍歷順序下的前趨結點
     *
     * @param node
     *            給定樹中的結點
     * @return 若該結點存在中序遍歷順序下的前趨結點,則返回其前趨結點;否則返回 null
     * @throws Exception
     */
    public TreeNode precessor(TreeNode node) throws Exception {
        if (node == null) {
            return null;
        }
 
        // 若該結點的左子樹不爲空,則其前趨結點就是左子樹中的最大關鍵字結點
        if (node.leftChild != null) {
            return maxElemNode(node.leftChild);
        }
        // 若該結點左子樹爲空
        TreeNode parentNode = node.parent;
        while (parentNode != null && node == parentNode.leftChild) {
            node = parentNode;
            parentNode = parentNode.parent;
        }
        return parentNode;
    }
 
    /**
     * insert: 將給定關鍵字插入到二叉查找樹中
     *
     * @param key
     *            給定關鍵字
     */
    public void insert(int key) {
        TreeNode parentNode = null;
        TreeNode newNode = new TreeNode(key, nullnullnull);
        TreeNode pNode = root;
        if (root == null) {
            root = newNode;
            return;
        }
        while (pNode != null) {
            parentNode = pNode;
            if (key < pNode.key) {
                pNode = pNode.leftChild;
            else if (key > pNode.key) {
                pNode = pNode.rightChild;
            else {
                // 樹中已存在匹配給定關鍵字的結點,則什麼都不做直接返回
                return;
            }
        }
        if (key < parentNode.key) {
            parentNode.leftChild = newNode;
            newNode.parent = parentNode;
        else {
            parentNode.rightChild = newNode;
            newNode.parent = parentNode;
        }
 
    }
 
    /**
     * insert: 從二叉查找樹中刪除匹配給定關鍵字相應的樹結點
     *
     * @param key
     *            給定關鍵字
     */
    public void delete(int key) throws Exception {
        TreeNode pNode = search(key);
        if (pNode == null) {
            throw new Exception("樹中不存在要刪除的關鍵字!");
        }
        delete(pNode);
    }
 
    /**
     * delete: 從二叉查找樹中刪除給定的結點.
     *
     * @param pNode
     *            要刪除的結點
     *
     *            前置條件: 給定結點在二叉查找樹中已經存在
     * @throws Exception
     */
    private void delete(TreeNode pNode) throws Exception {
        if (pNode == null) {
            return;
        }
        if (pNode.leftChild == null && pNode.rightChild == null) { // 該結點既無左孩子結點,也無右孩子結點
            TreeNode parentNode = pNode.parent;
            if (pNode == parentNode.leftChild) {
                parentNode.leftChild = null;
            else {
                parentNode.rightChild = null;
            }
            return;
        }
        if (pNode.leftChild == null && pNode.rightChild != null) { // 該結點左孩子結點爲空,右孩子結點非空
            TreeNode parentNode = pNode.parent;
            if (pNode == parentNode.leftChild) {
                parentNode.leftChild = pNode.rightChild;
                pNode.rightChild.parent = parentNode;
            else {
                parentNode.rightChild = pNode.rightChild;
                pNode.rightChild.parent = parentNode;
            }
            return;
        }
        if (pNode.leftChild != null && pNode.rightChild == null) { // 該結點左孩子結點非空,右孩子結點爲空
            TreeNode parentNode = pNode.parent;
            if (pNode == parentNode.leftChild) {
                parentNode.leftChild = pNode.leftChild;
                pNode.rightChild.parent = parentNode;
            else {
                parentNode.rightChild = pNode.leftChild;
                pNode.rightChild.parent = parentNode;
            }
            return;
        }
        // 該結點左右孩子結點均非空,則刪除該結點的後繼結點,並用該後繼結點取代該結點
        TreeNode successorNode = successor(pNode);
        delete(successorNode);
        pNode.key = successorNode.key;
    }
 
    /**
     * inOrderTraverseList: 獲得二叉查找樹的中序遍歷結點列表
     *
     * @return 二叉查找樹的中序遍歷結點列表
     */
    public List<TreeNode> inOrderTraverseList() {
        if (nodelist != null) {
            nodelist.clear();
        }
        inOrderTraverse(root);
        return nodelist;
    }
 
    /**
     * inOrderTraverse: 對給定二叉查找樹進行中序遍歷
     *
     * @param root
     *            給定二叉查找樹的根結點
     */
    private void inOrderTraverse(TreeNode root) {
        if (root != null) {
            inOrderTraverse(root.leftChild);
            nodelist.add(root);
            inOrderTraverse(root.rightChild);
        }
    }
 
    /**
     * toStringOfOrderList: 獲取二叉查找樹中關鍵字的有序列表
     *
     * @return 二叉查找樹中關鍵字的有序列表
     */
    public String toStringOfOrderList() {
        StringBuilder sbBuilder = new StringBuilder(" [ ");
        for (TreeNode p : inOrderTraverseList()) {
            sbBuilder.append(p.key);
            sbBuilder.append(" ");
        }
        sbBuilder.append("]");
        return sbBuilder.toString();
    }
 
    /**
     * 獲取該二叉查找樹的字符串表示
     */
    public String toString() {
        StringBuilder sbBuilder = new StringBuilder(" [ ");
        for (TreeNode p : inOrderTraverseList()) {
            sbBuilder.append(p);
            sbBuilder.append(" ");
        }
        sbBuilder.append("]");
        return sbBuilder.toString();
    }
 
    public TreeNode getRoot() {
        return root;
    }
 
    public static void testNode(BinarySearchTree bst, TreeNode pNode)
            throws Exception {
        System.out.println("本結點: " + pNode);
        System.out.println("前趨結點: " + bst.precessor(pNode));
        System.out.println("後繼結點: " + bst.successor(pNode));
    }
 
    public static void testTraverse(BinarySearchTree bst) {
        System.out.println("二叉樹遍歷:" + bst);
        System.out.println("二叉查找樹轉換爲有序列表: " + bst.toStringOfOrderList());
    }
 
    public static void main(String[] args) {
        try {
            BinarySearchTree bst = new BinarySearchTree();
            System.out.println("查找樹是否爲空? " + (bst.isEmpty() ? "是" "否"));
            int[] keys = new int[] { 15, 6, 18, 3, 7, 13, 20, 2, 9, 4 };
            for (int key : keys) {
                bst.insert(key);
            }
            System.out.println("查找樹是否爲空? " + (bst.isEmpty() ? "是" "否"));
            TreeNode minkeyNode = bst.minElemNode(bst.getRoot());
            System.out.println("最小關鍵字: " + minkeyNode.getKey());
            testNode(bst, minkeyNode);
            TreeNode maxKeyNode = bst.maxElemNode(bst.getRoot());
            System.out.println("最大關鍵字: " + maxKeyNode.getKey());
            testNode(bst, maxKeyNode);
            System.out.println("根結點關鍵字: " + bst.getRoot().getKey());
            testNode(bst, bst.getRoot());
            testTraverse(bst);
            System.out.println("****************************** ");
            testTraverse(bst);
        catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }
 
}
  

  

博客地址: http://www.cnblogs.com/oumyye/
博客版權: 本文以學習、研究和分享爲主,歡迎轉載,轉載請務必註明出處,謝謝合作。
如果文中有不妥或者錯誤的地方請指出。如果覺得本文對你有所幫助不如【推薦】一下!如果你有更好的建議,不如留言一起討論,共同進步!
發佈了8 篇原創文章 · 獲贊 4 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章