(详解)数据结构-----------栈与队列 c语言实现

news2025/1/12 13:22:47

本章将会详细讲解以下知识点:

目录

一:栈

        1:栈的定义,栈的特点

        2:用什么结构来实现栈与原因的分析?

        3:  (超详解)栈的常用接口并且附上测试用例

二:队列

        1:队列的定义,队列的特点

        2:用什么结构来实现队列与原因的分析?

        3:(超详解)队列的接口并且附上测试用例


正文开始~:-^-

        1:栈的定义及其特点

        首先栈是一种特殊线性表,其只能在一端进行插入删除的操作,进行数据删除插入的一端我们称它为栈顶,另外一端为栈底。栈中的数据元素遵循后进先出的原则。

        下面是栈的操作的叫法:

                压栈:表示数据元素插入到栈中,又称为进栈/入栈。入数据在栈顶

                出栈:表示栈中数据元素的删除的操作。出数据在栈顶

                栈的特点只能在栈一端进行数据元素的插入与删除,数据元素遵循后进先出的原则。

        2:用什么结构来实现栈与原因?

        经过前面我们对比顺序表与链表的优缺点,在实现栈的时候我们首选使用顺序表来进行实现栈这种数据结构。当然也可以使用链表来实现。

        原因:我们根据栈具有先进后出的特点,它只能在栈顶进行数据元素的插入与删除。

        而我们的顺序表就非常适合它,顺序表的尾插与尾删的时间复杂度都是O(1),且顺序表的缓存利用率比链表快,所以顺序表的结构更加优于链表。

         3:(详解)栈的常用接口

        栈的常用操作有:栈的初始化,栈的销毁,栈的入栈,栈的出栈,获取栈顶元素的值

        栈是否为空的判读,栈的大小

        在讲解这些接口之前,我们先来讲解栈的定义

        包含三个成员变量,S是我们开辟空间的首地址,top是用来记录元素有多少个,同时是数组中元素最后一个元素的下一个位置,capacity是用来表示数组空间容量的大小。

        代码如下:

//动态的顺序栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* S;
	int top;//用来表示栈中最后一个元素的下一个元素的位置
	int capacity;
}ST;

        1:栈的初始化:将三个成员变量的值全部弄为空或者是0

        代码如下:

void InitStack(ST* ps)
{
	assert(ps);
	ps->capacity = 0;
	ps->top = 0;
	ps->S = NULL;
}

        2:栈的入栈操作:每次入栈之前我们先要判断空间是否足够,然后在直接使用顺序表随机访问的特点对栈进行尾插,每次入栈完成我们都需要将top++。

        在下面代码中有一个非常精髓的地方,由于我们初始化的时候并没有给数组开辟空间之类的,所以我们巧用了三目运算符来对我们新开辟的空间的大小进行分配,如果是第一次我们就将空间的大小搞成4,之后的过程就是一次扩2倍的空间。

         代码如下:

        

