【数据结构与算法】受限线性表 --- 栈

news2024/11/25 20:37:36

【数据结构与算法】受限线性表 — 栈


文章目录

  • 【数据结构与算法】受限线性表 --- 栈
  • 前言
  • 一、栈的基本概念
  • 二、栈的顺序存储
  • 三、栈的分文件编写
  • 四、栈的链式存储
  • 五、栈的应用案例-就近分配
  • 六、 中缀表达式转后缀表达式以及基于后缀表达式运算
  • 总结


前言

本篇文章就栈的基本概念,栈的顺序存储,栈的分文件编写,栈的链式存储,栈的应用案例-就近分配, 中缀表达式转后缀表达式以及基于后缀表达式运算。


一、栈的基本概念

概念:
首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。定义中说是在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底。
特性
它的特殊之处在于限制了这个线性表的插入和删除的位置,它始终只在栈顶进行。这也就使得:栈底是固定的,最先进栈的只能在栈底。
操作:
栈的插入操作,叫做进栈,也成压栈。(如下图所示)
栈的删除操作,叫做出栈,也有的叫做弾栈,退栈。(如下图所示)
在这里插入图片描述


二、栈的顺序存储

  • 初始化栈
#define MAX 1024

struct SStack
{
	void* data[MAX];	//栈的数组

	int m_size;	//栈的大小

};

typedef void* SeqStack;

//初始化栈
SeqStack init_SeqStack() {
	struct SStack* myStack = malloc(sizeof(struct SStack));

	if (myStack == NULL)
	{
		return NULL;
	}

	//初始化数组
	memset(myStack->data, 0, sizeof(void*) * MAX);

	//初始化栈大小
	myStack->m_size = 0;

	return myStack;
}
  • 入栈
//入栈
void push_SeqStack(SeqStack stack, void* data) {
	//入栈本质  --- 数组尾插
	if (stack == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}

	struct SStack* mystack = stack;

	if (mystack->m_size == MAX)
	{
		return;
	}

	mystack->data[mystack->m_size] = data;

	mystack->m_size++;
}
  • 出栈
//出栈
void pop_SeqStack(SeqStack stack)
{
	//出栈本质  ---数组尾删
	if (stack == NULL)
	{
		return;
	}
	
	struct SStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return;
	}
	mystack->data[mystack->m_size - 1] = NULL;

	mystack->m_size--;
}
  • 返回栈顶
// 返回栈顶
void* top_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return NULL;
	}
	struct SStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return NULL;
	}

	return mystack->data[mystack->m_size - 1];
}
  • 返回栈大小
// 返回栈大小
int size_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}
	
	struct SStack* mystack = stack;

	return mystack->m_size;
}
  • 判断是否是空栈
//判断栈是否为空
int isEmpty_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;	//返回-1 代表真  空栈
	}

	struct SStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return 1;
	}

	return 0;	//返回0 代表假 不是空栈

}
  • 销毁栈
//销毁栈
void destroy_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return;
	}

	free(stack);
	stack = NULL;
}
  • 测试代码
struct Person
{
	char name[64];
	int age;
};

void test01()
{
	//初始化栈
	SeqStack myStack = init_SeqStack();

	//创建数据
	struct Person p1 = { "aaa", 10 };
	struct Person p2 = { "bbb", 20 };
	struct Person p3 = { "ccc", 30 };
	struct Person p4 = { "ddd", 40 };
	struct Person p5 = { "eee", 50 };

	//入栈
	push_SeqStack(myStack, &p1);
	push_SeqStack(myStack, &p2);
	push_SeqStack(myStack, &p3);
	push_SeqStack(myStack, &p4);
	push_SeqStack(myStack, &p5);

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	while (isEmpty_SeqStack(myStack) == 0)
	{
		struct Person* p = top_SeqStack(myStack);
		printf("姓名:%s 年龄:%d\n", p->name, p->age);

		//出栈
		pop_SeqStack(myStack);
	}

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	//销毁栈
	destroy_SeqStack(myStack);
}

int main() {
	test01();

	return 0;
}

三、栈的分文件编写

  • seqStack.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define  MAX 1024

//struct SStack
//{
//	void * data[MAX];  //栈的数组
//
//	int m_Size; //栈大小
//};

typedef void* SeqStack;

//初始化栈
SeqStack init_SeqStack();

//入栈
void push_SeqStack(SeqStack stack, void* data);

