栈的应用

根据之前链式栈的实现方法来实现栈的应用。https://blog.csdn.net/qq_41078889/article/details/103941387

目录

1.进制转换

2.括号匹配问题

3.四则运算表达式求值

1.进制转换

进制转换的过程中,我们用短除法得到余数,然后最后将余数倒过来写,这个和栈的后进先出的原理一致,所以可以使用栈实现。将每次得到的余数都压入栈中,当除完到最后时,将栈中的元素全部出栈,就会得到转换之后的数字。

typedef struct Node
{
	int data;
	struct Node* next;
}node;

//链栈的初始化
void InitStack(node** top)
{
	if ((*top = (node*)malloc(sizeof(node))) == nullptr)//为头结点开辟一个存储空间
	{
		return;
	}
	(*top)->next = nullptr; //将链栈的头结点指针域置为空
}

//判断链栈是否为空
bool StackEmpty(node* top)
{
	if (top->next == nullptr)
	{
		return true;
	}
	return false;
}

//进栈操作
int PushStack(node* top, int data)
{
	node* p = (node*)(malloc(sizeof(node)));
	if (p == nullptr)
	{
		printf("内存分配失败!\n");
	}
	else
	{
		p->data = data;
		p->next = top->next;
		top->next = p;
	}
	return 1;
}

//出栈操作
int PopStack(node* top)
{
	node* p = top->next;
	int tmp;
	if (p == nullptr)
	{
		printf("栈为空!\n");
	}

	{
		int val;
		top->next = p->next;
		val = p->data;
		tmp = val;
		free(p);   //释放p指向的结点
		return tmp;
	}
	
}

//取栈顶元素
int GetTop(node* top)
{
	node* p = top;
	if (StackEmpty(top))
	{
		printf("栈为空!\n");
	}
	if (p->next != nullptr)
	{
		p = p->next;
	}
	int data = p->data;
	return data;
}

//求表长操作
int StackLength(node *top)
{
	int count = 0;
	node *p = top;
	while (p->next != NULL)
	{
		count++;
		p = p->next;
	}
	return count;
}

//销毁链栈
void DestoryStack(node* top)
{
	node *p = top;
	node *q;
	while (p != nullptr)
	{
		q = p;
		p = p->next;
		free(q);
	}
}

//打印栈中元素
void StackPrint(node* top)
{
	node* p;
	if (StackEmpty(top))
	{
		printf("栈为空!\n");
	}
	printf("转换后为:");
	p = top;
	while (p->next != nullptr)
	{
		p = p->next;
		printf("%-3d", p->data);

	}
	printf("\n");
}

//进制转换
void atoB(node*top, int a, int b )
{
	assert(a != 0 && b != 0);

	int tmp1 = a % b;
	int tmp2 = a / b;
	while (tmp2 != 0 || tmp1 != 0)
	{
		PushStack(top, tmp1);
		tmp1 = tmp2 % b;
		tmp2 = tmp2 / b;
	}
}

int main()
{
	node *Ls;
	int a = 888;
	int b = 2;
	InitStack(&Ls);
	printf("将%d转换为%d进制\n", a, b);
	atoB(Ls, a, b);
	StackPrint(Ls);
	printf("栈的长度为:%d\n", StackLength(Ls));
	DestoryStack(Ls);
	return 0;
}

2.括号匹配问题

对于一个括号的序列,例如 {[()]} ,当我们从左向右遍历时,如果要确定右括号所对应的左括号是否存在,就需要将左括号记录下来,并且要做到最先记录的符号的最后被匹配,那么最合适的数据结构就是栈了。

遇到一个左括号,进栈,遇到一个右括号,判断其与栈顶元素是否匹配,如果不匹配,则返回0标识失败,当遇到’ \0’ 时遍历完括号串,这个时候判断栈是否为空,非空返回0,空返回1标识成功。

