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;
}
}

