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

栈的定义

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

栈包括:

  • 栈顶(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,一经查实,立即删除!

相关文章

源代码编译安装X11及相关库、vim,配置vim(3)

一、vim插件安装 首先安装插件管理器Vundle ()。参照官网流程即可。vim的插件管理器有多个&#xff0c;只用Vundle就够了。然后~/.vimrc里写上要安装的插件: filetype offset rtp~/.vim/bundle/Vundle.vim call vundle#begin() Plugin VundleVim/Vundle.vim Plugin powerline…

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…

[IoT]物联网(IoT)网络的安全性

确保物联网(IoT)网络的安全性是至关重要的&#xff0c;以下是一些关键措施来保障网络的安全性&#xff1a; 1. 数据加密 传输加密&#xff1a;使用TLS/SSL协议对数据传输进行加密&#xff0c;确保数据在传输过程中不被窃听或篡改。存储加密&#xff1a;对存储在设备或服务器上…

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

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

window.print()预览时表格显示不全

问题描述&#xff1a;使用element的table组件&#xff0c;表格列宽为自适应&#xff0c;但使用window.print()方法预览的页面会直接按预览宽度截取表格内容进行展示&#xff0c;造成表格可能的显示不全问题 解决方法&#xff1a;添加如下样式 media print {::v-deep {// 表头…

JetBrains IDEs和Visual Studio Code的对比

JetBrains IDEs和Visual Studio Code的对比 JetBrains IDEs是捷克JetBrains公司开发的一系列集成开发环境(IDE)。以下是具体介绍:IntelliJ IDEA是JetBrains 公司的一款产品 主要产品 IntelliJ IDEA:一款功能强大且广泛应用的Java集成开发环境,有开源免费的社区版和商业收…

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

一、说明 在 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;直到达到叶子节点或空节点。在回溯…

Mac 安装psycopg2出错:Error:pg_config executable not found的解决

在mac 上执行pip3 install psycopg2-binary出现如下错误&#xff1a; Error:pg_config executable not found然后我又到终端里执行 brew install postgresql16 显示 Warning: You are using macOS 15. We do not provide support for this pre-release version. It is expe…

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;主要是其价格和配置也确实全面对标了 比亚迪海豚。那么&…

从configure.ac到构建环境:解析Mellanox OFED内核模块构建脚本

在软件开发过程中,特别是在处理复杂的内核模块如Mellanox OFED(OpenFabrics Enterprise Distribution)时,构建一个可移植且高效的构建系统至关重要。Autoconf和Automake等工具在此过程中扮演着核心角色。本文将深入解析一个用于准备Mellanox OFED内核模块构建环境的Autocon…

从企业级 RAG 到 AI Assistant , Elasticsearch AI 搜索技术实践

文章目录 01 AI 搜索落地的挑战02 Elasticsearch 向量性能 5 倍提升03 Elasticsearch 企业版 AI 能力全面解读04 阿里云 Elasticsearch 将准确率提升至 95%05 AI Assistant 集成通义千问大模型实现 AI Ops01 AI 搜索落地的挑战 在过去一年中,基座大模型技术的快速迭代推动了 …

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

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

c# 常见的几种取整场景

软件取整&#xff0c;通常指的是在计算机软件中对数值进行取整操作&#xff0c;即将一个浮点数或小数转换为整数&#xff0c;同时确定如何处理小数部分。取整操作在编程和数学计算中非常常见&#xff0c;不同的取整方法适用于不同的场景。 常见的取整方法 向零取整&#xff08…

修仙模拟器代码分析

修仙模拟器代码分析 1. 概述 这是一个基于Python开发的修仙模拟器游戏&#xff0c;通过面向对象编程实现了一个具有丰富功能的文字冒险游戏系统。游戏模拟了修仙世界中的各种元素&#xff0c;包括修炼境界、灵根、门派、社交关系等多个方面。 2. 核心类结构 2.1 数据类&…