字符串四則運算表達式求值

四則表達式求值示例:
四則運算求值
示例中的運算式子爲10+20-30+4*(20-10+20*10-100),運算結果爲440

1. 後綴表達式

對於四則運算表達式,如 10+20-3*(10-5)+8 ,我們如何用程序實現這個運算表達式求值呢?可以使用 一種不需要括號的後綴表達法,我們也稱爲逆波蘭(RPN)表示,如上面的例子
10+20-3*(10-5)+8,使用後綴表示法就變成了這樣
10 20 + 3 10 5 - * - 8 +
我們把這樣的式子稱爲後綴表達式,叫後綴的原因爲 所有的符號都在要運算數字的後面,那麼我們如何使用後綴表達式計算結果呢?

以上面的後綴表達式爲例,運算規則是這樣的 從前往後,遇到數字則入棧,遇到符號則出棧做運算,將運算結果再入棧。 這裏使用棧的特性,先進先出(FILO)。

所以根據上面的規則,計算該後綴表達式的步驟如下:

  1. 10 和 20 入棧,此時棧中元素爲(10,20)
  2. 運算符號 “+” 號, 棧中元素出棧(10 和 20)做 “+” 操作後,結果(30),入棧;此時棧中元素爲30。
  3. 數字 3、10、5 依次入棧,此時棧中元素爲(30,3,10,5)
  4. 運算符號 “-” 號,棧中元素出棧(10 和 5),注意此時棧頂元素爲5,出棧後棧頂元素爲10,操作時順序爲 10 - 5, 結果爲5,入棧;此時棧中元素爲(30,3,5)
  5. 運算符號 “*”,棧中元素出棧(3 和 5)做 “*” 操作後,結果(15),入棧;此時棧中元素爲(30,15)
  6. 運算符號 “-” ,元素出棧(30和15)做 “-” 操作後,結果爲(15),入棧;此時棧中元素爲(15)
  7. 數字 8 入棧,此時棧中元素爲 (15, 8)
  8. 運算符號 “+” ,元素出棧 (15和8)做 “+” 操作後,結果爲(23)入棧;此時棧中元素爲(23),至此運算結束,運算結果爲23

2. 後綴表達式的推倒

我們把平時運算中用的四則表達式稱爲 中綴表達式,因爲運算符號都在中間,那麼我們如何將中綴表達式轉化爲後綴表達式呢?
規則如下:

從左到右遍歷中綴表達式中每一個數字和符號,若是數字則輸出,稱爲中綴表達式中的一部分;若是符號則判讀其與棧頂符號的優先級,是右括號或者優先級不高於棧頂符號(乘除高於加減)則棧頂元素依次出棧並輸出,直到棧爲空或者棧頂符號優先級低於該符號爲止,並將當前符號進棧。一直到最終輸出後綴表達式爲指

