二叉樹之BST樹载慈、AVL樹

1.二叉排序樹的介紹

二叉排序樹為一顆二叉樹惭等,或者為空,或者滿足如下條件:
如果它的左子樹不為空办铡,那么左子樹上的所有結(jié)點的值均小于它的根結(jié)點的值
如果它的右子樹不為空辞做,那么右子樹上的左右結(jié)點的值均大于它的根結(jié)點的值
根結(jié)點的左子樹和右子樹又是二叉排序樹。
二叉排序樹通常采用二叉鏈表作為存儲結(jié)構(gòu)寡具。中序遍歷二叉排序樹便可得到一個有序序列秤茅,一個無序序列可以通過構(gòu)造一棵二叉排序樹變成一個有序序列,構(gòu)造樹的過程即是對無序序列進行排序的過程童叠。

2.二叉排序樹的操作

我們對二叉排序的操作有三種:
查找
插入
刪除
其中二叉排序樹是一個動態(tài)樹表框喳,其特點就是樹的結(jié)構(gòu)不是一次生成的,而是在查找過程中,如果關(guān)鍵字不在樹表中五垮,在把關(guān)鍵字插入到樹表中乍惊。而對于刪除操作,它分為三種情況:

  1. 刪除結(jié)點為葉子結(jié)點(左右子樹均為NULL)放仗,所以我們可以直接刪除該結(jié)點
  2. 刪除結(jié)點只有左子樹或者右子樹润绎,此時只需要讓其左子樹或者右子樹直接替代刪除結(jié)點的位置,稱為刪除結(jié)點的雙親的孩子匙监,就可以了
  3. 當(dāng)要刪除的那個結(jié)點凡橱,其左右子樹都存在的情況下,則要從從左子樹中找出一個最大的值那個結(jié)點來替換我們需要刪除的結(jié)點

3.二叉排序樹代碼實現(xiàn):

class BinarySortTree():
    def __init__(self):
        self.root = None
    def add(self,node):
        if self.root == None:
            self.root = node
        else:
            self.root.add(node)
    def infixOrder(self):
        if self.root == None:
            print("None")
        else:
            self.root.infixOrder()
    def delete(self,value):
        if self.root == None:
            print("None")
        else:
            self.root.delete(value)

class Node():
    def __init__(self,value=None):
        self.value = value
        self.left = None
        self.right = None
        self.root = None
    def add(self,insertnode):
        if insertnode.value < self.value:
            if self.left != None:
               self.left.add(insertnode)
            else:
                self.left = insertnode
        if insertnode.value > self.value:
            if self.right != None:
                self.right.add(insertnode)
            else:
                self.right = insertnode
    def infixOrder(self):
        if self.left != None:
            self.left.infixOrder()
        print(self.value)
        if self.right != None:
            self.right.infixOrder()
    
    def delete(self,value):
        #判斷是否為根結(jié)點:
        if self.value == value:
            self.covervalue,self.covervaluefather = self.left.findmaxvalue()
            self.value = self.covervalue
            self.covervaluefather.right = None
            return 
        #找到刪除結(jié)點父結(jié)點
        # print(self.value)
        self.fathernode= self.findcurfathernode(value)
        #找到刪除結(jié)點
        if self.fathernode.left != None and self.fathernode.left.value == value:
            self.delnode = self.fathernode.left
        elif self.fathernode.right != None and self.fathernode.right.value == value:
            self.delnode = self.fathernode.right
        #判斷刪除結(jié)點的三種形態(tài)
        #1.刪除結(jié)點左右子結(jié)點均為空
        if self.delnode.left == None and self.delnode.right == None:
            if self.fathernode.left.value == value:
                self.fathernode.left = None
            else:
                self.fathernode.right = None   

        #2.刪除結(jié)點左右子結(jié)點均不為空
        elif self.delnode.left != None and self.delnode.right != None:
            #找當(dāng)前結(jié)點左子樹的最大值亭姥,刪除該結(jié)點并把該結(jié)點的值給當(dāng)前結(jié)點
            if self.delnode.left.right == None:
                self.delnode.value = self.delnode.left.value
                self.delnode.left = None
            else:
                self.covervalue,self.covervaluefather = self.delnode.left.findmaxvalue()
                self.delnode.value = self.covervalue
                self.covervaluefather.right = None
                

        #3.刪除結(jié)點左子結(jié)點或右子結(jié)點其中一個為空
        else:
            if self.fathernode.left != None and self.fathernode.left.value == value:
                if self.delnode.left != None:
                    self.fathernode.left = self.delnode.left
                else:
                    self.fathernode.left = self.delnode.right
            else:
                if self.delnode.left != None:
                    self.fathernode.right = self.delnode.left
                else:
                    self.fathernode.right = self.delnode.right


    #找到當(dāng)前結(jié)點下子樹的最大值稼钩,即一直往該結(jié)點的右子結(jié)點找就行
    def findmaxvalue(self):
        if self.right.right != None:
            self.right.findmaxvalue()
        else:
            covervalue = self.right.value
            return covervalue,self
    #找到待刪除結(jié)點的父結(jié)點
    def findcurfathernode(self,value):
        if self.right != None and self.right.value == value:
            # print("111",self.value)
            return self
        elif self.left != None and self.left.value == value:
            # print("222",self.value)
            return self
        else:
            if self.value > value:
                self = self.left.findcurfathernode(value)
            else:
                self = self.right.findcurfathernode(value)
        # print("113331",self.value)
        return self


    
