AVL树实现练习

urlyy

debug了好久才写出来的平衡树,代码里有注释可以方便理解
可能有代码冗余或者有误的地方,欢迎指出错误
(好像因为是看了别人的博客,所以混淆了结点高度这个概念,我先解释一下我代码里的Height=当前结点到叶子结点的经过的边+1)
很抱歉影响了你的观看体验!
在这里插入图片描述
在这里插入图片描述

推荐一个网站(数据结构操作可视化)
(我这里给出的是平衡树界面)
AVL Tree Visualzation
(我debug就是看着可视化的树查错的)

还是讲下一些关键操作吧:
1、左旋(基操)
左旋不会改变中序遍历
在这里插入图片描述
2、右旋(基操)
右旋不会改变中序遍历
在这里插入图片描述

3、LL(左左)
用下图举例的话,就是插入操作为在结点1的左孩子的左孩子处插入结点3,这样会使得平衡度== 2
就是对结点1进行一个右旋
在这里插入图片描述
4、RR(右右,在结点1的右孩子的右孩子处插入结点3,这样会使得平衡度==-2)
就是对结点1进行一个左旋
在这里插入图片描述
5、LR(左右,在结点1的左孩子的右孩子处插入结点3,这样会使得平衡度==-2)
就是先对结点2右旋,再对结点1左旋
在这里插入图片描述
6、RL(开摆!你们自己对照着LR比划去罢)
在这里插入图片描述

7、计算平衡度
首先得到当前结点的左右孩子的高度(高度的更新是在回溯的时候进行的,所以可以保证子结点的高度已经是正确的了)
然后就是return leftHeight-rightHeight了

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
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
import java.util.LinkedList;

/**
* @author 20级三班刘宇阳
* @create 2022/2/3
*/

