【转载】图解:二叉搜索树算法(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 => 8value = 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 ...
随机推荐
- access链接最原始代码,两种
using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web ...
- sql语句中where和having的区别
WHERE语句在GROUPBY语句之前:SQL会在分组之前计算WHERE语句. HAVING语句在GROUPBY语句之后:SQL会在分组之后计算HAVING语句.
- Controller 接口控制器详解
Controller 控制器,是 MVC 中的部分 C,为什么是部分呢?因为此处的控制器主要负责功能处理部分:1.收集.验证请求参数并绑定到命令对象:2.将命令对象交给业务对象,由业务对象处理并返回模 ...
- python核心编程学习记录之基础知识
虽然对python的基础知识有所了解,但是为了更深入的学习,要对python的各种经典书籍进行学习 第一章介绍python的优缺点,略过 第二章介绍python起步,第三章介绍python基础,仅记录 ...
- Delphi Xe2 后的版本如何让Delphi程序启动自动“以管理员身份运行"
由于Vista以后win中加入的UAC安全机制,采用Delphi开发的程序如果不右键点击“以管理员身份运行”,则会报错. 在XE2以上的Delphi版本处理这个问题已经非常简单了. 右建点击工程,选择 ...
- 介绍“Razor”— ASP.NET的一个新视图引擎
我的团队当前正在从事的工作之一就是为ASP.NET添加一个新的视图引擎. 一直以来,ASP.NET MVC都支持 “视图引擎”的概念—采用不同语法的模板的可插拔模块.当前ASP.NET MVC “默认 ...
- Memcached 分布式缓存实现原理
摘要 在高并发环境下,大量的读.写请求涌向数据库,此时磁盘IO将成为瓶颈,从而导致过高的响应延迟,因此缓存应运而生.无论是单机缓存还是分布式缓存都有其适应场景和优缺点,当今存在的缓存产品也是数不胜数, ...
- PHP面向对象的一些深入理解
1.$this就是这个对象的地址,$this不能在类外部使用.2.构造函数 __construct 和析构函数都没有返回值:一旦一个对象成为垃圾对象(没有任何变量引用的对象,或者=null),析构函数 ...
- Codeforces 735D:Taxes(哥德巴赫猜想)
http://codeforces.com/problemset/problem/735/D 题意:给出一个n,这个n可以分解成 n = n1 + n2 + -- + nk,其中k可以取任意数.要使得 ...
- Codefroces Gym 100781A(树上最长路径)
http://codeforces.com/gym/100781/attachments 题意:有N个点,M条边,问对两两之间的树添加一条边之后,让整棵大树最远的点对之间的距离最近,问这个最近距离是多 ...