【数据结构】详解栈

今天我们主要来了解栈!如果对知识点有模糊,可翻阅以往文章哦!

个人主页:小八哥向前冲~-CSDN博客

所属专栏:数据结构【c语言版】_小八哥向前冲~的博客-CSDN博客

c语言专栏:c语言_小八哥向前冲~的博客-CSDN博客

值得注意的是,如果你十分了解顺序表和链表,今天这期会很轻松哦!

哈哈哈哈!当然,这期也能检测你对顺序表和链表的理解!一起看看吧!

目录

栈的定义

顺序表和链表的比较

栈的实现--顺序表

初始化

栈为空的判定

入栈

出栈

销毁

栈顶数据

数据个数

题目操练:配括号问题

栈的实现--链表

栈为空的判定

入栈

出栈

销毁

栈顶数据

数据个数

码源--栈(顺序表)

码源--栈(链表)


栈的定义

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

上图理解一下:

注意:遵循后进先出的原则!

知道了这个原则,我们来巩固一下:

1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出 栈的顺序是( )。

A .12345ABCDE

B.EDCBA54321

C.ABCDE12345

D.54321EDCBA

2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()

A.1,4,3,2

B.2,3,4,1

C.3,1,4,2

D.3,4,2,1

显然:1.B      2.C

相信第一题不难,我们解释一下第二题:看到C选项,1,2,3进栈后,3出栈,而第二次出栈的只能是2或4,不可能是1,所以C错误!

了解了栈的概念,我们实现这个栈是使用顺序表还是链表呢?

  • 如果是顺序表的话,我们的栈顶应该要在数组末尾!如果在数组头部的话,数据进栈时还需要挪动其余数据以便数据的存入!效率很低
  • 如果是链表的话,我们的栈顶要在链表的头入栈时,头插即可!如果栈顶在链表尾部的话,虽然入栈尾插即可,但需要遍历,效率低,那么这时就需要使用双链表!

综上所述,我们栈使用顺序表较好!(两种都实现看看)

上图看看它们:

为了更好透彻了解顺序表和链表,我们将它们比较看看!

顺序表和链表的比较

图文更加直观:

这里的缓存利用率不做过多解释,详情见:https://www.cnblogs.com/yungyu16/p/13054874.html

栈的实现--顺序表

既然是要在顺序表基础上实现栈,那么就要实现顺序表和栈的基本框架。

(单链表若有不懂的知识点,可见:通讯录的实现(顺序表版本)-CSDN博客)

stack.h文件--包含各种需要的函数

栈里面的变量:top表示栈顶下标,capacity表示栈空间。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDateType;
//栈
typedef struct stack
{STDateType* a;int top;int capacity;
}ST;//栈的初始化和销毁
void STInit(ST* p);
void STDestroy(ST* p);
//入栈,出栈
void STpush(ST* p,STDateType x);
void STpop(ST* p);
//栈顶的数据
STDateType STtop(ST* p);
//栈的数据个数
int STsize(ST* p);
//判空
bool STEmpty(ST* p);

接下来我们一一实现!

初始化

我们要将栈中各个变量进行初始化。

void STInit(ST* p)
{assert(p);p->a = NULL;p->capacity = 0;p->top = 0;
}

栈为空的判定

我们在实现这个函数时,很多人会用 if语句来判断是否为空,但我们仔细一想,可以好好优化一下代码!

//判空
bool STEmpty(ST* p)
{assert(p);return p->top == 0;
}

入栈

经过我们刚刚的分析,入栈要在数组尾部!记得每次入栈需要判断空间是否够用哦!

void STpush(ST* p, STDateType x)
{assert(p);if (p->top == p->capacity){int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));if (tmp == NULL){perror("realloc failed!");return;}p->a = tmp;p->capacity = newcapacity;}p->a[p->top++] = x;
}

出栈

入栈要在尾部,出栈也要在尾部,后进先出的原则要时刻记住!

需要注意的是:当栈为空时,数据出不了栈!所以我们先需要判断是否为空!

void STpop(ST* p)
{assert(p);//出栈的话要判断一下空的情况assert(!STEmpty(p));p->top--;
}

