寫在前面的話:
linux 內核中數據結構的存儲已經不在用avl樹,我在對應的代碼中也沒有找到實現,應該是內核中全部用rbtree替換了.zebos中avl樹的實現相對較複雜,考慮了臨時緩衝等多種因素,不適合作爲初學者理解avl樹的入門代碼,因此,在網絡上找到兩篇姐姐avl樹的文章,講的很透徹了,以此作入門.理解其它avl樹的代碼應該很容易了.
正文理解:
AVL樹的介紹
AVL樹是根據它的發明者G.M. Adelson-Velsky和E.M. Landis命名的。
它是最先發明的自平衡二叉查找樹,也被稱爲高度平衡樹。相比於"二叉查找樹",它的特點是:AVL樹中任何節點的兩個子樹的高度最大差別爲1。
上面的兩張圖片,左邊的是AVL樹,它的任何節點的兩個子樹的高度差別都<=1;而右邊的不是AVL樹,因爲7的兩顆子樹的高度相差爲2(以2爲根節點的樹的高度是3,而以8爲根節點的樹的高度是1)。
AVL樹的查找、插入和刪除在平均和最壞情況下都是O(logn)。
如果在AVL樹中插入或刪除節點後,使得高度之差大於1。此時,AVL樹的平衡狀態就被破壞,它就不再是一棵二叉樹;爲了讓它重新維持在一個平衡狀態,就需要對其進行旋轉處理。學AVL樹,重點的地方也就是它的旋轉算法;在後文的介紹中,再來對它進行詳細介紹。
AVL樹的C實現
1. 節點
1.1 定義
1 typedef int Type; 2 3 typedef struct AVLTreeNode{ 4 Type key; // 關鍵字(鍵值) 5 int height; 6 struct AVLTreeNode *left; // 左孩子 7 struct AVLTreeNode *right; // 右孩子 8 }Node, *AVLTree;
AVL樹的節點包括的幾個組成對象:
(01) key -- 是關鍵字,是用來對AVL樹的節點進行排序的。
(02) left -- 是左孩子。
(03) right -- 是右孩子。
(04) height -- 是高度。
1.2 節點的創建
1 /* 2 * 創建AVL樹結點。 3 * 4 * 參數說明: 5 * key 是鍵值。 6 * left 是左孩子。 7 * right 是右孩子。 8 */ 9 static Node* avltree_create_node(Type key, Node *left, Node* right) 10 { 11 Node* p; 12 13 if ((p = (Node *)malloc(sizeof(Node))) == NULL) 14 return NULL; 15 p->key = key; 16 p->height = 0; 17 p->left = left; 18 p->right = right; 19 20 return p; 21 }
1.3 樹的高度
1 #define HEIGHT(p) ( (p==NULL) ? 0 : (((Node *)(p))->height) ) 2 3 /* 4 * 獲取AVL樹的高度 5 */ 6 int avltree_height(AVLTree tree) 7 { 8 return HEIGHT(tree); 9 }
關於高度,有的文章中將"空二叉樹的高度定義爲-1",而本文采用維基百科上的定義:樹的高度爲最大層次。即空的二叉樹的高度是0,非空樹的高度等於它的最大層次(根的層次爲1,根的子節點爲第2層,依次類推)。
1.4 比較大小
1 #define MAX(a, b) ( (a) > (b) ? (a) : (b)
2. 旋轉
AVL樹的基本操作一般涉及運作同在不平衡的二叉查找樹所運作的同樣的算法。但是要進行預先或隨後做一次或多次所謂的"AVL旋轉"。
以下圖表以四列表示四種情況,每行表示在該種情況下要進行的操作。在左左和右右的情況下,只需要進行一次旋轉操作;在左右和右左的情況下,需要進行兩次旋轉操作。
通過上圖可以知道,如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。這種失去平衡的可以概括爲4種姿態:LL(左左),LR(左右),RR(右右)和RL(右左)。需要說明的是,下面的內容只給出在這四中情況下如何進行旋轉使得avl樹達到新的平衡,以幫助讀者理解avl樹的代碼,內容不會涉及具體這樣的原因,你只需要知道,看,我進行這樣操作後,會重新得到一個二叉平衡搜索樹。下面給出它們的示意圖:
上圖中的4棵樹都是"失去平衡的AVL樹",從左往右的情況依次是:LL、LR、RL、RR。總的來說,AVL樹失去平衡時的情況一定是LL、LR、RL、RR這4種之一,它們都由各自的定義:
(1) LL:LeftLeft,也稱爲"左左"。插入或刪除一個節點後,根節點的左子樹的左子樹還有非空子節點,導致"根的左子樹的高度"比"根的右子樹的高度"大2,導致AVL樹失去了平衡。
例如,在上面LL情況中,由於"根節點(8)的左子樹(4)的左子樹(2)還有非空子節點",而"根節點(8)的右子樹(12)沒有子節點";導致"根節點(8)的左子樹(4)高度"比"根節點(8)的右子樹(12)"高2。
(2) LR:LeftRight,也稱爲"左右"。插入或刪除一個節點後,根節點的左子樹的右子樹還有非空子節點,導致"根的左子樹的高度"比"根的右子樹的高度"大2,導致AVL樹失去了平衡。
例如,在上面LR情況中,由於"根節點(8)的左子樹(4)的左子樹(6)還有非空子節點",而"根節點(8)的右子樹(12)沒有子節點";導致"根節點(8)的左子樹(4)高度"比"根節點(8)的右子樹(12)"高2。
(3) RL:RightLeft,稱爲"右左"。插入或刪除一個節點後,根節點的右子樹的左子樹還有非空子節點,導致"根的右子樹的高度"比"根的左子樹的高度"大2,導致AVL樹失去了平衡。
例如,在上面RL情況中,由於"根節點(8)的右子樹(12)的左子樹(10)還有非空子節點",而"根節點(8)的左子樹(4)沒有子節點";導致"根節點(8)的右子樹(12)高度"比"根節點(8)的左子樹(4)"高2。
(4) RR:RightRight,稱爲"右右"。插入或刪除一個節點後,根節點的右子樹的右子樹還有非空子節點,導致"根的右子樹的高度"比"根的左子樹的高度"大2,導致AVL樹失去了平衡。
例如,在上面RR情況中,由於"根節點(8)的右子樹(12)的右子樹(14)還有非空子節點",而"根節點(8)的左子樹(4)沒有子節點";導致"根節點(8)的右子樹(12)高度"比"根節點(8)的左子樹(4)"高2。
前面說過,如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。AVL失去平衡之後,可以通過旋轉使其恢復平衡,下面分別介紹"LL(左左),LR(左右),RR(右右)和RL(右左)"這4種情況對應的旋轉方法。
avl樹的旋轉,核心思想在旋轉這一節的第一張圖中表達的已經很清楚了,無論是左旋還是右旋(實質上只有兩種旋轉),在寫函數代碼的時候,函數的形參是失去平很的根節點root,函數的具體實現內容是,失去平衡的根節點root圍繞着平衡後的根節點 pivot進行相應的旋轉操作。
LL失去平衡的情況,可以通過一次旋轉讓AVL樹恢復平衡。如下圖:
圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。從中可以發現,旋轉之後的樹又變成了AVL樹,而且該旋轉只需要一次即可完成。
LL的旋轉代碼
1 /* 2 * LL:左左對應的情況(右單旋轉)。 3 * 4 * 返回值:旋轉後的根節點 5 */ 6 static Node* left_left_rotation(AVLTree k2) 7 { 8 AVLTree k1; 9 10 k1 = k2->left; 11 k2->left = k1->right; 12 k1->right = k2; 13 14 k2->height = MAX( HEIGHT(k2->left), HEIGHT(k2->right)) + 1; 15 k1->height = MAX( HEIGHT(k1->left), k2->height) + 1; 16 17 return k1; 18 }
我感覺上面函數名字起得有問題,應該叫做left_left_situation或者right_rotation,因爲實際上,對於LL的情形來說,實際上對應的是右旋轉:即傳入的形參是失去平衡的根節點,函數的內容是失去平衡的根節點root圍繞着重新平衡後的根節點pivot進行右旋轉操作。
2.2 RR的旋轉
理解了LL之後,RR就相當容易理解了。RR是與LL對稱的情況!RR恢復平衡的旋轉方法如下:
圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。RR旋轉也只需要一次即可完成。
RR的旋轉代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/* *
RR:右右對應的情況(左單旋轉)。 * *
返回值:旋轉後的根節點 */ static Node*
right_right_rotation(AVLTree k1) { AVLTree
k2; k2
= k1->right; k1->right
= k2->left; k2->left
= k1; k1->height
= MAX( HEIGHT(k1->left), HEIGHT(k1->right)) + 1; k2->height
= MAX( HEIGHT(k2->right), k1->height) + 1; return k2; } |
我感覺上面函數名字起得有問題,應該叫做right_right_situation或者left_rotation,因爲實際上,對於RR的情形來說,實際上對應的是左旋轉:即傳入的形參是失去平衡的根節點,函數的內容是失去平衡的根節點root圍繞着重新平衡後的根節點pivot進行左旋轉操作。
2.3 LR的旋轉
LR失去平衡的情況,需要經過兩次旋轉才能讓AVL樹恢復平衡。如下圖
第一次旋轉是圍繞"k1"進行的"RR旋轉",第二次是圍繞"k3"進行的"LL旋轉"。
LR的旋轉代碼
1 /* 2 * LR:左右對應的情況(左雙旋轉)。 3 * 4 * 返回值:旋轉後的根節點 5 */ 6 static Node* left_right_rotation(AVLTree k3) 7 { 8 k3->left = right_right_rotation(k3->left); 9 10 return left_left_rotation(k3); 11 } 12 複製代碼
左右旋轉的情況結合本節第一張圖最好理解。
2.4 RL的旋轉
RL是與LR的對稱情況!RL恢復平衡的旋轉方法如下:
RL的情況結合本節第一張圖最好理解。
1 /* 2 * RL:右左對應的情況(右雙旋轉)。 3 * 4 * 返回值:旋轉後的根節點 5 */ 6 static Node* right_left_rotation(AVLTree k1) 7 { 8 k1->right = left_left_rotation(k1->right); 9 10 return right_right_rotation(k1); 11 }
3. 插入
插入節點的代碼
1 /* 2 * 將結點插入到AVL樹中,並返回根節點 3 * 4 * 參數說明: 5 * tree AVL樹的根結點 6 * key 插入的結點的鍵值 7 * 返回值: 8 * 根節點 9 */ 10 Node* avltree_insert(AVLTree tree, Type key) 11 { 12 if (tree == NULL) 13 { 14 // 新建節點 15 tree = avltree_create_node(key, NULL, NULL); 16 if (tree==NULL) 17 { 18 printf("ERROR: create avltree node failed!\n"); 19 return NULL; 20 } 21 } 22 else if (key < tree->key) // 應該將key插入到"tree的左子樹"的情況 23 { 24 tree->left = avltree_insert(tree->left, key); 25 // 插入節點後,若AVL樹失去平衡,則進行相應的調節。 26 if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2) 27 { 28 if (key < tree->left->key) 29 tree = left_left_rotation(tree); 30 else 31 tree = left_right_rotation(tree); 32 } 33 } 34 else if (key > tree->key) // 應該將key插入到"tree的右子樹"的情況 35 { 36 tree->right = avltree_insert(tree->right, key); 37 // 插入節點後,若AVL樹失去平衡,則進行相應的調節。 38 if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2) 39 { 40 if (key > tree->right->key) 41 tree = right_right_rotation(tree); 42 else 43 tree = right_left_rotation(tree); 44 } 45 } 46 else //key == tree->key) 47 { 48 printf("添加失敗:不允許添加相同的節點!\n"); 49 } 50 51 tree->height = MAX( HEIGHT(tree->left), HEIGHT(tree->right)) + 1; 52 53 return tree; 54 }
4. 刪除
刪除節點的代碼
1 /* 2 * 刪除結點(z),返回根節點 3 * 4 * 參數說明: 5 * ptree AVL樹的根結點 6 * z 待刪除的結點 7 * 返回值: 8 * 根節點 9 */ 10 static Node* delete_node(AVLTree tree, Node *z) 11 { 12 // 根爲空 或者 沒有要刪除的節點,直接返回NULL。 13 if (tree==NULL || z==NULL) 14 return NULL; 15 16 if (z->key < tree->key) // 待刪除的節點在"tree的左子樹"中 17 { 18 tree->left = delete_node(tree->left, z); 19 // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。 20 if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2) 21 { 22 Node *r = tree->right; 23 if (HEIGHT(r->left) > HEIGHT(r->right)) 24 tree = right_left_rotation(tree); 25 else 26 tree = right_right_rotation(tree); 27 } 28 } 29 else if (z->key > tree->key)// 待刪除的節點在"tree的右子樹"中 30 { 31 tree->right = delete_node(tree->right, z); 32 // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。 33 if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2) 34 { 35 Node *l = tree->left; 36 if (HEIGHT(l->right) > HEIGHT(l->left)) 37 tree = left_right_rotation(tree); 38 else 39 tree = left_left_rotation(tree); 40 } 41 } 42 else // tree是對應要刪除的節點。 43 { 44 // tree的左右孩子都非空 45 if ((tree->left) && (tree->right)) 46 { 47 if (HEIGHT(tree->left) > HEIGHT(tree->right)) 48 { 49 // 如果tree的左子樹比右子樹高; 50 // 則(01)找出tree的左子樹中的最大節點 51 // (02)將該最大節點的值賦值給tree。 52 // (03)刪除該最大節點。 53 // 這類似於用"tree的左子樹中最大節點"做"tree"的替身; 54 // 採用這種方式的好處是:刪除"tree的左子樹中最大節點"之後,AVL樹仍然是平衡的。 55 Node *max = avltree_maximum(tree->left); 56 tree->key = max->key; 57 tree->left = delete_node(tree->left, max); 58 } 59 else 60 { 61 // 如果tree的左子樹不比右子樹高(即它們相等,或右子樹比左子樹高1) 62 // 則(01)找出tree的右子樹中的最小節點 63 // (02)將該最小節點的值賦值給tree。 64 // (03)刪除該最小節點。 65 // 這類似於用"tree的右子樹中最小節點"做"tree"的替身; 66 // 採用這種方式的好處是:刪除"tree的右子樹中最小節點"之後,AVL樹仍然是平衡的。 67 Node *min = avltree_maximum(tree->right); 68 tree->key = min->key; 69 tree->right = delete_node(tree->right, min); 70 } 71 } 72 else 73 { 74 Node *tmp = tree; 75 tree = tree->left ? tree->left : tree->right; 76 free(tmp); 77 } 78 } 79 80 return tree; 81 } 82 83 /* 84 * 刪除結點(key是節點值),返回根節點 85 * 86 * 參數說明: 87 * tree AVL樹的根結點 88 * key 待刪除的結點的鍵值 89 * 返回值: 90 * 根節點 91 */ 92 Node* avltree_delete(AVLTree tree, Type key) 93 { 94 Node *z; 95 96 if ((z = avltree_search(tree, key)) != NULL) 97 tree = delete_node(tree, z); 98 return tree; 99 }
好了,avl樹重點到此結束,後面的內容參考原文吧。
原文鏈接:http://blog.csdn.net/programmingring/article/details/37969745
以上轉自:https://www.cnblogs.com/3me-linux/p/6473836.html
// 代碼實現
#include <bits/stdc++.h>
using namespace std;
typedef int Type;
typedef struct AVLTreeNode {
Type key;
int height;
struct AVLTreeNode *left;
struct AVLTreeNode *right;
} Node, *AVLTree;
static Node* avltree_create_node(Type key, Node *left, Node *right) {
Node *p;
if((p = (Node *)malloc(sizeof(Node))) == NULL) {
return NULL;
}
p->key = key;
p->height = 0;
p->left = left;
p->right = right;
return p;
}
#define HEIGHT(p) ((p == NULL) ? 0 : ((Node *)p)->height)
#define MAX(a, b) ((a > b) ? (a) : (b))
//左左情況,右旋
static Node* avltree_right_rotation(AVLTree root) {
AVLTree pivot;
pivot = root->left;
root->left = pivot->right;
pivot->right = root;
root->height = MAX(HEIGHT(root->left), HEIGHT(root->right)) + 1;
pivot->height = MAX(HEIGHT(pivot->left), root->height) + 1;
return pivot;
}
//右右情況,左旋
static Node* avltree_left_rotation(AVLTree root) {
AVLTree pivot;
pivot = root->right;
root->right = pivot->left;
pivot->left = root;
root->height = MAX(HEIGHT(root->left), HEIGHT(root->right)) + 1;
pivot->height = MAX(HEIGHT(pivot->right), root->height) + 1;
return pivot;
}
//左右情況,先左旋後右旋
static Node* avltree_left_right_rotation(AVLTree root) {
root->left = avltree_left_rotation(root->left);
return avltree_right_rotation(root);
}
//右左情況,先右旋後左旋
static Node* avltree_right_left_rotation(AVLTree root) {
root->right = avltree_right_rotation(root->right);
return avltree_left_rotation(root);
}
//Insert
static Node* avltree_insert(AVLTree tree, Type key) {
if(tree == NULL) {
tree = avltree_create_node(key, NULL, NULL);
if(tree == NULL) {
printf("Error: create avltree node failed!\n");
return NULL;
}
}
else if(key < tree->key) {
tree->left = avltree_insert(tree->left, key);
if(HEIGHT(tree->left) - HEIGHT(tree->right) == 2) {
if(key < tree->left->key) {
tree = avltree_right_rotation(tree);
}
else {
tree = avltree_left_right_rotation(tree);
}
}
}
else if(key > tree->key) {
tree->right = avltree_insert(tree->right, key);
if(HEIGHT(tree->right) - HEIGHT(tree->left) == 2) {
if(key > tree->right->key) {
tree = avltree_left_rotation(tree);
}
else {
tree = avltree_right_left_rotation(tree);
}
}
}
else {
printf("Don't allow insert existed node!");
}
tree->height = MAX(HEIGHT(tree->left), HEIGHT(tree->right)) + 1;
return tree;
}
// MaxKey_Node
static Node* avltree_maximum(AVLTree tree) {
Node *tmp, *maxx = tree;
if(tree->left) {
tmp = avltree_maximum(tree->left);
if(tmp->key > maxx->key) {
maxx = tmp;
}
}
if(tree->right) {
tmp = avltree_maximum(tree->right);
if(tmp->key > maxx->key) {
maxx = tmp;
}
}
return maxx;
}
//MinKey_Node
static Node* avltree_minimum(AVLTree tree) {
Node *tmp, *minn = tree;
if(tree->left) {
tmp = avltree_minimum(tree->left);
if(tmp->key < minn->key) {
minn = tmp;
}
}
if(tree->right) {
tmp = avltree_minimum(tree->right);
if(tmp->key < minn->key) {
minn = tmp;
}
}
return minn;
}
//Delete
static Node* avltree_delete(AVLTree tree, Node *x) {
if(tree == NULL || x == NULL) {
return NULL;
}
if(x->key < tree->key) {
tree->left = avltree_delete(tree->left, x);
if(HEIGHT(tree->right) - HEIGHT(tree->left) == 2) {
Node *r = tree->right;
if(HEIGHT(r->left) < HEIGHT(r->right)) {
tree = avltree_left_rotation(tree);
}
else {
tree = avltree_right_left_rotation(tree);
}
}
}
else if(x->key > tree->key) {
tree->right = avltree_delete(tree->right, x);
if(HEIGHT(tree->left) - HEIGHT(tree->right) == 2) {
Node *l = tree->left;
if(HEIGHT(l->left) > HEIGHT(l->right)) {
tree = avltree_right_rotation(tree);
}
else {
tree = avltree_left_right_rotation(tree);
}
}
}
else {
if((tree->left) && (tree->right)) {
if(HEIGHT(tree->left) > HEIGHT(tree->right)) {
Node *maxx = avltree_maximum(tree->left);
tree->key = maxx->key;
tree->left = avltree_delete(tree->left, maxx);
}
else {
Node *minn = avltree_minimum(tree->right);
tree->key = minn->key;
tree->right = avltree_delete(tree->right, minn);
}
}
else {
Node *tmp = tree;
tree = tree->left ? tree->left : tree->right;
free(tmp);
}
}
if(tree) {
tree->height = MAX(HEIGHT(tree->left), HEIGHT(tree->right)) + 1;
}
return tree;
}
//Visit
static void avltree_visit(AVLTree tree) {
queue<AVLTree> q;
if(tree == NULL) {
return;
}
q.push(tree);
while(!q.empty()) {
Node *tmp = q.front();
q.pop();
printf("%d ", tmp->key);
if(tmp->left) {
q.push(tmp->left);
}
if(tmp->right) {
q.push(tmp->right);
}
}
printf("\n");
}
int main() {
int a[] = {1, 4, 3, 5, 2, 6, 7};
AVLTree root = NULL;
// 模擬insert
for(int i = 0; i < 7; ++i) {
root = avltree_insert(root, a[i]);
}
avltree_visit(root);
AVLTree tmp = avltree_create_node(2, NULL, NULL);
// 模擬delete
root = avltree_delete(root, tmp);
avltree_visit(root);
return 0;
}
/*
*
* O O O
* / \ del 2 / \ rotation / \
* O O ===> O O ===> O O
* \ / \ / \ / \ \
* O O O O O O O O
* \ \
* O O
*/
繼續加油~