数据结构·一篇搞定栈!

news2024/11/28 18:39:28

好久不见,超级想念
废话不多说,直接看
请添加图片描述

引言

在数据结构的大家族中,栈(Stack)是一种非常重要的线性数据结构,它的特点是后进先出(LIFO,Last In First Out)。栈在程序设计中有着广泛的应用,比如函数调用栈、浏览器的前进后退功能等。本文将详细介绍栈的基本概念、操作以及使用C语言实现栈的方法。

栈的基本概念

栈是一种只能在一端(称为栈顶,top)进行插入和删除操作的线性表。在栈中,允许插入和删除的一端称为栈顶,另一端称为栈底(bottom)。栈没有后驱结点,插入和删除运算都只能在栈顶进行。

栈的实现

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

在这里插入图片描述

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈

typedef int STDataType;
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;

我只做部分讲解,不太难哈哈哈

#include <stdio.h>  
#include <stdlib.h>  
#include <stdbool.h>  
  
// 定义栈的数据类型  
typedef int STDataType;  
  
// 定义栈的结构体  
typedef struct Stack  
{  
    STDataType* _a;  
    int _top; // 栈顶  
    int _capacity; // 容量  
} Stack;  
  
// 初始化栈  
void StackInit(Stack* ps)  
{  
    ps->_a = NULL;  
    ps->_top = -1;  
    ps->_capacity = 0;  
}  
// 扩容栈  
void StackResize(Stack* ps, int newCapacity)  
{  
    STDataType* temp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newCapacity);  
    if (!temp)  
    {  
        exit(-1); // 内存分配失败,退出程序  
    }  
    ps->_a = temp;  
    ps->_capacity = newCapacity;  
}  

在函数中,首先通过realloc函数重新分配内存,将栈的数据数组_a扩充到新的容量newCapacity。realloc函数的第一个参数是原有的数据数组_a,第二个参数是新的容量newCapacity乘以每个元素的大小。

接着,将realloc返回的新内存地址赋值给临时指针变量temp。如果realloc函数分配内存失败,temp将为NULL。在这种情况下,通过if语句检查temp是否为NULL,如果是,则调用exit(-1)函数退出程序,表示内存分配失败。

如果realloc函数成功分配了新的内存空间,将临时指针temp指向的内存地址赋值给栈结构体指针ps的数据数组_a,以此来更新栈的数据数组。

最后,将新的容量newCapacity赋值给栈结构体指针ps的_capacity,表示栈的容量已经扩充到新的值。这样,栈就成功扩容了。

// 入栈  
void StackPush(Stack* ps, STDataType data)  
{  
    if (ps->_top == ps->_capacity - 1) // 栈满,扩容  
    {  
        int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;  
        StackResize(ps, newCapacity);  
    }  
    ps->_a[++(ps->_top)] = data; // 先移动指针再赋值  
}  

在函数中,首先通过条件判断检查栈是否已满。判断条件为栈顶指针_top是否等于栈的容量_capacity减去1。如果栈已满,即栈顶指针指向了最后一个位置,则需要对栈进行扩容。

在扩容的部分,首先计算新的容量newCapacity。如果栈当前容量为0(即栈为空),则新容量设为4;否则新容量设为当前容量的两倍。接着调用StackResize函数,将栈结构体指针ps和新容量newCapacity作为参数,进行栈的扩容操作。

如果栈未满,或者扩容完成后,接着执行入栈操作。将要入栈的数据data存入栈的数据数组_a中,存储位置为栈顶指针_top的下一个位置,即++(ps->_top)。这里使用了前置递增运算符,先将栈顶指针_top加1,然后再将数据data存入该位置,表示先移动指针再赋值。

通过这段代码,实现了将数据入栈的功能,并在栈满时自动扩容,保证了栈的容量能够满足存储需求。

// 出栈  
void StackPop(Stack* ps)  
{  
    if (!StackEmpty(ps)) // 栈不为空  
    {  
        ps->_top--;  
        // 如果栈中元素过少,可以考虑缩容,这里省略  
    }  
}  
  
// 获取栈顶元素  
STDataType StackTop(Stack* ps)  
{  
    if (!StackEmpty(ps))  
    {  
        return ps->_a[ps->_top];  
    }  
    return -1; // 栈为空,返回-1或其他错误标识  
}  
  
