今天就昨天的分析写了一下代码,时间关系,因为还要上班,所以删除代码还没写出来。。。 先弄出来新增节点,查询(这个其实是现成的,就是搜索树的查询)和判断树是否是红黑树。
首先看红黑树节点的定义:
两个构造函数是关键:因为红黑树所有的叶子节点都是值为null的节点,也就是说,红黑树中所有的节点,除了值为null的节点之外,所有的节点都是有左右孩子的,所以当新创建一个节点的时候,要么创建一个null节点,要么创建一个节点,他的左右孩子都是null。另外因为节点里面定义了父节点,所以构造函数最好能把这个包含上,免得在树里赋值,略显麻烦。
final class RBNode<T extends Comparable<? super T>> extends TreeNode<T> { T value; RBNode<T> left; RBNode<T> right; // 颜色标识,true表示黑色,false表示红 boolean isBlack; //当前节点的父节点 RBNode<T> father; /** * 类RBNode.java的构造函数 * <p> * Title: 默认构造函数,构造一个T值为null的黑节点(叶子节点的属性) * </p> * <p> * Description: * </p> * createTime: 上午8:34:36 */ public RBNode(RBNode<T> father) { this.father = father; this.value = null; this.left = null; this.right = null; this.isBlack = true; } /** * 类RBNode.java的构造函数 * <p> * Title:构造一个有T值的红节点,并且他的两个孩子都是值为null的黑节点(默认新插入节点的属性) * </p> * <p> * Description: * </p> * createTime: 上午8:41:21 * * @param value */ public RBNode(RBNode<T> father,T value) { this.father = father; this.value = value; left = new RBNode<T>(this); right = new RBNode<T>(this); if(father == null){ isBlack = true; }else{ isBlack = false; } } @Override public T getValue() { return this.value; } /** * setBlack: 当前节点染成黑色 * void 返回类型 */ public void setBlack(){ this.isBlack = true; } /** * setRed: 当前节点染成红色 * void 返回类型 */ public void setRed(){ this.isBlack = false; } /** * setValue: 设置节点值 * * @return T 返回类型 */ public void setValue(T value) { this.value = value; } /** * getLeft: 获取左孩子节点节点 * * @return RBNode<T> 返回类型 */ public RBNode<T> getLeft() { return this.left; } /** * getRight: 获取右孩子节点节点 * * @return RBNode<T> 返回类型 */ public RBNode<T> getRight() { return this.right; } /** * getLeft: 设置左孩子节点节点 * * @return RBNode<T> 返回类型 */ public void setLeft(RBNode<T> leftChild) { this.left = leftChild; } /** * getRight: 设置右孩子节点节点 * * @return RBNode<T> 返回类型 */ public void setRight(RBNode<T> rightChild) { this.right = rightChild; } /** * 增加左子节点 addLeft: * * @param value * void 返回类型 */ public void addLeft(T value) { RBNode<T> leftChild = new RBNode<T>(this,value); this.left = leftChild; } /** * addRight: 增加右子节点 * * @param value * void 返回类型 */ public void addRight(T value) { RBNode<T> rightChild = new RBNode<T>(this,value); this.right = rightChild; // return null; // return 0; } public String toString(){ return value+"\t"; } }
红黑树的插入操作:
具体分析看这里:http://709002341.iteye.com/admin/blogs/2259248
@Override public void addNode(T value) { // TODO Auto-generated method stub RBNode<T> node = this.addNode(root,value); if(node == null){ System.out.println("增加失败,树里已经有值了"); return; } //如果插入节点的父亲不是黑色的 if(!node.father.isBlack){ adjustForAdd(node); } } /** * adjustForAdd: 调整节点 * void 返回类型 */ private void adjustForAdd(RBNode<T> node){ //用于情况一的递归结束: if(node.equals(this.root)){ node.setBlack(); return; } if(node.father.equals(this.root)){ return; } //叔叔节点 RBNode<T> uncle; //爷爷节点 RBNode<T> grandFather = node.father.father; if(grandFather.left.equals(node.father)){ uncle = grandFather.right; }else{ uncle = grandFather.left; } //处理情况1: 叔叔是红色的 if(!uncle.isBlack){ uncle.setBlack(); node.father.setBlack(); grandFather.setRed(); //递归处理: this.adjustForAdd(grandFather); //退出 return; }else{ //爷爷节点是否是左孩子 boolean isLeft = false; if(grandFather.father != null){ if(grandFather.father.left.equals(grandFather)){ isLeft = true; } } //调整后的子树跟节点 RBNode<T> root = null; //情况2:叔叔是黑色的,插入节点是父亲的左孩子,父亲是爷爷的左孩子 if(node.equals(node.father.left) && node.father.equals(grandFather.left)){ //以父节点为轴顺时针:LL root = rotateWithLeftChild(grandFather); }else if(node.equals(node.father.right) && node.father.equals(grandFather.left)){ //情况2:叔叔是黑色的,插入节点是父亲的右孩子,父亲是爷爷的左孩子 //以父节点为轴进行先逆后顺旋转,即RL旋转 root = rotateWithRL(grandFather); }else if(node.equals(node.father.right) && node.father.equals(grandFather.right)){ //情况3:叔叔是黑色的,插入节点是父亲的右孩子,父亲是爷爷的右孩子 //以父节点为轴进行逆时针旋转 root = rotateWithRightChild(grandFather); }else if(node.equals(node.father.left) && node.father.equals(grandFather.right)){ //情况4:叔叔是黑色的,插入节点是父亲的左孩子,父亲是爷爷的右孩子 //以父节点为轴进行逆时针旋转:RR root = rotateWithLR(grandFather); } //如果爷爷有父节点,把父节点指针指向新的爷爷节点 if(root.father != null){ if(isLeft){ root.father.setLeft(root); }else{ root.father.setRight(root); } }else{ //爷爷没有父节点,即爷爷就是根节点 this.root = root; } //把新爷爷节点和爷爷的孩子节点染色 root.setBlack(); root.left.setRed(); root.right.setRed(); } } /** * addNode: 在红黑树中插入值value * @param node 相对子树的根节点 * @param value * void 返回类型 返回插入值节点 */ public RBNode<T> addNode(RBNode<T> node, T value) { // TODO Auto-generated method stub if (node.getValue().compareTo(value) < 0) { if (node.getRight().value == null) { node.addRight(value); return node.right; } else { return addNode(node.getRight(), value); } } else if (node.getValue().compareTo(value) > 0) { if (node.getLeft().value == null) { node.addLeft(value); return node.left; } else { return addNode(node.getLeft(), value); } } //插入失败,树里面已经有值了 return null; }
查询:
@Override public boolean searchNode(T value) { // TODO Auto-generated method stub return this.searchNode(root, value) != null && this.searchNode(root, value).getValue() != null; } @Override public TreeNode<T> searchNode(TreeNode<T> root, T value) { // TODO Auto-generated method stub if(root == null || root.getValue() == null){ return null; }else if(root.getValue().compareTo(value) == 0){ return root; }else if(root.getValue().compareTo(value) > 0){ return this.searchNode(root.getLeft(), value); }else{ return this.searchNode(root.getRight(), value); } }
旋转:与搜索树略有不同的是红黑树旋转时要注意父节点的处理:
/** * rotateWithLeftChild: 带左子树旋转,适用于LL型(顺时针旋转) * * @param k2 * @return TreeNode 返回类型 */ private RBNode<T> rotateWithLeftChild(RBNode<T> t) { //定义临时节点k1,指向T的左孩子,旋转后替代T的位置 RBNode<T> k1 = t.getLeft(); k1.father = t.father; //将t(失衡节点)的左孩子指向k1(旋转后的t位置)的右孩子 t.setLeft(k1.getRight()); k1.getRight().father = t; //k1的右孩子位置指向t k1.setRight(t); t.father = k1; //以上,旋转完成 return k1; } /** * rotateWithRightChild: 带右子树旋转,适用于RR型(逆时针旋转) * * @param k1 * @return TreeNode 返回类型 */ private RBNode<T> rotateWithRightChild(RBNode<T> k1) { RBNode<T> k2 = k1.getRight(); k2.father = k1.father; k1.setRight(k2.getLeft()); k2.getLeft().father = k1; k2.setLeft(k1); k1.father = k2; return k2; } /** * rotateWithRL: 双旋转,先R后L * * @param k3 * @return TreeNode 返回类型 */ private RBNode<T> rotateWithRL(RBNode<T> k3) { k3.setLeft(rotateWithRightChild(k3.getLeft())); return rotateWithLeftChild(k3); } /** * rotateWithLR: 双旋转,先L后R * @param k1 * @return TreeNode 返回类型 */ private RBNode<T> rotateWithLR(RBNode<T> k1) { k1.setRight(rotateWithLeftChild(k1.getRight())); return rotateWithRightChild(k1); }
判断红黑树是否符合5项基本原则:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
其实,最主要的是判断4和5,利用两次遍历: 第一次遍历取所有的叶子节点,第二次遍历,查询叶子节点到根节点中黑节点的个数,并且查询是否中间有两个红色节点挨着。
/** * TreeIsRBTree: 两次遍历,查询该树是否红黑树 * * @param root * void 返回类型 */ public static <T> boolean TreeIsRBTree(Tree<T> t) { TreeNode<T> root = t.getRoot(); if(!((RBNode<? extends T>)t.getRoot()).isBlack){ return false; } //保存树节点,用于层次遍历 Queue<TreeNode<T>> q = new LinkedList<TreeNode<T>>(); //保存树的叶子节点 Queue<RBNode<? extends T>> qLeaf = new LinkedList<RBNode<? extends T>>(); q.offer(root); while (!q.isEmpty()) { TreeNode<T> temp = q.poll(); if(temp.getValue() == null){ qLeaf.offer((RBNode<? extends T>)temp); } if (temp.getLeft() != null) { q.offer(temp.getLeft()); } if (temp.getRight() != null) { q.offer(temp.getRight()); } } //从跟到叶子节点的黑节点个数 int num = 0; //是否进行黑节点个数初始化 boolean isNum = true; //判断树是否是红黑树 while(!qLeaf.isEmpty()){ RBNode<? extends T> temp = qLeaf.poll(); //当前叶子节点到根节点的黑节点个数 if(!temp.isBlack){ return false; } int number = 0; while(temp != null){ if(temp.father != null){ //两个红色的挨着 if(!temp.isBlack && !temp.father.isBlack){ return false; } } if(temp.isBlack){ if(isNum){ num ++; } number ++; } temp = temp.father; } isNum = false; if(number != num){ return false; } } return true; }
测试:
public static void main(String[] args) { // TODO Auto-generated method stub RBTree<Integer> t = new RBTree<Integer>(1); t.addNode(3); t.addNode(4); t.addNode(5); t.addNode(11); t.addNode(512); t.addNode(4123); t.addNode(7); t.addNode(2); t.addNode(4); System.out.println("中序遍历测试:"); TreeTools.midOrderTravel(t.getRoot()); System.out.println("\n前序遍历测试:"); TreeTools.preOrderTravel(t.getRoot()); System.out.println("\n后序遍历测试:"); TreeTools.backOrderTravel(t.getRoot()); System.out.println("\n层次遍历测试:"); TreeTools.levelTravel(t.getRoot()); System.out.println("\n树的深度:"+TreeTools.getTreeDepth(t.getRoot())); System.out.println("树的叶子个数:"+TreeTools.getLeafNum(t.getRoot())); System.out.println("该树是否是红黑树:"+TreeTools.TreeIsRBTree(t)); System.out.println("查询测试:"+t.searchNode(212321)); }
结果:
增加失败,树里已经有值了 中序遍历测试: 1 2 3 4 5 7 11 512 4123 前序遍历测试: 5 3 1 2 4 512 11 7 4123 后序遍历测试: 2 1 4 3 7 11 4123 512 5 层次遍历测试: 5 3 512 1 4 11 4123 2 7 树的深度:5 树的叶子个数:10 该树是否是红黑树:true 查询测试:false