【C++】第二十一节—一文详解 | 红黑树实现(规则+效率+结构+插入+查找+验证)

【C++】第二十一节—一文详解 | 红黑树实现(规则+效率+结构+插入+查找+验证)

 Hi,我是云边有个稻草人......who?me,be like——→

《C++》本篇文章所属专栏—持续更新中—欢迎订阅

目录

一、红黑树的概念

1.1 红黑树的规则

1.2 思考⼀下,红黑树如何确保最长路径不超过最短路径的2倍的?

1.3 红黑树的效率

二、红黑树的实现

2.1 红黑树的结构

2.2 红⿊树的插⼊

【红⿊树树插⼊⼀个值的⼤概过程】

【情况1:变⾊】

【情况2:单旋+变⾊】

【情况2:双旋+变⾊】

2.3 红黑树的插入代码实现

2.4 红黑树的查找

2.5 红黑树的验证

2.6 红黑树的删除

三、完整代码

RBTree.h

AVLTree.h

test.cpp


正文开始——

一、红黑树的概念

红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表⽰结点的颜⾊,可以是红⾊或者⿊⾊。 通过对任何⼀条从根到叶⼦的路径上各个结点的颜色进行约束,红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍,因⽽是接近平衡的。

1.1 红黑树的规则
  1. 每个结点不是红色就是黑色
  2. 根结点是黑色的
  3. 如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是黑色的,也就是说任意⼀条路径不会有连续的红⾊结点。
  4. 对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点(最长路径 <= 2*最短路径)
说明:《算法导论》等书籍上补充了⼀条每个叶⼦结点(NIL)都是黑色的规则。他这里所指的叶⼦结点不是传统的意义上的叶⼦结点,⽽是我们说的空结点,有些书籍上也把NIL叫做外部结点。NIL是为了⽅便准确的标识出所有路径,《算法导论》在后续讲解实现的细节中也忽略了NIL结点,所以我们知道⼀下这个概念即可。



见下图,为了让上面的第三点规则更闭环一点,补充了当红色节点没有孩子结点的情况,空节点也是黑色的。

1.2 思考⼀下,红黑树如何确保最长路径不超过最短路径的2倍的?
  • 由 规则4 可知,从根到NULL结点的每条路径都有相同数量的黑色结点,所以极端场景下,最短路径就是全是黑色结点的路径,假设最短路径⻓度为bh(black height)。
  • 由 规则2 和 规则3 可知,任意⼀条路径不会有连续的红⾊结点,所以极端场景下,最长的路径就是一黑一红间隔组成,那么最⻓路径的⻓度为2*bh。
  • 综合红⿊树的4点规则⽽⾔,理论上的全⿊最短路径和一黑一红的最长路径并不是在每棵红黑树都存在的。假设任意⼀条从根到NULL结点路径的⻓度为x,那么bh <= x <= 2*bh。

下面是经典的红黑树,可以根据下图去更好的理解上面红黑树的规则:

1.3 红黑树的效率

假设N是红黑树树中结点数量,h最短路径的长度,那么2^h - 1 <= N < 2^(2∗h) - 1 , 由此推出 h ≈ logN,也就是意味着红黑树增删查改最坏也就是⾛最⻓路径2*logN,那么时间复杂度还是O(logN)。

红黑树的表达相对AVL树要抽象⼀些,AVL树通过高度差直观的控制了平衡。红黑树通过4条规则的颜⾊约束,间接的实现了近似平衡,他们效率都是同⼀档次,但是相对而言,插入相同数量的结点,红黑树的旋转次数是更少的,因为他对平衡的控制没那么严格。


二、红黑树的实现

