数据结构·栈和队列

1. 栈

1.1 栈的概念及结构

        栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一段称为栈顶,另一端称为栈底。

        栈中的数据元素遵守 后进先出 LIFO(Last In First Out)的原则,后进来的数据先出去

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

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

                        

1.2  栈的实现

        我们先分析一下,栈我们应该用顺序表还是链表实现?

        栈中的数据的插入和删除都是在栈顶实现的,不需要随机位置的插入删除,此时链表的优势就不大了。相比之下顺序表还有内存命中率高的优点,所以最后我们选择用顺序表实现栈

                                

        头文件,源文件,测试文件这里我就不展示了,直接写栈中功能的实现

1.2.1 栈的初始化和销毁

        这个没什么好说的,直接上代码

1.2.2 判断栈是否真是空

        

1.2.3 压栈

        压栈涉及到扩容,如果栈内空间不够就要扩容,栈满的判断标准就是 栈顶top 和 总空间大小capacity 数值相同,然后压完之后不要忘记top++

1.2.4 出栈

        这个很简单,让栈顶回退一个位置就行了

                ​​​​​​​        

1.2.5 取栈顶元素

        这里要注意我们在取栈顶的时候要取 top-1 因为我们在初始化的时候设定的top为0,实际上此时的栈顶指向了一个空的位置,之后我们每压一次栈top都会向后移动一下,指向下一个空的位置,所以我们实际取栈顶的时候要 top-1 或者说,top是指向了栈顶的下一个元素

        ​​​​​​​​​​​​​​        ​​​​​​​        

1.2.6 返回栈的大小(多少有效数据)

        这没啥好说的,上代码

        

1.3 栈的完整代码

        Stack.h

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

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;	//栈顶
	int capacity;
}ST;


void STInit(ST* ps);
void STDestory(ST* ps);

//压栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);

//取栈顶元素
STDataType STTop(ST* ps);

int STSize(ST* ps);

bool STEmpty(ST* ps);

        Stack.c

#include"Stack.h"

//初始化
void STInit(ST* ps)
{
	assert(ps);

	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

//销毁
void STDestory(ST* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}




//压栈
void STPush(ST* ps, STDataType x)
{
	assert(ps);

	//满了,要扩容
	if (ps->top == ps->capacity)
	{
		//防止是第一次扩容,所以要判断一下
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;	
		STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		ps->a = tmp;
		ps->capacity = newcapacity;
	}

	ps->a[ps->top] = x;
	ps->top++;
}



//出栈
void STPop(ST* ps)
{
	assert(ps);
	//栈不为空才能删
	assert(!STEmpty(ps));

	ps->top--;
}


//取栈顶元素
STDataType STTop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));

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


//返回栈的大小(多少有效数据)
int STSize(ST* ps)
{
	assert(ps);

	return ps->top;
}


//判断栈是否真是空
bool STEmpty(ST* ps)
{
	assert(ps);

	//如果真是空就返回真
	//如果不是空就返回假
	return ps->top == 0;
}

2. 队列

2.1 队列的概念及结构

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

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

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

        ​​​​​​​        ​​​​​​​        

2.2 队列的实现

        首先我们分析一下应该怎么实现队列

        如果用顺序表的话,数据从前向后依次存放,此操作还比较方便,但是到删除时,从前向后删除,每删一个元素,就要将后面的元素向前挪一位,时间复杂度O(N)

        所以推荐使用链表解决问题,尾插数据,头删数据,就像上面画的那个类似管道的示意图一样,此时问题又来了,尾插数据需要遍历链表,这样时间复杂度的问题依旧没解决,怎么办?

        解决方案就是我们再引入一个新的结构体,专门用来维护这条链表,新的结构体中存放这个链表的头节点和尾节点的地址,还有这个链表的长度,这样只需将新节点尾插到ptail后面就好了,不用遍历链表了。

        创建链表节点:

        ​​​​​​​        ​​​​​​​        

        创建新结构体用于维护链表:

                        

        之后在主函数中用也这个新结构体来代替链表头节点进行传参,或者说这个新结构体代替的整条链表,而链表头节点只是一个节点,孰强孰弱,一目了然。

2.2.1 队列的初始化和销毁

        因为我们创建的链表是不带头的,所以初始化都是无脑置空就好

        

        

2.2.2 队列中剩余元素

        ​​​​​​​        ​​​​​​​ 

2.2.3 入队列

2.2.4 出队列

        出队列要注意链表的三种状态,分别是链表尾空、链表只有一个节点、链表有多个节点。为空的情况用断言处理,多个节点就正常删除头节点就行,但是要注意只有一个节点的时候要处理ptail 否则它将变成野指针

        ​​​​​​​        

