数据结构从入门到精通——栈

  • 前言
  • 一、栈
    • 1.1栈的概念及结构
    • 1.2栈的实现
    • 1.3栈的面试题
  • 二、栈的具体实现代码
    • 栈的初始化
    • 栈的销毁
    • 入栈
    • 出栈
    • 返回栈顶元素
    • 返回栈中的元素个数
    • 检测是否为空
    • Stack.h
    • Stack.c
    • test.c


前言

栈,作为一种后进先出(LIFO)的数据结构,在计算机科学中扮演着重要的角色。它的特性使得它在处理函数调用、括号匹配、表达式求值等问题时具有得天独厚的优势。然而,如果我们跳出传统思维的束缚,会发现栈的用途远不止于此。

在现代软件开发中,栈的概念被广泛应用在内存管理、并发控制等多个领域。以内存管理为例,每个线程都有自己的栈空间,用于存储局部变量和函数调用信息。这种隔离保证了线程之间的数据安全,避免了数据混乱和意外覆盖。

同样,在并发控制中,栈也发挥着不可替代的作用。通过维护一个任务栈,系统可以合理地调度和分配计算资源,确保任务按照特定的顺序执行,从而避免了并发访问导致的数据不一致问题。

不仅如此,栈的思想还可以被借鉴到生活的方方面面。想象一下,如果我们将日常生活比作一个栈,那么每一天的生活就是一个新的元素被推入栈中。而当我们结束一天的生活,这个元素就会被从栈中弹出,成为我们宝贵的回忆。这种后进先出的特性使得我们能够更好地珍惜当下,因为每一个现在都会成为过去,而每一个过去都是无法替代的。

在这个意义上,栈不仅仅是一种数据结构,更是一种生活态度。它提醒我们珍惜每一个当下,因为每一个现在都会成为我们未来回忆的一部分。同时,它也告诉我们,在面对困难和挑战时,要敢于迎难而上,因为只有这样,我们才能不断成长和进步。


一、栈

1.1栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。

进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

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

出栈:栈的删除操作叫做出栈。出数据也在栈顶。
在这里插入图片描述
Push是入栈

Pop是出栈
在这里插入图片描述

1.2栈的实现

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

在这里插入图片描述

在这里插入图片描述

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
#define N 10
typedef struct Stack
{STDataType _a[N];int _top; // 栈顶
}Stack;// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* _a;int _top; // 栈顶int _capacity; // 容量 
}Stack;
// 初始化栈 
void StackInit(Stack* ps); 
// 入栈 
void StackPush(Stack* ps, STDataType data); 
// 出栈 
void StackPop(Stack* ps); 
// 获取栈顶元素 
STDataType StackTop(Stack* ps); 
// 获取栈中有效元素个数 
int StackSize(Stack* ps); 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps); 
// 销毁栈 
void StackDestroy(Stack* ps); 

1.3栈的面试题

  1. 括号匹配问题

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

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

答案:B C

二、栈的具体实现代码

有关栈,虽然数组类型的栈类型结构更优,但在实际写题目的过程中,链表形式的栈更适用些,接下来我将实现一下链栈

栈的初始化

void STInit(ST* ps);//栈的初始化
void STInit(ST* ps)
{assert(ps);ps->a = NULL;ps->capacity = 0;ps->top = -1;//或者ps->top = 0;具体区别在于先++还是后++
}

栈的初始化是数据结构中栈操作的第一步,它涉及为栈分配内存空间并设置其初始状态。栈是一种后进先出(LIFO)的数据结构,这意味着最后一个被放入栈中的元素将是第一个被取出的元素。

在进行栈的初始化时,我们需要考虑几个关键步骤。首先,我们需要为栈定义一个合适的数据结构,这通常是一个数组或链表。数组实现的栈在内存中使用连续空间,而链表实现的栈则更为灵活,但可能会占用更多的内存。