2.1 红黑树的结构
//枚举值表示颜色 enum Colour { RED, BLACK }; //这里我们默认使用key/value结构实现 template<class K,class V> struct RBTreeNode { // 这里更新控制平衡加入parent指针 pair<K, V> _kv; RBTreeNode<K,V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; Colour _col; RBTreeNode(const pair<K,V>& kv) :_kv(kv) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) { } }; template<class K,class V> class RBTree { typedef RBTreeNode<class K, class V> Node; public: private: Node* _root; };
2.2 红⿊树的插⼊
【红⿊树树插⼊⼀个值的⼤概过程】
  1. 插⼊⼀个值按⼆叉搜索树规则进⾏插⼊,插⼊后我们只需要观察是否符合红⿊树的4条规则。
  2. 如果是空树插⼊,新增结点是⿊⾊结点。如果是⾮空树插⼊,新增结点必须红⾊结点,因为⾮空树插⼊,新增⿊⾊结点就破坏了规则4,规则4是很难维护的。
  3. ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是⿊⾊的,则没有违反任何规则,插⼊结束
  4. ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是红⾊的,则违反规则3。进⼀步分析,c是红⾊,p为红,g必为⿊(插入之前保证这棵树是一棵平衡的红黑树),这三个颜⾊都固定了,关键的变化看u的情况,需要根据u分为以下⼏种情况分别处理。

说明:下图中假设我们把新增结点标识为c (cur),c的⽗亲标识为p(parent),p的⽗亲标识为 g(grandfather),p的兄弟标识为u(uncle)。

【情况1:变⾊】
c为红,p为红,g为⿊,u存在且为红,则将p和u变⿊,g变红。在把g当做新的c,继续往上更新。

分析:因为p和u都是红⾊,g是⿊⾊,把p和u变⿊,左边⼦树路径各增加⼀个⿊⾊结点,g再变红,相当于保持g所在⼦树的⿊⾊结点的数量不变,同时解决了c和p连续红⾊结点的问题,需要继续往上更新是因为,g是红⾊,<1>如果g的⽗亲还是红⾊,那么就还需要继续处理;<2>如果g的⽗亲是黑色,则处理结束 了;<3>如果g就是整棵树的根,再把g变回黑色。

情况1只变⾊,不旋转。所以⽆论c是p的左还是右,p是g的左还是右,都是上⾯的变⾊处理⽅式。

  • 跟AVL树类似,图0我们展⽰了⼀种具体情况,但是实际中需要这样处理的有很多种情况。
  • 图1将以上类似的处理进⾏了抽象表达,d/e/f代表每条路径拥有hb个⿊⾊结点的⼦树,a/b代表每 条路径拥有hb-1个⿊⾊结点的根为红的⼦树,hb>=0。
  • 图2/图3/图4,分别展⽰了hb == 0/hb == 1/hb == 2的具体情况组合分析,当hb等于2时,这⾥组合情况上百亿种,这些样例是帮助我们理解,不论情况多少种,多么复杂,处理⽅式⼀样的,变⾊再继续往上处理即可,所以我们只需要看抽象图即可。
图3
【情况2:单旋+变⾊】
c为红,p为红,g为⿊,u不存在或者u存在且为⿊,u不存在,则c⼀定是新增结点,u存在且为⿊,则 c⼀定不是新增,c之前是⿊⾊的,是在c的⼦树中插⼊,符合情况1,变⾊将c从⿊⾊变成红⾊,更新上来的。

分析:p必须变⿊,才能解决,连续红⾊结点的问题,u不存在或者是⿊⾊的,这⾥单纯的变⾊⽆法解决问题,需要旋转+变⾊。

如果p是g的左,c是p的左,那么以g为旋转点进⾏右单旋,再把p变⿊,g变红即可。p变成这颗树的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为p的⽗亲是⿊⾊还是红⾊或者空都不违反规则。
如果p是g的右,c是p的右,那么以g为旋转点进⾏左单旋,再把p变⿊,g变红即可。p变成这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为p的⽗亲是⿊⾊还是红⾊或者空都不违反规则。
【情况2:双旋+变⾊】
c为红,p为红,g为⿊,u不存在或者u存在且为黑,u不存在,则c⼀定是新增结点,u存在且为黑,则c⼀定不是新增,c之前是黑色的,是在c的⼦树中插入,符合情况1,变⾊将c从黑色变成红色,更新上来的。

