C++:set和map(模拟实现)

目录

关联式容器

键值对

树形结构的关联式容器

set的介绍

set的使用

map的介绍

map的使用

multiset的介绍

multimap的介绍

底层结构

AVL树的概念

AVL树节点的定义

AVL树的旋转

左单旋

右单旋

先右单旋再左单旋

先左单旋再右单旋

模拟实现AVL树

红黑树

红黑树的概念

红黑树的插入操作

情况一: cur为红,p为红,g为黑,u存在且为红

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

红黑树的模拟实现

封装map和set


关联式容器

在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面 存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别? 关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高。

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代 表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然 有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应 该单词,在词典中就可以找到与其对应的中文含义。

template <class T1, class T2>
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)
    {}
}

树形结构的关联式容器

根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结 构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使 用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一 个容器。

set的介绍

1. set是按照一定次序存储元素的容器

2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。 set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。

3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行 排序。

4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对 子集进行直接迭代。

5. set在底层是用二叉搜索树(红黑树)实现的。

注意:

1. 与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对。

2. set中插入元素时,只需要插入value即可,不需要构造键值对。

3. set中的元素不可以重复(因此可以使用set进行去重)。

4. 使用set的迭代器遍历set中的元素,可以得到有序序列

5. set中的元素默认按照小于来比较

6. set中查找某个元素,时间复杂度为:$log_2 n$

7. set中的元素不允许修改(为什么?)

8. set中的底层使用二叉搜索树(红黑树)来实现。

set的使用

set的使用见:https://cplusplus.com/

map的介绍

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元 素。

2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的 内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型 value_type绑定在一起,为其取别名称为pair: typedef pair value_type;

3. 在内部,map中的元素总是按照键值key进行比较排序的。

4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序 对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。

5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。

6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

map的使用

map的使用见:https://cplusplus.com/

multiset的介绍

1. multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。

2. 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是组成 的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器 中进行修改(因为元素总是const的),但可以从容器中插入或删除。

3. 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则 进行排序。

4. multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭 代器遍历时会得到一个有序序列。

5. multiset底层结构为二叉搜索树(红黑树)。

注意:

1. multiset中再底层中存储的是的键值对

2. mtltiset的插入接口中只需要插入即可

3. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的

4. 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列

5. multiset中的元素不能修改

6. 在multiset中找某个元素,时间复杂度为$O(log_2 N)$

7. multiset的作用:可以对元素进行排序

multimap的介绍

1. Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对,其中多个键值对之间的key是可以重复的。

2. 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内 容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起, value_type是组合key和value的键值对: typedef pair value_type;

3. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对 key进行排序的。

4. multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代 器直接遍历multimap中的元素可以得到关于key有序的序列。

5. multimap在底层用二叉搜索树(红黑树)来实现。

注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以 重复的。

底层结构

前面对map/multimap/set/multiset进行了简单的介绍,在其文档介绍中发现,这几个容器有个 共同点是:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中 插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此 map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

它的左右子树都是AVL树

左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 $O(log_2 n)$,搜索时间复杂度O($log_2 n$)。

AVL树节点的定义

template<class T>
struct AVLTreeNode
{
 AVLTreeNode(const T& data)
     : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
 , _data(data), _bf(0)
 {}
 AVLTreeNode<T>* _pLeft;   // 该节点的左孩子
 AVLTreeNode<T>* _pRight;  // 该节点的右孩子
 AVLTreeNode<T>* _pParent; // 该节点的双亲
 T _data;
 int _bf;                  // 该节点的平衡因子
};

AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构, 使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

左单旋

值得注意的是,这里的h可以为0,也就是说a/b/c可以为空。 

//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* pparent = parent->_parent;
		Node* subRL = subR->_left;
		if (pparent)
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
			}
			else
			{
				pparent->_right = subR;
			}
		}
		else
		{
			_root = subR;
		}
		if (subRL)
		{
			subRL->_parent = parent;
		}
		parent->_right = subRL;
		parent->_parent = subR;
		subR->_parent = pparent;
		subR->_left = parent;
		parent->_bf = subR->_bf = 0;
	}

