本文基于上篇文章-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;
}