// 获取栈中有效元素个数  
int StackSize(Stack* ps)  
{  
    return ps->_top + 1;  
}  
  
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0  
int StackEmpty(Stack* ps)  
{  
    return ps->_top == -1;  
}  
  
// 销毁栈  
void StackDestroy(Stack* ps)  
{  
    free(ps->_a);  
    ps->_a = NULL;  
    ps->_top = -1;  
    ps->_capacity = 0;  
}  
  
int main()  
{  
    Stack s;  
    StackInit(&s);  
  
    // 入栈操作  
    StackPush(&s, 1);  
    StackPush(&s, 2);  
    StackPush(&s, 3);  
  
    // 获取栈顶元素  
    printf("栈顶元素为:%d\n", StackTop(&s));  
  
    // 出栈操作  
    StackPop(&s);  
    printf("出栈后,栈顶元素为:%d\n", StackTop(&s));  
  
    // 获取栈中有效元素个数  
    printf("栈中有效元素个数为:%d\n", StackSize(&s));  
  
    // 销毁栈  
    StackDestroy(&s);  
  
    return 0;  
}

在这里插入图片描述

例题

括号匹配

#define MAX_SIZE 10000

typedef struct {
    char data[MAX_SIZE];
    int top;
} Stack;

void initStack(Stack *stack) {
    stack->top = -1;
}

bool isEmpty(Stack *stack) {
    return stack->top == -1;
}

bool isFull(Stack *stack) {
    return stack->top == MAX_SIZE - 1;
}

void push(Stack *stack, char c) {
    if (isFull(stack)) {
        printf("Stack overflow\n");
    } else {
        stack->data[++stack->top] = c;
    }
}

char pop(Stack *stack) {
    if (isEmpty(stack)) {
        printf("Stack underflow\n");
        return '\0';
    } else {
        return stack->data[stack->top--];
    }
}

bool isValid(char *s) {
    Stack stack;
    initStack(&stack);

    for (int i = 0; s[i] != '\0'; i++) {
        if (s[i] == '(' || s[i] == '{' || s[i] == '[') {
            push(&stack, s[i]);
        } else {
            if (isEmpty(&stack)) {
                return false;
            }
            char left = pop(&stack);
            if ((s[i] == ')' && left != '(') || (s[i] == '}' && left != '{') || (s[i] == ']' && left != '[')) {
                return false;
            }
        }
    }

    return isEmpty(&stack);
}

这段代码定义了一个基于数组的栈结构,并实现了栈的基本操作(初始化、判断是否为空、判断是否已满、压栈、弹栈),以及一个isValid函数,用于检查一个给定的字符串s中的括号是否有效。

栈的定义和操作

  1. 栈的定义:使用了一个结构体Stack,其中包含一个字符数组data(用于存储栈元素)和一个整数top(用于表示栈顶的位置)。
  2. 初始化:initStack函数将栈顶指针top初始化为-1,表示栈为空。
  3. 判断是否为空:isEmpty函数检查top是否为-1。
  4. 判断是否已满:isFull函数检查top是否等于MAX_SIZE - 1。
  5. 压栈:push函数在栈未满时将字符c压入栈中。
  6. 弹栈:pop函数在栈非空时弹出栈顶元素,并返回该元素。

isValid函数分析
isValid函数用于检查一个字符串s中的括号是否有效。它使用前面定义的栈结构,并遵循以下逻辑:

  1. 遍历字符串s中的每个字符。
  2. 如果遇到左括号((、{ 或 [),则将其压入栈中。
  3. 如果遇到右括号()、} 或 ]),则执行以下步骤:
    首先检查栈是否为空。如果为空,说明没有对应的左括号可以匹配,返回false。
    然后弹出栈顶元素(即最近压入的左括号),检查它是否与当前的右括号匹配。如果不匹配,返回false。
  4. 遍历完字符串后,如果栈为空,则说明所有括号都有效匹配,返回true;否则,返回false。

注意事项

  1. 代码中使用的MAX_SIZE定义了栈的最大容量,但请注意,这个值在实际使用中可能需要调整,以确保足够大以处理可能的输入字符串。
  2. pop函数在栈空时返回\0,这只是一个表示错误的标记。在实际应用中,可能还需要考虑其他错误处理机制。
  3. 字符串s只包含括号字符,没有考虑其他字符(如字母、数字或空格)。如果输入字符串包含这些字符,isValid函数可能无法正确工作。
  4. isValid函数只考虑了三种括号类型(圆括号、大括号和方括号),并且假设它们的匹配是成对出现的。如果输入字符串包含其他类型的括号或括号不成对出现,函数将无法正确工作。

