C++数据结构之红黑树的实现

《C++数据结构之红黑树的实现》是一篇介绍红黑树实现的文章,通过本文,你可以了解到什么是红黑树以及如何实现红黑树。

什么是红黑树

红黑树是一种自平衡的二叉查找树,它具有良好的平衡性和查找性能。红黑树可以在O(log n)的时间内完成查找、插入和删除操作。

红黑树的一个重要性质是它的任何一个节点都有一个颜色(红色或黑色)属性。在插入、删除操作中,需要通过一定的规则来对节点的颜色、位置进行调整来维持红黑树的平衡性。

红黑树的实现

红黑树的实现需要涉及到节点的定义、插入、删除、查找等操作。下面分别进行介绍。

节点的定义

红黑树节点需要保存节点的数据、颜色、指向父节点、左子树、右子树的指针。定义如下:

template<typename T>
struct RBNode {
    T data;
    bool color;
    RBNode<T>* parent, * left, * right;

    RBNode(T data, bool color = false, RBNode<T>* parent = nullptr,
        RBNode<T>* left = nullptr, RBNode<T>* right = nullptr) : data(data), color(color), parent(parent), left(left), right(right) {}
};

插入操作

插入节点时,需要通过旋转和变色来保证红黑树的平衡性。

插入时,首先按照二叉查找树的规则找到插入位置,并插入一个红色节点。如果父节点是黑色,插入结束,红黑树仍然保持平衡;如果父节点是红色,则需要进行平衡操作。

平衡操作有以下几种:

  1. 如果插入节点的父节点是爷爷节点的左孩子,则叔叔节点是右孩子:

a. 如果叔叔节点是红色的,则重新给父节点和叔叔节点涂黑色,爷爷节点涂红色,并将爷爷节点作为当前节点进行后续平衡操作。

b. 如果叔叔节点是黑色的,并且当前节点是父节点的右孩子,则左旋转父节点,并把父节点作为当前节点进行后续平衡操作。

c. 如果叔叔节点是黑色的,并且当前节点是父节点的左孩子,则将父节点涂黑色,爷爷节点涂红色,然后以爷爷节点为支点进行右旋转。

  1. 如果插入节点的父节点是爷爷节点的右孩子:

a. 如果叔叔节点是红色的,则重新给父节点和叔叔节点涂黑色,爷爷节点涂红色,并将爷爷节点作为当前节点进行后续平衡操作。

b. 如果叔叔节点是黑色的,并且当前节点是父节点的左孩子,则右旋转父节点,并把父节点作为当前节点进行后续平衡操作。

c. 如果叔叔节点是黑色的,并且当前节点是父节点的右孩子,则将父节点涂黑色,爷爷节点涂红色,然后以爷爷节点为支点进行左旋转。

插入操作的代码实现如下:

template<typename T>
void RBTree<T>::insertFixup(RBNode<T>* node) {
    while (node->parent && node->parent->color == red) {
        RBNode<T>* p = node->parent;
        RBNode<T>* g = node->parent->parent;
        if (p == g->left) {
            RBNode<T>* u = g->right;
            if (u && u->color == red) {
                p->color = black;
                u->color = black;
                g->color = red;
                node = g;
            }
            else {
                if (node == p->right) {
                    node = p;
                    leftRotate(node);
                    p = node->parent;
                }
                p->color = black;
                g->color = red;
                rightRotate(g);
            }
        }
        else {
            RBNode<T>* u = g->left;
            if (u && u->color == red) {
                p->color = black;
                u->color = black;
                g->color = red;
                node = g;
            }
            else {
                if (node == p->left) {
                    node = p;
                    rightRotate(node);
                    p = node->parent;
                }
                p->color = black;
                g->color = red;
                leftRotate(g);
            }
        }
    }
    root->color = black;
}

template<typename T>
void RBTree<T>::insert(T data) {
    // 创建插入节点
    RBNode<T>* node = new RBNode<T>(data, red);

    // 查找插入位置
    RBNode<T>* p = nullptr;
    RBNode<T>* cur = root;
    while (cur) {
        p = cur;
        if (data < cur->data) {
            cur = cur->left;
        }
        else {
            cur = cur->right;
        }
    }

    // 连接父节点和插入节点
    node->parent = p;
    if (!p) {
        root = node;
    }
    else if (data < p->data) {
        p->left = node;
    }
    else {
        p->right = node;
    }

    // 修复红黑树
    insertFixup(node);
}

删除操作

