BST基础教学(详细注释)
生活随笔
收集整理的這篇文章主要介紹了
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
",get_rank(root,x) - 1);//注意:因為初始化時插入了INF和-INF,所以查詢排名時要減1(-INF不是第一小,是“第零小”)
135 if(cmd == 4)printf("%d
",get_val(root,x + 1));//同理,用排名查詢值得時候要查x + 1名,因為第一名(其實不是)是-INF
136 if(cmd == 5)printf("%d
",get_pre(x));
137 if(cmd == 6)printf("%d
",get_next(x));
138 }
139 return 0;
140 }
個人認為除了把左右rotate和一起之外沒什么特別難懂的地方
至今沒人知道這份代碼的注釋為什么這么詳細
當年自己都這么認真現在有什么理由不努力呢
> 別忘了 總有人在等著你
總結
以上是生活随笔為你收集整理的BST基础教学(详细注释)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于脂肪粒
- 下一篇: PDF密码去除常用工具