栈的实现及OJ练习(c语言)

news2024/11/20 12:43:47

目录

前言

栈的实现(数组栈)

初始化栈

入栈

出栈

获取栈顶元素

获取栈中有效元素个数

检测栈是否为空

销毁栈

最终代码:

选择练习

栈的OJ题

前言

我们在之前已经学习了顺序表和链表的概念,它们有这样的优缺点:

链表的优势:

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

2、按照申请释放、合理利用空间、不存在浪费

链表的劣势:

1、下标随机访问不方便,最坏时间复杂为O(n)

顺序表的优势:

1、支持下标随机访问,最坏时间复杂度为O(1)

顺序表的劣势:

1、头部或中间插入删除效率低,要挪动数据,最坏时间复杂度为O(n)

2、空间不够需要扩容,扩容有一定的消耗,且可能存在一定的空间浪费

3、只适合尾插尾删

        我们之所以要学习各种各样的数据结构,是因为在实际应用中要选取最适合的一种数据结构去实现我们的需求,它们各有各存在的意义,今天我们来学习两种新的数据结构栈:

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

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

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

栈的实现数组栈

概念:使用数组作为底层数据结构实现的栈

优势:

1. 简单高效:数组是一种连续的内存结构,可以直接通过索引访问元素。这使得在数组上进行栈操作(入栈和出栈)非常高效,时间复杂度为O(1)。相比于链表实现的栈,在插入和删除元素时不需要频繁地分配和释放内存。

2. 随机访问:由于数组具有随机访问的特性,我们可以通过索引快速地访问任意位置上的元素。这对于某些应用场景非常重要,例如需要查看或修改特定位置上的数据。

3. 存储连续性:由于数组是一段连续的内存空间,它能够更好地利用计算机硬件缓存系统带来的性能优势。相比之下,链表中每个节点可能分散在不同位置上,并且在遍历时会产生额外开销。

4. 空间效率:使用数组实现栈通常比使用链表实现占用更少的内存空间。链表节点除了保存数据本身外还需要额外空间来保存指向下一个节点地址等信息。

5. 实现简单:相对而言,在编写代码时使用基本类型(如整数、字符等)组成的简单静态数据结构(如数组)要比使用动态数据结构(如链表)更容易理解和实现。

注意事项:插入元素时需要提前确定存储空间的大小,并且如果栈满了,无法再添加新元素。这种情况下可能需要重新分配更大空间的数组并进行数据迁移操作。相比之下,链表可以动态地分配内存来适应不断变化的需求。

初始化栈

// 初始化栈
void StackInit(ST* pst)
{
    //首先要指向一个栈
	assert(pst);

	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;    //令pop表示栈顶元素的下一个元素的下标
}

注意事项:

关于top的初始值有两种情况:        

1、当top=0时,表示下一个要插入元素的位置,top等于多少栈里就有多少个元素

再详细的我也解释不出来了┭┮﹏┭┮

2、当top=-1时,表示栈顶元素的下标,下标等于-1时表示栈中没有有效元素

结论:相比于0,使用-1进行初始化可以更好地反映出该对象尚未包含任何有效数据项,同时还与我们学习的数组下标的概念契合(即下标为0表示数组第一个数字而当top=0时并没有这种说法)此外还可以更方便地判断堆叠是否为空,并与其他约定或标识符保持一致,从而提高代码的可读性和健壮性。

入栈

//入栈
void STPush(ST* pst, STDataType x)
{
    //首先要指向一个栈
	assert(pst);
    
    //判断栈是否已满,如果栈满则申请新的内存空间
	if (pst->top == pst->capacity)
	{
		int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}

    //如果栈未满则进行入栈操作(若初始化时pop=-1,则下面两行代码交换执行顺序)
	pst->a[pst->top] = x;    //此时pop表示的是栈顶元素的下一个元素的下标 
	pst->top++;              //top表示的下标数++
}

出栈

//出栈
void STPop(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	//top<0表示栈为空,top=0表示没有元素入栈,存在这两种情况就不能执行出栈操作(可以提供的图理解)
	assert(pst->top > 0);

    //直接对top执行减减操作以获取实际数组元素下标
    pst->top--;
}

获取栈顶元素

