栈和队列(详解)

news2024/11/29 12:37:35

🍕博客主页:️自信不孤单

🍬文章专栏:数据结构与算法

🍚代码仓库:破浪晓梦

🍭欢迎关注:欢迎大家点赞收藏+关注

文章目录

  • 🍓栈
    • 1. 栈的概念及结构
    • 2. 栈的实现
      • 2.1 初始化栈
      • 2.2 入栈
      • 2.3 检测栈是否为空
      • 2.4 出栈
      • 2.5 获取栈顶元素
      • 2.6 获取栈中有效元素个数
      • 2.7 销毁栈
    • 3. 接口测试
  • 🍊队列
    • 1. 队列的概念及结构
    • 2. 队列的实现
      • 2.1 初始化队列
      • 2.2 入队列
      • 2.3 检测队列是否为空
      • 2.4 出队列
      • 2.5 获取队列头部元素
      • 2.6 获取队列队尾元素
      • 2.7 获取队列中有效元素个数
      • 2.8 销毁队列
    • 3. 接口测试
    • 4. 拓展——环形队列


🍓栈

1. 栈的概念及结构

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

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

  • 出栈:栈的删除操作叫做出栈。出数据也在栈顶

在这里插入图片描述

在这里插入图片描述

2. 栈的实现

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

首先创建两个文件来实现栈:

  1. Stack.h(节点的声明、接口函数声明、头文件的包含)
  2. Stack.c(栈接口函数的实现)

接着创建 test.c 文件来测试各个接口
如图:

在这里插入图片描述

Stack.h 文件内容如下:

#pragma once
#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* pst);
// 入栈
void STPush(ST* pst, STDataType x);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool STEmpty(ST* pst);
// 出栈
void STPop(ST* pst);
// 获取栈顶元素
STDataType STTop(ST* pst);
// 获取栈中有效元素个数
int STSize(ST* pst);
// 销毁栈
void STDestroy(ST* pst);

接下来,我们在 Stack.c 文件中实现各个接口函数。

2.1 初始化栈

对栈进行置空。

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

2.2 入栈

判断扩容,然后直接尾插即可。

void STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->capacity == pst->top)
	{
		STDataType NewCapacity = (pst->capacity == 0 ? 4 : (pst->capacity) * 2);
		STDataType* tmp = (STDataType*)realloc(pst->a, NewCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = NewCapacity;
	}
	pst->a[pst->top++] = x;
}

2.3 检测栈是否为空

栈为空返回 ture,否则返回 false。

bool STEmpty(ST* pst)
{
	return pst->top == 0;
}

2.4 出栈

断言检测栈是否为空,非空直接尾删即可。

void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	pst->top--;
}

2.5 获取栈顶元素

断言检测栈是否为空,非空返回栈顶元素。

STDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	return pst->a[pst->top - 1];
}

2.6 获取栈中有效元素个数

返回栈中元素个数。

int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}

2.7 销毁栈

释放动态开辟好的内存,并对数据进行置空。

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

注:在每个接口函数中一定要合理地使用assert函数断言防止对空指针的引用。

3. 接口测试

test.c 文件内容如下:

#include "Stack.h"
#include <stdio.h>

void TestStack()
{
	ST stack;
	STInit(&stack);
	STPush(&stack, 4);
	STPush(&stack, 1);
	printf("%d ", STTop(&stack));
	STPop(&stack);
	STPush(&stack, 3);
	STPush(&stack, 2);

	while (!STEmpty(&stack))
	{
		printf("%d ", STTop(&stack));
		STPop(&stack);
	}

	STDestroy(&stack);
}

int main()
{
	TestStack();
	return 0;
}

运行结果:

在这里插入图片描述

🍊队列

1. 队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。 进行插入操作的一端称为队尾,进行删除操作的一端称为队头。队列中的数据元素遵循先进先出FIFO(First In First Out)的原则。

  • 入队列:队列的插入操作叫做入队列。从队尾插入数据。
  • 出队列:队列的删除操作叫做出队列。从对头删除数据。

