【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)

【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)

一、目的

1.熟悉算法设计的基本思想
2.掌握构建红黑树的方法

二、内容与设计思想

  1. 编写红黑树构建算法,中序遍历各节点,输出颜色和值;
    【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第1张图片
    【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第2张图片
    【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第3张图片
  2. 使用红黑树构建算法,并画图描述不同情况下的运行时间差异;

三、设计思想

考虑需要的函数与结构体

  • 需要通过输入插入建树,其中需要结点结构实现二叉搜索树遍历查找插入后的分情况处理结点旋转
  • 输出时需要一个中序遍历函数

细节实现

  • 已知红黑树的的特点,建树的时候要注意每次插入新节点都要保持其五个特性。

一棵红黑树是满足下面红黑性质的二叉搜索树:

  1. 各个结点或者是红色,或者是黑色的.
  2. 根结点是黑色的.
  3. 每个叶结点 (NIL) 是黑色的.
  4. 如果一个结点是红色,则它的两个子结点都是黑色的.
  5. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点.
    • 建树由插入新结点开始,即需寻找插入结点的规律使五个特性受到最小程度的破坏 。类似二叉搜索树找到新结点应存在的位置,再将其颜色设为红色’R’(若其颜色设为黑色,每次插入均会违反性质4;而插入颜色为红时,有可能不违反。
    • 插入新结点后存在的情况:
    1. 父结点为黑色,则未违反红黑树性质,不需调整。

    2. 父结点为红色:其中需调整只会影响到向上三层,也可理解为三层三层调整。其中祖结点为黑色,父结点与当前结点为红色,则不同情况共分为 当前结点为左或右叔结点为红或黑父结点为左或右分别叠加为8种
      其中当叔结点为红色时,只需重新设置颜色上移,此时当前结点为左或右无区别。
      其中 当其他状况相同,只有父结点在左或右的区别时为对称状态,只需将左右对换。
      因此,只需考虑3种情况: 设当前结点为z,当前结点的叔结点为y。

      1. z的父结点为左结点,叔结点y为红色,将不符的性质上移调整。

      z.p.color=B;
      y.color=B;
      z.p.p.color=R;
      z=z.p.p;
      【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第4张图片
      【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第5张图片

      2. z的父结点是左结点,叔结点y为黑色,z为左孩子。进行调整:

      z.p.color=B;
      z.p.p.color=R;
      Right_Rotate(z.p.p);【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第6张图片

      3. z的父结点为左结点,叔结点为黑色,z为右孩子。只需将其调整为情况二,再按照情况二的调整方式调整。

      z=z.p;
      LEFT-Rotate(z);【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第7张图片

【当z的父结点为右结点时,由于对称,将上述的所有right改为left,所有left改为right】

  • 根据了解红黑树,可写出红黑树结点的结构
    typedef struct Tree
    {
    	int data;
    	char color;
    	Tree*p;
    	Tree*left;
    	Tree*right;
    }
    
  • 以x为支点左旋 与 以y为支点右旋
    【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第8张图片
    均为分四步:结点之间拆分与连接 (右旋类似)【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第9张图片

四、实验过程

1.写出红黑树构建算法的源代码

#include 
using namespace std;

typedef struct Tree 		//红黑树结构
{
    int data;
    char color;
    Tree *left;
    Tree *right;
    Tree *parent;
} * Node;

Node root = nullptr; 	//记录根中内容


void Left_rotate(Node x)            //以x为支点左旋
{
    Node y = x->right;
    x->right=y->left;
    if(y->left!=nullptr)
        y->left->parent = x;
    y->parent = x->parent;
    if (x->parent == nullptr)
    {
        root = y;
    }else if(x==x->parent->left)
    {
        x->parent->left = y;
    }
    else
    {
        x->parent->right = y;
    }
    y->left = x;
    x->parent = y;
}

void Right_rotate(Node x)            //以x为支点右旋
{
    Node y = x->left;
    x->left = y->right;
    if(y->right!=nullptr)
        y->right->parent = x;
    y->parent = x->parent;

    if(x->parent==nullptr)
    {
        root = y;
    }else if (x==x->parent->right)
    {
        x->parent->right=y;
    }else
    {
        x->parent->left=y;
    }
    y->right = x;
    x->parent = y;
}

void Fixnode(Node x) //调整节点位置
{
    while ((x->parent!=nullptr) && (x->parent->color == 'R'))
    {
        Node grandparent = x->parent->parent;
        if (x->parent == grandparent->left) //父结点为祖父节点的左孩子
        {
            if (grandparent->right == nullptr || grandparent->right->color == 'B') //叔结点为黑色
            {
                if (x == x->parent->right)          //当前节点为右节点--------------->   case 1
                {
                    x = x->parent;
                    Left_rotate(x);
                }
                x->parent->color = 'B';             //当前节点为左节点---------------->  case 2
                grandparent->color = 'R';
                Right_rotate(grandparent);
            }
            else //叔结点为红色------------->  case 3
            {
                x->parent->color = 'B';
                grandparent->right->color = 'B';
                grandparent->color = 'R';
                x = grandparent;
            }
        }
        else if (x->parent == grandparent->right) //父结点为祖父节点的右孩子(与前三种情况对称)
        {
            if (grandparent->left == nullptr || grandparent->left->color == 'B') //叔结点为黑色
            {
                if (x == x->parent->left) //当前节点为左节点------------->case 4
                {
                    x = x->parent;
                    Right_rotate(x);
                }
                x->parent->color = 'B';//当前节点为右节点------------> case 5
                grandparent->color = 'R';
                Left_rotate(grandparent);
            }
            else //叔结点为红色---------------->case 6
            {
                x->parent->color = 'B';
                grandparent->left->color = 'B';
                grandparent->color = 'R';
                x = grandparent;
            }
        }
    }
    root->color = 'B';
}

void Insertnode(Node r, Node z) //找到位置插入结点
{
    Node y = nullptr; //找到新节点的父亲
    Node x = r;

    while (x != nullptr)
    {
        y = x; //记录x的上一个
        if (z->data < x->data)
        {
            x = x->left;
        }
        else
        {
            x = x->right;
        }
    }
    z->parent = y;
    if (y == nullptr) //当父结点为空时,新插入节点为根
    {
        root = z;
    }
    else if (z->data < y->data) //小于插在左边
    {
        y->left = z;
    }
    else //大于插在右边
    {
        y->right = z;
    }
    z->left = nullptr;
    z->right = nullptr;
    z->color = 'R';
    Fixnode(z);
}

void print(Node r) //中序输出结点颜色
{
    if (r != nullptr)
    {
        print(r->left);
        cout << r->color;
        print(r->right);
    }
}
int main()
{
    int N;
    int x;
    cin >> N;

    for (int i = 0; i < N; i++)
    {
        cin >> x;
        Node n = new Tree;
        n->data = x;
        Insertnode(root, n);
    }
    print(root);
    return 0;
}

左右旋的代码有一个最后检查 空指针的情况。下次使用树的指针时一定要注意避免空指针的访问问题。

根据理论计算,红黑树算法查找的时间复杂度应该为 O(logn),该实验测试的时间包括查找与插入建树与遍历总时间,所以通过插入方式构建元素个数为 N 的红黑树时,时间复杂度 O(log((n-1)!)),再经过遍历时间复杂度为 O(n).如图。

2.分别画出各个实验结果的折线图

【c/c++详解红黑树构建】实现插入构建红黑树,遍历输出颜色(含源码)_第10张图片

你可能感兴趣的:(算法,c语言,c++,b树,二叉树,树结构)