/**
* 平衡樹
* @param <K> key的类型 元素必须可比较
* @param <V> value的类型
*/
public class AVLTree<K extends Comparable<K>,V> {
//树结点类
private class Node{
//键
private K key;
//值
private V value;
//高度
private Integer height;
//左右父
private Node left,right,parent;

public Node(K key,V value) {
this.key=key;
this.value = value;
height=1;
}

/**
* 获得平衡度
* @return 平衡度
*/
private int getBalanceFactor(){
int leftHeight=left==null?0:left.getHeight();
int rightHeight=right==null?0:right.getHeight();
return leftHeight-rightHeight;
}

private int getHeight(){
return height;
}

private void setHeight(Integer height){
this.height=height;
}

/**
* 更新该结点的高度
*/
private void updateHeight(){
int leftHeight=left==null?0:left.getHeight();
int rightHeight=right==null?0:right.getHeight();
height=1+Math.max(leftHeight,rightHeight);
}

private K getKey() {
return key;
}

private void setKey(K key) {
this.key = key;
}

private V getValue() {
return value;
}

private void setValue(V value) {
this.value = value;
}

private void setKeyAndValue(K key,V value){
setKey(key);
setValue(value);
}

private Node getLeft() { return left; }

private void setLeft(Node left) { this.left = left; }

private Node getRight() { return right; }

private void setRight(Node right) { this.right = right; }

private Node getParent() { return parent; }

private void setParent(Node parent) { this.parent = parent; }

@Override
public String toString() {
return "Node{" +
"key=" + key +
", value=" + value +
'}';
}
}
//结点个数
private int size;
//根结点
private Node root;

public AVLTree(){
clear();
}

/**
* 1 2
* \ / \
* 2 => 1 3
* \
* 3
* @param node1 需要调整的该子树的根结点
* @return 新的根结点
*/
private Node leftRotate(Node node1){
Node node2=node1.getRight();
node1.setRight(node2.getLeft());
node1.setParent(node2);
if(node1.getRight()!=null){
node1.getRight().setParent(node1);
}
node2.setLeft(node1);
node1.updateHeight();
node2.updateHeight();
return node2;
}

/**
* 1 2
* / => / \
* 2 3 1
* /
* 3
* @param node1 需要调整的该子树的根结点
* @return 新的根结点
*/
private Node rightRotate(Node node1){
Node node2=node1.getLeft();
node1.setLeft(node2.getRight());
node1.setParent(node2);
if(node1.getLeft()!=null){
node1.getLeft().setParent(node1);
}
node2.setRight(node1);
node1.updateHeight();
node2.updateHeight();
return node2;
}

/**
* 作为put的递归工具方法
* @param root 当前的根结点
* @param key
* @param value
* @return
*/
private Node addNode(Node root,K key,V value){
if(root==null){
//直接返回结点,让父节点更新子结点,同时不走下面的reBalance
return new Node(key,value);
}
int cmpRes=key.compareTo(root.getKey());
//小于,看左子树
if(cmpRes<0){
//这里需要更新左子结点
Node leftChild=addNode(root.getLeft(),key,value);
root.setLeft(leftChild);
leftChild.setParent(root);
}else if(cmpRes>0){//大于,看右子树
//这里需要更新右子结点
Node rightChild=addNode(root.getRight(),key,value);
root.setRight(rightChild);
rightChild.setParent(root);
}else{
//key值相等就认为是更新
root.setValue(value);
}
return reBalance(root);
}

private Node reBalance(Node root){
int bf=root.getBalanceFactor();
//左子树高
if(bf>1){
Node leftChild=root.getLeft();
int childBF=leftChild.getBalanceFactor();
/**LL型
* 1 2
* / / \
* 2 3 1
* / =>
* 3
**/
if(childBF>0){
//右旋
root=rightRotate(root);
}
/** LR型
*
* 1 1 3
* / / / \
* 2 => 3 => 2 1
* \ /
* 3 2
*/
else{
//先左旋再右旋
//注意左旋是以2为根结点
root.setLeft(leftRotate(root.getLeft()));
root=rightRotate(root);
}
}else if(bf<-1){//右子树高
Node rightChild=root.getRight();
int childBF=rightChild.getBalanceFactor();
/**RL型
*
* 1 1 3
* \ \ / \
* 2 => 3 => 1 2
* / \
* 3 2
*/
if(childBF>0){
//注意右旋是以2为根结点
root.setRight(rightRotate(root.getRight()));
root=leftRotate(root);
}
/**RR型
* 1 2
* \ / \
* 2 => 1 3
* \
* 3
*/
else{
//左旋
root=leftRotate(root);
}
}
//这里可以在回溯时更新路径上的所有结点的高度(已平衡的结点会直接来这里更新高度)
root.updateHeight();
return root;
}

/**
* 根据key值寻找结点
* @param root 当前根节点
* @param key 当前键
* @return 目标结点
*/
private Node findNode(Node root,K key){
if(root==null){
return null;
}
int cmpRes=key.compareTo(root.getKey());
if(cmpRes<0){
return findNode(root.getLeft(),key);
}else if(cmpRes>0){
return findNode(root.getRight(),key);
}else{
return root;
}
}

/**
* 查找要被删除结点的左子树的最大节点
* @param root 当前根节点
* @param deletedNode 要被删除的结点
* @return
*/
private Node replaceNode(Node root,Node deletedNode){
if(root.getRight()==null){
deletedNode.setKeyAndValue(root.getKey(),root.getValue());
return null;
}
root.setRight(replaceNode(root.getRight(),deletedNode));
root.updateHeight();
return reBalance(root);
}

/**
* 删除结点
* @param root 当前根节点
* @param key 目标键
* @return 当前子树的新根节点(可能会更新也可能不会),用来给父节点更新子树
*/
private Node deleteNode(Node root,K key){
if(root==null){
return null;
}
int cmpRes=key.compareTo(root.getKey());
if(cmpRes<0){
//找左子树
Node leftChild=deleteNode(root.getLeft(), key);
root.setLeft(leftChild);
if(leftChild!=null){
//
leftChild.setParent(root);
}
}else if(cmpRes>0) {
//找右子树
Node rightChild=deleteNode(root.getRight(), key);
root.setRight(rightChild);
if(rightChild!=null){
rightChild.setParent(root);
}
}else{
//找到要删除的结点了
if(root.getLeft()==null&&root.getRight()==null){//叶子结点
if(root.getParent()==null){
setRoot(null);
}
return null;
}else if(root.getLeft()==null){//左子树为空,右子树不为空
root.getRight().setParent(root.getParent());
if(root.getParent()==null){
setRoot(root.getRight());
}
return root.getRight();
} else if(root.getRight()==null){//右子树为空,左子树不为空
root.getLeft().setParent(root.getParent());
if(root.getParent()==null){
setRoot(root.getLeft());
}
return root.getLeft();
}
else{//有两个儿子结点
Node leftChild=root.getLeft();
if(leftChild.getRight()==null){
//要被删除的结点用左儿子结点替换,同时他的右儿子要作为他左儿子的右儿子
leftChild.right=root.getRight();
root=leftChild;
}else{
//用左子树的最大节点覆盖被删除的结点(可视为删除),并更新左孩子
Node newLeft=replaceNode(leftChild,root);
root.setLeft(newLeft);
if(newLeft!=null){
newLeft.setParent(root);
}
}
}
}
root.updateHeight();
return reBalance(root);
}

/**
* 放入元素
* @param key 键
* @param value 值
*/
public void put(K key,V value){
if(root==null){ setRoot(new Node(key,value)); }
else{
setRoot(addNode(root,key,value));
//因为这些操作不涉及根结点的处理,所以需要单独写一个根结点的父节点置空
getRoot().setParent(null);
}
size++;
}

/**
* 获取键对应的值
* @param key
* @return 对应结点的值
*/
public V get(K key){
if(key==null){
throw new NullPointerException("key cannot be null");
}
Node node=findNode(root,key);
if(node==null){
return null;
}
return node.getValue();
}

/**
* 根据键删除对应结点
* @param key 键
*/
public void remove(K key){
if(key==null){
throw new NullPointerException("key cannot be null");
}
setRoot(deleteNode(root,key));
if(getRoot()!=null){
getRoot().setParent(null);
}
size--;
}

/**
* 重置树(可用于初始化)
*/
public void clear(){
size=0;
root=null;
}

public boolean isEmpty(){ return size==0; }

public int size() { return size; }

private void setSize(int size) { this.size = size; }

private Node getRoot() { return root; }

private void setRoot(Node root) { this.root = root; }

private StringBuilder preOrderTraverse(Node root,StringBuilder sb){
if(root==null){
//null结点为#
sb.append("# ");
return sb;
}
sb.append(root.getKey()).append(" ");
sb=preOrderTraverse(root.getLeft(),sb);
sb=preOrderTraverse(root.getRight(),sb);
return sb;
}

@Override
public String toString(){
StringBuilder sb=new StringBuilder();
sb.append("preOrderTraverseSequenceOfKey is [");
sb=preOrderTraverse(root,sb);
sb.deleteCharAt(sb.length()-1);
sb.append("]");
return sb.toString();
}
}

