前中後綴表達式的轉換和運算

0.知識點

1.scanf 和 sscanf 的區別 及 sscanf 的應用,注意*第二參數不能加.表示精度——2.1.1.3
2.判斷字符是否是數字——2.1.1.2
3.轉換

  • 字符型 轉爲 整型(ascii to int):atoi
  • 字符型 轉爲 長整型(ascii to int64):_atoi64
  • 字符型 轉爲 任意:sscanf——2.1.1.3;2.1.2
  • 長整型 轉爲 字符型:_i64toa
  • 整型 轉爲 字符型:itoa
    4.字符串清零,用fill,用memset好像不行:fill(polish, polish + strlen(polish), 0);——1.1.1.2;1.2.1.2

在這裏插入圖片描述

1.波蘭表達式和逆波蘭表達式

常見的算術表達式,稱爲中綴表達式,例如:

5 + ( 6 - 4 / 2 ) * 3

1.1波蘭表達式

波蘭表達式也稱爲前綴表達式,以上面的例子爲例,其波蘭表達式爲:

+ 5 * - 6 / 4 2 3

1.1.0 手寫的轉換和計算的技巧

  1. (手寫)中綴轉化爲前綴就是從左往右掃描,先存入第一個表達式的操作符,然後存入操作符左邊的表達式(表達式中的存取順序遞歸),再存入右邊的表達式
  2. (手寫運算):先逆轉,從左往右掃描,遇到操作符,取前兩個操作數運算(前數y後數x操作符/:yx/即爲x/y

1.1.1中綴表達式轉換前綴表達式的操作過程

(1)首先設定一個操作符棧,從右到左順序掃描整個中綴表達式:

1.1.1.0結構體

char strop[300], str[1000],polish[1000];//操作符棧,輸入的字符串,波蘭式棧
int topop,toppol;//操作符,波蘭式棧的棧頂指針(指向棧頂元素,-1表示棧空)

1.1.1.1運算

  • 如果是操作數,則直接歸入前綴表達式;
  • 如果是括號
    Ⅰ如果是右括號,則直接將其入棧;
    Ⅱ如果是左括號,則將棧中的操作符依次彈棧,歸入前綴表達式,直至遇到右括號,將右括號彈棧,處理結束;
  • 如果是其他操作符,則檢測棧頂操作符的優先級與當前操作符的優先級關係
    Ⅰ如果棧頂操作符不是操作符,那麼直接壓棧。例如棧頂是左括號 ),或者棧爲空。
    Ⅱ如果棧頂操作符優先級小於等於當前操作符優先級,這時將當前操作符壓棧(同Ⅰ,所以可以寫優先級函數,大於爲true,否則爲false)。
    Ⅲ如果棧頂操作符優先級大於當前操作符的優先級,則彈棧,並歸入前綴表達式,重複掃描這個字符

(2)當掃描完畢整個中綴表達式後,檢測操作符棧是否爲空,如果不爲空,則依次將棧中操作符彈棧,歸入前綴表達式。

(3)最後,將前綴表達式翻轉,得到中綴表達式對應的前綴表達式。
在這裏插入圖片描述

void POLISH(){
	int n = strlen(str);//總長
	reverse(str, str+n);//反轉。爲了從右往左掃描

	char t,tmp;//暫存的當前字符,彈棧時的暫用字符
	topop = -1; toppol = -1;//設棧空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作數
			polish[++toppol] = t;
		}
		else if (t == ')'){//右括號
			strop[++topop] = t;
		}
		else if (t == '('){//左括號
			while (1){
				tmp = strop[topop--];//彈棧
				if (tmp == ')'){
					break;
				}
				else if (topop != -1){//未彈完
					polish[++toppol] = tmp;
				}
				else
					return;
			}
		}
		else if(t=='*'||t=='/'||t=='+'||t=='-'){//操作符
			if (!priority(strop[topop], t)||topop==-1)
				strop[++topop] = t;
			else{
				tmp = strop[topop--];//彈棧
				polish[++toppol] = tmp;
				i--;
			}
		}
		else
			continue;
	}
	while (topop != -1){
		tmp = strop[topop--];//彈棧
		polish[++toppol] = tmp;
	}
	reverse(polish, polish + strlen(polish));
	return;
}

