四則表達式求值示例:
示例中的運算式子爲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)。
所以根據上面的規則,計算該後綴表達式的步驟如下:
- 10 和 20 入棧,此時棧中元素爲(10,20)
- 運算符號 “+” 號, 棧中元素出棧(10 和 20)做 “+” 操作後,結果(30),入棧;此時棧中元素爲30。
- 數字 3、10、5 依次入棧,此時棧中元素爲(30,3,10,5)
- 運算符號 “-” 號,棧中元素出棧(10 和 5),注意此時棧頂元素爲5,出棧後棧頂元素爲10,操作時順序爲 10 - 5, 結果爲5,入棧;此時棧中元素爲(30,3,5)
- 運算符號 “*”,棧中元素出棧(3 和 5)做 “*” 操作後,結果(15),入棧;此時棧中元素爲(30,15)
- 運算符號 “-” ,元素出棧(30和15)做 “-” 操作後,結果爲(15),入棧;此時棧中元素爲(15)
- 數字 8 入棧,此時棧中元素爲 (15, 8)
- 運算符號 “+” ,元素出棧 (15和8)做 “+” 操作後,結果爲(23)入棧;此時棧中元素爲(23),至此運算結束,運算結果爲23
2. 後綴表達式的推倒
我們把平時運算中用的四則表達式稱爲 中綴表達式,因爲運算符號都在中間,那麼我們如何將中綴表達式轉化爲後綴表達式呢?
規則如下:
從左到右遍歷中綴表達式中每一個數字和符號,若是數字則輸出,稱爲中綴表達式中的一部分;若是符號則判讀其與棧頂符號的優先級,是右括號或者優先級不高於棧頂符號(乘除高於加減)則棧頂元素依次出棧並輸出,直到棧爲空或者棧頂符號優先級低於該符號爲止,並將當前符號進棧。一直到最終輸出後綴表達式爲指
我們同樣依據上面的例子來進行說明,將中綴表達式10+20-3*(10-5)+8,轉化爲後綴表達式10 20 + 3 10 5 - * - 8 +
- 輸出10,此時後綴表達式輸出爲 (10)
- 符號 “+” 入棧,此時符號棧中元素爲(+)
- 輸出20,此時後綴表達式輸出爲 (10,20)
- 符號“-” ,棧頂元素比對,棧頂元素“+” 不高於“-”號,出棧並輸出,此時後綴表達式輸出爲 (10,20,+),符號入棧此時符號棧中元素爲(-)
- 輸出3,此時後綴表達式輸出爲 (10,20,+,3)
- 符號“*” 入棧,此時符號棧中元素爲(-,*)
- 符號“(” 入棧,此時符號棧中元素爲(-,*,"(" )
- 輸出10,此時後綴表達式輸出爲 (10,20,+,3,10)
- 符號“-” 入棧,此時符號棧中元素爲(-,*,"(" ,-)
10.數字5輸出, 此時後綴表達式輸出爲 (10,20,+,3,10,5) - 符號“)”,符號出棧直到匹配小括號“(”爲止,此時符號棧中元素爲(-,*),此時後綴表達式輸出爲 (10,20,+,3,10,5, -)
- 符號“+”,棧頂元素“*”優先級不低於“+”號,出棧輸出;此時棧頂元素“-”號也不低於符號“+”,出棧輸出;此時符號棧爲空,當前符號入棧,此時符號棧中元素爲(+),此時後綴表達式輸出爲 (10,20,+,3,10,5, -,*, -)
- 輸出數字8,此時後綴表達式輸出爲 (10,20,+,3,10,5, -,*, -, 8)
- 符號棧中元素全部出棧,即最終後綴表達式爲 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)- 事件過濾器