数据结构之线性表中的栈和队列【详解】

news2025/1/17 6:16:50

文章目录

  • 引言:
  • 栈和队列的讲解
    • (一、)什么是栈
      • 1.栈的概念、结构和图解:
        • (1.)顺序表和链表的对比(严格来说这两个结构是相辅相成的)
        • (2.)栈的概念和结构
        • (3.)栈的图解
      • 2.使用数组的形式实现栈:
    • (二、)什么是队列
      • 1.队列的概念、结构和图解:
        • (1.)队列的概念和结构
        • (2.)队列的图解
      • 2.使用链表的形式实现队列
    • (三、)总结:

引言:

北京时间2022/11/29 凌晨1点06分,今天我们来讲一下什么是栈和队列,写完这个博客,明天复习自我实现一下栈和队列,我们看一下能不能找到有关二叉树的教学视屏,假如找到了,我们就朝二叉树进发,找不到,我们就进行链表这部分的题目练习

栈和队列的讲解

(一、)什么是栈

1.栈的概念、结构和图解:

首先在这边我们学习什么是栈和队列的前提之下,我们先把双循环链表给收尾一下

(1.)顺序表和链表的对比(严格来说这两个结构是相辅相成的)

(1.1)首先是顺序表的优点:1.支持随机访问,需要随机访问结构的算法可以很好的适用 2.CPU告诉缓存的命中率更高
(1.2.)顺序表的缺点: 1.头部中部插入删除时间效率低 时间复杂度O(N) 2.连续的物理空间,空间不够了以后需要增容(增容有一定的消耗)3.按倍数增容,用不完就有一定的空间的浪费
(1.3.)(双向带头循环链表)链表的优点:1.任意位置插入删除效率高 时间复杂度 O(1) 2.按需申请释放空间
(1.4.)链表的缺点:1.不支持随机访问(用下标访问),意为着:一些排序在这种结构上不适用 2.链表存储一个值,同时也要储存链接指针,也有一点的消耗 3.CPU告诉缓存的命中率更低

(2.)栈的概念和结构

(2.1).栈的概念和结构:
栈:一种特殊的线性表,其只允许在固定的一段进行插入和删除元素的操作。进行数据插入和删除的操作的一端称为栈顶,另一端称为栈底,栈中的数据元素遵守后进先出(就像是电梯)的原则
压栈:栈的插入操作叫做进栈/压栈/入栈。入数据在栈顶
出栈:栈的删除操作叫做出栈。出数据也在栈顶(因为要遵守原则:先进后出,后进先出)
这边讲完了我么就来讲一下为什么要用数组来实现我的栈

(2.2)我们可以通过数组的形式来把栈给实现(也可以使用链表,但是链表没有数组好,所以我们就用数组实现就行)

(2.3)使用链表实现栈的方式:
如果用尾做栈顶,尾插尾删,要设计双向链表,否则删除数据效率低
如果用头做栈顶,头插头删,就可以设计成单链表
所以这些都有一些的不好,所以我们使用数组来实现我的栈就是最好的

(3.)栈的图解

在这里插入图片描述

2.使用数组的形式实现栈:

(4.1)具体的功能如下:(包括与栈相关的结构体形式的创建)

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
![在这里插入图片描述](https://img-blog.csdnimg.cn/8b77a23d14294724b263f8b3d5865b26.png#pic_center)

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;//栈顶的意思(位置)
	int capacity;

}ST;

void StackInist(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps,STDataType x);//此时的插入是在栈顶位置(栈的特点:先出后进,先进后出一定要理解透彻了)
void StackPop(ST* ps);
STDataType StackTop(ST* ps);//这个就是表明此时我要取我的栈顶的数据,在top位置(栈顶位置)获取我的数据
int StackSize(ST* ps);//这个是意思就是表示我的栈中有几个数据
bool StackEmpty(ST* ps);//此时这个函数的作用就是判断我的栈是否为空(bool这个返回值的意思就是返回真和假的意思,用int也是一样)

(4.2)具体各种接口函数的实现

(4.2.1)初始化在这里插入图片描述

(4.2.2)销毁

在这里插入图片描述

(4.2.3)放数据
在这里插入图片描述
(4.2.4)删除
在这里插入图片描述

(4.2.5)找top位置
在这里插入图片描述

(4.2.6)计算栈的大小
在这里插入图片描述
(4.2.7)判断栈是否为空
在这里插入图片描述
(4.2.8)遍历栈
在这里插入图片描述

(4.3)以上就是栈的数组实现

(二、)什么是队列

1.队列的概念、结构和图解:

(1.)队列的概念和结构

(1.1).队列的概念及其结构:
队列:只允许在一端进行插入数据的操作,在另一端进行删除数据的操作,队列具有先进先出的原则
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

