Java JDK1.8 HashMap红黑树的学习

JDK1.8中的HashMap的结构为 数组+链表+红黑树,本文基于JDK1.8中的HashMap源码中的红黑树操作进行学习分析来实现红黑树的新增、删除、查询。需要提到一点是JDK源码中在 if 判断语句中进行了大量的赋值操作,阅读一定要注意 if 中是判断还是赋值。

红黑树是一种含有红黑结点并能自平衡的二叉查找树。

  • 每个节点要么是黑色,要么是红色。
  • 根节点是黑色。
  • 每个叶子结点(NIL)是黑色。
  • 每个红色结点的两个子节点一定都是黑色。
  • 任一结点到每个叶子结点的路径都包含数量相同的黑结点。

平衡操作

  • 左旋:以某个结点作为中心,其右子节点的左子节点变为其右子节点,其右子节点变为其父节点并成为新的中心,左子节点不变。
  • 右旋:以某个结点作为中心,其左子节点的右子节点变为其左子节点,其左子节点变为其父节点并成为新的中心,右子节点不变。
  • 变色:结点由红变黑或由黑变红。

核心的数据结构

TreeNode<T> parent; 	// 父节点
TreeNode<T> left; 		// 左节点
TreeNode<T> right; 		// 右节点
boolean red; 		// 节点颜色:true(红色),false(黑色)
T key; 			// key值
int hash; 			// hash值,判断节点位置

平衡插入的逻辑:

1、插入结点的父节点为空,将当前结点设置为黑色,返回当前节点(设置为根节点)。
2、插入结点父节点是黑色,插入结点祖父节点为空,返回根节点。
3、插入结点的父节点是祖父节点的左节点
    1.祖父节点的右节点不为空并且是红色:父节点设置为黑色,祖父节点设置为红色,祖父节点的右节点设置为黑色,祖父节点设置为当前节点开始新一轮的循环。
    2.祖父节点为空或者黑色
        插入结点是父节点的右节点:对父节点进行左旋,如果父节点不为空,设为祖父节点。
        插入结点父节点不为空:设置父节点为黑色。如果祖父节点不为空,祖父节点设置为红色,并将祖父节点右旋。
4、插入结点的父节点是祖父节点的右节点(类似操作)
    1.祖父节点的左节点不为空并且是红色:父节点设置为黑色,祖父节点设置为红色,祖父节点左节点设置为黑色,祖父节点设置为当前节点进行新一轮的循环。
    2.祖父节点为空或者黑色
        插入结点是父节点的左节点:对父节点进行右旋,如果父节点不为空,设置祖父节点。
        插入结点父节点不为空:设置祖父节点为黑色。如果祖父节点不为空,祖父节点设置为红色,并将祖父节点左旋。

左旋右旋:

左旋
1、旋转结点不为空并且旋转结点的右节点不为空
    1.将旋转结点的右节点的左节点设置为旋转结点的右节点
        如果旋转结点右节点的左节点不为空,将旋转设置为旋转结点右节点的左节点的父节点
    2.将旋转结点的父节点设施为旋转结点的右节点的父节点
        如果旋转结点父节点为空,将旋转结点右节点设置根节点,颜色设置为黑色。
        如果旋转结点父节点不为空:
            旋转结点是其父节点的左节点:旋转结点的右节点设置为旋转结点父节点的左节点
            旋转结点是其父节点的右节点:旋转结点的右节点设置为旋转结点父节点的右节点
    3.将旋转结点左节点设置为其又结点的左节点
    4.将旋转结点右节点设置为旋转结点的父节点
右旋(类似左旋)

github

下面是代码

package com.datastructure.rbtree;

/**
 * 
 * 红黑树
 * 
 * @param <T>
 * 
 * @version 1.0
 */
public class RBTree<T extends Comparable<T>> {

	private TreeNode<T> root;

	public class TreeNode<T extends Comparable<T>> {

		TreeNode<T> parent; 	// 父节点
		TreeNode<T> left; 		// 左节点
		TreeNode<T> right; 		// 右节点
		boolean red; 			// 节点颜色:true(红色),false(黑色)
		T key; 					// key值
		int hash; 				// hash值,判断节点位置

		public TreeNode(T key, boolean red, TreeNode<T> parent, TreeNode<T> left, TreeNode<T> right) {
			this.key = key;
			this.red = red;
			this.parent = parent;
			this.left = left;
			this.right = right;
			this.hash = key.hashCode();
		}

	}

