码迷,mamicode.com
首页 > 其他好文 > 详细

BST基础教学(详细注释)

时间:2018-09-29 23:57:59      阅读:308      评论:0      收藏:0      [点我收藏+]

标签:交换   col   queue   read   一个   out   存在   val   底部   

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<queue>
  4 #include<cstring>
  5 #include<algorithm>
  6 #define re return
  7 #define rep(i,a,n) for(int i = a;i <= n;i++)
  8 #define per(i,n,a) for(int i = n;i >= a;i--)
  9 typedef long long LL;
 10 using namespace std;
 11 int read() {
 12     int out = 0,flag = 1;
 13     char c = getchar();
 14     while(c < 0 || c > 9) {
 15         if(c == -) flag = -1;
 16         c = getchar();
 17     }
 18     while(c >= 0 && c <= 9) {
 19         out = out * 10 + c - 0;
 20         c = getchar();
 21     }
 22     re flag * out;
 23 }
 24 //head
 25 
 26 const int maxn = 1000019,INF = 1e9;
 27 int na;
 28 int ch[maxn][2];//[i][0]代表i左儿子,[i][1]代表i右儿子
 29 int val[maxn],dat[maxn];
 30 int size[maxn],cnt[maxn];
 31 int tot,root;
 32 int New(int v) { //新增节点,
 33     val[++tot] = v;//节点赋值
 34     dat[tot] = rand();//随机优先级
 35     size[tot] = 1;//目前是新建叶子节点,所以子树大小为1
 36     cnt[tot] = 1;//新建节点同理副本数为1
 37     return tot;
 38 }
 39 void pushup(int id) { //和线段树的pushup更新一样
 40     size[id] = size[ch[id][0]] + size[ch[id][1]] + cnt[id];
 41     //本节点子树大小 = 左儿子子树大小 + 右儿子子树大小 + 本节点副本数
 42 }
 43 void build() {
 44     root = New(-INF),ch[root][1] = New(INF);//先加入正无穷和负无穷,便于之后操作(貌似不加也行)
 45     pushup(root);//因为INF > -INF,所以是右子树,
 46 }
 47 void Rotate(int &cur,int d) {
 48     int temp = ch[cur][d ^ 1];
 49     ch[cur][d ^ 1] = ch[temp][d];
 50     ch[temp][d] = cur;
 51     cur = temp;
 52     pushup(ch[cur][d]);
 53     pushup(cur);
 54     re;
 55 }
 56 void insert(int &id,int v) { //id依然是引用,在新建节点时可以体现
 57     if(!id) {
 58         id = ++tot;
 59         val[tot] = v;
 60         dat[tot] = rand();
 61         size[tot] = 1;
 62         cnt[tot] = 1;
 63         return;
 64     }
 65     if(v == val[id]) cnt[id]++;//若节点已存在,则副本数++;
 66     else { //要满足BST性质,小于插到左边,大于插到右边
 67         bool d = (v > val[id]);
 68         insert(ch[id][d],v);//递归实现
 69         if(dat[id] < dat[ch[id][d]]) Rotate(id,d ^ 1);//(参考一下图)与左节点交换右旋,与右节点交换左旋
 70     }
 71     pushup(id);//现在更新一下本节点的信息
 72 }
 73 void Del(int &id,int v) {
 74     if(!id)return ;//到这了发现查不到这个节点,该点不存在,直接返回
 75     if(v == val[id]) { //检索到了这个值
 76         if(cnt[id] > 1) {
 77             cnt[id]--,pushup(id);    //若副本不止一个,减去一个就好
 78             return ;
 79         }
 80         if(ch[id][0] || ch[id][1]) { //发现只有一个值,且有儿子节点,我们只能把值旋转到底部删除
 81             if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]]) { //当前点被移走之后,会有一个新的点补上来(左儿子或右儿子),按照优先级,优先级大的补上来
 82                 Rotate(id,1),Del(ch[id][1],v);//我们会发现,右旋是与左儿子交换,当前点变成右节点;左旋则是与右儿子交换,当前点变为左节点
 83             } else Rotate(id,0),Del(ch[id][0],v);
 84             pushup(id);
 85         } else id = 0;//发现本节点是叶子节点,直接删除
 86         return ;//这个return对应的是检索到值de所有情况
 87     }
 88     v < val[id] ? Del(ch[id][0],v) : Del(ch[id][1],v);//继续BST性质
 89     pushup(id);
 90 }
 91 int get_rank(int id,int v) {
 92     if(!id)return 0;//若查询值不存在,返回
 93     if(v == val[id])return size[ch[id][0]] + 1;//查询到该值,由BST性质可知:该点左边值都比该点的值(查询值)小,故rank为左儿子大小 + 1
 94     else if(v < val[id])return get_rank(ch[id][0],v);
 95     //发现需查询的点在该点左边,往左边递归查询
 96     else return size[ch[id][0]] + cnt[id] + get_rank(ch[id][1],v);
 97     //若查询值大于该点值。说明询问点在当前点的右侧,且此点的值都小于查询值,所以要加上cnt[id]
 98 }
 99 int get_val(int id,int rank) {
100     if(!id) return INF;//一直向右找找不到,说明是正无穷
101     if(rank <= size[ch[id][0]]) return get_val(ch[id][0],rank);
102     //左边排名已经大于rank了,说明rank对应的值在左儿子那里
103     else if(rank <= size[ch[id][0]] + cnt[id]) return val[id];
104     //上一步排除了在左区间的情况,若是rank在左与中(目前节点)中,
105     //则直接返回目前节点(中区间)的值
106     else return get_val(ch[id][1],rank - size[ch[id][0]] - cnt[id]);
107     //剩下只能在右区间找了,rank减去左区间大小和中区间,继续递归
108 }
109 int get_pre(int v) {
110     int id = root,pre;
111     while(id) {
112         if(val[id] < v) pre = val[id],id = ch[id][1];
113         else id = ch[id][0];
114     }
115     return pre;
116 }
117 int get_next(int v) {
118     int id = root,next;
119     while(id) {
120         if(val[id] > v) next = val[id],id = ch[id][0];
121         else id = ch[id][1];
122     }
123     return next;
124 }
125 int main() {
126     build();
127     //不要忘记初始化
128     //[运行build()会连同root一并初始化,所以很重要]
129     na = read();
130     for(int i = 1; i <= na; i++) {
131         int cmd = read(),x = read();
132         if(cmd == 1)insert(root,x);//函数都写好了,注意:需要递归的函数都从根开始,不需要递归的函数直接查询
133         if(cmd == 2)Del(root,x);
134         if(cmd == 3)printf("%d\n",get_rank(root,x) - 1);//注意:因为初始化时插入了INF和-INF,所以查询排名时要减1(-INF不是第一小,是“第零小”)
135         if(cmd == 4)printf("%d\n",get_val(root,x + 1));//同理,用排名查询值得时候要查x + 1名,因为第一名(其实不是)是-INF
136         if(cmd == 5)printf("%d\n",get_pre(x));
137         if(cmd == 6)printf("%d\n",get_next(x));
138     }
139     return 0;
140 }

