容器有序列式容器和关联式容器
序列式容器有 : vector, string, list, deque, 等等
关联式容器有 : map / multimap, set / multimap
关联式容器有树形结构和哈希结构, 今天我们主要说的是树形结构, 后面我们会给出哈希结构
表示具有一一对应关系的一种结构, 一般包含key 和 value , key代表键值, value 代表与key对应的信息.
一般计做 pair
SGI-STL中关于键值对的定义 :
template
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)
{}
};
map的文档简介
map的性质 :
1. 按照特定的次序 (按照key来比较) 存储键值对 pair
.
2. 在map中,key通常用于排序和唯一地标识元素,value中存储与键值key关联的内容。键值key和值value的类型可能不同
3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map中通过键值访问单个元素的速度通常比unordered_map容器(由哈希表实现的无序Map, 后序我们会实现)慢,但对map中的元素进行迭代时,可以得到一个有序的序列
5. map支持下标访问符
6. map底层通常用RBTree实现
例如 :
void TestMap()
{
// key和value的类型都给成字符串
map m1;
// C++11 的类表初始化
map m2{ { "apple", "苹果" },
{ "banan", "香蕉" },
{ "orange", "橘子" },
{ "peach", "桃子" },
{ "waterme", "水蜜桃" } };
cout << m2["apple"] << endl;
cout << m2["waterme"] << endl;
map m3(m2);
//key和value的类型都给成int
map M;
M.insert(pair(1,5));
M.insert(make_pair(1,5));
//key和value的类型不相同
map M1;
M1.insert(pair("nihao",1));
}
set的文档简介
set的性质
1. set是按照一定次序存储元素的容器, 不同的是set中只放value, 没有key.
2. 在set中,每个value必须是唯一的。set中的元素不能在容器中修改, 但是可以从容器中插入或删除它们。
3. 在内部,set中的元素总是按照其内部的特定严格弱排序准则进行排序。
4. set容器通过key访问单个元素的速度通常比unordered_set容器慢
5. set底层也是RBTree
例如 :
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 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;
}
1. 与map/multimap不同,map/multimap中存储的是真正的键值对
,set中只放value,但在底层实际存放的是由 构成的键值对。
2. set中插入元素时,只需要插入value即可,不需要构造键值对。
3. set中的元素不可以重复(因此可以使用set进行去重)。
4. set中的元素默认按照小于来比较
我们在这里不过多对map 和 set 的应用做解释, 大家可以打开上面的文档看一看, 很简单.
RBTree.hpp
#include
#include
using namespace std;
enum Color
{
Red,
Black
};
template
struct RBTNode
{
RBTNode(const V& data = V())
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _color(Red)
{}
RBTNode* _left;
RBTNode* _right;
RBTNode* _parent;
V _data;
Color _color;
};
template
class _RBTreeIterator
{
//封装红黑树节点
public:
typedef RBTNode Node;
typedef Node* pNode;
typedef _RBTreeIterator Self;
_RBTreeIterator(pNode node)
:_node(node)
{}
V& operator*()
{
return _node->_data;
}
V* operator->()
{
return &_node->_data;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
Self& operator++()
{
//1. 右子树存在 : 右子树的最左节点
//2. 右子树不存在 : 向上回溯
// 当前节点在parent的左边/_node->_parent != parent, 直接等于parent
// 当前节点在parent的右边, 向上调整, 直到_node不为parent的右边为止
if (_node->_right != nullptr)
{
_node = _node->_right;
while (_node->_left)
{
_node = _node->_left;
}
}
else
{
pNode parent = _node->_parent;
while (parent->_right == _node)
{
_node = parent;
parent = parent->_parent;
}
if (_node->_right != parent)
_node = parent;
}
return *this;
}
private:
pNode _node;
};
//K,V : pair
//KeyOfValue : 获取对应数据的Key
template
class RBTree
{
public:
typedef RBTNode Node;
typedef Node* pNode;
typedef _RBTreeIterator iterator;
RBTree(const V& data = V())
{
_header = new Node(data);
_header->_left = _header;
_header->_right = _header;
_header->_parent = nullptr;
}
iterator begin()
{
return iterator(_header->_left);
}
iterator end()
{
return iterator(_header);
}
pair Insert(const V& data)
{
//判断是否是空树
if (_header->_parent == nullptr)
{
//创建根节点
pNode root = new Node(data);
//根节点的颜色是黑色
root->_color = Black;
//更新双向链接关系,根的父亲, 头的三个指向
root->_parent = _header;
_header->_parent = root;
_header->_left = root;
_header->_right = root;
return make_pair(iterator(root),true);
}
//搜索合适的位置
pNode cur = _header->_parent;
pNode parent = nullptr;
KeyOfValue kov;
while (cur)
{
//更新父节点
parent = cur;
//如果当前K大于要找的K(键值对), 向左
if (kov(cur->_data) > kov(data))
cur = cur->_left;
else if (kov(cur->_data) < kov(data))
cur = cur->_right;
else
return make_pair(iterator(cur),false);
//相等, 插入失败
}
//创建一个新的节点, 进行双向链接(通过K值的关系)
cur = new Node(data);
pNode newNode = cur;
if (kov(parent->_data) > kov(data))
{
parent->_left = cur;
}
else
parent->_right = cur;
cur->_parent = parent;
//调整 : 修改颜色, 旋转
while (cur != _header->_parent && cur->_parent->_color == Red)
{
pNode parent = cur->_parent;
pNode gparent = parent->_parent;
//0.判断父亲在祖父的哪边
//1.叔叔存在而且是红色
// 直接修改颜色, 向上更新
//2.叔叔不存在/存在而且是黑的(旋转, 结束之后调整颜色,不在向上更新)
// cur与parent不是同向, 双旋转, parent在左-->先左旋(之后交换)再右旋
// cur与parent是同向, 单旋转, 左左->右单旋, 右右->左单旋
if (gparent->_left == parent)
{
pNode uncle = gparent->_right;
if (uncle && uncle->_color == Red)
{
//调整颜色
parent->_color = uncle->_color = Black;
gparent->_color = Red;
//向上更新
cur = gparent;
}
else
{
if (parent->_right == cur)
{
RotateLeft(parent);
swap(cur, parent);
}
//右旋
RotateRight(gparent);
//调整颜色使得满足条件
parent->_color = Black;
gparent->_color = Red;
break;
}
}
else
{
pNode uncle = gparent->_left;
if (uncle && uncle->_color == Red)
{
parent->_color = uncle->_color = Black;
gparent->_color = Red;
cur = gparent;
}
else
{
if (parent->_left == cur)
{
RotateRight(parent);
swap(cur, parent);
}
RotateLeft(gparent);
parent->_color = Black;
gparent->_color = Red;
break;
}
}
}
//根节点的颜色必须为黑色
_header->_parent->_color = Black;
//调整之后根可能发生变化
_header->_left = leftMost();
_header->_right = rightMost();
return make_pair(iterator(newNode), true);
}
iterator Find(const K& key)
{
pNode cur = _header->_parent;
KeyOfValue kov;
while (cur)
{
if (key == kov(cur->_data))
return iterator(cur);
else if (key < kov(cur->_data))
cur = cur->_left;
else
cur = cur->_right;
}
// 返回end的位置
return iterator(_header);
}
pNode leftMost()
{
//从根节点开始
pNode cur = _header->_parent;
//一直向左边走
while (cur && cur->_left)
{
cur = cur->_left;
}
return cur;
}
pNode rightMost()
{
//从根节点开始
pNode cur = _header->_parent;
//一直向右边走
while (cur && cur->_right)
{
cur = cur->_right;
}
return cur;
}
void RotateLeft(pNode parent)
{
//左旋
pNode subR = parent->_right;
pNode subRL = subR->_left;
//改变链接关系
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
if (parent == _header->_parent)
{
_header->_parent = subR;
subR->_parent = _header;
}
else
{
pNode pparent = parent->_parent;
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
parent->_parent = subR;
}
void RotateRight(pNode parent)
{
//右旋, 拿到左子树和左子树的右孩子
pNode subL = parent->_left;
pNode subLR = subL->_right;
//改变链接关系
subL->_right = parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
if (parent == _header->_parent)
{
_header->_parent = subL;
subL->_parent = _header;
}
else
{
if (parent->_parent->_left == parent)
{
parent->_parent->_left = subL;
}
else
parent->_parent->_right = subL;
subL->_parent = parent->_parent;
}
parent->_parent = subL;
}
private:
pNode _header;
};
MapSet.cpp
#include "RBTree.hpp"
template
class Map
{
struct MapKeyOfValue
{
//重载()运算符, 仿函数
const K& operator()(const pair& data)
{
return data.first;
}
};
public:
typedef typename RBTree, MapKeyOfValue>::iterator iterator;
iterator begin()
{
return _rbt.begin();
}
iterator end()
{
return _rbt.end();
}
pair Insert(const pair& data)
{
return _rbt.Insert(data);
}
/*
operator[]的原理是:
用构造一个键值对,然后调用insert()函数将该键值对插入到map中
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
operator[]函数最后将insert返回值键值对中的value返回
*/
V& operator[](const K& key)
{
pair ret = _rbt.Insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree, MapKeyOfValue> _rbt;
};
template
class Set
{
struct SetKeyOfValue
{
const K& operator()(const K& data)
{
return data;
}
};
public:
typedef typename RBTree::iterator iterator;
pair Insert(const K& data)
{
return _rbt.Insert(data);
}
iterator begin()
{
return _rbt.begin();
}
iterator end()
{
return _rbt.end();
}
iterator Find(const K& key)
{
return _rbt.Find(key);
}
private:
RBTree _rbt;
};
void test()
{
//测试用例
Map M;
M.Insert(make_pair(10, 1));
M.Insert(make_pair(3, 1));
M.Insert(make_pair(9, 1));
M.Insert(make_pair(2, 1));
M.Insert(make_pair(1, 1));
for (auto e : M)
{
cout << e.first << "--->" << e.second << endl;
}
M[1] = 100;
M[50] = 50;
for (auto e : M)
{
cout << e.first << "--->" << e.second << endl;
}
Set s;
s.Insert(1);
s.Insert(3);
s.Insert(9);
s.Insert(2);
s.Insert(1);
for (auto e : s)
{
cout << e << "--->" << e << endl;
}
}
int main()
{
test();
system("pause");
return 0;
}