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