1. 程式人生 > >演算法表示式求值演示(棧的應用)

演算法表示式求值演示(棧的應用)

【問題描述】 表示式計算是實現程式設計語言的基本問題之一,也是棧的應用的一個典型例子。設計一個程式,演示用算符優先法對算術表示式求值的過程。 【實現要求】 (1) 以字元序列的形式從終端輸入語法正確的、不含變數的整數表示式。利用下表給出的算符優先關係,實現對算術混合運算表示式的求值,並仿照求值中運算子棧、運算數棧、輸入字元和主要操作的變化過程。 (2) 擴充運算子集,如增加乘方、單目減、賦值等運算。 (3) 計算器的功能和模擬介面(選作)。 【測試資料】 下列表達式: 3*(7-2); 8; 1+2+3+4; 88-15; 1024/48; 1024/(48); (20+2)(6/2); 3-3-3; 8/(9-9); 2*(6+2*(3+6*(6+6))); (((6+6)*6+3)*2+6)*2; 【實現提示】

(1) 設定運算子棧和運算數棧輔助分析算符優先關係。 (2) 在讀入表示式的字元序列的同時,完成運算子和運算數(整數)的識別處理,以及相應的運算。 (3) 在識別出運算數的同時,要將其字元序列形式轉換成整數形式。 (4) 在程式的適當位置輸出運算子棧、運算數棧、輸入字元和主要操作的內容。

實現思路: 主要使用的是棧的結構。程式實現一共分為兩步:第一步,將輸入的表示式轉化為計算機方便處理的字尾表示式,;第二步,計算字尾表示式。 其中第一步,使用了兩個棧:操作符儲存棧(op)以及字尾表示式儲存棧(postexp)。而第二步,則是通過一個輔助棧,結合字尾表示式儲存棧(postexp)即主棧,分別對每個操作符以及對應得運算元進行計算,計算完成後再將其壓入主棧,最後當整個表示式計算完成後再將最後結果彈出,並輸出。 在處理單目操作符:“++”與“–”將其轉為“a”與“s”方便進行處理。 操作:

直接輸入表示式,再輸入“enter”即可得到表示式的結果,會忽略空格。

main.cpp


#include<iostream>
#include<string>
#include<stdlib.h>
#include"stack.h"
#include<Windows.h>

using namespace std;


char Precede(char a, char b) {
	int i, j;
	char pre[][10] = {
		/*運算子之間的優先順序製作成一張表格*/
		{ '>','>','<','<','<','>','<','<','<','>' },
		{ '>','>','<','<','<','>','<','<','<','>' },
		{ '>','>','>','>','<','>','<','<','<','>' },
		{ '>','>','>','>','<','>','<','<','<','>' },
		{ '<','<','<','<','<','>','<','<','<','0' },
		{ '>','>','>','>','0','=','>','>','>','>' },
		{ '>','>','>','>','<','>','>','<','<','>' },
		{ '>','>','>','>','<','>','>','>','>','>' },
		{ '>','>','>','>','<','>','>','>','>','>' },
		{ '<','<','<','<','<','0','<','<','<','=' } };
	switch (a) {
	case '+': i = 0; break;
	case '-': i = 1; break;
	case '*': i = 2; break;
	case '/': i = 3; break;
	case '(': i = 4; break;
	case ')': i = 5; break;
	case '^': i = 6; break;
	case 'a': i = 7; break;//用a代替++符號方便處理
	case 's': i = 8; break;//用s代替--符號方便處理
	case '#': i = 9; break;
	}
	switch (b) {
	case '+': j = 0; break;
	case '-': j = 1; break;
	case '*': j = 2; break;
	case '/': j = 3; break;
	case '(': j = 4; break;
	case ')': j = 5; break;
	case '^': j = 6; break;
	case '++': j = 7; break;
	case '--': j = 8; break;
	case '#': j = 9; break;
	}
	return pre[i][j];
}

