红黑树的插入

一、红黑树的介绍

先来看下算法导论对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.

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

插入情况1,z的叔叔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.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("红黑树");
    }
}

/*
3 4 6 7 9 11 9 18 14 12 17 19 22 20
  */
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);
    }

}
时间: 2024-08-31 08:14:49

红黑树的插入的相关文章

数据结构和算法05 之红-黑树(看完包懂~)

(友情提示,红-黑树是基于二叉搜索树的,如果对二叉搜索树不了解,可以先看看:二叉搜索树 )                从第4节的分析中可以看出,二叉搜索树是个很好的数据结构,可以快速地找到一个给定关键字的数据项,并且可以快速地插入和删除数据项.但是二叉搜索树有个很麻烦的问题,如果树中插入的是随机数据,则执行效果很好,但如果插入的是有序或者逆序的数据,那么二叉搜索树的执行速度就变得很慢.因为当插入数值有序时,二叉树就是非平衡的了,排在一条线上,其实就变成了一个链表--它的快速查找.插入和删除指

红黑树深入剖析及Java实现

红黑树是平衡二叉查找树的一种.为了深入理解红黑树,我们需要从二叉查找树开始讲起. BST 二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小,右节点的值要比父节点的值大.它的高度决定了它的查找效率. 在理想的情况下,二叉查找树增删查改的时间复杂度为O(logN)(其中N为节点数),最坏的情况下为O(N).当它的高度为logN+1时,我们就说二叉查找树是平衡的. BST的查找操作 T  key = a search key  Node ro

什么是红黑树?

       写这篇红黑树算法的目的:一是为了自己学习的总结:二是能够与大家一起交流沟通一起努力.文中有些内容学习自<算法导论>一书,部分来自于维基百科,我会在文中标注出来,有不明白的地方可以通过留言大家一起沟通.        首先,什么是红黑树呢? 红黑树是一种"平衡的"二叉查找树,它是一种经典高效的算法,能够保证在最坏的情况下动态集合操作的时间为O(lgn).红黑树每个节点包含5个域,分别为color,key,left,right和p. color是在每个节点上增加的

数据结构之红黑树

概述 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组.它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees).红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能. 二叉查找树(BST) 二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小,右

【算法导论】红黑树

红黑树          在了解红黑树之前,我们必须先了解二叉搜索树(又称二叉排序树,我在上一篇文章中有介绍),因为红黑树是一种特殊的二叉排序树:在每个节点上增加一个存储位来表示节点的颜色,因此红黑树共有五个域:color,key,lchild,rchild,p.          红黑树的提出:一个高度为h的二叉排序树可以实现任何一种基本的动态集合操作:插入.删除.查找等操作,但是当树才高度比较高时,二叉树就会退化成链表.而红黑树能确保在最坏的情况下,基本的动态集合操作的时间为O(logn).

算法之红黑树

红黑树(一) 原理和算法详细介   1 R-B Tree简介     R-B Tree,全称是Red-Black Tree,又称为"红黑树",它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的.(5)从一个

一步一图一代码,一定要让你真正彻底明白红黑树【转】

转自:http://blog.csdn.net/chenhuajie123/article/details/11951777 一步一图一代码,一定要让你真正彻底明白红黑树   作者:July   二零一一年一月九日 ----------------------------- 本文参考:I.  The Art of Computer Programming Volume III. Introduction to Algorithms, Second EditionIII.The Annotated

第十四章 红黑树——C++代码实现

红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值.除了具备该特性之外,红黑树还包括许多额外的信息. 红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black).红黑树的特性:(1) 每个节点或者是黑色,或者是红色.(2) 根节点是黑色.(3) 每个叶子节点是黑色. [注意:这里叶子节点,是指为空的叶子

红黑树:个人理解与Python实现

红黑树:个人理解与Python实现   [基本事实1] 红黑树是一种平衡的二叉查找树,无论插入还是删除操作都可以在O(lg n)内实现,而一般的二叉查找树则在极端情况下会退化为线性结构.红黑树之所以是平衡的二叉查找树,是因为每个节点都有表示其颜色的域值:红或黑,在插入和删除操作的时候依据节点的颜色向平衡的方向调整.根本原因当然是由红黑树定义所决定的:如果一个二叉查找树满足如下条件,那么它就称作红黑树:1.每个节点要么是红色,要么是黑色2.根结点是黑色3.每个叶节点(NIL)为黑色4.如果一个节点