C数据结构:栈

目录

栈的作用

栈的实现

栈的数据结构

栈的初始化

栈的销毁

栈的插入

栈的删除

获得栈顶元素

获得栈有效元素个数

判断栈是否为空

栈的使用

完整代码


栈是一种特殊结构的线性表

先来看看栈的图

之所以说它特殊,是因为它的插入删除功能比较特殊

栈的插入也叫作压栈/入栈/进栈

栈的插入只能在栈顶插入

栈的删除也叫作出栈

栈的删除只能在栈顶删除

栈的作用

栈这种特殊的结构可以让我们在解决特定问题的时候会有很大的帮助

类似的例如队列也是

这也是我们学习多种数据结构的原因

栈的实现

我们要实现的栈是能够动态扩容的栈,而不是静态栈

栈可以使用两种数据结构实现,一种是顺序表,一种是链表,这里是通过顺序表为底层实现的

因为静态在现实中并不常用,缺点也很明显(容易浪费空间或者空间不足)

下面先来看看栈的数据结构

栈的数据结构

typedef int STDataType;
typedef struct Stack
{STDataType* _a;int _top;		// 栈顶int _capacity;  // 容量 
}Stack;

结构体里有三个成员

1、_a是指向一块连续空间的指针

2、_top是栈顶(主要用于入栈出栈)

3、_capacity是栈的容量(便于后续调整_a指针指向的空间的大小)

栈的初始化

void StackInit(Stack* ps)
{assert(ps);ps->_a = NULL;ps->_capacity = 0;ps->_top = 0;
}

这里初始化暂时先让_a指针为NULL

所以capacity容量当然为0

也可以选择先malloc一块空间,先让_a指针指向它,后续再扩容,但capacity也要跟着一起改 

这里top的初始化就比较有争议了

top刚开始可以为0,也可以刚开始为-1

若top刚开始为0,则top表示栈顶数据的最后一个元素的下一个元素的下标,也可以表示栈的大小(size)

若top刚开始为-1,则top表示最后有效数据的下标(例如栈中有3个数据,那么top为3,top则为最后一个数据的下标)

这里我们选择了top=0,则下面写的代码也是为top=0而准备的

栈的销毁

void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_capacity = 0;ps->_top = 0;
}

销毁只需要释放掉_a指针指向的空间,并设为NULL,capacity、top=0即可 

栈的插入

void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->_capacity == ps->_top){int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);if (tmp == NULL){perror("realloc fail\n");return;}ps->_a = tmp;ps->_capacity = newcapacity;}ps->_a[ps->_top] = data;ps->_top++;
}

因为我们要插入数据,那么插入数据之前我们要先判断_a指针所指向的空间还是否有剩余,判断条件就是capacity = top,若条件满足则表示空间不足,则需要扩容

第一个newcapacity用了一个三目操作符,若刚开始的空间为0,则初始化newcapacity为4,否则newcapacity = capacity * 2

然后使用了一个tmp指针接收我们realloc函数的返回值,并为_a空间扩容

这里若_a = NULL,则realloc会当作malloc先开辟一块空间,并让tmp指向这块空间,所以到后面我们需要让_a = tmp

若tmp = NULL,则代表realloc扩容失败,perror函数打印错误信息,并return

不若则扩容成功,当_a获取到新的空间后我们直接在栈顶的位置插入data即可

最后让top++往后走

栈的删除

void StackPop(Stack* ps)
{assert(ps);assert(ps->_top > 0);ps->_top--;
}

删除只需要让top--即可(注意断言防止top越界)

为什么我们要单独写一个函数调用来让top--而不是在主函数中直接让top--?

因为若是别人使用我们实现的栈,别人并不知道我们的top的含义是什么

也就是我们刚开始讲的top初始化为0和top初始化为-1的区别 

获得栈顶元素

STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->_top > 0);return ps->_a[ps->_top - 1];
}

直接返回top-1位置的值即可 

获得栈有效元素个数

int StackSize(Stack* ps)
{assert(ps);return ps->_top;
}

前面也写了有效元素的个数即为top

所以直接返回top即可 

判断栈是否为空

int StackEmpty(Stack* ps)
{assert(ps);return ps->_top == 0;
}

若top为0则代表empty为空

栈的使用

int main()
{Stack st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);while (!StackEmpty(&st)){int tmp = StackTop(&st);StackPop(&st);printf("%d ", tmp);}StackDestroy(&st);
}

首先我们向栈中入了四个元素,1,2,3,4

若我们要将它们全部打印出来只能向上面一样写个循环

先判断若栈不为空,则循环继续

先保存栈顶元素到tmp中

将栈顶元素删除(这样才能取到下一个元素)

然后打印我们刚刚取到的栈顶元素(也就是刚刚删除掉的元素)

最后别忘了将栈销毁,防止内存泄漏

完整代码

Stack.h

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.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 
int StackEmpty(Stack* ps);// 销毁栈 
void StackDestroy(Stack* ps);

Stack.c

