平衡二叉树(AVL树)的基本操作(附有示意图)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
平衡二叉树关于树的深度是平衡的,具有较高的检索效率。
平衡二叉树或是一棵空树,或是具有下列性质的二叉排序树:其左子树和右子树都是平衡二叉树,而且左右子树深度之差绝对值不超过1. 由此引出了平衡因子(balance factor)的概念,bf定义为该结点的左子树的深度减去右子树的深度(有些书是右子树深度减去左子树深度,我是按照左子树减去右子树来计算的,下面的代码也是这样定义的),所以平衡二叉树的结点的平衡因子只可能是-1,0,1 ,某个结点的平衡因子绝对值大于1,该二叉树就不平衡。
平衡二叉树在出现不平衡状态的时候,要进行平衡旋转处理,有四种平衡旋转处理(单向右旋处理,单向左旋处理,双向旋转(先左后右)处理,双向旋转(先右后左)处理),归根到底是两种(单向左旋处理和单向右旋处理)。
文件"tree.h"
view plain
1.#include<iostream>
2.#include<stack>
3.#include<queue>
ing namespace std;
5.
6.const int LH=1; //左子树比右子树高1
7.const int EH=0; //左右子树一样高
8.const int RH=-1;//右子树比左子树高1
9.const int MAX_NODE_NUM=20; //结点数目上限
10.
11.class AVL_Tree;
12.
13.class AvlNode
14.{
15.int data;
16.int bf; //平衡因子
17. AvlNode *lchild;
18. AvlNode *rchild;
19.friend class AVL_Tree;
20.};
21.
22.class AVL_Tree
23.{
24.public:
25.int Get_data(AvlNode *p)
26. {
27.return p->data;
28. }
29.
30.void Create_AVl(AvlNode *&T) //建树
31. {
32. cout<<"输入平衡二叉树的元素,输入-1代表结束输入:";
33.int num[MAX_NODE_NUM];
34.int a,i=0;
35.while(cin>>a && a!=-1)
36. {
37. num[i]=a;
38. i++;
39. }
40.
41.if(num[0]==-1)
42. {
43. cout<<"平衡树为空"<<endl;
44. T=NULL;
45.return;
46. }
47.
48.int k=i;
49.bool taller=false;
50.for(i=0;i<k;i++)
51. Insert_Avl(T,num[i],taller);//逐个进行插入,插入过程看下面的示意图
52. cout<<"_____建树完成____"<<endl;
53. }
54.
55.void L_Rotate(AvlNode *&p)
56. {
57.//以p为根节点的二叉排序树进行单向左旋处理
58. AvlNode *rc=p->rchild;
59. p->rchild=rc->lchild;
60. rc->lchild=p;
61. p=rc;
62. }
63.
64.void R_Rotate(AvlNode *&p)
65. {
66.//以p为根节点的二叉排序树进行单向右旋处理
67. AvlNode *lc=p->lchild;
68. p->lchild=lc->rchild;
69. lc->rchild=p;
70. p=lc;
71. }
72.
73.void Left_Balance(AvlNode *&T)
74. {
75.//以T为根节点的二叉排序树进行左平衡旋转处理
76. AvlNode *lc,*rd;
77. lc=T->lchild;
78.switch(lc->bf)
79. {
80.case LH:
81.//新结点插在T的左孩子的左子树上,做单向右旋处理
82. T->bf=lc->bf=EH;
83. R_Rotate(T);
84.break;
85.case RH:
86.//新结点插在T的左孩子的右子树上,要进行双旋平衡处理(先左后右)
87. rd=lc->rchild;
88.switch(rd->bf)
89. {
90.case LH:
91.//插在右子树的左孩子上
92. T->bf=RH;
93. lc->bf=EH;
94.break;
95.case EH:
96. T->bf=lc->bf=EH;
97.break;
98.case RH:
99. T->bf=EH;
100. lc->bf=LH;
101.break;
102. }
103. rd->bf=EH;
104. L_Rotate(T->lchild);//先对T的左子树进行单向左旋处理
105. R_Rotate(T); //再对T进行单向右旋处理
106. }
107. }
108.
109.void Right_Balance(AvlNode *&T)
110. {
111.//以T为根节点的二叉排序树进行右平衡旋转处理
112. AvlNode *rc,*ld;
113. rc=T->rchild;
114.switch(rc->bf)
115. {
116.case RH:
117.//新结点插在右孩子的右子树上,进行单向左旋处理
118. T->bf=rc->bf=EH;
119. L_Rotate(T);
120.break;
121.case LH:
122.//新结点插在T的右孩子的左子树上,要进行右平衡旋转处理(先右再左)123. ld=rc->lchild;
124.switch(ld->bf)
125. {
126.case LH:
127. T->bf=LH;
128. rc->bf=EH;
129.break;
130.case EH:
131. T->bf=rc->bf=EH;
132.break;
133.case RH:
134. T->bf=EH;
135. rc->bf=RH;
136.break;
137. }
138. ld->bf=EH;
139. R_Rotate(T->rchild);//先对T的右子树进行单向右旋处理
140. L_Rotate(T); //再对T进行单向左旋处理
141. }
142. }
143.
144.bool Insert_Avl(AvlNode *&T,int num,bool &taller) //插入
145. {
146.//若在平衡二叉树中不存在结点值和num一样大小的结点
147.//则插入值为num的新结点,并返回true
148.//若因为插入而使得二叉排序树失去平衡,则做平衡旋转处理
149.//taller反映树是否长高
150.
151.if(!T)
152. {
153.//插入新结点,树长高,taller为true
154. T=new AvlNode;
155. T->data=num;
156. T->lchild=T->rchild=NULL;
157. T->bf=EH;
158. taller=true;
159. }
161. {
162.if(num==T->data)
163. {
164.//不重复插入
165. taller=false;
166.return false;
167. }
168.if(num<T->data) //继续在T的左子树中进行搜索
169. {
170.if(!Insert_Avl(T->lchild,num,taller))//插入不成功
171.return false;
172.if(taller) //已插入T的左子树,且左子树长高
173. {
174.switch(T->bf)
175. {
176.case LH:
177./*—————————————————————
178. / 插入前左子树高于右子树,需要进行做平衡处理
179. / 不管是单向左旋处理,还是先左后右平衡处理
180. / 处理结果都是使得插入新结点后,树的高度不变
181. /—————————————————————*/
182.
183. Left_Balance(T);
184. taller=false;
185.break;
186.case EH:
187.//插入前左右子树等高,现在插入新街点后,左子树比右子树高188.
189. T->bf=LH;
190. taller=true;
191.break;
192.case RH:
193.//插入前右子树比左子树高,现在新结点插入左子树后,树变为左右子树等高
194.
195. T->bf=EH;
196. taller=false;
197.break;
198.
199. }
200. }
201. }
202.else
204.//num>T->data 在T的右子树中继续搜索
205.if(!Insert_Avl(T->rchild,num,taller))
206.return false;
207.if(taller)
208. {
209.switch(T->bf)
210. {
211.case LH:
212.//插入前左子树比右子树高,现在插入T的右子树后,左右子树等高
213.
214. T->bf=EH;
215. taller=false;
216.break;
217.case EH:
218.//插入前左右子树等高,现在插入后,右子树比左子树高219.
220. T->bf=RH;
221. taller=true;
222.break;
223.
224.case RH:
225.//插入前右子树比坐子树高,插入后,排序树失去平衡,需要进行右平衡处理
226. Right_Balance(T);
227. taller=false;
228.break;
229.
230. }
231. }
232. }
233. }
234.return true;
235. }
236.
237.bool Search_Avl(AvlNode *T,int num,AvlNode *&f,AvlNode *&p) //搜索238. {
239.//用p带回查找到的顶点的地址,f带回p的双亲结点
240. p=T;
241.while(p)
242. {
243.if(p->data==num)
244.return true;
245.if(p->data>num)
246. {
247. f=p;
248. p=p->lchild;
249. }
250.else
251. {
252. f=p;
253. p=p->rchild;
254. }
255. }
256.return false;
257. }
258.
259.void Delete_AVL(AvlNode *&T,int num) //删除
260. {
261./*---------------------------------------------------------262. / 从树中删除一个节点后,要保证删后的树还是一棵平衡二叉树,
263. / 删除前,首先是在树中查找是否有这个结点,用p指向该结点,
264. / 用f指向p的双亲结点,这个结点在树中的位置有下面四种情况: 265. / 266. / 1:如果p指向的结点是叶子结点,那么直接将f指针的左子树或者267. / 右子树置空,然后删除p结点即可。
268. / 269. / 2:如果p指向的结点是只有左子树或右子树,那么只需要让p结点270. / 原来在f中的位置(左子树或右子树)用p的子树代替即可。
271. / 代替后,要修改f的平衡因子,在失去平衡的时候,要调用相应的272. / 做平衡旋转或右平衡旋转进行恢复.
273. / 274. / 3:如果p所指向的结点是根节点,那么直接将根节点置空
275. / 276. / 4:如果p所指向的结点左右子树都非空,为了删除p后原序列的顺277. / 序不变,就需要在原序列中先找出p的直接前驱(或者直接后继) 278. / 结点用那个结点的值来代替p结点的值,然后再删掉那个直接前
279. / 驱(或者直接后继)结点。
280. / 其中s指向的是要删除的结点,也就是p的直接前驱,q指向的是281. / s的双亲结点,此时,应该看s的平衡因子,在会出现失去平衡的282. / 情况时,就要根据实际情况采用左平衡旋转或是右平衡旋转,让
283. / 树恢复平衡,这点和插入操作时是相对应的。
284. /
285. / 在中序遍历序列中找结点的直接前驱的方法是顺着结点的左孩子
286. / 的右链域开始,一直到结点右孩子为空为止。
287. /---------------------------------------------------------*/ 288.
289. AvlNode *f=NULL;
290. AvlNode *p=NULL;
291. AvlNode *q=NULL;
292. AvlNode *s=NULL;
293.if(Search_Avl(T,num,f,p))
294. {
295.if(p->lchild && p->rchild) //左右子树均存在时
296. {
297. q=p;
298. s=p->lchild;
299.while(s->rchild)
300. {
301. q=s;
302. s=s->rchild;
303. }
304. p->data=s->data;
305.if(q!=p)
306. {
307.//q结点的右子树高度减少1
308.//所以平衡因子会+1
309. q->rchild=s->lchild;
310.switch(q->bf)
311. {
312.//删除前右子树高,现在就变成一样高
313.case RH:
314. q->bf=EH; break;
315.//删除前等高,现在就变成左子树比右子树高
316.case EH:
317. q->bf=LH; break;
318.//删除前左子树高,现在左子树又高了一,所以失去平衡319.case LH:
320. q->bf=EH;
321. Left_Balance(q);
322.break;
323. }
324. }
325.else
326. {
327.//p的左子树的右子树为空时
328.//q结点也就是p结点,由于s的右子树为空
329.//所以q结点的左子树高度降低1
330.//平衡因子-1
331. q->lchild=s->lchild;
332.switch(q->bf)
333. {
334.case LH:
335. q->bf=EH;break; 336.case EH:
337. q->bf=RH;break; 338.case RH:
339. q->bf=EH;
340. Right_Balance(q); 341.break;
342. }
343. }
344.delete s;
345. cout<<"删除结点成功"<<endl; 346.return ;
347. }
348.else
349. {
350.if(!p->lchild)
351. {
352. q=p;
353. p=p->rchild;
354. }
355.else
356. {
357. q=p;
358. p=p->lchild;
359. }
360.if(!T)
361. {
362. T->bf=EH;
363. T=p;
364. }
365.else if(q==f->lchild) 366. {
367. f->lchild=p;
368.switch(f->bf)
369. {
370.case LH:
371. f->bf=EH; break; 372.case EH:
373. f->bf=RH; break; 374.case RH:
375. f->bf=EH;
376. Right_Balance(f);
377.break;
378. }
379. }
380.else
381. {
382. f->rchild=p;
383.switch(f->bf)
384. {
385.case RH:
386. f->bf=EH; break; 387.case EH:
388. f->bf=LH; break; 389.case LH:
390. f->bf=EH;
391. Left_Balance(f); 392.break;
393. }
394. }
395.delete q;
396. cout<<"删除结点成功"<<endl; 397.return;
398. }
399. }
400.else
401. {
402. cout<<"要删除的结点不存在"<<endl; 403.return;
404. }
405. }
406.
407. InOrder_Traverse(AvlNode *T) //中序遍历408. {
409. stack<AvlNode *> s;
410. AvlNode *p=T;
411.
412.while(p || !s.empty())
413. {
414.if(p)
415. {
416. s.push(p);
417. p=p->lchild;
418. }
419.else
420. {
421. p=s.top();
422. s.pop();
423. cout<<p->data<<" ";
424. p=p->rchild;
425. }
426. }
427. }
428.
429.void Level_Traverse(AvlNode *T) //层次遍历430. {
431. queue<AvlNode *> q;
432. AvlNode *p=T;
433. q.push(p);
434.while(!q.empty())
435. {
436. p=q.front();
437. q.pop();
438. cout<<p->data<<" ";
439.if(p->lchild)
440. q.push(p->lchild);
441.if(p->rchild)
442. q.push(p->rchild);
443. }
444. }
445.
446.};
测试文件"main.cpp"
view plain
1.#include"tree.h"
2.
3.int main()
4.{
5. AVL_Tree tree;
6. AvlNode *root=NULL;
7. cout<<"____建立平衡二叉树____"<<endl;
8. tree.Create_AVl(root);
9.
10. cout<<"中序遍历二叉树为:";
11. tree.InOrder_Traverse(root);
12. cout<<endl;
13.
14. cout<<"层次遍历二叉树为:";
15. tree.Level_Traverse(root);
16. cout<<endl;
17.
18.int num;
19.bool taller=false;
20. cout<<"输入你要插入的结点的值:";
21. cin>>num;
22. tree.Insert_Avl(root,num,taller);
23.
24. cout<<"中序遍历二叉树为:";
25. tree.InOrder_Traverse(root);
26. cout<<endl;
27.
28. AvlNode *f=NULL;
29. AvlNode *p=NULL;
30. cout<<"输入你要搜索的结点的值:";
31. cin>>num;
32.if(tree.Search_Avl(root,num,f,p))
33. {
34. cout<<"查找得到的结点值为:"<<tree.Get_data(p)<<"的地址为:"<<p<<endl;
35.if(f==NULL)
36. cout<<"因为结点"<<tree.Get_data(p)<<"是根结点,所以没有双亲结点
"<<endl;
37.else
38. cout<<"该结点的双亲结点的值为:"<<tree.Get_data(f)<<endl;
39. }
40.else
41. cout<<"查找的结点不存在"<<endl;
42.
43. cout<<"输入你要删除的结点的值:";
44. cin>>num;
45. tree.Delete_AVL(root,num);
46.
47. cout<<"中序遍历二叉树为:";
48. tree.InOrder_Traverse(root);
49. cout<<endl;
50.
51.return 0;
52.}
测试结果
view plain
1.____建立平衡二叉树____
2.输入平衡二叉树的元素,输入-1代表结束输入:16 3 7 11 9 26 18 14 15 -1
3._____建树完成____
4.中序遍历二叉树为:3 7 9 11 14 15 16 18 26
5.层次遍历二叉树为:11 7 18 3 9 15 26 14 16
6.输入你要插入的结点的值:20
7.中序遍历二叉树为:3 7 9 11 14 15 16 18 20 26
8.输入你要搜索的结点的值:20
9.查找得到的结点值为:20的地址为:00380BB0
10.该结点的双亲结点的值为:26
11.输入你要删除的结点的值:15
12.删除结点成功
13.中序遍历二叉树为:3 7 9 11 14 16 18 20 26
14.Press any key to continue
下面是四种旋转的示意图
先右后左的和先左后右的相对应的所以不画了下面再画一个建树的过程就不画了太费时间了画这个刚好找到一个画好的直接贴
因为下面图中它的计算平衡因子的方式是右子树高度减去左子树高度,所以和我上面的计算方法相反,不过不影响观看
建立含有元素16 3 7 11 9 26 18 14 15 的平衡二叉树的过程如下所示:。