数据结构--栈与队列

news2024/11/24 14:30:30

目录


前言

1.栈

1.1栈的概念及结构

 1.2接口函数

 1.3函数实现

1.4如何使用

2.队列 

2.1队列的概念及结构

2.2接口函数

 2.3函数实现

2.4如何使用


前言

        前面我们已经学习了顺序表和链表,今天我们来学习栈与队列,这两种结构也属于线性表,实际上就是顺序表和链表结构的延展。


1.栈


1.1栈的概念及结构

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


 1.2接口函数

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

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

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;

// 支持动态增长的栈
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
bool StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);

 1.3函数实现

1.单个节点成员介绍

typedef struct Stack
{
	STDataType* _a;
	int _top; // 栈顶
	int _capacity; // 容量
}Stack;

        在这里我们使用顺序表来实现栈结构,当然使用单链表,双向链表也可以。我们可以看到有成员top,它是用来指向栈顶元素的,当然也可以指向栈顶元素的下一个,这取决于你的实现逻辑。这里的顺序表,显然也是动态的。


2.初始化栈

void StackInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_capacity = NULL;
	ps->_top = 0;

}

        关于top的初始化值的问题。

 这里可以将top初识化为0,当然也可以将top初始化为-1。这两种逻辑有何不同呢?

        1.将top初识化为0,假如在top位置插入一个数据,那么top就要向后移动一位。如果top不像后移动一位,那么就区分不了0位置是有数据还是没有数据了。所以top==0时,top表示的是指向栈顶元素的后一位。        

        2.将top初识化为-1,假如在top位置插入一个数据当然也是要向后面移动一位 。所以当top==-1时,top表示的时指向栈顶元素。

        在这里我是将top初始化为0了。


3.入栈

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->_top == ps->_capacity)
	{
		int newcapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
		Stack* tmp = (Stack*)realloc(ps->_a, sizeof(Stack) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		ps->_a = tmp;
		ps->_capacity = newcapacity;
	}
	
	ps->_a[ps->_top] = data;
	ps->_top++;
}

        要实现入栈操作是十分简单的,首先要创建一个新的节点,在这扩容操作没必要单独封装,因为只有入栈操作时才用的到。不要忘记断言。

        插入操作是十分简单的,只需要在top位置插入,再让top指针向后移动就好了。


4.出栈

void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->_top > 0);
	ps->_top--;
}

        出栈操作也是十分简单,只需要让top向前移动一位就好了。


5.返回栈顶元素

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->_top > 0);
	return ps->_a[ps->_top - 1];
}

        进行栈顶元素返回操作的时候,需要注意的是,要将top-1,因为top是指向栈顶元素的下一个位置。


6.获取栈中有效元素个数

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top;
}

        直接返回top的值就好了,在此逻辑种top值就是元素的个数。


7.检测栈是否为空

bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->_top == 0;//等于0为真,否则假
}

        在此逻辑下,top值为空就代表栈为空,所以只需要判断top是否为0就好了,真则返回true,假则返回false。


8.销毁栈

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_capacity = 0;
	ps->_top = 0;
}

        因为是用顺序表实现栈的,所以直接free掉malloc出来的空间就好了。

完!!!


1.4如何使用

 

int main()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);

	StackPush(&s, 3);
	
	printf("栈内又%d个数据\n", StackSize(&s));
	while (!StackEmpty(&s))
	{
		printf("%d\n", StackTop(&s));
		StackPop(&s);
	}
	printf("\n");
	StackDestroy(&s);
	return 0;
}

        我们要获取栈内元素时,可以利用循环操作,直到栈内数据为空,要注意的是,每获取一次栈顶元素时,要进行出栈操作,才能取到下一个数据。

        值得一提的是:如果入栈时,在数据没有全部入栈时,突然出栈一个数据,然后在进行入栈,最后数据的顺序会被打乱

