【数据结构】C语言实现栈(详细解读)

前言:

💥🎈个人主页:​​​​​​Dream_Chaser~ 🎈💥

✨✨专栏:http://t.csdn.cn/oXkBa

⛳⛳本篇内容:c语言数据结构--C语言实现栈

目录

什么是栈

        栈的概念及结构

实现栈的方式

链表的优缺点:

顺序表的优缺点:

栈的实现

a.头文件的包含

 b.栈的定义

c.接口函数     

接口函数的实现

1.栈的初始化

2.销毁栈

3.入栈

4.检测栈是否为空

5.出栈

6.获取栈顶元素

7.获取栈中有效元素个数

完整代码

Test.c

Stack.h

Stack.c


什么是

        栈的概念及结构

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

实现栈的方式

实现栈的方式有两种: 顺序表链表

链表的优缺点:

优点:

        1、任意位置插入删除O(1)

        2、按需申请释放空间

缺点:

        1、不支持下标随机访问

        2、CPU高速缓存命中率会更低

        先说链表实现栈的缺点:

  1. 额外内存开销:链表实现的栈需要为每个节点分配内存空间来存储数据和指针。相比于数组实现的栈,链表实现需要额外的内存开销来维护节点之间的指针关系,可能导致内存碎片化。

  2. 动态内存分配:链表实现的栈需要通过动态内存分配来创建和释放节点。这涉及到频繁的内存分配和释放操作,可能导致内存管理的复杂性和性能开销。在某些情况下,可能会出现内存分配失败或内存泄漏的问题。

  3. 指针操作开销:链表实现的栈需要通过指针进行节点之间的连接操作。这包括插入和删除节点时的指针修改,可能涉及到多个指针的更新。相比于数组实现的栈,链表实现的栈需要更多的指针操作,可能会带来一定的性能开销。

  4. 随机访问的限制:链表是一种顺序访问的数据结构,无法像数组一样通过索引进行随机访问。如果需要在栈中进行随机访问元素,链表实现的栈可能不太适合,而数组实现的栈更具优势。

顺序表的优缺点:

优点:1、尾插尾删效率不错。

        2、下标的随机访问。

        3、CPU高速缓存命中率会更高

缺点:

        1、前面部分插入删除数据,效率是O(N),需要挪动数据。

        2、空间不够,需要扩容。a、扩容是需要付出代价的b、一般还会伴随空间浪费。

        顺序表实现栈的优点

  1. 内存连续性:顺序表在内存中是连续存储的,相比于链表的动态内存分配,顺序表的元素在物理上更加紧凑。这样可以减少内存碎片化,提高内存的利用效率。

  2. 随机访问:顺序表可以通过索引直接访问栈中的元素,具有随机访问的能力。这意味着可以快速访问栈中任意位置的元素,而不需要遍历整个链表。

  3. 操作简单高效:顺序表的插入和删除操作只涉及元素的移动,不需要额外的指针操作和动态内存分配。这使得操作相对简单高效,并且在某些情况下比链表实现更快。

  4. 空间效率:相比于链表实现,顺序表不需要额外的指针来维护节点之间的连接关系,因此可以节省一定的空间开销。只需要存储元素本身和栈顶指针即可。

综上所述,用顺序表实现栈更好。

栈的实现

a.头文件的包含

#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<stdio.h>

 b.栈的定义

typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;//栈顶int capacity;//栈的容量
}ST;

c.接口函数     

// 初始化栈
void STInit(ST* pst); // 入栈
void STPush(ST* pst, STDataType data); // 出栈
void STPop(ST* pst); // 获取栈顶元素
STDataType STTop(ST* pst); // 获取栈中有效元素个数
int STSize(ST* pst); // 检测栈是否为空,如果为空返回true,如果不为空返回false
bool STEmpty(ST* pst); // 销毁栈
void STDestroy(ST* pst);

接口函数的实现

1.栈的初始化

        pst->top表示栈的顶部指针,通常情况下,它指向栈顶元素的下一个位置,而不是指向当前栈顶元素。通过 pst->top 可以确定栈中元素的个数,打印的时候记得将 top - 1。

