1. 括號匹配的檢驗
假設表達式中含有3種括號:(),[],{},其嵌套的順序隨意。檢驗括號是否匹配。
基本思想:在算法中設置一個棧,每讀入一個括號,若是右括號,則或者與棧頂匹配的左括號相互消解,或者是不合法的情況;若是左括號,則直接壓入棧中。若括號匹配,在算法的開始和結束時,棧都應該是空的。
代碼:
/* * 判斷表達式中的括號是否匹配,匹配輸出Yes,否則輸出No * {(zhang)+[lei]+{lei}={zhangleilei}} -> Yes * {(zhang)+[lei]+{lei}={zhangleilei(]}} -> Yes */ #include <stdio.h> #include <stdbool.h> #include <malloc.h> #define STACK_INIT_SIZE 10 //存儲空間初始分配量 #define STACKINCREMENT 10 //存儲空間分配增量 typedef struct { char *base; char *top; int stacksize; } SqStack; void InitStack(SqStack *s); bool GetTop(SqStack *s, char *c); bool StackEmpty(SqStack *s); bool Push(SqStack *s, char *c); bool Pop(SqStack *s, char *c); bool match(char a, char b); bool in(char c, char *ch); int main() { SqStack s; char ch,ch_prior; char *left = "([{"; char *right = ")]}"; bool flag = true; InitStack(&s); while (true) { if ((ch = getchar()) == '\n') break; if (in(ch, left)) //如果字符為左括號,進棧 { Push(&s, &ch); } else if (in(ch, right)) //如果字符為右括號,判斷棧頂元素是否存在並且與當前字符匹配 { if (GetTop(&s, &ch_prior) && match(ch_prior, ch)) { Pop(&s, &ch); } else { flag = false; break; } } } if (flag == true) { puts("Yes"); } else { puts("No"); } return 0; } //初始化棧 void InitStack(SqStack *s) { s->base = (char*)malloc(sizeof(char)*STACK_INIT_SIZE); s->top = s->base; s->stacksize = STACK_INIT_SIZE; } //獲得棧頂元素 bool GetTop(SqStack *s, char *c) { if (StackEmpty(s)) return false; *c = *(s->top - 1); return true; } //判斷棧是否為空 bool StackEmpty(SqStack *s) { if (s->base == s->top) return true; return false; } //進棧 bool Push(SqStack *s, char *c) { //如果空間不夠,增加空間的分配 if (s->top - s->base >= s->stacksize) { s->base = (char*)realloc(s->base, sizeof(char)*(s->stacksize + STACKINCREMENT)); s->stacksize = s->stacksize + STACKINCREMENT; } *(s->top++) = *c; return true; } //出棧 bool Pop(SqStack *s, char *c) { if (StackEmpty(s)) return false; *c = *(--s->top); return true; } //判斷左括號a和右括號b是否相匹配 bool match(char a, char b) { if (a == '('&&b == ')') return true; if (a == '['&&b == ']') return true; if (a == '{'&&b == '}') return true; return false; } //判斷字符c是否位於字符串ch中 bool in(char c, char *ch) { while (*ch&&*ch != c) ch++; if (*ch) return true; return false; }
2. 表達式求值
算符優先法求表達式的值。算符間的優先關系如下:
假設運算符a和b相繼出現,則+、-、*和/為a時的優先性均低於’(’但高於’)’;為了滿足從左算到右的規則,當a=b時,令a>b,’#’是表達式的結束符。為了算法簡潔,在表達式的最左邊也虛設一個’#’構成整個表達式的一對括號。
‘(’=’)’,表示當左右括號相遇時,括號內的運算已經完成。同理’#’=’#’表示整個表達式求值完畢。
在處理表達式前,首先設置兩個棧:操作數棧(OPND):用於寄存操作數和運算結果;運算符棧(OPTR):用於寄存運算符。
算法的基本思想:
1) 首先置操作數棧為空棧,表達式起始符’#’為運算符棧的棧底元素;
2) 依次讀入表達式中每個字符,若是操作數則進OPND棧,若是運算符則和OPTR棧的棧頂運算符比較優先權后做相應操作,直至整個表達式求值完畢(即OPTR棧的棧頂元素和當前讀入的字符均為’#’)。
代碼:
/* * 輸入算術表達式,運算符限定為"+ - * / ( )",操作數限定為0~9。求表達式的值。 * 其中以#表示表達式的開始和結束。 * 輸入:9+8-7*6/5*(4+3)-2+1# * 輸出:-40 * 輸入:((1+2)*3)+4*(5-2)# * 輸出:21 */ #include <stdio.h> #include <malloc.h> #include <stdbool.h> //使用bool類型時需包含的頭文件 #include <assert.h> //使用assert語句時需包含的頭文件 #define STACK_INIT_SIZE 20 #define STACKINCREMENT 5 typedef union { char character; int number; } ElemType; typedef struct { ElemType *base; ElemType *top; int stacksize; } Stack; void InitStack(Stack *s); bool push(Stack *s, ElemType *ch); bool pop(Stack *s, ElemType *ch); ElemType *GetTop(Stack *s); bool in(char c, char *ch); char precede(char a, char b); int compute(int a, char oper, int b); int main() { ElemType ch,ch_prior,temp; //temp用於進棧和出棧的臨時變量 ElemType left, right; ElemType result; Stack OPTR,OPND; //運算符棧和操作數棧 char *num = "0123456789"; char *oper = "+-*/()#"; InitStack(&OPTR); temp.character = '#'; push(&OPTR, &temp); InitStack(&OPND); ch.character = getchar(); //循環停止條件:棧頂元素為'#',並且當前待進棧元素也為'#'。表明#號中間的表達式計算完畢。 while (!(GetTop(&OPTR)->character == '#' && ch.character == '#')) { //若當前輸入字符為數字,進操作數棧。繼續讀取下一個字符。 if (in(ch.character, num)) { temp.number = ch.character - '0'; push(&OPND, &temp); ch.character = getchar(); } else if (in(ch.character, oper))//如果當前字符為運算符,比較棧頂運算符與當前運算符的優先級。 { switch (precede(GetTop(&OPTR)->character, ch.character)) { //若棧頂運算符優先級較高,則先計算中間值。 //注意,此時並未將當前運算符進棧,也沒有讀入下一個字符。 //而是在下一次while循環中繼續將當前運算符和棧頂運算符做比較。 case '>': pop(&OPND, &right); pop(&OPND, &left); pop(&OPTR, &ch_prior); temp.number = compute(left.number, ch_prior.character, right.number); push(&OPND,&temp); break; //若當前運算符優先級較高,將運算符進棧。繼續讀取下一個字符。 case '<': push(&OPTR, &ch); ch.character = getchar(); break; //兩個運算符優先級相同表明,棧頂運算符為'(',當前運算符為')'。此時將棧頂元素彈出。繼續讀取下一個字符。 case '=': pop(&OPTR, &ch_prior); ch.character = getchar(); break; } } } //此時,操作數棧只有一個元素,即為計算結果。 pop(&OPND, &result); printf("%d", result.number); return 0; } void InitStack(Stack *s) { s->base = (ElemType*)malloc(sizeof(ElemType)*STACK_INIT_SIZE); s->top = s->base; s->stacksize = STACK_INIT_SIZE; } bool push(Stack *s, ElemType *ch) { if (s->top - s->base >= s->stacksize) { s->base = (ElemType*)realloc(s->base, sizeof(ElemType)*(s->stacksize + STACKINCREMENT)); s->stacksize = s->stacksize + STACKINCREMENT; } *(s->top++) = *ch; return true; } bool pop(Stack *s, ElemType *ch) { if (s->top == s->base) //空棧 return false; *ch = *(--s->top); return true; } ElemType* GetTop(Stack *s) { if (s->top == s->base) return NULL; return (s->top - 1); } //檢驗字符c是否位於字符串ch中。 bool in(char c, char *ch) { while (*ch&&*ch != c) ch++; if (*ch) return true; return false; } //比較運算符a和b的優先級。 char precede(char a, char b) { switch (a) { case '+': if (in(b, "+-)#")) return '>'; else if (in(b, "*/(")) return '<'; break; case '-': if (in(b, "+-)#")) return '>'; else if (in(b, "*/(")) return '<'; break; case '*': if (in(b, "+-*/)#")) return '>'; else if (in(b, "(")) return '<'; break; case '/': if (in(b, "+-*/)#")) return '>'; else if (in(b, "(")) return '<'; break; case '(': if (in(b, "+-*/(")) return '<'; else if (in(b, ")")) return '='; break; case ')': if (in(b, "+-*/)#")) return '>'; break; case '#': if (in(b, "+-*/(")) return '<'; else if (in(b, "#")) return '='; break; } } //用於計算中間值。 int compute(int a, char oper, int b) { switch (oper) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': assert(b != 0); return a / b; } }