1. 程式人生 > >AVL平衡二叉查詢樹

AVL平衡二叉查詢樹

二叉排序樹:

定義

        二叉排序樹,又叫二叉查詢樹,它或者是一棵空樹;或者是具有以下性質的二叉樹: 
            1. 若它的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值; 
            2. 若它的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值; 
            3. 它的左右子樹也分別為二叉排序樹。
       比如下圖就是一棵普通的二叉排序樹: 

   如果按照中序遍歷的順序,一棵二叉排序樹的輸出結果就剛好是按照從小到大的順序輸出的,可以運用於二分演算法。

先對其資料結構進行定義:

typedef struct Binary_Tree_node
{
    int data;   //資料域
    struct Binary_Tree_node* lchild, * rchild;      //左右孩子結點
    
}Binode, * BiTree;

然後是插入操作:

//假設沒有相等的data,這裡我們不考慮相等的資料
//插入結點
void Insert_Binary_Tree(BiTree& bst ,int t)         //bst是根節點
{

    if (bst == NULL)    //空樹或者遞迴到了葉結點
    {
        BiTree newp = new Binode;
        newp->data = t;
        newp->lchild = NULL;
        newp->rchild = NULL;
        bst = newp; 
    }
    else
    {
        if (t > bst->data)      //比本結點大,則插入其右孩子結點,小就插入左孩子結點
            Insert_Binary_Tree(bst->rchild, t);
        else
            Insert_Binary_Tree(bst->lchild, t);
    }   
}

建立一棵樹:

//建立一棵二叉排序樹
BiTree Create_Binary_Tree()
{
    BiTree bst = NULL;
    int t;
    cin >> t;
    while (t != ENDKEY)         //只要輸入不等於-1,就一直往樹裡插入元素
    {
        Insert_Binary_Tree(bst, t);
        cin >> t;
    }
    return bst;
}

刪除操作:刪除操作比較複雜,本篇部落格主要是記錄AVL,所以此處不做贅述

BiTree Delete_Binary_Tree(BiTree bst, int t)
{
    Binode* newp, * f, * s, * q;    
    newp = bst;
    f = NULL;
    while (newp)                    
    {
        if (newp->data == t)
            break;
        f = newp;               
        if (t > newp->data)
            newp = newp->rchild;
        else
            newp = newp->lchild;
    }
    if (newp == NULL)               
        return bst;
    if (newp->lchild == NULL)
    {
        if (f == NULL)                  
             bst = bst->rchild;
        else if (f->lchild == newp)
            f->lchild = newp->rchild;
        else                            
            f->rchild = newp->rchild;

        delete[]newp;           
    }
    else if (newp->rchild == NULL)      
    {
        if (f == NULL)
            bst = bst->lchild;
        else if (f->lchild == newp)
            f->lchild = newp->lchild;
        else
            f->rchild = newp->lchild;

        delete[]newp;
    }
    else                
    {
        q = newp;
        s = newp->lchild;
        while (s->rchild)
        {
            q = s;              
            s = s->rchild;      
        }
        if (q == newp)
            q->lchild = s->lchild;  
        else    
            q->rchild = s->lchild;
        newp->data = s->data;
        delete[]s;
    }
    return bst;
}

搜尋二叉樹:

//搜尋二叉樹,根據輸入的資料搜尋二叉樹,返回這個資料的結點
BiTree Search_Binary_Tree(BiTree bst, int t)
{
    if (bst == NULL)
        return NULL;
    if (bst->data == t)
        return bst;
    else if (t > bst->data)
        return Search_Binary_Tree(bst->rchild, t);
    else
        return Search_Binary_Tree(bst->lchild, t);
}

平衡二叉排序樹:

可是當一棵二叉排序樹的某個節點的一枝相比於另一枝太長,搜尋的時間複雜度就會變成O(n),而為了提高效率,提出了AVL樹,即平衡二叉排序樹。 例如下圖就是一棵不平衡的二叉樹:


像這樣如果要搜尋0元素就會變得和線性表的時間複雜度一樣。 AVL樹是一種比查詢二叉樹還特別的樹,這種樹就可以幫助我們解決二叉查詢樹剛才的那種所有節點都傾向一邊的缺點的。具有如下特性:
1、具有二叉查詢樹的全部特性。
2、每個節點的左子樹和右子樹的高度差至多等於1。
例如:圖一就是一顆AVL樹了,而圖二則不是(節點右邊標的是這個節點的高度)。


