BST基础教学(详细注释)

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

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

时间: 2024-11-18 03:48:58

BST基础教学(详细注释)的相关文章

MySql常用操作【基础且详细(●&#39;?&#39;●)】

有那么挺长段时间没有敲代码了,今敲起来竟然有些sql都想不起来了?? 把以前整理sql的内容看了下,再加了点?? 主要参考自 MySql文档:https://dev.mysql.com/doc/refman/8.0/en/tutorial.html 易百教程:https://www.yiibai.com/mysql 一,基本操作 1.连接操作 连接远程数据库: $ mysql -h host -u -user -p (host:主机 user:用户名) 连接本地数据库: $ mysql -u u

java-se基础 2(注释和原生数据类型)

Java SE 第二讲: 1. Windows: notepad, editplus, ultraedit, gvim Linux: vi, vim, gedit 2. Java中的数据类型分为两大类: 1) 原生数据类型 (Primitive Data Type) 2) 引用类型(对象类型) (Reference Type) 3. 变量与常量:所谓常量,就是值不会变化的量:所谓变量,就是值可以变化的量. 4. 如何定义变量? 变量类型 变量名; int a; 5. 如何为变量赋值? 变量名 =

Qt5_简易画板_详细注释

代码下载链接:  http://pan.baidu.com/s/1hsc41Ek 密码: 5hdg 显示效果如下: 代码附有详细注释(代码如下) 1 /*** 2 * 先新建QMainWindow, 项目名称: DrawWidget 基类选择: QMainWindow, 3 * 类名默认, 然后在DrawWidget项目名上新建c++class文件, 选择基类: QWidget 4 */ 5 //先完成绘图区的实现 6 //如下为: drawwidget.h 7 #ifndef DRAWWIDG

codevs 2924 数独挑战 x(三种做法+超详细注释~)

2924 数独挑战 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 钻石 Diamond 题目描述 Description “芬兰数学家因卡拉,花费3个月时间设计出了世界上迄今难度最大的数独游戏,而且它只有一个答案.因卡拉说只有思考能力最快.头脑最聪明的人才能破解这个游戏.”这是英国<每日邮报>2012年6月30日的一篇报道.这个号称“世界最难数独”的“超级游戏”,却被扬州一位69岁的农民花三天时间解了出来. 看到这个新闻后,我激动不已,证明我们OI的实力的机会来了,我们虽然不是

多线程实现生产者消费者问题 详细注释 事件+临界区 信号量+临界区2种方法

生产者消费者问题:  该问题描述了两个共享固定大小缓冲区的线程--即所谓的"生产者"和"消费者"--在实际运行时会发生的问题.生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程.与此同时,消费者也在缓冲区消耗这些数据.该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据.具体我就不解释了   应该都懂 不懂请百度一下 我是用事件实现生产者消费者问题的同步  用临界区实现互斥    我的这个做法与经典做法不同 即用信号量

【Cocos2d-Js基础教学 入门目录】

从接触Cocos2dx-Js以来,它的绽放的绚丽让我无法不对它喜欢.我觉得Js在不断带给我们惊喜:在开发过程中,会大大提升我们对原型开发的利用率,使用Js语言做游戏开发,使游戏可测试性更加强大,但很多人觉得Cocos2d-Js引擎是一门很沉重的语言,里面的API非常深,这其实是错误的理解,Js对自身语言的扩展,对其他语言的通讯支持,都是非常强大的.目前官方对API的整合分为了Cocos2dx-Js和Cocos2dx-Js-Lite版本: Cocos2dx-Js官方的介绍是这样的: Cocos2d

30多个iOS常用动画,带详细注释

// //  CoreAnimationEffect.h //  CoreAnimationEffect // //  Created by VincentXue on 13-1-19. //  Copyright (c) 2013年 VincentXue. All rights reserved. // #import <Foundation/Foundation.h> /**  !  导入QuartzCore.framework  *  *  Example:  *  *  Step.1

ABP+Zero+Metronic+Redis的完美结合快速启动模板(超级代码详细注释版本)

微信扫一扫并支付成功,联系QQ:770628656获取所有源码(超级代码详细注释版本) 原文地址:https://www.cnblogs.com/abpbasic/p/8124792.html

Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

 原题链接   无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍, 看其low和dfn,满足dfn[ fa ]<low[ i ](0<i<=n, i 的 father为fa) -- 则桥为fa-i. 找桥的时候,要注意看有没有重边:有重边,则不是桥. 另外,本题的题意及测试样例中没有重边,所以不用考虑重边. 带详细注释的题解: #include<s