栈(简单介绍及其应用)

栈的定义

栈是一种只能在一端进行插入或者删除操作的线性表。表中允许插入,删除操作的一端叫做栈顶,另一端叫栈底。在这里插入图片描述
但栈没有数据元素时称为空栈,插入数据操作为进栈或入栈,删除数据操作为出栈或退栈。
栈的特点为后进先出,栈也成为后进先出表。如存入一组数据,顺序为1, 2, 3, 4,则出栈时顺序为4, 3, 2, 1 。

顺序栈

顺序栈采用顺序存储结构进行存储,分配一块连续的存储空间来存放栈中元素,并用一个变量(如int类型的top)指向当前的栈顶元素。

代码实现:

  • 声明顺序栈的类型SqStack(最大个数不超过MaxSize):
typedef struct{
	int data[MaxSize];
	int top;
}SqStack;
  • 初始化栈 //这时可以把top值赋值为-1,表示此栈为空
void InitStack(SqStack*& s) {
	s = (SqStack*)malloc(sizeof(SqStack));  //分配空间,首地址放在s中
	s->top = -1;							//栈顶指针为-1;
}
  • 销毁栈(释放顺序栈占用的空间)
void DestroyStack(SqStack* &s) {
	free(s);
}
  • 进栈
bool Push(SqStack*& s, int e) {
	if (s->top == MaxSize - 1) return false;	//判断是否栈满
	s->top++;
	s->data[s->top] = e;
	return true;
}
  • 出栈
bool Pop(SqStack*& s, int e) {
	if (s->top == -1) return false;		//栈为空
	e = s->data[s->top];				//取出栈顶元素
	s->top--;
	return true;
}

链栈

链栈一般采用带头结点的单链来实现,相比顺序栈,链栈的优点就是不存在栈满溢出的问题。
在进行插入和删除操作时,采用头插法进行插入,删除即删除首结点,在更换头结点的下一个结点。

代码如下:

  • 声明:
typedef struct node {
	int e;
	node* next;
};
  • 初始化
void InitStack(node* &s) {
	s = (node*)malloc(sizeof(node));	//栈顶指针为-1;
	s->next = NULL;						//栈空
}
  • 销毁栈
void DestroyStack(node* &s) {
	node* pre = s, * p = s->next;		//pre为头结点, p为首结点
	while (p != NULL) {
		free(p);
		pre = p;						//更换头结点的位置,仍用pre指向头结点
		p = pre->next;					//p指向后一个结点,仍用p指向首结点
	}
	free(pre);
}
  • 进栈
void Push(node*& s, int e) {
	node* p = (node*)malloc(sizeof(node));
	p->e = e;								//存入数据
	p->next = s->next;						//让p连接后面的数据结点
	s->next = p;							//p成为新的首结点
}

  • 出栈
bool Pop(node*& s, int e) {
	node* p = s->next;						//p为首结点
	if (p == NULL) return false;			//栈空
	e = p->e;								//取值
	s->next = p->next;						//删除首结点
	free(p);								//释放被删除结点的储存空间
	return true;
}

栈的简单运用

