【数据结构】栈和队列的模拟实现

前言:前面我们学习了单链表并且模拟了它的实现,今天我们来进一步学习,来学习栈和队列吧!一起加油各位,后面的路只会越来越难走需要我们一步一个脚印!

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:数据结构 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


什么是栈

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则(如下图所示)。
在这里插入图片描述
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。


如何实现栈

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。所以我们今天通过使用动态数组来模拟实现栈。


栈的基本结构

我们已经确定了通过动态数组来模拟实现栈,那么我们要实现哪些功能呢?

  1. 栈的初始化
  2. 栈的销毁
  3. 入栈
  4. 出栈
  5. 获取栈顶元素
  6. 检测栈是否为空
  7. 获取栈中有效元素个数

首先我们需要考虑如何记录栈顶元素的位置,我们用top来记录,但是top是不是应该是0还是什么数据呢?如果top指向的是0那么我top是0的时候栈中到底是一个元素还是没有元素,这是十分具有歧义的,所以我们这里用负一来表示top当有元素进入时就让top加加即可(如下图)。
在这里插入图片描述

具体结构如下:

typedef struct Stack
{STDataType* a;int top; //标识顶部int capacity;
}ST;

栈的基本功能

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* a;int top; //标识顶部int capacity;
}ST;//栈的初始化
void STInit(ST* pst);//栈的销毁
void STDestroy(ST* pst);//栈顶插入删除//入栈
void STPush(ST* pst, STDataType x);//出栈
void STPop(ST* pst);// 获取栈顶元素
STDataType STTop(ST* pst);// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst);// 获取栈中有效元素个数
int STSize(ST* pst);

栈的初始化

思路导读:前面我们讲到过用top来记录栈顶的位置,之所以让它从负一开始就是为了方便我们访问栈顶数据,也是为了防止歧义,如下图所示。我们初始化也只需要将开辟的空间指向空指针,空间容量为0即可。在这里插入图片描述代码实现:

void STInit(ST* pst)//栈的初始化
{assert(pst);pst->top = -1;//表示top指向栈顶元素的下一个位置pst->a = NULL;pst->capacity = 0;
}

栈的销毁

思路导读:我们只需要找到a开辟的空间将它释放掉并将他置为空指针即可,然后其它值Top处理为-1,空间容量置为0即可
代码实现

void STDestroy(ST* pst)//栈的销毁
{assert(pst);free(pst->a);pst->a = NULL;pst->top = -1;pst->capacity = 0;
}

入栈

思路导读:我们入栈首先就是得开辟一块空间存放即将要入栈的值,然后将这块空间传给a即可,然后再把栈顶元素赋值,即可完成入栈。(如果这块地方有不懂的友友可以去看我之前的博客有详细讲解动态顺序表)
代码实现:

void STPush(ST* pst, STDataType x)//入栈
{assert(pst);if (pst->top + 1 == pst->capacity)//判断是否有空间{int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);//防止开辟失败俩个空间都丢失if (tmp == NULL){perror("realloc fail");return;}pst->a = tmp;pst->capacity = newcapacity;}pst->top++;//top加一pst->a[pst->top] = x;//栈顶赋值
}

出栈

思路导读:关于出栈,我们要知道他是先进后出,在前面的图中我们都有详细的讲解,出去一个元素我们让top往下走一个即可。
代码实现

void STPop(ST* pst)//出栈
{assert(pst);//判断是不是空assert(pst->top >= 0);//判断有没有元素pst->top--;//top减减即可
}

获取栈顶元素

思路导读:首先我们还是得判断传来的数据是否为空,里面是否有元素,然后我们直接访问栈顶元素即可。

STDataType STTop(ST* pst)// 获取栈顶元素
{assert(pst);assert(pst->top >= 0);return pst->a[pst->top];
}

函数测试与效果图:

void Test2()
{ST sl;STInit(&sl);STPush(&sl, 0);printf("%d ", STTop(&sl));STPush(&sl, 9);printf("%d ", STTop(&sl));STPush(&sl, 2);printf("%d ", STTop(&sl));STPush(&sl, 2);printf("%d ", STTop(&sl));printf("上面是入栈后\n");printf("下面是出栈后栈顶元素\n");STPop(&sl);printf("%d ", STTop(&sl));STPop(&sl);printf("%d ", STTop(&sl));STPop(&sl);printf("%d ", STTop(&sl));STPop(&sl);
}

在这里插入图片描述


检测栈是否为空

思路导读:首先判断传来的是否有这个结构体,我们只需要判断top是否是负一如果是说明这个为真则返回0,不是则返回非0的结果
代码实现:

bool STEmpty(ST* pst)// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
{assert(pst);return pst->top == -1;//如果是-1则是空返回true,如果不是返回false
}

获取栈中有效元素个数

