1. 程式人生 > >Android版資料結構與演算法(八):二叉排序樹

Android版資料結構與演算法(八):二叉排序樹

本文目錄

前兩篇文章我們學習了一些樹的基本概念以及常用操作,本篇我們瞭解一下二叉樹的一種特殊形式:二叉排序樹(Binary Sort Tree),又稱二叉查詢樹(Binary Search Tree),亦稱二叉搜尋樹。

一、二叉排序樹定義

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

  • 若它的左子樹不為空,則左子樹上所有結點的值均小於它的根結點的值

  • 若它的右子樹不為空,則右子樹上所有結點的值均大於它的根結點的值

  • 它的左,右子樹也分別為二叉排序樹

也就是說二叉排序樹中左子樹結點值均小於根結點值,右子樹節點值均大於跟節點值,左右子樹同樣滿足上述約定。

如下圖,即為一顆二叉排序樹:

由二叉樹定義知道,我們通過中序遍歷二叉樹就可以按照從小到大順序排列二叉樹中所有元素。

如上圖中序遍歷結果為:35, 40, 42, 45, 50, 67

二、java程式碼實現二叉排序樹核心方法

下面我們通過java程式碼實現二叉排序樹中的幾個核心方法

我們先看下每個結點類定義如下:

 1class TreeNode{
 2        private int data;
 3        private TreeNode leftChild;
 4        private TreeNode rightChild;
 5        private
TreeNode parent; 6 7 public TreeNode(int data) { 8 this.data = data; 9 this.leftChild = null; 10 this.rightChild = null; 11 this.parent = null; 12 } 13}

很簡單,每個結點記錄自己以及左右孩子,父類的資訊。

二叉排序樹的建立(增加元素方法)

建立一顆二叉排序樹就是不斷往裡面新增元素。 
整體思路為:

  • 判斷整棵樹根結點是否建立過,如果沒有建立那麼第一個加入進來的元素指定為根結點,方法返回。

  • 如果二叉排序樹已經建立過,那麼再往裡面加入元素需要先找出其父節點,然後將要插入的元素掛載到父節點下即可。

  • 經過上面過程找出其父結點,這裡只需建立節點,掛載到父節點下即可,指定為父節點左孩子還是右孩子只需比較一下元素大小即可。

原始碼:

 1    public TreeNode put(int data){
 2        TreeNode node = root;
 3        TreeNode parent = null;
 4        //判斷二叉排序樹根結點是否存在,不存在則建立
 5        if (root == null){
 6            root = new TreeNode(data);
 7            return root;
 8        }
 9        //查詢其父類
10        while (node != null){
11            parent = node;//記錄其父親節點
12            if (data > node.data){
13                node = node.rightChild;
14            }else if (data < node.data){
15                node = node.leftChild;
16            }else {
17                //已經存在則直接返回
18                return node;
19            }
20        }
21        //建立新節點並插入原有樹中
22        node = new TreeNode(data);
23        if (data < parent.data){
24            parent.leftChild = node;
25        }else {
26            parent.rightChild = node;
27        }
28        node.parent = parent;
29        return node;
30    }
二叉排序樹的查詢

二叉排序樹中查詢比較簡單,思路為:

  • 當前結點與查詢的資料比較,相等則返回

  • 若小於當前結點則從左子樹查詢即可

  • 若大於當前結點則從右子樹查詢即可
    重複上述過程,這裡就看出二分查詢思想了

原始碼:

 1    public TreeNode searchNode(int data) {
 2        TreeNode node = root;
 3        if (node == null){
 4            return null;
 5        }else {
 6            while (node != null && data != node.data){
 7                if (data < node.data){
 8                    node = node.leftChild;
 9                }else {
10                    node = node.rightChild;
11                }
12            }
13        }
14        return node;
15    }
二叉排序樹的刪除

二叉排序樹的刪除操作分4中情況:

  • 若要刪除的結點無左右孩子也就是葉子結點,那麼直接刪除即可,將其父節點左或者右孩子置null即可

  • 若要刪除的結點有左孩子無右孩子,則只需要將刪除結點的左孩子與其父節點建立關係即可

  • 若要刪除的結點有右孩子無左孩子,則只需要將刪除結點的右孩子與其父節點建立關係即可

  • 若要刪除的結點左右孩子均有,就需要選一個結點將其替換,這裡需要保證選取的結點保證比左子樹都大,右子樹都小,可以選取左子樹中最大的結點,或者右子樹中最小的結點,並且需要將選取的結點從二叉排序樹中刪除。