2.2.5 取队头、取队尾

        ​​​​​​​        

        ​​​​​​​        

2.2.6 判断队列为空

                

2.3 队列的完整代码

        Queue.h

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

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


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

//初始化
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);

//判断队列为空
bool QueueEmpty(Queue* pq);

//队列中剩余元素
int QueueSize(Queue* pq);

        Queue.c

#include"Queue.h"

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

//销毁
void QueueDestory(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	QNode* next = NULL;
	while (cur)
	{
		next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode=(QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	
	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	//保正头部有的删
	assert(pq->phead);

	QNode* next = pq->phead->next;
	//当只有一个节点时,要处理ptail
	if (pq->phead == pq->ptail)
	{
		pq->ptail = NULL;
	}
	free(pq->phead);
	pq->phead = next;
	pq->size--;
}

//取队头
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//取队头前提是有队头
	assert(pq->phead);

	return pq->phead->val;
}

//取队尾
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	//取队尾前提是有队尾
	assert(pq->ptail);

	return pq->ptail->val;
}

//判断队列为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

//队列中剩余元素
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[linux]进程信号(信号的概念,信号的产生方式,信号的相关接口、指令,函数,信号怎么保存(原理),信号怎么处理)

目录 一、信号的概念 二、信号的产生方式 通过键盘发送信号 通过系统调用&#xff0c;指令 异常 软件条件 三、信号怎么保存&#xff08;原理&#xff09; 信号其他相关常见概念 在内核中表示 sigset_t 四、信号的相关接口、指令&#xff0c;函数 signal sigpr…

数据结构--树的遍历

数据结构--树的遍历 1. 前序中序后序遍历2. 前序中序后序遍历代码 1. 前序中序后序遍历 2. 前序中序后序遍历代码 /** public class TreeNode {int val 0;TreeNode left null;TreeNode right null;public TreeNode(int val) {this.val val;}} */// 前序遍历顺序&#xff1…

SQL函数学习记录

聚合函数 函数是编程语言的基础之一&#xff0c;在对数字的运算中&#xff0c;我们用的最多的就是聚合函数&#xff0c;本篇接下来就详细阐述下SQL中聚合函数的运用。 什么是聚合函数&#xff08;aggregate function&#xff09;&#xff1f; 聚合函数指的是对一组值执行计算…

MYSQL05高级_查看修改存储引擎、InnoDB和MyISAM对比、其他存储引擎介绍

文章目录 ①. 查看、修改存储引擎②. InnoDB和MyISAM对比③. Archive引擎 - 归档④. Blackhole引擎丢数据⑤. CSV - 引擎⑥. Memory引擎 - 内存表⑦. Federated引擎 - 访问远程表⑧. Merge引擎 - 管理多个MyISAM⑨. NDB引擎 - 集群专用 ①. 查看、修改存储引擎 ①. 查看mysql提…

C++ 之LeetCode刷题记录(三十六)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 目标&#xff1a;执行用时击败90%以上使用 C 的用户。 16. 最接近的三数之和 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你…

备战蓝桥杯Day20 - 堆排序的实现

一、每日一题 蓝桥杯真题之互质数的个数 我的解法&#xff1a; 两个数互质&#xff0c;说明两个数的最大公约数是1&#xff0c;定义了一个函数判断两个数的最大公约数&#xff0c;再在循环中判断&#xff0c;并实现计数。可以实现运行&#xff0c;缺点是时间复杂度很高&#…

【Leetcode每日一题】二分查找 - 寻找旋转排序数组中的最小值(难度⭐⭐)(22)

1. 题目解析 Leetcode链接&#xff1a;153. 寻找旋转排序数组中的最小值 这个题目乍一看很长很复杂&#xff0c;又是旋转数组又是最小值的 但是仔细想想&#xff0c;结合题目给的示例&#xff0c;不难看出可以用二分的方法来解决 核心在于找到给定数组里面的最小值 2. 算法原…

预训练大模型LLM的PEFT之—— Prefix Tuning

简介 Prefix Tuning是2021.01提出来的&#xff0c;在它之前&#xff0c;我们使用prompt主要是人工设计模板或者自动化搜索模板&#xff0c;也就是prompt范式的第一阶段&#xff0c;就是在输入上加上prompt文本&#xff0c;再对输出进行映射。这种离散模板对模型的鲁棒性很差。…

如何在Window系统部署BUG管理软件并结合内网穿透实现远程管理本地BUG

