顺序栈(数组形式)的实现

news2024/11/17 23:30:22

🌈什么是栈?

1.抽象化具象:可以理解为一个细长的乒乓球筒,一端封闭,放球只能从另一端放入球,取出球时也只能从该端取出。先进的球最后出,后进的球最先出。
2.定义:栈是一种线性数据结构,栈中元素只能先进后出,最早进入的元素的位置叫做栈底,最后进入的元素的位置叫做栈顶。
3.实现方法:数组或链表。
4.问用数组和链式结构哪个更好?
数组方便访问元素、尾插尾删(空间不够就扩容);链表方便尾插(用一个tail指针记录尾节点),但不方便尾删(尾删需要遍历一遍链表找到尾节点的前一个节点)。如果非要用链表实现栈,则以头插头删的方式进行入栈和出栈最好,此时尾节点是栈底,头节点是栈顶。
在这里插入图片描述

🌈栈的基本操作

1.进栈:把新元素放入栈中,只能从栈顶一侧放入元素,新元素的位置是新的栈顶。
2.出栈:把元素从栈中弹出,只有栈顶元素才可出栈,出栈元素的前一个元素会成为新的栈顶。

🌈数组顺序栈的实现

🎈方式一:用两个指针分别指向栈底和栈顶

假设栈中每个元素储存的信息是包含年龄、身高、体重三个变量的结构体。

☀️1.定义声明部分

STACK_SIZE 4:最开始初始化开辟的空间大小,可以存放4个元素
STACK_INCREASE 2:如果空间不够用,则每次增加2个位置

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define STACK_SIZE 4
#define STACK_INCREASE 2
typedef struct {
	int age;
	int height;
	double weight;
}Student;
typedef struct {
	Student* base;
	Student* top;
	int stackSize;
}Stack;

☀️2.初始化与销毁栈

用malloc动态开辟STACK_SIZE个空间,有动态开辟函数则一定会紧跟着释放动态内存函数free,因此初始化和销毁放在一起

//初始化一个栈
int InitStack(Stack* S) {
	S->base = (Student*)malloc(sizeof(Student) * STACK_SIZE);
	//判断是否申请空间成功
	if (!S->base) exit(-1);
	S->top = S->base;
	S->stackSize = STACK_SIZE;
	return 1;
}
//销毁栈
int DestroyStack(Stack* S) {
	//判断栈是否存在
	if (!S->base) return 0;
	//如果存在,则free掉空间
	free(S->base);
	//栈相关信息还原
	S->base = S->top = NULL;
	S->stackSize = 0;
	return 1;
}

☀️3.进栈

先判断是否有进栈空间,没有的话用realloc扩容。又由于realloc可能异地开辟新空间,因此在用base指针接收realloc空间的起始位置后,top也要在base的基础上变化。

//进栈
int Push(Stack* S, Student* stu) {
	//判断是否有可进栈空间
	//1.没有空间,扩容
	if (S->top - S->base == S->stackSize) {
		S->base = (Student*)realloc(S->base,sizeof(Student) 
			               * (STACK_SIZE + STACK_INCREASE));
		if (!S->base) exit(-1);
		S->top = S->base + S->stackSize;
		S->stackSize += STACK_INCREASE;
	}
	//2.有空间,
	*S->top++ = *stu;//一定要注意加*,因为是将stu地址中的值拿出来赋给top指向的空间
	return 1;
}

☀️4.出栈

stu指针是用来储存出栈的元素的

//出栈,同时存储出栈的数据
int Pop(Stack* S, Student* stu) {
	//如果栈为空,返回0
	if (S->top == S->base)return 0;
	*stu = *(--S->top);
	return 1;
}

☀️5.打印、遍历、计数

//打印一组学生信息
void Print(Student* s) {
	//判断传入地址是否是空
	if (!s)return;
	//打印
	printf("年龄:%5d,身高:%5d,体重:%5lf\n", s->age, s->height, s->weight);
}
//遍历
int DisplayStack(Stack* S) {
	//判断栈是否是空的,为空不遍历
	if (!S)return;
	//如果栈不为空,逐个遍历
	Student* p = S->base;
	while (p != S->top) {
		Print(p);
		p++;
	}
	return 1;
}
//判断栈中有多少个数据
int Count(Stack* S) {
	return(S->top - S->base);
}

}

☀️测试入栈、出栈

测试入栈后

//测试入栈
void testPush(Stack* S, int n) {
	Student stu;
	for (int i = 0;i < n;i++) {
		printf("请输入学生的年龄、身高、体重:\n");
		scanf("%d %d %lf", &stu.age, &stu.height, &stu.weight);
		Push(S, &stu);
	}
	DisplayStack(S);
}
//测试出栈
void testPop(Stack* S,Student* stu) {
	Pop(S, stu);
	DisplayStack(S);
	printf("出栈的元素:");
	Print(stu);
	printf("此时栈中元素个数:%d",Count(S));

}
int main() {
	Stack S;
	InitStack(&S);
	testPush(&S,5);
	printf("**********\n");
	Student stu;
	testPop(&S, &stu);
	DestroyStack(&S);
	return 0;
}

