网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
2.3 multimap的概念和使用
与multiset一样,都是可以出现重复的值,只是其中没有了map的operator[ ],就不过诉了。
3.map、set的底层实现
3.1红黑树的修改
为了给set、map做底层,我们需要完善一下红黑树在里面主要是
- 模板的改变:将原本第二个参数V改成T,T代表的是K,V组成成的键值对pair<K,V>
- 添加迭代器以及begin、end函数,让map、set也能用迭代器
- 修改插入的返回值:将原本的iterator改成pair<iterator,bool>,(这是STL源码内的设计,也是为了map的[]做准备)
修改后的源码:
#pragma once
#include<iostream>
using namespace std;
enum Color
{
BLACK,
RED
};
template<class T>
struct RBTreeNode {
RBTreeNode<T>\* _left = nullptr;
RBTreeNode<T>\* _right = nullptr;
RBTreeNode<T>\* _parent = nullptr;
T _data;
Color _col = RED;//默认生成的节点颜色是红色
RBTreeNode(const T& data)
:\_data(data)
{}
};
//迭代器
template<class T, class Ptr, class Ref>
struct \_TreeIterator
{
typedef _TreeIterator<T, Ptr, Ref> Self;
typedef RBTreeNode<T> Node;
Node\* _node;//迭代器的成员变量
\_TreeIterator(Node\* node)
:\_node(node)
{}
Self& operator++()
{
Node\* cur = _node;
if (cur->_right)//若右边不为空,则找到其左边的右边节点
{
cur = cur->_right;
while (cur->_left)
{
cur = cur->_left;
}
_node = cur;
}
else
{
Node\* parent = cur->_parent;
while (parent && parent->_left != cur)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return \*this;
}
Ref operator\*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator==(const Self & it)
{
return _node == it._node;
}
bool operator!=(const Self & it)
{
return _node != it._node;
}
};
template<class K, class T, class Compare>
class RBTree
{
typedef RBTreeNode<T> Node;
Compare kot;
public:
typedef _TreeIterator<T,T\*,T&> iterator;
typedef _TreeIterator<T,const T\*,const T&> const_iterator;
//typedef typename RBTree<K, pair<K, V>, Mapofkey>::iterator iterator;
// 在红黑树中插入值为data的节点,插入成功返回true,否则返回false
// 注意:为了简单起见,本次实现红黑树不存储重复性元素
iterator begin()
{
Node\* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
iterator end()
{
return iterator(nullptr);//end指向最后数据的后面故为空
}
const_iterator begin() const
{
Node\* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const\_iterator(cur);
}
const_iterator end() const
{
return const\_iterator(nullptr);//end指向最后数据的后面故为空
}
//此处用Node\* 的原因set处的iterator为了防止被修改所以set内的iterator本质还是const\_iterator,
//所以这里用了 Node\* 来代替iterator 的返回
pair<Node\*, bool> Insert(const T& data)
{
//此处和AVL平衡二叉树的性质一样找到所要插入节点的位置 大的在右 、 小的在左
Node\* parent = nullptr;
Node\* cur = _root;
if (cur == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make\_pair(_root, true);
}
//找到插入的位置!
while (cur)//当为null时表示此处就是要插入的位置!
{
if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return make\_pair(nullptr, false);
}
}
//找到位置后,插入
cur = new Node(data);//建立新节点
Node\* ret = cur;
//建立链接
if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//插入时要判断插入后是否会导致不平衡!对于红黑树来说主要问题有
//1. 不能出现连续的红节点
//2. 最长路径不超过最短路径的两倍
//判断是否需要变色/旋转
//
//1.当父亲节点为黑色时,当新增了一个红色节点时就结束插入了
//
//2.当父为红时:
// 情况一(仅变色即可):当parent为红 grandfather为黑 uncle存在且为黑 插入一个新节点
while (parent && parent->_col == RED)
{
Node\* g = parent->_parent;//grandfather
if (g->_left == parent)
{
Node\* u = g->_right;//uncle
if (u && u->_col == RED)//u存在且为红
{
//变色即可
u->_col = parent->_col = BLACK;
g->_col = RED;
//向上调整
cur = g;
parent = g->_parent;
//当g 的 父亲为黑时或者为null时停止调整
}
else //u不存在或者为黑
{
if (cur == parent->_left)//此处u不存在和当插入节点在左边时的情况一样直接右旋加变色即可
{
//旋转加变色
RotateR(g);
parent->_col = BLACK;
g->_col = RED;
}
else
{
//旋转加变色
RotateL(parent);
RotateR(g);
cur->_col = BLACK;
g->_col = RED;
}
}
}
else
{
Node\* u = g->_left;//uncle
if (u && u->_col == RED)//u存在且为红
{
//变色即可
u->_col = parent->_col = BLACK;
g->_col = RED;
//向上调整
cur = g;
parent = g->_parent;
//当g 的 父亲为黑时或者为null时停止调整
}
else //u不存在或者为黑
{
if (cur == parent->_right)//此处u不存在和当插入节点在左边时的情况一样直接右旋加变色即可
{
RotateL(g);
parent->_col = BLACK;
g->_col = RED;
}
else
{
RotateR(parent);
RotateL(g);
cur->_col = BLACK;
g->_col = RED;
}
}
}
}
_root->_col = BLACK;
return make\_pair(ret, true);
}
void Inorder()
{
\_Inorder(_root);
cout << endl;
}
// 获取红黑树最左侧节点
Node\* LeftMost()
{
Node\* cur = _root;
while (cur)
{
if (cur->_left == nullptr)
{
return cur;
}
cur = cur->_left;
}
return nullptr;
}
// 获取红黑树最右侧节点
Node\* RightMost()
{
Node\* cur = _root;
while (cur)
{
if (cur->_right == nullptr)
{
return cur;
}
cur = cur->_right;
}
return nullptr;
}
// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠\_IsValidRBTRee函数检测
// 1.每条路径中的黑色节点个数是否一样
// 2.最长路径不超过最短路径的两倍
// 3.不能出现连续的红色节点
// 4.根节点为黑色
bool IsValidRBTRee()
{
if (_root == nullptr) return true;
if (_root->_col == RED) return false;
Node\* cur = _root;
int blackCount = 0;
while (cur)
{
if (cur->_col == BLACK)
{
blackCount++;
}
cur = cur->_left;
}
return \_IsValidRBTRee(_root, blackCount, 0);
}
int Height()
{
if (_root == nullptr) return 0;
return \_Height(_root);
}
int Size()
{
if (_root == nullptr) return 0;
return \_Size(_root);
}
//检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
Node\* Find(const K& val)
{
Node\* cur = _root;
while (cur)
{
if (kot(cur->_data) == val)
{
return cur;
}
else if (kot(cur->_data) > val)
{
cur = cur->_left;
}
else {
cur = cur->_right;
}
}
return nullptr;
}
private:
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 lefthight = \_Height(root->_left);
int righthight = \_Height(root->_right);
return lefthight > righthight ? lefthight + 1 : righthight + 1;
}
void \_Inorder(Node\* root)
{
if (root == nullptr)return;
\_Inorder(root->_left);
cout << root->_data.first << " ";
\_Inorder(root->_right);
}
bool \_IsValidRBTRee(Node\* root, size_t blackCount, size_t pathBlack)
{
if (root == nullptr)
{
if (blackCount != pathBlack)//当为null时表示该路径已经结束,那么判断改路径的黑色节点(pathblack) 和其他路径的黑色节点(blacCount)是否相同
{
return false;
}
return true;
}
if (root->_col == RED)
{
if (root->_left && root->_right && (root->_left->_col == RED || root->_right->_col == RED))
{
cout << "有连续的红色节点" << endl;
return false;
}
}
if (root->_col == BLACK)
{
pathBlack++;
}
return \_IsValidRBTRee(root->_left, blackCount, pathBlack) &&
\_IsValidRBTRee(root->_right, blackCount, pathBlack);
}
// // 为了操作树简单起见:获取根节点
//Node\*& GetRoot();
void RotateR(Node\* parent)
{
Node\* SubL = parent->_left;//此处就为 cur
Node\* SubLR = SubL->_right;
//parent的左换成cur的右
parent->_left = SubLR;
//把cur的右孩子换成parent
SubL->_right = parent;
//注意还要修改其父指针
Node\* Ppnode = parent->_parent;
parent->_parent = SubL;
if (SubLR)//cur的右边可能为空
SubLR->_parent = parent;
if (_root == parent)//如果parent为根节点,则需要把subR改变成根节点并且其父亲为nullptr
{
_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;//此处就为 cur
Node\* SubRL = SubR->_left;
//parent的右换成cur的左
parent->_right = SubRL;
//把cur的左孩子换成parent
SubR->_left = parent;
Node\* Ppnode = parent->_parent;
//注意 还要修改其父指针
parent->_parent = SubR;
if (SubRL)//右边可能为空
SubRL->_parent = parent;
if (_root == parent)//如果parent为根节点,则需要把subR改变成根节点并且其父亲为nullptr
{
_root = SubR;
SubR->_parent = nullptr;
}
else
{
//同时还要考虑父亲 是祖先的左或右
if (Ppnode->_left == parent)
{
Ppnode->_left = SubR;
}
else
{
Ppnode->_right = SubR;
}
SubR->_parent = Ppnode;
}
}
private:
Node\* _root = nullptr;
};
为了让set、map能实现迭代器所以还要写好迭代器常用的重载operator*、operator++、operator!=、…
下面是实现过程也就是再将以及写好的红黑树进行再一次的封装,实现set、map所需要的功能。
3.2set的实现
#pragma once
#include"RBTree.h"
template<class K>
class MySet
{
public:
struct Setofkey
{
//自己所写的适配器用来从pair中调出key值
const K& operator()(const K& key)
{
return key;
}
};
//通过红黑树的迭代器来重定义生成自己的
//typename的意思是声明他是类型名
typedef typename RBTree<K, K, Setofkey>::const_iterator iterator;
typedef typename RBTree<K, K, Setofkey>::const_iterator const_iterator;
pair<iterator, bool> Insert(const K& key)
{
return _rb.Insert(key);
// pair<Node\*, bool> 给 pair<iterator, bool>
// pair的构造函数: template<class U, class V> pair (const pair<U,V>& pr);
// 这样就能通过一个pair去构造另外一个pair
// 所以返回来的pair<Node\*,bool> 就会对应给到要返回的pair<iterator,bool>的 iterator 和 bool 来进行构造
// 这样就能iterator避免内外的不一样,外部的是iterator其实是const\_iterator
}
pair<iterator, bool> Insert(const K& key)const
{
return _rb.Insert(key);//pair<iterator, bool>
}
//普通对象、const对象都能调用
iterator begin() const
{
return _rb.begin();
}
iterator end() const
{
return _rb.end();
}
private:
RBTree<K,K,Setofkey> _rb;
};
3.3map的实现
#pragma once
#include"RBTree.h"
template<class K,class V>
class MyMap
{
public:
//自己所写的适配器用来从pair中调出key值
struct Mapofkey
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
typedef typename RBTree<K, pair<const K, V>,Mapofkey>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, Mapofkey>::const_iterator const_iterator;
pair<iterator, bool> Insert(const pair<K,V>& kv)
{
return _rb.Insert(kv);
}
iterator begin()
{
return _rb.begin();
}
iterator end()
{
return _rb.end();
}
const_iterator begin() const
{
return _rb.begin();
}
const_iterator end() const
{
return _rb.end();
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = Insert(make\_pair(key,V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K,V>,Mapofkey> _rb;
};
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
{
return _rb.end();
}
const_iterator begin() const
{
return _rb.begin();
}
const_iterator end() const
{
return _rb.end();
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = Insert(make\_pair(key,V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K,V>,Mapofkey> _rb;
};
---
[外链图片转存中...(img-9Ji8Ev99-1715713212384)]
[外链图片转存中...(img-BURV05fc-1715713212384)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**