赞
踩
红黑树是含有红黑链接并满足以下条件的二叉查找树
1、红链接均为左连接;
2、没有任何一个结点同时和两条红链接相连;
3、该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。
因为每个结点都只会有一条指向自己的链接(即它的父结点指向它),我们可以在之前的Node结点中新增一个布尔类型的变量color来表示此链接的颜色。我们规定:如果指向此结点的链接是红色的,那么该变量的值为true;如果指向此结点的链接时黑色的,那么该变量的值为false。例如下图所示,c是当前结点,h指的是它的父结点,所以h的左子结点(即c结点)的color的值是true
- /**
- * 结点类
- */
- private class Node {
- /**
- * 存储键
- */
- private Key key;
- //存储值
- private Value value;
- //记录左子结点
- private Node left;
- //记录右子结点
- private Node right;
- //标记父结点指向当前结点的链接是否是红色:true表示是红色,false表示不是红色(即黑色)
- private boolean color;
-
- /**
- * 构造方法
- */
- public Node(Key key, Value value, Node left, Node right, boolean color) {
- this.key = key;
- this.value = value;
- this.left = left;
- this.right = right;
- this.color = color;
- }
-
- }

在对红黑树进行一些增删改的操作后,很有可能会出现红色的右链接或者两条连续的红色的链接,而这些都不满足红黑树的定义,所以我们需要对这些情况通过旋转进行修复,让红黑树保持平衡。
当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
例如:当前结点为h,它的右子结点为x;
左旋过程:
1、让x的左子结点变为h的右子结点:h.right=x.left;
2、让h成为x的左子结点:x.left=h;
3、让h的color属性变为x的color属性值:x.color=h.color;
4、让h的color属性变为RED:h.color=true;
当某个结点的左子结点为红色,左子结点的左子结点也为红色,此时需要右旋。
例如:当前结点为h,它的左子结点为x;
右旋过程:
1、让x的右子结点变为h的左子结点:h.left=x.right;
2、让h成为x的右子结点:x.right=h;
3、让h的color属性变为x的color属性值:x.color=h.color;
4、让h的color属性变为RED:h.color=true;
当一个结点的左子结点和右子结点的color都为RED时,此时只需要把左子结点和右子结点的颜色变为BLACK,同时让当前结点的颜色变为RED即可。
在结点Node对象中color属性表示的是父结点指向当前结点的连接的颜色,由于根结点不存在父结点,所以每次插入操作后,我们都需要把根结点的颜色设置为黑色。
- /**
- * @author: xuzhilei
- * @create: 2021-12-21
- * @description: 红黑树
- **/
- public class RedBlackTree<Key extends Comparable<Key>, Value> {
- /**
- * 根结点
- */
- private Node root;
- /**
- * 记录树中的元素个数
- */
- private int number;
- /**
- * 表示红色链接
- */
- private static final boolean RED = true;
- /**
- * 表示黑色链接
- */
- private static final boolean BLACK = false;
-
- /**
- * 结点类
- */
- private class Node {
- /**
- * 存储键
- */
- private Key key;
- //存储值
- private Value value;
- //记录左子结点
- private Node left;
- //记录右子结点
- private Node right;
- //标记父结点指向当前结点的链接是否是红色:true表示是红色,false表示不是红色(即黑色)
- private boolean color;
-
- /**
- * 构造方法
- */
- public Node(Key key, Value value, Node left, Node right, boolean color) {
- this.key = key;
- this.value = value;
- this.left = left;
- this.right = right;
- this.color = color;
- }
-
- }
-
- /**
- *构造方法
- */
- public RedBlackTree() {
- this.root = null;
- this.number = 0;
- }
-
- /**
- * 获取红黑树中的元素个数
- *
- * @return int
- */
- public int size() {
- return number;
- }
-
- /**
- * 判断当前结点x的父结点指向它的链接是否为红色
- *
- * @param x 当前结点
- * @return boolean
- */
- private boolean isRed(Node x) {
- if (x == null) {
- return false;
- }
- return x.color == RED;
- }
-
- /**
- * 左旋转
- * 当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
- * 前提:当前结点为h,它的右子结点为x;
- * 左旋过程:
- * 1.让x的左子结点变为h的右子结点:h.right=x.left;
- * 2.让h成为x的左子结点:x.left=h;
- * 3.让h的color属性变为x的color属性值:x.color=h.color;
- * 4.让h的color属性变为RED:h.color=true;
- *
- * @param h 当前结点
- * @return h的右子结点x
- */
- private Node rotateLeft(Node h) {
- //获取h结点的右子结点x
- Node x = h.right;
- //找到x结点的左子结点,让x结点的左子结点成为h结点的右子结点
- h.right = x.left;
- //让h成为x的左子结点
- x.left = h;
- //让h的color属性变为x的color属性值
- x.color = h.color;
- //让h的color属性变为RED
- h.color = RED;
- //返回x结点
- return x;
- }
-
- /**
- * 右旋转
- * 当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
- * 前提:当前结点为h,它的左子结点为x;
- * 右旋过程:
- * 1. 让x的右子结点成为h的左子结点:h.left = x.right;
- * 2. 让h成为x的右子结点:x.right=h;
- * 3. 让x的color变为h的color属性值:x.color = h.color;
- * 4. 让h的color为RED;
- *
- * @param h 当前结点
- * @return h的左子结点x
- */
- private Node rotateRight(Node h) {
- //获取h结点的左子结点x
- Node x = h.left;
- //找到x结点的右子结点,让x结点的右子结点成为h结点的左子结点
- h.left = x.right;
- //让h成为x的右子结点
- x.right = h;
- //让x的color变为h的color属性值
- x.color = h.color;
- //让h的color属性值变为RED
- h.color = RED;
- //返回x结点
- return x;
- }
-
- /**
- * 颜色反转
- * 当某个结点的左子结点是红色,且右子结点也是红色,需要颜色反转
- *
- * @param h 当前结点
- */
- private void flipColors(Node h) {
- //当前结点的color属性值变为RED
- h.color = RED;
- //左子结点和右子结点的color属性都变为BLACK
- h.left.color = BLACK;
- h.right.color = BLACK;
- }
-
- /**
- * 向整个树中插入键值对key-value
- *
- * @param key 待插入键
- * @param value 待插入值
- */
- public void put(Key key, Value value) {
- root = put(this.root, key, value);
- //根结点的color属性总是黑色
- root.color = BLACK;
- }
-
- /**
- * 往指定树h中插入键值对key-value,并返回插入元素后的新树
- *
- * @param h 指定树的根结点
- * @param key 待插入键
- * @param value 待插入值
- */
- private Node put(Node h, Key key, Value value) {
- //如果当前结点为空,则直接创建一个结点并返回此结点
- if (h == null) {
- //元素个数+1
- number++;
- return new Node(key, value, null, null, RED);
- }
-
- //比较结点h的键和参数key的大小
- int comp = key.compareTo(h.key);
- if (comp < 0) {
- //key比当前结点h的键要小,继续向h的左子结点所在的树进行插入操作
- h.left = put(h.left, key, value);
- } else if (comp > 0) {
- //key比当前结点h的键要大,继续向h的右子结点所在的树进行插入操作
- h.right = put(h.right, key, value);
- } else {
- //key与当前结点h的键相等,则用待插入的value替换当前结点的value值
- h.value = value;
- }
-
- //满足左旋转条件:当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
- if (isRed(h.right) && !isRed(h.left)) {
- h = rotateLeft(h);
- }
-
- //满足右旋转条件:当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
- if (isRed(h.left) && isRed(h.left.left)) {
- h = rotateRight(h);
- }
-
- //满足颜色反转:当某个结点的左子结点是红色,且右子结点也是红色,需要颜色反转
- if (isRed(h.left) && isRed(h.right)) {
- flipColors(h);
- }
-
- return h;
- }
-
- /**
- * 根据key在整个树中找出对应的value
- *
- * @param key 指定的key
- * @return Value 对应的value
- */
- public Value get(Key key) {
- return get(root, key);
- }
-
- /**
- * 根据key在指定树x中找出对应的value
- *
- * @param x 指定树的根结点
- * @param key 指定的key
- * @return Value 对应的value
- */
- private Value get(Node x, Key key) {
- if (x == null) {
- return null;
- }
- //比较当前结点x的键和参数key的大小
- int comp = key.compareTo(x.key);
- if (comp < 0) {
- //key比当前结点x的键要小,则继续向x的左子结点所在的树进行查找操作
- return get(x.left, key);
- } else if (comp > 0) {
- //key比当前结点x的键要大,则继续向x的右子结点所在的树进行查找操作
- return get(x.right, key);
- } else {
- //key与当前结点x的键相等,则返回结点x的value值
- return x.value;
- }
- }
-
- //测试
- public static void main(String[] args) {
- //创建红黑树
- RedBlackTree<String, String> redBlackTree = new RedBlackTree<>();
-
- //插入元素
- redBlackTree.put("1", "张三");
- redBlackTree.put("2", "李四");
- redBlackTree.put("3", "王五");
-
- //获取树中元素个数
- int size = redBlackTree.size();
- System.out.println(size);
-
- //从树中获取元素
- String s = redBlackTree.get("1");
- System.out.println(s);
-
- }
-
-
- }

以上是个人随手敲的demo,如有不正确的地方,可以在下方留言指正,谢谢。与各位CSDN的伙伴们共勉,每天记录一点点,每天进步一点点
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。