bst = BinarySortTree()
node1 = Node(1)
node3 = Node(3)
node5 = Node(5)
node7 = Node(7)
node9 = Node(9)
node10 = Node(10)
node12 = Node(12)
node2 = Node(2)
bst.add(node7)
bst.add(node3)
bst.add(node10)
bst.add(node12)
bst.add(node5)
bst.add(node1)
bst.add(node9)
bst.add(node2)
bst.infixOrder()
bst.delete(12)
bst.infixOrder()

4.AVL樹的介紹:
AVL 樹是一種平衡二叉樹,得名于其發(fā)明者的名字( Adelson-Velskii 以及 Landis)达罗。(可見名字長的好處坝撑,命名都能多占一個字母出來)。平衡二叉樹遞歸定義如下:
左右子樹的高度差小于等于 1粮揉。
其每一個子樹均為平衡二叉樹巡李。

平衡因子: 某個結(jié)點的左子樹的高度減去右子樹的高度得到的差值。
基于平衡因子扶认,我們就可以這樣定義 AVL 樹侨拦。
AVL 樹: 所有結(jié)點的平衡因子的絕對值都不超過 1 的二叉樹。

為了計算平衡因子辐宾,我們自然需要在節(jié)點中引入高度這一屬性狱从。在這里,我們把節(jié)點的高度定義為其左右子樹的高度的最大值叠纹。代碼如下:

def height(self,node):
        if node == None:
            return 0
        else:
            return max(self.height(node.left),self.height(node.right))+1

當(dāng)平衡因子的絕對值大于 1 時季研,就會觸發(fā)樹的平衡化操作。

樹的平衡化操作
二叉樹的平衡化有兩大基礎(chǔ)操作: 左旋和右旋誉察。左旋与涡,即是逆時針旋轉(zhuǎn);右旋持偏,即是順時針旋轉(zhuǎn)驼卖。這種旋轉(zhuǎn)在整個平衡化過程中可能進行一次或多次,這兩種操作都是從失去平衡的最小子樹根結(jié)點開始的(即離插入結(jié)點最近且平衡因子超過1的祖結(jié)點)鸿秆。
需要平衡的四種情況
LL 型
RR 型
LR 型
RL 型

AVL 樹的插入和刪除操作
基于上文的再平衡操作款慨,現(xiàn)在我們可以寫出完整的 AVL 樹的插入/刪除操作。
代碼是基于BST樹進行開發(fā)的谬莹,但是AVL樹的刪除還沒有寫檩奠,(太懶了~)

代碼實現(xiàn)

