数据结构之链表(1),单链表

目录

前言

一、什么是链表

二、链表的分类

三、单链表

四、单链表的实现

五、SList.c文件完整代码

六、使用演示

总结



前言

        本文讲述了什么是链表,以及实现了完整的单链表。


❤️感谢支持,点赞关注不迷路❤️


一、什么是链表

1.概念

概念:链表是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

简单来说,链表属于线性表的一种,在逻辑结构上是连续的,物理结构上不一定是连续的,通过指针连接。


2.节点

与顺序表不同的是,链表里的每个节点都是独立申请下来的空间,我们称之为“结点/结点”

结点的组成主要有两个部分:当前结点要保存的数据和保存下⼀个结点的地址(指针变量)。也可以说成数据域和指针域两部分

特点:链表中每个结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量来保存下一个结点位置,才能从当前结点找到下⼀个结点。


3.链表的性质

  1. 链式结构在逻辑上是连续的,在物理结构上不⼀定连续
  2. 结点一般是从堆上申请的
  3. 从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

4.与顺序表的对比

  1. 顺序表的 中间/头部 插入删除数据的时间复杂度为O(n),而链表相同功能时间复杂度为O(1)
  2. 顺序表增容需要申请请新空间,拷贝数据,释放旧空间。会有不小的消耗。链表每次申请一个节点,不存在拷贝,释放旧空间。
  3. 顺序表增容一般是呈2倍的增长,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200, 我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。链表不存在空间浪费

以上是链表相对与顺序表的优点,但不代表顺序表一定不如链表,顺序表在有些场景下效率还是非常高,因此选择使用什么数据结构是看场景的要求


二、链表的分类

链表的结构非常多样,有带头不带头,单向或者双向,循环不循环,这三种属性情况组合起来就有8种(2x2x2)链表结构:

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:单链表双向带头循环链表:

  1. 无头单向非循环链表(单链表):结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现该结构会带来很多优势,后面我们代码实现了就知道了。

本文主要讲述单链表


三、单链表

单链表的结构如下图:

单链表的结构声明:

typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType data;//节点数据
    struct SListNode* next;//指向下一节点的指针变量
}SLTNode;

以下单链表同样由3个文件组成:

  1. SList.h:单链表的结构声明,各种函数声明
  2. SList.c:用于函数的具体实现
  3. test.c:用于测试单链表(自行测试)


四、单链表的实现

1.SList.h文件

以下是该文件中的代码:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//定义一个链表结构
typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;//节点数据struct SListNode* next;//指向下一节点的指针变量
}SLTNode;//打印
void SLTPrint(SLTNode* phead);//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);//尾删
void SLTPopBack(SLTNode** pphead);//头删
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除指定位置pos的数据
void SLTErase(SLTNode** pphead, SLTNode* pos);//删除pos之后的数据
void SLTEraseAfter(SLTNode* pos);//销毁链表
void SListDestroy(SLTNode** pphead);

2.SList.c文件

1.SLTPrint函数

//打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;//循环打印while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

解析:该函数用于打印链表数据,使用一个pcur指针,只要不为空指针,就循环遍历下一个节点


2.SLTBuyNode函数

//申请节点
SLTNode* SLTBuyNode(SLTDataType x)
{//申请一个节点SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));if (node == NULL){perror("malloc fail!");exit(1);}//赋值node->data = x;node->next = NULL;return node;
}

解析:

  1. 该函数用于申请新节点,一个参数,其为新节点存储的数据
  2. 使用malloc函数申请,申请成功后将其数据域赋值为参数值,指针域赋值为空指针NULL
  3. 因为该函数主要为其他功能函数服务,因此只需写在 SList.c 文件中即可,无需在头文件SList.h 中声明


3.SLTPushBack函数

//尾插
//因为要修改plist本身,因此需要二级指针来接收
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{//断言pphead不能为空指针assert(pphead);//申请新节点SLTNode* newnode = SLTBuyNode(x);//如果pphead指向的第一个节点就是空指针if (*pphead == NULL){*pphead = newnode;}else{//找尾结点SLTNode* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = newnode;}
}

