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

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

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

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

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

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

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

  性质2. 根节点是黑色。

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

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

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

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

 

Java代码    收藏代码
  1. /* 
  2.  * 红黑树的java实现 
  3.  * @version 1.0 2012/4/25 
  4.  * @author akon 
  5.  */  
  6. package com.akon405.www;  
  7.   
  8. public class RBTree {  
  9.     RBTreeNode  nullNode=new RBTreeNode();//定义空节点  
  10.   
  11.     RBTreeNode RBTreeRoot=nullNode;//定义一个根节点  
  12.     //初始化空节点nullNode  
  13.     public void init(){  
  14.         nullNode.data=0;  
  15.         nullNode.color=null;  
  16.         nullNode.left=nullNode;  
  17.         nullNode.right=nullNode;  
  18.         nullNode.parent=nullNode;  
  19.     }  
  20.     //中序遍历红黑树操作(中序遍历之后便可以排序成功)  
  21.     public void inOrderRBTree(RBTreeNode x){  
  22.         if(x!=nullNode){  
  23.             inOrderRBTree(x.left);//先遍历左子树  
  24.             System.out.print(x.data+",");//打印中间节点  
  25.             inOrderRBTree(x.right);//最后遍历右子树  
  26.         }  
  27.     }  
  28.     //红黑树的插入操作  
  29.     public void insert(RBTree T,RBTreeNode k){  
  30.         RBTreeNode x=T.RBTreeRoot;        
  31.         RBTreeNode y=nullNode;    
  32.         RBTreeNode node=new RBTreeNode();  
  33.         node=k;  
  34.         while(x!=nullNode){//while语句可以找到k节点所要插入的位置的父亲节点y  
  35.             y=x;  
  36.             if(x.data>node.data){  
  37.                 x=x.left;  
  38.             }else{  
  39.                 x=x.right;  
  40.             }  
  41.         }  
  42.         node.parent=y;  
  43.         if(y==nullNode){//二叉查找树为空树的情况下,直接插入到根节点,这里的y为已知的k的父亲节点  
  44.             T.RBTreeRoot=node;  
  45.         }else if(node.data<y.data){//插入到父亲节点y的左边  
  46.             y.left=node;  
  47.         }else{//插入到父亲节点y的右边  
  48.             y.right=node;  
  49.         }  
  50.         node.left=nullNode;//叶节点的子树须为null  
  51.         node.right=nullNode;  
  52.         node.color="red";//red代表红色(插入红色的结点,因为这样可以在插入过程中尽量避免对树的调整)  
  53.           
  54.         insertFixup(node);//为了保证插入节点之后依然满足红黑树的性质,这里创建一个修复函数对红黑树的节点重新着色并旋转  
  55.     }  
  56.     //插入修复函数(颜色的调整,左旋,右旋)  
  57.     private void insertFixup(RBTreeNode k) {  
  58.         RBTreeNode y=nullNode;  
  59.         while(k.parent.color=="red"){//插入节点k的父亲节点为红色的情况下(因为插入的节点是红色节点,所以它的父亲节点必须为黑色)  
  60.   
  61.             if(k.parent==k.parent.parent.left){//(1)k父亲节点为其父亲节点的左孩子  
  62.                 y=k.parent.parent.right;//y的k的叔父节点  
  63.                 if(y.color=="red"){//case 1(k的叔父节点为红色)  
  64.                     k.parent.color="black";//k的父亲节点置为黑  
  65.                     y.color="black";  
  66.                     k.parent.parent.color="red";  
  67.                     k=k.parent.parent;  
  68.                 }else{//case 2(k的叔父节点为黑色)  
  69.                     if(k==k.parent.right){//k为右孩子节点  
  70.                         k=k.parent;  
  71.                         leftRotate(k);    
  72.                     }  
  73.                     k.parent.color="black";  
  74.                     k.parent.parent.color="red";  
  75.                     rightRotate(k);  
  76.                 }  
  77.             }else{//(2)k父亲节点为其父亲节点的右孩子,操作和前面(1)k父亲节点为其父亲节点的左孩子一样  
  78.                 y=k.parent.parent.left;  
  79.                 if(y.color=="red"){//case 1  
  80.                     k.parent.color="black";  
  81.                     y.color="black";  
  82.                     k.parent.parent.color="red";  
  83.                     k=k.parent.parent;  
  84.                 }else{//case 2  
  85.                     if(k==k.parent.right){  
  86.                         k=k.parent;  
  87.                         rightRotate(k);   
  88.                     }  
  89.                     k.parent.color="black";  
  90.                     k.parent.parent.color="red";  
  91.                     leftRotate(k);  
  92.                 }  
  93.   
  94.             }  
  95.         }  
  96.         RBTreeRoot.color="black";  
  97.     }  
  98.     //红黑树的删除操作  
  99.     public void delete(RBTreeNode x){//三种情况的节点  
  100.         RBTreeNode y;//y为真实删除的节点(x不一定是真实被删除的节点)  
  101.         //下面的if..else便可确定节点y(y为x节点或者为x的后继节点)  
  102.         if(x.left==nullNode||x.right==nullNode){  
  103.             y=x;  
  104.         }else{  
  105.             y=successor(x);  
  106.         }  
  107.           
  108.         //把x置为y的非空孩子节点  
  109.         if(y.left!=nullNode){  
  110.             x=y.left;  
  111.         }else{  
  112.             x=y.right;  
  113.         }  
  114.         //删除y节点  
  115.         x.parent=y.parent;  
  116.         if(y.parent==nullNode){  
  117.             RBTreeRoot=x;  
  118.         }else if(y==y.parent.left){  
  119.             y.parent.left=x;  
  120.         }else{  
  121.             y.parent.left=x;  
  122.         }  
  123.           
  124.         if(y!=x){  
  125.             x.data=y.data;  
  126.         }  
  127.         if(y.color=="black"){//修复红黑树(删除节点为红色的时候不影响红黑树的性质)  
  128.             deleteFixup(x);  
  129.         }  
  130.     }  
  131.     //删除修复函数  
  132.     public void deleteFixup(RBTreeNode x){  
  133.         RBTreeNode y;  
  134.         while(x!=RBTreeRoot&&x.color=="black"){  
  135.             if(x==x.parent.left){//x为其父亲节点的左孩子节点  
  136.                 y=x.parent.right;//x的兄弟节点y  
  137.                 if(y.color=="red"){//x的兄弟节点y为红色  
  138.                     //第一种情况--x的兄弟节点y为红色  
  139.                     y.color="black";  
  140.                     y.parent.color="red";  
  141.                     rightRotate(x.parent);  
  142.                     y=x.parent.right;  
  143.                 }else{//x的兄弟节点y为黑色  
  144.                     if(y.left.color=="black"&&y.right.color=="black"){//第二种情况--x的兄弟节点y为黑色,y的孩子节点均为黑色  
  145.                     y.color="red";  
  146.                     x=x.parent;  
  147.                     }else if(y.right.color=="black"&&y.left.color=="red"){//第三种情况--x的兄弟节点y为黑色,y的右孩子节点是黑色,左孩子是红色  
  148.                         y.left.color="red";  
  149.                         y.color="black";  
  150.                         leftRotate(x);  
  151.                         y=x.parent.right;  
  152.                     }else if(y.right.color=="red"){//第四种情况--x的兄弟节点y为黑色,y的右孩子节点为红色  
  153.                     y.color=x.parent.color;  
  154.                     x.parent.color="black";  
  155.                     y.right.color="black";  
  156.                     leftRotate(x);  
  157.                     x=RBTreeRoot;  
  158.                     }  
  159.                 }  
  160.             }else{//同样,原理一致,只是遇到左旋改为右旋,遇到右旋改为左旋,即可。其它代码不变。  
  161.                 y=x.parent.left;//x的兄弟节点  
  162.                 if(y.color=="red"){  
  163.                     y.color="black";  
  164.                     y.parent.color="red";  
  165.                     rightRotate(x.parent);  
  166.                     y=x.parent.right;  
  167.                 }else{  
  168.                     if(y.left.color=="black"&&y.right.color=="black"){  
  169.                     y.color="red";  
  170.                     x=x.parent;  
  171.                     }else if(y.right.color=="black"&&y.left.color=="red"){  
  172.                     y.left.color="red";  
  173.                     y.color="black";  
  174.                     leftRotate(x);  
  175.                     y=x.parent.right;  
  176.                     }else if(y.right.color=="red"){  
  177.                     y.color=x.parent.color;  
  178.                     x.parent.color="black";  
  179.                     y.right.color="black";  
  180.                     rightRotate(x);  
  181.                     x=RBTreeRoot;  
  182.                     }  
  183.                 }  
  184.             }  
  185.               
  186.         }  
  187.         x.color="black";  
  188.     }  
  189.     //查找节点的后继节点  
  190.     public RBTreeNode successor(RBTreeNode x){  
  191.         if(x.right!=nullNode){  
  192.             return searchMinNode(x.right);//右子树的最小值  
  193.         }  
  194.         RBTreeNode y=x.parent;  
  195.         while(y!=nullNode&&x==y.right){//向上找到最近的一个节点,其父亲节点的左子树包涵了当前节点或者其父亲节点为空  
  196.             x=y;  
  197.             y=y.parent;  
  198.         }  
  199.         return y;  
  200.     }  
  201.     //查找最小节点  
  202.     public RBTreeNode searchMinNode(RBTreeNode x){  
  203.         while(x.left!=nullNode){  
  204.             x=x.left;  
  205.         }  
  206.         return x;  
  207.     }  
  208.     //从r节点开始查找x节点  
  209.     public RBTreeNode search(RBTreeNode r,RBTreeNode x){  
  210.         if(r==nullNode||r.data==x.data){  
  211.             return r;  
  212.         }  
  213.         if(x.data<r.data){  
  214.             return search(r.left,x);  
  215.         }else{  
  216.             return search(r.right,x);  
  217.         }  
  218.     }  
  219.     //红黑树的左旋操作(选择的节点必须右孩子节点不为空)  
  220.     public void leftRotate(RBTreeNode x){  
  221.         //左旋分为三个步骤,每个步骤有两个操作,因为每个节点既有孩子节点又有父亲节点  
  222.         RBTreeNode y=x.right;//把x节点的右孩子节点赋给我们定义的y节点  
  223.           
  224.         //第一步,y的左孩子节点转变为x的右孩子节点  
  225.         x.right=y.left;  
  226.         y.right.parent=x;  
  227.           
  228.         //第二步,把x的父亲节点转变为y的父亲节点  
  229.         y.parent=x.parent;  
  230.         if(x.parent==nullNode){//x为根节点的情况下  
  231.             RBTreeRoot=y;  
  232.         }else if(x==x.parent.left){//x的父亲节点不为空并且x为其父亲节点的左孩子节点  
  233.             x.parent.left=y;  
  234.         }else{//x的父亲节点不为空并且x为其父亲节点的右孩子节点  
  235.             x.parent.right=y;  
  236.         }  
  237.           
  238.         //第三步,把x节点转变为y的左孩子节点  
  239.         y.left=x;  
  240.         x.parent=y;  
  241.           
  242.         //左旋完成  
  243.     }  
  244.     //红黑树的右旋操作(选择的节点必须左孩子节点不为空)  
  245.     public void rightRotate(RBTreeNode x){  
  246.         //右旋和左旋步骤基本一样,也分为三个步骤  
  247.         RBTreeNode y=x.left;  
  248.         //第一步,把y的右孩子节点转变为x的左孩子节点  
  249.         x.left=y.right;  
  250.         y.left.parent=x;  
  251.           
  252.         //第二步,把x的父亲节点转变为y的父亲节点  
  253.         y.parent=x.parent;  
  254.         if(x.parent==nullNode){  
  255.             RBTreeRoot=y;  
  256.         }else if(x.parent.left==x){  
  257.             x.parent.left=y;  
  258.         }else{  
  259.             x.parent.right=y;  
  260.         }  
  261.           
  262.         //第三步,把x节点转变为y的左孩子节点  
  263.         y.left=x;  
  264.         x.parent=y;  
  265.           
  266.         //右旋完成  
  267.     }  
  268.     /** 
  269.      * @param args 
  270.      */  
  271.     public static void main(String[] args) {  
  272. //       TODO Auto-generated method stub  
  273.         int[] A={20,8,16,34,73,17,32,89};  
  274.         RBTree rb=new RBTree();  
  275.         rb.init();  
  276.         //通过循环插入构造红黑树  
  277.         for(int i=0;i<A.length;i++){  
  278.             RBTreeNode x=new RBTreeNode();  
  279.             x.data=A[i];  
  280.             x.color=null;  
  281.             x.left=rb.nullNode;  
  282.             x.right=rb.nullNode;  
  283.             x.parent=rb.nullNode;  
  284.             rb.insert(rb,x);  
  285.         }  
  286.         rb.inOrderRBTree(rb.RBTreeRoot);//中序遍历红黑树  
  287.     }  
  288.   
  289. }  
  290.   
  291.   
  292. //红黑树的节点类  
  293. class RBTreeNode{  
  294.     int data;  
  295.     String color;  
  296.     RBTreeNode left;  
  297.     RBTreeNode right;  
  298.     RBTreeNode parent;  
  299. }  
Java代码    收藏代码
  1. 结果:17,32,34,73,89,  
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值