【初阶数据结构】栈

目录

  • 栈的概念及结构
  • 栈的实现
    • 栈的结构
    • 栈的初始化
    • 栈的销毁
    • 入栈
    • 出栈
    • 取栈顶元素
    • 判断栈是否为空
    • 取栈中元素个数
    • 代码测试
  • 完整代码
    • Stack.h
    • Stack.c
    • test.c

请添加图片描述

栈的概念及结构

  栈:是一种特殊的线性表,它只允许在固定的一端进行插入和删除元素的操作
  栈顶:进行数据插入和删除操作的那一端。
  栈底:栈顶的另一端就是栈底。
  压栈:栈的插入数据操作就叫压栈,也称进栈,入栈,入数据在栈顶
  出栈:栈的删除数据操作叫做出栈,出数据也在栈顶
  栈不能随机访问,只能在栈顶出入数据或访问数据。
  栈数据元素遵守后进先出LIFO(Last In First Out)的原则。
在这里插入图片描述

栈的实现

  栈同样有两种存储方式,一种是链式存储,叫做链栈,一种是顺序存储,叫做顺序栈,一般情况下,用顺序栈的方式比较多。是因为用顺序表来实现对栈的操作,一方面顺序表的扩容不会像链表一样要一个个节点去malloc那么频繁,内存空间也比较大,人们也不会特别在意空间浪费的问题,并且利用顺序表尾插尾删数据的代价比链表更小;另一方面顺序表的缓存利用率高,所以我们更倾向使用顺序栈。

栈的结构

  顺序表分为了静态顺序表和动态顺序表,但是为了方便扩容,一般都采用动态顺序表,同样的,顺序栈也分为了定长的静态栈和支持动态增长的栈,我们也主要来实现动态增长的栈。
  动态增长栈的结构如下:

typedef int STDataType;
typedef struct Stack
{STDataType* a;//顺序栈,即用数组实现,a指向栈底int top;int capacity;
}ST;

这里有一个问题需要注意:
我们定义的top是什么,指向哪里,应该初始化为什么
这是有两种情况:
情况一:
top指向栈顶的那个元素:

在这里插入图片描述
由上图可知,当top指向栈顶元素时,它初始化应该为-1,而不是0,因为这样才能使得栈中有一个元素的时候,top等于0且指向那一个元素。
情况二:
top指向栈顶的下一个元素:

在这里插入图片描述
如上图,当top指向栈顶的下一个元素时,它的初始化就为0,即top可看做栈中有效元素个数。

栈的初始化

  上面我们已经说了top的两种情况,这也对应着top的两种初始化,但其实两种初始化都是正确的,看你选择哪一种,这里我们选择第二种,top为0时栈空,相当于顺序表的size。
  初始化如下:

void STInit(ST* pst)//传址调用,才能修改形参中的值,用指针接收,不再多说
{assert(pst);//传参的指针不能为空pst->a = NULL;//可以先初始化为空,后面插入数据时在添加空间pst->top = 0;pst->capacity = 0;
}

栈的销毁

void STDestory(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->top = pst->capacity = 0;
}

入栈

  顺序栈只能在栈顶插入数据,其实也就等于顺序表的尾插,我们已经很清楚了,不清楚的可以再回去看看这个内容:顺序表。
  代码实现:

void STPush(ST* pst, STDataType x)
{assert(pst);if(pst->top == pst->capacity)//空间不够,要扩容{int newcapacity = pst->capacity == 0 ? 4 :2 * capacity;STDataType* tmp = realloc(pst->a, newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}pst->a = tmp;pst->capacity = newcapacity;}pst->a[pst->top] = x;pst->top++;
}

出栈

  同样的,出栈相当于顺序表的尾删,代码如下:

void STPop(ST* pst)
{assert(pst);assert(pst->top);//栈不能为空,为空则报错pst->top--;
}

取栈顶元素

  取栈顶元素也就等于读取数组中最后一个元素的值,要注意的是,下标是top还是top-1,由于我们定义的top是指向栈顶的下一个元素,所以下标应该为top-1.

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

判断栈是否为空

  判断栈是否为空,如果为空,则返回true,不为空,则返回false,此时我们用bool类型的函数去定义。我们上面在判断top的初始化时已经提到,当top == 0,则栈为空,代码如下:

bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
} 

取栈中元素个数

  上面说到,当我们初始化top = 0时,top就相当于size,即元素个数,只要返回top即可。

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

代码测试

  至此,栈的有关操作就结束了,最后也不要忘了调试所写代码是否正确。这里要注意的是,在打印栈的时候,我们不能像顺序表或者链表一样写一个打印函数出来,因为链表是后进先出的,当我们需要打印这个栈时,需要一个个出栈然后打印,代码如下:

while (!STEmpty(&s))//判断栈是否为空
{printf("%d ", STTop(&s));//先读取栈顶元素STPop(&s);//后取出栈顶元素
}

测试代码如下:
在这里插入图片描述

  当然,栈也不仅仅只能打印这一种,可以有很多种情况,具体要看是如何出栈和入栈的

比如说有三个数字1,2,3,进行出栈出栈操作
则三个数既可以先全部入栈再出栈,也可以边入栈边出栈,
则这三个数出栈后全部的可能序列为:
{3,2,1}、{2,3,1}、{2,1,3}、{1,2,3}、{1,3,2}

