【数据结构进阶】红黑树超详解 + 实现(附源码)

devtools/2025/1/24 16:10:27/
🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:数据结构">数据结构

目录

前言

一、红黑树介绍

二、红黑树原理详解

三、红黑树的实现

1. 节点定义

2. 红黑树类型定义及接口声明

3. 红黑树的插入(重点)

颜色设置

平衡调整

总代码 

4. 红黑树的查找

5. 中序遍历、拷贝构造和析构

6. 检查红黑树是否合法

7. 程序全部代码

总结


前言

        在传统二叉搜索树的基础上,我们学习了AVL树,它通过独特的平衡机制,确保了稳定高效的插入、查找和删除操作。然而,由于其频繁的平衡调整,可能使性能收到一定影响。因此,另一种自平衡二叉搜索树——红黑树应运而生。本篇文章,我们将深入探讨红黑树的实现原理,带你解开其简洁而深邃的设计之美。

        与AVL树相同,之后的红黑树实现当中,我们会将键值对(pair)作为节点数据域。

        如果你不是很了解二叉搜索树、AVL树或pair,可以参阅这两篇文章:

数据结构】二叉搜索树(二叉排序树)-CSDN博客

数据结构进阶】AVL树深度剖析 + 实现(附源码)-CSDN博客

        注:若无特殊说明,本文所提到的所有“路径”都指根节点到NULL的路径。

一、红黑树介绍

        红黑树(Red-Black-Tree)是一种自平衡二叉搜索树,但它并非像AVL树那样“严格平衡”,而是允许一定的不平衡存在,在保证增删查改效率没有太大影响的情况下,显著减少了平衡调整的次数,提升总体效率。

        AVL树一般通过节点的“平衡因子”来维持平衡,而红黑树通过给节点“着色”,确保其高效性。

在非空情况下,红黑树的性质(约束条件)如下:

1. 它是一棵二叉搜索树

2. 每一个节点都会被着色,不是黑色就是红色

3. 根节点必须为黑色

4. 对于一个红色节点,它的孩子或为空,或是黑色。也就是说路径上不能有连续红色节点

5. 从根节点到NULL节点的所有路径上黑色节点的数量都相同

有了以上约束条件,就可确保其没有一条路径长度能够超出其他路径的2倍,从而保证高效操作。 

如上图,每一条路径上都有相同数量的黑色节点。 

二、红黑树原理详解

        那么,红黑树为什么能够控制路径长度呢?

        来看一个极端情况:

        假设一棵红黑树的一条路径上有n个黑色节点,那么由于其所有路径上的黑色节点数量是相同的,所以其所有路径上都一定有n个黑色节点。对于这棵树,最长的可能路径上的节点就是一黑一红一黑一红(要确保无连续红色节点)......一共有2n个节点最短的可能路径上的节点是全黑的,一共有n个节点那么其他可能的路径长度都在n~2n之间。所以说没有一条路径长度能够超出其他路径长度的两倍,也就确保了根节点左右子树的高度比一定在1~2之间

接下来,我们分析一下红黑树的效率。

        设一棵红黑树一共有N个节点,它的最短可能路径的长度h,由于可能的路径长度都在h~2h之间,那么节点数N就满足 2^{h}-1\leq N\leq 2^{2h}-1 。由此可知,在路径最短情况下,其进行增删查改的时间消耗为O(logN);路径最长情况下,进行查找的时间消耗为O(2logN)。因此红黑树增删查改的时间复杂度为O(logN)

        红黑树的平衡控制相对AVL树较为抽象,但由于那几点约束条件,控制了最长路径和最短路径之比,间接地使红黑树达到了“近似平衡”,增删查改地时间消耗不会过大,并且相比AVL树,旋转调整次数会更少。

三、红黑树的实现

1. 节点定义

        红黑树节点及其颜色定义如下:

enum Color//枚举值表示颜色
{RED,BLACK
};//节点
template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;//数据域RBTreeNode<K, V>* _left;//指向左孩子的指针RBTreeNode<K, V>* _right;//指向右孩子的指针RBTreeNode<K, V>* _parent;//指向父亲的指针Color _col;//颜色RBTreeNode(const pair<K, V>& kv)//节点构造:_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};

与AVL树相同,红黑树也采用三叉链表结构,更有利于节点之间的控制访问。

在节点构造当中,我们并没有设置节点的初始颜色,之后在实现节点插入的实现当中,我们会重点讨论初始颜色的问题。

2. 红黑树类型定义及接口声明

        我们需要实现的接口如下:

//红黑树类
template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public://强制生成无参构造RBTree() = default;//拷贝构造RBTree(const RBTree<K, V>& t);//析构函数~RBTree();//插入bool Insert(const pair<K, V>& kv);//查找Node* Find(const K& key);//中序遍历void Inorder();//判断是否为合法红黑树bool IsValidRBTree();
private:Node* _root = nullptr;//根节点指针
};

3. 红黑树的插入(重点)

        为了保持红黑树增删查改的高效性,插入操作要保证满足红黑树的性质

        红黑树的插入过程大致如下:

1. 首先按照二叉搜索树的插入规则确定插入节点的位置。

2. 插入新节点,并确定新节点的颜色。

3. 为保持红黑树的性质,需要对原有树结构进行平衡调整


颜色设置

 我们首先来探讨新节点的颜色设置问题

        如果新插入的节点是根节点(树为空),毋庸置疑,为黑色。并且此时整个结构已经满足红黑树性质,插入完成。

        如果新插入的节点不是根节点,那么能否设置初始颜色为黑色呢?我们假设新插入的节点为黑色

不难发现,插入节点4之后,已经不满足红黑树性质5->2->4->NULL这条路径上有3个黑色节点,而5->7->6->NULL这条路径上有2个黑色节点实际上,如果插入黑色节点,不管插入到什么位置,该条路径上的黑色节点数都会增加,而其他路径上的黑色节点数不变,此时一定会违反红黑树的约束条件

那么,我们插入一个红色节点

此时可以看到,插入之后,整个结构依然满足红黑树的性质。当然,这是因为节点2是黑色节点,此时插入结束。若一个红色节点插入在红色节点的下方,出现连续红色节点,那么也会违反约束条件

总之,若插入黑色节点,一定违反红黑树规则,而插入红色节点,可能违反红黑树规则。所以我们退而求其次,将新节点(除根节点外)设置为红色


平衡调整

确定了新节点颜色之后,我们探讨平衡调整的问题

        之前已经提到,我们如果将新节点插入在红色节点的下方,就需要进行平衡调整。有两种情况需要分别讨论。

情况1:仅变色

如上图所示, parent和uncle都是红色,grandfather为黑色,此时插入新节点cur,出现连续红色节点。这种情况下,将parent和uncle变黑,grandfather变红,整个结构就满足了红黑树的性质

        但上图是一种具体情况,如果整棵树是另一棵树的子树,且节点4是红色的,那么变色之后就会再次出现连续的红色节点(节点2和节点4),此时就需要继续向上调整将grandfather作为新的cur,然后找到新的parent和uncle,进行变色或做其他调整(之后会讲解),然后再向上检查,直到遇到根节点或者无连续红色节点为止。

通过观察上图,不难发现:针对需要进行平衡调整的情况,新节点插入之后,parent一定为红,否则无需调整;grandfather一定为黑,否则未插入节点时就已经出现连续红色节点,红黑树原本就有问题。 那么,重要变量就是uncle节点

总结:当uncle为红时,仅需变色:将parent和uncle变黑,grandfather变红。然后将grandfather作为新的cur,找到新的parent和uncle,继续判断、调整,直到遇到根节点或无连续红色节点。

情况2:旋转+变色

        刚才提到uncle是重要变量,那么我们就给出uncle的其他可能情况(不存在或为黑),进而分析各种调整场景。

