数据结构学习笔记(7.栈的应用及简单的计算器)

本节知识点:

1.栈的应用一:编译器中成对符号匹配的检查!如:括号 :() 、{} 、[]  引号:'单引号' 、 “双引号”   这些符号匹配情况的检查!

算法思路:

 

这里有一个问题是需要注意的:就是单引号和双引号在使用的时候,是需要借助转移字符的!

 

示例代码: main.c文件:

#include <stdio.h>
#include <stdlib.h>
#include "LinkStack.h"

/*********************************************************************************
函数名: isLeft
函数功能:判断是否为左符号 是左符号返回1  不是返回0
参数:  要判断的字符  char类型
返回:  成功返回1   失败返回0 
*********************************************************************************/ 
int isLeft(char c)
{
	int ret = 0;
	switch (c)
	{
		case '<':
		case '(':
		case '[':
		case '{':
		case '\'':   // ‘和 “需要转义字符 原因分别在于字符和字符串 两种情况 
		case '\"':
		   ret = 1;	
		break;
		
		default :
			ret = 0;
		break;
	}
	return ret;
}

/*********************************************************************************
函数名: isRight
函数功能:判断是否为右符号 是右符号返回1  不是返回0
参数:  要判断的字符  char类型
返回:  成功返回1   失败返回0 
*********************************************************************************/ 
int isRight(char c)
{
	int ret = 0;
	switch (c)
	{
		case '>':
		case ')':
		case ']':
		case '}':
		case '\'':   // ‘和 “需要转义字符 原因分别在于字符和字符串 两种情况 
		case '\"':
		   ret = 1;	
		break;
		
		default :
			ret = 0;
		break;
	}
	return ret;
}

/*********************************************************************************
函数名:match
函数功能:判断左右符号是否匹配 
函数参数: char left为从栈顶获取的左符号   char right为刚刚获得右符号 
返回值:匹配成功返回1  失败返回0 
*********************************************************************************/
int match(char left ,char right)
{
	int ret = 0;
	switch (left)
	{
		case '<' : 
			ret = (right == '>');
		  break;
		case '{' :
			ret = (right == '}');
		  break;
		case '[' : 
			ret = (right == ']');
		  break;
		case '\'' :
			ret = (right == '\'');
		  break;
		case '\"' :
			ret = (right == '\"');
		  break;
		default :
			ret = 0;			
		  break;		
	} 
}

/*********************************************************************************
函数名:check
函数功能:检查字符串语法是否匹配 
函数参数:想要检查的字符串头地址 
返回值:匹配返回1  不匹配返回0 
*********************************************************************************/
int check(const char* code)
{
	int ret = 1;
	char* temp = NULL; 
	LinkStack* stack = NULL;
	stack = Creat_LinkStack();
	while(*code != '\0')
	{
		if( isLeft(*code) )
		{
			LinkStack_Push(stack ,(void*)code);
		}
		if( isRight(*code) )
		{
			temp = (char*)LinkStack_Pop(stack);
			if( (NULL == temp)||(!match(*temp, *code)) )//判断左符号和右符号如果不匹配 
			{
				printf("%c is not match!\n",*code);
				ret = 0; //返回失败标准 
				break;
			}
		} 
		code++;
	}
	if( (0 == Get_LinkStack_Length(stack))&&('\0' == *code) )
	{
		printf("Succeed!\n");
		ret = 1;
	}
	else
	{
		printf("Invalid code!\n");
		ret = 0;
	}
	Destroy_LinkStack(stack);
	return ret;
}

int main(int argc, char *argv[]) 
{
	const char* code = "#include <stdio.h>void main(){int TestArr\
	ay[5][5] = { {11,12,13,14,15},{16,17,18,19,20},{21,22,23,24,25},\
	{26,27,28,29,30},{31,32,33,34,35}};int* p1 = (int*)(&TestArray + 1);\
	int* p2 = (int*)(*(TestArray + 1) + 6);  //  *(&t[0] + 1)  &t[1] \
	[0]+6+4  printf("", *(*TestArray), *(*(TestArray + 1)), *(*(TestArray + 3) + 3),\
	 p1[-8], p2[4]);}";
	check(code);
	return 0;
}

注意:当需要检测成对出现但又互不相邻的事物时,可以使用栈"后进先出"的特性!栈非常适合于需要"就近匹配"的场合!

 

2.栈的应用二:中缀表达式转后缀表达式后缀表达式的计算

   中缀转后缀算法:

    后缀表达式计算的算法:

本节代码:

计算器的简单代码(中缀转后缀和后缀计算的练习):

#include <stdio.h>
#include <stdlib.h>
#include "LinkStack.h" 


int isNumber(char c)
{
    return ('0' <= c) && (c <= '9');
}

int isOperator(char c)
{
    return (c == '+') || (c == '-') || (c == '*') || (c == '/');
}

int isLeft(char c)
{
    return (c == '(');
}

int isRight(char c)
{
    return (c == ')');
}

int priority(char c)
{
    int ret = 0;
    
    if( (c == '+') || (c == '-') )
    {
        ret = 1;
    }
    
    if( (c == '*') || (c == '/') )
    {
        ret = 2;
    }
    
    return ret;
}
/********************************************************************************************
函数名:   transform
函数功能: 将中缀表达式转换成为后缀表达式 
参数:     const char* exp 字符串形式的中缀表达式   
           int* last_table 后缀表达式保存在这个int数组中 
           int* op_table   后缀表达式数组中 符号的位置保存在这个数组中  用来区分数字和符号  
返回值:  int last_num   用来保存后缀表达式表的长度 
********************************************************************************************/
int transform(const char* exp, int* last_table, int* op_table)
{
	int num = 0;
	int last_num = 0;
	int op_num = 0; 
	int temp = 0;
	int flag = 0;
	LinkStack* stack = Creat_LinkStack();//创建一个链式栈 
	while('\0' != exp[num])
	{
		while(isNumber(exp[num])) //判断是否为数字 
		{
			temp = 10*temp + exp[num]-48;
			num++;
			flag = 1;
		}
		if(1 == flag) //只有进入了上面的循环 才可以进行下面的操作 
		{
			last_table[last_num] = temp; //把数字保存在后缀表达式中 
			last_num++;
			temp = 0; //temp清零
			flag = 0; 
			
			if('\0' == exp[num])
			{
				break; //如果是最后一个数字就要跳出循环 
			}
		}
		if(isOperator(exp[num])) //判断是否为运算符 
		{
			/*栈顶符号优先级不低的情况*/
			while((0 != Get_SeqStack_Length(stack))&&(priority(exp[num]) <= (priority((char)(int)Get_LinkStack_Top(stack)))))
			{
				last_table[last_num] = (char)(int)LinkStack_Pop(stack);
				op_table[op_num] = last_num;
				op_num++;
				last_num++;	
			} 
			LinkStack_Push(stack,(void*)(int)exp[num]);
		} 
		else if( isLeft(exp[num]) ) //判断是否为左括号 
		{
			LinkStack_Push(stack,(void*)(int)exp[num]);
		}
		else if( isRight(exp[num]) ) //判断是否为右括号
		{
			while(!isLeft((char)(int)Get_LinkStack_Top(stack)))
			{
				last_table[last_num] = (char)(int)LinkStack_Pop(stack);
				op_table[op_num] = last_num;
				op_num++;
				last_num++;	
			}
			LinkStack_Pop(stack); // 将左括号从栈中弹出 
		} 
		else // 算式错误的情况 
		{
			 printf("Invalid expression!");
             break;	
		} 
		num++;
	} 
	/*遍历结束  把栈中的符号全部输出*/ 
	while( (Get_LinkStack_Length(stack)>0) && ('\0' == exp[num]))
	{
		last_table[last_num] = (char)(int)LinkStack_Pop(stack);
		op_table[op_num] = last_num;
		op_num++;
		last_num++;	
	} 
	op_table[op_num] = -1;  //负1 作为操作符表的结束 
	
	Destroy_LinkStack(stack); //销毁一个链式栈 
	return last_num;
}
/********************************************************************************************
函数名:  print_last_table
函数功能:打印后缀表达式 
参数:   后缀表达式数组 last_table   后缀表达式符号下表数组  op_table  后缀表达式长度 last_num
返回值: 无 
********************************************************************************************/
void print_last_table(const int* last_table, const int* op_table, const int last_num)
{
	int i = 0;
	int j = 0;
	for(i = 0; i<last_num; i++)
	{
		if(i == op_table[j])
		{
			printf("%c ",last_table[i]);
			j++;
		}
		else
		{
			printf("%d ",last_table[i]);
		}
	}
	printf("\n");
} 
/********************************************************************************************
函数名:  express
函数功能:计算左值和右值的四则运算 
参数:    int left 左值   int right右值  char op 运算符 
返回值: 返回计算的值 
********************************************************************************************/
int express(int left, int right, char op)
{
    int ret = 0;
    
    switch(op)
    {
        case '+':
            ret = left + right;
            break;
        case '-':
            ret = left - right;
            break;
        case '*':
            ret = left * right;
            break;
        case '/':
            ret = left / right;
            break;
        default:
            break;
    }
    
    return ret;
}

