【数据结构】-- 栈和队列

在这里插入图片描述

🐇

🔥博客主页: 云曦
📋系列专栏:数据结构

💨吾生也有涯,而知也无涯
💛 感谢大家👍点赞 😋关注📝评论

文章目录

  • 前言
  • 一、栈
    • 📙1.1 栈的概念及结构
    • 📙1.2 栈的实现
      • 📜1.2.1 栈的结构
      • 📜1.2.2 初始化栈
      • 📜1.2.3 入栈
      • 📜1.2.4 出栈
      • 📜1.2.5 获取栈顶元素
      • 📜1.2.6 获取栈中有效元素个数
      • 📜1.2.7 检测栈是否为空
      • 📜1.2.8 销毁栈
      • 📜1.2.9 栈所有接口的测试
  • 二、队列
    • 📙2.1 队列的概念及结构
    • 📙2.2 队列的实现
      • 📜2.2.1 队列的结构
      • 📜2.2.2 初始化队列
      • 📜2.2.3 队尾入队列
      • 📜2.2.4 检测队列是否为空
      • 📜2.2.5 队头出队列
      • 📜2.2.6 获取队列头部元素
      • 📜2.2.7 获取队列尾部元素
      • 📜2.2.8 获取队列中有效元素个数
      • 📜2.2.9 销毁队列
      • 📜2.2.10 队列所有接口的测试
  • 完整代码
    • 📙**Stack.h**
    • 📙**Stack.c**
    • 📙**Queue.h**
    • 📙**Queue.c**
    • 📙**test.c**

前言

在上期中我们把双向链表学完了,这期我们要开始新的一章 “栈和队列” 的讲解,栈和队列基本都是拿我们之前所学的顺序表或链表来实现,可谓是新瓶装旧酒,有了对顺序表和链表的认识,实现栈和队列并不是难事!

一、栈

📙1.1 栈的概念及结构

概念:栈也是一种特殊的线性表,其次栈只允许在固定一端插入删除,进行数据插入或删除操作的一端叫作栈顶,另一端为栈底。 栈中的数据元素遵守后进先出LIFO的原则。

  • 压栈:栈插入数据的操作叫作进栈/入栈/压栈,且入数据是在栈顶。
  • 出栈:栈删除数据的操作叫作出栈,出数据也在栈顶。

结构:
栈结构
注意:栈如果用链表实现的话要考虑找前一个节点的问题,因为栈的插入删除都是在栈顶,所以实现栈时推荐用顺序表来实现,因为顺序表在尾部插入数据的代价比较小。

📙1.2 栈的实现

📜1.2.1 栈的结构

既然用顺序表实现那么栈的结构就是顺序表的结构

typedef int STDataType;typedef struct Stack
{int* arr;int top;//栈顶int capacity;//容量
}ST;

📜1.2.2 初始化栈

因为在栈的结构里,插入数据只能在栈顶插入,所有只需要实现一个插入的接口即可,把malloc改到插入接口(push)里即可.

void STInit(ST* ps)
{assert(ps);ps->arr = NULL;ps->capacity = 0;ps->size = 0;
}

📜1.2.3 入栈

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->arr, sizeof(STDataType)*newcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->arr = tmp;ps->capacity = newcapacity;}//入栈ps->arr[ps->top] = x;ps->top++;
}

需要注意的是:realloc有一个特性,如果传入的参数是NULL,则会像malloc一样开辟一个空间出来,相当于调用了malloc。

📜1.2.4 出栈

出栈很简单让栈顶减1即可。

void STPop(ST* ps)
{assert(ps);//栈为空就不能再删了assert(ps->top > 0);ps->top--;
}

需要注意的是:插入的指针有可能为空或者栈有可能为空,两个情况都要检查一下

📜1.2.5 获取栈顶元素

top是有效数据个数后一个的位置,减1就为栈顶的元素

STDataType STTop(const ST* ps)
{assert(ps);return ps->arr[ps->top-1];
}

📜1.2.6 获取栈中有效元素个数

top就是有效数据的个数,直接返回top即可

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

📜1.2.7 检测栈是否为空

返回top是否等于0,等于返回真,不等于返回假

bool STEmpty(const ST* ps)
{assert(ps);return ps->top == 0;
}

📜1.2.8 销毁栈

注意:这里的销毁指得是把这块空间还给操作系统。

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

