1. 程式人生 > >QT簡易計算器--表示式計算核心演算法(二)

QT簡易計算器--表示式計算核心演算法(二)

概述:上篇我主要介紹了用QT做計算器的整個流程,這次主要就是分析一下,計算器表示式計算的演算法部分。因為也找了很多別人寫的程式碼,但大多都是隻支援個位數的加減乘除,小數也不支援,所以就在原有框架上,修改,優化,讓其滿足我想要的功能。
1,表示式計算思路。
表示式資料操作符分割–>轉為逆波蘭表示式–>計算逆波蘭表示式值–>輸出結果。
2,表示式計算詳解。
(1)表示式資料操作符分割
首先我們得到了一個字串表示式,我們將資料和操作符分割。
如:表示式—————> “23+1.23-(2-5)*3”
分割後為“23” “+” “1.23” “- ”“( ”“2”“ -” “5” “)”“*” “3”,然後將其存入一個QString陣列,這樣資料,操作符分割就便於後面計算,辨別是資料還是操作符了。遍歷陣列,只要第一個字元不是0-9就是操作符了。
下面是這部分程式碼詳解:

/*將表示式的資料,操作符分割,依次存入mask_buffer陣列中*/
int Calculator::mask_data(QString expression, QString *mask_buffer)
{
    int i,k = 0,cnt = 0;
    QString::iterator p = expression.begin();//獲取表示式頭指標
    int length = expression.length();//獲取表示式資料長度
    /*for 迴圈遍歷整個表示式,進行資料,操作符分割*/
    for(i = 0 ; i < length ; i += cnt,k++)
    {
        cnt = 0
; if(*p >= '0' && *p <= '9') { /*當第一個字元為0-9時,肯定是數字*/ QString temp = *p; p ++; cnt ++; /*看數字後面是否還是數字或小數點,直到遇到下個操作符,這個資料才結束*/ while((*p >= '0' && *p <= '9') || *p == '.') { temp += *p
; p++; cnt ++; } mask_buffer[k] = temp; }else{ /*不是數字,就是操作符,直接寫入陣列即可*/ QString temp = *p; p++; cnt ++; mask_buffer[k] = temp; } } return k; }

(2)轉為逆波蘭表示式。
既然用到逆波蘭表示式,首先了解一下什麼是逆波蘭表示式。我們正常的表示式是中綴表示式,逆波蘭就是將中綴表示式轉化為字尾表示式。我們就簡單粗暴一些,例子。
1+2*3-(2+5)———>中綴表示式。
1,2,3,*,+,2,5,+,- ———>字尾表示式。
中綴到字尾如何轉化呢,下面就是詳細的規則了:
首先我們定義一個數組A,堆疊B。陣列A用於儲存最後的字尾表示式,堆疊B就是一個過渡器,幫助我們轉化,再定義操作符的優先順序,如+ - * / ( )。
從左到右遍歷整個表示式。
1,遇到數字加入陣列A。
2,遇到左括號直接入棧到B。
3,遇到右括號,堆疊B出棧,加入到陣列A中,直到遇到左括號,將左括號出棧但不加入陣列A中。
4,如果遇到的是運算子:
(1)當堆疊B為空,直接入棧到B。
(2)當堆疊不為空,當前運算子優先順序大於堆頂運算子優先順序,入棧到B。
(3)當堆疊不為空,當前運算子優先順序小於等於堆頂運算子優先順序,B出棧到A陣列中,直到堆頂運算子優先順序小於當前運算子優先順序,再將當前運算子入棧B。
5,當遍歷完整個表示式,堆疊B不為空,依次出棧加入到A陣列中。

下面詳細步驟轉化過程:

陣列A[100],堆疊B.
表示式:1+2*3-(2+5)
遍歷:
"1"   ------>        A{"1"}  B{空}
"+"   ------>        A{"1"}  B{"+"}
"2"   ------>        A{"1","2"}  B{"+"}
"*"   ------>        A{"1","2"}  B{"+","*"}         //"*"優先順序高於"+"
"3"   ------>        A{"1","2","3"}  B{"+","*"}
"-"   ------>        A{"1","2","3","*","+"}  B{"-"} //"-"優先順序不大於"*",不大於"+",*,+出棧到A
"("   ------>        A{"1","2","3","*","+"}  B{"-","("}   
"2"   ------>        A{"1","2","3","*","+","2"}  B{"-","("}
"+"   ------>        A{"1","2","3","*","+","2"}  B{"-","(","+"}
"5"   ------>        A{"1","2","3","*","+","2","5"}  B{"-","(","+"}
")"   ------>        A{"1","2","3","*","+","2","5","+"}  B{"-"}     //遇到右括號,+出棧到AA{"1","2","3","*","+","2","5","+""-"}  B{空}   //遍歷結束,B中不為空,"-"出棧到A

下面是這部分原始碼:

/*獲取操作符優先順序*/
int Calculator::Priority(QString data)
{
    int priority;
    if(data == "(")
        priority = 1;
    else if(data == "+" || data == "-")
        priority = 2;
    else if(data == "*" || data == "/")
        priority = 3;
    else if (data == ")")
        priority = 4;
    else
        priority = -1;
    return priority;
}

/*將獲取到的分割好的表示式陣列,轉化為逆波蘭表示式,存入陣列repolish中*/
int Calculator::re_polish(QString *mask_buffer,QString *repolish,int length)
{
    QStack<QString> st2;
    int i = 0;
    for(int j = 0 ; j < length ; j++)
    {
        if(mask_buffer[j] != "(" && mask_buffer[j] != ")" && mask_buffer[j] != "+" && mask_buffer[j] != "-" && mask_buffer[j] != "*" && mask_buffer[j] != "/" )
            repolish[i++] = mask_buffer[j];
        else if(mask_buffer[j] == "("){
            st2.push(mask_buffer[j]);
        }
        else if(mask_buffer[j] == ")"){
            while(st2.top() != "(")
            {
                repolish[i++] = st2.top();
                st2.pop();
            }
            if(st2.top() == "(")
                st2.pop();
        }
        else if(st2.empty() || Priority(mask_buffer[j]) > Priority(st2.top()))
            st2.push(mask_buffer[j]);
        else{
            while(Priority(mask_buffer[j]) <= Priority(st2.top()))
            {
                repolish[i++] = st2.top();
                st2.pop();
                if(st2.empty())
                    break;
            }
            st2.push(mask_buffer[j]);
        }
    }
    while(!st2.empty())
    {
        repolish[i++] = st2.top();
        st2.pop();
    }
    return i;
}

(3)計算逆波蘭表示式值。
計算就比較簡單了,我們只需要維護一個儲存資料的堆疊就行了,我們有一個逆波蘭表示式陣列,上面得到的,再新建一個堆疊st。遍歷整個逆波蘭表示式陣列,遇到數字直接壓棧到st中,遇到“+”,“-”,“*”,“/”,就取出st棧頂元素a,st.pop(),取下一個棧頂元素b,st.pop()。計算數值對應操作符值(如操作符為+,b+a),將計算的值入棧到st中。直到表示式遍歷完,堆疊裡只剩一個數據,就是最後的結果值,取出。
我們還是以 1,2,3,*,+,2,5,+,- 為例。

遍歷陣列A[] = {"1","2","3","*","+","2","5","+""-"}
 "1"  ------>    st{1}
 "2"  ------>    st{1,2}
 "3"  ------>    st{1,2,3}
 "*"  ------>    st{1,6} //2*3 = 6
 "+"  ------>    st{7}   //1+6 = 7
 "2"  ------>    st{7,2}
 "5"  ------>    st{7,2,5}
 "+"  ------>    st{7,7}  //2+5 = 7
 "-"  ------>    st{0}    //7-7 = 0   即最後結果為0

下面是這部分原始碼:

/*計算逆波蘭表示式值並顯示*/
double Calculator::repolish_calculat(QString *repolish,int length)
{
    QStack <double> st;
    for(int m = 0 ; m < length ; m ++)
    {
        if(repolish[m] != "+" && repolish[m] != "-" && repolish[m] != "*" && repolish[m] != "/" )
        {
            /*Qstring轉化為double資料存入堆疊*/
            st.push(repolish[m].toDouble());
        }
        else
        {
            if(repolish[m] == "+")
            {
                double a = st.top();
                st.pop();
                double b = st.top();
                st.pop();
                st.push(b + a);
            }
            else if(repolish[m] == "-")
            {
                double a = st.top();
                st.pop();
                double b = st.top();
                st.pop();
                st.push(b - a);
            }
            else if(repolish[m] == "*")
            {
                double a = st.top();
                st.pop();
                double b = st.top();
                st.pop();
                st.push(b * a);
            }
            else if(repolish[m] == "/")
            {
                double a = st.top();
                st.pop();
                double b = st.top();
                st.pop();
                if(a != 0)
                   st.push(b/a);
                else
                {
                    ui->display->clear();
                    ui->put_data->setText("0 不能做除數");
                    return -1;
                }
            }
        }
    }
    QString res = QString::number(st.top(),'g',10);
    ui->display->clear();
    ui->put_data->setText(res);
    return st.top();
}

3,專案完整原始碼在上篇中有連結,需要的話可直接下載。

相關推薦

QT簡易計算器--表示式計算核心演算法

概述:上篇我主要介紹了用QT做計算器的整個流程,這次主要就是分析一下,計算器表示式計算的演算法部分。因為也找了很多別人寫的程式碼,但大多都是隻支援個位數的加減乘除,小數也不支援,所以就在原有框架上,修改,優化,讓其滿足我想要的功能。 1,表示式計算思路。

linux核心之排程演算法

  上層排程,linux排程的核心函式為schedule,schedule函式封裝了核心排程的框架。細節實現上呼叫具體的排程類中的函式實現。schedule函式主要流程為: 1,將當前程序從相應的執行佇列中刪除; 2,計算和更新排程實體和程序的相關排程資訊; 3,將當前進重

計算幾何與圖形學有關的幾種常用演算法

3.6 用向量的叉積判斷直線段是否有交         向量叉積計算的另一個常用用途是直線段求交。求交演算法是計算機圖形學的核心演算法,也是體現速度和穩定性的重要標誌,高效並且穩定的求交演算法是任何一個CAD軟體都必需要重點關注的。求交包含兩層概念,一個是判斷是否相

算法系列之九:計算幾何與圖形學有關的幾種常用演算法

3.6 用向量的叉積判斷直線段是否有交        向量叉積計算的另一個常用用途是直線段求交。求交演算法是計算機圖形學的核心演算法,也是體現速度和穩定性的重要標誌,高效並且穩定的求交演算法是任何一個CAD軟體都必需要重點關注的。求交包含兩層概念,一個是判斷是否相交,另一個是

資料結構與演算法--遞迴

