[map的实现原理--红黑树]

map--红黑树

事情起因于pta的某道题-树种统计

数据结构题肯定不能用STL里面的map,但是这个题明显就是用map做,于是我想能不能自己实现一个map呢?了解到map的实现原理是红黑树!

前置知识:二叉搜索树

红黑树的基本原理就是二叉搜索树,二叉搜索树又叫二叉排序树,定义是左儿子比他小,右儿子比他大。那么这样查找的时候,就可以按照这种方式以logn的级别去查找,效率很高。但要考虑一种最坏的情况那就是从开头到结尾,元素的插入都是按照从大到小(或从大到小)的顺序去插入,比如插入的数是 5 4 3 2 1,那么这个树的形状就是这个样子

那么这样的话,查找的复杂度就会变成O(n),因为他每次都会从他的左儿子往下找,就相当于遍历一个数组一样,那么有没有一种方法可以解决这种问题呢?有!它就是红黑树,那么它为什么可以控制这个链状的结构呢,那就是跟他的性质有关系了。

性质

1.结点有颜色,且要么是红,要么是黑。

2.根结点是黑色。

3.所有叶子都是黑色。(注意这里的叶子指的是空结点)

4.每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)

5.从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。

这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长,如图所示

这样的话,因为每条路径的黑结点数要相同,所以要使路径足够长的话,就是往任意两个黑结点中间插入一个红结点。就是这样的性质,保证了他不会出现只有一条链的情况,他的复杂度可以稳定在O(logn)

维护

既然了解了它的性质,那么如何在插入的时候就保持这样的性质呢?

首先新插入的结点把它设置为红色结点。然后我们来看这么一种情况

我们发现违反了性质4,那么我们要修正他,情况1:自己的父亲和叔叔都是红色,那么就令他的爸爸和叔叔的颜色都设置为黑色,把它爷爷的颜色设置为红色,那么他就会变成这个样子

那么我们来考虑情况2:如果将它爷爷设置为红色后,发现它爷爷的上一个也是红色怎么办?

我们发现此时它爷爷的叔叔并不是红色,那么此时就要考虑变色+旋转,方法是将新结点爷爷的父亲改为黑色,将它爷爷的爷爷设置为红色,然后以它爷爷的爷爷为旋转点旋转。(此情况为右旋),旋转为如图所示

我们再来稍微多考虑一个情况2中的小情况比如这样:

我们发现它不同于情况1的描述,与情况2的描述,但是又跟情况2有相似之处,因为他的叔叔空节点(默认为黑色),此时我们要先以它的父亲为旋转点左旋,如图所示

然后再接着跟情况2一样了,如果没有左旋的话,直接右旋会出现问题,具体可以自己手动模拟一下(自主思考还是很重要的!)

然后最后放一下树种统计的代码,如果会二叉搜索树的话,只需要看如何稳定时间logn就好了!

#include <cstdio>//表示颜色:0黑1红
#include <cstring>
#include <iostream>
typedef long long LL;
using namespace std;
const int maxn = 1e5+7;
int n,root; char s[55];
struct Map{
	char fi[55];//存串
	double se;//存出现的数量
	int fa,color,ls,rs;//父亲,颜色,左儿子,右儿子
}tree[maxn];

int cmp(char s1[],char s2[])//比较两个串谁的字典序比较小
{
	int len1 = strlen(s1);
	int len2 = strlen(s2);
	for(int i=0;i<min(len1,len2);i++)
	{
		if(toupper(s1[i]) == toupper(s2[i])) continue;
		if(toupper(s1[i]) < toupper(s2[i])) return -1;
		else return 1;
	}
	return len1 - len2;
}

