栈的基本概念

        栈是一种特殊的线性存储结构,是一种操作受到限制的线性表,特殊体现在两个地方:

        1、元素进栈出栈的操作只能从同一端完成,另一端是封闭的,通常将数据进栈叫做入栈,压栈等,出栈叫做弹栈、出栈等。

        2、栈中无论存数据还是取数据,都必须遵循“先进后出”的原则,即最先入栈的元素最先出栈。以上图为例,很容易可以看出是元素 1 最先入栈,然后依次是元素 2、3、4 入栈。在此基础上,如果想取出元素 1,根据“先进后出”的原则,必须先依次将元素 4、3、2 出栈,最后才能轮到元素 1 出栈。 

         

        由此我们可以对栈存储结构下一个定义:栈是一种“只能从一端存取元素,且存取过程必须遵循‘先进后出’原则”的线性存储结构。

        栈就类似于弹夹,只能从一端压入子弹,要取出子弹也只能对最上方的子弹进行操作,对应了栈先进后出,只能操作栈顶元素。

        上述提到,栈本质上是操作受到限制的线性表,线性表主要有两种:分别是数组和链表,所以栈有数组和链表两种实现方式。

        1.顺序存储的数组,优点: 节省空间,操作简单,学习成本较低,易于理解。缺点: 栈的大小从一开始就固定了,不利于动态扩容。

        2.非顺序存储的链表,优缺点:与数组栈正好相反。

        栈的核心操作包括出栈,入栈,判空,访问栈顶等,数组栈要比链表栈多一个判满操作。

数组栈

        下述代码实现了栈的基本操作,包括出栈、入栈、判空、判满、访问栈顶。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
//数组栈要提前设置最大容量
#define max 20typedef struct
{//以int类型数据为例int data[max];int index;//栈顶地址
}stack;
//压栈
bool stack_push(stack *sk,int data)
{//检测空指针if(sk == NULL)return false;//检测栈是否已满if(sk->index == max - 1)return false;//更新栈顶地址sk->index++;//数据入栈sk->data[sk->index] = data;return true;
}
//弹栈
bool stack_pop(stack *sk)
{//检测空指针if(sk == NULL)return false;//检测栈是否已空if(sk->index < 0)return false;//更新栈顶地址,无需将后面的数据置0,因为操作受限制,无法访问到后边的下标。sk->index--;return true;
}
//返回栈顶元素
int stack_top(stack *sk)
{//检测空指针if(sk == NULL)return false;//检测栈是否已空if(sk->index < 0)return false;return sk->data[sk->index];
}
bool stack_full(stack *sk)
{if(sk == NULL)return false;return sk->index == max - 1 ? true : false;
}
//查看栈是否已空
bool stack_empty(stack *sk)
{//检测空指针if(sk == NULL)return false;return sk->index < 0 ? true : false;
}int main(int argc, char const *argv[])
{//初始化栈,设置栈顶为-1stack sk;sk.index = -1;//逐个压栈,查看栈顶元素for (int i = 0; i < 20; i++){stack_push(&sk,i + 1);printf("%d ",stack_top(&sk));}printf("\n");stack_full(&sk) ? printf("已满\n") : printf("未满\n");stack_empty(&sk) ? printf("空\n") : printf("非空\n");stack_pop(&sk) ? printf("弹栈成功\n") : printf("弹栈失败\n");printf("此时栈顶元素为%d\n",stack_top(&sk));for (int i = 0; i < 20; i++){stack_pop(&sk) ? printf("弹栈成功\n") : printf("弹栈失败\n");}stack_empty(&sk) ? printf("空\n") : printf("非空\n");return 0;
}

 运行结果如下

        因为在中间进行了一次出栈,所以在循环中的最后一次弹栈时,栈已经空,进而弹栈失败。  