接下来,我们需要为栈分配内存空间。对于数组实现的栈,这通常意味着创建一个固定大小的数组来存储栈元素。对于链表实现的栈,我们需要创建一个空的链表节点作为栈顶。

在分配了内存空间之后,我们需要设置栈的初始状态。这通常意味着将栈顶指针或引用设置为一个表示栈为空的状态。对于数组实现的栈,这通常是数组的第一个位置或最后一个位置的索引。对于链表实现的栈,这通常是一个指向空链表节点的指针。

完成栈的初始化后,我们就可以开始执行栈的基本操作,如入栈(push)、出栈(pop)、查看栈顶元素(top)以及判断栈是否为空(is_empty)等。这些操作需要确保遵循栈的后进先出原则。

栈的销毁

void STDestroy(ST* ps);//栈的销毁
void STDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = 0;ps->top = -1;
}

栈的销毁是栈生命周期中的最后一个阶段,它标志着栈内所有数据元素的释放和栈结构本身的解除。在进行栈的销毁之前,必须确保栈中没有任何数据元素,否则可能会导致数据丢失或内存泄漏。

栈的销毁过程通常包括以下几个步骤:

首先,需要遍历栈内的所有元素,并将它们逐一释放。这通常涉及到调用每个元素的析构函数(如果是C++等支持面向对象编程的语言)或相应的清理函数(如果是C等过程式编程语言),以确保每个元素在被销毁前能够正确地完成其生命周期内的所有任务,如关闭文件、释放内存等。

其次,需要销毁栈本身的数据结构。这通常意味着释放栈所占用的内存空间。在大多数编程语言中,这可以通过调用类似于free(C语言)或delete(C++)这样的内存管理函数来完成。一旦栈的数据结构被销毁,它就不再是一个有效的栈,不能再执行入栈、出栈等操作。

最后,为了确保栈确实已经被销毁,可以在销毁后进行一些检查操作。例如,可以尝试对栈执行一些操作,如入栈或出栈,并检查是否会引发错误或异常。如果程序能够正确地检测到栈已经被销毁,并采取相应的错误处理措施,那么这就可以作为栈销毁过程完成的一个标志。

总的来说,栈的销毁是一个非常重要的过程,它确保了栈在不再需要时能够正确地释放其占用的资源,防止了内存泄漏和其他潜在的资源管理问题。同时,通过销毁栈,也可以为其他需要使用这些资源的操作提供空间,从而提高了整个程序的效率和可靠性。

入栈

//入栈
void STPush(ST* ps,STDatatype x);
void STPush(ST* ps,STDatatype x)
{assert(ps);ps->top++;if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDatatype* p = (STDatatype*)realloc(ps->a, sizeof(STDatatype)*newcapacity);if (p == NULL){perror("p malloc : ");return 0;}ps->a = p;ps->capacity = newcapacity;}ps->a[ps->top] = x;
}

“入栈”(Push)是栈(Stack)这种数据结构中的一个基本操作。栈是一种遵循后进先出(Last In First Out,简称LIFO)原则的数据结构,意味着最后放入栈中的元素将会是第一个被取出的。

“入栈”操作的具体含义是:

  1. 添加元素:将一个元素添加到栈的顶部。
  2. 栈顶变化:由于新元素被添加到栈顶,所以栈顶指针或引用会更新,指向这个新添加的元素。
  3. 栈大小变化:栈的大小(或容量)会增加,因为多了一个元素。

例如,假设我们有一个空的栈,并且按照以下顺序执行入栈操作:

  1. 入栈 A
  2. 入栈 B
  3. 入栈 C

那么,栈的当前状态将是 C 在顶部,B 在中间,A 在底部。如果我们现在执行出栈(Pop)操作,C 将被取出,接着是 B,最后是 A。

在实际应用中,栈常用于保存函数调用过程中的局部变量、参数和返回地址,实现递归调用,以及进行深度优先搜索等。

