AVL樹

什么是AVL樹衍腥?

AVL樹即二叉平衡樹岂膳。因?yàn)槎娌檎覙涞男螤顣?huì)受插入數(shù)據(jù)集的影響誓竿,如果數(shù)據(jù)呈現(xiàn)有序排列,則二叉排序樹是線性的闷营,查找算法效率不高。如果我們能保證不管數(shù)據(jù)是否有序知市,都能使二叉查找樹的高度盡可能的小傻盟。這種特殊的二叉查找樹即AVL樹。具有如下特征:

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

如果構(gòu)造AVL樹嫂丙?

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

** 插入操作 **

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


    圖片來自網(wǎng)絡(luò).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
  1. 插入節(jié)點(diǎn)在P的右孩子的右子樹上
    處理方式:對(duì)P點(diǎn)左旋轉(zhuǎn)處理隅肥。如圖所示


    圖片來自網(wǎng)絡(luò).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
  1. 插入節(jié)點(diǎn)在P的右孩子的左子樹上
    處理方式:先對(duì)C點(diǎn)做一次右旋轉(zhuǎn)竿奏,然后再對(duì)P點(diǎn)做一次左旋轉(zhuǎn)。如圖所示


    圖片來自網(wǎng)絡(luò).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)
  1. 插入節(jié)點(diǎn)在P的左孩子的右子樹上
    處理方式:先對(duì)C點(diǎn)做一次左旋轉(zhuǎn)腥放,然后再對(duì)P點(diǎn)做一次右旋轉(zhuǎn)泛啸。如圖所示


    圖片來自網(wǎng)絡(luò).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)既有左子樹又有右子樹:如果左子樹高度比較高,則選取左子樹值最大的節(jié)點(diǎn)聚请,將值賦值給當(dāng)前節(jié)點(diǎn)荠雕,并刪除那個(gè)值最大的節(jié)點(diǎn);如果右子樹高度比較高,則選取右子樹中值最小節(jié)點(diǎn)舞虱,將值賦值給當(dāng)前節(jié)點(diǎn)欢际,并刪除那個(gè)值最小的節(jié)點(diǎn)。 最后再做平衡化處理

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

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

'''AVL樹的實(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)有值矾兜,則才真正開始執(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)
            # 如果左右子樹不平衡损趋,則進(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)的右子樹上椅寺,則先左旋后右旋操作
                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í)
        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
        # 只有左子樹或右子樹;沒有子樹
        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)

AVL樹效率

查找節(jié)點(diǎn):時(shí)間復(fù)雜度為O(logN)
插入節(jié)點(diǎn):因?yàn)樾枰炔檎业焦?jié)點(diǎn)浑槽,然后進(jìn)行旋轉(zhuǎn)平衡操作(基本為1),所以也為O(logN)
刪除節(jié)點(diǎn):再查找到節(jié)點(diǎn)之后返帕,還需要檢查從刪除節(jié)點(diǎn)到根節(jié)點(diǎn)的平衡因子桐玻,所以時(shí)間復(fù)雜度為O(logN)

最后編輯于
?著作權(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)離奇詭異,居然都是意外死亡敞峭,警方通過查閱死者的電腦和手機(jī)踊谋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旋讹,“玉大人殖蚕,你說我怎么就攤上這事〕良#” “怎么了睦疫?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鞭呕。 經(jīng)常有香客問我笼痛,道長(zhǎng),這世上最難降的妖魔是什么琅拌? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任缨伊,我火速辦了婚禮,結(jié)果婚禮上进宝,老公的妹妹穿的比我還像新娘刻坊。我一直安慰自己,他們只是感情好党晋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布谭胚。 她就那樣靜靜地躺著徐块,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灾而。 梳的紋絲不亂的頭發(fā)上胡控,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音旁趟,去河邊找鬼昼激。 笑死,一個(gè)胖子當(dāng)著我的面吹牛锡搜,可吹牛的內(nèi)容都是我干的橙困。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼耕餐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼凡傅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肠缔,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤夏跷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后明未,有當(dāng)?shù)厝嗽跇淞掷锇l(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
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柳譬。 院中可真熱鬧喳张,春花似錦、人聲如沸美澳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舅桩,卻和暖如春酱虎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背擂涛。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工读串, 沒想到剛下飛機(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)容