销毁

我们既然用了开辟内存函数,当我们不使用栈时,要将空间释放掉!

void STDestroy(ST* p)
{assert(p);free(p->a);p->capacity = p->top = 0;
}

栈顶数据

在访问栈顶数据时,我们也要先判断栈是否为空,否则当栈为空时,访问栈顶数据便会越界访问!

//栈顶的数据
STDateType STtop(ST* p)
{assert(p);//出栈的话要判断一下空的情况assert(!STEmpty(p));return p->a[p->top-1];
}

数据个数

//栈的数据个数
int STsize(ST* p)
{assert(p);return p->top;
}

题目操练:配括号问题

既然我们已经实现的栈,我们来应用一下吧!

题目:详情--20. 有效的括号 - 力扣(LeetCode)

思路:

遍历数组,当是左括号时("(","{","】")时就入栈,当不是左括号时就出栈比较,直到遍历完成!

这样听是不是很简单呢?当然里面没有栈,我们需要将栈创建一下!

代码:

typedef char STDateType;
//栈
typedef struct stack
{STDateType* a;int top;int capacity;
}ST;//栈的初始化和销毁
void STInit(ST* p);
void STDestroy(ST* p);
//入栈,出栈
void STpush(ST* p,STDateType x);
void STpop(ST* p);
//栈顶的数据
STDateType STtop(ST* p);
//栈的数据个数
int STsize(ST* p);
//判空
bool STEmpty(ST* p);
//栈的初始化和销毁
void STInit(ST* p)
{assert(p);p->a = NULL;p->capacity = 0;p->top = 0;
}
void STDestroy(ST* p)
{assert(p);free(p->a);p->capacity = p->top = 0;
}
//入栈,出栈
void STpush(ST* p, STDateType x)
{assert(p);if (p->top == p->capacity){int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));if (tmp == NULL){perror("realloc failed!");return;}p->a = tmp;p->capacity = newcapacity;}p->a[p->top++] = x;
}
void STpop(ST* p)
{assert(p);//出栈的话要判断一下空的情况assert(!STEmpty(p));p->top--;
}
//栈顶的数据
STDateType STtop(ST* p)
{assert(p);//出栈的话要判断一下空的情况assert(!STEmpty(p));return p->a[p->top-1];
}
//栈的数据个数
int STsize(ST* p)
{assert(p);return p->top;
}
//判空
bool STEmpty(ST* p)
{assert(p);return p->top == 0;
}
bool isValid(char* s) {ST st;STInit(&st);while(*s){
//左括号入栈if(*s=='('||*s=='['||*s=='{'){STpush(&st,*s);}else
//不是左括号出栈比较{if(STEmpty(&st)){STDestroy(&st);return false;}char top=STtop(&st);STpop(&st);if(top=='('&&*s!=')'||top=='['&&*s!=']'||top=='{'&&*s!='}'){STDestroy(&st);return false;}}s++;}
//当栈中没有左括号比较完时,便匹配不成bool ret=STEmpty(&st);STDestroy(&st);return ret;
}

可能有人说太麻烦了,但c语言中没有栈,只能自己创建哦!这是用目前c语言最简单的方法!

栈的实现--链表

和顺序表一样,我们首先要创建栈和链表的基本框架!

stack.h文件--包含需要的函数

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;
//栈
typedef struct stack
{struct stcak* next;STDataType data;
}STNode;//栈的销毁
void STDestroy(STNode* phead);
//入栈
void STpush(STNode** pphead,STDataType x);
//出栈
void STpop(STNode** pphead);
//栈顶数据
STDataType STtop(STNode* phead);
//判空
bool STEmpty(STNode* phead);
//栈数据个数
int STsize(STNode* phead);

栈为空的判定

有了顺序表的基础,接下来依葫芦画瓢----最简单不过!

//判空
bool STEmpty(STNode* phead)
{return phead == NULL;
}

入栈

我们分析将链表的头部为栈顶,进出都在头!(这种方案最佳!)

//创建节点
STNode* STBuyNode(STDataType x)
{STNode* node = (STNode*)malloc(sizeof(STNode));if (node == NULL){perror("malloc failed!");return NULL;}node->data = x;node->next = NULL;return node;
}//入栈
void STpush(STNode** pphead, STDataType x)
{STNode* node = STBuyNode(x);node->next = *pphead;*pphead = node;
}

出栈

将头节点指向下一个节点,原来的头节点释放!

//出栈
void STpop(STNode** pphead)
{assert(!STEmpty(*pphead));STNode* cur = *pphead;*pphead = (*pphead)->next;free(cur);
}

销毁

同样的,动态开辟了空间,当我们不用栈时,要将开辟的空间释放掉!

//栈的销毁
void STDestroy(STNode* phead)
{STNode* cur = phead;while (cur){STNode* next = cur->next;free(cur);cur = next;}
}

栈顶数据

也是一样的,在访问栈顶数据时,要判断栈是否为空,防止越界访问!

//栈顶数据
STDataType STtop(STNode* phead)
{assert(!STEmpty(phead));return phead->data;
}

数据个数

遍历链表,将一个一个节点计数起来!

//栈数据个数
int STsize(STNode* phead)
{STNode* cur = phead;int count = 0;while (cur){count++;cur = cur->next;}return count;
}

码源--栈(顺序表)

stack.h文件

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDateType;
//栈
typedef struct stack
{STDateType* a;int top;int capacity;
}ST;//栈的初始化和销毁
void STInit(ST* p);
void STDestroy(ST* p);
//入栈,出栈
void STpush(ST* p,STDateType x);
void STpop(ST* p);
//栈顶的数据
STDateType STtop(ST* p);
//栈的数据个数
int STsize(ST* p);
//判空
bool STEmpty(ST* p);

stack.c文件

#include"stack.h"
//栈的初始化和销毁
void STInit(ST* p)
{assert(p);p->a = NULL;p->capacity = 0;p->top = 0;
}
void STDestroy(ST* p)
{assert(p);free(p->a);p->capacity = p->top = 0;
}
//入栈,出栈
void STpush(ST* p, STDateType x)
{assert(p);if (p->top == p->capacity){int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));if (tmp == NULL){perror("realloc failed!");return;}p->a = tmp;p->capacity = newcapacity;}p->a[p->top++] = x;
}
void STpop(ST* p)
{assert(p);//出栈的话要判断一下空的情况assert(!STEmpty(p));p->top--;
}
//栈顶的数据
STDateType STtop(ST* p)
{assert(p);//出栈的话要判断一下空的情况assert(!STEmpty(p));return p->a[p->top-1];
}
//栈的数据个数
int STsize(ST* p)
{assert(p);return p->top;
}
//判空
bool STEmpty(ST* p)
{assert(p);return p->top == 0;
}