那么看到这里,这篇小文章就快结束了哦
不要忘了今天是母亲节,大家不要忘了问候自己的妈妈哦,嘻嘻嘻
在这里插入图片描述
那么各位,我们下期再见咯,今天没有预告哈哈哈

在这里插入图片描述

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

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

相关文章

docker八大架构之单机架构

单机架构 什么是单机架构&#xff1f; 单机架构指的是应用服务和数据库服务公用同一台服务器。如下边两个图所示&#xff0c;当我们进行购物时&#xff0c;所有的物品信息和用户信息都是在同一个服务器下进行运行的&#xff0c;之所以称为单机架构就是因为它所有的操作是在同…

【AMBA Bus ACE 总线10 -- ACE Barrier transaction 详细介绍】

请阅读【AMBA Bus ACE 总线与Cache 专栏 】 欢迎学习:【嵌入式开发学习必备专栏】 文章目录 ACE AxBARAxBAR[1:0]的值及含义屏障的用途和重要性ACE AxBAR 用于表明是否是一个barrier 的transaction,对于它我们只需要有个简单的了解即可,现在已经不建议在transaction的层面上…

Unity VR在编辑器下开启Quest3透视(PassThrough)功能

现在有个需求是PC端串流在某些特定时候需要开启透视。我研究了两天发现一些坑,记录一下方便查阅,也给没踩坑的朋友一些思路方案。 先说结论,如果要打PC端或者在Unity编辑器中开启,那么OpenXR当前是不行的可能还需要一个长期的过程,必须需要切换到Oculus。当然Unity官方指…

异常处理/ROS2异常处理模块源码解读与浅析

文章目录 概述ros2/rcutils/src/error_handling模块自身异常处理错误状态结构与存储本模块初始化错误状态的设置错误状态的获取错误状态的清理不丢失旧错误状态把手段还原为目的其他 概述 本文从如下几个方面对 ROS2.0 中 rcutils 库 error_handling 错误处理模块的源码进行解…

整理好的中债国债3年期到期收益率数据集(2002-2023年)

01、数据简介 国债&#xff0c;又称国家公债&#xff0c;是由国家发行的债券&#xff0c;是中央ZF为筹集CZ资金而发行的一种ZF债券&#xff0c;是中央ZF向投资者出具的、承诺在一定时期支付利息和到期偿还本金的债权债务凭证。 中债&#xff0c;是指由中国中债登记结算有限责…

报表-接口类型的数据源

1、配置 在数据中进行如下配置 配置格式&#xff0c;换行的方式 #API $.data[0].children http://192.168.1.1:9200/apis/getInfo 行1&#xff1a;固定写法&#xff0c;标识这是一个接口类型的数据集 行2&#xff1a;JSONPath格式字符串&#xff0c;对接口的数据进行取值。…

轮转数组 与 消失的数字

轮转数组 思路一 创建一个新内存空间&#xff0c;将需轮转的数依次放入&#xff0c;之后在把其它数放入 代码&#xff1a; void rotate(int* nums, int numsSize, int k) {k k % numsSize;// 确定有效的旋转次数if(k 0)return;int* newnums (int*)malloc(sizeof(int) * nu…

Linux与Windows互传文件【笔记】

Linux与Windows互传文件【笔记】 前言前言推荐Linux与Windows互传文件首先确保Windows安装ssh如何传送文件问题 最后 前言 这是陈旧已久的草稿2023-05-10 00:01:24 这个是准备把计组课程华为智能计组的&#xff0c;传输文件。 最后发现&#xff0c;好像没有实现了。 现在202…

云器Lakehouse:Multi-Cluster弹性架构如何实现湖上高并发低延迟分析

导读 在当今快速发展的大数据时代&#xff0c;数据平台的性能和效率对于企业来说至关重要。云器Lakehouse的Multi-Cluster弹性架构为我们提供了一种全新的视角&#xff0c;以应对数据湖上高并发和低延迟分析的挑战。本文将深入探讨云器Lakehouse如何通过其独特的技术理念和架构…

MySql软件安装