就是区别于栈就对了(一个是同一端进同一端出,一个是一端进,另一端出)
此时可以明显的看出用数组和单链表都不适合实现我们的队列,所以这边最好就是使用双向循环链表是最合适的
(1.2)假如使用链表的结构:
在进行出数据(在链表的头上进行)的时候就是把一个数据出完之后(然后把它删除掉,然后把phead头指针指向下一个,然后再出另一个数据)
在入数据(在链表的尾部进行)的时候就是找尾,然后一直更新这个尾

(2.)队列的图解

在这里插入图片描述

2.使用链表的形式实现队列

(3.1)具体功能如下:

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

//定义各种结构(不管是顺序表还是链表都是在模仿老师的定义结构的方法),以后在定义结构的时候一定要学会自己去定义结构

//此时这个结构体的定义就很像是单量表实现的定义
typedef int QDataType;
typedef struct QueueNode //这个就是队列中的单链表的结构的定义(用单链表实现队列就一定要有单链表结构的定义)
{
	struct QueueNode* next;
	QDataType data;

}QueueNode;

typedef struct Queue //此时的这个结构体应该才是我(队列)的主结构体,上面那个结构体应该只是一个单量表的结构体(因为此时我是想要用单链表来实现我的队列),上面那个结构体也就是我的单链表中的一个一个的结点而已(就是用上面的这个结构体来设计我的队列中的每一个结点而已,这样就有点像是我的单链表)
{
	QueueNode* head;
	QueueNode* tail;//虽然这个结构体是队列的关键,但是这个结构体中的成员,还是要根据我的需求来定义的
}Queue;

//typedef struct Queue //此时的这个结构体应该才是我(队列)的主结构体,上面那个结构体应该只是一个单量表的结构体(因为此时我是想要用单链表来实现我的队列),上面那个结构体也就是我的单链表中的一个一个的结点而已(就是用上面的这个结构体来设计我的队列中的每一个结点而已,这样就有点像是我的单链表)
//{
//	QueueNode* head;
//	QueueNode* tail;//虽然这个结构体是队列的关键,但是这个结构体中的成员,还是要根据我的需求来定义的
//}Queue;


//并且此时的这个双指针可以不使用结构体定义(但是如果不使用结构体的话,此时的传参就要进行一定的改变了)
//例如:void QueueInit(Queue* pq); 不使用结构体此时就要写成 void QueueInit(QueueNode** pphead,QueueNode** pptail);
//void QueueInit(QueueNode** pphead, QueueNode** pptail);不仅要传二级指针(因为我会改变函数外部的值),而且要进行两个指针参数的传递,可谓是非常的复杂


//此时以上的这些代码的意思(就是创建了两个结构体(也就是两种类型),有了这两种类型,我就可以实现我的队列了),所以这个也就是我的队列的基本的结构体定义

//为什么一定是以下的接口:这个就是性质决定的
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);//这个就是表示取队头数据的意思
QDataType QueueBack(Queue* pq); //这个就是表示取队尾数据的意思
size_t QueueSize(Queue* pq);//这个就是计算队列中的数据个数
bool QueueEmpty(Queue* pq);//这个就是用来判断这个队列是否为空

(3.2)具体函数接口的是实现


#include"Stack.h"

//初始化
void QueueInit(Queue* pq)
{
	//此时为什么传参接收参数的时候不需要二级指针呢?
	//原因就是:我使用了两个结构体类型
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;

}

