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

Android版數據結構與算法(八):二叉排序樹

delet 概念 最好 指定 性能 and 並且 二叉樹 排列

本文目錄

技術分享圖片

前兩篇文章我們學習了一些樹的基本概念以及常用操作,本篇我們了解一下二叉樹的一種特殊形式:二叉排序樹(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}

Android版數據結構與算法(八):二叉排序樹