一篇文章带你了解红黑树并将其模拟实现

在这里插入图片描述

了解红黑树并将其模拟实现

  • 红黑树的概念和性质
    • 1. 概念
    • 2. 性质
  • 红黑树的结构
  • 红黑树的节点定义及红黑树结构成员定义
  • 红黑树的插入
    • 1. 按照二叉搜索的树规则插入新节点
    • 2. 检测新节点插入后,红黑树的性质是否造到破坏
      • 情况一: cur为红,p为红,g为黑,u存在且为红
      • 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
      • 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
    • 3. 插入代码解析
    • 4. 插入动态效果
      • 1. 以升序插入构建红黑树
      • 2. 以降序插入构建红黑树
      • 3. 随机插入构建红黑树
  • 红黑树的验证
  • 红黑树的遍历输出和左旋右旋
    • 1. 遍历输出
    • 2. 左单旋
    • 3. 右单旋
  • 红黑树模拟实现全部代码
  • 红黑树的应用
  • 红黑树与AVL树的比较

红黑树的概念和性质

1. 概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RedBlack通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的

在这里插入图片描述

2. 性质

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色的。
  3. 如果一个节点是红色的,那么它的两个子节点都是黑色的。
  4. 从任意节点到其每个叶子节点的每条路径都包含相同数量的黑色节点,这被称为“黑高相等性”。
  5. 每个叶子节点(NIL节点,通常表示为空节点)是黑色的。

红黑树的这些性质确保了树的平衡性,从而保证了树的高度在对数范围内,使得基本操作的时间复杂度保持在O(log n)级别

红黑树的结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点。

当在红黑树的实现中增加一个头结点时,目的是为了简化操作和处理边界情况,而不必为特殊情况单独编写代码。这个头结点通常是一个黑色节点,位于红黑树的根节点之上,其pParent指向红黑树的根节点,pLeft指向红黑树中最小的节点,pRight指向红黑树中最大的节点。

以下是对头结点的作用和实现细节的解释:

  1. 简化边界条件处理:头结点的存在使得根节点始终是黑色的,因为根节点是头结点的右子节点。这样,在插入和删除等操作中,不需要特殊情况处理根节点的颜色和父节点是否存在的问题。
  2. 快速访问最小和最大节点:头结点的pLeft指向红黑树中最小的节点,pRight指向最大的节点。这意味着你可以在O(1)时间内找到树中的最小和最大节点,而无需进行遍历。
  3. 统一根节点访问:无论是插入、删除还是其他操作,都可以始终从头结点开始进行操作,因为头结点的pParent指向了真正的根节点。这简化了代码逻辑,因为你不必特殊处理根节点的情况。

下面是一个头结点的示例实现:

struct Node {int data;Color color;Node* left;Node* right;Node* parent;
};class RedBlackTree {
private:Node* root; // 实际的根节点Node* header; // 头结点// ...其他成员函数和辅助函数...public:RedBlackTree() : root(nullptr), header(new Node) {header->color = BLACK;header->left = nullptr;header->right = nullptr;header->parent = nullptr;}// ...其他公共成员函数...
};

在这个示例中,我们添加了一个名为header的成员变量,它是一个指向头结点的指针。头结点的初始化在构造函数中完成,并确保它始终是黑色的,根节点则位于头结点的pParent中。头结点的存在将简化红黑树的操作和边界情况处理。

这是一种实现方式,但在后面我们实现不是采用的这种方式,当然你也可以选择这种方式实现,更为简易,红黑树的实现过程只是为了让我们更熟悉它的底层,现实中需要我们实现的场景很少

红黑树的节点定义及红黑树结构成员定义

这里我们的实现采用了三叉链的形式,后面我们会提到这种写法的好处

我们这里使用键值对的模板是为了方便后期模拟实现map和set

enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv){}
};template<class K, class V>
struct RBTree
{typedef RBTreeNode<K,V> Node;
private:Node* _root = nullptr;
};
  1. 枚举类型 Colour:这里定义了一个枚举类型,包括两个成员 REDBLACK。这个枚举类型用于表示红黑树中节点的颜色。在红黑树中,节点可以是红色或黑色,用这个枚举类型来表示节点颜色是一种常见的做法。

  2. 结构体 RBTreeNode<K, V>:这个结构体定义了红黑树的节点结构,其中包含以下成员变量:

    • _left_right:分别指向节点的左子节点和右子节点的指针。
    • _parent:指向节点的父节点的指针。
    • _kv:用于存储键值对(Key-Value Pair)的成员变量。这个键值对通常用于存储节点的数据。
    • _col:表示节点的颜色,可以是 REDBLACK

    构造函数 RBTreeNode(const pair<K, V>& kv) 用于初始化节点对象,将各个成员变量赋初值,包括键值对 _kv 和颜色 _col

  3. 结构体 RBTree<K, V>:这个结构体定义了整个红黑树的结构,其中包含以下成员变量和一个别名:

    • _root:指向红黑树的根节点的指针。根节点是红黑树的起始点,所有操作都从根节点开始。

    typedef RBTreeNode<K,V> Node; 定义了一个别名 Node,用于表示红黑树节点的类型。这样做可以简化代码,使得在代码中引用节点类型更加方便。

红黑树的插入

1. 按照二叉搜索的树规则插入新节点

