棧的兩個應用:括號匹配的檢驗和表達式求值


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.     表達式求值

算符優先法求表達式的值。算符間的優先關系如下:

clip_image001

假設運算符ab相繼出現,則+-*/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;
	}
}

pdf下載:http://pan.baidu.com/s/1bnhFiqj


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM