数据结构 - 栈的应用(符号匹配、中缀转后缀表达式、后缀表达式计算)

6 篇文章 0 订阅
3 篇文章 0 订阅

1、符号就近匹配

就近匹配算法思路:

1、从第一个字符串开始扫描 

2、当遇见普通字符串时忽略

3、当遇见左符号时压入桟中

4、当遇见右符号时,从桟中弹出栈顶符号,并进行匹配:

        匹配成功:继续读入下一个字符

        匹配失败:立即停止,并报错 

5、结束:

        成功:所有字符扫描完毕,且栈为空

        失败:匹配失败或所有字符扫描完毕但栈非空

代码实现:

#include "dm06_LinkStack.h"

bool isLeft(const char c)
{
	
	switch (c)
	{
	case '{' :
	case '<':
	case '[':
	case '(':
	case '\'':
	case '\"':
		return true; break;
	default:
		return false; break;
	}
}

bool isRight(const char c)
{
	switch (c)
	{
	case '}':
	case '>':
	case ']':
	case ')':
	case '\'':
	case '\"':
		return true; break;
	default:
		return false; break;
	}
}

bool match(const char l, const char r)
{
	bool ret = false;

	switch (l)
	{
	case '{':	ret = ('}' == r);	break;
	case '<':	ret = ('>' == r);	break;
	case '[':	ret = (']' == r);	break;
	case '(':	ret = (')' == r);	break;
	case '\'':	ret = ('\'' == r);	break;
	case '\"':	ret = ('\"' == r);	break;
	default:
		break;
	}

	return ret;
}

void scanner(const char* s)
{
	LinkStack* stack = LinkStack_Create();
	if (nullptr == stack)
	{
		printf("func scanner() err: nullptr\n");
		return;
	}

	int i = 0;
	int ret = 0;
	while (s[i] != '\0')
	{
		if (isLeft(s[i]))
		{
			ret = LinkStack_Push(stack, (void *)(s + i));

			if (0 != ret) {printf("func scanner() err: %d\n", ret); return; }
		}
		else if (isRight(s[i]))
		{
			char* lc = (char *)LinkStack_Top(stack);

			if (nullptr == lc)
			{
				printf("failed: %c 缺失对应的左值\n", s[i]);
				break;
			}

			if (isLeft(*lc) && match(*lc, s[i]))
			{
				LinkStack_Pop(stack);
			}
			else if (isLeft(*lc) && !match(*lc, s[i]))
			{
				printf("faild : %c 和 %c 不匹配\n", *lc, s[i]); 
				break;
			}
			else
			{
				printf("faild : 栈顶元素 %c 不是左值\n", *lc);
				break;
			}
		}

		i++;
	}

	if (LinkStack_Size(stack) == 0 && s[i] == '\0')
	{
		printf("success!\n");
	}
	else
	{
		printf("faild : 栈中元素不为空\n");
	}

	LinkStack_Destory(stack);

	return;
}

void main07()
{
	const char* code = "#include <stdio.h> int main(){int a[4][4]; int (*p)[4]; p = a[0]; return 0;}";
	
	scanner(code);

	cout << "---------------------------" << endl;
	system("pause");
	return;
}

 其中,头文件 dm06_LinkStack.h 请参考资源下载中的《栈的链式存储与实现》资源。链接:https://download.csdn.net/download/bailang_zhizun/12670871。下同。

2、中缀转后缀表达式

中缀转后缀算法思路

1、遍历中缀表达式中的数字和符号 

2、如果是数字,则直接输出

3、对于符号:

        左括号:直接进栈 

        运算符号:与栈顶元素的进行优先级比较 

                若栈顶符号优先级低: 此运算符号进栈(默认栈顶元素是左括号,左括号优先级最低)

                若栈顶符号优先级高: 将栈顶符号弹出并输出,之后进栈

        右括号:将栈顶元素弹出并输出,直到匹配左括号(左括号也要弹出不输出)

4、遍历结束:

        成功:将桟中所有符号弹出并输出

代码实现:

#include "dm06_LinkStack.h"

//返回true: 是操作符
//返回false: 不是操作符
bool is_operator(const char c)
{
	if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')')
		return true;

	return false;
}

