栈的应用—表达式转换和求值(代码实现)

63 阅读5分钟

关于表达式转换和求值的概念(手算,机算)概念见专栏数据结构的文章


下面我们一一实现代码:

首先实现简单的后/前 缀转中缀

后/前 缀转中缀

后 缀转中缀

以后缀转中缀为例:

用栈实现后缀表达式的计算:

  • ①从左往右扫描下一个元素,直到处理完所有元素
  • ②若扫描到操作数则压入栈,并回到①;
  • 否则执行③ ③若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①
代码使用9+(3-1)*3+8/2为例子:它的后缀表达式为 931-3*+82/+
  • 现在已经知道了后缀表达式为 931-3+82/+,首先把元素全部放入数组。遍历。然后按上面的规则计算*

它的计算代码如下:

//后缀表达式求值
int Calculate(char str[])
{
	char x,x1,x2;
	int i,x3,x4;
	LinkedStack head=Init_LinkedStack();//新建一个栈
	
	for(i=0;str[i]!='\0';i++) 
	{
		if(str[i]>='0'&&str[i]<='9')//如果读到的是数字。则入栈
		{ 
		   Push_LinkedStack(head,str[i]); 
		 } 
		 
		 else       //不是数字就是运算符,读到运算符时候栈中二个操作数出栈进行该操作符的计算 
		 {
		 	Pop_LinkedStack(head,&x2);    
		 	Pop_LinkedStack(head,&x1);  //出栈x2 x1 
		 	x3=int(x2-'0');              //转换为int 比如x2是字符'9' 此时对应ASCII表57 ,57-48正好等于9 ,字符转换成整数 
			x4=int(x1-48); 
			
			switch(str[i])
			{
			case'+':
			{
				x4 = x4+x3;
				break;
			}
			case'-':
			{
				x4 = x4-x3;
				break;
			}
			case'*':
			{
				x4 = x4*x3;
				break;
			}
			case'/':
			{
				if (x3==0)
				{
					printf("分母为零错误!");
				    exit(0);
				}
				else
				{
					x4 = x4/x3;
					break;
				}
			}
			}
			
			x1=char(x4+48); //把计算的结果转换为字符,再放入栈
			  Push_LinkedStack(head,x1); 
		  } 
	}
	
	Pop_LinkedStack(head,&x);  //for循环结束后,将计算结果存入x中 
	x=int(x-48);  //转换为int型
	return x; 
	
 } 

main函数调用:”

int main()
{
   int x; 
   char s[100]; //初始化数组 
    printf("请输入要计算的后缀表达式:\n");
   scanf("%s",s); //直接输入字符串,变成字符串数组s[100]的初始化 
   x=Calculate(s);
   printf("后缀表达式的计算结果为:%d\n", x);
   
   return 0;
}

图片.png


总体代码如下:

#include <stdio.h>
#include <stdlib.h>

/*定义链栈*/
typedef char elemtype;
typedef struct LinkedStackNode
{
    elemtype data;
    struct LinkedStackNode *next;
}LinkedStackNode, *LinkedStack;


/*链栈的初始化*/
LinkedStack Init_LinkedStack()
{
    LinkedStack top = (LinkedStackNode *)malloc(sizeof(LinkedStackNode)); //头结点 

    if(top != NULL)
    {
        top->next = NULL;
    }
    return top;
}


//销毁栈 动态申请空间
 void Destroy(LinkedStack top)
{
	LinkedStackNode *p,*p1;
	p = top;
	while (p != NULL)
	{
		p1 = p;
		p = p->next;
		free(p1);
	}
}


