目录
一、set
1.什么是set
set就是STL中的普通搜索二叉树,它的底层结构是红黑树。
其中T表示储存元素的类型,Compare表示比较使用的仿函数,Alloc表示空间配置器,也就是我们每插入一个元素都需要它来开辟空间,绝大部分时候不需要我们传递。
2.set的使用
(1)默认成员函数
首先是构造函数和拷贝构造函数
explicit set (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); ———— 默认构造函数,可以构造一个空set
template
set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type()); ———— 按任意容器的迭代器区间构造
set (const set& x); ———— 拷贝构造
~set(); ———— 析构函数
set& operator= (const set& x); ———— 赋值运算符重载
(2)迭代器
iterator begin(); ———— 返回起始位置迭代器
const_iterator begin() const; ———— 返回起始位置const迭代器
iterator end(); ———— 返回末尾位置后一个的迭代器
const_iterator end() const; ———— 返回末尾位置后一个的const迭代器
reverse_iterator rbegin(); ———— 返回末尾位置反向迭代器
const_reverse_iterator rbegin() const; ———— 返回末尾位置反向const迭代器
reverse_iterator rend(); ———— 返回起始位置前一个的反向迭代器
const_reverse_iterator rend() const; ———— 返回起始位置前一个的反向const迭代器
再C++11中也支持了cbegin、cend、crbegin、crend返回const迭代器,对迭代器解引用得到value值,但是set中key和value值相同,所以最终间接得到key值。
(3)容量操作
bool empty() const; ———— 判断是否为空
size_t size() const; ———— 返回有效数据数
(4)操作函数
pair insert (const value_type& val); ———— 插入一个元素并返回一个键值对(键值对是一个类,有两个成员变量,在std命名空间中),插入成功则pair中的迭代器是当前节点,bool值为true,插入失败则迭代器为end(),bool值为false
template
void insert (InputIterator first, InputIterator last); ———— 插入任意容器迭代器区间的元素
size_t erase (const value_t& val); ———— 删除对应元素
void erase (iterator position); ———— 删除迭代器位置上的元素
void erase (iterator first, iterator last); ———— 删除set迭代器范围内的元素
void clear(); ———— 清空set
iterator find (const value_type& val) const; ———— 查找set中的元素
size_t count (const value_type& val) const; ———— 返回set中的val出现的次数,由于set中的内容不重复,所以它只能返回1和0
3.测试代码
#include<iostream>
#include<set>
using namespace std;
int main()
{
set<int> s;
s.insert(4);
s.insert(7);
s.insert(1);
s.insert(15);
s.insert(12);
s.insert(18);
s.insert(3);
set<int>::iterator it = s.begin();
for (auto e : s)
{
cout << *it << " ";
++it;
}
cout << endl;
set<int>::iterator pos = s.find(15);
cout << *pos << endl;
s.erase(15);
set<int>::iterator it1 = s.begin();
while (it1 != s.end())
{
cout << *it1++ << " ";
}
cout << endl;
cout << s.size() << endl;
cout << s.max_size() << endl;
s.clear();
set<int>::iterator it2 = s.begin();
while (it2 != s.end())
{
cout << *it2++ << " ";
}
cout << endl;
set<int> s1(s);
set<int>::iterator it3 = s1.begin();
for (auto e : s1)
{
cout << *it3 << " ";
it3++;
}
cout << endl;
return 0;
}
二、map
1.键值对
键值对是用来表示具有一一对应关系的一种结构,一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
在学习二叉搜索树时,我们通过储存值的大小决定插入位置在左还是右,这个存储的值就是键值key,如果我们将储存的元素变成一个键值对,参数就由改成了,修改前是子节点位置与值相关联,修改后就是key与value相关联,搜索树通过递归找到对应key值,同时内部储存的对应信息value也就被找到了,比如英译汉的词典中,你通过输入一个英文单词作为key值,搜索二叉树找到对应节点的键值对就找到了value,也就是汉语意思。
在set和map中都会涉及键值对pair,class Alloc = allocator<const Key, T>,这里的pair其实就是一个包含两个变量的结构体,前面的值叫first,后面的叫second,代表前后的模板参数,再std的命名空间中就存在这样一个数据结构。
需要注意:Key不可以被修改,T可以。
2.什么是map
map就是STL中的Key,Value结构的搜索二叉树,它的底层结构也是红黑树。它的每个元素是一个键值对,用户通过输入key,可以找到对应的value值。这里的key和value就不一样了,map就可以实现类似英汉字典的功能了。
与map类似,但是这里的空间配置器已经直接出现了pair,也出现了需要传递的key和value的类型Key和T
3.map的使用
map和set的成员函数可实现的功能基本一致,迭代器解引用返回的是key对应节点出差呢的键值对pair,不过map重载了[]运算符,我们重点讲讲这个函数。
mapped_type& operator[] (const key_type& k); ———— []运算符重载
先看看它的实现:
mapped_type& operator[] (const key_type& k)
{
return (*((this->insert(make_pair(k,mapped_type()))).first)).second
}
你一定想说,这都什么呀这是?
我知道你急,但是你先别急,请抑制住你砸电脑的心情,至少我们看到了insert函数,那就先从insert函数开始。
pair insert (const value_type& val); ———— 插入一个元素【这个元素可使用std命名空间中的make_pair(key, value)函数进行构造】并返回pair,插入成功则返回的pair的迭代器指向插入位置,bool值为true,插入失败则pair的迭代器为end(),bool值为false
这样就好理解了,(*(( this->insert(make_pair(k,mapped_type())) ).first)).second中:this->insert(make_pair(k,mapped_type())) 的部分返回一个pair变量,我们把这个pair命名为p1,现在就是:(*((p1).first)).second。
如果insert成功插入,p的first是插入位置的迭代器,将它命名为i,得到:(*(i)).second,迭代器解引用得到pair,命名这个pair为p2得到:p2.second,此时我们就理解了最后返回的就是map节点储存的value值。
如果insert插入失败,p的first是map中已经存在的这个位置的迭代器,应用同样的办法也返回了该节点储存的value值。
总结来说,如果向operator []中传递一个map中不存在的key,那么它就为我们插入一个key,value的pair,value是默认构造,而如果后面有赋值则直接赋值到value上;如果传递一个已经存在的元素,他就会给我们返回对应的value。
因为它有插入数据的特性,所以对这个函数的使用请务必小心。
测试代码:
#include<iostream>
#include<map>
#include<set>
using namespace std;
int main()
{
map<int, char> m;
m.insert(make_pair(1, 'a'));
m.insert(make_pair(2, 'b'));
m.insert(make_pair(3, 'c'));
m.insert(make_pair(4, 'd'));
cout << m[1] << endl;
cout << m[5] << endl;
for (auto e : m)
{
cout << e.first << "->" << e.second << endl;
}
return 0;
}
三、multiset和multimap
multiset和multimap的底层实现也是红黑树,相比普通的set和map最大的区别就是它的元素是可以重复的,对于它的在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。
因为一个key对应多个value,所以multimap中不存在[]重载。
测试代码:
#include<iostream>
#include<map>
#include<set>
using namespace std;
int main()
{
multimap<string, string> m;
m.insert(make_pair("poor", "可怜的"));
m.insert(make_pair("poor", "贫穷的"));
m.insert(make_pair("argue", "争论"));
m.insert(make_pair("argue", "论证"));
m.insert(make_pair("argue", "声称"));
for (auto e : m)
{
cout << e.first << "->" << e.second << endl;
}
return 0;
}
运行结果:
证明multimap可以建立一个key对应多个value的映射。
四、map和set的封装
1.改造红黑树变量和类型
我们之前就已经写过红黑树,现在我们需要把他改变为更普适性的版本。
我们原来插入的元素都是pair,把它改为T,成员变量_kv改为_data。
当然,下面的class RBTree中的内容也要改,value的类型改为T,加了一个KeyOf用于根据T找到它对应的Key(你只储存data,也就是value,所以缺乏一个获取Key的方式)。
template<class T>
struct RBTreeNode
{
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
RBTreeNode(const T& data)
:_data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED)
{}
};
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
//各个成员函数
private:
Node* _root = nullptr;
};
2.增加迭代器
新建一个迭代器类,用Ref和Ptr两个模板类型规定iterator和const_iterator
迭代器内部储存一个Node*指针,而且迭代器的++(加加)按照中序遍历的方式访问,--(减减)按中序遍历的反方向访问。
对于++重载,由于遍历方式是中序遍历,所以在迭代器锁定在某一个位置时,它一定遍历完了左树和根节点,它就需要开始遍历右树,而开始遍历右树需要先从右树的最左节点开始,所以先向右走再一直向左走到头即可。不过还有一种情况,如果右树为空,那么这个子树实际上已经遍历完了,那么我们就需要向上走,向上走一个节点后如果这个节点是上一个节点的右子树根,那么证明上一层的子树也已经遍历完成,那就继续向上进,直到这个节点是上一个节点的左子树根,此时就证明它上一个节点的左子树遍历完成,需要走到它的根上。
对于--重载也很相似,锁定某一位置时它左子树遍历完成,那么就需要走到左子树的最右节点。如果左树为空同样向上走直到这个节点是上一个节点的右子树,走到父节点上即可。
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
typedef __RBTreeIterator<T, T&, T*> iterator;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
__RBTreeIterator(const iterator& s)
:_node(s._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
if (_node->_right)
{
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
Node* max = _node->_left;
while (max->_right)
{
max = max->_right;
}
_node = max;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
3.修改成员函数
一个是配套的迭代器和相关函数,另一个是成员函数对原来使用pair的部分进行修改,比如说KeyOfT仿函数定义对象来获取Key,以下为修改结果:
enum Colour
{
RED,
BLACK,
};
template<class T>
struct RBTreeNode
{
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
RBTreeNode(const T& data)
:_data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED)
{}
};
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
typedef __RBTreeIterator<T, T&, T*> iterator;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
__RBTreeIterator(const iterator& s)
:_node(s._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
if (_node->_right)
{
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
Node* max = _node->_left;
while (max->_right)
{
max = max->_right;
}
_node = max;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __RBTreeIterator<T, T&, T*> iterator;
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
iterator begin()
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin() const
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return const_iterator(left);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
pair<iterator, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root), true);
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(iterator(cur), false);
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent && parent->_col == RED)
{
Node* grandfater = parent->_parent;
if (parent == grandfater->_left)
{
Node* uncle = grandfater->_right;
// 情况一 uncle存在且为红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
cur = grandfater;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// 情况二
RotateR(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// 情况三
RotateL(parent);
RotateR(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
else // (parent == grandfater->_right)
{
Node* uncle = grandfater->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
cur = grandfater;
parent = cur->_parent;
}
else
{
// g
// p
// c
if (cur == parent->_right)
{
RotateL(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// g
// p
// c
RotateR(parent);
RotateL(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
}
void Inorder()
{
_Inorder(_root);
}
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Inorder(root->_right);
}
bool Check(Node* root, int blackNum, const int ref)
{
if (root == nullptr)
{
if (blackNum != ref)
{
cout << "违反规则:本条路径的黑色节点的数量跟最左路径不相等" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "违反规则:出现连续红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
++blackNum;
}
return Check(root->_left, blackNum, ref)
&& Check(root->_right, blackNum, ref);
}
bool IsBalance()
{
if (_root == nullptr)
{
return true;
}
if (_root->_col != BLACK)
{
return false;
}
int ref = 0;
Node* left = _root;
while (left)
{
if (left->_col == BLACK)
{
++ref;
}
left = left->_left;
}
return Check(_root, 0, ref);
}
iterator find(const T& data)
{
return _find(data, _root);
}
iterator _find(const T& data, Node* root)
{
if (root == nullptr)
return false;
if (data < root->_data)
_find(data, root->_left);
else if (data < root->_data)
_find(data, root->_right);
else
return iterator(root);
}
private:
Node* _root = nullptr;
};
template<class K>
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
4.建立map和set的头文件
set<K>在底层就是:RBTree<K, K, SetKeyOfT> _t,也就是Key和value都是key,SetKeyOfT直接返回value就是返回key
set.h
#pragma once
#include "RBTree.h"
namespace set
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
iterator begin() const
{
return _t.begin();
}
iterator end() const
{
return _t.end();
}
pair<iterator, bool> insert(const K& key)
{
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
return pair<iterator, bool>(ret.first, ret.second);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
map<K, T>在底层就是:RBTree<K, pair, MapKeyOfT> _t,K是key,而value是一个pair,MapKeyOfT直接返回value键值对。
map.h
#pragma once
#include "RBTree.h"
namespace map
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.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();
}
pair<iterator, bool> insert(const pair<const K, V>& kv)
{
return _t.Insert(kv);
}
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>, MapKeyOfT> _t;
};
}
5.测试代码
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>
using namespace std;
int main()
{
multimap<string, string> dict;
dict.insert(make_pair("left", "左边"));
dict.insert(make_pair("left", "剩余"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("left", "xxx"));
for (const auto& kv : dict)
{
cout << kv.first << ":" << kv.second << endl;
}
string arr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
multimap<string, int> countMap;
for (auto& e : arr)
{
auto it = countMap.find(e);
if (it == countMap.end())
{
countMap.insert(make_pair(e, 1));
}
else
{
it->second++;
}
}
for (const auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
return 0;
}