标签:pen 多次 ios set 模板题 strong fstream public memory
替罪羊树是不通过旋转而是重构的一种平衡树。当某一棵子树的节点总数超过其父节点的一定时,就进行重构操作。
为了判断是否需要重构,所以需要加入cover(实际节点个数)域。这次直接加入可重操作,所以还需要增加一个size域。为了体现C++面向对象的思想(分明就是Java用多了),所以判断一个子树是否需用重构写成成员函数bad()。(真开心,因为是重构,不需要那么多的father,终于可以轻松地删掉父节点指针)
1 template<typename T> 2 class ScapegoatTreeNode{ 3 private: 4 static const float factora = 0.69; //平衡因子 5 public: 6 T data; 7 int size, cover; //数的个数, 实际节点个数 8 int count; 9 ScapegoatTreeNode* next[2]; 10 11 ScapegoatTreeNode():size(1), cover(1), count(1){ 12 memset(next, 0, sizeof(next)); 13 } 14 15 ScapegoatTreeNode(T data):data(data), size(1), cover(1), count(1){ 16 memset(next, 0, sizeof(next)); 17 } 18 19 void maintain(){ //维护大小 20 cover = 1, size = count; 21 for(int i = 0; i < 2; i++) 22 if(next[i] != NULL) 23 cover += next[i]->cover, size += next[i]->size; 24 } 25 26 int cmp(T data){ 27 if(this->data == data) return -1; 28 return (data < this->data) ? (0) : (1); 29 } 30 31 boolean bad(){ 32 for(int i = 0; i < 2; i++) 33 // if(next[i] != NULL && next[i]->cover > this->cover * factora) 34 if(next[i] != NULL && next[i]->cover > (this->cover + 3) * factora) 35 return true; 36 return false; 37 } 38 39 inline void addCount(int val){ 40 if(count == 0 && val > 0) cover += 1; 41 size += val; 42 count += val; 43 if(size == 0) cover -= 1; 44 } 45 };
首先将要重构的子树拍平(遍历一次得到一个数组),然后利用这个数组进行重构。每次的节点为这个区间的中点,然后递归调用去把[l, mid - 1]重构左子树,[mid + 1, r]重构有子树。记得在每层函数返回之前进行子树大小的维护。
可能这段文字不好帮助理解,那就把上面那棵树重构了(虽然它很平衡,但是长得太丑了)。首先中序遍历得到一个有序的数组(由于我比较懒,所以用的vector,建议保存节点的地址或下标,不要只保存数据)
找到mid,然后递归生成它的左子树和右子树:
、
为了体现当l > r时,直接return NULL,特此注明的键值为7的左子树。
1 //查找操作,因为重构后,有一些节点会消失,需要重新维护一下cover 2 static ScapegoatTreeNode<T>* find(ScapegoatTreeNode<T>*& node, T data){ 3 if(node == NULL) return NULL; 4 int d = node->cmp(data); 5 if(d == -1) return node; 6 ScapegoatTreeNode<T>* s = find(node->next[d], data); 7 node->maintain(); 8 return s; 9 } 10 11 //中序遍历得到一个数组 12 void getLis(ScapegoatTreeNode<T>*& node){ 13 if(node == NULL) return; 14 getLis(node->next[0]); 15 if(node->count > 0) lis.push_back(node); 16 getLis(node->next[1]); 17 if(node->count <= 0) delete node; 18 } 19 20 //重构的主要过程 21 ScapegoatTreeNode<T>* rebuild(int from, int end){ 22 if(from > end) return NULL; 23 if(from == end){ 24 ScapegoatTreeNode<T>* node = lis[from]; 25 node->next[0] = node->next[1] = NULL; 26 node->size = node->count; 27 node->cover = 1; 28 return node; 29 } 30 int mid = (from + end) >> 1; 31 ScapegoatTreeNode<T>* node = lis[mid]; 32 node->next[0] = rebuild(from, mid - 1); 33 node->next[1] = rebuild(mid + 1, end); 34 node->maintain(); 35 return node; 36 } 37 38 //调用 39 void rebuild(ScapegoatTreeNode<T>*& node, ScapegoatTreeNode<T>*& father){ 40 lis.clear(); 41 getLis(node); 42 ScapegoatTreeNode<T>* ret = rebuild(0, (unsigned)lis.size() - 1); 43 if(father == NULL) root = ret; 44 else{ 45 father->next[father->cmp(ret->data)] = ret; 46 find(root, ret->data); 47 } 48 }
插入操作还是按照BST的性质进行插入。只不过中途要找到最后一个极其不平衡的子树的根节点(试想一下,一遇到有问题的就重构,那么替罪羊树的复杂度会变为多少)。这个节点又叫『替罪羊节点』。其它就没有可以多说的内容了。
1 static void insert(ScapegoatTreeNode<T>*& node, T data, ScapegoatTreeNode<T>*& last, ScapegoatTreeNode<T>*& father){ 2 if(node == NULL){ 3 node = new ScapegoatTreeNode<T>(data); 4 return; 5 } 6 int d = node->cmp(data); 7 if(d == -1){ 8 node->addCount(1); 9 return; 10 } 11 insert(node->next[d], data, last, father); 12 node->maintain(); 13 if(father == NULL && last != NULL) father = node; 14 if(node->bad()) last = node, father = NULL; 15 } 16 17 void insert(T data){ 18 ScapegoatTreeNode<T>* node = NULL, *father = NULL; 19 insert(root, data, node, father); 20 if(node != NULL) rebuild(node, father); 21 }
试想一下用Treap的删除方式,然而并不存在旋转操作。如果暴力重构,时间复杂度又会太高。
如果节点的count为1,那么惰性删除(相当于打lazy标记,也就是说,当重构的时候,这个节点才被真正地删除)。否则就在count上减1就好了。
如果是项目开发之类的,最好加入一些容错处理,而不是像,只是为了去ac一道模板题。
1 static boolean remove(ScapegoatTreeNode<T>*& node, T data, ScapegoatTreeNode<T>*& last, ScapegoatTreeNode<T>*& father){ 2 if(node == NULL) return false; 3 int d = node->cmp(data); 4 if(d == -1){ 5 node->addCount(-1); 6 return true; 7 } 8 boolean res = remove(node->next[d], data, last, father); 9 if(res) node->maintain(); 10 if(father == NULL && last != NULL) father = node; 11 if(node->bad()) last = node, father = NULL; 12 return res; 13 } 14 15 boolean remove(T data){ 16 ScapegoatTreeNode<T>* node = NULL, *father = NULL; 17 boolean res = remove(root, data, node, father); 18 if(node != NULL) rebuild(node, father); 19 return res; 20 }
·各种bound
思路可以参照Splay中的思路,唯一注意一点,如果当前的节点不存在,且按照cmp指示的方向并不存在,那么就得向另外一个方向来找(之前被坑了好多好多次,除非本来就要比这个数据大,然而在右子树中没找到,就这个意思,理解了就好)。
下面以lower_bound为例:
1 static ScapegoatTreeNode<T>* upper_bound(ScapegoatTreeNode<T>*& node, T val){ 2 if(node == NULL) return node; 3 int to = node->cmp(val); 4 if(val == node->data) to = 1; 5 ScapegoatTreeNode<T>* ret = upper_bound(node->next[to], val); 6 if(to == 0 && ret == NULL) ret = upper_bound(node->next[1], val); 7 return ((ret == NULL || node->data < ret->data) && node->data > val && node->count > 0) ? (node) : (ret); 8 }
·名次操作(没有变动,参照Splay内的名次操作)
替罪羊树的思路可以算是我见过的平衡树中最简单的,然而实现起来处处被坑,处处RE。另外可以通过多次实践来调整平衡因子和bad()函数,可以不通过改变主体过程就可以做到提高效率。下面是bzoj 3224的AC完整代码,另外加有不同参数的耗时。
1 /** 2 * bzoj 3 * Problem#3224 4 * Accepted 5 * Time:500ms / 436ms 6 * Memory:2628k / 2628k 7 */ 8 /** A table for factora 9 * factora 0.75 0.7 0.69 0.68 0.67 0.65 10 * Time 436ms 388ms 384ms 398ms 388ms 444ms 11 */ 12 #include<iostream> 13 #include<fstream> 14 #include<sstream> 15 #include<cstdio> 16 #include<cstdlib> 17 #include<cstring> 18 #include<ctime> 19 #include<cctype> 20 #include<cmath> 21 #include<algorithm> 22 #include<stack> 23 #include<queue> 24 #include<set> 25 #include<map> 26 #include<vector> 27 using namespace std; 28 typedef bool boolean; 29 #define smin(a, b) (a) = min((a), (b)) 30 #define smax(as, b) (a) = max((a), (b)) 31 template<typename T> 32 inline boolean readInteger(T& u){ 33 char x; 34 int aFlag = 1; 35 while(!isdigit((x = getchar())) && x != ‘-‘ && x != -1); 36 if(x == -1) return false; 37 if(x == ‘-‘){ 38 x = getchar(); 39 aFlag = -1; 40 } 41 for(u = x - ‘0‘; isdigit((x = getchar())); u = (u << 3) + (u << 1) + x - ‘0‘); 42 ungetc(x, stdin); 43 u *= aFlag; 44 return true; 45 } 46 47 template<typename T> 48 class ScapegoatTreeNode{ 49 private: 50 static const float factora = 0.69; //平衡因子 51 public: 52 T data; 53 int size, cover; //数的个数, 实际节点个数 54 int count; 55 ScapegoatTreeNode* next[2]; 56 57 ScapegoatTreeNode():size(1), cover(1), count(1){ 58 memset(next, 0, sizeof(next)); 59 } 60 61 ScapegoatTreeNode(T data):data(data), size(1), cover(1), count(1){ 62 memset(next, 0, sizeof(next)); 63 } 64 65 void maintain(){ //维护大小 66 cover = 1, size = count; 67 for(int i = 0; i < 2; i++) 68 if(next[i] != NULL) 69 cover += next[i]->cover, size += next[i]->size; 70 } 71 72 int cmp(T data){ 73 if(this->data == data) return -1; 74 return (data < this->data) ? (0) : (1); 75 } 76 77 boolean bad(){ 78 for(int i = 0; i < 2; i++) 79 // if(next[i] != NULL && next[i]->cover > this->cover * factora) 80 if(next[i] != NULL && next[i]->cover > (this->cover + 3) * factora) //+1 384ms +3 376ms +5 448ms 81 return true; 82 return false; 83 } 84 85 inline void addCount(int val){ 86 if(count == 0 && val > 0) cover += 1; 87 size += val; 88 count += val; 89 if(size == 0) cover -= 1; 90 } 91 }; 92 93 template<typename T> 94 class ScapegoatTree{ 95 protected: 96 static void insert(ScapegoatTreeNode<T>*& node, T data, ScapegoatTreeNode<T>*& last, ScapegoatTreeNode<T>*& father){ 97 if(node == NULL){ 98 node = new ScapegoatTreeNode<T>(data); 99 return; 100 } 101 int d = node->cmp(data); 102 if(d == -1){ 103 node->addCount(1); 104 return; 105 } 106 insert(node->next[d], data, last, father); 107 node->maintain(); 108 if(father == NULL && last != NULL) father = node; 109 if(node->bad()) last = node, father = NULL; 110 } 111 112 static boolean remove(ScapegoatTreeNode<T>*& node, T data, ScapegoatTreeNode<T>*& last, ScapegoatTreeNode<T>*& father){ 113 if(node == NULL) return false; 114 int d = node->cmp(data); 115 if(d == -1){ 116 node->addCount(-1); 117 return true; 118 } 119 boolean res = remove(node->next[d], data, last, father); 120 if(res) node->maintain(); 121 if(father == NULL && last != NULL) father = node; 122 if(node->bad()) last = node, father = NULL; 123 return res; 124 } 125 126 static ScapegoatTreeNode<T>* less_bound(ScapegoatTreeNode<T>*& node, T val){ 127 if(node == NULL) return node; 128 int to = node->cmp(val); 129 if(val == node->data) to = 0; 130 ScapegoatTreeNode<T>* ret = less_bound(node->next[to], val); 131 if(to == 1 && ret == NULL) ret = less_bound(node->next[0], val); 132 return ((ret == NULL || node->data > ret->data) && node->data < val && node->count > 0) ? (node) : (ret); 133 } 134 135 static ScapegoatTreeNode<T>* upper_bound(ScapegoatTreeNode<T>*& node, T val){ 136 if(node == NULL) return node; 137 int to = node->cmp(val); 138 if(val == node->data) to = 1; 139 ScapegoatTreeNode<T>* ret = upper_bound(node->next[to], val); 140 if(to == 0 && ret == NULL) ret = upper_bound(node->next[1], val); 141 return ((ret == NULL || node->data < ret->data) && node->data > val && node->count > 0) ? (node) : (ret); 142 } 143 144 static ScapegoatTreeNode<T>* findKth(ScapegoatTreeNode<T>*& node, int k){ 145 int ls = (node->next[0] == NULL) ? (0) : (node->next[0]->size); 146 int count = node->count; 147 if(k >= ls + 1 && k <= ls + count) return node; 148 if(k <= ls) return findKth(node->next[0], k); 149 return findKth(node->next[1], k - ls - count); 150 } 151 152 static ScapegoatTreeNode<T>* find(ScapegoatTreeNode<T>*& node, T data){ 153 if(node == NULL) return NULL; 154 int d = node->cmp(data); 155 if(d == -1) return node; 156 ScapegoatTreeNode<T>* s = find(node->next[d], data); 157 node->maintain(); 158 return s; 159 } 160 public: 161 ScapegoatTreeNode<T>* root; 162 vector<ScapegoatTreeNode<T>*> lis; 163 164 ScapegoatTree():root(NULL){ } 165 166 void getLis(ScapegoatTreeNode<T>*& node){ 167 if(node == NULL) return; 168 getLis(node->next[0]); 169 if(node->count > 0) lis.push_back(node); 170 getLis(node->next[1]); 171 if(node->count <= 0) delete node; 172 } 173 174 ScapegoatTreeNode<T>* rebuild(int from, int end){ 175 if(from > end) return NULL; 176 if(from == end){ 177 ScapegoatTreeNode<T>* node = lis[from]; 178 node->next[0] = node->next[1] = NULL; 179 node->size = node->count; 180 node->cover = 1; 181 return node; 182 } 183 int mid = (from + end) >> 1; 184 ScapegoatTreeNode<T>* node = lis[mid]; 185 node->next[0] = rebuild(from, mid - 1); 186 node->next[1] = rebuild(mid + 1, end); 187 node->maintain(); 188 return node; 189 } 190 191 void rebuild(ScapegoatTreeNode<T>*& node, ScapegoatTreeNode<T>*& father){ 192 lis.clear(); 193 getLis(node); 194 ScapegoatTreeNode<T>* ret = rebuild(0, (unsigned)lis.size() - 1); 195 if(father == NULL) root = ret; 196 else{ 197 father->next[father->cmp(ret->data)] = ret; 198 find(root, ret->data); 199 } 200 } 201 202 void insert(T data){ 203 ScapegoatTreeNode<T>* node = NULL, *father = NULL; 204 insert(root, data, node, father); 205 if(node != NULL) rebuild(node, father); 206 } 207 208 boolean remove(T data){ 209 ScapegoatTreeNode<T>* node = NULL, *father = NULL; 210 boolean res = remove(root, data, node, father); 211 if(node != NULL) rebuild(node, father); 212 return res; 213 } 214 215 void out(ScapegoatTreeNode<T>*& node){ //调试使用函数,打印树 216 if(node == NULL) return; 217 cout << node->data << "(" << node->size << "," << node->cover << "," << node->count << "){"; 218 out(node->next[0]); 219 cout << ","; 220 out(node->next[1]); 221 cout << "}"; 222 } 223 224 ScapegoatTreeNode<T>* less_bound(T data){ 225 return less_bound(root, data); 226 } 227 228 ScapegoatTreeNode<T>* upper_bound(T data){ 229 return upper_bound(root, data); 230 } 231 232 ScapegoatTreeNode<T>* findKth(int k){ 233 return findKth(root, k); 234 } 235 236 inline int rank(T data){ 237 ScapegoatTreeNode<T>* p = root; 238 int r = 0; 239 while(p != NULL){ 240 int ls = (p->next[0] != NULL) ? (p->next[0]->size) : (0); 241 if(p->data == data) return r + ls + 1; 242 int d = p->cmp(data); 243 if(d == 1) r += ls + p->count; 244 p = p->next[d]; 245 } 246 return r + 1; 247 } 248 }; 249 250 int n; 251 ScapegoatTree<int> s; 252 253 void printTree(){ 254 s.out(s.root); 255 putchar(‘\n‘); 256 } 257 258 inline void solve(){ 259 int opt, x; 260 readInteger(n); 261 while(n--){ 262 readInteger(opt); 263 readInteger(x); 264 if(opt == 1) s.insert(x); 265 else if(opt == 2) s.remove(x); 266 else if(opt == 3) printf("%d\n", s.rank(x)); 267 else if(opt == 4) printf("%d\n", s.findKth(x)->data); 268 else if(opt == 5) printf("%d\n", s.less_bound(x)->data); 269 else printf("%d\n", s.upper_bound(x)->data); 270 } 271 } 272 273 int main(){ 274 solve(); 275 return 0; 276 }
标签:pen 多次 ios set 模板题 strong fstream public memory
原文地址:http://www.cnblogs.com/yyf0309/p/6298303.html