class AVLTree():
    def __init__(self):
        self.root = None
    def add(self,node):
        if self.root == None:
            self.root = node
        else:
            self.root.add(node)
    def infixOrder(self):
        if self.root == None:
            print("None")
        else:
            self.root.infixOrder()
    def delete(self,value):
        if self.root == None:
            print("None")
        else:
            self.root.delete(value)
    def height(self):
        if self.root == None:
            return 0
        else:
            print("height",self.root.height(self.root))
        

class Node():
    def __init__(self,value=None):
        self.value = value
        self.left = None
        self.right = None
        self.root = None
        self.leftheight = 0
        self.rightheight = 0
    #獲得當(dāng)前結(jié)點的深度
    def height(self,node):
        if node == None:
            return 0
        else:
            return max(self.height(node.left),self.height(node.right))+1
    def add(self,insertnode):
        # print("111",self.value)
        if insertnode.value < self.value:
            if self.left != None:
               self.left.add(insertnode)
            else:
                self.left = insertnode
        elif insertnode.value > self.value:
            if self.right != None:
                self.right.add(insertnode)
            else:
                self.right = insertnode
        #1.首先我知道如果添加一個結(jié)點桩了,只會影響它所在的樹的深度,因此只需要找到它的爺爺及以上埠戳,再去判斷深度井誉,就可以平衡了
        #2.問題是怎么從爺爺往根結(jié)點找,這會兒又忘了這是個遞歸的過程整胃,我遞歸的從根結(jié)點一直往待插入結(jié)點的父結(jié)點找插入位置颗圣,完事之后回一直返回,這樣就能從爺爺找到根結(jié)點了
        # print("222",self.value)
        if self.height(self.left) - self.height(self.right) >1:
            if insertnode.value < self.left.value:
    
                self.ll()
            else:
                self.lr()
        elif self.height(self.right) - self.height(self.left) >1:
            if insertnode.value < self.right.value:
                self.rl()
            else:
                self.rr()

    def infixOrder(self):
        if self.left != None:
            self.left.infixOrder()
        print(self.value)
        if self.right != None:
            self.right.infixOrder()
    
    def delete(self,value):
        #判斷是否為根結(jié)點:
        if self.value == value:
            self.covervalue,self.covervaluefather = self.left.findmaxvalue()
            self.value = self.covervalue
            self.covervaluefather.right = None
            return 
        #找到刪除結(jié)點父結(jié)點
        # print(self.value)
        self.fathernode= self.findcurfathernode(value)
        #找到刪除結(jié)點
        if self.fathernode.left != None and self.fathernode.left.value == value:
            self.delnode = self.fathernode.left
        elif self.fathernode.right != None and self.fathernode.right.value == value:
            self.delnode = self.fathernode.right
        #判斷刪除結(jié)點的三種形態(tài)
        #1.刪除結(jié)點左右子結(jié)點均為空
        if self.delnode.left == None and self.delnode.right == None:
            if self.fathernode.left != None and self.fathernode.left.value == value:
                self.fathernode.left = None
            else:
                self.fathernode.right = None   

        #2.刪除結(jié)點左右子結(jié)點均不為空
        elif self.delnode.left != None and self.delnode.right != None:
            #找當(dāng)前結(jié)點左子樹的最大值屁使,刪除該結(jié)點并把該結(jié)點的值給當(dāng)前結(jié)點
            if self.delnode.left.right == None:
                self.delnode.value = self.delnode.left.value
                self.delnode.left = None
            else:
                self.covervalue,self.covervaluefather = self.delnode.left.findmaxvalue()
                self.delnode.value = self.covervalue
                self.covervaluefather.right = None
                

        #3.刪除結(jié)點左子結(jié)點或右子結(jié)點其中一個為空
        else:
            if self.fathernode.left != None and self.fathernode.left.value == value:
                if self.delnode.left != None:
                    self.fathernode.left = self.delnode.left
                else:
                    self.fathernode.left = self.delnode.right
            else:
                if self.delnode.left != None:
                    self.fathernode.right = self.delnode.left
                else:
                    self.fathernode.right = self.delnode.right
        
        
        
    #找到當(dāng)前結(jié)點下子樹的最大值在岂,即一直往該結(jié)點的右子結(jié)點找就行
    def findmaxvalue(self):
        if self.right.right != None:
            self.right.findmaxvalue()
        else:
            covervalue = self.right.value
            return covervalue,self
    #找到待刪除結(jié)點的父結(jié)點
    def findcurfathernode(self,value):
        if self.right != None and self.right.value == value:
            # print("111",self.value)
            return self
        elif self.left != None and self.left.value == value:
            # print("222",self.value)
            return self
        else:
            if self.value > value:
                self = self.left.findcurfathernode(value)
            else:
                self = self.right.findcurfathernode(value)
        # print("113331",self.value)
        return self
    def rr(self):
        #右右-> 左旋
        print("rr")
        head = Node()
        head.value = self.value
        head.left = self.left
        head.right = self.right.left
        self.value = self.right.value
        self.left = head
        self.right = self.right.right
    def ll(self):
        #左左 -> 右旋
        print("ll")
        head = Node()
        head.value = self.value
        head.right = self.right 
        head.left = self.left.right
        self.value = self.left.value
        self.right = head
        self.left = self.left.left
    def lr(self):
        #左右先左旋再右旋
        print("lr")
        self.left.rr()
        self.ll()
    def rl(self):
        #右左先右旋再左旋
        print("rl")
        self.right.ll()
        self.rr()

    