struct Tree{
	int sonx,nx;int tot = 0;
	void put(char x[])//插入数据的函数
	{
		if(tot == 0)
		{
			tot ++;root = 1;
			strcpy(tree[1].fi,x);
			tree[1].se ++;tree[1].color = 0;
			return ;
		}
		nx = root;
		while(1)
		{
			if(cmp(tree[nx].fi,x) < 0)
			{
				if(tree[nx].rs == 0)
				{
					tot ++;
					strcpy(tree[tot].fi,x);
					tree[tot].se ++;
					tree[tot].color = 1;
					tree[tot].fa = nx;
					tree[nx].rs = tot;
					break;
				}
				else nx = tree[nx].rs;
			}
			else if(cmp(tree[nx].fi,x) > 0)
			{
				if(tree[nx].ls == 0)
				{
					tot ++;
					strcpy(tree[tot].fi,x);
					tree[tot].se ++;
					tree[tot].color = 1;
					tree[tot].fa = nx;
					tree[nx].ls = tot;
					break;
				}
				else nx = tree[nx].ls;
			}
			else{
				tree[nx].se ++;
				return ;
			}
		}
		nx = tot;int pr = tree[nx].fa;//维护
		while(nx != 0 && pr != root && tree[tree[nx].fa].color == 1)
		{
			int uncle;
			if(tree[nx].fa == tree[tree[tree[nx].fa].fa].ls)
			{
				uncle = tree[tree[tree[nx].fa].fa].rs;
				if(tree[uncle].color == 1)
				{
					tree[tree[nx].fa].color = 0;
					tree[uncle].color = 0;
					tree[tree[tree[nx].fa].fa].color = 1;
					nx = tree[tree[nx].fa].fa;
				}
				else{
					if(nx == tree[tree[nx].fa].rs)
					{
						nx = tree[nx].fa;
						left_rotate(nx);
					}
					tree[tree[nx].fa].color = 0;
					tree[tree[tree[nx].fa].fa].color = 1;
					right_rotate(tree[tree[nx].fa].fa);
				} 
			}
			else{
                uncle = tree[tree[tree[nx].fa].fa].ls;
                if(tree[uncle].color == 1)
				{
					tree[tree[nx].fa].color = 0;
					tree[uncle].color = 0;
					tree[tree[tree[nx].fa].fa].color = 1;
					nx = tree[tree[nx].fa].fa;
				}
                else{
                    if(nx == tree[tree[nx].fa].ls)
					{
						nx = tree[nx].fa;
						right_rotate(nx);
					}
					tree[tree[nx].fa].color = 0;
					tree[tree[tree[nx].fa].fa].color = 1;
					left_rotate(tree[tree[nx].fa].fa);
                }
            }
		}
		tree[root].color = 0;
		/*printf("--------------------------------------------\n");
		for(int i=1;i<=tot;i++)
			printf("%d:%d %d %d %d\n",i,tree[i].fa,tree[i].ls,tree[i].rs,tree[i].color);
		printf("--------------------------------------------\n");*/
	}
	
	void left_rotate(int x)//左旋
	{
		int y = tree[x].rs;
		if(tree[y].ls != 0)
			tree[tree[y].ls].fa = x;
		tree[y].fa = tree[x].fa;
		if(root == x) root = y;
		else if(x == tree[tree[x].fa].ls)
			tree[tree[x].fa].ls = y;
		else tree[tree[x].fa].rs = y;
		tree[tree[y].ls].fa = x;
		tree[x].rs = tree[y].ls;
		tree[y].ls = x;
		tree[x].fa = y;
		return ;
	}
	
	void right_rotate(int x)//右旋
	{
		int y = tree[x].ls;
		if(tree[y].rs != 0)
			tree[tree[y].rs].fa = x;
		tree[y].fa = tree[x].fa;
		if(root == x) root = y;
		else if(x == tree[tree[x].fa].ls)
			tree[tree[x].fa].ls = y;
		else tree[tree[x].fa].rs = y;
		tree[tree[y].rs].fa = x;
		tree[x].ls = tree[y].rs;
		tree[y].rs = x;
		tree[x].fa = y;
		return ;
	}
	
}m;

void dfs(int rt)//按照中序遍历输出,因为这样建树,中序遍历即为从小到大的顺序
{
	if(tree[rt].ls != 0) dfs(tree[rt].ls);
	printf("%s %.4lf%%\n",tree[rt].fi,tree[rt].se * 100 / n);
	if(tree[rt].rs != 0) dfs(tree[rt].rs);
	return ; 
}

int main()
{
//	freopen("1.txt","w",stdout);
	scanf("%d",&n);getchar();
	for(int i=1;i<=n;i++)
	{
		fgets(s,55,stdin);//处理串
		int len = strlen(s);
		s[len-1] = '\0';
		m.put(s);
	}
	dfs(root);
	return 0;
}
/*测试数据
5
Red Alder
Yellow Birch
Red Oak
White Oak
Black Walnut

5
Red Alder
Yellow Birch
Red Oak
White Oak
Red Alder
*/
posted @ 2022-09-30 20:36  风丨铃  阅读(251)  评论(0编辑  收藏  举报