从入门到深入理解——栈

一、栈的定义

        栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除 操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out) 的原则。

         压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

         出栈:栈的删除操作叫做出栈。出数据也在栈顶。

         

        栈可以这样理解:相信大家都对枪械有一定粗略的了解,咱们就用压子弹来帮助大家进行理解。

        压栈,大家可以想象为压子弹,子弹是一发一发往下压,那压栈就是在容量之内一个数据一个数据往下压。 

        出栈,大家可以理解为子弹射出的过程,即:最后压入的子弹先出。数据最后的先出。

        这就是压栈与出栈的过程,当然也可以压一个出一个,压多个出一个都可以,大家完全就可以把栈当作压子弹和子弹射出。

        好了,基础知识我们已经掌握,那么,我们该用什么结构来实现栈呢?

        通过单链表,双链表还是什么?大家可以在此处进行思考,稍后公布答案。

二、栈的实现 

        上文说到,我们要选择一个结构来实现栈。我们来一一分析一下:

        双链表全称为:带头双向循环链表,用它来实现可以吗?还用说吗?太过于完美当然可以,但是要用两个指针,同学们,一个指针已经困扰大家已久,那两个指针想必是大家不想经历的大恐怖。所以,这个时候咱们把它先列为备胎(实在没办法在想它(在特殊情况下能渣则渣)🐶)。

        单链表全称为:不带头单向不循环链表,大家想单链表找到尾元素麻烦吗?找一遍时间复杂度为O(N)。虽然我们可以反转一下,但是你愿意用吗?要是放两个指针还不如用双链表。

        这个时候怎么办?难道我们要使用双链表?不,绝对不行。这时,数组意外路过,对啊,我们可以用数组。

        数组每次使用前像顺序表一样判断是否开辟空间,在用一个变量size来记录尾,这样不就完美符合要求了。那说干就干吧。打开我们心爱的VS。

        2.1 栈的结构

                栈的结构,可以借鉴一下顺序表,要有数组、容量和栈定元素。结构如下:

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;		// 栈顶int capacity;  // 容量 
}Stack;

        2.2 栈的初始化 

                要进行初始化,大家想一想top赋值为多大合适,如果为0,那么0是不是为栈的第一个元素?是不是?答案是:是的。那么,有没有办法不叫top指向数组第一个元素?有,使top的值为-1即可。在后续代码中,我会将top初始化为0(别问,问就是top此时可以当顺序表中的size使用)。代码如下:

// 初始化栈
void StackInit(Stack* ps)
{assert(ps);ps->a = NULL;ps->capacity = ps->top = 0;
}

        2.3 栈的销毁

                在我们今后写代码一定要记住:只要你malloc,realloc一定要free,你创建了就一定要销毁。

                那我们创建了一个栈,那么我们一定要销毁。代码如下:

// 销毁栈 
void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);//此处记得释放ps指向的数组,不要写成ps!!!ps->a = NULL;ps->capacity = ps->top = 0;
}

                注意事项写在代码里了,一定要记住!!!

        2.3 栈元素的插入

void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->capacity == ps->top){int newcapacity = ps->capacity == 0 ? 4 : 2 * sizeof(ps->capacity);STDataType* newnode = (STDataType*)realloc(ps->a,newcapacity*sizeof(STDataType));if (newnode == NULL){perror("realloc fail");return;}ps->capacity = newcapacity;ps->a = newnode;}//这里之所以没有封装成一个接口,是因为这里只用一次,其余的都不使用ps->a[ps->top] = data;ps->top++;//这里也可以合二为一//ps->a[ps->top++] = data;
}

                此处要点与顺序表类似,就不过多强调。

        2.4 栈元素的删除

// 出栈 
void StackPop(Stack* ps)
{assert(ps);assert(ps->top > 0);ps->top--;
}

                此处代码过于简单,那么能不能不把这个封装了,直接写。其实你要是想这么干,你可以试一试,不过提醒一下:可能会出乱子。还是那句话:专业的事专业的人做。

        2.5 栈顶元素获取

// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}

                对于此代码最后的返回值可能会有人有疑问,我简单解释一下:   

        2.6 栈元素有效个数获取 

// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{assert(ps);return ps->top;
}

                上文说过top初始化为0时可当size来使用,代码简单,不过多解释。

        2.7 栈是否为空判断

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;
}

                注意点:必须包含头文件:stdbool.h。

三、代码总览

        Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;		// 栈顶int capacity;  // 容量 
}Stack;// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

        Stack.c

#include"Stack.h"// 初始化栈
void StackInit(Stack* ps)
{assert(ps);ps->a = NULL;ps->capacity = ps->top = 0;
}
// 入栈 
void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->capacity == ps->top){int newcapacity = ps->capacity == 0 ? 4 : 2 * sizeof(ps->capacity);STDataType* newnode = (STDataType*)realloc(ps->a,newcapacity*sizeof(STDataType));if (newnode == NULL){perror("realloc fail");return;}ps->capacity = newcapacity;ps->a = newnode;}//这里之所以没有封装成一个接口,是因为这里只用一次,其余的都不使用ps->a[ps->top] = data;ps->top++;//这里也可以合二为一//ps->a[ps->top++] = data;
}
// 出栈 
void StackPop(Stack* ps)
{assert(ps);assert(ps->top > 0);ps->top--;
}
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{assert(ps);return ps->top;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;
}
// 销毁栈 
void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);//此处记得释放ps指向的数组,不要写成ps!!!ps->a = NULL;ps->capacity = ps->top = 0;
}

        测试代码:test.c

#include"Stack.h"int main()
{Stack p;StackInit(&p);StackPush(&p, 1);StackPush(&p, 2);StackPush(&p, 3);StackPush(&p, 4);while (!StackEmpty(&p)){printf("%d ", StackTop(&p));StackPop(&p);}StackDestroy(&p);return 0;
}

四、例题

        既然明白了,那么来几道题巩固一下吧!

        例一:

        设栈S和队列 Q的初始状态均为空,元素 abcdepg 依次进入栈S。若每个元素出栈后立即进入队列 Q,且7个元素出队的顺序是 bdcfeag,则栈S的容量至少是:

        

        例二: 

        若元素a,b,c,d,e,f依次进栈,允许进栈、退栈操作交替进行,但不允许连续3次进行退栈操作,不可能得到的出栈序列是()。

        A. dcebfa                        B. cbdaef                        C.bcaefd                        D.afedcb

        例三: 

        元素 a,b,c,d,e依次进入初始为空的栈中,若元素进栈后可停留、可出栈,直到所有元素都出栈,则在所有可能的出栈序列中,以元素d开头的序列个数是

        好了,我们的学习到现在就结束了,如有疑惑可私信,也可在评论区留言。

完! 

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

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

相关文章

《2024年AI安全报告》:AIML工具使用量飙升594.82%

人工智能&#xff08;AI&#xff09;不仅仅是一种开拓性的创新技术&#xff0c;甚至已经成为一种常态&#xff0c;企业正在工程、IT营销、财务、客户服务等领域迅速采用AI和机器学习&#xff08;ML&#xff09;工具。但与此同时&#xff0c;他们必须平衡AI工具带来的诸多风险&a…

SpringBootWeb 篇-深入了解请求响应(服务端接收不同类型的请求参数的方式)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 请求响应概述 1.1 简单参数 1.2 实体参数 2.3 数组集合参数 2.4 日期参数 2.5 json 参数 2.6 路径参数 3.0 完整代码 1.0 请求响应概述 当客户端发送不同的请求参…

整除C++

题目&#xff1a; 思路&#xff1a; 循环遍历7(可以被 7 整除的正整数最小为7)~n&#xff0c;如果i%70&#xff0c;就说明i可以被7整除. 代码&#xff1a; #include<iostream> using namespace std; int n;//一个正整数 int main(){scanf("%d",&n);//输入…

