封装红黑树实现map和set

本博客需要红黑树和搜索树二叉树的一些知识以及熟悉map和set的相关函数和迭代器,如果读者还不熟悉可以看这三篇博客:红黑树、二叉搜索树、map、set的使用

红黑树的封装

STL30源码分析

如果想到封装,大家应该会直接把RBtree复制两份,一份用K结构封装set,一份用K-V结构封装map。这样当然可以,但是两份高度相似的代码是不会体现出程序员的一流水平的。

我们来看看大佬是如何写map和set的实现的。因为大佬的代码十分严谨,因此会有许多的typedef,希望大家能耐心分析。读高手的代码也是一个程序员进步的重要方法。

#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>
// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>
// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
// typedefs:
typedef Key key_type;
typedef Key value_type;
private:
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing set
};
// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
// typedefs:
typedef Key key_type;
typedef T mapped_type;
typedef pair<const Key, T> value_type;
private:
typedef rb_tree<key_type, value_type,
select1st<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing map
};
// stl_tree.h
struct __rb_tree_node_base
{
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr;
color_type color;
base_ptr parent;
base_ptr left;
base_ptr right;
};
// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc
= alloc>
class rb_tree {
protected:
typedef void* void_pointer;
typedef __rb_tree_node_base* base_ptr;
typedef __rb_tree_node<Value> rb_tree_node;
typedef rb_tree_node* link_type;
typedef Key key_type;
typedef Value value_type;
public:
// insert用的是第二个模板参数左形参
pair<iterator,bool> insert_unique(const value_type& x);
// erase和find用第一个模板参数做形参
size_type erase(const key_type& x);
iterator find(const key_type& x);
protected:
size_type node_count; // keeps track of size of tree
link_type header;
};
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field;
};

或许也有很多人看不懂,那么我就给大家梳理一下。

首先我们从最底层的开始分析,我们看到rbtree_node的基类(一般base就是基类,后面会进行继承)是没有节点值类型的。那么多半在子类进行了节点值类型的声明:

果然如此,这里用了Value来表示节点值类型。那么这个Value是传的什么呢?

我们可以发现,传的是RBtree里面的第二个模板参数,因此我们发现这和传统的传K或者传K-V是不一样的。而这里的Value变成了一个统一的值,在set里面指代的是K,在map里面指代的是{K,V}整体。是否是这样的呢?我们看一下map和set

的确如此,stl把变化的k/k-v看成了一个整体,然后将这个整体进行插入。因此这里的Value要么是K,要么是pair<K,V>。

那这个RBtree里面的Key又是干嘛的呢?不应该只需要一个Value就完事了吗?这个我留在下面讲解,否则太多了大家一下理解不过来。下面的一小段暂时用不到Key

Value的比较问题🌟

但是处理前我们还得考虑一个问题,我们的Value要么是K,在进行查找、插入、删除的时候可以直接比较。但是如果是pair<K,V>结构怎么办呢?要如何进行比较呢。

pair能否可以直接进行比较呢?

pair有默认的比较方法:

template <class T1, class T2>
bool operator< (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs)
{ return lhs.first<rhs.first || (!(rhs.first<lhs.first) &&
lhs.second<rhs.second); }

大家第一眼看不懂,我给大家优化一下

return lhs.first<rhs.first || ((rhs.first==lhs.first) &&
lhs.second<rhs.second);

至于为什么pair的比较不高我优化的这种,是因为官方做了说明,如果传的是自定义类,只需要实现operator<重载就行了。那么原因就在这里,stl的pair比较符重载就只用了<符号就完成了,那么K和V的比较符重载也只需要operator<。

这不是主要问题,主要问题是官方默认的比较方案不是我们想要的。我们只要比较K,K相等了就会应该直接做出结论了,而不是继续比较V。

仿函数解决

那么解决方案就是用仿函数来比较,我们只要增加一个模板参量传仿函数类,然后就可以比较了。

//map
struct mapKofT {const K& operator()(const std::pair<K, V>& x) {return x.first;}
};
//set
struct setKofT {const K& operator()(const K& x) {return x;}
};

红黑树类(RBtree)的特殊处理

那么我们就来学着stl特殊处理一下我们的RBtree类

对RBtreenode的调整

