基本概念
紅黑樹是一種自平衡的二叉搜索樹。樹中的每一個結點的顏色不是黑色就是紅色。
紅黑樹可以視為一棵擴充二叉樹,用外部結點表示空指針。
二叉樹的存儲結構是使用二叉鏈表或者三叉鏈表來表示的,每個結點都存在指向該節點左右孩子的指針。但是葉子結點是沒有孩子結點的,所以將葉子結點中指向孩子節點的指針置為NULL,NULL為空指針。在紅黑樹中,使用外部結點表示空指針,就是使用外部結點表示NULL,這個結點是在物理內存上是不存在的,是想象出來以便於理解的。
紅黑樹的特性
1.根節點和所有的外部結點都是黑色的
2.從根節點到外部結點的圖中沒有連續兩個結點的顏色是紅色
3.所有從根到外部結點的路徑上都有相同數目的黑色結點
結點的階:從紅黑樹中任一結點x出發(不包括結點x),到達一個外部結點的任一路徑上的黑結點個數叫做結點x的黑高度,亦稱結點的階,記作bh(x),紅黑樹的高度定義為其根節點的黑高度
變換規則
1.顏色變換
2.左旋
左旋又分為兩種情況,
(1)我們操作的結點E是整棵樹的根節點,那么左旋實現為下面步驟
a. 將結點s的左子樹變化為E的右子樹。也就是將E.rightchild值修改為S.leftchild后將S.leftchild置為NULL,將S.leftchild.parent置為結點E的指針。
b.將結點E的父結點指針parent指向結點S
c.將s結點的父結點指針parent置為NULL。
(2)我們操作的結點E有父結點,那么左旋實現為下面步驟
a. 將結點s的左子樹變化為E的右子樹,也就是將E.rightchild值修改為S.leftchild后將S.leftchild置為NULL,將S.leftchild.parent置為結點E的指針
b.聲明一個輔助指針變量,指向結點E,即存儲的是E結點的指針。將結點S的父結點指針parent修改為結點E的父指針parent的值
c.通過輔助指針變量,將結點E的父指針parent修改指向結點S。
3.右旋
右旋同樣分為兩種情況,與左旋情況類似,故實際操作參考左旋。
插入
首先使用二叉搜索樹的插入算法將一個元素插入到紅黑樹中,該元素作為新的葉子結點插入到某一外部結點位置,才插入結點過程中需要為新元素染色。
注意:上述描述中一個很重要的點是,在插入元素時,是將元素作為葉子結點插入的,插入到原紅黑樹的外部結點。
插入結點染色情況
1.如果插入前是空樹,那么新插入的元素就會成為根節點,根據特征,需要將根節點染成黑色。
2.如果紅黑樹非空,那么在紅黑樹中插入新的結點時,所有的點都默認是紅色結點。
插入結點后調整和平衡過程
1.變顏色的情況: 當前結點的父親是紅色,且它的祖父結點的另一個結點(也就是叔叔結點)也是紅色:
(1)把父結點設為黑色
(2)把叔叔結點也設為黑色
(3)把祖父結點,也就是父親的父親設置為紅色(爺爺結點)
(4)把指針定義到祖父結點設為當前要操作的結點(爺爺結點)。分析點的變換規則,進行變換。
2.左旋:當前父結點是紅色,叔叔結點是黑色的時候,且當前的結點時右子樹,則進行左旋。左旋過程不需要進行顏色變換。
3.右旋:當前父結點時紅色,叔叔結點是黑色的時候,且當前的結點是左子樹,則進行右旋。右旋過程中需要進行顏色變換,具體右旋過程如下。
(1)把父結點變為黑色
(2)把祖父結點變為紅色(爺爺結點)
(3)以祖父結點進行右旋(爺爺結點)
舉例
為什么紅黑樹不能用於大型索引的快速查詢
紅黑樹從根本上來說是一棵二叉查找樹,對於千萬級的大型數據,那么紅黑樹的深度將會有幾十層甚至更多。我們以一棵只有三層的紅黑樹來分析。
原因1:這是一棵三層的紅黑樹。在數據存儲時,數據庫是存儲在磁盤中的。我們知道,在計算機的組成中,讀取速度的關系:寄存器 > 內存 > 磁盤。
假設我們在紅黑樹種查找數據4的結點,那么第一次就需要把數據從磁盤中讀取出來,查找根節點5處存儲的是不是數據4。這樣就完成了一次讀取磁盤。
很顯然,讀取根節點5時並沒有讀取到數據4。通過二叉樹的關系,我們需要沿着結點5的左子數進行,就需要再一次把數據從磁盤中讀取出來,查找到結點3中的數據。這樣就完成了磁盤的第二次讀取。
結點3中也沒有讀取到數據4,通過判斷,需要沿着結點3的右子樹進行。這時就需要再次把數據從磁盤中讀取出來,查找到數據4,此時就又完成第三次讀取磁盤。
當紅黑樹存儲的是大型數據,紅黑樹的深度將會達到幾十層甚至更深,那么磁盤的讀取次數就會很多。
原因2:在磁盤存儲時,現在的計算機的1頁空間可以存儲16k. 存儲的一個int型卻只有8個字節,讀取之后還要在讀取的數據中去查找。這樣就造成了讀取的浪費
綜上所述:紅黑樹不能用作大型索引的根本原因為:1.讀取磁盤的次數太多。2.讀取浪費太多
但是紅黑樹為什么可以用作hashmap的查找里面呢
因為這是一個純粹的內存問題,不存在索引問題。