原始碼:這裡我們選取右子樹最小的結點

 1    public void deleteNode(int data){
 2        TreeNode node = searchNode(data);
 3        if (node == null){
 4            throw new RuntimeException("未找到要刪除的節點");
 5        }else {
 6            delete(node);
 7        }
 8    }
 9
10    private void delete(TreeNode node) {
11        if (node == null){
12            throw new RuntimeException("未找到要刪除的節點");
13        }else {
14            TreeNode parent = node.parent;
15            //刪除的節點無左右孩子
16            if (node.leftChild == null && node.rightChild == null){
17                if (parent.leftChild == node){
18                    parent.leftChild = null;
19                }else {
20                    parent.rightChild = null;
21                }
22                return;
23            }
24            //刪除的節點有左無右
25            if (node.leftChild != null
26                    && node.rightChild == null){
27                if (parent.leftChild == node){
28                    parent.leftChild = node.leftChild;
29                }else {
30                    parent.rightChild = node.leftChild;
31                }
32                return;
33            }
34            //刪除的節點有右無左
35            if (node.leftChild == null
36                    && node.rightChild != null){
37                if (parent.leftChild == node){
38                    parent.leftChild = node.rightChild;
39                }else {
40                    parent.rightChild = node.rightChild;
41                }
42                return;
43            }
44            //刪除的結點左右都有
45            TreeNode rightMinNode = getRightMinNode(node.rightChild);
46            delete(rightMinNode);
47            node.data = rightMinNode.data;
48        }
49    }
50
51    //獲取右子樹最小的結點
52    private TreeNode getRightMinNode(TreeNode node) {
53        TreeNode minNode = node;
54        while (minNode != null && minNode.leftChild != null){
55            minNode = minNode.leftChild;
56        }
57        System.out.println("minNode" + minNode.data);
58        return minNode;
59    }
接下來我們測試一下

測試程式碼:

 1 SearchBinaryTree ss = new SearchBinaryTree();
 2 int[] array = {77,88,34,55,66,2,34,67,78};
 3 for (int data : array) {
 4     ss.put(data);
 5 }
 6 ss.midIter(ss.getRoot());
 7 System.out.println();
 8 SearchBinaryTree.TreeNode node = ss.searchNode(66);
 9 System.out.println("find node:"+node.getData());
10 ss.deleteNode(66);
11 SearchBinaryTree.TreeNode dnode = ss.searchNode(66);
12 if (dnode != null){
13     System.out.println("find node:"+node.getData());
14 }else {
15     System.out.println("not find node");
16 }
17 ss.midIter(ss.getRoot());

列印資訊如下:

1    2 34 55 66 67 77 78 88
2    find node:66
3    not find node
4    2 34 55 67 77 78 88

三、二叉排序樹效能問題

二叉排序樹最好的情況下其查詢效能是很高的,接近二分查詢法。 
但是在有些情況下構建出的二叉排序樹類似一個連結串列,其查詢效能為O(n),如下圖: 

構建出這樣的樹肯定不是我們希望的,需要調整此樹達到平衡的效果,這裡就需要二叉平衡樹了(AVL樹),關於AVL樹會在後續篇章介紹,這裡知道二叉平衡樹有這個問題就可以了。

四、總結

本篇主要介紹了二叉平衡樹以及Java程式碼實現其核心方法,希望你能掌握其與普通二叉樹的區別,以及其存在的問題,好了,本片到此為止,希望對你有用。

宣告:文章將會陸續搬遷到個人公眾號,以後也會第一時間釋出到個人公眾號,及時獲取文章內容請關注公眾號

 