// 获取栈顶元素
STDataType STTop(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	//top<0表示栈为空,top=0表示没有元素入栈,存在这两种情况就不能执行出栈操作(可以提供的图理解)
	assert(pst->top > 0);
	
    //当初始化top=0时,top的值与实际数组元素下标的值之间的关系是:实际下标 = top-1
    //所以这里要进行减一操作得到实际的数组元素下标后再输出
	return pst->a[pst->top - 1];
}

获取栈中有效元素个数

//获取栈中有效元素个数
int STSize(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
    
    //初始化top=0,则top等于多少栈中就有多少个元素
	return pst->top;
}

检测栈是否为空

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int STEmpty(ST* pst)
{
	//首先要指向一个栈
	assert(pst);
	
    //如果pst->top不为空则返回结果为真,为空则返回假
	return pst->top == NULL;
}

销毁栈

// 销毁栈
void STDestroy(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	
    //正常操作不再过多复述
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

最终代码:

Stack.h文件:

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

//支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;	  //表示栈顶位置
	int capacity; //栈的容量
}ST;

// 初始化栈
void STInit(ST* ps);

// 销毁栈
void STDestroy(ST* ps);

// 入栈
void STPush(ST* ps, STDataType data);

// 出栈
void STPop(ST* ps);

// 获取栈顶元素
STDataType STTop(ST* ps);

// 获取栈中有效元素个数
int STSize(ST* ps);

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int STEmpty(ST* ps);

Stack.c文件

#include "Stack.h"

// 初始化栈
void STInit(ST* pst)
{
    //首先要指向一个栈
	assert(pst);

	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;    //令pop表示栈顶元素的下一个元素的下标
}

// 销毁栈
void STDestroy(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* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}

    //如果栈未满则进行入栈操作(若初始化时pop=-1,则下面两行代码交换执行顺序)
	pst->a[pst->top] = x;    //此时pop表示的是栈顶元素的下一个元素的下标 
	pst->top++;              //top表示的下标数++
}

//出栈
void STPop(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	//top<0表示栈为空,top=0表示没有元素入栈,存在这两种情况就不能执行出栈操作(可以提供的图理解)
	assert(pst->top > 0);

    //直接对top执行减减操作以获取实际数组元素下标
    pst->top--;
}

// 获取栈顶元素
STDataType STTop(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	//top<0表示栈为空,top=0表示没有元素入栈,存在这两种情况就不能执行出栈操作(可以提供的图理解)
	assert(pst->top > 0);
	
    //当初始化top=0时,top的值与实际数组元素下标的值之间的关系是:实际下标 = top-1
    //所以这里要进行减一操作得到实际的数组元素下标后再输出
	return pst->a[pst->top - 1];
}

//获取栈中有效元素个数
int STSize(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
    
    //初始化top=0,则top等于多少栈中就有多少个元素
	return pst->top;
}

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int STEmpty(ST* pst)
{
	//首先要指向一个栈
	assert(pst);
	
    //如果pst->top不为空则返回结果为真,为空则返回假
	return pst->top == NULL;
}

test.c文件: 

#include "Stack.h"

int main()
{
	ST s;
    //初始化栈
	STInit(&s);
    //入栈
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);

    //出栈
	while (!STEmpty(&s)) //只要栈不为空就一直出栈
	{
		printf("%d ", STTop(&s)); //打印每一个栈顶元素
		STPop(&s); //打印完成后就让该栈顶元素出栈
	}

	printf("\n");
	return 0;
}

选择练习

