【数据结构】解密链表之旅(单链表篇)

前言

哈喽大家好,我是野生的编程萌新,首先感谢大家的观看。数据结构的学习者大多有这样的想法:数据结构很重要,一定要学好,但数据结构比较抽象,有些算法理解起来很困难,学的很累。我想让大家知道的是:数据结构非常有趣,很多算法是智慧的结晶,我希望大家在学习数据结构的过程是一种愉悦的心情感受。因此我开创了《数据结构》专栏,在这里我将把数据结构内容以有趣易懂的方式展现给大家。

 1.线性表链式存储结构定义

在上一篇博客中我们提到了线性表有两种存储方式,一种是顺序存储,一种是链式存储。线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。这就意味着,这些数据可以存在内存未被占用的任意位置。

 在之前的顺序结构中,每个数据元素只需要存储数据元素信息就可以了。现在链式结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址。链式存储结构相比于顺序存储结构的优势在于插入和删除操作的高效性。由于链式存储结构中的元素通过指针连接,所以在插入和删除元素时,只需改变指针的指向,不需要移动其他元素,因此效率较高。而顺序存储结构需要移动元素位置,效率较低。线性表的链式存储结构是通过节点之间的指针来实现的,每个节点包含两个部分:数据域(存储数据元素信息的域)和指针域(存储直接后继位置的域)。n个节点链接成一个链表,即为线性表的链式存储结构(链表)。

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表的存储结构就与火车车厢相似,淡季时车次的车厢会有所减少,相对的在旺季时车次的车厢会有所增加。这需要将火车的车厢去掉或者加上,不会影响其他的车厢,每节车厢都是独立的。就像下面这张图:

每节车厢都是独立的,每节车厢都有自己的车门。假设每节车厢都是锁住的状态,他们都需要不同的钥匙来解锁且每次只能携带一把钥匙,该如何从车头走向车尾呢?最简单的方法就是:在每节车厢存放下一节车厢的钥匙。那么在链表这个“火车”中,每节“车厢”的情况是什么样子的呢?

 与顺序表不同的是,链表每节“车厢”都是独立申请下来的空间,我们称为“节点/结点”。对于线性表来说,总得有头有尾啊,链表也不能例外。我们把链表中的第一个节点存储的位置叫做头指针,那么整个链表的存取就必须是从头指针开始运行了。之后的每一个节点,其实就是上一个后继指针指向的位置。既然如此,那最后一个节点的指针指向哪里?什么!最后一个?当然就意味着后继不存在了,所以我们规定最后一个节点的指针为空。有时候,我们为了更加方便地对链表进行操作,会在单链表的第一个节点之前附设一个节点,我们成为头节点。下图为在上图基础上加一个头节点:

 头指针和头结点的异同点

  1. 头指针和头节点都是链表的概念,用于表示和操作链表的入口。
  2. 头指针是一个指针变量,存储的是第一个节点的地址;头节点是一个特殊节点,位于链表的第一个位置,不包含有用的数据。
  3. 头指针用于遍历链表中的所有节点;头节点用于简化对链表的操作。
  4. 头指针在链表中的位置是可变的,可以随着节点的插入或删除而改变;头节点在链表中位置固定,一般不会发生变化。

链表的种类非常多样,我们主要根据是否有头节点、单向或双向、是否循环将链表分为8类:

1.带头或者不带头

2.单向或者双向:

 3.是否循环:

虽然有这么多的链表结构,其实我们最常用的还是两种结构:不带头的单向链表和双向循环链表。我们这一篇就主要围绕单链表来介绍。 

2.单链表各个功能的实现

单链表是一种最简单的链表数据结构,它由一系列节点组成,每个节点包含两部分:数据域和指针域。数据域用于存储节点的数据,指针域用于指向下一个节点。单链表的特点是节点之间只有一个指针连接,每个节点只能访问下一个节点,不能访问前一个节点。链表的头节点是第一个节点,尾节点是最后一个节点,尾节点的指针域通常指向一个空地址(NULL)。用C语言来描述单链表的结构指针:

