Day_24,数据结构扩张以红黑树为基础(简单扩充卫星数据)

二叉树,红黑树作为基础的数据结构,在大多数场合都已经够用了,但总有不够的时候,这时候我们就要对现有的基础数据结构进行扩张

如何扩张数据结构呢?

一般情况下,扩张数据结构可以分为如下四个步骤:
①选定一种基础的数据结构
②确定需要扩张、维护的附加信息(卫星数据)
③检验能否在基础数据结构的基础操作上(例如插入、删除)能否维护附加信息
④在扩张好的数据结构上增加新的操作
在扩张数据结构的时候,我们需要确保③,不然②和④终究只能是空想

具有动态顺序统计的红黑树

顺序统计量: 将样本观测值由小到大排列得到的统计量 。例如,在一个元素集合中,最小值是第一个顺序统计量(i=1),最大值是第n歌顺序统计量(i=n)。

按照上述四个步骤,我们进行红黑树的扩张:
①红黑树为基础数据结构
②给节点新增一个size附加信息,size为以该节点为根的子树(包括自身)的节点数
③在基础数据结构的基础操作上(例如插入、删除)能否维护附加信息详看下面(1)(2)(3)
④计算某节点在中序遍历中的顺序,我们称之为

(1)插入:插入的时候仅需要维护插入节点node,以及node到根节点简单路径上的节点
(2)删除:根据上篇红黑树的删除可知,在删除过程中,会发生位置变动的就是 删除的节点 或者 删除节点的后继节点,因此维护也只需要维护这两个节点其中之一到根节点简单路径上的节点
(3)左右旋:通过观察下图可知,左右旋的过程中实际上只影响了x,y两个节点,因此只要维护这两个节点即可
在这里插入图片描述

上代码

下面仅列出有发生变化的函数以及新增的函数,其余部分查看上面文章
红黑树的实现及理解

template<class T>
class RBTreeNode
{
public:
    RBTreeNode *left;//做孩子
    RBTreeNode *right;//右孩子
    RBTreeNode *p;//父亲
    T key;//该节点的值
    NodeColor color;//颜色
    int size;

    RBTreeNode(){
        size = 1;
    }
    RBTreeNode(T value){
        key = value;
        left = NULL;
        right = NULL;
        p = NULL;
        color = NodeColor(RED);
        size = 1;
    }
    RBTreeNode(T value,RBTreeNode * left,RBTreeNode *right,RBTreeNode *parent,NodeColor color){
        key = value;
        left = left;
        right = right;
        p = parent;
        color = color;
        size = 1;
    }
    ~RBTreeNode(){
    }
};

template<class T>
class RBSearchTree
{
public:
    RBSearchTree();//创建空树
    RBSearchTree(RBTreeNode<T> a[]);//传入节点组创建树
    RBSearchTree(T a[],int length);//传入节点值组,创建树
    ~RBSearchTree();
    MidWalk();//中序遍历
    FrontWalk();//前序遍历
    BackWalk();//后序遍历
    RBTreeNode<T> * Search(T value);//找到值相等的节点
    RBTreeNode<T> * Minimum();//树中最小节点
    RBTreeNode<T> * Maximum();//树中最大节点
    RBTreeNode<T> * TreePredecessor(RBTreeNode<T> *node);//某节点的前任 狗头.jpg  小于该节点的 值最大节点
    RBTreeNode<T> * TreeSuccessor(RBTreeNode<T> *node);//某节点的后驱             大于该节点的 值最小节点
    		RBTreeNode<T> * FindElementByPos(RBTreeNode<T> *node,int pos);//以node为根的子树中,找从小到大排序中的第pos个节点
    		int GetRank(RBTreeNode<T> *node);//计算node节点的秩(中序遍历中的位置)
    string Insert(T value);//插入
    string Delete(T value);//删除
private:
    RBTreeNode<T> *root;//根节点
    RBTreeNode<T> *NILNode;//叶节点(NIL节点)
    		InitNILNode();//初始化NILNode
    MidWalkInner(RBTreeNode<T> *node);//中序遍历
    RBTreeInsertFix(RBTreeNode<T> *node);//插入时恢复红黑树性质
    RBTreeDeleteFix(RBTreeNode<T> *node);//删除时恢复红黑树性质
    RBTreeNode<T> * SearchInner(RBTreeNode<T> *node,T value);//搜索
    RBTreeNode<T> * MinimumInner(RBTreeNode<T> *node);//树中最小节点
    RBTreeNode<T> * MaximumInner(RBTreeNode<T> *node);//树中最大节点
    RBTransplant(RBTreeNode<T> *pNode,RBTreeNode<T> *subNode);//单节点继承,subNode继承pNode原有的位置
    LeftRotate(RBTreeNode<T> *node);//左旋
    RightRotate(RBTreeNode<T> *node);//右旋
    		CalSize(RBTreeNode<T> *node);//計算size 其中 size = left.size + right.size + 1
    string strTrue;//用于返回true的字符串
    string strFalse;//用于返回false的字符串
    string strNull;//用于返回null的字符串
};

template<class T>
RBSearchTree<T>::RBSearchTree()
{
    strTrue = "true";
    strFalse = "false";
    strNull = "null";
    InitNILNode();
}

template<class T>
RBSearchTree<T>::InitNILNode()
{
    RBTreeNode<T> *NILNode = new RBTreeNode<T>();
    NILNode->color = NodeColor(BLACK);
    NILNode->size = 0;//哨兵size是0
}

template<class T>
RBSearchTree<T>::RBSearchTree(RBTreeNode<T> a[])
{
    strTrue = "true";
    strFalse = "false";
    strNull = "null";
    InitNILNode();
}



template<class T>
RBSearchTree<T>::RBSearchTree(T a[],int length)
{
    strTrue = "true";
    strFalse = "false";
    strNull = "null";
    InitNILNode();
    //这里直接写成
    //root = new RBTreeNode<T>(a[0],NILNode,NILNode,NILNode,NodeColor(BLACK));
    //他在识别的时候 left!=right!=p 可能跟引用关系有关 改成&试试??
    root = new RBTreeNode<T>(a[0]);
    root->p = NILNode;
    root->left = NILNode;
    root->right = NILNode;
    root->color = NodeColor(BLACK);
    //大体思路,循环遍历
      //小于找左孩子
      //大于找右孩子
      //孩子为空直接放置
      //如果i==0 将root指针指向该节点
    for (int i=1;i<length;i++)
    {
        Insert(a[i]);
    }
}


template<class T>
RBSearchTree<T>::MidWalk()
{
    RBTreeNode<T> *node = root;
    MidWalkInner(node);
    cout<<endl;
}

template<class T>
RBSearchTree<T>::MidWalkInner(RBTreeNode<T> *node)
{
    if (node!=NILNode)
    {
        MidWalkInner(node->left);
        cout<<"key= "<<node->key<<" color "<<node->color<<" p is "<<node->p->key<<" size= "<<node->size<<endl;//<<endl;//,"
        MidWalkInner(node->right);
    }
}

......

template<class T>
string RBSearchTree<T>::Insert(T value)
{
    RBTreeNode<T> *insertNode = new RBTreeNode<T>(value,NILNode,NILNode,NILNode,NodeColor(RED));
    RBTreeNode<T> *insertNodeParent = NILNode;
    RBTreeNode<T> *node = root;
    while(node != NILNode)
    {
        insertNodeParent = node;
        if (value <= node->key)
            node = node->left;
        else
            node = node->right;
    }
    insertNode->p = insertNodeParent;
    if (value <= insertNodeParent->key)
        insertNodeParent->left = insertNode;
    else
        insertNodeParent->right = insertNode;
    insertNode->left = NILNode;
    insertNode->right = NILNode;
    insertNode->color = NodeColor(RED);
    CalSize(insertNode); //插入的节点在最末端,因此影响的只有该节点到root最简单路径上的点
    RBTreeInsertFix(insertNode);
    return strTrue;
}

......

template<class T>
string RBSearchTree<T>::Delete(T value)
{
    RBTreeNode<T> *node = SearchInner(root,value);//找到要删除的节点
    RBTreeNode<T> *backupsNode; //备份指针,用于保存节点长度发生变化的节点(发生继承关系的节点)
    RBTreeNode<T> *successorNode = NILNode; //用于保存后继节点的父节点,计算size用
    NodeColor originColor = node->color;//记录原始颜色(如果删除的是黑色是会影响黑高的,红色则不会)
    if(node->left==NILNode) //单左节点
    {
        backupsNode = node->right;
        RBTransplant(node,node->right);
    }
    else if(node->right==NILNode)//单右节点
    {
        backupsNode = node->left;
        RBTransplant(node,node->left);
    }
    else
    {
    	//找到后继节点
        RBTreeNode<T> *temp = MinimumInner(node->right);
        successorNode = temp->p;
        originColor = temp->color;
        if (temp->p == node)
            backupsNode->p = temp;
        else //if (temp->p != node)
        {
            RBTransplant(temp,temp->right);
            temp->right = node->right;
            temp->right->p = temp;
        }
        RBTransplant(node,temp);
        temp->left = node->left;
        temp->left->p = temp;
        temp->color = node->color;
    }
    //如果删除的是在末节点,影响的还是只有他到root最简单路径上的点
    //但是当删除的点不是末端节点的时候,影响的就是后继节点到root最简单路径上的点
    if (successorNode==NILNode)
        CalSize(node);
    else
        CalSize(successorNode);
    if (originColor == NodeColor(BLACK))
        RBTreeDeleteFix(backupsNode);
    return strTrue;
}