int tranNum(stackL *postexp, string exp, int i)//將中綴表示式中的多位數,按照從低位到高位順序壓入postexp棧中,方便後續計算
{
	int len = 0,j;
	char expRead = exp[i];
	while (expRead <= '9'&&expRead >= '0')//獲得數符長度
	{
		//PushL(postexp, expRead);
		i++;
		expRead = exp[i];
		len++;
	}
	for (j = i-1; j >= i - len;j--)
	{
		PushL(postexp, exp[j]);
	}
	i--;			//使i恰好下次迴圈指向非數字
	PushL(postexp, '#');
	return i;
}
void tranPost(stackL *op, stackL*postexp, string exp)//將中綴表示式轉化為字尾表示式
{
	int i;
	char expRead, tempChar,preOp;
	InitStackL(op);
	InitStackL(postexp);
	PushL(op, '#');
	PushL(postexp, '#');

	for (i = 0; i < (int)exp.length(); i++)
	{
		expRead = exp[i];
		
		if (expRead == '#')				//表示式尾端
			break;
		else if (expRead == ' ')		//忽略空格
			continue;
		else if (expRead <= '9'&&expRead >= '0')	//處理運算元,傳入i,postexp,exp,返回i
		{
			i = tranNum(postexp, exp, i);
		}
		else if (expRead != '(' && expRead != ')' && expRead == exp[i + 1])//處理單目運算子(++、--)
		{
			if (expRead == '+')
				PushL(op, 'a');
			else if (expRead == '-')
				PushL(op, 's');
			else
			{
				cout << "error\n";
				break;
			}
			i++;									//使遍歷跳過自增自減符號到下一字元
		}
		else		//處理普通操作符
		{
			tempChar = GetTopL(op);
			preOp = Precede(tempChar, expRead);
			if (expRead == ')')
			{
				tempChar = PopL(op);
				while (tempChar != '(')
				{
					PushL(postexp, tempChar);
					//PushL(postexp, '#');

					tempChar = PopL(op);
				}
			}
			else if (preOp == '>')
			{
				PushL(postexp, PopL(op));
				PushL(op, expRead);
				//PushL(postexp, '#');
			}
			else if (preOp == '<')
				PushL(op, expRead);
		}
	}

	while (op->next->data != '#')
	{
		tempChar = PopL(op);
		PushL(postexp, tempChar);
	}
}

int ary(int expn)
{
	int i;
	int sum = 1;
	for (i = 0; i < expn; i++)
	{
		sum *= 10;
	}
	return sum;
}

int getValue(stackL *assistStack)//求得屬於同一數值的連續數符的值
{
	int value = 0;
	int ex = 0;
	char charValue;
	charValue = PopL(assistStack);
	while (charValue != '#')
	{
		value += (charValue - 48)*ary(ex);
		ex++;
		charValue = PopL(assistStack);
	}
	return value;
}

void pushBack(stackL *postexp, int valueBack)//將計算出來的int值化作字元,並從低位到高位壓入主棧中
{
	int i = 0;
	char valueChar;
	if (valueBack == 0)
		PushL(postexp, '0');
	while (valueBack != 0)
	{
		valueChar= (valueBack % 10)+48;
		PushL(postexp, valueChar);
		valueBack = valueBack / 10;
	}
	PushL(postexp, '#');
}

