红黑树 讲解

前言

1、红黑树(Red Black Tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。
它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。
2、直观演示红黑树的增删改查的网页:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
3、红黑树的构思巧妙

红黑性质

如果一颗二叉查找树,满足下面的红黑性质,则为一颗红黑树:
1、每个结点要么是红的要么是黑的
2、根结点是黑的
3、每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的
4、如果一个结点是红的,那么它的两个孩子都是黑的
5、 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点

红黑树 左旋转

在这里插入图片描述
在这里插入图片描述

红黑树 右旋转

在这里插入图片描述
在这里插入图片描述

红黑树 插入操作

按照普通二叉树方式,插入新增节点node,并使指针节点pointer指向node节点(指针节点pointer、父亲节点father、爷爷节点grandpa)
情况1
插入:pointer节点是根节点
解决:修改节点颜色为黑色,结束
情况2
插入:pointer的父节点是黑色
解决:满足红黑性质,无需操作,结束
情况3
插入:pointer的父节点颜色是红色,并且叔叔节点也是红色
解决:父节点和叔叔节点颜色变为黑色,爷爷节点颜色变为红色,此时将pointer指向爷爷节点。继续判断pointer属于哪个情况
情况4
插入:pointe的父亲节点颜色是红色,叔叔节点是黑色或者Null(如果采用哨兵,则叔叔节点不会出现Null情况),父亲节点是爷爷节点的左孩子,pointer是父亲节点的右孩子
解决:
方法1:左旋转(图a->图b,pointer指向由Node变为father),继续判断pointer所属情况
方法2:左旋转(图a->图b) + 右旋转(图b->图c) ,结束流程
在这里插入图片描述
情况5
插入:pointe的父亲节点颜色是红色,叔叔节点是黑色或者Null(如果采用哨兵,则叔叔节点不会出现Null情况),父亲节点是爷爷节点的左孩子,pointer是父亲节点的左孩子
解决:右旋转,结束流程
在这里插入图片描述
情况6
插入:pointe的父亲节点颜色是红色,叔叔节点是黑色或者Null(如果采用哨兵,则叔叔节点不会出现Null情况),父亲节点是爷爷节点的右孩子,pointer是父亲节点的左孩子
解决:
方法1:右旋转(图a->图b,pointer指向由Node变为father),继续判断pointer所属情况
方法2:右旋转(图a->图b) + 左旋转(图b->图c) ,结束流程
在这里插入图片描述
情况7
插入:pointe的父亲节点颜色是红色,叔叔节点是黑色或者Null(如果采用哨兵,则叔叔节点不会出现Null情况),父亲节点是爷爷节点的右孩子,pointer是父亲节点的右孩子
解决:左旋转,结束流程
在这里插入图片描述

红黑树 删除操作

1、在分析红黑树删除操作之前,先了解一下几个概念:
前驱:对于节点node,前驱指的是node节点左子树中value值最大的那个节点
后继:对于节点node,后继指的是node节点右子树中value值最小的那个节点
2、现在,打算删除节点node,则创建一个目标节点target指向node的前驱节点,如果没有前驱节点(例如:node节点没有左子树),那么target指向node节点
3、如果target和node不是同一个节点,那么交换target和node的数据,如此删除node节点变成删除target节点。如果target和node是同一个节点,无须交换数据,删除node节点同样变成删除target节点
4、如果target节点是红色,直接删除target;如果target节点是黑色,先修正红黑树,然后删除target节点

红黑树 删除修正操作

首先创建一个指针节点pointer指向待删除target节点,因为要删除target节点,所以经过target节点路径的黑高度会减1。现在开始分析pointer节点
情况1
情况:pointer为根节点
解决:pointer节点颜色变为黑色,结束分析、修正
原因:pointer每次上移,pointer上移后的右孩子会由黑色变为红色,左右子树黑高度一致
情况2
情况:pointer节点颜色是红色
解决:pointer节点变为黑色,结束分析、修正
原因:pointer初始为黑色,各种情况中,只有情况4会导致pointer上移,可能导致颜色变化。每次上移都会使右孩子黑高度减1,以便和左孩子黑高度一致,所以经过pointer节点路径的黑高度都会减1。若此时pointer为红色,直接改为黑色,可以使得经过pointer节点路径黑高度和原来一致。
情况3
情况:pointer的兄弟节点为null
解决:pointer上移,pointer指向父节点father
原因:因为pointer没有兄弟节点,所以经过pointer路径的黑高度和经过father路径的黑高度一致,指针上移,继续分析
在这里插入图片描述
情况4
情况:pointer的兄弟节点brother,brother是左兄弟,并且是红色
解决:对兄弟节点brother进行右旋转
在这里插入图片描述
情况5
情况:pointer的兄弟节点brother,brother是右兄弟,并且是红色
解决:对兄弟节点brother进行左旋转
在这里插入图片描述
情况6
情况:pointer兄弟节点brother,brother为黑色,brother的孩子节点为null或黑色,共四种情况(左孩子为null且右孩子为null,左孩子为null且右孩子为黑色;左孩子为黑色且右孩子为null;左孩子为黑色且右孩子为黑色)
解决:brother节点颜色改为红色(时间复杂度log₂n)
在这里插入图片描述
情况7
情况:pointer兄弟节点brother,brother为father的左孩子,brother为黑色,brother左孩子为null或黑色,brother右孩子为红色
解决:对brother右孩子左旋转
在这里插入图片描述
情况8
情况:pointer兄弟节点brother,brother为father的右孩子,brother为黑色,brother左孩子为红色,brother右孩子为null或黑色
解决:对brother左孩子右旋转
在这里插入图片描述
情况9
情况:pointer兄弟节点brother,brother为father的左孩子,brother为黑色,brother左孩子为红色,brother右孩子为null或黑色或红色
解决:对brother右旋转、brother左孩子变为黑色,结束
在这里插入图片描述
情况10
情况:pointer兄弟节点brother,brother为father的右孩子,brother为黑色,brother右孩子为红色,brother左孩子为null或黑色或红色
解决:对brother左旋转、brother右孩子变为黑色,结束
在这里插入图片描述

红黑树 代码实现

代码做过简单测试,如果发现有什么问题,欢迎留言,一起学习成长。

package p2;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RedBlackTreeTest {

	public static void main(String[] args) {
		int len = 20;
		int[] values = new int[len];
		for (int i = 0; i < len;) {
			values[i++] = (int) (Math.random() * 30);
			//values[i] = ++i * 10;
		}

		RedBlackTree tree;
		Node node;
		for (int i = 0; i < len; i++) {
			tree = new RedBlackTree();
			for (int j = 0; j < len; j++) {
				node = new Node(values[j]);
				tree.insertTreeNode(node);
			}
			tree.delete(values[i]);
			System.out.println(tree);
		}

	}

}

class RedBlackTree {
	Node mRoot;
	/**
	 * true,toString()为所有节点的树状内容;false,toString()为所有节点的线性形状内容
	 */
	boolean mTreeStyle = true;

	public void insertTreeNode(Node node) {
		if (node != null) {
			insertNode(node);
			insertFixup(node);
		}
	}

	/**
	 * 按照普通二叉树形式插入一个节点
	 * 
	 * @param node 待插入节点
	 */
	private void insertNode(Node node) {
		if (node == null) {
			throw new NullPointerException("node is null");
		}
		// 如果新增节点是根节点
		if (mRoot == null) {
			mRoot = node;
			mRoot.color = Color.Black;
			return;
		}

		// 按照普通二叉树方式,将node插入
		Node pointer = mRoot;
		while (true) {
			if (node.value <= pointer.value) {
				if (pointer.left == null) {
					pointer.left = node;
					node.pre = pointer;
					break;
				} else {
					pointer = pointer.left;
				}
			} else {
				if (pointer.right == null) {
					pointer.right = node;
					node.pre = pointer;
					break;
				} else {
					pointer = pointer.right;
				}
			}

		}
		// 将node的颜色改为Red
		node.color = Color.Red;
	}

	/**
	 * 插入修正
	 * 
	 * @param node 新插入的节点
	 */
	private void insertFixup(Node node) {
		Node pointer = node;
		while (true) {
			// 如果当前指针已经指向了根节点
			if (pointer.pre == null) {
				pointer.color = Color.Black;
				break;
			}

			// 如果指针节点的父节点为黑色,不会影响红黑树特性,无须修正
			if (pointer.pre.color == Color.Black) {
				break;
			}

			// 如果指针节点的父节点为红色,则必然有爷爷节点(因为根节点是黑色的)
			if (pointer.pre.color == Color.Red) {
				Node father = pointer.pre;
				Node grandpa = father.pre;
				Node uncle = (father == grandpa.left ? grandpa.right : grandpa.left);

				// 如果叔叔节点是null/黑色(此时已经无法通过调整颜色来符合红黑树特性),因此需要旋转
				if (uncle == null || uncle.color == Color.Black) {
					if (father == grandpa.left && pointer == father.right) { // 对pointer节点左旋转
						leftRotate(pointer);
						pointer = father;
						father = father.pre;
					}
					if (father == grandpa.left && pointer == father.left) { // 对father节点右旋转
						rightRotate(father);
						break;
					}
					if (father == grandpa.right && pointer == father.left) { // 对pointer节点右旋转
						rightRotate(pointer);
						pointer = father;
						father = father.pre;
					}
					if (father == grandpa.right && pointer == father.right) { // 对father节点左旋转
						leftRotate(father);
						break;
					}
				} else if (uncle.color == Color.Red) { // 如果叔叔节点也是红色
					// 将父节点和叔叔节点颜色变为黑色,爷爷节点变为红色
					/**
					 * 原因:颜色冲突,必须要改变部分节点的颜色。如果改变指针节点(初始为新增的node节点)颜色为黑色,必然使得根节点到指针节点的黑节点数目增1,
					 * 如果还有其它路径,那么其它路径的黑节点数目并未增1,导致各路径的黑色节点数目不一致(违反红黑树特性)。因此需要将父节点颜色由红色变为黑色,
					 * 这样会使该路径的黑节点数目增1,导致各路径黑节点数目不一致,因此需要将爷爷节点颜色由黑色(父节点颜色是红色,爷爷节点必是黑色,否则不满足红黑树特性)变为红色,
					 * 如此虽使得本路径黑节点数目不增加,可是叔叔路径的黑节点数目却少1并且和爷爷节点颜色皆为红色,因此还需将叔叔节点颜色由红色变为黑色。
					 * 如此可确保各路径的黑色节点数目一致并解决爷爷节点之下的颜色冲突!
					 * 此时爷爷节点可能存在颜色冲突问题,因此还需要继续判断,此时指针指向爷爷节点(每次执行都会使得指针上移,如果最终移动到根节点,则根节点保持黑色,各路径黑色节点数目增1)
					 */
					father.color = Color.Black;
					uncle.color = Color.Black;
					if (grandpa.pre != null) {
						grandpa.color = Color.Red;
					}
					pointer = grandpa;
				}
			}
		}
	}

	/**
	 * 查找首个值是value的Node
	 * 
	 * @param node  根节点
	 * @param value 待查找的节点值
	 * @return 如果存在值是value的节点,则返回该节点对象;否则,返回null
	 */
	public Node searchNode(Node node, int value) {
		if (node != null) {
			if (node.value == value) {
				return node;
			} else if (value < node.value) {
				return searchNode(node.left, value);
			} else {
				return searchNode(node.right, value);
			}
		}
		return null;
	}

	public void delete(int value) {
		Node node = searchNode(mRoot, value);
		if (node != null) {
			delete(node);
		}
	}

	public void delete(Node node) {
		if (node != null) {
			Node pointer = node;
			int tempValue;
			boolean first = true;
			// 目标节点:前驱,value小于或等于pointer节点的最大值节点(若pointer左右孩子都存在,则找;否则,不用找)
			// 也可以使用后继,value大于或等于pointer节点的最小值节点(若pointer左右孩子都存在,则找;否则,不用找)
			// 采用的是前驱,此处前驱也可能是自己
			Node target = pointer;
			while ((first && target.left != null) || (!first && target.right != null)) {
				if (first) {
					target = target.left;
					first = false;
				} else {
					target = target.right;
				}
			}

			// 指针节点和目标节点交换数据
			if (target != pointer) {
				tempValue = pointer.value;
				pointer.value = target.value;
				target.value = tempValue;
			}

			System.out.println("target value: " + target.value);

			// 待删节点颜色是黑色:先修正
			if (target.color == Color.Black) { // 待删节点颜色是黑色(TODO 此处繁琐)
				deleteFixup(target);
			}

			// 从树中移除target节点
			Node father = target.pre;
			// 如果target节点是根节点
			if (father == null) { // 因为采用前驱,如果target是根节点,则target没有左孩子
				Node targetRight = target.right;
				mRoot = targetRight;
				if (targetRight != null) {
					targetRight.pre = null;
				}
				mRoot.color = Color.Black;
			} else { // 如果target节点不是根节点
				if (target.left == null && target.right == null) {
					if (target == father.right) {
						father.right = null;
					} else {
						father.left = null;
					}
				} else if (target.left != null && target.right == null) {
					Node child = target.left;
					if (target == father.left) {
						father.left = child;
					} else {
						father.right = child;
					}
					child.pre = father;
				} else if (target.left == null && target.right != null) { // target可能和pointer都指向node
					Node child = target.right;
					if (target == father.left) {
						father.left = child;
					} else {
						father.right = child;
					}
					child.pre = father;
				}
			}
			// 释放、销毁target节点
			target.destruct();
			target = null;
		}
	}

	/**
	 * 修正红黑树结构,为删除黑色node节点做准备
	 * 
	 * @param node 待删除节点,节点颜色是黑色
	 */
	private void deleteFixup(Node node) {
		// 所有经过pointer节点的路径黑色节点数目都会被减1,因此想办法把经过pointer节点的路径上增加一个黑色节点
		Node pointer = node;
		Node father;
		Node brother;

		while (true) { // 每次循环都有可能更新pointer、brother、father
			father = pointer.pre;
			// 如果pointer节点是根节点
			if (father == null) {
				pointer.color = Color.Black;
				break;
			}

			if (pointer.color == Color.Red) {
				pointer.color = Color.Black;
				break;
			}

			brother = (pointer == father.left ? father.right : father.left);

			// 下面代码,pointer上移且指向黑色节点
			if (brother == null) {
				if (father.color == Color.Red) {
					father.color = Color.Black;
					break;
				} else {
					pointer = father;
				}
			}

			// 如果兄弟节点是红色
			// 因为指针节点是黑色的;兄弟节点是红色的,所以兄弟节点至少有一个直接孩子,并且该孩子是黑色的(各路径黑色节点一样;父节点是红色,孩子节点必是黑色)
			// 因为兄弟节点是红色的,父节点必然是黑色的(父节点如果是红色的,兄弟节点则必是黑色)
			if (brother != null && brother.color == Color.Red) {
				if (brother == father.left) {
					rightRotate(brother);
				} else {
					leftRotate(brother);
				}
			} else if (brother != null && brother.color == Color.Black) { // 如果兄弟节点是黑色
				Node brotherLeft = brother.left;
				Node brotherRight = brother.right;

				if (brother == father.left) {
					// brotherLeft、brotherRight一共有9种组合
					// 如果是此四种情况:brotherLeft为null且brotherRight为null;brotherLeft为null且brotherRight为黑色;brotherLeft为黑色且brotherRight为null;brotherLeft为黑色且brotherRight为黑色
					if ((brotherLeft == null || brotherLeft.color == Color.Black)
							&& (brotherRight == null || brotherRight.color == Color.Black)) {
						// 将brother颜色变为红色,则经过brother的路径黑色节点数目会减1;因为node(初始pointer和node节点为同一个对象)节点为黑色且要被删除。
						// 这样经过pointer和brother的路径都会减1,问题即转为:经过pointer、brother的父节点路径的黑色节点数目减1
						// Pointer上移指向父节点(如果得不到解决,pointer就上移一次,直至指向根节点)
						// 如果父节点是红色,则变为黑色,这样一增一减就平衡了;如果父节点是黑色的,则继续判断
						brother.color = Color.Red;
						pointer = father;
					} else if ((brotherLeft == null || brotherLeft.color == Color.Black)
							&& (brotherRight != null && brotherRight.color == Color.Red)) {
						// 如果是此两种情况:brotherRight为红色且brotherLeft为null;brotherRight为红色且brotherLeft为黑色
						leftRotate(brotherRight);
					} else if ((brotherLeft != null && brotherLeft.color == Color.Red)) {
						// 如果是此三种情况:brotherLeft为红色且brotherRight为null;brotherLeft为红色且brotherRight为红色;brotherLeft为红色且brotherRight为黑色
						rightRotate(brother);
						brotherLeft.color = Color.Black;
						break;
					}
				} else {
					// brotherLeft、brotherRight一共有9中组合
					// 如果是此四种情况:brotherLeft为null且brotherRight为null;brotherLeft为null且brotherRight为黑色;brotherLeft为黑色且brotherRight为null;brotherLeft为黑色且brotherRight为黑色
					if ((brotherLeft == null || brotherLeft.color == Color.Black)
							&& (brotherRight == null || brotherRight.color == Color.Black)) {
						// 将brother颜色变为红色,则经过brother的路径黑色节点数目会减1;因为node(初始pointer和node节点为同一个对象)节点为黑色且要被删除。
						// 这样经过pointer和brother的路径都会减1,问题即转为:经过pointer、brother的父节点路径的黑色节点数目减1
						// Pointer上移指向父节点(如果得不到解决,pointer就上移一次,直至指向根节点)
						// 如果父节点是红色,则变为黑色,这样一增一减就平衡了;如果父节点是黑色的,则继续判断
						brother.color = Color.Red;
						pointer = father;
					} else if ((brotherLeft != null && brotherLeft.color == Color.Red)
							&& (brotherRight == null || brotherRight.color == Color.Black)) {
						// 如果是此两种情况:brotherLeft为红色且brotherRight为null;brotherLeft为红色且brotherRight为黑色
						rightRotate(brotherLeft);
					} else if ((brotherRight != null && brotherRight.color == Color.Red)) {
						// 如果是此三种情况:brotherRight为红色且brotherLeft为null;brotherRight为红色且brotherLeft为红色;brotherRight为红色且brotherLeft为黑色
						leftRotate(brother);
						brotherRight.color = Color.Black;
						break;
					}
				}
			}
		}
	}

	/**
	 * 对node节点进行左旋转
	 * 
	 * @param node
	 */
	private void leftRotate(Node node) {
		Node father = node.pre;
		Node grandpa = father.pre;
		node.pre = grandpa;
		if (grandpa != null) {
			if (father == grandpa.left) {
				grandpa.left = node;
			} else {
				grandpa.right = node;
			}
		} else { // father是根节点
			mRoot = node;
		}
		Node nodeLeft = node.left;
		node.left = father;
		father.pre = node;
		father.right = nodeLeft;
		if (nodeLeft != null) {
			nodeLeft.pre = father;
		}
		Color tempColor = node.color;
		node.color = father.color;
		father.color = tempColor;
	}

	/**
	 * 对node节点进行右旋转
	 * 
	 * @param node
	 */
	private void rightRotate(Node node) {
		Node father = node.pre;
		Node grandpa = father.pre;
		node.pre = grandpa;
		if (grandpa != null) {
			if (father == grandpa.left) {
				grandpa.left = node;
			} else {
				grandpa.right = node;
			}
		} else { // father是根节点
			mRoot = node;
		}
		Node nodeRight = node.right;
		node.right = father;
		father.pre = node;
		father.left = nodeRight;
		if (nodeRight != null) {
			nodeRight.pre = father;
		}
		Color tempColor = node.color;
		node.color = father.color;
		father.color = tempColor;
	}

	public int getMaxValue(Node node) {
		if (node == null) {
			throw new NullPointerException("node is null");
		}
		return getMaxValue(node, Integer.MIN_VALUE);
	}

	/**
	 * 获取根节点rootNode所形成树的结点value最大值
	 * 
	 * @param node  以node为根节点的树
	 * @param value 默认值(如果没有节点数值比value大,则返回value;否则返回节点的最大值。因此value需要传一个最小值)
	 * @return
	 */
	private int getMaxValue(Node node, int value) {
		if (node != null) {
			value = node.value;
			int leftValue = getMaxValue(node.left, value);
			int rightValue = getMaxValue(node.right, value);
			if (leftValue > value || rightValue > value) {
				value = (leftValue > rightValue ? leftValue : rightValue);
			}
		}
		return value;
	}

	@Override
	public String toString() {
		String content;
		if (mTreeStyle) {
			content = generateTree(mRoot);
		} else {
			return getClass().getName();
		}
		return content;
	}

	/**
	 * 生成以node为根节点的树状结构信息内容,然后返回
	 * 
	 * @return
	 */
	String generateTree(Node node) {
		int height = getTreeHeightWidths(node, 0, new ArrayList<Integer>());
		int maxValue = getMaxValue(node);
		int singleLen = String.valueOf(maxValue).length();
		int capacity = 1;
		for (int i = 0; i < height; i++) {
			capacity <<= 1;
		}
		Map<Integer, String> map = new HashMap<Integer, String>(capacity);
		generateTree(node, height - 1, singleLen, 0, 0, map);
		StringBuilder sb = new StringBuilder();
		String value;
		int LeftLen;
		int tempLen;
		int i;
		// 当前行之上的所有节点之和(包括存在的和不存在的)
		int preNum;
		// 当前行应该有多少个节点(包括存在的和不存在的)
		int curRowNum;
		for (int curHeight = 0; curHeight < height; curHeight++) {
			curRowNum = 1;
			for (i = 0; i < curHeight; i++) {
				curRowNum <<= 1;
			}
			preNum = curRowNum - 1;

			for (LeftLen = -1, i = 0; i < curRowNum; i++) {
				value = map.get(preNum + i);
				if (value != null) {
					if (LeftLen == -1) {
						LeftLen = value.length();
					} else {
						tempLen = value.length();
						value = value.substring(LeftLen);
						LeftLen = tempLen;
					}
					sb.append(value);
				}
			}
			sb.append("\n");
		}
		return sb.toString();
	}

	void printTreeValues(Node node) {
		int height = getTreeHeightWidths(node, 0, new ArrayList<Integer>());
		int maxValue = getMaxValue(node);
		int singleLen = String.valueOf(maxValue).length();
		int capacity = 1;
		for (int i = 0; i < height; i++) {
			capacity <<= 1;
		}
		Map<Integer, Message> map = new HashMap<Integer, Message>(capacity);
		generateTreeMessage(node, height - 1, singleLen, 0, 0, map);
		Message msg;
		String value;
		int LeftLen;
		int tempLen;
		int i;
		// 当前行之上的所有节点之和(包括存在的和不存在的)
		int preNum;
		// 当前行应该有多少个节点(包括存在的和不存在的)
		int curRowNum;
		for (int curHeight = 0; curHeight < height; curHeight++) {
			curRowNum = 1;
			for (i = 0; i < curHeight; i++) {
				curRowNum <<= 1;
			}
			preNum = curRowNum - 1;

			for (LeftLen = -1, i = 0; i < curRowNum; i++) {
				msg = map.get(preNum + i);
				if (msg != null) {
					value = msg.value;
					if (LeftLen == -1) {
						LeftLen = value.length();
					} else {
						tempLen = value.length();
						value = value.substring(LeftLen);
						LeftLen = tempLen;
					}
					if (msg.color == Color.Black) {
						System.out.print(value);
					} else if (msg.color == Color.Red) {
						// TODO 注意:System.out和System.err同时使用,发现存在显示错位问题。将System.err换成System.out,显示正常
						System.err.print(value);
					}
				}
			}
			System.out.print("\n");
		}
	}

	/**
	 * 遍历以node节点为树根的树的所有节点。将value以特定字符串形式存储在map中
	 * 
	 * 
	 * @param node       根节点
	 * @param treeHeight 以node为根节点的树的高度(注意:高度从0开始计数)
	 * @param unitLen    单位字符串长度
	 * @param curHeight  当前节点node在树种的高度(注意:高度从0开始计数)
	 * @param LeftCount  node节点的左侧节点个数
	 * @param map        将各个节点的value值,以特定的字符串存储在map中
	 */
	private void generateTree(Node node, int treeHeight, int unitLen, int curHeight, int LeftCount,
			Map<Integer, String> map) {
		if (node != null) {
			int key = 1;
			for (int i = 0; i < curHeight; i++) {
				key <<= 1;
			}
			key--;
			key += LeftCount;

			String value = String.valueOf(node.value);
			int spaceLen = (1 << (treeHeight - curHeight)) * LeftCount * unitLen
					+ (((1 << (treeHeight - curHeight)) * unitLen - value.length()) >> 1);
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < spaceLen; i++) {
				sb.append(' ');
			}
			sb.append(value);
			map.put(key, sb.toString());

			generateTree(node.left, treeHeight, unitLen, curHeight + 1, LeftCount << 1, map);
			generateTree(node.right, treeHeight, unitLen, curHeight + 1, (LeftCount << 1) + 1, map);
		}
	}

	/**
	 * 遍历以node节点为树根的树的所有节点。将color、value以特定字符串形式存储在map中
	 * 
	 * 
	 * @param node       根节点
	 * @param treeHeight 以node为根节点的树的高度(注意:高度从0开始计数)
	 * @param unitLen    单位字符串长度
	 * @param curHeight  当前节点node在树种的高度(注意:高度从0开始计数)
	 * @param LeftCount  node节点的左侧节点个数
	 * @param map        将各个节点的value值,以特定的字符串存储在map中
	 */
	private void generateTreeMessage(Node node, int treeHeight, int unitLen, int curHeight, int LeftCount,
			Map<Integer, Message> map) {
		if (node != null) {
			int key = 1;
			for (int i = 0; i < curHeight; i++) {
				key <<= 1;
			}
			key--;
			key += LeftCount;

			String value = String.valueOf(node.value);
			int spaceLen = (1 << (treeHeight - curHeight)) * LeftCount * unitLen
					+ (((1 << (treeHeight - curHeight)) * unitLen - value.length()) >> 1);
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < spaceLen; i++) {
				sb.append(' ');
			}
			sb.append(value);
			map.put(key, new Message(node.color, sb.toString()));

			generateTreeMessage(node.left, treeHeight, unitLen, curHeight + 1, LeftCount << 1, map);
			generateTreeMessage(node.right, treeHeight, unitLen, curHeight + 1, (LeftCount << 1) + 1, map);
		}
	}

	/**
	 * 获取根节点rootNode所形成树的高度和各层宽度
	 * 
	 * @param node   根节点
	 * @param height 起始计数,从0开始
	 * @param list   各层节点的宽度(个数)
	 * @return 返回值是根节点rootNode所形成树的高度,list里面保存了各层节点的宽度
	 */
	int getTreeHeightWidths(Node node, int height, List<Integer> list) {
		if (list == null) {
			throw new NullPointerException("width list is null");
		}
		if (node != null) {
			height++;
			int size = list.size();
			for (int i = size; i < height; i++) {
				list.add(0);
			}
			int index = height - 1;
			int value = list.get(index);
			list.set(index, ++value);
			int leftHeight = getTreeHeightWidths(node.left, height, list);
			int rightHeight = getTreeHeightWidths(node.right, height, list);
			if (leftHeight > height || rightHeight > height) {
				height = (leftHeight > rightHeight ? leftHeight : rightHeight);
			}
		}
		return height;
	}

	// 没写全
	void checkLegal(Node node) {
		if (node != null) {
			if (node.left != null) {
				if (node.left.pre != node) {
					System.out.println("Illegal node value: " + node.value + "left value: " + node.left.value);
					return;
				}
			}
			if (node.right != null) {
				if (node.right.pre != node) {
					System.out.println("Illegal node value: " + node.value + "right value: " + node.right.value);
					return;
				}
			}

			checkLegal(node.left);

			checkLegal(node.right);
		}
	}
}

class Message {
	Color color;
	String value;

	Message(Color color, String value) {
		this.color = color;
		this.value = value;
	}
}

/**
 * 左孩子的value小于或等于node的value,右孩子的value大于node的value
 * 
 * @author guigui
 *
 */
class Node {
	int value;
	Color color;
	Node pre;
	Node left;
	Node right;

	Node(int value) {
		this.value = value;
	}

	Node(int value, Color color) {
		this.value = value;
		this.color = color;
	}

	void destruct() {
		pre = null;
		left = null;
		right = null;
	}
}

enum Color {
	Red, Black
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值