数据结构(C语言篇):(八)栈

目录

前言

一、概念与结构

二、栈的实现

2.1  头文件的准备

2.2  函数的实现

2.2.1  STInit( )函数(初始化)

2.2.2  STDestroy( )函数(销毁)

2.2.3  STPush( )函数(入栈)

2.2.4  STPop( )函数(出栈)

2.2.5  STTop( )函数(取栈顶元素)

2.2.6  STSize( )函数(获取栈中元素个数)

总结


前言

        栈作为一种基础且强大的线性数据结构,在计算机科学领域扮演着至关重要的角色。其"后进先出"(LIFO)的核心特性,使其成为处理函数调用、表达式求值、括号匹配等场景的理想选择。从程序执行时系统栈的管理,到日常开发中撤销操作、浏览历史等功能的实现,栈的应用无处不在。本文将深入探讨栈的工作原理、实现方式以及典型应用场景,帮助读者全面理解这一数据结构的精髓及其在实际工程中的价值。下面就让我们正式开始吧!


一、概念与结构

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

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

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

        压栈和出栈的示意图如下:

        栈的底层结构选型:

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

        如果使用数组来实现:

        此时入栈和出栈的时间复杂度都是O(1)

        如果使用链表来实现:

        此时出栈和入栈的时间复杂度也同样都是O(1)

二、栈的实现

2.1  头文件的准备

        我们将栈的结构和一系列栈相关的函数在头文件Stack.h中声明如下:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>//定义栈的结构
typedef int STDataType;
typedef struct Stack {STDataType* arr;int top; //指向栈顶的位置---刚好就是栈中有效数据个数int capacity;//栈的空间大小
}ST;//初始化
void STInit(ST* ps);
//销毁
void STDesTroy(ST* ps);//入栈——栈顶
void STPush(ST* ps, STDataType x);
//出栈———栈顶
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);//栈是否为空
bool STEmpty(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);

2.2  函数的实现

2.2.1  STInit( )函数(初始化)

        函数的实现逻辑如下:

        1.  初始化数组指针:先将栈的数组指针arr置为NULL,此时表示栈没有分配任何内存空间。

ps->arr = NULL;

        2.  初始化栈顶指针和容量:

  • ps->top = 0:将栈顶指针设置为0,表示空栈

  • ps->capacity = 0:将栈的容量设置为0,表示当前没有分配空间

ps->top = ps->capacity = 0;

        完整代码如下:

//初始化
void STInit(ST* ps)
{ps->arr = NULL;ps->top = ps->capacity = 0;}

2.2.2  STDestroy( )函数(销毁)

        函数实现逻辑如下:

        1.  释放动态数组内存:

  • 检查 ps->arr 是否为 NULL(是否分配了内存)

  • 如果分配了内存,使用 free() 释放数组内存

  • 避免对 NULL 指针调用 free()

if(ps->arr)free(ps->arr);

        2.  重置指针和状态:

  • ps->arr = NULL:将数组指针设置为 NULL,避免野指针

  • ps->top = 0:将栈顶指针重置为0,表示空栈

  • ps->capacity = 0:将容量重置为0,表示没有分配空间

ps->arr = NULL;
ps->top = ps->capacity = 0;

        完整代码如下:

//销毁
void STDesTroy(ST* ps)
{if(ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}

        使用示例如下:

ST stack;
STInit(&stack);      // 初始化栈// 使用栈进行各种操作
STPush(&stack, 10);
STPush(&stack, 20);
// ...STDesTroy(&stack);   // 销毁栈,释放资源
// 现在stack可以被重新初始化或丢弃

2.2.3  STPush( )函数(入栈)

        首先画图分析:

        函数实现逻辑如下:

        1.  参数验证:确保栈指针 ps 不为 NULL。

assert(ps);

        2.  检查空间是否足够:如果栈顶指针等于容量,说明栈已满,需要扩容。

if (ps->top == ps->capacity)

        3.  动态扩容机制:首次分配时,如果容量为0(初始状态),先分配四个元素的空间;后续扩容时,如果已有容量,就将容量扩大为原来的2倍。(使用 realloc 重新分配内存,可以保留原有数据)

int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));

        4.  错误处理:检查内存分配是否成功,如果失败就输出错误信息,并退出程序。