/********************************************************************************************
函数名:  compute
函数功能:后缀表达式计算 
参数:   后缀表达式数组 last_table   后缀表达式符号下表数组  op_table  后缀表达式长度 last_num
返回值: 返回计算的值 
********************************************************************************************/
int compute(const int* last_table, const int* op_table, const int last_num)
{
	LinkStack* stack = Creat_LinkStack();//创建一个链式栈
	int ret = 0;
	int i = 0;
	int j = 0;
	for(i = 0; i<last_num; i++)
	{
		if(i == op_table[j])
		{
			int right = (int)LinkStack_Pop(stack);
			int left = (int)LinkStack_Pop(stack);
			int result = express(left, right, last_table[i]);			
			LinkStack_Push(stack,(void*)result);
			j++;
		}
		else
		{

			LinkStack_Push(stack,(void*)last_table[i]);
		}
	}
	/*栈中只有一个数据  且遍历完全后缀表达式数组*/
	if((Get_LinkStack_Length(stack) == 1) && (last_num == i) )
	{
		 ret = (int)LinkStack_Pop(stack);
	} 
	else
	{
		 printf("Invalid expression!");
	}
	Destroy_LinkStack(stack); //销毁一个链式栈 
	return ret;
} 


int main(int argc, char *argv[]) 
{
	int last_table[100] = {0}; //后缀表达式存放在这个数组中 
	int op_table[100] = {0};   //用来存放后缀表达式中符号的下表  -1为结束 
	int last_num = 0;
	last_num = transform("78-(5*16)+28/2", last_table, op_table); 

	print_last_table(last_table, op_table, last_num);

	printf("\n%d\n",compute(last_table, op_table, last_num));

	return 0;
}

注意:第一,上面代码是一个可以接受多位数计算的后缀计算算法,last_table数组用来存放后缀表达式,op_table数组用来存放 last_table数组中运算符的数组下标!

           第二,上面对于链式栈的使用,是将需要压栈的数据直接当做void* StackData来处理的,没有用void* StackData来保存地址,仅仅当成了一个int型进行处理!当然对于压栈函数,就没有必要进行node有效值的判断了,因为存入的不是地址,是数据。如果排除了node为NULL的情况,则会导致数据为0的情况,压栈不成功,导致bug的存在!
           第三,当然这个计算器还有很多需要完善的地方,比如小数的计算、除数为0的情况的异常处理,表达式错误的异常处理等!

 

 

 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值