码源--栈(链表)

stack.h文件

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;
//栈
typedef struct stack
{struct stcak* next;STDataType data;
}STNode;//栈的销毁
void STDestroy(STNode* phead);
//入栈
void STpush(STNode** pphead,STDataType x);
//出栈
void STpop(STNode** pphead);
//栈顶数据
STDataType STtop(STNode* phead);
//判空
bool STEmpty(STNode* phead);
//栈数据个数
int STsize(STNode* phead);

stack.c文件

#include"stack.h"STNode* STBuyNode(STDataType x)
{STNode* node = (STNode*)malloc(sizeof(STNode));if (node == NULL){perror("malloc failed!");return NULL;}node->data = x;node->next = NULL;return node;
}//入栈
void STpush(STNode** pphead, STDataType x)
{STNode* node = STBuyNode(x);node->next = *pphead;*pphead = node;
}//出栈
void STpop(STNode** pphead)
{assert(!STEmpty(*pphead));STNode* cur = *pphead;*pphead = (*pphead)->next;free(cur);
}//栈顶数据
STDataType STtop(STNode* phead)
{assert(!STEmpty(phead));return phead->data;
}//判空
bool STEmpty(STNode* phead)
{return phead == NULL;
}//栈数据个数
int STsize(STNode* phead)
{STNode* cur = phead;int count = 0;while (cur){count++;cur = cur->next;}return count;
}//栈的销毁
void STDestroy(STNode* phead)
{STNode* cur = phead;while (cur){STNode* next = cur->next;free(cur);cur = next;}
}