//销毁(此时销毁的就是一个一个动态内存开辟出来的结点,这个结点就是从另一个结构体的定义之中来的)
void QueueDestory(Queue* pq)
{
	assert(pq);

	QueueNode* cur = pq->head;
	while (cur != NULL)
	{
		QueueNode* next = cur->next;//保存下一个(因为我是一个链表)
		free(cur);
		cur = next;

	}
    
	pq->head = pq->tail = NULL;
}
//插入
void QueuePush(Queue* pq, QDataType x)
{
	//因为此时已经把结构体的地址传过来了,所以此时就可以对结构体进行改变了
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	newnode->data = x;
	newnode->next = NULL;//这个就是结点是初始化
	//此时先进行队列有无数据的判断
	if (pq->head == NULL)//这个表示无
	{
		//这个就是此时的把我的结构体进行改变,但是要注意进行结构体的改变一定要进行结构体地址的传递,然后要用一级指针接收
		pq->head = pq->tail = newnode;//这个就是表示此时的这个队列一个值都还没有,所以此时就可以把这个newnode给我的head和tail
	}
	else//这个就是有(然后有数据,此时因为是一个队列,所以此时我们就需要在这个队列的后面插入一个新的结点,因为队列的特点(一头进一头出)),然后此时就需要在tail这个表示尾的指针后面进行一个尾插,这样就可以完成队列的插入功能
	{
		pq->tail->next = newnode;
		pq->tail = newnode;//上面那个是主要的完成插入的步骤,下面这个没什么主要的功能(其实就只是想让我的tail继续保持在最后一个结点的位置而已)


	}

}
//删除
void QueuePop(Queue* pq)
{
	//因为队列的特性(队尾入,队头出)
	//所以删除数据是已经规定死了的,只能在队头删
	assert(pq);
	//还是那两种写法:1.温柔的写法  2.暴力的写法
	//if (pq->head == NULL)
	//{
	//	return;
	//}
	//assert(pq->head != NULL);
	//并且下面这个函数 QueueEmpty(pq) 为真就是表示此时的pq->head为空,为假就表示此时的pq->head不为空
	assert(!QueueEmpty(pq));//这个在报错(就是说明此时的这个QueueEmpty(pq)函数为真,然后(!QueueEmpty(pq))加了一个感叹号就表示此时整个为假),所以此时这个断言的意思就是判断这个pq->head不为空,为空就报错

	Queue* next = pq->head->next;
	free(pq->head);
	pq->head = NULL;
	pq->head = next;//完成这些基本的操作,我们一定还要注意几个注意点
	//因为此时可能会把整个队列都给删空,所以此时当删到只有一个数据的时候,此时就要进行tail的置空,不然就有野指针问题
	if (pq->head == NULL)//此时这个为空,就是表示已经删完了(但是此时只是把head给处理完了,这边还剩了一个tail指针(此时如果把最后一个数据也删掉,那么此时的tail就会变成一个野指针),所以我们在删除最后一个数据的时候,一定要注意tail的置空,不然就是野指针)
	{
		pq->tail = NULL;
	}

}

//取队头的数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//此时就是表示我的头已经为空了,所以我已经不能再去取头数据了

	return pq->head->data;//这个就是表示队列头的数据

}
//取队尾的数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//判断队列不为空

	return pq->tail->data;//此时为了寻找我队列的尾(也就是入口),并且此时的tail就是指向我队列的尾数据,所以想要找尾中的数据,只需要把tail->data给返回去就行了
    
}

//计算队列中的数据个数
size_t QueueSize(Queue* pq)
{
	assert(pq);

	int n = 0;
	QueueNode* cur = pq->head;
	while (cur != NULL)//想要计算数据个数,自然要遍历我的队列
	{
		n++;
		cur = cur->next;//有了这步上面那步就不需要写成cur->next != NULL;了
	}

	return n;
}

//判断队列是否为空(在删除的时候会用到,因为删除的时候队列是不可以为空的,所以要进行一定的判断)
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;

}

(3.3)以上就是队列的链表基本实现

(三、)总结:

                            就是要多写,多练,多画图,睡觉啦!

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

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

相关文章

[MyBatis]一级缓存/二级缓存/三方缓存

缓存是一种临时存储少量数据至内存或者是磁盘的一种技术.减少数据的加载次数,可以降低工作量,提高程序响应速度 缓存的重要性是不言而喻的。mybatis的缓存将相同查询条件的SQL语句执行一遍后所得到的结果存在内存或者某种缓存介质当中&#xff0c;当下次遇到一模一样的查询SQL时…

[附源码]Python计算机毕业设计Djangossm新能源电动汽车充电桩服务APP

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

UICollectionView

文章目录前言基础概念UICollectionView与相关对象关系注意事项强大的控件相关的类重新使用视图提高性能例子一些方法前言 本篇&#xff1a;进行UICollectionView的学习 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 基础 概念 UICollectionView是我…

【毕业设计】31-基于单片机的农业蔬菜大棚温度自动控制系统设计(原理图工程+源码工程+仿真工程+答辩论文+答辩PPT)

typora-root-url: ./ 【毕业设计】31-基于单片机的农业蔬菜大棚温度自动控制系统设计&#xff08;原理图工程源码工程仿真工程答辩论文答辩PPT&#xff09; 文章目录typora-root-url: ./【毕业设计】31-基于单片机的农业蔬菜大棚温度自动控制系统设计&#xff08;原理图工程源…

NetCore OpenIdConnect验证为什么要设置Authority?

在使用Identity Server作Identity Provider的时候&#xff0c;我们在NetCore的ConfigureServices((IServiceCollection services))方法中,常需要指定options的Authority&#xff0c;如下代码所示&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 …

基于PHP+MYSQL酒店管理系统的设计与开发

随着人们生活条件的提高,旅游和出差已经成了家常便饭,但是因为他向异地所以第一个要解决的问题就是吃住问题,吃先对是比较好解决的一个问题,随便一个超市或者饭店甚至地摊就能解决这一问题,但是总不能露宿街头吧,所以很多人在到达目的地的第一时间就是去寻找一个能够入住的酒店…

一文彻底搞懂Mysql索引优化

