数据结构与算法——栈和队列

article/2025/11/7 13:30:40

😊数据结构与算法——栈和队列

    • 🚀前言
    • 🚀栈(satck)
      • 🚢栈的定义
      • 🚢共享栈(节省空间)
      • 🚢栈的表示和实现(顺序栈)
        • 👻顺序栈的定义
        • 👻初始化操作
        • 👻进栈操作
        • 👻出栈操作
        • 👻读取栈顶元素
      • 🚢栈的表示和实现(链栈)
        • 👻链栈的定义
    • 🚀队列(queue)
      • 🚢队列的定义
      • 🚢队列的顺序表示和实现(顺序队列)
        • 👻初始化操作
        • 👻入队操作
        • 👻出队操作
        • 👻获取队头元素操作
      • 🚢队列的链式表示和实现(链队列)
        • 👻初始化操作
        • 👻入队操作
        • 👻出队操作
      • 🚢双端队列
    • 💻总结

🚀前言

栈和队列是两种重要的线性结构。从数据结构角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集,它们是操作受限的线性表,因此,可称为限定性的数据结构。

在这里插入图片描述

但是从数据类型角度看,栈和队列是和线性表大不相同的两种重要的抽象数据类型。栈和队列的运用比较广泛,属于多型数据类型。



🚀栈(satck)


🚢栈的定义

(stack)是限定仅在表尾进行插入或删除操作的线性表。因此,对于栈来说,表尾端有其特殊的含义,称为栈顶(top),相应地,表头端称为栈底(bottom)。不包含元素的空表称为空栈

在这里插入图片描述

假设 S = ( a 1 , a 2 , . . . . a n ) S=(a1, a2,....an) S=(a1,a2,....an),则称 a 1 a1 a1为栈底元素, a n an an为栈顶元素。栈中元素按 a 1 , a 2 , . . . a n a1,a2,...an a1,a2,...an的次序进栈,那么出栈的第一个元素应为栈顶元素

栈的修改是按照后进先出的原则进行的,因此,栈又称为后进先出(last in first out)的线性表(简称LIFO)结构


🚢共享栈(节省空间)

两个栈共享一个存储空间,意义在于高效利用存储空间

📌第二种说法:两个栈底分别设置在一个空间的两端,栈顶向中间延伸


共享栈的定义

typedef struct Linknode{ElemType data;      //数据域struct Linknode *next;      //指针域
}LiStack;                   //栈类型定义

📌共享栈的栈满情况:当两个栈的top在空间中某一位置相遇时


🚢栈的表示和实现(顺序栈)

和线性表类似,栈也有两种存储表示方法——顺序栈和链栈

顺序栈,即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附加指针top表示栈顶元素在顺序栈中的位置。通常当top=-1时,表示此栈为空栈。


👻顺序栈的定义

# define MaxSize 10         //定义栈中元素的最大个数
typedef struct{ElemType data[MaxSize];    //静态数组存放栈中元素 int top;                   //栈顶指针
}SqStack;void testStack(){SqStack S;      //声明一个顺序栈(分配空间).....//后续操作
}

由于栈在使用过程中所需要最大空间的大小很难估计,所以,一般来说,在初始化设空栈时不应限定栈的最大容量,常规做法是:先为栈分配一个基本容量,然后在应用过程中,当栈的空间不够使用时再逐步扩大容量


👻初始化操作

构造一个空栈,分配内存空间