我們同樣依據上面的例子來進行說明,將中綴表達式10+20-3*(10-5)+8,轉化爲後綴表達式10 20 + 3 10 5 - * - 8 +

  1. 輸出10,此時後綴表達式輸出爲 (10)
  2. 符號 “+” 入棧,此時符號棧中元素爲(+)
  3. 輸出20,此時後綴表達式輸出爲 (10,20)
  4. 符號“-” ,棧頂元素比對,棧頂元素“+” 不高於“-”號,出棧並輸出,此時後綴表達式輸出爲 (10,20,+),符號入棧此時符號棧中元素爲(-)
  5. 輸出3,此時後綴表達式輸出爲 (10,20,+,3)
  6. 符號“*” 入棧,此時符號棧中元素爲(-,*)
  7. 符號“(” 入棧,此時符號棧中元素爲(-,*,"(" )
  8. 輸出10,此時後綴表達式輸出爲 (10,20,+,3,10)
  9. 符號“-” 入棧,此時符號棧中元素爲(-,*,"(" ,-)
    10.數字5輸出, 此時後綴表達式輸出爲 (10,20,+,3,10,5)
  10. 符號“)”,符號出棧直到匹配小括號“(”爲止,此時符號棧中元素爲(-,*),此時後綴表達式輸出爲 (10,20,+,3,10,5, -)
  11. 符號“+”,棧頂元素“*”優先級不低於“+”號,出棧輸出;此時棧頂元素“-”號也不低於符號“+”,出棧輸出;此時符號棧爲空,當前符號入棧,此時符號棧中元素爲(+),此時後綴表達式輸出爲 (10,20,+,3,10,5, -,*, -)
  12. 輸出數字8,此時後綴表達式輸出爲 (10,20,+,3,10,5, -,*, -, 8)
  13. 符號棧中元素全部出棧,即最終後綴表達式爲 10 20 + 3 10 5 - * - 8 +

下面是關於輸入任意字符串後計算表達式的值的代碼:

int calcResult(QString string)
{
	// QString轉Char*
	QByteArray byteArray = string.toLocal8Bit();
	char* pString = byteArray.data();

	QString tempString;
	QStringList tempStringList;		// 用於存放後綴結果
	QVector<char> operStack;

	for (int i = 0; i < string.length(); ++i)
	{
		if (pString[i] >= '0' && pString[i] <= '9')
			tempString.append(QChar(pString[i]));
		else
		{
			// 如果爲符號,則將數字加入到結果列表中
			if (!tempString.isEmpty())
			{
				tempStringList.append(tempString);
				tempString.clear();
			}
			
			char cValue = pString[i];

			// "("直接入棧
			if (cValue == '(')
				operStack.push_back(cValue);
			// 棧頂元素優先級不大於該符號則出棧,即若爲 * 和 / 則出棧
			else if (cValue == '*' || cValue == '/')
			{
				while (!operStack.isEmpty())
				{
					int size = operStack.size();
					char cOper = operStack[size - 1];
					if (cOper == '*' || cOper == '/')
						tempStringList.push_back(QChar(cOper));
					else
						break;
					operStack.pop_back();
				}
				operStack.push_back(cValue);
			}
			// 若爲)則匹配到(爲止的所有符號出棧
			else if (cValue == ')')
			{
				// 出棧到匹配(爲止
				char cTopStack = *(operStack.end() - 1);
				while (cTopStack != '(')
				{
					tempStringList.push_back(QChar(cTopStack));
					operStack.pop_back();

					cTopStack = *(operStack.end() - 1);
				}
				// 使(出棧
				operStack.pop_back();
			}
			// 棧頂元素優先級不大於該符號則出棧,即若爲 + 、 - 、* 、/ 則出棧
			else if (cValue == '+' || cValue == '-')
			{
				while (!operStack.isEmpty())
				{
					char cOper = *(operStack.end() - 1);
					if (cOper == '+' || cOper == '-' || cOper == '*' || cOper == '/')
					{
						tempStringList.push_back(QChar(cOper));
						operStack.pop_back();
					}
					else
						break;
				}
				// 該符號入棧
				operStack.push_back(cValue);
			}
		}
	}

	if (!tempString.isEmpty())
		tempStringList.append(tempString);
	while (!operStack.isEmpty())
	{
		tempStringList.append(QChar(*(operStack.end() - 1)));
		operStack.pop_back();
	}

	// 根據後綴表達式計算結果
	QVector<int> numberStack;	// 數字棧
	for (auto iter = tempStringList.begin(); iter != tempStringList.end(); ++iter)
	{
		QString tempValue = *iter;

		bool isNumber = false;
		int number = tempValue.toInt(&isNumber);
		if (isNumber)
			numberStack.push_back(number);
		else
		{
			// 取出棧頂的兩個數
			int count = numberStack.count();
			int number1 = numberStack[count - 2];
			int number2 = numberStack[count - 1];
			numberStack.pop_back();
			numberStack.pop_back();

			int result = 0;	// 計算結果
			if (tempValue == "+")
				result = number1 + number2;
			else if (tempValue == "-")
				result = number1 - number2;
			else if (tempValue == "*")
				result = number1 * number2;
			else if (tempValue == "/")
				result = number1 / number2;

			// 將結果入棧
			numberStack.push_back(result);
		}
	}

	return numberStack[0];
}

完整代碼如下:
頭文件:

#pragma once

#include <QtWidgets/QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVector>

class FourOperWidget : public QWidget
{
	Q_OBJECT

public:
	FourOperWidget();
	~FourOperWidget();

private:
	QLabel *m_ResultTag = nullptr;
	QLineEdit *m_CalcLineEdit = nullptr;
	QPushButton *m_PushButton = nullptr;

protected:
	bool eventFilter(QObject *watched, QEvent *event) override;

private slots:
	void onCalcButtonClicked(void);

private:
	int calcResult(QString string);
};

.cpp文件

#include "FouOperWidget.h"
#include <iostream>
#include <QVBoxLayout>
#include <QEvent>
#include <QKeyEvent>

FourOperWidget::FourOperWidget()
{

	m_ResultTag = new QLabel;
	m_ResultTag->setStyleSheet("font-size: 30px; color: rgb(255, 0, 0);");
	m_ResultTag->setMinimumHeight(50);
	m_CalcLineEdit = new QLineEdit;
	m_CalcLineEdit->installEventFilter(this);

	QString str = QString::fromLocal8Bit("計算");
	m_PushButton = new QPushButton(str);
	QObject::connect(m_PushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));

	QVBoxLayout *layout = new QVBoxLayout(this);

	QHBoxLayout *topLayout = new QHBoxLayout;
	layout->addLayout(topLayout);
	topLayout->addWidget(m_CalcLineEdit);
	topLayout->addWidget(m_PushButton);

	QLabel *resultTag = new QLabel(QString::fromLocal8Bit("計算結果爲:"));
	layout->addWidget(resultTag);
	layout->addWidget(m_ResultTag);

	layout->addStretch();
}