入栈,是每一个数据处理流程中不可或缺的一环。在信息技术的世界里,数据就如同图书馆的藏书,需要有序地存放和取用。而栈,便是这个数字世界中的一个小巧精致的藏书阁,它遵循着后进先出(LIFO)的规则,每一份数据都像是一本书,被轻轻放在栈顶,等待着被取用或者再次被存放。

每当有新的数据需要处理时,它就会被推入栈中。这个过程就像是图书馆里新到了一本畅销书,图书管理员会将它放在最显眼的位置,供读者最先取阅。同样,栈顶的数据也是最先被处理的,因为它是最后进栈的。这种有序的处理方式,保证了数据处理的效率和准确性。

然而,栈并非万能的。它的规则简单而明确,但也因此有局限性。有时候,我们需要按照不同的顺序来处理数据,这时候就需要使用到队列等其他数据结构。但无论如何,栈都是数据处理中不可或缺的一部分。

在软件开发的世界里,栈的作用更是举足轻重。函数调用、递归算法、异常处理等,都离不开栈的支持。每当一个函数被调用时,它的相关信息就会被压入调用栈中,等待函数执行完毕后弹出。这使得程序能够准确地跟踪函数的执行顺序,保证程序的正确运行。

同时,栈也是内存管理的重要手段。在编程中,我们经常需要动态地分配和释放内存。而栈就是内存分配的一种方式。它会自动管理分配给每个函数的内存空间,当函数执行完毕后,这些内存空间就会被自动释放,避免了内存泄漏等问题的发生。

总的来说,入栈是数据处理和程序运行中的关键步骤。它保证了数据的有序性和程序的正确性,是信息技术世界中不可或缺的一环。在未来的技术发展中,栈的作用将更加重要,我们也需要更加深入地理解和应用它。

出栈

//出栈
void STPop(ST* ps);
void STPop(ST* ps)
{assert(ps);assert(!STEmpty(&ps));ps->top--;
}

当元素从栈的顶部被移除时,这个过程被称为“出栈”。栈是一种遵循后进先出(LIFO)原则的数据结构,其中新元素总是被添加到栈顶,而只有栈顶的元素可以被移除。出栈操作会减少栈的大小,并返回被移除的元素。如果栈为空,则无法进行出栈操作。

返回栈顶元素

STDatatype STTop(ST* ps);//返回栈顶元素
STDatatype STTop(ST* ps)
{assert(ps);assert(!STEmpty(&ps));return ps->a[ps->top];
}

返回栈中的元素个数

int STSize(ST* ps);//返回栈中的元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}

检测是否为空

注意:在使用VS2022编译器编译C语言需要用到布尔类型的时候,需要添加头文件#include <stdbool.h>,添加了这个头文件才能使用布尔类型

bool STEmpty(ST* ps);//检测是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}

Stack.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.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 x);
//出栈
void STPop(ST* ps);
STDatatype STTop(ST* ps);//返回栈顶元素
int STSize(ST* ps);//返回栈中的元素个数
bool STEmpty(ST* ps);//检测是否为空

Stack.c

#include "Stack.h"void STInit(ST* ps)
{assert(ps);ps->a = NULL;ps->capacity = 0;ps->top = -1;//或者ps->top = 0;具体区别在于先++还是后++
}
void STDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = 0;ps->top = -1;
}
void STPush(ST* ps,STDatatype x)
{assert(ps);ps->top++;if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDatatype* p = (STDatatype*)realloc(ps->a, sizeof(STDatatype)*newcapacity);if (p == NULL){perror("p malloc : ");return 0;}ps->a = p;ps->capacity = newcapacity;}ps->a[ps->top] = x;
}
void STPop(ST* ps)
{assert(ps);assert(!STEmpty(&ps));ps->top--;
}bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}
STDatatype STTop(ST* ps)
{assert(ps);assert(!STEmpty(&ps));return ps->a[ps->top];
}
int STSize(ST* ps)
{assert(ps);return ps->top;
}

test.c

