BST
1
2
3
4
5
|
typedef struct node{
int value;
struct node* left;
struct node* right;
} node;
|
1
2
3
4
5
6
7
|
node* creatNode(int value){
node* newnode = (node*)malloc(sizeof(node));
newnode->value = value;
newnode->left = NULL;
newnode->right = NULL;
return newnode;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
node* insert(node* root, int value){
if(root == NULL){
return creatNode(value);
}
if(value<root->value){
root->left = insert(root->left,value);
}
else if(value>root->value){
root->right = insert(root->right,value);
}
return root;
}
|
1
2
3
4
5
6
7
8
9
|
node* search(node* root,int value){
if(root->value == value || root == NULL){
return root;
}
if(value<root->value){
return search(root->left,value);
}
return search(root->right,value);
}
|
1
2
3
4
5
6
7
|
node* findmin(node* root){
node* current = root;
while(current != NULL && current->left != NULL){
current = current->left;
}
return current;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
node* delete(node* root, int value){
if(root == NULL){
return root;
}
if(value < root->value){
root->left=delete(root->left,value);
}
else if(value > root->value){
root->right=delete(root->right,value);
} // 前半部分寻找结点
else if(value == root->value){ // 找到结点后
if(root->left == NULL){ // 若删除的结点没有或只有一个结点
node* temp = root->right;
free(root);
return temp;
}
else if(root->right == NULL){
node* temp = root->left;
free(root);
return temp;
}
// 若结点有两个结点,则找到其右树的最小结点将其代替
node* temp = findmin(root->right);
root->value = temp->value;
root->right = delete(root->right,temp->value);
}
return root;
}
|
1
2
3
4
5
6
7
|
void freeTree(node* root) {
if (root != NULL) {
freeTree(root->left);
freeTree(root->right);
free(root);
}
}
|
AVL
1
2
3
4
5
6
|
typedef struct node{
int data;
struct node* left;
struct node* right;
int height;
} node;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
int getheight(node* root){
if(root == NULL){
return 0;
}
return root->height;
}
int getbalance(node* root){
if(root == NULL){
return 0;
}
return getheight(root->left) - getheight(root->right);
}
|
1
2
3
4
5
6
7
8
|
node* creatnode(int data){
node* root = (node*)malloc(sizeof(node));
root->data = data;
root->left = NULL;
root->right = NULL;
root->height = 1;
return root;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
node* leftrotate(node* root){
node* r = root->right;
node* l = r->left;
r->left = root;
root->right = l;
root->height = max(getheight(root->left),getheight(root->right))+1;
r->height = max(getheight(r->left),getheight(r->right))+1;
return r;
}
node* rightrotate(node* root){
node* l = root->left;
node* r = l->right;
l->right = root;
root->left = r;
root->height = max(getheight(root->left) , getheight(root->right))+1;
l->height = max(getheight(l->left) , getheight(l->right))+1;
return l;
}
|
在此详细解释两种操作:
左旋操作为储存操作结点root的右结点r,以及右结点r的左结点l,将原root结点下降至原l结点的位置,并将储存的原l结点变为现l结点(即原root结点)的右结点
右旋操作为储存操作结点root的左结点l,以及左节点l的右结点r,将原root结点下降至原r结点的位置,并将储存的原r结点变为现r结点(即原root结点)的左结点
两种操作的本质内核都是尽可能降低原树的不平衡度,使原树中没有结点的位置被最大程度利用,从而使得树更加平衡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
node* insert(node* root, int data){
if(root==NULL){
return creatnode(data);
}
if(data < root->data){
root->left = insert(root->left,data);
}
else if(data > root->data){
root->right = insert(root->right,data);
} // 寻找结点应插入的位置
else if(data == root->data){
return root; // 插入相同结点则返回原有结点
}
// 找到插入位置
root->height = max(getheight(root->left),getheight(root->right))+1; // 实时更新树的高度
int balance = getbalance(root); // 计算目前的平衡因子
// 四种情况分类
if(balance > 1 && getbalance(root->left) >= 0){
return rightrotate(root);
}
if(balance < -1 && getbalance(root->right) <= 0){
return leftrotate(root);
}
if(balance > 1 && getbalance(root->left) < 0){
root->left = leftrotate(root->left);
return rightrotate(root);
}
if(balance < -1 && getbalance(root->right) > 0){
root->right = rightrotate(root->right);
return leftrotate(root);
}
return root;
}
|
四种分类情况分别代表了不同的失衡类型,若root结点的左树失衡,则第一类型为L,若右树失衡则第一类型为R,再进一步观察失衡树的平衡因子以确定下一步进行左旋还是右旋,左树高则为L型,右树高则为R型
LL型为左树严重失衡,使用右旋
RR型为右树严重失衡,使用左旋
LR型则先左旋左结点变为LL型,再右旋
RL型则先右旋右结点变为RR型,再左旋
总结来说,旋转操作的左右旋与类型的R与L相反,按步骤反向进行即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
node* delete(node* root , int data){
if(root == NULL){
return root;
}
if(data < root->data){
root->left = delete(root->left , data);
}
else if(data > root->data){
root->right = delete(root->right,data);
} // 寻找要删除的结点
else if(data == root->data){
if(root->left == NULL){
node* temp = root->right;
free(root);
return temp;
}
else if(root->right == NULL){
node* temp = root->left;
free(root);
return temp;
} // 删除结点若没有或只有一个子结点,则直接将子结点返回
else{
node* temp = findmin(root->right);
root->data = temp->data;
root->right = delete(root->right,temp->data);
}
// 若含有两个结点,则将其右子树的最小结点替换自身,并删除右子树的最小结点
}
if (root == NULL) { // 添加检查
return root;
}
root->height = max(getheight(root->left),getheight(root->right)) + 1; // 更新树的高度
int balance = getbalance(root); // 更新平衡因子
if (balance > 1 && getbalance(root->left) >= 0){
return rightrotate(root);
}
if (balance > 1 && getbalance(root->left) < 0){
root->left = leftrotate(root->left);
return rightrotate(root);
}
if(balance < -1 && getbalance(root->right) <= 0){
return leftrotate(root);
}
if(balance < -1 && getbalance(root->right) > 0){
root->right = rightrotate(root->right);
return leftrotate(root);
} // 此处四种情况同插入,本质是使新树平衡度提高,提升空间利用率
return root;
}
|
总结
AVL的构建要求高于BST,更加严格
因此AVL可以防止插入数据顺序过于整齐而导致整个树退化为链表,从而使时间复杂度由 log(n) 提升至 n
AVL的插入,查找,删除时间复杂度均为 log(n),但若进行频繁的插入,删除,红黑树的性能会优于AVL,因此 AVL 主要用于查找