思路导读:我们只需要将top加一即为元素个数,因为top指向的栈顶元素。

int STSize(ST* pst)// 获取栈中有效元素个数
{assert(pst);return pst->top + 1;
}

整体函数测试

void Test3()
{ST sl;STInit(&sl);STPush(&sl, 1);STPush(&sl, 3);STPush(&sl, 0);STPush(&sl, 9);STPush(&sl, 2);STPush(&sl, 8);printf("入栈后元素的个数:%d\n", STSize(&sl));printf("下面是入栈元素\n");while (!STEmpty(&sl)){	printf("%d ", STTop(&sl));STPop(&sl);//出栈}STDestroy(&sl);
}
int main()
{//Test1();//Test2();Test3();return 0;
}

具体效果图:
在这里插入图片描述
到这里关于栈的实现我们都已经讲完了,接下来我们开始队列的学习!


什么是队列

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出(我们可以通俗的理解成一头进一头出,先进去的先出去)。
入队列:进行插入操作的一端称为队尾。
出队列:进行删除操作的一端称为队头。
具体如下图
在这里插入图片描述


如何实现队列

由于队列的特殊性,只能一头进一头出,且先进的先出,在实现它的时候我们只需要考虑尾插和头删即可,在尾插和头删上效率较高的应该就是单链表了,但我们应当用一个tail的尾指针来记录,防止尾增的时候再次遍历链表。故我们通过单链表来实现队列。


队列的基本功能与结构

关于下面为什么会在用一个结构体来记录头指针和尾指针,是因为如果我们补定义一个结构体来记录,我们就会在入队列和出队列的时候,需要传一个二级指针来修改队列中的值,为了防止这样的麻烦我们就直接定义一个结构体来记录即可。


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);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* pq);// 获取队列中有效元素个数
int QueueSize(Queue* pq);

队列的初始化

思路导读:关于队列的初始化,我们只需要将头指针和尾指针置为空即可,然后将里面的元素置为0。
代码实现:

void QueueInit(Queue* pq)//队列的初始化
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}

队尾入队列

思路导读:首先我们得分两种情况:一种是该队列为空第一次入队列,一种是该队列不为空不是第一次入队列,但无论如何都要开辟一块空间存放新开辟的值。当为第一次入队列的时候,我们只需要将头节点和尾节点全部指向这块新开辟的空间即可。当不是第一次入的时候,我们需要找到尾结点将它指向新开辟的这块空间即可完成入队列,且每当进入一个元素我们让size加加即可知道队列中的元素个数。如下图所示:
在这里插入图片描述
代码实现:

void QueuePush(Queue* pq, QDataType x)//队尾入队列
{assert(pq);QNode* newnode = 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);Queue* tmp = pq->phead;//记录头指针,方便释放空间pq->phead = pq->phead->next;//让头指针指向下一个元素free(tmp);//释放头指针if (pq->phead == NULL)//如果队列中只有一个元素,将尾指针也置为空指针{pq->ptail = NULL;}pq->size--;
}

获取队列头部元素

思路导读:我们只需要找到头节点指向的val的值将它返回即可。
代码实现

QDataType QueueFront(Queue* pq)// 获取队列头部元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->phead->val;
}

获取队列队尾元素

思路导读:我们在这之前一直记录了一个尾部节点,因此获取队尾元素的时候我们不需要重新在遍历队列,只需要返回尾节点的值即可。
代码实现

QDataType QueueBack(Queue* pq)// 获取队列队尾元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->ptail->val;
}

检测队列是否为空

思路导读:我们只需要判断之前存储数据个数的size是否为空即可。
代码实现

bool QueueEmpty(Queue* pq)// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
{assert(pq);return pq->size == 0;
}

获取队列中有效元素个数

思路导读:我们直接返回记录元素个数的size即可获取队列中有效元素的个数。

int QueueSize(Queue* pq)// 获取队列中有效元素个数
{assert(pq);return pq->size;
}

销毁队列

思路导读:我们要把队列中的每一个结点都释放掉,我们需要一个指针dl记录头指针原本的位置,让头指针往后走遍历队列依次释放经过的位置,具体如下图
在这里插入图片描述
代码实现:

void QueueDestory(Queue* pq)//销毁队列
{assert(pq);Queue* dl = NULL;while (pq->phead){dl = pq->phead;pq->phead = pq->phead->next;free(dl);}pq->ptail = NULL;pq->size = 0;pq->phead = NULL;
}

整体函数测试

void Test2()
{Queue s;QueueInit(&s);QueuePush(&s, 1);QueuePush(&s, 2);QueuePush(&s, 3);QueuePush(&s, 4);QueuePush(&s, 5);printf("%d ", QueueFront(&s));		printf("%d ", QueueBack(&s));printf("%d\n", QueueSize(&s));QueuePop(&s);QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));if (!QueueEmpty(&s)){QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));}printf("%d\n", QueueSize(&s));QueueDestory(&s);
}int main()
{//Test1();Test2();return 0;
}

运行结果:在这里插入图片描述


整体代码

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.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);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* pq);// 获取队列中有效元素个数
int QueueSize(Queue* pq);
#include"Queen.h"void QueueInit(Queue* pq)//队列的初始化
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}void QueuePush(Queue* pq, QDataType x)//队尾入队列
{assert(pq);QNode* newnode = 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);Queue* tmp = pq->phead;//记录头指针,方便释放空间pq->phead = pq->phead->next;//让头指针指向下一个元素free(tmp);//释放头指针if (pq->phead == NULL)//如果队列中只有一个元素,将尾指针也置为空指针{pq->ptail = NULL;}pq->size--;
}QDataType QueueFront(Queue* pq)// 获取队列头部元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->phead->val;
}QDataType QueueBack(Queue* pq)// 获取队列队尾元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->ptail->val;
}bool QueueEmpty(Queue* pq)// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
{assert(pq);return pq->size == 0;
}int QueueSize(Queue* pq)// 获取队列中有效元素个数
{assert(pq);return pq->size;
}void QueueDestory(Queue* pq)//销毁队列
{assert(pq);Queue* dl = NULL;while (pq->phead){dl = pq->phead;pq->phead = pq->phead->next;free(dl);}pq->ptail = NULL;pq->size = 0;pq->phead = NULL;
}#define _CRT_SECURE_NO_WARNINGS 1
#include"Queen.h"void Test1()
{Queue sl;QueueInit(&sl);QueuePush(&sl, 1);//printf("%d\n", QueueFront(&sl));QueuePush(&sl, 9);//printf("%d\n",QueueFront(&sl));//QueuePop(&sl);QueuePush(&sl, 2);QueuePush(&sl, 10);printf("%d\n", QueueFront(&sl));printf("%d\n",QueueBack(&sl));
}void Test2()
{Queue s;QueueInit(&s);QueuePush(&s, 1);QueuePush(&s, 2);QueuePush(&s, 3);QueuePush(&s, 4);QueuePush(&s, 5);printf("%d ", QueueFront(&s));		printf("%d ", QueueBack(&s));printf("%d\n", QueueSize(&s));QueuePop(&s);QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));if (!QueueEmpty(&s)){QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));}printf("%d\n", QueueSize(&s));QueueDestory(&s);
}int main()
{//Test1();Test2();return 0;
}

结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


🫵🫵🫵 祝各位接下来好运连连 💞

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

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

相关文章

Golang基础-面向对象篇

文章目录 struct结构体类的表示与封装类的继承多态的基本要素与实现interface空接口反射变量的内置pairreflect包解析Struct TagStruct Tag在json中的应用 struct结构体 在Go语言中&#xff0c;可以使用type 关键字来创建自定义类型&#xff0c;这对于提高代码的可读性和可维护…

win11,引导项管理

1&#xff0c;打开cmd,输入msconfig 2,进入引导选项卡 3&#xff0c;删除不需要的引导项

ETL-使用kettle批量复制sqlserver数据到mysql数据库

文章标题 1、安装sqlserver数据库2、下载kettle3、业务分析4、详细流程&#xff08;1&#xff09;转换1&#xff1a;获取sqlserver所有表格名字&#xff0c;将记录复制到结果&#xff08;2&#xff09;转换2&#xff1a;从结果设置变量&#xff08;3&#xff09;转换3&#xff…

【Linux】:共享内存

共享内存 一.原理二.创建共享内存1.shmget2.写一个共享内存代码 三.进行通信1.各种接口2.各接口使用代码3.一次简单的通信四.共享内存的特点 一.原理 直接原理 共享内存顾名思义就是共同使用的一块空间。 很明显操作系统需要对这块内存进行管理&#xff0c;那么就避免不了先描…

Servlet执行流程Servlet 生命周期

Servlet 生命周期 对象的生命周期指一个对象从被创建到被销毁的整个过程 import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; WebServlet(urlPatterns "/demo",loadOnStartup 10) public class ServletDemo imple…

华为ac+fit漫游配置案例

Ap漫游配置: 其它配置上面一样,ap管理dhcp和业务dhcp全在汇聚交换机 R1: interface GigabitEthernet0/0/0 ip address 11.1.1.1 255.255.255.0 ip route-static 12.2.2.0 255.255.255.0 11.1.1.2 ip route-static 192.168.0.0 255.255.0.0 11.1.1.2 lsw1: vlan batch 100 200…

存储日志数据并满足安全要求

日志数据是包含有关网络中发生的事件的记录的重要信息&#xff0c;日志数据对于监控网络和了解网络活动、用户操作及其动机至关重要。 由于网络中的每个设备都会生成日志&#xff0c;因此收集的数据量巨大&#xff0c;管理和存储所有这些数据成为一项挑战&#xff0c;日志归档…