1.1.1.2AC代碼

#include <stdio.h>
#include<iostream>
#include <string>
#include<stdlib.h>
#include <ctype.h>
#include<algorithm>
using namespace std;

char strop[300], str[1000],polish[1000];//操作符棧,輸入的字符串,波蘭式棧
int topop,toppol;//操作符,波蘭式棧的棧頂指針(指向棧頂元素,-1表示棧空)

bool priority(char a, char b){//前後(a,b)優先級大小比較(大於true,否則false)
	if (a == '*' || a == '/')
		return true;
	else if (a == '+' || a == '-'){
		if (b == '+' || b == '-')
			return true;
		return false;
	}
	else//a爲定界符
		return false;
}
void POLISH(){
	int n = strlen(str);//總長
	reverse(str, str+n);//反轉。爲了從右往左掃描

	char t,tmp;//暫存的當前字符,彈棧時的暫用字符
	topop = -1; toppol = -1;//設棧空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作數
			polish[++toppol] = t;
		}
		else if (t == ')'){//右括號
			strop[++topop] = t;
		}
		else if (t == '('){//左括號
			while (1){
				tmp = strop[topop--];//彈棧
				if (tmp == ')'){
					break;
				}
				else if (topop != -1){//未彈完
					polish[++toppol] = tmp;
				}
				else
					return;
			}
		}
		else if(t=='*'||t=='/'||t=='+'||t=='-'){//操作符
			if (!priority(strop[topop], t)||topop==-1)
				strop[++topop] = t;
			else{
				tmp = strop[topop--];//彈棧
				polish[++toppol] = tmp;
				i--;
			}
		}
		else
			continue;
	}
	while (topop != -1){
		tmp = strop[topop--];//彈棧
		polish[++toppol] = tmp;
	}
	reverse(polish, polish + strlen(polish));
	return;
}
int main()
{
	cin.getline(str, sizeof(str));
	while (str != NULL && strcmp(str, "0") != 0){//輸入,非空且非0(輸入了0不必運算)
		POLISH();
		printf("%s\n", polish);
		//memset(polish, 0, sizeof(char));
		fill(polish, polish + strlen(polish), 0);
		cin.getline(str, sizeof(str));
	}

	system("pause");
	return 0;
}

1.2逆波蘭表達式

逆波蘭表達式也稱爲後綴表達式,以上面的例子爲例,其逆波蘭表達式爲:

5 6 4 2 / - 3 * +