解析:

  1. 该函数功能为:在链表尾部插入一个数据。因此需要链表的头部指针和需要插入的数据x
  2. 为什么头节点参数是二级指针?答:因为需要修改原指针变量本身,我们知道函数传参分为传值传参和传址传参(&),想要修改原变量内容就需要传址传参,而这里原变量就是一个指向链表头节点的指针变量,因此原变量通过&传参,就需要二级指针来接收一级指针变量的地址。所以这里就是用二级指针,下面其他的函数同理。
  3. 申请完新节点,插入到链表尾部时要分2种情况,1链表为空,2链表不为空。
  4. 链表为空则直接将新节点插入链表即可,也就是将指向头结点的指针指向新节点。链表不为空就需要寻找尾结点,尾节点的特点就是next指针指向空。循环之后将尾节点的next指针修改为新节点即可。


4.SLTPushFront函数

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);//直接申请节点,然后修改新节点指向即可SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}

解析:

  1. 功能:在链表头部插入一个新节点。参数同样是一个二级指针和新节点的数据
  2. 头插比较简单,只需将新申请的节点的 next 指针指向链表头结点,然后让指向链表头结点的指针指向新节点即可。


5.SLTPopBack函数

//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个节点的情况if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//两个及以上节点的情况//申请两个节点用于指向尾节点和倒数第二个节点SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}//销毁尾结点,然后让倒数第二个节点next指向空free(ptail);ptail = NULL;prev->next = NULL;}
}

解析:

  1. 功能:删除链表的最后一个节点,参数为一个二级指针
  2. 首先断言,不能传空指针并且链表不能为空
  3. 尾删也要分两种情况,因为如果只要一个节点,那么就不需要找倒数第二个节点。所以分为链表只有一个节点的情况和有多个节点的情况
  4. 只有一个节点,那么我们直接 free 掉该节点,然后让指向头结点的指针置空。多个节点,那么我们就需要找到最后一个节点 ptail 以及倒数第二个节点 prev,使用while循环即可,只要ptail 的下一个节点为空就跳出循环,此时 prev 为倒数第二个节点,然后释放掉最后一个节点,修改倒数第二个节点的next指针即可。


6.SLTPopFront函数

//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

解析:

  1. 功能:删除链表的头部节点
  2. 因为参数 *pphead 就是指向链表的头结点,因此删除简单,先创建一个next指针保存头结点的下一个节点,然后释放掉头节点,更改 *pphead 的指向即可


7.SLTFind函数

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{assert(phead);SLTNode* pcur = phead;while (pcur){//找到了if (pcur->data == x){return pcur;}pcur = pcur->next;}//没找到return NULL;
}

解析:

  1. 功能:查找数据为 x 的节点,返回该节点的地址。
  2. 因为查找操作不会影响头指针 phead,因此参数为一级指针即可
  3. 查找很简单,循环遍历链表,让 pcur 一直往后走,直到找到存储 x 的节点,返回该节点,没找到就返回空指针


8.SLTInsert函数

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//如果指定位置是头结点if (*pphead == pos){//头插SLTPushFront(pphead, x);}else{//新节点SLTNode* newnode = SLTBuyNode(x);//prev用于找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}

解析:

  1. 功能:在指定位置之前插入数据,参数为头指针地址、指定位置(用SLTFind函数确定)、需插入的数据x。
  2. 首先断言,头指针和pos指针都不能为空,也就是链表不能为空
  3. 然后,也分两种情况,因为如果指定位置刚好是头结点,那么只需要使用头插函数即可。如果指定位置不是头结点,我们就需要找到 pos 指向的结点的前一个节点。定义一个指针变量 prev,让它从头往后找,只要它的 next 指针不是 pos,那么就继续往下个节点走,直到找到pos的前一个节点。
  4. 找到之后,让 prev 的 next 指针指向新节点,再让新节点的 next 指针指向pos就完成了在指定位置前插入数据。


7.SLTInsertAfter函数

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//先让newnode的next指向下一个节点newnode->next = pos->next;//再修改pos的next指针pos->next = newnode;
}

解析:

  1. 功能:在指定位置之后插入数据。
  2. 因为受影响的节点只有pos指向的节点,所以只需要传pos指针和需要增加的数据 x 即可。
  3. pos指针使用SLTFind函数指定即可
  4. 实现简单,注意顺序即可,将新节点的next指针指向pos节点的下一个节点,然后让pos指针的next指针指向新节点即可。