在这里插入图片描述

在这里插入图片描述

2. 队列的实现

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

首先创建两个文件来实现队列:

  1. Queue.h(节点的声明、接口函数声明、头文件的包含)
  2. Queue.c(队列接口函数的实现)

接着创建 test.c 文件来测试各个接口
如图:

在这里插入图片描述

Queue.h 文件内容如下:

#pragma once
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

// 初始化队列
void QueueInit(Queue* pq);
// 队尾入队列
void QueuePush(Queue* pq, QDataType x);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* pq);
// 队头出队列
void QueuePop(Queue* pq);
// 获取队列头部元素
QDataType QueueFront(Queue* pq);
// 获取队列队尾元素
QDataType QueueBack(Queue* pq);
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
// 销毁队列
void QueueDestroy(Queue* pq);

接下来,我们在 Queue.c 文件中实现各个接口函数。

2.1 初始化队列

对队列进行置空。

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

2.2 入队列

在堆上申请一个节点,判断是否为第一个节点。如果是第一个节点,就让队列的头尾指针指向该节点;如果不是第一个节点,直接尾插即可。

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	if (NewNode == NULL)
	{
		perror("malloc fail");
		return;
	}

	NewNode->data = x;
	NewNode->next = NULL;

	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = NewNode;
	}
	else
	{
		pq->ptail->next = NewNode;
		pq->ptail = NewNode;
	}
	pq->size++;
}

2.3 检测队列是否为空

队列为空返回 ture,否则返回 false。

int QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

2.4 出队列

断言检测队列是否为空,非空直接头删,最后判断删除的是否是最后一个节点。如果是最后一个节点,需要将尾指针置空。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* next = pq->phead;
	free(pq->phead);
	pq->phead = next;
	pq->size--;
	if (pq->size == 0)
	{
		pq->ptail = NULL;
	}
}

2.5 获取队列头部元素

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}

2.6 获取队列队尾元素

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}

2.7 获取队列中有效元素个数

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

2.8 销毁队列

将队列中的所有元素都执行出队列操作即可。

void QueueDestroy(Queue* pq)
{
	assert(pq);
	while (!QueueEmpty(pq))
	{
		QueuePop(pq);
	}
}

注:在每个接口函数中一定要合理地使用assert函数断言防止对空指针的引用。

3. 接口测试

test.c 文件内容如下:

#include "Queue.h"
#include <stdio.h>

void TestQueue()
{
	Queue qu;
	QueueInit(&qu);
	QueuePush(&qu, 5);
	QueuePush(&qu, 0);
	QueuePush(&qu, 2);
	printf("%d ", QueueFront(&qu));
	printf("%d ", QueueBack(&qu));

	QueuePop(&qu);
	printf("%d ", QueueFront(&qu));

	QueuePop(&qu);
	QueuePop(&qu);

	QueuePush(&qu, 1);
	QueuePush(&qu, 3);
	QueuePush(&qu, 1);
	QueuePush(&qu, 4);

	while (!QueueEmpty(&qu))
	{
		printf("%d ", QueueFront(&qu));
		QueuePop(&qu);
	}

	QueueDestroy(&qu);
}

int main()
{
	TestQueue();
	return 0;
}

运行结果:

在这里插入图片描述

4. 拓展——环形队列

实际中我们有时还会使用一种队列叫循环队列。

循环队列是一种线性数据结构,其操作表现基于 先进先出 原则,并且 队尾被连接在队首之后以形成一个循环。它也被称为 “环形缓冲器”

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:

(1)为了避免 混淆,无法区分,那么可以 多开一个空间

(2)当 head == tail 时,就为

(3)当 tail 的下一个位置是 head 时,就是满。

循环队列如果用链表实现的话,不容易找尾,所以一般我们用数组来实现。


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

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

相关文章

MySQL运维篇(三)

