数据结构学习之路--深入探索栈的核心要点(附C源码)

   哈喽~大家!今天我们来学习栈的特别节目,精彩马上开始~


目录

前言

一、栈 

1 栈的概念

2 栈的结构

3 栈的实现 

3.1 栈的定义

3.2 栈的初始化 

3.3 入栈 

3.4 出栈 

3.5 取栈顶元素 

 3.6 判断栈是否为空

3.7 栈的大小

3.8 栈的销毁 

 二、源代码


前言

   栈和队列均是常见的数据结构。栈的特点是后进先出(即 Last In First Out(LIFO) ),其增删查元素都在栈顶实现。而队列的特点是先进先出,增加元素在队尾实现,删除和查看元素都在队首实现。本期我们来详细学习实现栈的结构。

一、栈 

我们先根据下图直观地了解栈:

1 栈的概念

   栈是一种特殊的线性表,特点是后进先出,它仅允许在固定一端进行插入和删除元素操作,最先加入的元素最后取出,最后加入的元素最先取出。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。文字描述难免过于死板,为了更好的帮助大家理解,附以下图解: 

将数字 1 到 7 依次入栈之后,此时栈顶元素是 7 ,第一个出栈的元素是 7。

2 栈的结构

3 栈的实现 

   栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

一般栈的实现需要完成几种函数的操作: 

  • 栈的初始化
  • 入栈
  • 出栈
  • 取出栈顶元素
  • 判断栈是否为空
  • 栈的销毁 
//初始化
void StackInit(ST* ps);
//入栈
void StackPush(ST* ps, STDateType x);
//出栈
void StackPop(ST* ps);
//取栈顶元素
STDateType GetTop(ST* ps);
//判空
bool StackEmpty(ST* ps);
//栈大小
int StackSize(ST* ps)
//栈的销毁
void StackDestory(ST* ps);

3.1 栈的定义

//这里我们实现的是动态的栈typedef int STDateType;    //方便数据类型的替换typedef struct Stack        
{STDateType* a;         //动态开辟数组int top;               //栈顶int capacity;          //容量,方便扩容
}ST;

首先,定义动态数组 a,采用动态数组的方式主要是便于后期容量的扩充;然后,定义一个变量 top 用于标识栈顶位置;最后,定义一个变量 capacity 用于统计栈可以容纳的数据个数。 

3.2 栈的初始化 

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

首先,将指向栈中数据内存空间的指针 a 初始化为NULL;然后,将指向栈顶元素的变量 top初始化为0;最后,再将标识栈当前容量的 capacity 初始化为0即可。 

3.3 入栈 

//入栈
void StackPush(ST* ps, STDataType x)
{//判空assert(ps);//判断容量是否已满//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1if (ps->top == ps->capacity){//为空就开辟四个空间,不为空,就扩容至二倍int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);if (tmp == NULL){printf("malloc fail\n");exit(-1);}//将新开辟的内存空间的首地址tmp赋值给aps->a = tmp;//更新capacityps->capacity = newCapacity;}//入栈ps->a[ps->top] = x;//top指向栈顶元素的下一个位置ps->top++;
}

在入栈之前,首先需要对顺序栈的空间容量进行检查,这里使用三目运算符进行判断:若当前容量 capacity 为空,则开辟4个数据的内存空间;若当前容量非空但已满,则将 capacity 的大小扩容至 2*capacity。然后调用 realloc 函数开辟新的内存空间,并返回新的内存空间的起始地址 tmp。接着将新开辟的内存空间的起始地址 tmp 赋值给 a,使 a 指向这片新开辟的空间。最后,将 x 插入栈顶位置,并让 top 继续指向栈顶元素的下一个位置。 

3.4 出栈 

//出栈
void StackPop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//出栈ps->top--;
}

在出栈之前,首先需要调用 StackEmpty(ps) 函数来判断栈是否为空,若不为空,则可以出栈。出栈操作就是将 top--,这里需要注意的是:出栈的数据还残留在内存中,只是逻辑上被删除了。 

3.5 取栈顶元素 

