AVL樹(shù)

什么是AVL樹(shù)?

AVL樹(shù)即二叉平衡樹(shù)。因?yàn)槎娌檎覙?shù)的形狀會(huì)受插入數(shù)據(jù)集的影響柬姚,如果數(shù)據(jù)呈現(xiàn)有序排列,則二叉排序樹(shù)勢(shì)線性的翅娶,查找算法效率不高。如果我們能保證不管數(shù)據(jù)是否有序好唯,都能使二叉查找樹(shù)盡可能的小竭沫。這種特殊的二叉查找樹(shù)即AVL樹(shù)。具有如下特征:

  1. 根的左子樹(shù)和右子樹(shù)的高度差的絕對(duì)值的最大值為1
  2. 根的左子樹(shù)和右子樹(shù)都是AVL樹(shù)

如何構(gòu)造AVL樹(shù)骑篙?

查詢操作和普通的二叉查找樹(shù)相同蜕提,但是插入節(jié)點(diǎn)和刪除節(jié)點(diǎn)都可能破壞原樹(shù)的的平衡性,所以要考慮每個(gè)節(jié)點(diǎn)的左子樹(shù)和右子樹(shù)的高度差不能超過(guò)1靶端,這時(shí)可以通過(guò)旋轉(zhuǎn)操作來(lái)進(jìn)行修正谎势。

插入操作

1.插入節(jié)點(diǎn)在P的左孩子的左子樹(shù)上
處理方式:對(duì)P點(diǎn)右旋轉(zhuǎn)處理。如圖所示

1.png

python實(shí)現(xiàn)右旋轉(zhuǎn)

def right_rotate(node):
 '''
 右旋轉(zhuǎn)平衡操作
 node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
 return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
 '''
 # 三步完成右旋轉(zhuǎn)操作
 node_left = node.left
 node.left = node_left.right
 node_left.right = node
 # 更新節(jié)點(diǎn)的高度
 node_left.height = max(get_height(node_left.left),
                        get_height(node_left.right)) + 1
 node.height = max(get_height(node.left), get_height(node.right)) + 1
 return node_left

2.插入節(jié)點(diǎn)在P的右孩子的右子樹(shù)上
處理方式:對(duì)P點(diǎn)左旋轉(zhuǎn)處理杨名。如圖所示

2.png

python實(shí)現(xiàn)左旋轉(zhuǎn)

def left_rotate(node):
 '''
 左旋轉(zhuǎn)平衡操作
 node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
 return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
 '''
 # 三步完成左旋轉(zhuǎn)操作
 node_right = node.right
 node.right = node_right.left
 node_right.left = node
 # 更新節(jié)點(diǎn)的高度
 node.height = max(get_height(node.left), get_height(node.right)) + 1
 node_right.height = max(
     get_height(node_right.left), get_height(node_right.right)) + 1
 return node_right

3.插入節(jié)點(diǎn)在P的右孩子的左子樹(shù)上
處理方式:先對(duì)C點(diǎn)做一次右旋轉(zhuǎn)脏榆,然后再對(duì)P點(diǎn)做一次左旋轉(zhuǎn)。如圖所示

3.png

python實(shí)現(xiàn)先右旋再左旋

def right_left_rotate(node):
 '''
 先右旋后右左旋平衡操作
 node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
 return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
 '''
 # 右旋
 node.right = right_rotate(node.right)
 # 左旋
 return left_rotate(node)

4.插入節(jié)點(diǎn)在P的左孩子的右子樹(shù)上
處理方式:先對(duì)C點(diǎn)做一次左旋轉(zhuǎn)台谍,然后再對(duì)P點(diǎn)做一次右旋轉(zhuǎn)须喂。如圖所示

4.png

python實(shí)現(xiàn)先左旋再右旋

def left_right_rotate(node):
 '''
 先左旋后右旋平衡操作
 node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
 return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
 '''
 # 左旋
 node.left = left_rotate(node.left)
 # 右旋
 return right_rotate(node)

刪除操作

  1. 要?jiǎng)h除的節(jié)點(diǎn)為葉子節(jié)點(diǎn),則直接刪除趁蕊,然后檢查該節(jié)點(diǎn)的父節(jié)點(diǎn)是否平衡坞生,如果不平衡,做平衡化處理

  2. 要?jiǎng)h除的節(jié)點(diǎn)只有左兒子或右兒子掷伙,則用左兒子或右兒子代替該節(jié)點(diǎn)是己,并做平衡花處理

  3. 要?jiǎng)h除的節(jié)點(diǎn)既有左子樹(shù)又有右子樹(shù):如果左子樹(shù)高度比較高,則選取左子樹(shù)值最大的節(jié)點(diǎn)任柜,將值賦值給當(dāng)前節(jié)點(diǎn)卒废,并刪除那個(gè)值最大的節(jié)點(diǎn);如果右子樹(shù)高度比較高宙地,則選取右子樹(shù)中值最小節(jié)點(diǎn)升熊,將值賦值給當(dāng)前節(jié)點(diǎn),并刪除那個(gè)值最小的節(jié)點(diǎn)绸栅。 最后再做平衡化處理