FourOperWidget::~FourOperWidget()
{

}

void FourOperWidget::onCalcButtonClicked(void)
{
	QString str = m_CalcLineEdit->text();
	if (str.isEmpty())
		return;

	int result = calcResult(str);
	m_ResultTag->setText(QString::number(result));
}

int FourOperWidget::calcResult(QString string)
{
	// QString轉Char*
	QByteArray byteArray = string.toLocal8Bit();
	char* pString = byteArray.data();

	QString tempString;
	QStringList tempStringList;		// 用於存放後綴結果
	QVector<char> operStack;

	for (int i = 0; i < string.length(); ++i)
	{
		if (pString[i] >= '0' && pString[i] <= '9')
			tempString.append(QChar(pString[i]));
		else
		{
			// 如果爲符號,則將數字加入到結果列表中
			if (!tempString.isEmpty())
			{
				tempStringList.append(tempString);
				tempString.clear();
			}
			
			char cValue = pString[i];

			// "("直接入棧
			if (cValue == '(')
				operStack.push_back(cValue);
			// 棧頂元素優先級不大於該符號則出棧,即若爲 * 和 / 則出棧
			else if (cValue == '*' || cValue == '/')
			{
				while (!operStack.isEmpty())
				{
					int size = operStack.size();
					char cOper = operStack[size - 1];
					if (cOper == '*' || cOper == '/')
						tempStringList.push_back(QChar(cOper));
					else
						break;
					operStack.pop_back();
				}
				operStack.push_back(cValue);
			}
			// 若爲)則匹配到(爲止的所有符號出棧
			else if (cValue == ')')
			{
				// 出棧到匹配(爲止
				char cTopStack = *(operStack.end() - 1);
				while (cTopStack != '(')
				{
					tempStringList.push_back(QChar(cTopStack));
					operStack.pop_back();

					cTopStack = *(operStack.end() - 1);
				}
				// 使(出棧
				operStack.pop_back();
			}
			// 棧頂元素優先級不大於該符號則出棧,即若爲 + 、 - 、* 、/ 則出棧
			else if (cValue == '+' || cValue == '-')
			{
				while (!operStack.isEmpty())
				{
					char cOper = *(operStack.end() - 1);
					if (cOper == '+' || cOper == '-' || cOper == '*' || cOper == '/')
					{
						tempStringList.push_back(QChar(cOper));
						operStack.pop_back();
					}
					else
						break;
				}
				// 該符號入棧
				operStack.push_back(cValue);
			}
		}
	}

	if (!tempString.isEmpty())
		tempStringList.append(tempString);
	while (!operStack.isEmpty())
	{
		tempStringList.append(QChar(*(operStack.end() - 1)));
		operStack.pop_back();
	}

	// 根據後綴表達式計算結果
	QVector<int> numberStack;	// 數字棧
	for (auto iter = tempStringList.begin(); iter != tempStringList.end(); ++iter)
	{
		QString tempValue = *iter;

		bool isNumber = false;
		int number = tempValue.toInt(&isNumber);
		if (isNumber)
			numberStack.push_back(number);
		else
		{
			// 取出棧頂的兩個數
			int count = numberStack.count();
			int number1 = numberStack[count - 2];
			int number2 = numberStack[count - 1];
			numberStack.pop_back();
			numberStack.pop_back();

			int result = 0;	// 計算結果
			if (tempValue == "+")
				result = number1 + number2;
			else if (tempValue == "-")
				result = number1 - number2;
			else if (tempValue == "*")
				result = number1 * number2;
			else if (tempValue == "/")
				result = number1 / number2;

			// 將結果入棧
			numberStack.push_back(result);
		}
	}

	return numberStack[0];
}

bool FourOperWidget::eventFilter(QObject *watched, QEvent *event)
{
	// 僅可以輸入數字、+ - * / 和 ( ) 
	if (event->type() == QEvent::KeyPress)
	{
		QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
		if (keyEvent == nullptr)
			return QWidget::eventFilter(watched, event);

		QString keyString = keyEvent->text();
		bool isNumber = false;
		keyString.toInt(&isNumber);
		if (isNumber)
			return false;

		if (keyString == "+" || keyString == "-" || \
			keyString == "*" || keyString == "/" || \
			keyString == "(" || keyString == ")")
			return false;

		if (keyEvent->key() == Qt::Key_Left || \
			keyEvent->key() == Qt::Key_Right || \
			keyEvent->key() == Qt::Key_Backspace)
			return false;

		return true;
	}

	return QWidget::eventFilter(watched, event);
}

這裏我使用了 過濾器,使文本輸入框只能輸入數字、+、-、*、/、(、)、左右方向鍵和BackSpace鍵。關於過濾器的使用可參照
Qt中的事件(2)- 事件過濾器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章