#include"Stack.h"int main()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);int top = STTop(&s);printf("%d ", top);STPop(&s);STPush(&s, 4);STPush(&s, 5);while (!STEmpty(&s)){int top = STTop(&s);printf("%d ", top);STPop(&s);}STDestroy(&s);return 0;
}

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

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

相关文章

NTFS Disk by Omi NTFS for mac v1.1.4中文版

NTFS Disk by Omi NTFS for Mac&#xff1a;NTFS文件系统的无缝桥梁 软件下载&#xff1a;NTFS Disk by Omi NTFS for mac v1.1.4中文版 &#x1f310; 跨平台访问&#xff0c;文件无阻 NTFS Disk by Omi NTFS for Mac 为您的Mac提供了对NTFS文件系统的无缝访问。无论您是在Win…

Unity插件之天气系统UniStorm

首先呢&#xff0c;它是一款强大的动态昼夜天气系统&#xff0c;能够以较快的帧速率创建AAA级动态生成的天气、照明和天空&#xff0c;并且具有300多个可定制的组件&#xff0c;允许用户创建任何可以想象的环境。 第一步&#xff1a;他需要两个物体Camera摄像机、Player播放器…

鸿蒙开发-UI-动画-页面内动画

鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 鸿蒙开发-UI-页面路由 鸿蒙开发-UI-组件导航-Navigation 鸿蒙开发-UI-组件导航-Tabs 鸿蒙开发-UI-图形-图片 鸿蒙开发-UI-图形-绘制几何图形 鸿蒙开发-UI-图形-绘制自定义图形 文章目录 前言 一、概述 二、页面内…

新一代信息技术下,AI如何重塑售前工作

近期&#xff0c;“新质生产力”一词犹如一颗璀璨的新星&#xff0c;频繁地在各大媒体平台上抢占热点位置&#xff0c;引发了大家的关注。关于“新质生产力”官方的解释是科技创新驱动经济高质量发展的必然产物&#xff0c;它代表着新一轮科技革命和产业变革的重要方向。 在今…

finishConnect(..) failed: Connection refused,服务本地正常服务器网关报400,nacos服务实例不能下线

①application里固定ip # Spring spring:cloud:inetutils:preferred-networks: 127.0.0.1 ②找到nacos服务下的protocol&#xff0c;删除下面所有&#xff0c;/nacos-server/data/protocol&#xff0c;删了不会有问题&#xff0c;而且这东西越用越大&#xff0c;删了好爽 ③重…

const XX=void0

在阅读源码中&#xff0c;经常会看到 const XXvoid0这里的 void 0其实就是 undefined&#xff0c;void 运算符总会返回一个 undefined 的结果。 那么&#xff0c;为什么要用 void 0 代替 undefined 呢&#xff1f; 总结&#xff1a; undefined 是一个「全局对象&#xff08;…

iOS——【自动引用计数】ARC规则及实现

1.3.3所有权修饰符 所有权修饰符一共有四种&#xff1a; __strong 修饰符__weak 修饰符__undafe_unretained 修饰符__autoreleasing 修饰符 __strong修饰符 _strong修饰符表示对对象的强引用&#xff0c;持有强引用的变量在超出其作用域的时候会被废弃&#xff0c;随着强引…

数组连续和 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 给定一个含有N个正整数的数组&#xff0c;求出有多少连续区间&#xff08;包括单个正整数&#xff09;&#xff0c;它们的和大于等于 x。 输入描述 第一行为两个…

Xcode升级到Xcode15.1或15.2之后,无法新建Category和Extension文件,如何解决?

项目场景&#xff1a; Xcode升级到15.1或15.2之后&#xff0c;无法新建Category和Extension文件&#xff0c;并且Xcode不报任何错误 问题描述 Xcode升级到15.1或15.2之后&#xff0c;无法新建Category和Extension文件&#xff0c;并且Xcode不报任何错误。 具体的操作步骤如下…

redis-集群 原生部署和工具自动部署