我们首先改一下RBtreenode类,我们就不学stl那么严谨还搞个继承了。

enum color {RED,BLACK
};	template<class T>struct RBtreenode {RBtreenode(const T& val, RBtreenode* parent = nullptr
, RBtreenode* left = nullptr, RBtreenode* right = nullptr, color color = RED):_val(val), _parent(parent), _left(left), _right(right), _color(color){}RBtreenode* _left;RBtreenode* _right;RBtreenode* _parent;T _val;color _color;};

RBtree的调整

template <class K,class T,class KofT>
class RBtree {typedef RBtreenode<T> node;
public:RBtree() = default;RBtree(const std::initializer_list<T>& x) {for (auto& num : x)insert(num);}node* find(const T& val) {node* cur = _root;while (cur) {if (_kot(cur->_val) < _kot(val)) cur = cur->_right;else if (_kot(cur->_val) > _kot(val))cur = cur->_left;else break;}return cur;}bool insert(const T& val) {if (_root == nullptr) {_root = new node(val);_root->_color = BLACK;return true;}node* cur = _root, * parent = nullptr;while (cur) {if (_kot(cur->_val) < _kot(val)) {parent = cur;cur = cur->_right;}else if (_kot(cur->_val) > _kot(val)) {parent = cur;cur = cur->_left;}else return false;}if (_kot(parent->_val) < _kot(val))cur = parent->_right = new node(val, parent);else cur = parent->_left = new node(val, parent);while (parent && parent->_color == RED) {//意思是到根节点或者父亲不为红色结束循环node* grandparent = parent->_parent;if (grandparent->_left == parent) {//约束条件,方便确定旋转方向node* uncle = grandparent->_right;if (uncle && uncle->_color == RED) {//只用变色的情况grandparent->_color = RED;uncle->_color = parent->_color = BLACK;cur = grandparent;parent = cur->_parent;}else {//对立的uncle为空或者uncle为黑色刚好是要旋转的if (parent->_left == cur) rotateR(grandparent);else rotateLR(grandparent);break;}}else {node* uncle = grandparent->_left;if (uncle && uncle->_color == RED) {grandparent->_color = RED;uncle->_color = parent->_color = BLACK;cur = grandparent;parent = cur->_parent;}else {if (parent->_right == cur)rotateL(grandparent);else rotateRL(grandparent);break;}}}_root->_color = BLACK;//这个必须要有,不然会报错}
private:void rotateL(node* cur) {node* subR = cur->_right;node* subRL = subR->_left;node* curparent = cur->_parent;cur->_right = subRL;if (subRL)subRL->_parent = cur;subR->_parent = curparent;subR->_left = cur;cur->_parent = subR;if (curparent == nullptr) {_root = subR;}else {if (curparent->_left == cur)curparent->_left = subR;else curparent->_right = subR;}subR->_color = BLACK;cur->_color = RED;}void rotateR(node* cur) {node* subL = cur->_left;node* subLR = subL->_right;node* curparent = cur->_parent;cur->_left = subLR;if (subLR)subLR->_parent = cur;cur->_parent = subL;subL->_parent = curparent;subL->_right = cur;if (curparent == nullptr) {_root = subL;}else {if (curparent->_left == cur)curparent->_left = subL;else curparent->_right = subL;}subL->_color = BLACK;cur->_color = RED;}void rotateLR(node* cur) {node* subL = cur->_left;rotateL(subL);rotateR(cur);}void rotateRL(node* cur) {node* subR = cur->_right;rotateR(subR);rotateL(cur);}node* _root = nullptr;KofT _kot;
};

这里我们主要看insert和find函数里面加的仿函数来区分T的类型,保证始终取K来比较

实现基础的map和set类

template<class K, class V>
class map {
public:map() {}map(const std::initializer_list<std::pair<K, V>>& list):_tree(list){}bool insert(const std::pair<K, V>& val) {return _tree.insert(val);}private:struct mapKofT {const K& operator()(const std::pair<K, V>& x) {return x.first;}};RBtree<K, std::pair<K, V>, mapKofT> _tree;
};template<class K>
class set {
public:set(){}set(const std::initializer_list<K>& list):_tree(list){}bool insert(const K& val) {return _tree.insert(val);}
private:struct setKofT {const K& operator()(const K& x) {return x;}};RBtree<K, K, setKofT>_tree;
};

看看能不能完成基础的插入操作:

#include<iostream>
#include<vector>
#include"map-set.h"
#include<map>using namespace std;
int main() {dgj::map<int, int>tree{ {1,2},{5,6} };for (int x = 0; x < 10; ++x) {tree.insert({ x,x });}	dgj::set<int>tree_set{ 1,2,3 };for (int x = 0; x < 10; ++x) {tree_set.insert(x);}return 0;
}
//运行上方代码是没问题的,注意我的map和set类用的dgj命名空间,如果读者要CV记得注意

迭代器的实现🌟

可以看到我们的insert函数实现的是不标准的,stl里面的insert返回的是pair<iterator,bool>,但是我们还没实现迭代器,因此就只能返回bool。下面我们实现一下。

map和set的迭代器底层介绍

考虑到所有人,所以我还是说一下map/set迭代器底层的是依靠是什么走的。

首先红黑树的底层是搜索二叉树,而搜索二叉树的中序遍历是有序的。map、set的迭代器就是走的红黑树的中序。而红黑树的中序该如何走呢?树的结构是不规则的,不能像链表、数组那样。

这里就要运用一下搜索二叉树的性质,左子树<根<右子树。

走中序遍历:

  1. 如果一个节点有右子树,那么它的中序下一个值就是右子树的最左边的那个值。
  2. 如果一个节点没有右子树,那么它就是这个树的最后一个节点,因此要不断往上找父亲,直到这个父亲的左子树指向它的树,那么这个父亲节点就是下一个中序遍历的值。

通过这两个点,就可以完成中序遍历的操作。也是迭代器的++操作。大家可以看上面的图来走一遍。对于迭代器--,那么就是反过来而已,没有区别,这里我就不多说了。

end()的规定

stl的实现方法:

stl是在根节点上面连一个哨兵位节点header,为红色,用它作end()。让header的左边连树的最左节点,header的右边连树最右节点。header的parent和根的parent互相连接。

那么在这棵树里面parent的parent是自己的节点只有header和根,我们只要再判断颜色就知道当前节点是否是哨兵位节点了。

因此在stl里面迭代器的--就有一个判断:

if (node->color == __rb_tree_red &&
node->parent->parent == node)
node = node->right;

目的就是判断此时是不是end,如果是那么--应该去最右侧节点。

但是它的这种也有缺点,我们的根节点、最左节点和最右节点是会变的。我们需要及时更新新的根节点、最左节点和最右节点与哨兵位的连接。

另一种实现方法

用根的parent即空指针作end()

我们还可以不加这个哨兵位,保持原封不动。那么最右侧节点++就会一直往上到空节点,我们只要提前判断就可以停在空节点位置。优势是并不要维持最左最右和根节点的更新。缺点就是我们如果要实现end()需要一定判断,如果是空,我们要把迭代器放到最右侧节点的位置。因此需要走一个循环找一下最右侧节点。但是我觉得这种处理方式是比stl的简单一些的。

因此我用这种方式来规定end()。

迭代器的实现

这里我不多嘴了,直接上代码

template<class T,class ref,class ptr>
class RBtree_iterator {typedef RBtreenode<T> node;typedef RBtree_iterator<T,ref,ptr> self;
public:RBtree_iterator() = default;RBtree_iterator(node*const cur,node*const root) :_cur(cur),_root(root){}self& operator++() {if (_cur->_right) {node* rcur = _cur->_right;while (rcur->_left)rcur = rcur->_left;_cur = rcur;}else {node* parent = _cur->_parent;node* dest = _cur;while (parent && parent->_right == dest) {dest = parent;parent = parent->_parent;}_cur = parent;}return *this;}self& operator--() {if (_cur == nullptr) {//处理end()--问题node* ret = _root;while (ret->_right)ret = ret->_right;return ret;}if (_cur->_left) {node* lcur = _cur->_left;while (lcur->_right)lcur = lcur->_right;_cur = lcur;}else {node* parent = _cur->_parent;node* dest = _cur;while (parent && parent->_left == dest) {dest == parent;parent = parent->_parent;}_cur = parent;}return *this;}self operator--(int) {self ret = --(*this);return ret;}self operator++(int) {self ret = ++(*this);return ret;}ref operator*() {return _cur->_val;}ptr operator->() {return &operator*();}bool operator!=(const self& x)const{return _cur != x._cur;}bool operator==(const self& x)const {return x._cur == _cur;}
private:node* _cur=nullptr;node* _root;//end()--需要_root
};

这里多一个ref和ptr的类模板参数是为了可同时实现const_iterator和iterator迭代器,而两者的不同就只是返回的引用或者指针对应的内容可不可修改。

然后我们在RBtree里面简单引入一下:

typedef RBtree_iterator<T, T&, T*> Iterator;
typedef RBtree_iterator<T,const T&,const T*> const_Iterator;
Iterator begin() {node* ret = _root;while (ret->_left)ret = ret->_left;return { ret,_root };
}
const_Iterator rbegin() {node* ret = _root;while (ret->_left)ret = ret->_left;return { ret,_root };
}
Iterator end() {return Iterator(nullptr, _root);
}
const_Iterator rend() {return const_Iterator(nullptr, _root);
}

同时在map里面使用

	template<class K, class V>class map {struct mapKofT {const K& operator()(const std::pair<K, V>& x) {return x.first;}};public:
#if 1typedef std::pair<const K, V> T;typedef RBtree_iterator<T, T&, T*> iterator;typedef RBtree_iterator<T, const T&, const T*> const_iterator;
#else typedef typename RBtree<K, 
std::pair<const K, V>, mapKofT>::Iterator iterator;typedef typename RBtree<K, 
std::pair<const K,V>, mapKofT>::const_Iterator const_iterator;
//这里要加const保证K不能被修改,下面的tree也要加,不然实例化出来因为类型不同报错
#endifiterator begin() {return _tree.begin();}const_iterator rbegin() {return _tree.rbegin();}iterator end() {return _tree.end();}const_iterator rend() {return _tree.rend();}public:map() {}map(const std::initializer_list<std::pair<K, V>>& list):_tree(list){}bool insert(const std::pair<K, V>& val) {return _tree.insert(val);}private:RBtree<K, std::pair<const K, V>, mapKofT> _tree;//这里要加const,保证K不能被修改};

对于map里面定义迭代器可以typedefRBtree里面的Iterator,但是要加typename告诉编译器你不是给的一个变量而是一个类型。也可以直接用RBtree_iterator类来定义iterator,因为最后实例化的是RBtree_iterator,而决定RBtree_iterator就只和T有关,因此只要保证有第一种和用第二种的T类型和map成员变量_tree的T类型一致都是可以的。

我们简单来使用一下范围for看是否可以:

是没问题的。

函数的实现

operator[]是map里面很重要的一个接口,可以实现增查修改等操作。

而实现它需要先把insert函数的标准形式实现出来。有了迭代器的实现这些就很好实现了。

Key的作用

我们进行查找的时候,只要用到K的值就行,如果只有T,那么最后传值是不清楚的。因此是需要加一个Key的变量。

iterator find(const K& val) {node* cur = _root;while (cur) {if (_kot(cur->_val) <val) cur = cur->_right;else if (_kot(cur->_val) > val)cur = cur->_left;else break;}return { cur,_root };
}

  insert的实现

insert只要把返回值改一下就行了:因为有对应的构造函数,因此可以用{}来构造返回

std::pair<Iterator,bool> insert(const T& val) {if (_root == nullptr) {_root = new node(val);_root->_color = BLACK;return { {_root,_root},true };}node* cur = _root, * parent = nullptr;while (cur) {if (_kot(cur->_val) < _kot(val)) {parent = cur;cur = cur->_right;}else if (_kot(cur->_val) > _kot(val)) {parent = cur;cur = cur->_left;}else return { {cur,_root},false };}node* ret = nullptr;if (_kot(parent->_val) < _kot(val))ret=cur = parent->_right = new node(val, parent);else ret=cur = parent->_left = new node(val, parent);while (parent && parent->_color == RED) {//意思是到根节点或者父亲不为红色结束循环node* grandparent = parent->_parent;if (grandparent->_left == parent) {//约束条件,方便确定旋转方向node* uncle = grandparent->_right;if (uncle && uncle->_color == RED) {//只用变色的情况grandparent->_color = RED;uncle->_color = parent->_color = BLACK;cur = grandparent;parent = cur->_parent;}else {//对立的uncle为空或者uncle为黑色刚好是要旋转的if (parent->_left == cur) rotateR(grandparent);else rotateLR(grandparent);break;}}else {node* uncle = grandparent->_left;if (uncle && uncle->_color == RED) {grandparent->_color = RED;uncle->_color = parent->_color = BLACK;cur = grandparent;parent = cur->_parent;}else {if (parent->_right == cur)rotateL(grandparent);else rotateRL(grandparent);break;}}}_root->_color = BLACK;//这个必须要有,不然会报错return { {ret,_root},true };
}

operator[]的实现

operator[]只有在map里面才有,因此我们把map函数都实现一下:

iterator find(const K& val) {return _tree.find(val);
}
std::pair<iterator,bool> insert(const std::pair<K, V>& val) {return _tree.insert(val);
}V& operator[](const K& val) {return insert(std::make_pair(val, V())).first->second;
}

这里我就只实现几个函数,只要把房间盖好,里面的装饰可以随便比划。

源码:

#pragma once
#include<iostream>
#include<initializer_list>
#include<vector>	
template <class K, class T, class KofT>class RBtree;template<class T, class ref, class ptr>class RBtree_iterator;template<class K, class V>class map {struct mapKofT {const K& operator()(const std::pair<K, V>& x) {return x.first;}};public:
#if 1typedef std::pair<const K, V> T;typedef RBtree_iterator<T, T&, T*> iterator;typedef RBtree_iterator<T, const T&, const T*> const_iterator;
#else typedef typename RBtree<K, std::pair<const K, V>, mapKofT>::Iterator iterator;typedef typename RBtree<K, std::pair<const K,V>, mapKofT>::const_Iterator const_iterator;
#endifiterator begin() {return _tree.begin();}const_iterator rbegin() {return _tree.rbegin();}iterator end() {return _tree.end();}const_iterator rend() {return _tree.rend();}public:map() {}map(const std::initializer_list< const std::pair<const K, V>>& list):_tree(list){}iterator find(const K& val) {return _tree.find(val);}std::pair<iterator,bool> insert(const std::pair<K, V>& val) {return _tree.insert(val);}V& operator[](const K& val) {return insert(std::make_pair(val, V())).first->second;}private:RBtree<K, std::pair<const K, V>, mapKofT> _tree;};template<class K>class set {struct setKofT {const K& operator()(const K& x) {return x;}};public:typedef typename RBtree<K, const K, setKofT>::Iterator iterator;typedef typename RBtree<K, const K, setKofT>::const_Iterator const_iterator;iterator begin() {return _tree.begin();}const_iterator rbegin() {return _tree.rbegin();}iterator end() {return _tree.end();}const_iterator rend() {return _tree.rend();}public:set(){}set(const std::initializer_list<const K>& list):_tree(list){}std::pair<iterator,bool> insert(const K& val) {return _tree.insert(val);}iterator find(const K& val) {return _tree.find(val);}private:RBtree<K,const K, setKofT>_tree;};enum color {RED,BLACK};template<class T>struct RBtreenode {RBtreenode(const T& val, RBtreenode* parent = nullptr,RBtreenode* left = nullptr, RBtreenode* right = nullptr, color color = RED):_val(val), _parent(parent), _left(left), _right(right), _color(color){}RBtreenode* _left;RBtreenode* _right;RBtreenode* _parent;T _val;color _color;};template<class T,class ref,class ptr>class RBtree_iterator {typedef RBtreenode<T> node;typedef RBtree_iterator<T,ref,ptr> self;public:RBtree_iterator() = default;RBtree_iterator(node*const cur,node*const root) :_cur(cur),_root(root){}self& operator++() {if (_cur->_right) {node* rcur = _cur->_right;while (rcur->_left)rcur = rcur->_left;_cur = rcur;}else {node* parent = _cur->_parent;node* dest = _cur;while (parent && parent->_right == dest) {dest = parent;parent = parent->_parent;}_cur = parent;}return *this;}self& operator--() {if (_cur == nullptr) {node* ret = _root;while (ret->_right)ret = ret->_right;return ret;}if (_cur->_left) {node* lcur = _cur->_left;while (lcur->_right)lcur = lcur->_right;_cur = lcur;}else {node* parent = _cur->_parent;node* dest = _cur;while (parent && parent->_left == dest) {dest == parent;parent = parent->_parent;}_cur = parent;}return *this;}self operator--(int) {self ret = --(*this);return ret;}self operator++(int) {self ret = ++(*this);return ret;}ref operator*() {return _cur->_val;}ptr operator->() {return &operator*();}bool operator!=(const self& x)const{return _cur != x._cur;}bool operator==(const self& x)const {return x._cur == _cur;}/*self& operator=(const self& x) {_cur = x._cur;_root = x._root;return *this;}*/private:node* _cur=nullptr;node* _root;};template <class K,class T,class KofT>class RBtree {typedef RBtreenode<T> node;public:typedef RBtree_iterator<T, T&, T*> Iterator;typedef RBtree_iterator<T,const T&,const T*> const_Iterator;Iterator begin() {node* ret = _root;while (ret->_left)ret = ret->_left;return { ret,_root };}const_Iterator rbegin() {node* ret = _root;while (ret->_left)ret = ret->_left;return { ret,_root };}Iterator end() {return Iterator(nullptr, _root);}const_Iterator rend() {return const_Iterator(nullptr, _root);}public:RBtree() = default;RBtree(const std::initializer_list<const T>& x) {for (auto& num : x)insert(num);}Iterator find(const K& val) {node* cur = _root;while (cur) {if (_kot(cur->_val) <val) cur = cur->_right;else if (_kot(cur->_val) > val)cur = cur->_left;else break;}return { cur,_root };}std::pair<Iterator,bool> insert(const T& val) {if (_root == nullptr) {_root = new node(val);_root->_color = BLACK;return { {_root,_root},true };}node* cur = _root, * parent = nullptr;while (cur) {if (_kot(cur->_val) < _kot(val)) {parent = cur;cur = cur->_right;}else if (_kot(cur->_val) > _kot(val)) {parent = cur;cur = cur->_left;}else return { {cur,_root},false };}node* ret = nullptr;if (_kot(parent->_val) < _kot(val))ret=cur = parent->_right = new node(val, parent);else ret=cur = parent->_left = new node(val, parent);while (parent && parent->_color == RED) {//意思是到根节点或者父亲不为红色结束循环node* grandparent = parent->_parent;if (grandparent->_left == parent) {//约束条件,方便确定旋转方向node* uncle = grandparent->_right;if (uncle && uncle->_color == RED) {//只用变色的情况grandparent->_color = RED;uncle->_color = parent->_color = BLACK;cur = grandparent;parent = cur->_parent;}else {//对立的uncle为空或者uncle为黑色刚好是要旋转的if (parent->_left == cur) rotateR(grandparent);else rotateLR(grandparent);break;}}else {node* uncle = grandparent->_left;if (uncle && uncle->_color == RED) {grandparent->_color = RED;uncle->_color = parent->_color = BLACK;cur = grandparent;parent = cur->_parent;}else {if (parent->_right == cur)rotateL(grandparent);else rotateRL(grandparent);break;}}}_root->_color = BLACK;//这个必须要有,不然会报错return { {ret,_root},true };}private:void rotateL(node* cur) {node* subR = cur->_right;node* subRL = subR->_left;node* curparent = cur->_parent;cur->_right = subRL;if (subRL)subRL->_parent = cur;subR->_parent = curparent;subR->_left = cur;cur->_parent = subR;if (curparent == nullptr) {_root = subR;}else {if (curparent->_left == cur)curparent->_left = subR;else curparent->_right = subR;}subR->_color = BLACK;cur->_color = RED;}void rotateR(node* cur) {node* subL = cur->_left;node* subLR = subL->_right;node* curparent = cur->_parent;cur->_left = subLR;if (subLR)subLR->_parent = cur;cur->_parent = subL;subL->_parent = curparent;subL->_right = cur;if (curparent == nullptr) {_root = subL;}else {if (curparent->_left == cur)curparent->_left = subL;else curparent->_right = subL;}subL->_color = BLACK;cur->_color = RED;}void rotateLR(node* cur) {node* subL = cur->_left;rotateL(subL);rotateR(cur);}void rotateRL(node* cur) {node* subR = cur->_right;rotateR(subR);rotateL(cur);}node* _root = nullptr;KofT _kot;};

下面是一些测试:

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

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

相关文章

关于使用FastGPT 摸索的QA

近期在通过fastGPT&#xff0c;创建一些基于特定业务场景的、相对复杂的Agent智能体应用。 工作流在AI模型的基础上&#xff0c;可以定义业务逻辑&#xff0c;满足输出对话之外的需求。 在最近3个月来的摸索和实践中&#xff0c;一些基于经验的小问题点&#xff08;自己也常常…

LeetCode 热题 100_二叉树的最近公共祖先(48_236_中等_C++)(二叉树;深度优先搜索)

LeetCode 热题 100_二叉树的最近公共祖先&#xff08;48_236&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;深度优先搜索&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&#xff08;深度优…

HTTP/HTTPS ②-Cookie || Session || HTTP报头

这里是Themberfue 上篇文章介绍了HTTP报头的首行信息 本篇我们将更进一步讲解HTTP报头键值对的含义~~~ ❤️❤️❤️❤️ 报头Header ✨再上一篇的学习中&#xff0c;我们了解了HTTP的报头主要是通过键值对的结构存储和表达信息的&#xff1b;我们已经了解了首行的HTTP方法和UR…

PyCharm+RobotFramework框架实现UDS自动化测试——(二)RobotFramework环境配置

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者&#xff0c;时光不负有心人。 文章目录 1.环境准配2.Pycharm中相关配置2.1. 安装Hyper RobotFramework Support 3.脚本执行环境3.1 执行单条的配置3.2 执行全部用例配置 4.工程运行4.1 单条用例运行4.…

Android原生开发同一局域网内利用socket通信进行数据传输

1、数据接收端代码如下&#xff0c;注意&#xff1a;socket 接收信息需要异步运行&#xff1a; // port 端口号自定义一个值&#xff0c;比如 8888&#xff0c;但需和发送端使用的端口号保持一致 ServerSocket serverSocket new ServerSocket(port); while (true) {//这里为了…

腾讯云AI代码助手编程挑战赛-算法小助手

作品简介 一个可以帮助学习计算机各种算法的AI小助手&#xff0c;提升工作效率。 技术架构 使用Html语言完成图形化页面的样式&#xff0c;使用JavaScript语言来操作对应的逻辑代码。 实现过程 1、创建一个界面 2、获取数据 3、添加按钮与功能 4、程序优化调试 开发环境…

使用 IntelliJ IDEA 创建简单的 Java Web 项目

以下是使用 IntelliJ IDEA 创建几个简单的 Java Web 项目的步骤&#xff0c;每个项目实现基本的登录、注册和查看列表功能&#xff0c;依赖 Servlet/JSP 和基本的 Java Web 开发。 前置准备 确保安装了 IntelliJ IDEA Ultimate&#xff08;社区版不支持 Web 应用&#xff09;。…

抓包工具之mitmproxy

一、mitmproxy简介 mitmproxy介绍 mitmproxy又名中间人攻击代理&#xff0c;是一个抓包工具&#xff0c;类似于WireShark、Filddler&#xff0c;并且它支持抓取HTTP和HTTPS协议的数据包&#xff0c;只不过它是一个控制台的形式操作。另外&#xff0c;它还有两个非常有用的组件…

Flutter项目开发模版,开箱即用(Plus版本)

前言 当前案例 Flutter SDK版本&#xff1a;3.22.2 本文&#xff0c;是由这两篇文章 结合产出&#xff0c;所以非常建议大家&#xff0c;先看完这两篇&#xff1a; Flutter项目开发模版&#xff1a; 主要内容&#xff1a;MVVM设计模式及内存泄漏处理&#xff0c;涉及Model、…

rk3568 , buildroot , qt ,使用sqlite, 动态库, 静态库

问题说明&#xff1a; 客户反馈 &#xff0c;buildroot 系统 &#xff0c;使用qt 使用sqlite &#xff0c;有报错&#xff0c;无法使用sqlite. 测试情况说明&#xff1a; 我自己测试&#xff0c;发现&#xff0c; buildroot 自己默认就是 使能了 sqlite 的。 是否解决说明&…

投机解码论文阅读:Falcon

题目&#xff1a;Falcon: Faster and Parallel Inference of Large Language Models through Enhanced Semi-Autoregressive Drafting and Custom-Designed Decoding Tree 地址&#xff1a;https://arxiv.org/pdf/2412.12639 一看它的架构图&#xff0c;可以发现它是基于EAGLE…

鸿蒙UI(ArkUI-方舟UI框架)

参考&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/arkts-layout-development-overview-V13 ArkUI简介 ArkUI&#xff08;方舟UI框架&#xff09;为应用的UI开发提供了完整的基础设施&#xff0c;包括简洁的UI语法、丰富的UI功能&#xff…

TensorFlow Quantum快速编程(基本篇)

一、TensorFlow Quantum 概述 1.1 简介 TensorFlow Quantum(TFQ)是由 Google 开发的一款具有开创性意义的开源库,它宛如一座桥梁,巧妙地将量子计算与 TensorFlow 强大的机器学习功能紧密融合。在当今科技飞速发展的时代,传统机器学习虽已取得诸多瞩目成就,然而面对日益…

Qt天气预报系统获取天气数据

Qt天气预报系统获取天气数据 1、获取天气数据1.1添加天气类头文件1.2定义今天和未来几天天气数据类1.3定义一个解析JSON数据的函数1.4在mainwindow中添加weatherData.h1.5创建今天天气数据和未来几天天气数据对象1.6添加parseJson定义1.7把解析JSON数据添加进去1.8添加错误1.9解…

国产编辑器EverEdit - 扩展脚本:关闭所有未修改文档

1 扩展脚本&#xff1a;关闭所有未修改文档 1.1 应用场景 当用户打开过多文档时&#xff0c;部分文档已经修改&#xff0c;而大部分没有修改&#xff0c;为了减少在众多已打开文档中来回跳转的不便&#xff0c;可以将没有修改的文档全部关闭&#xff0c;但目前提供的快速关闭窗…

高斯函数Gaussian绘制matlab

高斯 约翰卡尔弗里德里希高斯&#xff0c;&#xff08;德语&#xff1a;Johann Carl Friedrich Gau&#xff0c;英语&#xff1a;Gauss&#xff0c;拉丁语&#xff1a;Carolus Fridericus Gauss&#xff09;1777年4月30日–1855年2月23日&#xff0c;德国著名数学家、物理学家…

dolphinscheduler2.0.9升级3.1.9版本问题记录

相关版本说明 JDK&#xff1a;JDK (1.8&#xff09; DolphinScheduler &#xff1a;3.1.9 数据库&#xff1a;MySQL (8)&#xff0c;驱动&#xff1a;MySQL JDBC Driver 8.0.16 注册中心&#xff1a;ZooKeeper (3.8.4) 问题一&#xff1a;dolphinscheduler2.0.9对应zk版本使用…

Sqoop1.4.7安装

环境说明 准备三台服务器&#xff0c;分别为&#xff1a;bigdata141&#xff08;hadoop 主节点&#xff09;、bigdata142、bigdata143确保 hadoop 集群先启动好&#xff0c;hadoop 版本为 3.2.0如果只安装不使用的话&#xff0c;以上可以暂时不用管另准备一台服务器&#xff0…

每日学习30分轻松掌握CursorAI:初识Cursor AI

初识Cursor AI 一、什么是Cursor AI&#xff1f; Cursor AI是一款革命性的AI驱动型代码编辑器&#xff0c;它将传统的代码编辑功能与先进的人工智能技术相结合。它不仅是一个编辑器&#xff0c;更是一个智能编程助手&#xff0c;能够帮助开发者提高编码效率&#xff0c;解决编…

小米路由器IPv6 功能使用指南

本文不限于多层路由使用IPv6 的情况&#xff0c;提供解决IPv6 无法获取的更硬核的方法&#xff0c;需要有ssh 工具。&#xff08;无安卓设备&#xff0c;测试环境win、mac、ios&#xff09; 首先明确一点&#xff0c;就是如果想让你的设备得到GUA 地址&#xff0c;即访问 6.i…