📜1.2.9 栈所有接口的测试

void TestStack()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);STPush(&s, 5);printf("栈的有效元素个数%d\n", STSize(&s));while (!STEmpty(&s)){printf("%d ", STTop(&s));STPop(&s);}//STPop(&s);printf("\n栈的有效元素个数%d\n", STSize(&s));STDestroy(&s);}

测试1

当栈为空还在删除时:
测试2

二、队列

📙2.1 队列的概念及结构

概念:队列也是一种只允许在一端插入数据,在另外一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO。

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

结构:
结构图解
注意:队列也能用顺序表和链表实现,但使用链表的结构实现更优一些,因为如果是顺序表的话,头删要挪移数据才能完成,效率比链表低,所以实现队列推荐用链表来实现。

📙2.2 队列的实现

📜2.2.1 队列的结构

既然使用队列实现,那么结构自然是链表的形式。

typedef int QueDataType;typedef struct QueueNode
{QueDataType data;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;QNode* tail;int size;
}Que;

因为是单链表,出队列是头删,入队列是尾插,导致需要一个头指针和尾指针,且单链表计算长度要遍历一遍数组,这样下来:

  1. 要传头和尾的参数
  2. 计算链表长度要遍历链表,复杂度为O(N)
  • 不如直接把头尾指针和定义一个存储链表元素个数的变量整合为一个结构体这样就方便多了

📜2.2.2 初始化队列

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

只有一个插入的接口,那么就直接在插入接口获取节点就行。

📜2.2.3 队尾入队列

首先获取并初始化节点,然后就是尾插,尾插分为两种情况:

  1. 链表为空,特殊处理。
  2. 链表不为空,正常尾插。
void QueuePush(Que* pq, QueDataType x)
{//获取并初始化节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("newnode fail");exit(-1);}newnode->data = x;newnode->next = NULL;//入队列if (pq->phead == NULL){//链表为空pq->phead = newnode;pq->tail = newnode;}else{//链表不为空pq->tail->next = newnode;pq->tail = newnode;}pq->size++;
}

📜2.2.4 检测队列是否为空

直接返回pq->phead == NULL,等于返回真,不等于返回假

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

📜2.2.5 队头出队列

单链表头删很简单,但要注意:链表为空时,不能再删了,否则会出问题

void QueuePop(Que* pq)
{assert(pq);//检测链表是否为空,为空就不能再删了assert(!QueueEmpty(pq));//出队列QNode* Next = pq->phead->next;free(pq->phead);pq->phead = Next;pq->size--;
}

📜2.2.6 获取队列头部元素

phead指向的就是头节点,返回头节点的元素即可,但要注意:链表有可能为空,所以要检测一下

QueDataType QueueFront(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->phead->data;
}

📜2.2.7 获取队列尾部元素

tail指向的就是尾节点,直接返回尾节点的元素即可,但要注意:链表有可能为空,所以要检测一下

QueDataType QueueBack(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->tail->data;
}

📜2.2.8 获取队列中有效元素个数

size存储的就是队列的有效元素个数,返回size即可。

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

📜2.2.9 销毁队列

  • 这里的销毁指的也是把这片空间还给操作系统
  • 遍历一遍队列,记录下一个节点的位置,释放掉当前节点,最后将头尾指针置空,size置为0即可。
void QueueDestroy(Que* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* Next = cur->next;free(cur);cur = Next;}pq->phead = NULL;pq->tail = NULL;pq->size = 0;
}

📜2.2.10 队列所有接口的测试

TestQueue()
{Que obj;QueueInit(&obj);QueuePush(&obj, 1);QueuePush(&obj, 2);QueuePush(&obj, 3);QueuePush(&obj, 4);QueuePush(&obj, 5);printf("队尾元素:%d\n", QueueBack(&obj));printf("队列的有效数据个数:%d\n", QueueSize(&obj));while (!QueueEmpty(&obj)){printf("%d ", QueueFront(&obj));QueuePop(&obj);}//QueuePop(&obj);printf("\n队列的有效数据个数:%d\n", QueueSize(&obj));QueueDestroy(&obj);
}

测试1

当队列为空还在删时:
测试2

完整代码

*

《栈》