是不是觉得今天的比较简单?好了,我们下期见!

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

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

相关文章

树和二叉树的定义和基本术语

文章目录 前言一、树的定义二、树的基本术语三、二叉树的定义总结 前言 T_T此专栏用于记录数据结构及算法的&#xff08;痛苦&#xff09;学习历程&#xff0c;便于日后复习&#xff08;这种事情不要啊&#xff09;。所用教材为《数据结构 C语言版 第2版》严蔚敏。 一、树的定义…

win7开启远程桌面却连接不上,如何解决Win7系统开启远程桌面但无法连接的问题

在使用Win7系统时&#xff0c;有时候我们可能会遇到这样的问题&#xff1a;已经成功开启了远程桌面功能&#xff0c;但尝试连接时却总是失败。这可能是由于多种原因导致的&#xff0c;下面我们将详细分析并提供相应的解决方案。 确保本地网络连接正常 可以尝试通过Ping命令测试…

C++程序设计:C++的内存分布与管理

C的内存分布与管理 栈区堆区全局区代码区常量区 栈区 &#xff08;1&#xff09;什么是栈区&#xff1f; 栈区&#xff08;Stack&#xff09; 是用于存储函数调用&#xff0c;局部变量和函数参数的一种内存区域&#xff0c;它的特性就是先进后出&#xff08;FILO&#xff09;。…

Spring底层入门(七)

1、异常处理 在DispatcherServlet中&#xff0c;doDispatch(HttpServletRequest request, HttpServletResponse response) 方法用于进行任务处理&#xff1a; 在捕获到异常后没有立刻进行处理&#xff0c;而是先用一个局部变量dispatchException进行记录&#xff0c;然后统一由…

AI电视起风,三星电视打破“隔代飞跃”,在AI纪元再次领跑

作者 | 曾响铃 文 | 响铃说 要说什么是当下最热的话题&#xff0c;刚落下帷幕的北京车展一定是其中之一&#xff0c;除了各类让人眼花缭乱的新车&#xff0c;纷至沓来的各界行业大佬&#xff0c;也让车展话题度被不断拉高。在此之外&#xff0c;此次车展还刮起了一股“旋风”…

A100 解析:为何它成为 AI 大模型时代的首选?

前言 NVIDIA A100 Tensor Core GPU 可针对 AI、数据分析和 HPC 应用场景&#xff0c;在不同规模下实现出色的加速&#xff0c;有效助力更高性能的弹性数据中心。A100 采用 NVIDIA Ampere 架构&#xff0c;是 NVIDIA 数据中心平台的引擎。A100 的性能比上一代产品提升高达 20 倍…

QT:QT与操作系统

文章目录 信号槽与事件QT多线程概述原理完成倒计时程序 UDP回显服务器服务端客户端 信号槽与事件 在之前的信号槽中&#xff0c;已经有了一个基本的认识&#xff0c;那么对于QT中事件的理解其实就非常的类似&#xff0c;当用户进行某种操作的时候&#xff0c;就会触发事件&…

CCF-Csp算法能力认证,202206-1归一化处理(C++)含解析

前言 推荐书目&#xff0c;在这里推荐那一本《算法笔记》&#xff08;胡明&#xff09;&#xff0c;需要PDF的话&#xff0c;链接如下 「链接&#xff1a;https://pan.xunlei.com/s/VNvz4BUFYqnx8kJ4BI4v1ywPA1?pwd6vdq# 提取码&#xff1a;6vdq”复制这段内容后打开手机迅雷…

欧洲杯/奥运会-云直播

欧洲杯/奥运会要来了&#xff0c;如何升级自己的网站让你的顾客都能观赏直播已提高用户量呢&#xff1f;&#xff01; 【功能完善、平滑兼容】 云直播支持 RTMP 推流、 HLS 源站等多种直播源接入方式&#xff0c;提供直播 SDK&#xff0c;支持多终端适配&#xff0c;上行码率…

Unity之ShaderGraph入门简介与配置