bool Insert(const pair<K,V>& kv)
{if (_root == nullptr)//为空直接插入{_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur)//同搜索树规则找到插入位置{if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;//新插入节点即为红节点,不懂结合性质和我下面的讲解if (parent->_kv.first < kv.first)//同搜索树规则先直接插入{parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//更新新插入节点的父指针while (parent && parent->_col == RED){Node* grandfater = parent->_parent;assert(grandfater);assert(grandfater->_col == BLACK);if (parent == grandfater->_left)//具体看下面讲解{Node* uncle = grandfater->_right;// 情况一 : uncle存在且为红,变色+继续往上处理if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfater->_col = RED;// 继续往上处理cur = grandfater;parent = cur->_parent;}// 情况二+三:uncle不存在 + 存在且为黑else{// 情况二:右单旋+变色if (cur == parent->_left){RotateR(grandfater);parent->_col = BLACK;grandfater->_col = RED;}else{// 情况三:左右单旋+变色RotateL(parent);RotateR(grandfater);cur->_col = BLACK;grandfater->_col = RED;}break;}}else // (parent == grandfater->_right){Node* uncle = grandfater->_left;// 情况一if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfater->_col = RED;// 继续往上处理cur = grandfater;parent = cur->_parent;}else{// 情况二:左单旋+变色if (cur == parent->_right){RotateL(grandfater);parent->_col = BLACK;grandfater->_col = RED;}else{// 情况三:右左单旋+变色RotateR(parent);RotateL(grandfater);cur->_col = BLACK;grandfater->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

其实前面的代码和我们上一篇讲到的AVL树的插入类似,只不过这里的红黑树没有了平衡因子,而变为了颜色

接下来我们看每种情况的解析

2. 检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

情况一: cur为红,p为红,g为黑,u存在且为红

在这里插入图片描述

cur和p均为红解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

在这里插入图片描述

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转,p、g变色–p变黑,g变红

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

在这里插入图片描述

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,则转换成了情况2

3. 插入代码解析

  1. 如果 _root 是空的,表示红黑树为空,那么将创建一个新的节点 _root,并将其颜色设置为黑色。这是根节点,并且遵循红黑树规则的规定,根节点必须为黑色。
  2. 如果 _root 不为空,那么代码会在树中查找正确的位置来插入新节点。通过遍历树的方式,找到正确的父节点 parent,以及新节点要插入的位置 cur
  3. 如果要插入的键已经存在于树中,就返回 false,因为不允许键重复。
  4. 创建新节点 cur 并将其颜色设置为红色。新节点的颜色一开始设置为红色,这是为了满足红黑树的性质。
  5. 然后,代码进入一个循环,循环的目的是确保插入新节点后仍然满足红黑树的性质。这是红黑树插入操作的关键部分。
    • 在循环中,首先查看 parent 节点的颜色,如果 parent 是红色,那么需要进一步处理来保持红黑树性质。
    • 然后,代码会检查 uncle 节点的颜色,即 parent 的兄弟节点。根据 uncle 的颜色,分为情况一、情况二和情况三。
    • 最后,退出循环后将根节点的颜色设置为黑色,以确保根节点满足红黑树的性质。

4. 插入动态效果

1. 以升序插入构建红黑树

在这里插入图片描述

2. 以降序插入构建红黑树

在这里插入图片描述

3. 随机插入构建红黑树

在这里插入图片描述

红黑树的验证

bool IsBalance(){if (_root == nullptr){return true;}if (_root->_col == RED)//检查根节点是否为黑{cout << "根节点不是黑色" << endl;return false;}// 黑色节点数量基准值int benchmark = 0;return PrevCheck(_root, 0, benchmark);}

返回值函数PrevCheck的实现

bool PrevCheck(Node* root, int blackNum, int& benchmark)
{if (root == nullptr){if (benchmark == 0){benchmark = blackNum;return true;}if (blackNum != benchmark){cout << "某条黑色节点的数量不相等" << endl;return false;}else{return true;}}if (root->_col == BLACK){++blackNum;}if (root->_col == RED && root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return PrevCheck(root->_left, blackNum, benchmark)&& PrevCheck(root->_right, blackNum, benchmark);
}
  1. 黑高度相同性质检查:函数使用递归方式遍历树中的节点,并维护一个 blackNum 变量,用于跟踪从根节点到当前节点经过的黑色节点数。在遍历过程中,它会检查每条从根到叶子节点的路径上的黑色节点数是否相同。如果路径上的黑色节点数不相同,说明违反了红黑树的性质。
  2. 连续红色节点检查:在遍历的过程中,代码还检查是否存在连续的红色节点。在红黑树中,不允许存在相邻的两个红色节点。如果存在连续的红色节点,也违反了红黑树的性质。
  3. 返回值:函数返回一个布尔值,用于指示红黑树是否满足性质。如果在遍历过程中发现任何性质被破坏,函数将返回 false,否则返回 true

这个函数是用于验证红黑树的一种常见方法,用于检查树是否保持了红黑树的性质,包括黑高度相同和不连续的红色节点。如果该函数返回 true,则表示树是一个有效的红黑树,否则表示树的结构违反了红黑树的性质。

红黑树的遍历输出和左旋右旋

1. 遍历输出

void _InOrder(Node* root)
{if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);
}

这里的遍历是一种按照节点值的大小顺序遍历二叉搜索树(BST)的方法。在这里,红黑树也是一种BST,因此中序遍历可以用来按键的顺序输出树中的节点。

函数的主要逻辑包括:

  1. 如果输入的 root 节点为空(即树为空),则直接返回,不执行任何操作。
  2. 否则,函数会递归地执行以下操作:
    • 先递归调用 _InOrder 函数来遍历左子树(左子节点)。
    • 输出当前节点的键值对(这里假设键是整数,值是整数,根据需要调整输出格式)。
    • 最后递归调用 _InOrder 函数来遍历右子树(右子节点)。

2. 左单旋

void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}}
  1. 首先,将 parent 节点的右子节点 subRsubR 的左子节点 subRL 分别保存起来。subR 将会成为新的根节点,而 subRL 将成为 parent 节点的右子节点。
  2. 接下来,将 parent 节点的右子节点指针 _right 指向 subRL,同时确保 subRL 的父指针 _parent 指向 parent。这一步是将 subRLparent 连接起来。
  3. parent 节点的父节点指针 ppNode 保存起来。这是为了在旋转后更新 ppNode 的左子节点或右子节点指向 subR,以确保树的连接正确。
  4. subR 的左子节点指针 _left 指向 parent,同时将 parent 的父节点指针 _parent 指向 subR。这一步是将 parent 旋转到 subR 的位置上。
  5. 接下来,处理根节点的情况。如果 _root 指向 parent,那么现在 _root 应该指向 subR,因此将 _root 更新为 subR,并将 subR 的父指针设置为 nullptr
  6. 如果 _root 不指向 parent,则需要根据 ppNode 的情况来更新 ppNode 的左子节点或右子节点指向 subR,以确保整棵树的连接正确。

