目录
1.map和set的应用
map和set的使用通过查找相关文档即可熟悉。这里给出几道习题用于熟悉map和set的用法:
692. 前K个高频单词 使用map解题
KY264 单词识别 使用map解题
160. 相交链表 使用set解题
138. 复制带随机指针的链表 使用map解题
class Solution {
public:
Node* copyRandomList(Node* head) {
map<Node*,Node*> m;//map映射旧节点和新节点
Node* cur = head;
while(cur)
{
m[cur] = new Node(cur->val);//新节点使用旧节点的val值创建
cur = cur->next;
}
cur = head;
while(cur)
{
m[cur]->next = m[cur->next];//新节点的next指针是旧节点对应的v模型
m[cur]->random = m[cur->random];//新节点的random指针是旧节点对应的v模型
cur = cur->next;
}
return m[head];
}
};
2.红黑树的封装(部分功能)
namespace ly
{
enum Corlor //枚举颜色
{
RED, BLACK
};
template <class KV> //红黑树的节点并不知道KV到底是什么
struct RBTreeNode
{
RBTreeNode<KV>* _left;
RBTreeNode<KV>* _right;
RBTreeNode<KV>* _parent;
KV _data;
Corlor _cor;
RBTreeNode(const KV& data)
:_left(nullptr), _right(nullptr), _parent(nullptr),
_data(data), _cor(RED)
{}
};
template<class KV,class Ref,class Ptr>//迭代器是指向某一节点的,所以其模板参数为KV
struct RBTreeIterator//也就说迭代器也不知道指向的KV是什么
{
typedef RBTreeNode<KV> Node;
typedef RBTreeIterator<KV, Ref, Ptr> Self;
Node* _node;
RBTreeIterator(Node* node)
:_node(node)
{}
Ref operator*()//解引用就是返回一个KV的引用。至于怎么用,由调用的用户决定
{
return _node->_data;
}
Ptr operator->()//同理
{
return &_node->_data;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
// 如何实现迭代器++?
Self& operator++()
{
if (_node->_right)//情况1,如果当前节点的右子树不为空
{
//很明显,下一个节点将是右子树中最左节点
Node* min = _node->_right;
while (min && min->_left)
{
min = min->_left;
}
_node = min;
}
else//情况2,当前节点的右子树为空
{
//那么迭代器就要往上找
//中序遍历的顺序为 左 根 右
//也就是说,如果当前节点是其父节点的左孩子
//那么下一个位置其父节点
//如果当前节点是其父节点的右孩子
//说明中序遍历已经走完了,需要重复遍历
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && parent->_right == cur)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
};
//使用三个参数兼容set和map使用一份红黑树
//第一个参数可用作查找
//第二个参数才是红黑树真正放的节点(要不为K,要不为KV)
//第三个参数是从KV提取K的仿函数
template <class K, class KV, class KOfKV>
class RBTree
{
public:
typedef RBTreeNode<KV> Node;
typedef RBTreeIterator<KV, KV&, KV*> iterator;//普通迭代器
typedef RBTreeIterator<KV, const KV&, const KV*> const_iterator;//const迭代器
iterator begin()
{
//最左节点便是第一个节点
//也就是begin指向的节点
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
iterator end()
{
//以空作为end迭代器
return iterator(nullptr);
}
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);
}
//insert返回键值对
//注意,这里返回的迭代器都是普通迭代器
pair<iterator,bool> insert(const KV& data)
{
KOfKV kof;//仿函数对象,负责提取KV中的K
if (_root == nullptr)
{
_root = new Node(data);
_root->_cor = BLACK; //根节点的颜色必须是黑色
return make_pair(_root, true);//插入成功second设为true
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kof(data) < kof(cur->_data))
{
parent = cur;
cur = cur->_left;
}
else if (kof(data) > kof(cur->_data))
{
parent = cur;
cur = cur->_right;
}
else
{
return make_pair(cur,false);//存在则设为false
}
}
cur = new Node(data);
Node* ret = cur;//因为等会cur的位置会变,这里先记录一下
cur->_cor = RED; //新插入的节点颜色为红色
if (kof(data) < kof(parent->_data))
{
parent->_left = cur;
cur->_parent = parent;
}
else if (kof(data) > kof(parent->_data))
{
parent->_right = cur;
cur->_parent = parent;
}
// 此时如果新插入的节点破坏了红黑树的性质,就必须做一些微调
while (parent && parent->_cor == RED)
{
// 调整动作
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_cor == RED)
{
// 情况1
parent->_cor = uncle->_cor = BLACK;
grandfather->_cor = RED;
cur = grandfather;
parent = cur->_parent;
}
else // unle不存在或uncle存在且为黑
{
if (parent->_left == cur)
{
// 情况2
RotateR(grandfather);
parent->_cor = BLACK;
grandfather->_cor = RED;
}
else if (parent->_right == cur)
{
// 情况3
RotateL(parent);
RotateR(grandfather);
cur->_cor = BLACK;
grandfather->_cor = RED;
}
break; //关键
}
}
else if (grandfather->_right == parent) //镜像即可
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_cor == RED)
{
parent->_cor = uncle->_cor = BLACK;
grandfather->_cor = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (parent->_right == cur)
{
// 情况2
RotateL(grandfather);
parent->_cor = BLACK;
grandfather->_cor = RED;
}
else if (parent->_left == cur)
{
// 情况3
RotateR(parent);
RotateL(grandfather);
cur->_cor = BLACK;
grandfather->_cor = RED;
}
break; //关键
}
}
}
_root->_cor = BLACK; //确保根节点颜色为黑色
return make_pair(ret,true);
}
private:
Node* _root = nullptr;
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curL = cur->_left; //cur的左树
parent->_right = curL; //cur的左树变成parent的右树
if (curL)
{
curL->_parent = parent;
}
Node* oldParent = parent->_parent; //记录parent的父节点
parent->_parent = cur; //cur作为parent的父节点
cur->_left = parent; //parent作为cur的左树
if (oldParent == nullptr)
{
_root = cur; //直接让cur作为根节点(因为parent的旧父节点为空)
cur->_parent = nullptr;
}
else
{
if (oldParent->_left == parent)
{
oldParent->_left = cur;
cur->_parent = oldParent;
}
else if (oldParent->_right == parent)
{
oldParent->_right = cur;
cur->_parent = oldParent;
}
}
}
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curR = cur->_right;
parent->_left = curR; //cur的右树作为parent的左树
if (curR)
{
curR->_parent = parent;
}
Node* oldParent = parent->_parent;
parent->_parent = cur;
cur->_right = parent; //parent作为cur的右树
if (oldParent == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (oldParent->_left == parent)
{
oldParent->_left = cur;
cur->_parent = oldParent;
}
else if (oldParent->_right == parent)
{
oldParent->_right = cur;
cur->_parent = oldParent;
}
}
}
};
}
解析:迭代器的++算法。这里也有一道对应力扣的题:面试题 04.06. 后继者
3.map的封装(部分功能)
#include "RBTree.h"
namespace ly
{
template<class K, class V>
class map
{
public:
struct KeyOfMap//提供给红黑树的仿函数
{
K operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
//加上typename表示是某类中的类型而非静态变量
typedef typename RBTree<K, pair<const K, V>, KeyOfMap>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, KeyOfMap>::const_iterator const_iterator;
iterator begin()
{
return _rb.begin();
}
iterator end()
{
return _rb.end();
}
const_iterator begin() const
{
return _rb.begin();
}
const_iterator end() const
{
return _rb.end();
}
pair<iterator, bool> insert(const pair<const K, V>& kv)
{
return _rb.insert(kv);
}
V& operator[](const K& k)
{
//只通过insert接口实现[]运算符重载
//1.先使用临时变量接收insert的返回值
pair<iterator, bool> p = insert(make_pair(k, V()));
//2.p中的first就是迭代器,指向了insert的节点
return p.first->second;
}
private:
RBTree<K, pair<const K, V>, KeyOfMap> _rb;//红黑树对象
};
}
4.set的封装(部分功能)
#include "RBTree.h"
namespace ly
{
template<class K>
class set
{
public:
struct KeyOfSet//提供给红黑树的仿函数
{
K operator()(const K& k)
{
return k;
}
};
//加上typename表示是某类中的类型而非静态变量
//需要注意,通常我们不希望改变set中K值
//所以set使用的迭代器都是const迭代器
typedef typename RBTree<K, K, KeyOfSet>::const_iterator iterator;
typedef typename RBTree<K, K, KeyOfSet>::const_iterator const_iterator;
iterator begin() const
{
return _rb.begin();
}
iterator end() const
{
return _rb.end();
}
pair<iterator, bool> insert(const K& k)
{
/*又因为set使用的const迭代器
而底层红黑树的insert接口返回的是普通迭代器
而普通迭代器是不能类型转换成const迭代器的*/
//return _rb.insert(k);
//所以需要在底层的迭代器上做更改
//使得普通迭代器能够构造出const迭代器
//所以,第一步:先使用普通迭代器的pair对象接收返回值
pair<typename RBTree<K, K, KeyOfSet>::const_iterator, bool> p = _rb.insert(k);
//第二步:再用普通迭代器构造出const迭代器
return pair<iterator, bool>(p.first, p.second);
}
private:
RBTree<K, K, KeyOfSet> _rb;//红黑树对象
};
}
修改后的迭代器模块:
template<class KV,class Ref,class Ptr>//迭代器是指向某一节点的,所以其模板参数为KV
struct RBTreeIterator//也就说迭代器也不知道指向的KV是什么
{
typedef RBTreeNode<KV> Node;
typedef RBTreeIterator<KV, Ref, Ptr> Self;
//普通迭代器
typedef RBTreeIterator<KV, KV&, KV*> iterator;
Node* _node;
RBTreeIterator(Node* node)
:_node(node)
{}
//构造函数接收一个普通迭代器作为参数
RBTreeIterator(const iterator& it)
:_node(it._node)
{}
//...............省略.............
};
5.完成代码
https://gitee.com/AVine/AVine_Code/tree/master/3_15%20%E5%B0%81%E8%A3%85map%E5%92%8Cset