栈的链式结构以及中缀表达式转后缀表达式(逆波兰式)C语言实现

栈的数据结构就不多说了,后进先出,只有一个出口。最典型的应用就是中缀表达式转后缀表达式,因为计算机不能识别人类所认知的中缀表达式,逆波兰式多用于编译器实现、自然语言处理这些常用的领域。下面介绍中缀转后缀的具体过程。

比如1+3*(2+5)转化成后缀表达式是:1325+*+,转化算法为:

1、对于数字,直接输出
2、对于符号:
    左括号:进栈,不管栈中是否有元素
    运算符:若此时栈为空,直接进栈。
            若栈中有元素,则与栈顶符号运算符进行优先级比较:若新符号优先级高,则新符号进栈(默认左括号优先级最低)
            若新符号优先级低,将栈顶符号弹出并输出,之后使新符号进栈。
    右括号:不断将栈顶符号弹出并输出,直到匹配到左括号为止,再接着读取下一个符号,需注意,左右括号匹配即可,不需
            要将其输出。
3、遍历结束时,将栈中所有符号弹出并输出。

直接上代码。

linkstack.h头文件,其实就是栈的操作和节点类型声明

#pragma once
#ifndef _LINKSTACK_H_
#define _LINKSTACK_H_

typedef struct Node * pNode;
typedef struct Stack * LinkStack;

struct Node
{
	char data;
	pNode next;
};

struct Stack
{
	pNode top;
	int size;
};

LinkStack Create();//创建栈
int IsEmpty(LinkStack lstack);//判断栈的大小是否为空
int getSize(LinkStack lstack);//获取栈的大小
int Push(LinkStack lstack, char val);//元素入栈
pNode getTop(LinkStack lstack);//获取栈顶元素
pNode Pop(LinkStack lstack);//弹出栈顶元素
void Destory(LinkStack lstack);//销毁栈

#endif // !_LINKSTACK_H_

linkstack.cpp文件是栈的所有操作,创建、栈是否为空、压栈出栈等

#include
#include
#include"linkstack.h"

LinkStack Create()
{
	LinkStack lstack = (LinkStack)malloc(sizeof(struct Stack));
	if (lstack != NULL)
	{
		lstack->top = NULL;
		lstack->size = 0;
	}
	return lstack;
}

int IsEmpty(LinkStack lstack)
{
	if (lstack->top == NULL || lstack->size == 0)
		return 1;
	return 0;
}

int getSize(LinkStack lstack)
{
	return lstack->size;
}

int Push(LinkStack lstack, char val)
{
	pNode node = (pNode)malloc(sizeof(struct Node));
	if (node != NULL)
	{
		node->data = val;
		node->next = getTop(lstack);
		lstack->top = node;
		lstack->size++;
	}
	return 1;
}

pNode getTop(LinkStack lstack)
{
	if (lstack->size != 0)
	{
		return lstack->top;
	}
	return NULL;
}

pNode Pop(LinkStack lstack)
{
	if (IsEmpty(lstack))
		return NULL;
	pNode node = lstack->top;
	lstack->top = lstack->top->next;
	lstack->size--;
	return node;
}

void Destory(LinkStack lstack)
{
	if (IsEmpty(lstack))
	{
		free(lstack);
		printf("栈已空,不必销毁!\n");
		return;
	}
	do
	{
		pNode pTmp;
		pTmp = Pop(lstack);
		free(pTmp);
	} while (lstack->size > 0);
	printf("栈销毁成功!\n");
}

main.cpp逆波兰式转换实现的代码段

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include"linkstack.h"
/*
中缀转后缀,后缀表达式也叫逆波兰式,不需要括号,但是也能按照有括号的中缀表达式的正确优先级执行
栈在中缀转后缀过程中起到了关键作用。
1、对于数字,直接输出
2、对于符号:
	左括号:进栈,不管栈中是否有元素
	运算符:若此时栈为空,直接进栈。
			若栈中有元素,则与栈顶符号运算符进行优先级比较:若新符号优先级高,则新符号进栈(默认左括号优先级最低)
			若新符号优先级低,将栈顶符号弹出并输出,之后使新符号进栈。
	右括号:不断将栈顶符号弹出并输出,直到匹配到左括号为止,再接着读取下一个符号,需注意,左右括号匹配即可,不需
			要将其输出。
3、遍历结束时,将栈中所有符号弹出并输出。
*/