分类讨论之前,我们首先需要明确需要进行平衡调整的情况下,uncle和cur的关系

一个节点被标记为cur(确定插入位置之后),仅有两种情况:

1. 它是新增节点;2. 它是场景1向上调整时标记的节点


当uncle不存在时,cur一定是新增节点。原因:若cur不是新增节点,则其必然是向上调整时标记的节点,那么一定发生了变色,也就是说cur所在的某条路径A上至少有两个黑色节点(包括一个根节点)。而uncle不存在,则从根节点到uncle(NULL)位置的路径上的黑色节点数一定少于路径A,两条路径黑色节点数量不一致,不满足红黑树性质。

当uncle为黑时,cur一定时向上调整时标记的节点。原因:uncle为黑,则从根节点到uncle的路径B上至少有两个黑色节点。如果cur是新增节点,那么从根节点到cur的路径上的黑色节点数一定少于路径B,两条路径黑色节点数量不一致,不满足红黑树性质。

uncle不存在:

这种情况下,如果只是改变parent和grandfather的颜色,并不能解决问题:变色之后路径4->2->1->NULL上有1个黑色节点,而路径4->NULL上没有黑色节点,不满足红黑树性质

        此时就要配合旋转操作来解决问题。根据三个节点的相对位置,需要我们分情况进行单旋或双旋,从而调整树的结构:

单旋+变色:

可以看到,我们以grandfather为旋转点,进行右/左单旋,然后将parent变黑,grandfather变红,整个结构满足红黑树性质,并且该部分的根已经变成黑色,无需继续向上调整,插入结束

双旋+变色:

双旋完成后,将cur变黑,grandfather变红,整个结构满足红黑树,并且该部分的根已经变成黑色,无需继续向上调整,插入结束

总结:当uncle不存在时,需要根据实际情况进行旋转+变色。单旋完成后要将parent变黑,grandfather变红;双旋完成后要将cur变黑,grandfather变红。操作结束后,该部分的根成为黑色,不会出现连续红色节点,无需再向上调整。 

uncle为黑:

        刚才已经提到,uncle为黑时,cur一定是向上调整时标记的节点。所以我们使用抽象图来表示插入状况:

如图所示, a、b、c、d、e分别表示相应黑色节点数量的子树,当a、b的黑色节点数由n-1变为n时,说明发生了变色,使得cur变为红色。此时由于uncle是黑色,如果直接将parent变黑,grandfather变红,那么根节点到a、b、c路径上的黑色节点数与到d、e的路径不相等,不满足红黑树的性质。所以要以grandfather为旋转点,分情况进行旋转再变色

不难发现,uncle为黑时的旋转、变色逻辑与uncle不存在时完全相同,这里博主就不再一一列举各种旋转情况。总结:当uncle为黑时,需要根据实际情况进行旋转+变色。单旋完成后要将parent变黑,grandfather变红;双旋完成后要将cur变黑,grandfather变红。操作结束后,该部分的根成为黑色,不会出现连续红色节点,无需再向上调整。 


注意:平衡调整完成之后,整棵树的根节点可能变为红色。所以平衡调整结束后,一定要将根节点设置为黑色

红黑树插入的总体过程

总代码 

红黑树插入代码实现:

//插入
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 (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(kv);cur->_col = RED;//插入红色节点if (kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//parent为红,进行平衡调整while (parent && parent->_col == RED){//确定grandfatherNode* grandfather = parent->_parent;if (parent == grandfather->_left){//确定uncleNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//uncle为红,仅变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上判断cur = grandfather;parent = cur->_parent;}else//uncle为黑或不存在,旋转+变色{if (cur == parent->_left)//右单旋{RotateR(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else//左右双旋{RotateL(parent);RotateR(grandfather);//变色cur->_col = BLACK;grandfather->_col = RED;}break;//旋转完成,直接跳出循环}}else{//确定uncleNode* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//uncle为红,仅变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上判断cur = grandfather;parent = cur->_parent;}else//uncle为黑或不存在,旋转+变色{if (cur == parent->_right)//左单旋{RotateL(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else//右左双旋{RotateR(parent);RotateL(grandfather);//变色cur->_col = BLACK;grandfather->_col = RED;}break;//旋转完成,直接跳出循环}}}//最后将根节点设置为黑色_root->_col = BLACK;return true;
}//右单旋
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 (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent) ppNode->_left = subL;else ppNode->_right = subL;subL->_parent = ppNode;}
}//左单旋
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 (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppNode->_left == parent) ppNode->_left = subR;else ppNode->_right = subR;subR->_parent = ppNode;}
}

