紅黑樹的概念:
紅黑數,是一種二叉搜索樹,但在每一個節點上增加一個存儲位表示節點的顏色,可以是Red或Black。通過對任意一條從根到葉子的路徑上各個節點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出兩倍,因而是接近平衡的。
紅黑樹的性質(規則):
1.每個節點不是紅色就是黑色
2.根節點是黑色
3.如果一個節點是紅色的,則它的兩個孩子節點是黑色的(紅色不能挨紅色)
4.對於每個節點,從該節點到其所有後代葉節點的簡單路徑上,均包含相同數目的黑色節點
5.每個葉子節點都是黑色的(此處的葉子節點指的是空節點)
滿足以上性質:紅黑樹就能保證:其最長路徑中節點個數不會超過最短路徑個數的兩倍。
平衡樹的目的:只是讓二叉樹的高度儘可能的低,不要退化成單支數即可。
插入操作:
1.按照普通搜索樹的方式進行插入
2.只會插入紅色的節點(有意識的破壞紅紅不能相鄰的規則)重點
3.檢查紅色是否是相鄰的?
----1.如果我的父親是黑色的,就沒有破壞規則。插入結束
----2.如果我的父親是紅色的,紅紅相鄰了,我們需要修復。
提問:我的祖父是什麼顏色?
一定是黑色的,因爲已經確定父親是紅色的,如果祖父不爲紅,則表示之前就已經觸發了紅紅相鄰。
4.檢查根是否是黑色的。
一組隨機數的調整:
75,147,166,97,12,154,26,84,83,100,78,170,37,58,128,146,143,183,91
紅黑樹大的規則:
- 非紅即黑
- 收尾皆黑
- 紅不相鄰
- 黑色同數
L(最長的路徑)<= 最短路徑*2
近似於平衡樹,可以降低 插入/查找/刪除的時間複雜度(log(N))
怎麼插入?代碼如何實現?
public class Node {
enum Color{
Red,
Black
}
Node left;
Node right;
Node parent;
int key;
Color color=Color.Red;
public Node (int key){
this.key = key;
}
}
public class Tree {
Node root;
void insert(int key){
if (root==null){
root=new Node(key);
root.color =Node.Color.Black;
return;
}
//按照搜索樹的方法插入
Node parent = null;
Node cur=root;
while (cur!=null){
if (key==cur.key){
System.out.println("插入重複:"+key);
return;
}else if (key<cur.key){
parent=cur;
cur=cur.left;
}else {
parent=cur;
cur = cur.right;
}
}
if (key<parent.key){
cur=parent.left=new Node(key);
cur.parent =parent;
}else {
cur=parent.right = new Node(key);
cur.parent = parent;
}
while (parent!=null&&parent.color==Node.Color.Red&&parent.parent!=null){
Node grandpa=parent.parent;
if (parent==grandpa.left){
//左
Node uncle=grandpa.right;
if (uncle!=null&&uncle.color==Node.Color.Red){
grandpa.color=Node.Color.Red;
parent.color=uncle.color=Node.Color.Black;
cur=grandpa;
parent=grandpa.parent;
}else {
//此時uncle爲null或者color爲黑色
//左右的情況
if (cur==parent.right){
leftRotate(parent);
Node tmp=cur;
cur=parent;
parent=tmp;
}
//走到這不管有沒有經歷過左旋 都是左左的情況
rightRotate(grandpa);
grandpa.color = Node.Color.Red;
parent.color=Node.Color.Black;
//當我們的目前子樹的根節點爲黑的時候就不需要在做調整了
break;
}
}else {
//右
Node uncle=grandpa.left;
if (uncle!=null&&uncle.color==Node.Color.Red){
grandpa.color=Node.Color.Red;
parent.color=uncle.color=Node.Color.Black;
cur=grandpa;
parent=grandpa.parent;
}else {
//此時uncle爲null或者color爲黑色
//右左的情況
if (cur==parent.left){
rightRotate(parent);
Node tmp=cur;
cur=parent;
parent=tmp;
}
//走到這不管有沒有經歷過左旋 都是左左的情況
leftRotate(grandpa);
grandpa.color = Node.Color.Red;
parent.color=Node.Color.Black;
//當我們的目前子樹的根節點爲黑的時候就不需要在做調整了
break;
}
}
}
root.color=Node.Color.Black;
}
private void rightRotate(Node parent) {
Node cur=parent.left;
Node rightOfCur=cur.right;
Node grandpa=parent.parent;
cur.right=parent;
cur.parent=grandpa;
parent.parent=cur;
parent.left=rightOfCur;
if (grandpa!=null){
if (parent==grandpa.left){
grandpa.left =cur;
}else {
grandpa.right=cur;
}
}else {
root=cur;
}
if (rightOfCur!=null){
rightOfCur.parent=parent;
}
}
private void leftRotate(Node parent) {
Node cur=parent.right;
Node leftOfCur=cur.left;
Node grandpa=parent.parent;
parent.parent=cur;
parent.right=leftOfCur;
cur.parent=grandpa;
cur.left=parent;
if (grandpa!=null){
if (parent==grandpa.left){
grandpa.left=cur;
}else {
grandpa.right=cur;
}
}else {
root=cur;
}
if (leftOfCur!=null){
leftOfCur.parent=parent;
}
}
}
public class Test {
public static void main(String[] args) {
Random random=new Random(20200219);
Tree tree=new Tree();
for (int i = 0; i < 20; i++) {
int key=random.nextInt(200);
System.out.println(key);
tree.insert(key);
}
System.out.println("插入成功");
}
}
B-樹:多叉搜索樹
多叉搜索樹:
1.可以保存多個key和多個孩子
2.節點中保存的key有序
3.規定一個值M
4.如果B-樹是M階樹,含義:
- 除根之外,節點中最多的有M-1個key
- 除根之外,節點中最多有M個child
5 .無論何時,child總比key多一個。
查找:
每個節點中有一組Key(有序)
遍歷查找即可(一般來說數組的長度有限-1024)
如果找到了,返回節點和下標
如果沒有找到,應該會對應一個孩子的位置
繼續去孩子節點中找
如果孩子是null,表示key不在樹中
插入操作:
我們進行試着以2-3樹插入一組數據 { 97,38,75,65,93,29,25,36,3,25,67,86,80,36,89,62,31,6,4,77 }
B-樹的概念:
- 根節點至少有兩個孩子(根中至少有個key)
- 每個非根節點至少有M/2(上取整)個孩子,至多有M個孩子
分裂時自然保證了
index是中間位置的key往上走,
左部分留在當前節點,
右部分創建新節點,搬家
分家後,節點中至少還有一半個孩子 - 每個非根節點至少有M/2-1(上取整)個關鍵字,至多有M-1個關鍵字,並且以升序排列
- key[ i ]和key[ i+1 ]之間的孩子節點的值介於key[ i ],key[ i+1 ]之間
- 所有的葉子節點都在同一層
插入只往葉子節點插,樹是往上生長的。
插入的規則:只往葉子裏插入,根據剛纔的規則,找到和是的葉子
- 判斷是否滿足key < M-1 的規則,如果滿足,插入完畢
- 如果不滿足,開始準備分裂
分裂:
- 創建一個新的節點
- 把midindex往右的所有key連同child一起搬到新的節點 rightPart
- midIndex往左的所有key連同child留在原節點不動
- 把midIndex所在位置的key按照方式插入到父節點上去,同時key右邊指向rightPart
如果父節點爲null,創建新節點 [ 0 ]是原節點[ 1 ]是rightPart
插入時,按照插入排序當時的方式key往後搬,記着把child也往後搬
現在再次以6-7樹我們再次插入一次元素:{ 97,38,75,65,93,29,25,36,3,25,67,86,80,36,89,62,31,6,4,77 }
M階 B-樹:每個節點中最多能有M-1個key
B-樹 的第一層:M-1個key
B-樹 的第二層: M*(M-1)個key
第三次估算有: M^3個key, 第四次估算有: M^4個key
按這樣增長,可存key個數特別大的。