线性结构---堆栈

这篇博客探讨了堆栈在计算中的应用,包括将中缀表达式转换为后缀表达式、计算前缀表达式的值以及如何用两个堆栈模拟队列。文章详细解释了解题思路和算法实现,提供了具体的输入输出格式和样例,以及解题过程中可能遇到的问题和解决方案。
摘要由CSDN通过智能技术生成

堆栈


表达式转换   (25分)

算术表达式有前缀表示法、中缀表示法和后缀表示法等形式。日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个运算数中间。请设计程序将中缀表达式转换为后缀表达式。

输入格式:

输入在一行中给出不含空格的中缀表达式,可包含+-*\以及左右括号(),表达式不超过20个字符。

输出格式:

在一行中输出转换后的后缀表达式,要求不同对象(运算数、运算符号)之间以空格分隔,但结尾不得有多余空格。

输入样例:

2+3*(7-4)+8/4

输出样例:

2 3 7 4 - * + 8 4 / +

解题思路:利用堆栈模拟。将数字从字符串中分离并存储到另一个字符串中,对于操作符也进行分离,并按rank标识为优先权。

两者分离处理之后,1)当输入为操作符时,输出该操作符之前的数字(某种程度上来说,数字的顺序并没有改变,改变的只是操作符的位置),

2)将操作符的优先级与堆栈中的优先级进行比较,决定入栈还是出栈(该操作是循环进行的,直到不能出栈为止),

3)最后将新操作符入栈,再读取下一个数字或操作符,直到表达式结束。

提交代码:

编译器:g++

#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
const int MAXN = 24;//定义最大的堆栈容量
int main()
{
	int opr[MAXN], top = 0, rank = 3;//opr数组存储操作优先级,top标识当前存储多少元素,rank标识优先权
	char chopr[MAXN];//存储字符操作符
	char ch;
	bool isneg = true;//判断是否为负数
	string str = "";//用于存储数字
	while((ch = getchar()) != '\n')
	{
		if(isneg && ch == '-')//如果输入的 '-' 是负号,则存储到字符串str中
		{
			isneg = false, str += ch;//isneg = false,标识下一个 '-' 将作为减号处理
			continue;
		}
		else if(isneg && ch == '+')//题中还有输入为 '+',的情况,则丢弃该操作符
		{
			isneg = false;
			continue;
		}
		else if(ch == '.')//如果存在 '.',则表示为该数字是浮点数,将该字符存储到字符串中,'.'之后的加减号必定不是单目运算符
			isneg = false, str += ch;
		else if('0' <= ch && ch <= '9')//将数字存储到字符中,之后的加减号必定也不是单目运算符
			isneg = false, str += ch; 
		if(ch == '*' || ch == '/' || ch == '+' || ch == '-' || ch == '(' || ch == ')')
		{
			if(str != "")//表示数字部分分离完成,如果不为空则输出
				cout<<str<<' ';
			str = "";
			if(ch != ')') isneg = true;//考虑到这个 ')'之后可能进行加减操作,所以不对isneg处理,其余情况都可能表示负号,正号
			if (ch == '(') rank = 3;//以下的判断是对操作符赋予优先级
			else if(ch == '*' || ch == '/') rank = 2;
			else if(ch == '+' || ch == '-') rank = 1;
			else if(ch == ')') rank = 0;
			while(top && rank <= opr[top - 1] && opr[top - 1] != 3)//模拟出栈操作,利用优先级判断是否可以出栈
			{
				if(rank == 0 && opr[top - 1] == 3)//对于'('不作输出,则直接丢弃
				{
					top--;
					break;
				}
				cout<<chopr[top - 1]<<' ';
				top--;
			}
			if(rank == 0 && opr[top - 1] == 3) top--;//对于')'与最近的'('匹配之后不作处理
			else opr[top] = rank, chopr[top] = ch, top++;//否则,将输入的操作符入栈
			continue;
		}
	}
	if(str != "")//将最后一个数字输出
	{
		cout<<str;
		if(top) cout<<' ';
	}
	while(top)//将所有的操作符出栈
	{
		cout<<chopr[top - 1];
		top--;
		if(top) cout<<' ';
	}
	return 0;
}

求前缀表达式的值   (25分)

算术表达式有前缀表示法、中缀表示法和后缀表示法等形式。前缀表达式指二元运算符位于两个运算数之前,例如2+3*(7-4)+8/4的前缀表达式是:+ + 2 * 3 - 7 4 / 8 4。请设计程序计算前缀表达式的结果值。

输入格式:

输入在一行内给出不超过30个字符的前缀表达式,只包含+-*\以及运算数,不同对象(运算数、运算符号)之间以空格分隔。

输出格式:

输出前缀表达式的运算结果,保留小数点后1位,或错误信息ERROR

输入样例:

+ + 2 * 3 - 7 4 / 8 4

输出样例:

13.0

解题思路:

这道题深深地陷在求后缀表达式的坑里,怎么模拟都不得要领。

于是用表达式树进行了求解。

如果将表达式中的数字作为叶节点,操作符作为非叶节点,那么就能构造出树的形式。

比如表达式:2 + 3 * (7 - 4)+ 8 / 4,可构造如下的结构


如果对这棵树进行先序遍历,那么就是输入样例+ + 2 * 3 - 7 4 /  8 4

如果能将前缀表达式转换为树的形式,然后再将树转换为中缀表达式求解即可,就可以利用中缀表达式求得结果

1)既然是先序遍历得到输入样例,那么就用先序遍历建树,建树方式就是数字作为叶节点,操作符作为非叶节点。

2)之后利用中序遍历求解。

具体实现看代码。

提交代码:

编译器:g++

#include <iostream>
#include <string.h>
using namespace std;
typedef struct Tnode tnode;
const int MAXN = 31;
struct Tnode{
	double num;
	char opr;
	struct Tnode *lc, *rc;
};//作为树结点,其中的double、char部分可以使用联合体,可以节约空间
char str[MAXN];//存储数字
tnode *buildTree(void);//先序建树
double getNum(char *str);//将字符数字转换为数字
double LDR(tnode *t, bool &islaw);//中序遍历
double cal(double a, double b, char opr);
int main()
{
	tnode *t = NULL;
	bool islaw = true;//判断是否可以运算
	t = buildTree();
	double ans = LDR(t, islaw);
	if(islaw) printf("%.1lf\n", ans);
	else printf("ERROR\n");
	return 0;
}
tnode *buildTree(void)
{
	tnode * t = new tnode;
	scanf("%s",str);
	if('0' <= str[0] && str[0] <= '9')
	{
		t->num = getNum(str);
		t->opr = ' ';
		t->lc = t->rc = NULL;
	}
	else if((str[0] == '-' || str[0] == '+') && strlen(str) > 1)//判断是不是正负号
	{
		t->num = getNum(str);
		t->opr = ' ';
		t->lc = t->rc = NULL;
	}
	else//如果非数字部分,先序建树
	{
		t->num = 0;
		t->opr = str[0];
		t->lc = buildTree();
		t->rc = buildTree();
	}
	return t;
}
double getNum(char *str)
{
    double num = 0, digit = 0.1;//整数部分,小数部分
    bool isfloat = false;
    int len = (int)strlen(str);
    for(int i = 0; i < len; ++i)
    {
        if(str[i] == '.')
            isfloat = true;
        else if('0' <= str[i] && str[i] <= '9')
        {
            if(isfloat)
                num += digit * (str[i] - '0'), digit /= 10;//小数部分转换
            else
                num = num * 10 + (str[i] - '0');//整数部分转换
        }
    }
    if(str[0] == '-') num = -num;//取负
    return num;
}
double LDR(tnode *t, bool &islaw)
{
	double ans = 0;
	if(t->opr != ' ')
	{
		double a = LDR(t->lc, islaw);
		double b = LDR(t->rc, islaw);//取出两个数
		if(t->opr == '/' && b == 0)//除数为零时,非法
		{
			islaw = false;
			return 0;
		}
		else
			ans = cal(a, b, t->opr);
	}
	else
		ans = t->num;
	return ans;
}
double cal(double a, double b, char opr)
{
	switch(opr)
	{
		case '+': return a + b;
		case '-': return a - b;
		case '*': return a * b;
		case '/': return a / b;
	}
}


堆栈模拟队列   (25分)

设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q。

所谓用堆栈模拟队列,实际上就是通过调用堆栈的下列操作函数:

  • int IsFull(Stack S):判断堆栈S是否已满,返回1或0;
  • int IsEmpty (Stack S ):判断堆栈S是否为空,返回1或0;
  • void Push(Stack S, ElementType item ):将元素item压入堆栈S
  • ElementType Pop(Stack S ):删除并返回S的栈顶元素。

实现队列的操作,即入队void AddQ(ElementType item)和出队ElementType DeleteQ()

输入格式:

输入首先给出两个正整数N1N2,表示堆栈S1S2的最大容量。随后给出一系列的队列操作:A item表示将item入列(这里假设item为整型数字);D表示出队操作;T表示输入结束。

输出格式:

对输入中的每个D操作,输出相应出队的数字,或者错误信息ERROR:Empty。如果入队操作无法执行,也需要输出ERROR:Full。每个输出占1行。

输入样例:

3 2
A 1 A 2 A 3 A 4 A 5 D A 6 D A 7 D A 8 D D D D T

输出样例:

ERROR:Full
1
ERROR:Full
2
3
4
7
8
ERROR:Empt
解题思路:

此处参考了大神的想法,点这里

题中的两个的堆栈的大小是相同的,取两者中最小的那个

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 1002;
struct Stack{
    int s[MAXN];
    int t;
}s1, s2;//s1作为入队使用,s2作为出队使用。
bool IsFull(int n);
bool IsEmpty(int n);
void Push(int item);
int Pop(void);
int main()
{
    int n, m;
    int item;
    char ch;
    scanf("%d%d", &n, &m);
    s1.t = s2.t = 0;
    n = n < m? n: m;
    while((ch = getchar()) != 'T')
    {
        if(ch == 'A')
        {
            scanf("%d", &item);
            if(IsFull(n))
                printf("ERROR:Full\n");
            else
                Push(item);
        }
        else if(ch == 'D')
        {
            if(IsEmpty(n))
                printf("ERROR:Empty\n");
            else
                printf("%d\n",Pop());
        }
    }
    return 0;
}
bool IsFull(int n)
{
    if(s2.t != 0 && s1.t == n)//如果s1栈满,并且s2栈非空,那么表示队列已满
        return true;
    if(s2.t == 0 && s1.t == n)//如果s1栈满,则将元素压入s2中
    {
        while(s2.t < n && s1.t > 0)
        {
            s2.s[s2.t] = s1.s[s1.t - 1];
            s1.t--, s2.t++;
        }
    }
    return false;
}
bool IsEmpty(int n)
{
    if(s1.t == 0 && s2.t == 0)//如果两个栈都为空,则为空
        return true;
    if(s2.t == 0)//如果s2为空,则将s1中的元素压入到s2中
    {
        while(s2.t < n && s1.t > 0)
        {
            s2.s[s2.t] = s1.s[s1.t - 1];
            s1.t--, s2.t++;
        }
    }
    return false;
}
void Push(int item)
{
    s1.s[s1.t++] = item;
}
int Pop(void)
{
    int tmp = s2.s[s2.t - 1];
    s2.t--;
    return tmp;
}

Pop Sequence   (25分)

Given a stack which can keep MMM numbers at most. Push NNN numbers in the order of 1, 2, 3, ..., NNN and pop randomly. You are supposed to tell if a given sequence of numbers is a possible pop sequence of the stack. For example, ifMMM is 5 and NNN is 7, we can obtain 1, 2, 3, 4, 5, 6, 7 from the stack, but not 3, 2, 1, 7, 5, 6, 4.

Input Specification:

Each input file contains one test case. For each case, the first line contains 3 numbers (all no more than 1000):MMM (the maximum capacity of the stack), NNN (the length of push sequence), and KKK (the number of pop sequences to be checked). Then KKK lines follow, each contains a pop sequence of NNN numbers. All the numbers in a line are separated by a space.

Output Specification:

For each pop sequence, print in one line "YES" if it is indeed a possible pop sequence of the stack, or "NO" if not.

Sample Input:

5 7 5
1 2 3 4 5 6 7
3 2 1 7 5 6 4
7 6 5 4 3 2 1
5 6 4 3 7 2 1
1 7 6 5 4 3 2

Sample Output:

YES
NO
NO
YES
NO

解题思路:

   再次链接一个大神的解题思路:点这里

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 1002;
int Stackout[MAXN], Stackin[MAXN], Stack[MAXN];//分别是存储出栈顺序,入栈顺序,模拟入栈出栈的堆栈
int main()
{
    int n, Max, k;
    cin>>Max>>n>>k;
    for(int i = 0; i <= n; ++i)//入栈顺序
        Stackin[i] = i;
    while(k--)
    {
        bool Yes = true;
        int index = 1, top = 0;
        for(int i = 0; i < n; ++i)//题中的某个出栈顺序
            cin>>Stackout[i];
        for(int i = 0; i < n && Yes; ++i)//模拟用Stack出栈入栈
        {
            if(top != 0 && Stack[top - 1] == Stackout[i])//如果栈顶元素,与出栈顺序相同,那么出栈
            {
                top--;
            }
            else
            {
                bool Find = false;
                for(; index <= n; ++index)//将元素入栈
                {
                    Stack[top++] = Stackin[index];
                    if(top > Max) Yes = false;
                    if(Stackout[i] == Stackin[index])//直到下一个出栈元素与栈中元素相同,则进栈结束
                    {
                        Find = true; index++;
                        break;
                    }
                }
                if(!Find) Yes = false;
                else top--;
            }
        }
        if(Yes) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值