红黑树
1.五大性质
- 每个节点要么是黑色,要么是红色
- 根节点为黑色
- 每个叶子节点(
null
)为黑色 - 每个红色的两个子节点一定是黑色
- 任意一节点到每个叶子节点的路径上黑节点的数目相同。
红黑树并不是一个完美的平衡二叉树,根据节点五可知到子节点黑色节点数目相同。所以我们称红黑树这种平衡为黑色完美平衡
2.基本操作
- 左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
-
右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
-
变色:结点的颜色由红变黑或由黑变红。
正文
红黑树查找
红黑树查找,与二叉搜索树相同。
红黑树插入
若插入位置父节点为黑色,则不用做任何处理。(同二分搜索树)
当插入新元素破坏了红黑树的性质
情景分析
1. 当根节点为空时。直接插入,根据性质2:根节点为黑色,变为黑色。
2. 插入节点已存在时。将新元素的值传给对应节点
3. 若插入位置父节点为黑色,则不用做任何处理。(同二分搜索树)。不影响黑色完美平衡。
4. 插入节点父节点为红色。
4.1 叔叔节点存在并且为红色节点
>红节点的子节点为黑色----》**爷爷节点为黑色**
>1.将父节点和叔叔节点变为黑色
>2.将爷爷节点变为红色
>3.将爷爷节点变为当前节点进行后续处理。
4.2叔叔节点不存在或为黑色节点,并父节点是爷爷节点的左子节点。
-
插入节点为父节点的左子节点
处理: > 1. 变色 ,夫系欸但为黑色,爷爷节点变为红色 > 2. 进行右旋
2. 插入节点为父节点的右节点
处理:
- 以p节点进行左旋
- 进行变色处理—》父节点变为红色
- 以爷爷节点进行右旋 ,若I节点有右节点则给爷爷节点的左节点
4.3叔叔节点不存在或为黑色节点,并父节点是爷爷节点的右子节点。
- 当插入节点为父节点的右节点时(对之前的操作相反)
变色之后进行左旋
2. 当插入节点为父节点的左节点时
先右旋,变色再以爷爷节点左旋
代码
package RBTREE;
import com.sun.prism.paint.Gradient;
import com.sun.xml.internal.bind.v2.model.core.ID;
public class RBTree<K extends Comparable<K>, V> {
private static final boolean RED = true; //红
private static final boolean BLACK = false; //黑
//节点 的内部类
static class RBNode<K extends Comparable<K>, V> {
private RBNode parent;
private RBNode left;
private RBNode right;
private boolean color;
private K key;
private V value;
/**
* 内部类无参构造和有参构造
*/
public RBNode() {
}
public RBNode(RBNode parent, RBNode left, RBNode right, boolean color, K key, V value) {
super();
this.parent = parent;
this.left = left;
this.right = right;
this.color = color;
this.key = key;
this.value = value;
}
public RBNode getParent() {
return parent;
}
public void setParent(RBNode parent) {
this.parent = parent;
}
public RBNode getLeft() {
return left;
}
public void setLeft(RBNode left) {
this.left = left;
}
public RBNode getRight() {
return right;
}
public void setRight(RBNode right) {
this.right = right;
}
public boolean isColor() {
return color;
}
public void setColor(boolean color) {
this.color = color;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
@Override
public String toString() {
return value.toString();
}
}
private RBNode root;
private int size;
/**
* 初始化红黑树
*
* @param root
* @param size
*/
public RBTree() {
this.root = null;
this.size = 0;
}
//辅助方法
/**
* 返回节点的父节点
*
* @param node
* @return
*/
private RBNode parentof(RBNode node) {
if (node != null && node != root) {
return node.parent;
}
return null;
}
/**
* 判断节点是否为红色
*
* @param node
* @return
*/
private boolean isRed(RBNode node) {
if (node != null) {
return node.color == RED;
}
return false;
}
/**
* 判断节点是否为黑色
*
* @param node
* @return
*/
private boolean isBlack(RBNode node) {
if (node != null) {
return node.color == BLACK;
}
return false;
}
/**
* 设置节点为红色
*
* @param node
*/
private void setRed(RBNode node) {
if (node != null) {
node.color = RED;
}
}
/**
* 设置节点为黑色
*
* @param node
*/
private void setBlack(RBNode node) {
if (node != null) {
node.color = BLACK;
}
}
/**
* 左旋
*/
private void leftRotate(RBNode x) {
RBNode y = x.right;
x.right = y.left;
if (y.left != null) {
y.left.parent = x;
}
if (x.parent != null) {
y.parent = x.parent;
if (x == x.parent.left) {
x.parent.left = y;
} else {
x.parent.right = y;
}
}else {
this.root = y;
this.root.parent =null;
}
x.parent = y;
y.left = x;
}
/**
* 右旋
* @param y
*/
private void rightRotate(RBNode y){
RBNode x =y.left;
y.left =x.right;
if (x.right !=null) {
x.right.parent =y;
}
if (y.parent !=null) {
x.parent =y.parent;
if (y==y.parent.left) {
y.parent.left =x;
}else{
y.parent.right = x;
}
}else {
this.root=x;
this.root.parent =null;
}
y.parent =x;
x.right=y;
}
/**
* 中序遍历
*/
public void inOrderprint() {
inOrderprint(this.root);
}
private void inOrderprint(RBNode node) {
if (node != null) {
inOrderprint(node.left);
System.out.println("Key:" + node.key + "......value:" + node.value + "...color:" + node.color);
inOrderprint(node.right);
}
}
/**
* 插入
*/
public void insert(K key,V value){
RBNode node =new RBNode();
node.setKey(key);
node.setValue(value);
node.setColor(true);
insert(node);
}
private void insert(RBNode node){
RBNode partent =null;
RBNode x=this.root;
while(x != null){
partent = x;
int cmp =node.key.compareTo(x.key);
if (cmp>0) {
x=x.right;
}else if (cmp < 0) {
x=x.left;
}else {
x.setValue(node.getValue());
return;
}
}
node.parent=partent;
if (partent != null) {
int cmp=node.key.compareTo(partent.key);
if (cmp>0) {
partent.right=node;
}else{
partent.left=node;
}
}else {
this.root=node;
}
size++;
//修复红黑树平衡的方法
insertFixUp(node);
}
private void insertFixUp(RBNode node){
this.root.setColor(BLACK);
RBNode parent =parentof(node);
RBNode gparent=parentof(parent);
//插入结点的父节点为红色
if (parent != null && isRed(parent)) {
//如果父节点时红色 那么一定存在爷爷节点。
//定义一个叔叔节点
RBNode uncle = null;
//父节点是爷爷节点的左孩子
if (parent == gparent.left) {
uncle = gparent.right;
// 叔叔节点存在,并且为红色(叔-父双红)
if(uncle !=null && isRed(uncle)){
setBlack(parent);
setBlack(uncle);
setRed(gparent);
insertFixUp(gparent);
return;
}
// 叔叔节点不存在或为黑色
if (uncle == null || isBlack(uncle)) {
// LL双红 父节点变黑 爷爷节点变红 以爷爷节点右旋
if(node == parent.left){
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
return;
}
//插入节点是左孩子 则先父节点左旋变为 LL双红 再以父节点进行处理
if (node == parent.right) {
leftRotate(parent);
insertFixUp(parent);
return;
}
}
}else {
// 父节点是爷爷节点的右孩子
uncle =gparent.left;
// 护树节点存在,并且为红色、
if (uncle != null && isRed(uncle)) {
// 父节点和叔叔节点变黑 爷爷节点变红 再处理
setBlack(uncle);
setBlack(parent);
setRed(gparent);
insertFixUp(gparent);
return;
}
// 叔叔节点不存在或者为黑色
if (uncle == null || isBlack(uncle)) {
//插入节点是父节点的右孩子 父节点变黑 爷节点变红 左旋
if (node == parent.right) {
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
return;
}
//插入节点是父节点的左孩子
//先右旋 得到RR双红 父节点做处理
if (node == parent.left) {
rightRotate(parent);
insertFixUp(parent);
return;
}
}
}
}
}
/**
* (递归实现)查找"红黑树x"中键值为key的节点
*/
public RBNode search(K key) {
return search(root, key);
}
private RBNode search(RBNode x, K key) {
if (x == null) {
return x;
}
int cmp = x.key.compareTo(key);
if (cmp < 0) {
return search(x.left, key);
} else if (cmp > 0) {
return search(x.right, key);
} else {
return x;
}
}
}
package RBTREE;
public class test {
private static final int[] a = {10, 40, 30, 60, 90, 70, 20, 50, 80, 75,88,87,98,23,44,55,66,77,1,2,3,4,35,36,33,32,31,89,81,82,83,84,85,86,79};
private static final boolean mDebugInsert = false; // "插入"动作的检测开关(false,关闭;true,打开)
private static final boolean mDebugDelete = false; // "删除"动作的检测开关(false,关闭;true,打开)
public static void main(String[] args) {
int i, ilen = a.length;
RBTree<Integer,Integer> tree = new RBTree<>();
System.out.printf("== 原始数据: ");
for (i = 0; i < ilen; i++) {
System.out.printf("%d ", a[i]);
}
System.out.printf("\n");
for (i = 0; i < ilen; i++) {
tree.insert(a[i], i+1);
// 设置mDebugInsert=true,测试"添加函数"
if (mDebugInsert) {
System.out.printf("== 添加节点: %d\n", a[i]);
System.out.printf("== 树的详细信息: \n");
// S
System.out.printf("\n");
}
}
System.out.printf("\n== 中序遍历: ");
tree.inOrderprint();
}
}
问题
- 为什么再hashmap中要用红黑树,而不是二分搜索树或咋是AVL平衡树
时间复杂度:
二分搜索树 : logn 当插入序列有序时 时间复杂度为n
AVL平衡树 :long 但是没插入一个树都会改变树形结构 频繁改变会对系统 资源造成浪费
红黑树:由性质:红节点的子节点必须为黑节点 与性质:任意节点的值到叶子节点的黑节点数相等
可知不看红节点,红黑树是一颗完美AVL平衡树。时间复杂度为logn。
而高度差最大相差1倍 故而红黑树的时间复杂度为2logn。(logn)
红黑树容差率更大,更加节省系统资源,故而使用的是红黑树,而不是二分搜索树或AVL平衡树