4. 红黑树的查找

        红黑树的查找逻辑与传统二叉搜索树相同,注意按照查找。

代码如下:

//查找
Node* Find(const K& key)
{Node* cur = _root;while (cur){if (key < cur->_kv.first){cur = cur->_left;//小了往左走}else if (key > cur->_kv.first){cur = cur->_right;//大了往右走}else{return cur;//找到了,返回}}return nullptr;//没找到,返回空指针
}

5. 中序遍历、拷贝构造和析构

        三个函数的实现逻辑与AVL树完全相同。注意拷贝时不要忘记parent指针。

代码如下:

//中序遍历
void Inorder()
{_Inorder(_root);
}
void _Inorder(Node* root)
{if (root == nullptr) return;_Inorder(root->_left);cout << root->_kv.first << ' ' << root->_kv.second << endl;_Inorder(root->_right);
}
//拷贝构造
RBTree(const RBTree<K, V>& t)
{_root = _Copy(t._root);
}
Node* _Copy(Node* root, Node* parent = nullptr)
{if (root == nullptr) return nullptr;Node* NewRoot = new Node(root->_kv);NewRoot->_col = root->_col;//复制颜色NewRoot->_parent = parent;//设置父指针//递归拷贝左子树和右子树NewRoot->_left = _Copy(root->_left, NewRoot);NewRoot->_right = _Copy(root->_right, NewRoot);return NewRoot;
}
//析构函数
~RBTree()
{_Destroy(_root);
}
void _Destroy(Node* root)
{if (root == nullptr) return;_Destroy(root->_left);_Destroy(root->_right);delete root;
}

6. 检查红黑树是否合法

        判断一棵红黑树是否合法,就要判断它是否满足红黑树的性质。可以从以下几点入手:

1. 检查根节点是否为黑色。

2. 当一个节点为红色时,判断其父亲是否是黑色。

3. 检查各个路径上的黑色节点数是否相等。

对于第三点,可以先遍历一条路径(可以选择走最左路径,避免递归),记录路径上的黑色节点个数,然后再判断其他所有路径上的黑色节点个数是否与之一致。

代码实现:

//判断是否为合法红黑树
bool IsValidRBTree()
{//空树,合法if (_root == nullptr) return true;//根节点为红,非法if (_root->_col == RED){cout << "根节点为红" << endl;return false;}int refNum = 0;//记录黑色节点个数Node* cur = _root;while (cur){if (cur->_col == BLACK) refNum++;cur = cur->_left;}//递归检查所有路径return _Check(_root, 0, refNum);
}//路径检查
bool _Check(Node* root, int num, const int refNum)
{if (root == nullptr){//遍历到空,进行黑色节点比较if (num != refNum){cout << "黑色节点数量不相等" << endl;return false;}return true;}//检查是否有连续红色节点if (root->_col == RED && root->_parent->_col == RED){cout << "有连续红色节点" << endl;return false;}//记录当前路径的黑色节点数if (root->_col == BLACK) num++;//递归检查左子树和右子树return _Check(root->_left, num, refNum) && _Check(root->_right, num, refNum);
}

接下来我们写一段代码,插入一组数据,验证红黑树的合法性:

int main()
{RBTree<int, int> t;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto& e : a){t.Insert({ e,e });}t.Inorder();cout << t.IsValidRBTree() << endl;return 0;
}

运行结果:

7. 程序全部代码

红黑树实现全部代码如下:

#include <iostream>
#include <utility>
using namespace std;enum Color//枚举值表示颜色
{RED,BLACK
};//节点
template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;//数据域RBTreeNode<K, V>* _left;//指向左孩子的指针RBTreeNode<K, V>* _right;//指向右孩子的指针RBTreeNode<K, V>* _parent;//指向父亲的指针Color _col;//颜色RBTreeNode(const pair<K, V>& kv)//节点构造:_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}
};//红黑树类
template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public://强制生成无参构造RBTree() = default;//拷贝构造RBTree(const RBTree<K, V>& t){_root = _Copy(t._root);}//析构函数~RBTree(){_Destroy(_root);}//插入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 (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(kv);if (kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//parent为红,进行平衡调整while (parent && parent->_col == RED){//确定grandfatherNode* grandfather = parent->_parent;if (parent == grandfather->_left){//确定uncleNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//uncle为红,仅变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上判断cur = grandfather;parent = cur->_parent;}else//uncle为黑或不存在,旋转+变色{if (cur == parent->_left)//右单旋{RotateR(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else//左右双旋{RotateL(parent);RotateR(grandfather);//变色cur->_col = BLACK;grandfather->_col = RED;}break;//旋转完成,直接跳出循环}}else{//确定uncleNode* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//uncle为红,仅变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上判断cur = grandfather;parent = cur->_parent;}else//uncle为黑或不存在,旋转+变色{if (cur == parent->_right)//左单旋{RotateL(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else//右左双旋{RotateR(parent);RotateL(grandfather);//变色cur->_col = BLACK;grandfather->_col = RED;}break;//旋转完成,直接跳出循环}}}//最后将根节点设置为黑色_root->_col = BLACK;return true;}//查找Node* Find(const K& key){Node* cur = _root;while (cur){if (key < cur->_kv.first){cur = cur->_left;//小了往左走}else if (key > cur->_kv.first){cur = cur->_right;//大了往右走}else{return cur;//找到了,返回}}return nullptr;//没找到,返回空指针}//中序遍历void Inorder(){_Inorder(_root);}//判断是否为合法红黑树bool IsValidRBTree(){//空树,合法if (_root == nullptr) return true;//根节点为红,非法if (_root->_col == RED){cout << "根节点为红" << endl;return false;}int refNum = 0;//记录黑色节点个数Node* cur = _root;while (cur){if (cur->_col == BLACK) refNum++;cur = cur->_left;}//递归检查所有路径return _Check(_root, 0, refNum);}
private://右单旋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 (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent) ppNode->_left = subL;else ppNode->_right = subL;subL->_parent = ppNode;}}//左单旋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 (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppNode->_left == parent) ppNode->_left = subR;else ppNode->_right = subR;subR->_parent = ppNode;}}//中序遍历void _Inorder(Node* root){if (root == nullptr) return;_Inorder(root->_left);cout << root->_kv.first << ' ' << root->_kv.second << endl;_Inorder(root->_right);}//拷贝构造Node* _Copy(Node* root, Node* parent = nullptr){if (root == nullptr) return nullptr;Node* NewRoot = new Node(root->_kv);NewRoot->_col = root->_col;//复制颜色NewRoot->_parent = parent;//设置父指针//递归拷贝左子树和右子树NewRoot->_left = _Copy(root->_left, NewRoot);NewRoot->_right = _Copy(root->_right, NewRoot);return NewRoot;}//销毁void _Destroy(Node* root){if (root == nullptr) return;_Destroy(root->_left);_Destroy(root->_right);delete root;}//路径检查bool _Check(Node* root, int num, const int refNum){if (root == nullptr){//遍历到空,进行黑色节点比较if (num != refNum){cout << "黑色节点数量不相等" << endl;return false;}return true;}//检查是否有连续红色节点if (root->_col == RED && root->_parent->_col == RED){cout << "有连续红色节点" << endl;return false;}//记录当前路径的黑色节点数if (root->_col == BLACK) num++;//递归检查左子树和右子树return _Check(root->_left, num, refNum) && _Check(root->_right, num, refNum);}Node* _root = nullptr;//根节点指针
};int main()
{RBTree<int, int> t;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto& e : a){t.Insert({ e,e });}t.Inorder();cout << t.IsValidRBTree() << endl;return 0;
}

