POJ 2418 各种二叉排序树
题意很明确,统计各个字符串所占总串数的百分比,暴力的话肯定超时,看了书上的题解后发现这题主要是用二叉排序树来做,下面附上n种树的代码。
简单的二叉排序树,不作任何优化(C语言版的):
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 5 typedef struct node{ 6 char name[52]; 7 struct node *lchild, *rchild; 8 int count; 9 }node; 10 11 node *root= NULL; 12 int n = 0; 13 14 void insert(node **root, char *s){ //恶心的二级指针,以后还是用指针引用来替代吧 15 if(*root==NULL) { 16 node *p = (node*)malloc(sizeof(node)); 17 strcpy(p->name,s); 18 p->lchild = p->rchild = NULL; 19 p->count = 1; 20 *root = p; //切记这个不能漏了!! 21 } 22 else { 23 int cmp= strcmp(s,((*root)->name)); 24 if(cmp==0) ((*root)->count)++; 25 else if(cmp<0) insert(&((*root)->lchild),s); 26 else insert(&((*root)->rchild),s); 27 } 28 } 29 30 void midOrder(node *root){ 31 if(root!=NULL){ 32 midOrder(root->lchild); 33 printf("%s %.4f\n",root->name,((double)(root->count)/(double)n)*100); 34 midOrder(root->rchild); 35 } 36 } 37 38 int main(){ 39 char s[52]; 40 while(gets(s)){ 41 insert(&root,s); 42 ++n; 43 } 44 midOrder(root); 45 return 0; 46 }
然后这是AVL(平衡二叉树),目前我只会插入的平衡,删除还不会,代码量还挺多的:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 using namespace std; 6 7 struct AVL 8 { 9 char name[32]; 10 int count; 11 AVL *left, *right; 12 int height; 13 }; 14 15 int n = 0; 16 17 int Height(AVL *p) { return p== NULL? -1: p->height; } 18 19 AVL* LLRotate(AVL *p) 20 { 21 AVL *p2= p->left; 22 p->left= p2->right; 23 p2->right= p; 24 p->height= max(Height(p->left), Height(p->right))+1; 25 p2->height= max(Height(p->left), p->height)+1; 26 return p2; 27 } 28 29 AVL* RRRotate(AVL *p) 30 { 31 AVL *p2= p->right; 32 p->right= p2->left; 33 p2->left= p; 34 p->height= max(Height(p->left), Height(p->right))+1; 35 p2->height= max(p->height, Height(p2->right))+1; 36 return p2; 37 } 38 39 AVL* LRRotate(AVL *p) 40 { 41 p->left= RRRotate(p->left); 42 return LLRotate(p); 43 } 44 45 AVL* RLRotate(AVL *p) 46 { 47 p->right= LLRotate(p->right); 48 return RRRotate(p); 49 } 50 51 AVL* Insert(const char *s, AVL *p) 52 { 53 if(p==NULL){ 54 p= (AVL*)malloc(sizeof(AVL)); 55 strcpy(p->name, s); 56 p->left= p->right= NULL; 57 p->height= 0; 58 p->count= 1; 59 } 60 else { 61 int cmp= strcmp(s,p->name); 62 if(cmp==0) ++(p->count); 63 else if(cmp<0){ 64 p->left= Insert(s,p->left); 65 if(Height(p->left) - Height(p->right) == 2){ 66 if(strcmp(s,p->left->name)<0) 67 p= LLRotate(p); 68 else p= LRRotate(p); 69 } 70 } 71 else if(cmp>0){ 72 p->right= Insert(s,p->right); 73 if(Height(p->right) - Height(p->left) == 2){ 74 if(strcmp(s,p->right->name)>0) 75 p= RRRotate(p); 76 else p= RLRotate(p); 77 } 78 } 79 } 80 p->height= max(Height(p->left), Height(p->right))+1; 81 return p; 82 } 83 84 void midOrder(AVL* p) 85 { 86 if(p){ 87 midOrder(p->left); 88 printf("%s %.4f\n",p->name,double(p->count)/n*100); 89 midOrder(p->right); 90 } 91 } 92 93 int main() 94 { 95 AVL *root= NULL; 96 char s[32]; 97 while(gets(s)){ 98 root= Insert(s,root); 99 ++n; 100 } 101 midOrder(root); 102 return 0; 103 }
以上两者都参考了书上的代码模板,想简单一点的话可以直接用STL中封装的很好的 map(红黑树):
1 #include<cstdio> 2 #include<map> 3 #include<iterator> 4 #include<string> 5 #include<iostream> 6 using namespace std; 7 8 int main() 9 { 10 int n =0; 11 string s; 12 map<string,int> tree; 13 while(getline(cin,s)){ //直接用cin的话空格会读不进去 14 ++tree[s]; 15 ++n; 16 } 17 map<string,int>::iterator it; 18 for(it=tree.begin(); it!=tree.end(); ++it){ 19 // cout<<it->first; 20 printf("%s %.4f\n",it->first.c_str(), it->second*100.0/n); 21 } 22 }
Treap树堆,通过随机确定的优先级来平衡二叉排序树,参考了刘汝佳大白书上的模板,旋转处的小技巧确实很巧妙:(如果单纯针对本题的话 remove 和 find 函数都可以不要,代码比AVL短一些)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<ctime> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int maxn= 32; 8 9 struct node 10 { 11 node *ch[2]; //左右子树 12 int r; //优先级 13 int s; //结点总数 14 char name[maxn]; //结点名称 15 int num; //结点出现次数 16 node(char s2[]) 17 { 18 strcpy(name,s2); 19 num= 1; 20 ch[0]= ch[1]= NULL; 21 r= rand(); 22 s= 1; 23 } 24 bool operator <(const node &n2) const { return r < n2.r; } 25 int cmp(char s2[]) const 26 { 27 int d= strcmp(name,s2); 28 if(!d) return -1; 29 return d<0? 0:1; 30 } 31 void maintain() 32 { 33 s= 1; 34 if(ch[0]!=NULL) s+= ch[0]->s; 35 if(ch[1]!=NULL) s+= ch[1]->s; 36 } 37 }; 38 39 node *treap= NULL; 40 int n= 0; 41 42 void rotate(node* &p, int d) 43 { 44 node* k= p->ch[d^1]; 45 p->ch[d^1]= k->ch[d]; 46 k->ch[d]= p; 47 p->maintain(); 48 k->maintain(); 49 p= k; 50 } 51 52 void insert(node* &p, char s2[]) 53 { 54 if(p==NULL) p= new node(s2); 55 else { 56 int d= strcmp(s2,p->name); 57 if(!d) p->num++; 58 else { 59 d= d<0? 0:1; 60 insert(p->ch[d],s2); 61 if(p->ch[d]->r > p->r) rotate(p,d^1); 62 } 63 } 64 p->maintain(); 65 } 66 67 void remove(node* &p, char s2[]) 68 { 69 if(p==NULL){ 70 puts("该结点不存在,无法删除..."); 71 return ; 72 } 73 int d= p->cmp(s2); 74 if(d==-1){ 75 node *u= p; 76 if(p->ch[0]==NULL) p= p->ch[1]; 77 else if(p->ch[1]==NULL) p= p->ch[0]; 78 else { 79 int d2= (p->ch[0]->r > p->ch[1]->r ? 1: 0); 80 rotate(p,d2); 81 remove(p->ch[d2],s2); 82 } 83 delete u; 84 } 85 else remove(p->ch[d],s2); 86 if(p!=NULL) p->maintain(); 87 } 88 89 bool find(node* p, char s2[]) 90 { 91 while(p){ 92 int d= p->cmp(s2); 93 if(d==-1) return 1; 94 else p= p->ch[d]; 95 } 96 return 0; 97 } 98 99 void midOrder(node *p) 100 { 101 if(p){ 102 midOrder(p->ch[0]); 103 printf("%s %.4f\n",p->name,double(p->num)/n*100); 104 midOrder(p->ch[1]); 105 } 106 } 107 108 int main() 109 { 110 char s[32]; 111 srand(time(NULL)); 112 while(gets(s)){ 113 insert(treap,s); 114 ++n; 115 } 116 midOrder(treap); 117 return 0; 118 }
实质上,这题也可以不用树来做,读入所有字符串并排序后,通过 lower_bound 和 upper_bound 函数的差值来依次求出每个字串的数量/百分比(这是从《挑战》书上学到的):
1 #include<cstdio> 2 #include<cstring> 3 #include<string> 4 #include<algorithm> 5 #include<iostream> 6 #include<cstdlib> 7 using namespace std; 8 string str[1000006]; 9 10 //这种方法可能相对会慢一点:2610ms,27640K,其余几种都是1000+ms 11 12 int main() 13 { 14 int i= 0; 15 while(getline(cin,str[i++])) ; 16 sort(str,str+i); 17 18 //因为最后的换行符也会读进去,所以ed要从1开始,而总数量是i-1而不是i 19 int st,ed= 1; 20 while(ed<i){ 21 st= lower_bound(str+ed, str+i, str[ed]) - str; 22 ed= upper_bound(str+ed, str+i, str[ed]) - str; 23 printf("%s %.4f\n",str[st].c_str(), double(ed-st)/(i-1)*100); 24 } 25 return 0; 26 }
这两天稍微看了看数据结构,感觉还是挺费脑子的,各种变式的二叉排序树,很容易混淆或者记不住,看来要加深理解才能熟练运用。