void STInit(ST* pst)
{assert(pst);//防止敲代码的人传过来是NULL指针pst->a = NULL;//栈底//top不是数组下标,不能理解成数组下标,因为栈只能拿到栈顶的元素,而数组可以随机访问拿到中间元素//pst->top=-1;//指向栈顶元素pst->top = 0;//指向栈顶元素的下一个位置pst->capacity = 0;}

分别解释一下各自的含义: 

  1.  pst 是指向栈的指针,指向栈的首节点或头节点。
  2. -> 是一个成员访问运算符,用于通过指针访问结构体或类的成员
  • pst ->a 是指向存储栈元素的数组的指针。栈中的元素通常被存储在数组中,通过 pst->a 可以访问和操作该数组。在 STInit 函数中, pst->a 被设置为 NULL,表示栈底为空,即栈中没有任何元素。

  • pst->capacity 表示栈的容量,即栈可以容纳的最大元素数量。当栈中元素的数量达到 pst->capacity 时,栈被认为已满,无法再进行入栈操作。在初始化时,pst->capacity 的值通常被设置为 0,表示栈的初始容量为 0。

  • pst->top 表示栈顶指针,它指向当前栈顶元素的下一个位置。在栈为空时,pst->top 的值为 0,表示栈底。随着元素的入栈和出栈操作,pst->top 的值会相应地增加或减少,指向栈中下一个元素的位置。

2.销毁栈

为了防止野指针的出现,栈销毁后记得将指针置空。

void STDestroy(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;
}

3.入栈

三元运算符

condition ? value1: value2 

它的含义是,如果条件condition为真(非0),则整个表达式的值为value1;如果条件为假(0),则整个表达式的值为value2

解析:

int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;

这段代码的执行顺序如下:

  1. 首先,评估条件pst->capacity == 0 这将检查  pst 指针所指向的结构体中的 capacity 成员是否等于 0
  2. 如果条件为真(pst->capacity 等于 0),则表达式的值为 4,将其赋给 newCapacity  
  3. 如果条件为假(pst->capacity不等于 0),则表达式的值为pst->capacity * 2,将其赋给 newCapacity

realloc函数:【C进阶】-- 动态内存管理_Dream_Chaser~的博客-CSDN博客

动图:

函数代码:


void STPush(ST* pst,STDataType x)
{if (pst->top == pst->capacity){int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}pst->a = tmp;//返回的是realloc出来的内存块的地址pst->capacity = newCapacity;//把扩容后的空间大小赋值给栈容量}pst->a[pst->top] = x;//先放值pst->top++;//再++
}

【注意事项】

1️⃣检查栈的顶部指针 top 是否等于栈的容量 capacity 。如果这两个值相等,那么说明栈已经满了,无法再添加新的元素。

 2️⃣接着判断此时栈的容量是否是0,若是0,则把4赋值给newcapacity作为新的栈容量。此后若栈满了,则把此时栈满时的容量 * 2进行扩容,赋值给newcapacity作为新的栈容量。

3️⃣先放入新的元素入栈,接着pst->top指向栈顶元素的指针++

4.检测栈是否为空

        栈为空返回true,不为空返回false

bool STEmpty(ST* pst)//栈为空返回true,不为空返回false
{//写法一//assert(pst);//if (pst->top == 0)//{//	return true;//}//else//{//	return false;//}//写法二return pst->top == 0;
}
  • 当栈为空时,表示栈中没有任何元素。此时,栈顶指针 top 的值通常被设置为特定的初始值(例如0或-1),指向栈底或栈外。在这种情况下,栈顶指针没有指向有效的元素,因此栈被认为是空的。

  • 当栈非空时,表示栈中至少有一个元素。此时,栈顶指针top的值指向栈顶元素的位置。栈顶元素是最后一个被入栈的元素,也是最先被访问或移除的元素。只要栈中有元素存在,栈顶指针都会指向有效的位置。

        因此,在STEmpty(ST* pst)函数中,当栈为空时,即栈顶指针top的值为0(或其他特定初始值),我们返回 true 表示栈为空。反之,如果栈非空,即栈顶指针 top 的值大于0,我们返回 false 表示栈不为空。

5.出栈

        先用assert判断传过来的pst指针是否指向NULL。接着判断栈是否为NULL,为NULL,STEmpty(pst)返回true,!STEmpty(pst)就是false,断言失败,程序终止。反之断言成功,程序正常执行。

图解:

void STPop(ST* pst)
{assert(pst);assert(!STEmpty(pst));pst->top--;
}

【注意事项】

          接着将指向栈顶的指针--,通过将栈顶指针top减一,可以将指针向栈底方向移动,从而使栈顶指向下一个元素。

        指针的移动并不会直接导致元素的销毁。指针的移动只是改变了栈顶指针的位置,使其指向了栈中的下一个元素。元素本身并不会被销毁,只是在后续的操作中,可能无法直接访问被移除的元素。

6.获取栈顶元素

图解:因为前面定义的时候pst->top=0,表示指向栈顶元素的下一个位置。

pst->top-1表示栈顶元素在数组中的索引。

STDataType STTop(ST* pst)
{assert(pst);assert(!STEmpty(pst));return pst->a[pst->top - 1];
}

        需要注意的是,在实际使用中,应确保栈不为空(即栈中有元素存在),才能执行取栈顶元素的操作。因此,在代码中使用了 assert(!STEmpty(pst)) 进行栈非空的断言校验。

代码执行:

7.获取栈中有效元素个数

图解:由图看出,pst->top此时是指向下标为4的位置的,所以栈此时的有效个数就为4

int STSize(ST* pst)
{assert(pst);return pst->top;
}

代码执行: 

完整代码

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
void TestStack1()
{ST st;STInit(&st);STPush(&st, 1);STPush(&st, 2);STPush(&st, 3);STPush(&st, 4);while (!STEmpty(&st)){printf("%d ", STTop(&st));//栈顶元素STPop(&st);}STDestroy(&st);
}
void TestStack2()
{ST st;STInit(&st);STPush(&st, 1);STPush(&st, 2);printf("%d ", STTop(&st));STPush(&st, 3);STPush(&st, 4);printf("\n");printf("%d ", STTop(&st));//printf("%d", STSize(&st));//while (!STEmpty(&st))//{//	printf("%d ", STTop(&st));//栈顶元素//	STPop(&st);//}STDestroy(&st);
}
void TestStack3()
{ST st;STInit(&st);STPush(&st, 1);STPush(&st, 2);STPush(&st, 3);STPush(&st, 4);//printf("%d", STSize(&st));STDestroy(&st);
}int main()
{TestStack1();//入栈出栈//TestStack2();//获取栈顶元素//TestStack3();//计算栈中有效元素个数 return 0;
}

Stack.h

#pragma once
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<stdio.h>
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;//栈顶的位置int capacity;//栈的容量
}ST;void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst,STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool  STEmpty(ST* pst);
int STSize(ST*pst);

Stack.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
void STInit(ST* pst)
{assert(pst);pst->a = NULL;//栈底//top不是下标//pst->top=-1;//指向栈顶元素pst->top = 0;//指向栈顶元素的下一个位置pst->capacity = 0;}void STDestroy(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;
}void STPush(ST* pst,STDataType x)
{if (pst->top == pst->capacity){int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;//true,4.false,括2倍STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));//返回值地址相等就是原地扩容,不同就是异地扩if (tmp == NULL){perror("realloc fail");return;}pst->a = tmp;//返回的是realloc出来的内存块的地址pst->capacity = newCapacity;//把扩容后的空间大小赋值给栈容量}pst->a[pst->top] = x;//先放值pst->top++;//再++
}void STPop(ST* pst)
{assert(pst);assert(!STEmpty(pst));pst->top--;
}STDataType STTop(ST* pst)
{assert(pst);assert(!STEmpty(pst));return pst->a[pst->top - 1];
}bool STEmpty(ST* pst)//栈为空返回true,不为空返回false
{//assert(pst);//if (pst->top == 0)//{//	return true;//}//else//{//	return false;//}return pst->top == 0;
}
int STSize(ST* pst)
{assert(pst);return pst->top;
}

        栈面试题还在持续更新中,感谢支持!

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

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

相关文章

AI创作助手:介绍 TensorFlow 的基本概念和使用场景

目录 背景 环境测试 入门示例 背景 TensorFlow 是一个强大的开源框架&#xff0c;用于实现深度学习和人工智能模型。它最初由 Google 开发&#xff0c;现在已经成为广泛使用的机器学习框架之一。 TensorFlow 简单来说就是一个用于创建和运行机器学习模型的库。它的核心概念…

2023年骨传导耳机推荐,一文读懂骨传导运动耳机哪个牌子好!

这几年&#xff0c;耳机圈开始流行起骨传导耳机&#xff0c;这种耳机通过贴合耳道附近的颌骨通过振动传递声音到听觉神经&#xff0c;相比较入耳式耳机来说&#xff0c;更有利于耳道卫生&#xff0c;而且在听歌同时可保持对环境声的感知&#xff0c;深受不少运动达人的喜爱。我…

LVS之keepalived

1、keepalived 概述 总结&#xff1a;Keepalived 软件就是通过VRRP协议来实现高可用功能。 应用场景&#xff1a;企业应用中&#xff0c;单台服务器承担应用存在单点故障的危险 单点故障一旦发生&#xff0c;企业服务将发生中断&#xff0c;造成极大的危害 VRRP通信原理&…

【Rust】Rust学习 第十六章无畏并发

安全且高效的处理并发编程是 Rust 的另一个主要目标。并发编程&#xff08;Concurrent programming&#xff09;&#xff0c;代表程序的不同部分相互独立的执行&#xff0c;而 并行编程&#xff08;parallel programming&#xff09;代表程序不同部分于同时执行&#xff0c;这两…

iOS逆向初探:揭开iOS App的神秘面纱

逆向是一种分析和还原应用程序的过程&#xff0c;它能够揭示应用程序内部的工作原理和代码结构。接下来我们将全面介绍iOS上的逆向&#xff0c;包括其概念、常用工具和具体实例。 1. 什么是iOS逆向&#xff1f; iOS平台逆向是将应用程序的二进制代码&#xff08;通常是经过编…

使用PyMuPDF添加PDF水印

使用Python添加PDF水印的博客文章。 C:\pythoncode\new\pdfwatermark.py 使用Python在PDF中添加水印 在日常工作中&#xff0c;我们经常需要对PDF文件进行处理。其中一项常见的需求是向PDF文件添加水印&#xff0c;以保护文件的版权或标识文件的来源。本文将介绍如何使用Py…

反向传播求变量导数

反向传播求变量导数 1. 相关习题2. 推导流程2.1 相关公式2.3 变量导数求解 3. 代码实现3.1 参数对应3.2 代码实现 以前只知道反向传播通过链式法则实现今天看书发现图片上求出来的值自己算不出来所以自己算了一下&#xff0c;记录一下&#xff0c;并运行了书中的代码相关书籍&a…

第一讲使用IDEA创建Java工程——HelloWorld

一、前言导读 为了能够让初学者更快上手Java,不会像其他书籍或者视频一样,介绍一大堆历史背景,默认大家已经知道Java这么编程语言了。本专栏只会讲解干货,直接从HelloWord入手,慢慢由浅入深,讲个各个知识点,这些知识点也是目前工作中项目使用的,而不是讲一些老的知识点…

java学习004

常用数据结构对应 php中常用的数据结构是Array数组&#xff0c;相对的在java开发中常用的数据结构是ArrayList和HashMap&#xff0c;它们可以看成是array的拆分&#xff0c;一种简单的对应关系为 PHPJAVAArray: array(1,2,3)ArrayListlArray: array(“name” > “jack”,“…

OpenEuler华为欧拉系统安装

OpenEuler华为欧拉系统安装 一、OpenEuler简介1、OpenEuler概述2、OpenEuler特性 二、OpenEuler部署安装1、安装前配置2、安装引导3、选择语言4、安装信息摘要 三、欧拉系统安装图形化界面1、需要在超级管理员&#xff0c;在root权限下操作2、启动图形化界面 四、手动安装VMwar…

系统报错mfc100u.dll丢失的解决方法(完美解决dll问题)

系统文件mfc100u.dll丢失和出错&#xff0c;极有可能是盗号木马、流氓软件等恶意程序所导致&#xff0c;其感染相关文件并加载起来&#xff0c;一旦杀毒软件删除被感染的文件&#xff0c;就会导致相关组件缺失&#xff0c;游戏等常用软件运行不起来&#xff0c;且提示“无法启动…

验证码识别DLL ,滑块识别SDK,OCR图片转文字,机器视觉找物品

验证码识别DLL ,滑块识别SDK 你们用过哪些OCR提取文字&#xff0c;识图DLL&#xff0c;比如Opencv,Labview机器视觉找物品之类&#xff1f;

共创无线物联网数字化新模式|协创数据×企企通采购与供应链管理平台项目成功上线

近日&#xff0c;全球无线物联网领先者『协创数据技术股份有限公司』&#xff08;以下简称“协创数据”&#xff09;SRM采购与供应链项目全面上线&#xff0c;并于近日与企企通召开成功召开项目上线总结会。 基于双方资源和优势&#xff0c;共同打造了物联网特色的数字化采购供…

连接pgsql数据库 sslmode sslrootcert sslkey sslcert 参数的作用

sslmode 参数的作用 sslmode 参数用于指定数据库连接时使用的 SSL 加密模式。SSL&#xff08;Secure Sockets Layer&#xff09;是一种加密协议&#xff0c;用于保护数据在客户端和服务器之间的传输过程&#xff0c;以增加数据传输的安全性。sslmode 参数可以设置不同的值&…

leetcode刷题之283:移动零

问题 实现思路 首先, 将dest指向-1 位置, cur指向下标为0 的位置, 在cur遍历的过程中: 1) 遇到非零元素则与下标dest1 位置的元素交换, 2) 若遇到零元素则只继续cur遍历. 下标为1 的位置上是 非零元素 执行1) 交换得到右图结果 随后cur 得到下图结果 下标为2 的位置上是零…

