線性結構---堆棧

堆棧


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


發佈了48 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章