java-红黑树-代码实现(一)

        今天就昨天的分析写了一下代码,时间关系,因为还要上班,所以删除代码还没写出来。。。 先弄出来新增节点,查询(这个其实是现成的,就是搜索树的查询)和判断树是否是红黑树。

       首先看红黑树节点的定义:

     两个构造函数是关键:因为红黑树所有的叶子节点都是值为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

猜你喜欢

转载自709002341.iteye.com/blog/2259886
今日推荐