//判断左右符号是否匹配,匹配返回1,不匹配返回0
int Match(char a, char b)
{
	if (a == '(' && b == ')')
		return 1;
	else if (a == '[' && b == ']')
		return 1;
	else if (a == '{' && b == '}')
		return 1;
	else 
		return 0;
}

//括号匹配
int Matching(node*top,char expre[],int *data)
{
	for (int i = 0; expre[i] != '\0'; i++)
	{
		if (expre[i] == '(' || expre[i] == '[' || expre[i] == '{')//如果是左括号就进栈
			PushStack(top, expre[i]);
		else
			//StackEmpty十分重要,如果缺少当括号字符串缺少左括号时就会崩溃
			if (!StackEmpty(top) && Match(GetTop(top,data), expre[i]))
				PopStack(top,data);
			else 
				return 0;
	}
	if (StackEmpty(top))
		return 1;
	else
		return 0;
}

int main()
{
	node *Ls;
	int data;
	InitStack(&Ls);
	char expre[] = "{[(]}";
	if (Matching(Ls,expre,&data))
		printf("match success!");
	else
		printf("match fail~");
	DestoryStack(Ls);
    return 0;
}

3.四则运算表达式求值

首先我们先来看一下什么是后缀表达式,对于9+(3-1)*3+10/2,如果用后缀表达式表示就是9 3 1 - 3 * + 10 2 / +。

那么我们可以从上面的式子看出后缀表达式就是所有的符号都要在运算数字的后面出现。

现在我们看一下后缀表达式是如何计算出表达式的正确结果的。

后缀表达式计算的规则:从左到右遍历表达式的每个数字和符号,如果运到的是数字就进栈,运到的是符号就将栈顶的两个数字出栈,进行运算,运算结果入栈,一直到最终的获取结果。

1.首先初始化一个栈,用来对运算的数字进出使用。

2.然后将表达式中的的前三个数入栈(因为都是数字),9 3 1入栈。

3.接下来是“-”号,将栈顶两个元素出栈,1为减数,3为被减数。进行运算3-1=2,然后将2进栈。

4.数字3进栈。

5.后面是“*”,将3和2出栈相乘得6,在进行入栈。

6.6和9出栈,9+6=15,15入栈。

7.接下来是10  2 两个数字进栈。

8.接下来是“/”号,10/2=5,5入栈。

9.最后一个符号为+,5+15=20,入栈。

10.20出栈,栈变为空。

后缀表达式快速的解决了问题,那么后缀表达式是怎样来的???

呃,我们把平时标准的四则运算表达式9+(3-1)*3+10/2叫做中缀表达式,现在的问题就是如何将中缀表达式转化为后缀表达式9 3 1 - 3 * + 10 2 / +。

转化规则:从左到右遍历中缀表达式的数字和符号,如果是数字就输出成为后缀表达式的一部分。如果是符号的话,就判断它与栈顶符号的优先级,是右括号或者优先级低于栈顶符号,则让栈顶元素依次出栈并输出,将当前符号进站,一直到最终的后缀表达式为止。

接下来看具体的转化过程。

1.先初始化一个空栈,用来对符号进行出栈使用。

2.接下来向右遍历,第一个数字是9,输出。将+进栈。