左单旋和右单旋和我们之前的AVL树实现基本一致,若不能理解请看上一篇文章

3. 右单旋

void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR){subLR->_parent = parent;}Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}
  1. 首先,将 parent 节点的左子节点 subLsubL 的右子节点 subLR 分别保存起来。subL 将会成为新的根节点,而 subLR 将成为 parent 节点的左子节点。
  2. 接下来,将 parent 节点的左子节点指针 _left 指向 subLR,同时确保 subLR 的父指针 _parent 指向 parent。这一步是将 subLRparent 连接起来。
  3. parent 节点的父节点指针 ppNode 保存起来。这是为了在旋转后更新 ppNode 的左子节点或右子节点指向 subL,以确保树的连接正确。
  4. subL 的右子节点指针 _right 指向 parent,同时将 parent 的父节点指针 _parent 指向 subL。这一步是将 parent 旋转到 subL 的位置上。
  5. 接下来,处理根节点的情况。如果 _root 指向 parent,那么现在 _root 应该指向 subL,因此将 _root 更新为 subL,并将 subL 的父指针设置为 nullptr
  6. 如果 _root 不指向 parent,则需要根据 ppNode 的情况来更新 ppNode 的左子节点或右子节点指向 subL,以确保整棵树的连接正确。

红黑树模拟实现全部代码