//取栈顶元素
STDataType StackTop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//取栈顶元素return ps->a[ps->top - 1];
}

在取栈顶元素之前,首先需要调用函数 StackEmpty(ps) 判断栈是否为空,若栈不为空则可以取栈顶元素。因为 top 指向栈顶元素的下一个位置,所以 top 需要先进行--,再取栈顶元素。 

 3.6 判断栈是否为空

//判空
bool StackEmpty(ST* ps)
{assert(ps);return ps->top==0;      //返回栈的top,若为0则为空,非0则不为空;
}

判断一个栈是否为空,若为空则返回 true,否则返回 false。这里需要特别说明的一点是,我们将 top 指向栈顶元素的下一个位置,而非指向栈顶元素本身。 

 注意:

  • 当top初始化为:top=0;此时top指向栈顶元素的下一个位置;
  • 当top初始化为:top=-1;此时top指向栈顶元素。

3.7 栈的大小

//大小
int StackSize(ST* ps)
{//判空assert(ps);return ps->top;
}

因为 top 指向栈顶元素的下一个位置,而 top 的下标又是从0开始的,所以 top 所在位置的下标就是所求栈的大小,也就是栈中元素个数。 

3.8 栈的销毁 

//销毁
void StackDestory(ST* ps)
{//判空assert(ps);//realloc开辟的空间,需要调用free释放free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}

对于栈的销毁,首先需要释放由 realloc 动态申请开辟的内存空间,使用 free 函数进行释放。然后将 top 和 capacity 初始化为0。 

 二、源代码

Stack.h#pragma once
#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 StackInit(ST* ps);//入栈
void StackPush(ST* ps, STDataType x);//出栈
void StackPop(ST* ps);//取栈顶元素
STDataType StackTop(ST* ps);//判空
bool StackEmpty(ST* ps);//大小
int StackSize(ST* ps);//销毁
void StackDestory(ST* ps);
Stack.c#include"Stack.h"​//初始化
void StackInit(ST* ps)
{//判空assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;
}​​//入栈
void StackPush(ST* ps, STDataType x)
{//判空assert(ps);//判断容量是否已满//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1if (ps->top == ps->capacity){//为空就开辟四个空间,不为空,就扩容至二倍int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);if (tmp == NULL){printf("malloc fail\n");exit(-1);}//将新开辟的内存空间的首地址tmp赋值给aps->a = tmp;//更新capacityps->capacity = newCapacity;}//入栈ps->a[ps->top] = x;//top指向栈顶元素的下一个位置ps->top++;
}​​//出栈
void StackPop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//出栈ps->top--;
}​​//取栈顶元素
STDataType StackTop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//取栈顶元素return ps->a[ps->top - 1];
}​​//判空
bool StackEmpty(ST* ps)
{assert(ps);return ps->top==0;      //返回栈的top,若为0则为空,非0则不为空;
}​​//大小
int StackSize(ST* ps)
{//判空assert(ps);return ps->top;
}​​//销毁
void StackDestory(ST* ps)
{//判空assert(ps);//realloc开辟的空间,需要调用free释放free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}
​
test.c#include"Stack.h"void TestStack()
{ST st;//初始化StackInit(&st);//入栈StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);StackPush(&st, 5);StackPush(&st, 6);StackPush(&st, 7);//出栈while (!StackEmpty(&st)){//读取栈顶元素printf("%d ",StackTop(&st));//出栈StackPop(&st);}printf("\n");//销毁StackDestory(&st);
}int main()
{TestStack();return 0;
}

   本期的内容不多,相信大家学起来也相对容易,提前透露一下下:我们下期来玩队列哈~如果你们觉得本篇文章对自己有所帮助,给博主留下三连支持噢,你的支持是我创作的最大动力!那我们下期再会啦~

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

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

相关文章

掀起区块链开发狂潮!Scaffold-eth带你一键打造震撼DApp

文章目录 前言一、Scaffold-eth是什么&#xff1f;二、安装和配置1.准备工作2.安装3.配置开发环境 三、进阶使用1.放入自己的合约2.部署运行 总结 前言 前面的文章传送&#x1f6aa;&#xff1a;hardhat入门 与 hardhat进阶 在之前的文章中&#xff0c;我们已经探讨了使用Har…

输入变量数据通过隶属函数从真实论域转变到模糊论域的示例

1. 示例1 假设我们有一个关于顾客满意度的调查&#xff0c;调查数据是顾客对某项服务的评分&#xff0c;评分范围是1到5分。现在&#xff0c;我们希望对这些评分进行模糊化处理&#xff0c;以便更好地理解和解释顾客的满意度。 首先&#xff0c;我们定义三个模糊集合&#xf…

边缘计算网关主要有哪些功能?-天拓四方

随着物联网&#xff08;IoT&#xff09;的快速发展和普及&#xff0c;边缘计算网关已经成为了数据处理和传输的重要枢纽。作为一种集成数据采集、协议转换、数据处理、数据聚合和远程控制等多种功能的设备&#xff0c;边缘计算网关在降低网络延迟、提高数据处理效率以及减轻云数…

Python 入门指南(五)

原文&#xff1a;zh.annas-archive.org/md5/97bc15629f1b51a0671040c56db61b92 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十六章&#xff1a;Python 中的对象 因此&#xff0c;我们现在手头上有一个设计&#xff0c;并且准备将该设计转化为一个可工作的程序&a…

C++从入门到精通——类和对象(下篇)

1. 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _mont…

spring webflux 小结

一、WebFlux 简介 WebFlux 是 Spring Framework5.0 中引入的一种新的反应式Web框架。通过Reactor项目实现Reactive Streams规范&#xff0c;完全异步和非阻塞框架。本身不会加快程序执行速度&#xff0c;但在高并发情况下借助异步IO能够以少量而稳定的线程处理更高的吞吐&…

QT creator 代码中有中文,提示常量中有换行符解决方案

QT creator 代码中有中文&#xff0c;提示常量中有换行符解决方案 参考视频问题问题解决 参考 感谢感谢,非常感谢,有你,让Qt不再困难,困扰我四年的问题解决了!!! https://blog.csdn.net/m0_45866718/article/details/112389513 视频 https://www.bilibili.com/video/BV1Fp4…

Towards Street-Level Client-Independent IP Geolocation(2011年)(第一部分)

被引次数:306 Wang Y, Burgener D, Flores M, et al. Towards {Street-Level}{Client-Independent}{IP} Geolocation[C]//8th USENIX Symposium on Networked Systems Design and Implementation (NSDI 11). 2011. Abstract 一个高度精确的客户端独立的地理定位服务将是互联…

MGRE环境下的ospf实验

MGRE环境下的ospf实验 一.拓扑图 二.实验步骤 1.分配各路由网段IP [R1]int g 0/0/0 [R1-GigabitEthernet0/0/0]ip address 16.0.0.1 24 [R1-GigabitEthernet0/0/0]int g 0/0/1 [R1-GigabitEthernet0/0/1]ip address 116.0.0.1 24[R2]int g 0/0/0 [R2-GigabitEthernet0/0/0]…

【Linux】磁盘阵列RAID技术

目录 一、RAID介绍 1.1 什么是RAID技术&#xff1f; 1.2 为什么要使用RAID技术&#xff1f; 二、RAID级别 2.1 常见的RAID级别 2.2 常见RAID介绍 三、RAID特性对比 一、RAID介绍 1.1 什么是RAID技术&#xff1f; 把多块独立的物理磁盘按不同的方式组合起来形成一个硬盘…

基于Python的景区票务人脸识别系统(V2.0)

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

【C语言】带你完全理解指针(五)练习

复习一下对数组名的理解 数组名的理解 数组名是数组首元素的地址 但是有2个例外&#xff1a; 1. sizeof(数组名)&#xff0c;这里的数组名表示整个数组&#xff0c;计算的是整个数组的大小&#xff0c;单位是字节 2. &数组名&#xff0c;这里的数组名表示整个数组&#xff…