按照這種規則進行建立二叉樹,可以保證這棵二叉樹始終是左右相對平衡的,可以保證搜尋的時間複雜度達到O(log n),提高了效率。

針對樹的失衡有四種情況,我們總結如下:
1、左左型:做右旋
2、右右型:做左旋
3、左右型:先左旋,再右旋
4、右左型:先右旋,再左旋

下面一個一個寫,對於實現每個旋轉的函式可以作為私有的對內介面(C++),不對外開放使用

1、左左型:做右旋:

像下面這種:

我們做如下右旋操作:

即順時針旋轉結點,使雙親結點被自己的左孩子替代,然後自己變成左孩子結點的右孩子結點

程式碼實現:

          //左左型單旋轉
          void Lleft(BiTree &bst)           //bst為不平衡的結點
          {
           BiTree lch = bst->lchild;        //儲存不平衡結點的左孩子結點
           bst->lchild = lch->rchild;
           lch->rchild = bst;
           bst = lch;
          }

2、右右型:做左旋:

像下面這種:

逆時針旋轉結點,使自己被自己的右孩子替代,然後自己變成右孩子的左孩子結點

程式碼實現:

//右右型單旋轉
void Rright(BiTree &bst)            //bst為不平衡的結點
{
    BiTree rch = bst->rchild;       //儲存不平衡結點的右孩子結點
    bst->rchild = rch->lchild;
    rch->lchild = bst;
    bst = rch;
}

3、左右型:先左旋,再右旋:

像下面這種:

先對其左旋將其變成左左型,再右旋讓其平衡

程式碼實現:

//左右型雙旋轉
void Lright(BiTree &bst)
{
    //先做左旋,將其變成左左型
    BiTree lch = bst->lchild;
    BiTree lrch = bst->lchild->rchild;
    bst->lchild = lrch;
    bst->lchild->lchild = lch;
    bst->lchild->lchild->rchild = NULL;     //可能存在bug   todo,目前來看沒有哈哈哈

    Lleft(bst);             //再將其右旋
}

4、右左型:先右旋,再左旋:

老規矩,先貼圖:

程式碼實現:

//右左型雙旋轉
void Rleft(BiTree &bst)
{
    //先做右旋,將其變成右右型
    BiTree rch = bst->rchild;
    BiTree rlch = bst->rchild->lchild;
    bst->rchild = rlch;
    bst->rchild->rchild = rch;
    bst->rchild->rchild->lchild = NULL;

    Rright(bst);                //再右旋
}

實現重點來了

我們每插入一個結點元素都要保證整棵樹的每一個結點是平衡的,就要在插入這個結點後,從這個插入的結點往上逐個雙親結點去檢查,看是否破壞了樹的平衡,如果破壞了則對其進行左右旋轉操作,保證其平衡性,直到樹的根節點。

因為樹的遞迴性,在插入的時候是遞迴插入,

按照棧的順序,遞迴的過程中第一次遞迴的函式先入棧,以此類推,然後直到到達結束條件,前面的才依次出棧,

所以可以在插入函式最後面使用檢查每個結點的函式,出棧過程中,就可以沿著新插入結點一路回退檢查每個雙親結點了。

注意最後一行

程式碼實現:

//插入結點
void Insert_Binary_Tree(BiTree& bst ,int t)         //bst是根節點
{

    if (bst == NULL)    //空樹或者葉結點
    {
        BiTree newp = new Binode;
        newp->data = t;
        newp->lchild = NULL;
        newp->rchild = NULL;
        bst = newp; 
    }
    else
    {
        if (t > bst->data)
            Insert_Binary_Tree(bst->rchild, t);
        else
            Insert_Binary_Tree(bst->lchild, t);
    }   
    //在最後檢查這個結點,在遞迴的時候就可以將遞迴過程中從根節點往下的所有結點都檢查了,按照出棧順序,可以保證都是在插入後才依次向上檢查的。
    Check_Binary_Tree(bst);     
}

要實現這一功能就必須有一個檢查是否平衡的依據———就是每個結點的左右子樹的深度。這個函式是作為私有的對內介面。

計算深度的實現:

//一個結點的深度
int Binary_Tree_height(BiTree bst)      //bst為要計算的結點
{
    if (bst == NULL)
        return 0;
    int l = Binary_Tree_height(bst->lchild);
    int r = Binary_Tree_height(bst->rchild);
    return max(l, r) + 1;
}

然後是一個檢查這個結點是否平衡的函式,對外:

檢查平衡函式:

//如果出現不平衡的情況就旋轉
void Check_Binary_Tree(BiTree  &bst)        //bst為要檢查的結點
{   
    if (bst == NULL)
        return;
    if (Binary_Tree_height(bst->lchild) - Binary_Tree_height(bst->rchild) > 1)
    {
        if (Binary_Tree_height(bst->lchild->lchild) > Binary_Tree_height(bst->lchild->rchild))
            Lleft(bst);
        else
            Lright(bst);
    }
    if (Binary_Tree_height(bst->rchild) - Binary_Tree_height(bst->lchild) > 1)
    {
        if (Binary_Tree_height(bst->rchild->rchild) > Binary_Tree_height(bst->rchild->lchild))
            Rright(bst);
        else
            Rleft(bst);
    }
}

最後,對程式碼進行一下彙總:

AVL.h檔案
#pragma once
#include<iostream>
constexpr auto ENDKEY = -1;
template<typename T1, typename T2>
constexpr auto max(T1 x, T2 y) { return x>y?x:y; }

using namespace std;

typedef struct Binary_Tree_node
{
    int data;   //資料域
    struct Binary_Tree_node* lchild, * rchild;      //左右孩子結點
}Binode, * BiTree;

//訪問二叉樹
void visit(int c, int level)
{
    printf("%d位於第%d層\n", c, level);
}

//中序遍歷
void Midorder_Traverse(BiTree T, int level)
{
    if (T)
    {
        Midorder_Traverse(T->lchild, level + 1);
        visit(T->data, level);
        Midorder_Traverse(T->rchild, level + 1);
    }
}

//一個結點的深度
int Binary_Tree_height(BiTree bst)
{
    if (bst == NULL)
        return 0;
    int l = Binary_Tree_height(bst->lchild);
    int r = Binary_Tree_height(bst->rchild);
    return max(l, r) + 1;
}

//遍歷整棵樹,如果出現不平衡的情況就旋轉

//左左型單旋轉
void Lleft(BiTree &bst)
{
    BiTree lch = bst->lchild;       //儲存不平衡結點的左孩子結點
    bst->lchild = lch->rchild;
    lch->rchild = bst;
    bst = lch;
}

//右右型單旋轉
void Rright(BiTree &bst)
{
    BiTree rch = bst->rchild;       //儲存不平衡結點的右孩子結點
    bst->rchild = rch->lchild;
    rch->lchild = bst;
    bst = rch;
}

//左右型雙旋轉
void Lright(BiTree &bst)
{
    //先做左旋,將其變成左左型
    BiTree lch = bst->lchild;
    BiTree lrch = bst->lchild->rchild;
    bst->lchild = lrch;
    bst->lchild->lchild = lch;
    bst->lchild->lchild->rchild = NULL;     //可能存在bug   todo

    Lleft(bst);
}

//右左型雙旋轉
void Rleft(BiTree &bst)
{
    //先做右旋,將其變成右右型
    BiTree rch = bst->rchild;
    BiTree rlch = bst->rchild->lchild;
    bst->rchild = rlch;
    bst->rchild->rchild = rch;
    bst->rchild->rchild->lchild = NULL;

    Rright(bst);
}

void Check_Binary_Tree(BiTree  &bst)
{   
    if (bst == NULL)
        return;
    if (Binary_Tree_height(bst->lchild) - Binary_Tree_height(bst->rchild) > 1)
    {
        if (Binary_Tree_height(bst->lchild->lchild) > Binary_Tree_height(bst->lchild->rchild))
            Lleft(bst);
        else
            Lright(bst);
    }
    if (Binary_Tree_height(bst->rchild) - Binary_Tree_height(bst->lchild) > 1)
    {
        if (Binary_Tree_height(bst->rchild->rchild) > Binary_Tree_height(bst->rchild->lchild))
            Rright(bst);
        else
            Rleft(bst);
    }
    //Check_Binary_Tree(bst->lchild);
    //Check_Binary_Tree(bst->rchild);
}

//假設沒有相等的data
//插入結點
void Insert_Binary_Tree(BiTree& bst ,int t)         //bst是根節點
{

    if (bst == NULL)    //空樹或者葉結點
    {
        BiTree newp = new Binode;
        newp->data = t;
        newp->lchild = NULL;
        newp->rchild = NULL;
        bst = newp; 
    }
    else
    {
        if (t > bst->data)
            Insert_Binary_Tree(bst->rchild, t);
        else
            Insert_Binary_Tree(bst->lchild, t);
    }   
    Check_Binary_Tree(bst);
}

