1.实现一个简单的计算器,输入一个包含圆括号、加减乘除、求余等符号组成的算术表达式字符串,输出该算术表达式的值。要求:
(1)系统至少能实现加、减、乘、除、求余等运算;
(2)利用栈实现并将表达式结果输出。
1.算法设计及算法步骤:
程序分析:计算器功能实现,利用顺序栈实现中缀表达式到后缀表达式的转变,继而进行相应的(+,-,*,/)简单运算。
中缀表达式转后缀表达式:
申请一段连续的顺序栈字符空间进行运算符存储,以便输入输出顺序控制。
通过一个数字数组和一个字符数组保存后缀表达式。两个数组同步数组下标,当数字数组存入一个数字时候,字符数组存入一个!号作为标识,且记录数组下标的int变量++;当字符数组存入一个符号时,数字数组下标位置默认为0,不用做标识,只需把记录数组下标的int变量++。
通过flag变量记录字符解析状态,如果连续解析到数字字符,则数字拼接,如果解析到字符,如果flag标识上次读取的为数字,则先将数字输出录入数组,再对字符进行分析。
字符如果是’(‘号,或者栈顶指向-1,则字符直接录入栈中;如果遇到右括号,执行出栈操作,并将出栈符号输出(录入到数组),直到左括号也出栈,左括号不输出;遇到运算符时,弹出所有优先级大于或等于该运算符的栈顶元素,然后将运算符入栈(即如果是乘除,则乘除出栈,新符号进栈,
如果是加减,则四则运算出栈,遇到左括号停止出栈,新符号进栈。)最终(字符值为NULL)将栈中的元素依次出栈,输出。
后缀表达式运算:
申请一段连续的顺序栈数字(int)空间存储读取到的数字,以便表达式运算。
如果数字数组下标位置不为NULL则读取到数字,并将数字入栈,如果字符数组下表位置不为!号则说明后缀表达式这个位置是一个运算符,则出栈两个数字进行数学运算,运算后将结果入栈。
当读取到数组下标位置为NULL的时候则说明后缀表达式读取,读取完毕,返回最终结果。
C语言实现:
#define OK 1
#define ERROR 0
#define MAXSIZE 50
#include<stdio.h>
#include<stdlib.h> //引入库文件
#include<string.h>
typedef int status; //status是函数的类型,其值是函数结果状态代码,如OK等
//多用来当返回值用,注意这里Status是一个整型,如果return OK 则代表返回值 1,return ERROR代表 0
//栈的理解:就相当于竖起来的线性表,对于顺序存储,这种概念更深,
//栈(Stack)是限定仅在表尾进行插入和删除操作的线性表.栈有空栈概念,会有一个跟踪栈顶的指针top,
//定义当top为-1时即为空栈, top总小于StackSize,当为满栈时,top=StackSize-1
//延伸:总结栈和堆的概念及区别
//c语言中char以及字符串:
// 表示字符型数组a中可以存放2个字符,第1个字符用a[0]访问,第2个字符用a[1]访问,最大下标可以用0~(2-1)范围的。比如a[100]合法下标范围是0~99;
//当a需要保存字符串时,需要注意,字符串必须以0值结尾,表示成字符就是'\0',而且这个0不算在字符串中的字符,那么你用a数组最多只能保存n-1个字符组成
//的数组,如果是char a[2];的话只能保存一个字符组成的字符串;如:char a[20]={ "Hello !" };或者char a[20]={ 'H','e','l','l','o','\0' };这时字符串占
//用6个数组元素,但字符串长度为5,如果你用strlen语句计算的长度也为5,你最多可在这个数组中保存长度为19的字符串,需要自己在末尾添加0或'\0',前面语句
//char a[20]={ "Hello !" };是编译器自动帮你加了结尾符0;
// 当懒得数字符串中字符个数时,也可以让编译器帮你数:char a[]={"Hello"};这与你自己写:char a[6]={ "Hello" };是一样的。
//char a[2];
//这是声明。声明变量 a 是 char 型数组,有2个元素。
//语句里 写 a[0] 表示它是 char 型数组a 里的 第一个 元素
//a[1] 是 char 型数组a 里的 第二个 元素。
//char a[2];
//也可以看成 是 字符串 变量 a。 由于 字符串要用1个单元存放字符串 结束符,所以只能 存放 长度为 1 的字符串。
//声明,带初始化写法:
//char a[2]={ 'A', 'x'}; 初始化 a[0]='A'; a[1]='x'; -- 单引号括起的是 字符常量
//char a[2]="A"; 初始化 字符串 "A" -- 双引号括起的是 字符串,含 字符串结束符。
/*实现一个简单的计算器,输入一个包含圆括号、加减乘除、求余等符号组成的算术表达式字符串,输出该算术表达式的值。要求:
(1)系统至少能实现加、减、乘、除、求余等运算;
(2)利用栈实现并将表达式结果输出。*/
//要想让计算机具有处理我们通常的标准(中缀)表达式的能力,必须有:
//1.将中缀表达式转化成后缀表达式(栈用来进出运算的符号)
//2.将后缀表达式进行运算得出结果(栈用来进出运算的数字)
//c语言的字符串是以字符数组组成的,不能用普通运算符去运算字符串
//指针可以保存一个字符串常量,是只读不可写的
//栈实现利用栈的顺序存储,字符串分隔,并将数字保存到数组中,将符号保存到字符数组中
//要明白一点,数据结构,注重算法思想,过程的学习,c语言是比较基础的语言,
//虽然库中也有很多方便的封装函数,但其基础的实现也是由人编写代码实现目的的,
//且可能函数实现要比我们自己写的程序更加复杂,与我们目的相比冗余更多,
//实现功能上,尽量自己去写底层代码,而减少函数的调用使用
//中缀表达式转后缀表达式时用到的字符栈
typedef char ElemType;
//声明栈类型
typedef struct SqStack{
ElemType data[MAXSIZE];
int top;
}SQTA ;
typedef struct SqStack *zhanChar;//指针,保存地址的变量
//后缀表达式计算时用到的数字栈
typedef int ElemTypes;
typedef struct SqStacks{
ElemTypes data[MAXSIZE];
int top;
}SQTAs ;
typedef struct SqStacks *zhanInt;//指针,保存地址的变量
//定义的结构体如果是指针,访问成员时就用->
//如果定义的是结构体变量,访问成员时就用.
int begin();//主界面,根据用户输入内容执行不同操作
int transform(char shizi[]); //将中缀表达式转为适合计算机运算的后缀表达式
int count(char shiziChar[], int shiziInt[]); //对后缀表达式进行运算并将结果返回
int transform(char shizi[]){ //数组本身是一个表达地址
char shiziChar [100]={};//用来存储符号排序后的符号 //如果位置是数字,存储!
int shiziInt [100]={}; //用来存储数字数组,如果是位置是字符,则存储为0
char *p=shizi; //shizi本身也是一个指针,但因为名字原因,这里还是用指针p代替
int a =0;//用来记录分隔后的数
int index=0;//用来记录
int flag=1;//用来记录是否正在拼数字 即(是否在拼数字状态)
zhanChar zc=(zhanChar)malloc(sizeof(SQTA ));//如果分配成功则返回指向被分配内存的指针
zc->top=-1;
do{
if(*p>=48&& *p<=57){ //读到一个数字字符
a *=10;
a+=*p-48;
flag=1;
}else{ //读到一个字符,或者字符串结束
if(flag==1){
shiziInt[index]=a; //把拼接好的数字录入数组
shiziChar[index]='!';//把这一位置用!标识
index++;
a=0;
//到此,相当于一个数录入完毕
}
if(*p==NULL){
//printf("读取完毕");
while(zc->top!=-1){
shiziChar[index]=zc->data[zc->top];
index++;
zc->top--;
}
break;
} else if(zc->top==-1||*p=='('){ // 如果字符是(或者zc->top==-1,直接入栈
zc->top++;
zc->data[zc->top]=*p;
}else if(*p==')'){
//遇到右括号,执行出栈操作,并将出栈符号输出,直到左括号也出栈,左括号不输出
//遇到+--/ 时,弹出所有优先级大于或等于该运算符的栈顶元素,然后将运算符入栈
//最终将栈中的元素依次出栈,输出
do{
if(zc->data[zc->top]=='('){
zc->top--; //
break;
}else{
shiziChar[index]=zc->data[zc->top];
index++;
zc->top--;
}
}while(1);
} else if(*p=='*'||*p=='/'){ //如果是乘除,则乘除出栈,新符号进栈
while(zc->top>=0&&zc->data[zc->top]=='*'||zc->data[zc->top]=='/'){
shiziChar[index]=zc->data[zc->top];
index++;
zc->top--;
}
zc->top++;
zc->data[zc->top]=*p;
}else if(*p=='+'||*p=='-'){ //如果是加减,则四则运算出栈,遇到左括号停止出栈,新符号进栈
while(zc->top>=0&&zc->data[zc->top]!='('){
shiziChar[index]=zc->data[zc->top];
index++;
zc->top--;
}
zc->top++;
zc->data[zc->top]=*p;
}
flag=0;
}
p++;
//如果单单输入一个纯数字
}while(1);
int i=0;
printf("\n");
while(shiziInt[i]!=NULL||shiziChar[i]!=NULL){ //数字数组默认为0
if(shiziInt[i]!=NULL){
printf("%d",shiziInt[i]);
}else if(shiziChar[i]!='!'){
printf("%c",shiziChar[i]);
}
i++;
}
printf("\n");
int counts=0;
counts=count(shiziChar,shiziInt);
return counts;
}
// printf("%\n",p);
// printf("%s\n",shizi);
// printf("%c\n",shizi[0]);
// printf("%p\n",shizi);
// printf("%p\n",shizi[0]);
// printf("%p\n",shizi[1]);
int count(char shiziChar[],int shiziInt[]){
int i=0;//用来记录
zhanInt zi=(zhanInt)malloc(sizeof(SQTAs ));//如果分配成功则返回指向被分配内存的指针
zi->top=-1;
int count=0; //运算后的值,或最终结果
int count1=0,count2=0;
while(shiziInt[i]!=NULL||shiziChar[i]!=NULL){
if(shiziInt[i]!=NULL){ //读取到数字的时候进栈
zi->top++;
zi->data[zi->top]=shiziInt[i];
}else if(shiziChar[i]!='!'){ //遇到字符的时候进行运算
count2=zi->data[zi->top]; //记录栈顶
zi->top--;
count1=zi->data[zi->top]; //记录栈顶-1
if(shiziChar[i]=='+'){
count=count1+count2;
}else if(shiziChar[i]=='-'){
count=count1-count2;
}else if(shiziChar[i]=='*'){
count=count1 * count2;
}else if(shiziChar[i]=='/'){
count=count1 / count2;
}
zi->data[zi->top]=count;
}
i++;
}
count=zi->data[zi->top];
zi->top--;
return count;
}
int begin(){
printf("------麦兜给您计算计算器--------\n");
char shizi[100]=""; //最终的式子
printf("给我要计算的式子:\n");
scanf("%99s",&shizi);
printf("麦兜努力计算中....");
printf("%d",transform(shizi));
}
int main() {
begin();
return 1;
}