右单旋

//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* pparent = parent->_parent;
		Node* subLR = subL->_right;
		if (pparent)
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
		}
		else
		{
			_root = subL;
		}
		subL->_parent = pparent;

		if (subLR)
		{
			subLR->_parent = parent;
		}
		parent->_left = subLR;
		parent->_parent = subL;
		subL->_right = parent;
		parent->_bf = subL->_bf = 0;
	}

先右单旋再左单旋

//右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(subR);
		RotateL(parent);

		if (-1 == bf)
		{
			subR->_bf = 1;
		}
		else if (1 == bf)
		{
			parent->_bf = -1;
		}
	}

先左单旋再右单旋

 

//左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateL(subL);
		RotateR(parent);

		if (-1 == bf)
		{
			parent->_bf = 1;
		}
		else if (1 == bf)
		{
			subL->_bf = -1;
		}
	}

模拟实现AVL树

AVL树的平衡因子是向上更新的, 是否继续更新依据:子树的高度是否变化

1、parent->_bf == 0说明之前parent->_bf是 1 或者 -1

说明之前parent一边高一边低,这次插入填上矮的那边,parent所在子树高度不变,不需要继续往上更新

 2、parent->_bf == 1 或 -1 说明之前是parent->_bf == 0,两边一样高,现在插入一边更高了,

 parent所在子树高度变了,继续往上更新

 3、parent->_bf == 2 或 -2,说明之前parent->_bf == 1 或者 -1,现在插入严重不平衡,违反规则

旋转的目的:

1、让这颗子树左右高度不超过1

2、旋转过程中继续保持他是搜索树

3、更新调整孩子节点的平衡因子

4、让这颗子树的高度跟插入前保持一致

#pragma once
#include<iostream>
#include<utility>
#include<assert.h>
using namespace std;


template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode(const pair<K, V>& kv)
		:_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
		,_kv(kv)
		,_bf(0)
	{}

	AVLTreeNode<K, V>* _parent;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;

	pair<K, V> _kv;
	int _bf;//balance  factor
};



template<class K, class V>
class AVLTree
{
public:
	typedef AVLTreeNode<K, V>  Node;
	AVLTree()
		:_root(nullptr)
	{}

