目录

一、栈的核心概念

什么是栈?

栈的核心特性

二、栈的基本操作

三、C 语言实现栈的两种方式

1. 顺序栈(基于数组实现)

实现代码

顺序栈的优缺点

2. 链式栈(基于链表实现)

实现代码

链式栈的优缺点

四、栈的典型应用场景

五、总结


栈(Stack)作为一种经典的线性数据结构,因其 "后进先出"(LIFO)的特性,在编程领域有着广泛的应用。无论是表达式求值、函数调用,还是括号匹配等场景,都能看到栈的身影。本文将带你用 C 语言从零实现栈,深入理解其工作原理与实际应用。

一、栈的核心概念

什么是栈?

栈是一种限定仅在表尾进行插入和删除操作的线性表。这里的 "表尾" 被称为栈顶(Top),另一端则称为栈底(Bottom)

栈的核心特性

栈最显著的特点是后进先出(Last In First Out,LIFO)

  • 最后进入栈的元素,最先被取出
  • 最先进入栈的元素,最后被取出

生活中的栈示例:

  • 叠放的书本,只能从最上方取书
  • 浏览器的历史记录,"后退" 功能总是返回上一个访问的页面
  • 手机应用的任务栈,最近打开的应用最先被关闭

二、栈的基本操作

栈的操作围绕栈顶进行,主要包括以下几种:

操作名称功能描述时间复杂度
initStack初始化栈O(1)
push向栈顶插入元素(入栈)O(1)
pop从栈顶移除元素(出栈)O(1)
peek获取栈顶元素(不删除)O(1)
isEmpty判断栈是否为空O(1)
isFull判断栈是否已满O(1)
size获取栈中元素个数O(1)

三、C 语言实现栈的两种方式

在 C 语言中,栈通常有两种实现方式:基于数组的顺序栈和基于链表的链式栈。

1. 顺序栈(基于数组实现)

顺序栈使用固定大小的数组作为底层存储,实现简单且访问高效。

实现代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define MAX_SIZE 100  // 栈的最大容量// 栈的结构体定义
typedef struct {int data[MAX_SIZE];  // 存储栈元素的数组int top;             // 栈顶指针(-1表示空栈)
} 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;
}// 入栈操作
bool push(Stack *stack, int value) {if (isFull(stack)) {printf("栈已满,无法入栈!\n");return false;}stack->data[++stack->top] = value;  // 先移动栈顶指针,再存入数据return true;
}// 出栈操作
bool pop(Stack *stack, int *value) {if (isEmpty(stack)) {printf("栈为空,无法出栈!\n");return false;}*value = stack->data[stack->top--];  // 先取出数据,再移动栈顶指针return true;
}// 获取栈顶元素
bool peek(Stack *stack, int *value) {if (isEmpty(stack)) {printf("栈为空,无法获取栈顶元素!\n");return false;}*value = stack->data[stack->top];return true;
}// 获取栈的大小
int size(Stack *stack) {return stack->top + 1;  // 栈顶索引+1即为元素个数
}// 打印栈中所有元素
void printStack(Stack *stack) {if (isEmpty(stack)) {printf("栈为空!\n");return;}printf("栈元素(从栈底到栈顶):");for (int i = 0; i <= stack->top; i++) {printf("%d ", stack->data[i]);}printf("\n");
}// 主函数示例
int main() {Stack stack;initStack(&stack);int value;// 测试入栈push(&stack, 10);push(&stack, 20);push(&stack, 30);printStack(&stack);  // 输出:10 20 30// 测试获取栈顶元素if (peek(&stack, &value)) {printf("栈顶元素:%d\n", value);  // 输出:30}// 测试出栈if (pop(&stack, &value)) {printf("出栈元素:%d\n", value);  // 输出:30}printStack(&stack);  // 输出:10 20printf("栈的大小:%d\n", size(&stack));  // 输出:2printf("栈是否为空:%s\n", isEmpty(&stack) ? "是" : "否");  // 输出:否return 0;
}
顺序栈的优缺点
  • 优点:实现简单,元素访问速度快(数组随机访问特性)
  • 缺点
    • 容量固定,无法动态扩容(可能溢出)
    • 内存利用率不高(可能浪费空间)

2. 链式栈(基于链表实现)

链式栈使用链表节点存储元素,可动态调整大小,内存利用率更高。