1.2.0 手寫的轉換和計算的技巧

  1. (手寫)中綴轉化爲前綴就是從左往右掃描,遇數就存直到遇見第一個操作符,先取其右表達式(右表達式的取法遞歸)再取該操作符
  2. (手寫運算):從左往右掃描,遇到操作符,取前兩個操作數運算(前數x後數y操作符/:xy/即爲x/y

1.2.1中綴表達式轉換後綴表達式的操作過程

1.2.1.0結構體

你需要設定一個棧strop,和一個線性表 repolish 。
strop用於臨時存儲運算符和左括號分界符( ,repolish用於存儲後綴表達式。

char strop[300], str[1000], repolish[1000];//操作符棧,輸入的字符串,逆波蘭式棧
int topop, toprepol;//操作符,逆波蘭式棧的棧頂指針(指向棧頂元素,-1表示棧空)

1.2.1.1運算

遍歷原始表達式中的每一個表達式元素
(1)如果是操作數,則直接追加到 repolish中。只有 運算符 或者 分界符( 纔可以存放到 棧strop中
(2)如果是分界符

  • 如果不是運算符,則二者沒有可比性,則直接將此運算符op1壓棧。 例如棧頂是左括號 ( ,或者棧爲空。
  • 如果是左括號 ( , 則 直接壓入strop,等待下一個最近的 右括號 與之配對。
  • 如果是右括號),則說明有一對括號已經配對(在表達式輸入無誤的情況下)。不將它壓棧,丟棄它,然後從strop中出棧,得到元素tmp,將tmp依次追加到L裏。一直循環,直到出棧元素tmp是 左括號 ( ,同樣丟棄他。

(3)如果是運算符(用op2表示)

  • 如果strop棧頂元素(用op1表示) 不是運算符,則二者沒有可比性,則直接將此運算符op2壓棧。 例如棧頂是左括號 ( ,或者棧爲空。
  • 如果strop棧頂元素(用op1表示) 是運算符 ,則比較op1和 op2的優先級。如果op1(前) < op2(後) ,則直接將此運算符op2壓棧。
  • 如果不滿足op1(前) < op2(後),則將op1出棧,並追加到repolish,重複步驟3。
  • 也就是說,如果在strop棧中,有2個相鄰的元素都是運算符,則他們必須滿足:下層運算符的優先級一定小於上層元素的優先級,才能相鄰。

(4)最後,如果strop中還有元素,則依次彈出追加到repolish後,就得到了後綴表達式。

void RePolish(){
	int n = strlen(str);//總長

	char t, tmp;//暫存的當前字符,彈棧時的暫用字符
	topop = -1; toprepol = -1;//設棧空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作數
			repolish[++toprepol] = t;
		}
		else if (t == '('){//左括號
			strop[++topop] = t;
		}
		else if (t == ')'){//右括號
			while (1){
				tmp = strop[topop--];//彈棧
				if (tmp == '(')
					break;
				else if (topop != -1){//未彈完
					repolish[++toprepol] = tmp;
				}
				else{//全彈完了還沒遇到(
					return;
				}
			}
		}
		else if (t == '*' || t == '/' || t == '+' || t == '-'){//操作符
			if (!(strop[topop] == '*' || strop[topop] == '/' || strop[topop] == '+' || strop[topop] == '-')||topop==-1){
				strop[++topop] = t;
			}
			else if (!priority(strop[topop], t)){
				strop[++topop] = t;
			}
			else{
				tmp = strop[topop--];//彈棧
				repolish[++toprepol] = tmp;
				i--;//下一循環仍然判斷此數
			}
		}
		else
			continue;
	}
	while (topop>-1){
		tmp = strop[topop--];//彈棧
		repolish[++toprepol] = tmp;
	}
	return;
}

1.2.1.2AC代碼

#include <stdio.h>
#include<iostream>
#include <string>
#include<stdlib.h>
#include <ctype.h>
#include<algorithm>
using namespace std;

char strop[300], str[1000], repolish[1000];//操作符棧,輸入的字符串,逆波蘭式棧
int topop, toprepol;//操作符,逆波蘭式棧的棧頂指針(指向棧頂元素,-1表示棧空)

bool priority(char a, char b){//前後(a,b)優先級大小比較(大於true,小於等於false)
	if (a == '*' || a == '/')
		return true;
	else{
		if (b == '+' || b == '-')
			return true;
		else
		return false;
	}
}
void RePolish(){
	int n = strlen(str);//總長

	char t, tmp;//暫存的當前字符,彈棧時的暫用字符
	topop = -1; toprepol = -1;//設棧空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作數
			repolish[++toprepol] = t;
		}
		else if (t == '('){//左括號
			strop[++topop] = t;
		}
		else if (t == ')'){//右括號
			while (1){
				tmp = strop[topop--];//彈棧
				if (tmp == '(')
					break;
				else if (topop != -1){//未彈完
					repolish[++toprepol] = tmp;
				}
				else{//全彈完了還沒遇到(
					return;
				}
			}
		}
		else if (t == '*' || t == '/' || t == '+' || t == '-'){//操作符
			if (!(strop[topop] == '*' || strop[topop] == '/' || strop[topop] == '+' || strop[topop] == '-')||topop==-1){
				strop[++topop] = t;
			}
			else if (!priority(strop[topop], t)){
				strop[++topop] = t;
			}
			else{
				tmp = strop[topop--];//彈棧
				repolish[++toprepol] = tmp;
				i--;//下一循環仍然判斷此數
			}
		}
		else
			continue;
	}
	while (topop>-1){
		tmp = strop[topop--];//彈棧
		repolish[++toprepol] = tmp;
	}
	return;
}
int main()
{
	cin.getline(str, sizeof(str));
	while (str != NULL && strcmp(str, "0") != 0){//輸入,非空且非0(輸入了0不必運算)
		RePolish();
		printf("%s\n", repolish);

		fill(repolish, repolish + strlen(repolish), 0);
		cin.getline(str, sizeof(str));
	}

	system("pause");
	return 0;
}

1.2.2優勢

它的優勢在於只用兩種簡單操作,入棧和出棧就可以搞定任何普通表達式的運算。其運算方式如下:
如果當前字符爲變量或者爲數字,則壓棧,如果是運算符,則將棧頂兩個元素彈出作相應運算,結果再入棧,最後當表達式掃描完後,棧裏的就是結果。

2.運算

2.1中綴表達式的運算

寫了兩個數組stDit(棧頂指針即下標top1),stOp(棧頂指針即下標top2)作爲操作數,操作符的順序棧

2.1.0結構體(順序棧)


double stDit[300];//操作數棧(順序結構)
char stOp[300], str[300];//操作符棧,輸入的字符串
int top1, top2;//操作數棧的棧頂指針,操作符棧的棧頂指針(就是數組(棧)的下標)

2.1.1運算

double Calc()
{
	double x, y, tmp;
	char op;
	int i, n = strlen(str);

	top1 = top2 = -1;//棧頂爲-1表示棧空(下標爲0就是有一個棧內元素了)
	stOp[++top2] = '#';//操作符棧壓入一個#界定符
	str[n++] = '#';//輸入的字符串最後插上一個界定符(這樣讀取到最後和一開始壓入的界定符就匹配成一對了)

	for (i = 0; i < n; ++i)
	{
		if (str[i] == ' ')
			continue;
		if (isdigit(str[i]))				//是數字
		{
			sscanf(str + i, "%lf", &tmp);//從子字符串中str+i開始讀取一個爲%lf型的值  ==(只要符合  能轉化爲%lf型  的條件就自動往下一個地址找直到不符合條件)== 賦給tmp
			stDit[++top1] = tmp;			//stack push
			//跳過後頭的數字直到非數字
			while (isdigit(str[i + 1]))		//+1很重要
				++i;
		}
		else								//是操作符
		{
			while (Priority(stOp[top2]) >= Priority(str[i]))//如果操作棧頂的操作符優先級高,則作+-*/運算(直到新操作符優先級高)
			{
				if (str[i] == '#' && stOp[top2] == '#')//掃描完畢
					return stDit[top1];
				y = stDit[top1--];//取棧頂操作數(x是前一個數,y在x後頭)
				x = stDit[top1--];//取再前一個的操作數
				op = stOp[top2--];//取棧頂操作符
				stDit[++top1] = Operate(x, y, op);//計算結果入棧
			}
			stOp[++top2] = str[i];			//如果新操作符優先級高,str[i]進棧
		}
	}
	return stDit[top1];
}

設變量
bool pipei 是 是否是合法的表達式的 標識符
double x,y 是 做基本運算時出棧的兩個暫存的數
char op 是 做基本運算時出棧的暫存的字符
double tmp 是 判斷子字符串爲數字後,取出轉而放入的暫存的數
int i, n = strlen(str) 是 遍歷字符串,當前字符是第i個,總長是n個

top1 = top2 = -1;//棧頂爲-1表示棧空(下標爲0就是有一個棧內元素了)
	stOp[++top2] = '#';//操作符棧壓入一個#界定符
	str[n++] = '#';//輸入的字符串最後插上一個界定符(這樣讀取到最後和一開始壓入的界定符就匹配成一對了)

遍歷字符串

  1. 如果是空格
    跳過

  2. 如果是數字
    ——2.1從子字符串中str+i開始讀取一個爲%lf型的值 (只要符合 能轉化爲%lf型 的條件就自動往下一個地址找直到不符合條件) 賦給tmp
    sscanf(str + i, “%lf”, &tmp);
    ——2.2操作數棧stDit棧頂指針先+1,再讓tmp入棧
    ——2.3跳過後頭的數字直到非數字(連續的數字都在sscanf函數中把值賦給tmp併入棧,已經使用過了)

  3. 如果不是數字,則爲操作符
    ——3.1判斷優先級
    ——3.2若小於,則操作數入棧
    ——3.3若大於,取出操作數棧頂的兩個操作數放入x,y(x是前一個數,y在x後頭)和操作符棧頂的一個操作符;運算並讓計算結果入棧;i–使得下一次循環仍要比較當前字符(當前字符還未用)
    ——3.4若等於,如果操作符棧頂元素爲’(‘且當前字符爲’)’,則讓‘(’出棧;如果操作符棧頂元素爲’#‘且當前字符爲’#’,則運算結束,返回操作數棧頂元素(當前應只有一個元素了,即最終結果);否則異常標識符賦false並返回

  4. 否則異常標識符賦false並返回

如果遍歷完了還未返回同樣異常,異常標識符賦false並返回

2.1.1.2判斷數字:isdigit

if (isdigit(str[i]))

頭文件#include <ctype.h>
如果是數字,返回true
否則,返回false

2.1.1.3從字符串中讀取:sscanf的運用

sscanf和scanf的區別:
scanf從鍵盤(stdin)的緩存區中讀取,賦值到一個或多個數據類型中
sscanf從字符串中讀取,賦值到一個或多個數據類型中

  1. 函數語法編輯
    int sscanf( const char *buffer, const char *format, [ argument ] … );
    其中
    buffer 字符串(也可以是子字符串)
    format 賦值到一個或多個數據類型中 “····”
    argument 可選自變量(一個或多個)注意是地址,若有變量名一定要加&取地址符

  2. 第二個參數可以是一個或多個 {%[*] [width] [{h | I | I64 | L}]type | ’ ’ | ‘\t’ |
    ‘\n’ | 非%符號}
    注:
    (1) * 亦可用於格式中, (即 %d 和 %s) 加了星號 () 表示跳過此數據不讀入. (也就是不把此數據讀入參數中)
    (2){a|b|c}表示a,b,c中選一,[d],表示可以有d也可以沒有d。
    (3)width表示讀取寬度。
    (4){h | l | I64 | L}:參數的size,通常h表示單字節size,I表示2字節 size,L表示4字節size(double例外),l64表示8字節size。
    (5)type :這就很多了,就是%s,%d之類。
    (6)特別的:%
    [width] [{h | l | I64 | L}]type 表示滿足該條件的被過濾掉,不會向目標參數中寫入值
    失敗返回0 ,否則返回格式化的參數個數
    (7)如果讀取的字符串,不是以空格來分隔的話,就可以使用%[]。
    (8)注意這裏的第二個參數不能加.*表示精確度,否則該函數無用

  3. 返回值
    函數將返回成功賦值的字段個數;返回值不包括已讀取但未賦值的字段個數。 返回值爲 0 表示沒有將任何字段賦值。 如果在第一次讀取之前到達字符串結尾,則返回EOF。
    如果buffer或format是NULL調用指針,無效參數處理程序,如中所述參數驗證。 如果允許繼續執行,則這些函數返回 -1 並將errno設置爲EINVAL。
    成功則返回參數數目,失敗則返回-1,錯誤原因存於errno中。
    經多次測試[來源請求],在linux系統中成功返回的是成功轉換的值的個數

例如:
sscanf(“1 2 3”,"%d %d %d",buf1, buf2, buf3); 成功調用返回值爲3,即buf1,buf2,buf3均成功轉換。
sscanf(“1 2”,"%d %d %d",buf1, buf2, buf3); 成功調用返回值爲2,即只有buf1,buf2成功轉換。
(注意:此處buf均爲地址)

  1. 使用實例
char buf[512];

(1)一般用法

sscanf("123456 ", "%s", buf);
printf("%s\n", buf);
結果爲:123456

(2)取指定長度的字符串。如在下例中,取最大長度爲4字節的字符串。

sscanf("123456 ", "%4s", buf);
printf("%s\n", buf);
結果爲:1234

(3)取到指定字符爲止的字符串。如在下例中,取遇到空格爲止字符串。

sscanf("123456 abcdedf", "%[^ ]", buf);
printf("%s\n", buf);
結果爲:123456

(4)取僅包含指定字符集的字符串。如在下例中,取僅包含1到9和小寫字母的字符串。

sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf);
printf("%s\n", buf);
結果爲:123456abcdedf

(5) 取到指定字符集爲止的字符串。如在下例中,取遇到大寫字母爲止的字符串。

sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf);
printf("%s\n", buf);
結果爲:123456abcdedf

(6)給定一個字符串iios/12DDWDFF@122,獲取 / 和 @ 之間的字符串,先將 "iios/"過濾掉,再將非’@'的一串內容送到buf中

sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf);
printf("%s\n", buf);
結果爲:12DDWDFF