	bool insert(const pair<K, V>& kv)
	{
		if (nullptr == _root)
		{
			Node* newNode = new Node(kv);
			_root = newNode;
			return true;
		}

		Node* cur = _root;
		Node* parent = cur->_parent;
		while (cur)
		{
			parent = cur;
			if (kv.first < cur->_kv.first)
			{
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)
			{
				cur = cur->_right;
			}
			else
			{
				return false;;
			}
		}
		cur = new Node(kv);
		if (kv.first < parent->_kv.first)
		{
			parent->_left = cur;
		}
		else if (kv.first > parent->_kv.first)
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		while (parent)
		{
			if (parent->_left == cur)
			{
				--(parent->_bf);
			}
			else
			{
				++(parent->_bf);
			}

            // 是否继续更新依据:子树的高度是否变化
			// 1、parent->_bf == 0说明之前parent->_bf是 1 或者 -1
			// 说明之前parent一边高一边低,这次插入填上矮的那边,parent所在子树高度不变,不需要继续往上更新
			// 2、parent->_bf == 1 或 -1 说明之前是parent->_bf == 0,两边一样高,现在插入一边更高了,
			// parent所在子树高度变了,继续往上更新
			// 3、parent->_bf == 2 或 -2,说明之前parent->_bf == 1 或者 -1,现在插入严重不平衡,违反规则
			// 就地处理--旋转

			// 旋转:
			// 1、让这颗子树左右高度不超过1
			// 2、旋转过程中继续保持他是搜索树
			// 3、更新调整孩子节点的平衡因子
			// 4、让这颗子树的高度跟插入前保持一致

			if (0 == parent->_bf)//当前树的高度并未增加
			{
				return true;
			}
			else if(1 == parent->_bf || -1 == parent->_bf)//当前树的高度+1
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (2 == parent->_bf || -2 == parent->_bf)//左右树高度差为2,需要旋转
			{
				if (parent->_bf == 2 && parent->_right->_bf == 1)//左单旋
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && parent->_left->_bf == -1)//右单旋
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && parent->_right->_bf == -1)//右左双旋
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && parent->_left->_bf == 1)//左右双旋
				{
					RotateLR(parent);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* pparent = parent->_parent;
		Node* subRL = subR->_left;
		if (pparent)
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
			}
			else
			{
				pparent->_right = subR;
			}
		}
		else
		{
			_root = subR;
		}
		if (subRL)
		{
			subRL->_parent = parent;
		}
		parent->_right = subRL;
		parent->_parent = subR;
		subR->_parent = pparent;
		subR->_left = parent;
		parent->_bf = subR->_bf = 0;
	}



	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* pparent = parent->_parent;
		Node* subLR = subL->_right;
		if (pparent)
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
		}
		else
		{
			_root = subL;
		}
		subL->_parent = pparent;

		if (subLR)
		{
			subLR->_parent = parent;
		}
		parent->_left = subLR;
		parent->_parent = subL;
		subL->_right = parent;
		parent->_bf = subL->_bf = 0;
	}


	//右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(subR);
		RotateL(parent);

		if (-1 == bf)
		{
			subR->_bf = 1;
		}
		else if (1 == bf)
		{
			parent->_bf = -1;
		}
	}

	//左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateL(subL);
		RotateR(parent);

		if (-1 == bf)
		{
			parent->_bf = 1;
		}
		else if (1 == bf)
		{
			subL->_bf = -1;
		}
	}

	void Inorder()
	{
		_Inorder(_root);
	}

	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int lh = Height(root->_left);
		int rh = Height(root->_right);

		return lh > rh ? lh + 1 : rh + 1;
	}

	bool IsBalance()
	{
		return _IsBalance(_root);
	}

	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		if (rightHeight - leftHeight != root->_bf)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}

		return abs(rightHeight - leftHeight) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}


private:
	void _Inorder(Node* root)
	{
		if (nullptr == root)
			return;

		_Inorder(root->_left);
		cout << root->_kv.first << " : " << root->_kv.second << endl;
		_Inorder(root->_right);
	}

	Node* _root;
};

测试代码:

#include"AVLTree.h"


//void TestAVLTree()
//{
//	//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
//	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
//	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
//	AVLTree<int, int> t;
//	for (auto e : a)
//	{
//		t.insert(make_pair(e, e));
//	}
//
//	t.Inorder();
//
//	cout << t.IsBalance() << endl;
//}


void TestAVLTree()
{
	srand(time(0));
	const size_t N = 1000;
	AVLTree<int, int> t;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand();
		t.insert(make_pair(x, x));
		//cout << t.IsBalance() << endl;
	}

	t.Inorder();

	cout << t.IsBalance() << endl;
}


int main()
{
	TestAVLTree();
	return 0;
}

红黑树

红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍,因而是接近平衡的。

 红黑树的性质:

1. 每个结点不是红色就是黑色

2. 根节点是黑色的 

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 

4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点 

5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

注意:第三、四条性质是在插入过程中需要严格遵守的。

红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点

2. 检测新节点插入后,红黑树的性质是否造到破坏

新节点的默认颜色是红色!

新节点的默认颜色是红色!!

新节点的默认颜色是红色!!!

因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整:

 如图,在abcde中任意一个位置插入,其父节点的颜色为黑,没有违反任何规则,不需要进行调整,插入完成。

但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连 在一起的红色节点,此时需要对红黑树分情况来讨论:

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

情况一: cur为红,p为红,g为黑,u存在且为红

有四种插入位置,但是处理 方法都一样,仅以第一种位置进行讲解。

 如图所示,只要将g节点改为红色,将p和u节点改为黑色就可以。完成修改之后分为下列几种情况:

1. g节点的父节点为黑色,完成调整。

2. g节点的父节点为红色,向上进行再一次调整。

3. g节点为根节点,将g节点颜色改为黑色。

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

左单旋:

右单旋:

 左右双旋:

右左双旋:

红黑树的模拟实现

#pragma once
#include<iostream>
using namespace std;

enum colour
{
	RED,
	BLACK,
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode(const pair<K, V>& p)
		:_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
		,_col(RED)
		,_kv(p)
	{}

	RBTreeNode<K, V>* _parent;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	colour _col;
	pair<K, V> _kv;
};

template<class K, class V>
class RBTree
{
public:
	typedef RBTreeNode<K, V>  Node;

	RBTree()
		:_root(nullptr)
	{}

	bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

		//插入新节点
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if (kv.first == cur->_kv.first)
			{
				return false;
			}
			else if (kv.first < cur->_kv.first)
			{
				cur = cur->_left;
			}
			else
			{
				cur = cur->_right;
			}
		}
		cur = new Node(kv);
		cur->_parent = parent;
		if (kv.first < parent->_kv.first)
		{
			parent->_left = cur;
		}
		else if (kv.first > parent->_kv.first)
		{
			parent->_right = cur;
		}

		while (parent && RED == parent->_col)
		{
			Node* grand = parent->_parent;
			if (parent == grand->_left)
			{
				Node* uncle = grand->_right;
				if (uncle && RED == uncle->_col)//叔叔为红色节点的情况
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grand->_col = RED;
					cur = grand;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)//右单旋
					{
						RotateR(grand);
						parent->_col = BLACK;
						grand->_col = RED;
					}
					else//左右双旋
					{
						RotateL(parent);
						RotateR(grand);
						cur->_col = BLACK;
						grand->_col = RED;
					}
					break;
				}
			}
			else if (parent == grand->_right)
			{
				Node* uncle = grand->_left;
				if (uncle && RED == uncle->_col)//叔叔为红色节点的情况
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grand->_col = RED;
					cur = grand;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)//左单旋
					{
						RotateL(grand);
						parent->_col = BLACK;
						grand->_col = RED;
					}
					else//右左双旋
					{
						RotateR(parent);
						RotateL(grand);
						cur->_col = BLACK;
						grand->_col = RED;
					}
					break;
				}
			}

			//最后强制将根节点颜色置为黑色
			_root->_col = BLACK;
		}
		return true;

	}



	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)
		{
			//cout << blackNum << endl;
			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);
	}


private:
	void RotateR(Node* pn)
	{
		Node* ppn = pn->_parent;
		Node* subL = pn->_left;
		Node* subLR = subL->_right;
		if (ppn)
		{
			if (pn == ppn->_left)
			{
				ppn->_left = subL;
			}
			else
			{
				ppn->_right = subL;
			}
		}
		else
		{
			_root = subL;
		}
		subL->_parent = ppn;
		if (subLR)
		{
			subLR->_parent = pn;
		}
		pn->_left = subLR;

		pn->_parent = subL;
		subL->_right = pn;
	}

	void RotateL(Node* pn)
	{
		Node* ppn = pn->_parent;
		Node* subR = pn->_right;
		Node* subRL = subR->_left;
		if (ppn)
		{
			if (pn == ppn->_left)
			{
				ppn->_left = subR;
			}
			else
			{
				ppn->_right = subR;
			}
		}
		else
		{
			_root = subR;
		}
		subR->_parent = ppn;

		if (subRL)
		{
			subRL->_parent = pn;
		}
		pn->_right = subRL;

		subR->_left = pn;
		pn->_parent = subR;

	}



	Node* _root;
};

封装map和set

在我们将红黑树封装为map和set之前,我们先要了解这个封装是怎么实现的。也就是如何安排模板参数,实现代码最大限度的复用 。

注意:从源码中可以看到——创建的节点的内容的类型是由map和set的第二个模板参数决定的。