//出栈
void pop_SeqStack(SeqStack stack);

//返回栈顶
void* top_SeqStack(SeqStack stack);

//返回栈大小
int size_SeqStack(SeqStack stack);

//判断栈是否为空
int isEmpty_SeqStack(SeqStack stack);

//销毁栈
void destroy_SeqStack(SeqStack stack);
  • seqStack.c
#include "seqStack.h"

struct SStack
{
	void* data[MAX];  //栈的数组

	int m_Size; //栈大小
};

//初始化栈
SeqStack init_SeqStack()
{
	struct SStack* myStack = malloc(sizeof(struct SStack));

	if (myStack == NULL)
	{
		return NULL;
	}

	//初始化数组
	memset(myStack->data, 0, sizeof(void*) * MAX);

	//初始化栈大小
	myStack->m_Size = 0;

	return myStack;
}
//入栈
void push_SeqStack(SeqStack stack, void* data)
{
	//入栈本质  --- 数组尾插
	if (stack == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}

	struct SStack* mystack = stack;
	if (mystack->m_Size == MAX)
	{
		return;
	}

	mystack->data[mystack->m_Size] = data;

	mystack->m_Size++;
}
//出栈
void pop_SeqStack(SeqStack stack)
{
	//出栈本质  --- 数组尾删
	if (stack == NULL)
	{
		return;
	}

	struct SStack* mystack = stack;

	if (mystack->m_Size == 0)
	{
		return;
	}

	mystack->data[mystack->m_Size - 1] = NULL;

	mystack->m_Size--;

}
//返回栈顶
void* top_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return NULL;
	}

	struct SStack* mystack = stack;

	if (mystack->m_Size == 0)
	{
		return NULL;
	}
	return mystack->data[mystack->m_Size - 1];
}
//返回栈大小
int size_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}

	struct SStack* mystack = stack;

	return mystack->m_Size;

}
//判断栈是否为空
int isEmpty_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;//返回-1代表真  空栈
	}

	struct SStack* mystack = stack;

	if (mystack->m_Size == 0)
	{
		return 1;
	}

	return 0; //返回0 代表 不是空栈

}
//销毁栈
void destroy_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return;
	}

	free(stack);
	stack = NULL;
}
  • 栈的顺序存储.c
# include "seqStack.h"
// 测试
struct Person
{
	char name[64];
	int age;
};

void test01()
{
	//初始化栈
	SeqStack myStack = init_SeqStack();

	//创建数据
	struct Person p1 = { "aaa", 10 };
	struct Person p2 = { "bbb", 20 };
	struct Person p3 = { "ccc", 30 };
	struct Person p4 = { "ddd", 40 };
	struct Person p5 = { "eee", 50 };

	//入栈
	push_SeqStack(myStack, &p1);
	push_SeqStack(myStack, &p2);
	push_SeqStack(myStack, &p3);
	push_SeqStack(myStack, &p4);
	push_SeqStack(myStack, &p5);

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	while (isEmpty_SeqStack(myStack) == 0)
	{
		struct Person* p = top_SeqStack(myStack);
		printf("姓名:%s 年龄:%d\n", p->name, p->age);

		//出栈
		pop_SeqStack(myStack);
	}

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	//销毁栈
	destroy_SeqStack(myStack);
}

int main() {
	test01();

	return 0;
}

四、栈的链式存储

  • 初始化

//节点结构体
struct stackNode {
	struct stackNode* next;

};

//栈的结构体
struct LStack
{
	struct stackNode pHeader;
	int m_size;
};

typedef void* LinkStack;

//初始化
LinkStack init_LinkStack() {
	struct LStack* myStack = malloc(sizeof(struct LStack));

	if (myStack == NULL)
	{
		return NULL;
	}
	myStack->pHeader.next = NULL;
	myStack->m_size = 0;

	return myStack;
}
  • 入栈
//入栈
void push_LinkStack(LinkStack stack, void * data)
{
	// 入栈本质 链表头插
	if (stack == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}
	struct LStack* mystack = stack;

	//将用户数据 取出前4字节用
	struct stackNode* New = data;

	//更新链表指向
	New->next = mystack->pHeader.next;
	mystack->pHeader.next = New;

	//更新链表长度
	mystack->m_size++;
}
  • 出栈