void PushStack(ST* ps, STDataType x)
{
	assert(ps);
	//先检查空间够不够
	if (ps->top == ps->capacity)
	{
		int newcapcity = (ps->capacity == 0 ? 4 : 2 * ps->capacity);
		STDataType* tmp = (STDataType*)realloc(ps->S, sizeof(STDataType) * newcapcity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->S = tmp;
		ps->capacity = newcapcity;
	}
	ps->S[ps->top] = x;
	++ps->top;
}

         3:栈的出栈操作:这里我们需要用暴力的解法先判断栈是否为空,如果为空就给我们报错,不为空才能进行出栈的操作,然后我们的就top--。

        代码如下:

void PopStack(ST* ps)
{
	assert(ps);
	//非空
	assert(ps->top > 0);

	ps->top--;

}

        4:栈的获取栈顶元素的操作:首先还是得判断栈是否为空,为空就这个接口就会报错。

        在这个代码中我们要注意的是我们栈顶元素得下标并不是top而是top-1

        代码如下:

        

STDataType GetStack(ST*ps)
{
	assert(ps);
	//非空
	assert(ps->top > 0);

	return ps->S[ps->top-1];
}

        5:栈的大小:我们直接返回top就行。

        代码如下:

        

int SizeStack(ST* ps)
{
	assert(ps);
	
	return ps->top;
}

        6:判断栈是否为空:这里如果top为0,则说明栈中无元素返回true,如果有元素返回true,这里要注意c语言中并没有这两个关键字,我们需要引入头文件<stdbool.h>

        代码如下:

        

bool EmptyStack(ST* ps)
{
	assert(ps);
	  //这里代码的意思是如果top为0,则返回真,不为0返回false
    return ps->top == 0;
  
}

        为了体现栈的特点所以我们在打印出栈中数据的时候,并不是直接遍历数组的

        我们使用的是--->

        

while (!EmptyStack(&st))
	{
		printf("%d ", GetStack(&st));
		PopStack(&st);
	}

        这里是每次取栈顶的元素,然后在删除栈顶的元素。这样才能体现出栈的特点。

        

        图解:

         

        栈的详解就到这里结束了。



    1:队列的定义与特点

        队列的定义:队列也是一种特殊的线性表,它只能在一端进行插入操作,在另外一端进行删除的操作,我们将插入一端称为队尾,将删除的一端我们称之为队头,这种结构在我们生活中非常常见,我们可以想象一下我们在食堂排队的情况,如果有人来了那么它就会往队尾进行插入,打完饭的人则会从队头出队。

        出队:将队头的元素进行删除的操作

        入队:在队尾插入数据的操作。

        队列特点:只能在队头进行删除的操作,在队尾进行插入的操作,遵循先进先出原则

        图:

        

         2:用啥结构来实现队列与原因?

        先公布答案:相对与顺序表来说我们一般使用链表来实现队列这一数据结构。

        原因:链表在进行头删的时候更加方便,在进行尾插的时候我们只需要定义一个尾指针就可以节省时间了。

        3:(详解)队列的常用接口

        队列的接口包括:初始化队列,销毁队列,入队操作,出队操作,判断队列是否为空

获取队头元素,获取队尾元素,判断队列的大小。 

       首先我们先来了解队列的结构代码分析:

typedef int QueueDataType;

typedef struct QNode
{
	struct QNode* next;
	QueueDataType data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;//用来记录有多少个结点,可以直接算大小
}Que;

     首先队列的每个元素都是一个链表的基本结构,我们只需要记录链表的头节点与尾结点我们就可以将队列给表示出来,而又因为我们一般传参的时候只传地址所以我们就将头指针与尾指针定义到一个结构体当中,并且增加一个成员变量size,用来记录队列元素的个数。

            将两个指针放到同一个结构体中,有一个好处就是我们只需要通过结构体指针就可以改变我们的指针的值,不然就需要使用二级指针来修改它们的值。

 

        1:队列的初始化:如果你和我的初始化不同那么可能会导致后面一些接口中步骤有些差异。初始化并不唯一

        代码如下:

            

void QueueInit(Que* pq)
{
	assert(pq);
	pq->head = pq->tail = 0;
	pq->size = 0;
}

        2:入队操作:这里需要分为两种情况,第一张是插入第一个元素的时候我们需要将两个指针的指向给改变,另外一种情况我们只需要在尾指针的后面插入一个新的结点即可,与此同时我们将尾指针指向新结点,插入之后我们的size++;

        代码如下:

        

void QueuePush(Que* pq, QueueDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail:");
		exit(-1);
	}
	
	//为第一个结点的时候
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	//不是第一个结点
	else
	{
		pq->tail->next = newnode;
	}
	pq->tail = newnode;
	newnode->data = x;
	newnode->next = NULL;
	pq->size++;
}

        3:出队操作:首先得判断队列中是否含有数据元素,且有两类,一类是队列中只有一个元素,那么我们需要将这个元素删除,并且将head与tail的值置为空,在size--;

        代码如下:

void QueuePop(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//只有一个结点的时候
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* phead = pq->head->next;
		free(pq->head);
		pq->head = phead;
	}
	pq->size--;

       4:获取队头元素:这个非常简单,先判断队列是否为空,不为空我们只需要返回head->data,就行。

        代码如下:

QueueDataType QueueFront(Que* pq)
{
	assert(pq);//判断pq是否有意义
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

        5:获取队尾元素:与上面的思路类似,只需要返回tail指针所指向的数据元素就行

        

QueueDataType QueueBack(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

        6:判断队列是否为空:为空那么head指针就为空,head为NULL则返回true,否则返回false

        代码如下:

               

bool QueueEmpty(Que* pq)
{
	assert(pq);
	return pq->head==NULL;
}

        7:队列的元素个数判断:我们根据我们所定义的结构可在,我们只需要返回size即可。

        

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

        在某种情况下可以使用栈转化为队列,也可以使用队列转化为栈,让我们一起想一想它是如何转化的吧。


 

 !!!本章完,感谢大家的耐心观看!

        

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

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

相关文章

QT6为工程添加资源文件

如果在同一个文件夹 如果不在同一个文件夹 然后浏览资源位置&#xff0c;找到文件就可以了

【超简单】远程服务器使用 plt.show() 和 cv2.imshow() 可视化图像

远程服务器可视化图像 我的配置MobaXterm 远程显示VSCode 远程显示 我的配置 服务器 Ubuntu 20.04.3 LTSAnaconda 本地电脑 Win11MobaXtermVSCode MobaXterm 远程显示 配置好服务器连接&#xff08;此处略&#xff09;&#xff1b; 连接服务器&#xff0c;并激活使用的 A…

对接webservice接口时报错:发送方和接收方 Action 不匹配

趁着早上有时间&#xff0c;赶紧记录一下&#xff0c;哈哈。 错误提示如下&#xff1a; 1、英文版&#xff1a; <s:Envelope xmlns:s“http://schemas.xmlsoap.org/soap/envelope/”><s:Body><s:Fault>a:ActionNotSupportedThe message with Action ‘’ ca…

自然语言处理(四):全局向量的词嵌入(GloVe)

全局向量的词嵌入&#xff08;GloVe&#xff09; 全局向量的词嵌入&#xff08;Global Vectors for Word Representation&#xff09;&#xff0c;通常简称为GloVe&#xff0c;是一种用于将词语映射到连续向量空间的词嵌入方法。它旨在捕捉词语之间的语义关系和语法关系&#…

【USRP】集成化仪器系列2 :示波器,基于labview实现

USRP 示波器 1、设备IP地址&#xff1a;默认为192.168.10.2&#xff0c;请勿 修改&#xff0c;运行阶段无法修改。 2、中心频率&#xff1a;当需要生成不同频率单载波的 时候请直接修改中心频率&#xff0c;在运行的时候您 也可以直接修改中心频率。 3、接收增益&#xff1a;…

线程安全-搞清synchronized的真面目

多线程编程中&#xff0c;最难的地方&#xff0c;也是最重要的一个地方&#xff0c;还是一个最容易出错的地方&#xff0c;更是一个特别爱考的地方&#xff0c;就是线程安全问题。 万恶之源&#xff0c;罪魁祸首&#xff0c;多线程的抢占式执行,带来的随机性. 如果没有多线程,此…

paddle.load与pandas.read_pickle的速度对比(分别在有gpu 何无gpu 对比)

有GPU 平台 测试通用代码 import time import paddle import pandas as pd# 测试paddle.load start_time time.time() paddle_data paddle.load(long_attention_model) end_time time.time() print(f"Paddle load time: {end_time - start_time} seconds")# 测试…

【USRP】调制解调系列4:BPSK、QPSK、8PSK、OQPSK、Pi/4DQPSK,基于labview的实现

PSK Phase Shift Keying – 相移键控 在某些调制解调器中用于数据传输的调制系统&#xff0c;在最简单的方式中&#xff0c;二进制调制信号产生0和1。载波相位来表示信号占和空或者二进制1和O。对于有线线路上较高的数据传输速率&#xff0c;可能发生4个或8个不同的相移&…

系统架构:软件工程

文章目录 资源知识点自顶向下与自底向上形式化方法结构化方法敏捷方法净室软件工程面向服务的方法面向对象的方法快速应用开发螺旋模型软件过程和活动开放式源码开发方法功用驱动开发方法统一过程模型RUP基于构件的软件开发UML 资源 信息系统开发方法 知识点 自顶向下与自底…

uniapp 配置网络请求并使用请求轮播图

由于平台的限制&#xff0c;小程序项目中不支持 axios&#xff0c;而且原生的 wx.request() API 功能较为简单&#xff0c;不支持拦截器等全局定制的功能。因此&#xff0c;建议在 uni-app 项目中使用 escook/request-miniprogram 第三方包发起网络数据请求。 官方文档&#xf…

7. 搭建网络

7.1 神经网络 ① 把网络结构放在Sequential里面&#xff0c;好处就是代码写起来比较简介、易懂。 ② 可以根据神经网络每层的尺寸&#xff0c;根据下图的公式计算出神经网络中的参数。 7.2 搭建神经网络 import torch import torchvision from torch import nn from torch.…

【Day-22慢就是快】代码随想录-二叉树-理论基础

二叉树的种类 满二叉树 如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 深度为K&#xff0c;有2^k-1个节点。 完全二叉树 在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余…

提升代码可读性与可维护性:利用责任链模式优化你的Spring Boot代码

1. 基本介绍 责任链是一种非常常见的设计模式, 具体我就不介绍了, 本文是讲解如何在SpringBoot中优雅的使用责任链模式 1.1. 代码执行流程 基本步骤如下 : SpringBoot启动时, 需要获取 handler 对应Bean, 不同业务对应着不同的多个处理器, 比如 购票业务, 可能需要检查参数是…

LeetCode第21~25题解

CONTENTS LeetCode 21. 合并两个有序链表&#xff08;简单&#xff09;LeetCode 22. 括号生成&#xff08;中等&#xff09; LeetCode 21. 合并两个有序链表&#xff08;简单&#xff09; 【题目描述】 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两…

使用awvs进行web安全扫描

1、安装 docker pull secfa/docker-awvs docker run -it -d -name awvs -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs2、账号密码 # https://ip:13443/ # 用户名:adminadmin.com # 密码:Admin1233、使用 ps:需要征得甲方的同意

重磅!OpenAI突然发布企业版ChatGPT:没有限制、更快、更强、更安全的GPT-4

这是由【小瑶智能体】 AI创作的第 4 篇科技文章 大模型研究测试传送门 GPT-4传送门&#xff08;免墙&#xff0c;可直接测试&#xff0c;遇浏览器警告点高级/继续访问即可&#xff09;&#xff1a;Hello, GPT4! 大家好&#xff0c;我是小瑶智能体&#xff0c;一个喜欢分享人…

VR全景展示:打造三维、立体化的VR房产参观方式

我们从近期的新闻中可以了解到&#xff0c;房地产行业正在经历挑战和压力&#xff0c;因为房地产销售市场的持续低迷&#xff0c;导致很多公司出现了债务危机。线下销售模式效果不佳&#xff0c;很多房企开始转战线上销售&#xff0c;VR全景展示方案为房地产销售带来了全新的体…

open cv快速入门系列---数字图像基础

目录 一、数字图像基础 1.1 数字图像和图像单位 1.2 区分图片分辨率与屏幕分辨率 1.3 图像的灰度与灰度级 1.4 图像的深度 1.5 二值图像、灰度图像与彩色图像 1.6 通道数 二、数字图像处理 2.1 图像噪声及其消除 2.2 数字图像处理技术 2.2.1 图像变换 2.2.2 图像增强…

汇编代码:在代码段中使用栈完成数据的倒序

前言 在代码段中使用栈完成数据的倒序 1、代码如下 assume cs:codesgcodesg segmentdw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987Hdw 0,0,0,0,0,0,0,0 ;用dw定义8个字型数据&#xff0c;在程序加载后&#xff0c;将取得8个字的;内存空间&#xff0c;存放这8个数据。我…

Linux通信--构建进程通信的 方案之管道(下)|使用匿名管道实现功能解耦|命名管道实现serveclient通信

文章目录 一、管道的应用实例-父进程唤醒子进程&#xff0c;子进程执行某种任务 二、命名管道 1.创建一个命名管道 2.匿名管道与命名管道的区别 3.命名管道的打开规则 4.用命名管道实现server&client通信 一、匿名管道的应用实例-父进程唤醒子进程&#xff0c;子进程执…