實驗項目二:棧的基本操作及其應用
實驗目的:
1.掌握棧的定義及實現;
2.掌握利用棧求解算術表達式的方法。
實驗要求:
1、 使用鏈式存儲結構完成棧的各種基本操作;
2、 補充完成In(c), Preced(t1,t2),Operate(a,theta,b)三個函數。
實驗題目:棧的基本操作及其應用
實驗過程:
1、通過修改完善教材中的算法3.22,利用棧來實現算術表達式求值的算法。對算法3.22中調用的幾個函數要給出其實現過程:
(1) 函數In(c):判斷c是否爲運算符;
(2) 函數Precede(t1,t2):判斷運算符t1和t2的優先級;
(3) 函數Operate(a,theta,b):對a和b進行二元運算theta。
2、程序運行時,輸入合法的算術表達式(中間值及最終結果要在0~9之間,可以包括加減乘除和括號),便可輸出相應的計算結果。
實驗提示:(僅供參考,每個函數的具體實現可以有多種方法,希望有創新)
1. 將棧的定義和實現單獨保存在頭文件“stack.h”中,然後在表達式求值的源程序中包含此頭文件(即#include“stack.h”)。
2.表達式求值源程序的具體實現
(1) 主函數如下:
void main()
{
Printf(“請輸入算術表達式,並以#結束.\n”);
Printf(“the result of expression is:%d\n”,EvaluateExpression());
}
(2) 函數EvaluateExpression的實現見算法3.22
(3) 函數In(c)的實現可以採用以下方式:
Status In(SElemType c)// 應在前面有定義typedef char SElemType;
{ // 判斷c是否爲運算符
switch(c)
{
case'+':return TRUE;
……//補充完整
default:return FALSE;
}
}
(4) 函數Precede(t1,t2)的實現可以採用以下形式:
SElemType Precede(SElemType t1,SElemType t2)
{ //根據教材表3.1,判斷兩個運算符的優先關係
SElemType f;
switch(t2)
{
case '+':
case '-':if(t1=='('||t1=='#')
f='<';
else
f='>';
break;
……//補充完整
}
return f;
}
(5) 函數Operate(a,theta,b)的實現可以採用以下方式:
SElemType Operate(SElemType a,SElemType theta,SElemType b)
{
SElemType c;
a=a-48;
b=b-48;
switch(theta)
{
case'+':c=a+b+48;
break;
……//補充完整
}
return c;
}
選做內容:進一步改進,使表達式的中間值及最終結果不侷限於0~9之間的個位數。(如果完成要在實驗報告中註明)。
實驗結果:
輸入:2*(4-1)+8
輸出:14
該程序能夠完成個位數的四則運算。
實驗分析:
1.棧的操作的特點;
2.列舉調試運行過程中出現的錯誤並分析原因。
要求:
(1) 程序要添加適當的註釋,程序的書寫要採用縮進格式。
(2) 程序要具在一定的健壯性,即當輸入數據非法時,程序也能適當地做出反應。
(3) 程序要做到界面友好,在程序運行時用戶可以根據相應的提示信息進行操作。
(4) 上傳源程序到課堂派,源程序保存爲calculator.cpp。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <stdlib.h>
using namespace std;
#define MAXSIZE 100 //順序棧存儲空間的初始分配量
#define OK 1
#define ERROR 0
typedef int Status;
typedef char ElemType;
//鏈棧的存儲結構
typedef struct StackNode
{
ElemType data;
struct StackNode *next;
}StackNode,*LinkStack;
//鏈棧初始化
Status InitStack(LinkStack &S)
{
S=NULL;
return OK;
}
//入棧操作
Status Push(LinkStack &S,char e)
{
LinkStack p;
p=new StackNode;
p->data=e;
p->next=S;
S=p;
return OK;
}
//鏈棧的出棧
Status Pop(LinkStack &S,char &e)
{
LinkStack p;
if(S==NULL) return ERROR;
e=S->data;
p=S;
S=S->next;
delete p;
return OK;
}
//取棧頂元素
char GetTop(LinkStack S)
{
if(S!=NULL)
return S->data;
}
//判斷是否是運算符
bool In(char c)
{
if(c=='+'||c=='-'||c=='*'||c=='/'||c=='('||c==')'||c=='#') return 1;
else return 0;
}
//比較OPTR棧頂元素和ch的優先級(運算符的優先級)
char Precede(char t1,char t2)
{
char f;
if(t1=='+'||t1=='-')
{
if(t2=='*'||t2=='/'||t2=='(') f='<';
else f='>';
}
else if(t1=='*'||t1=='/')
{
if(t2=='(') f='<';
else f='>';
}
else if(t1=='(')
{
if(t2==')') f='=';
else f='<';
}
else if(t1==')')
{
f='>';
}
else if(t1=='#')
{
if(t2=='=') f='=';
else f='<';
}
return f;
}
//計算簡單算術式
char Operate(char a,char theta,char b)
{
char ans;
if(theta=='+') ans=(a-'0')+(b-'0')+'0';
else if(theta=='-') ans=(a-'0')-(b-'0')+'0';
else if(theta=='*') ans=(a-'0')*(b-'0')+'0';
else if(theta=='/') ans=(a-'0')/(b-'0')+'0';
return ans;
}
//構建函數,求解算數表達式
char EvaluateExpression()
{
printf("請輸入一個表達式:\n");
StackNode *OPND;//定義運算數棧
StackNode *OPTR;//定義運算符棧
InitStack(OPND); //初始化OPND棧,壓入操作數和運算結果
InitStack(OPTR); //初始化OPTR棧,壓入操作符
char c;//起始輸入#
cin>>c;
Push(OPTR,c); //將表達式起始符'#'壓入OPTR棧頂
char ch;
cin>>ch;
while(ch!='#'||GetTop(OPTR)!='#') //用ch!='#'來判斷表達式是否掃描完畢,表達式
{ //沒有掃描完畢或OPTR棧頂元素不爲'#'則繼續執行
if(!In(ch))
{
Push(OPND,ch);
cin>>ch;
}
else
switch(Precede(GetTop(OPTR),ch))
{
case '<':
Push(OPTR,ch);cin>>ch;
break;
case '>':
char theta;
char a,b;
Pop(OPTR,theta);
Pop(OPND,b);
Pop(OPND,a);
Push(OPND,Operate(a,theta,b));
break;
case '=':
char x;
Pop(OPTR,x);
cin>>ch;
break;
}
}
return GetTop(OPND);
}
int main()
{
char res=EvaluateExpression();
printf("結果爲:");
cout<<res;
return 0;
}
終於完成這個程序了啊,心裏很高興。過程一如既往的困難,因爲自己的不細心和小失誤,但還好堅持寫完了。
書上代碼還是不能全信啊,要獨立思考。
有兩個印象深刻的收穫:
1、如何查bug?
第一遍我在查bug的時候照着書本一個字母一個字母的查,這辦法挺傻的。
後來我就改變策略了。
如果程序出錯了,(1)你可以把分函數一個一個執行,從而精確尋找錯誤,(2)在程序語句中插入如printf等,通過運行結果查找錯誤,(3)和別人正確的代碼對比,把別人的分函數複製過來使用,精確尋找錯誤。
2、之前只注意到寫程序的時候不能使用中文符號,沒想到這次在輸入中使用了中文符號致錯,花了好長時間才找出來。