完整代码

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef int STDataType;typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;//初始化和销毁
void STInit(ST* pst);
void STDestory(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

#include"Stack.h"//初始化
void STInit(ST* pst)
{pst->a = NULL;pst->capacity = 0;pst->top = 0;
}//销毁
void STDestory(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->top = pst->capacity = 0;
}//入栈
void STPush(ST* pst, STDataType x)
{assert(pst);if (pst->top == pst->capacity){int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* new = (STDataType*)realloc(pst->a, newcapacity*sizeof(STDataType));if (new == NULL){perror("realloc fail");return;}pst->a = new;pst->capacity = newcapacity;}pst->a[pst->top] = x;pst->top++;
}//出栈
void STPop(ST* pst)
{assert(pst);assert(pst->top);pst->top--;
}//取栈顶元素
STDataType STTop(ST* pst)
{assert(pst);assert(pst->top);return pst->a[pst->top - 1];
}//判断栈是否为空
bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
}//获取元素个数
int STSize(ST* pst)
{assert(pst);return pst->top;
}

test.c

#include"Stack.h"void test()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);printf("栈中元素个数为:%d\n", STSize(&s));STPop(&s);STPop(&s);printf("取出栈顶元素:%d\n", STTop(&s));printf("栈中元素个数为:%d\n", STSize(&s));printf("打印栈中元素:");while (!STEmpty(&s)){printf("%d ", STTop(&s));STPop(&s);}STDestory(&s);
}int main()
{test();return 0;
}

  今天的内容到此结束啦,感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。

在这里插入图片描述

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

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

相关文章

Elasticsearch:向量相似度技术和评分

作者&#xff1a;来自 Elastic Valentin Crettaz 当需要搜索自由文本并且 CtrlF / CmdF 不再有效时&#xff0c;使用词法搜索引擎通常是你想到的下一个合理选择。 词汇搜索引擎擅长分析要搜索的文本并将其标记为可在搜索时匹配的术语&#xff0c;但在理解和理解被索引和搜索的…

TN3399(rk3399)开发板安装ubuntu 22.04

先安装20.04 从20.04升级到22.04后&#xff0c;没有声音驱动了&#xff0c;但是显示支持高清了 声音加个usb声卡就行,5块钱

x264 帧类型代价计算原理:slicetype_frame_cost 函数分析

slicetype_frame_cost 函数 函数功能 这个函数的核心是计算编码一系列帧(从 p0 到p1,以 b 为当前帧)的代价 cost,并根据这个代价 cost来辅助帧类型决策。它考虑了运动搜索的结果、帧间和帧内预测的成本,并且可以并行处理以提高效率。该函数在帧类型决策、MBtree 分析、场…

三.Ubuntu安装MySql数据库

三.Ubuntu安装MySql数据库 1.首先进入Console,登录Ubuntu系统后,更新源,命令:apt update,如图所示。 安装MySQL命令:apt install mysql-server 执行期间按回车,进行下一步,执行过程如图所示: 选择yes或no,此步选择yes 安装完成。 2.提高MySQL安全性,该命令…

苹果cms:生成网站地图报错站点Connection refused

我们搭建苹果cms站点之后&#xff0c;会使用SEO工具包生成网站地图&#xff0c;在这个过程中我们会遇到一个问题 Connection refused 注意&#xff1a;这个问题并不是数据库无法连接&#xff0c;而是redis关闭了&#xff0c;原因是由于站点开启了redis缓存机制&#xff0c;SEO请…

java项目之企业资产管理系统(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的企业资产管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 管理员功能有个人中心&…

pikachu靶场通关之csrf漏洞通关教程

目录 CSRF&#xff08;get型&#xff09; 1.打开网站&#xff0c;点击右上角提示 2.登录之后&#xff0c;点击修改个人信息 3.修改上述内容&#xff0c;打开抓包工具 4.抓到修改用户信息的数据包 5.构造虚假url&#xff0c;诱导用户点击 6.弹到修改后的界面 ​编辑 7.返…

跨ROS系统通信:使用TCP实现节点间的直连

当涉及到在机器人操作系统&#xff08;ROS&#xff09;环境中的通信时&#xff0c;标准做法通常是在同一个ROS网络内通过话题和服务进行。但在某些特定情况下&#xff0c;比如当你有两个分布在不同网络中的ROS系统时&#xff0c;标准的通信方法可能不太适用。此时&#xff0c;一…

偶然发现了Python的一个BUG。。。

一般情况下&#xff0c;dict(id1, **{id: 1})这句代码应该报TypeError。但如果在捕获了其他异常的情况下&#xff0c;再来执行这句代码&#xff0c;却是会报KeyError&#xff0c;如下图&#xff1a; Python3.10和Python3.9也能复现该情况&#xff0c;正当我摩拳踩掌&#xff0c…

QT客户端开发的注意事项

QT客户端开发是一个涉及图形用户界面&#xff08;GUI&#xff09;设计、网络编程、数据库交互等多个方面的复杂过程。以下是在进行QT客户端开发时应注意的一些关键事项&#xff0c;通过关注这些事项&#xff0c;可以提高QT客户端应用的质量和开发效率。北京木奇移动技术有限公司…

Python 机器学习 基础 之 监督学习 [决策树集成] 算法 的简单说明

Python 机器学习 基础 之 监督学习 [决策树集成] 算法 的简单说明 目录 Python 机器学习 基础 之 监督学习 [决策树集成] 算法 的简单说明 一、简单介绍 二、监督学习 算法 说明前的 数据集 说明 三、监督学习 之 决策树集成 1、随机森林 2、梯度提升回归树&#xff08;梯…

【Spring】AbstractApplicationContext源码解读

这个类源码一打开&#xff0c;我滴妈有一种我不卷了&#xff0c;我送外卖去了的感觉1500行 但是还是对你自己说 坚持坚持再坚持。35岁再送外卖也不迟。 这里我们先看这个抽象类的注释&#xff1a; Abstract implementation of the ApplicationContext interface. Doesnt man…