//出栈
void pop_LinkStack(LinkStack stack)
{
	//出栈本质链表头删
	if (stack == NULL)
	{
		return;
	}
	
	struct LStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return;
	}

	//更新指针指向  缓存第一个有数据的节点
	struct stackNode* pFirst = mystack->pHeader.next;

	mystack->pHeader.next = pFirst->next;

	//更新栈大小
	mystack->m_size--;

  • 返回栈顶元素
// 返回栈顶元素
void* top_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return NULL;
	}

	struct LStack* mystack = stack;
	if (mystack->m_size == 0)
	{
		return NULL;
	}
	
	return mystack->pHeader.next;
}
  • 返回栈的个数
// 返回栈的个数
int size_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}
	
	struct LStack* mystack = stack;

	return mystack->m_size;
}
  • 判断栈是否为空
//判断栈是否为空
int isEmpty_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}
	struct LStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return 1;
	}

	return 0;
}
  • 销毁
//销毁
void destroy_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return;
	}
	free(stack);
	stack = NULL;
}
  • 测试代码:
//测试
struct Person
{
	void* node;
	char name[64];
	int age;
};


void test01()
{
	//初始化栈
	LinkStack myStack = init_LinkStack();

	//创建数据
	struct Person p1 = { NULL,"aaa", 10 };
	struct Person p2 = { NULL,"bbb", 20 };
	struct Person p3 = { NULL,"ccc", 30 };
	struct Person p4 = { NULL,"ddd", 40 };
	struct Person p5 = { NULL,"eee", 50 };

	//入栈
	push_LinkStack(myStack, &p1);
	push_LinkStack(myStack, &p2);
	push_LinkStack(myStack, &p3); 
	push_LinkStack(myStack, &p4);
	push_LinkStack(myStack, &p5);

	printf("链式存储-- 栈的元素个数为: %d\n", size_LinkStack(myStack));

	while (isEmpty_LinkStack(myStack) == 0)	//栈不为空, 查看栈顶元素,出栈
	{
		struct Person* p = top_LinkStack(myStack);
		printf("姓名: %s 年龄:%d\n", p->name, p->age);

		//出栈
		pop_LinkStack(myStack);
	}

	printf("链式存储-- 栈的元素个数为: %d\n", size_LinkStack(myStack)); 

	destroy_LinkStack(myStack);

}

在这里插入图片描述


五、栈的应用案例-就近分配

