红黑树(二)

红黑树的插入操作

1. 以排序二叉树的方法插入新节点,并将它设置为红色。
设置红色:如果设置黑色,会导致根节点到叶子节点的路径上多出一个额外的黑节点,这样会很难调整。
但是设置为红色可能会导致出现两个连续的红色节点(违反了性质4),因此需要通过颜色转换和树旋转来进行调整。
2. 颜色转换和树旋转

新插入的节点N, N的父亲节点设置为P,P的兄弟节点设置为B,P的附近点设置为G。(可以理解为newnode,parent,brother,grandparent)。

情形1:新节点N是树的根节点(root),设置为黑色即可。
情形2:新节点N的父节点P为黑色。此时,新插入的节点P为红色,不违反红黑树的各性质。

情形3:父节点P和父节点的兄弟节点B都是红色的。

分析:插入之后破坏了性质4(两个连续的红色节点),所以如图的调整。但是此时新插入节点的父节点的父节点G改变了颜色,那么此时G.color 与 G.parent.color之间的关系就是新的问题所在,所以循环中下一次迭代将G作为新的输入。


这张图相当于在P和N之间做了一个旋转。


情形4:父节点P是红色的,而其兄弟节点B是黑色或者缺少。这里将插入的节点分为左右子节点讨论,其实没有什么区别。

分析:如第二张图,插入的节点是其根结点的右节点,破坏了性质5:一个节点向下到叶子节点的简单路径上的黑色节点数相同。所以进行了一次旋转,状态就和第一张图相似,但是此时破坏了性质4:连续两个红节点,且兄弟节点B为黑色,所以旋转维持性质4.



以上是父节点P作为左子节点,如果是右子节点的情况和这个类似,只是选择的旋转方向不同,这里就不做分析了。

参考代码:

public void add(T data) {
		// 如果根节点为空
		if(root == null){
			root = new Node(data, null, null, null); 
		}
		else{
			Node current = root;
			Node parent = null;
			int cmp = 0;
			//搜索到合适的叶子节点,以该节点为父节点添加新的节点
			while(current != null){//找到合适的位置 假定的"null"会被我指定的值替换
				parent = current;
				cmp = data.compareTo(current.data);
				if(cmp < 0){
					current = current.left;
				}
				else{
					current = current.right;
				}
			}
			//创建新节点, 通过直接的比较知道了它的父节点的值, 接下来就是通过比较 判断是他父节点的   左还是右节点
			Node newNode = new Node(data, parent, null, null);
			if(cmp > 0){
				parent.right = newNode;
			}
			else{
				parent.left = newNode;
			}
			rbInsert_Fixup(newNode);
		}
	}

	//维护红黑树性质
	private void rbInsert_Fixup(Node newNode){
		newNode.color = RED;
		//直到这个新节点的父节点不是根,且newNode的父节点不是红色
		while(newNode != null && newNode != root && newNode.parent.color == RED){
			//如果newNode的父节点是其父节点的左节点
			if(parentOf(newNode) == leftOf(parentOf(parentOf(newNode)))){
				//获取newNode的父节点的兄弟节点
				Node y = rightOf(parentOf(parentOf(newNode)));
				//如果它的父节点的兄弟节点是红色
				if(colorOf(y) == RED){
					//将newNode的父节点设为黑色
					setColor(parentOf(newNode), BLACK);
					//将newNode的父节点的兄弟节点设为黑色
					setColor(y, BLACK);
					//将newNode的父节点的父节点染成红色
					setColor(parentOf(parentOf(newNode)), RED);
					newNode = parentOf(parentOf(newNode));
				}
				//如果newNode的父节点的兄弟节点是黑色
				else{
					//如果newNode是其父节点的右节点
					if(newNode == rightOf(parentOf(newNode))){
						//获取newNode的父节点设为newNode
						newNode = parentOf(newNode);
						rotateLeft(newNode);
					}
					//将newNode的父节点设为黑色
					setColor(parentOf(newNode), BLACK);
					//将newNode的父节点的父节点染成红色
					setColor(parentOf(parentOf(newNode)), RED);
					rotateRight(parentOf(parentOf(newNode)));
				}
			}
			//如果newNode 的父节点是其父节点的右节点
			else{
				//获取newNode的父节点的兄弟节点
				Node y = leftOf(parentOf(parentOf(newNode)));
				//如果它的父节点的兄弟节点是红色
				if(colorOf(y) == RED){
					//将newNode的父节点设为黑色
					setColor(parentOf(newNode), BLACK);
					//将newNode的父节点的兄弟节点设为黑色
					setColor(y, BLACK);
					//将newNode的父节点的父节点染成红色
					setColor(parentOf(parentOf(newNode)), RED);
					newNode = parentOf(parentOf(newNode));
				}
				//如果newNode的父节点的兄弟节点是黑色
				else{
					//如果newNode是其父节点的右节点
					if(newNode == leftOf(parentOf(newNode))){
						//获取newNode的父节点设为newNode
						newNode = parentOf(newNode);
						rotateRight(newNode);
					}
					//将newNode的父节点设为黑色
					setColor(parentOf(newNode), BLACK);
					//将newNode的父节点的父节点染成红色
					setColor(parentOf(parentOf(newNode)), RED);
					rotateLeft(parentOf(parentOf(newNode)));
				}
			}
		}
		//将根节点设为黑色
		root.color = BLACK;
	}

可参考:点击打开链接中对各性质的描述。


以上就是这篇的主要内容。欢迎指出错误和不足之处,谢谢!

猜你喜欢

转载自blog.csdn.net/kobe_jr/article/details/80044333