1. 一个栈的初始状态为空。现将元素 1 2 3 4 5 A B C D E 依次入栈,然后再依次出栈,则元素出栈的顺序是(
A、 12345ABCDE
B、 EDCBA54321
C、 ABCDE12345
D、 54321EDCBA

答案:B 

解析:栈中的元素遵循后进先出的原则

2. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A、 1,4,3,2
B、 2,3,4,1
C、 3,1,4,2
D、 3,4,2,1

答案: C

解析:请自行演算🤡

栈的OJ题

20. 有效的括号 - 力扣(LeetCode)

具体解题思路如下图所示:

括号不仅仅是数量匹配,顺序也要匹配 

文字解释: 读取到左括号就放入栈中,读取到右括号就将其对应的左括号,如果转换后得到的的左括号与栈中存放的左括号相同(实际上比较的是ASCII码)则证明有一对儿匹配的内容括号,此时将原来栈中存放的左括号出栈,然后进行下一次的读取操作

字符串中如果第一个是右括号就直接返回假 

//右括号转换为左括号
char pairs(char a) 
{
    if (a == '}') 
    {
        return '{';
    }

    if (a == ']')
    {
         return '[';
    }

    if (a == ')') 
    {
        return '(';
    }
        
    return 0;
}

bool isValid(char* s) 
{
    //获取数组长度
    int n = strlen(s);

    //如果数量不满足成对情况则直接返回flase
    if (n % 2 == 1) 
    {
        return false;
    }

    //stk数组用来表示存储左括号的栈(起始为空栈),top表示下一个要插入元素的位置
    int stk[n + 1], top = 0;

    //逐个读取原字符串中的字符并进行循环匹配
    for (int i = 0; i < n; i++) 
    {
        //读取源自符串中的内容,如果此时的字符为左括号则ch=0,为右括号则ch存储与之相对的左括号
        char ch = pairs(s[i]);
        if (ch) 
        {
            //一旦存在一对不匹配的情况则直接返回flase
            if (top == 0 || stk[top - 1] != ch) 
            {
                return false;
            }
            //如果匹配则将栈顶元素出栈(左括号出栈)
            top--;
        } 
        //如果ch为空证明此时读取的是左括号,将入栈以便下一次的判断
        else
        {
            stk[top++] = s[i];
        }
    }

    //如果源自符串中括号的顺序和数量均匹配,则栈中最后的结果为空,top==0的结果为真
    return top == 0;
}

~未完待续~

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

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

相关文章

周年纪念篇

一周年纪念&#xff01; 凌晨逛手机版csdn时才突然发现已经错过一周年了&#xff0c;但我当闰年来纪念一下不过分吧hhh 浅浅的整些怀念的东西吧&#xff01; 这是人生第一段代码&#xff1a;不是hello world写不起&#xff0c;而是纯爱单推人更有性价比。 有这段代码在&#x…

Zabbix Proxy分布式监控

目录 Zabbix Proxy简介 实验环境 proxy端配置 1.安装仓库 2.安装zabbix-proxy 3.创建初始数据库 4.导入初始架构和数据&#xff0c;系统将提示您输入新创建的密码 5.编辑配置文件 /etc/zabbix/zabbix_proxy.conf&#xff0c;配置完成后要重启。 agent客户端配置 zabbix…

可燃气体监测仪|燃气管网监测解决办法

可燃气体监测仪是城市生命线中&#xff0c;燃气监测运行系统的前端监测设备&#xff0c;其主要作用是对燃气管网的安全状况进行实时监测。燃气管道在使用过程中&#xff0c;由于老化、裂纹、锈蚀等问题&#xff0c;容易导致燃气出现泄漏问题&#xff0c;从而引发一系列的安全事…

强烈 推荐 13 个 Web前端在线代码IDE

codesandbox.io&#xff08;国外&#xff0c;提供免费空间&#xff09; 网址&#xff1a;https://codesandbox.io/ CodeSandbox 专注于构建完整的 Web 应用程序&#xff0c;支持多种流行的前端框架和库&#xff0c;例如 React、Vue 和 Angular。它提供了一系列增强的功能&…

GSVA,GSEA,KEGG,GO学习

目录 GSVA 1&#xff1a;获取注释基因集 2&#xff1a;运行 GSEA 1,示例数据集 2,运行 GSEA_KEGG富集分析 GSEA_GO富集分析 DO数据库GSEA MSigDB数据库选取GSEA KEGG 1&#xff1a;运行 2&#xff1a;绘图 bar图 气泡图 绘图美化 GO GSVA 1&#xff1a;获取注…

springboot实现在线人数统计

在线人数统计 笔者做了一个网站&#xff0c;需要统计在线人数。 在线有两种&#xff1a; 一、如果是后台系统如果登录算在线&#xff0c;退出的时候或者cookie、token失效的时候就算下线 二、如果是网站前台&#xff0c;访问的时候就算在线 今天我们来讲一下第2种情况&…

【论文解读】FFHQ-UV:用于3D面部重建的归一化面部UV纹理数据集

【论文解读】FFHQ-UV 论文地址&#xff1a;https://arxiv.org/pdf/2211.13874.pdf 0. 摘要 我们提出了一个大规模的面部UV纹理数据集&#xff0c;其中包含超过50,000张高质量的纹理UV贴图&#xff0c;这些贴图具有均匀的照明、中性的表情和清洁的面部区域&#xff0c;这些都是…

损失函数(Loss Function)与代价函数(Cost Function)、目标函数(Objective Function)区别

损失函数定义在单个样本上&#xff0c;算的是一个样本的误差。 代价函数定义在整个训练集上&#xff0c;是所有样本误差的平均&#xff0c;也就是损失函数的平均。 目标函数定义为最终需要优化的函数&#xff0c;等于经验风险 结构风险&#xff08;也就是Cost Function 正则化…

Windows10下Mysql8.0安装教程

文章目录 1.下载Mysql8.02.解压Mysql安装包到指定目录3.初始化Mysql服务4.安装Mysql服务5.启动Mysql服务6.登录Mysql服务7.修改Mysql密码8.重启Mysql服务停止服务启动服务 1.下载Mysql8.0 链接&#xff1a;https://pan.baidu.com/s/1uP2xZj8g05xg-oHX_nfnmA 提取码&#xff1a;…

基于SSM的在线投稿系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

PC端使子组件的弹框关闭

子组件 <template><el-dialog title"新增部门" :visible"showDialog" close"close"> </el-dialog> </template> <script> export default {props: {showDialog: {type: Boolean,default: false,},},data() {retu…

计算机毕业设计选题推荐-个人健康微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

C/C++字符判断 2021年12月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C字符判断 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C字符判断 2021年12月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 对于给定的字符&#xff0c;如果该字符是大小写字母或…

团结引擎已全面支持 OpenHarmony 操作系统

Unity 中国宣布与开放原子开源基金会达成平台级战略合作。 据称团结引擎已全面支持 OpenHarmony 操作系统&#xff0c;同时将为 OpenHarmony 生态快速带来更多高品质游戏与实时 3D 内容。Unity 称现在用户可以 “在 OpenHarmony 框架中感受到与安卓和 iOS 同样丝滑的游戏体验”…

网络运维与网络安全 学习笔记2023.11.18

网络运维与网络安全 学习笔记 第十九天 今日目标 冲突域和交换机工作原理、广播域和VLAN原理 VLAN配置、TRUNK原理与配置、HYBRID原理与配置 冲突域和交换机工作原理 冲突域概述 定义 网络设备发送的数据&#xff0c;产生冲突的区域&#xff08;范围&#xff09; 对象 “数…

HUAWEI华为MateBook X 2020款i5集显(EUL-W19P)原装出厂Windows10系统

链接&#xff1a;https://pan.baidu.com/s/1eZuLjarWH2PjAWVqMWnzjQ?pwd2374 提取码&#xff1a;2374 原厂系统自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、华为电脑管家等预装程序

LeetCode(28)盛最多水的容器【双指针】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 盛最多水的容器 1.题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水…

python——第十天

今日目标&#xff1a; 常见排序和查找 常见排序和查找: 冒泡排序 选择排序 插入排序 选择排序&#xff1a; 假设"第一个值"是最小值&#xff0c;就要每一轮找到真正的最小值&#xff0c;并且和假设的这个值交换 [1, 3, 2, 10, -8, 9, -30, 7] 1、 [-30, 3, 2, 10, -8…

微信小程序开发学习——页面布局、初始导航栏与跳转

1.盒模型 要求实现效果如图所示&#xff1a; 所有WXML元素都可以看作盒子&#xff0c;在WXSS中"box model”这一术语是用来设计和布局时使用盒模型本质上是一个盒子&#xff0c;封装周围的WXML元素它包括: 边距&#xff0c;边框&#xff0c;填充和实际内容&#xff0c;模…

【C++】【Opencv】cv::warpAffine()仿射变换函数详解,实现平移、缩放和旋转等功能

仿射变换是一种二维变换&#xff0c;它可以将一个二维图形映射到另一个二维图形上&#xff0c;保持了图形的“形状”和“大小”不变&#xff0c;但可能会改变图形的方向和位置。仿射变换可以用一个线性变换矩阵来表示&#xff0c;该矩阵包含了六个参数&#xff0c;可以进行平移…