我们在学习树的时候,可发现树在一些极端环境下,结构变为一种单向的链式结构。
这种结构,使得我们的查找的时间复杂度可能降到 n 。因此,人们就创造了平衡二叉树,平衡二叉树的设计,使得它的使用,在任何情况下,时间复杂度都为 log(n) 。
平衡二叉树相较普通的二叉树,在结点的设定上多了一个高度,这个高度的记录,就是为了计算结点的平衡因子。
平衡因子即根结点的左右树的高度差值。平衡因子 = 左子树高度 - 右子树高度 ,当平衡因子为0、1、-1 的时候树为平衡状态,当平衡因子为2、-2 的时候,表示树失衡,需要通过左移或者右移来恢复平衡。
平衡二叉树的结点结构:
typedef struct AVL_Node
{
int data;
int height;
struct AVL_Node* left;
struct AVL_Node* right;
}AVL_Node, AVL_Tree;
平衡因子 = root->left->height - root->right->height;
平衡二叉树,借助平衡因子的值,进行判断是否需要左移或者右移。
右移:
void Rightmove(AVL_Tree& A)
{
AVL_Tree New_node = A->left;
AVL_Tree Old_node = A;
Old_node->left = New_node->right;
New_node->right = Old_node;
Old_node->height = Height(Old_node);
New_node->height = Height(New_node);
A = New_node;
}
需要注意的是我们每次的旋转过后,我们必须要重新计算该结点的高度。
左移:
void Leftmove(AVL_Tree& A)
{
AVL_Tree New_node = A->right;
AVL_Tree Old_node = A;
Old_node->right = New_node->left;
New_node->left = Old_node;
Old_node->height = Height(Old_node);
New_node->height = Height(New_node);
A = New_node;
}
这里有一点需要特别注意,oldnode的高度计算一定要在newnode的前面。不然高度的计算会有错误!!!
以上是两种基本的旋转方式,但是在实际使用中,我们还会遇到两种另外的情况。
其一:进行右旋的时候,左子树平衡因子为-1
其二:进行左旋的时候,右子树的平衡因子为1
结点高度的计算
int Height(AVL_Tree A)
{
if (A->left == NULL && A->right == NULL)
return 1;
else if (A->left != NULL && A->right == NULL)
return A->left->height + 1;
else if (A->right != NULL && A->left == NULL)
return A->right->height + 1;
else
return A->left->height > A->right->height ? A->left->height + 1 : A->right->height + 1;
}
这里每个结点的初始高度为1,所以每层加1。
平衡因子的计算
int BF(AVL_Tree A)
{
if (A->left == NULL && A->right == NULL)
return 0;
else if (A->left != NULL && A->right == NULL)
return A->left->height;
else if (A->right != NULL && A->left == NULL)
return -A->right->height;
else
return A->left->height - A->right->height;
}
以上是平衡二叉树的基本操作
结点插入
void Insert(AVL_Tree& A, int B)
{
if (A == NULL)
{
Create_node(A, B);
return;
}
if (A->data > B)
Insert(A->left, B);
else
Insert(A->right, B);
A->height = Height(A);
if (BF(A) == 2)
{
if (A->left != NULL && BF(A->left) == -1)//这里记得判断A->left是否为空指针
Leftmove(A->left);
Rightmove(A);
}
else if (BF(A) == -2)
{
if (A->right != NULL && BF(A->right) == 1)//这里记得判断A->right是否为空指针
Rightmove(A->right);
Leftmove(A);
}
}
插入的实现是递归的操作,因为结点的高度是随着每个结点的插入改变的。
树的创建
void Create_Tree(AVL_Tree& A, int* arr, int num)
{
int rab = 0;
for (rab = 0; rab < num; rab++)
{
Insert(A, arr[rab]);
}
}
树的遍历
void In_order(AVL_Tree A)
{
if (A->left != NULL)
In_order(A->left);
printf("%d %d\n", A->data, A->height);
if (A->right != NULL)
In_order(A->right);
}
平衡二叉树的创建就到这了,我们可以发现平衡二叉树的平衡是绝对的平衡,使得我们在查找的时候,效率特别高。但是缺点也很明显,就是频繁的旋转,导致时间的流失。
谢谢观看!!!
有错误,多多指教!!!
全部代码:
typedef struct AVL_Node
{
int data;
int height;
struct AVL_Node* left;
struct AVL_Node* right;
}AVL_Node, *AVL_Tree;
int Height(AVL_Tree A)
{
if (A->left == NULL && A->right == NULL)
return 1;
else if (A->left != NULL && A->right == NULL)
return A->left->height + 1;
else if (A->right != NULL && A->left == NULL)
return A->right->height + 1;
else
return A->left->height > A->right->height ? A->left->height + 1 : A->right->height + 1;
}
int BF(AVL_Tree A)
{
if (A->left == NULL && A->right == NULL)
return 0;
else if (A->left != NULL && A->right == NULL)
return A->left->height;
else if (A->right != NULL && A->left == NULL)
return -A->right->height;
else
return A->left->height - A->right->height;
}
void Rightmove(AVL_Tree& A)
{
AVL_Tree New_node = A->left;
AVL_Tree Old_node = A;
Old_node->left = New_node->right;
New_node->right = Old_node;
Old_node->height = Height(Old_node);
New_node->height = Height(New_node);
A = New_node;
}
void Leftmove(AVL_Tree& A)
{
AVL_Tree New_node = A->right;
AVL_Tree Old_node = A;
Old_node->right = New_node->left;
New_node->left = Old_node;
Old_node->height = Height(Old_node);
New_node->height = Height(New_node);
A = New_node;
}
void Create_node(AVL_Tree& A, int B)
{
A = (AVL_Tree)malloc(sizeof(AVL_Node));
A->data = B;
A->height = 1;
A->left = A->right = NULL;
}
void Insert(AVL_Tree& A, int B)
{
if (A == NULL)
{
Create_node(A, B);
return;
}
if (A->data > B)
Insert(A->left, B);
else
Insert(A->right, B);
A->height = Height(A);
if (BF(A) == 2)
{
if (A->left != NULL && BF(A->left) == -1)
Leftmove(A->left);
Rightmove(A);
}
else if (BF(A) == -2)
{
if (A->right != NULL && BF(A->right) == 1)
Rightmove(A->right);
Leftmove(A);
}
}
void Create_Tree(AVL_Tree& A, int* arr, int num)
{
int rab = 0;
for (rab = 0; rab < num; rab++)
{
Insert(A, arr[rab]);
}
}
void In_order(AVL_Tree A)
{
if (A->left != NULL)
In_order(A->left);
printf("%d %d\n", A->data, A->height);
if (A->right != NULL)
In_order(A->right);
}
int main()
{
AVL_Tree root = NULL;
int arr[] = {1,12,44,32,65,89,15,21,34,35,68,49};
int num = sizeof(arr) / sizeof(arr[0]);
Create_Tree(root, arr, num);
In_order(root);
return 0;
}