`
akon405
  • 浏览: 45182 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

2012年4月25日---红黑树的现实和操作

阅读更多

出去流浪了一段时间,现在我又回来了,内容继续更新,算法继续学习。

在最近看的是红黑树,而且在这里停留了很久,因为总是遇到NullPointerException的问题,每天都在对程序进行调试,今天终于搞定了。这里先插入出现NullPointerException的情形:

1字符串变量未初始化; 
2接口类型的对象没有用具体的类初始化
3当一个对象的值为空时,你没有判断为空的情况。

这里简单介绍一下什么是红黑树:

 红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  性质1. 节点是红色或黑色。

  性质2. 根节点是黑色。

  性质3 每个叶节点是黑色的。

  性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

  性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

上面就是红黑树的定义,可以看出红黑树是二叉查找树的扩充,所以其大多数的操作都和二叉查找树相识,只不过还需要考虑红黑树以上的5个基本性质,所以就会在二叉查找树的基础上进行一些改进和补充。下面就是具体代码:

 

/*
 * 红黑树的java实现
 * @version 1.0 2012/4/25
 * @author akon
 */
package com.akon405.www;

public class RBTree {
	RBTreeNode  nullNode=new RBTreeNode();//定义空节点

	RBTreeNode RBTreeRoot=nullNode;//定义一个根节点
	//初始化空节点nullNode
	public void init(){
		nullNode.data=0;
		nullNode.color=null;
		nullNode.left=nullNode;
		nullNode.right=nullNode;
		nullNode.parent=nullNode;
	}
	//中序遍历红黑树操作(中序遍历之后便可以排序成功)
	public void inOrderRBTree(RBTreeNode x){
		if(x!=nullNode){
			inOrderRBTree(x.left);//先遍历左子树
			System.out.print(x.data+",");//打印中间节点
			inOrderRBTree(x.right);//最后遍历右子树
		}
	}
	//红黑树的插入操作
	public void insert(RBTree T,RBTreeNode k){
		RBTreeNode x=T.RBTreeRoot;		
		RBTreeNode y=nullNode;	
		RBTreeNode node=new RBTreeNode();
		node=k;
		while(x!=nullNode){//while语句可以找到k节点所要插入的位置的父亲节点y
			y=x;
			if(x.data>node.data){
				x=x.left;
			}else{
				x=x.right;
			}
		}
		node.parent=y;
		if(y==nullNode){//二叉查找树为空树的情况下,直接插入到根节点,这里的y为已知的k的父亲节点
			T.RBTreeRoot=node;
		}else if(node.data<y.data){//插入到父亲节点y的左边
			y.left=node;
		}else{//插入到父亲节点y的右边
			y.right=node;
		}
		node.left=nullNode;//叶节点的子树须为null
		node.right=nullNode;
		node.color="red";//red代表红色(插入红色的结点,因为这样可以在插入过程中尽量避免对树的调整)
		
		insertFixup(node);//为了保证插入节点之后依然满足红黑树的性质,这里创建一个修复函数对红黑树的节点重新着色并旋转
	}
	//插入修复函数(颜色的调整,左旋,右旋)
	private void insertFixup(RBTreeNode k) {
		RBTreeNode y=nullNode;
		while(k.parent.color=="red"){//插入节点k的父亲节点为红色的情况下(因为插入的节点是红色节点,所以它的父亲节点必须为黑色)

			if(k.parent==k.parent.parent.left){//(1)k父亲节点为其父亲节点的左孩子
				y=k.parent.parent.right;//y的k的叔父节点
				if(y.color=="red"){//case 1(k的叔父节点为红色)
					k.parent.color="black";//k的父亲节点置为黑
					y.color="black";
					k.parent.parent.color="red";
					k=k.parent.parent;
				}else{//case 2(k的叔父节点为黑色)
					if(k==k.parent.right){//k为右孩子节点
						k=k.parent;
						leftRotate(k);	
					}
					k.parent.color="black";
					k.parent.parent.color="red";
					rightRotate(k);
				}
			}else{//(2)k父亲节点为其父亲节点的右孩子,操作和前面(1)k父亲节点为其父亲节点的左孩子一样
				y=k.parent.parent.left;
				if(y.color=="red"){//case 1
					k.parent.color="black";
					y.color="black";
					k.parent.parent.color="red";
					k=k.parent.parent;
				}else{//case 2
					if(k==k.parent.right){
						k=k.parent;
						rightRotate(k);	
					}
					k.parent.color="black";
					k.parent.parent.color="red";
					leftRotate(k);
				}

			}
		}
		RBTreeRoot.color="black";
	}
	//红黑树的删除操作
	public void delete(RBTreeNode x){//三种情况的节点
		RBTreeNode y;//y为真实删除的节点(x不一定是真实被删除的节点)
		//下面的if..else便可确定节点y(y为x节点或者为x的后继节点)
		if(x.left==nullNode||x.right==nullNode){
			y=x;
		}else{
			y=successor(x);
		}
		
		//把x置为y的非空孩子节点
		if(y.left!=nullNode){
			x=y.left;
		}else{
			x=y.right;
		}
		//删除y节点
		x.parent=y.parent;
		if(y.parent==nullNode){
			RBTreeRoot=x;
		}else if(y==y.parent.left){
			y.parent.left=x;
		}else{
			y.parent.left=x;
		}
		
		if(y!=x){
			x.data=y.data;
		}
		if(y.color=="black"){//修复红黑树(删除节点为红色的时候不影响红黑树的性质)
			deleteFixup(x);
		}
	}
	//删除修复函数
	public void deleteFixup(RBTreeNode x){
		RBTreeNode y;
		while(x!=RBTreeRoot&&x.color=="black"){
			if(x==x.parent.left){//x为其父亲节点的左孩子节点
				y=x.parent.right;//x的兄弟节点y
				if(y.color=="red"){//x的兄弟节点y为红色
					//第一种情况--x的兄弟节点y为红色
					y.color="black";
					y.parent.color="red";
					rightRotate(x.parent);
					y=x.parent.right;
				}else{//x的兄弟节点y为黑色
					if(y.left.color=="black"&&y.right.color=="black"){//第二种情况--x的兄弟节点y为黑色,y的孩子节点均为黑色
					y.color="red";
					x=x.parent;
					}else if(y.right.color=="black"&&y.left.color=="red"){//第三种情况--x的兄弟节点y为黑色,y的右孩子节点是黑色,左孩子是红色
						y.left.color="red";
						y.color="black";
						leftRotate(x);
						y=x.parent.right;
					}else if(y.right.color=="red"){//第四种情况--x的兄弟节点y为黑色,y的右孩子节点为红色
					y.color=x.parent.color;
					x.parent.color="black";
					y.right.color="black";
					leftRotate(x);
					x=RBTreeRoot;
					}
				}
			}else{//同样,原理一致,只是遇到左旋改为右旋,遇到右旋改为左旋,即可。其它代码不变。
				y=x.parent.left;//x的兄弟节点
				if(y.color=="red"){
					y.color="black";
					y.parent.color="red";
					rightRotate(x.parent);
					y=x.parent.right;
				}else{
					if(y.left.color=="black"&&y.right.color=="black"){
					y.color="red";
					x=x.parent;
					}else if(y.right.color=="black"&&y.left.color=="red"){
					y.left.color="red";
					y.color="black";
					leftRotate(x);
					y=x.parent.right;
					}else if(y.right.color=="red"){
					y.color=x.parent.color;
					x.parent.color="black";
					y.right.color="black";
					rightRotate(x);
					x=RBTreeRoot;
					}
				}
			}
			
		}
		x.color="black";
	}
	//查找节点的后继节点
	public RBTreeNode successor(RBTreeNode x){
		if(x.right!=nullNode){
			return searchMinNode(x.right);//右子树的最小值
		}
		RBTreeNode y=x.parent;
		while(y!=nullNode&&x==y.right){//向上找到最近的一个节点,其父亲节点的左子树包涵了当前节点或者其父亲节点为空
			x=y;
			y=y.parent;
		}
		return y;
	}
	//查找最小节点
	public RBTreeNode searchMinNode(RBTreeNode x){
		while(x.left!=nullNode){
			x=x.left;
		}
		return x;
	}
	//从r节点开始查找x节点
	public RBTreeNode search(RBTreeNode r,RBTreeNode x){
		if(r==nullNode||r.data==x.data){
			return r;
		}
		if(x.data<r.data){
			return search(r.left,x);
		}else{
			return search(r.right,x);
		}
	}
	//红黑树的左旋操作(选择的节点必须右孩子节点不为空)
	public void leftRotate(RBTreeNode x){
		//左旋分为三个步骤,每个步骤有两个操作,因为每个节点既有孩子节点又有父亲节点
		RBTreeNode y=x.right;//把x节点的右孩子节点赋给我们定义的y节点
		
		//第一步,y的左孩子节点转变为x的右孩子节点
		x.right=y.left;
		y.right.parent=x;
		
		//第二步,把x的父亲节点转变为y的父亲节点
		y.parent=x.parent;
		if(x.parent==nullNode){//x为根节点的情况下
			RBTreeRoot=y;
		}else if(x==x.parent.left){//x的父亲节点不为空并且x为其父亲节点的左孩子节点
			x.parent.left=y;
		}else{//x的父亲节点不为空并且x为其父亲节点的右孩子节点
			x.parent.right=y;
		}
		
		//第三步,把x节点转变为y的左孩子节点
		y.left=x;
		x.parent=y;
		
		//左旋完成
	}
	//红黑树的右旋操作(选择的节点必须左孩子节点不为空)
	public void rightRotate(RBTreeNode x){
		//右旋和左旋步骤基本一样,也分为三个步骤
		RBTreeNode y=x.left;
		//第一步,把y的右孩子节点转变为x的左孩子节点
		x.left=y.right;
		y.left.parent=x;
		
		//第二步,把x的父亲节点转变为y的父亲节点
		y.parent=x.parent;
		if(x.parent==nullNode){
			RBTreeRoot=y;
		}else if(x.parent.left==x){
			x.parent.left=y;
		}else{
			x.parent.right=y;
		}
		
		//第三步,把x节点转变为y的左孩子节点
		y.left=x;
		x.parent=y;
		
		//右旋完成
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
//		 TODO Auto-generated method stub
		int[] A={20,8,16,34,73,17,32,89};
		RBTree rb=new RBTree();
		rb.init();
		//通过循环插入构造红黑树
		for(int i=0;i<A.length;i++){
			RBTreeNode x=new RBTreeNode();
			x.data=A[i];
			x.color=null;
			x.left=rb.nullNode;
			x.right=rb.nullNode;
			x.parent=rb.nullNode;
			rb.insert(rb,x);
		}
		rb.inOrderRBTree(rb.RBTreeRoot);//中序遍历红黑树
	}

}


//红黑树的节点类
class RBTreeNode{
	int data;
	String color;
	RBTreeNode left;
	RBTreeNode right;
	RBTreeNode parent;
}
结果:17,32,34,73,89,
  

 

 

1
2
分享到:
评论

相关推荐

    Microsoft_Project_-_房地产项目计划分级管理模板

    - **开始时间**:2012年10月4日 - **完成时间**:2012年10月4日 - **前置任务**:无 - **备注**:这是项目启动的第一个步骤,为后续的工作打下基础。 **1.2 办理公司注册及前期工作** - **任务描述**:完成项目...

    西门子工业自动化资料大全 (2012年11月版).pdf

    ### 西门子工业自动化资料大全 (2012年11月版) #### 概述 本资料集合涵盖了西门子(中国)工业业务领域内最常用的技术文档和技术资源,旨在为用户提供全面且深入的技术支持和服务。这些文档包括但不限于操作指南、...

    有关红黑树的算法研究

    文档最初版本发布于2008年2月在德国Dagstuhl举行的数据结构研讨会上,并在同年4月的Maresias算法分析会议上进行了更新。 #### 二、红黑树概述 红黑树是由Guibas和Sedgewick在1978年提出的一种自平衡二叉查找树。它...

    nServer-v2.1023[FTP + MYSQL + HTTP + PHP(FCGI)]

    2012年05月25日 - 配置文件放到到anrip/config目录 - 控制台代码模块化 2012年05月20日 - 更新PHP版本为5.4.3 - 优化配置文件编译脚本 - 更新WEB文件浏览器 2012年05月08日 - 更新PHP版本为5.4.2 - 更新PHP配置...

    立体库开发计划

    ##### (四)系统实施阶段(2012年01月30日-2012年02月30日) - **采购管理系统**(2012年01月30日-2012年02月30日) - **库存管理系统**(2012年01月30日-2012年02月30日) - **设备管理系统**(2012年01月30日-...

    2012年学生会文艺部年度工作总结.doc

    - **时间**:2012年11月4日 - **活动**:第xx届田径运动会 - **成果**:方正队展现出高昂的精神风貌,为经管系赢得了荣誉。 #### 晨跑计划 - **时间**:2012年9月9日开始 - **意义**:促进学生健康生活习惯的养成,...

    策略为王股票软件源代码测试使用- 2024年4月10日-上证A股所有历史数据-日K线-分析家格式-SUPERST

    策略为王股票软件源代码测试使用--- 2024年4月10日---上证A股所有历史数据---日K线--分析家格式--SUPERSTK 策略为王股票软件源代码测试使用--- 2024年4月10日---上证A股所有历史数据---日K线--分析家格式--SUPERSTK

    策略为王股票软件源代码测试使用- 2024年4月10日-深证A股所有历史数据-日K线-分析家数据格式-SUPE

    策略为王股票软件源代码测试使用--- 2024年4月10日--深证A股所有历史数据---日K线---分析家数据格式---SUPERSTK 策略为王股票软件源代码测试使用--- 2024年4月10日--深证A股所有历史数据---日K线---分析家数据格式--...

    IEC60601-1-2 第四版2012

    ### IEC60601-1-2 第四版2012 #### 标题及描述解析 - **标题**: "IEC60601-1-2 第四版2012" - **描述**: "IEC60601-1-2 第四版2012" 这两个部分直接给出了该标准的基本信息,即**IEC60601-1-2第四版**是在2012年...

    2019年4月24日-5月18日哔哩哔哩平台美食视频发布量.xls

    2019年4月24日-5月18日哔哩哔哩平台美食视频发布量.xls

    MISRA-C-2012-AMD-1

    MISRA-C:2012的修订版在2016年4月首次发布,并且在英国沃里克郡Nuneaton的Watling Street出版。 MISRA的使命是提供世界级的最佳实践指南,以确保嵌入式控制系统和独立软件的安全和稳定应用。MISRA是由汽车制造商、...

    2020年3月23日-4月22日蘑菇街地域指数.xls

    2020年3月23日-4月22日蘑菇街地域指数.xls

    X-Cart Gold 4.5.4.zip

    ] 2012年10月25日的目的 - 错误(0127371):测试箱(Sandbox)终点是PayPal快速结帐API签名认证机制的改变。固定的。 [!] 2012年10月24日,的随机 - 问题(0124236):PayPal快速结帐取消重定向到购物车即使调用...

    2020年3月16日-4月15日薇娅直播产品结构.xls

    2020年3月16日-4月15日薇娅直播产品结构.xls

    2020年3月16日-4月15日李佳琦直播产品结构.xls

    2020年3月16日-4月15日李佳琦直播产品结构.xls

    Dynamixel-ax-12-library

    2012年4月10日-Dynamixel库现在具有如何通过Arduino设置和编程Dynamixel伺服的示例2012年1月17日-下载部分中的库和Arduino草图已更新为可与Ardunio 1.0一起使用更多信息机器人电子手册 通讯概述 MX系列控制表 指令...

    2020年3月24日-4月23日“淘宝直播” 地域指数.xls

    2020年3月24日-4月23日“淘宝直播” 地域指数.xls

    STK3101规格书

    - 发布日期:2012年4月18日 - 更新内容:增加I²C连续读取图解,添加软件复位寄存器以及软件复位请求。 - 发布者:Lucas - **版本号:1.08** - 发布日期:2012年3月27日 - 更新内容:修改PS中断状态图并添加...

    2012-1-6月中国游戏产业报告

    ### 2012年1-6月中国游戏产业报告知识点总结 #### 一、报告概览 - **发布时间**:2012年1-6月 - **发布机构**:中国版协游戏工委(GPC)、国际数据公司(IDC)、中国互联网信息中心(CNNIC)、中新游戏(伽马新媒...

Global site tag (gtag.js) - Google Analytics