int main()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	printf("%d\n", StackTop(&s));
	StackPop(&s);
	StackPush(&s, 3);
	
	printf("栈内又%d个数据\n", StackSize(&s));
	while (!StackEmpty(&s))
	{
		printf("%d\n", StackTop(&s));
		StackPop(&s);
	}
	printf("\n");
	StackDestroy(&s);
	return 0;
}


2.队列 


2.1队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头


2.2接口函数

        队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* _pNext;
	QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
	int size;
}Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);

 2.3函数实现

1.单个节点成员介绍

typedef struct QListNode
{
	struct QListNode* _pNext;
	QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
	int size;
}Queue;

        

        实现队列我们使用头尾两个指针是最优的,但为什么,不在实现函数内直接定义,而直接在结构体内封装头和尾呢?这样做可以避免二级指针发的使用。如果队列为空,第一次进行入队操作时,就涉及到要改变结构体指针的操作,这时我们就要用到结构体指针的指针,也就是二级指针。但如果,像这样封装一下,就起到了哨兵位的作用就不用传二级指针了。


2.初始化队列

void QueueInit(Queue* q)
{
	assert(q);
	q->_front=q->_rear = NULL;
	
	q->size = 0;
}

3.队尾入队列

void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->_data = data;
	newnode->_pNext = NULL;
	if (q->_rear == NULL)
	{
		q->_front = q->_rear = newnode;

	}
	else
	{
		q->_rear->_pNext = newnode;
		q->_rear = newnode;
	}
	q->size++;
}

        入队列操是十分简单的,只需要利用队尾指针添加新节点就好了。


4.队头出队列

void QueuePop(Queue* q)
{
	assert(q);
	assert(q->_front);
	QNode* cur = q->_front;
	q->_front = q->_front->_pNext;
	free(cur);
	cur = NULL;
	if (q->_front == NULL)
		q->_rear = NULL;
		
	q->size--;
}

        在进行出队操作时,注意保存队头的下一个节点,然后free出队。要注意的是,队列数据为空,就不能进行出队操作了,可以进行断言assert(q->_front)防止。当只剩一个节点的时候,头尾指针指向一个节点,这是就要判断队头是否为空,如果为空,那也要将尾指针置空,防止野指针。


5.获取队列头部元素

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->_front);
	return q->_front->_data;
	
}

6.获取队列尾部元素

QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->_rear);
	return q->_rear->_data;
}

7.获取队列中有效元素个数

int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}

8.检查队列是否为空

bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->_front == NULL;
}

        这里只需要检查对头是否为空就好了。


9.销毁队列

void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->_front;
	while (cur)
	{
		QNode* next = cur->_pNext;
		free(cur);
		cur = next;
	}
	q->_front = q->_rear = NULL;
	q->size = 0;
}

        这里我们依然是保存后一个,销毁当前节点,所有节点都销毁后,将头尾指针置空。

完!!!


2.4如何使用

int main()
{
	Queue s;
	QueueInit(&s);
	QueuePush(&s, 1);
	QueuePush(&s, 2);
	QueuePush(&s, 3);
	QueuePush(&s, 4);
	QueuePush(&s, 5);
	printf("%d个元素\n", QueueSize(&s));
	
	while (!QueueEmpty(&s))
	{
		printf("%d ", QueueFront(&s));
		QueuePop(&s);
	}
	QueueDestroy(&s);
	
	return 0;
}

        这里我们也是利用循环,将所有数据获取,获取一个数据,出一个数据。

值得一提的是:如果入队时,在数据没有全部入栈时,突然出队一个数据,然后在进行入栈,最后数据的顺序也不会被打乱

int main()
{
	Queue s;
	QueueInit(&s);
	QueuePush(&s, 1);
	QueuePush(&s, 2);
	QueuePush(&s, 3);
	QueuePush(&s, 4);
	printf("%d\n", QueueFront(&s));
	QueuePop(&s);
	QueuePush(&s, 5);
	printf("%d个元素\n", QueueSize(&s));
	
	while (!QueueEmpty(&s))
	{
		printf("%d ", QueueFront(&s));
		QueuePop(&s);
	}
	QueueDestroy(&s);
	
	return 0;
}