(7)給定一個字符串"hello, world",僅保留"world"。(注意:“,”之後有一空格)

sscanf("hello, world", "%*s%s", buf);
printf("%s\n", buf);
結果爲:world  

P.S. %*s表示第一個匹配到的%s被過濾掉,即hello被過濾了,
如果沒有空格則結果爲NULL。

2.1.1.4判斷優先級

返回值:-1小於;0等於;1大於;2匹配錯誤
在這裏插入圖片描述
在這裏插入圖片描述
可參照: https://blog.csdn.net/weixin_44266050/article/details/102751739

int Priority(char s1,char s2)//返回操作符的優先級大小
//-1小於;0等於;1大於;2匹配錯誤
{
	if (s1 == '+' || s1 == '-'){
		if (s2 == '+' || s2 == '-' || s2 == ')' || s2 == '#')
			return 1;
		else
			return -1;
	}
	else if (s1 == '*' || s1 == '/'){
		if (s2 == '(')
			return -1;
		else
			return 1;
	}
	else if (s1 == '('){
		if (s2 == '+' || s2 == '-' || s2 == '*' || s2 == '/' || s2 == '(')
			return -1;
		else if (s2 == ')')
			return 0;
		else
			return 2;
	}
	else if (s1 == ')'){
		if (s2 == '(')
			return 2;
		else
			return 1;
	}
	else if (s1 == '#'){
		if (s2 == '#')
			return 0;
		else if (s2 == ')')
			return 2;
		else
			return -1;
	}
}

