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

🌈什么是栈?

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) {//如果栈为空,返回0if (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.hqwc.cn/news/91638.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

爬虫逆向实战(二十四)--某鸟记录中心

一、数据接口分析 主页地址&#xff1a;某鸟记录中心 1、抓包 通过抓包可以发现数据接口是front/record/search/page 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现&#xff0c;请求参数是加密的 请求头是否加密&#xff1f; 通过查…

各种排序算法性能对比

C数据结构与算法 目录 冒泡排序 ​ 插入排序 ​ 选择排序 ​ 上图中最后一列为&#xff1a;nn*(n-1)/2 ​

北京收录2023开学了《乡村振兴战略下传统村落文化旅游设计》中建博后许少辉八一新书

北京收录2023开学了《乡村振兴战略下传统村落文化旅游设计》中建博后许少辉八一新书

Linux进程信号

文章目录 信号入门什么是linux信号&#xff1f;信号处理的常见方式查看系统定义的信号列表 产生信号通过终端按键产生信号调用系统函数向进程发送信号由软件条件产生信号硬件异常产生信号 阻塞信号阻塞信号相关常见概念信号在内核中的表示sigset_t信号操作函数sigprocmasksigpe…

设计师都去哪些网站找样机素材

在当今的设计领域&#xff0c;3D样机素材已经成为一个重要的领域。3D样机素材可以让设计师更好地展示他们的设计理念和概念&#xff0c;也可以帮助客户更好地理解设计。为了帮助设计师更容易地创建3D样机素材&#xff0c;以下是我推荐的10个易于使用的3D样机素材网站。 即时设…

uniapp使用wx.requirePrivacyAuthorize实现微信小程序隐私政策

一、前言 微信小程序官方出了一个公告《关于小程序隐私保护指引设置的公告》。不整的话&#xff0c;后果很多授权无法使用&#xff0c;详见《小程序用户隐私保护指引内容介绍》 。 二、操作流程 1、在 微信小程序后台的【设置】- 【服务内容与声明】 &#xff0c;设置好用户隐…

Linux的基本使用和Web程序部署(JavaEE初阶系列18)

目录 前言&#xff1a; 1.Linux 1.1Linux是什么 1.2Linux发行版 1.3Linux环境搭建 1.3.1环境搭建方式 1.3.2使用云服务器 1.4使用终端软件连接到Linux 1.4.1什么是终端软件 1.4.2使用Xshell登录主机 1.5Linux常用的命令 1.5.1ls 1.5.2cd 1.5.3pwd 1.5.4touch 1.…

CocosCreator3.8研究笔记(二)windows环境 VS Code 编辑器的配置

一、设置文件显示和搜索过滤步骤 为了提高搜索效率以及文件列表中隐藏不需要显示的文件&#xff0c; VS Code 需要设置排除目录用于过滤。 比如 cocoscreator 中&#xff0c;编辑器运行时会自动生成一些目录&#xff1a;build、temp、library&#xff0c; 所以应该在搜索中排除…

《存储IO路径》专题:IO块设备的创建

今天我们来一起学习一下Linux块设备层。它就像是一位大厨&#xff0c;为我们准备各种数据的饕餮盛宴。这个大厨非常厉害&#xff0c;不仅能够读取和写入数据&#xff0c;还能对数据进行各种复杂的操作&#xff0c;比如切割、合并、复制等等。那么&#xff0c;块设备层是如何实现…

SpringBoot初级开发--整体应用的统一性异常管理(7)

在整个系统中&#xff0c;通常会要求有统一性的异常抛出&#xff0c;统一的异常格式&#xff0c;统一的异常界面&#xff0c;而不是把整个堆栈错误信息抛出&#xff0c;这样对整个系统的安全性以及错误定位都非常不好&#xff0c;接下来我们紧接上一章的源码&#xff0c;加上统…

JVM之程序计数器和栈

Java虚拟机&#xff08;JVM&#xff09;是运行Java程序的关键组件&#xff0c;它负责将Java源代码转换为可执行的字节码&#xff0c;并在运行时管理内存、执行程序等。在JVM的内部&#xff0c;有许多重要的组成部分&#xff0c;如下图&#xff1a; 1. JVM程序计数器 程序计数器…

CSRF(跨站请求伪造)和SSRF(服务端请求伪造)漏洞复现:风险与防护方法

这篇文章旨在用于网络安全学习&#xff0c;请勿进行任何非法行为&#xff0c;否则后果自负。 环境准备 一、CSRF&#xff08;跨站请求伪造&#xff09; 示例&#xff1a;假设用户在银行网站A上登录并保持会话活动&#xff0c;同时他也在浏览其他网站。攻击者在一个不可信任…