python實(shí)現(xiàn)代碼

#!/usr/bin/python
# encoding: utf-8

'''AVL樹(shù)的實(shí)現(xiàn)'''


def get_height(node):
    return node.height if node else -1


def tree_min(node):
    '''找最小值'''
    temp = node
    while temp.left:
        temp = temp.left
    return temp


def tree_max(node):
    '''找最大值'''
    temp = node
    while temp.right:
        temp = temp.right
    return temp


def right_rotate(node):
    '''
    右旋轉(zhuǎn)平衡操作
    node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
    return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
    '''
    # 三步完成右旋轉(zhuǎn)操作
    node_left = node.left
    node.left = node_left.right
    node_left.right = node
    # 更新節(jié)點(diǎn)的高度
    node_left.height = max(get_height(node_left.left),
                           get_height(node_left.right)) + 1
    node.height = max(get_height(node.left), get_height(node.right)) + 1
    return node_left


def left_rotate(node):
    '''
    左旋轉(zhuǎn)平衡操作
    node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
    return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
    '''
    # 三步完成左旋轉(zhuǎn)操作
    node_right = node.right
    node.right = node_right.left
    node_right.left = node
    # 更新節(jié)點(diǎn)的高度
    node.height = max(get_height(node.left), get_height(node.right)) + 1
    node_right.height = max(
        get_height(node_right.left), get_height(node_right.right)) + 1
    return node_right


def left_right_rotate(node):
    '''
    先左旋后右旋平衡操作
    node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
    return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
    '''
    # 左旋
    node.left = left_rotate(node.left)
    # 右旋
    return right_rotate(node)


def right_left_rotate(node):
    '''
    先右旋后右左旋平衡操作
    node: 要旋轉(zhuǎn)的節(jié)點(diǎn)
    return: 旋轉(zhuǎn)后作為根的節(jié)點(diǎn)
    '''
    # 右旋
    node.right = right_rotate(node.right)
    # 左旋
    return left_rotate(node)


def printTree(node):
    if node:
        print node.key
        printTree(node.left)
        printTree(node.right)


class Node(object):

    def __init__(self, key):
        # height為當(dāng)前節(jié)點(diǎn)的高度
        self.key = key
        self.left = None
        self.right = None
        self.height = 0


class AVLTree(object):

    def __init__(self):
        self.root = None

    def find(self, key):
        '''查找一個(gè)值'''
        if self.root is None:
            return None
        else:
            # 如果根節(jié)點(diǎn)有值级野,則才真正開(kāi)始執(zhí)行查詢函數(shù)
            return self._find(key)

    def _find(self, key):
        # 真正的查詢函數(shù)
        start = self.root
        while start:
            if key == start.key:
                return start
            elif key < start.key:
                start = start.left
            elif key > start.key:
                start = start.right
        return None

    def insert(self, node):
        # 把第一個(gè)插入的節(jié)點(diǎn)設(shè)置為根節(jié)點(diǎn)
        if self.root is None:
            self.root = node
        else:
            self.root = self._insert(self.root, node)

    def _insert(self, index, node):
        '''
        index: 根節(jié)點(diǎn)
        node: 要插入的節(jié)點(diǎn)
        '''
        # 遞歸實(shí)現(xiàn)插入

        # 遞歸結(jié)束條件
        if index is None:
            index = node
        elif node.key < index.key:
            index.left = self._insert(index.left, node)
            # 如果左右子樹(shù)不平衡,則進(jìn)行平衡操作
            if get_height(index.left) - get_height(index.right) == 2:
                # 如果插在最左邊粹胯,則右旋
                if node.key < index.left.key:
                    index = right_rotate(index)
                # 如果插在左子節(jié)點(diǎn)的右子樹(shù)上蓖柔,則先左旋后右旋操作
                else:
                    index = left_right_rotate(index)
        elif node.key > index.key:
            index.right = self._insert(index.right, node)
            if get_height(index.right) - get_height(index.left) == 2:
                if node.key > index.right.key:
                    index = left_rotate(index)
                else:
                    index = right_left_rotate(index)
        # 更新高度
        index.height = max(get_height(index.left), get_height(index.right)) + 1
        return index

    def delete(self, key):
        # 更新根節(jié)點(diǎn)
        self.root = self._delete(self.root, key)

    def _delete(self, index, key):
        '''
        index: 根節(jié)點(diǎn)
        node: 要?jiǎng)h除的節(jié)點(diǎn)
        '''
        if key < index.key:
            index.left = self._delete(index.left, key)
            if get_height(index.right) - get_height(index.left) == 2:
                if get_height(index.right.right) > get_height(index.right.left):
                    index = left_rotate(index)
                else:
                    index = right_left_rotate(index)
            index.height = max(get_height(index.left), get_height(index.right))
        elif key > index.key:
            index.right = self._delete(index.right, key)
            if get_height(index.left) - get_height(index.right) == 2:
                if get_height(index.left.left) > get_height(index.left.right):
                    index = right_rotate(index)
                else:
                    index = left_right_rotate(index)
            index.height = max(get_height(index.left), get_height(index.right))
        # 當(dāng)要?jiǎng)h除的節(jié)點(diǎn)左右子樹(shù)都存在時(shí)
        elif index.left and index.right:
            if get_height(index.left) <= get_height(index.right):
                index.key = tree_min(index.right).key
                index.right = self._delete(index.right, index.key)
            else:
                index.key = tree_max(index.left).key
                index.left = self._delete(index.left, index.key)
            index.height = max(get_height(index.left),
                               get_height(index.right)) + 1
        # 只有左子樹(shù)或右子樹(shù);沒(méi)有子樹(shù)
        else:
            if index.right:
                index = index.right
            else:
                index = index.left
        return index


