二叉搜尋樹、AVL以及紅黑自平衡二叉搜尋樹
本文符號意義
q: 插入節點
M: 實際刪除節點(後繼節點)
P: 當前節點的父節點
G: 當前節點的爺爺節點
S: 當前節點的叔叔節點(即父節點的兄弟節點)
L: 左孩子
R: 右孩子
非空:節點的key值不為空
二叉搜尋樹
二叉搜尋樹的基本操作有search(搜尋)、insert(插入)、delete(刪除)
搜尋
key值小於當前節點,則搜尋當前節點的左子樹,反之右子樹,直到葉節點(左右孩子不存在)。若遇到相同key則返回True。
二叉搜尋樹的搜尋的時間複雜度最好是O(logn),但在以下兩種情況下,將和線性搜尋O(n)無異。
插入
搜尋到葉節點,若比葉節點key小,則新增為當前葉節點的左孩子,反之右孩子。
刪除
若預刪除節點M不是葉節點會破壞樹的性質。一種簡單的方法是尋找後繼節點,將後繼節點儲存的資料複製給預刪除節點,然後刪除後繼節點即可。後繼節點最多一個孩子節點。
分成以下幾種情形討論:
- M是葉節點。無後繼節點,直接刪除(如圖中M,N,T)
- M只有左孩子。左孩子即為後繼節點(如圖中M就是P的後繼節點)
- M有右孩子。
- M的右孩子沒有左孩子。右孩子就是後繼節點(如圖中T就是S的後繼節點)
- M的右孩子有左孩子。左子樹中key最小的節點就是後繼節點,即最靠左的節點(只有右孩子或為葉節點)(如圖中N就是G的後繼節點)
# 尋找後繼節點以及刪除操作參考程式碼
def findSuccessor (self, node):
# 找到後繼節點, 即右子樹中最左的節點
currentNode = node.right.left
while True:
if not currentNode.hasLeft() or not currentNode.left.key:
return currentNode
currentNode = currentNode.left
def delete(self, key):
p = self._search(key)
if p.key != key:
print('the {} is not on the tree'.format(key))
else:
if p.hasRight():
if p.right.hasLeft():
succ = self.findSuccessor(p)
p.key = succ.key
succ.parent.left = succ.right
if succ.hasRight():
succ.right.parent = succ.parent
else:
p.key = p.right.key
if p.right.hasRight():
p.right.right.parent = p
p.right = p.right.right
else:
if p.hasLeft():
p.parent.left = p.left
p.left.parent = p.parent
else:
if p.isLeft():
p.parent.left = None
else:
p.parent.right = None
自平衡二叉搜尋樹
AVL樹以及紅黑樹是自平衡二叉搜尋樹。較二叉搜尋樹而言主要的差別在於為了編碼方便,在每個原本意義上的葉節點下加兩個key為空的節點作為新的葉節點(讓空的葉節點顯式存在能使對樹的操作更為簡便)。
自平衡二叉搜尋樹的基本操作與二叉搜尋樹相同,僅在插入和刪除樹中的節點的同時,加一個調節樹結構的過程使之儘量左右平衡。無論是AVL樹還是紅黑樹都需要旋轉操作來調節樹的結構。旋轉可分為左旋轉與右旋轉,如圖所示:
可見,旋轉操作是以兩個節點(node1, node2)為基準。node1是老的子樹根節點,node2是node1的孩子,是新的子樹根節點。當node1, node2排列成"/“則進行右旋,當排列成”\"則進行左旋。
def _leftRotate(self, oldRoot, newRoot):
# newRoot是oldRoot的右孩子
oldRoot.right = newRoot.left
if newRoot.left is not None:
newRoot.left.parent = oldRoot
newRoot.parent = oldRoot.parent
if oldRoot.parent is not None:
if oldRoot.parent.left == oldRoot: # 舊的根節點是左孩子
oldRoot.parent.left = newRoot
else:
oldRoot.parent.right = newRoot
else:
self.root = newRoot
oldRoot.parent = newRoot
newRoot.left = oldRoot
def _rightRotate(self, oldRoot, newRoot):
# newRoot是oldRoot的左孩子
oldRoot.left = newRoot.right
if newRoot.right is not None:
newRoot.right.parent = oldRoot
newRoot.parent = oldRoot.parent
if oldRoot.parent is not None:
if oldRoot.parent.left == oldRoot:
oldRoot.parent.left = newRoot
else:
oldRoot.parent.right = newRoot
else:
self.root = newRoot
oldRoot.parent = newRoot
newRoot.right = oldRoot
對於插入與刪除操作需要注意兩點:
無論插入節點q還是刪除節點M,都無需調節q或M的孩子的結構,因此只需以其為起點自底向上調整即可。
實際刪除的節點最多隻有一個非空孩子節點。
AVL樹
AVL樹的每個節點都有一個**平衡因子(balance factor, bf)**屬性,即該節點左子樹的高度減去右子樹的高度(也有書籍定義右減左) 。
平衡
更新平衡因子(updateBalance)
- 新插入節點為右孩子,其父節點bf減1,反之加1
- 若父節點更新之前bf等於0, break,反之以父節點為新的當前節點繼續向上更新
旋轉平衡樹結構(rebalance)
當節點的bf的值大於1,表明左子樹過深,需要右旋以減小深度,反之若bf小於-1,表明右子樹過深,需要左旋以較小深度。旋轉後,以當前節點為根的子樹高度不變或減小1,較小1則需要繼續向上更新。
旋轉後bf更新公式如下:
左旋:
右旋:
注:node2是node1的左孩子,進行右旋為例:
圖中,T1,T2,T3表示子樹。h1,h2,h3為樹的高度。
旋轉前:
旋轉後:
(3)-(2)得:
(4)-(1)得:
插入
- 搜尋待插入葉節點q
- 賦予q以key值,bf設定為0,並新增空L、R孩子葉節點
- 以q為起點自底向上更新bf:
- 若當前節點bf小於-1或大於1,則rebalance, break(旋轉後,當前節點的子樹必然恢復原來的高度,故無需繼續向上更新)
- 當前節點父節點不存在,break
- 父節點存在,當前節點為左孩子,父節點bf加1,反之減1
- 若父節點bf等於0,break
刪除
- 搜尋預刪除節點
- 找到後繼節點M
- 以M為起點自底向上更新bf:
- 若當前節點bf小於-1或大於1,則需要rebalance調整樹結構,以當前節點有右孩子為例(這裡與插入後調整有點區別)
- 若右孩子平衡因子為0,則調整後樹的高度是不變的,break
- 若右孩子平衡因子為-1或1,則調整後,需要以當前節點父節點為新的當前節點
- 當前節點父節點不存在,break
- 父節點存在:
- 父節點bf等於0,若當前節點為左孩子,bf加1,反之減1,break
- 父節點bf不等於0, 若當前節點為左孩子,bf加1,反之減1, 繼續向上更新
- 若當前節點bf小於-1或大於1,則需要rebalance調整樹結構,以當前節點有右孩子為例(這裡與插入後調整有點區別)
- 刪除後繼節點
# 插入和刪除節點後調整bf的參考程式碼
def rebalanceInsert(self, currentNode):
# 插入節點,重新調整樹至平衡
if currentNode.bf < 0: # 左旋
if currentNode.right.bf > 0:
self.rightRotate(currentNode.right)
self.leftRotate(currentNode)
else:
self.leftRotate(currentNode)
elif currentNode.bf > 0:
if currentNode.left.bf < 0:
self.leftRotate(currentNode.left)
self.rightRotate(currentNode)
else:
self.rightRotate(currentNode)
def rebalanceDelete(self, currentNode):
# 刪除節點,重新調整
nextNode = None
if currentNode.bf < 0: # 右樹過深,需要左旋調整
if currentNode.right.bf < 0:
self.leftRotate(currentNode)
nextNode = currentNode.parent
elif currentNode.right.bf == 0: # 若當前節點的右孩子平衡因子為0,則旋轉後以當前節點為根的子樹高度不變,故結束向上更新
self.leftRotate(currentNode)
else:
self.rightRotate(currentNode.right)
self.leftRotate(currentNode)
nextNode = currentNode.parent
else:
if currentNode.left.bf > 0:
self.rightRotate(currentNode)
nextNode = currentNode.parent
elif currentNode.left.bf == 0:
self.rightRotate(currentNode)
else:
self.leftRotate(currentNode.left)
self.rightRotate(currentNode)
nextNode = currentNode.parent
return nextNode
def updateInsertBF(self, currentNode):
# 插入節點後,更新平衡因子
if abs(currentNode.bf) > 1: # 樹失衡則進行旋轉調節
self.rebalanceInsert(currentNode)
return
if currentNode.parent is not None:
if currentNode.isLeft():
currentNode.parent.bf += 1
else:
currentNode.parent.bf -= 1
if currentNode.parent.bf != 0:
self.updateInsertBF(currentNode.parent)
def updateDeleteBF(self, currentNode):
# 刪除節點後更新平衡因子
if abs(currentNode.bf) > 1: # 樹失衡則進行旋轉調節
currentNode = self.rebalanceDelete(currentNode)
if currentNode is None:
return
if currentNode.parent is not None:
oldBF = currentNode.parent.bf
if currentNode.isLeft():
currentNode.parent.bf -= 1
else:
currentNode.parent.bf += 1
if oldBF != 0: # 父節點為根的子樹,原本不平衡,那麼刪除節點後其子樹高度必改變,故需要繼續向上更新
self.updateDeleteBF(currentNode.parent)
紅黑樹
紅黑樹的每個節點都有一個顏色(color)屬性,根節點以及葉子節點(key為空)均為黑色,而其他節點滿足如下兩條規則:
- rule1: 父子節點不能同為紅色,但可以同為黑色。
- rule2: 某個節點到其子樹任意葉節點的路徑上包含的黑色節點個數(稱為black height)相同。
節點顏色的更新要以這兩條準則為基礎。
平衡
插入或刪除後,如果樹中的顏色違反了上面兩條規則,則需要變更節點顏色,必要時需要旋轉。過程比較複雜下面針對插入與刪除的不同情形細講。
插入
- 搜尋到待插入葉節點q
- 賦予q待插入key值,標記為紅色,並新增空L、R孩子葉節點
- 以q為起點自底向上更新color:
- q是根節點,將其標記為黑色,break
- q的父節點P是黑色,break
- q的父節點P是紅色(違反rule1,需調整):
- q的叔叔節點S是紅色,將P與S變更為黑色, break
- S是黑色:
- q、P以及q的爺爺節點G排列滿足’/‘或’’,則以(G, P)為基準右旋或左旋,G變紅色,P變黑色, break
- q、P以及q的爺爺節點G排列滿足’>‘或’<’,則以(P, q)為基準右旋或左旋, 轉上1
刪除
- 搜尋到待刪除節點的位置
- 找到後繼節點M,將其key複製給待刪除節點
- 以M為起點自底向上更新color:
- M是紅色,其左右孩子L和R必為空,直接刪除M,break
- M是黑色:
- L和R若存在一個非空則必為紅色(參考rule2),刪除M,非空孩子接替其位置,並繼承M的顏色, break
- L和R都是空:
- M的兄弟節點S是紅色,S變黑, M的父節點P變紅,再以(P,S)為基準進行旋轉,轉下2
- S是黑色:
- S的孩子全部為空:若P為黑色,則將S變紅; 反之將P、S的顏色交換, break
- S的右孩子R是紅色,左孩子任意,S變紅,P及R變黑,再以(P,S)為基準進行旋轉,break
- S的左孩子L是紅色,L變黑,S變紅,再以(S,L)為基準進行旋轉,轉上2
# 插入和刪除節點後調整bf的參考程式碼
def updateInsertColor(self, currentNode):
# 插入節點後更新顏色
if currentNode.isRoot():
currentNode.color = 0
return
elif not currentNode.parent.color: # 父節點為黑色,不用更新
return
else:
uncle = currentNode.parent.getSibling()
if uncle.color: # case1: 存在叔叔節點且顏色是紅色
uncle.color = 0
currentNode.parent.color = 0
uncle.parent.color = 1 # 將爺爺節點顏色變更為紅色
self.updateInsertColor(uncle.parent)
else:
if currentNode.parent.isLeft():
if currentNode.isLeft(): # case2:當前節點、其父節點及爺爺節點位於一條直線上
currentNode.parent.parent.color = 1
currentNode.parent.color = 0
self.rightRotate(currentNode.parent.parent)
else: # case3: 當前節點、其父節點及爺爺節點不在一條直線上,先轉換到一條直線上,
self.leftRotate(currentNode.parent)
self.updateInsertColor(currentNode.left)
else:
if currentNode.isRight():
currentNode.parent.parent.color = 1
currentNode.parent.color = 0
self.leftRotate(currentNode.parent.parent)
else:
self.rightRotate(currentNode.parent)
self.updateInsertColor(currentNode.right)
def updateDeleteColor(self, currrentNode):
# 刪除節點後更新顏色
if currrentNode.color: # case1:M是紅色,直接刪除
return
else:
if currrentNode.left.key: # case2: M是黑色,子節點有個非空節點就,變為紅色
currrentNode.left.color = 0
return
elif currrentNode.right.key:
currrentNode.right.color = 0
return
else: # case3: M是黑色,子節點都是空節點(最複雜的情形)
S = currrentNode.getSibling()
if S.color: # 1: S是紅色,將其變更為黑色
currrentNode.parent.color = 1
S.color = 0
if S.isLeft():
self.rightRotate(currrentNode.parent)
else:
self.leftRotate(currrentNode.parent)
if not (S.left.color or S.right.color): # S沒有紅色孩子
if currrentNode.parent.color:
currrentNode.parent.color, S.color = S.color, currrentNode.parent.color
else:
S.color = 1
elif S.right.color: # 右孩子為紅色,左孩子顏色任意
if S.isRight(): # S的紅色節點滿足'\'
S.right.color = 0
S.color = currrentNode.parent.color
currrentNode.parent.color = 0
self.leftRotate(currrentNode.parent)
else: # S的紅色節點滿足'>'
S.left.color = 0
S.color = 1
self.rightRotate
相關推薦
二叉搜尋樹、AVL以及紅黑自平衡二叉搜尋樹
本文符號意義
q: 插入節點
M: 實際刪除節點(後繼節點)
P: 當前節點的父節點
G: 當前節點的爺爺節點
S: 當前節點的叔叔節點(即父節點的兄弟節點)
L: 左孩子
R: 右孩子
非空:節點的key值不為空
二叉搜尋樹
二叉搜尋樹的基本操作有search
二叉查詢樹(BST) | 平衡二叉查詢樹(AVL) | 紅黑樹(RBT)
二叉查詢樹(BST)
特點:對任意節點而言,左子樹(若存在)的值總是小於本身,而右子(若存在)的值總是大於本身。
查詢:從根開始,小的往左找,大的往右找,不大不小的就是這個節點了;
插入:從根開始,小的往左,大的往右,直到葉子,就插入,
時間複雜度期望為
資料結構中常見的樹(BST二叉搜尋樹、AVL平衡二叉樹、RBT紅黑樹、B-樹、B+樹、B*樹)
BST樹
即二叉搜尋樹:
1.所有非葉子結點至多擁有兩個兒子(Left和Right);
2.所有結點儲存一個關鍵字;
3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹;
如:
紅黑樹、自平衡二叉樹、AVL樹、B樹的比較
1. 紅黑樹和自平衡二叉(查詢)樹區別
紅黑樹放棄了追求完全平衡,追求大致平衡,在與平衡二叉樹的時間複雜度相差不大的情況下,保證每次插入最多隻需要三次旋轉就能達到平衡,實現起來也更為簡單。
平衡二叉樹追求絕對平衡,條件比較苛刻,實現起來比較麻煩,每次插入新節點之後需要旋轉的
淺談二叉查詢樹、AVL樹、紅黑樹、B樹、B+樹的原理及應用
一、二叉查詢樹
1、簡介
二叉查詢樹也稱為有序二叉查詢樹,滿足二叉查詢樹的一般性質,是指一棵空樹具有如下性質:
任意節點左子樹不為空,則左子樹的值均小於根節點的值.
任意節點右子樹不為空,則右子樹的值均大於於根節點的值.
任意節點的左右子樹也分別是二叉查
二叉排序樹、紅黑樹、AVL樹最簡單的理解
前言
[為什麼寫這篇]
之前在知乎上看過一個提問:為什麼紅黑樹比AVL樹用的場景更為廣泛?其實,這兩者應用場景都挺廣泛的。紅黑樹在 STL 和 Linux 都有一定的運用。而AVL樹也在 Windows程序地址空間管理 中得到了使用。既然紅黑樹和AVL樹這麼
資料結構中的樹(二叉樹、二叉搜尋樹、AVL樹)
> [資料結構動圖展示網站](https://www.cs.usfca.edu/~galles/visualization/Algorithms.html)
### 樹的概念
樹(英語:tree)是一種抽象資料型別(ADT)或是實作這種抽象資料型別的資料結構,用來模擬具有樹狀結構性質的資料集合。它是由n(
AVL樹——自平衡二叉搜尋樹
概念
AVL(Adelson-Velskii and Landis)樹得名於它的發明者 G.M. Adelson-Velsky 和 E.M. Landis,他們在 1962 年的論文《An algorithm for the organization of information》中
二叉查詢樹(BST),平衡二叉查詢樹(AVL),紅黑樹(RBT),B~/B+樹(B-tree)的比較
http://www.iteye.com/topic/614070
此少俠總結的特棒,直接收藏了。
我們這個專題介紹的動態查詢樹主要有: 二叉查詢樹(BST),平衡二叉查詢樹(AVL),紅黑樹(RBT),B~/B+樹(B-tree)。這四種樹都具備下面幾個優勢:
(1) 都
Javascript之資料結構與演算法的自平衡二叉搜尋樹(AVL)實現
Javascript之資料結構與演算法的自平衡二叉搜尋樹(AVL)實現
簡介
程式碼實現
簡介
AVL樹是一種自平衡樹。新增或移除節點時, AVL樹會嘗試自平衡。任意一個節點(不論深 度)的左子樹和右子樹高度最多相差1
數據結構(5) 第五天 快速排序、歸並排序、堆排序、高級數據結構介紹:平衡二叉樹、紅黑樹、B/B+樹
平衡二叉樹 let b+樹 堆排 mark 9.png 思想 incr 相等 01 上次課程回顧
希爾排序 又叫減少增量排序
increasement = increasement / 3 + 1
02 快速排序思想
思想: 分治法 + 挖坑
B樹、B+樹、紅黑樹、AVL樹
付出 而不是 通過 找到 磁盤讀寫 三次 復雜度 節點 span 定義及概念
B樹
二叉樹的深度較大,在查找時會造成I/O讀寫頻繁,查詢效率低下,所以引入了多叉樹的結構,也就是B樹。階為M的B樹具有以下性質:
1、根節點在不為葉子節點的情況下兒子數為 2 ~ M2、除根結
AVL樹-自平衡二叉查詢樹(Java實現)
在電腦科學中,AVL樹是最先發明的自平衡二叉查詢樹。AVL樹得名於它的發明者 G.M. Adelson-Velsky 和 E.M. Landis,他們在 1962 年的論文 "An algorithm for the organization of inform
紅黑樹、AVL樹、B樹的比較
AVL, 紅黑樹,B樹
前段日子在研究著這幾中樹的優劣
首先我們來談談AVL樹,AVL樹是一棵平衡的二叉查詢樹。它的平衡因子為-1,1,0不平衡達到2就會將樹進行平衡化。 AVL對數字的分佈要求比較高,比如說是隨機數,有人曾經做過計算,在利用AVL樹與紅黑樹進
8.紅黑樹的定義,紅黑樹的效能分析和與平衡二叉樹的比較
平衡二叉樹
平衡二叉樹或者是一顆空的二叉排序樹,或是具有下列性質的二叉排序樹:
根節點的左子樹和右子樹的深度最多相差1根節點的左子樹和右子樹都是平衡二叉樹
平衡因子
平衡因子是該節點的左子樹的深度與右子樹的深度之差。
最小不平衡子樹
在平衡二叉樹的構造過程中,以距離插入節
Jmeter之處理session、cookie以及如何做關聯(二)Jmeter如何提取響應頭部的JSESSIONID
就是利用Jmeter做介面測試的時候,如何提取頭部的JSESSIONID然後傳遞到下一個請求,繼續完成當前使用者的請求。其實,關於這個問題有三種種解決方法:3)如果響應頭裡面有這個JSESSIONID,我們可以通過新增cookie來解決這個問題,今天這篇部落格,我們重點講解第
平衡樹、AVL樹
平衡樹
平衡樹有AVL樹、紅黑樹、2-3樹、2-3-4樹
AVL樹
AVL樹是最早的一種平衡樹,它以發明者的名字命名。
特徵
在AVL樹中節點的左子樹和右子樹的高度差不會大於1
實現
在AVL樹中每個節點都儲存著一個額外的資料,它的左子樹和右子樹的高度差,這個差值不能大於1。插入一個元素後,
資料結構之自平衡二叉查詢樹(1)
今天開始,我們再來認識一個新的二叉樹,稱為自平衡二叉查詢樹。AVL樹是最先發明的自平衡二叉查詢樹。
AVL樹的特點是:對於樹中的任何節點,節點的左右子樹的高度差距最大為1,所以AVL樹也稱為高度平衡樹。AVL樹得名於它的發明者G.M. Adelson-Velsky和E.M.
B樹,B-樹,B*樹,B+和紅黑樹的區別
B樹
即二叉搜尋樹:
1.所有非葉子結點至多擁有兩個兒子(Left和Right);
2.所有結點儲存一個關鍵字;
3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹;
如:
為什麼要有紅黑樹?什麼是紅黑樹?畫了20張圖,看完這篇你就明白了
為什麼要有紅黑樹
想必大家對二叉樹搜尋樹都不陌生,首先看一下二叉搜尋樹的定義:
二叉搜尋樹(Binary Search Tree),或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的