......

template<class T>
RBSearchTree<T>::LeftRotate(RBTreeNode<T> *node)
{
    RBTreeNode<T> *y = node->right;
    node->right = y->left;
    if (y->left !=NILNode)
        y->left->p = node;
    y->p = node->p;
    if (node->p == NILNode)
        root = y;
    else if (node->p->left ==  node)
        node->p->left = y;
    else
        node->p->right = y;
    y->left = node;
    node->p = y;

    //下面两句话看图就明白了
    y->size = node->size;
    node->size = node->left->size + node->right->size + 1;
}

template<class T>
RBSearchTree<T>::RightRotate(RBTreeNode<T> *node)
{
    RBTreeNode<T> *y = node->left;
    node->left = y->right;
    if (y->right !=NILNode)
        y->right->p = node;
    y->p = node->p;
    if (node->p == NILNode)
        root = y;
    else if (node->p->left == node)
        node->p->left = y;
    else
        node->p->right = y;
    y->right = node;
    node->p = y;

    //下面两句话看图就明白了
    y->size = node->size;
    node->size = node->left->size + node->right->size + 1;
}

template<class T>
RBSearchTree<T>::CalSize(RBTreeNode<T> *node)
{
    while (node->p !=NILNode)
    {
        node->size = node->left->size + node->right->size + 1;
        node = node->p;
    }
    //上面这么计算每次都会漏掉根节点
    node->size = node->left->size + node->right->size + 1; //根节点计算进去
}

template<class T>
RBTreeNode<T> * RBSearchTree<T>::FindElementByPos(RBTreeNode<T> *node,int pos)
{
    int tempPos = node->left->size + 1;//确定node点的排序大小
    if (tempPos == pos)
        return node;
    else if (tempPos < pos)//所找节点在右子树
        return FindElementByPos(node->right,pos-tempPos);
    else//所找节点在左子树
        return FindElementByPos(node->left,pos);
}

template<class T>
int RBSearchTree<T>::GetRank(RBTreeNode<T> *node)
{
    int rank = node->left->size + 1;
    RBTreeNode<T> *temp = node;
    while (temp != root)//可以很明显知道循环终止条件就是一直找到root节点
    {
        if (temp == temp->p->right)//当node是右节点的时候,需要加上父节点及左子树的size,如果是左节点秩不发生变化
            rank = temp->p->left->size + rank + 1;
        temp = temp->p;
    }
    return rank;
}

int main()
{
    int a[11] = {6,5,2,5,7,8,1,3,9,4,11};
    RBTreeNode<int> *node;
    RBSearchTree<int> *tr = new RBSearchTree<int>(a,11);
    node = tr->Search(11);
    cout<<"node rank is "<<tr->GetRank(node)<<endl;
    node = tr->Search(7);
    node = tr->FindElementByPos(node,1);
    cout<<"find node key is "<<node->key<<endl;
    tr->MidWalk();
    node = tr->Search(2);
    cout<<"node value is "<<node->key<<endl;
    node = tr->TreePredecessor(node);
    cout<<"predecessor value is "<<node->key<<endl;
    node = tr->Search(2);
    cout<<"2 node value is "<<node->key<<endl;
    tr->Delete(2);
    node = tr->Search(2);
    cout<<"delete 2 node value is "<<node->key<<endl;
    tr->MidWalk();
    tr->Insert(2);
    node = tr->Search(2);
    cout<<"insert 2 node value is "<<node->key<<endl;
    tr->MidWalk();
}

运行结果如下
在这里插入图片描述

补充:
1、将NILNode的初始化重新声明了一个函数叫做InitNILNode(),方便多处调用
2、封装了一个计算Size的方法CalSize(RBTreeNode *node)
3、新增加了两个操作
①FindElementByPos(RBTreeNode *node,int pos):找到以node为根的第i小的节点
②GetRank(RBTreeNode *node):获取node节点的秩

1、2没什么,主要就是阐述下3中两个方法的思想:
3中两个方法都是建立在附加信息size的基础上进行计算
①二叉树的性质是,左节点小于右节点,我们主要根据这条性质找到数组中第i小的元素
②中序遍历中,我们是先输出左节点,然后是父节点,最后是右节点;因此元素是左节点的时候,元素的秩就是 node->left->size + 1 ,当元素是右节点的时候,元素的秩就是 temp->p->left->size + rank + 1

罗马不是一天建成的,努力吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值