删除节点时,是突出之处。当要删除的节点有两个非空子节点时,无法直接删除该节点。如果直接删除该节点,会破坏红黑树的平衡性,因此,在删除前需要找到要删除节点的前驱或后继节点来替换要删除的节点。同时,需要进行旋转和变色来保持红黑树平衡。

① 如果要删除的节点没有孩子或只有一个孩子,直接删除。

② 如果要删除的节点有两个孩子,则找到它的后继节点(该节点的左子树中最小的节点),并将后继节点的数据替换到要删除的节点中,然后删除后继节点。

删除操作的代码实现如下:

template<typename T>
void RBTree<T>::removeFixup(RBNode<T>* node, RBNode<T>* p) {
    while (node != root && (!node || node->color == black)) {
        if (node == p->left) {
            RBNode<T>* s = p->right;
            if (s->color == red) {
                s->color = black;
                p->color = red;
                leftRotate(p);
                s = p->right;
            }
            if ((!s->left || s->left->color == black) && (!s->right || s->right->color == black)) {
                s->color = red;
                node = p;
                p = p->parent;
            }
            else {
                if (!s->right || s->right->color == black) {
                    s->left->color = black;
                    s->color = red;
                    rightRotate(s);
                    s = p->right;
                }
                s->color = p->color;
                p->color = black;
                s->right->color = black;
                leftRotate(p);
                node = root;
            }
        }
        else {
            RBNode<T>* s = p->left;
            if (s->color == red) {
                s->color = black;
                p->color = red;
                rightRotate(p);
                s = p->left;
            }
            if ((!s->right || s->right->color == black) && (!s->left || s->left->color == black)) {
                s->color = red;
                node = p;
                p = p->parent;
            }
            else {
                if (!s->left || s->left->color == black) {
                    s->right->color = black;
                    s->color = red;
                    leftRotate(s);
                    s = p->left;
                }
                s->color = p->color;
                p->color = black;
                s->left->color = black;
                rightRotate(p);
                node = root;
            }
        }
    }
    if (node) {
        node->color = black;
    }
}

template<typename T>
void RBTree<T>::remove(const T& data) {
    RBNode<T>* node = searchNode(data);
    if (!node) {
        return;
    }

    // 记录删除节点的颜色和父节点
    bool color = node->color;
    RBNode<T>* p = node->parent;

    // 如果有两个孩子,则找到后继节点,用后继节点替代
    if (node->left && node->right) {
        RBNode<T>* s = node->right;
        while (s->left) {
            s = s->left;
        }
        node->data = s->data;
        node = s;
        p = s->parent;
        color = s->color;
    }

    // 替换节点为其左右孩子节点
    RBNode<T>* child = node->left ? node->left : node->right;
    if (child) {
        child->parent = p;
    }

    if (!p) {
        root = child;
    }
    else if (node == p->left) {
        p->left = child;
    }
    else {
        p->right = child;
    }

    // 修复红黑树
    if (color == black) {
        removeFixup(child, p);
    }

    delete node;
}

查找操作

查找操作与普通二叉查找树中的查找操作相同,效率为O(log n)。具体实现如下:

template<typename T>
RBNode<T>* RBTree<T>::searchNode(const T& data) const {
    RBNode<T>* cur = root;
    while (cur) {
        if (cur->data == data) {
            return cur;
        }
        else if (data < cur->data) {
            cur = cur->left;
        }
        else {
            cur = cur->right;
        }
    }
    return nullptr;
}

示例说明

下面给出两个关于红黑树的示例:

示例一

给定一个字符串数组,如何用红黑树实现字符串去重?

思路:遍历字符串数组,将其插入红黑树中,如果遇到重复的字符串,则不插入,最终得到的树中的所有节点即为去重后的字符串。插入时,需要重载比较运算符。

代码实现:

#include "RBTree.h"
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<string> strs = { "hello", "world", "hello", "cpp", "world" };
    RBTree<string> tree;
    for (auto& str : strs) {
        tree.insert(str);
    }
    // 输出去重后的字符串
    tree.inOrder([](const string& str) {
        cout << str << " ";
    });
    // 输出结果:cpp hello world 
    return 0;
}

示例二

如何用红黑树实现一个有序集合?

思路:通过红黑树来实现一个有序集合,可以保证该集合中的元素按照从小到大的顺序排列。在插入节点和删除节点时,保持红黑树的平衡性,通过遍历树,可以输出有序集合中的元素。

代码实现:

#include "RBTree.h"
#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> nums = { 3, 1, 4, 5, 2 };
    RBTree<int> tree;
    for (auto& num : nums) {
        tree.insert(num);
    }

    // 遍历树,输出集合元素
    tree.inOrder([](int num) {
        cout << num << " ";
    });
    // 输出结果:1 2 3 4 5

    return 0;
}

以上就是关于C++数据结构之红黑树的实现的完整攻略,希望能对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++数据结构之红黑树的实现 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 数据结构之数组Array实例详解

    数据结构之数组Array实例详解 什么是数组? 数组是一种由相同类型元素组成的集合,它们在内存中是连续存储的。通过下标可以访问数组中的元素,下标从0开始,到length-1结束。 定义数组 使用Array构造函数 可以使用Array构造函数来创建数组。以下是一些数组的创建方式。 var array1 = new Array(); // 创建空数组 var a…

    数据结构 2023年5月17日
    00
  • C语言二叉树的概念结构详解

    C语言二叉树的概念结构详解 什么是二叉树 二叉树是一种特殊的树形结构,它由一个根节点和若干个子树组成,其中每个节点都最多有两个子节点,分别称为它的左子节点和右子节点。 二叉树的结构 一个二叉树通常由以下几个结构组成: 数据域:存储节点所包含的数据 左节点:节点左侧的子节点,如果为空节点,则表示当前节点没有左子树 右节点:节点右侧的子节点,如果为空节点,则表示…

    数据结构 2023年5月17日
    00
  • C++数据结构之二叉搜索树的实现详解

    C++数据结构之二叉搜索树的实现详解 1. 什么是二叉搜索树? 二叉搜索树是一种二叉树,其中每个节点都包含一个键值,且每个节点的键值都大于其左子树中任何节点的键值,小于其右子树中任何节点的键值。如下图所示: 9 / \ 4 15 / \ 12 20 在上面的二叉搜索树中,节点的键值分别是9, 4, 15, 12, 20,且每个节点的键值都符合上述定义。 2.…

    数据结构 2023年5月17日
    00
  • C语言植物大战数据结构二叉树递归

    C语言植物大战数据结构二叉树递归攻略 什么是二叉树? 二叉树是一种树形结构,每个节点最多只能有两个子节点。这两个子节点被称为左子树和右子树。二叉树具有自己的结构,因此它们也适合表示具有层次结构的数据。 什么是递归? 递归是一种算法的编写技巧,通过自己来定义自己的方法,以达到解决问题的目的。递归算法把复杂的问题简单化,但是也存在着可能导致程序无限递归的风险。 …

    数据结构 2023年5月17日
    00
  • 常用的Java数据结构知识点汇总

    常用的Java数据结构知识点汇总 简介 Java中的数据结构是Java程序开发中非常重要的一部分。掌握常用的数据结构知识点是编写高效、优秀的Java程序的关键之一。本文将详细讲解Java中常用的数据结构知识点,并提供代码示例说明。 数组(Array) 数组是一组相同类型的数据集合,通过数组下标来访问数据,数组长度确定后就无法改变。在Java中,数组可以是基本…

    数据结构 2023年5月17日
    00
  • Python数据结构之顺序表的实现代码示例

    针对“Python数据结构之顺序表的实现代码示例”,我可以给出以下完整攻略: 什么是顺序表 顺序表是一种线性结构,是用一维数组来存储数据元素的有序集合。它支持随机访问,可以对任意位置的元素进行查找、插入、删除等操作。 顺序表的实现代码示例 以下是Python中实现顺序表的示例代码,以及相关的操作函数,包括创建空表、获取表长度、查找元素、插入元素、删除元素等。…

    数据结构 2023年5月17日
    00
  • 2021最新Android笔试题总结美团Android岗职能要求

    2021最新Android笔试题总结和美团Android岗职能要求 简介 本文主要介绍了2021最新的Android笔试题总结和美团Android岗职能要求,旨在为正在面试美团Android岗位的面试者提供参考。 笔试题总结 下面是近期美团Android面试中出现的一些笔试题目: 1. 请描述Android中BroadcastReceiver的生命周期。 安…

    数据结构 2023年5月17日
    00
  • C语言数据结构之模式匹配字符串定位问题

    C语言数据结构之模式匹配字符串定位问题 什么是模式匹配字符串定位? 模式匹配字符串定位即在一个文本串中匹配一个模式串,并且返回模式串在文本串中第一次出现的位置。 例如,对于文本串“this is a test string”,我们想要匹配模式串“test”,我们期望得到的结果是第一次出现的位置为10。 KMP算法 算法思路 KMP算法是一种高效的字符串匹配算…

    数据结构 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部