希望这篇文章对你有帮助!!!

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

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

相关文章

【数据结构初阶】单链表(附全部码源)

单链表 1&#xff0c;单链表的概念及结构2&#xff0c;单链表的实现2.1初始化内容&#xff08;所需文件&#xff0c;接口&#xff09;2.2申请结点2.3打印单链表2.4尾插2.5头插2.6尾删2.7头删2.8查找2.9在pos位置之后插入2.10在pos位置前面插入2.11删除pos之后的值2.12删除pos位…

2023亚太杯数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

接口测试系列之 —— 接口安全测试

“开源 Web 应用安全项目”(OWASP)在 2019 年发布了 API 十大安全风险 《OWASP API 安全 Top10》&#xff1a;失效的对象级别授权、失效的用户身份验证、过 度的数据暴露、资源缺乏和速率限制、失效的功能级授权、批量分配、安全配置 错误、注入、资产管理不当、日志和监视不足…

百家网约车平台发布“阳光五条” 多举措加强司机保障

11月17日&#xff0c;免佣联盟百家网约车平台发布“阳光五条”&#xff0c;通过加大免佣力度、实行车费保镖司机版、72小时保护期等措施&#xff0c;加强对网约车司机的权益保障。 近年&#xff0c;交通运输部推动交通运输新业态平台企业落实“阳光行动”等工作&#xff0c;加…

图像分类系列(三) GoogLeNet InceptionV1学习详细记录

前言 ​ 在上一期中介绍了VGG&#xff0c;VGG在2014年ImageNet 中获得了定位任务第1名和分类任务第2名的好成绩&#xff0c;而今天要介绍的就是同年分类任务的第一名——GoogLeNet 。 ​ 作为2014年ImageNet比赛冠军&#xff0c;GoogLeNet 比VGG更深的网络&#xff0c;比Alex…

11月17日,国家大基金三期隆重开启,共同见证芯片产业新时代!

11月17日&#xff0c;国家大基金三期隆重开启&#xff0c;共同见证芯片产业新时代&#xff01; 自国家大基金二期于2019年10月注册成立以来&#xff0c;一直积极响应国家战略和新兴行业发展规划&#xff0c;对设计创新行业的投资加大规模&#xff0c;比如智能汽车、智能电网、人…

初识Linux:目录的创建销毁

目录 ​编辑 提示&#xff1a;以下指令均在Xshell 7 中进行 零、桌面的本质 &#x1f4bb; 扩展&#x1f387;&#xff1a; 一、cd指令&#xff1a; 1、cd - &#xff1a; 2、cd ~&#xff1a; 重命名命令&#xff1a;alias 二、stat指令 冷知识&#xff1a; 如果…

如何在工作外发展副业?主业和副业该如何权衡

有一句话说得好&#xff0c;不要把所有的鸡蛋放在一个篮子里。在面对繁忙的工作生活之外&#xff0c;想要拥有额外的收入来源那就是做一份不影响主业的副业。而副业的发展&#xff0c;不仅能够增加收入&#xff0c;更可以拓展个人的技能和兴趣。 主业跟副业该如何权衡呢&#x…

时间序列预测(6) — ARIMA实现单输入单输出负荷预测

目录 1 数据准备与可视化 2 简单数据探索与清洗 3 差分处理 4 绘制ACF与PACF图像&#xff0c;完成模型选择 5 建立ARIMA和SARIMA模型 5.1 初步建模 5.2 精细化建模 5.3 最终的模型 ARIMA作为成熟的统计学模型已被各种软件以各种方式实现&#xff0c;在Python中我们最常使…

服装鞋帽箱包展示预约小程序的效果是什么

市场上售卖服装、鞋帽箱包的品牌店或小店摊贩非常多&#xff0c;同时这些产品又是人们生活的必需品&#xff0c;以前购买服装等纺织产品&#xff0c;消费者习惯前往线下商场或品牌店&#xff0c;但如今更多的消费者习惯于线上购买&#xff0c;传统门店经营面临困境。 通过【雨科…

