红黑树的插入

一、红黑树的介绍

先来看下算法导论对R-B Tree的介绍:
  红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。
通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其
他路径长出俩倍,因而是接近平衡的。

  前面说了,红黑树,是一种二叉查找树,既然是二叉查找树,那么它必满足二叉查找树的一般性质。
下面,在具体介绍红黑树之前,咱们先来了解下 二叉查找树的一般性质:
1.在一棵二叉查找树上,执行查找、插入、删除等操作,的时间复杂度为O(lgn)。
    因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为O(lgn)。
    至于n个结点的二叉树高度为lgn的证明,可参考算法导论 第12章 二叉查找树 第12.4节。
2.但若是一棵具有n个结点的线性链,则此些操作最坏情况运行时间为O(n)。

 

红黑树,能保证在最坏情况下,基本的动态几何操作的时间均为O(lgn)。

我们知道,红黑树上每个结点内含五个域,color,key,left,right,p。如果相应的指针域没有,则设为NIL。

一般的,红黑树,满足以下性质,即只有满足以下全部性质的树,我们才称之为红黑树:

1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

 

注:实际上考虑的情况有6种,而其中的三种与另外三种是对称的。这取决于下面算法中  p[z] = left[p[p[z]]]这一句。

 

红黑树插入的第一种情况(RB-INSERT-FIXUP(T, z)代码的具体分析一)

为了保证阐述清晰,重述下RB-INSERT-FIXUP(T, z)的源码:

RB-INSERT-FIXUP(T, z)
 1 while color[p[z]] = RED
 2     do if p[z] = left[p[p[z]]]
 3           then y ← right[p[p[z]]]
 4                if color[y] = RED
 5                   then color[p[z]] ← BLACK                    ▹ Case 1
 6                        color[y] ← BLACK                       ▹ Case 1
 7                        color[p[p[z]]] ← RED                   ▹ Case 1
 8                        z ← p[p[z]]                            ▹ Case 1
 9                   else if z = right[p[z]]
10                           then z ← p[z]                       ▹ Case 2
11                                LEFT-ROTATE(T, z)              ▹ Case 2
12                           color[p[z]] ← BLACK                 ▹ Case 3
13                           color[p[p[z]]] ← RED                ▹ Case 3
14                           RIGHT-ROTATE(T, p[p[z]])            ▹ Case 3
15           else (same as then clause
                         with "right" and "left" exchanged)
16 color[root[T]] ← BLACK

 //case1表示情况1,case2表示情况2,case3表示情况3.

咱们,先来透彻分析红黑树插入的第一种情况:

插入情况1z的叔叔y是红色的。

第一种情况,即上述代码的第5-8行:
 5                   then color[p[z]] ← BLACK                    ▹ Case 1
 6                        color[y] ← BLACK                       ▹ Case 1
 7                        color[p[p[z]]] ← RED                   ▹ Case 1
 8                        z ← p[p[z]]                            ▹ Case 1

如上图所示,a:z为右孩子,b:z为左孩子。

只有p[z]和y(上图a中A为p[z],D为z,上图b中,B为p[z],D为y)都是红色的时候,才会执行此情况1.

咱们分析下上图的a情况,即z为右孩子时

因为p[p[z]],即c是黑色,所以将p[z]、y都着为黑色(如上图a部分的右边),

此举解决z、p[z]都是红色的问题,将p[p[z]]着为红色,则保持了性质5.

红黑树插入的第二种、第三种情况

插入情况2:z的叔叔y是黑色的,且z是右孩子

插入情况3:z的叔叔y是黑色的,且z是左孩子

这俩种情况,是通过z是p[z]的左孩子,还是右孩子区别的。

参照上图,针对情况2,z是她父亲的右孩子,则为了保持红黑性质,左旋则变为情况3,此时z为左孩子,

因为z、p[z]都为黑色,所以不违反红黑性质(注,情况3中,z的叔叔y是黑色的,否则此种情况就变成上述情况1 了)。

 我们已经看出来了,情况2,情况3都违反性质4(一个红结点的俩个儿子都是黑色的)。

所以情况2->左旋后->情况3,此时情况3同样违反性质4,所以情况3->右旋,得到上图的最后那部分。