实现代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>// 链表节点结构体
typedef struct Node {int data;               // 节点数据struct Node *next;      // 指向下一个节点的指针
} Node;// 栈的结构体(链式栈)
typedef struct {Node *top;  // 栈顶指针(指向链表头节点)int size;   // 栈中元素个数
} LinkedStack;// 初始化栈
void initLinkedStack(LinkedStack *stack) {stack->top = NULL;stack->size = 0;
}// 判断栈是否为空
bool isLinkedStackEmpty(LinkedStack *stack) {return stack->top == NULL;
}// 入栈操作
void pushLinkedStack(LinkedStack *stack, int value) {// 创建新节点Node *newNode = (Node *)malloc(sizeof(Node));if (newNode == NULL) {printf("内存分配失败!\n");return;}newNode->data = value;newNode->next = stack->top;  // 新节点指向当前栈顶stack->top = newNode;        // 更新栈顶指针stack->size++;
}// 出栈操作
bool popLinkedStack(LinkedStack *stack, int *value) {if (isLinkedStackEmpty(stack)) {printf("栈为空,无法出栈!\n");return false;}Node *temp = stack->top;*value = temp->data;stack->top = stack->top->next;  // 更新栈顶指针free(temp);                     // 释放节点内存stack->size--;return true;
}// 获取栈顶元素
bool peekLinkedStack(LinkedStack *stack, int *value) {if (isLinkedStackEmpty(stack)) {printf("栈为空,无法获取栈顶元素!\n");return false;}*value = stack->top->data;return true;
}// 打印栈中元素
void printLinkedStack(LinkedStack *stack) {if (isLinkedStackEmpty(stack)) {printf("栈为空!\n");return;}printf("栈元素(从栈顶到栈底):");Node *current = stack->top;while (current != NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}// 销毁栈(释放所有节点内存)
void destroyLinkedStack(LinkedStack *stack) {Node *temp;while (stack->top != NULL) {temp = stack->top;stack->top = stack->top->next;free(temp);}stack->size = 0;
}// 主函数示例
int main() {LinkedStack stack;initLinkedStack(&stack);int value;// 测试入栈pushLinkedStack(&stack, 100);pushLinkedStack(&stack, 200);pushLinkedStack(&stack, 300);printLinkedStack(&stack);  // 输出:300 200 100// 测试获取栈顶元素if (peekLinkedStack(&stack, &value)) {printf("栈顶元素:%d\n", value);  // 输出:300}// 测试出栈if (popLinkedStack(&stack, &value)) {printf("出栈元素:%d\n", value);  // 输出:300}printLinkedStack(&stack);  // 输出:200 100printf("栈的大小:%d\n", stack.size);  // 输出:2// 销毁栈destroyLinkedStack(&stack);return 0;
}
链式栈的优缺点
  • 优点
    • 容量动态调整,无固定大小限制
    • 内存利用率高(按需分配)
  • 缺点
    • 实现相对复杂
    • 每个节点需要额外存储指针,内存开销略大
    • 访问速度不如顺序栈(链表顺序访问特性)

四、栈的典型应用场景

  1. 表达式求值:编译器使用栈处理数学表达式(如后缀表达式转换与计算)

  2. 括号匹配:检查代码中的括号是否正确配对(如()[]{}

    // 括号匹配示例函数
    bool isParenthesesValid(char *str) {Stack stack;initStack(&stack);int i = 0;while (str[i] != '\0') {if (str[i] == '(' || str[i] == '[' || str[i] == '{') {push(&stack, str[i]);  // 左括号入栈} else if (str[i] == ')' || str[i] == ']' || str[i] == '}') {if (isEmpty(&stack)) return false;  // 右括号多了char top;pop(&stack, &top);// 检查括号是否匹配if ((str[i] == ')' && top != '(') ||(str[i] == ']' && top != '[') ||(str[i] == '}' && top != '{')) {return false;}}i++;}return isEmpty(&stack);  // 左括号是否多了
    }
    
  3. 函数调用:程序运行时,函数调用信息(返回地址、局部变量等)通过栈存储

  4. 浏览器历史记录:实现 "前进"、"后退" 功能

  5. 撤销操作:文本编辑器中的撤销(Undo)功能

五、总结

栈作为一种基础数据结构,虽然操作简单,但应用场景广泛。在 C 语言中,我们可以根据实际需求选择实现方式:

  • 当元素数量固定且已知时,优先选择顺序栈(实现简单、效率高)
  • 当元素数量不确定或动态变化时,优先选择链式栈(内存利用率高)

掌握栈的原理与实现,不仅能帮助你解决实际编程问题,更为理解更复杂的数据结构(如树、图)奠定基础。建议结合实际场景多做练习,加深对栈的理解与应用。

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

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

相关文章

用户系统的架构设计与实现策略(二)

一个用户系统除了基本的用户业务功能&#xff0c;还应囊括用户的权限设计及其实现。这本文中我们将探讨一下关于用户权限的设计与实现方法论。 简介 在构建现代应用系统的过程中&#xff0c;很少有设计决策会像访问控制机制那样&#xff0c;对安全性、可扩展性和用户体验产生…

深度学习-逻辑回归

逻辑回归的目的 逻辑回归只判断样本属于正类的概率是多大&#xff0c;0-1之间 找到一组最佳的权重&#xff08;w1,w2,w3,…&#xff09; &#xff0c;b&#xff0c;使得模型预测的概率 P(Y1) 尽可能接近样本的真实标签&#xff08;1 或 0&#xff09;。 计算过程 前向传播过程…

对象池模式:减少GC的Kotlin实战指南

对象池模式通过对象复用机制&#xff0c;将对象生命周期从"创建-销毁"转变为"借出-归还"&#xff0c;显著减少GC压力。下面通过完整实例展示其实现细节。 一、对象池工作原理图解 #mermaid-svg-Edrz4np9hD6DJdNi {font-family:"trebuchet ms",v…

Java接口报错:Packet for query is too large - 解决方案与架构思考

Java接口报错&#xff1a;Packet for query is too large - 解决方案与架构思考 背景与技术原理解决方案体系&#xff08;扩展版&#xff09;一、MySQL服务端配置&#xff08;永久生效&#xff09;配置文件修改&#xff08;推荐生产环境&#xff09; 文件路径参考Linux: /etc/m…

7月2日作业

思维导图 一、创建一个进程扇 代码 #include <25041head.h>int main(int argc, const char *argv[]) {pid_t pid;for(int i1;i<4;i){pidfork();if(pid>0){sleep(1);}if(pid0){printf("我是子进程%d:%d,父进程%d\n",i,getpid(),getppid());sleep(1);re…

设计模式(九)

职责链模式&#xff08;Chain of Responsibility&#xff09;详解 一、核心概念 职责链模式将请求的发送者和接收者解耦&#xff0c;使多个对象都有机会处理请求。这些对象连接成一条链&#xff0c;请求沿着链传递&#xff0c;直到有一个对象处理它为止。该模式允许动态调整处…

左神算法之Zigzag方式打印矩阵

目录 Zigzag方式打印矩阵1. 题目2. 解释3. 思路4. 代码5. 总结 Zigzag方式打印矩阵 1. 题目 用zigzag的方式打印矩阵&#xff0c;比如下面的矩阵&#xff1a; 0 1 2 3 4 5 6 7 8 9 10 11打印顺序为&#xff1a;0 1 4 8 5 2 3 6 9 10 7 11 2. 解释 Zigzag打印矩阵是指按照…

【前端批量下载图片,并打包成压缩包下载】

一、需求说明 我现在有个需求&#xff1a; 1.列表中有个下载按钮&#xff0c;点击下载&#xff0c;将列表中所有的图片打成压缩包&#xff0c;并下载 2.效果演示点击查看效果 最终效果&#xff1a; 二、安装下载插件 实现此功能需要两个插件&#xff1a;jszip、file-saver …

NV133NV137美光固态闪存NV147NV148

NV133NV137美光固态闪存NV147NV148 美光固态闪存技术矩阵深度解析&#xff1a;NV133至NV148的全面较量 一、性能参数&#xff1a;数据高速公路的“车速”比拼 读写速度&#xff1a;从“乡间小道”到“高铁动脉” 美光NV系列固态闪存的核心竞争力在于其读写速度的跃升。以NV15…

从LLM到WM:大语言模型如何进化成具身世界模型?

1.引言这学期在方老师开设的《机器人大模型基础和前沿》选修课上接触并学习了具身智能方面的相关知识。作为交互组的组长&#xff0c;我和组员们在幻尔机器狗的功能开发上有切身的实践与探索&#xff0c;在张江具身智能大会上&#xff0c;也见识到了前沿的技术和行业的发展现状…

第十六届蓝桥杯C++B组国赛题解+复盘总结

文章目录 写在前面1、新型锁2、互质藏卡3、数字轮盘4、斐波那契字符串5、项链排列6、蓝桥星数字7、翻倍8、近似回文字符串9、子串去重10、涂格子 写在前面 打了三年&#xff0c;第十六届是我最后一次参加了&#xff0c;终于如愿以偿国一啦。 这场的大多题目都补了&#xff0c;…

【TTS】2024-2025年主流开源TTS模型的综合对比分析

以下是针对2024-2025年主流开源与商用TTS模型的综合技术选型分析&#xff0c;结合GitHub热度、功能特性、部署成本及中文支持等核心维度进行对比&#xff0c;并附详细实践建议。 一、开源TTS模型对比&#xff08;2024-2025年主流方案&#xff09; 模型名称开源/厂商克隆支持中…

redis延时双删,为什么第一次删除

Redis延时双删策略中第一次删除的作用 在缓存与数据库一致性方案中&#xff0c;"延时双删"&#xff08;Delayed Double-Delete&#xff09;是一种经典策略&#xff0c;其核心流程如下&#xff1a; 第一次删除&#xff1a;更新数据库前&#xff0c;先删除缓存 更新数…

深度学习1(深度学习和机器学习的区别,神经网络)

深度学习和机器学习的区别 深度学习和机器学习都是人工智能&#xff08;AI&#xff09;的重要分支&#xff0c;但它们在方法、应用场景和技术细节上有显著区别。 机器学习通过算法让计算机从数据中学习规律&#xff0c;并做出预测或决策。核心是特征工程&#xff08;人工提取数…

这才叫窗口查询!TDEngine官方文档没讲透的实战玩法

第1章&#xff1a;你不知道的TDEngine窗口查询——开局就不简单 先别急着翻白眼&#xff0c;提到时间窗口查询&#xff0c;可能你脑子里立马浮现的就是那些常规套路&#xff1a;GROUP BY time_interval、FIRST()、LAST()&#xff0c;再加上点AVG()和MAX()&#xff0c;一锅端。…

Day50 预训练模型+CBAM模块

目录 一、resnet结构解析 二、CBAM放置位置的思考 三、针对预训练模型的训练策略 a.差异化学习率 b.三阶段式解冻与微调 (Progressive Unfreezing) 四、尝试对vgg16cbam进行微调策略 是否可以对于预训练模型增加模块来优化其效果&#xff0c;这里会遇到一个问题&#xff…

快速说一下TDD BDD DDD

基本概念 TDD&#xff08;测试驱动开发&#xff09;、BDD&#xff08;行为驱动开发&#xff09;和 DDD&#xff08;领域驱动设计&#xff09;是软件开发领域中几个重要的概念&#xff0c;它们各自有着独特的侧重点与应用场景&#xff0c;以下为你详细介绍&#xff1a; 测试驱…

浅析基于深度学习算法的英文OCR技术工作原理及其应用场景

在数字化信息飞速发展的当下&#xff0c;大量的文本信息以各种形式存在&#xff0c;从传统的纸质文档到电子图片中的文字内容。如何高效地将这些非结构化的文本转化为计算机能够理解和处理的格式&#xff0c;成为了提高信息处理效率的关键。英文 OCR&#xff08;Optical Charac…

AI时代SEO关键词策略

内容概要 在人工智能&#xff08;AI&#xff09;驱动的新时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;关键词策略正迎来颠覆性变革。本篇文章将系统解析AI技术如何重塑关键词研究、内容优化及流量提升的全过程&#xff0c;帮助企业实现高效可持续的在线曝光。通过…

免费一键自动化申请、续期、部署、监控所有 SSL/TLS 证书,ALLinSSL开源免费的 SSL 证书自动化管理平台

目录 一、前言二、ALLinSSL 简介亮点核心功能 三、操作步骤部署安装授权DNS服务商授权你的主机服务器自动化部署ssl测试自动申请ssl证书 一、前言 SSL证书是每个网站必备的&#xff0c;但是现在的免费的ssl证书有效期是3个月&#xff0c;以后CA/B Forum 调整 SSL 证书最长有效期…