红黑树基本功能 —— C++实现

烂漫一生 提交于 2020-01-16 20:50:49

^ _ ^ 感谢各位大佬光临,I love You

mua~


红黑树是一种高级的数据结构

可以说红黑树是普通二叉搜索树的升级版本,可以自动平衡


此文章不对红黑树的原理进行解析,因为我太懒了~

文章底部将会插入一些红黑树原理的一些结构图(单旋转、双旋转)

此文章将以三个 ( .h 文件 ) 和 一个 ( .cpp文件 ) 来实现红黑树:

  1. Except.h (异常类包含的头文件
  2. Wrapper.h (自定义的引用类型包含的头文件
  3. RedBlackTreeH.h (红黑树实现的头文件
  4. Main.cpp (包含main的头文件,用来测试红黑树

为了测试,所有成员都将以 public 展示


一、Except.h 实现

#pragma once

#include <string>

using namespace std;

class DSException		// 作为异常类的基类
{
public:
    DSEexception(const string& msg = "") : message(msg){}
    virtual ~DSException(){}

    virtual string toString() const
    {
        return "Exception " + string(": ") + what();
    }
    virtual string what() const
    {
        return message;
    }
        
private:
    string message;
};

// 有重复的异常类
class DuplicateItemException : public DSException
{
public:
    DuplicateItemException(const string& msg = "“) : DSException(msg){}    
};

// 不存在(为空)的异常类
class NullPointerException : public DSException
{
public:
    NullPointerException(const string& msg = "") : DSException(msg){}
};

二、Wrapper.h 实现

其中将用到异常类,所以包含 Except.h 头文件

#pragma once

#include "Except.h"

template <class Object>
class Cref
{
public:
    Cref() : obj(NULL){}
    explicit Cref(const Object& x) : obj(&x){}

    const Object & get() const
    {
        if (isNull())	 // obj 是个空值  获取它的值将 引发异常
        {
            throw NullPointerException();
        }
        else
        {
            return *obj;	
        }
    }

    bool isNull() const
    {
        return obj == NULL;
    }

private:
    const Object* obj;    
};

注: Cref 类只是对指针进行一个包装而已


三、RedBlackTreeH.h 实现

实现红黑树最基本的功能,其中包含最重要的动态平衡 insert方法

其中会用到异常类、引用类、和双端队列,所有将引入这些头文件


#pragma once

#include "Wrapper.h"
#include "Except.h"
#include <deque>		// 层级遍历会用到 双端队列

// 声明红黑树和结点类
template <class T>
class RedBlackTree;	

template <class T>
class RedBlackNode;

template <class T>
class RedBlackNode
{
public:
    T 			value;		// 数据值
    RedBlackNode<T>* 	left;		// 左孩子
    RedBlackNode<T>* 	right;		// 右孩子 
    int 		color;		// 颜色
    
    // 构造结点
    RedBlackNode(const T& val = T(), RedBlackNode<T>* li = nullptr, RedBlackNode<T>* ri = nullptr,
        int k = RedBlackTree<T>::Black) : value(val), left(li), right(ri), color(k) {}

    friend class RedBlackTree<T>;	// 使得红黑树 能够操控结点
};

template <class T>
class RedBlackTree		// 红黑树
{
public:
    enum { RED, BLACK };	// 枚举颜色

    using Node = RedBlackNode<T>;		// 取结点的别名

public:
    Node* header;		// 头结点
    Node* nullNode;		// 空结点
    Node* current;		// 当前结点
    Node* parent;		// 父结点
    Node* grand;		// 祖父结点 
    Node* great;		// 曾祖父结点

public:
    // 不是根的根   一个负值最大的数
    RedBlackTree(const T& val);		// 构造函数  创建伪根
    ~RedBlackTree();

public:
    bool isEmpty();		// 判断是否为空
    void makeEmpty();		// 释放树的内存
    void levelPrint();		// 层级遍历
    // 返回一个封装好的引用类型
    Cref<T> findMin();		// 查找最小值
    Cref<T> findMax();		// 查找最大值
    Cref<T> find(const T& val) const;		// 查找数据

public:
    // ---红黑树核心部分
    void Insert(const T& x);	// 插入数据  自动平衡
    void rotateWithLeftChild(Node*& k2) const;		// 右侧旋转
    void rotateWithRightChild(Node*& k1) const;		// 左侧旋转

    // 实现过程中,并不会使用该方法,只是了解一下原理
    // void doubleRotateWithLeftChild(Node*& k3) const;		// 向右双旋转 由单旋转实现
    // void doubleRotateWithRightChild(Node*& k1) const;  	// 向左双旋转

    // 重新调整红黑树   自动平衡功能实现
    void handleReorient(const T& item);
    // 通过父结点 判断 item的旋转情况
    RedBlackNode<T>* rotate(const T& item, Node* theParent) const;	// 将多种旋转可能封装在一起

    void reclaimMemory(Node*& t) const;		// 释放内存
    
    void inPrint(Node* t);		// 中序遍历

    RedBlackNode<T>* remove(Node* root, T const& data);		// 删除数据
};

template <class T>
RedBlackTree<T>::~RedBlackTree()
{
    makeEmpty();		// 释放内存
    
    delete header;		// 释放伪根
    delete nullNode;		// 释放空结点 
}

template <class T>
void RedBlackTree<T>::Insert(const T& x)
{
    current = great = grand = parent = header;
    nullNode->value = x;

    // 找到合适的位置   树中有 x  还是 nullNode中的x
    while(current->value != x)
    {
        great = grand; grand = parent; parent = current;
        current = x < current->value ? current->left : current->right;

        // 当前结点左右孩子都是红色的情况下  将发生旋转
        if(current->left->color == RED && current->right->color == RED)
        {
            handlReoriet(x);
        }
    }

    if(current != nullNode)	// 插入了重复的数据  抛出异常
        throw DuplicateItemException();

    current = new Node(x, nullNode, nullNode); // current 的左右孩子都初始为 nullNode结点
    
    // 挂载到 父结点上
    if(parent->value > x)
        parent->left = current;
    else
        parent->right = current;

    handleReorient(x);		// 判断是否旋转
}

template <class T>
void RedBlackTree<T>::rotateWithLeftChild(Node*& k2) const
{
    Node* k1 = k2->left;
    k2->left = k1->right;	// 横向平移
    k1->right = k2;
    k2 = k1;			// 旋转过后, k1 为根结点
}

template <class T>
void RedBlackTree<T>::rotateWithRightChild(Node*& k1) const
{
    Node* k2 = k1->right;
    k1->right = k2->left;
    k2->left = k1;
    k1 = k2;
}

// 双旋转 功能实现原理
//template <class T>
//void RedBlackTree<T>::doubleRotateWithLeftChild(Node*& k3) const
//{
//    rotateWithRightChild(k3->left);	// 先将 k3的左孩子向左移动 
//    rotateWithLeftChild(k3);		// 再将 k3向右移动
//}

//template <class T>
//void RedBlackTree<T>::doubleRotateWithRightChild(Node*& k1) const
//{
//    rotateWithLeftChild(k1->right);
//    rotateWithRightChild(k1);
//}

// 非常重要一个方法实现
template <class T>
void RedBlackTree<T>::handleReorint(const T& item)
{
    // 插入的结点是红色的    或者当前结点有两个红色的孩子
    current->color = RED;
    current->left->color = BLACK;
    current->right->color = BLACK;

    if(parent->color == RED)
    {
        grand->color = RED;
        // 内孙子则多一次旋转   变成双旋转
        if(item < grand->value != item < parent->value)
        {
            parent = rotate(item, grand);
        }
        current = rotate(item, great);
        current->color = BLACK;		// 父结点是红的,则叶子结点必须是黑的
    }
    
    header->right->color = BLACK;	// 根结点必须是黑的    
       
}

template <class T>
RedBlackNode<T>* RedBlackTree<T>::rotate(const T& item, Node* theParent) const
{
    if(item < theParent->value)		// 在父结点的左边
    {
        item < theParent->left ?	// 判断在父结点左孩子的哪一边
            rotateWithLeftChild(theParent->left) :
            rotateWithRightChild(theParent->left);

        return theParent->left;
    }
    else
    {
        item < theParent->right?
            rotateWithLeftChild(theParent->right) :
            rotateWithRightChild(theParent->right);
        
        return theParent->right;
    } 
}

template <class T>
bool RedBlackTree<T>::isEmpty()
{
    return header->right == nullNode;		// 判断根结点是不是等于空结点
}

template <class T>
void RedBlackTree<T>::makeEmpty()
{
    reclaimMemory(header->right);

    header->right = nullNode;		// 清空内容后   根结点指向空结点
}

template <class T>
void RedBlackTree<T>::reclaimMemory(Node*& t) const
{
    if(t != t->left)    // 最后空结点和左右孩子都是一样的
    {
        // 递归遍历每一个结点  释放内存
        reclaimMemory(t->left);
        reclaimMemory(t->right);
        
        delete t;
    }
}

template <class T>
void RedBlackTree<T>::inPrint(Node* t)		// 中序遍历
{
    if(t != nullNode)
    {
        inPrint(t->left);
        cout << t->value << " ";
        inPrint(t->right);
    }       
}

template <class T>
void RedBlackTree<T>::levelPrint()		// 层级遍历
{
    deque<Node*>* deq = new deque<Node*>;	// 创建一个双端队列
    
    Node* root = header->right;
    deq->push-back(root);

    while(!deq->empty())
    {
        root = deq->front();			// 获取队头的结点
        cout << root->value << " ";
        deq->pop_front();		// 队头的结点出列

        if(root->left != nullNode)
        {
            deq->push_back(root->left);
        }
        if(root->right != nullNode)
        {
            deq->push_back(root->right);
        }
    }
    
}

template <class T>
RedBlackNode<T>* RedBlackTree<T>::remove(Node* root, T const& data)
{
    if(root != nullNode)
    {
        // 找到要删除的结点
        if(root->value > data)		
        {
            root->left = remove(root->left, data);
            return root;
        }
        else if(root->value < data)
        {
            root->right = remove(root->right, data);
            return root;
        }
        else
        {
            // 判断要删除的结点是否左右孩子都存在
            if(root->left != nullNode && root->right != nullNode)
            {
                Node* node = root->right;	// 通过结点的右孩子 找到左边最小的结点
                while(node->left != nullNode)
                {
                    node = node->left;
                }
                root->value = node->value;
                
                root->right = remove(root->right, root->value);  // 从右孩子开始递归删除 一样的值
            }
            else
            {
                Node*& delNode = root;
                if(root->left != nullNode)
                {
                    root = root->left;		// 接管内存
                }
                else if(root->right != nullNode)
                {
                    root = root->right;
                }
                delete delNode;		// 释放被删除的结点
                delNode = nullNode;
            }
            
            return root;
        }     
    }
    
}

template <class T>
Cref<T> RedBlackTree<T>::findMin()
{
    if(isEmpty())
        return Cref<T>();		// 返回一空引用
    
    Node* itr = header->right;

    while(itr->left != nullNode)	// 找到最左边的叶子结点
    {
        itr = itr->left;
    }

    return Cref<T>(itr->value);
}

template <class T>
Cref<T> RedBlackTree<T>::findMax()
{
    if(isEmpty())
        return Cref<T>();
    
    Node* itr = header->right;
    
    while(itr->right != nullNode)	// 找到最右边的叶子结点
    {
        itr = itr->right;
    }

    return Cref<T>(itr->value);
}

template <class T>
Cref<T> RedBlackTree<T>::find(const T& val) const
{
    nullNode->value = val;		// 空结点的值为 val 终止循环条件

    Node* curr = header->right;

    for(;;)
    {
        if(val < curr->value)
        {
            curr = curr->left;
        }
        else if(val > curr->value)
        {
            curr = curr->right;
        }
        else if(curr != nullNode)	// 找到了数据
        {
            return Cref<T>(curr->value);
        }
        else	  // 找了整棵树,都没有找到  到空结点停止寻找 
        {
            return Cref<T>();
        }
    }
    
}

四、Main.cpp 测试红黑树

#include <iostream>
#include "RedBlackTreeH.h"

int main()
{
    const int NUM_MINUS_MAX = -99999;
    
    // 创建一个包含伪根的红黑树
    RedBlackTree<int>* t = new RedBlackTree<int>(NUM_MINUS_MAX);

    // 插入数据
    t->Insert(50);
    t->Insert(40);
    t->Insert(30);
    t->Insert(59);
    t->Insert(80);

    cout << "中序遍历:" << endl;
    t->inPrint(t->header->right);			// 升序打印数据
    cout << endl;

    t->remove(t->header->right, 30);		// 删除数据
    
    cout << "中序遍历:" << endl;
    t->inPrint(t->header->right);			// 此时 30已经不存在
    cout << endl;
    
    if(t->findMax().get() == 80)
        cout << "找到了最大值" << endl;
    if(t->findMax().get() == 40)			// 30被删除  40则为最小值 
        cout << "找到了最小值" << endl;

    if(t->find(59).get() == 59)
    {
        cout << "找到了数据" << endl;
    }
    
    if(t->find(100).isNull())
    {
        cout << "没有找到数据" << endl;
    }

    delete t;

    return 0;
}

运行结果为:

中序遍历:
30  40  50  59  80
中序遍历:
40  50  59  80
找到了最大值
找到了最小值
找到了数据
没有找到数据 

其中一些代码需要加一些结构图来理解

右侧旋转与左侧旋转:##

在这里插入图片描述

在这里插入图片描述

双旋转结构图:

在这里插入图片描述

四种旋转情况:

在这里插入图片描述

插入结点产生旋转的几种可能性结构图:

在这里插入图片描述

看图理解的比较快哟~


谢谢大佬观看我的文章,小弟我三生有幸 ^ _ ^

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!