那么map和set的第一个模板参数的作用是什么呢?第一个模板参数类型是从第二个模板参数类型中能提取出来的数据类型(比如从pair<int , string>中提取出int)。

那么为什么要把第一个类型的数据提取出来呢?是为了比较大小。封装set时当然可以直接比较节点中内容的大小,但如果封装的是map,并不能直接直接比较pair结构,因为我们想要比较的是pair.first,这时候就需要将第一个参数提取出来了。

接下来是迭代器,我们要怎么实现迭代器的迭代(++)呢?可以分为三种情况:

1. 如果右子树不为空,下一个就是右子树的最左节点。

2. 如果右子树为空,去找祖先节点,向上溯源的过程中找到孩子是祖先的左孩子的节点就是下一个节点,如果没有就说明走到了尽头。

接下来将红黑树封装为map和set:

在实现的过程中又有下列几个注意点:

1. 由于set的迭代器不允许修改数据,所以set的iterator和const_iterator都是红黑树中的const_iterator。

2. 由于支持方括号需要插入函数的返回类型为pair<iterator, bool>,但是set迭代器都是const_iterator,所以红黑树需要支持普通迭代器构造const迭代器。

RBTree.h


#pragma once
#include<iostream>
using namespace std;

enum colour
{
	RED,
	BLACK,
};

template<class T>
struct RBTreeNode
{
	RBTreeNode(const T& t)
		:_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
		,_col(RED)
		,_value(t)
	{}

	RBTreeNode<T>* _parent;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	colour _col;
	T _value;
};

template<class V, class Ref, class Ptr>
class _RBTree_iterator
{
public:
	typedef RBTreeNode<V>  Node;
	typedef  _RBTree_iterator<V, Ref, Ptr>  Self;
	typedef  _RBTree_iterator<V, V&, V*>  iterator;
	_RBTree_iterator(Node* np)
		:_n(np)
	{}

	_RBTree_iterator(const iterator& it)
		:_n(it._n)
	{}

	Ref operator*()
	{
		return _n->_value;
	}
	Ptr operator->()
	{
		return &(_n->_value);
	}

	Self& operator++()
	{
		if (_n->_right)
		{
			_n = _n->_right;
			while (_n->_left)
			{
				_n = _n->_left;
			}
		}
		else
		{
			Node* parent = _n->_parent;
			while (parent && _n != parent->_left)
			{
				_n = parent;
				parent = parent->_parent;
			}
			_n = parent;
		}
		return *this;
	}

	bool operator!=(const Self& b)
	{
		return _n != b._n;
	}

	Node* _n;
};

template<class K, class V, class KeyOfV>
class RBTree
{
public:
	typedef RBTreeNode<V>  Node;
	typedef  _RBTree_iterator<V, V&, V*>  iterator;
	typedef  _RBTree_iterator<V, const V&, const V*>  const_iterator;

	iterator begin()
	{
		Node* cur = _root;
		while (cur->_left)
		{
			cur = cur->_left;
		}
		return cur;
	}

	iterator end()
	{
		return iterator(nullptr);
	}

	const_iterator begin() const
	{
		Node* cur = _root;
		while (cur->_left)
		{
			cur = cur->_left;
		}
		return cur;
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}

	RBTree()
		:_root(nullptr)
	{}

	pair<iterator, bool> insert(const V& v)
	{
		if (_root == nullptr)
		{
			_root = new Node(v);
			_root->_col = BLACK;
			return pair<iterator, bool>(_root, true);
		}
		KeyOfV ext;
		//插入新节点
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if (ext(v) == ext(cur->_value))
			{
				return pair<iterator, bool>(cur, false);
			}
			else if (ext(v) < ext(cur->_value))
			{
				cur = cur->_left;
			}
			else
			{
				cur = cur->_right;
			}
		}
		cur = new Node(v);
		Node* newnode = cur;
		cur->_parent = parent;
		if (ext(v) < ext(parent->_value))
		{
			parent->_left = cur;
		}
		else if (ext(v) > ext(parent->_value))
		{
			parent->_right = cur;
		}

