逆波蘭算法(後綴表達式)

逆波蘭式

即我們平時所說的後綴表達式(將運算符寫在操作數之後)

實現逆波蘭式的算法,難度並不大,但爲什麼要將看似簡單的中序表達式轉換爲複雜的逆波蘭式?原因就在於這個簡單是相對人類的思維結構來說的,對計算機而言中序表達式是非常複雜的結構。相對的,逆波蘭式在計算機看來卻是比較簡單易懂的結構。因爲計算機普遍採用的內存結構是棧式結構,它執行先進後出的順序。

舉個例子:
1+2就是一箇中綴式,轉換成後綴式爲1 2 +
1+2*3…… ……1 2 3 * +
1+(2+5)…… ……1 2 5 + +
在計算這些表達式的值時,通過將數字入棧,遇到運算符後在把數字出棧計算,計算完再次入棧。一直循環……
比如1 2 3 * +
在這裏插入圖片描述
在這裏插入圖片描述
通過以上實例,我們可以看到,計算機利用棧的性質計算後綴式的結果與我們人類用中綴式計算的結果並無差別。但是,我們不要忘了計算機的計算速度可是很快的,在進行一些比較複雜或是計算量較大的運算時,計算機可以迅速的得出答案,這樣讓計算機幫助我們去計算可以省下大量的時間何精力。
但前面也說了,計算機的思維方式和我們人的思維方式不同,計算機是硅基生物,而我們是碳生物。硅基生物雖然計算快我們比不上,但它不像我們碳基生物那麼聰明,不懂得變通。因此,想讓計算機幫我們計算,我們還需要把計算表達式轉換成計算機看得懂的方式才行。

中綴轉後綴

在中綴式中,有運算符的優先級以及括號的限制,因此,在計算的時候需要考慮計算先後的問題,但在後綴式中只有入棧與出棧的操作,因此,我們在中綴轉後綴的時候需要提前把計算順序考慮進去,以便於計算機可以直接運算。
在這裏插入圖片描述
比如:符號入棧
在這裏插入圖片描述
比如:符號出棧
在這裏插入圖片描述
比如:括號
在這裏插入圖片描述
代碼

/* 判斷優先級 --> 決定是否出入棧
** 符號優先級高於符號棧,如符號棧
** 符號優先級低於符號棧,符號棧裏的符號出棧,入數字棧,繼續向下比較
*/
bool IsPop(char a, char b)	//a爲待比較符號,b爲符號棧棧頂元素
{
	if (b == '(') return false;
	if (a == '*' || a == '/')
	{
		if (b == '+' || b == '-')
		{
			//可以如符號棧
			return false;
		}
		else
		{
			return true;
		}
	}
	//if (a == '+' || a == '-')
	//{
		//a符號爲最低優先級,符號棧出棧
	return true;
	//}
}

/* 中綴轉後綴 src帶入中綴式,des帶出後綴式 */
void InfixToSuffix(char* src, char* des)
{
	/* 符號棧 */
	Stack symbol;
	symbol.data = (char*)malloc(strlen(src)/2);
	symbol.top = 0;

	int i = 0;
	int k = 0;
	while (src[i] != '\0')
	{
		/*----------- 特殊符號判斷------------------------------*/
		if (src[i] == ' ')		//如果當前字符是空格,則往後走一個
		{
			i++;
			continue;
		}
		else if (src[i] == '(')	//左括號直接入棧
		{
			Push(&symbol, src[i]);
		}
		else if (src[i] == ')')	//遇到 ) ,輸出( )之間的所有符號
		{
			while (Top(&symbol) != '(')
			{
				des[k++] = Top(&symbol);
				des[k++] = ' ';
				Pop(&symbol);
			}
			Pop(&symbol);
		}
		/*----------------- 數字 -------------------------------*/
		else if (isdigit(src[i]))
		{
			des[k++] = src[i];
			if (!isdigit(src[i + 1]))		//數字後加空格
			{
				des[k++] = ' ';
			}
		}
		/*----------------- 運算符 -------------------------------*/
		else
		{
			switch (src[i])		
			{
			case '+':case '-':case '*':case '/':
				if (Empty(&symbol))		//如果符號棧爲空,直接入符號
				{
					Push(&symbol, src[i]);
				}
				else				//否則,判斷是否選擇入符號棧還是出棧頂元素
				{
					if (IsPop(src[i], Top(&symbol)))	//出符號棧
					{
						des[k++] = Top(&symbol);
						Pop(&symbol);
						continue;
					}
					else			//當前符號優先級高,入符號棧
					{
						Push(&symbol, src[i]);
					}
				}
				break;
			default:
				break;
			}

		}

		i++;	/* 遍歷下一字符 */
	}
	/*字符串已遍歷完,把符號棧中剩餘的符號入棧到數字棧中 */
	while (!Empty(&symbol))
	{
		des[k++] = Top(&symbol);
		des[k++] = ' ';
		Pop(&symbol);
	}
	des[k] = '\0';

	free(symbol.data);
}

當然,以上代碼運行的條件是要有一個棧,棧的實現到沒有多難,以下爲棧的代碼

Stack.h

#ifndef _STACK_H
#define _STACK_H

#define ElemType char
#define MAXSIZE 20

typedef struct _Stack
{
	ElemType *data;
	int top;		//棧頂指針(有效元素的個數)
	int size;
}Stack;

/* 初始化 */
void Init_Stack(Stack* st);
/* 判空 */
bool Empty(Stack* st);
/* 入棧 */
void Push(Stack* st, ElemType val);
/* 出棧 */
ElemType Pop(Stack* st);
/* 獲取棧頂元素 */
ElemType Top(Stack* st);

#endif

Stack.cpp

#include <stdbool.h>
#include <malloc.h>
#include "Stack.h"

/* 判滿 */
static bool Full(Stack* st)
{
	return st->top == MAXSIZE;
}



/* 判空 */
bool Empty(Stack* st)
{
	return st->top == 0;
}
/* 初始化 */
void Init_Stack(Stack* st)
{
	st->data = (ElemType*)malloc(sizeof(ElemType) * 20);
	st->size = MAXSIZE;
	st->top = 0;
}
/* 入棧 */
void Push(Stack* st, ElemType val)
{
	if (Full(st)) return;
	st->data[st->top++] = val;
}
/* 出棧 */
ElemType Pop(Stack* st)
{
	if (Empty(st))	return -1;
	st->top--;
	return st->data[st->top];
}
/* 獲取棧頂元素 */
ElemType Top(Stack* st)
{
	if (Empty(st))	return -1;
	return st->data[st->top - 1];
}

測試

main.cpp

int main()
{
	char str[50] = "";
	printf("請輸入表達式:");
	scanf("%[^\n]", str);		/* 輸入字符串,以回車爲結束標誌*/

	char strRet[50] = "";		/* 接收中綴式轉換成的後綴式 */
	
	InfixToSuffix(str, strRet);		/* 中綴轉後綴 */

	printf("%s\n", strRet);			/* 輸出後綴式*/

	return 0;
}


C++利用後綴轉中綴(逆波蘭算法)實現計算器

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