栈的操作和c语言实现算术表达式求值

栈是一种特殊的线性表,按照“后进先出”的原则处理数据。

栈的基本操作有两种:一种是入栈(Push),即把数据保存到栈顶,注意在入栈前应该先检查栈是否已满,如满则不能入栈操作,未满则修改栈顶指针,使其向上移动一个元素的位置,然后将数据保存到栈顶指针所指的位置。 一种是出栈(Pop),即把栈顶数据弹出,注意在出栈之前检查栈是否为空,如果栈为空没有数据则提示不能进行出栈操作,若不空,则修改栈顶指针,使其指向栈中的下一个元素。

1.编写操作栈的头文件  

typedef struct stack
{
	DATA data[SIZE+1];   //数据元素  top为o表示栈为空 
	int top;   //定义栈顶
	 
} seqStack;


其中DATA定义栈中元素的数据类型,SIZE表示栈的最大容量,SIZE+1表示栈的数据从下标为1开始,当top为0时,表示栈为空。

2.初始化栈

seqStack *seqStackInit()
{
	seqStack *p;
	if(p=(seqStack *)malloc(sizeof(seqStack)))  //申请内存
	{
		p->top=0;
		return p;
	} 
	return NULL;  //申请内存失败 
} 


初始化栈需要做两件事,首先按照SIZE指定的大小申请一片内存空间,保存栈中数据,然后设置栈顶指针的值为0,表示空栈。

3.判断栈的状态

int seqStackIsempty(seqStack *s)  //栈是否为空
{
	return(s->top==0);
} 

int seqStackIsfull(seqStack *s)  //栈是否满
{
	return(s->top==SIZE);
} 

对栈操作前先判断栈的状态,入栈前栈是否满,出栈前栈是否为空。


4.清空栈  释放栈所占的内存空间

void seqStackClear(seqStack *s)
{
	s->top=0;   //清空栈 
}

void seqStackfree(seqStack *s)  //释放栈的空间 
{
	if(s)
	  free(s); 
	
} 


清空栈时,即把栈顶指针top置0,释放栈时,因为栈是用malloc函数分配的,所以不使用时用free函数释放分配的内存空间。


5.入栈操作

int seqStackPush(seqStack *s,DATA data)
{
	if((s->top+1)>SIZE)       //栈的上一个位置超过最大位置 
	{
		printf("栈溢出\n");
		return 0;
	}
	s->data[++s->top]=data;   //修改栈顶指针,把元素入栈
	return 1; 
}


入栈时,判断若top已经满了(s->top==size),则top+1就超出栈的最大内存空间,提示栈溢出,做出错处理。若没有溢出,则栈顶指针向上移动一位,把数据放入其中,即入栈。

6.出栈操作

int seqStackPop(seqStack *s)     //出栈
{
	if(s->top==0)
	{
		printf("栈为空:\n");
		exit(0);
	}
	return (s->data[s->top--]);  //从栈顶弹出元素 
} 

出栈与入栈相反,从栈顶弹出一个数据元素。先判断top是否为0,若为0,则表示空栈,提示出错,若不为0,修改栈顶指针减一,即往下移动一位,返回此时栈顶指针所指位置,原来栈顶元素弹出,即出栈。

7.获取栈顶元素

DATA seqStackPeek(seqStack *s)  //读出栈顶元素
{
	if(s->top==0)
	{
		printf("栈为空");
		exit(0);
	}
	return (s->data[s->top]); //返回栈顶元素 
} 


在出栈操作中,是把原来的栈顶元素弹出(抛弃了),即出栈后栈顶元素就不存在了,若只是读取一下栈顶的元素而不删除,就不需要将top指针往下移动,不修改指针位置,这样返回栈顶后栈顶数据元素还在。

以上为栈的基本操作,下面举一个实用例子深入理解栈的相关操作。

栈的实际运用:算术表达式求值


在计算机中输入一个算式如:(2+3)*5  容易知道先计算2+3=5,再将5*5=25,,但计算机在读入算式时的具体过程是怎样的?


计算机先逐步扫描:先是(括号,这时不知道右括号在哪里出现,所以先保存起来,然后是2,也不知道该字符下一位是什么,也先保存,然后是+,这时知道是加上一个数,但不知道加什么,先保存起来,然后是3,这时知道是2+3,直接相加吗?不能,如果3后面是*,或者/的高优先级话就先计算,所以也先保存,然后是),这时括号内的2+3就可以取出运算了,得出结果5后再保存起来(由此可以看出加减的优先级大于()的优先级),然后是*,保存起来,然后是5,保存起来,然后是=号,表示整个算式结束了,将2+3得到的5与5相乘,输出最终结果。


由上分析可知,程序需要保存操作数,保存运算符(+,-,*,/,=,(,)),还需要确定优先级(乘除大于加减,加减大于括号,括号可以改变优先级的顺序,相同优先级从左往右计算)。

为了简化程序,只考虑加减乘除四则运算。

首先调用库函数:

#include
#include
#include
#define SIZE 50
typedef int DATA;  //定义栈元素类型 
#include "biaodashi.h" 


第六行引用最开始的栈的基本操作程序为头文件,


具体函数模块分析:

1.每次读取检查字符是否为运算符

int isOperator(char c)    //检查字符是否为运算符
{
	switch(c)
	{
		case '+':
		case '-':
		case '*':
		case '/':
		case '(':
		case ')':
		case '=':
			return 1;
			break;
		default: 
		   return 0;
		   break;       //字符c是运算符则返回1,,否则返回0 	
	}
} 



定义isOperator函数,如果字符为+,-,*,/,(,),=,运算符,则返回1,否则返回0,。

2.比较运算符的优先级:

注:在计算表达式时,为了方便判断增设“=”为表达式定界符,即以=表示开始,=表示结束


int PRI(char oper1,char oper2)     //判断两个运算符的优先级
//如果oper1>oper2返回1    如果oper1oper2
			break;
		case '*':
		case '/':
			if(oper1=='*'||oper1=='/'||oper1==')')
			  pri=1;   //oper1>oper2
			else
			  pri=-1;     //oper1oper2
			   break; 	  
	}
	return pri;
} 


在该函数中对oper1和oper2两个运算符比较,34到40行表示:oper2为+或-号,若oper1为(或=号,oper1oper2,pri=1。41到47行同理。48到55行表示:oper2等于(时,若oper1等于),即)(,这样右括号后直接左括号是不对的,oper2等于(,若oper1不为),则oper1oper2,pri=1。  最后返回pri。


3.对于优先级高的运算符,需要将运算符两侧的数据进行计算,


int Calc(int a,int oper,int b)  //计算两个操作数的结果
{
	switch(oper)     //根据运算符计算
	{
		case '+':return a+b;
			
		case '-':return a-b;
		
		case '*':return a*b;
			
		case '/':
			if(b!=0)
			  return a/b;
			else
			{
				printf("除数不能为0!\n");
				exit(0);
			}
	} 
	
} 


当为除数时,注意考虑除数不能为0,

4.写好以上函数部分,就可以开始计算表达式的值:

int CalcExp(char exp[])   //表达式计算函数
{
	
	seqStack *StackOper,*StackData;   //指向两个栈的指针变量,一个操作符,一个运算数
	int i=0,flag=0;   //flag作为标志,用来处理多位数
	DATA a,b,c,q,x,t,oper;
	StackOper=seqStackInit();
	StackData=seqStackInit();    //初始化两个栈
	
	q=0;  //变量q保存多位数的操作
	x='=';
	seqStackPush(StackOper,x);  //首先把等号=进入操作栈
	x=seqStackPeek(StackOper); //获取操作栈的首元素 
	c=exp[i++]; 
	while(c!='='||x!='=')  //循环处理表达式中的每一个字符
	{
		if(isOperator(c))  //如果是运算符
		{
			if(flag){
				seqStackPush(StackData,q);  //表达式入栈
				q=0;   //操作数清零 
				flag=0;   //标志清零,表示操作数已经入栈 
			}
			switch(PRI(x,c))   //判断运算符优先级
			{
				case -1:
					seqStackPush(StackOper,c);  //运算符进栈
					c=exp[i++];
					break;
				case 0:
					c=seqStackPop(StackOper);  //运算符括号,等号出栈,被抛弃
					c=exp[i++];   //取下一个 字符 
					break; 
				case 1:
					oper=seqStackPop(StackOper);   //运算符出栈
					b=seqStackPop(StackData);
					a=seqStackPop(StackData);  //两个操作数出栈
					t=Calc(a,oper,b);  //计算结果
					seqStackPush(StackData,t);  //将运算结果入栈 
				 break;
				  
			} 
		}else if(c>='0'&&c<='9')  //如果输出的字符在0到9之间
		{
			c-='0';   //把字符转换成数字
			q=q*10+c;       //多位数的进位处理
			
			c=exp[i++];  //取出下一位字符
			 flag=1;  //设置标志,表示操作数未入栈
		} 
		else {
			printf("输入错误\n");
			getch();
			exit(0);
		}
		x=seqStackPeek(StackOper);  //获取栈顶操作符 
	} 
	q=seqStackPop(StackData);   
	seqStackfree(StackOper);  
	seqStackfree(StackData);  //释放内存占用空间
	return q;  //出栈,返回结果 
	
	 
} 

该部分注解:  

首先是以一个字符串exp为参数,返回一个整型数(即表达式的结果),一个标志变量flag,作用是处理当操作数是多位数时处理多位数的情况(代码一次只能从表达式中获取一个字符),当操作数是多位数就把每一位获取后再入栈。flag为0表示没有操作数入栈,为1表示有操作数需要入栈。

然后将变量x为“=”,将该符号作为表达式的第一个运算符入栈,然后循环诸葛处理表达式字符串中的每一个字符,直到遇到表达式的结束字符“=”为止。

 5.主函数

int main()
{
	int c;
	char exp[80];
	printf("请输入需要计算的表达式(以=结束):\n");
	scanf("%s",exp);
	printf("%s%d\n",exp,CalcExp(exp));
	getch();
	return 0;
}

最后编译结果:

栈的操作和c语言实现算术表达式求值_第1张图片












你可能感兴趣的:(数据结构之线性结构,c语言,栈的操作,算术表达式求值,数据结构,线性结构)