8.SLTErase函数

//删除指定位置pos的数据
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);//如果pos位置是第一个位置if (*pphead == pos){//头删SLTPopFront(pphead);}else{//找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//释放prev->next = pos->next;free(pos);pos = NULL;}
}

解析:

  1. 功能:删除指定位置的节点。
  2. 参数需用到头节点和pos指向节点
  3. 也要分两种情况,因为如果需删除节点刚好是头结点,直接头删即可,如果不是头结点,则需要找到指定位置的前一个节点,老套路,找到之后,先修改 prev 的next指针,然后释放pos指向的节点。


9.SLTEraseAfter函数

//删除pos之后的数据
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* del = pos->next;//让pos指向它之后的之后的数据pos->next = pos->next->next;free(del);del = NULL;
}

解析:

  1. 功能:删除pos位置后一个节点
  2. 只需要pos指针即可
  3. 实现简单,先保存需删除的节点地址,然后改变pos的next指针指向,最后释放掉 del 即可。


10.SListDestroy函数

//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;//循环销毁链表while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}//记得将头指针指置空*pphead = NULL;
}

解析:

  1. 功能:销毁整个链表
  2. 断言确保链表不为空
  3. 创建pcur循环遍历链表,只要pcur不为空,释放该空间。
  4. 最后记得将头指针置空


五、SList.c文件完整代码

#include "SList.h"//打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;//循环打印while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}//申请节点
SLTNode* SLTBuyNode(SLTDataType x)
{//申请一个节点SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));if (node == NULL){perror("malloc fail!");exit(1);}//赋值node->data = x;node->next = NULL;return node;
}//尾插
//因为要修改plist本身,因此需要二级指针来接收
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{//断言pphead不能为空指针assert(pphead);//申请新节点SLTNode* newnode = SLTBuyNode(x);//如果pphead指向的第一个节点就是空指针if (*pphead == NULL){*pphead = newnode;}else{//找尾结点SLTNode* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = newnode;}
}//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);//直接申请节点,然后修改新节点指向即可SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个节点的情况if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//两个及以上节点的情况//申请两个节点用于指向尾节点和倒数第二个节点SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}//销毁尾结点,然后让倒数第二个节点next指向空free(ptail);ptail = NULL;prev->next = NULL;}
}//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{assert(phead);SLTNode* pcur = phead;while (pcur){//找到了if (pcur->data == x){return pcur;}pcur = pcur->next;}//没找到return NULL;
}//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//如果指定位置是头结点if (*pphead == pos){//头插SLTPushFront(pphead, x);}else{//新节点SLTNode* newnode = SLTBuyNode(x);//prev用于找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//先让newnode的next指向下一个节点newnode->next = pos->next;//再修改pos的next指针pos->next = newnode;
}//删除指定位置pos的数据
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);//如果pos位置是第一个位置if (*pphead == pos){//头删SLTPopFront(pphead);}else{//找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//释放prev->next = pos->next;free(pos);pos = NULL;}
}//删除pos之后的数据
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* del = pos->next;//让pos指向它之后的之后的数据pos->next = pos->next->next;free(del);del = NULL;
}//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;//循环销毁链表while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}//记得将头指针指置空*pphead = NULL;
}


六、使用演示

不完全演示:

#include "SList.h"void SListTest1()
{//指针要初始化为空SLTNode* plist = NULL;//尾插SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);printf("尾插:");SLTPrint(plist);//头插SLTPushFront(&plist, 11);SLTPushFront(&plist, 12);SLTPushFront(&plist, 13);SLTPushFront(&plist, 14);SLTPushFront(&plist, 15);printf("头插:");SLTPrint(plist);//尾删SLTPopBack(&plist);printf("尾删:");SLTPrint(plist);//头删SLTPopFront(&plist);printf("头删:");SLTPrint(plist);//指定位置之前插入数据SLTInsert(&plist, SLTFind(plist, 2), 66);printf("在2的前面插入66:");SLTPrint(plist);//删除指定位置数据SLTErase(&plist, SLTFind(plist, 66));printf("删除66:");SLTPrint(plist);//销毁链表SListDestroy(&plist);printf("销毁:");SLTPrint(plist);
}int main()
{SListTest1();return 0;
}