2.1.1.5基本運算

double Operate(double x, double y, char op)
{
	if (op == '+') return x + y;
	if (op == '-') return x - y;
	if (op == '*') return x*y;
	if (op == '/') return x / y;
	else return -1;
}

2.1.2AC代碼

#include <iostream>
#include <cstdio>
#include <string.h>
#include <ctype.h>
using namespace std;

double stDit[300];//操作數棧(順序結構)
char stOp[300], str[300];//操作符棧,輸入的字符串
int top1, top2;//操作數棧的棧頂指針,操作符棧的棧頂指針(就是數組(棧)的下標)

int Priority(char s1,char s2)//返回操作符的優先級大小
//-1小於;0等於;1大於;2匹配錯誤
{
	if (s1 == '+' || s1 == '-'){
		if (s2 == '+' || s2 == '-' || s2 == ')' || s2 == '#')
			return 1;
		else
			return -1;
	}
	else if (s1 == '*' || s1 == '/'){
		if (s2 == '(')
			return -1;
		else
			return 1;
	}
	else if (s1 == '('){
		if (s2 == '+' || s2 == '-' || s2 == '*' || s2 == '/' || s2 == '(')
			return -1;
		else if (s2 == ')')
			return 0;
		else
			return 2;
	}
	else if (s1 == ')'){
		if (s2 == '(')
			return 2;
		else
			return 1;
	}
	else if (s1 == '#'){
		if (s2 == '#')
			return 0;
		else if (s2 == ')')
			return 2;
		else
			return -1;
	}
}
double Operate(double x, double y, char op)
{
	if (op == '+') return x + y;
	if (op == '-') return x - y;
	if (op == '*') return x*y;
	if (op == '/') return x / y;
	else return -1;
}