个人认为除了把左右rotate和一起之外没什么特别难懂的地方

至今没人知道这份代码的注释为什么这么详细

当年自己都这么认真现在有什么理由不努力呢

#include<iostream>#include<cstdio>#include<queue>#include<cstring>#include<algorithm>#define re return#define rep(i,a,n) for(int i = a;i <= n;i++)#define per(i,n,a) for(int i = n;i >= a;i--)typedef long long LL;using namespace std;int read() {int out = 0,flag = 1;char c = getchar();while(c < ‘0‘ || c > ‘9‘) {if(c == ‘-‘) flag = -1;c = getchar();}while(c >= ‘0‘ && c <= ‘9‘) {out = out * 10 + c - ‘0‘;c = getchar();}re flag * out;}//head
const int maxn = 1000019,INF = 1e9;int na;int ch[maxn][2];//[i][0]代表i左儿子,[i][1]代表i右儿子int val[maxn],dat[maxn];int size[maxn],cnt[maxn];int tot,root;int New(int v) { //新增节点,val[++tot] = v;//节点赋值dat[tot] = rand();//随机优先级size[tot] = 1;//目前是新建叶子节点,所以子树大小为1cnt[tot] = 1;//新建节点同理副本数为1return tot;}void pushup(int id) { //和线段树的pushup更新一样size[id] = size[ch[id][0]] + size[ch[id][1]] + cnt[id];//本节点子树大小 = 左儿子子树大小 + 右儿子子树大小 + 本节点副本数}void build() {root = New(-INF),ch[root][1] = New(INF);//先加入正无穷和负无穷,便于之后操作(貌似不加也行)pushup(root);//因为INF > -INF,所以是右子树,}void Rotate(int &cur,int d) {int temp = ch[cur][d ^ 1];ch[cur][d ^ 1] = ch[temp][d];ch[temp][d] = cur;cur = temp;pushup(ch[cur][d]);pushup(cur);re;}void insert(int &id,int v) { //id依然是引用,在新建节点时可以体现if(!id) {id = ++tot;val[tot] = v;dat[tot] = rand();size[tot] = 1;cnt[tot] = 1;return;}if(v == val[id]) cnt[id]++;//若节点已存在,则副本数++;else { //要满足BST性质,小于插到左边,大于插到右边bool d = (v > val[id]);insert(ch[id][d],v);//递归实现if(dat[id] < dat[ch[id][d]]) Rotate(id,d ^ 1);//(参考一下图)与左节点交换右旋,与右节点交换左旋}pushup(id);//现在更新一下本节点的信息}void Del(int &id,int v) {if(!id)return ;//到这了发现查不到这个节点,该点不存在,直接返回if(v == val[id]) { //检索到了这个值if(cnt[id] > 1) {cnt[id]--,pushup(id);    //若副本不止一个,减去一个就好return ;}if(ch[id][0] || ch[id][1]) { //发现只有一个值,且有儿子节点,我们只能把值旋转到底部删除if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]]) { //当前点被移走之后,会有一个新的点补上来(左儿子或右儿子),按照优先级,优先级大的补上来Rotate(id,1),Del(ch[id][1],v);//我们会发现,右旋是与左儿子交换,当前点变成右节点;左旋则是与右儿子交换,当前点变为左节点} else Rotate(id,0),Del(ch[id][0],v);pushup(id);} else id = 0;//发现本节点是叶子节点,直接删除return ;//这个return对应的是检索到值de所有情况}v < val[id] ? Del(ch[id][0],v) : Del(ch[id][1],v);//继续BST性质pushup(id);}int get_rank(int id,int v) {if(!id)return 0;//若查询值不存在,返回if(v == val[id])return size[ch[id][0]] + 1;//查询到该值,由BST性质可知:该点左边值都比该点的值(查询值)小,故rank为左儿子大小 + 1else if(v < val[id])return get_rank(ch[id][0],v);//发现需查询的点在该点左边,往左边递归查询else return size[ch[id][0]] + cnt[id] + get_rank(ch[id][1],v);//若查询值大于该点值。说明询问点在当前点的右侧,且此点的值都小于查询值,所以要加上cnt[id]}int get_val(int id,int rank) {if(!id) return INF;//一直向右找找不到,说明是正无穷if(rank <= size[ch[id][0]]) return get_val(ch[id][0],rank);//左边排名已经大于rank了,说明rank对应的值在左儿子那里else if(rank <= size[ch[id][0]] + cnt[id]) return val[id];//上一步排除了在左区间的情况,若是rank在左与中(目前节点)中,//则直接返回目前节点(中区间)的值else return get_val(ch[id][1],rank - size[ch[id][0]] - cnt[id]);//剩下只能在右区间找了,rank减去左区间大小和中区间,继续递归}int get_pre(int v) {int id = root,pre;while(id) {if(val[id] < v) pre = val[id],id = ch[id][1];else id = ch[id][0];}return pre;}int get_next(int v) {int id = root,next;while(id) {if(val[id] > v) next = val[id],id = ch[id][0];else id = ch[id][1];}return next;}int main() {build();//不要忘记初始化//[运行build()会连同root一并初始化,所以很重要]na = read();for(int i = 1; i <= na; i++) {int cmd = read(),x = read();if(cmd == 1)insert(root,x);//函数都写好了,注意:需要递归的函数都从根开始,不需要递归的函数直接查询if(cmd == 2)Del(root,x);if(cmd == 3)printf("%d\n",get_rank(root,x) - 1);//注意:因为初始化时插入了INF和-INF,所以查询排名时要减1(-INF不是第一小,是“第零小”)if(cmd == 4)printf("%d\n",get_val(root,x + 1));//同理,用排名查询值得时候要查x + 1名,因为第一名(其实不是)是-INFif(cmd == 5)printf("%d\n",get_pre(x));if(cmd == 6)printf("%d\n",get_next(x));}return 0;}

BST基础教学(详细注释)

标签:交换   col   queue   read   一个   out   存在   val   底部   

原文地址:https://www.cnblogs.com/yuyanjiaB/p/9727036.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!