暖阳脚本_ 定制企业软件开发的4个趋势:AI、RPA、云应用、边缘计算

根据 Statista 的统计数据显示&#xff0c;企业级软件市场在全球范围内占据了领先地位&#xff0c;预测到2028年&#xff0c;市场规模将接近3760亿美元。企业应用软件市场的稳健增长&#xff0c;甚至在经济不景气的时候也能持续&#xff0c;这充分表明软件解决方案对于提升企业…

使用 Redis BitMap 实现签到与查询历史签到以及签到统计功能(SpringBoot环境)

目录 一、前言二、Redis BitMap 位图原理2.1、BitMap 能解决什么2.2、BitMap 存储空间计算2.3、BitMap 存在问题 三、Redis BitMap 操作基本语法和原生实现签到3.1、基本语法3.2、Redis BitMap 实现签到操作指令 四、SpringBoot 使用 Redis BitMap 实现签到与统计功能4.1、代码…

避雷指南:电视盒子哪个牌子最好?最具性价比电视盒子排行榜

电视盒子有些会出现死机和卡顿&#xff0c;广告植入过多&#xff0c;操作复杂等问题&#xff0c;大家在选购时极易踩雷&#xff0c;我身为数码测评员&#xff0c;本期测评的主题是电视盒子哪个牌子最好&#xff0c;购入了市面上最热销的电视盒子对比后整理了最具性价比电视盒子…

ubuntu提高 github下载速度

Github一般用于Git的远程仓库&#xff0c;由于服务器位于国外&#xff0c;国内访问速度比较慢&#xff0c;为了提高访问速度&#xff0c;决定绕过DNS域名解析。 获取Github的IP地址 按下ctrl&#xff0b;alt&#xff0b;T打开命令终端&#xff0c;输入&#xff1a; nslookup gi…

数据治理入门

处理模式 模式名称常见场景常见框架批处理夜间几个小时&#xff0c;无人值守hive spark datax流处理7*24H一直运行&#xff0c;无人值守maxwell, flink, flume, kafka即席处理人机交互接口访问 web页面 数据治理的意义 数据质量低&#xff1a;数据错误&#xff0c;不准确或不…

Gooxi国鑫金秋发布会圆满召开,引领数智新未来

10月24日&#xff0c;主题为“芯加速创鑫局”的2023 Gooxi第四代英特尔至强可扩展处理器平台新品发布会隆重召开&#xff0c;Gooxi重磅发布基于第四代英特尔至强可扩展处理器平台系列新品&#xff0c;Gooxi英特尔平台算力迎来全新升级进化&#xff0c;为AI注入全新发展动力&…

微信小程序相机相册授权后,需要重启客户端才能正常调用相机,无法调起窗口选择图片,无反应解决方案

最近微信小程序很多功能突然不能使用&#xff0c;本篇针对无法调起相册进行说明 解决方案 检查小程序隐私协议是否配置&#xff0c;操作步骤这里不在详细说明&#xff0c;点击教程按照上面的教程&#xff0c;找到入口后点击完善或者更新 选择选中的照片或视频这个权限要申请 之…

牛客机考题编程题输入输出

有时空可以练练这里的题目&#xff1a; https://ac.nowcoder.com/acm/contest/5652 做个总结&#xff0c;其实就两种输入类型&#xff1a; 一种是下面这种&#xff0c;需要对输入的每行进行运算 这种就是循环读取每行的数做一个运算&#xff1a; import sys while True:line …

CodeWhisperer--手把手教你使用一个十分强大的工具

Amazon CodeWhisperer 是一款能够帮助我们智能生成代码的工具。经过数十亿行代码的训练&#xff0c;可以根据提示和现有代码实时生成从片段到完整功能的代码建议。类似 Cursor 和 Github Copilot 编码工具。目前&#xff0c;CodeWhisperer 兼容 Python、Java 和 JavaScript&…