//建立一棵二叉排序樹
BiTree Create_Binary_Tree()
{
    BiTree bst = NULL;
    int t;
    cin >> t;
    while (t != ENDKEY)
    {
        Insert_Binary_Tree(bst, t);
        cin >> t;
    }
    return bst;
}

//二叉排序樹的刪除
BiTree Delete_Binary_Tree(BiTree bst, int t)
{
    Binode* newp, * f, * s, * q;    
    newp = bst;
    f = NULL;
    while (newp)                    
    {
        if (newp->data == t)
            break;
        f = newp;               
        if (t > newp->data)
            newp = newp->rchild;
        else
            newp = newp->lchild;
    }
    if (newp == NULL)               
        return bst;
    if (newp->lchild == NULL)
    {
        if (f == NULL)                  
             bst = bst->rchild;
        else if (f->lchild == newp)
            f->lchild = newp->rchild;
        else                            
            f->rchild = newp->rchild;

        delete[]newp;           
    }
    else if (newp->rchild == NULL)      
    {
        if (f == NULL)
            bst = bst->lchild;
        else if (f->lchild == newp)
            f->lchild = newp->lchild;
        else
            f->rchild = newp->lchild;

        delete[]newp;
    }
    else                
    {
        q = newp;
        s = newp->lchild;
        while (s->rchild)
        {
            q = s;              
            s = s->rchild;      
        }
        if (q == newp)
            q->lchild = s->lchild;  
        else    
            q->rchild = s->lchild;
        newp->data = s->data;
        delete[]s;
    }
    Check_Binary_Tree(bst);
    return bst;
}

//搜尋二叉樹
BiTree Search_Binary_Tree(BiTree bst, int t)
{
    if (bst == NULL)
        return NULL;
    if (bst->data == t)
        return bst;
    else if (t > bst->data)
        return Search_Binary_Tree(bst->rchild, t);
    else
        return Search_Binary_Tree(bst->lchild, t);
}
測試資料
#include<iostream>
#include"BST.h"
using namespace std;
int main()
{
    BiTree bst = Create_Binary_Tree();
    int level = 1;
    Midorder_Traverse(bst, level);
    system("pause");
}

測試結果:

相關推薦

AVL平衡查詢

二叉排序樹: 定義 二叉排序樹,又叫二叉查詢樹,它或者是一棵空樹;或者是具有以下性質的二叉樹: 1. 若它的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值; 2. 若它的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值;

篇2-平衡查詢AVL

一、AVL樹定義         在資料結構中,AVL樹是最先發明的自平衡二叉查詢樹。在AVL樹中任何節點的兩個子樹的高度差的絕對值不能超過一,所以它也被稱為高度平衡樹。查詢、插入和刪除在平均和最壞情況下都是O(log n)。增加和刪除可能需要通過一次

查詢(BST),平衡查詢(AVL),紅黑(RBT),B~/B+(B-tree)的比較

http://www.iteye.com/topic/614070 此少俠總結的特棒,直接收藏了。 我們這個專題介紹的動態查詢樹主要有: 二叉查詢樹(BST),平衡二叉查詢樹(AVL),紅黑樹(RBT),B~/B+樹(B-tree)。這四種樹都具備下面幾個優勢: (1) 都

AVL-自平衡查詢(Java實現)

      在電腦科學中,AVL樹是最先發明的自平衡二叉查詢樹。AVL樹得名於它的發明者 G.M. Adelson-Velsky 和 E.M. Landis,他們在 1962 年的論文 "An algorithm for the organization of inform

查詢(BST) | 平衡查詢AVL) | 紅黑(RBT)

二叉查詢樹(BST) 特點:對任意節點而言,左子樹(若存在)的值總是小於本身,而右子(若存在)的值總是大於本身。 查詢:從根開始,小的往左找,大的往右找,不大不小的就是這個節點了; 插入:從根開始,小的往左,大的往右,直到葉子,就插入, 時間複雜度期望為

篇3-平衡查詢之紅黑

一、概述         紅黑樹是一種自平衡樹在電腦科學中。二叉樹的每個節點都有一個額外的位,該位通常被解釋為節點的顏色(紅色或黑色)。這些顏色位用於確保樹在插入和刪除期間保持近似平衡。通過以滿足某些屬性的方式用兩種顏色之一繪製樹的每個節點來

【演算法學習】AVL平衡搜尋原理及各項操作程式設計實現(C++)