运行结果:


总结

        以上就是本文的全部内容,感谢支持。

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

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

相关文章

19、网络安全合规复盘

数据来源&#xff1a;5.网络安全合规复盘_哔哩哔哩_bilibili

精密制造的革新:光谱共焦传感器与工业视觉相机的融合

在现代精密制造领域&#xff0c;对微小尺寸、高精度产品的检测需求日益迫切。光谱共焦传感器凭借其非接触、高精度测量特性脱颖而出&#xff0c;而工业视觉相机则以其高分辨率、实时成像能力著称。两者的融合&#xff0c;不仅解决了传统检测方式在微米级别测量上的局限&#xf…

【C++】入门基础知识-1

&#x1f36c;个人主页&#xff1a;Yanni.— &#x1f308;数据结构&#xff1a;Data Structure.​​​​​​ &#x1f382;C语言笔记&#xff1a;C Language Notes &#x1f3c0;OJ题分享&#xff1a; Topic Sharing 目录 前言&#xff1a; C关键字 命名空间 命名空间介…

使用 Llama-index 实现的 Agentic RAG-Router Query Engine

前言 你是否也厌倦了我在博文中经常提到的老式 RAG(Retrieval Augmented Generation | 检索增强生成) 系统&#xff1f;反正我是对此感到厌倦了。但我们可以做一些有趣的事情&#xff0c;让它更上一层楼。接下来就跟我一起将 agents 概念引入传统的 RAG 工作流&#xff0c;重新…

凌晨1点开播!Meta Connect 2024开发者大会,聚焦Llama新场景和AR眼镜

作者&#xff1a;十九 编辑&#xff1a;李宝珠 北京时间 9 月 26 日凌晨 1 点&#xff0c;Meta Connect 2024 开发者大会即将举行&#xff0c;马克扎克伯格将聚焦 AI 和元宇宙&#xff0c;向大家分享 Llama 模型的更多潜在应用&#xff0c;并介绍 Meta 最新产品 AR 眼镜和 Meta…

OceanBase云数据库战略实施两年,受零售、支付、制造行业青睐

2022年OceanBase推出云数据库产品OB Cloud,正式启动云数据库战略。两年来OB Cloud发展情况如何&#xff0c;9月26日&#xff0c;OceanBase公有云事业部总经理尹博学向记者作了介绍。 尹博学表示&#xff0c;OB Cloud推出两年以来&#xff0c;已服务超过700家客户&#xff0c;客…

智算中心动环监控:构建高效、安全的数字基础设施@卓振思众

在当今快速发展的数字经济时代&#xff0c;智算中心作为人工智能和大数据技术的核心支撑设施&#xff0c;正日益成为各行业实现智能化转型的重要基石。为了确保这些高性能计算环境的安全与稳定&#xff0c;卓振思众动环监控应运而生&#xff0c;成为智算中心管理的重要组成部分…

理解Java引用数据类型(数组、String)传参机制的一个例子

目录 理解Java引用数据类型&#xff08;数组、String&#xff09;传参机制的一个例子理解样例代码输出 参考资料 理解Java引用数据类型&#xff08;数组、String&#xff09;传参机制的一个例子 理解 引用数据类型传递的是地址。用引用类型A给引用类型B赋值&#xff0c;相当于…

Linux(含麒麟操作系统)如何实现多显示器屏幕采集录制

技术背景 在操作系统领域&#xff0c;很多核心技术掌握在国外企业手中。如果过度依赖国外技术&#xff0c;在国际形势变化、贸易摩擦等情况下&#xff0c;可能面临技术封锁和断供风险。开发国产操作系统可以降低这种风险&#xff0c;确保国家关键信息基础设施的稳定运行。在一…

【C++位图】构建灵活的空间效率工具

目录 位图位图的基本概念如何用位图表示数据位图的基本操作setresettest 封装位图的设计 总结 在计算机科学中&#xff0c;位图&#xff08;Bitmap&#xff09;是一种高效的空间管理数据结构&#xff0c;广泛应用于各种场景&#xff0c;如集合操作、图像处理和资源管理。与传统…

一文读懂 Pencils Protocol 近期不可错过的市场活动

