栈(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("\n");
	system("pause");
	return 0;
}

void transform(char * ch)
{
	int flag = 1;		//遇到负数时的符号。
	int number = 0;		//记录数字
	int i;
	for (i = 0;'\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("错误,中缀表达式的括号不匹配!\n");
				return;
			}
			if (!IsEmpty(s1) && ')' == ch[i])		//遇到右括号且栈不空
			{
				while (!IsEmpty(s1) && s1->next->c != '(')
				{
					printf("%c ", Pop(s1));
				}
				if (IsEmpty(s1))
				{
					printf("错误,中缀表达式的括号不匹配!\n");
					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\n", 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;'\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;
	}
}

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

  • 函数调用

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

 

转载于:https://www.cnblogs.com/zy666/p/10504290.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值