2012年4月25日---红黑树的现实和操作
2014-03-15 10:48
134 查看
出去流浪了一段时间,现在我又回来了,内容继续更新,算法继续学习。
在最近看的是红黑树,而且在这里停留了很久,因为总是遇到NullPointerException的问题,每天都在对程序进行调试,今天终于搞定了。这里先插入出现NullPointerException的情形:
这里简单介绍一下什么是红黑树:
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
上面就是红黑树的定义,可以看出红黑树是二叉查找树的扩充,所以其大多数的操作都和二叉查找树相识,只不过还需要考虑红黑树以上的5个基本性质,所以就会在二叉查找树的基础上进行一些改进和补充。下面就是具体代码:
Java代码
/*
* 红黑树的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;
}
Java代码
结果:17,32,34,73,89,
在最近看的是红黑树,而且在这里停留了很久,因为总是遇到NullPointerException的问题,每天都在对程序进行调试,今天终于搞定了。这里先插入出现NullPointerException的情形:
1字符串变量未初始化; 2接口类型的对象没有用具体的类初始化 3当一个对象的值为空时,你没有判断为空的情况。
这里简单介绍一下什么是红黑树:
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
上面就是红黑树的定义,可以看出红黑树是二叉查找树的扩充,所以其大多数的操作都和二叉查找树相识,只不过还需要考虑红黑树以上的5个基本性质,所以就会在二叉查找树的基础上进行一些改进和补充。下面就是具体代码:
Java代码
/*
* 红黑树的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;
}
Java代码
结果:17,32,34,73,89,
相关文章推荐
- 排序二叉树,平衡二叉树和红黑树的概念以及相关的操作讲解
- 数据结构之红黑树(三)——删除操作
- 红黑树插入操作的C++实现
- 数据结构之红黑树(三)——删除操作
- 红黑树的移除节点操作图解--转载
- 红黑树的操作
- TreeMap关键源码解析-红黑树操作
- 红黑树删除操作
- AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中
- 红黑树的插入操作
- 红黑树删除操作学习笔记
- Data structure-6 红黑树(BlackRedTree)插入操作
- appium按照现实的文字来进行操作(IOS)
- 红黑树操作及实现
- 浅谈红黑树的添加删除操作
- 红黑树操作
- 红黑树节点的删除操作
- 红黑树基本操作
- 查找 -- 红黑树的删除操作
- 红黑树插入操作超详解