最後附上整個類的全部原始碼,拷貝過去就可以用了:

  1    public class SearchBinaryTree {
  2
  3    private TreeNode root;//二叉樹根結點
  4
  5    public TreeNode getRoot() {
  6        return root;
  7    }
  8
  9    //中序遍歷二叉排序樹:按照從小到大排序
 10    public void midIter(TreeNode node){
 11        if (node == null){
 12            return;
 13        }
 14        midIter(node.leftChild);
 15        System.out.print(" "+node.data);
 16        midIter(node.rightChild);
 17    }
 18
 19    public TreeNode put(int data){
 20        TreeNode node = root;
 21        TreeNode parent = null;
 22        //判斷二叉排序樹根結點是否存在,不存在則建立
 23        if (root == null){
 24            root = new TreeNode(data);
 25            return root;
 26        }
 27        //查詢其父類
 28        while (node != null){
 29            parent = node;//記錄其父親節點
 30            if (data > node.data){
 31                node = node.rightChild;
 32            }else if (data < node.data){
 33                node = node.leftChild;
 34            }else {
 35                //已經存在則直接返回
 36                return node;
 37            }
 38        }
 39        //建立新節點並插入原有樹中
 40        node = new TreeNode(data);
 41        if (data < parent.data){
 42            parent.leftChild = node;
 43        }else {
 44            parent.rightChild = node;
 45        }
 46        node.parent = parent;
 47        return node;
 48    }
 49
 50    public void deleteNode(int data){
 51        TreeNode node = searchNode(data);
 52        if (node == null){
 53            throw new RuntimeException("未找到要刪除的節點");
 54        }else {
 55            delete(node);
 56        }
 57    }
 58
 59    private void delete(TreeNode node) {
 60        if (node == null){
 61            throw new RuntimeException("未找到要刪除的節點");
 62        }else {
 63            TreeNode parent = node.parent;
 64            //刪除的節點無左右孩子
 65            if (node.leftChild == null && node.rightChild == null){
 66                if (parent.leftChild == node){
 67                    parent.leftChild = null;
 68                }else {
 69                    parent.rightChild = null;
 70                }
 71                return;
 72            }
 73            //刪除的節點有左無右
 74            if (node.leftChild != null
 75                    && node.rightChild == null){
 76                if (parent.leftChild == node){
 77                    parent.leftChild = node.leftChild;
 78                }else {
 79                    parent.rightChild = node.leftChild;
 80                }
 81                return;
 82            }
 83            //刪除的節點有右無左
 84            if (node.leftChild == null
 85                    && node.rightChild != null){
 86                if (parent.leftChild == node){
 87                    parent.leftChild = node.rightChild;
 88                }else {
 89                    parent.rightChild = node.rightChild;
 90                }
 91                return;
 92            }
 93            //刪除的結點左右都有
 94            TreeNode rightMinNode = getRightMinNode(node.rightChild);
 95            delete(rightMinNode);
 96            node.data = rightMinNode.data;
 97        }
 98    }
 99
100    private TreeNode getRightMinNode(TreeNode node) {
101        TreeNode minNode = node;
102        while (minNode != null && minNode.leftChild != null){
103            minNode = minNode.leftChild;
104        }
105        System.out.println("minNode" + minNode.data);
106        return minNode;
107    }
108
109    public TreeNode searchNode(int data) {
110        TreeNode node = root;
111        if (node == null){
112            return null;
113        }else {
114            while (node != null && data != node.data){
115                if (data < node.data){
116                    node = node.leftChild;
117                }else {
118                    node = node.rightChild;
119                }
120            }
121        }
122        return node;
123    }
124
125
126    public class TreeNode{
127        private int data;
128        private TreeNode leftChild;
129        private TreeNode rightChild;
130        private TreeNode parent;
131
132        public TreeNode(int data) {
133            this.data = data;
134            this.leftChild = null;
135            this.rightChild = null;
136            this.parent = null;
137        }
138
139        public int getData() {
140            return data;
141        }
142
143        public void setData(int data) {
144            this.data = data;
145        }
146
147        public TreeNode getLeftChild() {
148            return leftChild;
149        }
150
151        public void setLeftChild(TreeNode leftChild) {
152            this.leftChild = leftChild;
153        }
154
155        public TreeNode getRightChild() {
156            return rightChild;
157        }
158
159        public void setRightChild(TreeNode rightChild) {
160            this.rightChild = rightChild;
161        }
162
163        public TreeNode getParent() {
164            return parent;
165        }
166
167        public void setParent(TreeNode parent) {
168            this.parent = parent;
169        }
170    }
171}