分析:p必须变黑,才能解决,连续红⾊结点的问题,u不存在或者是黑色的,这里单纯的变⾊无法解决问题,需要旋转+变色。

如果p是g的左,c是p的右,那么先以p为旋转点进行左单旋,再以g为旋转点进行右单旋,再把c变黑,g变红即可。c变成这棵树新的根,这样⼦树黑色结点的数量不变,没有连续的红色结点了,且 不需要往上更新,因为c的父亲是黑色还是红色或者空都不违反规则。
如果p是g的右,c是p的左,那么先以p为旋转点进⾏右单旋,再以g为旋转点进⾏左单旋,再把c变黑,g变红即可。c变成这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且 不需要往上更新,因为c的⽗亲是黑色还是红色或者空都不违反规则。
2.3 红黑树的插入代码实现
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* grandfather = parent->_parent; if (grandfather->_left == parent) { // g // p u // Node* uncle = grandfather->_right; // 情况一:uncle存在且为红 if (uncle && uncle->_col == RED) { // 变色 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else // 情况二:uncle不存在,或者存在且为黑 { if (cur == parent->_left) { // 单旋+变色 // g // p u //c RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // 双旋+变色 // g // p u // c RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } else { // g // u p Node* uncle = grandfather->_left; // 情况一:叔叔存在且为红,变色即可 if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else // 叔叔不存在,或者存在且为黑 { // 情况二:叔叔不存在或者存在且为黑 // 单旋+变色 // g // u p // c if (cur == parent->_right) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { //双旋+变色 // g // u p // c RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK; return true; }
2.4 红黑树的查找

按⼆叉搜索树逻辑实现即可,搜索效率为 O(logN)

Node* Find(const K& key) { Node* cur = _root; while (cur) { if (cur->_kv.first < key) { cur = cur->_right; } else if(cur->_kv.first > key) { cur = cur->_left; } else { return cur; } } return nullptr; }
2.5 红黑树的验证

这里获取最长路径和最短路径,检查最长路径不超过最短路径的2倍是不可行的,因为就算满足这个条件,红黑树也可能颜色不满足规则,当前暂时没出问题,后续继续插入还是会出问题的。所以我们还是去检查4点规则,满足这4点规则,⼀定能保证最长路径不超过最短路径的2倍。

  1. 规则1枚举颜色类型,天然实现保证了颜色不是黑色就是红色。
  2. 规则2直接检查根即可
  3. 规则3前序遍历检查,遇到红色结点查孩子不太方便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲的颜色就⽅便多了。
  4. 规则4前序遍历,遍历过程中用形参记录跟到当前结点的blackNum(黑色结点数量),前序遍历遇到黑色结点就++blackNum,⾛到空就计算出了⼀条路径的黑色结点数量。再任意⼀条路径黑色结点数量作为参考值,依次比较即可。
// blackNum 根到当前结点的黑色结点的数量 bool Check(Node* root, int blackNum, const int refNum) { if (root == nullptr) { // 前序遍历走到空时,意味着一条路径走完了 //cout << blackNum << endl; if (refNum != blackNum) { cout << "存在黑色结点的数量不相等的路径" << endl; return false; } return true; } // 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了 if (root->_col == RED && root->_parent->_col == RED) { cout << root->_kv.first << "存在连续的红色结点" << endl; return false; } if (root->_col == BLACK) { blackNum++; } return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum); } bool IsBalance() { if (_root == nullptr) return true; if (_root->_col == RED) return false; //参考值 int refNum = 0; Node* cur = _root; while (cur) { if (cur->_col == BLACK) { ++refNum; } cur = cur->_left; } return Check(_root, 0, refNum); }
2.6 红黑树的删除

红黑树的删除本章节不做讲解,有兴趣的同学可参考:《算法导论》或者《STL源码剖析》中讲解。


三、完整代码

可以进行红黑树和AVL树的一些性能测试

RBTree.h
#pragma once enum Colour { RED, BLACK }; // 这里我们默认按key/value结构实现 template<class K, class V> struct RBTreeNode { // 这里更新控制平衡也要加入parent指针 pair<K, V> _kv; RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; Colour _col; RBTreeNode(const pair<K, V>& kv) :_kv(kv) , _left(nullptr) , _right(nullptr) , _parent(nullptr) { } }; template<class K,class V> class 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* grandfather = parent->_parent; if (grandfather->_left == parent) { // g // p u // Node* uncle = grandfather->_right; // 情况一:uncle存在且为红 if (uncle && uncle->_col == RED) { // 变色 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else // 情况二:uncle不存在,或者存在且为黑 { if (cur == parent->_left) { // 旋转+变色 // g // p u //c RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // 旋转+变色 // g // p u // c RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } else { // g // u p Node* uncle = grandfather->_left; // 情况一:叔叔存在且为红,变色即可 if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else // 叔叔不存在,或者存在且为黑 { // 情况二:叔叔不存在或者存在且为黑 // 单旋+变色 // g // u p // c if (cur == parent->_right) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { //双旋+变色 // g // u p // c RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK; return true; } void InOrder() { _InOrder(_root); cout << endl; } // blackNum 根到当前结点的黑色结点的数量 bool Check(Node* root, int blackNum, const int refNum) { if (root == nullptr) { // 前序遍历走到空时,意味着一条路径走完了 //cout << blackNum << endl; if (refNum != blackNum) { cout << "存在黑色结点的数量不相等的路径" << endl; return false; } return true; } // 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了 if (root->_col == RED && root->_parent->_col == RED) { cout << root->_kv.first << "存在连续的红色结点" << endl; return false; } if (root->_col == BLACK) { blackNum++; } return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum); } bool IsBalance() { if (_root == nullptr) return true; if (_root->_col == RED) return false; // 参考值 int refNum = 0; Node* cur = _root; while (cur) { if (cur->_col == BLACK) { ++refNum; } cur = cur->_left; } return Check(_root, 0, refNum); } int Height() { return _Height(_root); } int Size() { return _Size(_root); } Node* Find(const K& key) { Node* cur = _root; while (cur) { if (cur->_kv.first < key) { cur = cur->_right; } else if (cur->_kv.first > key) { cur = cur->_left; } else { return cur; } } return nullptr; } protected: int _Size(Node* root) { if (root == nullptr) return 0; return _Size(root->_left) + _Size(root->_right) + 1; } int _Height(Node* root) { if (root == nullptr) return 0; int leftHeight = _Height(root->_left); int rightHeight = _Height(root->_right); return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1; } 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 (ppNode == nullptr) 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* parentParent = parent->_parent; subR->_left = parent; parent->_parent = subR; if (parentParent == nullptr) { _root = subR; subR->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subR; } else { parentParent->_right = subR; } subR->_parent = parentParent; } } void _InOrder(Node* root) { if (root == nullptr) { return; } _InOrder(root->_left); cout << root->_kv.first << ":" << root->_kv.second << endl; _InOrder(root->_right); } private: Node* _root = nullptr; };
AVLTree.h
#pragma once template<class K, class V> struct AVLTreeNode { // 需要parent指针,后续更新平衡因子可以看到 pair<K, V> _kv; AVLTreeNode<K, V>* _left; AVLTreeNode<K, V>* _right; AVLTreeNode<K, V>* _parent; int _bf; // balance factor AVLTreeNode(const pair<K, V>& kv) :_kv(kv) , _left(nullptr) , _right(nullptr) , _parent(nullptr) , _bf(0) { } }; template<class K, class V> class AVLTree { typedef AVLTreeNode<K, V> Node; public: bool Insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); 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); if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; // 更新平衡因子 while (parent) { if (cur == parent->_right) { parent->_bf++; } else { parent->_bf--; } if (parent->_bf == 0) { break; } else if (parent->_bf == 1 || parent->_bf == -1) { cur = parent; parent = parent->_parent; } else if (parent->_bf == 2 || parent->_bf == -2) { // 旋转 if (parent->_bf == -2 && cur->_bf == -1) { RotateR(parent); } else if (parent->_bf == 2 && cur->_bf == 1) { RotateL(parent); } else if (parent->_bf == -2 && cur->_bf == 1) { RotateLR(parent); } else if (parent->_bf == 2 && cur->_bf == -1) { RotateRL(parent); } else { assert(false); } break; } else { assert(false); } } return true; } void InOrder() { _InOrder(_root); cout << endl; } bool IsBalanceTree() { return _IsBalanceTree(_root); } Node* Find(const K& key) { Node* cur = _root; while (cur) { if (cur->_kv.first < key) { cur = cur->_right; } else if (cur->_kv.first > key) { cur = cur->_left; } else { return cur; } } return nullptr; } int Height() { return _Height(_root); } int Size() { return _Size(_root); } private: int _Size(Node* root) { if (root == nullptr) return 0; return _Size(root->_left) + _Size(root->_right) + 1; } bool _IsBalanceTree(Node* root) { // 空树也是AVL树 if (nullptr == root) return true; // 计算pRoot结点的平衡因子:即pRoot左右子树的高度差 int leftHeight = _Height(root->_left); int rightHeight = _Height(root->_right); int diff = rightHeight - leftHeight; // 如果计算出的平衡因子与pRoot的平衡因子不相等,或者 // pRoot平衡因子的绝对值超过1,则一定不是AVL树 if (abs(diff) >= 2) { cout << root->_kv.first << "高度差异常" << endl; return false; } if (root->_bf != diff) { cout << root->_kv.first << "平衡因子异常" << endl; return false; } // pRoot的左和右如果都是AVL树,则该树一定是AVL树 return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right); } int _Height(Node* root) { if (root == nullptr) return 0; int leftHeight = _Height(root->_left); int rightHeight = _Height(root->_right); return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1; } void _InOrder(Node* root) { if (root == nullptr) { return; } _InOrder(root->_left); cout << root->_kv.first << ":" << root->_kv.second << endl; _InOrder(root->_right); } 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 (ppNode == nullptr) if (parent == _root) { _root = subL; subL->_parent = nullptr; } else { if (ppNode->_left == parent) { ppNode->_left = subL; } else { ppNode->_right = subL; } subL->_parent = ppNode; } parent->_bf = 0; subL->_bf = 0; } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) subRL->_parent = parent; Node* parentParent = parent->_parent; subR->_left = parent; parent->_parent = subR; if (parentParent == nullptr) { _root = subR; subR->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subR; } else { parentParent->_right = subR; } subR->_parent = parentParent; } parent->_bf = subR->_bf = 0; } void RotateLR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; int bf = subLR->_bf; RotateL(parent->_left); RotateR(parent); if (bf == -1) { subL->_bf = 0; parent->_bf = 1; subLR->_bf = 0; } else if (bf == 1) { subL->_bf = -1; parent->_bf = 0; subLR->_bf = 0; } else if (bf == 0) { subL->_bf = 0; parent->_bf = 0; subLR->_bf = 0; } else { assert(false); } } void RotateRL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; int bf = subRL->_bf; RotateR(parent->_right); RotateL(parent); if (bf == 0) { subR->_bf = 0; subRL->_bf = 0; parent->_bf = 0; } else if (bf == 1) { subR->_bf = 0; subRL->_bf = 0; parent->_bf = -1; } else if (bf == -1) { subR->_bf = 1; subRL->_bf = 0; parent->_bf = 0; } else { assert(false); } } private: Node* _root = nullptr; };
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<vector> #include<assert.h> using namespace std; #include"RBTree.h" #include"AVLTree.h" // 测试代码 void TestRBTree1() { RBTree<int, int> t; // 常规的测试用例 int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 }; // 特殊的带有双旋场景的测试用例 //int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; for (auto e : a) { /*if (e == 14) { int x = 0; }*/ t.Insert({ e, e }); cout << "Insert:" << e << "->"; cout << t.IsBalance() << endl; } t.InOrder(); cout << t.IsBalance() << endl; } // 插入一堆随机值,测试平衡,顺便测试一下高度和性能等 void TestRBTree2() { const int N = 10000000; vector<int> v; v.reserve(N); srand(time(0)); for (size_t i = 0; i < N; i++) { v.push_back(rand() + i); } size_t begin2 = clock(); RBTree<int, int> t; for (auto e : v) { t.Insert(make_pair(e, e)); } size_t end2 = clock(); cout << t.IsBalance() << endl; cout << "RBTree Insert:" << end2 - begin2 << endl; cout << "RBTree Height:" << t.Height() << endl; cout << "RBTree Size:" << t.Size() << endl; size_t begin1 = clock(); // 确定在的值 /*for (auto e : v) { t.Find(e); }*/ // 随机值 for (size_t i = 0; i < N; i++) { t.Find((rand() + i)); } size_t end1 = clock(); cout << "RBTree Find:" << end1 - begin1 << endl << endl; } void TestAVLTree2() { const int N = 10000000; vector<int> v; v.reserve(N); srand(time(0)); for (size_t i = 0; i < N; i++) { v.push_back(rand() + i); } size_t begin2 = clock(); AVLTree<int, int> t; for (auto e : v) { t.Insert(make_pair(e, e)); } size_t end2 = clock(); cout << t.IsBalanceTree() << endl; cout << "AVLTree Insert:" << end2 - begin2 << endl; cout << "AVLTree Height:" << t.Height() << endl; cout << "AVLTree Size:" << t.Size() << endl; size_t begin1 = clock(); // 确定在的值 /*for (auto e : v) { t.Find(e); }*/ // 随机值 for (size_t i = 0; i < N; i++) { t.Find((rand() + i)); } size_t end1 = clock(); cout << "AVLTree Find:" << end1 - begin1 << endl; } int main() { TestRBTree2(); TestAVLTree2(); return 0; }

这运行的结果真是,唉?!

完——


 室内系的TrackMaker_hanser

be like... ...

至此结束——

我是云边有个稻草人

期待与你的下一次相遇... ... ...

Read more

【linux】linux基础IO(六)软硬链接(软链接,硬链接)

【linux】linux基础IO(六)软硬链接(软链接,硬链接)

小编个人主页详情<—请点击 小编个人gitee代码仓库<—请点击 linux系列专栏<—请点击 倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己! 目录 * 前言 * 一、如何建立文件之间的软硬链接 * 建立文件之间的软链接 * 建立文件之间的硬链接 * 二、深入理解软硬链接 * 如何理解软链接 * 软链接的应用场景 * 如何理解硬链接 * 硬链接的应用场景 * 硬链接能否链接目录,为什么? * 总结 前言 【linux】linux基础IO(五)深入理解文件系统——书接上文 详情请点击<——,本文会在上文的基础上进行讲解,所以对上文不了解的读者友友请点击前方的蓝字链接进行学习 本文由小编为大家介绍——【linux】linux基础IO(六)软硬链接(软链接,硬链接) 一、如何建立文件之间的软硬链接 软硬链接其实是一个统称,将其进行分开叫做软链接,硬链接,下面小编就带领大家看一下如何在指令层面建立文件之间的软链接,

By Ne0inhk
Flutter 三方库 fsrs 突破鸿蒙端智能认知交互模型的高频动态复习算法引擎适配:搭建复杂离线记忆曲线追踪体系全息掌握大脑突触留存衰退参数助力超效在线学习-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 fsrs 突破鸿蒙端智能认知交互模型的高频动态复习算法引擎适配:搭建复杂离线记忆曲线追踪体系全息掌握大脑突触留存衰退参数助力超效在线学习-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 fsrs 突破鸿蒙端智能认知交互模型的高频动态复习算法引擎适配:搭建复杂离线记忆曲线追踪体系全息掌握大脑突触留存衰退参数助力超效在线学习 前言 在 OpenHarmony 智慧教育与个人效能类应用开发中,如何帮助用户高效记忆海量知识点(如单词、医学条目、法律条文)?如果仅仅采用简单的均匀复习,学习效率会由于大量重复已知内容而极其低下。fsrs(Free Spaced Repetition Scheduler)算法库为开发者提供了一套比传统的 Anki (SM-2) 更先进、基于 DSR 模型(Difficulty, Stability, Retrievability)的现代间隔重复调度算法。本文将实战介绍如何在鸿蒙端利用该算法构建一个顶级水平的学习大脑。 一、原直线性 / 概念介绍 1.1 基础原理/概念介绍 fsrs 的核心逻辑是基于 基于三个动态指标的三阶模型:难度 (Difficulty)、稳定性

By Ne0inhk
Flutter 组件 bip340 适配鸿蒙 HarmonyOS 实战:次世代 Schnorr 签名,为鸿蒙 Web3 与隐私计算筑牢加密防线

Flutter 组件 bip340 适配鸿蒙 HarmonyOS 实战:次世代 Schnorr 签名,为鸿蒙 Web3 与隐私计算筑牢加密防线

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 bip340 适配鸿蒙 HarmonyOS 实战:次世代 Schnorr 签名,为鸿蒙 Web3 与隐私计算筑牢加密防线 前言 在鸿蒙(OpenHarmony)生态迈向去中心化金融(DeFi)、隐私通讯及安全资产管理等高阶安全场景的背景下,如何实现更高性能、更具扩展性且抗攻击能力的数字签名架构,已成为决定应用闭环安全性的“压舱石”。在鸿蒙设备这类强调分布式鉴权与芯片级安全(TEE/SE)的移动终端上,如果依然沿用传统的 ECDSA 签名算法,由于由于其固有的可延展性风险与高昂的聚合验证成本,极易由于由于在大规模节点验证时的 CPU 负载过高导致交互滞后。 我们需要一种能够实现签名线性聚合、计算逻辑极简且具备原生抗延展性的密码学方案。 bip340 为 Flutter 开发者引入了比特币 Taproot 升级的核心——Schnorr 签名算法。它不仅在安全性上超越了传统标准,更通过其线性的数学特性,

By Ne0inhk
2025年最新最全Linux 系统安装Minio详细教程

2025年最新最全Linux 系统安装Minio详细教程

1.MinIO简介 MinIO 是一款高性能、分布式对象存储系统,专为云原生和容器化环境设计。它采用 Apache License 2.0 开源协议,兼容 Amazon S3 API,支持海量数据的存储与管理。 核心特点 高性能架构 MinIO 使用纠删码技术实现数据冗余,读写速度可达每秒数百 GB,适合高吞吐场景。 兼容 S3 协议 完全兼容 Amazon S3 API,现有基于 S3 的应用无需修改即可迁移到 MinIO。 轻量级部署 单二进制文件即可运行,最低配置仅需 512MB 内存,支持 Kubernetes 和 Docker 快速部署。 多云支持 提供混合云解决方案,能在公有云、私有云和边缘计算环境中无缝运行。 典型应用场景

By Ne0inhk