AVLTree即(Adelson-Velskii-Landis Tree),是加了額外條件的二叉搜尋樹。其平衡條件的建立是為了確保整棵樹的深度為O(nLogn)。平衡條件是任何節點的左右子樹的高度相差不超過1. 在下面的程式碼中,程式設計實現了AVL樹的建立、查詢、插入、

搜尋AVL平衡搜尋

二叉搜尋樹 二叉搜尋樹是二叉樹的的一種,只不過多了個限制條件,即左子樹節點的值都小於該點,右子樹節點的值都大於該點。 定義: // 樹節點 template <typename T> class Node { public: T key; Nod

資料結構之自平衡查詢(1)

今天開始,我們再來認識一個新的二叉樹,稱為自平衡二叉查詢樹。AVL樹是最先發明的自平衡二叉查詢樹。 AVL樹的特點是:對於樹中的任何節點,節點的左右子樹的高度差距最大為1,所以AVL樹也稱為高度平衡樹。AVL樹得名於它的發明者G.M. Adelson-Velsky和E.M.

用DSW演算法一次性平衡查詢

參考原文(原文作者:Timothy J. Rolfe ):http://penguin.ewu.edu/~trolfe/DSWpaper/ DSW演算法用於平衡BST(二叉查詢樹),而且平衡後的二叉樹是一顆完全二叉樹(complete binary tree-即葉子節點頂

平衡AVL 紅黑 B 查詢 B+ B-

B-Trees B≤numberofchildren&lt;2BB \le number of children &lt; 2BB≤numberofchildren<2B B−1≤numberofkeys&lt;2B−1B-1

平衡搜尋的簡單操作(AVL)

結構體 typedef struct AVLNode * Position; typedef Position AVLTree; struct AVLNode { int data; AVLTree left; AVLTree right; int height; };

AVL平衡的各種問題(Balanced Binary Tree)

AVL樹或者是一棵空樹,或者是具有以下性質的非空二叉搜尋樹: 1. 任一結點的左、右子樹均為AVL樹; 2.根結點左、右子樹高度差的絕對值不超過1.   1.宣告 #include<iostream> #include<cstdio> #inc

查詢Avl

定義不再敘述,看程式 應該是這本書,我看的第二版 https://book.douban.com/subject/10530466/ 下面是二叉查詢樹,我認為比較好的一點是在插入的時候返回修改的子樹,代替了c裡面的指標,這是我看c資料結構想改寫為java時所沒有想到的。 還有一點,使用者在插

AVL——自平衡搜尋

概念 AVL(Adelson-Velskii and Landis)樹得名於它的發明者 G.M. Adelson-Velsky 和 E.M. Landis,他們在 1962 年的論文《An algorithm for the organization of information》中

AVL平衡,紅黑原理。

二叉搜尋樹 插入和刪除操作必須先查詢,查詢效率代表了二叉搜尋樹中各個操作的效能 最優情況:二叉搜尋樹為完全二叉樹,比較次數Log2^N 最壞情況:二叉搜尋樹為單支樹,平均比較次數N/2 平衡二叉樹 平衡樹: AVL樹,紅黑樹 AVL樹:(二叉搜尋樹改良版)

AVL平衡總結

我個人是通過b站學習的懂得 b站視訊連線: https://www.bilibili.com/video/av37955102?from=search&seid=14638889623357631324 https://www.bilibili.com/video/av379

查詢平衡、紅黑、B-/B+效能對比

1. 二叉查詢樹 (Binary Search Tree) BST 的操作代價分析: (1) 查詢代價: 任何一個數據的查詢過程都需要從根結點出發,沿某一個路徑朝葉子結點前進。因此查詢中資料比較次數與樹的形態密切相關。  當樹中每個結點左右子樹高度大致相同時,樹高為

終於搞懂了什麼是查詢AVL,B,B+,紅黑

二叉查詢樹: 二叉查詢樹就是左結點小於根節點,右結點大於根節點的一種排序樹,也叫二叉搜尋樹。也叫BST,英文Binary Sort Tree。 二叉查詢樹比普通樹查詢更快,查詢、插入、刪除的時間複雜度為O(logN)。但是二叉查詢樹有一種極端的情況,就是會變成一種線性連結

淺談查詢AVL、紅黑、B、B+的原理及應用

一、二叉查詢樹 1、簡介 二叉查詢樹也稱為有序二叉查詢樹,滿足二叉查詢樹的一般性質,是指一棵空樹具有如下性質: 任意節點左子樹不為空,則左子樹的值均小於根節點的值. 任意節點右子樹不為空,則右子樹的值均大於於根節點的值. 任意節點的左右子樹也分別是二叉查