Pencils Protocol 是 Scroll 上综合性的 DeFi 协议&#xff0c;自 9 月 18 日开始其陆续在 Tokensoft、Bounce、Coresky 等平台开启 DAPP 通证的销售&#xff0c;并分别在短期内完成售罄。吸引了来自韩国、CIS、土耳其等 70 多个国家的 5 万多名认证用户&#xff0c;反响热烈&a…

Jmeter关联,断言,参数化

一、关联 常用的关联有三种 1.边界提取器 2.JSON提取器 3.正则表达式提取器 接下来就详细讲述一下这三种的用法 这里提供两个接口方便练习 登录接口 接口名称&#xff1a;登录 接口提交方式&#xff1a;POST 接口的url地址&#xff1a;https://admin-api.macrozheng.com/a…

C#常用数据结构栈的介绍

定义 在C#中&#xff0c;Stack<T> 是一个后进先出&#xff08;LIFO&#xff0c;Last-In-First-Out&#xff09;集合类&#xff0c;位于System.Collections.Generic 命名空间中。Stack<T> 允许你将元素压入栈顶&#xff0c;并从栈顶弹出元素。 不难看出&#xff0c;…

Vue引入js脚本问题记录(附解决办法)

目录 一、需求 二、import引入问题记录 三、解决方式 一、需求 我想在我的Vue项目中引入jquery.js和bootstrap.js这种脚本文件&#xff0c;但发现不能单纯的import引入&#xff0c;问题如下。 二、import引入问题记录 我直接这么引入&#xff0c;发现控制台报错TypeError: …

华为HarmonyOS地图服务 11 - 如何在地图上增加点注释?

场景介绍 本章节将向您介绍如何在地图的指定位置添加点注释以标识位置、商家、建筑等&#xff0c;并可以通过信息窗口展示详细信息。 点注释支持功能&#xff1a; 支持设置图标、文字、碰撞规则等。支持添加点击事件。 PointAnnotation有默认风格&#xff0c;同时也支持自定…

《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch9 策略梯度方法 Box 8.1 马尔可夫决策过程的平稳分布

Box 8.1&#xff1a; 马尔可夫决策过程的平稳分布 整理自 链接 分析平稳分布的关键工具是 P π ∈ R n n P_\pi \in {\mathbb R}^{n\times n} Pπ​∈Rnn&#xff0c;它是给定策略 π π π 下的概率转移矩阵。 如果状态被索引为 s 1 , ⋯ , s n s_1,\cdots, s_n s1​,⋯…

idea2021git从dev分支合并到主分支master

1、新建分支 新建一个名称为dev的分支&#xff0c;切换到该分支下面&#xff0c;输入新内容 提交代码到dev分支的仓库 2、切换分支 切换到主分支&#xff0c;因为刚刚提交的分支在dev环境&#xff0c;所以master是没有 3、合并分支 点击push&#xff0c;将dev里面的代码合并到…

图片尺寸不合适?这3款免费好用的AI绘图神器帮你免费无缝拓展!一键扩展画面之外的内容,真的泰裤啦!

大家好&#xff0c;我是灵魂画师向阳 在处理图片素材时&#xff0c;大家有没有遇到过尺寸不合适但又不能裁切的情况&#xff1f;是不是也想过图像要是能自己“长”出一块就好了&#xff1f;这种要求在以前或许很难实现&#xff0c;但生产式 AI 技术出现后它就不再是问题了&…

基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue

基于GIKT深度知识追踪模型的习题推荐系统 目录结构 Flask-BackEnd flask后端 app 后端主体文件 alg 深度学习模块 data 数据集data_process.py 数据预处理gikt.py GIKT模型pebg.py PEBG模型params.py 一些参数train.py 仅模型训练train_test.py 模型训练和测试-五折交叉验证t…

WebGIS开发四大开源框架对比

本篇文章主要介绍GIS开发四大地图框架的差异和特点。 Cesium: Cesium是目前主流的一款三维地图框架&#xff0c;支持桌面端、web端、移动端等多平台。Mapbox&#xff1a;高清经纬度矢量瓦片&#xff0c;个性化前端表达&#xff0c;前端矢量绘制&#xff0c;支持海量地名地址。…