【Python数据结构】——二叉平衡树AVL(查找、构建、删除、插入、打印、遍历)
生活随笔
收集整理的這篇文章主要介紹了
【Python数据结构】——二叉平衡树AVL(查找、构建、删除、插入、打印、遍历)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/7/28 20:57
# @Author : @linlianqin
# @Site :
# @File : 二叉平衡樹專題(創(chuàng)建、插入、查找).py
# @Software: PyCharm
# @description:'''二叉平衡樹的特點:在二叉查找樹的基礎上對每一個節(jié)點的左右子樹的高度進行了規(guī)定——每一個節(jié)點的左右子樹的高度差不超過1
1)任何一個節(jié)點都有:左子樹節(jié)點值 <= 節(jié)點值 < 右子樹節(jié)點值
2)任何一個節(jié)點的左子樹高度和右子樹高度之差不超過1兩個概念:
1)當前節(jié)點的高度 = max(左子樹節(jié)點高度,右子樹節(jié)點高度) + 1
2)平衡因子 = 左子樹節(jié)點高度 - 右子樹節(jié)點高度調(diào)式代碼過程中出現(xiàn)的問題:
1)高度值混淆:高度值規(guī)定——空樹高度為0,默認節(jié)點高度值為1,葉節(jié)點的高度值默認為1;
2)注意插入方法和二叉查找樹的插入方法的區(qū)別:
BST的插入過程中,頭節(jié)點是不會發(fā)生改變的,因為只需要滿足左<=中<右即可;
AVL的插入過程中,由于要維持樹的平衡,同時要滿足BST的性質(zhì),因此再插入過程中,各個子樹的根節(jié)點是會發(fā)生改變的,因此插入結(jié)束后,需要將最新的頭節(jié)點返回,以便后續(xù)的遍歷
3)在更新節(jié)點高度值的時候,設計更新節(jié)點高度值函數(shù)的時候沒有更新節(jié)點的高度值,而是直接返回了,
導致節(jié)點高度值出現(xiàn)錯誤
錯誤代碼:return max(self.getTreeHeight(root.left),self.getTreeHeight(root.right))+1
正確代碼:root.height = max(self.getTreeHeight(root.left),self.getTreeHeight(root.right))+1
4)插入元素后,在進行左旋\右旋的時候,沒有接受返回來的調(diào)整平衡后的子樹根節(jié)點,導致最后遍歷AVL樹的時候元素不完整
錯誤代碼:self.AVL_L(root.left)
正確代碼:root.left = self.AVL_L(root.left)'''# 定義二叉平衡樹的節(jié)點類,比普通的二叉樹節(jié)點類多了一個高度屬性
class TreeNode:def __init__(self,val,left=None,right=None,height=1):self.val = valself.left = leftself.right = rightself.height = height# 二叉平衡樹的基本操作
class AVL_ops:# 計算以當前節(jié)點為根節(jié)點的樹的高度def getTreeHeight(self,root):if root == None:return 0return root.height# 更新以當前節(jié)點為根節(jié)點的樹的高度 = max(左子樹節(jié)點高度,右子樹節(jié)點高度) + 1def updateTreeHeight(self,root):## 這里不需要判斷左右節(jié)點是否為空,是因為在self.getTreeHeight函數(shù)中已經(jīng)將空節(jié)點的高度設置為0了root.height = max(self.getTreeHeight(root.left),self.getTreeHeight(root.right))+1# 計算當前節(jié)點的平衡因子 = (左子樹節(jié)點高度 - 右子樹節(jié)點高度)def getNodeBalanceFactor(self,root):return self.getTreeHeight(root.left)- self.getTreeHeight(root.right)# 在二叉平衡樹中的查找目標值—-因為AVL實際上是BST因此查找也是一樣的def AVL_search(self,root,target):# 訪問到了空樹的時候,說明目標值不存在if root is None:return False# 相等說明目標值存在if target == root.val:return True# 目標值在當前節(jié)點的左子樹上if target < root.val:return self.AVL_search(root.left,target)# 目標值在當前節(jié)點的右子樹上if target > root.val:return self.AVL_search(root.right,target)# 在二叉平衡樹中進行插入——和BST的插入有所區(qū)別,這里是因為需要保證樹的高度處于平衡狀態(tài)## 插入一個元素后,高度失去平衡的節(jié)點的平衡因子一定是2或者-2只需要將這里的失衡的節(jié)點調(diào)節(jié)就可以## 事實證明:只需要將失衡的節(jié)點進行調(diào)節(jié)整棵樹就可以達到平衡狀態(tài)## 這里分為左旋和右旋兩種方式進行平衡調(diào)節(jié),對于失衡的樹有LL,LR,RR,RL四種結(jié)構(gòu)## 其中LL右旋達到平衡、LR先左節(jié)點左旋為LL再右旋、RR左旋達到平衡、RL先右節(jié)點右旋為RR然后再達到平衡### 左旋:def AVL_L(self,root):# 1)臨時節(jié)點存放根節(jié)點的右節(jié)點temp = root.right# 2)將根節(jié)點的右節(jié)點更新為temp的左節(jié)點root.right = temp.left# 3)將temp節(jié)點的左節(jié)點更新為根節(jié)點temp.left = root# 4)更新temp和root的高度值self.updateTreeHeight(root)self.updateTreeHeight(temp)# 5)更新根節(jié)點root = tempreturn root### 右旋:和左旋相反def AVL_R(self,root):# 1)臨時節(jié)點存放根節(jié)點的左節(jié)點temp = root.left# 2)將根節(jié)點的左節(jié)點更新為temp的右節(jié)點root.left = temp.right# 3)將temp節(jié)點的右節(jié)點更新為根節(jié)點temp.right = root# 4)更新temp和root的高度值self.updateTreeHeight(root)self.updateTreeHeight(temp)# 5)更新根節(jié)點root = tempreturn root# 插入的話首先需要進行失衡節(jié)點處的樹型判斷LL\LR\RR\RL# 步驟:# 1)先將元素按照左小右大的性質(zhì)插入到對應葉節(jié)點末尾;# 2)再更新插入元素后的各節(jié)點高度;# 3)根據(jù)各節(jié)點高度值計算各節(jié)點的平衡因子;# 4)根據(jù)節(jié)點及其左右子節(jié)點的平衡因子來判斷樹形;# 5)根據(jù)樹形選擇左旋、右旋組合來將插入后的失衡二叉樹調(diào)整至平衡二叉樹AVL# 6)返回最新的根節(jié)點完成插入操作def AVL_insert(self,root,target):if root is None:return TreeNode(target)# target插入到左子樹,說明左子樹會出現(xiàn)失衡,樹形可能為LL或者LRif root.val > target:root.left = self.AVL_insert(root.left,target)# 更新樹高self.updateTreeHeight(root)# 判斷樹形if self.getNodeBalanceFactor(root) == 2:# LL型,右旋if self.getNodeBalanceFactor(root.left) == 1:root = self.AVL_R(root)# LR型,右旋elif self.getNodeBalanceFactor(root.left) == -1:root.left = self.AVL_L(root.left)root = self.AVL_R(root)# target插入到右子樹,說明右子樹會出現(xiàn)失衡,樹形可能為RR或者RLelse:root.right = self.AVL_insert(root.right, target)# 更新樹高self.updateTreeHeight(root)# 判斷樹形if self.getNodeBalanceFactor(root) == -2:# RR型,左旋if self.getNodeBalanceFactor(root.right) == -1:root = self.AVL_L(root)# RL型,先對右節(jié)點右旋再對根節(jié)點左旋elif self.getNodeBalanceFactor(root.right) == 1:root.right = self.AVL_R(root.right)root = self.AVL_L(root)return root# todo:刪除元素,其實就是插入的反向操作def AVL_delete(self,root,target):pass# AVL創(chuàng)建——其實就是一個一個插入元素def AVL_create(self,nums):if len(nums) == 0:return Noneroot = TreeNode(nums[0])for num in nums[1:]:root = self.AVL_insert(root,num)return root# 層序遍歷AVLdef AVL_layer(self,root):if root is None:returnq = [root]Height = []while q:newQ = []for node in q:print(node.val,end=',')Height.append(node.height)if node.left is not None:newQ.append(node.left)if node.right is not None:newQ.append(node.right)q = newQprint("\n對應節(jié)點的高度")print(Height)# 中序# 遍歷二叉查找數(shù),中序遍歷def AVL_mid_scan(self,root):if root is None:return# 遍歷左子樹self.AVL_mid_scan(root.left)# 遍歷根節(jié)點print(root.val, end=',')self.AVL_mid_scan(root.right)li = [1,2,3,4,5,6,7]
print("原始列表:",li)# 創(chuàng)建
print("創(chuàng)建AVL")
root = AVL_ops().AVL_create(li)
# 層序遍歷
print("層序遍歷")
AVL_ops().AVL_layer(root)
print("中序遍歷")
AVL_ops().AVL_mid_scan(root)print("\n插入數(shù)值-1")
root = AVL_ops().AVL_insert(root,-1)
print("插入數(shù)值-1后的層序遍歷")
AVL_ops().AVL_layer(root)
print("插入數(shù)值-1后的中序遍歷")
AVL_ops().AVL_mid_scan(root)print("\n插入數(shù)值5")
root = AVL_ops().AVL_insert(root,-1)
print("插入數(shù)值5后的層序遍歷")
AVL_ops().AVL_layer(root)
print("插入數(shù)值5后的中序遍歷")
AVL_ops().AVL_mid_scan(root)
示例結(jié)果:
原始列表: [1, 2, 3, 4, 5, 6, 7]
創(chuàng)建AVL
層序遍歷
4,2,6,1,3,5,7,
對應節(jié)點的高度
[3, 2, 2, 1, 1, 1, 1]
中序遍歷
1,2,3,4,5,6,7,
插入數(shù)值-1
插入數(shù)值-1后的層序遍歷
4,2,6,1,3,5,7,-1,
對應節(jié)點的高度
[4, 3, 2, 2, 1, 1, 1, 1]
插入數(shù)值-1后的中序遍歷
-1,1,2,3,4,5,6,7,
插入數(shù)值5
插入數(shù)值5后的層序遍歷
4,2,6,-1,3,5,7,-1,1,
對應節(jié)點的高度
[4, 3, 2, 2, 1, 1, 1, 1, 1]
插入數(shù)值5后的中序遍歷
-1,-1,1,2,3,4,5,6,7,
注意和二叉查找樹的細節(jié)區(qū)別,細節(jié)區(qū)別見本文代碼前部分《遇到的問題》
總結(jié)
以上是生活随笔為你收集整理的【Python数据结构】——二叉平衡树AVL(查找、构建、删除、插入、打印、遍历)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出etcd系列 – 心跳和选举
- 下一篇: 【Python数据结构】——并查集的实现