☀️测试结果

在这里插入图片描述

🎈方式二:只用一个指针指向栈底

假设栈中每个元素只包含一个DataType类型的数据。

☀️1.定义声明部分(list.h)

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int DataType;
typedef struct Stack {
	DataType* a;
	int top;
	int capacity;
}Stack;

☀️2.初始化与销毁栈

void InitStack(Stack* ps) {
	assert(ps);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

void Destroy(Stack* ps) {
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

☀️3.进栈

void Push(Stack* ps,DataType x) {
	//传入指针必须合法
	assert(ps);
	//判断是否有足够空间入栈,空间不够则扩容
	if (ps->top == ps->capacity) {
		ps->capacity =ps->capacity== 0 ? 4 : ps->capacity * 2;
		DataType* tmp = realloc(ps->a, sizeof(Stack) * ps->capacity);
		if (tmp == NULL) {
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
	}
	ps->a[ps->top++] = x;
}

注意:
1.对ps中的capacity成员赋值时,如果初次入栈capacity为0时,给capacity赋值为4,否则就是原capacity的二倍。
2.动态申请内存时,为何要用realloc而不是malloc?
答:当realloc的第一个参数为空指针的话,其功能和malloc一样。扩容时不管栈为不为空,扩容时不管栈为不为空,都可以用该逻辑。

☀️4.出栈

void Pop(Stack* ps)  {
	//传入指针必须合法
	assert(ps);
	//栈内不能没有元素
	assert(ps->top>0);
	ps->top--;
}

☀️5.得到栈顶元素、判空

DataType GetTopVal(Stack* ps) {
	//传入指针必须合法
	assert(ps);
	//栈内不能没有元素
	assert(ps->top > 0);
	return ps->a[ps->top-1];
}

bool IsEmpty(Stack* ps) {
	assert(ps);
	return ps->top == 0;
}

☀️测试

测试思路:先入栈5个元素,再依次出栈并打印出出栈元素。出栈顺序应该与入栈顺序相反。

void test() {
	Stack st;
	InitStack(&st);
	Push(&st, 1);
	Push(&st, 2);
	Push(&st, 3);
	Push(&st, 4);
	Push(&st, 5);

	while (!IsEmpty(&st)) {
		printf("%d ", GetTopVal(&st));
		Pop(&st);
	}
	Destroy(&st);
}
int main() {
	test();
	return 0;
}

☀️测试结果

在这里插入图片描述

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

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

相关文章

【C++】map/multimap容器

1.map基本概念 2.map构造和赋值 #include <iostream> using namespace std;//map容器 构造和赋值 #include<map>//遍历输出map容器 void printMap(const map<int, int>& m) {for (map<int, int>::const_iterator it m.begin(); it ! m.end(); it)…

Ubuntu学习---跟着绍发学linux课程记录(第一部分)

文章目录 1、启动、关闭、挂起、恢复&#xff08;电源&#xff09;2、更多虚拟机操作2.1 电源设置2.2 硬件参数设置2.3 状态栏2.4 全屏显示 3、快照与系统恢复4、桌面环境5、文件系统6、用户目录7、创建目录和文件8、命令行&#xff1a;文件列表ls 9、命令行&#xff1a;切换目…

分布式任务调度框架

分布式任务调度框架 学习为主 文章目录 分布式任务调度框架一、概念二、架构图三、任务调度的流程定时触发任务是如何实现的&#xff1f;&#xff1a;使用时间轮实现定时执行任务逻辑:3.1 对到达now时间后的任务&#xff08;超出now 5秒外)3.2 对到达now时间后的任务&#xff0…

从代码设计看 Glide 之生命周期(上)

欢迎关注我的其他平台账号&#xff1a; 掘金&#xff1a;0xforee 个人博客&#xff1a;0xforee’s blog 微信公众号&#xff1a; 上期我们探索了一个具备核心功能的图片加载库该怎么设计。这一期我们来看看如何给这个图片加载库关联生命周期管理。 欢迎关注本系列其他文章&…

PPPoE连接无法建立的排查和修复

嗨&#xff0c;亲爱的读者朋友们&#xff01;你是否曾经遇到过PPPoE连接无法建立的问题&#xff1f;今天我将为你详细解析排查和修复这个问题的步骤。 检查物理连接 首先&#xff0c;我们需要确保物理连接没有问题。请按照以下步骤进行检查&#xff1a; - 检查网线是否插好&…

vue训练场练习props和$emit,实现大写输入,小写输出。

场景&#xff1a; 在vue官网训练场&#xff0c;使用训练场中的组件。 仅作为练习笔记&#xff0c;仅供产考。 App.vue 组件代码 方式一&#xff1a;Watch监听 方式二&#xff1a;input绑定

安卓系列机型--软扩容“system分区扩容”操作步骤解析 增加系统分区大小

感兴趣的友友要区别扩容的概念。软扩容与硬扩容。硬扩容指拆解手机字库。更换大容量的字库来达到硬扩容。例如864硬扩容为8256等等。所谓的软扩容指的是将系统默认的系统分区大小修改分区表增大分区。例如原来系统分区默认2G。修改分区表为3G大小。意义在于可以刷写有些需要扩容…

ssm+vue“魅力”繁峙宣传网站源码和论文

ssmvue“魅力”繁峙宣传网站源码和论文102 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身…

CH341 USB总线转接芯片

产品概述&#xff1a; CH341是一个USB总线的转接芯片&#xff0c;通过USB总线提供异步串口、打印口、并口以及常用的2线和4线等同步串行接口。 在异步串口方式下&#xff0c;CH341提供串口发送使能、串口接收就绪等交互式的速率控制信号以及常用的MODEM联络信号&#xff0c;用于…

css transition属性

如果想实现一些效果&#xff1a;比如一个div容器宽高拉伸效果&#xff0c;或者一些好看的有过渡的效果可以使用 定义和用法 transition 属性是一个简写属性&#xff0c;用于设置四个过渡属性&#xff1a; transition-property transition-duration transition-timing-func…

视频动态壁纸 Dynamic Wallpaper for Mac中文

Dynamic Wallpaper是一款Mac平台上的动态壁纸应用程序&#xff0c;它可以根据时间等因素动态切换壁纸&#xff0c;提供更加生动和多样化的桌面体验。 Dynamic Wallpaper包含了多个动态壁纸&#xff0c;用户可以根据自己的喜好选择和切换。这些动态壁纸可以根据时间等因素进行自…

目标检测后的图像上绘制边界框和标签

效果如图所示&#xff0c;有个遗憾就是CV2在图像上显示中文有点难&#xff0c;也不想用别的了&#xff0c;所以改成了英文&#xff0c;代码在下面了&#xff0c;一定要注意一点&#xff0c;就是标注文件的读取一定要根据自己的实际情况改一下&#xff0c;我的所有图像的标注文件…

大数据Flink(七十):SQL 动态表 连续查询

文章目录 SQL 动态表 & 连续查询 一、​​​​​​​SQL 应用于流处理的思路

【牛客网题目】反转链表

目录 描述 解题分析 描述 给定一个单链表的头结点pHead(该头节点是有值的&#xff0c;比如在下图&#xff0c;它的val是1)&#xff0c;长度为n&#xff0c;反转该链表后&#xff0c;返回新链表的表头。 数据范围&#xff1a; 0≤n≤1000 要求&#xff1a;空间复杂度O(1) &a…

UDP 广播

一、UDP 通信图解 UDP通信、本地套接字_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132523536?spm1001.2014.3001.5501 #include <sys/types.h> #include <sys/socket > ssize_t sendto(in…

macOS Sonoma 14beta 7(23A5337a)更新发布,附黑/白苹果系统镜像

系统介绍&#xff08;镜像请前往黑果魏叔官网下载&#xff09; 黑果魏叔8 月 31 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 14 开发者预览版 Beta 7 更新&#xff08;内部版本号&#xff1a;23A5337a&#xff09;&#xff0c;本次更新距离上次发布隔了 8 天。 …

Windows安装FFmpeg说明

下载地址 官网 Download FFmpeg Csdn ffmpeg安装包&#xff0c;ffmpeg-2023-08-28-git-b5273c619d-full-build.7z资源-CSDN文库 解压安装&#xff0c;添加环境变量 命令行输入ffmpeg 安装成功

typora使用

1.主题配置 先打开主题文件夹&#xff0c; 文件–>>偏好设置–>>外观–>>打开主题文件夹 1.1字体 修改字体需要修改css文件&#xff0c;确定当前所用主题&#xff0c;可以在typora菜单点击主题&#xff0c;看看当前勾选的是哪个主题&#xff0c;比如gith…

【狂神】Spring5笔记(1-9)

目录 首页&#xff1a; 1.Spring 1.1 简介 1.2 优点 2.IOC理论推导 3.IOC本质 4.HelloSpring ERROR 5.IOC创建对象方式 5.1、无参构造 这个是默认的 5.2、有参构造 6.Spring配置说明 6.1、别名 6.2、Bean的配置 6.3、import 7.DL依赖注入环境 7.1 构造器注入 …

【项目源码】一套基于springboot+Uniapp框架开发的智慧医院3D人体导诊系统源码

智慧医院3D人体导诊系统源码 开发语言&#xff1a;java 开发工具&#xff1a;IDEA 前端框架&#xff1a;Uniapp 后端框架&#xff1a;springboot 数 据 库&#xff1a;mysql 移 动 端&#xff1a;微信小程序、H5 “智慧导诊”以人工智能手段为依托&#xff0c;为…