【数据结构】详解栈

news2024/11/15 18:07:19

今天我们主要来了解栈!如果对知识点有模糊,可翻阅以往文章哦!

个人主页:小八哥向前冲~-CSDN博客

所属专栏:数据结构【c语言版】_小八哥向前冲~的博客-CSDN博客

c语言专栏:c语言_小八哥向前冲~的博客-CSDN博客

值得注意的是,如果你十分了解顺序表和链表,今天这期会很轻松哦!

哈哈哈哈!当然,这期也能检测你对顺序表和链表的理解!一起看看吧!

目录

栈的定义

顺序表和链表的比较

栈的实现--顺序表

初始化

栈为空的判定

入栈

出栈

销毁

栈顶数据

数据个数

题目操练:配括号问题

栈的实现--链表

栈为空的判定

入栈

出栈

销毁

栈顶数据

数据个数

码源--栈(顺序表)

码源--栈(链表)


栈的定义

  • :一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
  • 出栈:栈的删除操作叫做出栈。出数据也在栈顶

上图理解一下:

注意:遵循后进先出的原则!

知道了这个原则,我们来巩固一下:

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

A .12345ABCDE

B.EDCBA54321

C.ABCDE12345

D.54321EDCBA

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

A.1,4,3,2

B.2,3,4,1

C.3,1,4,2

D.3,4,2,1

显然:1.B      2.C

相信第一题不难,我们解释一下第二题:看到C选项,1,2,3进栈后,3出栈,而第二次出栈的只能是2或4,不可能是1,所以C错误!

了解了栈的概念,我们实现这个栈是使用顺序表还是链表呢?

  • 如果是顺序表的话,我们的栈顶应该要在数组末尾!如果在数组头部的话,数据进栈时还需要挪动其余数据以便数据的存入!效率很低
  • 如果是链表的话,我们的栈顶要在链表的头入栈时,头插即可!如果栈顶在链表尾部的话,虽然入栈尾插即可,但需要遍历,效率低,那么这时就需要使用双链表!

综上所述,我们栈使用顺序表较好!(两种都实现看看)

上图看看它们:

为了更好透彻了解顺序表和链表,我们将它们比较看看!

顺序表和链表的比较

图文更加直观:

这里的缓存利用率不做过多解释,详情见:https://www.cnblogs.com/yungyu16/p/13054874.html

栈的实现--顺序表

既然是要在顺序表基础上实现栈,那么就要实现顺序表和栈的基本框架。

(单链表若有不懂的知识点,可见:通讯录的实现(顺序表版本)-CSDN博客)

stack.h文件--包含各种需要的函数

栈里面的变量:top表示栈顶下标,capacity表示栈空间。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDateType;
//栈
typedef struct stack
{
	STDateType* a;
	int top;
	int capacity;
}ST;

//栈的初始化和销毁
void STInit(ST* p);
void STDestroy(ST* p);
//入栈,出栈
void STpush(ST* p,STDateType x);
void STpop(ST* p);
//栈顶的数据
STDateType STtop(ST* p);
//栈的数据个数
int STsize(ST* p);
//判空
bool STEmpty(ST* p);

接下来我们一一实现!

初始化

我们要将栈中各个变量进行初始化。

void STInit(ST* p)
{
	assert(p);
	p->a = NULL;
	p->capacity = 0;
	p->top = 0;
}

栈为空的判定

我们在实现这个函数时,很多人会用 if语句来判断是否为空,但我们仔细一想,可以好好优化一下代码!

//判空
bool STEmpty(ST* p)
{
	assert(p);
	return p->top == 0;
}

入栈

经过我们刚刚的分析,入栈要在数组尾部!记得每次入栈需要判断空间是否够用哦!

void STpush(ST* p, STDateType x)
{
	assert(p);
	if (p->top == p->capacity)
	{
		int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
		STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
		if (tmp == NULL)
		{
			perror("realloc failed!");
			return;
		}
		p->a = tmp;
		p->capacity = newcapacity;
	}
	p->a[p->top++] = x;
}

出栈

入栈要在尾部,出栈也要在尾部,后进先出的原则要时刻记住!

需要注意的是:当栈为空时,数据出不了栈!所以我们先需要判断是否为空!

void STpop(ST* p)
{
	assert(p);
	//出栈的话要判断一下空的情况
	assert(!STEmpty(p));
	p->top--;
}

销毁

我们既然用了开辟内存函数,当我们不使用栈时,要将空间释放掉!

void STDestroy(ST* p)
{
	assert(p);
	free(p->a);
	p->capacity = p->top = 0;
}

栈顶数据

在访问栈顶数据时,我们也要先判断栈是否为空,否则当栈为空时,访问栈顶数据便会越界访问!

//栈顶的数据
STDateType STtop(ST* p)
{
	assert(p);
	//出栈的话要判断一下空的情况
	assert(!STEmpty(p));
	return p->a[p->top-1];
}

数据个数

//栈的数据个数
int STsize(ST* p)
{
	assert(p);
	return p->top;
}

题目操练:配括号问题

既然我们已经实现的栈,我们来应用一下吧!

题目:详情--20. 有效的括号 - 力扣(LeetCode)

思路:

遍历数组,当是左括号时("(","{","】")时就入栈,当不是左括号时就出栈比较,直到遍历完成!

这样听是不是很简单呢?当然里面没有栈,我们需要将栈创建一下!

代码:

typedef char STDateType;
//栈
typedef struct stack
{
	STDateType* a;
	int top;
	int capacity;
}ST;

//栈的初始化和销毁
void STInit(ST* p);
void STDestroy(ST* p);
//入栈,出栈
void STpush(ST* p,STDateType x);
void STpop(ST* p);
//栈顶的数据
STDateType STtop(ST* p);
//栈的数据个数
int STsize(ST* p);
//判空
bool STEmpty(ST* p);
//栈的初始化和销毁
void STInit(ST* p)
{
	assert(p);
	p->a = NULL;
	p->capacity = 0;
	p->top = 0;
}
void STDestroy(ST* p)
{
	assert(p);
	free(p->a);
	p->capacity = p->top = 0;
}
//入栈,出栈
void STpush(ST* p, STDateType x)
{
	assert(p);
	if (p->top == p->capacity)
	{
		int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
		STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
		if (tmp == NULL)
		{
			perror("realloc failed!");
			return;
		}
		p->a = tmp;
		p->capacity = newcapacity;
	}
	p->a[p->top++] = x;
}
void STpop(ST* p)
{
	assert(p);
	//出栈的话要判断一下空的情况
	assert(!STEmpty(p));
	p->top--;
}
//栈顶的数据
STDateType STtop(ST* p)
{
	assert(p);
	//出栈的话要判断一下空的情况
	assert(!STEmpty(p));
	return p->a[p->top-1];
}
//栈的数据个数
int STsize(ST* p)
{
	assert(p);
	return p->top;
}
//判空
bool STEmpty(ST* p)
{
	assert(p);
	return p->top == 0;
}
bool isValid(char* s) {
    ST st;
    STInit(&st);
    while(*s)
    {
//左括号入栈
        if(*s=='('||*s=='['||*s=='{')
        {
            STpush(&st,*s);
        }
        else
//不是左括号出栈比较
        {
            if(STEmpty(&st))
            {
                STDestroy(&st);
                return false;
            }
            char top=STtop(&st);
            STpop(&st);
            if(top=='('&&*s!=')'
            ||top=='['&&*s!=']'
            ||top=='{'&&*s!='}')
            {
            STDestroy(&st);
            return false;
            }
        }
        s++;
    }
//当栈中没有左括号比较完时,便匹配不成
    bool ret=STEmpty(&st);
    STDestroy(&st);
    return ret;
}

可能有人说太麻烦了,但c语言中没有栈,只能自己创建哦!这是用目前c语言最简单的方法!

栈的实现--链表

和顺序表一样,我们首先要创建栈和链表的基本框架!

stack.h文件--包含需要的函数

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;
//栈
typedef struct stack
{
	struct stcak* next;
	STDataType data;
}STNode;


//栈的销毁
void STDestroy(STNode* phead);
//入栈
void STpush(STNode** pphead,STDataType x);
//出栈
void STpop(STNode** pphead);
//栈顶数据
STDataType STtop(STNode* phead);
//判空
bool STEmpty(STNode* phead);
//栈数据个数
int STsize(STNode* phead);

栈为空的判定

有了顺序表的基础,接下来依葫芦画瓢----最简单不过!

//判空
bool STEmpty(STNode* phead)
{
	return phead == NULL;
}

入栈

我们分析将链表的头部为栈顶,进出都在头!(这种方案最佳!)

//创建节点
STNode* STBuyNode(STDataType x)
{
	STNode* node = (STNode*)malloc(sizeof(STNode));
	if (node == NULL)
	{
		perror("malloc failed!");
		return NULL;
	}
	node->data = x;
	node->next = NULL;
	return node;
}

//入栈
void STpush(STNode** pphead, STDataType x)
{
	STNode* node = STBuyNode(x);
	node->next = *pphead;
	*pphead = node;
}

出栈

将头节点指向下一个节点,原来的头节点释放!

//出栈
void STpop(STNode** pphead)
{
	assert(!STEmpty(*pphead));
	STNode* cur = *pphead;
	*pphead = (*pphead)->next;
	free(cur);
}

销毁

同样的,动态开辟了空间,当我们不用栈时,要将开辟的空间释放掉!

//栈的销毁
void STDestroy(STNode* phead)
{
	STNode* cur = phead;
	while (cur)
	{
		STNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

栈顶数据

也是一样的,在访问栈顶数据时,要判断栈是否为空,防止越界访问!

//栈顶数据
STDataType STtop(STNode* phead)
{
	assert(!STEmpty(phead));
	return phead->data;
}

数据个数

遍历链表,将一个一个节点计数起来!

//栈数据个数
int STsize(STNode* phead)
{
	STNode* cur = phead;
	int count = 0;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

码源--栈(顺序表)

stack.h文件

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDateType;
//栈
typedef struct stack
{
	STDateType* a;
	int top;
	int capacity;
}ST;

//栈的初始化和销毁
void STInit(ST* p);
void STDestroy(ST* p);
//入栈,出栈
void STpush(ST* p,STDateType x);
void STpop(ST* p);
//栈顶的数据
STDateType STtop(ST* p);
//栈的数据个数
int STsize(ST* p);
//判空
bool STEmpty(ST* p);

stack.c文件

#include"stack.h"
//栈的初始化和销毁
void STInit(ST* p)
{
	assert(p);
	p->a = NULL;
	p->capacity = 0;
	p->top = 0;
}
void STDestroy(ST* p)
{
	assert(p);
	free(p->a);
	p->capacity = p->top = 0;
}
//入栈,出栈
void STpush(ST* p, STDateType x)
{
	assert(p);
	if (p->top == p->capacity)
	{
		int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
		STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
		if (tmp == NULL)
		{
			perror("realloc failed!");
			return;
		}
		p->a = tmp;
		p->capacity = newcapacity;
	}
	p->a[p->top++] = x;
}
void STpop(ST* p)
{
	assert(p);
	//出栈的话要判断一下空的情况
	assert(!STEmpty(p));
	p->top--;
}
//栈顶的数据
STDateType STtop(ST* p)
{
	assert(p);
	//出栈的话要判断一下空的情况
	assert(!STEmpty(p));
	return p->a[p->top-1];
}
//栈的数据个数
int STsize(ST* p)
{
	assert(p);
	return p->top;
}
//判空
bool STEmpty(ST* p)
{
	assert(p);
	return p->top == 0;
}

码源--栈(链表)

stack.h文件

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;
//栈
typedef struct stack
{
	struct stcak* next;
	STDataType data;
}STNode;


//栈的销毁
void STDestroy(STNode* phead);
//入栈
void STpush(STNode** pphead,STDataType x);
//出栈
void STpop(STNode** pphead);
//栈顶数据
STDataType STtop(STNode* phead);
//判空
bool STEmpty(STNode* phead);
//栈数据个数
int STsize(STNode* phead);

stack.c文件

#include"stack.h"

STNode* STBuyNode(STDataType x)
{
	STNode* node = (STNode*)malloc(sizeof(STNode));
	if (node == NULL)
	{
		perror("malloc failed!");
		return NULL;
	}
	node->data = x;
	node->next = NULL;
	return node;
}

//入栈
void STpush(STNode** pphead, STDataType x)
{
	STNode* node = STBuyNode(x);
	node->next = *pphead;
	*pphead = node;
}

//出栈
void STpop(STNode** pphead)
{
	assert(!STEmpty(*pphead));
	STNode* cur = *pphead;
	*pphead = (*pphead)->next;
	free(cur);
}

//栈顶数据
STDataType STtop(STNode* phead)
{
	assert(!STEmpty(phead));
	return phead->data;
}

//判空
bool STEmpty(STNode* phead)
{
	return phead == NULL;
}

//栈数据个数
int STsize(STNode* phead)
{
	STNode* cur = phead;
	int count = 0;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

//栈的销毁
void STDestroy(STNode* phead)
{
	STNode* cur = phead;
	while (cur)
	{
		STNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

是不是觉得今天的比较简单?好了,我们下期见!

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

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

相关文章

【ARM】ARM寄存器和异常处理

目录 1.指令的执行过程 2. ARM处理器概述 3.ARM指令集 4.ARM存储模型 5. ARM工作模式 6.ARM寄存器组织 &#xff08;1&#xff09;寄存器 &#xff08;2&#xff09; ARM寄存器 &#xff08;3&#xff09;CPSR寄存器​​​​​​​ 7. ARM异常处理 &#xff08;1&am…

26 | 备库为什么会延迟好几个小时?

在官方的 5.6 版本之前,MySQL 只支持单线程复制,由此在主库并发高、TPS 高时就会出现严重的主备延迟问题。 coordinator 就是原来的 sql_thread, 不过现在它不再直接更新数据了,只负责读取中转日志和分发事务。真正更新日志的,变成了 worker 线程。而 work 线程的个数,就是…

IDEA远程连接Docker服务

1.确保你的服务器已经安装docker docker安装步骤可查看&#xff1a;CentOS 9 (stream) 安装 Docker 2.安装完docker后开启远程连接 默认配置下&#xff0c;Docker daemon只能响应来自本地Host的客户端请求。如果要允许远程客户端请求&#xff0c;需要在配置文件中打开TCP监听…

CCF-Csp算法能力认证,202206-1归一化处理(C++)含解析

前言 推荐书目&#xff0c;在这里推荐那一本《算法笔记》&#xff08;胡明&#xff09;&#xff0c;需要PDF的话&#xff0c;链接如下 「链接&#xff1a;https://pan.xunlei.com/s/VNvz4BUFYqnx8kJ4BI4v1ywPA1?pwd6vdq# 提取码&#xff1a;6vdq”复制这段内容后打开手机迅雷…

抖音小店保证金真的能退回吗?内行人的真实情况大揭秘!

哈喽~我是电商月月 新手开抖音小店都会纠结于保证金的缴纳&#xff0c;不同类目保证金金额不同&#xff0c;不同营业执照办理的店铺&#xff0c;保证金收费也不同&#xff0c;具体的缴纳金额大家可以去浏览器搜索抖店官网进行查询 当然找不到的&#xff0c;也可以直接问我&am…

C++程序设计:C++的内存分布与管理

C的内存分布与管理 栈区堆区全局区代码区常量区 栈区 &#xff08;1&#xff09;什么是栈区&#xff1f; 栈区&#xff08;Stack&#xff09; 是用于存储函数调用&#xff0c;局部变量和函数参数的一种内存区域&#xff0c;它的特性就是先进后出&#xff08;FILO&#xff09;。…

揭秘抖音快速涨10000粉的方法:巨量千川投流让你轻松快速增粉

抖音已经成为了当今社交平台的热门之一&#xff0c;而如何快速涨粉已经成为了很多人关注的焦点。本文将揭秘一种高效的方式——巨量千川投流&#xff0c;通过官方真实流量和真实粉丝&#xff0c;每天快速涨关注&#xff0c;实现快速增粉1000~10万。 巨量千川投流是一种专业的抖…

proteus使用问题

1、无法和视频里面一样新建工程 2、实验效果和视频不也一样 自己的电路图(灯不亮)&#xff1a;

vant中van-tabs使用中的小问题

1. 怎么去掉默认选中的效果 van-tabs默认情况下启用第一个标签&#xff0c;实际开发中不满足需求&#xff0c;想要点击后再进行选中 解决办法 首先&#xff0c;在标签组数中&#xff0c;添加一个占位标签在样式中设置首个标签不显示代码如下&#xff1a; //js 实际有意思的…

<sa8650> snapdragon-auto-hqx-4-5-6-0基线代码camera开发错误记录

<sa8650> snapdragon-auto-hqx-4-5-6-0基线代码camera开发错误记录 一、 qcxserver自启动关不掉二、 qcxserver启动后串行器(MAX96717)detect不到?三、 qcarcam_test运行后系统log提示QCarCamReserve错误四、 tuning错误导致的异常DumpCore本文主要记录CAMERA开发过程中的一…

spring框架学习记录(3)

Spring事务 Spring事务简介 事务作用&#xff1a;在数据层保障一系列的数据库操作同成功同失败Spring事务作用&#xff1a;在数据层或业务层保障一系列的数据库操作同成功或同失败 Spring事务角色 事务管理员&#xff1a;发起事务方&#xff0c;在Spring中通常指代业务层开…

计算机网络学习记录 物理层 Day2

计算机网络学习记录 你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner gitee https://gitee.com/Qiuner 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会…

48. 旋转图像/240. 搜索二维矩阵 II

48. 旋转图像 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 &#xff1a; 输入&#xff1a;matrix [[5,1,9,11],[2,4,…

【STM32】西南交大嵌入式实验四:异步串行通信

实验板串行相关电路&#xff1a; 数码管等外设的电路前面的实验已经提到过&#xff0c;不再赘述。 使用cubeMX配置项目&#xff1a; 因为数码管&#xff0c;蜂鸣器这些外设对应的引脚在前面的项目里已经配置过&#xff0c;可以选择前面的一个项目&#xff0c;另存为&#xff0…

[Kubernetes] sealos部署 K8s 集群

文章目录 1.sealos 介绍2.操作系统基础配置3.安装部署 K8s4.验证 K8s 集群5.部署测试资源 1.sealos 介绍 Sealos 是一个基于 Kubernetes 内核的云操作系统发行版。它采用云原生方式&#xff0c;摒弃传统的云计算架构&#xff0c;转向以 Kubernetes 为云内核的新架构。这使得企…

【训练与预测】02 - 完整的模型验证套路

02 - 完整的模型验证套路 模型图 验证一个模型就是指使用已经训练好的模型&#xff0c;然后给它提供输入。 test.py import torch import torchvision from PIL import Imagedevice torch.device("cuda" if torch.cuda.is_available() else "cpu") ima…

C/C++ 初级球球大作战练手

效果演示&#xff1a; https://live.csdn.net/v/385490 游戏初始化 #include <stdbool.h> #include<stdio.h> #include<stdlib.h> #include<time.h> #include<graphics.h> #include <algorithm> #include<math.h> #include<mmsy…

飞天使-k8s知识点31-rancher的正确打开方式

文章目录 安装之前优化一下内核参数以及系统内核版本 rancher安装主要是使用以下命令nginx的配置为解决办法 安装之前优化一下内核参数以及系统内核版本 内核版本 4.17 cat > /etc/modules-load.d/iptables.conf <<EOF ip_tables iptable_filter EOF 然后重启服务器…

燃气电力瓶装气行业入户安检小程序开发

我们开发的小区业主入户安检小程序&#xff0c;旨在满足燃气、电力以及其他需要入户安检的行业需求。该程序支持自定义安检项目&#xff0c;实现线下实地安检与线上数据保存的完美结合。在安检过程中&#xff0c;我们可以拍照或录像&#xff0c;以确保安检的透明性和可追溯性&a…

亚马逊是如何铺设多个IP账号实现销量大卖的?

一、针对亚马逊平台机制&#xff0c;如何转变思路&#xff1f; 众所周知&#xff0c;一个亚马逊卖家只能够开一个账号&#xff0c;一家店铺&#xff0c;这是亚马逊平台明确规定的。平台如此严格限定&#xff0c;为的就是保护卖家&#xff0c;防止卖家重复铺货销售相同的产品&a…