网站开发成本主要有哪些常用搜索引擎有哪些
目录
set和map区别
set和map的插入
set和map的实现
修改红黑树的模板参数
修改比较时使用的变量
迭代器的实现
迭代器的定义
*解引用重载
->成员访问重载
++自增重载
==重载
封装迭代器
RBTree迭代器封装
封装set迭代器
对set迭代器进行修改
封装map迭代器
修改对insert的返回值进行
map的[ ]解引用重载
代码汇总
set和map区别
set和map是两种容器,与vector,string,list...不同的是,set和map属于关联性容器;
关联性容器意味着数据的存放是有一些规则的,不是将数据随意存放的。
关联性容器规定了数据存放的规则,也就意味着其插入时需要对数据进行调整来满足规则要求,关联性容器严格的插入要求也促成了容器在某些特定功能上的效率增强。
set和map底层实现使用的是红黑树,这也使得set和map在对数据查找方面的效率极高。查找的时间复杂度可以达到O(logN)。100万的数据大约只需要查找30次。
set<int> s; //此时set存储一个数据,类型是int
map<int, string> m; //map存储两个数据,int和string :查找数据的时候使用int类型
set和map是同一种关联性容器,但是map可以存储两种数据。
set只存储了key(数据),进行查找的时候就只能用T类型进行查找;
map存储了key和T,虽然存储了两种数据,但是在进行查找的时候,map还是使用key,也就是第一种类型进行查找的。
map多储存的T使得其找查找到key后能够读取到其中存储的T数据。
关于set和map的使用场景区分:对于一次考试成绩要在大量数据中查找有没有60(或其他分数)分的,就可以将数据存储在set中,然后进行查找,key中存储的就是成绩;而如果要在找到60分成绩的同时能够输出对应成绩学生的姓名,此时使用map就更合适,此时key存储成绩,而T存储学生姓名。
set和map的插入
set的插入:因为set只存储一种数据,所以set插入与其他容器的插入并没区别,直接将数据插入即可。
set<int> s; //此时set存储一个数据,类型是int
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
但是map中存放的数据有两种,其插入要求插入pair类型的对象。
详细可见一下文档。
map - C++ Referencehttps://legacy.cplusplus.com/reference/map/map/
pair包含两个成员变量:一个是first存储Key,另一个second存储T;
所以构造pair对象是对map进行插入时不可避免的。
pair的构造有两种:1)通过函数make_pair()来直接创建pair对象
2)在C++11支持多参数的构造,所以也可以直接通过{ }来实现构造。
map<int, string> m; //map存储两个数据,int和string :查找数据的时候使用int类型
pair<int, string> pa = make_pair(1, "hello");m.insert(pa); //通过pair对象进行插入m.insert(make_pair(2, "world")); //通过临时变量直接进行插入。m.insert({ 0,"best" }); //通过多参数的构造,实现对pair对象的构造
关于set和map的使用就就介绍到这里,详细可以看下方文档。
map - C++ Referencehttps://legacy.cplusplus.com/reference/map/map/set - C++ Reference
https://legacy.cplusplus.com/reference/set/set/?kw=set下面将详细介绍set和map封装实现逻辑。
set和map的实现
set和map的底层是使用红黑树进行的实现,关于《红黑树》已经出过详细介绍了,此处将借助红黑树的底层逻辑来进一步封装出set和map容器。
红黑树剖析-CSDN博客文章浏览阅读476次,点赞28次,收藏13次。红黑树用于高效查找数据,及防止了普通搜索二叉树,也规避了AVL树的插入时多次旋转的代价,本文深度剖析了红黑树插入的逻辑,步骤以及插入后调整的方法,帮助读者能够理解和正确使用红黑树。https://blog.csdn.net/2401_87944878/article/details/146522638此处贴一下红黑树的实现代码,如对红黑树的实现有详细理解可直接跳过。
#pragma once
#include<iostream>
using namespace std;namespace cfl
{enum Col{RED,BLACK};template<class K, class V>struct RBTreeNode{//默认构造函数RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Col _col;};template<class K, class V>class RBTree{typedef RBTreeNode<K, V> Node;public://进行左旋void RotateL(Node* parent){//调整parent的父节点,右节点//调整cur的父节点,左节点//调整cur_left的父节点//调整parent的父节点的指向Node* pparent = parent->_parent;Node* cur = parent->_right;Node* cur_left = cur->_left;cur->_parent = pparent;if (parent == _root) //注意:如果parent是根,在旋转后要对根进行更新{_root = cur;}else{if (pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}}parent->_right = cur_left;parent->_parent = cur;if (cur_left)cur_left->_parent = parent;cur->_left = parent;}//进行右旋void RotateR(Node* parent){//调整parent的父节点,左节点//调整cur的父节点,右节点//调整cur_right的父节点//调整parent的父节点的指向Node* pparent = parent->_parent;Node* cur = parent->_left;Node* cur_right = cur->_right;cur->_parent = pparent;if (parent == _root){_root = cur;}else{if (pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}}parent->_left = cur_right;parent->_parent = cur;cur->_right = parent;if (cur_right)cur_right->_parent = parent;}bool Insert(const pair<K, V> kv){Node* newnode = new Node(kv);if (_root == nullptr){_root = newnode; //根节点为空,直接进行赋值_root->_col = BLACK;return true;}//根节点不为空,找节点插入位置Node* pcur = _root;Node* parent = nullptr;while (pcur){parent = pcur;if (pcur->_kv.first > kv.first){pcur = pcur->_left;}else if (pcur->_kv.first < kv.first){pcur = pcur->_right;}else{return false; //相等不需要插入}}//找到节点的位置//进行插入if (parent->_kv.first > kv.first){parent->_left = newnode;}else{parent->_right = newnode;}newnode->_parent = parent;//检查节点是否满足要求//....//父节点是黑色,满足条件if (parent->_col == BLACK){_root->_col = BLACK;return true;}else //父节点是红色,此时出现连续的红色,需要进行调整{pcur = newnode;while (parent && parent->_col == RED){Node* grandparent = parent->_parent;if (parent == grandparent->_left) //分类确定uncle节点{Node* uncle = grandparent->_right;if (uncle && uncle->_col == RED){//对节点进行变色uncle->_col = parent->_col = BLACK;grandparent->_col = RED;pcur = grandparent; //继续向上调整parent = pcur->_parent;}else //uncle节点是空或uncle节点是黑色{//判断旋转方式//上述if条件中已经确定了parent==grandparent->_leftif (pcur == parent->_left){//以grandparent为中心,进行右旋RotateR(grandparent);//进行变色parent->_col = BLACK;grandparent->_col = RED;_root->_col = BLACK;//此处parent是当前子树的根且是黑色,不用继续向上调整了return true;}else //cur==parent->_right{//需要进行双旋RotateL(parent);RotateR(grandparent);//调色pcur->_col = BLACK;grandparent->_col = parent->_col = RED;_root->_col = BLACK;return true;}}}else //parent=grandparent->rigth{Node* uncle = grandparent->_left;if (uncle && uncle->_col == RED){//对节点进行变色uncle->_col = parent->_col = BLACK;grandparent->_col = RED;pcur = grandparent; //继续向上调整parent = pcur->_parent;}else //uncle节点是空或uncle节点是黑色{//判断旋转方式//上述if条件中已经确定了parent==grandparent->_rightif (pcur == parent->_right){//以grandparent为中心,进行右旋RotateL(grandparent);//进行变色parent->_col = BLACK;grandparent->_col = RED;//此处parent是当前子树的根且是黑色,不用继续向上调整了_root->_col = BLACK;return true;}else //cur==parent->_left{//需要进行双旋RotateR(parent);RotateL(grandparent);//调色pcur->_col = BLACK;grandparent->_col = parent->_col = RED;_root->_col = BLACK;return true;}}}}}_root->_col = BLACK;return true;}bool Isbance(){int num = 0; //记录根到叶子节点有多少个黑节点Node* pcur = _root;while (pcur){if (pcur->_col == BLACK){++num;}pcur = pcur->_left;}return Isbance(_root, num, 0); //num是每条支路黑节点个数的参照}private:bool Isbance(Node* root, int num, int each) //each记录当前之路黑节点个数{if (root == nullptr){if (each == num) //每条路的黑色节点数相同return true;cout << "黑色节点个数不对" << endl;return false;}if (root->_col == BLACK){each++;}else{if (root->_parent->_col == RED) //看父节点是不是红色{return false;cout << root->_kv.first << " 红节点连续" << endl;}}return Isbance(root->_left, num, each) && Isbance(root->_right, num, each);}Node* _root = nullptr;};
}
修改红黑树的模板参数
红黑树的模板参数就现在看来需要两个:1)确定查找数据的类型;2)用于存储数据的类型。
注意:关于红黑树的模板参数并不止这两种,在后面还需要添加。
对于set来说:查找数据的类型和存储数据的类型是一样的,都是T。
而对于map来说:查找数据的类型是key,而存储数据的类型是pair。
template<class T>
class set
{typedef RBTree<T, T> Tree; //查找类型和存储类型相同private:Tree _tree;
};
template<class K,class V>
class map
{typedef RBTree<K, pair<K,V>> Tree; //查找类型和存储类型相同private:Tree _tree;
};
template<class K, class T>
class RBTree
{//......
}
在上面贴出的红黑树实现代码中,使用的均是pair进行数据存储,此时需要将pair均改为T来实现泛型编程的效果。
template<class K, class T>
struct RBTreeNode
{//默认构造函数RBTreeNode(const T& kv):_date(date), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}T _date;RBTreeNode<K, T>* _left;RBTreeNode<K, T>* _right;RBTreeNode<K, T>* _parent;Col _col;
};
关于RBTree中代码的修改在此处不再贴出,将会放在结尾总结处统一贴出。
修改比较时使用的变量
经过上面对红黑树模板参数的修改后,T中可能存储的是自定义类型pair也肯能是内置类型int...;
在插入函数Insert中,要对数据进行比较:内置类型可以直接进行比较,但是对于pair类型其比较方式并不是我们希望的方式。
可以看到pair的比较方式是:先比较first变量,再比较second变量。但是我们理想的比较方式是只对first中存储的Key类型进行比较,所以此处不能直接使用pair的比较方法。
如果能够拿到pair的first中存储的键值Key就好了,直接将Key进行比较就行了。为了实现拿到键值Key此处采用内部类的方式实现。
在set和map中分别实现内部类,来获取其比较是用的数据;对于set来说返回的就是T,而对于map来说返回的则是pair中存储的first。在RBTree添加一个模板参数用来保存内部类即可。
struct SetofKey{//重载()运算符const T& operator()(const T& date){return date;}};
public:typedef RBTree<T, T,SetofKey> Tree; //查找类型和存储类型相同
struct MapofKey{//重载()运算符const K& operator()(const pair<K, V>& kv){return kv.first;}};
public:typedef RBTree<K, pair<K,V>,MapofKey> Tree; //查找类型和存储类型相同
template<class K, class T,class TofKey>
class RBTree
{typedef RBTreeNode<K, T> Node;
public:// ......
}
当需要进行数据比较的时候,先用内部类取出需要比较的数据即可。
比如一下修改(以下仅是部分修改代码)。
TofKey getkey;
//根节点不为空,找节点插入位置
Node* pcur = _root;
Node* parent = nullptr;
while (pcur)
{parent = pcur;if (getkey(pcur->_date) > getkey(date)){pcur = pcur->_left;}else if (getkey(pcur->_date) < getkey(date)){pcur = pcur->_right;}else{return false; //相等不需要插入}
}
迭代器的实现
set和map的底层是红黑树,所以其迭代器自然就是指针,通过结构体对指针进行封装重载++,==以及*解引用等。
迭代器的定义
//迭代器的实现
template<class T,class Ref ,class Ptr>
struct TreeIterator
{typedef RBTreeNode<T> Node;typedef TreeIterator<T, Ref, Ptr> Self;typedef //构造函数TreeIterator(Node* node):_node(node){ }Node* _node;
};
其中的Ref和Ptr用来区分非const和const迭代器。
*解引用重载
//解引用重载
Self operator*()
{return _node->_date;
}
->成员访问重载
//重载->访问
Ptr operator->()
{return &(_node->_date);
}
++自增重载
对于指针的++,红黑树的遍历顺序是中序遍历;
所以在重载++的时候,下一个指针位置分为两种情况:1)当前节点的右节点不为空,找右子树的最小节点(即最左侧节点);2)当前节点右节点为空时,向上找,直到找到一个节点是其父节点的左节点时,其父节点就是下一个节点。
//重载++
Self operator++()
{if (_node->_right){//找右树的最小节点Node* pcur = _node->_right;while (pcur->_left){pcur = pcur->_left;}_node = pcur;return *this;}else{//向上找Node* parent = _node->_parent;Node* pcur = _node;while (parent){if (pcur == parent->_left){_node = parent;return *this;}else{pcur = parent;parent = pcur->_parent;}}_node = nullptr;return *this;}
}
==重载
//==重载
bool operator==(Self& it)
{return _node == it._node;
}bool operator!=(Self& it)
{return _node != it._node;
}
封装迭代器
RBTree迭代器封装
template<class K, class T,class TofKey>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef TreeIterator<T, T&, T*> iterator;typedef TreeIterator<T, const T&, const T*> const_iterator;//beginiterator begin(){Node* pcur = _root;//找最小节点,即最左侧while (pcur && pcur->_left){pcur = pcur->_left;}return pcur; //隐式类型转化,通过pcur指针进行iterator的构造}//enditerator end(){return nullptr;}
}
const版本
//begin
const_iterator begin()const
{Node* pcur = _root;//找最小节点,即最左侧while (pcur && pcur->_left){pcur = pcur->_left;}return pcur; //隐式类型转化,通过pcur指针进行iterator的构造
}//end
const_iterator end()const
{return nullptr;
}
封装set迭代器
对于插入到红黑树中的数据来说,用于比较的数据是不能被修改的,所以对set来说其存储的key值不能进行修改,所以set的普通迭代器的实质还是const迭代器。
template<class T>
class set
{public:typedef RBTree<T, T,SetofKey> Tree; //查找类型和存储类型相同typedef typename Tree::const_iterator iterator;typedef typename Tree::const_iterator cosnt_iterator;//set的beginiterator begin()const{return _tree.begin();}//set的enditerator end()const{return _tree.end();}
}
迭代器实现后却出现了报错,说无法进行转化。 其原因是_tree.begin()返回的是普通迭代器,而set的普通迭代器实际上还是const迭代器,普通迭代器和const迭代器虽然是来自同一个模板,但是其是完全不同的两个类,所以此处需要用返回的普通迭代器中的数据来构造一个const迭代器。
对set迭代器进行修改
template<class T>
class set
{struct SetofKey{//重载()运算符const T& operator()(const T& date){return date;}};
public:typedef RBTree<T, T,SetofKey> Tree; //查找类型和存储类型相同typedef typename Tree::iterator Iterator; //用来表示普通迭代器typedef typename Tree::const_iterator iterator;typedef typename Tree::const_iterator cosnt_iterator;//set的beginiterator begin(){Iterator it = _tree.begin(); //接收返回的普通迭代器return iterator(it._node);}//set的enditerator end(){Iterator it=_tree.end(); return iterator(it._node);}
}
封装map迭代器
map的K也不能进行修改,但是T可以进行修改,所以map普通迭代器不变,关于如何保证map的K不被修改在后面进行分析。
template<class K,class V>
class map
{
public:typedef RBTree<K, pair<K,V>,MapofKey> Tree; //查找类型和存储类型相同typedef typename Tree::iterator iterator;typedef typename Tree::const_iterator const_iterator;iterator begin(){return _tree.begin();}iterator end(){return _tree.end();}
}
以上就是map的iterator的封装了,但是还需要实现对Key值的不能修改,此处的实现就比较简单了,可以直接在pair的模板参数中使用const Key即可。
typedef RBTree<K, pair<const K,V>,MapofKey> Tree; //查找类型和存储类型相同
修改对insert的返回值进行
可以看到在库中insert的返回值是pair对象,所以此处对原红黑树insert要进行修改,用newnode来创建iterator即可。
将return true修改为return make_pair(newnode,true)即可
与set迭代器封装一样,set的insert也需要先存储返回值,在进行转化。
map的[ ]解引用重载
map的解引用重载的实现,底层还是insert,对没有的数据进行插入,对已经存在的数据返回T。
V& operator[](const K& key)
{pair<iterator, bool> ret = _tree.Insert({ key, V() });return ret.first._node->_date.second;
}
代码汇总
<map>
template<class K,class V>
class map
{struct MapofKey{//重载()运算符const K& operator()(const pair<K, V>& kv){return kv.first;}};
public:typedef RBTree<K, pair<const K,V>,MapofKey> Tree; //查找类型和存储类型相同typedef typename Tree::iterator iterator;typedef typename Tree::const_iterator const_iterator;iterator begin(){return _tree.begin();}iterator end(){return _tree.end();}pair<iterator,bool> insert(const pair<K,V>& date){pair<iterator, bool> ret = _tree.Insert(date);return make_pair(ret.first._node, ret.second);}V& operator[](const K& key){pair<iterator, bool> ret = _tree.Insert({ key, V() });return ret.first._node->_date.second;}private:Tree _tree;
};
<set>
template<class T>
class set
{struct SetofKey{//重载()运算符const T& operator()(const T& date){return date;}};
public:typedef RBTree<T, T,SetofKey> Tree; //查找类型和存储类型相同typedef typename Tree::iterator Iterator;typedef typename Tree::const_iterator iterator;typedef typename Tree::const_iterator cosnt_iterator;//set的beginiterator begin(){Iterator it = _tree.begin();return iterator(it._node);}//set的enditerator end(){Iterator it=_tree.end();return iterator(it._node);}pair<iterator, bool> insert(const T& date){pair<Iterator, bool> ret = _tree.Insert(date);return make_pair(ret.first._node, ret.second);}private:Tree _tree;
};
<RBTree>
enum Col
{RED,BLACK
};template<class T>
struct RBTreeNode
{//默认构造函数RBTreeNode(const T& date):_date(date), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}T _date;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Col _col;
};//迭代器的实现
template<class T,class Ref ,class Ptr>
struct TreeIterator
{typedef RBTreeNode<T> Node;typedef TreeIterator<T, Ref, Ptr> Self;//构造函数TreeIterator(Node* node):_node(node){ }//解引用重载Ref operator*(){return _node->_date;}//重载->访问Ptr operator->(){return &(_node->_date);}//重载++Self operator++(){if (_node->_right){//找右树的最小节点Node* pcur = _node->_right;while (pcur->_left){pcur = pcur->_left;}_node = pcur;return *this;}else{//向上找Node* parent = _node->_parent;Node* pcur = _node;while (parent){if (pcur == parent->_left){_node = parent;return *this;}else{pcur = parent;parent = pcur->_parent;}}_node = nullptr;return *this;}}//==重载bool operator==(Self& it){return _node == it._node;}bool operator!=(Self& it){return _node != it._node;}Node* _node;
};template<class K, class T,class TofKey>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef TreeIterator<T, T&, T*> iterator;typedef TreeIterator<T, const T&, const T*> const_iterator;//beginiterator begin(){Node* pcur = _root;//找最小节点,即最左侧while (pcur && pcur->_left){pcur = pcur->_left;}return pcur; //隐式类型转化,通过pcur指针进行iterator的构造}//enditerator end(){return nullptr;}//beginconst_iterator begin()const{Node* pcur = _root;//找最小节点,即最左侧while (pcur && pcur->_left){pcur = pcur->_left;}return pcur; //隐式类型转化,通过pcur指针进行iterator的构造}//endconst_iterator end()const{return nullptr;}//进行左旋void RotateL(Node* parent){//调整parent的父节点,右节点//调整cur的父节点,左节点//调整cur_left的父节点//调整parent的父节点的指向Node* pparent = parent->_parent;Node* cur = parent->_right;Node* cur_left = cur->_left;cur->_parent = pparent;if (parent == _root) //注意:如果parent是根,在旋转后要对根进行更新{_root = cur;}else{if (pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}}parent->_right = cur_left;parent->_parent = cur;if (cur_left)cur_left->_parent = parent;cur->_left = parent;}//进行右旋void RotateR(Node* parent){//调整parent的父节点,左节点//调整cur的父节点,右节点//调整cur_right的父节点//调整parent的父节点的指向Node* pparent = parent->_parent;Node* cur = parent->_left;Node* cur_right = cur->_right;cur->_parent = pparent;if (parent == _root){_root = cur;}else{if (pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}}parent->_left = cur_right;parent->_parent = cur;cur->_right = parent;if (cur_right)cur_right->_parent = parent;}pair<iterator,bool> Insert(const T date){Node* newnode = new Node(date);if (_root == nullptr){_root = newnode; //根节点为空,直接进行赋值_root->_col = BLACK;return make_pair(newnode, true);}TofKey getkey;//根节点不为空,找节点插入位置Node* pcur = _root;Node* parent = nullptr;while (pcur){parent = pcur;if (getkey(pcur->_date) > getkey(date)){pcur = pcur->_left;}else if (getkey(pcur->_date) < getkey(date)){pcur = pcur->_right;}else{return make_pair(nullptr,false); //相等不需要插入}}//找到节点的位置//进行插入if (getkey(parent->_date) > getkey(date)){parent->_left = newnode;}else{parent->_right = newnode;}newnode->_parent = parent;//检查节点是否满足要求//....//父节点是黑色,满足条件if (parent->_col == BLACK){_root->_col = BLACK;return make_pair(newnode, true);}else //父节点是红色,此时出现连续的红色,需要进行调整{pcur = newnode;while (parent && parent->_col == RED){Node* grandparent = parent->_parent;if (parent == grandparent->_left) //分类确定uncle节点{Node* uncle = grandparent->_right;if (uncle && uncle->_col == RED){//对节点进行变色uncle->_col = parent->_col = BLACK;grandparent->_col = RED;pcur = grandparent; //继续向上调整parent = pcur->_parent;}else //uncle节点是空或uncle节点是黑色{//判断旋转方式//上述if条件中已经确定了parent==grandparent->_leftif (pcur == parent->_left){//以grandparent为中心,进行右旋RotateR(grandparent);//进行变色parent->_col = BLACK;grandparent->_col = RED;_root->_col = BLACK;//此处parent是当前子树的根且是黑色,不用继续向上调整了return make_pair(newnode, true);}else //cur==parent->_right{//需要进行双旋RotateL(parent);RotateR(grandparent);//调色pcur->_col = BLACK;grandparent->_col = parent->_col = RED;_root->_col = BLACK;return make_pair(newnode, true);}}}else //parent=grandparent->rigth{Node* uncle = grandparent->_left;if (uncle && uncle->_col == RED){//对节点进行变色uncle->_col = parent->_col = BLACK;grandparent->_col = RED;pcur = grandparent; //继续向上调整parent = pcur->_parent;}else //uncle节点是空或uncle节点是黑色{//判断旋转方式//上述if条件中已经确定了parent==grandparent->_rightif (pcur == parent->_right){//以grandparent为中心,进行右旋RotateL(grandparent);//进行变色parent->_col = BLACK;grandparent->_col = RED;//此处parent是当前子树的根且是黑色,不用继续向上调整了_root->_col = BLACK;return make_pair(newnode, true);}else //cur==parent->_left{//需要进行双旋RotateR(parent);RotateL(grandparent);//调色pcur->_col = BLACK;grandparent->_col = parent->_col = RED;_root->_col = BLACK;return make_pair(newnode, true);}}}}}_root->_col = BLACK;return make_pair(newnode, true);}bool Isbance(){int num = 0; //记录根到叶子节点有多少个黑节点Node* pcur = _root;while (pcur){if (pcur->_col == BLACK){++num;}pcur = pcur->_left;}return Isbance(_root, num, 0); //num是每条支路黑节点个数的参照}private:bool Isbance(Node* root, int num, int each) //each记录当前之路黑节点个数{if (root == nullptr){if (each == num) //每条路的黑色节点数相同return true;cout << "黑色节点个数不对" << endl;return false;}if (root->_col == BLACK){each++;}else{if (root->_parent->_col == RED) //看父节点是不是红色{return false;cout << root->_date << " 红节点连续" << endl;}}return Isbance(root->_left, num, each) && Isbance(root->_right, num, each);}Node* _root = nullptr;
};