注,情况2、3都只违反性质4,其它的性质1、2、3、5都不违背。

 

下面附上java代码:

 

效果图如下:

 

 

 

package com.hjzgg.rbt;

import java.awt.Rectangle;

public class RBTNode {
    public static final boolean RED = true;
    public static final boolean BLACK = false;
    public RBTNode[] child = new RBTNode[2];
    public RBTNode parent;
    public int key;
    public boolean color;
    public RBTNode(RBTNode parent, int key, boolean color) {
        super();
        this.parent = parent;
        this.key = key;
        this.color = color;
        child[0] = child[1] = null;
    }
    
    private int level;//这个节点在树中的层次
    private Rectangle rect;//节点在图形面板中的位置
    public int getLevel() {
        return level;
    }
    public void setLevel(int level) {
        this.level = level;
    }
    public Rectangle getRect() {
        return rect;
    }
    public void setRect(Rectangle rect) {
        this.rect = rect;
    }
    
}
package com.hjzgg.rbt;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class RBTGraphic {
    private RBTNode T = null;
    private final int distNode = 50;//节点之间的距离
    private final int heightNode = 50;//节点的高度
    private final int widthNode = 50;//节点的宽度
    private final int levelHeight = 100;//层与层之间的高度
    private ArrayList<Rectangle> line = new ArrayList<Rectangle>();
    private int curY = 0;
    private int curX = 10;
    
    public RBTGraphic(RBTNode T) {
        super();
        this.T = T;
        prepareGraphic();
    }
    
    public class TreeFrame extends JFrame{
        private JPanel panel = new JPanel(){
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                for(Rectangle rect : line){
                    g.drawLine(rect.x, rect.y, rect.width, rect.height);
                }
            }
        };
        private JScrollPane scrollPane = new JScrollPane(panel);
        public TreeFrame() throws HeadlessException {
            super();
            init();
        }

        public TreeFrame(String title) throws HeadlessException {
            super(title);
            init();
        }
        
        private void init(){
            setLayout(new BorderLayout());
            panel.setLayout(null);
            drawTree(T);
            add(scrollPane, BorderLayout.CENTER);
            int width = curX + 50;
            int height = curY + 50;
            //这里要设置面板的PreferredSize,如果当前Frame大小不能显示PreferredSize那么才会出现滚动条
            panel.setPreferredSize(new Dimension(width, height));
            if(width > 600) width = 600;
            if(height > 300) height = 500;
            setBounds(400, 100, width, height);
            setVisible(true);
        }
        
        public void drawTree(RBTNode o){
            if(o==null) return;
            JLabel label = new JLabel(o.key+"", JLabel.CENTER);
            label.setBounds(o.getRect());
            label.setFont(new Font("宋体",Font.BOLD, 32));
            label.setForeground(Color.WHITE);
            label.setOpaque(true);
            if(o.color == RBTNode.RED)
                label.setBackground(Color.RED);
            else
                label.setBackground(Color.BLACK);
            panel.add(label);
            if(o.child[0]==null && o.child[1]==null) return;
            int x = o.getRect().x+ widthNode/2;
            int y = o.getLevel()*levelHeight+heightNode;
            for(int i=0; i<2; ++i){
                drawTree(o.child[i]);
                if(o.child[i]==null) continue;
                int xx = o.child[i].getRect().x + widthNode/2;
                int yy = y+levelHeight-heightNode;
                line.add(new Rectangle(x, y, xx, yy));
            }
        }
    }
    
    private void prepareNodeLevel(RBTNode o, int level){//确定每一个节点的层次
        if(o==null) return; 
        o.setLevel(level);
        prepareNodeLevel(o.child[0], level+1);
        prepareNodeLevel(o.child[1], level+1);
        if(curY < (level+1)*levelHeight) curY = (level+1)*levelHeight;
    }
    
    private void prepareNodeSize(RBTNode o){//确定节点的坐标位置
        if(o==null) return;
        if(o.child[0]==null && o.child[1]==null){//终结点
            int x = curX; curX+=distNode+widthNode;
            int y = o.getLevel()*levelHeight;
            o.setRect(new Rectangle(x, y, widthNode, heightNode));
            return;
        }
        prepareNodeSize(o.child[0]);
        prepareNodeSize(o.child[1]);
        
        int leftChildx = o.child[0] != null ? o.child[0].getRect().x : o.child[1].getRect().x;
        int rightChildx = o.child[1] == null ? o.child[0].getRect().x : o.child[1].getRect().x;
        //根据左右两边孩子的节点,确定父节点的坐标尺寸
        int parentx = (leftChildx+rightChildx)/2;
        int parenty =o.getLevel()*levelHeight;
        o.setRect(new Rectangle(parentx, parenty, widthNode, heightNode));
    }
    
    private void prepareGraphic(){
        prepareNodeLevel(T, 0);
        prepareNodeSize(T);
    }
    
}
package com.hjzgg.rbt;

