AVL 樹 【資料結構與演算法分析 c 語言描述】

jerrkill發表於2019-01-13

1. 概述

主要知識點

  • AVL 樹定義
  • 單旋轉(左左單旋轉、右右單旋轉)
  • 雙旋轉(左右雙旋轉、右左雙旋轉)

2. 什麼是 AVL 樹

定義
帶有平衡條件的二叉查詢樹。
平衡條件:其每個節點的左子樹和右子樹的高度最多相差1的二叉查詢樹。(空樹的高度為 -1)。

  • 所有的樹操作都可以以時間 O(logN) 執行。
  • 當插入時有可能會破壞平衡條件,我們通過旋轉(rotation)來進行修正。

3. 如何解決不平衡情況

根據 AVL 樹的特性可以知道:α 點的兩顆子樹的高度差2(α 是需要平衡的節點)。
所以這種不平衡在插入時可能出現的情況如下四種:

  1. α 的左兒子的左子樹進行一次插入。(左左單旋轉)
  2. α 的左兒子的右子樹進行一次插入。 (左右雙旋轉)
  3. α 的右兒子的左子樹進行一次插入。 (右左雙旋轉)
  4. α 的右兒子的右子樹進行一次插入。 (右右單旋轉)

以上四種情況可以合成兩種情況

  • 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 的情況需要用到單旋轉來修正。
左左單旋轉(順時針)
file
右右單旋轉(逆時針)
file

6. 雙旋轉

進行兩次單旋轉。
2、3 的情況需要用雙旋轉來修正。
左右雙旋轉:先右右單旋轉再左左單旋轉
file
右左雙旋轉:先左左單旋轉再右右單旋轉
file

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. 測試截圖

file
最後附上一個講單雙旋轉講得很好的文章
單雙旋轉詳解

高度自律,深度思考,以勤補拙

相關文章