总结

        红黑树通过引入节点颜色和一系列平衡规则,在保持高效查询性能的同时,优化了插入和删除操作的复杂度。与AVL树相比,红黑树对平衡的要求较为宽松,避免了频繁的旋转调整,从而提升了动态操作的效率。尽管红黑树的结构较为复杂,但它通过颜色标记、旋转操作以及路径黑色节点数量的控制,成功实现了查找、插入和删除操作的平衡。在实际应用中,红黑树被广泛用于操作系统、数据库等领域,发挥着其重要的作用。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤


http://www.ppmy.cn/devtools/153176.html

相关文章

HTML5 Canvas实现的跨年烟花源代码

以下是一份基于HTML5 Canvas实现的跨年烟花源代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml">…

【Redis】持久化机制

目录 前言&#xff1a; RDB 触发RDB持久化方法有俩种&#xff1a; 1.手动触发 2.自动触发 RDB文件的优缺点&#xff1a; AOF: AOF工作机制&#xff1a;​编辑 ​编辑重写机制&#xff1a; 前言&#xff1a; Redis是一个内存数据库&#xff0c;将数据存储在内存中&…

计算机网络 (57)改进“尽最大努力交付”的服务

前言 计算机网络中的“尽最大努力交付”服务是网络层的一种数据传输方式。这种服务的特点是网络层只负责尽力将数据报从源端传输到目的端&#xff0c;而不保证数据传输的可靠性。 一、标记与分类 为数据分组打上标记&#xff1a; 给不同性质的分组打上不同的标记&#x…

