連結串列與棧的典型應用——簡單計算機的實現

發表於2016-11-05

先來看一個粗糙的簡單計算器的實現。他只支援加減乘除,並且一次只能對兩個小於10的正整數做一次運算

這是一個粗糙的實現,但是但是仍舊有一些要點體現在上面,比如對 表示式的分析中使用了while(str[i] && str[i]!=’\n’)我們首先要判斷是不是到表示式字串 的結尾了,如果不是還要判斷是不是’\n’字元。因為呼叫fgets輸入表示式時Enter鍵也被儲存了。

上面的程式只能進行一次 兩個小於十的正整數 的加減乘除運算。如下

連結串列與棧的典型應用——簡單計算機的實現

當然,這連個簡單的計算器都算不上。
1: 如果我要計算 2+3+4怎麼辦。也就是說上面的不支援大於兩個運算元的運算
2 :如果我要 計算2的立方怎麼辦 。也就是說上面支援的運算太少了,這個其實問題不大。因為我們通過加單的新增就可以了。所以後面的正真實現中我們還是隻實現了加減乘除四個典型運算以及帶括號的運算
3 :如果我要計算 2+3*4怎麼辦?有的人可能會問,這有什麼怎麼辦了。計算就好了。但是我們知道應該先算 3*4 然後再 +2

但是計算器不知道啊。也就是說上面的並未支援優先順序。這在windows自帶的計算機上有體現在檢視裡面將計算器切換到標準型

連結串列與棧的典型應用——簡單計算機的實現

再切換到科學型。

連結串列與棧的典型應用——簡單計算機的實現

很顯然,標準型並未支援優先順序運算,他只是簡單的從左往右運算

4 :如果我要計算 20+10怎麼辦,也就是說運算數可以是任意的並不固定為小於十的正整數,因為我們輸入的一個連續的 表示式字串。
那麼就需要更復雜一點的語句分析才能提取出正真的數字。當然後面的正真實現中,也只是實現了 整形所能表示的正整數的相加,並未實現小數,負數之類。當然他們原理都是相同的,只是在表示式的分析上覆雜一些而已。

所以,即使是一個簡單的計算器,也應該上面的四個要求。
1,2, 4,的要求主要在表示式字串的分析上,也就是從裡面分析出 字元 和數字。無非是語法分析變複雜一點。
問題核心是在 如何運算子的優先順序上。

這就需要了解 字尾(逆波蘭)表示式。
定義:不包含括號,運算子放在兩個運算物件的後面,所有的計算按運算子出現的順序,
嚴格從左向右進行(不再考慮運算子的優先規則,如:(2 + 1) * 3 , 即2 1 + 3 *

我們知道計算機並不知道什麼優先順序。最簡單的它只會從左到有讀取然後運算。
那麼根據上面 字尾表示式的定義。如果能將表達是轉換成字尾表示式,那麼運算就簡單了。可以使用一個棧來實現。
比如:3+12*2-(6+1)*2;
轉換成字尾表示式 3 12 2 * + 6 1 + 2 * –
使用一個棧,然後從左到右遍歷,遇到數字我們就其入棧,遇到運算子,就出棧兩個數字然後做運算,並將運算結構入棧。這樣一直下去到最後。棧中存放的就為最終的結果

連結串列與棧的典型應用——簡單計算機的實現

也就是說如果得到了字尾表示式那麼就可以用上面的方法來從左到右計算了。而不存在優先順序的問題了
那麼,現在問題就變成了怎麼將表示式變成計算機易於計算的字尾表示式,這裡同樣是用到棧來實現的。
下面我們通過對 + *以及( )這幾個典型的運算子的處理 來闡述轉換成字尾表示式的一般原理。

我們假設表示式是合法的。當讀到一個運算元的時候,立即把他放到輸出中。
遇到其他運算子時”+” ,”*” , “(” 那麼久從棧中彈出元素直到當前棧頂元素的優先順序比當前遇到的運算子低,然後再將該運算子入棧需要注意
的一點是 除非是在處理一個右括號’)’否則絕不從棧中移走左括號'(‘。
如果遇到右括號’)’,那麼就將棧元素彈出並寫到輸出,直到遇到一個對應的左括號。注意:這時候右括號不入棧,彈出的左括號也不輸出。而是僅僅丟棄他們
最後當我們讀到表示式的結尾時,再將棧中的元素依次彈出寫到輸出中直到棧空變得到了字尾表示式

比如 a+b*c+(d*e+f)*g
字尾表示式為: a b c * + d e * f + g * +

轉換過程如下

連結串列與棧的典型應用——簡單計算機的實現

連結串列與棧的典型應用——簡單計算機的實現

連結串列與棧的典型應用——簡單計算機的實現

得到字尾表示式後一切就簡單了,計算機只需想上面說的那樣從左到右簡單計算就行了。

下面我們對 計算器編寫過程中的幾個核心部分的程式碼詳細說明
整體思路是 從鍵盤得到一個字串形式的表示式,然後從左到右對其進行分析,依次分離出數字和運算子然並建立對應節點,
然後像上面介紹的放法一樣,如果是數字就 插入到一個連結串列中(這裡並不輸出而是放到連結串列中供後續使用),如果是運算子,就放入棧中。
最終當分析表示式結尾時,彈出棧中所有元素並依此插入連結串列中,最終連結串列就是我們需要的字尾表示式。

然後對連結串列從左到右遍歷並使用之前所說的對字尾表示式的計算方法。計算出最終結果
需要注意的是,我們沒有做錯誤檢查,所以輸入的表示式必須是合法的

求字尾表示式的程式碼註釋

根據字尾表達是求運算結果的註釋:

測試結果如下
第二行給出了字尾表示式

連結串列與棧的典型應用——簡單計算機的實現

下面是所有程式碼實現

head.h

head.c

測試程式碼

相關文章