	/**
	 * 
	 * 方法描述:遍历
	 *
	 */
	public void preOrder() {
		preOrder(root());
		System.out.println();
	}
	
	public void preOrder(TreeNode<T> tree) {
		if (tree != null) {
			StringBuffer treeBuffer = new StringBuffer();
			treeBuffer.append(tree.key);
			treeBuffer.append("(" + (tree.red ? "红色" : "黑色") + ")");
			treeBuffer.append((tree.parent != null ? "" : " 是根节点"));
			treeBuffer.append("     其左子节点:" + (tree.left != null ? tree.left.key : "NIL(空)"));
			treeBuffer.append("     其右子节点:" + (tree.right != null ? tree.right.key : "NIL(空)"));
			System.out.println(treeBuffer);
			preOrder(tree.left);
			preOrder(tree.right);
		}
	}

	public RBTree() {
		root = null;
	}

	/**
	 * 
	 * 方法描述:获取根节点
	 * 
	 * @return
	 * 
	 */
	public TreeNode<T> root() {
		if (root == null) {
			return this.root;
		}
		for (TreeNode<T> r = root, p;;) {
			if ((p = r.parent) == null)
				return r;
			r = p;
		}
	}

	/**
	 * 
	 * 方法描述:获取查询节点对象
	 * 
	 * @param k
	 * @return
	 * 
	 */
	public TreeNode<T> find(T k) {
		int h = k.hashCode();
		TreeNode<T> p = root();
		do {
			int ph;
			T pk;
			TreeNode<T> pl = p.left, pr = p.right, q;
			if ((ph = p.hash) > h)
				p = pl;
			else if (ph < h)
				p = pr;
			else if ((pk = p.key) == k || (k != null && k.equals(pk)))
				return p;
			else if (pl == null)
				p = pr;
			else if (pr == null)
				p = pl;
			else if ((q = find(k)) != null)
				return q;
			else
				p = pl;
		} while (p != null);
		return null;
	}

	/**
	 * 
	 * 方法描述:插入结点
	 * 
	 * @param t
	 * 
	 */
	public void putTreeVal(T t) {
		int h = t.hashCode();
		System.out.println("新增数据:" + t);
		TreeNode<T> root = root();
		if (root == null) {
			System.out.println("初始化根节点");
			this.root = new TreeNode<T>(t, false, null, null, null);
			return;
		}
		for (TreeNode<T> p = root;;) {
			int dir = 0, ph;
			if ((ph = p.key.hashCode()) > h)
				dir = -1;
			else if (ph < h)
				dir = 1;
			TreeNode<T> xp = p;
			if ((p = (dir <= 0) ? p.left : p.right) == null) {
				TreeNode<T> x = new TreeNode<T>(t, false, null, null, null);
				if (dir <= 0)
					xp.left = x;
				else
					xp.right = x;
				x.parent = xp;
				balanceInsertion(root, x);
				return;
			}
		}
	}

	/**
	 * 
	 * 方法描述:移除结点
	 * 
	 * @param t
	 * 
	 */
	public void removeTreeVal(T t) {
		System.out.println("删除节点:" + t);
		TreeNode<T> first = root(), rl;
		if (first == null)
			return;
		if (root.parent != null)
			root = root();
		if (root == null || ((root.right == null || (rl = root.left) == null || rl.left == null))) {
			return;
		}
		TreeNode<T> p = find(t);
		TreeNode<T> pl = p.left, pr = p.right, replacement;
		if (pl != null && pr != null) {
			TreeNode<T> s = pr, sl;
			while ((sl = s.left) != null)
				s = sl;
			boolean c = s.red;
			s.red = p.red;
			p.red = c;
			TreeNode<T> sr = s.right;
			TreeNode<T> pp = p.parent;
			if (s == pr) {
				p.parent = s;
				s.right = p;
			} else {
				TreeNode<T> sp = s.parent;
				if ((p.parent = sp) != null) {
					if (s == sp.left)
						sp.left = p;
					else
						sp.right = p;
				}
				if ((s.right = pr) != null)
					pr.parent = s;
			}
			p.left = null;
			if ((p.right = sr) != null)
				sr.parent = p;
			if ((s.left = pl) != null)
				pl.parent = s;
			if ((s.parent = pp) == null)
				root = s;
			else if (p == pp.left)
				pp.left = s;
			else
				pp.right = s;
			if (sr != null)
				replacement = sr;
			else
				replacement = p;
		} else if (pl != null)
			replacement = pl;
		else if (pr != null)
			replacement = pr;
		else
			replacement = p;
		if (replacement != p) {
			TreeNode<T> pp = replacement.parent = p.parent;
			if (pp == null)
				root = replacement;
			else if (p == pp.left)
				pp.left = replacement;
			else
				pp.right = replacement;
			p.left = p.right = p.parent = null;
		}
		balanceDeletion(root, replacement);
		if (replacement == p) {
			TreeNode<T> pp = p.parent;
			p.parent = null;
			if (pp != null) {
				if (p == pp.left)
					pp.left = null;
				else if (p == pp.right)
					pp.right = null;
			}
		}
	}