五.读写分离 5.1 介绍 读写分离,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作&#xff0c;从数据库提供读操作&#xff0c;这样能有效地减轻单台数据库的压力。 通过MyCat即可轻易实现上述功能&#xff0c;不仅可以支持MySQL&#x…

【论文总结】Composition Kills: A Case Study of Email Sender Authentication

构成杀伤力&#xff1a; 电子邮件发送者认证的案例研究 摘要 基于组件的软件设计是构建现代软件系统的一种主要工程方法。然而&#xff0c;由于不同组件之间对信息的解释可能不一致&#xff0c;这种编程范式产生了安全问题。在本文中&#xff0c;我们利用这种不一致来识别电子…

双列集合 JAVA

双列集合 一次需要添加一对数据&#xff0c;分别为键和值键不可以重复&#xff0c;值可以重复键和值是一一对应的&#xff0c;每一个键只可以找到自己对应的值键值对在java中也叫做Entry对象 #mermaid-svg-zKLj0vUbRaN9zlse {font-family:"trebuchet ms",verdana,ar…

SpringBoot2-基础入门(二)

SpringBoot2 - 基础入门&#xff08;二&#xff09; 了解自动装配原理 文章目录 SpringBoot2 - 基础入门&#xff08;二&#xff09;了解自动装配原理一、依赖管理1.1 父项目做依赖管理1.2 starer场景启动器 2、自动配置2.1 自动配置依赖2.2 组件扫描 3、配置文件3.1 各种配置…

【软件测试知识】

目录 软件测试软件测试模型瀑布模型V 模型W 模型敏捷开发模型 软件开发流程软件测试方法白盒测试黑盒测试 软件测试 软件测试模型 说到开发模型&#xff0c;从软件发展来看&#xff0c;比较典型的有瀑布模型&#xff0c;V 模型和 W 模型以及 敏捷开发模型。并不是说开发模型的…

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

&#x1f600;如果对你有帮助的话&#x1f60a; &#x1f33a;为博主点个赞吧 &#x1f44d; &#x1f44d;点赞是对博主最大的鼓励&#x1f60b; &#x1f493;爱心发射~&#x1f493; 目录 一、发送邮件1、启用客户端SMTP服务2、导入jar包3、邮箱参数配置MailClientdemo.html…

第13届蓝桥杯Scratch省赛真题集锦

编程题 第 1 题 问答题 报数游戏 题目说明 背景信息: 5个男生和3个女生&#xff0c;8个人围成一个圆圈&#xff0c;给定一个数字n (2 小于等于n 小于等于5)。从第一个开始依次报数&#xff0c;当报数为n时&#xff0c;这个人离开圆圈。然后下一个从1开始报数&#xff0c;再次报…

MySQL---使用索引优化、大批量插入数据优化

1. 使用索引优化 索引是数据库优化最常用也是最重要的手段之一, 通过索引通常可以帮助用户解决大多数的MySQL 的性能优化问题&#xff1a; create table tb_seller (sellerid varchar (100),name varchar (100),nickname varchar (50),password varchar (60),status varchar…

高级Java多线程面试题及回答

高级Java多线程面试题及回答 1)现在有T1、T2、T3三个线程&#xff0c;你怎样保证T2在T1执行完后执行&#xff0c;T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到&#xff0c;目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单&#xff0c;可…

网络安全的红利还能吃几年?

在我看来这是一个伪命题&#xff0c;因为网络安全的核心和本质是持续对抗&#xff0c;只要威胁持续存在&#xff0c;网络安全的红利就会持续存在&#xff01; 对于网络安全新入行的同学们来说&#xff0c;这是一个最坏的时代&#xff0c;因为你只能自己搭环境才能重现那些大牛们…

网络编程 lesson5 IO多路复用

select 当需要在一个或多个文件描述符上等待事件发生时&#xff0c;可以使用select函数。 select函数是一个阻塞调用&#xff0c;它会一直等待&#xff0c;直到指定的文件描述符上有事件发生或超时。 select函数详解 int select(int nfds, fd_set *readfds, fd_set *writefd…

初识SPDK,从SPDK的软件架构到使用实操

相信很多做存储的同学都听说过SPDK,它是Intel开发的一套开源存储栈。SPDK的全称为存储高性能开发包(Storage Performance Development Kit),从名称可以看出SPDK其实就是一个第三方的程序库。但是这个程序库却是非常强大的,下图是SPDK的软件模块图,从该图可以看出,几乎囊…

Linux---用户管理命令(useradd、userdel、usermod、passwd、id)

1. 用户与用户组 Linux系统是一个多用户多任务的分时操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向 系统管理员申请一个账号&#xff0c;然后以这个账号的身份进入系统。 Linux系统中可以&#xff1a; 配置多个用户、配置多个用户组、用户可以…

什么?电路板上还要喷漆?

什么是三防漆&#xff1f; 三防漆是一种特殊配方的涂料&#xff0c;用于保护线路板及其相关设备免受环境的侵蚀。三防漆具有良好的耐高低温性能&#xff1b;其固化后成一层透明保护膜&#xff0c;具有优越的绝缘、防潮、防漏电、防震、防尘、防腐蚀、防老化、耐电晕等性能。 在…

MIT6824——lab4(实现一个分片kv存储)的一些实现,问题,和思考

Part A 分片控制器 1. 整体思路 和lab3A一样&#xff0c;shardctler也是一个服务&#xff0c;由客户端调用。这个服务建立在raft集群上&#xff0c;保证容错。 shardctler也应该保证线性一致性和重复请求的问题&#xff0c;因此也需要记录clientid和messageid。 shardctler保…

BFT 最前线 | 张一鸣成立个人基金;马斯克:AI是双刃剑;阿里首席安全科学家离职;卡内基梅隆究团队:解决农业虫卵问题的机器人

文 | BFT机器人 名人动态 CELEBRITY NEWS 01 字节跳动创始人张一鸣 在香港成立个人投资基金 在卸任CEO两年后&#xff0c;字节跳动创始人张一鸣在香港成立了一家个人投资基金。香港公司注册处网站显示&#xff0c;该基金名为Cool River Venture&#xff0c;性质是私人股份有限…

doris索引

目前 Doris 主要支持两类索引&#xff1a; - 内建的智能索引&#xff1a;包括前缀索引和 ZoneMap 索引。 - 用户创建的二级索引&#xff1a;包括 Bloom Filter 索引 和 Bitmap倒排索引。其中 ZoneMap 索引是在列存格式上&#xff0c;对每一列自动维护的索引信息&#xff0c;包…

Go 语言实战案例:猜谜游戏在线词典SOCKS5代理服务器 Go学习路线

字节跳动后端入门 - Go 语言原理与实践& vscode配置安装Go 3.1猜谜游戏 3.1.2 生成随机数v2 package mainimport ("fmt""math/rand""time" )func main() {maxNum : 100rand.Seed(time.Now().UnixNano())secretNumber : rand.Intn(maxNum)fmt…

OS之页面置换算法

目录 一、最佳置换算法(OPT) 定义 案例 二、先进先出置换算法(FIFO) 定义 案例 FIFO特有的异常 三、最近最久未使用置换算法(LRU) 定义 案例 四、时钟置换算法(CLOCK) 定义 案例 五、改进型的时钟置换算法 定义 案例 一、最佳置换算法(OPT) 定义 每次选择淘汰…

GoWeb -- gin框架的入门和使用(2)

前言 书接上回&#xff0c;在gin的框架使用中&#xff0c;还有着许多方法以及它们的作用&#xff0c;本篇博客将会接着上次的内容继续记录本人在学习gin框架时的思路和笔记。 如果还没有看过上篇博客的可以点此跳转。 map参数 请求url&#xff1a; http://localhost:8080/us…