double Calc(bool &pipei)
{
	pipei = true;
	double x, y, tmp;
	char op;
	int i, n = strlen(str);

	top1 = top2 = -1;//棧頂爲-1表示棧空(下標爲0就是有一個棧內元素了)
	stOp[++top2] = '#';//操作符棧壓入一個#界定符
	str[n++] = '#';//輸入的字符串最後插上一個界定符(這樣讀取到最後和一開始壓入的界定符就匹配成一對了)

	for (i = 0; i < n; ++i)
	{
		if (str[i] == ' ')
			continue;
		if (isdigit(str[i]))				//是數字
		{
			sscanf(str + i, "%lf", &tmp);//從子字符串中str+i開始讀取一個爲%lf型的值  ==(只要符合  能轉化爲%lf型  的條件就自動往下一個地址找直到不符合條件)== 賦給tmp
			stDit[++top1] = tmp;			//stack push
			//跳過後頭的數字直到非數字
			while (isdigit(str[i + 1]))		//+1很重要
				++i;
		}
		else								//是操作符
		{
			int pri = Priority(stOp[top2],str[i]);
			if (pri == -1){
				stOp[++top2] = str[i];
			}
			if (pri == 1){
				y = stDit[top1--];//取棧頂操作數(x是前一個數,y在x後頭)
				x = stDit[top1--];//取再前一個的操作數
				op = stOp[top2--];//取棧頂操作符
				stDit[++top1] = Operate(x, y, op);//計算結果入棧
				i--;//下一循環仍要比較這一字符
			}
			if (pri == 0){
				if (str[i] == ')'&&stOp[top2] == '(')
					top2--;
				else if (str[i] == '#'&&stOp[top2] == '#')
					return stDit[top1];
				else{
					pipei = false;
					return stDit[top1];
				}
			}
			else{
				pipei = false;
				return stDit[top1];
			}
		}
	}
	pipei = false;
	return stDit[top1];
}
int main()
{
	//循環運算並輸出,輸入在while中,每次循環先輸入纔會再判斷
	while (gets(str) != NULL && strcmp(str, "0") != 0)//非空且非輸入了0
	{
		bool pipei;
		double sum = Calc(pipei);
		if (pipei)
			printf("%.2lf\n", sum);
		else
			printf("匹配出錯\n");
	}
	system("pause");
	return 0;
}

