1. 程式人生 > >中綴轉字尾——逆波蘭表示

中綴轉字尾——逆波蘭表示

計算機無法識別數學表示式中的括號以及四則運算的先後順序,因此需要把數學表達示轉換成一種計算機能識別的,逆波蘭表示就能很好的解決這個問題。逆波蘭表示是數學表示式中的一種不需要括號的字尾表達法。即把一箇中綴數學表示式改變成一個字尾表示。中綴表示的意思就是運算子在要計算的數字中間,而後綴表示就是運算子在要計算的數字之後。

       例如:9+(3-1)*3+6/2   這就是一箇中綴表示,和正常的數學表示式一樣

                 931-3*+62/+     這就是一個字尾表示

轉換規則:

       1.從左到右遍歷中綴表示式的每一個數字和符號,若是數字就輸出,即成為字尾表示式的一部分;

       2.若是符號,則判斷其與棧頂符號的優先順序,是右括號或優先順序不高於棧頂符號(乘除優先加減)則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出字尾表示式為止;若是左括號或者優先順序高於棧頂符號則入棧。

以上面的例子為例說明規則:  (假定同一優先順序在棧內的優先順序高於棧外優先順序)

      1.從左到右依次遍歷的第一個字數是9,因為是數字,不入棧,照常輸出;

      2.第二個字元是+,因為是符號,同時棧內為空,則入棧;

     3.第三個字元是(,因為是左括號,入棧;

     4.第四個字元是3,因為是數字,不入棧,照常輸出;

     5.第五個字元是-,因為其優先順序比(低,入棧;

     6.第六個字元是1,因為是數字,不入棧,照常輸出;

     7.第七個字元是),因為是右括號,棧中靠近棧頂的第一個左括號之後的所有元素輸出,該例中則把棧頂元素-輸出,同時棧中的左括號(出棧,但是並不輸出,右括號)也不輸出;        //此時棧中就剩下字元+

     8.第八個字元是*,因為其優先順序高於棧頂元素+,入棧;

     9.第九個字元是3,因為是數字,不入棧,照常輸出;  

     10.第十個字元是+,因為其優先順序低於棧頂元素*,因此,棧頂元素*輸出,此時棧中的棧頂元素變為字元+,因為先進棧的優先順序高於後進棧(可理解為同一優先順序中棧內的優先順序高於棧外的優先順序),因此棧頂元素+輸出,此時,棧為空,字元+入棧;(當然,假如棧中還存在元素,但是其棧頂元素優先順序高於當前遍歷字元的優先順序,同樣把當前遍歷的字元入棧,如第5步;反之,繼續把棧中元素輸出,直到棧頂元素優先基高於當前遍歷的字元或者棧為空為止)

     11.12.13各位看官根據我說的自己試著寫一下吧。最後結果就在上面哦。

      上面所述是中綴轉字尾的具體實施步驟,根據這寫出程式碼還是有一定難度的,當然大神除外,大神的話就不需要看下面了。

      程式碼的實現的難度在於怎麼去很好的區分四則運算子的優先順序,以及括號的優先順序,還有棧內棧外的優先順序。為解決這個麻煩事,作了以下假定:

       棧內      數值                    棧外        數值

        -             4                         -             3

        +            4                        +             3

        *             6                        *              5

        /             6                        /              5

        (             2                       (               10

                                               )              2

解釋下:加減的優先順序低於乘除的優先順序,且棧外的加減的優先順序低於棧內的優先順序,因此可以假設用數值3代表棧外的加減,棧內的加減的優先順序要比棧外高,因此只需要把數值加1即可,即4,同理可得乘除的,括號這比較特殊,左邊的括號在棧外時其優先順序要最大才行,因為遇到左括號就需要入棧,但是在棧中時為了能讓其他的再進來,就必須又要保證它的優先順序最小才行,右括號一遍歷到就需要把離棧頂最近的左括號上的所有元素都輸出,而且括號不要輸出,所以可以假定右括號在棧外的數值和棧中左括號值相等,當出現相等的情況時就能明白有一對括號出現。

       說了一堆是不是沒看懂啊,沒關係,我現在就附上我寫的程式碼,不過我這程式碼有很大的侷限性,不過解決這個中綴轉字尾是沒什麼問題的。(侷限性就讓各位看官自己猜吧)

這是main.cpp檔案

#include"stack.h"

int main()
{
	SqStack s;
	char str[] = "9+(3-1)*3+6/2";

	init_stack(&s);      //初始化棧
	mid_lat(&s, str);    //中綴轉字尾

	printf_s("%s\n", str);

	return 0;
}
這是stack.h檔案
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#define MAXSIZE 30
#define ElemType char

typedef struct
{
	ElemType data[MAXSIZE];
	int top;
}SqStack;

void init_stack(SqStack *s);    //初始化棧
bool my_push(SqStack *s, ElemType value);   //入棧
bool my_pop(SqStack *s,ElemType *p);   //出棧
bool mid_lat(SqStack *s, char *p);    //中綴轉字尾
int sign_fig(char *p);                //符號轉換成數值
這是stack.cpp檔案
#include"stack.h"

void init_stack(SqStack *s)    //初始化棧
{
	assert(s);

	s->top = -1;       //棧為空時,即s->top=-1
}
bool my_push(SqStack *s, ElemType value)    //入棧
{
	assert(s);
	if (s->top == MAXSIZE - 1)       //棧滿
	{
		return false;
	}
	s->top++;
	s->data[s->top] = value;
	return true;
}
bool my_pop(SqStack *s,ElemType *p)  //出棧
{
	assert(s);
	if (s->top == -1)        //棧空
	{
		return false;
	}
	*p = s->data[s->top];
	s->top--;
	return true;
}
int sign_fig(char *p)          //符號轉換成數值
{
	assert(p);
	switch (*p)
	{
	case '-':
	case '+':return 3;
		break;
	case '*':
	case '/':return 5;
		break;
	case '(':return 10;
		break;
	case ')':return 2;
		break;
	}
	
}
bool mid_lat(SqStack *s, char *p)
{
	assert(s || p);

	char *q;
	char temp;
	char temp_bra;
	int num_out;
	int num_in;

	q = p;

	while (*p != '\0')      //遍歷表示式
	{
              //遍歷表示式,為數字時輸出
		if (*p >= '0'&&*p <= '9' || *p == ' ') //這是程式的不足處,不足的不多寫了,害羞臉
		{
			*q = *p;      
			q++;
			p++;
		}
		else
		{
                      //棧為空時,棧外是符號時直接入棧
			if (s->data[s->top] == -1)
			{
				my_push(s, *p);
				p++;
			}
			else
			{
                                //比較棧內棧外符號的優先順序
				num_out = sign_fig(p);     //棧外符號數值
				temp = s->data[s->top];
				if (temp == '(')
				{
					num_in = 2;//棧內符號數值,棧內為左括號時,數值賦為2
				}
				else
				{
					num_in = sign_fig(&temp) + 1;//棧內符號數值
				}
                               //比較數值,確定優先順序
                               //棧內數值小於棧外數值時,即棧內棧頂元素的優先順序低於棧外優先順序
                              //則棧外元素進棧
				if (num_in < num_out)  
				{
					my_push(s, *p);
					p++;
				}
                             //棧內數值大於棧外數值時,則出棧,輸出
				else if (num_in > num_out)
				{
					my_pop(s, q);
					q++;
				}
                            //相等時,代表出現了一對括號,棧中左括號出棧,但是不輸出
				else
				{
					my_pop(s, &temp_bra);
					p++;
				}
			}
		}
	}
	while (s->top != -1)   //遍歷完後,清空棧內元素,把還存在棧中的符號輸出
	{
		my_pop(s, q);
		q++;
	}
	*q = '\0';    //因為是用字串來表示表示式,最後需要新增‘\0’
	return true;
}