📙Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//栈
typedef int STDataType;typedef struct Stack
{int* arr;int top;//栈顶int capacity;//容量
}ST;//初始化栈
void STInit(ST* ps);
//入栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);
//获取栈顶元素
STDataType STTop(const ST* ps);
//获取栈中有效元素的个数
int STSize(const ST* ps);
//检测栈是否为空
bool STEmpty(const ST* ps);
//销毁栈
void STDestroy(ST* ps);

📙Stack.c

#include "Stack.h"void STInit(ST* ps)
{assert(ps);ps->arr = NULL;ps->capacity = 0;ps->top = 0;
}void STPush(ST* ps, STDataType x)
{assert(ps);//检测容量if (ps->top == ps->capacity){//使用三目操作符来解决扩容问题int newcapacity = ps->capacit==0 ? 4 : ps->capacity*2;STDataType* tmp = (STDataType*)realloc(ps->arr, sizeof(STDataType)*newcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->arr = tmp;ps->capacity = newcapacity;}//入栈ps->arr[ps->top] = x;ps->top++;
}void STPop(ST* ps)
{assert(ps);//栈为空就不能再删了assert(ps->top > 0);ps->top--;
}STDataType STTop(const ST* ps)
{assert(ps);return ps->arr[ps->top-1];
}int STSize(const ST* ps)
{assert(ps);return ps->top;
}bool STEmpty(const ST* ps)
{assert(ps);return ps->top == 0;
}void STDestroy(ST* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->capacity = 0;ps->top = 0;
}

*

《队列》

📙Queue.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//队列
typedef int QueDataType;typedef struct QueueNode
{QueDataType data;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;QNode* tail;int size;
}Que;//初始化队列
void QueueInit(Que* pq);
//入队列
void QueuePush(Que* pq, QueDataType x);
//检测队列是否为空
bool QueueEmpty(Que* pq);
//出队列
void QueuePop(Que* pq);
//获取队列头部元素
QueDataType QueueFront(Que* pq);
//获取队列尾部元素
QueDataType QueueBack(Que* pq);
//获取队列中有效元素的个数
int QueueSize(Que* pq);
//销毁队列
void QueueDestroy(Que* pq);

📙Queue.c

#include"Queue.h"void QueueInit(Que* pq)
{assert(pq);pq->phead = NULL;pq->tail = NULL;pq->size = 0;
}void QueuePush(Que* pq, QueDataType x)
{assert(pq);//获取并初始化节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("newnode fail");exit(-1);}newnode->data = x;newnode->next = NULL;//入队列if (pq->phead == NULL){//链表为空pq->phead = newnode;pq->tail = newnode;}else{//链表不为空pq->tail->next = newnode;pq->tail = newnode;}pq->size++;
}bool QueueEmpty(Que* pq)
{assert(pq);return pq->phead == NULL;
}void QueuePop(Que* pq)
{assert(pq);//检测链表是否为空,为空就不能再删了assert(!QueueEmpty(pq));//出队列QNode* Next = pq->phead->next;free(pq->phead);pq->phead = Next;pq->size--;
}QueDataType QueueFront(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->phead->data;
}QueDataType QueueBack(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->tail->data;
}int QueueSize(Que* pq)
{assert(pq);return pq->size;
}void QueueDestroy(Que* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* Next = cur->next;free(cur);cur = Next;}pq->phead = NULL;pq->tail = NULL;pq->size = 0;
}

*

《测试》

📙test.c