3.第三个字符是“(”,因为它是左括号,还没有进行配对,进栈。

4.数字3输出,-号进栈,数字1输出。

5.后面的符号是),这时候需要和前面的(进行配对,所以栈顶元素依次出栈,到(输出。

6.接下来是数字3输出,下一个符号*的优先级高于栈中符号的优先级,因此不输出,*进栈。

7.之后是符号+,优先级低于栈顶元素,栈中的元素全部输出,将当前的+入栈。

8.数字10输出,/ 入栈。

9.最后一个数字2输出。

10.因为已经到了最后,将栈中的元素全部输出,得到最终的表达式9 3 1 - 3 * + 10 2 / +。

int Priority(char ch)	//判断操作符优先级
{
	switch (ch)
	{
	case '(':
		return 3;
	case '*':
	case '/':
		return 2;
	case '+':
	case '-':
		return 1;
	default:
		return 0;
	}
}

int midCal(node*num, node* operato,char*str)
{
	assert(str != nullptr);
	int i = 0;
	int tmp = 0;
	int num1 = 0;
	int num2 = 0;
	while (str[i] != '\0')
	{
		if (str[i] >= '0' && str[i] <= '9')	//判断表达式是否为数字,数字直接进栈
		{
			//将字符转化为数字
			tmp = tmp * 10 + str[i] - '0';
			i++;
			if (str[i] > '9' || str[i] < '0')
			{
				PushStack(num, tmp);	//操作数进栈
				tmp = 0;	
			}
		}
		else//运算符
		{
			//	1、操作符出栈不计算
			if (GetTop(operato) == '(' && str[i] == ')')	//直接出栈,不计算,栈顶为'(' ,表达式为')'
			{
				PopStack(operato);	//括号直接出栈
				i++;
				continue;	//继续下一次循环
			}
			//	2、操作符进栈
			if ((StackEmpty(operato) == true) 	//操作符栈为空
				|| (Priority(str[i]) > Priority(GetTop(operato))) 	//表达式操作符优先级 > 栈顶操作符优先级
				|| (GetTop(operato) == '(' && str[i] != ')')) 	//栈顶为'('  && 表达式当前值不为 ')'
			{
				PushStack(operato, str[i]);	//操作符进栈
				i++;
				continue;	//继续下一次循环
			}
			//	2、操作符出栈计算	
			if (Priority(str[i]) <= Priority(GetTop(operato)) 	//表达式操作符优先级 <= 栈顶操作符优先级
				|| (str[i] == '0' && StackEmpty(operato) != true) 	//表达式为空 && 操作符栈不为空
				|| str[i] == ')')	//表达式为')'
			{
				num1 = GetTop(num);	//数字栈顶出栈
				num2 = GetTop(num);	//数字栈第二个数字出栈
				switch (PopStack(operato))	
				{
				case '+':
					PushStack(num, num2 + num1);
					break;
				case '-':
					PushStack(num, num2 - num1);
					break;
				case '*':
					PushStack(num, num2 * num1);
					break;
				case '/':
					PushStack(num, num2 / num1);
					break;
				}
			}
		}
	}
	printf("Result = %d\n", GetTop(num));
}

 

LIN协议是一种用于低成本、低速率串行网络的通信协议,主要应用于车辆电子系统中。以下是关于LIN协议应用的一些分享。 首先,LIN协议在车辆电子系统中常用于连接各种低速率从设备,如门控制单元、窗户控制单元、座椅控制单元等。通过LIN总线,这些从设备可以与车辆主控制单元进行通信和控制。相比于其他高速率通信协议,如CAN协议,LIN协议更适用于这些低速率从设备的通信需求。 其次,LIN协议具有较低的成本优势。由于LIN协议使用的是普通的串行通信线路,不需要专用的硬件设备支持,因此整体成本相对较低。这使得LIN协议在汽车电子系统中得到广泛的应用,特别是在非关键性应用中。 此外,LIN协议还具有较低的功耗。由于LIN协议使用的是低速率通信,通信过程中的能耗相对较低。这在需要长时间运行的系统中尤为重要,例如关闭引擎后仍需要继续运行的车辆电子系统。 最后,LIN协议还支持多主从架构。这意味着在LIN总线上可以连接多个主设备和从设备,实现复杂的通信和控制功能。这种灵活性使得LIN协议在车辆电子系统中能够满足不同设备之间的通信需求。 总之,LIN协议在车辆电子系统中应用广泛,并且具有成本低、功耗低和多主从支持等优势。随着车辆电子化的发展,LIN协议在汽车行业中的应用前景将会更加广阔。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值