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

  • 前言
  • 一、栈
    • 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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows系统搭建it-tools工具箱并结合内网穿透实现公网远程访问

文章目录 1. 使用Docker本地部署it-tools2. 本地访问it-tools3. 安装cpolar内网穿透4. 固定it-tools公网地址 本篇文章将介绍如何在Windows上使用Docker本地部署IT- Tools&#xff0c;并且同样可以结合cpolar实现公网访问。 在前一篇文章中我们讲解了如何在Linux中使用Docker搭…

FPGA FIFO 读取模式

FPGA FIFO 读取模式分两种&#xff1a; Normal Mode: In normal mode, the “rdreq” signal serves as the read request or read enable. When this signal goes high, the data output provides the first data from the FIFO.Essentially, in normal mode, data is availa…

pytest测试框架使用基础07 fixture—parametrize获取参数的几种常用形式

【pytest】parametrize获取参数的几种常用形式: a.数据结构 b.文件 c.数据库 d.conftest.py配置一、直接在标签上传参 1.1 一个参数多个值 pytest.mark.parametrize("参数", (参数值1, 参数值2, 参数值3))示例&#xff1a; import pytest # 单个参数的情况 pytest.…

如何向各大媒体网站投稿 海外媒体发稿平台有哪些

在数字化时代&#xff0c;各大媒体网站是企业推广和个人展示的重要平台。通过在媒体网站上发布文章&#xff0c;可以有效地扩大影响力和提升知名度。但是&#xff0c;如何投稿到各大媒体网站呢&#xff1f;以下是一些常用的方法和步骤。 1. 研究目标媒体 在投稿之前&#xff0…

宠物空气净化器值得入手吗?选购宠物空气净化器关注哪些方面?

一开始养猫时&#xff0c;每天看着可爱的猫咪在家里快乐奔跑&#xff0c;让人心情愉悦。然而&#xff0c;作为铲屎官都知道&#xff0c;猫咪会掉毛&#xff0c;特别是在换毛期间&#xff0c;地板、沙发上都会有一大堆猫毛&#xff0c;甚至衣服也可能沾满猫毛。养猫家庭中&#…

安装邮件服务器postfix、mail客户端发送邮件

安装邮件服务器postfix、mail客户端发送邮件 1 安装postfix sudo apt-get update sudo apt-get install postfix -y安装过程中会让你选择一种Postfix配置类型&#xff0c;直接选择默认的第二种配置Internet Site就可以了。 选择ok之后会让你填入域名&#xff0c;一般会自动填…

鞋服品牌怎样合理把控订货深度和宽度

在鞋服品牌的运营管理中&#xff0c;订货深度和宽度是两个至关重要的概念。订货深度指的是某一款式或规格的产品数量&#xff0c;而订货宽度则代表品牌所涵盖的产品种类和款式。合理把控订货深度和宽度对于品牌的库存管理、销售情况以及顾客满意度都有着深远的影响。本文将探讨…

MindOpt优化器: 浅谈版本0.x和1.x之间API的差异

Mindopt 是一个优化求解器&#xff0c;如果它有两个主要版本——0.xx和1.x.x&#xff08;最新版本1.1.1&#xff09;&#xff0c;它们代表着软件开发的两个不同阶段。版本1.0.0表示软件的一个大的里程碑&#xff0c;代表着软件第一个正式的“成熟”发布版本&#xff0c;而0.25是…

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

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

说说flexbox(弹性盒布局模型)及适用场景?

文章目录 一、是什么二、属性flex-directionflex-wrapflex-flowjustify-contentalign-itemsalign-contentorderflex-growflex-basisflexalign-self 三、应用场景参考文献 一、是什么 Flexible Box 简称 flex&#xff0c;意为”弹性布局”&#xff0c;可以简便、完整、响应式地…

轻松关掉电脑用户账户控制弹窗!

对于新装的或者是电脑自带的系统来说,是不是会经常遇到的一个情况是,就是在你运行某些程序或者改动一些系统设置的时候,每次都会弹出个提示框,以提示你这个操作是安全的,某一个提示还行,但是每次运行程序都有提示框弹出,就会有点烦了,那么这个提示框是什么呐,如何关闭…

如何实现企业内部ERP、OA、CRM、MES系统的快速一体化集成

在企业数字化规划中&#xff0c;企业纷纷引入ERP&#xff08;企业资源规划&#xff09;、OA&#xff08;办公自动化&#xff09;、CRM&#xff08;客户关系管理&#xff09;、MES&#xff08;制造执行系统&#xff09;、电商ERP(电商订单系统)等多种信息化管理系统&#xff0c;…

你自降门槛 我就该回头吗?

作者&#xff1a;朱溪 琥珀酒研社快评&#xff1a; 曾经的你让我高攀不起 现在的我对你爱答不理 同事的哥哥 因为拿不出30万的彩礼和女友分了 两年后他开了公司也有了钱 女友跑来求复合 同事的哥哥想都没想就拒绝了 用金钱给爱情设门槛&#xff0c;爱情黄了 一个能力…

500强企业不会告诉你的真话,计算机专业如何更好地进大厂

“世界上所有的500强企业&#xff0c;都告诉你学历不重要&#xff0c;但他们绝对不会去一般学校招聘。” 听起来很残酷&#xff0c;可这就是事实。从一所普通的学校毕业&#xff0c;“混”了一个很普通的专业&#xff0c;最后只能入职一家低薪的小公司或者无法就业&#xff0c…

Codeforces Round 931(Div.2) A~D2

A. Too Min Too Max &#xff08;数学&#xff09; 题意&#xff1a; 给定长度为 n n n的数组 a a a&#xff0c;求下列表达式的最大值&#xff0c; ( i , j , k , l ) (i,j,k,l) (i,j,k,l)为不同的索引 ∣ a i − a j ∣ ∣ a j − a k ∣ ∣ a k − a l ∣ ∣ a l − a…

C/C++的内存管理与初阶模板

引言 我们在学习C的时候&#xff0c;会经常在堆上申请空间&#xff0c;所以这个时候就体现了内存管理遍历。 图下是我们常见的计算机的内存划分&#xff1a; 我也在图下对部分变量存在的位置&#xff0c;及时标注。(如果有任何问题可以联系博主修改&#xff0c;感谢大家。) 那…

OpenAI劲敌吹新风! Claude 3正式发布,Claude3使用指南

Claude 3是什么&#xff1f; 是Anthropic 实验室近期推出的 Claude 3 大规模语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;系列&#xff0c;代表了人工智能技术的一个显著飞跃。 该系列包括三个不同定位的子模型&#xff1a;Claude 3 Haiku、Claude 3…

Compose UI 之 MediumLarge TopAppBar

Medium&Large TopAppBar 前面文章介绍了 Small 类型的 TopAppBar&#xff1a;TopAppBar CenterAlignedTopAppBar 。下来介绍 Medium 和 Large 类型的 TopAppBar&#xff1a;MediumTopAppBar LargeTopAppBar 。 MediumTopAppBar 上面介绍了Small 类型的 TopAppBar (TopAp…

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(二)

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;前导&#xff09; Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;一&#xff09; Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;三&#xff09; 五、实验目的 本次实验使用电脑上的…

贪吃蛇c++

#include<bits/stdc.h> #include<conio.h> // 用于获取按键输入using namespace std;const int width 20; const int height 20; int x, y, fruitX, fruitY, score; int tailX[100], tailY[100]; // 蛇的身体位置数组 int nTail; // 蛇的长度 enum eDirecton { S…