2.2後綴表達式的運算(最易計算,運算符順序恰爲運算順序)

運算規則

逆波蘭式的數基本在前,遇符運算

  1. 運算符在式中出現的順序恰爲表達式的運算順序;
  2. 每個運算符和在它之前出現且緊靠它的兩個操作數構成一個最小表達式。(前數在前,後數在後,例:x y /既是x/y

AC代碼

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<ctype.h>
#include<algorithm>
#include<iostream>
using namespace std;

char str[1000], strop[400];//逆波蘭字符串,操作符棧
double strnum[450];//操作數棧
int topop,topnum;//操作符,操作數棧的棧頂指針(指向棧頂元素,-1爲棧空)
double op1, op2;//操作暫存數1,2

double OP(double x, double y, char t){
	if (t == '+')
		return (x + y);
	else if (t == '-')
		return (x - y);
	else if (t == '*')
		return (x*y);
	else if (t == '/')
		return (x / y);
}
double compute(){
	char t;//暫存符號
	topop = -1; topnum = -1;
	for (int i = 0; str[i] != NULL; i++){
		t = str[i];
		if (t == ' ')
			continue;
		else if (isdigit(t)){
			double tmp;
			sscanf(str + i, "%lf", &tmp);//注意這裏的第二個參數不能加.*表示精確度,否則該函數無用
			strnum[++topnum] = tmp;
			while (isdigit(str[++i]));//跳過操作過的連續爲數字的字符
			i--;//跳過一直到非數字字符,爲了下次循環判斷改數應先--抵掉for中的++
		}
		else{
			op2 = strnum[topnum--];
			op1 = strnum[topnum--];
			strnum[++topnum]=OP(op1, op2, t);
		}
	}
	return strnum[topnum];
}

int main(){
	cin.getline(str, sizeof(str));
	while (str != NULL&&strcmp(str, "0") != 0){//輸入不空且不爲0
		printf("%.2lf\n", compute());

		fill(str, str + strlen(str), 0);
		cin.getline(str, sizeof(str));
	}
	system("pause");
	return 0;
}

2.3前綴表達式的運算

運算規則

和後綴類似,顧名思義,波蘭式逆轉之後基本就是逆波蘭式的運算
波蘭式的數基本在後,所以逆轉之後,遇符運算(兩個操作數的順序和逆波蘭式相反

1.逆轉表達式
2. 運算符在式中出現的順序恰爲表達式的運算順序;
3. 每個運算符和在它之前出現且緊靠它的兩個操作數構成一個最小表達式。(前數在後,後數在前,例:y x /既是x/y

AC代碼

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<ctype.h>
#include<algorithm>
#include<iostream>
using namespace std;

char str[1000], strop[400];//逆波蘭字符串,操作符棧
double strnum[450];//操作數棧
int topop, topnum;//操作符,操作數棧的棧頂指針(指向棧頂元素,-1爲棧空)
double op1, op2;//操作暫存數1,2

double OP(double x, double y, char t){
	if (t == '+')
		return (x + y);
	else if (t == '-')
		return (x - y);
	else if (t == '*')
		return (x*y);
	else if (t == '/')
		return (x / y);
}
double compute(){
	reverse(str, str + strlen(str));

	char t;//暫存符號
	topop = -1; topnum = -1;
	for (int i = 0; str[i] != NULL; i++){
		t = str[i];
		if (t == ' ')
			continue;
		else if (isdigit(t)){
			double tmp;
			sscanf(str + i, "%lf", &tmp);//注意這裏的第二個參數不能加.*表示精確度,否則該函數無用
			strnum[++topnum] = tmp;
			while (isdigit(str[++i]));//跳過操作過的連續爲數字的字符
			i--;//跳過一直到非數字字符,爲了下次循環判斷改數應先--抵掉for中的++
		}
		else{
			op1 = strnum[topnum--];
			op2 = strnum[topnum--];
			strnum[++topnum] = OP(op1, op2, t);
		}
	}
	return strnum[topnum];
}

int main(){
	cin.getline(str, sizeof(str));
	while (str != NULL&&strcmp(str, "0") != 0){//輸入不空且不爲0
		printf("%.2lf\n", compute());

		fill(str, str + strlen(str), 0);
		cin.getline(str, sizeof(str));
	}
	system("pause");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章