if __name__ == '__main__':
    alist = [10, 6, 2, 12, 13, 8]
    tree = AVLTree()
    for i in alist:
        node = Node(i)
        tree.insert(node)
    printTree(tree.root)
    tree.find(8)
    tree.delete(8)
    print("====分割線====")
    printTree(tree.root)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市风纠,隨后出現(xiàn)的幾起案子况鸣,更是在濱河造成了極大的恐慌,老刑警劉巖竹观,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镐捧,死亡現(xiàn)場(chǎng)離奇詭異潜索,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)懂酱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)竹习,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人列牺,你說(shuō)我怎么就攤上這事整陌。” “怎么了瞎领?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵泌辫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我九默,道長(zhǎng)震放,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任驼修,我火速辦了婚禮澜搅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邪锌。我一直安慰自己勉躺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布觅丰。 她就那樣靜靜地躺著饵溅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妇萄。 梳的紋絲不亂的頭發(fā)上蜕企,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音冠句,去河邊找鬼轻掩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛懦底,可吹牛的內(nèi)容都是我干的唇牧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼聚唐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼丐重!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起杆查,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扮惦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后亲桦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體崖蜜,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浊仆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豫领。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抡柿。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖氏堤,靈堂內(nèi)的尸體忽然破棺而出沙绝,到底是詐尸還是另有隱情搏明,我是刑警寧澤鼠锈,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站星著,受9級(jí)特大地震影響购笆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜虚循,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一同欠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧横缔,春花似錦铺遂、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至膛锭,卻和暖如春粮坞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背初狰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工莫杈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奢入。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓筝闹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親腥光。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丁存,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 樹(shù)的概述 樹(shù)是一種非常常用的數(shù)據(jù)結(jié)構(gòu),樹(shù)與前面介紹的線性表柴我,棧解寝,隊(duì)列等線性結(jié)構(gòu)不同,樹(shù)是一種非線性結(jié)構(gòu) 1.樹(shù)的定...
    Jack921閱讀 4,442評(píng)論 1 31
  • 什么是AVL樹(shù)艘儒? AVL樹(shù)即二叉平衡樹(shù)聋伦。因?yàn)槎娌檎覙?shù)的形狀會(huì)受插入數(shù)據(jù)集的影響夫偶,如果數(shù)據(jù)呈現(xiàn)有序排列,則二叉排序...
    心_的方向閱讀 598評(píng)論 0 0
  • 什么是AVL樹(shù)觉增? AVL樹(shù)兵拢,又稱為平衡二叉樹(shù),它是一種特殊的二叉查找樹(shù)(Binary Search Tree, B...
    wqbu閱讀 839評(píng)論 0 0
  • AVL樹(shù)是帶有平衡條件的查找二叉樹(shù)逾礁。這個(gè)平衡條件要容易保持说铃,而且他要保證樹(shù)的深度為O(logN) 原文地址:htt...
    喵了個(gè)嗚s閱讀 7,249評(píng)論 0 12
  • 生活中砾嫉,“沒(méi)睡好”三個(gè)字出現(xiàn)的頻次實(shí)在是太高了幼苛,似乎任何問(wèn)題都可以用“沒(méi)睡好”三個(gè)字來(lái)解釋原因。 情緒低落焕刮、頭疼舶沿、...
    身高管理師閱讀 402評(píng)論 0 0