【数据结构与算法04】红黑树

本文详细介绍了红黑树的基本概念,包括它的定义、颜色限制和基本运算,如插入、删除。重点阐述了左旋和右旋操作,解释了如何通过这些旋转来保持树的平衡。此外,还提到了红黑树在Java中的应用,通过代码片段展示了其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义:自平衡二叉查找树,每个节点都带有颜色属性,颜色或红色或黑色。

运算限制:1节点是红色或黑色。2根节点是黑色。3每个叶节点(NIL节点,空节点)是黑色的。4每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)。5从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点

基本概念:左旋就是把原节点摘下来,用原节点的右子节点替换原节点,原节点变成右子节点的左子节点,如果原右子节点存在左子节点,则把该左子节点当成原节点的右子节点。右旋就是把原节点摘下来,用原节点的左子节点替换原节点,原节点变成做左子节点的右子节点,如果原左子节点存在右子节点,则把该右子节点当成原节点的左子节点。右旋跟左旋一样,是左旋的逆过程。下面有图。

基本运算:

1,插入。

2,删除 。

3,查找,跟二叉树一样,省略

 

左旋与右旋

对x左旋得到右图,对y右旋得到左图

左旋右旋

 

 

看代码,下面代码摘自jdk treemap 类,看解析部分就可以了,下面辅助代码的作用在解析中都说了。

public class RBTree<K, V> implements Tree<K, V> {

	private Node<K, V> root;
	private static final boolean RED = false;
	private static final boolean BLACK = true;
	private int size = 0;
	private int modCount = 0;
	
	
	@SuppressWarnings("unchecked")
	public V put(K key,V value){
		if (key == null) throw new NullPointerException();
		//如果跟节点是空,就插入到跟节点
		if (root == null){
			root = new Node<>();
			root.key = key;
			root.value = value;
			size = 1 ;
			modCount ++ ;
			return null;
		}
		//从根节点开始循环,找到插入节点的位置和它的父节点,这里跟二叉查找树是一样的,主要是下面自平衡部分,也就是红黑树优于查找树的原由
		Node<K, V> current  = root;
		Node<K,V> parent;
		Comparable<? super K> k = (Comparable<? super K>) key;
		int comparaberResult ;
		 do {
             parent = current;
             comparaberResult = k.compareTo(current.key);
             if (comparaberResult < 0)//小于向左找
            	 current = current.leftChild;
             else if (comparaberResult > 0)//大于向右找
            	 current = current.rightChild;
             else{
            	 //已经存在这个key,修改key的值
            	 current.value=value;
                 return value ;
             }
             //循环直到找到新节点要插入的位置,并找到父节点parent和插入规则comparaberResult
         } while (current != null);
		 //走到这里表示上面的修改没进行,所以是插入新节点
		 Node<K,V> node = new Node<>();
		 node.key = key;
		 node.value = value;
		 node.parent = parent;
		 //根据比较结果,知道新节点是左子节点还是右子节点
		 if (comparaberResult < 0)
			 parent.leftChild = node;
         else
	         parent.rightChild = node;
		 
		 //插入之后让树保持平衡
	     fixAfterInsertion(node);
	     size++;
	     modCount++;
	     return null;
	}

