所用知识:C语言,堆栈操作
算法思想来自慕课浙江大学《数据结构》陈老师,何老师
笔记:
1.堆栈:
1.1 引子
一种数据结构,在函数调用,表达式求值等都有广泛的应用
中缀表达式:a+b*c-d/e:生活中经常使用,但是计算机不好识别
后缀表达式:abc*+dc/-:生活中不好使用,但计算机容易识别
例:
总结:因此需要一种存储方法,能顺序的存储运算数,在需要时倒叙输出。-->堆栈有
堆栈的特点:后入先出。
1.2堆栈的操作
操作集:长度为maxSize的堆栈S ==> stack,堆栈的元素elementType,那么如下的操作
stack createStack(int maxSize);//生成空堆栈,其最大长度为maxSize
int isFull(stack s,int maxSize);//判断栈s是否为空
void push(stack s,elementType item);//将元素压入堆栈
int isEmpty(stack s);//判断堆栈是否为空
elementType pop(stack s);//删除并返回栈顶元素
1.2.1
用数组来表示堆栈的方法可以参照文件夹下面的同级目录的文件stackArray.c
同时还有使用一个数组来表示两个堆栈,twoStack.c
1.2.2使用链表来模拟堆栈
因为链表有两头,但是堆栈的头指针我们会选择链表的头节点,因为便于删除操作,尾节点不方便进行删除操作
下面是模拟的示意图
head(头指针,没有元素的) --> node1(top指针) --> node2(堆栈中的元素) --> node3 --> node4
代码可以见:stackLinkList.c
1.2.3 利用堆栈进行表达式求值
我们平时所见到的表达式都是中缀表达式,但是如果想求表达式的值,先把中缀表达着中文后缀表达式,
在利用堆栈对后缀表达式进行求值
中缀表达式转后缀表达式的思路;
首先看一个简单的:
中缀表达式:2 + 9 / 3 - 5 后缀表达式:2 9 3 / + 5 -
通过对比我们发现,中缀表达式和后缀表达式的数字顺序并没有发生改变,只是符号位置发生了改变
所以,我们可以构思,使用一个栈来存在运算符,遇到数字就输出,遇到符号就压栈,如果读入的符号比栈顶的符号
优先级高,就压栈,否则就弹栈并输出
下面是中缀表达式:2 + 9 / 3 - 5压栈和弹栈的具体步骤
输出 堆栈内元素
2
+
9
/(优先级比+高,压栈)
3
/ + -(首先弹出/,但是因为同级运行需要按照从左往右的运算,所以+也需要弹栈)
5 -
- (元素全部读取完毕以后,-弹栈)
所以中缀表达式:2 + 9 / 3 - 5的后缀表达式为 2 9 3 / + 5 -
但是如果遇到带有()的表达式怎么办:遇到"("把"("压入栈中,此时栈中的"("运算级别最低,在按照上面的运算符的运算规则来
进行压栈和弹栈。直到读取到")",才把"("弹栈,但是对"("和")"不进行输出
举个例子:
求下面中缀表达式:2 * (9+6/3-5)+4的后缀表达式
输出 堆栈内元素(top->bottom)
2
*
( *
9 + ( *
6
/ + ( *
3
- ( *(首先弹出/,但是因为同级运行需要按照从左往右的运算,所以+也需要弹栈,并输出)
/ +
5
- *(遇到")",把栈里依次弹出,直到遇到第一个"(",但是对"("和")"不进行输出)
* +(遇到+,先弹出*,然后输出)
4 +
+ NULL(没有元素可读,弹出堆栈中最后一个元素)
所以中缀表达式:2 * (9+6/3-5)+4的后缀表达式为: 2 9 6 3 / + 5 - * 4 +
代码实现
思路:中缀表达式先转后缀表达式在求值
只支持0-9之间的数字的+-*/以及(),支持括号里面嵌套括号,如3*(2*3-4*1+(3*5)/3)*3+2
不支持分开的括号3*(2*3-4*1+(3*5)/3)*3+2+(4/2)
如果有给出修改意见,感激不尽。写的比较匆忙,代码的复用性和注释的完整性可能不是很好

1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<math.h> 5 #include<ctype.h> 6 #define MAXSIZE 100 7 8 9 #include<stdio.h> 10 #include<stdlib.h> 11 /*使用链表来模拟堆栈,*/ 12 13 14 15 /*=======================================================定义double类型的堆栈链式存储并进行相关的压栈,弹栈等操作===============================================================*/ 16 17 //定义double类型的堆栈的链式存储 18 typedef struct node3{ 19 double element; 20 struct node3 *next; 21 }integerSta,*pIntegerStack; 22 23 //构造一个double类型的链式栈 24 pIntegerStack createIntegerEmptyStack(){ 25 pIntegerStack stack; 26 stack = (pIntegerStack)malloc(sizeof(integerSta)); 27 if(stack){ 28 stack->next=NULL; 29 } 30 return stack; 31 } 32 33 //判断double类型的链式栈是否为空 34 int isIntegerEmpty(pIntegerStack stack){ 35 if(stack->next==NULL){ 36 return 1; 37 }else{ 38 return 0; 39 } 40 } 41 42 //压栈 43 void pushInteger(pIntegerStack stack,double element){ 44 pIntegerStack node = (pIntegerStack)malloc(sizeof(integerSta)); 45 node->element=element; 46 node->next=stack->next; 47 stack->next=node; 48 } 49 50 //弹栈 51 double popInteger(pIntegerStack stack){ 52 double element; 53 pIntegerStack topHead; 54 if(isEmpty(stack)){ 55 printf("the stack is empty,can not pop"); 56 return; 57 }else{ 58 topHead = stack->next; 59 stack->next = topHead->next; 60 element = topHead->element; 61 free(topHead); 62 return element; 63 } 64 } 65 66 //取栈顶元素 67 double getIntegetStackTopElement(pIntegerStack stack){ 68 return (stack->next->element); 69 } 70 71 //打印栈内元素 72 void toStringInteger(pIntegerStack stack){ 73 pIntegerStack top = stack->next; 74 printf("toString:"); 75 while(top!=NULL){ 76 printf("%f ",top->element); 77 top=top->next; 78 } 79 printf("\n"); 80 } 81 82 83 84 85 /*================================================定义一个存储字符的线性表=========================================================================*/ 86 87 88 89 //构造一个线性表存储后缀表达式 90 typedef struct node2{ 91 char data[MAXSIZE]; 92 int length; 93 }li,*pList; 94 95 96 97 //初始化线性表 98 pList createEmptyList(){ 99 pList p; 100 p = (pList)malloc(sizeof(li)); 101 if(p){ 102 p->length=-1; 103 } 104 return p; 105 } 106 107 //向线性表中插入元素 108 void insert(pList list,char c){ 109 if(list){ 110 list->data[++(list->length)]=c; 111 } 112 } 113 114 //将线性表中的元素打印 115 void toStringList(pList list){ 116 int i; 117 for(i=0;i<=list->length;i++){ 118 printf("%c ",list->data[i]); 119 } 120 printf("\n"); 121 122 } 123 124 125 126 /*==================================================定义一个字符栈并进行相关操作=================================================================*/ 127 //定义字符栈 128 typedef struct node{ 129 char element; 130 struct node *next; 131 }sta,*pStack; 132 133 //创建一个空的字符链式栈 134 pStack createEmptyStack(){ 135 pStack stack; 136 stack = (pStack)malloc(sizeof(sta)); 137 if(stack){ 138 stack->next=NULL; 139 } 140 return stack; 141 } 142 143 //判断链式栈是否为空 144 int isEmpty(pStack stack){ 145 if(stack->next==NULL){ 146 return 1; 147 }else{ 148 return 0; 149 } 150 } 151 152 //把元素压入栈 153 void push(pStack stack,char element){ 154 pStack node = (pStack)malloc(sizeof(sta)); 155 node->element=element; 156 node->next=stack->next; 157 stack->next=node; 158 } 159 160 //把元素弹栈 161 char pop(pStack stack){ 162 char element; 163 pStack topHead; 164 if(isEmpty(stack)){ 165 printf("the stack is empty,can not pop"); 166 return NULL; 167 }else{ 168 topHead = stack->next; 169 stack->next = topHead->next; 170 element = topHead->element; 171 free(topHead); 172 return element; 173 } 174 } 175 176 //获取栈顶元素 177 char getTopElement(pStack stack){ 178 if(isEmpty(stack)){ 179 printf("the stack is empty,can not get top element"); 180 return; 181 }else{ 182 return (stack->next->element); 183 } 184 } 185 186 187 188 /*=============================================中缀表达式转后缀表达式=====================================================================*/ 189 190 191 //打印字符数组 192 void charArrayToString(char *arr){ 193 char *p = arr; 194 while(*p!='\0'){ 195 printf("%c ",*p); 196 p = p+1; 197 } 198 } 199 200 201 202 /* 203 判断字符c是否存在字符数组arr中 204 存在,返回1 205 不存在,返回0 206 */ 207 int isCharArrContainChar(char arr[],char c){ 208 char *p =arr; 209 while(*p!='\0'){ 210 if(*p==c){ 211 return 1; 212 } 213 p = p+1; 214 } 215 return 0; 216 } 217 218 219 /*给定一个运算符,判断他的优先级,返回数字越大,优先级越高,例如 220 operatorPriorityArr是一个二维数组,下面是模拟的内容,优先级用户可以自定义,数组按元素优先级由低到高排列 221 0 +- 第0优先级 222 1 * / 第1优先级 223 2 () 第2优先级 224 225 c:等待判断优先级的运算符 226 */ 227 int getCharIndex(char c,char operatorPriorityArr[][MAXSIZE],int length){ 228 int i; 229 for(i=0;i<length;i++){ 230 if(isCharArrContainChar(operatorPriorityArr[i],c)){ 231 return i; 232 } 233 } 234 return -1; 235 } 236 237 238 /* 239 判断字符栈的栈顶元素的优先级和读入字符的优先级 240 参数: 241 stackTopEle:栈顶元素 242 readChar:读入的元素 243 operatorPriorityArr:用户自定义的运算符优先级二维字符数组,数组按元素优先级由低到高排列 244 length:二维数组的行数 245 246 比较规则: 247 1.如果读入的字符为"(",那么"("的优先级最高,直接压栈 248 2.如果栈顶元素为"(",其优先级最低,eadChar直接入栈 249 3.如果读入的元素readChar优先级大于栈顶元素,则readChar入栈。例如eadChar为"*" topChar为"+",则"*"入栈 250 4.如果读入的元素readChar优先级小于或者等于(因为运算需要按照从左往右的顺序)栈顶元素,则topChar弹栈并输出(记录)。 251 再次判断readChar和当前栈顶元素的优先级,如果当readChar还是优先级小于或者等于当前栈顶元素,接着弹栈并输出(记录)。 252 一直比较,直到readChar的优先级大于栈顶元素或者栈为空。 253 5.如果readChar为")",一直弹栈到到第一次遇到"(",并把"("也弹栈,对"("和")"不做输出,其运算符弹栈且输出(记录) 254 255 返回值: 256 1:压栈 257 0:读入")" 直到把第一个"("弹栈栈为止 258 2:弹出当前的栈顶元素,并继续比较 259 */ 260 int compareOperatorChar(char stackTopEle,char readChar,char operatorPriorityArr[][MAXSIZE],int length){ 261 int index1,index2; 262 if(stackTopEle=='('){ 263 return 1;//栈为空,直接压栈 264 }else if(readChar==')'){ 265 return 0;//读入遇到")",直到把第一个"("弹栈栈为止 266 }else{ 267 //获取两个运算符的优先级 268 index1 = getCharIndex(stackTopEle,operatorPriorityArr,length); 269 index2 = getCharIndex(readChar,operatorPriorityArr,length); 270 if(index1<index2){//比较优先级 271 return 1; 272 }else{ 273 return 2; 274 } 275 } 276 } 277 278 279 /* 280 根据优先级的返回结果进行对应的压栈和弹栈操作 281 参数: 282 stack:准备好的空字符栈 283 readChar:读入的字符 284 operatorPriorityArr:定义的优先级规则 285 list:储存后缀表达式的字符线性表 286 length:二维数组的行数 287 */ 288 void comparePriority(pStack stack,char readChar,char operatorPriorityArr[][MAXSIZE],pList list,int length){ 289 if(isEmpty(stack)){ 290 push(stack,readChar); 291 return 1; 292 }else{ 293 char topEle = getTopElement(stack); 294 int result = compareOperatorChar(topEle,readChar,operatorPriorityArr,length); 295 if(result==0){ 296 while(getTopElement(stack)!='('){ 297 insert(list,pop(stack)); 298 } 299 pop(stack); 300 return; 301 }else if(result==1){ 302 push(stack,readChar); 303 return; 304 }else{ 305 insert(list,pop(stack)); 306 //递归再次比较 307 comparePriority(stack,readChar,operatorPriorityArr,list,length); 308 } 309 } 310 } 311 312 313 /* 314 根据中缀表达式或者后缀表达式,转换规则: 315 1.对于数字,进行原样的输出或者记录 316 2.对于运算符,根据优先级进行压栈弹栈操作,优先级比较规则参照上面 317 参数: 318 stack:准备好的空字符栈 319 arr:中缀表达式的字符数组,支持空格隔开 320 operatorPriorityArr:定义的优先级规则 321 list:储存后缀表达式的字符线性表 322 length:二维数组的行数 323 */ 324 char* getBehindExpress(pStack stack,char *arr,char operatorPriorityArr[3][MAXSIZE],pList list,int length){ 325 char *p = arr; 326 while(*p!='\0'){ 327 if(*p>='0' && *p<='9'){ 328 insert(list,*p); 329 }else if(*p==' '){ 330 p = p+1; 331 continue; 332 }else if(getCharIndex(*p,operatorPriorityArr,length)!=-1){//判断运算符是否和法 333 comparePriority(stack,*p,operatorPriorityArr,list,length); 334 }else{ 335 printf("the operational character is illegal\n"); 336 return "error"; 337 } 338 p = p+1; 339 } 340 //当数字读取完毕后,要把栈里面的运算符全部弹栈 341 while(!isEmpty(stack)){ 342 insert(list,pop(stack)); 343 } 344 } 345 346 //打印字符栈里面的元素 347 void toString(pStack stack){ 348 pStack top = stack->next; 349 printf("toString:"); 350 while(top!=NULL){ 351 printf("%c ",top->element); 352 top=top->next; 353 } 354 printf("\n"); 355 } 356 357 358 359 360 /*==================================================计算后缀表达式的值==========================================================================*/ 361 /*计算规则如下: 362 求后缀表达式的值 363 6 2 / 3 - 4 2 * + = 364 365 后缀表达式的求解原理:遇到数值先记录(压栈),遇到符号才计算(弹栈两个元素)。计算离符号最近的两个数 366 1.6 2 / ==> 3 压栈 367 2.3 3 - ==> 0 压栈 368 3.0 4 2 * ==> 0 8 369 4.0 8 + ==>8 370 5.8 = ==> 8 371 */ 372 double getValueByAfterExpress(pList list,pStack stack){ 373 int i,store; 374 double temp,a1,a2; 375 for(i=0;i<=list->length;i++){ 376 //printf("test"); 377 char c = list->data[i]; 378 if(c>='0' && c<='9'){ 379 store = c-'0';//字符转换为数字 380 temp = store*1.0;//整形转换为double型 381 //printf("double:%f\n",temp); 382 pushInteger(stack,temp);//数字就压栈 383 }else{ 384 //弹栈并根据运算符做运算 385 double a1 = popInteger(stack); 386 double a2 = popInteger(stack); 387 if(list->data[i]=='+'){ 388 temp = a1+a2; 389 pushInteger(stack,temp); 390 } 391 392 if(list->data[i]=='-'){ 393 temp = a2-a1; 394 pushInteger(stack,temp); 395 } 396 if(list->data[i]=='*'){ 397 temp = a1*a2; 398 pushInteger(stack,temp); 399 } 400 if(list->data[i]=='/'){ 401 temp = a2/a1; 402 pushInteger(stack,temp); 403 } 404 405 } 406 //toStringInteger(stack); 407 } 408 //最终的栈顶元素即为所求的值 409 return getIntegetStackTopElement(stack); 410 } 411 412 413 414 void main(){ 415 int n = 3,i; 416 pStack stack = createEmptyStack(); 417 pStack numberStack = createIntegerEmptyStack(); 418 pList list = createEmptyList(); 419 420 char arr1[] = "2 * ( 9 + 6 / 3 - 5 ) + 4"; 421 char arr2[] = "2 + 9 / 3 - 5"; 422 char arr3[] = "3*(2*3-4*1+(3*5)/3)*3+2"; 423 char operatorPriorityArr[3][MAXSIZE]={"+-","*/","()"}; 424 //计算二维数组的行数 425 int length=sizeof(operatorPriorityArr)/sizeof(operatorPriorityArr[0]); 426 //char operatorArr[] = "+-*/()"; 427 getBehindExpress(stack,arr3,operatorPriorityArr,list,length); 428 //toStringList(list); 429 double value = getValueByAfterExpress(list,numberStack); 430 printf("the express %s caculate result is %f",arr3,value); 431 //double c = (double)('9.23'); 432 //printf("test:%f",c); 433 //printf("%d",length); 434 }