#include"Stack.h"
#include"Queue.h"void TestStack()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);STPush(&s, 5);printf("栈的有效元素个数%d\n", STSize(&s));while (!STEmpty(&s)){printf("%d ", STTop(&s));STPop(&s);}//STPop(&s);printf("\n栈的有效元素个数%d\n", STSize(&s));STDestroy(&s);}TestQueue()
{Que obj;QueueInit(&obj);QueuePush(&obj, 1);QueuePush(&obj, 2);QueuePush(&obj, 3);QueuePush(&obj, 4);QueuePush(&obj, 5);printf("队尾元素:%d\n", QueueBack(&obj));printf("队列的有效数据个数:%d\n", QueueSize(&obj));while (!QueueEmpty(&obj)){printf("%d ", QueueFront(&obj));QueuePop(&obj);}//QueuePop(&obj);printf("\n队列的有效数据个数:%d\n", QueueSize(&obj));QueueDestroy(&obj);
}int main()
{TestStack();TestQueue();return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7.3 详解NiN模型--首次使用多层感知机(1x1卷积核)替换掉全连接层的模型

一.前提知识 多层感知机&#xff1a;由一个输入层&#xff0c;一个或多个隐藏层和一个输出层组成。&#xff08;至少有一个隐藏层&#xff0c;即至少3层&#xff09; 全连接层&#xff1a;是MLP的一种特殊情况&#xff0c;每个节点都与前一层的所有节点连接&#xff0c;全连接…

面试题:说说vue2的生命周期函数?说说vue3的生命周期函数?说说vue2和vue3的生命周期函数对比?

说说vue2的生命周期函数&#xff1f;说说vue3的生命周期函数&#xff1f;说说vue2和vue3的生命周期函数对比&#xff1f; 一、说说vue2的生命周期函数1.1 vue生命周期分为四个阶段、8个钩子1.1.1 beforeCreate 和 created 初始化阶段1.1.2 beforeMount 和 mounted 挂载阶段1.1.…

bigemap如何添加四维地图?

工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 打开软件&#xff0c;要提示需要授权和添加地图&#xff0c;需要授权可以联系客服处理&#xff0c;然后点击选择地图这个按钮&#xff0c;列表中有个添加按钮点进去选择…

提高 After Effects 效率的 40 个最佳快捷键

After Effects 是运动图形和视觉效果的强大工具&#xff0c;但它也可能让人不知所措。拥有如此多的特性和功能&#xff0c;很容易让人迷失在软件中。但是&#xff0c;有一种方法可以简化您的工作流程并提高工作效率 - 使用键盘快捷键。 After Effects素材文件巨大、占用电脑内…

快递管理系统springboot 寄件物流仓库java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 快递管理系统springboot 系统有1权限&#xff1a;管…

android 开发中常用命令

1.反编译 命令&#xff1a;apktool d <test.apk> -o <folderdir> 其中&#xff1a;test.apk是待反编译文件的路径&#xff0c;folderdir是反编译后的文件的存储位置。 apktool d -f <test.apk> -o <folderdir> 注意&#xff1a;如果dir已经存在&am…

【Java】2021 RoboCom 机器人开发者大赛-高职组(初赛)题解

7-1 机器人打招呼 机器人小白要来 RoboCom 参赛了&#xff0c;在赛场中遇到人要打个招呼。请你帮它设置好打招呼的这句话&#xff1a;“ni ye lai can jia RoboCom a?”。 输入格式&#xff1a; 本题没有输入。 输出格式&#xff1a; 在一行中输出 ni ye lai can jia Robo…

快手商品详情数据API 抓取快手商品价格、销量、库存、sku信息

快手商品详情数据API是用来获取快手商品详情页数据的接口&#xff0c;请求参数为商品ID&#xff0c;这是每个商品唯一性的标识。返回参数有商品标题、商品标题、商品简介、价格、掌柜昵称、库存、宝贝链接、宝贝图片、商品SKU等。 接口名称&#xff1a;item_get 公共参数 名…

单例模式-java实现

介绍 单例模式的意图&#xff1a;保证某个类在系统中有且仅有一个实例。 我们可以看到下面的类图&#xff1a;一般的单例的实现&#xff0c;是属性中保持着一个自己的私有静态实例引用&#xff0c;还有一个私有的构造方法&#xff0c;然后再开放一个静态的获取实例的方法给外界…

多线程与并发编程面试题总结

多线程与并发编程 多线程 线程和进程的区别&#xff1f; 从操作系统层面上来讲&#xff1a;进程(process)在计算机里有单独的地址空间&#xff0c;而线程只有单独的堆栈和局部内存空间&#xff0c;线程之间是共享地址空间的&#xff0c;正是由于这个特性&#xff0c;对于同…

php如何对接伪原创api

在了解伪原创api的各种应用形态之后&#xff0c;我们继续探讨智能写作背后的核心技术。需要说明的是&#xff0c;智能写作和自然语言生成、自然语言理解、知识图谱、多模算法等各类人工智能算法都有紧密的关联&#xff0c;在百度的智能写作实践中&#xff0c;常根据实际需求将多…

Vue [Day7]

文章目录 自定义创建项目ESlint 代码规范vuex 概述创建仓库向仓库提供数据使用仓库中的数据通过store直接访问通过辅助函数 mapState&#xff08;简化&#xff09;mutations传参语法(同步实时输入&#xff0c;实时更新辅助函数 mapMutationsaction &#xff08;异步辅助函数map…