class AVLTreeDemo{
public static void main(String[] args) {
AVLTree<Integer,Integer> tree=new AVLTree<>();
LinkedList<Integer> list=new LinkedList<>();
for(int i=1;i<=10;i++){
list.add(i);
}
list.add(0);
// System.out.println(list);
System.out.println("----------put测试----------");
System.out.println("输出的是前序遍历的key序列:");
for(Integer it:list){
tree.put(it,it);
System.out.println(tree);
}
tree.remove(2);
System.out.println(tree);
// System.out.println("------------get测试--------------");
// System.out.println(tree.get(2));
// System.out.println("------------delete测试--------------");
// tree.remove(2);
// System.out.println(tree);
// tree.remove(1);
// System.out.println(tree);
// tree.remove(-1);
// System.out.println(tree);
// tree.remove(0);
// System.out.println(tree);
// tree.put(11,11);
// System.out.println(tree);
// tree.remove(8);
// System.out.println(tree);
// tree.remove(9);
// tree.remove(11);
// System.out.println(tree);
// tree.remove(3);
// System.out.println(tree);
// tree.remove(6);
// tree.remove(4);
// System.out.println(tree);
}
}
  • 标题: AVL树实现练习
  • 作者: urlyy
  • 创建于 : 2022-02-05 12:41:44
  • 更新于 : 2023-06-19 13:29:26
  • 链接: https://urlyy.github.io/2022/02/05/AVL树练习/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
AVL树实现练习