	/**
	 * 
	 * 方法描述:插入时保持红黑树结构
	 * 
	 * xp 	父节点				xp = x.parent
	 * xpp 	祖父节点				xpp = xp.parent
	 * xppl	祖父节点的左节点			xppl = xpp.left
	 * xppr	祖父节点的右节点			xppr = xpp.right
	 * 
	 * @param root
	 * @param x
	 * @return
	 * 
	 */
	public TreeNode<T> balanceInsertion(TreeNode<T> root, TreeNode<T> x) {
		// 插入结点设置为红色
		x.red = true;	
		for (TreeNode<T> xp, xpp, xppl, xppr;;) {
			// 父节点为空,那么当前节点作为根节点,并设置为黑色
			if ((xp = x.parent) == null) {
				x.red = false;
				return x;
			// 父节点是黑色或者祖父节点为空,不用处理,直接插入
			} else if (!xp.red || (xpp = xp.parent) == null)
				return root;
			// 父节点是祖父节点的左节点
			if (xp == (xppl = xpp.left)) {
				// 祖父节点的右节点不为空并且是红色
				if ((xppr = xpp.right) != null && xppr.red) {
					xppr.red = false;	// 祖父节点右节点设置为黑色
					xp.red = false;		// 父节点设置为黑色
					xpp.red = true;		// 祖父节点设置为红色
					x = xpp;			// 祖父节点设置为当前节点,可以开始新一轮的循环
				// 祖父节点的右节点为空或者是黑色
				} else {
					// 插入结点是父节点的右节点
					if (x == xp.right) {
						root = rotateLeft(root, x = xp);					// 父节点左旋
						xpp = (xp = x.parent) == null ? null : xp.parent;	// 设置祖父节点
					}
					// 父节点不为空
					if (xp != null) {
						xp.red = false;						// 设置父节点为黑色
						// 祖父节点不为空
						if (xpp != null) {
							xpp.red = true;					// 祖父节点设置为红色
							root = rotateRight(root, xpp);	// 祖父节点右旋
						}
					}
				}
			// 父节点是祖父节点右节点(与上边对称)
			} else {
				if (xppl != null && xppl.red) {
					xppl.red = false;
					xp.red = false;
					xpp.red = true;
					x = xpp;
				} else {
					if (x == xp.left) {
						root = rotateRight(root, x = xp);
						xpp = (xp = x.parent) == null ? null : xp.parent;
					}
					if (xp != null) {
						xp.red = false;
						if (xpp != null) {
							xpp.red = true;
							root = rotateLeft(root, xpp);
						}
					}
				}
			}
		}
	}

