数据结构-栈、队列和数组

栈的定义

栈是只允许在一端进行插入或删除操作的线性表。首先栈式一种线性表,但限定这种线性表只能在某一端进行插入和删除操作,如图所示。

栈包括:

  • 栈顶(Top)。允许进入插入删除的那一端。

  • 栈底(Buttom)。不许与进行插入和删除的一端。

  • 空栈。不含任何元素的空表。

从图中可以很明显的看到栈的操作特性为后进先出(Last In First Out, LIFO)。当我们运行高级语言程序时编译器执行语句采用的就是栈的形式。

img

栈的数学性质

n个不同元素进栈,出栈元素不同排列的个数为:

$$
\frac{1}{n+1}C^{n}_{2n}
$$

这公式称为卡特兰(Catalan)数。

栈的顺序存储结构

采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶元素的位置。

栈的顺序存储类型:

#define MaxSize 10 //定义栈中元素的最大个数
​
typedef struct SqStack
{Elemtype data[MaxSize]; //存放栈中元素int top;  //栈顶指针
}SqStack;

顺序栈的实现

在此代码中,以top == -1来当作栈空条件。

顺序栈的初始化
//初始化一个空栈
void InitStack(SqStack * S){S->top = -1;
}

顺序栈的判空
// 判断栈是否为空
bool StackEmpty(const SqStack *S)
{return S->top == -1 ? true : false; //若 top == -1 则判断为空
}

顺序栈的进栈
//进栈操作
bool Push(SqStack *S, Elemtype x){//栈满结束操作if(S->top + 1 == MaxSize) return false;S->top ++; //栈顶指针指向进栈的元素位置S->data[S->top] = x;return true;
}

顺序栈的出栈
//出栈操作                x用于接受出栈的元素
bool Pop(SqStack *S,Elemtype *x){//栈为空结束操作if(StackEmpty(S)) return false;*x = S->data[S->top--];return true;
}

获取顺序栈的栈顶元素
// 获取栈顶元素
bool GetTop(SqStack *S, Elemtype *x)
{// 栈为空结束操作if (StackEmpty(S))return false;*x = S->data[S->top];return true;
}

销毁顺序栈
//销毁栈
bool DestroyStack(SqStack *S){//只需将top = -1即可,因为是静态存储剩余空间会自动释放S->top = -1;
}

共享栈

为了更有效的利用存储空间,可以使用共享栈的形式,通过栈底位置相对不变的特性,可让两个顺序栈共享一个一位数组空间,将栈底分别设置在共享空间的两端,即0MaxSize -1处,而当两个栈顶指针相邻时,则判断栈满。

栈的链式存储结构

采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率。

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

栈的链式存储结构实现
栈的初始化
//不带头节点初始化   用二级指针是因为传入的是以StackLinkNode*形式,原先结构体就是以指针的形式,所以要用二级指针并通过解引用的方式来进行值的修改,单传StackLinkNode形式以及用一级指针无法通过传值来对结构体类型进行修改。
void InitStackLink(StackLink ** S){*S = NULL;
}

入栈
// 入栈操作 均插入到头节点后面的位置
int StackLinkPush(StackLink **S, Elemtype x)
{StackLink *newNode = (StackLink *)malloc(sizeof(StackLink));// 系统没有足够空间,插入失败if (newNode == NULL)return false;newNode->data = x;newNode->next = (*S);*S = newNode;
​return true;
}

出栈
// 出栈操作 删除头节点后一个的节点  x为出栈的元素
int StackLinkPop(StackLink **S, Elemtype *x)
{// 判断栈是否为空if (StackLinkEmpty(S))return false;
​*x = (*S)->data;StackLink *p = (*S);(*S) = p->next;// 释放删除节点的空间free(p);p = NULL;
}

队列

队列的基本概念
队列的定义

队列(Queue)也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队。删除元素称为出队或离队。其操作的特性是先进先出(First In First Out, FIFO)