#include "Stack.h"void StackInit(Stack* ps)
{assert(ps);ps->_a = NULL;ps->_capacity = 0;ps->_top = 0;
}void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->_capacity == ps->_top){int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);if (tmp == NULL){perror("realloc fail\n");return;}ps->_a = tmp;ps->_capacity = newcapacity;}ps->_a[ps->_top] = data;ps->_top++;
}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;
}int StackEmpty(Stack* ps)
{assert(ps);return ps->_top == 0;
}void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_capacity = 0;ps->_top = 0;
}

Test.c

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

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

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

相关文章

书单 | 6本AI领域名家名作,大模型时代,趁风而起!

–文末赠书– 大模型时代&#xff0c;想抓住风口吗&#xff1f; 本期书单就来分享6本AI领域名家名作&#xff0c;给大家把大模型时代那些事儿讲清楚&#xff01; 放心&#xff0c;入门的同学也可以从最基础的学起~~ 快来看看有哪些书吧…… 01 ▊《多模态大模型&#xff1…

全栈开发之路——前端篇(5)组件间通讯和接口等知识补充

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 第二篇&#xff1a;介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇&#xff1a;setup语法&#xff0c;设置响应式数据。 第四篇&#xff1a;数据绑定、计算属性和watch监视 辅助文档&…

免费试用Gemini 1.5 Pro

https://aistudio.google.com/app/prompts/new_chat 人工智能学习网站 https://chat.xutongbao.top

如果出现一个工具,可以让前端开发彻底不用再手写UI,这个工具意义大吗?干货!

求这样的一个工具&#xff0c;可以让后端开发、嵌入式开发、产品经理、UI设计师都能用&#xff0c;注意&#xff0c;不是在一个简单的静态页生成&#xff0c;也不是类似飞冰那种 generator &#xff0c;而是真正让设计师和开发者在各自的那侧达成自治&#xff0c;可以做到吗&am…

The 2024 International Collegiate Programming Contest in Hubei Province, China

目录 H. Genshin Impact Startup Forbidden III K. Points on the Number Axis B I. Colorful Tree 估计还会补D&#xff0c;I不补了&#xff0c;补不动了 H. Genshin Impact Startup Forbidden III 对于一个有鱼的池塘&#xff0c;有周围与自己本身五个关键位置可以捕获当…

大模型微调之 在亚马逊AWS上实战LlaMA案例(三)

大模型微调之 在亚马逊AWS上实战LlaMA案例&#xff08;三&#xff09; 使用 QLoRA 增强语言模型&#xff1a;Amazon SageMaker 上 LLaMA 2 的高效微调 语言模型在自然语言处理任务中发挥着关键作用&#xff0c;但训练和微调大型模型可能会占用大量内存且耗时。在本文中&…

『ZJUBCA Collaboration』WTF Academy 赞助支持

非常荣幸宣布&#xff0c;浙江大学区块链协会收到WTF Academy的赞助与支持&#xff0c;未来将共同开展更多深度合作。 WTF Academy是开发者的Web3开源大学&#xff0c;旨在通过开源教育让100,000名开发者进入到Web3。截止目前&#xff0c;WTF开源教程在GitHub收获超15,000 ⭐&a…

安装docker20.10.18版本步骤

安装docker20.10.18版本步骤 准备低版本安装包 #安装20.10.18版本的dockercd /opt #切换目录#上传需要的docker20.10.18.zip安装包unzip docker20.10.18.zip #解压cd docker20.10.18/ #切换目录yum install -y *.rpm #安装systemctl enable --now docker.service #开机自启并…

How Linux Works I - How Linux Start Up

目录 Linux如何启动&#xff1f; 启动信息 内核启动初始化与启动选项 写在前面&#xff1a;上一个专栏中我写完了内核源码层面看Linux&#xff0c;我们把抽象层拉高一点&#xff0c;看看Linux是如何工作的&#xff01; Linux如何启动&#xff1f; BIOS&#xff08;Basic Inpu…

Milvus向量数据库(一)Milvus存储byte[]类型源向量数据

两种路线&#xff1a; 第一种是把byte[]转换为List< float >&#xff0c;然后存储到Milvus的floatVector中第二种是把byte[]转换为ByteBuffer&#xff0c;然后存储到Milvus的BinaryVector中 步骤&#xff1a; 我先用的是第一种&#xff0c;但是在转换float过程中&…

道可道,非常道,名可名,非常名;学习道德经新解读!打破思想钢印——早读(逆天打工人爬取热门微信文章解读)

你读过道德经吗? 引言Python 代码第一篇 洞见 原来这就是&#xff1a;穷人的思想钢印第二篇 人民日报 来了&#xff01;新闻早班车要闻社会政策 结尾 知识始于好奇 终于智慧 好奇心驱使我们探索 而智慧则是自由思想的结晶 引言 玄之又玄 众妙之门 今天真的是大开我的眼界 我之…

W801学习笔记二十四:NES模拟器游戏

之前已经实现了NES模拟器玩游戏。W801学习笔记九&#xff1a;HLK-W801制作学习机/NES游戏机(模拟器) 现在要在新版本掌机中移植过来。 1、把NES文件都拷贝到SD卡中。 这回不会受内存大小限制了。我这里拷贝了4个&#xff0c;还可以拷贝更多。 2、应用初始化中&#xff0c;加载…