栈的应用--四则运算表达式求值

       本文基于上篇文章-C语言单链表实现栈(stack)数据结构,使用数据结构栈做一个四则运算的应用。四则运算的讲解部分在大话数据结构4.9节有清晰的阐述,但是这节很遗憾没有代码实现,所以本文基于栈的数据结构完成四则运算的代码实操。

        问题的难点在于,如果使用普通的逻辑,当字符串的运算处理不能按照从左到右处理时(比如括号或者乘除运算在加减的后面),计算会很痛苦。
而书中介绍的方式是按照四则运算的优先级重新排列字符串(中缀->后缀),之后按照顺序依次计算。中缀表达式(就是我们输入的式子)便于人类理解,后缀表达式便于计算机运算。所以需要中缀->后缀。

按照大话数据结构的逻辑,可以将四则运算过程分为两个步骤:
1.将中缀表达式转化为后缀表达式(栈用来存放进出运算的符号)
2.将后缀表达式进行运算得出结果(栈用来进出运算的数字)

中缀->后缀的规则
1.从左到右遍历中缀表达式的每个数字和符号,是数字则输出成为后缀表达式的一部分,是符号则分为如下3种情况:
1.1 如果栈是空的,直接将操作符存储到栈中
1.2 如果该操作符(这里的操作符仅指加减乘除)的优先级>栈顶元素的优先级,也是将操作符push到栈中
1.3 如果该操作符(这里的操作符仅指加减乘除)的优先级<=栈顶元素的优先级, 就要弹出pop栈顶元素到后缀表达式中,
直到该操作符优先级大于栈顶元素的优先级(如果该操作符是加减,就要弹出所有元素;如果该操作符是乘除,就要弹出直到栈顶元素变为加减),最后将该操作符入栈。或者pop遇到括号停止
1.4 如果操作符是’(‘,直接push到栈中,
1.5 如果操作符是’)‘,将栈中的操作符一直pop直至遇到'('

后缀表达式的计算规则
从左到右遍历表达式,数字直接进栈,符号就将处于栈顶的两个元素出栈进行计算,结果进栈,直到获得最后结果。

目前实现的比较简单,有以下工作未做:
1.未对输入做检查,也就是必须人工保证输入有效,输入不能有空格、=及其他无效字符,只能包括数字及加减乘除括号。
2.输入的数字只能是个位数,不能出现两位数,比如11/2等,因为从字符串中分割数字目前只支持分割出来个位数。
3.代码写的比较长,没有根据功能做封装。

参考:

https://blog.csdn.net/qianyayun19921028/article/details/89228263?spm=1001.2014.3001.5501

 

20200418(牛客华为机试题目:四则运算)

对代码进行封装成3个接口:

mid_to_post:中缀转后缀

compute_post:根据后缀计算结果,注意除法注意除数和被除数的顺序,减法注意减数和被减数的顺序

pre:前处理,对于包含负数的进行处理

 

C语言代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>  //signal()
#include<string.h>
#include<sys/stat.h>
#include<time.h>
#include<stdarg.h>

#if 1
#define INFO_DEBUG    "%d lines in "__FILE__", complie time is "__TIME__" "__DATE__" \n",__LINE__
#define ADI_RUN_ERROR      1
#define ADI_NULL_POINTER  -1
#define ADI_OK             0
#define ADI_PRINT         printf("[%s][%d]:",__FUNCTION__,__LINE__);printf
#define ADI_ASSERT(c,prt,ret)       if(c) {printf(prt);return ret;}
#endif

#define STACK_TYPE int

typedef struct nodeStack{
    STACK_TYPE elem;
    struct nodeStack *next;
}nodeStack;

#define PARAM_FAIL -1
#define P_NULL -2
#define SUCCESS 0
#define BUFF_SIZE 100


/*初始化:只创建头结点(栈顶指针)*/
nodeStack* initStack()
{
    nodeStack *head = (nodeStack*)malloc(sizeof(nodeStack));
    if(NULL==head) {printf("NULL");return head;}
    head->next = NULL;
    head->elem = 0;
    return head;
}

