红黑树--自顶向下插入

红黑树是具有下列着色性质的二叉查找树:
1、每一个节点红或者黑
2、根是黑色的
3、如果一个节点是红色的,那么它的子节点必须是黑色的
4、从一个节点到一个null指针的每一条路径必须包含相同数目的黑色节点

在执行插入操作时,如果父节点为黑节点则直接插入红节点。但如果父节点是红节点,如果插入黑节点则会违反法则4,黑色节点数目变多。如果插入红节点则会违反法则3,出现连续的红节点。因此采用旋转操作和改变节点的颜色(约定null节点为黑)。

父节点(P)为红节点,插入红节点(X),执行单旋转或者双旋转(假设父节点的兄弟节点S为黑节点),G是祖父节点。双圆圈代表红节点


这里写图片描述

旋转后根节点为黑,G节点为红。整体满足红黑树性质。如果S节点为红,则需要将根节点涂成红色,G节点变为黑色。因为从根节点到C只有一个黑节点。若此时祖父节点为红,则需要继续旋转。因此采用自顶向下的方式保证父节点为红时,S不是红色的。

如果一个节点的两个儿子都是红节点,则进行颜色翻转。若此节点为根节点,则立马将其颜色翻转为黑色。


这里写图片描述

如果X的父节点为红,则进行旋转。之前的自顶向下的操作保证了X父节点的兄弟节点不可能为红。

实现代码如下
头文件

template <typename Comparable>
class RedBlackTree
{
public:
    //negInf表示无穷小,头节点的值,头节点的右链指向根节点
    explicit RedBlackTree(const Comparable & negInf);

    RedBlackTree(const RedBlackTree & rhs);
    RedBlackTree(RedBlackTree && rhs);
    ~RedBlackTree();

    const Comparable & findMin()const;
    const Comparable & findMax()const;
    bool contains(const Comparable & x)const;
    bool isEmpty()const;
    void printTree()const;

    void makeEmpty();
    void insert(const Comparable & x);
    void remove(const Comparable & x);

    enum {RED,BLACK};

    RedBlackTree & operator=(const RedBlackTree & rhs);
    RedBlackTree & operator=(RedBlackTree && rhs);

private:
    struct RedBlackNode
    {
        Comparable      element;
        RedBlackNode    *left;
        RedBlackNode    *right;
        int             color;

        RedBlackNode(const Comparable & theElement=Comparable{},
                     RedBlackNode *lt=nullptr,RedBlackNode *rt=nullptr,
                     int c=BLACK)
        :element(theElement),left(lt),right(rt),color(c){}

        RedBlackNode(Comparable && theElement=Comparable{},
                     RedBlackNode *lt=nullptr,RedBlackNode *rt=nullptr,
                     int c=BLACK)
        :element(theElement),left(lt),right(rt),color(c){}

    };

    RedBlackNode *header;//头节点,指向根节点
    RedBlackNode *nullNode;//空节点,为黑,指示nullptr指针

    //用于insert操作及其辅助对象
    RedBlackNode *current;
    RedBlackNode *parent;
    RedBlackNode *grand;
    RedBlackNode *great;

    void reclaimMemory(RedBlackNode *t);
    void printTree(RedBlackNode *t)const;

    RedBlackNode * clone(RedBlackNode * t)const;

    //红黑树操作
    void handleReorient(const Comparable & item);
    RedBlackNode * rotate(const Comparable & item,RedBlackNode *theParent);
    void rotateWithLeftChild(RedBlackNode * & k2);
    void rotateWithRightChild(RedBlackNode * & k1);

};

cpp文件

#include "RedBlackTree.hpp"
#include <iostream>
using namespace std;

template <typename Comparable>
RedBlackTree<Comparable>::RedBlackTree(const Comparable & negInf)
{
    nullNode=new RedBlackNode;
    nullNode->left=nullNode->right=nullNode;

    header=new RedBlackNode{negInf};
    header->left=header->right=nullNode;

}

template <typename Comparable>
void RedBlackTree<Comparable>::printTree()const
{
    if(header->right==nullNode)
        cout<<"Empty Tree"<<endl;
    else
        printTree(header->right);
}

template <typename Comparable>
void RedBlackTree<Comparable>::printTree(RedBlackNode *t)const
{
    if(t!=nullNode)
    {
        printTree(t->left);
        cout<<t->element;
        printTree(t->right);
    }
}

template <typename Comparable>
RedBlackTree<Comparable>::RedBlackTree(const RedBlackTree & rhs)
{
    nullNode=new RedBlackNode;
    nullNode->left=nullNode->right=nullNode;

    header=new RedBlackNode{rhs.header->element};
    header->left=nullNode;
    header->right=clone(rhs.header->right);

}

template <typename Comparable>
typename RedBlackTree<Comparable>::RedBlackNode *
RedBlackTree<Comparable>::clone(RedBlackNode * t)const
{
    if(t==t->left)//不能使用t==nullNode,因为是两个不同的对象
        return nullNode;
    else
        return new RedBlackNode{t->element,clone(t->left),clone(t->right),t->color};
}

//传入根节点的父节点,返回根节点(根节点指的是子树的根节点,不是整棵树的根节点)
template <typename Comparable>
typename RedBlackTree<Comparable>::RedBlackNode *
RedBlackTree<Comparable>::rotate(const Comparable & item,RedBlackNode * theParent)
{
    if(item<theParent->element)
    {
        item<theParent->left->element?
        rotateWithLeftChild(theParent->left):
        rotateWithRightChild(theParent->left);
        return theParent->left;
    }
    else
    {
        item<theParent->right->element?
        rotateWithLeftChild(theParent->right):
        rotateWithRightChild(theParent->right);
        return theParent->right;
    }
}
//如果存在一个节点有两个红儿子,则执行颜色翻转和旋转,item是要被插入的项
template <typename Comparable>
void RedBlackTree<Comparable>::handleReorient(const Comparable & item)
{
    current->color=RED;
    current->left->color=BLACK;
    current->right->color=BLACK;

    if(parent->color==RED)
    {
        grand->color=RED;
        //判断是否是双旋转
        if(item<grand->element!=item<parent->element)
            parent=rotate(item,grand);
        current=rotate(item, great);
        current->color=BLACK;//current此时为子树根节点,旋转后子树根节点始终为黑
    }
    header->right->color=BLACK;//根节点始终为黑
}

template <typename Comparable>
void RedBlackTree<Comparable>::insert(const Comparable & x)
{
    current=parent=grand=header;
    nullNode->element=x;

    while (current->element!=x)
    {
        great=grand;grand=parent;parent=current;
        current=x<current->element?current->left:current->right;

        if(current->left->color==RED&&current->right->color==RED)
        {
            handleReorient(x);
        }
    }

    if(current!=nullNode)//已经存在x
        return;

    //插入树叶
    current=new RedBlackNode{x,nullNode,nullNode};

    if(x<parent->element)
        parent->left=current;
    else
        parent->right=current;
    handleReorient(x);
}

//单旋转,见AVL树操作
template <typename Comparable>
void RedBlackTree<Comparable>::rotateWithLeftChild(RedBlackNode * & k2)
{
    RedBlackNode * k1=k2->left;
    k2->left=k1->right;
    k1->right=k2;
    k2=k1;


}

template <typename Comparable>
void RedBlackTree<Comparable>::rotateWithRightChild(RedBlackNode * & k1)
{
    RedBlackNode * k2=k1->right;
    k1->right=k2->left;
    k2->left=k1;
    k1=k2;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值