链表栈

        链表栈的存储方式是链表,所以在实现栈之前需要先定义链表的基本操作,再实现栈。

        下述代码实现了栈的基本操作包括判空、出栈、入栈、访问栈顶,链表栈不存在判满操作,只有数组栈需要,因为链表并不存在满的情况。

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>typedef struct node
{__uint32_t data;struct node *next;
}Node;typedef struct 
{struct node *top;
}stack;
//定义链表的头插法,首元结点就是栈顶,对应了入栈操作
void head_insert(Node *list,__uint32_t data)
{Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = data;new_node->next = list->next;list->next = new_node;
}
//定义链表的头删法,首元结点就是栈顶,对应了出栈操作
void head_delete(Node *list)
{Node *tmp = list->next;list->next = list->next->next;free(tmp);
}
//入栈,调用头插法
bool stack_push(stack *sk,__uint32_t data)
{if(sk->top == NULL)return false;head_insert(sk->top,data);return true;
}
//出栈,调用头删法
bool stack_pop(stack *sk)
{//判断是否为空栈if(sk->top->next == NULL)return false;head_delete(sk->top);return true;
}
//访问栈顶元素
int stack_top(stack *sk)
{//判断是否为空栈if(sk->top->next == NULL)return -1;return sk->top->next->data;
}
//栈判空,也就是链表判空
bool stack_empty(stack *sk)
{return sk->top->next == NULL ? true : false;
}
//摧毁整个链表
void list_destroy(Node *list)
{Node *current = list;while (current){Node *tmp = current;current = current->next;free(tmp);}}int main(int argc, char const *argv[])
{//定义一个链表Node *list = (Node *)malloc(sizeof(Node));list->data = 0;list->next = NULL;//定义栈顶指针stack *sk = (stack *)malloc(sizeof(stack));sk->top = list;stack_empty(sk) ? printf("空\n") : printf("非空\n");for (int i = 0; i < 10; i++){stack_push(sk,i + 1);printf("%d ",stack_top(sk));}printf("\n");printf("栈顶元素为%d\n",stack_top(sk));stack_empty(sk) ? printf("空\n") : printf("非空\n");stack_pop(sk);printf("栈顶元素为%d\n",stack_top(sk));for (int i = 0; i < 10; i++)stack_pop(sk) ? printf("弹栈成功\n") : printf("弹栈失败\n");stack_empty(sk) ? printf("空\n") : printf("非空\n");list_destroy(list);free(sk);return 0;
}

运行结果如下

        因为在中间进行了一次出栈,所以在循环中的最后一次弹栈时,栈已经空,进而弹栈失败。 

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

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

相关文章

【springboot】IDEA手动创建SpringBoot简单工程(无插件)

大致步骤 创建Maven工程 引入依赖 提供启动类 详细教程 创建Maven工程 修改pom.xml文件 添加父节点 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.3</…

独立开发第二周:构建、执行、规划

一 第二周的独立开发旅程落下帷幕。相较于第一周的适应&#xff0c;本周的核心词是“聚焦”与“执行”。 目标非常明确&#xff1a;在产品开发上取得进展&#xff1b;在个人工作节奏上&#xff0c;将上周初步形成的框架进行实践与固化。 同时&#xff0c;为至关重要的自媒体运营…

在YOLO-World中集成DeformConv、CBAM和Cross-Modal Attention模块的技术报告

在YOLO-World中集成DeformConv、CBAM和Cross-Modal Attention模块的技术报告 1. 引言 1.1 项目背景 目标检测是计算机视觉领域的核心任务之一,而YOLO(You Only Look Once)系列算法因其出色的速度和精度平衡而广受欢迎。YOLO-World是YOLO系列的最新发展,专注于开放词汇目标…

从UI设计到数字孪生实战应用:构建智慧金融的风险评估与预警平台

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;传统金融风控的 “滞后困境” 与数字孪生的破局之道金融风险的隐蔽性、突…

【Linux】权限相关指令

前言&#xff1a; 上两篇文章我们讲到了&#xff0c;关于Linux中的基础指令。 【Linux】初见&#xff0c;基础指令-CSDN博客【Linux】初见&#xff0c;基础指令&#xff08;续&#xff09;-CSDN博客 本文我们来讲Linux中关于权限中的一些指令 shell命令 Linux严格来说是一个操…

前端学习3--position定位(relative+absolute+sticky)

继上一篇&#xff0c;做下拉菜单的时候&#xff0c;涉及到了position&#xff0c;这篇就来学习下~先看下position在下拉菜单中的应用&#xff1a;一、关键代码回顾&#xff1a;/* 下拉菜单容器 */ .dropdown {position: relative; /* ➊ 关键父级 */ }/* 下拉内容&#xff08;默…

APP Inventor使用指南

APP Inventor使用指南一、组件介绍二、逻辑设计设计方法&#xff1a;设计实例&#xff08;参考嘉立创教程&#xff09;点击跳转 &#xff1a; app inventor&#xff08;点不开的话需要&#x1fa84;&#x1fa84;&#x1fa84;&#x1fa84;&#x1fa84;&#xff09; 一、组…

SpringAI实现保存聊天记录到redis中

redis相关准备添加依赖我利用redission来实现<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.37.0</version> </dependency>添加配置文件spring:redis:database: 5host: 127.0.0.1…