import java.util.Scanner;

public class RBTree {
    public RBTNode T = null;
    private boolean isRoot = true;//是不是第一个插入的节点,也就是根节点
    
    public void rotateT(RBTNode o, int rotate){//rotate表明旋转方向
        RBTNode k = o.child[rotate^1];
        if(o.parent==null)
            T = k;
        else if(o.parent.child[0] == o)
            o.parent.child[0] = k;
        else
            o.parent.child[1] = k;
        k.parent = o.parent;
        o.child[rotate^1] = k.child[rotate];
        k.child[rotate] = o;
        o.parent = k;
    }
    
    private void rbtInsertFixup(RBTNode o){//红黑树平衡的调整,并更改节点的颜色
        if(o.parent.color == RBTNode.RED){
            int childIndex;//左子树或者是右子树索引
            if(o.parent == o.parent.parent.child[0])
                childIndex = 0;
            else 
                childIndex = 1;
            
            //找到o节点对应的叔节点
            RBTNode ou = o.parent.parent.child[childIndex^1];
            if(ou!=null && ou.color == RBTNode.RED){//如果叔节点的颜色是红色
                o.parent.parent.color = RBTNode.RED;
                ou.color = RBTNode.BLACK;
                o.parent.color = RBTNode.BLACK;
            } else {//叔节点是空或者是黑色
                if(o == o.parent.child[childIndex^1]){
                    o = o.parent;
                    rotateT(o, childIndex);
                }
                o.parent.color = RBTNode.BLACK;
                o.parent.parent.color = RBTNode.RED;
                rotateT(o.parent.parent, childIndex^1);
            }
            T.color = RBTNode.BLACK;
        }
    }
    
    public void outT(RBTNode o){
        if(o==null) return;
        System.out.print(o.key+" ");
        outT(o.child[0]);
        outT(o.child[1]);
    }
    
    public void rbtInsert(RBTNode o, RBTNode op, int key, int childIndex){//红黑树的插入
        if(o == null){
            o = new RBTNode(op, key, RBTNode.RED);
            if(op==null){
                T = o;
                T.color = RBTNode.BLACK;
            } else {
                op.child[childIndex] = o;
                o.parent = op;
            }
            if(o.color==RBTNode.RED)
                rbtInsertFixup(o);
        } else if(o.key < key){
            rbtInsert(o.child[0], o, key, 0);
            if(o.color==RBTNode.RED)
                rbtInsertFixup(o);
        } else {
            rbtInsert(o.child[1], o, key, 1);
            if(o.color==RBTNode.RED)
                rbtInsertFixup(o);
        }
    }
    
    public void rbtDelete(){//红黑树的删除
        
    }
    

    public static void main(String[] args) {
        RBTree rbt = new RBTree();
        Scanner scan = new Scanner(System.in);
        for(int i=0; i<15; ++i){
            int key = scan.nextInt();
            rbt.rbtInsert(rbt.T, null, key, 0);
//            rbt.outT(rbt.T);
//            System.out.println();
        }
        new RBTGraphic(rbt.T).new TreeFrame("红黑树");
    }
}

/*
  2 3 4 6 7 9 11 9 18 14 12 17 19 22 20
  */

 

posted @ 2015-09-12 00:23  hjzqyx  阅读(443)  评论(0编辑  收藏  举报