# define MaxSize 10         //定义栈中元素的最大个数
typedef struct{ElemType data[MaxSize];    //静态数组存放栈中元素 int top;                   //栈顶指针
}SqStack;void InitStack(SqStack &S){S.top = -1;             //初始化栈顶指针
}void testStack(){SqStack S;      //声明一个顺序栈(分配空间)InitStack(S);.....//后续操作
}//栈的判空操作
bool StackEmpty(SqStack S){if(S.top == -1){return true;        //栈空}else{return false;       //不为空}
}

第二种定义

在这里插入图片描述

按设定的初始分配量进行第一次存储分配,base可称为是栈底指针,在顺序占中,它始终指向栈底的位置,若base的值为NULL,则表明栈结构不存在,top表示栈顶指针,其初始值指向栈底,即top==base空栈可以表示为top==base。每当插入新的栈顶元素时,指针top增加1,删除栈顶元素时,指针top减1,因此,非空栈中的栈顶指针始终在栈顶元素的下一个位置上

#define STACK_INIT_SIZE 100;        //存储空间初始分配量
#define STACKINCREMENT 10;          //存储空间分配增量
typedef struct{SElemType *base;        //栈底指针SElemType *top;         //栈顶指针int stacksize;          //当前已分配的空间
}SqStack;void InitStack(SqStack &S){//构造一个空栈S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));if(!S.base) exit(OVERFLOW);         //存储分配失败S.top = S.base;S.stacksize = STACK_INIT_SIZE;
}void testStack(){SqStack S;          //声明一个顺序栈InitStack(S);....//后续操作
}

👻进栈操作

若栈未满,则将x加入使之成为新栈顶

#define MaxSize 10;     //定义栈中元素的最大个数
typedef struct{ElemType data[MaxSize];     //静态数组存放栈中元素int top;                    //栈顶指针
}SqStack;//新元素入栈
bool Push(SqStack &S, ElemType x){if(S.top == MaxSize-1){     //表示栈满了return false;}S.top = S.top + 1;          //指针先加一S.data[S.top] = x;          //新元素入栈return true;
}

👻出栈操作

若栈非空,则释放栈顶元素,并返回。

#define MaxSize 10;     //定义栈中元素的最大个数
typedef struct{ElemType data[MaxSize];     //静态数组存放栈中元素int top;                    //栈顶指针
}SqStack;//出栈操作
bool Pop(SqStack &S, ElemType &x){if(S.top == -1){        //栈空,报错return false;}x = S.data[S.top];      //栈顶元素先出栈S.top = S.top - 1;      //指针再减1return true;
}

👻读取栈顶元素

若栈非空,则用x返回栈顶元素

#define MaxSize 10;     //定义栈中元素的最大个数
typedef struct{ElemType data[MaxSize];     //静态数组存放栈中元素int top;                    //栈顶指针
}SqStack;//读取栈顶元素
bool GetTop(SqStack &S, ElemType &x){if(S.top == -1){        //栈空,报错return false;}x = S.data[S.top];      //记录栈顶元素return true;
}

🚢栈的表示和实现(链栈)

对于链栈的基本操作来说,和单链表的插入删除很类似,所以就不在赘述,链栈的入栈和出栈操作,其实就对应单链表的插入和删除操作


👻链栈的定义

typedef struct Linknode{ElemType data;      //数据域struct Linknode *next;      //指针域
}LiStack;                   //栈类型定义

栈的非法操作
📌上溢:当栈满了的情况下再次放入元素会造成此情况
📌下溢:当栈空了的情况下再次删除元素会造成此情况



🚀队列(queue)


🚢队列的定义


和栈相反,队列(queue)是一种先进先出(first in first out)的线性表缩写为FIFO)。它只允许在表的一端进行插入,在另一端进行删除元素。这种数据结构概括起来就和我们平时排队是一样的道理,最早进入到的队列的元素最先离开。

在这里插入图片描述


在队列中,只允许插入的一端叫做队尾(rear),允许删除的一端则称为队头(front)。假设队列为 q = ( a 1 , a 2 , . . . a n ) , q=(a1,a2,...an), q=(a1,a2,...an)那么, a 1 a1 a1就是队头元素, a n an an就是队尾元素。队列中的元素是按照 a 1 , a 2 , a 3.... a n a1,a2,a3....an a1,a2,a3....an的顺序进入的,退出队列也只能按照这个次序依次退出,也就是说,只有在 a 1 , a 2 , a 3... a n − 1 a1,a2,a3...an-1 a1,a2,a3...an1都离开队列之后, a n an an才能退出队列


  • 📌队首队尾指针的两种指法
    • ⭐队首指针(front)指向:队头元素的前一个存储位置

    • ⭐队尾指针(rear)指向:队尾元素

    • ⚡队首指针(front)指向:队头元素

    • ⚡队尾指针(rear)指向:队尾元素的下一个存储位置

📌假溢出:队中有空间,元素无法入队


🚢队列的顺序表示和实现(顺序队列)


和顺序栈类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放队列头到队列尾的元素之外,还需要附设两个指针frontrear分别指示队列头元素以及队列尾元素的位置。基本操作基于循环队列,循环队列的引出是为了解决假溢出的问题。


  • 📌循环队列的性质
    • ⛅数组实现
      • 空队列:front == rear
      • 满队列:牺牲一个单元判满(不牺牲的话队空队满无法区分)
      • (rear+1)% maxSize == front
      • 进队:rear新 = (rear旧+1)% maxSize
      • 出队:front新 = (front旧+1)% maxSize
      • 队中元素个数/长度:(rear - front + maxSize) % maxSize

👻初始化操作

初始化队列,构造一个空队列

#define MaxSize 10         //定义队列中元素的最大个数
typedef struct{ElemType data[MaxSize];     //用静态数组存放队列元素int front, rear;            //队头指针和队尾指针
}SqQueue;//初始化队列
void InitQueue(SqQueue &Q){//初始化 队头、队尾指针指向0Q.rear = Q.front = 0;
}void testQueue(){//声明一个队列(顺序存储)SqQueue Q;InitQueue(Q);...//后续操作
}//判断队列是否为空
bool QueueEmpty(SqQueue Q){if(Q.rear == Q.front){  //队空条件return true;}else{return false;}
}

👻入队操作

若队列未满,将x加入,使之称为新的队尾

#define MaxSize 10         //定义队列中元素的最大个数
typedef struct{ElemType data[MaxSize];     //用静态数组存放队列元素int front, rear;            //队头指针和队尾指针
}SqQueue;//入队
bool EnQueue(SqQueue &Q, ElemType x){if((Q.rear+1)%MaxSize == Q.front){       //判断队列是否已满return false;       //对满则报错}Q.data[Q.rear] = x;     //将x插入队尾Q.rear = (Q.rear + 1) % MaxSize;    //队尾指针后移return true;
}

👻出队操作

若队列非空,删除队头元素并返回x

#define MaxSize 10         //定义队列中元素的最大个数
typedef struct{ElemType data[MaxSize];     //用静态数组存放队列元素int front, rear;            //队头指针和队尾指针
}SqQueue;//出队(删除一个队头元素,并返回x)
bool DeQueue(SqQueue &Q, ElemType &x){if(Q.rear == Q.front){      //判断队空return false;           //队空则报错}x = Q.data[Q.front];Q.front = (Q.front+1)%MaxSize;return true;
}

👻获取队头元素操作

读队头元素,若队列非空,则将队头元素赋值给x

#define MaxSize 10         //定义队列中元素的最大个数
typedef struct{ElemType data[MaxSize];     //用静态数组存放队列元素int front, rear;            //队头指针和队尾指针
}SqQueue;//获得队头元素的值,用x返回
bool GetHead(SqQueue Q, ElemType &x){if(Q.rear == Q.front){return false;       //队空报错}x = Q.data[Q.front];return true;
}

🚢队列的链式表示和实现(链队列)


和线性表类似,队列也可以有两种存储表示

用链表表示的队列简称为链队列,一个链队列显然需要两个分别指示队头和队尾的指针(分别称为头指针和尾指针)才能唯一确定。

链队列的操作即为单链表的插入和删除操作的特殊情况,只是需要修改尾指针或头指针

一般情况下,删除队列头元素时仅需修改头结点中的指针,但当队列中最后一个元素被删除后,队列表尾指针也丢失了,因此需对队尾指针重新赋值(指向头结点)


👻初始化操作

初始化队列,构造一个空队列

📌带头结点:

typedef struct LinkNode{        //链式队列结点ElemType data;struct LinkNode *next;
}LinkNode;typedef struct{     //链式队列LinkNode *front, *rear  //队列的队头和队尾指针
}LinkQueue;//初始化队列(带头结点)
void InitQueue(LinkQueue &Q){//初始化 front、rear都指向头结点Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));Q.front -> next = NULL;
}viod testLinkQueue(){LinkQueue Q;        //声明一个队列InitQueue(Q);       //初始化队列
}//判断队列是否为空
bool IsEmpty(LinkQueue Q){if(Q.front == Q.rear){return true;}else{return false;}
}

📌不带头结点:

typedef struct LinkNode{        //链式队列结点ElemType data;struct LinkNode *next;
}LinkNode;typedef struct{     //链式队列LinkNode *front, *rear  //队列的队头和队尾指针
}LinkQueue;//初始化队列(不带头结点)
void InitQueue(LinkQueue &Q){//初始化 front、rear都指向头结点Q.front = NULL;Q.rear = NULL;
}viod testLinkQueue(){LinkQueue Q;        //声明一个队列InitQueue(Q);       //初始化队列
}//判断队列是否为空
bool IsEmpty(LinkQueue Q){if(Q.front == NULL){return true;}else{return false;}
}

👻入队操作

若队列未满,将x加入,使之称为新的队尾
📌带头结点:

typedef struct LinkNode{        //链式队列结点ElemType data;struct LinkNode *next;
}LinkNode;typedef struct{     //链式队列LinkNode *front, *rear  //队列的队头和队尾指针
}LinkQueue;//新元素入队(带头结点)
void EnQueue(LinkQueue &Q, ElemType x){LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));s->data = x;s->next = NULL;Q.rear->next = s;       //新结点插入到rear之后Q.rear = s;             //修改表尾指针
}

📌不带头结点:

typedef struct LinkNode{        //链式队列结点ElemType data;struct LinkNode *next;
}LinkNode;typedef struct{     //链式队列LinkNode *front, *rear  //队列的队头和队尾指针
}LinkQueue;//新元素入队(不带头结点)
void EnQueue(LinkQueue &Q, ElemType x){LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));s->data = x;s->next = NULL;if(Q.front == NULL){        //在空队列中插入第一个元素Q.front = s;            //修改队头队尾指针Q.rear = s;}else{Q.rear->next = s;       //新结点插入到rear结点之后Q.rear = s;             //修改rear指针}
}

👻出队操作

若队列非空,删除队头元素并返回x

📌带头结点:

typedef struct LinkNode{        //链式队列结点ElemType data;struct LinkNode *next;
}LinkNode;typedef struct{     //链式队列LinkNode *front, *rear  //队列的队头和队尾指针
}LinkQueue;//队头元素出队操作(带头结点)
bool DEQueue(LinkQueue &Q, ElemType &x){if(Q.front == Q.rear){return      //空队列}LinkNode *p = Q.front->next;x = p->data;        //用变量x返回队头元素Q.front->nexy = p->nexy;        //修改头结点的next指针if(Q.rear == p){                //此次是最后一个结点出队Q.rear = Q.front            //修改rear指针}free(p);                        //释放结点空间return true;
}

📌不带头结点:

typedef struct LinkNode{        //链式队列结点ElemType data;struct LinkNode *next;
}LinkNode;typedef struct{     //链式队列LinkNode *front, *rear  //队列的队头和队尾指针
}LinkQueue;//队头元素出队操作(不带头结点)
bool DEQueue(LinkQueue &Q, ElemType &x){if(Q.front == Q.rear){return      //空队列}LinkNode *p = Q.front;      //p指向此次出队的结点x=p->data;                  //用变量x返回队头结点元素Q.front = p->next;          //修改front指针if(Q.rear == p){            //此次是最后一个结点出队Q.front = NULL;         //front指向NULLQ.rear = NULL;          //rear 指向NULL}free(p);                    //释放结点空间return true;
}

🚢双端队列

除了栈和队列之外,还有一种限定性数据结构——双端队列(deque)

双端队列是限定插入和删除操作在表的两端进行的线性表。着两端分别称作端点1端点2

在这里插入图片描述


在实际使用中,还可以有输出受限的双端队列(即一个端点允许插入和删除,另一个端点只允许插入的双端队列)和输入受限的双端队列(即一个端点允许插入和删除,另一个端点只允许删除的双端队列)。而如果限定双端队列从某个端点插入的元素只能从该端点删除,则该双端队列就蜕变为了两个栈底相邻接的栈了



💻总结

本节文章到这里就结束啦,以上内容涵盖数据结构与算法中栈和队列的基本概念以及基本操作,结合代码片段分析各基本操作的具体实现,在本文中,栈的链式存储以及循环队列是理解较为不易的点,需结合具体操作认真分析,希望各位小伙伴都能有所收获,一如既往希望我的文章能给各位小伙伴们带来帮助,数据结构与算法专栏也在持续更细中!!!

🎨觉得不错的话记得点赞收藏呀!!🎨

😀别忘了给我关注~~😀


http://chatgpt.dhexx.cn/article/Fwfb0HyS.shtml

相关文章

数据结构:栈和队列(Stack Queue)【详解】

友情链接:数据结构专栏 目录 栈和队列【知识框架】 栈一、栈的基本概念1、栈的定义2、栈的常见基本操作 二、栈的顺序存储结构1、栈的顺序存储2、顺序栈的基本算法(1)初始化(2)判栈空(3)进栈&a…

web 移动端开发基础

web 移动端开发基础 文章目录 web 移动端开发基础了解视口相关内容meta 视口标签掌握二倍图用法物理像素 & 物理像素比多倍图二倍精灵图做法 了解移动端常见选择方案掌握移动端常见问题解决方案移动端常见的布局方式流式布局(百分比布局)flex 布局 -…

Web移动端

1.PC端和移动端的区别: PC端:屏幕大 用网页固定版心,要考虑浏览器兼容问题,(布局:浮动+定位+标准流) 移动端: 手机屏幕小,网页宽度多数为100%,是适配手机屏幕宽度 移动端则基本不需要考虑兼容性问题,放心大胆使用CS…

移动端网页开发基础

文章目录 前言一、浏览器1.pc端常见浏览器2.移动端常见浏览器 二、视口1.布局视口layout viewport2.视觉视口visual viewport3.理想视口ideal viewport 三、二倍图1.物理像素和物理像素比2.二倍图用法 四、移动端开发选择1.单独制作移动端页面2.响应式兼容pc移动端3.移动端常见…

20.【移动端Web开发之响应式布局】

文章目录 【移动端Web开发之响应式布局】前端小抄(20)一、响应式开发1.1 响应式开发原理1.2 响应式布局容器 二、Bootstrap前端开发框架2.1 Bootstrap简介2.2 Bootstrap使用2.3 布局容器 三、Bootstrap栅格系统3.1 栅格系统简介3.2 栅格选项参数3.3 列嵌套3.4 列偏移3.5 列排序…

H5移动web开发

目录 1、H5 的新特性有哪些?C3 的新特性有哪些? 2、如何使一个盒子水平垂直居中? 方法一:利用定位(常用方法,推荐) 方法二:利用 margin:auto 方法三:利用 display:table-cell 方法四…

手摸手带你学移动端WEB开发

HTML常用标签总结手摸手带你学CSSHTML5与CSS3知识点总结 好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star ⭐⭐⭐⭐⭐转载请注明出处!⭐⭐⭐⭐⭐ 链接:https://blog.c…

移动端网页开发(一)

一、项目代码初始化 1.打开index.html将<meta></meta>标签补充完整 <html><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width,initial-scale1.0&#xff0c;minimum-scale1.0,maximum-…

web移动开发

web移动开发 手机内置浏览器&#xff1a; Android&#xff1a;Andriod BrowserIOS&#xff1a;Mobile SafariBlackBerry&#xff1a;WebkitSymbian S60&#xff1a; Web Browser for S60 其浏览器的核心都是基于Webkit 智能手机Web浏览器的特点: 有限的屏幕尺寸触屏、缩放硬…

移动端页面开发

移动端页面开发需要掌握的…… 移动端开发指的是&#xff1a;需要适配移动设备的网页开发移动开发与pc端开发没有本质的区别&#xff0c;使用的也还是HTML/CSS/JS的技术 一、移动端与pc端开发的区别在于&#xff1a; 1.浏览器不同 手机浏览器是webkit的天下&#xff0c;就目…

从零开始学习移动端Web开发

背景 近年来&#xff0c;随着智能手机的普及&#xff0c;移动端开发受到了异常的关注。从传统的安卓、IOS原生手机系统应用开发&#xff0c;转向了移动端Web开发或者是混合开发&#xff0c;既然有需求&#xff0c;那就让我们一起来学习移动端Web开发吧。本文旨在让读者以最快的…

移动端网站开发

受限于流量太少&#xff0c;以前的手机网站无法做成像现在一样的效果&#xff0c;只能通过超链接的形式实现简单的网页 随着3G&#xff0c;4G&#xff0c;5G的商用&#xff0c;我们的流量越来越多&#xff0c;网速越来越快。越来越多的应用开始去开发网页版。 移动端仿真 未来…

移动端开发

2022.3.5 学习笔记 目录 四、移动端开发方案 五、 移动端技术解决方案 六、移动端常见布局 移动端开发之流式布局&#xff1a; 一、基础 二、制作京东移动端首页案例 四、移动端开发方案 ①单独制作移动端页面 &#xff08;主流&#xff09; 京东商城手机版 淘宝触屏版…

web前端开发之移动端基础

web前端开发之移动端基础 一、物理像素(px) 定义&#xff1a;虚拟像素&#xff0c;可以理解为“直觉”像素&#xff0c;CSS和JS使用的抽象单位&#xff0c;浏览器内的一切长度都是以CSS像素为单位的&#xff0c;CSS像素的单位是px。 1.2 像素到底是什么 像素&#xff08;px…

前端移动端web开发(一)

一、前端开发 1.前端开发分类&#xff1a; PC端开发&#xff1a;页面主要运行在PC端浏览器中 移动端开发&#xff1a;页面主要运行在手机上 移动web开发 在移动端表现良好的页面&#xff0c;如新浪网 混合式开发&#xff08;Hybrid App&#xff09; 也叫“套壳开发”&#xf…

移动端Web开发 基础知识

文章目录 移动端Web开发移动端基础浏览器视口样式编写分辨率和设备像素比二倍图SVG矢量图 移动端Web开发 移动Web开发的两种主流方案&#xff0c;一种是单独制作移动端页面&#xff0c;另一种是制作响应式页面 移动端页面&#xff1a; 单独制作移动端页面的优势和劣势&#…

(一)移动端 Web 开发基础

文章目录 一、移动 Web 开发基础概念1. 像素(1) 分辨率(2) 物理像素(3) CSS 像素(4) 设备像素比(5) 标清屏和高清屏(6) 缩放(7) PPI / DPI 2. 视口 viewport 二、移动 Web 开发基础知识1. box-sizing2. 图标字体3. flex 布局(1) 什么是 flex 布局(2) flex 布局的基本概念(3) fl…

移动端web开发

相关阅读&#xff1a; WebApp与Native App的区别&#xff1f; 在高端智能手机系统中有两种应用程序&#xff1a;一种是基于本地(操作系统)运行的APP;一种是基于高端机的浏览器运行的WebApp&#xff08;基于WEB形式的应用程序&#xff09; Native App&#xff1a; 1、开发成本非…

Web前端开发 移动端开发(快速入门)

目录 一、理论知识1.视口2.物理像素和物理像素比3.二倍图4.移动端开发选择 二、移动端开发流程1.技术选型2.搭建文件结构3.SEO优化3大标签4.设置favicon.ico(logo图片)5.视口标签和初始化样式6.设置自适应尺寸&#xff08;两种方法&#xff09;1.方法一&#xff1a;创建common.…

移动端web开发笔记(一)

我本来一直在开发PC端的网页的&#xff0c;但是看到很多招聘都要求要有移动端开发的经验&#xff0c;所以开始学习一下&#xff01; 先搞清楚两个概念&#xff0c;移动端web开发&#xff0c;web app开发 1、 移动web开发&#xff08;pc端的页面用手机浏览器打开&#xff09; …