//top: 栈顶元素
//c: 需要比较的元素(字符串中的元素)
//返回true: 栈顶元素优先级低   ;
//返回false: 栈顶元素优先级高;
bool priority_compare(const char* top, const char c)
{
	bool ret = false;

	if (nullptr == top) return true;		//此时栈中没有元素,所以返回true

	switch (*top)
	{
	case '+':
	case '-':	if ('*' == c || '/' == c)	ret = true; break;
	case '*':
	case '/':	break;
	case '(':	ret = true; break;
	default: break;
	}
	return ret;
}

void infix_to_suffix(const char* s)
{
	LinkStack* stack = LinkStack_Create();
	if (nullptr == stack)	return;

	int i = 0;
	int ret = 0;
	while (s[i] != '\0')
	{
		char cur_c = s[i];

		if (!is_operator(cur_c))
		{
			cout << cur_c << " ";
		}
		else
		{
			if ('(' == cur_c)
			{
				//左括号 '(' 直接进栈
				LinkStack_Push(stack, (void *)(s+i));
			}
			else if (')' == cur_c)
			{
				//右括号:将栈顶元素弹出并输出,直到匹配左括号(左括号也要弹出不输出)
				char tmp = '\0';
				while (tmp = *(char *)LinkStack_Pop(stack))
				{
					if ('(' == tmp) break;
					
					cout << tmp << " ";
				}
			}
			else
			{
				char* top = (char *)LinkStack_Top(stack);

				if ( priority_compare(top, cur_c) )
				{
					LinkStack_Push(stack, (void *)(s+i));
				}
				else
				{
					LinkStack_Pop(stack);
					cout << *top << " ";
					LinkStack_Push(stack, (void *)(s + i));
				}
			}
		}
		i++;
	}

	while (LinkStack_Size(stack) > 0)
	{
		cout << *(char *)LinkStack_Pop(stack) << " ";
	}
	cout << endl;
}

void main08()
{
	const char* code1 = "8+(3-1)*5";		//suffix: 831-5*+
	const char* code2 = "5+4";				//suffix: 54+
	const char* code3 = "1+2*3";			//suffix: 123*+
	const char* code4 = "9+(3-1)*5+8/2";	//suffix: 931-5*82/++

	infix_to_suffix(code4);

	cout << "---------------------------" << endl;
	system("pause");
	return;
}

3、后缀表达式计算

后缀表达式计算思路:

1、遍历后缀表达式中的数字和符号

2、对于数字,直接进栈

3、对于符号:

        从桟中弹出右操作数

        从桟中弹出左操作数

        根据符号进行计算 

        将运算结果压入桟中

4、遍历结束:

        成功:桟中的唯一数字为计算结果

代码实现:

#include "dm06_LinkStack.h"

//根据不同的操作符号执行不同的操作
template<typename T>
T calclute(T ln, T rn, const char oper)
{
	T ret = 0;

	switch (oper)
	{
	case '+':	ret = ln + rn;	break;
	case '-':	ret = ln - rn;	break;
	case '*':	ret = ln * rn;	break;
	case '/':	(0 == rn) ? (ret = -1) : (ret = ln / rn);	break;
	default:	ret = -1; break;
	}
	return ret;
}

//多用函数!!!!!!!!!!!!!!!!!!!!!!!
int int_value(const char c)
{
	return c - '0';
}

void calculate_saffix_new(const char* s)
{
	LinkStack* stack = LinkStack_Create();
	if (nullptr == stack)	return;

	int i = 0;
	int ret = 0;

	while (s[i] != '\0')
	{
		const char tmp = s[i];

		if (!is_operator(tmp))
		{
			//1-1 是数字则直接压入桟中
			LinkStack_Push(stack, (void *)int_value(tmp));
		}
		else
		{
			int right = (int)LinkStack_Pop(stack);
			int left = (int)LinkStack_Pop(stack);
			int result = calclute<int>(left, right, tmp);

			LinkStack_Push(stack, (void *)result);
		}

		i++;
	}

	ret = (int)LinkStack_Pop(stack);
	cout << "Result: " << ret << endl;


	LinkStack_Destory(stack);

	return;
}

void main09()
{
	const char* code1 = "54+";				//value: 9
	const char* code2 = "123*+";			//value: 7
	const char* code3 = "831-5*+";			//value: 18
	const char* code4 = "931-5*82/++";		//value: 23

	calculate_saffix_new(code4);

	cout << "---------------------------" << endl;
	system("pause");
	return;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值