怎样在 C 语言中实现栈?

🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!
📙C 语言百万年薪修炼课程 通俗易懂,深入浅出,匠心打磨,死磕细节,6年迭代,看过的人都说好。

分割线

文章目录

  • 如何在 C 语言中实现栈
  • 一、栈的基本概念
  • 二、栈的操作
  • 三、使用数组实现栈
  • 四、使用链表实现栈
  • 五、两种实现方式的比较
    • (一)空间复杂度
    • (二)时间复杂度
    • (三)灵活性
    • (四)适用场景
  • 六、栈的应用场景
    • (一)函数调用
    • (二)表达式求值
    • (三)括号匹配
    • (四)回溯算法
  • 七、总结

分割线


如何在 C 语言中实现栈

在 C 语言中,栈(Stack)是一种常见的数据结构,它遵循后进先出(Last In First Out,LIFO)的原则。这意味着最后添加到栈中的元素将首先被移除。

一、栈的基本概念

栈是一种线性数据结构,具有以下特点:

  1. 栈顶(Top):栈的顶部元素,是进行插入和删除操作的一端。
  2. 栈底(Bottom):栈的底部元素,是相对固定的一端。

二、栈的操作

常见的栈操作包括:

  1. push:将元素压入栈顶。
  2. pop:弹出栈顶元素。
  3. peek(或者 top):获取栈顶元素但不弹出。
  4. isEmpty:判断栈是否为空。

三、使用数组实现栈

以下是使用数组来实现栈的 C 语言代码示例:

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>#define MAX_SIZE 100typedef struct {int items[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, int element) {if (isFull(stack)) {printf("Stack Overflow!\n");return;}stack->items[++stack->top] = element;
}// 弹出栈顶元素
int pop(Stack *stack) {if (isEmpty(stack)) {printf("Stack Underflow!\n");return -1;}int element = stack->items[stack->top];stack->top--;return element;
}// 获取栈顶元素但不弹出
int peek(Stack *stack) {if (isEmpty(stack)) {printf("Stack is empty!\n");return -1;}return stack->items[stack->top];
}int main() {Stack stack;initStack(&stack);push(&stack, 10);push(&stack, 20);push(&stack, 30);printf("Top element: %d\n", peek(&stack));int poppedElement = pop(&stack);if (poppedElement!= -1) {printf("Popped element: %d\n", poppedElement);}printf("Top element after pop: %d\n", peek(&stack));return 0;
}

在上述代码中:

  • initStack 函数用于初始化栈,将栈顶指针设置为 -1,表示栈为空。
  • isEmpty 函数通过检查栈顶指针是否为 -1 来判断栈是否为空。
  • isFull 函数通过检查栈顶指针是否达到数组的最大索引来判断栈是否已满。
  • push 函数在压入元素之前,先检查栈是否已满。如果未满,将元素添加到栈顶,并更新栈顶指针。
  • pop 函数在弹出元素之前,先检查栈是否为空。如果不为空,返回栈顶元素,并更新栈顶指针。
  • peek 函数返回栈顶元素,但不修改栈的状态。

四、使用链表实现栈

除了使用数组,我们还可以使用链表来实现栈。以下是使用链表实现栈的 C 语言代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef struct Node {int data;struct Node *next;
} Node;typedef struct {Node *top;
} Stack;// 初始化栈
void initStack(Stack *stack) {stack->top = NULL;
}// 判断栈是否为空
bool isEmpty(Stack *stack) {return stack->top == NULL;
}// 压入元素到栈
void push(Stack *stack, int element) {Node *newNode = (Node *)malloc(sizeof(Node));if (newNode == NULL) {printf("Memory allocation failed!\n");return;}newNode->data = element;newNode->next = stack->top;stack->top = newNode;
}// 弹出栈顶元素
int pop(Stack *stack) {if (isEmpty(stack)) {printf("Stack Underflow!\n");return -1;}Node *temp = stack->top;int element = temp->data;stack->top = stack->top->next;free(temp);return element;
}// 获取栈顶元素但不弹出
int peek(Stack *stack) {if (isEmpty(stack)) {printf("Stack is empty!\n");return -1;}return stack->top->data;
}// 打印栈
void printStack(Stack *stack) {Node *current = stack->top;printf("Stack: ");while (current!= NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}int main() {Stack stack;initStack(&stack);push(&stack, 10);push(&stack, 20);push(&stack, 30);printStack(&stack);int poppedElement = pop(&stack);if (poppedElement!= -1) {printf("Popped element: %d\n", poppedElement);}printStack(&stack);printf("Top element: %d\n", peek(&stack));return 0;
}

在这个链表实现的栈中:

  • 每个节点包含数据和指向下一个节点的指针。
  • initStack 函数将栈顶指针初始化为 NULL,表示空栈。
  • push 函数创建一个新节点,将其数据设置为要压入的元素,并将其链接到当前栈顶节点之前,更新栈顶指针。
  • pop 函数如果栈不为空,删除栈顶节点,返回其数据,并更新栈顶指针,同时释放已删除节点的内存。
  • peek 函数返回栈顶节点的数据。
  • printStack 函数用于打印栈中的所有元素。

五、两种实现方式的比较

(一)空间复杂度

  • 数组实现:在创建栈时需要预先分配固定大小的连续内存空间。如果栈的实际使用空间小于预分配的空间,会造成一定的内存浪费;如果栈的实际使用空间超过预分配的空间,还需要进行扩容操作,可能涉及到数据的复制和内存的重新分配,增加了额外的开销。
  • 链表实现:每个节点在需要时动态分配内存,不会造成内存的预先浪费。但是,每个节点除了存储数据外,还需要额外的空间来存储指针,因此会有一些额外的内存开销。

(二)时间复杂度

  • 数组实现:
    • push 操作:在栈未满的情况下,时间复杂度为 O(1)
    • pop 操作:在栈不为空的情况下,时间复杂度为 O(1)
    • 访问栈顶元素:时间复杂度为 O(1)
  • 链表实现:
    • push 操作:需要创建新节点并更新指针,时间复杂度为 O(1)
    • pop 操作:需要删除节点并更新指针,时间复杂度为 O(1)
    • 访问栈顶元素:时间复杂度为 O(1)

总体来说,两种实现方式在常见操作的时间复杂度上是相同的。

(三)灵活性

  • 数组实现:由于数组的大小是固定的,在需要动态调整栈的大小时,操作相对复杂。
  • 链表实现:可以更灵活地添加和删除元素,不需要考虑固定大小的限制。

(四)适用场景

  • 数组实现:适用于事先知道栈的最大规模,并且对内存使用较为敏感的场景。
  • 链表实现:适用于无法确定栈的最大规模,或者需要更灵活地管理栈的空间的场景。

六、栈的应用场景

(一)函数调用

在计算机程序中,当一个函数调用另一个函数时,系统会将当前函数的执行上下文(包括参数、局部变量、返回地址等)压入栈中。当被调用函数执行完毕后,系统从栈中弹出之前保存的执行上下文,恢复到调用函数继续执行。

(二)表达式求值

在对算术表达式进行求值时,可以使用栈来存储操作数和运算符。通过按照特定的规则进行入栈和出栈操作,可以实现表达式的正确求值。

(三)括号匹配

检查一段包含括号(如 ()[]{})的文本中括号是否匹配,可以使用栈来辅助判断。遇到左括号入栈,遇到右括号时与栈顶的左括号进行匹配,如果匹配成功则弹出栈顶元素,否则表示括号不匹配。

(四)回溯算法

在一些需要回溯的算法中,如深度优先搜索、八皇后问题等,可以使用栈来保存中间状态,以便在需要时进行回溯。

七、总结

在 C 语言中,我们可以使用数组或链表来实现栈。两种实现方式各有优缺点,应根据具体的应用场景选择合适的实现方式。理解栈的概念和实现原理对于解决许多编程问题非常有帮助,并且在实际的开发中有着广泛的应用。


分割线

🎉相关推荐

  • 📙C 语言百万年薪修炼课程 通俗易懂,深入浅出,匠心打磨,死磕细节,6年迭代,看过的人都说好。
  • 🍅博客首页-关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!
  • 📙CSDN专栏-C语言修炼
  • 📙技术社区-墨松科技

C语言



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

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

相关文章

动手学深度学习(Pytorch版)代码实践 -循环神经网络-55循环神经网络的从零开始实现和简洁实现

55循环神经网络的实现 1.从零开始实现 import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l import matplotlib.pyplot as plt import liliPytorch as lp# 读取H.G.Wells的时光机器数据集 batch_size, num_ste…

ElasticSearch第一天

学习目标&#xff1a; 能够理解ElasticSearch的作用能够安装ElasticSearch服务能够理解ElasticSearch的相关概念能够使用Postman发送Restful请求操作ElasticSearch能够理解分词器的作用能够使用ElasticSearch集成IK分词器能够完成es集群搭建 第一章 ElasticSearch简介 1.1 什么…

只会vue的前端开发工程师是不是不能活了?最近被一个flutter叼了

**Vue与Flutter&#xff1a;前端开发的新篇章** 在前端开发的世界里&#xff0c;Vue.js和Flutter无疑是两颗璀璨的明星。Vue以其轻量级、易上手的特点吸引了大量前端开发者的青睐&#xff0c;而Flutter则以其跨平台、高性能的优势迅速崛起。那么&#xff0c;对于只会Vue的前端…

【深度学习基础】环境搭建 linux系统下安装pytorch

目录 一、anaconda 安装二、创建pytorch1. 创建pytorch环境&#xff1a;2. 激活环境3. 下载安装pytorch包4. 检查是否安装成功 一、anaconda 安装 具体的安装说明可以参考我的另外一篇文章【环境搭建】Linux报错bash: conda: command not found… 二、创建pytorch 1. 创建py…

OceanBase:引领下一代分布式数据库技术的前沿

OceanBase的基本概念 定义和特点 OceanBase是一款由蚂蚁金服开发的分布式关系数据库系统&#xff0c;旨在提供高性能、高可用性和强一致性的数据库服务。它结合了关系数据库和分布式系统的优势&#xff0c;适用于大规模数据处理和高并发业务场景。其核心特点包括&#xff1a; …

python调用阿里云汇率接口

整体请求流程 介绍&#xff1a; 本次解析通过阿里云云市场的云服务来实现程序中对货币汇率实时监控&#xff0c;首先需要准备选择一家可以提供汇率查询的商品。 https://market.aliyun.com/apimarket/detail/cmapi00065831#skuyuncode5983100001 步骤1: 选择商品 如图点击…

debian 12 Install

debian 前言 Debian是一个基于Linux内核的自由和开放源代码操作系统&#xff0c;由全球志愿者组成的Debian项目维护和开发。该项目始于1993年&#xff0c;由Ian Murdock发起&#xff0c;旨在创建一个完整的、基于Linux的自由软件操作系统。 debian download debian 百度网盘…

分布式应用系统设计:即时消息系统

即时消息(IM)系统&#xff0c;涉及&#xff1a;站内消息系统 组件如下&#xff1b; 客户端&#xff1a; WEB页面&#xff0c;IM桌面客户端。通过WebSocket 跟ChatService后端服务连接 Chat Service&#xff1a; 提供WebSocket接口&#xff0c;并保持跟“客户端”状态的维护。…

会声会影分割音频怎么不能用 会声会影分割音频方法 会声会影视频制作教程 会声会影下载免费中文版2023

将素材中的音频分割出来&#xff0c;对声音部分进行单独编辑&#xff0c;是剪辑过程中的常用操作。会声会影视频剪辑软件在分割音频后&#xff0c;还可以对声音素材进行混音编辑、音频调节、添加音频滤镜等操作。有关会声会影分割音频怎么不能用&#xff0c;会声会影分割音频方…

如何快速制作您的数据可视化大屏?

数据大屏可视化主要就是借助图形&#xff0c;利用生动、直观的形式展示出数据信息的具体数值&#xff0c;使得使用者短时间内更加直观的接受到大量信息。数据大屏以直观、高度视觉冲击力的方式向受众揭示数据背后隐藏的规律&#xff0c;传达数据价值。其以图形化的形式呈现数据…

全国产T3+FPGA的SPI与I2C通信方案分享

近年来&#xff0c;随着中国新基建、中国制造2025规划的持续推进&#xff0c;单ARM处理器越来越难胜任工业现场的功能要求&#xff0c;特别是如今能源电力、工业控制、智慧医疗等行业&#xff0c;往往更需要ARM FPGA架构的处理器平台来实现例如多路/高速AD采集、多路网口、多路…

Tomcat多实例

一、Tomcat多实例 Tomcat多实例是指在同一台服务器上运行多个独立的tomcat实例&#xff0c;每个tomcat实例都具有独立的配置文件、日志文件、应用程序和端口&#xff0c;通过配置不同的端口和文件目录&#xff0c;可以实现同时运行多个独立的Tomcat服务器&#xff0c;每个服务…

element-plus 按需导入问题 404等问题

场景 新开一个项目&#xff0c;需要用element-plus这个ui库&#xff0c;使用按需引入。 这是我项目的一些版本号 "element-plus": "^2.7.6","vue": "^3.2.13","vue-router": "^4.0.3",过程&#xff08;看解决方法…

FastGPT+OneAI接入网络模型

文章目录 FastGPT连接OneAI接入网络模型1.准备工作2.开始部署2.1下载 docker-compose.yml2.2修改docker-compose.yml里的参数 3.打开FastGPT添加模型3.1打开OneAPI3.2接入网络模型3.3重启服务 FastGPT连接OneAI接入网络模型 1.准备工作 本文档参考FastGPT的官方文档 主机ip接…

JVM是如何管理内存的?图文详解GC垃圾回收算法

前言&#xff1a;在C/C中对于变量的内存空间一般都是由程序员手动进行管理的&#xff0c;往往会伴随着大量的 malloc 和 free 操作&#xff0c;常常会有很多问题困扰开发者&#xff0c;这个代码会不会发生内存泄漏&#xff1f;会不会重复释放内存&#xff1f;但是在Java开发中我…

基于企业微信第三方接口开发,移除群成员通知

移除群成员通知 返回示例 {"flag": 0, "receiver": 0, "sender_name": "", "is_room": 1, "server_id": 15318083, "send_time": 1687688952, "sender": 1688855749266556, "referid&…

Spring源码十九:Bean实例化流程二

上一篇我们在Spring源码十八&#xff1a;Bean实例化流程一 中&#xff0c;主要讨论了Spring在实例化前的两重要准备工作&#xff0c;1、获取我们前面注册好的BeanDefinition&#xff0c;将GenericBeanDefinition封装为RootBeanDefinition如果Bean Definition只存在父容器中&…

计算器原生js

目录 1.HTML 2.CSS 2.JS 4.资源 5.运行截图 6.下载连接 7.注意事项 1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-s…

【C++】引用变量详解

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

文档去重(TF-IDF,MinHash, SimHash)

2个doc有些相似有些不相似&#xff0c;如何衡量这个相似度&#xff1b; 直接用Jaccard距离&#xff0c;计算量太大 TF-IDF: TF*IDF TF&#xff1a;该词在该文档中的出现次数&#xff0c; IDF&#xff1a;该词在所有文档中的多少个文档出现是DF&#xff0c;lg(N/(1DF)) MinHash …