几乎所有的编译器都具有检测括号是否匹配的能力,那么如何实现编译器中的符号成对检测?
如下字符串: 5+5*(6)+9/3*1)-(1+3(

算法思路
从第一个字符开始扫描
当遇见普通字符时忽略,
当遇见左括号时压入栈中
当遇见右括号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空

#include "seqStack.h"

/*
从第一个字符开始扫描
当遇见普通字符时忽略,
当遇见左括号时压入栈中
当遇见右括号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空
*/

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

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

void printError(char* str, char* errMsg, char* pos)
{
	printf("错误信息:%s\n", errMsg);
	
	printf("%s\n", str);
	//计算打印空格数量
	int num = pos - str;
	for (int i = 0; i < num; i++)
	{
		printf(" ");
	}
	printf("^\n");

}

void test01()
{
	char* str = "5+5*(6)+9/3*1-(1+3(";

	char* p = str;

	//初始化栈
	SeqStack myStack = init_SeqStack();

	while (*p != '\0')
	{
		//如果是左括号,入栈
		if (isLeft(*p))
		{
			//入栈
			push_SeqStack(myStack,p); 
		}

		//如果是又括号
		if (isRight(*p))
		{
			//栈中有元素 出栈
			if (size_SeqStack(myStack) > 0)
			{
				pop_SeqStack(myStack);
			}
			else
			{
				//右括号没用匹配到对应的左括号,立即停止,并报错
				printError(str, "右括号没有匹配到对应的左括号!", p);
				break;
			}
		}
		p++;
	}

	//遍历结束 判断是否有 左括号没有匹配到对应的右括号
	while (size_SeqStack(myStack) > 0)
	{
		printError(str, "左括号没有匹配到对应的右括号!", top_SeqStack(myStack));
		//出栈
		pop_SeqStack(myStack);
	}

	//销毁栈
	destroy_SeqStack(myStack);
	myStack = NULL;
}


int main()
{

	test01();
	return 0;
}

总结
当需要检测成对出现但又互不相邻的事物时可以使用栈“后进先出”的特性
栈非常适合于需要“就近匹配”的场合


六、 中缀表达式转后缀表达式以及基于后缀表达式运算

中缀转后缀算法:
遍历中缀表达式中的数字和符号:
对于数字:直接输出
对于符号:
左括号:进栈
运算符号:与栈顶符号进行优先级比较
若栈顶符号优先级低:此符号进栈
(默认栈顶若是左括号,左括号优先级最低)
若栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈
右括号:将栈顶符号弹出并输出,直到匹配左括号,将左括号和右括号同时舍弃
遍历结束:将栈中的所有符号弹出并输出
实例
5 + 4 => 5 4 +
1 + 2 * 3 => 1 2 3 * +
8 +( 3 – 1 ) * 5 => 8 3 1 – 5 * +

计算规则
遍历后缀表达式中的数字和符号
对于数字:进栈
对于符号:
从栈中弹出右操作数
从栈中弹出左操作数
根据符号进行运算
将运算结果压入栈中
遍历结束:栈中的唯一数字为计算结果
例如:8 3 1 – 5 * +


总结

到这里这篇文章的内容就结束了,谢谢大家的观看,如果有好的建议可以留言喔,谢谢大家啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2128968.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医院HISPACS存储备份 要求全周期保存

内蒙古赤峰医院 HIS PACS数据备份 &#xff0c;质量好可以满足15-30年存储的空间需求&#xff0c;用着放心

NIDS——suricata(三)

一、监控ICMP流量 1、ICMP流量特征 四大特征分别为&#xff1a;消息类型&#xff08;Type&#xff09;、代码&#xff08;Code&#xff09;、校验和&#xff08;Checksum&#xff09;、数据字段&#xff08;Data Field&#xff09;。这里我们使用 type消息类型。 ICMP 消息的类…

Cookie、Web Storage介绍

概述 Cookie、LocalStorage、SessionStorage、IndexDB这些作为浏览器的存储入口&#xff0c;也是经典的八股文了&#xff0c;本文再次冷饭热吃来介绍这些API&#xff0c;主要是因为在其他文章中看到了一些个人感觉有用的小知识点&#xff0c;所以在这记录一下&#xff0c;以便…

招加盟商视频怎么拍效果好?

一定没有人比我更适合分享这篇文章了&#xff0c;我自己曾经就是做宣传片的&#xff0c;而且还有一家酸奶品牌做全国招商。 我来分享下加盟招商视频怎么拍效果好&#xff1f; 一、定脚步 说个题外话&#xff0c;以前我做传媒公司的时候&#xff0c;很多客户找我做宣传片&…

Ubuntu系统Docker部署数据库管理工具DbGate并实现远程查询数据

文章目录 前言1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 前言 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数…

Windows安装字体的几种方式

1.选中你要安装的字体&#xff0c;然后右键&#xff0c;点击“为所有用户安装”&#xff0c;这样字体就会安装在C:\Windows\Fonts&#xff1b;如果你点了“安装”&#xff0c;那么就会安装在C:\Users\你电脑用户的名字\AppData\Local\Microsoft\Windows\Fonts。 像电脑自带的字…

strncmp函数的使用

目录 1.头文件 2.strncmp函数的使用 小心&#xff01;VS2022不可直接接触&#xff0c;否则&#xff01;没这个必要&#xff0c;方源面色淡然一把抓住&#xff01;顷刻炼化&#xff01; 1.头文件 strncmp函数的使用需要头文件 #include<string.h> 2.strncmp函数的使用 …

第一篇:教你轻松部署本地大模型(Ollama)

一、前言 搞研发的&#xff0c;想学习大模型的&#xff0c;很多都想本地部署一波&#xff0c;体验一下&#xff0c;部署是学习的第一步&#xff0c;我们不仅仅是要理论的巨人&#xff0c;还要成为实战的专家。不要恐惧&#xff0c;不要恐惧&#xff0c;不要恐惧&#xff0c;重…

【数学建模】典型相关分析

典型相关分析&#xff08;Canonical Correlation Analysis, CCA&#xff09;是一种统计方法&#xff0c;用于寻找两个多变量数据集之间的线性关系。这种分析方法可以用来衡量两个数据集之间的相关性&#xff0c;并找出能够最好地解释这种相关性的变量组合。 典型相关分析的基本…

Android系列基础知识总结

四大组件 Activity Activity生命周期 不同场景下Activity生命周期的变化过程 启动Activity&#xff1a; onCreate()—>onStart()—>onResume()&#xff0c;Activity进入运行状态。Activity退居后台&#xff1a; 当前Activity转到新的Activity界面或按Home键回到主屏&a…

WebGL系列教程二(环境搭建及着色器初始化)

目录 1 前言2 新建html页面3 着色器介绍3.1 顶点着色器、片元着色器与光栅化的概念3.2 声明顶点着色器3.3 声明片元着色器 4 坐标系(右手系)介绍5 着色器初始化5.1 给一个画布canvas5.2 获取WebGL对象5.3 创建着色器对象5.4 获取着色器对象的源5.5 绑定着色器的源5.6 编译着色器…

好看好听的小猪包扩音器,轻巧便携更好用,得胜E10上手

我们以前在学校、景区等地方&#xff0c;都见过教师、讲解员所使用的扩音器&#xff0c;这类设备大多做得笨重粗糙&#xff0c;而且音质一般&#xff0c;声音特别粗糙刺耳&#xff0c;对使用者和旁观者影响都不好。最近我发现了一款得胜E10扩音器&#xff0c;大大提升了这种便携…

HTTPX 与 AIOHTTP 与 Requests:选择哪个?

Python 有三个众所周知的库用于发送 HTTP&#xff08;超文本传输协议&#xff09;请求&#xff1a; HTTPX、AIOHTTP 和 Requests。 所有这些都有其独特的优点和缺点。 通常&#xff0c;您需要对同步 HTTP 请求使用 Requests&#xff0c;在需要同步和异步混合时使用 HTTPX&#…

场景解决方案丨突破成本限制,中小企业如何快速搭建后台管理系统

信息化时代下业务数据量激增&#xff0c;云计算、物联网、人工智能等技术的成本大幅度降低及普及&#xff0c;这些变化推动着市场需求发生改变&#xff0c;使数字化转型成为各行业的共同趋势。在这一背景下&#xff0c;大型企业利用其经济和技术优势巩固市场领导地位&#xff0…

养宠浮毛严重怎么清理?希喂、范罗士、IAM宠物空气净化器真实测评

宠物毛发不等于宠物浮毛&#xff1f;这可真是问到我的知识盲区了。养猫来我已经接受了目光所及之处都是猫毛&#xff0c;可以心平气和的打扫被猫毛占据的屋子。自认为是很勤快的人&#xff0c;家里也一直保持着干净整洁&#xff0c;结果上星期鼻子不舒服去看医生&#xff0c;告…

发现一个有趣的滑动组件样式,并给它实现了

最近在浏览网站的时候看到一个有趣的滑动组件的样式&#xff0c;觉得很有趣。一时有兴趣就去用代码实现了一下&#xff1a; 这里我就不从零开始实现了&#xff0c;借用 Material UI Slider 快速实现&#xff0c;基本上就是重新写样式覆盖。 Material UI 上的 Slider 组件默认样…

面试产品经理,怎样描述过往经历,才能让面试官印象深刻?

金三银四求职季&#xff0c;你是不是也有面试的冲动&#xff01;但面试并不是头脑一热就能取得好结果&#xff0c;在此之前&#xff0c;必须得有周全的准备&#xff0c;才能应对好面试官的“连环问”&#xff01; 所以&#xff0c;给大家分享这篇产品经理面试干货文章&#xf…

【数学建模】相关系数

第一部分&#xff1a;相关系数简介 总体与样本&#xff1a; 总体&#xff1a;指研究对象的全体&#xff0c;比如全国人口普查数据。样本&#xff1a;从总体中抽取的一部分个体&#xff0c;如通过问卷调查收集的学生数据。 皮尔逊相关系数&#xff1a; 总体皮尔逊相关系数&…

GPIO(General Purpose Input/Output)输入/输出

GPIO最简单的功能是输出高低电平&#xff1b;GPIO还可以被设置为输入功能&#xff0c;用于读取按键等输入信号&#xff1b;也可以将GPIO复用成芯片上的其他外设的控制引脚。 STM32F407ZGT6有8组IO。分别为GPIOA~GPIOH&#xff0c;除了GPIOH只有两个IO&#xff0c;其余每组IO有…

cesium.js 入门到精通(5)

现在我们看这个地图是 属于一个平面的 如果我们想把这个弄成 那种真实的高低起伏的 山脉 或者 其他的建筑显示 我们可以使用 添加地形 terrainProvider: new Cesium.CesiumTerrainProvider({url: "./terrains/gz",}), 这是一个参数 配置 地形 整体代码 <templa…