所用知識: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 }
