【数据结构】平衡二叉树—AVL树
(百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 "An algorithm for the organization of information" 中发表了它。
平衡二叉树(Balanced Binary Tree)是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。
基本操作
单旋转:左旋转、右旋转
双旋转:左旋转与右旋转结合
操作判断
1. 按照二叉搜索树的方式增加节点,新增节点称为一个叶节点。
2. 从新增节点开始,回溯到第一个失衡节点。(如果回溯到根节点,还没有失衡节点,就说明该树已经符合AVL性质)。
3. 找到断的边,并确定断弦的方向(左OR右)。
4. 以断边下端为根节点,确定两个子树中的哪一个深度大(左子树还是右子树)。
5. 如果第2和第3步中的方向一致(都为左或者都为右),需要单旋转以失衡节点为根节点的子树。否则,双旋转以失衡节点为根节点的子树。若是左左(LL),则需要一次右旋转;若是右右(RR),则需要一次左旋转;若是左右(LR),则需要先左旋转再右旋转;若是右左(RL),则需要先右旋转再左旋转。
单向右旋平衡处理案例分析(LL)
从上图可以看出,从叶子结点往上回溯,(5)是第一个失衡结点,失衡方向是左边,而(5)的左子树根结点(3)的左子树深度比右子树大,所以该情况属于LL,只需要以(5)为转轴进行一次右旋转。
单向左旋平衡处理案例分析(RR)
从上图可以看出,从叶子结点往上回溯,(10)是第一个失衡结点,失衡方向是右边,而(10)的左子树根结点(13)的右子树深度比左子树大,所以该情况属于RR,只需要以(10)为转轴进行一次左旋转。
双向旋转(先左后右)平衡处理案例分析(LR)
先以(5)为轴向左旋转
后以(8)为轴向右旋转
从上图可以看出,从叶子结点往上回溯,(8)是第一个失衡结点,失衡方向是左边,而(5)的左子树根结点(7)的右子树深度比左子树大,所以该情况属于LR,因此需要先以(5)为转轴进行一次左旋转,后以(8)为转轴进行一次右旋转。
双向旋转(先右后左)平衡处理(RL)
先以(13)为轴向右旋转
后以(8)为轴向左旋转
从上图可以看出,从叶子结点往上回溯,(8)是第一个失衡结点,失衡方向是右边,而(13)的右子树根结点(10)的左子树深度比右子树大,所以该情况属于RL,因此需要先以(13)为转轴进行一次右旋转,后以(8)为转轴进行一次左旋转。
插入操作
向AVL树中插入元素可能会导致树失去平衡。但是,我们只需要调整插入点至根节点的路径上不满足平衡状态的各节点中深度最大的那个即可。假设该最深节点为X。导致X失去平衡可能有四种情况:
(1)插入点位于X的左子结点的左子树-左左。
(2)插入点位于X的左子结点的右子树-左右。
(3)插入点位于X的右子结点的左子树-右左。
(4)插入点位于X的右子结点的左子树-右右。
情况1和4是对称的,成为外侧插入,可以通过单旋转解决。而情况2和3也是对称的,成为内侧插入。通过双旋转解决。
删除操作
(1)判断当前根结点是否等于要删除的节点,否则进入步骤(2),是则进入(1.1)。
(1.1)如果是叶子结点直接删除,然后进入步骤(3),否则进入(1.2)。
(1.2)如果该结点只有左子树,则直接删除结点把左子树的根结点往上提,再步骤(3),否则进入步骤(1.3)。
(1.3)如果该结点只有右子树,则直接删除结点把右子树的根结点往上提,再步骤(3),否则进入步骤(1.4)。
(1.4)如果左右子树都不为空,则从左子树获取前驱(后驱同理),替换当前删除结点,再从前驱结点的原父结点往上遍历检查高度和平衡,也就是步骤(3)。
(2)判断当前根结点是否大于删除节点,是则进入当前根结点的右子树继续递归删除,否则进入当前根结点的左子树继续递归删除,最后重新进入步骤(1),直到遍历完整棵树。
(3)从移除结点的父结点开始往上遍历,检查高度是否变化和失衡,若高度发生变化,则重新赋值最新高度,若失衡,则按照以上的旋转方式检查进行旋转。
AVL树的实现练习(Java)
1 public class AVLTree {
2
3 private TreeNode root=null;
4
5 /**
6 * 获取树的高度
7 * @param subTree
8 * @return
9 */
10 private int height(TreeNode subTree){
11 if(subTree == null){
12 return 0;
13 }else{
14 return subTree.height;
15 }
16 }
17
18 /**
19 * 中序遍历
20 * @param subTree
21 */
22 public void inOrder(TreeNode subTree){
23 if(subTree!=null){
24 inOrder(subTree.leftChild);
25 visted(subTree);
26 inOrder(subTree.rightChild);
27 }
28 }
29
30 public void visted(TreeNode subTree){
31 System.out.print(subTree.data+"("+subTree.height+")"+",");
32 }
33
34 /**
35 * 右旋转(LL)
36 * @param subTree 转轴节点
37 */
38 public void r_Rotate(TreeNode sn){
39
40 TreeNode pn = sn.parent;
41 TreeNode ln = sn.leftChild;
42
43 sn.leftChild = ln.rightChild;
44 if(ln.rightChild !=null){
45 ln.rightChild.parent = sn;
46 }
47 ln.rightChild = sn;
48
49 sn.parent = ln;
50 ln.parent = pn;
51 if(pn != null && pn.rightChild==sn){
52 pn.rightChild = ln;
53 }else if(pn != null && pn.leftChild==sn){
54 pn.leftChild = ln;
55 }
56
57 if(pn == null){
58 this.root = ln;
59 }
60
61 sn.height = Math.max(height(sn.leftChild), height(sn.rightChild))+1;
62 ln.height = Math.max(height(ln.leftChild), height(ln.rightChild))+1;
63 }
64
65 /**
66 * 左旋转(RR)
67 * @param subTree 转轴节点
68 */
69 public void l_Rotate(TreeNode sn){
70
71 TreeNode pn = sn.parent;
72 TreeNode rn = sn.rightChild;
73
74 sn.rightChild = rn.leftChild;
75 if(rn.leftChild !=null){
76 rn.leftChild.parent = sn;
77 }
78 rn.leftChild = sn;
79
80 sn.parent = rn;
81 rn.parent = pn;
82 if(pn != null && pn.rightChild==sn){
83 pn.rightChild = rn;
84 }else if(pn != null && pn.leftChild==sn){
85 pn.leftChild = rn;
86 }
87
88 if(pn == null){
89 this.root = rn;
90 }
91
92 sn.height = Math.max(height(sn.leftChild), height(sn.rightChild))+1;
93 rn.height = Math.max(height(rn.leftChild), height(rn.rightChild))+1;
94 }
95
96 /**
97 * 插入
98 * @param subTree
99 * @param iv
100 */
101 public void insertNote(TreeNode subTree, int iv){
102
103 if(subTree == null){
104 /*空树,根结点赋值*/
105 subTree = new TreeNode(iv);
106 this.root = subTree;
107
108 }else if(subTree.data > iv){
109 /*插入左子树*/
110 if(subTree.leftChild == null){
111 TreeNode newNode = new TreeNode(iv);
112 subTree.leftChild = newNode;
113 newNode.parent = subTree;
114 }else{
115 insertNote(subTree.leftChild, iv);
116 /*判断是否需要旋转*/
117 if(compareHeight(subTree.leftChild,subTree.rightChild) == 2){
118 if(compareHeight(subTree.leftChild.leftChild,subTree.leftChild.rightChild)>=0){
119 r_Rotate(subTree);
120 }else{
121 l_Rotate(subTree.leftChild);
122 r_Rotate(subTree);
123 }
124 }
125 }
126
127 }else if(subTree.data < iv){
128 /*插入右子树*/
129 if(subTree.rightChild == null){
130 TreeNode newNode = new TreeNode(iv);
131 subTree.rightChild = newNode;
132 newNode.parent = subTree;
133 }else{
134 insertNote(subTree.rightChild, iv);
135 /*判断是否需要旋转*/
136 if(compareHeight(subTree.rightChild, subTree.leftChild) == 2){
137 if(compareHeight(subTree.rightChild.rightChild, subTree.rightChild.leftChild)>=0){
138 l_Rotate(subTree);
139 }else{
140 r_Rotate(subTree.rightChild);
141 l_Rotate(subTree);
142 }
143 }
144 }
145 }
146
147
148 /*重新赋值当前结点高度*/
149 subTree.height = Math.max(height(subTree.leftChild), height(subTree.rightChild))+1;
150 }
151
152 /**
153 * 删除结点
154 * @param subTree
155 * @param dv
156 */
157 public void deleteNote(TreeNode subTree, int dv){
158 if(subTree == null){
159 System.out.println("node is not exist.");
160
161 }else if(subTree.data == dv){
162
163 /*叶结点删除*/
164 if (subTree.leftChild == null && subTree.rightChild == null) {
165 if(subTree.parent != null){
166 /*非单结点树*/
167 if(subTree.parent.leftChild == subTree){
168 subTree.parent.leftChild = null;
169 }else{
170 subTree.parent.rightChild = null;
171 }
172 TreeNode cn = subTree.parent;
173 subTree.parent = null;
174 /*递归往上检查父类高度是否变化*/
175 deleteCheck(cn);
176 }else{
177 /*单结点树*/
178 this.root = null;
179 }
180
181 /*删除结点只存在左子树*/
182 }else if(subTree.leftChild != null && subTree.rightChild == null){
183 TreeNode ln = subTree.leftChild;
184 ln.parent = subTree.parent;
185 subTree.leftChild = null;
186 if(subTree.parent != null){
187 if(subTree.parent.leftChild == subTree){
188 subTree.parent.leftChild = ln;
189 }else{
190 subTree.parent.rightChild = ln;
191 }
192 subTree.parent = null;
193 /*递归往上检查父类高度是否变化*/
194 deleteCheck(ln.parent);
195 }else{
196 this.root = ln;
197 }
198
199 /*删除结点只存在右子树*/
200 }else if(subTree.leftChild == null && subTree.rightChild != null){
201 TreeNode rn = subTree.rightChild;
202 rn.parent = subTree.parent;
203 subTree.rightChild = null;
204 if(subTree.parent != null){
205 if(subTree.parent.leftChild == subTree){
206 subTree.parent.leftChild = rn;
207 }else{
208 subTree.parent.rightChild = rn;
209 }
210 subTree.parent = null;
211 /*递归往上检查父类高度是否变化*/
212 deleteCheck(rn.parent);
213 }else{
214 this.root = rn;
215 }
216
217 /*删除结点左右子树非空*/
218 }else{
219 TreeNode cn; //删除
220 /*寻找直接前驱*/
221 TreeNode tn = subTree.leftChild;
222
223 if(tn.rightChild == null){
224 if(subTree.parent == null){
225 this.root = tn;
226 }else if(subTree.parent.leftChild == subTree){
227 subTree.parent.leftChild = tn;
228 }else if(subTree.parent.rightChild == subTree){
229 subTree.parent.rightChild = tn;
230 }
231 tn.parent = subTree.parent;
232 tn.rightChild = subTree.rightChild;
233 subTree.rightChild.parent = tn;
234 cn = tn;
235 }else{
236 while(tn.rightChild != null){
237 tn = tn.rightChild;
238 }
239 /*释放前驱结点*/
240 cn = tn.parent;
241 if(tn.leftChild != null){
242 tn.parent.rightChild = tn.leftChild;
243 tn.leftChild.parent = tn.parent;
244 tn.parent = null;
245 tn.leftChild = null;
246 }else{
247 tn.parent.rightChild = null;
248 tn.parent = null;
249 }
250
251 /*tn是删除节点的左子树最大值(即前驱),替换删除节点*/
252 if(subTree.parent == null){
253 this.root = tn;
254 }else if(subTree.parent.leftChild == subTree){
255 subTree.parent.leftChild = tn;
256 }else if(subTree.parent.rightChild == subTree){
257 subTree.parent.rightChild = tn;
258 }
259 tn.parent = subTree.parent;
260 tn.leftChild = subTree.leftChild;
261 subTree.leftChild.parent = tn;
262 tn.rightChild = subTree.rightChild;
263 subTree.rightChild.parent = tn;
264 tn.height = subTree.height;
265
266 }
267 subTree.parent = null;
268 subTree.leftChild = null;
269 subTree.rightChild = null;
270 subTree = null;
271
272 /*递归往上检查父类高度是否变化*/
273 deleteCheck(cn);
274 }
275
276 }else if(subTree.data > dv){
277 /*从左子树继续递归删除*/
278 deleteNote(subTree.leftChild, dv);
279
280 }else if(subTree.data < dv){
281 /*从右子树继续递归删除*/
282 deleteNote(subTree.rightChild, dv);
283 }
284 }
285
286 /**
287 * 删除往上递归检查父节点树高度是否发生变化和是否失衡
288 * @param subTree
289 */
290 public void deleteCheck(TreeNode subTree){
291
292 if(subTree != null){
293 TreeNode pn = subTree.parent;
294 int hl = height(subTree.leftChild);
295 int hr = height(subTree.rightChild);
296 int height = hl>=hr?hl+1:hr+1;
297 int cp = compareHeight(subTree.leftChild,subTree.rightChild);
298
299 if(subTree.height != height){
300 subTree.height = height;
301 }
302
303 /*判断是否LL或LR*/
304 if(cp == 2){
305 if(compareHeight(subTree.leftChild.leftChild,subTree.leftChild.rightChild)>=0){
306 r_Rotate(subTree);
307 }else{
308 l_Rotate(subTree.leftChild);
309 r_Rotate(subTree);
310 }
311 }
312
313 /*判断是否RR或RL*/
314 if(cp == -2){
315 if(compareHeight(subTree.rightChild.rightChild, subTree.rightChild.leftChild)>=0){
316 l_Rotate(subTree);
317 }else{
318 r_Rotate(subTree.rightChild);
319 l_Rotate(subTree);
320 }
321 }
322
323 /*继续往上检查父类*/
324 deleteCheck(pn);
325 }
326 }
327
328
329 /**
330 * 比较子树高度差
331 * @param subTree1
332 * @param subTree2
333 * @return
334 */
335 public int compareHeight(TreeNode subTree1, TreeNode subTree2){
336 int h1 = height(subTree1);
337 int h2 = height(subTree2);
338 return h1 - h2;
339 }
340
341
342 /**
343 * 二叉树的节点数据结构
344 */
345 private class TreeNode{
346
347 private int data;
348 private TreeNode parent = null;
349 private TreeNode leftChild=null;
350 private TreeNode rightChild=null;
351 private int height = 1;
352
353 public TreeNode(int data){
354 this.data=data;
355 }
356 }
357
358 public static void main(String[] args) {
359
360 AVLTree avl = new AVLTree();
361
362 avl.insertNote(avl.root, 8);
363 avl.insertNote(avl.root, 3);
364 avl.insertNote(avl.root, 10);
365 avl.insertNote(avl.root, 1);
366 avl.insertNote(avl.root, 6);
367 avl.insertNote(avl.root, 14);
368 avl.insertNote(avl.root, 4);
369 avl.insertNote(avl.root, 7);
370 avl.insertNote(avl.root, 19);
371 avl.insertNote(avl.root, 20);
372 avl.insertNote(avl.root, 13);
373
374 System.out.print("create t over:");
375 avl.inOrder(avl.root);
376 System.out.println("");
377
378 System.out.print("delete 1 over:");
379 avl.deleteNote(avl.root, 1);
380 avl.inOrder(avl.root);
381 System.out.println("");
382
383 System.out.print("delete 7 over:");
384 avl.deleteNote(avl.root, 7);
385 avl.inOrder(avl.root);
386 System.out.println("");
387
388 System.out.print("delete 6 over:");
389 avl.deleteNote(avl.root, 6);
390 avl.inOrder(avl.root);
391 System.out.println("");
392
393 System.out.print("delete 3 over:");
394 avl.deleteNote(avl.root, 3);
395 avl.inOrder(avl.root);
396 System.out.println("");
397
398 }
399 }
运行结果:
create t over:1(1),3(3),4(1),6(2),7(1),8(4),10(2),13(1),14(3),19(2),20(1),
delete 1 over:3(2),4(1),6(3),7(1),8(4),10(2),13(1),14(3),19(2),20(1),
delete 7 over:3(1),4(2),6(1),8(4),10(2),13(1),14(3),19(2),20(1),
delete 6 over:3(1),4(2),8(4),10(2),13(1),14(3),19(2),20(1),
delete 3 over:4(1),8(3),10(2),13(1),14(4),19(2),20(1),