/*栈stack插入操作可称为push,直接插在末尾,头指针指向栈顶*/
int push(nodeStack *stack,STACK_TYPE elem)
{
    if(NULL == stack)
    {
        ADI_PRINT("err NULL\n");
        return P_NULL;
    }

	nodeStack *node = (nodeStack*)malloc(sizeof(nodeStack));
	if(NULL==node) {printf("NULL");return P_NULL;}
	node->next = stack->next;
	stack->next = node;//将栈顶指针指向新的node
	node->elem = elem;
	stack->elem++;
//	ADI_PRINT("push elem = %c\n",elem);

    return SUCCESS;
}

int pop(nodeStack *stack,STACK_TYPE *ele)
{
    if(NULL == stack)
    {
        ADI_PRINT("err NULL\n");
        return P_NULL;
    }

    if(NULL != stack->next)
    {
		nodeStack *temp = stack->next;
//		ADI_PRINT("pop ele = %c\n",stack->next->elem);
		*ele = stack->next->elem;
		stack->elem--;
		stack->next = stack->next->next;
		free(temp);
		return SUCCESS;
    }
    else
    {
		ADI_PRINT("stack bottom,pop err !\n");
		return PARAM_FAIL;
    }
    
}

int longStack(nodeStack *stack)
{
//	ADI_PRINT("long = %d\n",stack->elem);
    return stack->elem;
}

int printStack(nodeStack *stack)
{
    if(NULL == stack)
    {
        ADI_PRINT("err NULL\n");
        return P_NULL;
    }
    int i = 0;

    ADI_PRINT("top -> bottom\n");
    while(NULL != stack->next)
    { 
        stack = stack->next;
        ADI_PRINT("list[%d] = %c\n",i,stack->elem);
        i++;
    }

    return SUCCESS;
}

/*中缀->后缀*/
void mid_to_post(char *mid, char *post)
{
    int ret = 0;
    nodeStack *stack_compute = initStack();
    if(NULL == stack_compute)
    {
        ADI_PRINT("err \n");
        return ;
    }

	memset(post,0,BUFF_SIZE);
	int count_buff=0, count_last=0;
	STACK_TYPE temp = 0;

	while('\0' != mid[count_buff])
	{
		ADI_PRINT("mid[count_buff] = %c\n",mid[count_buff]);
		if(('0'<=mid[count_buff])&&('9'>=mid[count_buff])) //是数字
		{
			post[count_last]=mid[count_buff];
			count_last++;
		}
		else//是加减乘除运算符
		{
			if(0 == longStack(stack_compute))//如果存储符号的栈是空的,直接push
			{
				push(stack_compute,mid[count_buff]);
			}
			else
			{
				if( ( ('*'==mid[count_buff])||('/'==mid[count_buff]) ) &&
				    ( ('+'==stack_compute->next->elem)||('-'==stack_compute->next->elem) )||//优先级高于栈顶符号,直接push
					 ('('==mid[count_buff])//操作符是'('
				  )
				{
					push(stack_compute,mid[count_buff]);
				}
				else
				{
					if(0 < longStack(stack_compute))
					{
						if(')'==mid[count_buff])//如果操作符是’)‘,将栈中的操作符一直pop直至遇到'('
						{
							do
							{
							    printStack(stack_compute);
								ret = pop(stack_compute,&temp);
								if(SUCCESS != ret) {ADI_PRINT("pop err\n");return ;}
								if(temp != '(')
								{
									post[count_last] = temp;
									count_last++;
								}
							}
							while(temp != '(');
						}
						else//该操作符(这里的操作符仅指加减乘除)的优先级<=栈顶元素的优先级,操作符是加减,栈顶元素是乘除加减,;或者操作符是乘除,栈顶元素是乘除
						{
							if( ('+'==mid[count_buff])||('-'==mid[count_buff]) )//操作符是加减,pop全部或者遇到括号停止
							{
								while(0 < longStack(stack_compute)&&('('!=stack_compute->next->elem)&&(')'!=stack_compute->next->elem))
								{
									ret = pop(stack_compute,&temp);
									if(SUCCESS != ret) {ADI_PRINT("pop err\n");return ;}
									post[count_last] = temp;
									count_last++;
								}
							}
							else//操作符是乘除,栈顶元素是乘除,一直pop直到栈顶元素变为加减或者遇到括号停止
							{
								do
								{
									while(0 < longStack(stack_compute)&&('('!=stack_compute->next->elem)&&(')'!=stack_compute->next->elem))
									{
										ret = pop(stack_compute,&temp);
										if(SUCCESS != ret) {ADI_PRINT("pop err\n");return ;}
										post[count_last] = temp;
										count_last++;
									}
								}
								while(0 < longStack(stack_compute)&&('+'!=stack_compute->next->elem)&&('-'!=stack_compute->next->elem)&&('('!=stack_compute->next->elem)&&(')'!=stack_compute->next->elem));
							}
						}
					}
					if(('(' != mid[count_buff])&&(')' != mid[count_buff]))
					{
						push(stack_compute,mid[count_buff]);//将当前元素进栈
					}
				}
			}
		}
		count_buff++;
	}
	while(0 < longStack(stack_compute))
	{
		pop(stack_compute,&temp);
		post[count_last] = temp;
		count_last++;
	}
	ADI_PRINT("houzhui:%s\n",post);
	post[count_last] = '\0';
}