队列常见的基本操作
  • InitQueue(&Q):初始化队列,构造一个空队列。

  • QueueEmpty(&Q):判队列空,若队列Q为空返回true,否则返回false

  • EnQueue(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾。

  • DeQueue(&Q,&x):出队,若队列Q非空,删除队头元素,并用x返回。

  • GetHead(Q,&x):读队头元素,若队列Q非空,则将队头元素赋值给x。

队列的顺序存储结构
队列的顺序存储

队列的顺序实现是指分配一块连续的存储单元存放队列中的元素(相当于操作受限的顺序表),并附设两个指针:队头指针front指向队头元素,队尾指针rear指向队尾元素的下一个位置(也可以指向最后一个元素,根据实际情况而定)。

存储类型可描述为:

typedef struct SqQueue
{ElemType data[MaxSize]; // 存放队列元素int front, rear;        // 队头指针和队尾指针
} SqQueue;

循环队列

为了解决顺序队列一些缺点,引出了循环队列的概念。将顺序队列臆造称一个环状的空间,将队列的表在逻辑上视为一个环,即称循环队列。所以当首指针Q.front = MaxSize - 1后,再前进一个位置就重新回到0,可以通过模运算(%)实现。

  • 初始时:Q.front = Q.rear = 0,代表队列为空。

  • 队首指针进1:Q.front = (Q.front + 1) % MaxSize,每当队头指针到最后一个存储空间位置就重新指回0处。

  • 队尾指针进1:Q.rear = (Q.rear + 1) % MaxSize,逻辑与队首指针相同。

  • 队列长度:(Q.rear + MaxSize - Q.front) % MaxSize。因为循环队列有可能会出现Q.rear > Q.front的情况,所以需要通过模运算控制最大值并保证在Q.front > Q.rear情况也可以使用。

  • 判空条件:Q.front == Q.rear


判断队满的三种处理方式:

  • 牺牲一个单元来区分对空还是队满,入队时少用一个单元,即可通过(Q.front + 1) % MaxSize == Q.rear来判断是否为队满,如下图所示。

img

  • 类型中增设表示元素个数的数据成员,如size。这样,队空的条件为Q.size == 0,队满的条件为Q.size == MaxSize

  • 类型中增加tag数据成员,以区分队满还是队空。tag = 0来代表上一个操作为删除元素,若此时Q.front == Q.rear,因为上次为删除操作,所以只有队空的情况。反之tag = 1代表插入操作,只有队满才会导致Q.front == Q.rear

顺序存储结构队列的具体实现
初始化
// 初始化
SqQueue *InitSqQueue()
{SqQueue *Q = (SqQueue *)malloc(sizeof(SqQueue));Q->front = Q->rear = 0;// 初始化队头队尾指针与存放个数
}

判空
//判空
bool SqQueueEmpty(const SqQueue *Q){return Q->front == Q->rear; 
}

入队
// 入队操作
bool EnSqQueue(SqQueue *Q, ElemType x)
{// 判断是否元素已满  相当于浪费了最后一个存储空间,等于只能存储MaxSize -1 个值if ((Q->rear + 1) % MaxSize == Q->front)return false;Q->data[Q->rear] = x;Q->rear = (Q->rear + 1) % MaxSize;return true;
}

出队
// 出队
bool PopSqQueue(SqQueue *Q, ElemType *x)
{// 判断队列是否为空if (SqQueueEmpty(Q))return false;*x = Q->data[Q->front];
​Q->front = (Q->front + 1) % MaxSize;
​return true;
}

队列的链式存储结构
队列的链式存储

通过链表的形式来表示队列就称为队列的链式存储,它实际就是一个同时带有队头指针和队尾指针的单链表。头指针指向队头节点,尾指针指向队尾节点,即单链表的最后一个节点。

队列的链式存储类型描述为:

//链式队列节点
typedef struct LinkNode{ElemType data;struct LinkNode *next;
}LinkNode;
​
//链式队列
typedef struct LinkQueue{LinkNode *front,*rear; //队头和队尾指针
}*LinkQueue;

队列的链式存储结构基本操作
初始化
// 初始化
LinkQueue InitLinkQueue()
{LinkQueue Q = (LinkQueue)malloc(sizeof(LinkQueue));// 建立头节点和尾节点并指向同一块空间Q->front = Q->rear = (LinkNode *)malloc(sizeof(LinkNode));//设头节点的下一个节点为空即此时尾节点的下一个节点也为空Q->front->next = NULL;return Q;
}

判断空
bool LinkQueueEmpty(const LinkQueue Q)
{return Q->front == Q->rear;
}

入队
// 入队
bool EnLinkQueue(LinkQueue Q, ElemType x)
{LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));if (s == NULL)return false;s->data = x;s->next = NULL;Q->rear->next = s;//移动尾节点Q->rear = s;
}

