數據結構之樹篇3——平衡二叉樹(AVL樹)

引入

上一篇寫了二叉排序樹,構建一個二叉排序樹,若是構建序列是徹底有序的,則會出現這樣的狀況:html

顯然這種狀況會使得二叉搜索樹退化成鏈表。當出現這樣的狀況,二叉排序樹的查找也就退化成了線性查找,因此咱們須要合理調整二叉排序樹的形態,使得樹上的每一個結點都儘可能有兩個子結點,這樣整個二叉樹的高度就會大約在\(log(n)\) 左右,其中 \(n\) 爲結點個數。算法

基本性質

AVL樹也稱爲平衡二叉樹,是一種自平衡的二叉排序樹,本質上仍然是一顆二叉排序樹,只是增長了「平衡」的要求,平衡是指,對AVL樹中任何節點的兩個子樹的高度之差(稱爲平衡因子)的絕對值不超過 \(1\) 。能保證上面這一點,AVL樹的高度就能始終保持在 \(O(logn)\) 級別。數據結構

數據結構定義

因爲須要對每一個結點都獲得平衡因子,所以在AVL樹的結構中加入一個變量height,用來記錄當前結點爲根結點的子樹的高度:dom

typedef struct Node
{
    char data;
    int height;
    struct Node* Left;
    struct Node* Right;
}*AVLTree;

獲取 root 結點高度:測試

int getHeight(Node *root){
    if(!root) return 0;//空節點高度爲0
    return root->height;
}

基本操做

查找

​ AVL樹是一顆二叉查找樹,所以查找操做與二叉查找樹相同。由於AVL樹的高度爲 \(O(logn)\) 級別,因此查找操做的時間複雜度爲 \(O(logn)\)spa

能夠獲得和二叉查找樹徹底相同的代碼:3d

//找不到返回NULL,找到返回該節點。
//非遞歸
Node* Find(AVLTree t, int x) {
    if (!t)return NULL;
    if (t->data == x) return t;
    if (x < t->data) return BSTreeFind(t->Left, x);
    if (x > t->data) return BSTreeFind(t->Right, x);
}
//非遞歸
Node* Find(AVLTree T,int x) {
    BSTree p = T;
    while (p) {
        if (x == p->data)
            return p;
        p = x > p->data ? p->Right : p->Left;
    }
    return NULL;
}

插入

左旋

先拋開AVL樹的插入問題,看下面左邊的二叉排序樹。你們原本和平共處,忽然有一天 B 以爲本身的權值比 A 大,要造反,可是B要作根結點,必須也要保證調整後的樹仍然是一顆二叉排序樹。code

☆上全部權值都比A小, ∆ 上全部權值都比B大,無需在調整中進行位置變更;由於調整後B的左孩子變成了A,那麼▲必須移動到其餘地方去,由於A、B、▲的權值關係知足 A<▲<B ,因此讓▲成爲A的右子樹便可。htm

這個調整過程稱爲左旋,分解調整過程以下:blog

代碼以下:

void L(AVLTree *root){
    Node* temp = (*root)->Right; //root指向結點A,temp指向結點B
    (*root)->Right = temp->Left; //圖示步驟2
    temp->Left = *root; //圖示步驟3
    root->height = max(getHeight(root->Left), getHeight(root->Rihgt)) + 1;//更新結點A高度
    temp = max(getHeight(temp->Left), getHeight(temp->Rihgt)) + 1;//更新結點B高度
    *root = temp;//圖示步驟4
}

右旋

右旋是左旋的逆過程,以下:

分解調整過程以下:

代碼以下:

void R(AVLTree *root) {
    Node* temp = (*root)->Left;//root指向結點B,temp指向結點A
    (*root)->Left = temp->Right;
    temp->Right = *root;
    root->height = max(getHeight(root->Left), getHeight(root->Rihgt)) + 1;
    temp = max(getHeight(temp->Left), getHeight(temp->Rihgt)) + 1;
    *root = temp;
}

​ 接下來討論AVL樹的插入操做,假設如今已經有一顆平衡二叉樹,那麼在向其中插入一個結點時,必定會有結點的平衡因子發生改變,此時就可能會有結點的平衡因子大於1 ,這樣以該結點爲根結點的子樹就是失去平衡的,會使平衡二叉樹發生失衡的狀況能夠分爲下面四種:

LL、RR型

左左(LL)、右右(RR),LL,RR只表示樹型(致使樹失去平衡的插入位置),不是左旋、右旋的意思。

img

對於LL型,須要以A結點爲根進行右旋;

對於RR型,須要以A爲根結點進行左旋。

因此代碼以下:

void RR_Rotate(AVLTree *root){
    L(root);
}