bst = AVLTree()
node1 = Node(1)
node3 = Node(3)
node4 = Node(4)
node5 = Node(5)
node7 = Node(7)
node8 = Node(8)
node9 = Node(9)
node10 = Node(10)
node11 = Node(11)
node12 = Node(12)
node13 = Node(13)
node14 = Node(14)
node15 = Node(15)
bst.add(node13)
bst.add(node11)
bst.add(node14)
bst.add(node10)
bst.add(node12)
bst.add(node15)
bst.add(node9)
# bst.add(node15)
# bst.add(node9)
# bst.add(node2)
# bst.add(node3)
# bst.infixOrder()
bst.delete(9)
# bst.infixOrder()
# bst.height()
print(bst.root.value)
print(bst.root.left.value)
print(bst.root.right.value)
print(bst.root.left.left.value)
print(bst.root.left.right.value)
# print(bst.root.right.left.value)
print(bst.root.right.right.value)
# print(bst.root.left.left.left.value)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蛮寂,隨后出現(xiàn)的幾起案子蔽午,更是在濱河造成了極大的恐慌,老刑警劉巖酬蹋,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件及老,死亡現(xiàn)場離奇詭異,居然都是意外死亡范抓,警方通過查閱死者的電腦和手機骄恶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匕垫,“玉大人僧鲁,你說我怎么就攤上這事∠蟊茫” “怎么了寞秃?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長单芜。 經(jīng)常有香客問我,道長犁柜,這世上最難降的妖魔是什么洲鸠? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮馋缅,結(jié)果婚禮上扒腕,老公的妹妹穿的比我還像新娘。我一直安慰自己萤悴,他們只是感情好瘾腰,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著覆履,像睡著了一般蹋盆。 火紅的嫁衣襯著肌膚如雪费薄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天栖雾,我揣著相機與錄音楞抡,去河邊找鬼。 笑死析藕,一個胖子當(dāng)著我的面吹牛召廷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播账胧,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼竞慢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了治泥?” 一聲冷哼從身側(cè)響起筹煮,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎车摄,沒想到半個月后寺谤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡吮播,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年变屁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片意狠。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡粟关,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出环戈,到底是詐尸還是另有隱情闷板,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布院塞,位于F島的核電站遮晚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拦止。R本人自食惡果不足惜县遣,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汹族。 院中可真熱鬧萧求,春花似錦、人聲如沸顶瞒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榴徐。三九已至守问,卻和暖如春匀归,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酪碘。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工朋譬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兴垦。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓徙赢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親探越。 傳聞我的和親對象是個殘疾皇子狡赐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

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