隐藏页面特效

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

1|0map--红黑树


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


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

2|0前置知识:二叉搜索树


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

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

3|0性质


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

2.根结点是黑色。

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

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

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

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

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

4|0维护


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


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

我们发现违反了性质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 */

__EOF__

本文作者风丨铃
本文链接https://www.cnblogs.com/-Wind-/p/16745890.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   风丨铃  阅读(336)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示