/*判栈空*/
int LinkedStack_Empty(LinkedStack top)
{
    if (top->next == NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
    
}

/*入栈*/
int Push_LinkedStack(LinkedStack top, elemtype x)
{
    LinkedStackNode *node;
    node = (LinkedStackNode *)malloc(sizeof(LinkedStackNode));  

    if (node == NULL)
    {
        return 0;
    }
    else
    {
        node->data = x;
        node->next = top->next;
        top->next = node;
        return 1;
    }
    
}

/*出栈*/
int Pop_LinkedStack(LinkedStack top, elemtype * x)
{
    LinkedStackNode *node; 
    if (top->next == NULL)
    {
        return 0;
    }
    else
    {
        node = top->next;
        *x = node->data;
        top->next = node->next;
        free(node);
        return 1;
    }
    
}

//获取栈顶元素
int StackGet(LinkedStack top,char *x)
{
	LinkedStackNode *p; //申请结点
    p=top;
	if(p->next==NULL)
	return 0; //栈空,无法出栈
	else
	{*x=p->next->data;//栈顶元素 x获取 
	return 1;  }
 } 

//后缀表达式求值
int Calculate(char str[])
{
	char x,x1,x2;
	int i,x3,x4;
	LinkedStack head=Init_LinkedStack();//新建一个栈
	
	for(i=0;str[i]!='\0';i++) 
	{
		if(str[i]>='0'&&str[i]<='9')//如果读到的是数字。则入栈
		{ 
		   Push_LinkedStack(head,str[i]); 
		 } 
		 
		 else       //不是数字就是运算符,读到运算符时候栈中二个操作数出栈进行该操作符的计算 
		 {
		 	Pop_LinkedStack(head,&x2);    
		 	Pop_LinkedStack(head,&x1);  //出栈x2 x1 
		 	x3=int(x2-'0');              //转换为int 比如x2是字符'9' 此时对应ASCII表57 ,57-48正好等于9 ,字符转换成整数 
			x4=int(x1-48); 
			
			switch(str[i])
			{
			case'+':
			{
				x4 = x4+x3;
				break;
			}
			case'-':
			{
				x4 = x4-x3;
				break;
			}
			case'*':
			{
				x4 = x4*x3;
				break;
			}
			case'/':
			{
				if (x3==0)
				{
					printf("分母为零错误!");
				    exit(0);
				}
				else
				{
					x4 = x4/x3;
					break;
				}
			}
			}
			
			x1=char(x4+48); //把计算的结果转换为字符,再放入栈
			  Push_LinkedStack(head,x1); 
		  } 
	}
	
	Pop_LinkedStack(head,&x);  //for循环结束后,将计算结果存入x中 
	x=int(x-48);  //转换为int型
	return x; 
	
 } 



int main()
{
   int x; 
   char s[100]; //初始化数组 
    printf("请输入要计算的后缀表达式:\n");
   scanf("%s",s); //直接输入字符串,变成字符串数组s[100]的初始化 
   x=Calculate(s);
   printf("后缀表达式的计算结果为:%d\n", x);
   
   return 0;
}

前 缀转中缀

这里说明下如果要计算的是前缀表达式,你想下,就是扫描的顺序变了下,从右向左扫描

这里一定要注意,出栈顺序变了,x3,x4要变下。不然容易变-值

图片.png

图片.png

运行截图:

图片.png


总体代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/*定义链栈*/
typedef char elemtype;
typedef struct LinkedStackNode
{
    elemtype data;
    struct LinkedStackNode *next;
}LinkedStackNode, *LinkedStack;


/*链栈的初始化*/
LinkedStack Init_LinkedStack()
{
    LinkedStack top = (LinkedStackNode *)malloc(sizeof(LinkedStackNode)); //头结点 

    if(top != NULL)
    {
        top->next = NULL;
    }
    return top;
}


//销毁栈 动态申请空间
 void Destroy(LinkedStack top)
{
	LinkedStackNode *p,*p1;
	p = top;
	while (p != NULL)
	{
		p1 = p;
		p = p->next;
		free(p1);
	}
}


/*判栈空*/
int LinkedStack_Empty(LinkedStack top)
{
    if (top->next == NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
    
}

/*入栈*/
int Push_LinkedStack(LinkedStack top, elemtype x)
{
    LinkedStackNode *node;
    node = (LinkedStackNode *)malloc(sizeof(LinkedStackNode));  

    if (node == NULL)
    {
        return 0;
    }
    else
    {
        node->data = x;
        node->next = top->next;
        top->next = node;
        return 1;
    }
    
}

/*出栈*/
int Pop_LinkedStack(LinkedStack top, elemtype * x)
{
    LinkedStackNode *node; 
    if (top->next == NULL)
    {
        return 0;
    }
    else
    {
        node = top->next;
        *x = node->data;
        top->next = node->next;
        free(node);
        return 1;
    }
    
}

//获取栈顶元素
int StackGet(LinkedStack top,char *x)
{
	LinkedStackNode *p; //申请结点
    p=top;
	if(p->next==NULL)
	return 0; //栈空,无法出栈
	else
	{*x=p->next->data;//栈顶元素 x获取 
	return 1;  }
 } 

//后缀表达式求值
int Calculate(char str[])
{
	int len=strlen(str);
	char x,x1,x2;
	int i,x3,x4;
	LinkedStack head=Init_LinkedStack();//新建一个栈
	
	for(i=len-1;i>=0;i--) 
	{
		if(str[i]>='0'&&str[i]<='9')//如果读到的是数字。则入栈
		{ 
		   Push_LinkedStack(head,str[i]); 
		 } 
		 
		 else       //不是数字就是运算符,读到运算符时候栈中二个操作数出栈进行该操作符的计算 
		 {
		 	Pop_LinkedStack(head,&x2);    
		 	Pop_LinkedStack(head,&x1);  //出栈x2 x1 
		 	x3=int(x2-48);              //转换为int 比如x2是字符'9' 此时对应ASCII表57 ,57-48正好等于9 ,字符转换成整数 
			x4=int(x1-48); 
			
			switch(str[i])
			{
			case'+':
			{
				x4 = x4+x3;
				break;
			}
			case'-':
			{
				x4 = x3-x4;
				break;
			}
			case'*':
			{
				x4 = x4*x3;
				break;
			}
			case'/':
			{
				if (x4==0)
				{
					printf("分母为零错误!");
				    exit(0);
				}
				else
				{
					x4 = x3/x4;
					break;
				}
			}
			}
			
			x1=char(x4+48); //把计算的结果转换为字符,再放入栈
			  Push_LinkedStack(head,x1); 
		  } 
	}
	
	Pop_LinkedStack(head,&x);  //for循环结束后,将计算结果存入x中 
	x=int(x-48);  //转换为int型
	return x; 
	
 } 



int main()
{
   int x; 
   char s[100]; //初始化数组 
    printf("请输入要计算的前缀表达式:\n");
   scanf("%s",s); //直接输入字符串,变成字符串数组s[100]的初始化 
   x=Calculate(s);
   printf("前缀表达式的计算结果为:%d\n", x);
   
   
   return 0;
}

下面实现输入一个中缀,转变为后/前缀,然后直接用上面已经有的代码,也可以计算

中缀转 后/前缀

中缀转 后缀

以中缀转后缀为例:

运算方法:

左到右处理各个元素,直到末尾。

可能遇到三种情况:

  • ① 遇到操作数。直接加入后缀表达式。
  • ② 遇到界限符。遇到“(”直接入栈;遇到“)”则依次弹出栈内运算符加入后缀表达式直到 弹出“(”为止注意:“(”不加入后缀表达式。
  • ③ 遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式, 若碰到“(” 或栈空则停止。之后再把当前运算符入栈

总结如下:

  • 总结:所有操作数入S1,所有左括号和运算符入S2,遇到右括号时S2出,遇到运算符需要进行比较优先级

这里先把运算符的优先级比较函数写在前面:

nt Compare(char str1) {
    if (str1 == '#') {
        return 0;
    } else if (str1 == '+' || str1 == '-') {
        return 1;
    } else if (str1 == '*' || str1 == '/') {
        return 2;
    } else if (str1 == '(' || str1 == ')') {
        return 0;
    }
    else {
        return -1; //字母或者数字 
    }
}

下面开始写转换代码:

这里使用的是顺序栈

void conversion()
{
	//结果栈 
    char endStack[100];
    int endTop = -1;
    
    //操作符栈 
    char symbolStack[100];
    int symbolTop = -1;
    
    char str[100];
   printf("转换前输入:\n");
    scanf("%s", str);
    int length = (int)strlen(str);
    
     for (int i = 0; i < length; ) {
        if (Compare(str[i]) == -1) {    //是字母或者数字就把它压入结果栈中 
            endStack[++endTop] = str[i++];
        } else if (str[i] == '(') {     //遇到(直接压入操作符栈中 
            symbolStack[++symbolTop] = str[i++];
        } else if (str[i] == ')') {     //遇到)将操作符栈中的符号全部弹出压入结果栈中 
            while (Compare(symbolStack[symbolTop]) != 0) {
                endStack[++endTop] = symbolStack[symbolTop--];
            }
            symbolTop--;
            i++;
        } else {     //遇到操作符 
            if (symbolTop == -1 || Compare(symbolStack[symbolTop]) < Compare(str[i])) { //操作符栈为空或者字符串中的操作符优先级大于操作符栈顶操作符的优先级就将该操作符压入操作符栈中 
                symbolStack[++symbolTop] = str[i++];
            } else {     //若字符串中的操作符优先级小于或等于操作符栈顶操作符的优先级,就将操作符栈的栈顶元素弹出压入结果栈中,并且字符串中的位置不变,直到操作符栈为空或遇到高优先级的操作符 
                endStack[++endTop] = symbolStack[symbolTop--];
            }
            if (str[i] == '#') {
                break;
            }
        }
    }
     printf("转换后:\n");
    for (int i = 0; i < endTop + 1; i++) {
        printf("%c", endStack[i]);
    }
}

运行截图:

图片.png


整体代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*定义链栈*/
typedef char elemtype;
typedef struct LinkedStackNode
{
    elemtype data;
    struct LinkedStackNode *next;
}LinkedStackNode, *LinkedStack;


/*链栈的初始化*/
LinkedStack Init_LinkedStack()
{
    LinkedStack top = (LinkedStackNode *)malloc(sizeof(LinkedStackNode)); //头结点 

    if(top != NULL)
    {
        top->next = NULL;
    }
    return top;
}


//销毁栈 动态申请空间
 void Destroy(LinkedStack top)
{
	LinkedStackNode *p,*p1;
	p = top;
	while (p != NULL)
	{
		p1 = p;
		p = p->next;
		free(p1);
	}
}


/*判栈空*/
int LinkedStack_Empty(LinkedStack top)
{
    if (top->next == NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
    
}

/*入栈*/
int Push_LinkedStack(LinkedStack top, elemtype x)
{
    LinkedStackNode *node;
    node = (LinkedStackNode *)malloc(sizeof(LinkedStackNode));  

    if (node == NULL)
    {
        return 0;
    }
    else
    {
        node->data = x;
        node->next = top->next;
        top->next = node;
        return 1;
    }
    
}

/*出栈*/
int Pop_LinkedStack(LinkedStack top, elemtype * x)
{
    LinkedStackNode *node; 
    if (top->next == NULL)
    {
        return 0;
    }
    else
    {
        node = top->next;
        *x = node->data;
        top->next = node->next;
        free(node);
        return 1;
    }
    
}

//获取栈顶元素
int StackGet(LinkedStack top,char *x)
{
	LinkedStackNode *p; //申请结点
    p=top;
	if(p->next==NULL)
	return 0; //栈空,无法出栈
	else
	{*x=p->next->data;//栈顶元素 x获取 
	return 1;  }
 } 
 
  


//后缀表达式求值
int Calculate(char str[])
{
	char x,x1,x2;
	int i,x3,x4;
	LinkedStack head=Init_LinkedStack();//新建一个栈
	
	for(i=0;str[i]!='\0';i++) 
	{
		if(str[i]>='0'&&str[i]<='9')//如果读到的是数字。则入栈
		{ 
		   Push_LinkedStack(head,str[i]); 
		 } 
		 
		 else       //不是数字就是运算符,读到运算符时候栈中二个操作数出栈进行该操作符的计算 
		 {
		 	Pop_LinkedStack(head,&x2);    
		 	Pop_LinkedStack(head,&x1);  //出栈x2 x1 
		 	x3=int(x2-'0');              //转换为int 比如x2是字符'9' 此时对应ASCII表57 ,57-48正好等于9 ,字符转换成整数 
			x4=int(x1-48); 
			
			switch(str[i])
			{
			case'+':
			{
				x4 = x4+x3;
				break;
			}
			case'-':
			{
				x4 = x4-x3;
				break;
			}
			case'*':
			{
				x4 = x4*x3;
				break;
			}
			case'/':
			{
				if (x3==0)
				{
					printf("分母为零错误!");
				    exit(0);
				}
				else
				{
					x4 = x4/x3;
					break;
				}
			}
			}
			
			x1=char(x4+48); //把计算的结果转换为字符,再放入栈
			  Push_LinkedStack(head,x1); 
		  } 
	}
	
	Pop_LinkedStack(head,&x);  //for循环结束后,将计算结果存入x中 
	x=int(x-48);  //转换为int型
	return x; 
	
 } 
 
 //优先级比较 #是结束符 
 int Compare(char str1) {
    if (str1 == '#') {
        return 0;
    } else if (str1 == '+' || str1 == '-') {
        return 1;
    } else if (str1 == '*' || str1 == '/') {
        return 2;
    } else if (str1 == '(' || str1 == ')') {
        return 0;
    }
    else {
        return -1; //字母或者数字 
    }
}


//void mid_to_last(char mid[])
//{
//	LinkedStack mystack=Init_LinkedStack();//结果栈 
//	LinkedStack opstack=Init_LinkedStack();//操作符栈
//	
//	int length=(int)strlen(mid);//数组长度
//	
//	for(int i=0;i<length;){
//		if(Compare(mid[i])==-1) //是数字或者字母 入栈结果栈 
//		{
//			Push_LinkedStack(mystack,mid[i]);
//			i++;
//		}
//		else if(mid[i]=='('){ //遇到左括号直接 入栈操作栈 
//		   Push_LinkedStack(opstack,mid[i]);
//		   i++;
//		}
//		else if(mid[i]==')'){ //遇到右括号 将操作栈中的全部弹出 压入结果栈 
//		  while (! LinkedStack_Empty(opstack)) //栈不为空 
//		   {
//		   		char x;
//		   		Pop_LinkedStack(opstack,&x);
//   			 	Push_LinkedStack(mystack,x); 					  
//		  }
//		  i++;
//		}
//		else { //遇到操作符
//		//操作符栈为空或者字符串中的操作符优先级大于操作符栈顶操作符的优先级就将该操作符压入操作符栈中 
//		char x;
//		StackGet(opstack,&x);//获取此时栈顶操作符 
//		 if(LinkedStack_Empty(opstack)||Compare(x) < Compare(mid[i]))
//		  //压入操作符栈中
//		  {
//		  Push_LinkedStack(opstack,mid[i]);
//		  i++;}
//		  else {  //若字符串中的操作符优先级小于或等于操作符栈顶操作符的优先级,就将操作符栈的栈顶元素弹出压入结果栈中,并且字符串中的位置不变,直到操作符栈为空或遇到高优先级的操作符 
//		    	char x1;
//		   		Pop_LinkedStack(opstack,&x1);
//   			 	Push_LinkedStack(mystack,x1); 	
//		  } 
//		  if(mid[i]=='#'){
//		  	break;
//		  }
//		}
//		
//	} 
   
   
   
 void conversion()
{
	//结果栈 
    char endStack[100];
    int endTop = -1;
    
    //操作符栈 
    char symbolStack[100];
    int symbolTop = -1;
    
    char str[100];
   printf("转换前输入:\n");
    scanf("%s", str);
    int length = (int)strlen(str);
    
     for (int i = 0; i < length; ) {
        if (Compare(str[i]) == -1) {    //是字母或者数字就把它压入结果栈中 
            endStack[++endTop] = str[i++];
        } else if (str[i] == '(') {     //遇到(直接压入操作符栈中 
            symbolStack[++symbolTop] = str[i++];
        } else if (str[i] == ')') {     //遇到)将操作符栈中的符号全部弹出压入结果栈中 
            while (Compare(symbolStack[symbolTop]) != 0) {
                endStack[++endTop] = symbolStack[symbolTop--];
            }
            symbolTop--;
            i++;
        } else {     //遇到操作符 
            if (symbolTop == -1 || Compare(symbolStack[symbolTop]) < Compare(str[i])) { //操作符栈为空或者字符串中的操作符优先级大于操作符栈顶操作符的优先级就将该操作符压入操作符栈中 
                symbolStack[++symbolTop] = str[i++];
            } else {     //若字符串中的操作符优先级小于或等于操作符栈顶操作符的优先级,就将操作符栈的栈顶元素弹出压入结果栈中,并且字符串中的位置不变,直到操作符栈为空或遇到高优先级的操作符 
                endStack[++endTop] = symbolStack[symbolTop--];
            }
            if (str[i] == '#') {
                break;
            }
        }
    }
     printf("转换后:\n");
    for (int i = 0; i < endTop + 1; i++) {
        printf("%c", endStack[i]);
    }
}

int main()
{
    conversion();
   return 0;
}

中缀转 前缀

如果要实现中缀转前缀:

就改变下扫描顺序,从右向左扫描:

(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
(2) 从右至左扫描中缀表达式
(3) 遇到操作数时,将其压入S2;
(4) 遇到运算符时,比较其与S1栈顶运算符的优先级
(4-1) 如果S1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈;
(4-2) 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入S1;
(4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
(5) 遇到括号时:
(5-1) 如果是右括号“)”,则直接压入S1;
(5-2) 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃;
(6) 重复步骤(2)至(5),直到表达式的最左边;
(7) 将S1中剩余的运算符依次弹出并压入S2;
(8) 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式

就如上面所示:

需要修改以下几点:

图片.png

运行截图:

图片.png


好像上面代码还是不是很完善,待续,参考下网上其他人的代码

待续

参考