平衡二叉树 又叫 AVL树
为什么出现平衡二叉树? 对于前面的二叉排序树,如果数组大小排列是随机的,则能大大提升速度,但设想如果数组是有序的,那么二叉排序树就更像一个链表了,没有得到想要的效果。而平衡二叉树能改变这种窘况。
平衡二叉树也叫平衡二叉搜索树,也叫AVL树可以保证查询效率较高。
平衡二叉树是一颗空树或者左右两个子树的高度之差不超过 1 的二叉排序树,且左右两个子树都是平衡二叉树。平衡二叉树的常用实现方法有: 红黑树,AVL (算法,不是树), 替罪羊树 , Treap , 伸展树。
要求:给你一个数列,创建对应的平衡二叉树。 数列: 【4,3,6,5,7,8】
首先需要了解几个概念:
平衡因子:将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。
最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树.。
平衡调整: 分为LL型, LR型,RR型 和RL型
LL型调整
由于在A的左孩子(L)的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1增至2。下面图1是LL型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B顺时针旋转一样。
LL型调整的一般形式如下图2所示,表示在A的左孩子B的左子树BL(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将A的左孩子B提升为新的根结点;②将原来的根结点A降为B的右孩子;③各子树按大小关系连接(BL和AR不变,BR调整为A的左子树)。
RR型调整
由于在A的右孩子®的右子树®上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图3是RR型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B逆时针旋转一样。
RR型调整的一般形式如下图4所示,表示在A的右孩子B的右子树BR(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:
LR型
由于在A的左孩子(L)的右子树®上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1变为2。图5是LR型的最简单形式。显然,按照大小关系,结点C应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。
LR型调整的一般形式如下图6所示,表示在A的左孩子B的右子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将B的右孩子C提升为新的根结点;②将原来的根结点A降为C的右孩子;③各子树按大小关系连接(BL和AR不变,CL和CR分别调整为B的右子树和A的左子树)。
RL型调整
由于在A的右孩子®的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图7是RL型的最简单形式。显然,按照大小关系,结点C应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。
RL型调整的一般形式如下图8所示,表示在A的右孩子B的左子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将B的左孩子C提升为新的根结点;②将原来的根结点A降为C的左孩子;③各子树按大小关系连接(AL和BR不变,CL和CR分别调整为A的右子树和B的左子树)。
上面的过程非常清楚且简单易懂,但是还有一些细节的地方需要推敲,例如:
1、为什么初始都是三个结点,但是LL 型和RR型在下面却变成了两个顶点呢?
其实还是三个顶点,你不妨可以将LL型和RR型按照三个顶点的形式推一下,你会发现C顶点的两个子树最后还是在C身上并且相对位置也不会改变,那么其实就可以直接将他看出B的一个子树就i行了,这样能简化操作,当然如果你觉得不好记,也可以统一按照三结点的方式进行处理。
2、添加一个结点后可能导致多个结点不平衡,应该操作那个结点呢?
由于我们是使用递归的方法,所以在最下面添加完一个结点后再慢慢向上判断它的父节点是不是平衡结点,然后再判断父节点的父节点是不是平衡结点,所以处理的是最下面的结点,也就是最小不平衡子树。
3、怎么对一个结点进行平衡判断呢?
其实前面学过了使用递归的方法求结点的高度,可以在这里也是用递归的方法求一个结点的左子树高度和右子树高度,如果高度差的绝对值大于1的话就是不平衡的。这种方法虽然简单,但是会进行大量重复的计算,效率非常低,所以更好的一种方法是在每个结点中添加一个height属性记录结点的高度,每次添加一个结点后需要对这个结点的所有父结点进行高度维护(维护未必就是直接加一,而是heigth = 左右子树高度中的最大值 +1)。
4、就算知道一个结点不平衡,如果知道他是那种类型呢?
根据新增结点的值和该结点左右子树值的比较可以知道新增结点的添加位置,由此判断。
代码:
main.c
#include"node.h"
#include
#include
int main(int argc, char *argv[]) {
Node *tree = NULL;
int key = 1;
puts("输入值(-1退出):");
scanf("%d",&key);
while(key != -1) {
tree = addNode(tree, key);
puts("输入值(-1退出):");
scanf("%d",&key);
}
puts("先序遍历为:");
preOlderTraverser(tree);
}
node.h
#ifndef NODE_H
#define NODE_H
#include
#include
#include
typedef struct Node {
int val;
struct Node *left, *right;
int height;
}Node;
void preOlderTraverser(Node *tree);
Node* createNode(int val);
Node* rl_rotate(Node *tree);
Node* rr_rotate(Node *tree);
Node* ll_rotate(Node *tree);
Node* lr_rotate(Node *tree);
Node* addNode(Node *tree, int val);
void modifyHeight(Node *tree);
#endif
node.c
#include"node.h"
Node* addNode(Node *tree, int val) {
if(tree == NULL) {
return createNode(val);
}
if(val < tree->val) {
if(tree->left == NULL) {
tree->left = createNode(val);
}
else tree->left = addNode(tree->left, val);
}
else if(val > tree->val) {
if(tree->right == NULL) {
tree->right = createNode(val);
}
else
tree->right = addNode(tree->right, val);
}
else {
puts("元素重复");
}
int lheight = 0, rheight = 0;
if(tree->left != NULL) {
lheight = tree->left->height;
}
if(tree->right != NULL) {
rheight = tree->right->height;
}
//修改结点高度
modifyHeight(tree);
//判断该树是不是平衡二叉树
int balance = abs(lheight-rheight);
//需要进行平衡调整
if(balance > 1) {
if(tree->left != NULL && val < tree->left->val) {
tree = ll_rotate(tree);
}
else if(tree->right != NULL && val > tree->right->val) {
tree = rr_rotate(tree);
}
else if(tree->left != NULL && val > tree->left->val) {
tree = lr_rotate(tree);
}
else if(tree->right != NULL && val < tree->right->val){
tree = rl_rotate(tree);
}
}
return tree;
}
Node* lr_rotate(Node *tree) {
Node *A = tree;
Node *B = tree->left;
Node *C = tree->left->right;
B->right = C->left;
A->left = C->right;
C->left = B;
C->right = A;
//修正结点的高度
modifyHeight(A);
modifyHeight(B);
modifyHeight(C);
return C;
}
Node* rr_rotate(Node *tree) {
Node *A = tree;
Node *B = tree->right;
A->right = B->left;
B->left = A;
//修正结点的高度
modifyHeight(A);
modifyHeight(B);
return B;
}
Node* ll_rotate(Node *tree) {
Node *A = tree;
Node *B = tree->left;
A->left = B->right;
B->right = A;
//修正结点的高度
modifyHeight(A);
modifyHeight(B);
return B;
}
Node* rl_rotate(Node *tree) {
Node *A = tree;
Node *B = tree->right;
Node *C = tree->right->left;
B->left = A->left;
A->left = C->right;
A->right = B->right;
B->right = C->left;
C->left = B;
C->right = A;
//修正结点的高度
modifyHeight(A);
modifyHeight(B);
modifyHeight(C);
return C;
}
Node* createNode(int val) {
Node *node = (Node*)malloc(sizeof(Node));
node->height = 1;
node->left = NULL;
node->right = NULL;
node->val = val;
return node;
}
void preOlderTraverser(Node *tree) {
if(tree == NULL) {
return;
}
printf("%d\n",tree->val);
preOlderTraverser(tree->left);
preOlderTraverser(tree->right);
}
void modifyHeight(Node *tree) {
int lheight = 0;
int rheight = 0;
if(tree->left != NULL) {
lheight = tree->left->height;
}
if(tree->right != NULL) {
rheight = tree->right->height;
}
tree->height = (lheight > rheight ? lheight : rheight)+1;
}