/*根据后缀表达式计算结果*/
int compute_post(char *post)
{
    nodeStack *stack_compute = initStack();
    if(NULL == stack_compute)
    {
        ADI_PRINT("err \n");
        return P_NULL;
    }
    
	int count = 0,result = 0;
	int temp_1,temp_2;
	while('\0' != post[count])
	{
		if(('0'<=post[count])&&('9'>=post[count]))
		{
			char temp_char = post[count];
			result = atoi(&temp_char);
			//ADI_PRINT("temp = %d\n",result);
			push(stack_compute,result);
		}
		else
		{
			pop(stack_compute,&temp_1);
			pop(stack_compute,&temp_2);
			ADI_PRINT("temp_1 = %d,temp_2 = %d\n",temp_1,temp_2);
			switch(post[count])
			{
				case '*': push(stack_compute,temp_1*temp_2);break;
				case '/': push(stack_compute,temp_2/temp_1);break;//注意先pop出来temp_1的是分子,后pop出来的temp_2是分母,因为先pop出来的意味着后进入的
				case '+': push(stack_compute,temp_1+temp_2);break;
				case '-': push(stack_compute,temp_2-temp_1);break;//注意先pop出来temp_1是减数,后pop出来的temp_2是被减数,因为先pop出来的意味着后进入的,后进入的是减数
			}				
		}
		count++;
	}
	pop(stack_compute,&result);
	return result;
}

//前处理:对负数进行处理,比如比如3+2*{1+2*[-4/(8-6)+7]}中的-4就不好弄,通过加0及括号的方式解决,-4改为(0-4),3+2*{1+2*[0-4/(8-6)+7]}
void pre(char *mid)
{
	int count = 0;
	int i;
	char temp = 0;
	int length = strlen(mid);
	while('\0' != mid[count])
	{
		if( ('-' == mid[count]) &&  (('0' > mid[count-1])||('9' < mid[count-1])) )
		{
		    ADI_PRINT("count = %d\n",count);
			temp = mid[count+1];
			for(i=length;i!=count+2;i--)
			{
				mid[i-1+3] = mid[i-1];//多了3个字符,0和两个括号,整体后移3个字符
			}
			mid[count]='(';
			mid[count+1]='0';
			mid[count+2]='-';
			mid[count+3]=temp;
			mid[count+4]=')';
			count+=4;
		}
		count++;
	}	
}

int main()
{
    char mid[BUFF_SIZE];
    char post[BUFF_SIZE];
    int result = 0;
    while(1)
    {
        memset(mid,0,BUFF_SIZE);
		ADI_PRINT("please input compute:\n");
		scanf("%s",mid);//scanf的输入中间不能有空格,有空格就视为输入结束
		ADI_PRINT("input = %s\n",mid);

        /*华为机试,四则运算,要将[],{}都变为()*/
		int count = 0;
		while('\0' != mid[count])
		{
			if('[' == mid[count]) {mid[count] = '(';}
			else if('{' == mid[count]) {mid[count] = '(';}
			else if(']' == mid[count]) {mid[count] = ')';}
			else if('}' == mid[count]) {mid[count] = ')';}
			count++;
		}
		printf("mid = %s\n",mid);

        pre(mid);
        printf("mid = %s\n",mid);
		mid_to_post(mid,post);
		result = compute_post(post);
		ADI_PRINT("final result = %d\n",result);
    }
    return SUCCESS;
}

 

 

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值