• 栈(stack)的应用


    栈(stack)通常也被称之为“堆栈”。它的本质是线性表。堆(heap)通常我们也称它为优先队列,本质是树。此处讲述一些stack的应用。

    • 平衡符号

    编译器在检查(){}这样成对出现的符号所造成的语法错误时,通常并不需要去设计一个很复杂的程序去判断。而是使用一个简单的算法,这个算法用到一个栈。算法描述如下:

    做一个空栈,从这串代码的开始读到末尾。如果读到的字符是一个开放字符——左括号,那么将它入栈。如果是一个封闭符号——右括号,这时将栈中的元素弹出。如果弹出的元素是封闭符号对应的开放符号,那么正确(正确的时候不做任何提示),否则就报错。如果这时的栈为空,那么说明缺失了开放字符,报错。当这串代码读完时,如果栈不为空,那么报错。

    下面给出其C语言实现的代码:栈及其实现不表

    int IsSyntaxError(const char *p,const int num)
    {
    	while(end != p)
    	{
    		if('(' == *p || '{' == *p)
    			{
    				Push(*p);
    			}
    		if(')' == *p)
    		{
    			char ch;
    			ch = Pop();
    			if('(' != ch))
    			{
    				return error;
    			}
    		}
    		if('}' == *p)
    		{
    			char ch;
    			ch = Pop();
    			if('{' != ch))
    			{
    				return error;
    			}
    		}
    	}
    	if(!StackIsEmpty())
    		return error;
    }
    
    • 表达式转换(中缀表达式转后缀表达式)

    中缀表达式:操作符位于操作数中间,例如:2+3*5。我们现在使用的算术表达式就是中缀表达式。

    后缀表达式:操作符放在两个操作数的后面,并且严格遵守从左向右的运算规则。而且后缀表达式相比于前缀表达式是没有括号运算符的。例如:2 3 *(对应的中缀表达式就是2*3)。

    前缀表达式:与后缀表达式刚好相反,操作符位于两个操作数之前。

    前缀表达式我们也常称为“波兰表达式”,后缀表达式常称为“逆波兰表达式”。

    下面是将中缀表达式转换成后缀表达式的一般步骤:假设我们从标准输入读取一个中缀表达式

    1. 读到一个数字时,立即将数字放入输出流。

    2. 读到左括号时,将其压入堆栈中。

    3. 遇到右括号时,右括号本身不入栈,从栈顶开始弹出操作符,放入输出流,直到遇到一个左括号为止,将这个左括号弹出,但是不放入输出流。

    4. 遇到运算符时,若该运算符的优先级高于当前栈顶运算符的优先级,则将它压入栈,若该运算符的优先级小于等于当前栈顶运算符的优先级,将栈顶运算符弹出到输出流,然后按照规则继续与新的栈顶运算符进行比较,直到运算符优先级大于栈顶运算符的优先级,将运算符压入栈。

    5. 按照以上步骤将表达式处理完后,此时若堆栈不为空,则将栈中所有运算符弹出到输出流。

    需要注意的是,左括号的优先级的问题,它在栈外时,优先级最高,在栈内时优先级最低。因此必须处理好左括号的优先级。我的代码只是实现了转换,但是实现的并不怎么好。下面给出代码。

    栈的头文件

    #ifndef STACK
    #define STACK
    #include<stdlib.h>
    #include<stdio.h>
    typedef struct StackNode stack;
    typedef stack * PSNode;
    struct StackNode
    {
    	char c;
    	PSNode next;
    };
    PSNode CreatStack();
    int IsEmpty(PSNode S);
    void Push(PSNode S,char c);
    char Pop(PSNode S);
    #endif // !STACK

    栈的实现

    #include "stack.h"
    
    PSNode CreatStack()
    {
    	PSNode top;
    	top = (PSNode)malloc(sizeof(stack));
    	top->next = NULL;
    	return top;
    }
    
    int IsEmpty(PSNode S)
    {
    	if (NULL == S->next)
    	{
    		return 1;
    	}
    	else
    	{
    		return 0;
    	}
    }
    
    void Push(PSNode S,char c)
    {
    	PSNode temp;
    	temp = (PSNode)malloc(sizeof(stack));
    	temp->c = c;
    	temp->next = S->next;
    	S->next = temp;
    }
    
    char Pop(PSNode S)
    {
    	if (IsEmpty(S))
    	{
    		printf("错误!栈为空!");
    		return 0;
    	}
    	else
    	{
    		char c;
    		PSNode temp;
    		temp = S->next;
    		S->next = S->next->next;
    		c = temp->c;
    		free(temp);
    		return c;
    	}
    }
    

    main.c文件

    #include"stack.h"
    #define MAX 100
    
    void transform(char *ch);		//转换函数
    PSNode s1;
    int main()
    {
    	char str[MAX];
    	printf("请输入中缀表达式:");
    	scanf("%s", str);
    	s1 = CreatStack();
    	printf("转换后的后缀表达式:");
    	transform(str);
    	printf("
    ");
    	system("pause");
    	return 0;
    }
    
    void transform(char * ch)
    {
    	int flag = 1;		//遇到负数时的符号。
    	int number = 0;		//记录数字
    	int i;
    	for (i = 0;'' != ch[i]; i++)
    	{
    		//对负数的处理,第一个数是负数和左括号后面有减号就是负数。
    		if (('-' == ch[i] && 0 == i) || ch[i-1] == '(' && '-' == ch[i])
    		{
    			flag = -1;
    			continue;
    		}
    		//将字符串转换成整数(代码不支持浮点数)
    		if ('0' <= ch[i] && '9' >= ch[i])	
    		{
    			number = number * 10 + ch[i] - '0';
    			if (ch[i+1] < '0' || ch[i+1] > '9')
    			{
    				printf("%d ", flag * number);
    				number = 0;
    				flag = 1;
    			}
    		}
    		else
    		{
    			//先处理栈为空的情况,避免栈不为空,但是经常Pop操作后变空了的情况。
    			if (IsEmpty(s1) && ')' == ch[i])		//遇到右括号,但是栈为空
    			{
    				printf("错误,中缀表达式的括号不匹配!
    ");
    				return;
    			}
    			if (!IsEmpty(s1) && ')' == ch[i])		//遇到右括号且栈不空
    			{
    				while (!IsEmpty(s1) && s1->next->c != '(')
    				{
    					printf("%c ", Pop(s1));
    				}
    				if (IsEmpty(s1))
    				{
    					printf("错误,中缀表达式的括号不匹配!
    ");
    					return;
    				}
    				else
    				{
    					Pop(s1);			//弹出左括号,但是不输出
    				}
    			}
    			if ('+' == ch[i])						//遇到加号
    			{
    				if (IsEmpty(s1))
    				{
    					Push(s1,ch[i]);
    				}
    				else 
    				{
    					while (!IsEmpty(s1) && '(' != s1->next->c )
    					{
    						printf("%c ", Pop(s1));
    					}
    					Push(s1, ch[i]);
    				}
    			}
    			if ('-' == ch[i])						//遇到减号
    			{
    				if (IsEmpty(s1))
    				{
    					Push(s1, ch[i]);
    				}
    				else
    				{
    					while (!IsEmpty(s1) && '(' != s1->next->c)
    					{
    						printf("%c ", Pop(s1));
    					}
    					Push(s1, ch[i]);
    				}
    			}
    			if ('*' == ch[i])		//遇到乘号
    			{
    				if (IsEmpty(s1))
    				{
    					Push(s1, ch[i]);
    				}
    				else
    				{
    					while (!IsEmpty(s1) && ('*' == s1->next->c || '/' == s1->next->c))
    					{
    						printf("%c ", Pop(s1));
    					}
    					Push(s1, ch[i]);
    				}
    			}
    			if ('/' == ch[i])		//遇到除号
    			{
    				if (IsEmpty(s1))
    				{
    					Push(s1, ch[i]);
    				}
    				else
    				{
    					while (!IsEmpty(s1) && ('*' == s1->next->c || '/' == s1->next->c))
    					{
    						printf("%c ", Pop(s1));
    					}
    					Push(s1, ch[i]);
    				}
    			}
    			if ('(' == ch[i] )		//遇到左括号
    			{
    				Push(s1, ch[i]);
    			}
    		}
    	}
    	while (!IsEmpty(s1))	//读入结束后,栈不空就将栈中所有运算符弹出
    	{
    		printf("%c ", Pop(s1));
    	}
    }
    • 后缀表达式求值

    1. 当遇到操作数时,直接压入栈中。

    2. 遇见操作符时,就从栈中弹出两个操作数,把这两个操作数按照操作符的运算规则进行运算,将运算结果也压入栈中。

    3. 重复以上两个步骤,直到将表达式计算完毕。

    main.c文件

    #include"stack.h"
    ElementType Calc(Pstack s, char *str);
    int main()
    {
    	char str[MAX];
    	printf("请输入后缀表达式:");
    	gets(str);
    	Pstack s;
    	s = CreatStack();
    	ElementType data = Calc(s, str);
    	Pop(s);			//弹出最后一个元素
    	if (!IsEmpty(s))
    	{
    		while (!IsEmpty(s))
    		{
    			Pop(s);
    		}
    		printf("错误,表达式运算符数目过少!按任意键结束...");
    		getchar();
    		exit(0);
    	}
    	printf("计算结果是:%d
    ", data);
    	system("pause");
    	return 0;
    }
    
    ElementType Calc(Pstack s, char * str)
    {
    	ElementType num = 0;
    	int flag = 1;
    	ElementType temp1, temp2, temp = 0;
    	for (int i = 0;'' != str[i]; i++)
    	{
    		if ('-' == str[i] && ' '!= str[i + 1])	//负号后面跟的不是空格表明这里有个负数
    		{
    			flag = -1;
    			continue;
    		}
    		if ('0' <= str[i] && '9' >= str[i])		//将数字入栈
    		{
    			num = 10 * num + str[i] - '0';
    			if (' ' == str[i + 1])
    			{
    				Push(s, flag * num);
    				num = 0;
    				flag = 1;
    			}
    			continue;
    		}
    		if (' ' == str[i])
    		{
    			continue;
    		}
    		if (' ' != str[i])
    		{
    			//从栈中弹出两个操作数
    			temp2 = Pop(s);
    			temp1 = Pop(s);
    			if (INT_MAX == temp1 || INT_MAX == temp2)
    			{
    				printf("表达式错误!按任意键结束程序...");
    				getchar();
    				exit(0);
    			}
    			switch (str[i])
    			{
    			case '+':
    			{
    				temp = temp1 + temp2;
    				Push(s, temp);
    				break;
    			}
    			case '-':
    			{
    				temp = temp1 - temp2;
    				Push(s, temp);
    				break;
    			}
    			case '*':
    			{
    				temp = temp1 * temp2;
    				Push(s, temp);
    				break;
    			}
    
    			case '/':
    			{
    				temp = temp1 / temp2;
    				Push(s, temp);
    				break;
    			}
    			default:
    			{
    				break;
    			}
    			}
    		}
    	}
    	return s->next->data;			//返回计算结果
    }

    在这段代码的栈实现中,稍微更改了Pop()函数。将修改后的Pop函数放在下面.

    ElementType Pop(Pstack s)
    {
    	Pstack temp;
    	ElementType dat;
    	if (NULL == s->next)
    	{
    		printf("栈为空!"); 
    		return INT_MAX;
    	}
    	else
    	{
    		temp = s->next;
    		dat = temp->data;
    		s->next = s->next->next;
    		free(temp);
    		return dat;
    	}
    }
    

    结合上面的中缀表达式转后缀表达式,以及后缀表达式求值。我们可以得到计算一个中缀表达式的方案。当然了,在中缀表达式转后缀表达式的过程中就可以边转边计算。(这样的算法是联机的)

    • 函数调用

    当调用一个函数时,主调程序的所有局部变量都需要存储起来,否则被调用的新函数将会覆盖调用例程的变量。当然,还需要将主调程序的当前位置必须存储,这样当被调函数执行完后,才能返回到原来的地方继续执行。这些都可以用栈来方便的实现。对于递归函数而言,递归总是能够被去除的(编译器完成这个操作),这样需要借助一个栈。去除递归可能会使程序的执行速度变快,但是也会使的程序的简明性下降。

     

  • 相关阅读:
    Vue总结
    Android适配总结
    Java为什么需要四种引用?
    回溯递归:八皇后
    eclipse中的maven build 、maven clean 、 maven install作用
    JS时间格式CST转GMT
    什么是ECMAScript、什么又是ECMA?
    maven打包工程出现错误 Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
    Vue三步完成跨域请求
    Eclipse导入别人项目爆红叉
  • 原文地址:https://www.cnblogs.com/zy666/p/10504290.html
Copyright © 2020-2023  润新知