数据结构和算法分析(9)表栈和队列的实际应用(一)
在接下来的几篇博文中,将介绍表、栈、队列在编程实践中的应用。
(1)表达式求值:
输入一个中缀表达式,操作符包括(+ - * / ^)。转化为后缀表达式之后并计算表达式的值:
要求:
1.输入的中缀表达式必须是一个完整的字符串;
2.不限制数字的位数和正负,负数用()括起来;
代码如下:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 #define EmptyTOS (-1) 6 #define MinStackSize 5 7 8 struct StackRecord{ 9 int Capacity;//能存元素最大量 10 int TopOfStack;//记录新插入的元素所在数组中的位置 11 char Array[30][5];//字符串数组,每个字符串的大小最多为5 12 }; 13 typedef struct StackRecord *Stack; 14 15 void MakeEmpty(Stack s){ 16 s->TopOfStack=EmptyTOS; 17 } 18 Stack CreateStack(int MaxElement){ 19 Stack s; 20 if(MaxElement<MinStackSize){ 21 printf("要创建的栈太小,应大于5。\n"); 22 exit(0); 23 }else{ 24 s=malloc(sizeof(struct StackRecord)); 25 s->Capacity=MaxElement; 26 MakeEmpty(s); 27 } 28 return s; 29 } 30 //判断栈是否为空栈 31 int IsEmpty(Stack S){ 32 return S->TopOfStack==EmptyTOS; 33 } 34 //判断是否满了,当为1是满了,为0是表示未满 35 int IsFull(Stack S){ 36 if(S->TopOfStack+1>=S->Capacity){ 37 return 1; 38 }else 39 return 0; 40 } 41 //压栈 42 void Push(char *x,Stack S){ 43 if(IsFull(S)){ 44 printf("栈已经满了!\n"); 45 }else{ 46 strcpy(S->Array[++S->TopOfStack],x); 47 } 48 } 49 //只获得头元素 50 char *Top(Stack S){ 51 if(IsEmpty(S)){ 52 printf("此栈为空,无法取栈头元素!\n"); 53 exit(0); 54 }else{ 55 return S->Array[S->TopOfStack]; 56 } 57 } 58 //只删除头元素 59 void Pop(Stack S){ 60 if(IsEmpty(S)){ 61 printf("此栈为空,无法去除栈头元素!\n"); 62 }else{ 63 S->TopOfStack--; 64 } 65 } 66 //获取头元素并删除 67 char *PopAndTop(Stack S){ 68 if(IsEmpty(S)){ 69 printf("此栈为空,无法执行获取栈头元素和去除栈头元素!\n"); 70 exit(0); 71 }else{ 72 return S->Array[S->TopOfStack--]; 73 } 74 } 75 //释放栈空间 76 void DisposeStack(Stack s){ 77 if(s!=NULL){ 78 free(s); 79 } 80 } 81 void printStack(Stack s){ 82 int i; 83 for(i=0;i<=s->TopOfStack;i++){ 84 printf("%s ",s->Array[i]); 85 } 86 } 87 //位置是从栈头开始计算,所以谁小谁离栈顶比较远 88 int LastPosition(char *p,Stack temp){ 89 int i; 90 if(exist(p,temp)){ 91 for(i=temp->TopOfStack;i>=0;i--){ 92 if(strcmp(temp->Array[i],p)){ 93 return i; 94 } 95 } 96 } 97 else{ 98 printf("临时栈没有%s这个操作符\n",p); 99 return -1; 100 } 101 } 102 int IsNumber(char p){ 103 if(p>='0'&&p<='9'){ 104 return 1; 105 }else 106 return 0; 107 } 108 //由于这里允许负数的存在,所以与之前的函数不同 109 int IsOperator(char *p){//这个函数是给turnInto函数使用的 110 //当长度不为1.肯定是数字,不是操作符 111 if(p[1]!='\0'){ 112 return 0; 113 } 114 //当长度为1,且不为从0到9的数时,才是操作符 115 if(IsNumber(p[0])){ 116 return 0; 117 }else 118 return 1; 119 } 120 int exist(char *p,Stack temp){ 121 int i; 122 for(i=0;i<=temp->TopOfStack;i++){ 123 if(strcmp(temp->Array[i],p)==0){ 124 return 1; 125 } 126 } 127 return 0; 128 } 129 //用这个递归提取函数明显比以前用的顺序处理要好得多 130 void handleString(char *s,Stack s_center){ 131 //printf("即将操作的字符串%s\n",s); 132 int i=0; 133 int flag=-1;//当递归调用这个函数,上一个函数的flag会影响下一个函数的flag值,所以最好每次用时都初始化,而且函数段中每次用flag都最好先赋初值 134 char temp[5]; 135 if(s[0]=='\0'){ 136 return; 137 } 138 if(s[i]=='('&&s[i+1]=='-'){//处理类似1.(-34*76+21)-12或者2.(-43)+76这种形式的表达式字符串 139 flag=0; 140 temp[flag]='-'; 141 i=i+2;//i指到-号后的第一个数字 142 while(IsNumber(s[i])){ 143 temp[++flag]=s[i]; 144 temp[flag+1]='\0'; 145 i++; 146 }//如果数字过后的符号不是右括号,类似1 147 if(s[i]!=')'){ 148 Push("(",s_center); 149 Push(temp,s_center); 150 s=s+i; 151 handleString(s,s_center); 152 }else{//等于右括号,类似2 153 Push(temp,s_center); 154 i++; 155 s=s+i; 156 handleString(s,s_center); 157 } 158 } 159 //如果字符串的开头是是数字,就将这一串数字压栈,并将数字后的第一个操作符压栈 160 else//else不能少,这里是为了在一次调用此函数时只能执行一次操作,他们是互斥关系 161 if(IsNumber(s[i])){ 162 flag=-1; 163 while(IsNumber(s[i])){ 164 temp[++flag]=s[i]; 165 temp[flag+1]='\0'; 166 i++; 167 } 168 Push(temp,s_center); 169 s=s+i; 170 handleString(s,s_center); 171 } 172 else 173 if(!IsNumber(s[0])) { 174 temp[0]=s[0]; 175 temp[1]='\0'; 176 Push(temp,s_center); 177 handleString(s+1,s_center); 178 } 179 } 180 int Max(int a,int b){ 181 return a>b?a:b; 182 } 183 void turnInto(Stack A,Stack B){ 184 Stack s_temp=CreateStack(15); 185 int i;int max;int leftbracketPosition; 186 for(i=0;i<=A->TopOfStack;i++){ 187 //printStack(s_temp);printf("\n"); 188 //如果不是操作符,直接输出到后缀表达式 189 if(!IsOperator(A->Array[i])){ 190 strcpy(B->Array[++B->TopOfStack],A->Array[i]); 191 //printf("输出中存的有:%s\n",A->Array[i]); 192 }else{ 193 char c=A->Array[i][0]; 194 //printf("\n操作的字符是第%d个%c\n",i+1,A->Array[i][0]); 195 switch(c){ 196 case '(':{ 197 Push(A->Array[i],s_temp); 198 break; 199 } 200 case ')':{ 201 while(!strcmp( "(",Top(s_temp) )==0){ 202 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 203 } 204 Pop(s_temp); 205 break; 206 } 207 case '+': 208 case '-':{ 209 if(exist("(",s_temp)){//如果存在左括号,将左括号右侧全部运算符弹出 210 while(Top(s_temp)[0]!='('){ 211 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 212 } 213 Push(A->Array[i],s_temp); 214 }else{//如果不存在左括号,将栈中所有元素弹出 215 while(!IsEmpty(s_temp)){ 216 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 217 } 218 Push(A->Array[i],s_temp); 219 } 220 break; 221 } 222 case '*': 223 case '/':{ 224 if(IsEmpty(s_temp)){ 225 Push(A->Array[i],s_temp); 226 } 227 else{ 228 if(exist("(",s_temp)){ 229 leftbracketPosition=LastPosition("(",s_temp); 230 if(exist("/",s_temp)||exist("*",s_temp)){ 231 max=Max(LastPosition("/",s_temp),LastPosition("*",s_temp)); 232 if(max>leftbracketPosition){//表明符号在左括号右侧 233 while(Top(s_temp)[0]!='*' || Top(s_temp)[0]!='/'){ 234 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 235 } 236 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 237 Push(A->Array[i],s_temp); 238 }else{//符号在左括号左侧 239 Push(A->Array[i],s_temp); 240 } 241 }else{//存在左括号,但既不存在乘号也不存在除号,判断此时有没有乘方号 242 //如果有乘方号,且乘方号在左括号右侧 243 if(exist("^",s_temp)&&(LastPosition("^",s_temp)>leftbracketPosition)){ 244 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 245 Push(A->Array[i],s_temp); 246 }else{//不存在乘方号或者存在乘方号,但乘方号在左括号的左侧 247 Push(A->Array[i],s_temp); 248 } 249 } 250 }else{//不存在左括号时,只要临时栈中有乘号或除号就不可能再有乘方号 251 if(exist("*",s_temp)||exist("/",s_temp)){ 252 while(Top(s_temp)[0]!='*'&&Top(s_temp)[0]!='/'){ 253 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 254 } 255 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 256 Push(A->Array[i],s_temp); 257 }else{//表示既没有左括号也没有乘号或除号,此时考虑栈中有没有乘方号 258 if(exist("^",s_temp)){//如果有乘方号,肯定在栈顶 259 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 260 Push(A->Array[i],s_temp); 261 }else{ 262 Push(A->Array[i],s_temp); 263 } 264 }//不存在*或/的结束 265 }//不存在左括号的结束 266 } 267 break; 268 } 269 case '^':{ 270 if(IsEmpty(s_temp)){ 271 Push(A->Array[i],s_temp); 272 }else{ 273 //最高优先级,但临时栈中有可能还有乘方号,要把临时栈中与当前操作符有相同优先级的并在左括号的右边的符号弹出 274 if(!exist("^",s_temp)){ 275 strcpy(s_temp->Array[++s_temp->TopOfStack],A->Array[i]); 276 break; 277 }else{ 278 if(exist("(",s_temp)&& ( LastPosition("(",s_temp)<LastPosition("^",s_temp) ) ){ 279 while(Top(s_temp)[0]!='^'){ 280 //printf("%s",Top(temp)); 281 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 282 //printf("输出中存的有:%s\n",A->Array[i]); 283 } 284 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 285 //printf("输出中存的有:%s\n",B->Array[B->TopOfStack]); 286 Push(A->Array[i],s_temp); 287 //printStack(temp); 288 }else{//包括不存在左括号和存在左括号且左括号在^右边的情况 289 if(!exist("(",s_temp)){ 290 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 291 Push(A->Array[i],s_temp); 292 }else 293 strcpy(s_temp->Array[++s_temp->TopOfStack],A->Array[i]); 294 } 295 } 296 } 297 break; 298 } 299 default:printf("未知错误\n"); 300 }//switch语句的结束 301 }//如果是操作符的结束 302 }//for循环结束 303 while(!IsEmpty(s_temp)){ 304 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 305 } 306 } 307 //利用斐波那契数快速求幂 308 unsigned long FB_QiuMi(int x,int n){ 309 if(n<=2){ 310 //printf("不用求幂公式!\n"); 311 switch(n){ 312 case 1:return x; 313 case 2:return x*x; 314 } 315 } 316 //记录斐波那契数列的下标 317 int N=0; 318 int f=1; 319 int temp; 320 int flag=f+1; 321 while(flag<=n){ 322 temp=flag; 323 flag=flag+f; 324 f=temp; 325 N++; 326 } 327 int less=n-temp; 328 int i; 329 unsigned long result=x; 330 flag=x; 331 for(i=0;i<N;i++){ 332 temp=result; 333 result=result*flag; 334 flag=temp; 335 } 336 while(less>0){ 337 result=result*x; 338 less--; 339 } 340 return result; 341 } 342 void Calculate(Stack A){ 343 //定义一个辅助计算后缀表达式的栈 344 Stack as=CreateStack(10); 345 long result; 346 int a,b,temp,i; 347 char str[5]; 348 for(i=0;i<=A->TopOfStack;i++){ 349 //printf("%s\n",A->Array[i]); 350 if(!IsOperator(A->Array[i])){ 351 Push(A->Array[i],as); 352 }else{ 353 int b=atof(PopAndTop(as)); 354 int a=atoi(PopAndTop(as)); 355 char c=A->Array[i][0]; 356 //printf("a=%d,b=%d",a,b); 357 //putchar(c);printf("\n"); 358 switch(c){ 359 case '+':temp=a+b;break; 360 case '*':temp=a*b;break; 361 case '-':temp=a-b;break; 362 case '/':temp=a/b;break; 363 case '^':temp=FB_QiuMi(a,b);break; 364 default:printf("不知名错误!\n"); 365 } 366 ltoa(temp,str,10); 367 Push(str,as); 368 //printStack(as); 369 } 370 } 371 if(as->TopOfStack<=0){ 372 strcpy(str,Top(as)); 373 result=atol(str); 374 printf("计算的结果为:%ld\n",result); 375 } 376 } 377 int main(){ 378 printf("请输入一个中缀表达式:"); 379 printf("(最多40个字符的字符串,可以包含任意整数,+,-,*,/,^和()两个括号!)\n"); 380 Stack s_center=CreateStack(35); 381 Stack s_later=CreateStack(35); 382 char x[40]; 383 gets(x); 384 char *p=x; 385 handleString(p,s_center); 386 printf("此中缀表达式为:\n"); 387 printStack(s_center); 388 //将中缀表达式转化为后缀表达式 389 turnInto(s_center,s_later); 390 printf("\n这个后缀表达式是:\n"); 391 printStack(s_later);printf("\n"); 392 Calculate(s_later); 393 return 0; 394 }
与之前写的只有+和*两种运算的计算器作对比,这个程序改进了不少:
1.虽然+和*运算没有先后之分,但-和/和^运算要严格按照顺序;
2.递归地处理一个字符串,在字符串开头可以是符号"("也可以是整数的数字,也可以是负数的数字。(之前写的程序使用while循环来提取字符串,第一个字符不能为符号,只能为数字。)
3.在将中缀转化为后缀表达式时,我用的是switch语句,而之前我用的if else语句。相对而言,switch语句使逻辑结构要更加清晰。
(2)符号匹配:
栈在计算机语言的编译过程中用来进行语法检查,试编写一个算法,用来检查一个C/C++语言程序源文件中的大括号、方括号和圆括号是否匹配。
要求:
1.若能够全部匹配则返回1,否则返回0。
2.成对的符号有(),[],{},//,/* */。
3.如果符号不匹配,输出错误信息。
4.如果符号匹配,输出一遍正确的代码(注释的内容去掉,无效的空行去掉)。
1 /* 2 1.在处理源码文件时,把去掉注释并去掉空行的源码输出到新建的文件中 3 2.在用栈处理文件字符匹配的过程中,采用栈的链表实现,链表带有头结点。 4 3.需要进行匹配的有(),<>,{},[],//这些符号,其中/符号不在注释里时必须成对出现。 5 4.自定义readline()从文件中读一行。 6 */ 7 /*5.当前注释符号中不能再次包含此符号,否则编译器会提示语法错误。*/ 8 9 #include <stdio.h> 10 #include <string.h> 11 #include <stdlib.h> 12 13 //存无注释源码的文件名 14 char *newfile="F:/VS/NoAnnotationSoundCode.txt"; 15 16 struct SymbolNode{ 17 char handlesymbol; 18 struct SymbolNode *Next; 19 }; 20 21 typedef struct SymbolNode *Symbol; 22 typedef struct SymbolNode *PtrToNode; 23 typedef PtrToNode Stack; 24 25 Stack createStack(){ 26 Stack symbolstack=malloc(sizeof(struct SymbolNode)); 27 symbolstack->Next=NULL; 28 return symbolstack; 29 } 30 Symbol TopAndPop(Stack test_stack){ 31 PtrToNode temp=test_stack->Next; 32 test_stack->Next=test_stack->Next->Next; 33 return temp; 34 } 35 Symbol Top(Stack test_stack){ 36 return test_stack->Next; 37 } 38 void Push(char p,Stack test_stack){ 39 //printf("%c进栈",p); 40 PtrToNode temp=malloc(sizeof(struct SymbolNode)); 41 temp->handlesymbol=p; 42 temp->Next=test_stack->Next; 43 test_stack->Next=temp; 44 } 45 void Pop(Stack test_Stack){ 46 PtrToNode temp=test_Stack->Next; 47 test_Stack->Next=test_Stack->Next->Next; 48 //printf("%c出栈\n",temp->handlesymbol); 49 free(temp); 50 } 51 int IsEmpty(Stack test_stack){ 52 return test_stack->Next==NULL; 53 } 54 int IsQuotationAround(char *p,int i){ 55 if(p[i+1]=='\''&&p[i-1]=='\'' || p[i+1]=='\"'&&p[i-1]=='\"' ){ 56 return 1; 57 }else 58 return 0; 59 } 60 char *Read_Line(FILE *fp){ 61 int lines=100; 62 char *str=malloc(sizeof(char)*100); 63 fgets(str,lines,fp); 64 //printf("读取的字符串是%s\n",str); 65 return str; 66 } 67 int totalSpaceOrTableOrAnotation(char *p){ 68 int i=0; 69 //printf("当前操作的字符串是%sEOF\n",p); 70 //printf("当前字符串的长度是%d\n",strlen(p)); 71 while(p[i]==' '||p[i]=='\t') 72 i++; 73 if(p[i]=='\n'){//在每行一开始就是\n肯定是无效行 74 return 1; 75 }else{ 76 if(p[i]=='/'&&p[i+1]=='/') 77 return 1; 78 else 79 return 0; 80 } 81 } 82 void printStack(Stack s){ 83 PtrToNode ptr=s->Next; 84 while(ptr!=NULL){ 85 putchar(ptr->handlesymbol); 86 ptr=ptr->Next; 87 } 88 } 89 int symbolMatch(char *p){ 90 FILE *fp; 91 int linenumber=0;//记录行数 92 //首先新建一个存无注释源码的文件 93 FILE *outputFilePoint=fopen(newfile,"w"); 94 if(outputFilePoint==NULL){ 95 printf("无法新建存源码的文件"); 96 return 0; 97 } 98 Stack temp_stack=createStack(15);//用于存需要进行匹配的符号。 99 if((fp=fopen(p,"r"))!=NULL){ 100 char *p;//指向读取的每一行的字符串 101 int i; 102 while(!feof(fp)){ 103 p=Read_Line(fp); 104 //printf("%d.输出栈中的元素:",linenumber); 105 linenumber++; 106 //printStack(temp_stack); 107 //printf("\n"); 108 if(totalSpaceOrTableOrAnotation(p)){//如果是空行或者只有注释的行,丢掉。 109 continue; 110 }else{ 111 i=0;//每一行都要重新初始化,防止上一行的对此行产生影响 112 while(!( p[i]=='\0' && !IsQuotationAround(p,i) ) ){//当字符不是每行的末尾时 113 //putchar(p[i]); 114 //处理栈中为*号的情况。 115 if(Top(temp_stack)!=NULL&&Top(temp_stack)->handlesymbol=='*'){ 116 if(p[i]!=Top(temp_stack)->handlesymbol){ 117 i=i+1; 118 continue;} 119 else{ 120 if(p[i+1]!='/'){ 121 i++; 122 continue;} 123 else{ 124 Pop(temp_stack); 125 i=i+2; 126 } 127 } 128 } 129 //当不属于/*注释范围时。 130 //putchar(p[i]); 131 switch(p[i]){ 132 case '/'://不需要压入栈中进行匹配 133 { 134 if(p[i+1]=='/'){ 135 fputc('\n',outputFilePoint); 136 if(!feof(fp)){//以后的为无效注释文本,忽略,开始下一行 137 p=Read_Line(fp);i=0; 138 linenumber++; 139 while(totalSpaceOrTableOrAnotation(p)){ 140 p=Read_Line(fp); 141 linenumber++; 142 } 143 } 144 }else if(p[i+1]=='*'){ 145 Push('*',temp_stack); 146 i++; 147 }else{ 148 fputc(p[i],outputFilePoint); 149 } 150 }break; 151 case '{': 152 { 153 if(!IsQuotationAround(p,i)) 154 Push(p[i],temp_stack); 155 fputc(p[i],outputFilePoint); 156 }break; 157 case '}': 158 { 159 if(!IsQuotationAround(p,i)){ 160 if(Top(temp_stack)->handlesymbol=='{'){ 161 Pop(temp_stack); 162 }else 163 printf("在源码的%d行出现错误\n",linenumber); 164 } 165 fputc(p[i],outputFilePoint); 166 }break; 167 case '(': 168 { 169 if(!IsQuotationAround(p,i)) 170 Push(p[i],temp_stack); 171 fputc(p[i],outputFilePoint); 172 }break; 173 case ')': 174 { 175 if(!IsQuotationAround(p,i)){ 176 if(Top(temp_stack)->handlesymbol=='('){ 177 Pop(temp_stack); 178 }else{ 179 printf("在源码的%d行出现错误\n",linenumber); 180 } 181 } 182 fputc(p[i],outputFilePoint); 183 }break; 184 case '[': 185 { 186 if(!IsQuotationAround(p,i)) 187 Push(p[i],temp_stack); 188 fputc(p[i],outputFilePoint); 189 }break; 190 case ']': 191 { 192 if(!IsQuotationAround(p,i)){ 193 if(Top(temp_stack)->handlesymbol=='['){ 194 Pop(temp_stack); 195 }else{ 196 printf("在源码的%d行出现错误\n",linenumber); 197 } 198 } 199 fputc(p[i],outputFilePoint); 200 }break; 201 default: 202 { 203 if(!(i>0&&p[i-1]=='/'&&p[i-2]=='*')){ 204 fputc(p[i],outputFilePoint); 205 } 206 } 207 }//对每个字符进行处理的结束 208 i++; 209 }//当字符指针没有到行尾的while语句的结束 210 }//此行为有效行的else语句的结束 211 }//打开文件语句的结束 212 }else{ 213 printf("打开文件%s失败!\n",p); 214 } 215 fclose(fp); 216 fclose(outputFilePoint);//打开文件得指针一定要关闭,否则会影响后面的读写。 217 if(IsEmpty(temp_stack)){ 218 return 1; 219 }else{ 220 return 0; 221 } 222 } 223 void printFile(char *p){ 224 printf("\n--------------------%s---------------------\n",p); 225 FILE *fp=NULL; 226 if((fp=fopen(p,"r"))==NULL){ 227 printf("打开文件失败"); 228 exit(0); 229 }else{ 230 printf("打开文件成功!\n"); 231 //用来存读到的东西 232 char ch; 233 while(!feof(fp)){ 234 ch=fgetc(fp); 235 putchar(ch); 236 } 237 } 238 fclose(fp); 239 } 240 int main(){ 241 char *file="SymbolMatch.c"; 242 FILE *fp=NULL; 243 //如果此源码文件没有语法错误,就打印出去掉所有注释的源码文件 244 if(symbolMatch(file)){ 245 printFile(newfile); 246 }else{ 247 printf("符号匹配出现错误!\n"); 248 } 249 return 0; 250 }
上面这张图片是程序运行的结果的部分截屏。
在本地目录的F:/VS/NoAnnotationSoundCode.txt下存有上面源码去掉注释和空行的版本:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 char *newfile="F:/VS/NoAnnotationSoundCode.txt"; 5 struct SymbolNode{ 6 char handlesymbol; 7 struct SymbolNode *Next; 8 }; 9 typedef struct SymbolNode *Symbol; 10 typedef struct SymbolNode *PtrToNode; 11 typedef PtrToNode Stack; 12 Stack createStack(){ 13 Stack symbolstack=malloc(sizeof(struct SymbolNode)); 14 symbolstack->Next=NULL; 15 return symbolstack; 16 } 17 Symbol TopAndPop(Stack test_stack){ 18 PtrToNode temp=test_stack->Next; 19 test_stack->Next=test_stack->Next->Next; 20 return temp; 21 } 22 Symbol Top(Stack test_stack){ 23 return test_stack->Next; 24 } 25 void Push(char p,Stack test_stack){ 26 PtrToNode temp=malloc(sizeof(struct SymbolNode)); 27 temp->handlesymbol=p; 28 temp->Next=test_stack->Next; 29 test_stack->Next=temp; 30 } 31 void Pop(Stack test_Stack){ 32 PtrToNode temp=test_Stack->Next; 33 test_Stack->Next=test_Stack->Next->Next; 34 free(temp); 35 } 36 int IsEmpty(Stack test_stack){ 37 return test_stack->Next==NULL; 38 } 39 int IsQuotationAround(char *p,int i){ 40 if(p[i+1]=='\''&&p[i-1]=='\'' || p[i+1]=='\"'&&p[i-1]=='\"' ){ 41 return 1; 42 }else 43 return 0; 44 } 45 char *Read_Line(FILE *fp){ 46 int lines=100; 47 char *str=malloc(sizeof(char)*100); 48 fgets(str,lines,fp); 49 return str; 50 } 51 int totalSpaceOrTableOrAnotation(char *p){ 52 int i=0; 53 while(p[i]==' '||p[i]=='\t') 54 i++; 55 if(p[i]=='\n'){ 56 return 1; 57 }else{ 58 if(p[i]=='/'&&p[i+1]=='/') 59 return 1; 60 else 61 return 0; 62 } 63 } 64 void printStack(Stack s){ 65 PtrToNode ptr=s->Next; 66 while(ptr!=NULL){ 67 putchar(ptr->handlesymbol); 68 ptr=ptr->Next; 69 } 70 } 71 int symbolMatch(char *p){ 72 FILE *fp; 73 int linenumber=0; 74 FILE *outputFilePoint=fopen(newfile,"w"); 75 if(outputFilePoint==NULL){ 76 printf("无法新建存源码的文件"); 77 return 0; 78 } 79 Stack temp_stack=createStack(15); 80 if((fp=fopen(p,"r"))!=NULL){ 81 char *p; 82 int i; 83 while(!feof(fp)){ 84 p=Read_Line(fp); 85 linenumber++; 86 if(totalSpaceOrTableOrAnotation(p)){ 87 continue; 88 }else{ 89 i=0; 90 while(!( p[i]=='\0' && !IsQuotationAround(p,i) ) ){ 91 if(Top(temp_stack)!=NULL&&Top(temp_stack)->handlesymbol=='*'){ 92 if(p[i]!=Top(temp_stack)->handlesymbol){ 93 i=i+1; 94 continue;} 95 else{ 96 if(p[i+1]!='/'){ 97 i++; 98 continue;} 99 else{ 100 Pop(temp_stack); 101 i=i+2; 102 } 103 } 104 } 105 switch(p[i]){ 106 case '/': 107 { 108 if(p[i+1]=='/'){ 109 fputc('\n',outputFilePoint); 110 if(!feof(fp)){ 111 p=Read_Line(fp);i=0; 112 linenumber++; 113 while(totalSpaceOrTableOrAnotation(p)){ 114 p=Read_Line(fp); 115 linenumber++; 116 } 117 } 118 }else if(p[i+1]=='*'){ 119 Push('*',temp_stack); 120 i++; 121 }else{ 122 fputc(p[i],outputFilePoint); 123 } 124 }break; 125 case '{': 126 { 127 if(!IsQuotationAround(p,i)) 128 Push(p[i],temp_stack); 129 fputc(p[i],outputFilePoint); 130 }break; 131 case '}': 132 { 133 if(!IsQuotationAround(p,i)){ 134 if(Top(temp_stack)->handlesymbol=='{'){ 135 Pop(temp_stack); 136 }else 137 printf("在源码的%d行出现错误\n",linenumber); 138 } 139 fputc(p[i],outputFilePoint); 140 }break; 141 case '(': 142 { 143 if(!IsQuotationAround(p,i)) 144 Push(p[i],temp_stack); 145 fputc(p[i],outputFilePoint); 146 }break; 147 case ')': 148 { 149 if(!IsQuotationAround(p,i)){ 150 if(Top(temp_stack)->handlesymbol=='('){ 151 Pop(temp_stack); 152 }else{ 153 printf("在源码的%d行出现错误\n",linenumber); 154 } 155 } 156 fputc(p[i],outputFilePoint); 157 }break; 158 case '[': 159 { 160 if(!IsQuotationAround(p,i)) 161 Push(p[i],temp_stack); 162 fputc(p[i],outputFilePoint); 163 }break; 164 case ']': 165 { 166 if(!IsQuotationAround(p,i)){ 167 if(Top(temp_stack)->handlesymbol=='['){ 168 Pop(temp_stack); 169 }else{ 170 printf("在源码的%d行出现错误\n",linenumber); 171 } 172 } 173 fputc(p[i],outputFilePoint); 174 }break; 175 default: 176 { 177 if(!(i>0&&p[i-1]=='/'&&p[i-2]=='*')){ 178 fputc(p[i],outputFilePoint); 179 } 180 } 181 } 182 i++; 183 } 184 } 185 } 186 }else{ 187 printf("打开文件%s失败!\n",p); 188 } 189 fclose(fp); 190 fclose(outputFilePoint); 191 if(IsEmpty(temp_stack)){ 192 return 1; 193 }else{ 194 return 0; 195 } 196 } 197 void printFile(char *p){ 198 printf("\n--------------------%s---------------------\n",p); 199 FILE *fp=NULL; 200 if((fp=fopen(p,"r"))==NULL){ 201 printf("打开文件失败"); 202 exit(0); 203 }else{ 204 printf("打开文件成功!\n"); 205 char ch; 206 while(!feof(fp)){ 207 ch=fgetc(fp); 208 putchar(ch); 209 } 210 } 211 fclose(fp); 212 } 213 int main(){ 214 char *file="SymbolMatch.c"; 215 FILE *fp=NULL; 216 if(symbolMatch(file)){ 217 printFile(newfile); 218 }else{ 219 printf("符号匹配出现错误!\n"); 220 } 221 return 0; 222 }
你可以用这个程序来获得不带注释的源码,而且还能进行符号的匹配。
(3)进制转换:
把十进制整数转换为二至十六之间的任意进制输出。
算法原理:N=(N div d)*d+N mod d(其中:div为整除操作,mod为求余操作)。
例如:(1348)10=(2504)8
N | N div 8 | N mod 8 |
1348 | 168 | 4 |
168 | 21 | 0 |
21 | 2 | 5 |
2 | 0 | 2 |
1 #include <stdio.h> 2 #include <stdlib.h> 3 struct numNode{ 4 char *Array; 5 int Capicity; 6 int TopOfStack; 7 }; 8 typedef struct numNode *PtrToNode; 9 typedef PtrToNode Stack; 10 Stack create(int size){ 11 Stack tempStack=malloc(sizeof(struct numNode)); 12 if(size<20) 13 size=20; 14 tempStack->Array=malloc(sizeof(char)*size); 15 tempStack->Capicity=size; 16 tempStack->TopOfStack=-1; 17 return tempStack; 18 } 19 char Top(Stack s){ 20 return s->Array[s->TopOfStack]; 21 } 22 char TopAndPop(Stack s){ 23 if(s->TopOfStack<0){ 24 printf("栈为空!\n"); 25 }else{ 26 return s->Array[s->TopOfStack--]; 27 } 28 } 29 int isFull(Stack s){ 30 if(s->TopOfStack+1<s->Capicity){ 31 return 0; 32 }else{ 33 return 1; 34 } 35 } 36 int isEmpty(Stack s){ 37 return s->TopOfStack==-1; 38 } 39 void Push(int element,Stack s){ 40 if(element>16||element<0){ 41 printf("出现错误"); 42 } 43 char ch; 44 switch(element){ 45 case 15:ch='f';break; 46 case 14:ch='e';break; 47 case 13:ch='d';break; 48 case 12:ch='c';break; 49 case 11:ch='b';break; 50 case 10:ch='a';break; 51 default:ch=(char)(48+element); 52 } 53 if(!isFull(s)){ 54 s->Array[++s->TopOfStack]=ch; 55 }else{ 56 printf("栈空间已满!\n"); 57 } 58 } 59 void Pop(Stack s){ 60 s->TopOfStack--; 61 } 62 char *Conversion(long element,int n){ 63 Stack tempStack=create(25); 64 //printf("%d",element); 65 while(element){ 66 Push(element%n,tempStack); 67 element=element/n; 68 } 69 char *temp; 70 int i=0; 71 temp=malloc(sizeof(char)*25); 72 while(!isEmpty(tempStack)) 73 temp[i++]=TopAndPop(tempStack); 74 temp[i]='\0'; 75 return temp; 76 } 77 int main(){ 78 long element; 79 scanf("%d",&element); 80 printf("%d转化为2进制是:%s\n",element,Conversion(element,2)); 81 printf("%d转化为8进制是:%s\n",element,Conversion(element,8)); 82 printf("%d转化为10进制是:%s\n",element,Conversion(element,10)); 83 printf("%d转化为16进制是:%s\n",element,Conversion(element,16)); 84 return 0; 85 }
(4)栈与递归的综合案例--n阶汉诺塔(Tower of Hanoi)问题:
有三个分别为A,B,C的塔座,在塔座A上插有n个直径大小不同的圆盘,依小到大进行编号(1~n)。现在要求将A上的n个圆盘移至塔座Z上并仍按同样顺序叠排,圆盘移动时必须遵循下列规则:
(1)每次只能移动一个圆盘;
(2)圆盘可以在A,B,C任意一格塔座上;
(3)任何时刻都不能将一个较大的圆盘压在较小的圆盘之上。
试编写一个程序实现输出搬运过程和最终搬运结果。
算法:把n个圆盘的问题转化为两个n-1个圆盘的问题,当n=1时不需要再递归,只需要直接移动即可。
1 //汉诺塔问题的递归解法 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <time.h> 5 struct HnNodeLeader{ 6 char name; 7 struct HnNode *Next; 8 }; 9 struct HnNode{ 10 int element; 11 struct HnNode *Next; 12 }; 13 typedef struct HnNode *PtrToNode; 14 typedef struct HnNodeLeader *Stack; 15 Stack createStack(char ch){ 16 Stack tempStack=malloc(sizeof(struct HnNodeLeader)); 17 tempStack->name=ch; 18 tempStack->Next=NULL; 19 return tempStack; 20 } 21 void Push(int x,Stack s){ 22 PtrToNode temp=malloc(sizeof(struct HnNode)); 23 temp->element=x; 24 temp->Next=s->Next; 25 s->Next=temp; 26 } 27 int IsEmpty(Stack s){ 28 return s->Next==NULL; 29 } 30 PtrToNode TopAndPop(Stack s){ 31 if(IsEmpty(s)){ 32 return NULL; 33 } 34 PtrToNode temp=s->Next; 35 s->Next=s->Next->Next; 36 return temp; 37 } 38 PtrToNode Top(Stack s){ 39 return s->Next; 40 } 41 int TotalNumOfStack(Stack s){ 42 PtrToNode temp=s->Next; 43 int i=0; 44 while(temp!=NULL){ 45 i++; 46 temp=temp->Next; 47 } 48 return i; 49 } 50 void Move(Stack local,Stack target){ 51 Push(TopAndPop(local)->element,target); 52 } 53 void printAndFreeStack(Stack temp){ 54 int i=0; 55 while(!IsEmpty(temp)){ 56 printf("%4d",TopAndPop(temp)->element); 57 i++; 58 if(i%20==0){ 59 printf("\n"); 60 } 61 } 62 } 63 //把a栈中的n个元素通过b栈送到c栈上 64 void TransferHanNoi(int n,Stack a,Stack b,Stack c){ 65 if(n==1){ 66 Move(a,c); 67 printf("圆盘%2d从栈%c移动到栈%c\n",Top(c)->element,a->name,c->name); 68 return; 69 } 70 TransferHanNoi(n-1,a,c,b);//把n-1个元素从a移动到b 71 Move(a,c);//再把a中的一个元素移动到c中。 72 printf("圆盘%2d从栈%c移动到栈%c\n",Top(c)->element,a->name,c->name); 73 TransferHanNoi(n-1,b,a,c);//再移动b中的所有元素到c中 74 } 75 int main(){ 76 srand(time(NULL)); 77 //数值太大,非常耗时间。所以测试数要非常小。 78 int testNum=rand()%13+1; 79 printf("测试汉诺塔圆盘数为%d时候的转移问题:\n",testNum); 80 Stack A=createStack('A'); 81 Stack B=createStack('B'); 82 Stack C=createStack('C'); 83 int i; 84 for(i=1;i<=testNum;i++){ 85 Push(i,A); 86 } 87 TransferHanNoi(TotalNumOfStack(A),A,B,C); 88 printAndFreeStack(C); 89 return 0; 90 }
(5)循环链表与贪婪算法的综合案例--Josephus问题:
N个人从1到N编号,围坐成一个圆圈。从一号开始传递一个热土豆。经过M次传递后拿着热土豆的人将被清除离座,围坐的圆圈锁紧,由坐在被清除的人后面的人拿起热土豆继续进行游戏。最后剩下的人获得胜利。
试编写一程序得出胜利者的编号:
算法:构造循环链表,模拟n个人围成的圈,然后依次删除需要删除的元素。每次删除一个元素后,圈更新,问题又重新开始:
1 //用循环链表 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <time.h> 5 6 struct PersonNode{ 7 int Num; 8 struct PersonNode *Next; 9 }; 10 typedef struct PersonNode *List; 11 typedef struct PersonNode *PtrToNode; 12 13 List createList(){ 14 List tempList=malloc(sizeof(struct PersonNode)); 15 tempList->Next=NULL; 16 return tempList; 17 } 18 int IsEmpty(List list){ 19 if(list->Next==NULL) 20 return 1; 21 else 22 return 0; 23 } 24 void InsertIntoList(int n,List list){ 25 //创建临时结点 26 PtrToNode temp=malloc(sizeof(struct PersonNode)); 27 temp->Num=n; 28 //找到位置 29 if(IsEmpty(list)){ 30 list->Next=temp; 31 temp->Next=list->Next; 32 return; 33 } 34 PtrToNode p1=list->Next; 35 while(p1->Next!=list->Next){ 36 p1=p1->Next; 37 } 38 temp->Next=list->Next; 39 p1->Next=temp; 40 } 41 int ClearProcess(List list,int M){ 42 int flag=1; 43 if(IsEmpty(list)){ 44 printf("表为空!\n"); 45 return -1; 46 } 47 int i; 48 PtrToNode ptr=list->Next; 49 //当循环链表中元素不止一个时,继续删除 50 while(ptr!=ptr->Next){ 51 //当M=0时,找到当前结点前一个 52 if(M==0){ 53 PtrToNode p=ptr->Next; 54 while(p->Next!=ptr) 55 p=p->Next; 56 ptr=p; 57 }else{ 58 for(i=0;i<M-1;i++) 59 ptr=ptr->Next; 60 } 61 //处理过程 62 PtrToNode temp=ptr->Next; 63 ptr->Next=ptr->Next->Next; 64 printf("%2d.游戏者%2d出局! ",flag,temp->Num); 65 if(flag%5==0) 66 printf("\n"); 67 flag++; 68 free(temp); 69 ptr=ptr->Next; 70 } 71 return ptr->Num; 72 } 73 void printList(List list){ 74 int flag=0; 75 if(IsEmpty(list)){ 76 printf("链表为空"); 77 return; 78 } 79 PtrToNode ptr=list->Next; 80 while(ptr->Next!=list->Next){ 81 flag++; 82 printf("%3d",ptr->Num); 83 ptr=ptr->Next; 84 if(flag%30==0) 85 printf("\n"); 86 } 87 printf("%3d",ptr->Num); 88 printf("\n"); 89 } 90 int main(){ 91 srand(time(NULL)); 92 int N,M; 93 int i; 94 N=rand()%100+1;//人数不超过100 95 M=rand()%50;//传递次数 96 List list=createList(); 97 for(i=0;i<N;i++){ 98 InsertIntoList(i+1,list); 99 } 100 printf("N的大小为%d,M的大小为%d\n",N,M); 101 //printList(list); 102 int winerNum=ClearProcess(list,M); 103 printf("\n获胜者是%d",winerNum); 104 return 0; 105 }