【转载】图解:二叉搜索树算法(BST)
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“岁月极美,在于它必然的流逝”
“春花 秋月 夏日 冬雪”
— 三毛
一、树 & 二叉树
树是由节点和边构成,储存元素的集合。节点分根节点、父节点和子节点的概念。
如图:树深=4; 5是根节点;同样8与3的关系是父子节点关系。
二叉树binary tree,则加了“二叉”(binary),意思是在树中作区分。每个节点至多有两个子(child),left child & right child。二叉树在很多例子中使用,比如二叉树表示算术表达式。
如图:1/8是左节点;2/3是右节点;
二、二叉搜索树 BST
顾名思义,二叉树上又加了个搜索的限制。其要求:每个节点比其左子树元素大,比其右子树元素小。
如图:每个节点比它左子树的任意节点大,而且比它右子树的任意节点小
三、BST Java实现
直接上代码,对应代码分享在 Github 主页
BinarySearchTree.java
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
|
package org.algorithm.tree; /* * Copyright [2015] [Jeff Lee] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 二叉搜索树(BST)实现 * * Created by bysocket on 16/7/7. */ public class BinarySearchTree { /** * 根节点 */ public static TreeNode root; public BinarySearchTree() { this.root = null; } /** * 查找 * 树深(N) O(lgN) * 1. 从root节点开始 * 2. 比当前节点值小,则找其左节点 * 3. 比当前节点值大,则找其右节点 * 4. 与当前节点值相等,查找到返回TRUE * 5. 查找完毕未找到, * @param key * @return */ public TreeNode search (int key) { TreeNode current = root; while (current != null && key != current.value) { if (key < current.value ) current = current.left; else current = current.right; } return current; } /** * 插入 * 1. 从root节点开始 * 2. 如果root为空,root为插入值 * 循环: * 3. 如果当前节点值大于插入值,找左节点 * 4. 如果当前节点值小于插入值,找右节点 * @param key * @return */ public TreeNode insert (int key) { // 新增节点 TreeNode newNode = new TreeNode(key); // 当前节点 TreeNode current = root; // 上个节点 TreeNode parent = null; // 如果根节点为空 if (current == null) { root = newNode; return newNode; } while (true) { parent = current; if (key < current.value) { current = current.left; if (current == null) { parent.left = newNode; return newNode; } } else { current = current.right; if (current == null) { parent.right = newNode; return newNode; } } } } /** * 删除节点 * 1.找到删除节点 * 2.如果删除节点左节点为空 , 右节点也为空; * 3.如果删除节点只有一个子节点 右节点 或者 左节点 * 4.如果删除节点左右子节点都不为空 * @param key * @return */ public TreeNode delete (int key) { TreeNode parent = root; TreeNode current = root; boolean isLeftChild = false; // 找到删除节点 及 是否在左子树 while (current.value != key) { parent = current; if (current.value > key) { isLeftChild = true; current = current.left; } else { isLeftChild = false; current = current.right; } if (current == null) { return current; } } // 如果删除节点左节点为空 , 右节点也为空 if (current.left == null && current.right == null) { if (current == root) { root = null; } // 在左子树 if (isLeftChild == true) { parent.left = null; } else { parent.right = null; } } // 如果删除节点只有一个子节点 右节点 或者 左节点 else if (current.right == null) { if (current == root) { root = current.left; } else if (isLeftChild) { parent.left = current.left; } else { parent.right = current.left; } } else if (current.left == null) { if (current == root) { root = current.right; } else if (isLeftChild) { parent.left = current.right; } else { parent.right = current.right; } } // 如果删除节点左右子节点都不为空 else if (current.left != null && current.right != null) { // 找到删除节点的后继者 TreeNode successor = getDeleteSuccessor(current); if (current == root) { root = successor; } else if (isLeftChild) { parent.left = successor; } else { parent.right = successor; } successor.left = current.left; } return current; } /** * 获取删除节点的后继者 * 删除节点的后继者是在其右节点树种最小的节点 * @param deleteNode * @return */ public TreeNode getDeleteSuccessor(TreeNode deleteNode) { // 后继者 TreeNode successor = null; TreeNode successorParent = null; TreeNode current = deleteNode.right; while (current != null) { successorParent = successor; successor = current; current = current.left; } // 检查后继者(不可能有左节点树)是否有右节点树 // 如果它有右节点树,则替换后继者位置,加到后继者父亲节点的左节点. if (successor != deleteNode.right) { successorParent.left = successor.right; successor.right = deleteNode.right; } return successor; } public void toString(TreeNode root) { if (root != null) { toString(root.left); System.out.print("value = " + root.value + " -> "); toString(root.right); } } } /** * 节点 */ class TreeNode { /** * 节点值 */ int value; /** * 左节点 */ TreeNode left; /** * 右节点 */ TreeNode right; public TreeNode( int value) { this .value = value; left = null ; right = null ; } } |
1. 节点数据结构
首先定义了节点的数据接口,节点分左节点和右节点及本身节点值。如图
代码如下:
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
|
/** * 节点 */ class TreeNode { /** * 节点值 */ int value; /** * 左节点 */ TreeNode left; /** * 右节点 */ TreeNode right; public TreeNode( int value) { this .value = value; left = null ; right = null ; } } |
2. 插入
插入,和删除一样会引起二叉搜索树的动态变化。插入相对删处理逻辑相对简单些。如图插入的逻辑:
a. 从root节点开始
b.如果root为空,root为插入值
c.循环:
d.如果当前节点值大于插入值,找左节点
e.如果当前节点值小于插入值,找右节点
代码对应:
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
|
/** * 插入 * 1. 从root节点开始 * 2. 如果root为空,root为插入值 * 循环: * 3. 如果当前节点值大于插入值,找左节点 * 4. 如果当前节点值小于插入值,找右节点 * @param key * @return */ public TreeNode insert ( int key) { // 新增节点 TreeNode newNode = new TreeNode(key); // 当前节点 TreeNode current = root; // 上个节点 TreeNode parent = null ; // 如果根节点为空 if (current == null ) { root = newNode; return newNode; } while ( true ) { parent = current; if (key < current.value) { current = current.left; if (current == null ) { parent.left = newNode; return newNode; } } else { current = current.right; if (current == null ) { parent.right = newNode; return newNode; } } } } |
3.查找
其算法复杂度 : O(lgN),树深(N)。如图查找逻辑:
a.从root节点开始
b.比当前节点值小,则找其左节点
c.比当前节点值大,则找其右节点
d.与当前节点值相等,查找到返回TRUE
e.查找完毕未找到
代码对应:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/** * 查找 * 树深(N) O(lgN) * 1. 从root节点开始 * 2. 比当前节点值小,则找其左节点 * 3. 比当前节点值大,则找其右节点 * 4. 与当前节点值相等,查找到返回TRUE * 5. 查找完毕未找到, * @param key * @return */ public TreeNode search ( int key) { TreeNode current = root; while (current != null && key != current.value) { if (key < current.value ) current = current.left; else current = current.right; } return current; } |
4. 删除
首先找到删除节点,其寻找方法:删除节点的后继者是在其右节点树种最小的节点。如图删除对应逻辑:
结果为:
a.找到删除节点
b.如果删除节点左节点为空 , 右节点也为空;
c.如果删除节点只有一个子节点 右节点 或者 左节点
d.如果删除节点左右子节点都不为空
代码对应见上面完整代码。
案例测试代码如下,BinarySearchTreeTest.java
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
|
package org.algorithm.tree; /* * Copyright [2015] [Jeff Lee] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 二叉搜索树(BST)测试案例 {@link BinarySearchTree} * * Created by bysocket on 16/7/10. */ public class BinarySearchTreeTest { public static void main(String[] args) { BinarySearchTree b = new BinarySearchTree(); b.insert( 3 );b.insert( 8 );b.insert( 1 );b.insert( 4 );b.insert( 6 ); b.insert( 2 );b.insert( 10 );b.insert( 9 );b.insert( 20 );b.insert( 25 ); // 打印二叉树 b.toString(b.root); System.out.println(); // 是否存在节点值10 TreeNode node01 = b.search( 10 ); System.out.println( "是否存在节点值为10 => " + node01.value); // 是否存在节点值11 TreeNode node02 = b.search( 11 ); System.out.println( "是否存在节点值为11 => " + node02); // 删除节点8 TreeNode node03 = b.delete( 8 ); System.out.println( "删除节点8 => " + node03.value); b.toString(b.root); } } |
运行结果如下:
1
2
3
4
5
|
value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 8 -> value = 9 -> value = 10 -> value = 20 -> value = 25 -> 是否存在节点值为10 => 10 是否存在节点值为11 => null 删除节点8 => 8 value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 9 -> value = 10 -> value = 20 -> value = 25 -> |
四、小结
与偶尔吃一碗“老坛酸菜牛肉面”一样的味道,品味一个算法,比如BST,的时候,总是那种说不出的味道。
树,二叉树的概念
BST算法
相关代码分享在 Github 主页
【转载】图解:二叉搜索树算法(BST)的更多相关文章
- 二叉搜索树算法详解与Java实现
二叉查找树可以递归地定义如下,二叉查找树或者是空二叉树,或者是满足下列性质的二叉树: (1)若它的左子树不为空,则其左子树上任意结点的关键字的值都小于根结点关键字的值. (2)若它的右子树不为空,则其 ...
- [LeetCode] Delete Node in a BST 删除二叉搜索树中的节点
Given a root node reference of a BST and a key, delete the node with the given key in the BST. Retur ...
- [Swift]LeetCode450. 删除二叉搜索树中的节点 | Delete Node in a BST
Given a root node reference of a BST and a key, delete the node with the given key in the BST. Retur ...
- [LeetCode] 450. Delete Node in a BST 删除二叉搜索树中的节点
Given a root node reference of a BST and a key, delete the node with the given key in the BST. Retur ...
- LeetCode 230. 二叉搜索树中第K小的元素(Kth Smallest Element in a BST)
230. 二叉搜索树中第K小的元素 230. Kth Smallest Element in a BST 题目描述 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的 ...
- [LeetCode]230. 二叉搜索树中第K小的元素(BST)(中序遍历)、530. 二叉搜索树的最小绝对差(BST)(中序遍历)
题目230. 二叉搜索树中第K小的元素 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素. 题解 中序遍历BST,得到有序序列,返回有序序列的k-1号元素. 代 ...
- [leetcode] 230. Kth Smallest Element in a BST 找出二叉搜索树中的第k小的元素
题目大意 https://leetcode.com/problems/kth-smallest-element-in-a-bst/description/ 230. Kth Smallest Elem ...
- 【LeetCode】230. 二叉搜索树中第K小的元素 Kth Smallest Element in a BST
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 公众号:负雪明烛 本文关键词:算法题,刷题,Leetcode, 力扣,二叉搜索树,BST ...
- [Swift]LeetCode701. 二叉搜索树中的插入操作 | Insert into a Binary Search Tree
Given the root node of a binary search tree (BST) and a value to be inserted into the tree, insert t ...
随机推荐
- java web sql注入测试(3)---现象分析
那为什么出现以上问题呢?这是程序代码层控制不当导致的.如果web前端对输入数据控制严格,会对数据库进行操作的字符串,在客户端做敏感字符转义处理,或者在操作数据库的dao层,使用动态参数的sql,不使用 ...
- Qt源码包中的一段代码
之所以单独拣出来是因为Qt的开发者们给普通开发者提供了高效编程的商业代码例子: bool QWidget::testAttribute_helper(Qt::WidgetAttribute attri ...
- javascript 正则表达式(二)
/* 正则表达式方法:test(),exec(),String对象方法:match(),search(),replace(),split() 1.test()方法: 用法: regexp对象实例.t ...
- 必备的 Java 参考资源列表(转)
包含必备书籍.站点.博客.活动等参考资源的完整清单级别: 初级 Ted Neward, 主管,ThoughtWorks, Neward & Associates 2009 年 3 月 02 日 ...
- 关闭 Windows 的常用端口
netstat -ano 可以看到目前开着哪些端口 netstat -ano|findstr <端口号> 可以找到开放的端口的那条,最后还列出了 PID. 然后到任务管理器中,你可以查 ...
- 160930、Javascript的垃圾回收机制与内存管理
一.垃圾回收机制-GC Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存. 原理:垃圾收集器会定期(周期性 ...
- Linux驱动学习笔记(6)信号量(semaphore)与互斥量(mutex)【转】
转自:http://blog.chinaunix.net/uid-24943863-id-3193530.html 并发导致竟态,从而导致对共享数据的非控制访问,产生非预期结果,我们要避免竟态的发生. ...
- flex datagrid 换行
<mx:DataGrid id="myGrid" width="100%" height="90%" headerStyleName= ...
- mysql表导入到oracle
一.创建jack表,并导入一下数据 mysql),flwo )) engine=myisam; Query OK, rows affected (0.08 sec) mysql> load da ...
- linux下echo命令详解(转)
linux的echo命令, 在shell编程中极为常用, 在终端下打印变量value的时候也是常常用到的, 因此有必要了解下echo的用法 echo命令的功能是在显示器上显示一段文字,一般起到一个 ...