【c++】红黑树模拟实现Map / Set

关联式容器

    容器有序列式容器和关联式容器

        序列式容器有 : 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的文档简介

    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的文档简介

    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;
}

 

map和set的区别

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;
}

 

你可能感兴趣的:(c++,关联式容器,c++,map,set)