char buffer[256] = { 0 };
void Put(char ch)
{
	static int index = 0;
	buffer[index++] = ch;//把字符存进来,然后索引index后移
}
//优先级比较函数
int Priority(char ch)
{
	int ret = 0;
	switch (ch)
	{
	case '+':
	case '-':
		ret = 1;
		break;
	case '*':
	case '/':
		ret = 2;
		break;
	default:
		break;
	}
	return ret;
}

//判断字符是否是数字
int isNumber(char ch)
{
	return (ch >= '0' && ch <= '9');
}
//判断字符是否是运算符
int isOperator(char ch)
{
	return (ch == '+' || ch == '-' || ch == '*' || ch == '/');
}
//判断字符是否是左括号
int isLeft(char ch)
{
	return (ch == '(');
}
//判断字符是否是右括号
int isRight(char ch)
{
	return (ch == ')');
}
//中缀转后缀
int Transform(const char *str)
{
	//先创建一个栈
	LinkStack lstack = Create();//创建栈
	//创建完栈后就开始遍历字符串,如果是数字就输出,运算符入栈……
	int i = 0;
	while (str[i] != '\0')
	{
		//判断是否是数字
		if (isNumber(str[i]))
		{
			Put(str[i]);//存到buffer中
		}
		//判断是否是运算符
		else if (isOperator(str[i]))
		{
			//如果是运算符,则先判断栈是否为空
			if (!IsEmpty(lstack)) //如果栈不为空
			{
				//比较此运算符与栈顶符号的优先级
				while (!IsEmpty(lstack) && Priority(*((char *)getTop(lstack))) >= Priority(str[i]))
				{
					//如果栈顶符号优先级高,就将栈顶符号弹出并输出
					//直到栈顶符号的优先级小于此符号
					//或者栈空
					Put(Pop(lstack)->data);//将栈顶元素弹出,并输出到buffer中
				}
			}
			Push(lstack, str[i]);//如果栈为空,或者所有比当前符号优先级高的都已经弹出,则将新符号入栈
		}
		else if(isLeft(str[i]))//如果是左括号直接入栈
		{
			Push(lstack, str[i]);
		}
		else if (isRight(str[i]))//如果是右括号
		{
			//判断栈顶是不是左括号,如果不是,就弹出,直到匹配到左括号
			while (!isLeft(*((char *)getTop(lstack))))
			{
				//弹出栈顶元素存入到buffer中
				Put(Pop(lstack)->data);
				if (IsEmpty(lstack))
				{
					printf("没有匹配到左括号!\n");//如果直到栈空都没有发现左括号
					return -1;
				}
			}
			Pop(lstack);//直到循环结束,就匹配到了左括号,左括号出栈,但是不保存
		}
		else
		{
			printf("有不能识别的字符!\n");
			return -1;
		}
		i++;
	}
	//遍历结束
	if (str[i] == '\0')
	{
		//遍历结束后将栈中所有元素弹出
		while (!IsEmpty(lstack))
		{
			if (getTop(lstack)->data == '(') //如果栈中还有左括号,证明缺少括号
			{
				printf("有没匹配的“(”,缺少“)”!\n");
				return -1;
			}
			Put(Pop(lstack)->data);
		}
	}
	else
	{
		printf("遍历没有完成!\n");
	}
	return 1;
}

int main()
{
	char str[1024] = { 0 };
	printf("请输入四则表达式:\n");
	scanf("%s", str);

	if (Transform(str) == -1)
		printf("遍历中出现错误,无法完成转换!\n");
	else
		printf("转化后的表达式是:%s\n", buffer);
	system("pause");
	return 0;
}

以上就是逆波兰式生成的代码。至于逆波兰式如何计算,稍后有时间再写吧。

你可能感兴趣的:(数据结构,C语言)