Windows系统如何安装与使用TortoiseSVN客户端,并实现在公网访问本地SVN服务器

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

2023年以就业为目的学习Java还有必要吗?

文章目录 1活力四射的 Java2从零开始学会 Java3talk is cheap, show me the code4结语写作末尾 现在学 Java 找工作还有优势吗&#xff1f; 在某乎上可以看到大家对此问题的热议&#xff1a;“2023年以就业为目的学习Java还有必要吗&#xff1f;” 。有人说市场饱和&#xff0c…

关于lenra你需要了解的

monorepo&#xff1a;项目代码管理方式&#xff0c;单个仓库中管理多个项目是一种设计思想 lenra&#xff1a;是一种工具&#xff0c;对于使用npm和git管理多软件包代码仓库的工作流程进行优化 使用这些工具的优点&#xff1a; 公共依赖只要安装一次&#xff0c;Monorepo 中…

C/C++内存管理(1):C/C++内存分布,C++内存管理方式

一、C/C内存分布 1.1 1.2 二、C内存管理方式 C可以通过操作符new和delete进行动态内存管理。 2.1 new和delete操作内置类型 int main() {int* p1 new int;// 注意区分p2和p3int* p2 new int(10);// 对*p2进行初始化 10int* p3 new int[10];// p3 指向一块40个字节的int类…

网络运维与网络安全 学习笔记2023.11.21

网络运维与网络安全 学习笔记 第二十二天 今日目标 端口隔离原理与配置、路由原理和配置、配置多路由器静态路由 配置默认路由、VLAN间通信之路由器 端口隔离原理与配置 端口隔离概述 实现报文之间的2层隔离&#xff0c;除了使用VLAN技术以后&#xff0c;还可以使用端口隔…

Linux socket编程(5):三次握手和四次挥手分析和SIGPIPE信号的处理

在我之前写的Wireshark抓包&#xff1a;理解TCP三次握手和四次挥手过程中&#xff0c;通过抓包分析了TCP传输的三次握手和四次挥手的过程。在这一节中&#xff0c;将分析在Linux中的三次握手和四次挥手的状态和过程&#xff0c;另外还有一个在我们编程过程中值得注意的SIGPIPE信…

gitBash中如何使用Linux中的tree命令

文章目录 在gitBash中安装tree的目的如何安装安装完成,就可以直接完美适配Linux系统了在gitBash中安装tree的目的 如下图,powershell虽然可以看做是window下的Linux系统,但是根本就不适配很多Linux中的命令 如何安装 tree.exe安装网址 下载 tree 命令的 二进制包,安装 tr…

OAK相机通过振动测试!

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手君。 当…

【python】Python生成GIF动图,多张图片转动态图,pillow

pip install pillow 示例代码&#xff1a; from PIL import Image, ImageSequence# 图片文件名列表 image_files [car.png, detected_map.png, base64_image_out.png]# 打开图片 images [Image.open(filename) for filename in image_files]# 设置输出 GIF 文件名 output_g…

java算法学习索引之数组矩阵问题

一 将正方形矩阵顺时针转动90 给定一个NN的矩阵matrix&#xff0c;把这个矩阵调整成顺时针转动90后的形式。 顺时针转动90后为&#xff1a; 【要求】额外空间复杂度为O&#xff08;1&#xff09;。 public void rotate(int[][] matrix) {int tR 0; // 左上角行坐标int tC 0;…

香港科技大学广州|机器人与自主系统学域博士招生宣讲会—同济大学专场!!!(暨全额奖学金政策)

在机器人和自主系统领域实现全球卓越—机器人与自主系统学域 硬核科研实验室&#xff0c;浓厚创新产学研氛围&#xff01; 教授亲临现场&#xff0c;面对面答疑解惑助攻申请&#xff01; 一经录取&#xff0c;享全额奖学金1.5万/月&#xff01; &#x1f559;时间&#xff1a;…

STM32 Flash

FLASH简介 Flash是常用的用于存储数据的半导体器件&#xff0c;它具有容量大&#xff0c;可重复擦写&#xff0c;按“扇区/块”擦除、掉电后数据可继续保存的特性。 常见的FLASH主要有NOR FLASH和NAND FLASH两种类型。NOR和NAND是两种数字门电路&#xff0c;可以简单地认为FL…

多线程的概念

点击链接返回标题-> 什么是进程&#xff1f; 进程&#xff08;Process&#xff09;&#xff0c;是程序的基本执行实体。 在早期面向进程设计的计算机结构中&#xff0c;进程是程序的基本执行实体&#xff1b; 在当代面向线程设计的计算机结构中&#xff0c;进程是线程的容器…