【运维】什么是Prometheus普罗米修斯?组件式开发

Prometheus是一种开源的监控和报警工具&#xff0c;广泛应用于云计算和DevOps运维中。它主要用于收集和存储时间序列数据&#xff0c;以监控系统的性能和健康状态。 组件式开发&#xff0c;检测服务器就要下载一个检测服务器的组件。 https://prometheus.io/download/ 下载官网…

《从入门到精通:蓝桥杯编程大赛知识点全攻略》(五)-数的三次方根、机器人跳跃问题、四平方和

本博客将详细探讨如何通过二分查找算法来解决这几个经典问题。通过几个实际的例子&#xff0c;我们将展示如何在这些问题中灵活应用二分查找&#xff0c;优化计算过程&#xff0c;并在面对大数据量时保持高效性。 目录 前言 数的三次方根 算法思路 代码如下 机器人跳跃问题…

梯度提升决策树树(GBDT)公式推导

### 逻辑回归的损失函数 逻辑回归模型用于分类问题&#xff0c;其输出是一个概率值。对于二分类问题&#xff0c;逻辑回归模型的输出可以表示为&#xff1a; \[ P(y 1 | x) \frac{1}{1 e^{-F(x)}} \] 其中 \( F(x) \) 是一个线性组合函数&#xff0c;通常表示为&#xff…

20_PlayerPresKey类

修改前 修改后 创建PlayerPrefsKey.cs 编写代码 public static class PlayerPrefsKey{public static readonly string Acct "Acct"; // 账号键名public static readonly string Pass "Pass"; // 密码键名 }重新编写 这样写的好处是将变量存储到一个共有…

使用EVE-NG-锐捷实现静态路由

一、项目拓扑 二、项目实现 1、路由器R1配置 进入特权模式 enable 进入全局模式 configure terminal更改名称为R1 hostname R1关闭域名解析。在域名解析开启的情况下&#xff0c;输错的命令会当做域名进行解析&#xff0c;卡住30秒左右&#xff0c;直至解析超时 …