什么redis集群&#xff1f; redis集群是一个提供在多个redis节点之间共享数据的程序集。它并不像redis主从复制模式那样仅提供一个master节点来提供写服务&#xff0c;而是会提供多个master节点来提供写服务&#xff0c;每个master节点中存储的数据都不一样&#xff0c;这些数据…

【保姆级爬虫】微博关键词搜索并获取博文和评论内容(python+selenium+chorme)

微博爬虫记录 写这个主要是为了防止自己忘记以及之后的组内工作交接&#xff0c;至于代码美不美观&#xff0c;写的好不好&#xff0c;统统不考虑&#xff0c;我只能说&#xff0c;能跑就不错了&#xff0c;上学压根没学过python好吧&#xff0c;基本上是crtlc&ctrlv丝滑小…

开启AI绘画新纪元:让创意在指尖绽放

文章目录 一、了解AI绘画的基本原理二、选择合适的AI绘画工具三、掌握AI绘画的基本技巧四、借鉴与创新&#xff1a;从模仿到创作五、参与社区交流&#xff0c;共同成长《AI绘画教程&#xff1a;Midjourney使用方法与技巧从入门到精通》亮点推荐内容简介作者简介目录 在科技日新…

Linux centos 常用的网络负载和网速查看工具和命令

在 CentOS 上查看网络速度和网络负载&#xff0c;可以使用多种工具&#xff0c;以下是一些常用的命令行工具&#xff1a; iftop - 用于实时监视网络带宽使用情况。 安装命令&#xff1a; sudo yum install iftop 使用命令&#xff1a; sudo iftop nload - 一个简单的控制…

Doris-数据分区

数据分区&#xff1a;即将大表划分为小表&#xff0c;数据分区主要有两个级别&#xff1a;Partition和Bucket&#xff08;Tablet&#xff09;。 Partition&#xff1a;逻辑分区&#xff0c;按照一定规则将表按照行进行划分&#xff0c;每个部分就是一个Partition。 Bucket&…

flink重温笔记(十):Flink 高级 API 开发——flink 四大基石之 State(涉及Checkpoint)

Flink学习笔记 前言&#xff1a;今天是学习 flink 的第 10 天啦&#xff01;学习了 flink 四大基石之 State &#xff08;状态&#xff09;&#xff0c;主要是解决大数据领域增量计算的效果&#xff0c;能够保存已经计算过的结果数据状态&#xff01;重点学习了 state 的类型划…

相对于 Linux,Windows Server 存在的意义是什么?

相对于 Linux&#xff0c;Windows Server 存在的意义是什么&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux 的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给…

新版AndroidStudio的Gradle窗口显示task list not built 问题解决

在使用新版AndroidStudio时&#xff0c;会出现&#xff0c;Task List not built 的问题。如果你记得task的名字&#xff0c;当然可以 直接通过命令 gradle taskname 或者 ./gradlew taskName直接执行即可&#xff0c;但是若是记不住&#xff0c;还是把这个任务构建处理比较好用…

借助工具优化开发流程,提升开发体验

背景 最近在做一个demo&#xff0c;只有一个html页面&#xff0c;需要开启一个https web server&#xff0c;以此来实现在浏览器中访问。 改造前 改造前&#xff0c;每次修改文件保存后都要刷新一下浏览器。 如果只是短时间&#xff0c;每次修改后手动刷新浏览器也还行。主要…

C++:Stack和Queue的模拟实现

创作不易&#xff0c;感谢三连&#xff01; 一、容器适配器 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结)&#xff0c;该种模式是将一个类的接口转换成客户希望的另外一个接口。 就如同是电源适配器将不适用的交流电…

day04-Maven

一、初识 Maven Maven 是 Apache 旗下的一个开源项目&#xff0c;是一款用于管理和构建 java 项目的工具。 官网&#xff1a;https://maven.apache.org/ Maven的作用 依赖管理&#xff08;方便快捷的管理项目依赖的资源(jar包)&#xff0c;避免版本冲突问题&#xff09;统一项目…