遞迴條件: 1.遞迴條件:每次調自己,然後記錄當時的狀態 2.基準條件:執行到什麼時候結束遞迴,不然遞迴就會無休止的呼叫自己, 遞迴的資料結構:棧(先進先出)和彈夾原理一樣,每一次呼叫自己都記錄了當時的一種狀態,然後把這種狀態的結果返回。 棧相對應的資料結構:佇列(先進後出

演算法之排序

排序演算法很多,常用的排序演算法有:氣泡排序、插入排序、選擇排序、歸併排序、快速排序、計數排序、基數排序、桶排序。 接下來一一介紹幾種排序的時間複雜度及優缺點。 插入排序與氣泡排序的時間複雜度相同O(n^2),開發中我們更傾向插入排序,而不是氣泡排序 排序演算法執行效率: 1.最好、最壞、平均情況時間

圖——基本的圖演算法圖的遍歷

圖——基本的圖演算法(二)圖的遍歷 1. 基本概念 圖的遍歷指的是從圖中的某個頂點出發訪問圖中其餘的頂點,且每個頂點只被訪問一次的這個過程。通常來說,圖的遍歷次序有兩種:深度優先遍歷(Depth first Search, DFS)和廣度優先遍歷(Breadth First Se

吳恩達老師機器學習筆記K-means聚類演算法

運用K-means聚類演算法進行影象壓縮 趁熱打鐵,修改之前的演算法來做第二個練習—影象壓縮 原始圖片如下: 程式碼如下: X =imread('bird.png'); % 讀取圖片 X =im2double(X); % unit8轉成double型別 [m,n,z]=size

深入理解線性迴歸演算法:正則項的詳細分析

前言 當模型的複雜度達到一定程度時,則模型處於過擬合狀態,類似這種意思相信大家看到個很多次了,本文首先討論了怎麼去理解複雜度這一概念,然後回顧貝葉斯思想(原諒我有點囉嗦),並從貝葉斯的角度去理解正則項的含義以及正則項降低模型複雜度的方法,最後總結全文。     &nb

Logistic迴歸之梯度上升優化演算法

Logistic迴歸之梯度上升優化演算法(二) 有了上一篇的知識儲備,這一篇部落格我們就開始Python3實戰 1、資料準備 資料集:資料集下載 資料集內容比較簡單,我們可以簡單理解為第一列X,第二列Y,第三列是分類標籤。根據標籤的不同,對這些資料點進行分類。  

「日常訓練&知識學習」莫隊演算法:樹上莫隊Count on a tree II,SPOJ COT2

題意與分析 題意是這樣的,給定一顆節點有權值的樹,然後給若干個詢問,每次詢問讓你找出一條鏈上有多少個不同權值。 寫這題之前要參看我的三個blog:CFR326D2E、CFR340D2E和HYSBZ-1086,然後再看這幾個Blog—— 參考A:https://blog.sengxian.com/algori

從零開始學演算法選擇排序

從零開始學演算法(二)選擇排序 選擇排序 演算法介紹 演算法原理 演算法簡單記憶說明 演算法複雜度和穩定性 程式碼實現 選擇排序 程式碼是Javascript語言寫的(幾乎是虛擬碼) 演算

決策樹-剪枝演算法

ID3演算法的的原理,它是以資訊熵為度量,用於決策樹節點的屬性選擇,每次優選資訊量最多 的屬性,以構造一顆熵值下降最快的決策樹,到葉子節點處的熵值為0,此時每個葉子節點對應的例項集中的例項屬於同一類。 理想的決策樹有三種: 1.葉子節點數最少 2.葉子加點深度最小 3.葉子節點數最少且葉子

Docker核心技術

Docker核心技術 Docker的常用命令 幫助命令 映象命令 docker images docker search docker pull 容器命令 1.拉取映象

程式設計與演算法第八週測驗 3:棋盤問題

3:棋盤問題 檢視 提交 統計 提問 總時間限制:  1000ms   記憶體限制:  65536kB 描述 在一個給定形狀的棋盤(形狀可能是不規則的)上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放

程式設計與演算法第八週測驗 2:A Knight's Journey

2:A Knight's Journey 檢視 提交 統計 提問 總時間限制:  1000ms   記憶體限制:  65536kB 描述 Background The knight is getting bor

程式設計與演算法第八週測驗 1:紅與黑

1:紅與黑 檢視 提交 統計 提問 總時間限制:  1000ms   記憶體限制:  65536kB 描述 有一間長方形的房子,地上鋪了紅色、黑色兩種顏色的正方形瓷磚。你站在其中一塊黑色的瓷磚上,只能向相鄰的黑

共識演算法—— DPoS股份授權證明、PBFT實用拜占庭容錯

DPoS簡介 DPoS(Delegated-Proof-of-Stake)即股份授權證明,目的是解決PoS和PoW的不足,DPoS是由被社群選取的可信賬戶(受託人,得票數為所有委託人得前101位)來建立區塊,為了成為正式委託人,使用者要去社群拉票,獲得足夠多的使用者信任,使用者根據自己持有的

Windows核心基礎:虛擬記憶體空間佈局

32位Windows作業系統支援32位定址,因此2的32次方就等於4GB,每個程式在執行時都會被對映進4GB空間的記憶體空間,這4GB空間不全是使用者可以使用的,其中0x7fffffff-0xffffffff是2GB的核心空間,這部分用來儲存核心的資料,使用者程式是無法直接訪問的。

聚類演算法

密度聚類 密度聚類假設聚類結構能通過樣本分佈的緊密程度確定,通常情況下密度聚類演算法從樣本密度的角度來考察樣本之間的可連線性,並基於可連線樣本不斷擴充套件聚類 簇以獲得最終的聚類結果 DBSCAN 基於一組鄰域引數來刻畫樣本分佈的緊密程度。 事先不用預設聚類簇數