typedef int SLNDataType;
typedef struct SListNode 
{SLNDataType val;struct SListNode* next;
}SLNode;

在这里我们主要详细介绍单链表的插入删除等操作。在单链表中插入有尾插、头插、任意位置插入等操作,每次插入都需要申请空间,每次申请空间的操作都相同,我们干脆写一个函数来实现申请空间,这样能使我们的操作更加方便。

SLNode* CreateNode(SLNDataType* x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->val = x;newnode->next = NULL;return newnode;
}

2.1单链表的尾插和尾删

单链表的尾插操作步骤:

  1. 创建一个新的节点,设置其数据域为要插入的值,指针域为空。
  2. 检查链表是否为空。若为空,则将新节点作为链表的第一个节点。
  3. 若链表不为空,需要找到链表的最后一个节点。从链表的头节点开始遍历,直到遍历到最后一个节点(即指针域为空的节点)。
  4. 将最后一个节点的指针域指向新节点,将新节点插入到链表中。

我们需要使用单链表指针变量来创建头指针,所以我们在传参时要使用二级指针。我们来实现一下单链表的尾插操作:

void SLPushBack(SLNode** phead, SLNDataType x)
{assert(phead);SLNode* newnode=CreateNode(x);if (*phead == NULL){*phead = newnode;}else{SLNode* tail = *phead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}

单链表的尾删具体操作步骤为:

  1. 判断链表是否为空。如果链表为空,则无法进行尾删操作,直接返回。
  2. 如果链表只有一个结点,则将链表的头指针置为空,删除这个结点即可。
  3. 如果链表有多个结点,则需要遍历到倒数第二个结点,即指针指向要删除结点的前一个结点。
  4. 将前一个结点的 next 指针指向 NULL,断开要删除结点和链表的连接。
  5. 释放要删除结点的内存空间。

我们来实现一下单链表尾删操作:

void SLPopBack(SLNode** phead)
{assert(phead);assert(*phead);if ((*phead)->next == NULL){free(*phead);*phead = NULL;}else{SLNode* prev = NULL;SLNode* tail = *phead;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}

2.2单链表的头插和头删

单链表的头插具体操作步骤为:

  1. 首先,创建一个新的节点,并将要插入的元素值赋给新节点的数据域。
  2. 为了将新节点插入到链表中,需要将新节点的next指针指向链表的第一个节点,即原本的第一个节点。
  3. 然后,将链表的头节点指向新节点,即将新节点设为链表的第一个节点。

我们来实现一下这个操作:

void SLPushFront(SLNode** phead, SLNDataType x)
{assert(phead);SLNode* newnode = CreateNode(x);newnode->next = *phead;*phead = newnode;
}

单链表的头删具体操作步骤为:

  1. 检查链表是否为空。如果链表为空,无法进行头删操作,直接返回。
  2. 创建一个临时变量tmp,将其指向链表的第一个节点。
  3. 将链表的头节点指针指向第一个节点的下一个节点,即tmp->next。
  4. 释放temp指向的节点。

我们来实现一下这个操作:

void SLPopFront(SLNode** phead)
{assert(phead);assert(*phead);SLNode* tmp = (*phead)->next;free(*phead);*phead = tmp;
}

2.3单链表的任意位置插入和任意位置删除

单链表的任意位置插入和顺序表中的任意插入有所不同,在单链表中我们需要先编写一个链表数据元素的查找函数,然后输入一个节点值,接着返回相对应的节点,然后进行插入删除操作。链表数据元素查找函数实现如下:

SLNode* SLFind(SLNode* phead, SLNDataType x)
{SLNode* cur = phead;while (cur != NULL){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}

我们创建一个单链表节点指针变量接收查找函数的返回信息。单链表的任意位置插入操作的具体步骤为:

  1. 首先,判断要插入的位置是否合法,即判断插入的位置是否超出了链表的范围。如果超出了范围,则不进行插入操作。

  2. 然后我们还要判断一下插入位置是否是在头节点,如果插入的位置是头节点,那我们直接调用头插的函数就行,反之,创建一个新结点,将要插入的数据放入在其中。

  3. 遍历链表,找到插入位置的前一个节点,即要在其后面插入新节点。

  4. 将新节点的指针域指向插入位置的前一个节点原来指向的节点。

  5. 将插入位置的前一个节点的指针域指向新节点。

我们来实现一下这个操作:

void SLInsert(SLNode** phead, SLNode* pos, SLNDataType x)
{assert(phead);assert(pos);if (*phead == pos){SLPushFront(phead, x);}else{SLNode* prev = *phead;while (prev->next != pos){prev = prev->next;}SLNode* newnode = CreateNode(x);prev->next = newnode;newnode->next = pos;}
}

当然还是有人喜欢任意位置之后插入,我们也来实现一下:

void SLInsertAfter(SLNode* pos, SLNDataType x)
{assert(pos);SLNode* newnode = CreateNode(x);newnode->next = pos->next;pos->next = newnode;
}

讲完任意位置插入了,我们现在来整任意位置删除,任意位置删除的基本操作步骤为:

  1. 首先判断单链表是否为空,若为空则无法进行删除操作,直接返回。
  2. 如果要删除的位置是头节点,则将头节点指向下一个节点,并释放原来的头节点(头删操作)。
  3. 如果要删除的位置不是头节点,需找到要删除节点的前一个节点。遍历单链表,找到要删除节点的前一个节点。可以使用两个指针prev和pos,prev指向要删除节点的前一个节点,pos指向要删除节点。当pos指向要删除节点时,prev指向的即为要删除节点的前一个节点。
  4. 判断要删除节点是否存在,如果pos为空,则说明要删除的节点不存在,直接返回。
  5. 将p的next指针指向q的next指针,即跳过要删除的节点。
  6. 释放要删除节点q的内存空间。

我们来实现一下这个操作:

void SLErase(SLNode** phead, SLNode* pos)
{assert(phead);if (*phead == pos){SLPopFront(phead);}else{SLNode* prev = *phead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}

 到了这里应该知道我要干什么了吧,实现任意位置之后的删除:

void SLEraseAfter(SLNode* pos)
{assert(pos);assert(pos->next);SLNode* tmp = pos->next;pos->next = tmp->next;free(tmp);tmp = NULL;
}

2.4单链表的销毁

单链表的销毁指的是将整个链表都删除,回收其占用的内存空间。单链表的销毁的具体步骤为:

  1. 首先需要定义一个指针变量,用于遍历链表。假设此变量为cur,并将其初始化为链表的头节点。
  2. 创建一个临时指针变量,用于保存当前节点的下一个节点。假设此变量为next。
  3. 使用循环遍历链表,直到cur指针变量为空。
  4. 在循环中,将next指针变量指向cur指针变量的下一个节点。
  5. 释放cur指针变量指向的节点的内存空间。可以使用free()函数来实现内存的释放。
  6. 将cur指针变量指向next指针变量,即将cur指针变量移动到下一个节点上。
  7. 重复步骤3-6,直到遍历完整个链表,即cur指针变量指向空。

当cur指针变量为空时,说明链表的所有节点都已经被删除,此时整个链表就被销毁了。我们来实现一下这个操作:

void SLDestory(SLNode** phead)
{assert(phead);SLNode* cur = *phead;while (cur){SLNode* next = cur->next;free(cur);cur = next;}*phead = NULL;
}

在进行链表的销毁时要注意:我们需要确保链表中的每个节点都被释放,以避免内存泄漏。同时,需要注意释放节点内存空间前,需要先保存下一个节点的指针,否则在释放当前节点后,就无法访问到下一个节点了。

3.多文件实现单链表

SList.h文件:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLNDataType;
typedef struct SListNode 
{SLNDataType val;struct SListNode* next;
}SLNode;
void SLprint(SLNode* phead);//链表打印
void SLPushBack(SLNode** phead, SLNDataType x);//链表的尾插
void SLPushFront(SLNode** phead, SLNDataType x);//链表的头插
void SLPopBack(SLNode** phead);//链表的尾删
void SLPopFront(SLNode** phead);//链表的头删
SLNode* SLFind(SLNode* phead, SLNDataType x);//链表的查找
void SLInsert(SLNode** phead, SLNode* pos, SLNDataType x);//链表的插入
void SLErase(SLNode** phead, SLNode* pos);//链表的删除
void SLInsertAfter(SLNode* pos, SLNDataType x);//后面插入
void SLEraseAfter(SLNode* pos);//后面删除
void SLDestory(SLNode** phead);//链表的销毁

SList.c文件:

#include"SList.h"
void SLprint(SLNode* phead)
{SLNode* cur = phead;while (cur!= NULL){printf("%d->", cur->val);cur = cur->next;}printf("NULL\n");
}
SLNode* CreateNode(SLNDataType* x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->val = x;newnode->next = NULL;return newnode;
}
void SLPushBack(SLNode** phead, SLNDataType x)
{assert(phead);SLNode* newnode=CreateNode(x);if (*phead == NULL){*phead = newnode;}else{SLNode* tail = *phead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}
void SLPushFront(SLNode** phead, SLNDataType x)
{assert(phead);SLNode* newnode = CreateNode(x);newnode->next = *phead;*phead = newnode;
}
void SLPopBack(SLNode** phead)
{assert(phead);assert(*phead);if ((*phead)->next == NULL){free(*phead);*phead = NULL;}else{SLNode* prev = NULL;SLNode* tail = *phead;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}
void SLPopFront(SLNode** phead)
{assert(phead);assert(*phead);SLNode* tmp = (*phead)->next;free(*phead);*phead = tmp;
}
SLNode* SLFind(SLNode* phead, SLNDataType x)
{SLNode* cur = phead;while (cur != NULL){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}
void SLInsert(SLNode** phead, SLNode* pos, SLNDataType x)
{assert(phead);assert(pos);if (*phead == pos){SLPushFront(phead, x);}else{SLNode* prev = *phead;while (prev->next != pos){prev = prev->next;}SLNode* newnode = CreateNode(x);prev->next = newnode;newnode->next = pos;}
}
void SLErase(SLNode** phead, SLNode* pos)
{assert(phead);if (*phead == pos){SLPopFront(phead);}else{SLNode* prev = *phead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
void SLInsertAfter(SLNode* pos, SLNDataType x)
{assert(pos);SLNode* newnode = CreateNode(x);newnode->next = pos->next;pos->next = newnode;
}
void SLEraseAfter(SLNode* pos)
{assert(pos);assert(pos->next);SLNode* tmp = pos->next;pos->next = tmp->next;free(tmp);tmp = NULL;
}
void SLDestory(SLNode** phead)
{assert(phead);SLNode* cur = *phead;while (cur){SLNode* next = cur->next;free(cur);cur = next;}*phead = NULL;
}

test.c文件(在这里测试函数功能):

#include"SList.h"
void test()
{SLNode* plist = NULL;//测试尾插SLPushBack(&plist, 4);SLPushBack(&plist, 5);SLPushBack(&plist, 6);SLPushBack(&plist, 7);SLprint(plist);//测试头插SLPushFront(&plist, 3);SLPushFront(&plist, 2);SLPushFront(&plist, 1);SLPushFront(&plist, 0);SLprint(plist);//测试尾删SLPopBack(&plist);SLprint(plist);//测试头删SLPopFront(&plist);SLprint(plist);//测试任意插入SLNode* pos = SLFind(plist, 3);SLInsert(&plist, pos, 30);SLprint(plist);//测试任意删除pos = SLFind(plist, 30);SLErase(&plist, pos);SLprint(plist);
}
int main()
{test();return 0;
}

我们看看对各个函数测试结果:

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

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

相关文章

自制无感无刷电机驱动板

自制无感无刷电机驱动板 分别测试了基于C251的STC32G单片机、Arduino AVR的ATmega328PB、以及ARM的ST32F103单片机。 &#x1f9f2;测试转动效果 ✒目前市面上开源的有关无刷电机的项目数不胜数&#xff0c;其控制原理都大同小异&#xff0c;在没有领透其技术要领情况下&#x…

苍穹外卖项目---------收获以及改进(9-12)

①Spring Task-------实现系统定时任务 概念&#xff1a; 应用场景&#xff1a; 使用步骤&#xff1a; 实现订单超时和前一天派送中的订单的自动任务处理&#xff1a; Component Slf4j public class Mytask {Autowiredprivate OrderServiceimpl orderServiceimpl;/*** 处理订…

yarn 安装以及报错处理

前一种报错是由于没有安装yarn导致的&#xff0c;使用以下命令即可安装&#xff1a; npm install -g yarn 如果成功安装&#xff0c;将显示Yarn的版本号。 yarn --version 第二种报错是因为系统上的执行策略限制导致的。执行策略是一种安全功能&#xff0c;用于控制在计算机…

Kafka官方文档中文版+Kafka面试题详解!

你了解kafka吗&#xff1f;目前它在一线互联网公司以迅雷不及掩耳之势得到了广泛的应用。但究竟是什么呢&#xff1f;Kafka最初于2011年在LinkedIn开发&#xff0c;自那时起经历了很多改进。如今它是一个完整的平台&#xff0c;允许您冗余地存储荒谬的数据量&#xff0c;拥有一…

SH150S1光电吊舱

SH150S1光电吊舱 1产品应用 SH150S1是一款三轴三光吊舱&#xff0c;集成了最远测程达3.0km&#xff0c;精度小于2米的半导体激光测距机&#xff0c;640512高分辨率红外相机&#xff0c;30倍光学变倍可见光相机以及高稳定精度平台框架&#xff1b;可安装于中小型无人机&#x…

【每日刷题】Day40

【每日刷题】Day40 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 202. 快乐数 - 力扣&#xff08;LeetCode&#xff09; 2. 217. 存在重复元素 - 力扣&#xff08;Le…

vue2 报错,警告 解决方法 component name“Index“should always be multi-word

报错原因&#xff1a; 组件名称应该为俩个或俩个以上单词组成的&#xff0c;并且还要是大驼峰命名&#xff0c;例如&#xff1a;MyIndex&#xff0c;MyLogin等 解决方法一&#xff1a; 将组件名称改为俩个或俩个以上单词组成的名称&#xff0c;且为大驼峰命名&#xff0c;例如…

Postman基础功能-Collection集合和批量运行

一、Collection&#xff08;集合&#xff09;介绍 当我们对一个或多个系统中的很多接口用例进行维护时&#xff0c;首先想到的就是对接口用例进行分类管理&#xff0c;同时还希望对这批接口用例做回归测试。 在 Postman 中也提供了这样一个功能&#xff0c;就是 Collec…

免费的GPT4终于要来了!OpenAI直播发布会详细解读!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

Docker下载镜像出现“missing signature key”如何解决?

“missing signature key” 通常与 Docker 配置有关&#xff0c;具体是 Docker 试图验证镜像的签名但未能找到相应的密钥。这种情况可能发生在启用了 Docker Content Trust (DCT) 的环境中&#xff0c;DCT 是一种安全功能&#xff0c;要求所有镜像必须有签名才能拉取。 原因 …

刚刚OpenAI发布ChatGPT-4o模型,免费使用GPT4o并免费提供更多功能

就在今日凌晨1点&#xff0c;OpenAI举行了春季发布会&#xff0c;发布了GPT-4o 并免费提供更多功能。 亲测GPT-4o已经可以免费试用&#xff0c;每个人都可以使用它并从中受益&#xff0c;GPT4终于不再是少部分人的玩物。 点击加入ChatGPT4交流群&#xff1a;https://www.aijour…

微信小程序发送订阅消息sendMessage

微信小程序发送订阅消息sendMessage 请注意订阅消息一次性订阅只只能授权一次接受一条消息多次授权会累加接受次数&#xff0c;wx.requestSubscribeMessage调用授权 目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放 //授权弹框&#xff0c;只弹出…

交通地理信息系统实习教程(二)

这篇文章服务于GIS背景用户以及有志于GIS的朋友 操作源数据位置&#xff1a;【免费】交通地理信息系统实习二源数据资源-CSDN文库 软件安装包位置&#xff1a;【免费】TransCad-交通地理信息系统软件资源-CSDN文库 一、最短路径分析 1.1软件启动说明 这里需要给出一个必要的…

硬件FMEA与软件FMEA的区别——FMEA软件

​免费试用FMEA软件-免费版-SunFMEA 在产品开发和制造过程中&#xff0c;失效模式与影响分析&#xff08;FMEA&#xff09;作为一种预防性的质量工具&#xff0c;对于确保产品性能和质量至关重要。然而&#xff0c;硬件FMEA和软件FMEA在应用和实践方面存在显著的区别。本文旨在…

基于阿里云向量检索 Milvus 版与 PAI 搭建高效的检索增强生成(RAG)系统

阿里云向量检索 Milvus 版现已无缝集成于阿里云 PAI 平台&#xff0c;一站式赋能用户构建高性能的检索增强生成&#xff08;RAG&#xff09;系统。您可以利用 Milvus 作为向量数据的实时存储与检索核心&#xff0c;高效结合 PAI 和 LangChain 技术栈&#xff0c;实现从理论到实…

java面试题:判断字符串包含字母、数字、空格、符号的数量

在Java中&#xff0c;你可以使用正则表达式来检查字符串中包含多少个字母、数字、空格和符号。也可以使用基础api来实现业务逻辑&#xff0c;方法如下&#xff1a; 1 使用Character类的静态方法 以下代码定义了一个countCharacters方法&#xff0c;它遍历字符串中的每个字符&a…

解决:微信支付 由于商家传入的H5交易参数有误,该笔交易暂时无法完成,请联系商家解决

微信支付参数配置完成后请求报这个。在网上搜索后发现是ip配置的问题&#xff0c;我配置的是127.0.0.1。 而微信支付的时候需要获取用户的真实ip

Mysql关键字闭坑

Mysql关键字闭坑 INTERVAL 因为需求&#xff0c;所以数据设计时用到了一个INTERVAL字段,程序编译及启动都没有任何问题&#xff0c;但是到了查询的时候&#xff0c;直接控制台报语法异常&#xff0c;这就奇怪了&#xff0c;用的是MP插件作为查询&#xff0c;为啥报出语法错误&…

求职招聘平台小程序源码系统 附带源代码以及完整的安装部署教程

系统概述 求职招聘平台小程序源码系统是一款基于微信小程序的求职招聘平台&#xff0c;旨在为广大求职者和招聘企业提供一个高效、便捷的线上交流平台。该系统具备以下特点&#xff1a; 1.功能齐全&#xff1a;系统包含职位发布、简历投递、在线沟通、面试安排等求职招聘全流程…

Android Saving Activity State使用说明和注意事项

1、说明 在管理activity生命周期的简单介绍中提到当一个activity被暂停或停止时&#xff0c;该activity的状态被保留。因为当activity对象被暂停或停止时仍然保留在内存中&#xff0c;所有有关成员的信息和当前的状态仍然可用。这样&#xff0c;用户对该activity所做的任何更改…