目录
一补充知识
1关联式容器
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高
2键值对
通常用pair<class K,class V>来表示具有一一对应关系的一种结构:
该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义
二set
1set的介绍
1. set是按照一定次序存储元素的容器(升序)
2. 在set中,元素的value也标识它(value就是key,类型为T),每个value必须是唯一的(去重)
set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
3. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
子集进行直接迭代。
4. set在底层是用红黑树实现的。
2set的使用
关于set的接口,有需要自己跳转到链接:set接口说明
set的模板参数列表:
T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较
#include <set>
void TestSet()
{
// 用数组array中的元素构造set
int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,
6, 8, 0 };
set<int> s(array, array + sizeof(array) / sizeof(array));
cout << s.size() << endl;
// 正向打印set中的元素,从打印结果中可以看出:set可去重
for (auto& e : s)
cout << e << " ";
cout << endl;
// 使用迭代器逆向打印set中的元素
for (auto it = s.rbegin(); it != s.rend(); ++it)
cout << *it << " ";
cout << endl;
// set中值为3的元素出现了几次
cout << s.count(3) << endl;
}
在set中,还存在着一个key模型搜索:multiset:
#include<set>
int main()
{
// key模型搜索
// 排序 不去重,允许冗余
multiset<int> s1;
s1.insert(1);
s1.insert(11);
s1.insert(3);
s1.insert(4);
s1.insert(2);
s1.insert(4);
s1.insert(2);
s1.insert(1);
s1.insert(2);
s1.insert(1);
multiset<int>::iterator it = s1.begin();
while (it != s1.end())
{
//*it = 1;
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
三map
1map的说明
1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
value_type绑定在一起3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
5. map通常被实现为红黑树。
关于pair的结构体,stl对它的定义:
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
};
2map的使用
map的插入与打印:
#include<map>
int main()
{
map<string, string> dict;
pair<string, string> kv1("sort", "排序");
dict.insert(kv1);
dict.insert(pair<string, string>("left", "左边"));
dict.insert(make_pair("right", "右边"));
// 隐式类型转换
//pair<string, string> kv2 = { "string", "字符串" };
dict.insert({ "string", "字符串" });
//map<string, string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
// iterator key不能修改 value可以修改
// const_iterator key不能修改 value不能修改
//it->first += 'x';
it->second += 'x';
//cout << (*it).first << ":" << (*it).second << endl;
cout << it->first << ":" << it->second << endl;//省略了一个箭头
//cout << it.operator->()->first << ":" << it.operator->()->second << endl;
++it;
}
cout << endl;
for (auto& kv : dict)
{
//auto& [x, y] = kv;
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
/*for (auto& [x, y] : dict)
{
cout << x << ":" << y << endl;
}
cout << endl;*/
return 0;
}
运算符重载[]的使用:共有几种情况
#include<map>
int main()
{
map<string, string> dict;
dict.insert({ "string","字符串" });
// 插入(一般不会这么用)
dict["right"];
cout << dict["right"] << endl;
// 插入+修改
dict["left"] = "左边";
cout << dict["left"] << endl;
// "查找"
cout << dict["string"] << endl;
// 修改
dict["right"] = "右边";
cout << dict["right"] << endl;
return 0;
}
四容器在oj中的使用
前k个高频单词:oj链接
两个数组的交集:oj链接
五AVL树
1概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右
子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树具有以下性质:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)平衡因子=右子树高度 - 左子树高度
2插入
aAVL树本身也是二叉搜索树,按照搜索树的规则进行插入;
b插入在父亲的左边,平衡因子--;插入在父亲的右边,平衡因子++;
c考虑父亲的平衡因子
==0,父亲所在的子树高度不变,不在往上进行更新了;
==1/-1,父亲所在的子树高度变了,要往上进行更新;
==2/-2,父亲所在的子树已经不平衡了,要进行旋转处理;
bool Insert(const pair<A, V>& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
Node* _parent = nullptr;
Node* cur = root;
while (cur)
{
if (cur->_v.first > key.first)
{
_parent = cur;
cur = cur->left;
}
else if (cur->_v.first < key.first)
{
_parent = cur;
cur = cur->right;
}
else
{
//不存在
return false;
}
}
//cur位置是插入新节点的位置
cur = new Node(key);
//插入在左还是右
if (_parent->_v.first > key.first)
{
//小的在左边
_parent->left = cur;
}
else if (_parent->_v.first < key.first)
{
//大的在右边
_parent->right = cur;
}
cur->parent = _parent;
//调整平衡因子
while (_parent)
{
if (_parent->left == cur)
{
//左边增加--
_parent->_bf--;
}
else
{
//右边增加++
_parent->_bf++;
}
//满足性质不用在进行判断 1->0 -1->0
if (_parent->_bf == 0)
{
break;
}
//高度发生改变要往上进行调节 0 -> 1/-1
else if (_parent->_bf == 1 || _parent->_bf == -1)
{
//继续循环
cur = _parent;
_parent = _parent->parent;
}
else if (_parent->_bf == 2 || _parent->_bf == -2)
{
//旋转来解决不平衡...
}
else
{
assert(false);
}
}
return true;
}
3AVL的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:
3.1右单旋
说明:由于在不同情况下具象图的情况有很多,所有在大多数都是采用抽象图来进行显示:
抽象图:
具象图:(h==0的情况) 以抽象图来进行分析:(新插入的节点在60的左子树中)
以parent为旋转点进行右单旋(顺时针方向进行旋转):
(目的)左子树过高,为了降低左子树的高度使树平衡
void RotateR(Node* _parent)
{
Node* subL = _parent->left;
Node* subLR = subL->right;
_parent->left = subLR;
//指向父亲节点的修正
if (subLR != nullptr) subLR->parent = _parent;
subL->right = _parent;
//记录_parent的父亲节点,它的位置不同会有多种情况
Node* _pparent = _parent->parent;
_parent->parent = subL;
//_parent是根节点
if (_parent == root)
{
root = subL;
subL->parent = nullptr;
}
//_parent只是子树
else
{
if (_pparent->left == _parent)
{
_pparent->left = subL;
}
else if (_pparent->right == _parent)
{
_pparent->right = subL;
}
subL->parent = _pparent;
}
_parent->_bf = subL->_bf = 0;
}
代码难写在于指向的父亲节点的进行修正!!
3.2左单旋
如果右子树过高就要进行左单旋:(新插入的节点在30的右子树中)
以parent为旋转点进行左单旋(逆时针),降低右子树的高度
void RotateL(Node* _parent)
{
Node* subR = _parent->right;
Node* subRL = subR->left;
_parent->right = subRL;
if (subRL != nullptr) subRL->parent = _parent;
subR->left = _parent;
//进行_parent节点的保留
Node* _pparent = _parent->parent;
_parent->parent = subR;
//如果_parent是根节点
if (_parent == root)
{
root = subR;
//没有了父节点
subR->parent = nullptr;
}
//草率了
//else subR->parent = _parent->parent;
//在pparent的左还是右未知
else
{
if (_pparent->left == _parent)
{
_pparent->left = subR;
//_pparent->_bf++;上层的bf从来没有改过
}
else if (_pparent->right == _parent)
{
_pparent->right = subR;
//_pparent->_bf--;
}
subR->parent = _pparent;
}
_parent->_bf = subR->_bf = 0;
}
如果左单旋或者右单旋无法解决问题,那么就要考虑使用两者的结合组成双旋
3.3左右双旋
抽象图的三个节点的位置类似数学符号<:遇到这种情况要进行左右双旋
但是:在更新平衡因子的时候会有三种不同的情况:
第一种:自己就是新增:
第二种:插入在左边:
第三种:插入在右边:
三种不同的情况决定我们接下来写出的代码在平衡因子中要表现出来!!
void RotateLR(Node* _parent)
{
//调整平衡因子
Node* subL = _parent->left;
Node* subLR = subL->right;
int bf = subLR->_bf;
RotateL(_parent->left);
RotateR(_parent);
//左边新增
if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
_parent->_bf = 1;
}
//右边新增
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
_parent->_bf = 0;
}
//自己就是新增
else if (bf == 0)
{
subLR->_bf = subL->_bf = _parent->_bf = 0;
}
else
{
assert(false);
}
}
3.4右左双旋
与左右双旋的情况是反过来的:
分析平衡因子也是有三种情况:与左右双旋的情况是类似的,自己动手分析下吧_(:з」∠)_
void RotateRL(Node* _parent)
{
//调整平衡因子
Node* subR = _parent->right;
Node* subRL = subR->left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(_parent);
//左边新增
if (bf == -1)
{
subRL->_bf = 0;
subR->_bf = 1;
_parent->_bf = 0;
}
//右边新增
else if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
_parent->_bf = -1;
}
//自己就是新增
else if (bf == 0)
{
subRL->_bf = 0;
subR->_bf = 0;
_parent->_bf = 0;
}
else
{
assert(false);
}
}
4判断AVL树是否平衡
根据AVL树的性质来进行判断:高度差的绝对值<=1;再随便判断一下平衡因子是否正确
int _Height(Node* root)
{
if (root == nullptr) return 0;
return max(_Height(root->left), _Height(root->right))+1;
}
bool _IsBalance(Node* root)
{
if (root == nullptr) return true;
int HeightLeft = _Height(root->left);
int HeightRight = _Height(root->right);
//1高度
if (abs(HeightLeft-HeightRight) >= 2) return false;
//2平衡因子
if (HeightRight - HeightLeft != root->_bf) return false;
return _IsBalance(root->left)&&_IsBalance(root->right);
}
完整源代码
#pragma once
#include<utility>
#include<iostream>
#include<assert.h>
using namespace std;
namespace bit
{
template<class A, class V>
struct Node
{
Node<A, V>* parent;
Node<A, V>* left;
Node<A, V>* right;
pair<A, V> _v;
int _bf;
Node(const pair<A, V>& v)
:parent(nullptr),
left(nullptr),
right(nullptr),
_v(v),
_bf(0)
{}
};
template<class A, class V>
class AVL
{
typedef Node<A, V> Node;
public:
Node* find(const A& key)
{
if (root == nullptr) return nullptr;
Node* cur = root;
while (cur)
{
if (cur->_v.first < key)
{
cur = cur->left;
}
else if (cur->_v.first > key)
{
cur = cur->right;
}
else
{
//找到
return cur;
break;
}
}
//为空
return nullptr;
}
bool Insert(const pair<A, V>& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
Node* _parent = nullptr;
Node* cur = root;
while (cur)
{
if (cur->_v.first > key.first)
{
_parent = cur;
cur = cur->left;
}
else if (cur->_v.first < key.first)
{
_parent = cur;
cur = cur->right;
}
else
{
//不存在
return false;
}
}
//cur位置是插入新节点的位置
cur = new Node(key);
//插入在左还是右
if (_parent->_v.first > key.first)
{
//小的在左边
_parent->left = cur;
}
else if (_parent->_v.first < key.first)
{
//大的在右边
_parent->right = cur;
}
cur->parent = _parent;
//调整平衡因子
while (_parent)
{
if (_parent->left == cur)
{
//左边增加--
_parent->_bf--;
}
else
{
//右边增加++
_parent->_bf++;
}
//满足性质不用在进行判断 1->0 -1->0
if (_parent->_bf == 0)
{
break;
}
//高度发生改变要往上进行调节 0 -> 1/-1
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)
{
RotateL(_parent);
}
//右单旋 ->左数太高
else if (_parent->_bf == -2 && cur->_bf == -1)
{
RotateR(_parent);
}
//左右双旋 ->先以_parent->left为旋转点先进行左单旋
else if (_parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(_parent);
}
//右左双旋
else if (_parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(_parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
void RotateL(Node* _parent)
{
Node* subR = _parent->right;
Node* subRL = subR->left;
_parent->right = subRL;
if (subRL != nullptr) subRL->parent = _parent;
subR->left = _parent;
//进行_parent节点的保留
Node* _pparent = _parent->parent;
_parent->parent = subR;
//如果_parent是根节点
if (_parent == root)
{
root = subR;
//没有了父节点
subR->parent = nullptr;
}
//草率了
//else subR->parent = _parent->parent;
//在pparent的左还是右未知
else
{
if (_pparent->left == _parent)
{
_pparent->left = subR;
//_pparent->_bf++;上层的bf从来没有改过
}
else if (_pparent->right == _parent)
{
_pparent->right = subR;
//_pparent->_bf--;
}
subR->parent = _pparent;
}
_parent->_bf = subR->_bf = 0;
}
void RotateR(Node* _parent)
{
Node* subL = _parent->left;
Node* subLR = subL->right;
_parent->left = subLR;
if (subLR != nullptr) subLR->parent = _parent;
subL->right = _parent;
Node* _pparent = _parent->parent;
_parent->parent = subL;
if (_parent == root)
{
root = subL;
subL->parent = nullptr;
}
else
{
if (_pparent->left == _parent)
{
_pparent->left = subL;
}
else if (_pparent->right == _parent)
{
_pparent->right = subL;
}
subL->parent = _pparent;
}
_parent->_bf = subL->_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)
{
subLR->_bf = 0;
subL->_bf = 0;
_parent->_bf = 1;
}
//右边新增
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
_parent->_bf = 0;
}
//自己就是新增
else if (bf == 0)
{
subLR->_bf = subL->_bf = _parent->_bf = 0;
}
else
{
assert(false);
}
}
void RotateRL(Node* _parent)
{
//调整平衡因子
Node* subR = _parent->right;
Node* subRL = subR->left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(_parent);
//左边新增
if (bf == -1)
{
subRL->_bf = 0;
subR->_bf = 1;
_parent->_bf = 0;
}
//右边新增
else if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
_parent->_bf = -1;
}
//自己就是新增
else if (bf == 0)
{
subRL->_bf = 0;
subR->_bf = 0;
_parent->_bf = 0;
}
else
{
assert(false);
}
}
bool IsBalance()
{
return _IsBalance(root);
}
void Inorder()
{
_Inorder(root);
cout << endl;
}
int Height()
{
return _Height(root);
}
int Size()
{
return _Size(root);
}
private:
bool _IsBalance(Node* root)
{
if (root == nullptr) return true;
int HeightLeft = _Height(root->left);
int HeightRight = _Height(root->right);
//1高度
if (abs(HeightLeft-HeightRight) >= 2) return false;
//2平衡因子
if (HeightRight - HeightLeft != root->_bf) return false;
return _IsBalance(root->left)&&_IsBalance(root->right);
}
void _Inorder(Node* root)
{
if (root == nullptr) return;
_Inorder(root->left);
cout << root->_v.first << ":" << root->_v.second << ' ';
_Inorder(root->right);
}
int _Height(Node* root)
{
if (root == nullptr) return 0;
return max(_Height(root->left), _Height(root->right))+1;//root这一层
}
int _Size(Node* root)
{
if (root == nullptr) return 0;
return _Size(root->left) + _Size(root->right) + 1;
}
Node* root = nullptr;
};
void AVLTreeTset1()
{
AVL<int, int> av;
int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
av.Insert({ e,e });
cout << "Insert" << ":" << e << "->" << av.IsBalance() << endl;
}
av.Inorder();
cout << av.IsBalance() << endl;
}
void TestAVLTree2()
{
const int N = 1000000;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand() + i);
//cout << v.back() << endl;
}
size_t begin2 = clock();
AVL<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
}
size_t end2 = clock();
cout << "Insert:" << end2 - begin2 << endl;
cout << t.IsBalance() << endl;
cout << "Height:" << t.Height() << endl;
cout << "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 << "Find:" << end1 - begin1 << endl;
}
}
六红黑树
1概念
红黑树:是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,通过颜色来控制平衡。
2性质/规则
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
3插入
红黑树有红色,黑色可供选择,我们要在进行插入时要选择那个颜色来进行插入?
选择黑色我们必然违反规则四,而选择红色我们有可能违反规则三:这种情况我们可以进行调整来消除:所以在插入时默认选择的是红色:在设计红黑树的节点颜色给红色作为缺省值
现在我们来分析插入时的三种情况:(用p来代表父节点,u代表父的兄弟节点,g代表祖先节点)
①p为红色,u为红色
在不违反规则四的情况下,将p,u变为黑,g变为红:
接下来还要进行对g的判断:g为根节点时,将它再变为黑就结束;g不为根节点时还要再进行处理:
②p为红色,u不存在
u不存在时,对p,g进行变色还不够,还要在此基础上进行单旋,保证遵守规则四!!
③p为红色,u为黑色
出现这种情况一定是下面的①情况变上来的:与第②中情况就的处理方法类似:
p,g进行变色+右单旋(p是g的右孩子就进行左单旋)
上面的三种情况严格来说只有两种情况:u为红处理变色;
u为黑/不存在既要变色也要进行旋转,至于是左单旋还是右单旋要看p在g的那边来确定
而如果要插入的节点在p的右边,根据我们在AVL树学习的知识: g,p,cur组成的形状为<的要进行左右双旋;形状为>的要进行右左双旋;
有了上面AVL旋转的分析,看到这里应该不是问题!!
代码实现
bool Insert(const V& val)
{
//根为空
if (_root == nullptr)
{
_root = new Node(val);
_root->_col = Blank;
return true;
}
//不为空
//要记录cur的parent!!(又忘了(⊙﹏⊙))
Node* parent = nullptr;
Node* cur=_root;
while (cur)
{
//比我小往左走
if (val.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
//比我大往右走
else if (val.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
//存在相等就失败
return false;
}
}
//cur为空:进行插入
cur = new Node(val);
if (val.first < parent->_kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent&& parent->_col == Red)
{
Node* greadfather = parent->_parent;
//关键看uncle的位置,情况
//1.uncle的位置
if (greadfather->_left == parent)
{
Node* uncle = greadfather->_right;
//2.uncle有三种情况:为红,不存在,为黑
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Blank;
greadfather->_col = Red;
//要向上进行搜索
cur = greadfather;
parent = cur->_parent;
}
//不存在/为黑
else
{
//以greadfather进行右旋
if (parent->_left == cur)
{
// g
// p u
// c
RotateR(greadfather);
parent->_col = Blank;
greadfather->_col = Red;
//uncle为黑也不用管
//(这种情况是由前面的uncle为Red变来的)
}
//在parent右边:以parent进行左旋;以greadfather进行右旋
else
{
// g
// p u
// c
RotateL(parent);
RotateR(greadfather);
cur->_col = Blank;
greadfather->_col = Red;
}
break;
}
}
else
{
Node* uncle = greadfather->_left;
//uncle为红
if (uncle && uncle->_col == Red)
{
uncle->_col = parent->_col = Blank;
greadfather->_col = Red;
cur = greadfather;
parent = cur->_parent;
}
//不存在/为黑
else
{
//在右边:以greadgather进行左旋
// g
// u p
// c
if (parent->_right == cur)
{
RotateL(greadfather);
parent->_col = Blank;
greadfather->_col = Red;
}
//在左边:以parent进行右旋;以greadfather进行左旋
else
{
// g
// u p
// c
RotateR(parent);
RotateL(greadfather);
cur->_col = Blank;
greadfather->_col = Red;
}
break;
}
}
}
//不管有没有变
_root->_col = Blank;
return true;
}
4判断是否平衡
结合红黑树的性质来进行判断:
根节点的判断;是否有连续红节点的判断;每条路径是否有相同黑节点的判断
bool Check(Node* root,int Num,int BlankNum)
{
if (root == nullptr)
{
if (Num == BlankNum) return true;
else return false;
}
if (root->_col == Blank) Num++;
if (root->_col == Red && root->_parent->_col == Red) return false;
return Check(root->_left, Num, BlankNum)&&
Check(root->_right, Num, BlankNum);
}
bool _IsBalance(Node* root)
{
if (root->_col == Red) return false;
Node* cur = root;
int BlankNum = 0;
while (cur)
{
if (cur->_col == Blank) BlankNum++;
cur = cur->_left;
}
return Check(root,0,BlankNum);
}
bool IsBalance()
{
return _IsBalance(_root);
}
完整源代码
enum Colour
{
Red,
Blank
};
namespace RB
{
template<class K,class V>
struct RBTreeNode
{
RBTreeNode(const pair<K,V>& val)
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _col(Red)
, _kv(val)
{}
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
Colour _col;
pair<K, V> _kv;
//V _kv;
};
template<class K,class V>
class RBTree
{
public:
typedef RBTreeNode<K, V> Node;
void RotateL(Node* parent)
{
Node* parentR = parent->_right;
Node* parentRL = parentR->_left;
parent->_right = parentRL;
if (parentRL != nullptr) parentRL->_parent = parent;
parentR->_left = parent;
//记录
Node* pparent = parent->_parent;
parent->_parent = parentR;
if (_root == parent)
{
_root = parentR;
//(⊙o⊙)…
parentR->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = parentR;
}
else
{
pparent->_right = parentR;
}
parentR->_parent = pparent;
}
}
void RotateR(Node* parent)
{
Node* parentL = parent->_left;
Node* parentLR = parentL->_right;
parent->_left = parentLR;
if (parentLR != nullptr)
{
parentLR->_parent = parent;
}
parentL->_right = parent;
Node* pparent = parent->_parent;
parent->_parent = parentL;
if (_root == parent)
{
_root = parentL;
parentL->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = parentL;
}
else
{
pparent->_right = parentL;
}
parentL->_parent = pparent;
}
}
bool Insert(const pair<K,V>& val)
{
//根为空
if (_root == nullptr)
{
_root = new Node(val);
_root->_col = Blank;
return true;
}
//不为空
//要记录cur的parent!!(又忘了(⊙﹏⊙))
Node* parent = nullptr;
Node* cur=_root;
while (cur)
{
//比我小往左走
if (val.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
//比我大往右走
else if (val.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
//存在相等就失败
return false;
}
}
//cur为空:进行插入
cur = new Node(val);
if (val.first < parent->_kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent&& parent->_col == Red)
{
Node* greadfather = parent->_parent;
//关键看uncle的位置,情况
//1.uncle的位置
if (greadfather->_left == parent)
{
Node* uncle = greadfather->_right;
//2.uncle有三种情况:为红,不存在,为黑
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Blank;
greadfather->_col = Red;
//要向上进行搜索
cur = greadfather;
parent = cur->_parent;
}
//不存在/为黑
else
{
//以greadfather进行右旋
if (parent->_left == cur)
{
// g
// p u
// c
RotateR(greadfather);
parent->_col = Blank;
greadfather->_col = Red;
//uncle为黑也不用管
//(这种情况是由前面的uncle为Red变来的)
}
//在parent右边:以parent进行左旋;以greadfather进行右旋
else
{
// g
// p u
// c
RotateL(parent);
RotateR(greadfather);
cur->_col = Blank;
greadfather->_col = Red;
}
break;
}
}
else
{
Node* uncle = greadfather->_left;
//uncle为红
if (uncle && uncle->_col == Red)
{
uncle->_col = parent->_col = Blank;
greadfather->_col = Red;
cur = greadfather;
parent = cur->_parent;
}
//不存在/为黑
else
{
//在右边:以greadgather进行左旋
// g
// u p
// c
if (parent->_right == cur)
{
RotateL(greadfather);
parent->_col = Blank;
greadfather->_col = Red;
}
//在左边:以parent进行右旋;以greadfather进行左旋
else
{
// g
// u p
// c
RotateR(parent);
RotateL(greadfather);
cur->_col = Blank;
greadfather->_col = Red;
}
break;
}
}
}
//不管有没有变
_root->_col = Blank;
return true;
}
void InOrder()
{
_InOrder(_root);
}
bool IsBalance()
{
return _IsBalance(_root);
}
private:
bool Check(Node* root,int Num,int BlankNum)
{
if (root == nullptr)
{
if (Num == BlankNum) return true;
else return false;
}
if (root->_col == Blank) Num++;
if (root->_col == Red && root->_parent->_col == Red) return false;
return Check(root->_left, Num, BlankNum)&&
Check(root->_right, Num, BlankNum);
}
bool _IsBalance(Node* root)
{
if (root->_col == Red) return false;
Node* cur = root;
int BlankNum = 0;
while (cur)
{
if (cur->_col == Blank) BlankNum++;
cur = cur->_left;
}
return Check(root,0,BlankNum);
}
void _InOrder(Node* root)
{
if (root == nullptr) return;
_InOrder(root->_left);
cout << root->_kv.first << ":"<<root->_kv.second<<endl;
//cout << Return(root->_kv) << endl;
_InOrder(root->_right);
}
Node* _root = nullptr;
};
void TestRBTree1()
{
//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
RBTree<int, int> t1;
for (auto e : a)
{
if (e == 10)
{
int i = 0;
}
// 1、先看是插入谁导致出现的问题
// 2、打条件断点,画出插入前的树
// 3、单步跟踪,对比图一一分析细节原因
t1.Insert({ e,e });
cout << "Insert:" << e << "->" << t1.IsBalance() << endl;
}
t1.InOrder();
cout << t1.IsBalance() << endl;
}
void TestRBTree2()
{
const int N = 1000000;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand() + i);
//cout << v.back() << endl;
}
size_t begin2 = clock();
RBTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e,e));
//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
}
size_t end2 = clock();
cout << t.IsBalance() << endl;
}
}
七map和set的封装
1基本框架
set和map的valute不同:一个是key,一个是pair<key,valute>;但两者的底层都是通过红黑树来实现的;
思路一:我们可以使用两份相同的红黑树代码,把valute改下进行可以实现目标,但这会使我们代码出现冗余,而且里面有大量重复工作,不是很好的解决办法~-~;
让我们来尝试着通过STL源码来找到map和set的实现思路:
通过类模板自己推演出类型,解决代码冗余的问题;
我们来仿照上面的思路,来进行对红黑树的简单改造:
template<class V>
struct RBTreeNode
{
RBTreeNode(const V& val)
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _col(Red)
, _kv(val)
{}
RBTreeNode<V>* _parent;
RBTreeNode<V>* _left;
RBTreeNode<V>* _right;
Colour _col;
//pair<K, V> _kv;
V _kv;
};
template<class K, class V>
class RBTree
{
public:
typedef RBTreeNode<V> Node;
void insert(const V& val)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
//比我小往左走
if (kot(val) < kot(cur->_kv))
{
parent = cur;
cur = cur->_left;
}
//比我大往右走
else if (kot(val) > kot(cur->_kv))
{
parent = cur;
cur = cur->_right;
}
else
{
//存在相等就失败
return make_pair(Iterator(cur),false);
}
}
....
}
private:
Node* _root=nullptr;
};
//map
template<class K,class V>
class map
{
private:
RBTree<K, pair<K,V>> _t;
};
//set
class set
{
private:
RBTree<K, K> _t;
};
简单改造后,我们会发现一个问题:在插入时我们要与节点上的值进行比较:set比较的是key;map比较的是pair<K,V>中的第一个:
而在实现insert中我们并不知道当前是哪一个容器要进行插入,这要怎么解决呢?
解决:在map和set中各自写一个类来实现valute的返回值,传递给模板:让它自己去推演出要实例化的类型:借助这个类型创建对象,对象在各自去调用对应的类方法来确定返回值!
2红黑树的迭代器
如果有学习过list的迭代器并自己独立实现过的话:到了这里应该对你来说不是问题:
template<class V,class Ref,class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<V> Node;
typedef RBTreeIterator<V, Ref, Ptr> Slef;
Node* _it;
RBTreeIterator(Node* it)
:_it(it)
{}
Ref operator*()
{
return _it->_kv;
}
Ptr operator->()
{
return &_it->_kv;
}
bool operator!=(const Slef& it)
{
return _it != it._it;
}
};
但这里的beign()位置可不是根节点,而是左子树的最右节点!
而end()在stl中把它当成header节点,这里我们简单点,把它当成nullptr来使用就行
stl中红黑树结构:
2.1operator++
根据搜索二叉树走的是中序遍历来进行分析:共有两种情况:
上面说的可能有点抽象,根据代码来理解理解:
Slef& operator++()
{
//找右子树的最左节点(最小值)
if (_it->_right != nullptr)
{
Node* leftMin = _it->_right;
while (leftMin->_left)
{
leftMin = leftMin->_left;
}
_it = leftMin;
}
//右数为空
else
{
//往上找到parent是祖先节点的左边
Node* cur = _it;
Node* parent = cur->_parent;
//有可能_it是根的情况
while (parent && parent->_right == cur)
{
cur = parent;
parent = parent->_parent;
}
_it = parent;
}
return *this;
实现operator--就是与++的情况反过来的思路,这里就不展开叙述:
Slef& operator--()
{
//找左数的最右节点
if (_it->_left != nullptr)
{
Node* left = _it->_left;
while (left->_right)
{
left = left->_right;
}
_it = left;
}
else
{
//找是祖先节点的右节点
Node* cur = _it;
Node* parent = cur->_parent;
while (parent && parent->_left == cur)
{
cur = parent;
parent = parent->_parent;
}
_it = parent;
}
return *this;
}
3find
在红黑树的模板中,当时我们传入了K:你可能会想:模板中有V就能够实现绝大部分valute的实例化了,要K有什么用?
其实它是为了实现find函数准备的!!要进行查找一个值用的就是它的K值:
Iterator find(const K& val)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv > val)
{
cur = cur->_left;
}
else if (cur->_kv < val)
{
cur = cur->_right;
}
else
{
//相等
return Iterator(cur);
}
}
return Iterator(nullptr);
}
4insert和map的operator[]
在stl实现中,insert的返回值的类型为:pair<iterator,bool>
返回值有两种情况:假设插入的值为:cur
1如果值存在了,返回make_pair(cur,false)
2如果不存在,返回make_pair(cur,true)
而map实现operator[]可以通过insert来找到对应的值;即pair<K,V>中的V
完整源代码
// RBTree.h
#pragma once
#include<iostream>
using namespace std;
//用枚举来定义颜色方便初始化
enum Colour
{
Red,
Blank
};
namespace bit
{
template<class V>
struct RBTreeNode
{
RBTreeNode(const V& val)
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _col(Red)
, _kv(val)
{}
RBTreeNode<V>* _parent;
RBTreeNode<V>* _left;
RBTreeNode<V>* _right;
Colour _col;
//pair<K, V> _kv;
V _kv;
};
template<class V,class Ref,class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<V> Node;
typedef RBTreeIterator<V, Ref, Ptr> Slef;
Node* _it;
RBTreeIterator(Node* it)
:_it(it)
{}
Ref operator*()
{
return _it->_kv;
}
Ptr operator->()
{
return &_it->_kv;
}
bool operator!=(const Slef& it)
{
return _it != it._it;
}
Slef& operator++()
{
//找右子树的最左节点(最小值)
if (_it->_right != nullptr)
{
Node* leftMin = _it->_right;
while (leftMin->_left)
{
leftMin = leftMin->_left;
}
_it = leftMin;
}
//右数为空
else
{
//往上找到parent是祖先节点的左边
Node* cur = _it;
Node* parent = cur->_parent;
//有可能_it是根的情况
while (parent && parent->_right == cur)
{
cur = parent;
parent = parent->_parent;
}
_it = parent;
}
return *this;
}
//++思路反过来
Slef& operator--()
{
//找左数的最右节点
if (_it->_left != nullptr)
{
Node* left = _it->_left;
while (left->_right)
{
left = left->_right;
}
_it = left;
}
else
{
//找是祖先节点的右节点
Node* cur = _it;
Node* parent = cur->_parent;
while (parent && parent->_left == cur)
{
cur = parent;
parent = parent->_parent;
}
_it = parent;
}
return *this;
}
};
template<class K, class V, class KeyOfT>
class RBTree
{
public:
typedef RBTreeNode<V> Node;
typedef RBTreeIterator<V, V&, V*> Iterator;
typedef RBTreeIterator<V, const V&, const V*> Const_Iterator;
Const_Iterator begin() const
{
//空树的情况
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return cur;
}
Const_Iterator end() const
{
return nullptr;
}
Iterator begin()
{
//空树的情况
Node* cur = _root;
while (cur&&cur->_left)
{
cur = cur->_left;
}
return Iterator(cur);
}
Iterator end()
{
return Iterator(nullptr);
}
//函数三件套
RBTree() = default;
//拷贝构造
RBTree(const RBTree<K, V, KeyOfT>& t)
{
_root = Copy(t._root);
}
//不用引用
RBTree<K, V,KeyOfT>& operator=(RBTree<K, V, KeyOfT> t)
{
swap(t._root, _root);
return *this;
}
~RBTree()
{
Destory(_root);
}
void RotateL(Node* parent)
{
Node* parentR = parent->_right;
Node* parentRL = parentR->_left;
parent->_right = parentRL;
if (parentRL != nullptr) parentRL->_parent = parent;
parentR->_left = parent;
//记录
Node* pparent = parent->_parent;
parent->_parent = parentR;
if (_root == parent)
{
_root = parentR;
//(⊙o⊙)…
parentR->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = parentR;
}
else
{
pparent->_right = parentR;
}
parentR->_parent = pparent;
}
}
void RotateR(Node* parent)
{
Node* parentL = parent->_left;
Node* parentLR = parentL->_right;
parent->_left = parentLR;
if (parentLR != nullptr)
{
parentLR->_parent = parent;
}
parentL->_right = parent;
Node* pparent = parent->_parent;
parent->_parent = parentL;
if (_root == parent)
{
_root = parentL;
parentL->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = parentL;
}
else
{
pparent->_right = parentL;
}
parentL->_parent = pparent;
}
}
//模版的k为它准备的!!
Iterator find(const K& val)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv > val)
{
cur = cur->_left;
}
else if (cur->_kv < val)
{
cur = cur->_right;
}
else
{
//相等
return Iterator(cur);
}
}
return Iterator(nullptr);
}
pair<Iterator,bool> Insert(const V& val)
{
//根为空
if (_root == nullptr)
{
_root = new Node(val);
_root->_col = Blank;
return make_pair(Iterator(_root), true);
}
//不为空
//要记录cur的parent!!(又忘了(⊙﹏⊙))
//类模板传的是类型!!
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
//比我小往左走
if (kot(val) < kot(cur->_kv))
{
parent = cur;
cur = cur->_left;
}
//比我大往右走
else if (kot(val) > kot(cur->_kv))
{
parent = cur;
cur = cur->_right;
}
else
{
//存在相等就失败
return make_pair(Iterator(cur),false);
}
}
//cur为空:进行插入
cur = new Node(val);
//要返回cur要对cur进行保留
Node* newnode = cur;
if (kot(val) < kot(parent->_kv))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == Red)
{
Node* greadfather = parent->_parent;
//关键看uncle的位置,情况
//1.uncle的位置
if (greadfather->_left == parent)
{
Node* uncle = greadfather->_right;
//2.uncle有三种情况:为红,不存在,为黑
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Blank;
greadfather->_col = Red;
//要向上进行搜索
cur = greadfather;
parent = cur->_parent;
}
//不存在/为黑
else
{
//以greadfather进行右旋
if (parent->_left == cur)
{
// g
// p u
// c
RotateR(greadfather);
parent->_col = Blank;
greadfather->_col = Red;
//uncle为黑也不用管
//(这种情况是由前面的uncle为Red变来的)
}
//在parent右边:以parent进行左旋;以greadfather进行右旋
else
{
// g
// p u
// c
RotateL(parent);
RotateR(greadfather);
cur->_col = Blank;
greadfather->_col = Red;
}
break;
}
}
else
{
Node* uncle = greadfather->_left;
//uncle为红
if (uncle && uncle->_col == Red)
{
uncle->_col = parent->_col = Blank;
greadfather->_col = Red;
cur = greadfather;
parent = cur->_parent;
}
//不存在/为黑
else
{
//在右边:以greadgather进行左旋
// g
// u p
// c
if (parent->_right == cur)
{
RotateL(greadfather);
parent->_col = Blank;
greadfather->_col = Red;
}
//在左边:以parent进行右旋;以greadfather进行左旋
else
{
// g
// u p
// c
RotateR(parent);
RotateL(greadfather);
cur->_col = Blank;
greadfather->_col = Red;
}
break;
}
}
}
//不管有没有变
_root->_col = Blank;
return make_pair(Iterator(newnode),true);
}
void InOrder()
{
_InOrder(_root);
}
bool IsBalance()
{
return _IsBalance(_root);
}
private:
Node* _root = nullptr;
//前序遍历进行new节点
Node* Copy(const Node* root)
{
if (root == nullptr) return nullptr;
Node* newroot = new Node(root->_kv);
newroot->_col = root->_col;
newroot->_left=Copy(root->_left);
if (newroot->_left != nullptr) newroot->_left->_parent = newroot;
newroot->_right = Copy(root->_right);
if (newroot->_right != nullptr) newroot->_right->_parent = newroot;
return newroot;
}
//后序遍历进行析构
void Destory(const Node* root)
{
if (root == nullptr) return;
Destory(root->_left);
Destory(root->_right);
delete root;
root = nullptr;
}
bool Check(Node* root, int Num, int BlankNum)
{
if (root == nullptr)
{
if (Num == BlankNum) return true;
else return false;
}
if (root->_col == Blank) Num++;
if (root->_col == Red && root->_parent->_col == Red) return false;
return Check(root->_left, Num, BlankNum) &&
Check(root->_right, Num, BlankNum);
}
bool _IsBalance(Node* root)
{
if (root->_col == Red) return false;
Node* cur = root;
int BlankNum = 0;
while (cur)
{
if (cur->_col == Blank) BlankNum++;
cur = cur->_left;
}
//检查每条路径的黑色节点是否相同
return Check(root, 0, BlankNum);
}
void _InOrder(Node* root)
{
if (root == nullptr) return;
_InOrder(root->_left);
//cout << root->_kv.first << ":"<<root->_kv.second<<endl;
cout << Return(root->_kv) << endl;
_InOrder(root->_right);
}
};
}
// myset.h
#pragma once
namespace bit
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& val)
{
return val;
}
};
public:
typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKeyOfT>::Const_Iterator const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin() const
{
return _t.begin();
}
const_iterator end() const
{
return _t.end();
}
pair<iterator,bool> insert(const K& val)
{
return _t.Insert(val);
}
private:
RBTree<K, const K, SetKeyOfT> _t;
};
void PrintConstIterator(const set<int>& m)
{
set<int>::const_iterator it = m.begin();
while (it!=m.end())
{
cout << *it << ' ';
++it;
}
cout << endl;
}
void test_set()
{
set<int> s;
s.insert(4);
s.insert(2);
s.insert(5);
s.insert(15);
s.insert(7);
s.insert(1);
s.insert(5);
s.insert(7);
cout << "ConstIterator:" << endl;
PrintConstIterator(s);
cout << "Iterator:" << endl;
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
set<int> copy = s;
for (auto e : copy)
{
cout << e << " ";
}
cout << endl;
}
}
//mymap.h
#pragma once
namespace bit
{
template<class K,class V>
class map
{
//内部类,返回在红黑树中要进行使用的值
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& val)
{
return val.first;
}
};
public:
//迭代器封装
typedef typename RBTree<K, pair<const K,V>, MapKeyOfT>::Iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Const_Iterator const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin() const
{
return _t.begin();
}
const_iterator end() const
{
return _t.end();
}
iterator find(const K& val)//模板的第一个类型这里要用到
{
return _t.find(val);
}
pair<iterator,bool> insert(const pair<K, V>& val)
{
return _t.Insert(val);
}
//不同情况[]的使用含义不同
V& operator[](const K& val)
{
pair<iterator, bool> ret = _t.Insert(make_pair(val, V()));
return ret.first->second;//用迭代器的方式返回红黑树储存的第二个值
}
private:
//第一个值不能进行修改
RBTree<K, pair<const K,V>, MapKeyOfT> _t;
};
//测试代码
void PrintMap(const map<int, int>& m)
{
map<int, int>::const_iterator it = m.begin();
while (it != m.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
}
void test_map()
{
map<int, int> m;
m.insert({1,1});
m.insert({ 3,3 });
m.insert({ 9,9 });
m.insert({ 5,5 });
cout << "ConstIterator:" << endl;
PrintMap(m);
cout << "Iterator"<<endl;
map<int, int> n = m;
map<int, int>::iterator it = n.begin();
while (it != m.end())
{
//it->first += 'x';
//cout << it.operator->()->first << ":" << it->second << endl;
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
}
void test_map1()
{
string arr[] = { "苹果", "西瓜","西瓜" ,"草莓","草莓","草莓"};
map<string, int> countMap;
for (auto& e : arr)
{
countMap[e]++;
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
}
}
//test.cpp
#include"RBTree.h"
#include"mymap.h"
#include"myset.h"
int main()
{
bit::test_set();
//bit::test_map1();
return 0;
}
以上便是学习map和set的知识总结,有错误欢迎在评论区指出,Thanks♪(・ω・)ノ