if (tmp == NULL)
{perror("realloc fail!");exit(1);
}

        5.  更新指针和容量:首先更新数组指针指向新分配的内存,然后更新容量为新分配的大小。

ps->arr = tmp;
ps->capacity = newCapacity;

        6.  压入元素:将元素 x 放入栈顶位置 ps->arr[ps->top],然后栈顶指针 ps->top 自增1。

ps->arr[ps->top++] = x;

        完整代码如下所示:

//入栈——栈顶
void STPush(ST* ps, STDataType x)
{assert(ps);//判断空间是否足够if (ps->top == ps->capacity){//增容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newCapacity;}//空间足够ps->arr[ps->top++] = x;
}

        使用示例如下:

ST stack;
STInit(&stack);STPush(&stack, 10);  // 分配4个空间,压入10
STPush(&stack, 20);  // 压入20
STPush(&stack, 30);  // 压入30  
STPush(&stack, 40);  // 压入40,栈满
STPush(&stack, 50);  // 扩容到8个空间,压入50

2.2.4  STPop( )函数(出栈)

        要想实现出栈函数,我们需要先实现一个STEmpty (判空)函数,如下所示:

//栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}

       画图分析如下:

        STPop的实现逻辑如下:

        1.  前置条件检查:首先使用用 STEmpty 函数检查栈是否为空,如果栈为空,则断言失败,不能执行弹出操作。同时需要确保栈中至少有一个元素可弹出。

        2.  执行弹出操作:首先将栈顶指针 top 减1,这样原来的栈顶元素就不再属于栈的有效范围,但实际上并没有删除数据,只是改变了栈的边界。

ps->top--;

        完整代码如下所示:

//出栈———栈顶
void STPop(ST* ps)
{assert(!STEmpty(ps));ps->top--;
}

        使用示例如下:

ST stack;
STInit(&stack);STPush(&stack, 10);
STPush(&stack, 20);
STPush(&stack, 30);
// 栈: [10, 20, 30], top=3STPop(&stack);  // 弹出30
// 栈: [10, 20], top=2STPop(&stack);  // 弹出20  
// 栈: [10], top=1STPop(&stack);  // 弹出10
// 栈: 空, top=0

2.2.5  STTop( )函数(取栈顶元素)

        画图分析如下:

        函数的实现逻辑如下:

        1.  前置条件检查:首先使用 STEmpty 函数检查栈是否为空,如果栈为空,则断言失败,不能获取栈顶元素。需要确保栈中至少有一个元素。

assert(!STEmpty(ps));

        2.  返回栈顶元素:

  • ps->top 指向下一个空位置(栈顶的下一个位置)

  • ps->top - 1 就是当前栈顶元素的位置

  • 返回该位置的元素值

return ps->arr[ps->top - 1];

        完整代码如下:

//取栈顶元素
STDataType STTop(ST* ps)
{assert(!STEmpty(ps));return ps->arr[ps->top - 1];
}

        使用示例:

ST stack;
STInit(&stack);STPush(&stack, 10);
STPush(&stack, 20);
STPush(&stack, 30);
// 栈: [10, 20, 30], top=3STDataType topValue = STTop(&stack);  // 获取栈顶值30
printf("栈顶元素: %d\n", topValue);   // 输出: 栈顶元素: 30// 栈仍然保持: [10, 20, 30], top=3

2.2.6  STSize( )函数(获取栈中元素个数)

        函数实现逻辑如下:

        1.  参数验证:确保栈指针 ps 不为 NULL。

assert(ps);

        2.  返回元素数量:直接返回栈顶指针 top 的值。因为 top 既指向下一个空位置,又表示当前元素数量。

return ps->top;

        完整代码如下:

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

        使用示例如下:

ST stack;
STInit(&stack);printf("初始栈大小: %d\n", STSize(&stack));  // 输出: 0STPush(&stack, 10);
STPush(&stack, 20);
STPush(&stack, 30);printf("当前栈大小: %d\n", STSize(&stack));  // 输出: 3STPop(&stack);
printf("弹出后栈大小: %d\n", STSize(&stack));  // 输出: 2

总结

        本期我为大家介绍了数据结构中,栈的结构和其相关函数的实现逻辑,下期我将继续为大家带来队列的相关内容,请大家多多关注和支持~~~

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

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

相关文章

Elasticsearch数据迁移快照方案初探(一):多节点集群配置踩坑记

背景介绍 在生产环境中&#xff0c;我们经常需要将测试环境的Elasticsearch索引数据迁移到生产环境。这次我们遇到了一个典型的多节点集群快照配置问题&#xff1a;需要为所有节点添加path.repo配置&#xff0c;但过程中遇到了各种挑战。 问题描述 我们的Elasticsearch集群包含…

leedcode 算法刷题第二十天

39. 组合总和 class Solution { public:vector<vector<int>> result;vector<int> temp;void backtructing(vector<int>& candidates, int target, int sum,int start){if(sumtarget){result.push_back(temp);return;}if(sum>target){return;}f…

身份证实名认证API集成—身份核验接口-网络平台安全合规

在数字化浪潮席卷各行各业的今天&#xff0c;网络空间的安全问题日益受到关注。为防范网络诈骗、虚假注册、身份盗用等风险&#xff0c;国家陆续出台多项法律法规&#xff0c;如《网络安全法》《个人信息保护法》等&#xff0c;明确要求互联网服务提供者落实用户真实身份核验机…

谷歌TIGER爆火!生成式召回颠覆推荐系统:用语义ID破解冷启动+多样性难题,3大数据集性能碾压传统模型

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

分享一个实用的B站工具箱(支持音视频下载等功能)

文章目录 📖 介绍 📖 🏡 演示环境 🏡 📒 一款实用的B站工具箱 📒 💥 项目亮点 💥 🛠️ 下载与安装 🚀 使用指南 📢 注意事项 ⚓️ 相关链接 ⚓️ 📖 介绍 📖 很多小伙伴在B站追番或者学习时,总会遇到一个很头疼的问题:想把视频下载到本地,要么被限…

大话 IOT 技术(4) -- 答疑篇

文章目录前言手机能与设备直接通信吗多协议能统一用一个吗假设我们统一用http协议假设我们统一用mqtt协议bypass服务端和设备不能mqtt直接通信设备必有wifi 和蓝牙功能设备为什么不能自己连接网络配网模式是什么后话当你迷茫的时候&#xff0c;请点击 物联网目录大纲 快速查看前…

机器视觉学习-day14-绘制图像轮廓

1. 轮廓的概念轮廓是目标物体或者区域在图像外部的边界线&#xff0c;通常由一系列像素点相连组成&#xff0c;这些像素点共同构成了一个封闭的形状&#xff0c;这样形状就是轮廓。轮廓与边缘不同&#xff1a;轮廓是连续的&#xff0c;边缘可以连续也可以离散轮廓是完整的&…

Linux shell getopts 解析命令行参数

Linux shell getopts 解析命令行参数getopts语法 getopts 选项字符串 名称 [ 参数 ...]示例1&#xff08;有前置冒号&#xff09;: while getopts ":hdo:" optname; do ...... done示例1&#xff08;无前置冒号&#xff09; while getopts "hdo:" optname…

DeepInteraction++基于多模态交互的自动驾驶感知与规划框架

DeepInteraction++基于多模态交互的自动驾驶感知与规划框架 1 论文核心概念 DeepInteraction++ 提出了一种名为"模态交互"(modality interaction)的新策略,用于自动驾驶中的多模态(LiDAR 和相机)感知任务。其核心思想是不将多模态信息融合为单一表示,而是分别…

忆联参与制定消费级SSD团体标准正式出版! 以“高可靠”引领行业提质增效与用户体验升级

引言​在AIPC爆发、数据价值凸显的当下&#xff0c;存储设备已超越简单容器&#xff0c;成为智能体验基石&#xff0c;其性能与可靠性直接关乎用户效率与资产安全。然而&#xff0c;消费级SSD长期缺乏统一权威的可靠性标准&#xff0c;使厂商缺乏质量对标依据&#xff0c;用户亦…

微服务搭建(SpringBoot + Dubbo + Nacos)

1.项目接口2. 编辑pom.xml和application.yml文件2.1父工程pom.xml<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:s…

android中常见布局及其约束

0 布局的定义 布局可以理解为一种​​容器​​&#xff0c;用于​​组织与排列界面上的控件​​。 布局是一个相框&#xff0c;控件就是你要展示的照片。• 你&#xff08;布局规则&#xff09;决定这些照片怎么排列&#xff1a;是从上到下整齐放&#xff08;LinearLayout&am…

Rust语言能干什么

Rust 语言的应用范围非常广&#xff0c;几乎覆盖了现代软件开发的全部领域。它最初以“系统级语言”身份出道&#xff0c;但现在已经远远超出了这个范畴。下面我从几个关键方向给你梳理一下&#xff0c;Rust 到底能干什么&#xff0c;以及为什么在这些领域它特别有优势。 1. 系…

只需一个设置就可以解决Microsoft Edge浏览器打不开网页的问题

Microsoft Edge是一款功能强大的网络浏览器&#xff0c;预装在Windows 10、11系统中。通过这个简单易懂的教程&#xff0c;学习如何修复Microsoft Edge浏览器打不开的问题。1、打开计算机找到C盘&#xff0c;双击打开&#xff1a;2、打开【用户】➜【Admin】➜【AppData】➜【L…

AI 应用 图文 解说 (二) -- 百度智能云 ASR LIM TTS 语音AI助手源码

文章的目的为了记录AI应用学习的经历&#xff0c;降低AI的入门难度。同时记录开发流程和要点有些记忆模糊&#xff0c;防止忘记。也希望可以给看到文章的朋友带来一些收获。 相关链接&#xff1a; AI 应用 图文 解说 (一) -- 百度智能云 实现 语音 聊天-CSDN博客 AI 应用 图文 …

计算机Python毕业设计推荐:基于Django的博客网站设计与实现【python/大数据/深度学习/机器学习定制】

精彩专栏推荐订阅&#xff1a;在下方主页&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、项目介绍二、…

当 AI 开始 “筛选” 信息:算法偏见会加剧认知鸿沟吗?如何构建公平的 AI 生态?

AI 筛选信息的现状与原理​在信息爆炸的时代&#xff0c;AI 筛选信息已成为各领域不可或缺的关键技术。在社交媒体平台上&#xff0c;如抖音、小红书等&#xff0c;AI 根据用户的点赞、评论、浏览历史等数据&#xff0c;精准推送用户可能感兴趣的内容&#xff0c;极大提升了用户…

2023年IEEE IOTJ SCI1区TOP,动态环境下无人机目标覆盖任务路径规划,深度解析+性能实测

目录1.摘要2.问题模型3.算法设计4.结果展示5.参考文献6.代码获取7.算法辅导应用定制读者交流1.摘要 无人机&#xff08;UAV&#xff09;作为物联网应用的重要工具&#xff0c;正广泛应用于智能农业监测、智能交通监测等领域&#xff0c;并逐渐成为国内外研究热点。然而&#x…

计算机视觉(四):二值化

二值化&#xff0c;就是将图像从彩色或灰度模式转换为只有两种颜色&#xff08;通常是黑色和白色&#xff09;的模式。这个过程的本质是设定一个阈值 (Threshold)&#xff0c;将图像中所有像素的灰度值与这个阈值进行比较。 基本原理 二值化的核心原理非常简单&#xff1a; 灰度…

(二)设计模式(Command)

文章目录项目地址一、设计模式1.1 Command Design1. 创建命令接口2. 创建支付的Command类3. CommandScheduler4. 使用1.2 Chain of Responsibility1. 接口创建2. 审批人3. 发起审批1.3 State Pattern1. 创建简单的状态机定义动作和状态状态机使用状态机1.x Iterator1.x Observe…