void LL_Rotate(AVLTree *root) {
    R(root);
}

LR,RL型

左右(LR)、右左(RL)。

img

對於LR型,須要先以B結點爲根結點進行一次左旋,再以A結點爲根結點進行一次右旋。

對於RL型,須要先以B結點爲根結點進行一次右旋,再以A結點爲根結點進行一次左旋。

void LR_Rotate(AVLTree *root) {
    L(&(*root)->Left);
    R(root);
}
void RL_Rotate(AVLTree *root) {
    R(&(*root)->Right);
    L(root);
}

插入結點

插入算法就是出現不平衡狀態時,判斷須要使用哪一種旋轉方式來使得二叉樹保持平衡

AVLTree InsertAVLTree(AVLTree root, int x) {
    if (root == NULL) {
        root = new Node;
        root->Left = NULL;
        root->Right = NULL;
        root->data = x;
        return root;
    }
    if (x > root->data) { 
            //遞歸返回插入位置的父節點或者祖父……。
            root->Right = InsertAVLTree(root->Right, x); 
            //若是插入以後失去了平衡
            if (height(root->Left) - height(root->Right) == -2) {
                //若是插入的值大於,當前節點的左孩子節點,說明該節點是插在root的右子樹上的
                if (x > root->Left->data) RR_Rotate(&root);
                else RL_Rotate(&root);
            }
    }

    else if (x < root->data) {
        root->Left = InsertAVLTree(root->Left, x);
        if (height(root->Left) - height(root->Right) == 2) {
            if (x < root->Left->data) LL_Rotate(&root);
            else LR_Rotate(&root);
        }
    }
    else { 
        cout << "the number is already included." << endl;
        return NULL; 
    }
    return root;
}

刪除結點

和二叉排序樹的節點的刪除差很少,就是多出來一個判斷從哪一個子樹刪除節點的問題。

void AVLTreeDel(AVLTree *root, int data)
{
    if (!*root) {
        cout << "delete failed" << endl;
        return;
    }
    Node *p = *root;  
    if (data == p->data) {
        //左右子樹都非空  
        if (p->Left && p->Right) {
            //在高度更大的那個子樹上進行刪除操做
            //進左子樹,右轉到底,進右子樹,左轉到底,轉彎碰壁,殺孩子。
            if (height(p->Left) > height(p->Right)) {
                Node *pre=NULL,*q = p->Left;
                if (!q->Right)
                    q->Right = p->Right;
                else {
                    while (q->Right) {
                        pre = q;
                        q = q->Right;
                    }
                    pre->Right = q->Left;
                    q->Left = p->Left;
                    q->Right = p->Right;
                }
                *root = q;
            }
            else {
                Node *pre = NULL, *q = p->Right;
                if (!q->Left)
                    q->Left = p->Left;
                else {
                    while (q->Left) {
                        pre = q;
                        q = q->Left;
                    }
                    pre->Left = q->Right;
                    q->Left = p->Left;
                    q->Right = p->Right;
                }
                *root=q;
            }
        }
        else 
            (*root) = (*root)->Left ? (*root)->Left : (*root)->Right;
        delete p;
    }
    else if (data < p->data){//要刪除的節點在左子樹中  
        //在左子樹中進行遞歸刪除  
        AVLTreeDel(&(*root)->Left, data);
        //判斷是否仍然知足平衡條件  
        if (height(p->Right) - height(p->Left) == 2){
            //若是當前節點右孩子的左子樹更高
            if (height(p->Right->Left) > height(p->Right->Right))
                RL_Rotate(root);
            else  
                RR_Rotate(root);
        }
    }
    else{
        AVLTreeDel(&(*root)->Right, data);
        if (height(p->Left) - height(p->Right) == 2) {
            if (height((*root)->Left->Left) > height((*root)->Left->Right))
                LL_Rotate(root);
            else
                LR_Rotate(root);
        }
    }
}

完整測試代碼:

#pragma once
#include "top.h"

typedef BTreeNode Node, *AVLTree;

void RR_Rotate(AVLTree *root){
    Node* Right = (*root)->Right;
    (*root)->Right = Right->Left;
    Right->Left = *root;
    *root = Right;
}

