怎样用word做网站/网页推广怎么做
一、背景
二叉搜索树具体较高的运行效率,其查找的时间复杂度为log(N)。但是在极端情况下,假如插入一组从大到小的数据,如图所示,则其查找的时间复杂度变为N。
红黑树是在二叉搜索树的基础上,加入红黑性质,使其成为一颗平衡二叉树,从而保证其查找的时间复杂度为log(N),被广泛应用于java的集合框架中,如TreeMap,TreeSet和ConcurrentHashMap。
二、红黑性质
一颗红黑树是满足下面红黑性质的二叉搜索树:
1)每个节点非黑即红。
2)根节点是黑色的。
3)空的叶子节点默认为黑色。
4)父节点是红色,其子节点必须为黑色。
5)从任意节点到其叶节点的每条路径上,都含有相同个数的黑色节点。
三、 基本操作
3.1. 左旋
3.2. 右旋
四、 插入
将一个新的节点按照下面顺序插入红黑树中:
1)将新节点按照二叉搜索树的插入方式插入
2)将新节点着为红色
如果插入的是节点默认为是黑色,不一定会引发冲突;反过来插入节点默认是红色也不一定只会违反“红色节点的子节点必须是黑色的”这一个条件;只是相对来说默认为红色冲突的可能较少。
3)将整棵树进行修正,使其符合红黑性质
因为插入的节点是红色节点,所以修正过程主要关注“红色节点的子节点必须是黑色的”这一特性即可,具体修正过程见4.1-4.4。
4.1. 当插入节点为根节点
直接将其着为黑色即可,如图所示。
4.2. 当插入节点的父节点为黑色
此时无需处理, 如图所示。
4.3. 当前节点的父节点为红色,其为祖父节点的左子节点
这种场景比较复杂,但其核心是将红色的节点移到根节点,然后将根节点设为黑色。可以细分为以下3种
4.3.1.1. 叔节点是黑色,当前节点是其父节点的左子节点
第1步将父节点设为黑色,将祖父节点设为红色;第2步以祖父节点为支点进行右旋,如图所示。
4.3.1.2. 叔节点是黑色,当前节点是其父节点的右子节点
第1步将当前节点5的父节点4进行左旋,然后4设为当前节点;
执行完后,当前节点4的父节点为红色且其位于父节点的左边,符合4.3.1.1的规则,照套即可。
4.3.1.3. 叔节点是红色
第1步将父节点和叔节点设为黑色,父节点设为红色,把祖父节点置为当前节点;
执行完后可以节点5的父节点为黑色,满足4.2的规则,照套进行。
4.4. 当前节点的父节点为红色,其为祖父节点的右子节点
规则同4,3类似,这里不再详叙,具体可以看代码。
五、 源码分析
5.1. 红黑树的节点
/*** @author JayLai* @Time 2018.01.22 22:36* @param <T>*/
public class RBTNode <T extends Comparable<T>>{boolean color; //颜色T key; //关键字(键值)RBTNode<T> left; //左孩子RBTNode<T> right; //右孩子RBTNode<T> parent; // 父结点/*** 有参构造函数* @param color* @param key* @param left* @param right* @param parent*/public RBTNode(boolean color, T key, RBTNode<T> left, RBTNode<T> right, RBTNode<T> parent) {super();this.color = color;this.key = key;this.left = left;this.right = right;this.parent = parent;}
}
5.2. 红黑树
/*** 红黑树* 关建点:将红色的节点移到根节点,然后将根节点设为黑色* @author JayLai* @Time 2018.01.23 15:41* @param <T>*/
public class RBTree <T extends Comparable<T>>{private RBTNode<T> root; //根节点private static final boolean RED = false;private static final boolean BLACK = true;/*** 无参构造函数*/public RBTree(){root = null;}/*** 返回指定节点的父节点* @param node* @return*/private RBTNode<T> parentOf(RBTNode<T> node){return node != null ? node.parent : null;}/*** 返回指定节点的颜色* @param node* @return*/private boolean colorOf(RBTNode<T> node){return node != null ? node.color : BLACK;}/*** 判断指定节点是否为红色* @param node* @return*/private boolean isRed(RBTNode<T> node){return (node != null) && (node.color==RED) ? true : false;}/*** 判断指定节点是否为黑色* @param node* @return*/private boolean isBlack(RBTNode<T> node){return !isRed(node);}/*** 将节点设为黑色* @param node*/private void setBlack(RBTNode<T> node){if(node != null){node.color = BLACK;}}/*** 将节点设为红色* @param node*/private void setRed(RBTNode<T> node){if(node != null){node.color = RED;}}/*** 设置指定节点的父节点* @param node* @param parent*/private void setParent(RBTNode<T> node, RBTNode<T> parent){if(node != null){node.parent = parent;}}/*** 设置指定节点的颜色* @param node* @param color*/private void setColor(RBTNode<T> node, boolean color){if(node != null){node.color = color;}}/*** 中序遍历* @param node*/public void inOrder(RBTNode<T> node) {if (node != null) {// 中序遍历左子树inOrder(node.left);// 访问根节点System.out.print("键值:" +node.key + " 颜色:" + ((isRed(node) == true ? "Red" : "Black")) + " ");// 中序遍历右子树inOrder(node.right);}}/*** 从小到达打印整一棵树红黑树*/public void printTree(){if(root != null){inOrder(root);}System.out.println();}/*** 对红黑树的节点x进行左旋转* @param x*/private void leftRotate (RBTNode<T> x){RBTNode<T> y = x.right; //x的右子节点x.right = y.left; //将y的左子节点设为x的右子节点if(y.left != null){y.left.parent = x; //将y的左子节点的父亲设为x}y.parent = x.parent; // 将x的父节点设为y的父节点if(x.parent == null){ //如果x的父节点为空this.root = y;}else{if(x.parent.left == x){ //如果x位于其父节点的左边x.parent.left = y;}else{ //如果x位于其父节点的右边x.parent.right = y;}}y.left = x; //将x设为y的左子节点x.parent = y; //将x的父节点设为y}/*** 对红黑树的节点x进行右旋转* * @param x*/public void rightRotate(RBTNode<T> x){RBTNode<T> y = x.left; //x的左子节点x.left = y.right; //将y的右子节点设为x的左子节点if(y.right != null){ y.right.parent = x; // 将y的右子节点的父亲设为x}y.parent = x.parent; // 将x的父节点设为y的父节点if(x.parent == null){ //如果x的父节点为空this.root = y;}else{if(x.parent.left == x){ //如果x位于其父节点的左边x.parent.left = y;}else{ //如果x位于其父节点的右边x.parent.right = y;}}y.right = x; //将x设为y的右子节点x.parent = y; //将x的父节点设为y}/*** 通过添加键值(key)进行节点插入* @param key*/public void insert(T key){// 将新节点设为红色RBTNode<T> newNode = new RBTNode<T>(RED, key, null, null, null);if(newNode != null){insert(newNode);}}/*** 将节点插到红黑树中* @param node*/public void insert(RBTNode<T> node){RBTNode<T> parent = null;RBTNode<T> current = this.root;int ret;//1. 将节点插入到二叉树中while(current != null){parent = current;ret = node.key.compareTo(current.key);if(ret > 0){current = current.right;}else{current = current.left;}}node.parent = parent;if(parent == null){this.root = node;}else{ret = node.key.compareTo(parent.key);if(ret < 0){parent.left = node;}else{parent.right = node;}}//2. 将其修正为红黑树insertFixUp(node);}/*** 将插入节点的二叉树修正为红黑树* @param node*/private void insertFixUp(RBTNode<T> node){RBTNode<T> parent, gparent;// 当前节点的父节点是红色while(((parent = parentOf(node))!=null) && isRed(parent)){gparent = parentOf(parent);//1父节点是祖父节点的左子节点if(parent == gparent.left){//case 1.1条件:叔叔节点是红色//处理方法: 将父节设为黑色,将叔节点设为黑色,将祖父节点设为红色,将祖父节点设为当前节点RBTNode<T> uncle = gparent.right;if((uncle != null) && isRed(uncle)){setBlack(uncle);setBlack(parent);setRed(gparent);node = gparent; continue;}//case 1.2条件:叔叔节点是黑色,当前节点是其父节点的右子节点//叔节点若未为空也是黑色//处理方法:将父点作为新的当前节点,以新的当前节点为支点进行左旋if(parent.right == node){RBTNode<T> tmp;leftRotate(parent);tmp = parent;parent = node;node = tmp;}//case 1.3条件:叔叔是黑色,且当前节点是左孩子//处理方法: 将父节点点设为黑色,将祖父节点设为红色。以祖父节点为支点进行右旋。setBlack(parent);setRed(gparent);rightRotate(gparent);//2 父节点是祖父节点的左子节点} else{// Case 2.1条件:叔叔节点是红色RBTNode<T> uncle = gparent.left;if ((uncle!=null) && isRed(uncle)) {setBlack(uncle);setBlack(parent);setRed(gparent);node = gparent;continue;}// Case 2.2条件:叔叔是黑色,且当前节点是左子节点if (parent.left == node) {RBTNode<T> tmp;rightRotate(parent);tmp = parent;parent = node;node = tmp;}// Case 2.3条件:叔叔是黑色,且当前节点是右子节点setBlack(parent);setRed(gparent);leftRotate(gparent);}}//将根节点设为黑色setBlack(this.root);}
}
5.3.测试代码
public class RBTreeApp {public static void main(String[] args) {
int arr1[] = { 10 };
int arr2[] = { 10, 8};
int arr3[] = { 10, 8, 6};
int arr4[] = { 10, 8, 6, 4};
int arr5[] = { 10, 8, 6, 4, 5 };RBTree<Integer> tree1 = new RBTree<Integer>();
RBTree<Integer> tree2 = new RBTree<Integer>();
RBTree<Integer> tree3 = new RBTree<Integer>();
RBTree<Integer> tree4 = new RBTree<Integer>();
RBTree<Integer> tree5 = new RBTree<Integer>();for (int i = 0; i < arr1.length; i++) {
tree1.insert(arr1[i]);
}
for (int i = 0; i < arr2.length; i++) {
tree2.insert(arr2[i]);
}
for (int i = 0; i < arr3.length; i++) {
tree3.insert(arr3[i]);
}
for (int i = 0; i < arr4.length; i++) {
tree4.insert(arr4[i]);
}
for (int i = 0; i < arr5.length; i++) {
tree5.insert(arr5[i]);
}tree1.printTree();
tree2.printTree();
tree3.printTree();
tree4.printTree();
tree5.printTree();
}
}
5.4. 测试结果