专属小彩蛋&#xff1a;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff08;前言 - 床长人工智能教程&#xff09; 目录 一、索引介绍 二、性能分析 三、查询优化 一、索引介绍…

Python使用矩阵分解法推荐系统找到类似的音乐

这篇文章是如何使用几种不同的矩阵分解算法计算相关艺术家。最近我们被客户要求撰写关于的矩阵分解法推荐系统研究报告&#xff0c;包括一些图形和统计输出。代码用Python编写&#xff0c;以交互方式可视化结果。 加载数据 这可以使用Pandas加载到稀疏矩阵中&#xff1a; # r…

Jmeter 使用BeanShell断言,实现自动获取文章列表,并判断文章是否为当天发布的

系列文章目录 提示&#xff1a;阅读本章之前&#xff0c;请先阅读目录 文章目录系列文章目录前言一、正则表达式提取器&#xff0c;提取所有文章id二、循环控制器&#xff0c;读取每篇文章的创建时间三、JSON提取器&#xff0c;提取创建时间&#xff0c;然后进行判断四、运行结…

Java设计模式七大原则-里氏替换原则

里氏替换原则 OO中的继承性的思考和说明 继承包含这样一层含义&#xff1a;父类中凡是已经实现好的方法&#xff0c;实际上是在设定规范和契约&#xff0c;虽然它不强制要求所有的子类必须遵循这些契约&#xff0c;但是如果子类对这些已经实现的方法任意修改&#xff0c;就会对…

1540_AURIX_TriCore内核架构_FPU

全部学习汇总&#xff1a; GreyZhang/g_tricore_architecture: some learning note about tricore architecture. (github.com) 这一次看一下TriCore的FPU&#xff0c;浮点处理单元。说起来&#xff0c;这算是很多MCU中的一个高级模块了。 1. 在TriCore中FPU其实是可选实现的&a…

动画演示选择排序(Selection Sort)

1、排序规则 1.1 一句话总结选择排序 从数组中第一个数字开始&#xff0c;数组中每个数字都要和后面所有数字比一次大小&#xff0c;每每次循环遍历当前最小值&#xff0c;放在当前循环范围内的最小位置。当完成第 N - 1 次循环之后&#xff0c;排序完成。N 数组长度 - 1。 …

113.(leaflet篇)leaflet根据距离截取线段

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

不同类型的 SSL 证书解释

了解不同类型的 SSL 证书&#xff1a;扩展验证 (EV)、组织验证 (OV) 和域名验证 (DV)。 查看用例及更多。 SSL/TLS 证书用于验证网站的身份并在服务器和浏览器之间创建安全连接。有许多不同类型的 SSL 证书选项可用&#xff0c;它们都有其独特的用例和价值主张。证书颁发机构 …

【项目实战合集】计算机视觉毕业设计项目怎么选,超30个经典案例供你选择...

每年到了搞毕业设计的时候&#xff0c;很多学生朋友都很头疼&#xff0c;指导老师给不了好题目&#xff0c;自己也没有什么好的想法&#xff0c;怕选的太容易了过不了&#xff0c;怕选的太难了做不出&#xff01;今年我们在计算机视觉方向出了【超过30个基于Pytorch框架】的实战…

FineReport 动态图表表格软件-函数计算组成和语法

1. 概述 1.1 版本 1.2 功能简介 在设计模板时用户需要频繁的使用公式函数&#xff0c;例如&#xff1a;求和、求个数、做判断等等。 本文介绍函数的计算组成和语法。 2. 计算语法 2.1 概览 组成部分 语法 示例 函数 函数语法详情查看对应函数&#xff1a; SUM(合同金额…

AGI意识科学每周速递 | 2022年11月第四期

AGI&意识科学每周速递 | 2022年11月第四期 心识研究院 Mindverse Research 2022-11-28 17:00 发表于上海 收录于合集#AGI&意识科学每周速递24个 本周主要内容&#xff1a;程序辅助语言模型 PAL、AI 外交官 CICERO、视觉语言图灵测试、NLP 的持续学习、胎儿的大脑皮层…

winograd卷积实践

winograd卷积基本原理参考 Winograd算法实现卷积原理_Luchang-Li的博客-CSDN博客_optimizing batched winograd convolution on gpus winograd卷积图示&#xff1a; 注意这张图里面隐藏了input和output channel。实际上每个空间维度里面还包含了batch和in/out channel维度。 …

从pom文件里面找不到对应的Maven依赖,通过下面的方法完美解决

如下&#xff0c;我想获取gson对应的依赖 第一步&#xff1a;进入引入对应包的类里面 第二步&#xff1a;进入包&#xff1a;Ctrl 左键 ctrl左键点击gson后&#xff0c;会自动跳转到这个文件夹 第三步&#xff1a;打开依赖图 按箭头点击后&#xff0c;会出现下面的依赖图 …