TD03-栈和队列的应用


栈的应用

数值转换
括号匹配
行编辑程序
迷宫求解
表达式求值
递归中的应用(如递归的函数)
执行函数时,局部变量的存储(栈结构)
处理函数或过程调用(main里面调用了其他函数等的过程)

一、数值转换

1、初始化一个空栈S。
2、当十进制数N非零时,循环执行以下操作: 把N与8求余得到的八进制数压入栈S; N更新为N与8的商。
3、当栈S非空时,循环执行以下操作: 弹出栈顶元素e; 输出e。

//数值转换
void conversion(int N)
{
	SqStack S;
	InitStack(S);
	while (N)
	{
		Push(S, N % 8);
		N = N / 8;
	}
	while (!StackEmpty(S))
	{
		ElemType e;
		Pop(S, e);
		printf("%d", e);
	}
}

时间和空间复杂度均为O(log8n)。

二、括号匹配

检验算法借助一个栈
1、每当读入一个左括号,则直接入栈,等待相匹配的同类右括号;
2、每当读入一个右括号,若与当前栈顶的左括号类型相同,则二者匹配,将栈顶的左括号出栈;
3、直到表达式扫描完毕。
(设置一标记性变量flag,用来标记匹配结果以控制循环及返回结果,1表示正确匹配,0表示错误匹配,flag初值为1。)

//括号匹配
bool Matching()
{
	SqStack S;
	InitStack(S);
	int flag = 1;
	ElemType ch;
	ElemType s;
	scanf_s("%c", &ch);
	while (ch != '#' && flag)
	{
		switch (ch)
		{
		case '['||'(':
			Push(S, ch);
			break;
		case ')':
			if (!StackEmpty(S) && GetTop(S) == '(')
				Pop(S, s);
			else 
				flag = 0;
			break;
		case ']':
			if (!StackEmpty(S) && GetTop(S) == '[')
				Pop(S, s);
			else
				flag = 0;
			break;
		}
		scanf_s("%c", &ch);
	}
	if (StackEmpty(S) && flag)
		return true;
	else return false;
}

时间和空间复杂度均为O(n)

三、表达式求值

Polish notation(波兰表达式=前缀表达式)
Reverse Polish notation(逆波兰表达式=后缀表达式)

1、中缀表达式

运算符在两个操作数中间

2、后缀表达式

运算符在两个操作数后面

中缀转后缀的手算方法:
(1)确定中缀表达式中各个运算符的运算顺序
(2)按 左操作数 右操作数 运算符的方式组合成新的操作数
(3)如有运算符没被处理,继续(2)
(运算顺序不唯一,后缀表达式也不唯一,可以左优先即只要左边的运算符能先计算就先计算左边的)

后缀表达式的计算:
从左向右扫描,让运算符前最近的两个操作数执行运算,合为一个操作数(注意操作数左右顺序)

特点:
最后出现的操作数先被运算(因为是最近的两个操作数,LIFO后进先出—>栈!!)