输入包含±*/四种运算,(含有()括号的合法算术表达式,且操作数为多位整数,并计算其值,表达式以#开始,并以#结束)。

  1. 创建两个栈,一个运算符栈来存储运算符,一个数字栈来存储数字。

  2. 对一个输入的式子进行遍历,例如给一个字符数组str[100]赋值为 #(4+3)*12#

  3. 对str遍历
    (1)第一为#存入运算符栈;
    (2)遍历第二为‘(’,根据符号优先级,‘(’比‘#’优先级高,应该压入运算符栈;
    (3)再第三次遍历为数字4,压入数字栈,并再压入一个‘|’来隔开下一位数字;
    (4)第四次为‘+’,根据符号优先级,应该入运算符栈;
    (5)第五压入数字3进数字栈,压入分隔符’|’;
    (6)第六为‘)’,根据优先级比‘+’低,不应该入栈【判断是否运算符栈顶为‘(’,若是,把‘(’弹出,并遍历下一位str数组元素】,并且需要在数字栈中弹出两个数字进行运算【取出数字时,是一个顺序颠倒的字符数组,需要进行转化,如若取出为“|21”需转化为数字12】,在将运算结果压入数据栈,在压入一个分隔符’|‘;
    (7)第七为元素为‘ * ’,比栈顶’#‘优先级高,入栈;
    (8)第八为数字12,入栈,此时栈内数据为 |21|7
    (9)最后是#,优先级最低,在数字栈弹出两个数字,运算符栈弹出*,进行计算,压入计算结果和分隔符;

  4. 将数字栈中的结果弹出,得到的是字符数组 |48,转化为int类型84;【转化方法如下代码】

  5. 了解运算符的优先级,参考下面代码的Panduan()函数;

    可将数字栈的数据元素类型改为int类型,这样就无需进行字符数组反转再转化。也正由于栈的后进先出的特点,才需要对此问题进行处理。

参考代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
const int MaxSize = 100;

typedef struct linknode {
	char data[MaxSize];
	int top;
};

void InitStack(linknode* &s) {
	s = (linknode*)malloc(sizeof(linknode));
	s->top = -1;
}

bool Push(linknode* &s, char e) {
	if (s->top == MaxSize-1) {
		return false;
	}
	s->top++;
	s->data[s->top] = e;
	return true;
}

bool Pop(linknode*& s, char &a) {
	if (s->top == -1) {
		return false;
	}
	a = s->data[s->top];
	s->top--;
	return true;
}

char GetTop(linknode* s) {
	return s->data[s->top];
}

int PanDuan(char a, char b) { //-1表示a小于b  1表示a大于b  0表示相等
	if (a == '+') {
		if (b == '#' || b == '+' || b == '-' || b == '(') return 1;  
		else if (b == '/' || b == '*' || b == ')') return -1;
	}
	if (a == '-') {
		if (b == '#' || b == '-' || b == '+' || b == '(') return 1;
		else if (b == '/' || b == '*' || b == ')') return -1;
	}
	if (a == '*') {
		if (b == '+' || b == '-' || b == '*' || b == '/' || b == '(' || b == '#') return 1;
		else if (b == ')') return -1;
	}
	if (a == '/') {
		if (b == '+' || b == '-' || b == '*' || b == '/' || b == '(' || b == '#') return 1;
		else if (b == ')') return -1;
	}
	if (a == '(') {
		/*if (b == '-' || b == '+' || b == '*' || b == '/' || b == ')') return 1;
		else if (b == '(' || b == '#') return 1;*/
		return 1;
	}
	if (a == ')') {
		if (b == '-' || b == '+' || b == '*' || b == '/' || b == '(') return -1;
		else if (b == ')'|| b == '#') return 0;
	}
	if (a == '#') {
		if (b == '-' || b == '+' || b == '*' || b == '/' || b == '(' || b == ')') return -1;
		else if (b == '#') return 0;
	}
}

void rev(char str[], int i) {
	int s = 0, e = i;
	char temp;
	while (s < e) {
		temp = str[s];
		str[s] = str[e];
		str[e] = temp;
		s++;
		e--;
	}
}

int ExpEvaluation(char str[]) {
	int result;  //结果
	char Rch[10] = "";  //结果字符数组
	linknode* dataStack; //数字栈
	linknode* optrStack; //运算符栈
	InitStack(dataStack);
	InitStack(optrStack);
	int index = 0; //字符串索引
	Push(optrStack, str[index]);
	index++;
	while (str[index] != '\0' || GetTop(optrStack) != '#') {
		if ('0' <= str[index] && str[index] <= '9') {      //数字直接进入数字栈
			Push(dataStack, str[index]);
			index++;
			if ('0' <= str[index] && str[index] <= '9') continue;
			else Push(dataStack, '|');  //用|隔开每一个数字
		}
		else {
			if (PanDuan(str[index], GetTop(optrStack)) >= 0) {  // a比栈顶的运算符优先级大
				Push(optrStack, str[index]);
				index++;
			}
			else {
				char ch;
				if (optrStack->data[optrStack->top] == '(') {
					Pop(optrStack, ch);
					index++;
					continue;
				}
				char a[10] = {}, b[10] = {}, op;
				Pop(optrStack, op);
				int a1, b1;
				int flag = 0;
				int i = 0;
				Pop(dataStack, ch);
				while (1) {
					if (GetTop(dataStack) == '|' || dataStack->top == -1) break;
					Pop(dataStack, a[i]);
					i++;
				}
				rev(a, i - 1);
				char str[10]; //将a,b转化为字符串
				sscanf(a, "%s", str, _countof(str));
				sscanf(str, "%d", &a1);  //将a间接转化为数字
				Pop(dataStack, ch);
				i = 0;
				while (1) {
					if (GetTop(dataStack) == '|' || dataStack->top == -1) break;
					Pop(dataStack, b[i]);
					i++;
				}
				rev(b, i - 1);
				sscanf(b, "%s", str, _countof(str));
				sscanf(str, "%d", &b1);  //将b间接转化为数字
				
				i = 0;
				switch (op)
				{
				case '-': result = b1 - a1;
					snprintf(Rch, sizeof(Rch), "%d", result);
					for (int i = 0; i < strlen(Rch); i++) {
						Push(dataStack, Rch[i]);
					}
					Push(dataStack, '|');
					break;
				case '+': result = b1 + a1;
					snprintf(Rch, sizeof(Rch), "%d", result);
					for (int i = 0; i < strlen(Rch); i++) {
						Push(dataStack, Rch[i]);
					}
					Push(dataStack, '|');
					break;
				case '*': result = a1 * b1;
					snprintf(Rch, sizeof(Rch), "%d", result);
					for (int i = 0; i < strlen(Rch); i++) {
						Push(dataStack, Rch[i]);
					}
					Push(dataStack, '|');
					break;
				case '/': result = b1 / a1;
					snprintf(Rch, sizeof(Rch), "%d", result);
					for (int i = 0; i < strlen(Rch); i++) {
						Push(dataStack, Rch[i]);
					}
					Push(dataStack, '|');
					break;
				}
			}
		}
	}
	char dataStackTop; //数字栈栈顶的‘|’
	Pop(dataStack, dataStackTop);  //去掉‘|’
	int i = 0;
	while (dataStack->top != -1) {
		Pop(dataStack, Rch[i]);
		i++;
	}
	rev(Rch,i-1);
	sscanf(Rch, "%d", &result);
	return result;
}

int main() {
	printf("输入包含+-*/四种运算,(表达式以#开始,并以#结束):\n");
	char str[MaxSize];
	int result = 0;
	scanf_s("%s", str);
	result = ExpEvaluation(str);
	printf("\n结果为:%d", result);
}

测试结果:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Davin Sun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值