红黑树的概念:
红黑数,是一种二叉搜索树,但在每一个节点上增加一个存储位表示节点的颜色,可以是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个数特别大的。