赞
踩
首先要说明为什么要引进平衡二叉树
【例】搜索树结点按照不同的插入次序,将导致不同的深度和平均查找长度(ASL),例如下图
第二种比较“均匀”,最平衡的方式是左右结点树相同,但是这个条件比较苛刻,可是适当放宽这个条件,因此引进了平衡二叉树
“平衡因子”(Balance Factor,简称BF):BF(T)=hL-hR,其中hL和hR分别为T的左、右子树的高度
平衡二叉树(Balanced Binary Tree)(AVL树)是空树,或者
对于任一结点左、右子树高度差的绝对值不超过1,即|BF(T)|<=1
如果一棵高度为h的二叉平衡树最少的结点数是n,那么满足递推公式nh=nh-1+nh-2+1,与斐波那契数列有如下对应关系
数学上可以证明,给定结点数为n的AVL树的最大高度为O(log2n)!
下面介绍二叉树的调整
1. 插入操作
在平衡二叉树中插入结点与二叉查找树最大的不同在于要随时保证插入后整棵二叉树是平衡的。那么调整不平衡树的基本方法就是: 旋转 。 下面我们归纳一下平衡旋转的4中情况
1) 绕某元素左旋转
80 90
/ \ 左旋 / \
60 90 ---- -> 80 120
/ \ / \ /
85 120 60 85 100
/
100
a) BST树 b ) AVL树
分析一下:在插入数据100之前,a图的B ST树只有80节点的平衡因子是-1(左高-右高),但整棵树还是平衡的。加入100之后,80节点的平衡因子就成为了-2,此时平衡被破坏。需要左旋转成b 图。
当树中节点X的右孩子的右孩子上插入新元素,且平衡因子从-1变成-2后,就需要绕节点X进行左旋转。
2) 绕某元素右旋转
100 85
/ \ 右旋 / \
85 120 ------ -> 60 100
/ \ \ / \
60 90 80 90 120
\
80
a) B ST树 b) AVL树
当树中节点X的左孩子的左孩子上插入新元素,且平衡因子从1变成2后,就需要绕节点X进行右旋转。
3) 绕某元素的左子节点左旋转,接着再绕该元素自己右旋转。 此情况下就是左旋与右旋 的结合,具体操作时可以分 解成这两种操作,只是围绕点不一样而已。
100 100 90
/ \ 左旋 / \ 右旋 / \
80 120 ------> 90 120 ------> 80 100
/ \ / / \ \
60 90 80 60 85 120
/ / \
85 60 85
当树中节点X的左孩子的右孩子上插入新元素,且 平衡因子从1变成2后,就需要 先绕X的左子节点Y左旋转,接着再绕X右旋转
4) 绕某元素的右子节点右旋转,接着再绕该元素自己左旋转。 此情况下就是 右旋与左旋 的结合,具体操作时可以分解 成这两种操作,只是围绕点不一样而已 。
80 80 85
/ \ 右 旋 / \ 左 旋 / \
60 100 ------> 60 85 -------> 80 100
/ \ \ / / \
85 120 100 60 90 120
\ / \
90 90 120
当树中节点X的右孩子的左孩子上插入新元素,且 平衡因子从-1变成-2后,就需要 先绕X的右子节点Y右旋转,接着再绕X左旋转
- #include "stdio.h"
- #include "stdlib.h"
-
- typedef int ElementType;
- typedef struct AVLNode *Position;
- typedef Position AVLTree; /* AVL树类型 */
- struct AVLNode{
- ElementType Data; /* 结点数据 */
- AVLTree Left; /* 指向左子树 */
- AVLTree Right; /* 指向右子树 */
- int Height; /* 树高 */
- };
-
- int Max ( int a, int b )
- {
- return a > b ? a : b;
- }
-
- //树的建立
- AVLTree createTree();
- //先序遍历
- void preOrder(AVLTree t);
- //中序遍历
- void intOrder(AVLTree t);
- //后序遍历
- void postOrder(AVLTree t);
- //二叉树的遍历
- void Print(AVLTree t);
-
-
- //求出树的高度
- int GetHeight(AVLTree A){
- int HL,HR,MaxH;
- if(A){
- HL= GetHeight(A->Left);
- HR= GetHeight(A->Right);
- MaxH=(HL>HR)?HL:HR;
- return (MaxH+1);
- }
- else return 0;
- };
-
- //左单旋(LL旋转)
- AVLTree SingleLeftRotation ( AVLTree A )
- { /* 注意:A必须有一个左子结点B */
- /* 将A与B做左单旋,更新A与B的高度,返回新的根结点B */
-
- AVLTree B = A->Left;
- A->Left = B->Right;
- B->Right = A;
- A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
- B->Height = Max( GetHeight(B->Left), A->Height ) + 1;
-
- return B;
- }
-
- //右单旋
- AVLTree SingleRightRotation ( AVLTree A )
- { /* 注意:A必须有一个右子结点B */
- /* 将A与B做右单旋,更新A与B的高度,返回新的根结点B */
-
- AVLTree B = A->Right;
- A->Right = B->Left;
- B->Left = A;
- A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
- B->Height = Max( GetHeight(B->Right), A->Height ) + 1;
-
- return B;
- }
-
- AVLTree DoubleLeftRightRotation ( AVLTree A )
- { /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
- /* 将A、B与C做两次单旋,返回新的根结点C */
-
- /* 将B与C做右单旋,C被返回 */
- A->Left = SingleRightRotation(A->Left);
- /* 将A与C做左单旋,C被返回 */
- return SingleLeftRotation(A);
- }
-
- AVLTree DoubleRightLeftRotation ( AVLTree A )
- { /* 注意:A必须有一个右子结点B,且B必须有一个左子结点C */
- /* 将A、B与C做两次单旋,返回新的根结点C */
-
- /* 将B与C做左单旋,C被返回 */
- A->Right = SingleLeftRotation(A->Right);
- /* 将A与C做右单旋,C被返回 */
- return SingleRightRotation(A);
- }
-
- AVLTree Insert( AVLTree &T, ElementType X )
- { /* 将X插入AVL树T中,并且返回调整后的AVL树 */
- if ( !T ) { /* 若插入空树,则新建包含一个结点的树 */
- AVLTree TT;
- TT = (AVLTree)malloc(sizeof(struct AVLNode));
- TT->Data = X;
- TT->Height = 0;
- TT->Left = TT->Right = NULL;
- T=TT;
- } /* if (插入空树) 结束 */
-
- else if ( X < T->Data ) {
- /* 插入T的左子树 */
- T->Left = Insert( T->Left, X);
- /* 如果需要左旋 */
- if ( GetHeight(T->Left)-GetHeight(T->Right) == 2 )
- if ( X < T->Left->Data )
- T = SingleLeftRotation(T); /* 左单旋 */
- else
- T = DoubleLeftRightRotation(T); /* 左-右双旋 */
- } /* else if (插入左子树) 结束 */
-
- else if ( X > T->Data ) {
- /* 插入T的右子树 */
- T->Right = Insert( T->Right, X );
- /* 如果需要右旋 */
- if ( GetHeight(T->Left)-GetHeight(T->Right) == -2 )
- if ( X > T->Right->Data )
- T = SingleRightRotation(T); /* 右单旋 */
- else
- T = DoubleRightLeftRotation(T); /* 右-左双旋 */
- } /* else if (插入右子树) 结束 */
-
- /* else X == T->Data,无须插入 */
-
- /* 别忘了更新树高 */
- T->Height = Max( GetHeight(T->Left), GetHeight(T->Right) ) + 1;
-
- return T;
- }
-
- int main()
- {
- AVLTree t= NULL;
- Insert(t,30);
- Insert(t,33);
- Insert(t,50);
- Insert(t,35);
- Insert(t,40);
- Insert(t,52);
-
- Print(t);
- system("pause");
- }
-
-
- AVLTree createTree() //树的建立
- {
- ElementType ch;
- AVLTree t;
-
- scanf("%d",&ch);//输入二叉树数据
- if(ch==-1) //判断二叉树是否为空
- t=NULL;
- else
- {
- t=(struct AVLNode *)malloc(sizeof(struct AVLNode)); //二叉树的生成
- t->Data=ch;
- t->Left=createTree();
- t->Right=createTree();
- }
- t->Height = Max( GetHeight(t->Left), GetHeight(t->Right) ) + 1;
- return t;
- }
-
-
- void preOrder(AVLTree t) //先序遍历
- {
- if(t)
- {
- printf("%d ",t->Data);
- preOrder(t->Left);
- preOrder(t->Right);
- }
- };
-
- void intOrder(AVLTree t) //中序遍历
- {
- if(t)
- {
- intOrder(t->Left);
- printf("%d ",t->Data);
- intOrder(t->Right);
- }
- };
-
- void postOrder(AVLTree t) //后序遍历
- {
- if(t)
- {
- postOrder(t->Left);
- postOrder(t->Right);
- printf("%d ",t->Data);
- }
- };
-
- //二叉树的遍历
- void Print(AVLTree t){
- printf("前序遍历:");
- preOrder(t) ;
- printf("\n");
- printf("中序遍历:");
- intOrder(t);
- printf("\n");
- printf("后序遍历:");
- postOrder(t);
- printf("\n");
- printf("\n");
- };

举个例子哈,如果依次插入30,33,50,35,40,52
那么在插入50的时候有一次RR旋转,插入40的时候有一次LR旋转,插入52的时候又有一次RR插入
结果如下
后面还有平衡二叉树的删除和红黑树,待我日后写哈哈哈
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。