int power(int value, int expn)
{
	int result = 1;
	for (int i = 0; i < expn; i++)
	{
		result *= value;
	}
	return result;
}
int partCalu(stackL *postexp,stackL *assistStack )//計算小段式的值,此時兩個運算元分別在兩個棧中,運算元在輔助棧裡
{
	int value_1, value_2, value_3;
	char opera;
	PushL(assistStack, PopL(postexp));
	while (GetTopL(postexp) <= '9'&&GetTopL(postexp) >= '0')//使兩個運算元和運算子都在輔助棧中,方便運算元的還原
	{
		PushL(assistStack, PopL(postexp));
	}

	value_1 = getValue(assistStack);
	value_2 = getValue(assistStack);
	opera = PopL(assistStack);

	switch (opera)
	{
	case '+':
		value_3 = value_1 + value_2;
		break;
	case '-':
		value_3 = value_1 - value_2;
		break;
	case '*':
		value_3 = value_1 * value_2;
		break;
	case '/':
		if (value_2 == 0)
		{
			cout << "error:除數為0" << endl;
			return 0;
		}
		else
		value_3 = value_1 / value_2;
		break;
	case'^':
		value_3 = power(value_1, value_2);
	default:
		break;
	}
	if (assistStack->next == NULL && postexp->next == NULL)
	{
		cout << value_2;
		return 0;
	}
	else if (assistStack->next == NULL && postexp->next->next == NULL)//結束條件
	{
		cout << value_3;
		return 0;
	}
	pushBack(postexp, value_3);//將部分求值結果重新壓入主棧

	if (GetTopL(assistStack) <= '9'&&GetTopL(assistStack) >= '0')//再次滿足部分求值條件
		partCalu(postexp, assistStack);
}
int caluValue(stackL *postexp)
{
	int control = 1;
	int value = 0;
	stackL *assistStack;
	char postRead,opera;
	assistStack = (stackL*)malloc(sizeof(stackL));
	InitStackL(assistStack);

	postRead = GetTopL(postexp);
	while (postexp->next != NULL)
	{
		/*主棧棧頂為#時進行是否部分求值檢測,
		如果輔助棧頂為數符的話,則進行部分求值,求值後將值繼續壓入主棧,
		壓入時注意低位先壓入,這樣就可以在連續求值時進行遞迴*/
		if (postRead == '#'&&assistStack->next!=NULL)
		{
			if (GetTopL(assistStack) <= '9'&&GetTopL(assistStack) >= '0')
			{
				control =partCalu(postexp, assistStack);
				if (control == 0)
					return 0;
			}
				
			else if(GetTopL(assistStack) > '9'|| GetTopL(assistStack) < '0')
				PushL(assistStack, PopL(postexp));
		}
		else if (postRead == 'a'||postRead == 's')
		{
			opera=PopL(postexp);
			if (GetTopL(postexp) != '#')
			{
				cout << "單目運算子運用錯誤,應放在數字後" << endl;
				exit(0);
			}
			else
			{
				PushL(assistStack,PopL(postexp));
				while (GetTopL(postexp) <= '9'&&GetTopL(postexp) >= '0')//將數符彈出主棧 並壓入輔助棧中
				{
					PushL(assistStack, PopL(postexp));
				}
				if (opera == 'a')
					pushBack(postexp, getValue(assistStack) + 1);
				else if (opera == 's')
					pushBack(postexp, getValue(assistStack) - 1);
			}
		}
		else
			PushL(assistStack, PopL(postexp));
		postRead = GetTopL(postexp);
	}
}

int main()
{
	while (1)
	{
		string express;
		cout << "please enter the expression statement,and end with '#'" << endl;
		cin >> express;

		stackL *op, *postexp;
		op = (stackL*)malloc(sizeof(stackL));	// 操作符棧
		postexp = (stackL*)malloc(sizeof(stackL));	//字尾表示式棧

		tranPost(op, postexp, express);	//將輸入的中綴表示式轉化為字尾表示式
		cout << express << " = ";
		caluValue(postexp);	//計算字尾表示式的值
		cout << "\n\n";
		free(op);
		free(postexp);
	}
}


stack.h

#include<stdlib.h>
#include<iostream>

using namespace std;

typedef char Elemtype;

typedef struct LinkStack
{
	Elemtype data;
	struct LinkStack *next;
}stackL;

//初始化
void InitStackL(stackL *lst)
{
	lst->next = NULL;
}
//判斷是否為空,為空則返回1
int StackLEmpty(stackL *lst)
{
	return (lst->next == NULL);
}

//壓入
void PushL(stackL *lst, Elemtype x)
{
	stackL *p;
	p = (stackL*)malloc(sizeof(stackL));
	p->next = lst->next;
	p->data = x;
	lst->next = p;
}

//彈出
Elemtype PopL(stackL* lst)
{
	stackL *toFree;
	if (lst->next == NULL);
		//cout << "the stack is empty\n";
	else
	{
		Elemtype x;
		x = (lst->next)->data;
		toFree = lst->next;
		lst->next = (lst->next)->next;
		free(toFree);
		return x;
	}
	return 0;
}

//獲取棧頂元素
Elemtype GetTopL(stackL* lst)
{
	if (lst->next == NULL);
		//cout << "the stack is empty\n";
	else
	{
		Elemtype x;
		x = lst->next->data;
		return x;
	}
	return 0;
}