数据结构课程设计(二)---算术表达式求值
1、任務簡述:
一個算術表達式是由操作數(operand)、運算符(operator)和括號組成的。假設操作數均是正實數,運算符只含加減乘除四種運算符。編程利用“算符優先法”求算術表達式的值。
要求:
(1) 從鍵盤或文件讀入一個合法的算術表達式,輸出相應的后綴表達式。后綴表達式中,數據與數據之間加分隔符;
(2) 輸出正確的計算結果,保留兩位小數點;
(3) 考慮算法的健壯性,當表達式錯誤時,要給出錯誤提示
(4) 可以連續輸入,即輸入完一個表達式,轉換和計算完成后可以提示用戶繼續輸入表達式,直到用戶輸入一個“#”則退出程序。
2、算法描述:
數據結構
typedef struct my_stack
{
int a[N];
int top;
}ST;//棧,用來中綴轉后綴
在中綴轉后綴的代碼直接參考了老師的代碼,對數字,+,-,*,/分別進行了考慮和判斷錯誤,并且寫出了錯誤原因,在進行求和使,由于有小數點的問題(放在總結里面討論了),因為我是用char來存放數字,所以轉換回去要采用強制轉換,例如:(int)a,除此以外沒什么問題,一開始,我還在考慮-可以是單目運算符,也可以是多目運算符,但是題目只要求正數,所以只考慮-為雙目運算符。
中綴轉后綴的具體轉換方式:
1.從左到右進行遍歷
2.運算數,直接輸出.
3.左括號,直接壓入堆棧,(括號是最高優先級,無需比較)(入棧后優先級降到最低,確保其他符號正常入棧)
4.右括號,(意味著括號已結束)不斷彈出棧頂運算符并輸出直到遇到左括號(彈出但不輸出)
5.運算符,將該運算符與棧頂運算符進行比較,
如果優先級高于棧頂運算符則壓入堆棧(該部分運算還不能進行),
如果優先級低于等于棧頂運算符則將棧頂運算符彈出并輸出,然后比較新的棧頂運算符.
(低于彈出意味著前面部分可以運算,先輸出的一定是高優先級運算符,等于彈出是因為同等優先級,從左到右運算)
直到優先級大于棧頂運算符或者棧空,再將該運算符入棧.
6.如果對象處理完畢,則按順序彈出并輸出棧中所有運算符.
3、源代碼
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define N 30//數據結構 typedef struct my_stack {int a[N];int top; }ST;//棧,用來中綴轉后綴 //所使用的函數 int isempty(ST *T); //判斷棧是否為空 int isfull(ST *T); //判斷棧是否為滿 int gettop(ST *T); //得到棧頂元素 int pop(ST *T); //彈棧 void push(ST *T,int s); //入棧 void transfer(char *in,char *post); //中綴轉后綴 float Calculate_zhong(char *post); //計算中綴的值(實際上用后綴來計算) //主函數 main() {char zhong[N],hou[N]; //zhong為中綴表達式 hou為后綴表達式 float answer; //存儲計算結果system("color 1E");printf("--------------081810221朱林昊--------------\n");printf("\n--------------表達式求值--------------\n\n"); //說明該代碼的實現功能 printf("需要計算的中綴表達式為: ");scanf("%s",zhong);while(strcmp(zhong,"#")!=0) //當輸入為"#"結束 {transfer(zhong,hou); //中綴轉后綴 printf("\n\n轉化后的后綴表達式為:"); //下面的空格,"\n","-"只是為了輸出好看 printf("%s\n",hou);printf("\n--------------計算結果--------------\n");answer=Calculate_zhong(hou);printf("\n %s",hou);printf(" = %f\n",answer);printf("\n--------------計算完畢--------------\n");printf("\n\n需要計算的中綴表達式為: ");scanf("%s",zhong);} }int isempty(ST *T) //判斷棧是否為空 {if(T->top<0)return 1;elsereturn 0; }int isfull(ST *T) //判斷棧是否為滿 {if(T->top==N-1)return 1;elsereturn 0; }int gettop(ST *T) //得到棧頂元素 {return T->a[T->top]; }int pop(ST *T) //彈棧 {int x;if(T->top<0) //棧為空 {printf("Zhan is empty,can not pop!\n");exit(0);}else{x=T->a[T->top];(T->top)--;return x;} }void push(ST *T,int s) //入棧 {if(T->top==N-1) //棧滿了 {printf("Zhan is full,can not push,you can modify N and then you can push again.\n");exit(0);}else{(T->top)++;T->a[T->top]=s;} }void transfer(char *in,char *post) //將中綴表達式轉化為后綴表達式,參考了老師的代碼,加上了關于小數的討論 {ST T;//棧 int i,j,flag=0; //flag=1說明是現在棧頂是數,用來判斷是否出現連續兩個運算符int count; //記錄每個數中小數點的個數,超過一個則表達式有誤 int right=0,left=0; //left和right用來記錄算式里面左右括號的個數 T.top=-1;for(i=0,j=0;in[i]!='\0';i++){switch(in[i]){case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':for(count=0;(in[i]<='9'&&in[i]>='0')||in[i]=='.';i++,j++){post[j]=in[i];if(in[i]=='.') //記錄小數點出現的個數 count++;} i--;if(count>1){printf("\n表達式錯誤!!!!\n\n錯誤原因:數中有兩個小數點\n");exit(0);}post[j]=' ';//用空格來分割兩個數 j++;flag=1; //目前為數字,則flag為1 break;case '(':if(flag)//如果括號前是數字,則表達式有誤 {printf("\n表達式錯誤!!!!\n\n錯誤原因:數字后直接跟括號\n");exit(0);}push(&T,in[i]); left++;//左括號個數加一 break;case ')':right++; //右括號個數加一 while(gettop(&T)!='('){post[j]=pop(&T);j++;}pop(&T); break;case '+':case '-':if(!flag&&i!=0)//如果運算符前是運算符,則表達式有誤{printf("\n表達式錯誤!!!!\n\n錯誤原因:有連續兩個運算符之間沒有數字\n");exit(0);}while(!isempty(&T)&&gettop(&T)!='('){post[j]=pop(&T);j++;}push(&T,in[i]);flag=0;//目前為符號,所以flag為0 break;case '*':case '/':if(!flag)//如果運算符前是運算符,則表達式有誤 {printf("\n表達式錯誤!!!!\n\n錯誤原因:有連續兩個運算符之間沒有數字\n");exit(0);} while(!isempty(&T)&&(gettop(&T)=='/'||gettop(&T)=='*')){post[j]=pop(&T);j++;}push(&T,in[i]);flag=0;break;default:printf("\n表達式錯誤!!!!\n\n錯誤原因:輸入非法字符,無法試別\n");exit(0);}}if(left!=right){printf("\n表達式錯誤!!!!\n\n錯誤原因:左右括號不匹配\n");exit(0);}while(!isempty(&T)) {post[j]=pop(&T);j++;}post[j]='\0'; }float Calculate_zhong(char *post) {int i,j,top=-1,flag; //top為棧頂,初始值為-1,flag用來判斷數字是否存在小數點 int len; //len表示數字小數點前的長度float temp,aa[N]; //aa[N]用來存放表達式中的數字,temp為臨時變量 char ch[N]; //先把數字的表達式存到ch[N]中,再轉化為數字存到aa[N] for(i=0,j;post[i]!='\0';i++) //依此開始讀取棧的后綴表達式的內容 {if(post[i]>='0'&&post[i]<='9')//如果當前為數字,先將數字存到ch中,再轉化為float類型并存到aa中 {flag=0; //初始為0 j=0; //用來記錄字符串的長度 while(post[i]!=' ')//將這一串代表數字的字符串存到ch中,直到數字結束 {if(post[i]=='.')//判斷是否有小數點,分別討論 flag=1; //有小數點 ch[j]=post[i];//把數字存入到ch[N]中 i++;j++;}ch[j]='\0'; //加上這個,表示字符串結尾 if(flag)//有小數點的情況,先算小數點前的,再算小數點后的,分開計算 {for(j=0;ch[j]!='.';j++);//先求長度,找到j的位置,那么長度為j-1 len=j-1;for(j=0,temp=0.;ch[j]!='.';j++) //計算小數點前的和temp+=(ch[j]-'0')*pow(10,len-j);for(j++,len++;ch[j]!='\0';j++) //計算小數點前的和temp+=(ch[j]-'0')*pow(10,len-j);}else//沒小數點的情況{for(j=0;ch[j]!='\0';j++);//求出相應的長度 len=j-1;for(j=0,temp=0.;ch[j]!='\0';j++)temp+=(ch[j]-'0')*pow(10,len-j);}top++;aa[top]=temp;//temp入棧,到這里對數字的處理就結束了 }else //如果是運算符,棧頂兩個數出棧,并把這兩個數的運算結果入棧!!!!! {switch(post[i]) //根據不同的運算結果進行運算 {case'+':temp=aa[top];top--;temp+=aa[top]; //本來這里需要再top--,但是后面由于需要入棧,及需要top++,所以這里就沒有做top-- aa[top]=temp;break;case'-':temp=aa[top];top--;temp=aa[top]-temp; //本來這里需要再top--,但是后面由于需要入棧,及需要top++,所以這里就沒有做top--aa[top]=temp;break;case'*':temp=aa[top];top--;temp=temp*aa[top]; //本來這里需要再top--,但是后面由于需要入棧,及需要top++,所以這里就沒有做top--aa[top]=temp;break;case'/':temp=aa[top];top--;temp=aa[top]/temp; //本來這里需要再top--,但是后面由于需要入棧,及需要top++,所以這里就沒有做top--aa[top]=temp;}}}return aa[top];//最終的計算結果就在棧頂 }//263行4、運行結果
正確情況:
錯誤情況:
5、總結
性能分析:
時間復雜度:假設表達式有n個數字,m個符號(運算符,括號),那么中綴表達式轉后綴表達式操作需要進行n+m次操作,而計算則需要f(m)次(和m有關,計算次數為運算符的個數,即m減去括號的個數,但是可以確定,和m是線性關系,f(m)=km+b),所以時間復雜度可以認為是O(n+km).
空間復雜度:由于需要用到棧來存放數字和符號,并且在數字,符號之間要加入空格來方便讀取操作,所以空間復雜度為O(2n+2m-1)
遇到的問題與解決方法:
小數點問題:即這里要求數字為float或者double類型,那么就會出現小數點,所以我們不妨用char類型來存,然后強制轉換,但是對于小數本身,我們也可以分段考慮,考慮小數點前,和小數點后,即把數字拆開來看。
心得體會:
運行結果經過演算,都是正確的,對于錯誤的表達式,可以說明相應的錯誤原因。其實這道題可以改進,就是可以不僅僅局限于復數的處理,如果-為單目運算符,那么它可以出現在表達式開頭或者右括號的右邊,那么我們可以1.特判,直接判斷就行了;2.補0;這兩個方法都是可以的,但是補0雖然簡單,但是其實他改變了表達式(雖然值沒有變)。
針對上面的思路,我們可以處理更多的單目運算符,比如”^”,”根號”等等,這個是非常有意義的,比如可以用來自動生成那些小學生計算題的答案,那么網上考試會變得非常方便(即,甚至不用傳答案上去,只需要傳試卷)
存在問題和改進方法:
這道題由于老師寫好了中綴轉后綴的代碼,所以我也中規中矩的用了棧來寫,但是其實可以用二叉樹來操作,就像第一題的結構那樣,葉子為數字,其父節點先是運算符,然后是計算結果,這樣可能在處理功能上更優,比如不需要加空格,那么樹來存儲比我用棧來存儲會節省空間(雖然空間復雜度量級相同,和m+n有關)
總結
以上是生活随笔為你收集整理的数据结构课程设计(二)---算术表达式求值的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工作124:报错可以直接查看下面红字寻找
- 下一篇: 复杂脑网络之图论参数计算(BCT工具包)