1. 概述
主要知識點
- AVL 樹定義
- 單旋轉(左左單旋轉、右右單旋轉)
- 雙旋轉(左右雙旋轉、右左雙旋轉)
2. 什麼是 AVL 樹
定義
帶有平衡條件的二叉查詢樹。
平衡條件:其每個節點的左子樹和右子樹的高度最多相差1的二叉查詢樹。(空樹的高度為 -1)。
- 所有的樹操作都可以以時間 O(logN) 執行。
- 當插入時有可能會破壞平衡條件,我們通過旋轉(rotation)來進行修正。
3. 如何解決不平衡情況
根據 AVL 樹的特性可以知道:α 點的兩顆子樹的高度差2(α 是需要平衡的節點)。
所以這種不平衡在插入時可能出現的情況如下四種:
- α 的左兒子的左子樹進行一次插入。(左左單旋轉)
- α 的左兒子的右子樹進行一次插入。 (左右雙旋轉)
- α 的右兒子的左子樹進行一次插入。 (右左雙旋轉)
- α 的右兒子的右子樹進行一次插入。 (右右單旋轉)
以上四種情況可以合成兩種情況
- 1 和 4 是插入發生在外部(左左、右右)單旋轉處理。
- 2 和 3 是插入發生的內部(左右、右左)雙旋轉處理。
4. 關於旋轉
旋轉是對樹的常用操作,有兩個最重要的屬性:
- 旋轉軸
- 旋轉方向
4.1 旋轉軸的確定
- 單旋轉:α 的孩子節點。
- 雙旋轉:α 的孫子節點。
α 為需要平衡的節點即是不滿足 AVL 特性的最小樹的跟節點。
4.2 旋轉方向
- 左左單旋轉(順時針)。
- 右右單旋轉(逆時針)
4.3 單旋轉還是雙旋轉
- 情況 1 和情況 4 用單旋轉。
- 情況 2 和情況 3 需要用雙旋轉。
根據二叉搜尋樹的特性可以得到(設插入數為 x,旋轉軸為 β)
- x 的大小階於 α 的值與 β 的值之外(即是情況 1 和 4,外部插入)。
- x 的大小階於 α 的值與 β 的值之間(即是情況 2 和 3,內部插入)。
5. 單旋轉
單旋轉:進行一次旋轉。
1、4 的情況需要用到單旋轉來修正。
左左單旋轉(順時針)
右右單旋轉(逆時針)
6. 雙旋轉
進行兩次單旋轉。
2、3 的情況需要用雙旋轉來修正。
左右雙旋轉:先右右單旋轉再左左單旋轉
右左雙旋轉:先左左單旋轉再右右單旋轉
7. 程式碼實現
標頭檔案,核心在 insert 跟旋轉,其他實現跟二叉搜尋樹一樣二叉搜尋樹
typedef int element_type;
struct avl_node;
typedef struct avl_node *position;
typedef struct avl_node *avl_tree;
avl_tree make_empty(avl_tree t);
position find(element_type x, avl_tree t);
position find_min(avl_tree t);
position find_max(avl_tree t);
avl_tree insert(element_type x, avl_tree t);
avl_tree delete(element_type x, avl_tree t);
element_type retrieve(position p);
position single_rotate_with_left_left(position k2);
position single_rotate_with_right_right(position k2);
position double_rotate_with_left_right(position k3);
position double_rotate_with_right_left(position k3);
int height(position p);
int max(int a, int b);
void print_tree(avl_tree t);
void test();
struct avl_node
{
element_type element;
avl_tree left;
avl_tree right;
int height;
}
所有程式碼
/**
* AVL樹
*/
#include <stdio.h>
#include <stdlib.h>
#include "avltree.h"
#define error(str) fatal_error(str)
#define fatal_error(str) fprintf(stderr, "%s\n", str),exit(1)
int height(position p)
{
if (NULL == p)
return -1;
else
return p->height;
}
position find(element_type x, avl_tree t)
{
if (NULL == t)
return NULL;
if (x > t->element)
return find(x, t->right);
else if (x < t->element)
return find(x, t->left);
else
return t;
}
position find_min(avl_tree t)
{
if (NULL != t)
while (NULL != t->left)
t = t->left;
return t;
}
position find_max(avl_tree t)
{
if (NULL == t)
return NULL;
if (NULL == t->right)
return t;
else
return find_max(t->right);
}
avl_tree delete( element_type x, avl_tree t)
{
position temp;
if (NULL == t)
error("empty tree");
if (x > t->element)
t->right = delete(x, t->right);
else if (x < t->element)
t->left = delete(x, t->left);
else if(t->left && t->right) {
temp = find_min(t->right);
t->element = temp->element;
t->right = delete(t->element, t->right);
} else {
temp = t;
if (NULL == t->left)
t = t->right;
else if (NULL == t->right)
t = t->left;
free(temp);
temp = NULL;
}
return t;
}
avl_tree make_empty(avl_tree t)
{
if (NULL != t) {
make_empty(t->left);
make_empty(t->right);
free(t);
}
return NULL;
}
avl_tree insert(element_type x, avl_tree t)
{
if (NULL == t) {
t = (avl_tree)malloc(sizeof(struct avl_node));
if (NULL == t)
fatal_error("out of space");
t->element = x;
t->left = t->right = NULL;
t->height = 0;
} else if (x < t->element) {
t->left = insert(x, t->left);
if ( ( height(t->left) - height(t->right) ) == 2 ) {
if (x < t->left->element)
t = single_rotate_with_left_left(t);
else
t = double_rotate_with_left_right(t);
}
} else if (x > t->element) {
t->right = insert(x, t->right);
if ( ( height(t->right) - height(t->right) ) == 2 ) {
if (x > t->right->element)
t = single_rotate_with_right_right(t);
else
t = double_rotate_with_right_left(t);
}
}
//節點高度為 左右節點高度最大值 + 1
t->height = max(height(t->left), height(t->right) ) + 1;
return t;
}
// 左左單旋轉
position single_rotate_with_left_left(position k2)
{
position k1;
k1 = k2->left;
k2->left = k1->right;
k1->right = k2;
k1->height = max( height(k1->left), height(k1->right) ) + 1;
k2->height = max( height(k2->left), height(k2->right) ) + 1;
return k1;
}
// 右右單旋轉
position single_rotate_with_right_right(position k2)
{
position k1;
k1 = k2->right;
k2->right = k1->left;
k1->left = k2;
k1->height = max( height(k1->left), height(k1->right) ) + 1;
k2->height = max( height(k2->left), height(k2->right) ) + 1;
return k1;
}
// 左右雙旋轉
position double_rotate_with_left_right(position k3)
{
k3->left = single_rotate_with_right_right(k3->left);
return single_rotate_with_left_left(k3);
}
// 右左雙旋轉
position double_rotate_with_right_left(position k3)
{
k3->right = single_rotate_with_left_left(k3->right);
return single_rotate_with_right_right(k3);
}
int max(int a, int b)
{
return a > b ? a : b;
}
void print_tree(int depth, int left, avl_tree t)
{
int i;
if (t) {
for(i = 0; i < left; i++)
printf(" ");
printf("%d\n", t->element);
print_tree(depth + 1, left - 1, t->left);
print_tree(depth + 1, left + 1, t->right);
}
}
void test()
{
avl_tree t;
printf("\n ====== test for building the AVLTree ====== \n");
printf("\n [the left-left single rotate case] test with inserting 3, 2, 1 in trun \n");
t = NULL;
t = insert(3, t);
t = insert(2, t);
t = insert(1, t);
print_tree(1,5, t);
}
int main(int argc, char const *argv[])
{
test();
return 0;
}
8. 測試截圖
最後附上一個講單雙旋轉講得很好的文章
單雙旋轉詳解