		while (parent && RED == parent->_col)
		{
			Node* grand = parent->_parent;
			if (parent == grand->_left)
			{
				Node* uncle = grand->_right;
				if (uncle && RED == uncle->_col)//叔叔为红色节点的情况
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grand->_col = RED;
					cur = grand;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)//右单旋
					{
						RotateR(grand);
						parent->_col = BLACK;
						grand->_col = RED;
					}
					else//左右双旋
					{
						RotateL(parent);
						RotateR(grand);
						cur->_col = BLACK;
						grand->_col = RED;
					}
					break;
				}
			}
			else if (parent == grand->_right)
			{
				Node* uncle = grand->_left;
				if (uncle && RED == uncle->_col)//叔叔为红色节点的情况
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grand->_col = RED;
					cur = grand;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)//左单旋
					{
						RotateL(grand);
						parent->_col = BLACK;
						grand->_col = RED;
					}
					else//右左双旋
					{
						RotateR(parent);
						RotateL(grand);
						cur->_col = BLACK;
						grand->_col = RED;
					}
					break;
				}
			}

			//最后强制将根节点颜色置为黑色
			_root->_col = BLACK;
		}
		return pair<iterator, bool>(newnode, true);

	}


private:
	void RotateR(Node* pn)
	{
		Node* ppn = pn->_parent;
		Node* subL = pn->_left;
		Node* subLR = subL->_right;
		if (ppn)
		{
			if (pn == ppn->_left)
			{
				ppn->_left = subL;
			}
			else
			{
				ppn->_right = subL;
			}
		}
		else
		{
			_root = subL;
		}
		subL->_parent = ppn;
		if (subLR)
		{
			subLR->_parent = pn;
		}
		pn->_left = subLR;

		pn->_parent = subL;
		subL->_right = pn;
	}

	void RotateL(Node* pn)
	{
		Node* ppn = pn->_parent;
		Node* subR = pn->_right;
		Node* subRL = subR->_left;
		if (ppn)
		{
			if (pn == ppn->_left)
			{
				ppn->_left = subR;
			}
			else
			{
				ppn->_right = subR;
			}
		}
		else
		{
			_root = subR;
		}
		subR->_parent = ppn;

		if (subRL)
		{
			subRL->_parent = pn;
		}
		pn->_right = subRL;

		subR->_left = pn;
		pn->_parent = subR;

	}



	Node* _root;
};

set.h

#pragma once
#pragma once
#include"RBTree.h"

namespace szg
{
	template<class K>
	class set
	{
	public:
		struct setKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
		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& k)
		{
			return _t.insert(k);
		}
	private:
		RBTree<K, K, setKeyOfT> _t;
	};



}

map.h

#pragma once
#include"RBTree.h"

namespace szg
{
	template<class K, class V>
	class map
	{
	public:
		struct mapKeyOfT
		{
			const K& operator()(const pair<K, V>& v)
			{
				return v.first;
			}
		};
		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 _m.begin();
		}

		iterator end()
		{
			return _m.end();
		}

		const_iterator begin() const
		{
			return _m.begin();
		}

		const_iterator end() const
		{
			return _m.end();
		}

		pair<iterator, bool> insert(const pair<K, V>& p)
		{
			return _m.insert(p);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> p = insert(make_pair(key, V()));
			return p.first->second;
		}

	private:
		RBTree<K, pair<const K, V>, mapKeyOfT> _m;
	};

	void test_map()
	{
		int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		map<int, int> m;
		for (auto e : a)
		{
			m.insert(make_pair(e, e));
		}

		for (auto e : m)
		{
			cout << e.first << ":" << e.second << endl;
		}

		map<string, int> countMap;
		string arr[] = { "苹果", "香蕉", "葡萄", "葡萄", "梨", "葡萄", "苹果", "哈密瓜", "梨", "苹果", "葡萄", "葡萄", "葡萄" };
		for (auto& e : arr)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}


	}

}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JDSZGLLL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值