堆栈
表达式转换 (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()
。
输入格式:
输入首先给出两个正整数N1
和N2
,表示堆栈S1
和S2
的最大容量。随后给出一系列的队列操作: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;
}