	private void fixAfterInsertion(Node<K,V> x) {
		//默认新节点为红色,以此来调整上层树的平衡
		x.color = RED;
		//因为红色节点的子节点必须为黑色,所以此处只要父节点是红色就要一直调到变成黑色。
        while (x != null && x != root && x.parent.color == RED) {
        	//不管父节点是左子节点还是右子节点,做的事情都是一样。就是把父节点变成黑色,
        	//然后把父节点的父节点(父父节点)变成红色去找违反规则的节点,相当于父父节点是一个新插入的红色节点,循环
            //如果父节点是左子节点
        	if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Node<K,V> y = rightOf(parentOf(parentOf(x)));
                //而且右子节点也是红色,那我同时把两个红色变成黑色,两边黑色节点的数量还是相等的,父节点变成红色,再进循环判断是否需要修改上面节点的颜色。
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                //右子节点是黑色,如果此时我只把左子节点变成黑色,相当于左边新加了一个黑色,那左右两边黑色就不相等了,为了达到第一种情况,
                //这个时候如果新加的节点是左子节点,我们只需要先把他的父节点变成黑色,把父父节点变成红色,然后右旋,
               //结果为新加的黑色放到了父父节点,右子节点新加红色,左子节点为新加的红色节点,变成了第一种情况。ok
                } else {
                	//由于我们整体要向右旋,如果新节点是右子节点,
                	//那么在整体右旋结束后,新节点会变成右子节点的左子节点,而我们预期的结果是第一种情况,也就是左右都是红色,
                	//所以在此之前,我们要先把新加右节点变成加左节点,由于新加节点和它的父节点都是红色,所以直接用父节点左旋就可以了
                    if (x == rightOf(parentOf(x))) {
                        //x取父节点左旋
                    	x = parentOf(x);
                    	//左旋完成后,x变成了左子节点
                    	//左旋就是把原节点摘下来,用原节点的右子节点替换原节点,原节点变成右子节点的左子节点,
                    	//如果原右子节点存在左子节点,则把该左子节点当成原节点的右子节点
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    //右旋完成后,变成了上面左右子节点都是红色的情况。循环
                    //右旋跟上面左旋一样,是左旋的逆过程
                    //把原节点摘下来,用原节点的左子节点替换原节点,原节点变成做左子节点的右子节点
                    //如果原左子节点存在右子节点,则把该右子节点当成原节点的左子节点
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Node<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }
	
	//删除节点跟二叉树一样的,只是多了调整结构的部分,跟插入一样的原理
	 public V remove(Object key) {
	        Node<K,V> p = getNode(key);
	        if (p == null)
	            return null;

	        V oldValue = p.value;
	        deleteNode(p);
	        return oldValue;
	    }

	 final Node<K,V> getNode(Object key) {
	        if (key == null) throw new NullPointerException();
	        @SuppressWarnings("unchecked")
	        Comparable<? super K> k = (Comparable<? super K>) key;
	        Node<K,V> p = root;
	        while (p != null) {
	            int cmp = k.compareTo(p.key);
	            if (cmp < 0)
	                p = p.leftChild;
	            else if (cmp > 0)
	                p = p.rightChild;
	            else
	                return p;
	        }
	        return null;
	    }

	 private void deleteNode(Node<K,V> p) {
	        modCount++;
	        size--;

	        //先处理有两个子节点的情况
	        if (p.leftChild != null && p.rightChild != null) {
	        	Node<K,V> s = successor(p);//进到t.rightChild != null条件语句里面得到右子节点的最左子节点s
	            p.key = s.key;
	            p.value = s.value;
	            p = s;
	        }

	        Node<K,V> replacement = (p.leftChild != null ? p.leftChild : p.rightChild);

	        if (replacement != null) {
	            replacement.parent = p.parent;
	            if (p.parent == null)
	                root = replacement;
	            else if (p == p.parent.leftChild)
	                p.parent.leftChild  = replacement;
	            else
	                p.parent.rightChild = replacement;

	            p.leftChild = p.rightChild = p.parent = null;

	            if (p.color == BLACK)
	                fixAfterDeletion(replacement);
	        } else if (p.parent == null) {
	            root = null;
	        } else {
	            if (p.color == BLACK)
	                fixAfterDeletion(p);

	            if (p.parent != null) {
	                if (p == p.parent.leftChild)
	                    p.parent.leftChild = null;
	                else if (p == p.parent.rightChild)
	                    p.parent.rightChild = null;
	                p.parent = null;
	            }
	        }
	    }
	
	 
	 static <K,V> RBTree.Node<K,V> successor(Node<K,V> t) {
	        if (t == null)
	            return null;
	        else if (t.rightChild != null) {
	        	Node<K,V> p = t.rightChild;
	            while (p.leftChild != null)
	                p = p.leftChild;
	            return p;
	        } else {
	        	Node<K,V> p = t.parent;
	        	Node<K,V> ch = t;
	            while (p != null && ch == p.rightChild) {
	                ch = p;
	                p = p.parent;
	            }
	            return p;
	        }
	    }
	
	 
	 private void fixAfterDeletion(Node<K,V> x) {
	        while (x != root && colorOf(x) == BLACK) {
	            if (x == leftOf(parentOf(x))) {
	            	Node<K,V> sib = rightOf(parentOf(x));

	                if (colorOf(sib) == RED) {
	                    setColor(sib, BLACK);
	                    setColor(parentOf(x), RED);
	                    rotateLeft(parentOf(x));
	                    sib = rightOf(parentOf(x));
	                }

	                if (colorOf(leftOf(sib))  == BLACK &&
	                    colorOf(rightOf(sib)) == BLACK) {
	                    setColor(sib, RED);
	                    x = parentOf(x);
	                } else {
	                    if (colorOf(rightOf(sib)) == BLACK) {
	                        setColor(leftOf(sib), BLACK);
	                        setColor(sib, RED);
	                        rotateRight(sib);
	                        sib = rightOf(parentOf(x));
	                    }
	                    setColor(sib, colorOf(parentOf(x)));
	                    setColor(parentOf(x), BLACK);
	                    setColor(rightOf(sib), BLACK);
	                    rotateLeft(parentOf(x));
	                    x = root;
	                }
	            } else {
	            	Node<K,V> sib = leftOf(parentOf(x));

	                if (colorOf(sib) == RED) {
	                    setColor(sib, BLACK);
	                    setColor(parentOf(x), RED);
	                    rotateRight(parentOf(x));
	                    sib = leftOf(parentOf(x));
	                }

	                if (colorOf(rightOf(sib)) == BLACK &&
	                    colorOf(leftOf(sib)) == BLACK) {
	                    setColor(sib, RED);
	                    x = parentOf(x);
	                } else {
	                    if (colorOf(leftOf(sib)) == BLACK) {
	                        setColor(rightOf(sib), BLACK);
	                        setColor(sib, RED);
	                        rotateLeft(sib);
	                        sib = leftOf(parentOf(x));
	                    }
	                    setColor(sib, colorOf(parentOf(x)));
	                    setColor(parentOf(x), BLACK);
	                    setColor(leftOf(sib), BLACK);
	                    rotateRight(parentOf(x));
	                    x = root;
	                }
	            }
	        }
	        setColor(x, BLACK);
	    }

	
	
	 private static <K,V> boolean colorOf(Node<K,V> p) {
	        return (p == null ? BLACK : p.color);
	    }

	    private static <K,V> Node<K,V> parentOf(Node<K,V> p) {
	        return (p == null ? null: p.parent);
	    }

	    private static <K,V> void setColor(Node<K,V> p, boolean c) {
	        if (p != null)
	            p.color = c;
	    }

	    private static <K,V> Node<K,V> leftOf(Node<K,V> p) {
	        return (p == null) ? null: p.leftChild;
	    }

	    private static <K,V> Node<K,V> rightOf(Node<K,V> p) {
	        return (p == null) ? null: p.rightChild;
	    }

	    /** From CLR */
	    private void rotateLeft(Node<K,V> p) {
	        if (p != null) {
	            Node<K,V> r = p.rightChild;
	            p.rightChild = r.leftChild;
	            if (r.leftChild != null)
	                r.leftChild.parent = p;
	            r.parent = p.parent;
	            if (p.parent == null)
	                root = r;
	            else if (p.parent.leftChild == p)
	                p.parent.leftChild = r;
	            else
	                p.parent.rightChild = r;
	            r.leftChild = p;
	            p.parent = r;
	        }
	    }

	    /** From CLR */
	    private void rotateRight(Node<K,V> p) {
	        if (p != null) {
	            Node<K,V> l = p.leftChild;
	            p.leftChild = l.rightChild;
	            if (l.rightChild != null) l.rightChild.parent = p;
	            l.parent = p.parent;
	            if (p.parent == null)
	                root = l;
	            else if (p.parent.rightChild == p)
	                p.parent.rightChild = l;
	            else p.parent.leftChild = l;
	            l.rightChild = p;
	            p.parent = l;
	        }
	    }

	
	static class Node<K, V> {
		K key;
		V value;
		Node<K, V> leftChild;
		Node<K, V> rightChild;
		Node<K, V> parent;
		boolean color = BLACK;
	}

	public Node<K, V> getRoot() {
		return root;
	}

	public void setRoot(Node<K, V> root) {
		this.root = root;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值