	/**
	 * 
	 * 方法描述:删除时保持红黑树结构
	 * 
	 * @param root
	 * @param x
	 * @return
	 * 
	 */
	public TreeNode<T> balanceDeletion(TreeNode<T> root, TreeNode<T> x) {
		for (TreeNode<T> xp, xpl, xpr;;) {
			if (x == null || x == root)
				return root;
			else if ((xp = x.parent) == null) {
				x.red = false;
				return x;
			} else if (x.red) {
				x.red = false;
				return root;
			} else if ((xpl = xp.left) == x) {
				if ((xpr = xp.right) != null && xpr.red) {
					xpr.red = false;
					xp.red = true;
					root = rotateLeft(root, xp);
					xpr = (xp = x.parent) == null ? null : xp.right;
				}
				if (xpr == null)
					x = xp;
				else {
					TreeNode<T> sl = xpr.left, sr = xpr.right;
					if ((sr == null || !sr.red) && (sl == null || !sl.red)) {
						xpr.red = true;
						x = xp;
					} else {
						if (sr == null || !sr.red) {
							if (sl != null)
								sl.red = false;
							xpr.red = true;
							root = rotateRight(root, xpr);
							xpr = (xp = x.parent) == null ? null : xp.right;
						}
						if (xpr != null) {
							xpr.red = (xp == null) ? false : xp.red;
							if ((sr = xpr.right) != null)
								sr.red = false;
						}
						if (xp != null) {
							xp.red = false;
							root = rotateLeft(root, xp);
						}
						x = root;
					}
				}
			} else {
				if (xpl != null && xpl.red) {
					xpl.red = false;
					xp.red = true;
					root = rotateRight(root, xp);
					xpl = (xp = x.parent) == null ? null : xp.left;
				}
				if (xpl == null)
					x = xp;
				else {
					TreeNode<T> sl = xpl.left, sr = xpl.right;
					if ((sl == null || !sl.red) && (sr == null || !sr.red)) {
						xpl.red = true;
						x = xp;
					} else {
						if (sl == null || !sl.red) {
							if (sr != null)
								sr.red = false;
							xpl.red = true;
							root = rotateLeft(root, xpl);
							xpl = (xp = x.parent) == null ? null : xp.left;
						}
						if (xpl != null) {
							xpl.red = (xp == null) ? false : xp.red;
							if ((sl = xpl.left) != null)
								sl.red = false;
						}
						if (xp != null) {
							xp.red = false;
							root = rotateRight(root, xp);
						}
						x = root;
					}
				}
			}
		}
	}

	/**
	 * 
	 * 方法描述:左旋
	 * 左旋:以某个结点作为中心,其右子节点的左子节点变为其右子节点,其右子节点变为其父节点并成为新的中心,左子节点不变。
	 * p	旋转结点
	 * pp	旋转结点父节点
	 * r	旋转结点右节点
	 * rl	旋转结点右节点的左节点
	 * 
	 * @param root
	 * @param p
	 * @return
	 * 
	 */
	public TreeNode<T> rotateLeft(TreeNode<T> root, TreeNode<T> p) {
		TreeNode<T> r, pp, rl;
		if (p != null && (r = p.right) != null) {	// 旋转结点和旋转结点的右节点不为空
			if ((rl = p.right = r.left) != null)	// 将旋转结点右节点的左节点设置为旋转结点的右节点,并判断旋转结点右节点的左节点是否为空
				rl.parent = p;						// 如果旋转结点右节点的左节点不为空,将其的父节点设置为旋转结点
			if ((pp = r.parent = p.parent) == null)	// 将旋转结点的父节点设置为旋转结点右节点的父节点,并判断旋转结点父节点是否为空
				(root = r).red = false;				// 如果旋转结点父节点为空,将旋转结点右节点设置为根节点,颜色设置为黑色
			else if (pp.left == p)					// 如果旋转结点父节点的左节点是旋转结点
				pp.left = r;						// 将旋转结点的右节点设置为旋转结点父节点的左节点
			else
				pp.right = r;						// 否则将旋转结点右节点设置为旋转结点父节点的右节点
			r.left = p;								// 将旋转结点左节点设置为其又结点的左节点
			p.parent = r;							// 将旋转结点右节点设置为旋转结点的父节点
		}
		return root;
	}

	/**
	 * 
	 * 方法描述:右旋
	 * 类似左旋
	 * 
	 * @param root
	 * @param p
	 * @return
	 * 
	 */
	public TreeNode<T> rotateRight(TreeNode<T> root, TreeNode<T> p) {
		TreeNode<T> l, pp, lr;
		if (p != null && (l = p.left) != null) {
			if ((lr = p.left = l.right) != null)
				lr.parent = p;
			if ((pp = l.parent = p.parent) == null)
				(root = l).red = false;
			else if (pp.right == p)
				pp.right = l;
			else
				pp.left = l;
			l.right = p;
			p.parent = l;
		}
		return root;
	}

}

测试代码

package com.datastructure.rbtree;

/**
 * 
 * 红黑树测试
 * 
 * @version 1.0
 */
public class RBTreeTest {
	
	public static void main(String[] args) {
		final int a[] = {10, 90, 20, 80, 30, 70, 40, 50, 60};
		
		RBTree<Integer> tree = new RBTree<Integer>();
		for (int i : a) {
			tree.putTreeVal(i);
			tree.preOrder();
		}
		
		tree.removeTreeVal(60);
		tree.preOrder();
		
		tree.removeTreeVal(50);
		tree.preOrder();
	}
	
}

 

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值