文章目录 前言1. 本地安装配置BUG管理系统2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射本地服务3. 测试公网远程访问4. 配置固定二级子域名4.1 保留一个二级子域名5.1 配置二级子域名6. 使用固定二级子域名远程 前言 BUG管理软件,作为软件测试工程师的必备工具之一。在…

MYSQL学习笔记:索引

MYSQL学习笔记&#xff1a;索引 文章目录 MYSQL学习笔记&#xff1a;索引索引的分类索引的创建删除索引优化B树索引B树InnoDB主键和二级索引树聚集索引与非聚集索引哈希索引INNODB的自适应哈希索引索引和慢查询 用索引也是要涉及磁盘I/O的操作的索引也是一种数据结构&#xff0…

c语言day4 运算符 表达式 三大控制结构

1&#xff1a; 2&#xff1a; 输入一个年月日 计算这是这一年的第几天 17 int year,month,day;18 printf("请输入年份 月份 日期");19 scanf("%d %d %d",&year,&month,&day);20 int feb28;21 if(year%40&&year%1…

哈工大中文mistral介绍(Chinese-Mixtral-8x7B)

Chinese-Mixtral-8x7B基于Mistral发布的模型Mixtral-8x7B进行了中文扩词表增量预训练。扩充后的词表显著提高了模型对中文的编解码效率&#xff0c;并通过大规模开源语料对扩词表模型进行增量预训练&#xff0c;使模型具备了强大的中文生成和理解能力。 开源地址见https://gith…

前端-DOM树

dom树描述网页元素关系的一个专有名词&#xff0c;如html内包含了head、body&#xff0c;而head内包含meta、title、script等&#xff0c;body内包含div等元素&#xff1b;网页所有内容都在document里面&#xff0c;网页内容以树状形式排列&#xff0c;所以称之为dom树 dom树内…

centos服务配置springboot服务开机启动

在做后端服务运维时&#xff0c;经常遇到服务器重启时&#xff0c;需要移动一堆后端服务。服务器故障自动重启时&#xff0c;通常无人通知。把springboot服务的jar包配置开机启动太有必要了&#xff0c;虽然不是很复杂&#xff0c;这里记录一下太有必要了。 创建jar包启动和停…

看完这篇,终于理解如何制作产品使用说明书啦!

制作产品说明书是一项重要的任务&#xff0c;它不仅提供了产品的详细信息&#xff0c;还可以帮助用户正确地使用和维护产品&#xff0c;确保产品说明书的质量和可使用性。在制作产品说明书时&#xff0c;掌握注意事项&#xff0c;可以帮助你更加高效地制作产品说明书。以下是Lo…

lv20 QT主窗口

熟悉创建主窗口项目 1 QAction 2 主窗口 菜单栏&#xff1a;fileMenu menuBar()->addMenu(tr("&File")); 工具栏&#xff1a;fileToolBar addToolBar(tr("File")); 浮动窗&#xff1a;QDockWidget *dockWidget new QDockWidget(tr("Dock W…

基于Python3的数据结构与算法 - 06 topk问题

一、引入 问题&#xff1a;目前共有n个数&#xff0c;设计算法得到前k大的数。&#xff08;m<n&#xff09; 解决思路&#xff1a; 排序后切片&#xff1a;O(n*lognm) O(n*logn)排序LowB三人组&#xff1a;O(mn) 例如冒泡排序&#xff0c;交换m次&#xff0c;即可取前m…

uniapp 安装安卓、IOS模拟器并调试

一、安装Android模拟器并调试 1.下载并安装Android Studio。 2.创建简单project。 3.安装模拟器。 完成安卓模拟器的安装。 4.启动模拟器。 5.hbuilderx选择模拟器、运行。 点击刷新按钮后出现模拟器&#xff0c;勾选并运行。 6.调试。 在 HBuilderX 中&#xff0c;项目启…

如何将字体添加到 ONLYOFFICE 桌面编辑器8.0

作者&#xff1a;VincentYoung 为你写好的文字挑选一款好看的字体然而自带的字体列表却找不到你喜欢的怎么办&#xff1f;这只需要自己手动安装一款字体即可。这里教你在不同的桌面操作系统里的多种字体安装方法。 ONLYOFFICE 桌面编辑器 ONLYOFFICE 桌面编辑器是一款免费的办…

Java设计模式 | 七大原则之合成复用原则

基本介绍 合成复用原则&#xff08;Composite Reuse Principle&#xff09;尽量使用合成/聚合的方式&#xff0c;而不是使用继承 设计原则核心思想总结 找出应用中可能需要变化之处&#xff0c;把他们独立出来&#xff0c;不要和那些不需要变化的代码混在一起针对接口编程&…