工程师使用IT服务台软件可以解决哪些问题?

现如今企业数字化建设已初具规模&#xff0c;业务系统基本已告一段落&#xff0c;而下一步关注的重点则从技术转向管理&#xff0c;如何能让这些系统更好运行起来&#xff0c;如何提高管理效率已是重中之重。在此向您推荐一款高效的IT服务管理工具——ServiceDesk Plus&#xf…

GEEMAP 基本操作(一)如何拉伸图像

图像拉伸是最基础的图像增强显示处理方法&#xff0c;主要用来改善图像显示的对比度&#xff0c;地物提取流程中往往首先要对图像进行拉伸处理。图像拉伸主要有三种方式&#xff1a;线性拉伸、直方图均衡化拉伸和直方图归一化拉伸。 GEE 中使用 .sldStyle() 的方法来进行图像的…

Docker容器学习:部署安装Docker基础使用

目录 1、安装Docker-CE 1&#xff09;参考阿里云的yum安装 2&#xff09;二进制安装docker&#xff08;推荐、生产环境使用较多&#xff09; 3&#xff09;配置Docker镜像加速 2、下载系统镜像&#xff08;Ubuntu、 centos&#xff09; 1&#xff09;先查看我们所需的镜像…

视频转云存的痛点

现在整个运营商体系里面&#xff0c;有大量的视频转云存储的需求&#xff0c;但是视频云存储有一个比较大的痛点&#xff0c;就是成本&#xff01; 成本一&#xff1a;存储成本&#xff1b; 我们以1000路2M视频转云存&#xff0c;存储时间为90天为例&#xff08;B端存储时间有…

【ARM】Day6 cotex-A7核UART总线实验

cotex-A7核UART总线实验 1. 键盘输入一个字符‘a’&#xff0c;串口工具显示‘b’ 2. 键盘输入一个字符串"nihao"&#xff0c;串口工具显示“nihao” uart.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_rcc.h" #include "stm3…