出队
//出队
bool PopLinkQueue(LinkQueue Q, ElemType *x){if(LinkQueueEmpty(Q))return false;
​LinkNode *p = Q->front->next;*x = p->data;Q->front->next = p->next;//若只有一个节点则变为空if(Q->rear == p){Q->rear == Q->front;}free(p);return true;
}

双端队列

双端队列是指允许两端都可以进行入队和出队操作的队列,如图所示。其元素的逻辑结构仍是线性结构。

img

栈和队列的应用

栈在括号匹配中的应用

假设表达式中包含()[]这三种括号,其嵌套的顺序任意即([]())[([][])]等均为正确格式,即左括号和右括号必定可以成一对且匹配到时为同种类型,[(])([())(()]均为不正确的格式。

算法分析:

当输入括号序列:[([][])]

  • 计算机入栈第1个括号[后,仅当第8个括号]出现后才会出栈。

  • 获得第2个括号(,由于)在第7个位置)才会出栈,所以也将(入栈。

  • 第3个[由于第4个]即为相应匹配,根据就近原则,第3个和第4个有限匹配出栈,而非第1个括号。所以第3个和第4个括号匹配后就会进行出栈。

  • 第5个和第6个和第3个第4个括号同理,也根据就近原则直接匹配成功。

  • 此时扫描到第7个位置的),由于前面没有其他的(,只会和第2个入栈的(进行匹配,最后匹配成功出栈。

  • 第8个也同理,与第1个[匹配成功后出栈。

  • 当扫描完成后判断栈是否为空,为空则匹配成功,不为空则代表匹配失败。

括号匹配代码实现
// 括号匹配 传入括号字符串以及对应的长度
bool bracketCheck(char str[], int length)
{SqStack S;// 初始化栈InitStack(&S);for (size_t i = 0; i < length; i++){// 为左括号就入栈if (str[i] == '(' || str[i] == '['){Push(&S, str[i]);}else{//扫描到右括号但栈已为空if(StackEmpty(&S)) return false;Elemtype popEl;Pop(&S, &popEl);//仅当弹出的括号为一对时算匹配成功if (str[i] == ')' && popEl != '('){return false;}if (str[i] == ']' && popEl != '['){return false;}}}// 为空代表匹配成功,不为空代表匹配失败return StackEmpty(&S);
}

数组和特殊矩阵

数组的定义

数组是由n(n>=1)个相同类型的数据元素构成的有限序列,每个数据元素称为一个数组元素,每个元素在n个线性关系中的序号称为该元素的下标,下标的取值范围称为数组的维界。

数组与线性表的关系:数组是线性表的推广。一维数组可视为一个线性表;二维数组可视为其元素也是定长线性表的线性表。

数组的存储结构

以一维数组A[0...n-1]为例,其存储结构关系式为

$$
LOC(a_{i})=LOC(a_{0})+i{\times}L(0{\leqslant}i<n)
$$

其中,L是每个数组元素所占的存储单元,就是数据类型的大小(sizeof(ElemType))。

多维数组

对于多维数组由两种映射方法:按行优先按列优先

按行优先存储的基本思想是:先行后列,先存储行号较小的元素,行号相等先存储列号较小的元素。设二维数组的行下标与列下标的范围分别为[0,h_1][0,h_2],则存储结构关系式为

$$
LOC(a_{i,j})=LOC(a_{0,0})+[i{\times}(h_{2}+1)+j]{\times}L
$$

LOC(a_0,0)为数组的起始地址,i*(h_2 +1) + j就是行号乘矩阵的列数得到一行的个数后再加上要搜寻的元素的列号就可以搜索到元素。

例:对于数组A_[2][3]

按行优先的存储形式为:

$$
A_{[2][3]}=\begin{pmatrix} a_{[0][0]} & a_{[0][1]} & a_{[0][2]} \\ a_{[1][0]} & a_{[1][1]} & a_{[1][2]} \\ \end{pmatrix} \qquad \underbrace{a_{[0][0]} a_{[0][1]} a_{[0][2]}} \quad \underbrace{a_{[1][0]} a_{[1][1]} a_{[1][2]}}
$$

右边式子即先存储第一行后再存储第二行。

按列优先则存储结构关系式为

$$
LOC(a_{i,j})=LOC(a_{0,0})+[j{\times}(h_{1}+1)+i]{\times}L
$$

LOC(a_0,0)为数组的起始地址,j*(h_1 +1) + i就是列号乘矩阵的行数得到一列的个数后再加上要搜寻的元素的行号就可以搜索到元素。

例:对于数组A_[2][3]

按行优先的存储形式为:

$$
A_{[2][3]}=\begin{pmatrix} a_{[0][0]} & a_{[0][1]} & a_{[0][2]} \\ a_{[1][0]} & a_{[1][1]} & a_{[1][2]} \\ \end{pmatrix} \qquad \underbrace{a_{[0][0]} a_{[1][0]}} \quad \underbrace{a_{[0][1]} a_{[1][1]}} \quad \underbrace{a_{[0][2]} a_{[1][2]}}
$$

右边式子即先存储第一列后再存储第二列,最后存储第三列。

特殊矩阵的压缩存储

压缩存储:指为多个相同的元素分配一个空间,零元素不分配存储空间。

特殊矩阵:指具有许多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素分布有一定规律。

对称矩阵

1-1

如图所示,以i=j为主对角线,对于矩阵A任意元素都满足:

$$
a_{i,j}=a_{j,i}
$$

则称为对称矩阵。对于n阶对称矩阵,可以将n阶对称矩阵A存放再一维数组B[n(n+1)/2]中,比如只放下三角部分(i>j)及主对角的元素。

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

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

相关文章

Mysql快速列出来所有列信息

文章目录 需求描述实现思路1、如何查表信息2、如何取字段描述信息3、如何将列信息一行展示4、拼接最终结果 需求描述 如何将MySQL数据库中指定表【tb_order】的所有字段都展示出来&#xff0c;以备注中的中文名为列名。 实现思路 最终展示效果&#xff0c;即拼接出可执行执行…

LLM大模型实践10-聊天机器人

大型语言模型带给我们的激动人心的一种可能性是&#xff0c;我们可以通过它构建定制的聊天机器人 &#xff08;Chatbot&#xff09;&#xff0c;而且只需很少的工作量。在这一章节的探索中&#xff0c;我们将带你了解如何利用会话形式&#xff0c;与具 有个性化特性&#xff08…

用python实现烟花代码,完整代码拿走不谢

有时候用python实现一些有趣的代码&#xff0c;既有趣&#xff0c;又能提升知识 使用Python实现动态烟花代码 效果如下&#xff1a; 不废话&#xff0c;直接上代码&#xff1a; import pygame from random import randint, uniform, choice import mathvector pygame.math…

【Java项目】基于SpringBoot的【校园交友系统】

【Java项目】基于SpringBoot的【校园交友系统】 技术简介&#xff1a;系统软件架构选择B/S模式、SpringBoot框架、java技术和MySQL数据库等&#xff0c;总体功能模块运用自顶向下的分层思想。 系统简介&#xff1a;系统主要包括管理员和用户。 (a) 管理员的功能主要有首页、个人…

使用强化学习训练神经网络玩俄罗斯方块

一、说明 在 2024 年暑假假期期间&#xff0c;Tim学习并应用了Q-Learning &#xff08;一种强化学习形式&#xff09;来训练神经网络玩简化版的俄罗斯方块游戏。在本文中&#xff0c;我将详细介绍我是如何做到这一点的。我希望这对任何有兴趣将强化学习应用于新领域的人有所帮助…

大湾区经济网与澳门红刊杂志社签署战略合作

大湾区经济网澳门1月9日电&#xff08;王强&#xff09;2025年1月9日&#xff0c;在粤港澳大湾区建设稳步推进的时代背景下&#xff0c;大湾区经济网与澳门红刊杂志社成功签署了合作协议&#xff0c;标志着双方在媒体战略合作领域迈出了坚实的一步&#xff0c;将共同为粤港澳大…

力扣 二叉树的最大深度

树的遍历&#xff0c;dfs与bfs基础。 题目 注意这种题要看根节点的深度是0还是1。 深度优先遍历dfs&#xff0c;通过递归分别计算左子树和右子树的深度&#xff0c;然后返回左右子树深度的最大值再加上 1。递归会一直向下遍历树&#xff0c;直到达到叶子节点或空节点。在回溯…

Chapter 4.6:Coding the GPT model

4 Implementing a GPT model from Scratch To Generate Text 4.6 Coding the GPT model 本章从宏观视角介绍了 DummyGPTModel&#xff0c;使用占位符表示其构建模块&#xff0c;随后用真实的 TransformerBlock 和 LayerNorm 类替换占位符&#xff0c;组装出完整的 1.24 亿参数…

IDEA的Git界面(ALT+9)log选项不显示问题小记

IDEA的Git界面ALT9 log选项不显示问题 当前问题idea中log界面什么都不显示其他选项界面正常通过命令查询git日志正常 预期效果解决办法1. 检查 IDEA 的 Git 设置2. 刷新 Git Log (什么都没有大概率是刷新不了)3. 检查分支和日志是否存在4. 清理 IDEA 缓存 (我用这个成功解决)✅…

埃安UT正式入局纯电小车之争,海豚能否守擂成功

文/王俣祺 导语&#xff1a;2025年刚刚来临&#xff0c;第一波车市竞争就开打了&#xff0c;早在去年广州车展就吸睛无数的埃安 UT &#xff0c;日前正式开启预售&#xff0c;被称为比亚迪海豚的“最强对手”&#xff0c;主要是其价格和配置也确实全面对标了 比亚迪海豚。那么&…

java中的日期处理:只显示日期,不显示时间的两种处理方式

需要记录某个操作的操作时间,数据库中该字段为DATE类型; 插入数据的时候,使用数据库函数NOW()获取当前日期并插入: <insert id="batchInsertOrgTestersByProjectId">insert into project_org_testers(project_unid, org_tester_id,franchise_date) value…

Nginx代理同域名前后端分离项目的完整步骤

前后端分离项目&#xff0c;前后端共用一个域名。通过域名后的 url 前缀来区别前后端项目。 以 vue php 项目为例。直接上 server 模块的 nginx 配置。 server{ listen 80; #listen [::]:80 default_server ipv6onlyon; server_name demo.com;#二配置项目域名 index index.ht…

【大数据基础】大数据概述

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈大数据技术原理与应用 ⌋ ⌋ ⌋专栏系统介绍大数据的相关知识&#xff0c;分为大数据基础篇、大数据存储与管理篇、大数据处理与分析篇、大数据应用篇。内容包含大数据概述、大数据处理架构Hadoop、分布式文件系统HDFS、分布式数…

解决Qt打印中文字符出现乱码

在 Windows 平台上&#xff0c;默认的控制台编码可能不是 UTF-8&#xff0c;这可能会导致中文字符的显示问题。 下面是在 Qt 应用程序中设置中文字体&#xff0c;并确保控制台输出为 UTF-8 编码&#xff1a; 1. Qt 应用程序代码 在 Qt 中&#xff0c;我们可以使用 QApplic…

测试用例颗粒度说明

当我们在编写测试用例时&#xff0c;总是会遇到一个问题&#xff1a;如何确定测试用例的颗粒度&#xff1f;测试用例过于粗糙&#xff0c;可能无法全面覆盖系统的细节&#xff1b;而颗粒度过细&#xff0c;又会导致测试重复、冗余。掌握合适的颗粒度&#xff0c;不仅可以提高测…

【大模型(LLM)面试全解】深度解析 Layer Normalization 的原理、变体及实际应用

系列文章目录 大模型&#xff08;LLMs&#xff09;基础面 01-大模型&#xff08;LLM&#xff09;面试全解&#xff1a;主流架构、训练目标、涌现能力全面解析 02-【大模型&#xff08;LLM&#xff09;面试全解】深度解析 Layer Normalization 的原理、变体及实际应用 大模型&…

VoiceBox:基于文本引导的多语种通用大规模语音生成

VoiceBox:基于文本引导的多语种通用大规模语音生成 Voicebox: Text-Guided Multilingual Universal Speech Generation at Scale Voicebox是由MetaAI发布的一个类似大语言模型的生成式语音模型。它是一种基础模型,可以完成类似大语言模型的功能,可以针对语音数据进行编辑、…

ModuleNotFoundError: No module named ‘setuptools_rust‘ 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

基于 GEE 制作研究区影像覆盖图

目录 1 研究区影像覆盖图案例 2 基于 GEE 制作研究区影像覆盖图完整代码 3 运行结果 在写论文的时候&#xff0c;会有一小节内容专门介绍自己的研究区和使用的影像数据。为了让论文非常漂亮&#xff0c;有时候就需要做出研究区的地理位置图和所用卫星影像覆盖图&#xff0c;…

Mysql--基础篇--多表查询(JOIN,笛卡尔积)

在MySQL中&#xff0c;多表查询&#xff08;也称为联表查询或JOIN操作&#xff09;是数据库操作中非常常见的需求。通过多表查询&#xff0c;你可以从多个表中获取相关数据&#xff0c;并根据一定的条件将它们组合在一起。MySQL支持多种类型的JOIN操作&#xff0c;每种JOIN都有…