void LL_Rotate(AVLTree *root) {
    Node* Left = (*root)->Left;
    (*root)->Left = Left->Right;
    Left->Right = *root;
    *root = Left;
}
void LR_Rotate(AVLTree *root) {
    RR_Rotate(&(*root)->Left);
    return LL_Rotate(root);
}
void RL_Rotate(AVLTree *root) {
    LL_Rotate(&(*root)->Right);
    RR_Rotate(root);
}
AVLTree AVLTreeInsert(AVLTree root, int x) {
    if (root == NULL) {
        root = new Node;
        root->Left = NULL;
        root->Right = NULL;
        root->data = x;
        return root;
    }
    if (x > root->data) { 
            root->Right = AVLTreeInsert(root->Right, x); 
            //遞歸返回插入位置的父節點或者祖父……,若是失去了平衡
            if (height(root->Left) - height(root->Right) == -2) {
            //若是插入的值大於,當前節點的右孩子節點,說明該節點是插在root的右子樹上的
                //if (x > root->Left->data) RR_Rotate(&root);不能保證該節點必定有左子樹
                if (x > root->Right->data)RR_Rotate(&root);
                else RL_Rotate(&root);
            }
    }
    else if (x < root->data) {
        root->Left = AVLTreeInsert(root->Left, x);
        if (height(root->Left) - height(root->Right) == 2) {
            if (x < root->Left->data) LL_Rotate(&root);
            else LR_Rotate(&root);
        }
    }
    else { 
        cout << "the number is already included." << endl;
        return NULL; 
    }
    return root;
}

AVLTree AVLTreeCreat(int *a, int length) {
    AVLTree T = NULL;
    for (int i = 0; i < length; i++) {
        T = AVLTreeInsert(T, a[i]);
    }
    return T;
}
Node* AVLFind(AVLTree T, int x) {
    Node *p = T;
    while (p) {
        if (x == p->data) break;
        p = x > p->data ? p->Right : p->Left;
    }
    return p;
}

AVLTree AVLMax(AVLTree p)
{
    if (!p) return NULL;
    if (p->Right == NULL)
        return p;
    return AVLMax(p->Right);
}

AVLTree AVLMin(AVLTree p)
{
    if (!p)
        return NULL;
    if (p->Left == NULL)
        return p;
    return AVLMin(p->Left);
}

void AVLTreeDel(AVLTree *root, int data)
{
    if (!*root) {
        cout << "delete failed" << endl;
        return;
    }
    Node *p = *root;  
    if (data == p->data) {
        //左右子樹都非空  
        if (p->Left && p->Right) {
            //在高度更大的那個子樹上進行刪除操做
            //進左子樹,右轉到底,進右子樹,左轉到底,轉彎碰壁,殺孩子。
            if (height(p->Left) > height(p->Right)) {
                Node *pre=NULL,*q = p->Left;
                if (!q->Right)
                    q->Right = p->Right;
                else {
                    while (q->Right) {
                        pre = q;
                        q = q->Right;
                    }
                    pre->Right = q->Left;
                    q->Left = p->Left;
                    q->Right = p->Right;
                }
                *root = q;
            }
            else {
                Node *pre = NULL, *q = p->Right;
                if (!q->Left)
                    q->Left = p->Left;
                else {
                    while (q->Left) {
                        pre = q;
                        q = q->Left;
                    }
                    pre->Left = q->Right;
                    q->Left = p->Left;
                    q->Right = p->Right;
                }
                *root=q;
            }
        }
        else 
            (*root) = (*root)->Left ? (*root)->Left : (*root)->Right;
        delete p;
    }
    else if (data < p->data){//要刪除的節點在左子樹中  
        //在左子樹中進行遞歸刪除  
        AVLTreeDel(&(*root)->Left, data);
        //判斷是否仍然知足平衡條件  
        if (height(p->Right) - height(p->Left) == 2){
            //若是當前節點右孩子的左子樹更高
            if (height(p->Right->Left) > height(p->Right->Right))
                RL_Rotate(root);
            else  
                RR_Rotate(root);
        }
    }
    else{
        AVLTreeDel(&(*root)->Right, data);
        if (height(p->Left) - height(p->Right) == 2) {
            if (height((*root)->Left->Left) > height((*root)->Left->Right))
                LL_Rotate(root);
            else
                LR_Rotate(root);
        }
    }
}

int height(BTree L) {
    if (L == NULL)
        return 0;
    int left = height(L->Left);
    int right = height(L->Right);
    return left >= right ? left + 1 : right + 1;
}

void checkCreat() {
    int length = 10;
    int *a = getNoRepateRandomArray(length, 10);

    for (int i = 0; i < length; i++) {
        cout << a[i] << ",";
    }
        
    cout << endl;
    AVLTree T = AVLTreeCreat(a, length);
    int t = rand() % length;
    AVLTreeDel(&T, a[t]);
    for (int i = t; i < length - 1; i++) {
        a[i] = a[i + 1];
    }
    
    Preorder(T);
    cout << endl;
    Inorder(T);
    cout << endl;
    Postorder(T);
    cout << endl;
    free(a);
}
相關文章
相關標籤/搜索