Unity中使用EzySlice实现模型切割与UV控制完全指南

引言 在Unity中实现3D模型的动态切割是一个常见的需求&#xff0c;无论是用于游戏特效、建筑可视化还是医疗模拟。本文将全面介绍如何使用EzySlice插件实现高效的模型切割&#xff0c;并深入探讨如何通过Shader Graph精确控制切割面的UV映射。 第一部分&#xff1a;EzySlice基…

【c++学习记录】状态模式,实现一个登陆功能

状态模式建议为对象的所有可能状态新建一个类&#xff0c; 然后将所有状态的对应行为抽取到这些类中。 原始对象被称为上下文 &#xff08;context&#xff09;&#xff0c; 它并不会自行实现所有行为&#xff0c; 而是会保存一个指向表示当前状态的状态对象的引用&#xff0c;…

Docker 搭建 Harbor 私有仓库

1 部署 Harbor 注意&#xff1a;docker、docker-compose、Harbor的版本是否适配&#xff0c;这里使用的版本如下表&#xff1a; Docker版本Docker Compose版本Harbor版本v19.09.8v1.29.2v2.8.2 1.1 安装 docker-compose # 下载 docker-compose 1.29.2 版本 curl -L "h…

C++类模板继承部分知识及测试代码

目录 0.前言 1.类模板基本使用 2.类模板继承 2.1类模板继承过程中的模板参数 情况1&#xff1a;父类非模板&#xff0c;子类为模板 情况2&#xff1a;父类模板&#xff0c;子类为非模板 情况3&#xff1a;父类模板&#xff0c;子类为模板 3.STL中的模板类分析 3.1STL中…

Laravel + Python 图片水印系统:实现与调试指南

前言 本系统通过 Laravel 作为前端框架接收用户上传的图片&#xff0c;调用 Python 脚本处理水印添加&#xff0c;最终返回处理后的图片。这种架构充分利用了 Laravel 的便捷性和 Python 图像处理库的强大功能。 一、Python 水印处理脚本 from PIL import Image, ImageEnhance …

【速通RAG实战:企业应用】25、从数智化场景看RAG:是临时方案,还是终局架构?

引言&#xff1a;RAG为何成为数智化场景的"必争之地"&#xff1f; 当ChatGPT在2023年掀起生成式AI浪潮时&#xff0c;一个矛盾逐渐凸显&#xff1a;大语言模型&#xff08;LLM&#xff09;能生成流畅文本&#xff0c;却常陷入"幻觉"&#xff08;虚构事实&a…

[Python] -实用技巧篇1-用一行Python代码搞定日常任务

在日常开发或数据处理过程中,我们常常为了一些简单的小任务写出数行代码。但实际上,Python 提供了大量强大且简洁的语法糖和标准库工具,让你用“一行代码”轻松搞定复杂操作。 本文将通过多个典型场景展示如何用“一行 Python 代码”高效完成常见任务。 一、文件操作:快速…

单细胞入门(1)——介绍

一、单细胞转录组测序流程介绍 单细胞测序能够探索复杂组织中单个细胞的不同生物学特性&#xff0c;帮助我们认识细胞与细胞之间的差异。这些检测方法有助于研究细胞谱系、细胞功能、细胞分化、细胞增殖和细胞应答&#xff0c;提升我们对复杂生物系统的理解&#xff0c;包括肿…

数据结构与算法之美:跳表

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞&#xff0c;关注&am…

从0设计一个短链接服务:如何实现尽可能短、可变长的短网址系统?

从 0 设计一个短链接服务&#xff1a;如何实现尽可能短、可变长的短网址系统&#xff1f; 在日常生活中&#xff0c;我们经常在短信、微博、广告营销中看到“短链接”&#xff0c;如&#xff1a; https://t.cn/EXaQ4xY https://bit.ly/3Yp9zJk相比冗长复杂的原始 URL&#xff0…

Microsoft Word 中 .doc 和 .docx 的区别

Microsoft Word 中 .doc 和 .docx 的区别 解释 Microsoft Word 中 .doc 和 .docx 文件格式的区别。这些格式都是 Word 处理文档的标准&#xff0c;但它们在结构、兼容性和功能上存在显著差异。下面我将详细说明。 1. 基本定义 .doc&#xff1a;这是 Microsoft Word 的旧格式&am…

Springboot aop面向切面编程

aop:面向切面编程&#xff0c;理解在一个流程中插入一个切面&#xff0c;这样切面方法会在指定位置执行能无影响的在某些方法前或者后插入一些动作springboot使用1.引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>sprin…