用栈实现中缀表达式转后缀表达式
初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。
从左到右处理各个元素,直到末尾。可能遇到三种情况:
①遇到操作数。直接加入后缀表达式。
②遇到界限符。遇到“(” 直接入栈;遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(”为止。注意:“(”不加入后缀表达式。
③遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。
按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。

用栈实现后缀表达式的计算:
(1)从左往右扫描下一个元素,直到处理完所有元素
(2)若扫描到操作数则压入栈,并回到(1);否则执行(3)
(3)若扫描到运算符,则弹出两个栈顶元素,执行运算,运算结果压回栈顶,回到(1)
(注意先出栈的是右操作数;若表达式合法,则最后栈中只会留下一个元素,就是最终结果)

用栈实现中缀表达式的计算:
初始化两个栈,操作数栈和运算符栈;
若扫描到操作数,压入操作数栈;
若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈(期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈)

课本------【算法步骤】
1、初始化OPTR栈和OPND栈,将表达式起始符“#”压入OPTR栈。
2、扫描表达式,读入第一个字符ch,如果表达式没有扫描完毕至“#”或OPTR的栈顶元素不为“#”时,则循环执行以下操作:
·若ch不是运算符,则压入OPND栈,读入下一字符ch;
·若ch是运算符,则根据OPTR的栈顶元素和ch的优先级比较结果,做不同的处理:
• 若是小于,则ch压入OPTR栈,读入下一字符ch;
• 若是大于,则弹出OPTR栈顶的运算符,从OPND栈弹出两个数,进行相应运算,结果压入OPND栈;
• 若是等于,则OPTR的栈顶元素是“(”且ch是“)”,这时弹出OPTR栈顶的“(”,相当于括号匹配成功,然后读入下一字符ch。
3、OPND栈顶元素即为表达式求值结果,返回此元素。

表1 运算符比较
运算符比较

//计算表达式
char EvaluateExpression()
{
	SqStack OPND;
	SqStack OPTR;
	InitStack(OPND);
	InitStack(OPTR);
	Push(OPTR, '#');
	ElemType c;
	scanf_s("%c", &c);
	while (c != '#' || GetTop(OPTR) != '#')
	{
		if (!In(c))	//bool In(ElemType c)		//判断是否运算符
		{
			Push(OPND, c);
			scanf_s("%c", &c);
		}
		else
		{
			switch (Precede(GetTop(OPTR),c))	//Precede(ElemType s,ElemType c)	//判断栈顶元素与当前字符c的优先级
			{
			case '<':			//如果栈顶元素优先级小于c
				Push(OPTR, c);
				scanf_s("%c", c);
				break;
			case '>':			//如果栈顶元素优先级大于c,弹出栈顶运算符,并依次弹出运算数栈中的两个元素,先弹出的是“右操作数”
				Pop(OPTR, theta);
				Pop(OPND, b);
				Pop(OPND, a);
				Push(OPND, Operate(a, theta, b));
				break;
			case '=':			//当右括号遇到左括号,弹出左括号(括号里的运算符已在之前完成运算操作)
				Pop(OPTR, x);
				scanf_s("%c", c);
				break;
			}
		}
	}
	return GetTop(OPND);		//栈顶元素即为表达式所求值
}

时间和空间复杂度均为O(n)

3、前缀表达式

运算符在两个操作数前面

中缀转前缀的手算方法:
(1)确定中缀表达式中各个运算符的运算顺序
(2)按 运算符 左操作数 右操作数的方式组合成新的操作数
(3)如有运算符没被处理,继续(2)
(运算顺序不唯一,后缀表达式也不唯一,可以右优先即只要右边的运算符能先计算就先计算右边的)

用栈实现后缀表达式的计算:
(1)从右往左扫描下一个元素,直到处理完所有元素
(2)若扫描到操作数则压入栈,并回到(1);否则执行(3)
(3)若扫描到运算符,则弹出两个栈顶元素,执行运算,运算结果压回栈顶,回到(1)
(注意先出栈的是左操作数;若表达式合法,则最后栈中只会留下一个元素,就是最终结果)

队列的应用

数的层次遍历
图的广度优先搜索
缓冲区
计算机系统中的应用(主机与打印机之间速度不匹配问题,打印数据缓冲区CPU资源竞争
页面替换算法
舞伴问题

一、舞伴问题

对于舞伴配对问题,先入队的男士或女士先出队配成舞伴,因此设置两个队列分别存放男士和女士入队者。假设男士和女士的记录存放在一个数组中作为输入,然后依次扫描该数组的各元素,并根据性别来决定是进入男队还是女队。当这两个队列构造完成之后,依次将两队当前的队头元素出队来配成舞伴,直至某队列变空为止。此时,若某队仍有等待配对者,则输出此队列中排在队头的等待者的姓名,此人将是下一轮舞曲开始时第一个可获得舞伴的人。

时间和空间复杂度均为O(n)

tips

递归算法与非递归算法:
递归算法中有大量的重复运算,效率会低些

消除递归不一定要用栈:
对于单向递归和尾递归而言,可用迭代的方式消除递归

栈和队列的比较:
在这里插入图片描述


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值