堆棧
表達式轉換 (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;
}