OIDC 与 OAuth2.0学习

OpenID Connect (OIDC) 和 OAuth 2.0 是两种不同的协议&#xff0c;它们通常一起使用&#xff0c;但服务于不同的目的。下面是它们的 主要区别和联系&#xff1a; OAuth 2.0 OAuth 2.0 是一个授权框架&#xff0c;它允许第三方应用代表用户获取对服务器资源的有限访问权限。…

geoserver SQL注入、Think PHP5 SQL注入、spring命令注入

文章目录 一、geoserver SQL注入CVE-2023-25157二、Think PHP5 SQL注入三、Spring Cloud Function SpEL表达式命令注入&#xff08;CVE-2022-22963&#xff09; 一、geoserver SQL注入CVE-2023-25157 介绍&#xff1a;GeoServer是一个开源的地理信息系统&#xff08;GIS&#…

凡尔码安全巡检卡替代传统纸质记录卡

建筑行业、物业管理、医院等行业的安全巡检的记录方式通常以&#xff1a;1、纸质记录&#xff1a;巡检人员使用纸质巡检表格&#xff0c;手动填写巡检时间、巡检区域、巡检发现的问题以及处理情况。这种方式简单直接&#xff0c;但可能存在信息记录不完整、易丢失等问题。 2、电…

如何远程控制另一部手机:远程控制使用方法

在现今高科技的社会中&#xff0c;远程控制手机的需求在某些情境下变得越来越重要。不论是为了协助远在他乡的家人解决问题&#xff0c;还是为了确保孩子的在线安全&#xff0c;了解如何实现这一功能都是有益的。本文将为您简要介绍几种远程控制手机的方法及其使用要点。 KKVi…

共享旅游卡免费旅游真实反馈,有图有真相?

新伙伴体验&#xff0c;云南昆大丽6天5晚品质双人游&#xff0c;真实反馈&#xff01;珠海伙伴蔡总&#xff0c;加入千益畅行共享旅游卡团队&#xff0c;自己亲自体验“云南昆大丽6天5晚品质双人游”真实反馈&#xff0c;分享全程内容截图&#xff0c;无半点虚假&#xff01; …

1_1. Linux简介

1_1. Linux简介 文章目录 1_1. Linux简介1. 我们用linux来干嘛2. 计算机组成3. 操作系统4. Linux哲学思想5. Linux目录6. Linux分区类型 1. 我们用linux来干嘛 1. 大家都知道linux是一个操作系统&#xff0c;它是一个基础的软件&#xff0c;操作系统是硬件与应用程序的中间层。…

CLI举例:通过URL分类控制用户访问的网站

华为CLI举例&#xff1a;通过URL分类控制用户访问的网站 配置基于URL分类的URL过滤功能&#xff0c;可以实现对用户访问的某一类网站的控制。既可以是FW自带的预定义分类&#xff0c;也可以是管理员配置的自定义分类。 组网需求 如图1所示&#xff0c;FW作为企业网关部署在网络…

API低代码平台介绍3-异构数据源的数据查询功能

异构数据源的数据查询功能 在上一篇文章中我们通过API平台定义了一个最基本的数据查询接口&#xff0c;本篇文章我们将上升难度&#xff0c;在原有接口的基础上&#xff0c;实现在MySQL数据库和Oracle数据库同时进行数据查询。   什么场景会需要同时对异构数据源进行查询&…

C++map和set(个人笔记)

Cmap和set 1.set1.1set的使用1.1.1 set的模板参数列表1.1.2set的构造1.1.3set的迭代器1.1.4 set的容量1.1.5 set修改操作1.1.6 set的具体使用例子 2.map2.1map的使用2.1.1map的模板参数列表2.1.2map的构造2.1.3map的迭代器2.1.4 map的容量与元素访问2.1.5 map中元素的修改2.1.6…