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和一塊兒以外沒什麼特別難懂的地方ios

至今沒人知道這份代碼的註釋爲何這麼詳細函數

當年本身都這麼認真如今有什麼理由不努力呢ui

相關文章
相關標籤/搜索