1.打开mysql官网网址 MySQL :: Download MySQL Community Server 2.本次针对版本8的图形化界面安装&#xff0c;下载成功后接下来对MySQL进行安装 3.图形化下载后有一个MSI文件 4.我们安装典型即可&#xff0c;选择第一个 5.选择数据库信息存放的路径&#xff0c;我默认放在C盘…

知识库优劣详解:牵牛易帮 VS HelpLook AI知识库

知识库不仅可以帮助企业有效管理知识&#xff0c;还能提高员工工作效率和质量&#xff0c;因此越来越多的企业选择搭建知识库。在众多搭建知识库的工具中&#xff0c;有的企业会选择免费的牵牛易帮&#xff0c;有的则会更加倾向于付费的HelpLook AI知识库。其中的原因有很多。今…

U盘文件遇损?拯救“文件或目录损坏且无法读取”的秘籍!

在数字化时代&#xff0c;U盘已成为我们日常生活与工作中不可或缺的数据存储和传输工具。然而&#xff0c;有时我们可能会遇到一个非常令人沮丧的问题——U盘中的文件或目录突然损坏且无法读取。这种突发状况往往让人措手不及&#xff0c;甚至可能引发数据丢失的严重后果。那么…

第十二篇:数据库系统导论 - 探索数据管理的基石

数据库系统导论 - 探索数据管理的基石 1 引言 数据的力量&#xff1a;揭秘数据库系统的核心 在信息时代&#xff0c;数据无处不在&#xff0c;它们成为了企业和社会运作的基础。我们如何储存、检索、更新和维护这些数据&#xff0c;决定了我们能否从这些数据中获得力量。数据…

gin自定义验证器+中文翻译

gin自定义验证器中文翻译 1、说明2、global.go3、validator.go4、eg&#xff1a;main.go5、调用接口测试 1、说明 gin官网自定义验证器给的例子相对比较简单&#xff0c;主要是语法级别&#xff0c;便于入门学习&#xff0c;并且没有给出翻译相关的处理&#xff0c;因此在这里记…

Windows离线安装snmp服务

打开1里面有教程 选择“管理” 启动&#xff0c;发现不行 再把2拷贝到&#xff1a; 在启动就可以了&#xff08;查看服务&#xff1a;ctrlshiftEsc&#xff09;

JUC下的Java java.util.concurrent.Locks详解

java.util.concurrent.locks 包介绍 java.util.concurrent.locks 包是Java并发编程中非常重要的一个部分&#xff0c;它提供了比内置synchronized关键字更为灵活的锁机制&#xff0c;用于多线程环境下的同步控制。这个包中最核心的是Lock接口&#xff0c;以及一系列实现类&…

17 SPI FLASH读写

SPI 协议简介 SPI 即 Serial Periphera linterface 的缩写&#xff0c;顾名思义就是串行外围设备接口&#xff0c;主要用于与FLASH、实时时钟、AD 转换器等外设模块的通信&#xff0c;它是一种高速的全双工同步的通信总线。 SPI 设备分为主设备和从设备&#xff0c;SPI 通信必…

vs code中如何使用git

由于本地代码有了一些储备&#xff0c;所以想通过网址托管形式&#xff0c;之前一直使用了github&#xff0c;但是鉴于一直被墙&#xff0c;无法登录账号&#xff0c;所以选择了国内的gitee来作为托管网站。 gitee的网址&#xff1a;Gitee - 基于 Git 的代码托管和研发协作平台…

蓝桥杯-网络安全比赛(7)基础知识 HTTP、TTL、IP数据包、MSS、MTU、ARP、LLMNR、MDNS、NBNS。

1. IP中TTL值能够给我提供什么信息&#xff1f;2. IP头部中标志、13位偏移、32位源IP地址、目标IP、IP数据包格式&#xff0c;有多少字节3. IP头部中的16位标识是什么&#xff1f;4. MSS 和MTU分别有多大&#xff1f;5. 怎么获取路由IP信息&#xff1f;PING、NSLOOKUP、TRACERT…

记忆化搜索专题

前言 如果要记忆化搜索的话&#xff0c;如果数据是10的九次方&#xff0c;我们不可能开一个那么大的数组来存储&#xff0c;所以我们要学会用map来存储 leecode1553 class Solution {unordered_map<int, int> memo; public:int minDays(int n) {if (n < 1) {return n;…