1. 程式人生 > 其它 >AVL樹插入的python實現

AVL樹插入的python實現

AVL樹的插入python實現

  • AVL樹:AVL樹是一棵自平衡的二叉搜素樹

  • AVL樹有以下性質:

    • 根的左右子樹的高度之差的絕對值不超過1
    • 根的左右子樹都是平衡二叉樹
  • 插入一個節點可能會破壞AVL樹的平衡,可以通過旋轉操作來進行修正

  • 插入一個節點後,只有從插入節點到根節點的路徑上的節點的平衡可能改變。我們需要找出第一個破壞了平衡條件的節點,稱之為k,k的兩個子樹的高度差為2

  • 不平衡的出現可能有四種情況

    • 左旋:不平衡是由對K的右孩子的右子樹插入導致的:左旋

    • 右旋:不平衡是由於對k的左孩子的左子樹插入導致的:右旋

    • 右旋-左旋:不平衡是由於對k的右孩子的左子樹插入導致的:右旋-左旋

    • 左旋-右旋:不平衡是由於對k的左孩子的右子樹插入導致的:左旋-右旋

程式碼實現

class AVLNode:
    def __init__(self,data):
        self.data = data
        self.rchild = None
        self.lchild = None
        self.parent = None
        self.bf=0 #bf為平衡值,預設為0,插入右孩子時bf+1,插入左孩子時bf-1
class AVLTree:
    def __init__(self,li=None):
        self.root=None
        #迴圈插入AVL樹
        if li:
            for val in li:
                self.insert_no_rec(val)
    def rotate_left(self,p,c):#左旋:由右子樹的右孩子插入引起失衡
        #根據左旋的規律,如果右子樹的左孩子存在,左旋後,右子樹的左孩子將成為原始根節點的右孩子,原始根節點成為右子樹的左孩子
        s2=c.lchild
        p.rchild=s2
        if s2:
            s2.parent=p
        c.lchild=p
        p.parent=c


        p.bf=0
        c.bf=0
        return c
    def rotate_right(self,p,c):#右旋:由左子樹的左孩子插入導致失衡
        #根據右旋的規律,如果左子樹的右孩子存在,右旋後,左子樹的右孩子將成為原始根節點的左孩子,原始根節點成為原始左子樹的右孩子
        s2=c.rchild
        p.lchild=s2
        if s2:
            s2.parent=p
        c.rchild=p
        p.parent=c

        p.bf=0
        c.bf=0
        return c
    def rotate_right_left(self,p,c):#右旋-左旋: 不平衡是由於對k的右孩子的左子樹插入導致的

        
        g=c.lchild
        s3=g.rchild
        c.lchild=s3
        if s3:
            s3.parent=c
        g.rchild=c
        c.parent=g

        s2=g.lchild
        p.rchild=s2
        if s2:
            s2.parent=p
        g.lchild=p
        p.parent=g

        #更新bf
        if g.bf>0:#往右偏
            c.bf=0
            p.bf=-1
        elif g.bf<0: #插入到s2中
            c.bf=1
            p.bf=0
        else:#s1,s2,s3,s4都是空
            p.bf=0
            c.bf=0
        g.bf=0
        return g
    def rotate_left_right(self,p,c):
        g=c.rchild
        s2=g.lchild
        c.rchild=s2
        if s2:
            s2.parent=c
        g.lchild=c
        c.parent=g

        s3=g.rchild
        p.lchild=s3
        if s3:
            s3.parent=p
        g.rchild=p
        p.parent=g

        if g.bf>0: #右邊多
            p.bf=0
            c.bf=-1
        elif g.bf<0:#左邊多
            c.bf=0
            p.bf=1
        else:
            c.bf=0
            p.bf=0
        g.bf=0
        return g

    def pre_order(self, root):  # 前序遍歷
        if root:
            print(root.data, end=',')
            self.pre_order(root.lchild)
            self.pre_order(root.rchild)

    def in_order(self, root):  # 中序遍歷
        if root:
            self.in_order(root.lchild)
            print(root.data, end=',')
            self.in_order(root.rchild)

    def post_order(self, root):  # 後序遍歷
        if root:
            self.post_order(root.lchild)
            self.post_order(root.rchild)
            print(root.data, end=',')

    def insert_no_rec(self,val):
        #插入
        p=self.root
        #當根節點不存在時,插入的點即為根節點
        if not p:
            self.root=AVLNode(val)
            return
        #當根節點存在時
        while True:
            #如果值小於當前節點的值,則查詢他的左孩子,如果左孩子存在,則另左孩子節點為當前節點,進入下次插入,如果左孩子不存在,則將插入節點插入到當前節點的左孩子,並使當前節點的bf-1
            if val< p.data:
                if p.lchild:
                    p=p.lchild
                else:
                    p.lchild=AVLNode(val)
                    p.lchild.parent=p
                    node=p.lchild
                    p.bf=p.bf-1
                    break
             #如果值大於當前節點的值,則查詢他的右孩子,如果右孩子存在,則令右孩子節點為當前節點,進行下一次插入,如果右孩子不存在,則將插入節點插入到當前節點的右孩子,使當前節點的bf+1
            elif val > p.data:
                if p.rchild:
                    p =p.rchild
                else:
                    p.rchild=AVLNode(val)
                    p.rchild.parent=p
                    node=p.rchild
                    p.bf=p.bf+1
                    break

            else:
                return
        #2.更新balance factor
        #p為當前節點,p.parent存在即當前節點不是根節點時
        while p.parent:#node.parent不空
            if p.parent.lchild==p:#傳遞是從左子樹開始
                if p.parent.bf== -1: #原來的p.parent.bf==-1,更新後為-2                 
                    #記錄當前節點的父親節點和祖父節點,以便後面的連結
                    head=p.parent.parent
                    tmp=p.parent
                    #插入節點後,當前節點有三種狀態,只有左孩子,只有右孩子,左右孩子都有,對應了三種不同的結果
                    if p.bf>0:#只有右孩子時,發生左旋-右旋
                        n=self.rotate_left_right(p.parent,p)
                    elif p.bf<0:#只有左孩子時,發生右旋
                        n=self.rotate_right(p.parent,p)
                    else:
                        break
                elif p.parent ==1: #原來的p.parent.bf==1,更新後變為0              
                    #當前節點只有一個孩子節點時/或平衡點不為0時,當前節點的父親節點bf=0
                    if p.bf ==0:
                        break
                    else:
                        p.parent.bf=0
                        break
                else:#當前節點父親節點bf=0,當前節點只有一個孩子節點時/或平衡點不為0時,當前節點的父親節點bf=-1,並將問題向上傳遞
                    if p.bf==0:
                        break
                    else:
                        p.parent.bf=-1
                        p=p.parent
                        continue
            else:#右子樹插入
                if p.parent.bf ==1:
                    head = p.parent.parent
                    tmp = p.parent
                    if p.bf<0:#右子樹的左孩子插入
                        n=self.rotate_right_left(p.parent,p)
                    elif p.bf>0:
                        n=self.rotate_left(p.parent,p)
                    else:
                        break
                elif p.parent.bf == -1:
                    if  p.bf==0:
                        break
                    else:
                        p.parent.bf=0
                else:
                    if p.bf==0:
                        break
                    else:
                        p.parent.bf=1
                        p=p.parent
                        continue
            n.parent=head
            if head:  # head不是空
                if tmp == head.lchild:
                    head.lchild = n
                else:
                    head.rchild = n
                break
            else:
                self.root = n
                break