前言 ShaderGraph是Unity的一个可视化着色器编辑工具,它允许开发者在不编写代码的情况下创建复杂的着色器效果。ShaderGraph提供了一个直观的图形界面,用户可以通过拖拽节点并连接它们来构建自定义的着色器。用户可以在ShaderGraph中使用各种节点,如数学运算、纹理采样、颜…

报表-接口类型的数据源

1、配置 在数据中进行如下配置 配置格式&#xff0c;换行的方式 #API $.data[0].children http://192.168.1.1:9200/apis/getInfo 行1&#xff1a;固定写法&#xff0c;标识这是一个接口类型的数据集 行2&#xff1a;JSONPath格式字符串&#xff0c;对接口的数据进行取值。…

Linux Ubuntu(玩客云) qBittorrent docker BT下载(qbittorrent 密码错误无法登录 ip地址被禁止登录等)

提示&#xff1a; 需要提前安装Docker 根据qBittorrent官网的更新日志https://www.qbittorrent.org/news &#xff0c;4.6.1.0包含一个重大更新。可以看到自4.6.1.0开始&#xff0c;qBittorrent将弃用adminadmin默认密码&#xff0c;采用随机密码&#xff0c;将在终端控制台输出…

OpenSearch 与 Elasticsearch:7 个主要差异及如何选择

OpenSearch 与 Elasticsearch&#xff1a;7 个主要差异及如何选择 1. 什么是 Elasticsearch&#xff1f; Elasticsearch 是一个基于 Apache Lucene 构建的开源、RESTful、分布式搜索和分析引擎。它旨在处理大量数据&#xff0c;使其成为日志和事件数据管理的流行选择。 Elasti…

#友元函数与友元类

目录 1.概念 2.友元函数 3.友元类 1.概念 友元提供了一种突破封装的方式&#xff0c;有时提供了便利。但是友元会增加耦合度&#xff0c;破坏了封装&#xff0c;所以友元不宜多 用。 友元分为&#xff1a;友元函数和友元类 2.友元函数 友元函数可以直接访问类的私有成员&a…

计算机网络学习记录 物理层 Day2

计算机网络学习记录 你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner gitee https://gitee.com/Qiuner 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会…

Android build.prop生成过程源码分析

Android的build.prop文件是在Android编译时刻收集的各种property【LCD density/语言/编译时间, etc.】&#xff1b;编译完成之后&#xff0c;文件生成在out/target/product/<board【OK1000】>/system/目录下&#xff1b;在Android运行时刻可以通过property_get()[c/c域] …

BI赋能金融新质生产力,16家金融机构智能BI创新实践分享

2024年政府工作报告强调&#xff0c;要“大力发展科技金融、绿色金融、普惠金融、养老金融、数字金融”&#xff0c;同时“大力推进现代化产业体系建设&#xff0c;加快发展新质生产力”。对于金融行业而言&#xff0c;培育新质生产力是高质量发展的关键着力点。金融机构可以通…

Linux学习笔记3---WSL2交叉编译

ARM 裸机、Uboot 移植、Linux 移植这些都需要在 Ubuntu 下进行编译&#xff0c;编译就需要编译器&#xff0c;在上一章里面已经讲解了如何在 Liux 进行 C 语言开发&#xff0c;里面使用 GCC 编译器进行代码编译&#xff0c;但使用的 gcc 编译器是针对 X86 架构的&#xff01;而…

极简—springMVC工作流程

1、流程图 2、流程 发起请求&#xff1a;客户端通过 HTTP 协议向服务器发起请求。前端控制器&#xff1a;这个请求会先到前端控制器 DispatcherServlet&#xff0c;它是整个流程的入口点&#xff0c;负责接收请求并将其分发给相应的处理器。处理器映射&#xff1a;DispatcherS…

自动控制原理学习--平衡小车的控制算法(二)

上一节 在matlab建模&#xff0c;这一节PID控制. 一、模型 直接先放一张matlab simulink的模型&#xff08;只有直线速度环和平衡环&#xff0c;串联PID&#xff09;&#xff0c;就在上一节的基础上加了两个PID。 二、PID控制 PID的好处就是可以不用动力学建模&#xff08;当…