#pragma once
#include<iostream>
#include<assert.h>
#include<time.h>
using namespace std;enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv){}
};template<class K, class V>
struct RBTree
{typedef RBTreeNode<K,V> Node;
public:bool Insert(const pair<K,V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfater = parent->_parent;assert(grandfater);assert(grandfater->_col == BLACK);if (parent == grandfater->_left){Node* uncle = grandfater->_right;// 情况一 : uncle存在且为红,变色+继续往上处理if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfater->_col = RED;// 继续往上处理cur = grandfater;parent = cur->_parent;}// 情况二+三:uncle不存在 + 存在且为黑else{// 情况二:右单旋+变色if (cur == parent->_left){RotateR(grandfater);parent->_col = BLACK;grandfater->_col = RED;}else{// 情况三:左右单旋+变色RotateL(parent);RotateR(grandfater);cur->_col = BLACK;grandfater->_col = RED;}break;}}else // (parent == grandfater->_right){Node* uncle = grandfater->_left;// 情况一if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfater->_col = RED;// 继续往上处理cur = grandfater;parent = cur->_parent;}else{// 情况二:左单旋+变色if (cur == parent->_right){RotateL(grandfater);parent->_col = BLACK;grandfater->_col = RED;}else{// 情况三:右左单旋+变色RotateR(parent);RotateL(grandfater);cur->_col = BLACK;grandfater->_col = RED;}break;}}}_root->_col = BLACK;return true;}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){if (_root == nullptr){return true;}if (_root->_col == RED){cout << "根节点不是黑色" << endl;return false;}// 黑色节点数量基准值int benchmark = 0;return PrevCheck(_root, 0, benchmark);}private:bool PrevCheck(Node* root, int blackNum, int& benchmark){if (root == nullptr){if (benchmark == 0){benchmark = blackNum;return true;}if (blackNum != benchmark){cout << "某条黑色节点的数量不相等" << endl;return false;}else{return true;}}if (root->_col == BLACK){++blackNum;}if (root->_col == RED && root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return PrevCheck(root->_left, blackNum, benchmark)&& PrevCheck(root->_right, blackNum, benchmark);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR){subLR->_parent = parent;}Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}private:Node* _root = nullptr;
};

红黑树的应用

红黑树是一种自平衡的二叉搜索树,由于其平衡性和高效性能,广泛用于各种计算机科学和软件工程领域。以下是一些红黑树的常见应用:

  1. C++ STL中的std::mapstd::set:C++标准模板库(STL)中的std::mapstd::set通常使用红黑树实现。它们用于实现有序的关联容器,其中键值对被自动排序并保持平衡,以支持高效的查找、插入和删除操作。
  2. 数据库系统:红黑树常用于数据库系统中的索引结构,如B+树的实现中。数据库中的索引需要高效地支持查找、范围查询和排序等操作,而红黑树是一种性能良好的选择。
  3. 操作系统:红黑树在操作系统中用于进程调度、虚拟内存管理和文件系统的实现。在这些场景中,需要高效的数据结构来管理和操作各种系统资源。
  4. 网络路由表:红黑树被广泛用于路由表的实现,以支持网络路由器和交换机等网络设备的高效路由查找。
  5. 编译器:编译器可以使用红黑树来管理符号表,以便进行语法分析、类型检查和代码生成。
  6. 计算机图形学:在计算机图形学中,红黑树可用于空间分割数据结构,如八叉树和四叉树的实现,以支持高效的图形渲染和碰撞检测。
  7. 高性能库和框架:红黑树常被用作高性能库和框架中的核心数据结构,以提供高效的数据管理和操作功能。
  8. 实时系统:实时系统需要高效的数据结构来管理任务和事件,红黑树可以用于实现定时器和调度器。

总的来说,红黑树是一种多功能的数据结构,适用于需要高效的自平衡二叉搜索树的任何领域,特别是需要高效的插入、删除和查找操作的场景。其平衡性和性能使其成为各种应用中的重要工具。

红黑树与AVL树的比较

红黑树(Red-Black Tree)和AVL树(Adelson-Velsky and Landis Tree)都是自平衡二叉搜索树,它们在维护平衡性和支持高效的插入、删除和查找操作方面有很多相似之处,但也有一些关键的区别。以下是红黑树和AVL树之间的比较:

  1. 平衡性要求
    • 红黑树:红黑树放宽了平衡性要求,它保证了树的高度最多是其2倍。
    • AVL树:AVL树要求更为严格,它保证树的高度差不超过1,因此AVL树的平衡性更强。
  2. 性能
    • 红黑树:由于平衡性要求相对较低,插入和删除操作在平均情况下可能比AVL树更快,因此在那些需要频繁插入和删除操作的场景中,红黑树可能更适合。
    • AVL树:AVL树在查找操作上可能稍微快一些,因为它的平衡性更严格,但它的插入和删除操作可能更慢,因为它需要更频繁地执行旋转操作来保持平衡。
  3. 旋转操作
    • 红黑树:红黑树的旋转操作相对较少,因为它放宽了平衡性要求。通常,红黑树的旋转操作较少,但需要更多的颜色变换操作来保持平衡。
    • AVL树:AVL树的旋转操作更频繁,因为它要求更严格的平衡性。这可能导致在插入和删除操作中执行更多的旋转。
  4. 内存消耗
    • 红黑树:由于红黑树的平衡性要求较低,通常会消耗更少的内存,因为不需要额外的平衡因子来跟踪节点的高度差。
    • AVL树:AVL树需要为每个节点存储平衡因子,这可能会导致更多的内存消耗。
  5. 应用场景
    • 红黑树:适用于需要高效的插入和删除操作,而对查找操作的性能要求相对较低的场景,如C++的STL中的std::mapstd::set
    • AVL树:适用于对查找操作有更高性能要求的场景,但可以容忍较慢的插入和删除操作的情况,如数据库索引。

总的来说,选择使用红黑树还是AVL树取决于应用的需求和性能要求,在C++的map和set的实现中,底层调用的就是红黑树,但在实际的面试和工作中,红黑树的应用可能更为广泛。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/76696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从“白人饭”到美味佳肴,拓世AI为你打造独一无二的饮食计划

最近“白人饭”作为一种饮食方式在社交媒体上火了&#xff0c;成为打工人新的“午餐之光”。所谓“白人饭”&#xff0c;就是花最少的功夫准备仅仅能维系基本器官正常运作的食物&#xff0c;主打生吃或者简单炒&#xff0c;比如一个丹麦网友晒出的同事的午饭就是几根小胡萝卜和…

AjaxJavaScriptcss模仿百度一下模糊查询功能

1、效果 如下图所示&#xff0c;我们在输入大学时&#xff0c;程序会到后端查询名字中包含大学的数据&#xff0c;并展示到前端页面。 用户选择一个大学&#xff0c;该大学值会被赋值到input表单&#xff0c;同时关闭下拉表单&#xff1b; 当页面展示的数据都不符合条件时&…

四化智造MES(WEB)与金蝶云星空对接集成原材料/标准件采购查询(待采购)连通采购订单新增(其他采购订单行关闭-TEST)

四化智造MES&#xff08;WEB&#xff09;与金蝶云星空对接集成原材料/标准件采购查询&#xff08;待采购&#xff09;连通采购订单新增(其他采购订单行关闭-TEST) 数据源系统:四化智造MES&#xff08;WEB&#xff09; MES系统是集成生产管理、品质管理、设备管理、BI数据中心、…

AI是风口还是泡沫?

KlipC报道&#xff1a;狂热的人工智能追捧潮有所冷静&#xff0c;投资者在“上头”的追涨之后&#xff0c;开始回归到对基本面的关注。 KlipC的合伙人Andi D表示&#xff1a;“近日&#xff0c;有关英伟达二季度“破纪录”财报涉嫌造假的话题正在社交媒体和投资者论坛中甚嚣尘上…

MATLAB实现数据插值

目录 一.理论知识 二.一维插值实例 三.二维插值实例 一.理论知识 所谓插值&#xff0c;顾名思义&#xff0c;插入数值。很多时候&#xff0c;我们仅有离散点上的数据&#xff0c;这时如果我们想要分析变量之间的函数关系&#xff0c;则无法实现。但如果通过插值处理&#xf…

C#学习系列之UDP同端口收发问题

C#学习系列之UDP同端口收发问题 前言解决办法关于JoinMulticastGroup总结 前言 想测试自己的程序问题&#xff0c;建立了两个UDP程序&#xff0c;一个往端口中接到数就传出去&#xff0c;另一个从这个端口接数据来解析。 出现的问题是 每次打开端口&#xff0c;另一个程序就无…

AIGC之文本内容生成概述(下)—— GPT

GPT&#xff08;GenerativePre-TrainedTransformer&#xff09; 提到GPT模型&#xff0c;就不得不说众所周知的ChatGPT模型&#xff0c;ChatGPT的发展可以追溯到2018年&#xff0c;当时OpenAI发布了第一代GPT模型&#xff0c;即GPT-1&#xff0c;该模型采用Transformer结构和自…

Python实现机器学习(上)— 基础知识介绍及环境部署

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本门课程将介绍人工智能相关概念&#xff0c;重点讲解机器学习原理机器基本算法&#xff08;监督学习及非监督学习&#xff09;。使用python&#xff0c;结合sklearn、jupyter-notebook进行编程&#xff0c;介绍iris、匹马…

Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示…

Docker部署jenkins

目录 一、jenkins原理二、Docker部署jenkins1.下载jenkins镜像文件2.查看下载的jenkins镜像3.创建Jenkins挂载目录并授权权限4.创建并启动Jenkins容器5.查看jenkins是否启动成功6.查看docker容器日志7.配置镜像加速8.访问Jenkins页面&#xff0c;输入ip地址加上9000端口9.获取管…

aarch64 arm64 部署 stable diffusion webui 笔记 【1】准备 venv 安装pytorch 验证cuda

aarch64 pytorch(没有aarch64对应版本&#xff0c;自行编译) pytorch-v2.0.1 cuda arm64 aarch64 torch 2.0.1cu118 源码编译笔记【2】验证cuda安装 成功_hkNaruto的博客-CSDN博客 创建venv [rootceph3 stable-diffusion-webui]# /usr/local/Python-3.10.12/bin/python3 -m v…

flutter开发实战-实现自定义bottomNavigationBar样式awesome_bottom_bar

flutter开发实战-实现自定义bottomNavigationBar样式awesome_bottom_bar 在开发过程中&#xff0c;需要自定义bottomNavigationBar样式&#xff0c;可以自定义实现&#xff0c;这里使用的是awesome_bottom_bar库 一、awesome_bottom_bar 在pubspec.yaml中引入awesome_bottom_…

使用阿里云轻量应用服务器安装Docker进行SpringBoot项目的部署上线

零、写在前面 项目源码&#xff1a;QiuShicheng/Qiu-blog (github.com) 项目是跟着B站up主【三更草堂】做的&#xff0c;本人最终系统是修改了一些前端代码。 (注&#xff1a;源码中前端代码未修改&#xff0c;仍是up主提供的&#xff09; 购买了一个轻量应用服务器2核2G&a…

2023年世界机器人大会回顾

1、前记&#xff1a; 本次记录是我自己去世界机器人博览会参观的一些感受&#xff0c;所有回顾为个人感兴趣部分的机器人产品分享。整个参观下来最大的感受就是科学技术、特别是机器人技术和人工智能毫无疑问地、广泛的应用在我们日常生活的方方面面&#xff0c;在安全巡检、特…

SolVES4.1学习2——导入数据运行模型

使用样例数据运行模型很容易&#xff0c;运行自己的数据要根据教程先对数据进行预处理之后根据教程导入数据。 首先新建一个solves数据库&#xff0c;之后restore。导入数据大概的流程为&#xff1a; 1、导入数据 首先使用PostGIS导入矢量数据。矢量数据包括点位和范围数据。…

亚马逊API接口解析,实现获得AMAZON商品详情

要解析亚马逊API接口并实现获取亚马逊商品详情&#xff0c;你需要按照以下步骤进行操作&#xff1a; 了解亚马逊开发者中心&#xff1a;访问亚马逊开发者中心&#xff0c;并了解相关的API文档、开发者指南和规定。注册开发者账号&#xff1a;在亚马逊开发者中心上注册一个开发…

分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测

分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测 目录 分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测预测效果基本介绍程序设计参考资料致谢 预测效果 基本介绍 分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网…

目标检测笔记(十四): 使用YOLOv8完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)

文章目录 一、目标检测介绍二、YOLOv8介绍三、源码获取四、环境搭建4.1 环境检测 五、数据集准备六、 模型训练6.1 方式一6.2 方式二6.3 针对其他任务 七、模型验证八、模型测试九、模型转换9.1 转onnx9.1.1 方式一 9.2 转tensorRT9.2.1 trtexec9.2.2 代码转换9.2.3 推理代码 一…

SpringBoot整合SSM-junit测试

前提 &#xff1a;创建一个新的springboot模块 创建一个员工案例(搭建) 创建员工实体类创建员工的控制层创建员工的服务层&#xff08;接口–实现类&#xff09;创建员工的数据层&#xff08;接口–实现类&#xff09; 以上的4种文件 是使用SSM必备的文件 创建员工实体类 属性…

0基础学习VR全景平台篇 第97篇:VR步进式漫游

蛙色VR步进式漫游正式上线&#xff01; 为全行业室内场景提供三维空间重建能力&#xff0c;基于真实场景复刻&#xff0c;多维展示打破线下时空限制&#xff0c;提供高性价比的VR空间应用解决方案。 一、什么是步进式漫游&#xff1f; VR步进式漫游&#xff0c;基于AI特征点提…