当前位置: 首页 > article >正文

线性数据结构:单向链表

放弃眼高手低,你真正投入学习,会因为找到一个新方法产生成就感,学习不仅是片面的记单词、学高数......只要是提升自己的过程,探索到了未知,就是学习。

考虑到可能有小白在合并代码时出现各种细节问题,本文最后有合并后的代码(源文件+头文件+main函数思路大纲),只要知道了原理,功能都是小问题!学习先模仿,理清思路,然后自己从头到尾实现一遍!下面先进行分类讲解!希望本文对你有所帮助!记得一键三连哦!

目录

                                         一.链表的理解

                                二.链表的分类(重点理解)

                       三.无头单向非循环链表

                                          三.1 节点结构以及理解 

                                 三.2遍历链表

                        三.3尾插

                        三.4增加节点

                        三.5尾删

                        三.6头删

                        三.7头插

                        三.8查找数据

                        三.8插在目标节点前面

                        三.9插在目标节点后面

                        三.10删除目标节点

                        四.合并代码


在上篇我们学习了顺序表,它是在计算机内存中以数组形式保存的线性表,特点是通过一组地址连续的存储的单元依次存储线性表中的各个元素,既然地址是连续的,它存在以下几个问题:

1:在中间/头部插入删除,需要对整个空间地址、内容进行移动,比较麻烦

2:如果空间不够用,那么就需要扩容,这个过程需要拷贝原空间数据,释放旧空间,消耗较大

3:空间浪费问题,当你已经扩容好了一片空间,但是很多用不完或者又不够用,需要重新对空间问题进行解决

综合起来,我们需要更加高效的线性存储结构:链表

一.链表的理解

定义:链表是一种线性结构,由一系列节点组成,链表存在头(头节点)和尾(尾节点),节点之间通过指针关联,每一个数据元素都是独立存储的(地址不连续)。链表通过指针链接次序实现数据元素的逻辑顺序,每个节点包含两个组成部分:数据域与指针。数据域用来存储数据,指针负责指向下一个节点。链表的起点为头节点,尾节点则是空指针(NULL),用来表示链表的结束。

抽象图示:

特点: 1:动态性。灵活删除或者插入节点

            2:内存利用率高。充分利用空间,实现动态内存管理

            3:插入与删除操作效率高。顺序表的删除插入时间复杂度为O(N),那么链表的对应时                   间复杂度则达到了O(1),可以在很短的时间内完成插入删除操作

            4:查询效率低。我们知道链表从头到尾,需要遍历整个链表才能找到目标节点,它的查                      找时间复杂度则为O(N)

二.链表的分类(重点理解)

链表主要可以分为以下3大类:

单向链表:单向链表中的每一个节点只包含一个指针,指向下一个节点,数据元素的逻辑顺序通过链表中的指针链接次序实现

双向链表:双向链表中的每个节点包含2个指针,分别指向前一个节点与后一个节点

循环链表:循环链表的第一个节点和最后一个节点通过指针相连,形成一个环状结构,循环链表可以是单向也可以是双向的

在这3大类中我们还需要了解几个概念:头指针头节点带头节点不带头节点

头指针:本质是一个结构体类型的指针,存储第一个节点的地址

头节点:本质是一个节点,有数据域与指针。在单链表的第一个节点之前再附加一个节点,称为                  头节点,头节点的数据域可以不放任何信息,也可以记录链表长度。若链表是带有头节                  点的,则头指针指向头节点的存储位置

带头节点:与其它节点一样,有数据域跟指针,可以理解为一个结构体变量,只是头指针指向头节点存储位置,图示:

不带头节点 :我们知道带头节点是指针指向头节点,那么反之,不带头节点是头指针指向第一个节点的存储位置

那么我们简单总结:无论是否有头节点,都存在一个头指针,头指针都指向链表的一个节点,只是区分头指针是指向第一个节点还是头节点,图示理解:

链表会根据:单头或者不带头单向或者双向循环或者非循环进行组合,一共可以组合出8种

其中只要掌握2种,就可以触类旁通了,我们先重点来学习以下第一种:

无头单向非循环链表         带头双向循环链表

三.无头单向非循环链表

上面我们已经了解了无头跟有头的区别,那这里我们轻而易举的可以画个图:

 三.1 节点结构以及理解 

我们创建了一个指针next跟数据域,这个数据域的类型就是你要存储的类型,可自行设置,指针指向下一个节点的地址。

这里解释一下next指针: 链表中的next是指向下一个节点的指针(通常称为next)。所有节点通过next指针连接起来,使得数据元素可以按照一定的顺序排列。在双向链表中还有一个指向前一个节点的指针:prev,这使得双向链表可以从头到尾或从尾到前遍历,这里仅仅简单科普一下!

下面我们看几个常见写法,来进行错误纠正:

我们先用typedef对结构体类型进行重命名, 命名之前结构体类型是struct Student,命名之后可以简写为Student,这样减少了代码量,方便阅读。上图第一种写法跟第二种写法都存在一个问题:在进行重命名的结构体里面,类型必须写全,这是因为现在是属于声明阶段,出了这个结构体以后才可以用简写的方式。举个例子:一个方案的发布必须是已经制作好的。这里也一样,typedef目前在这个结构体只是类似方案制作阶段,出了这个结构体之后才可以简化使用。

三.2遍历链表

在遍历链表时,我们需要让指针灵活的动起来,而不能直接让节点里面的指针动起来,不然就出问题了,因此我们需要创建一个指针指向节点中的第一个指针,改变这个指针的指向是不是就达到了动态节点的效果!比如我新创建了一个指针 cur (在下文我们方便讲解,还会用到这个指针),每次通过改变cur的指向来改变当前访问的节点。这里需要注意cur指针是不会真正动的,只是它的指向地址发生改变。图示理解如下:

在进行链表的增删查找功能前,我们需要掌握遍历,代码参考如下:

我们仔细解释一下图中比较重要的地方:

1:为什么不需要判断指针head的有效性?因为head是头指针,指向第一个节点,指针是空指针只能说明指向的地址不存在,而指向的地址的空间内容是否存在不相干,就好比通讯录空间,我只是没有存储联系人进去,但是有这个空间,这个大家可以理解吗?我再举个例子:现在有一个水杯(head),我需要去拿水杯喝水(head指向地址的内容),杯子里面有没有水跟这个水杯存不存在不相关!

2:指针head跟指针cur的关系:head是头指针,是不可以移动的,而将head指针指向的地址交给cur,通过cur的指向移动,实现对节点内容的管理

  3:对while循环的条件的讨论:我们看下面2个图

我们知道头指针head将第一个节点的地址传给curcur是指向下一个节点的地址的,当cur到达最后一个节点时,此时cur的指向还是最后一个节点的地址,而cur->next已经是指向空指针了,就少进入了循环一次。cur是通过cur->next来改变cur的指向的,而cur->next已经指向下一个节点了。这个我们结合2张图理解(物理图示与逻辑图示):

 

 三.3尾插

我们从字面上理解,就是在当前最后一个节点后面再插入新节点!再把新节点与尾节点连接起来,那么我们需要用3个操作:找尾节点  开辟新节点  连接节点 (最后会对3个步骤汇总代码讲思路)

先解释一下为什么要用二级指针?因为涉及空链表跟链表存在的讨论,需要更改地址的问题,

*head表示头指针本身,用来判断空指针(也就是空链表),空链表也就是*head==NULL

head表示头指针的地址,用来判断链表的存在与否,链表不存在也就是head==NULL

下面我们进行第一个操作,找尾节点。我们已经知道如何遍历链表了!既然指针指向下一个节点的地址,那么当这个指针是空指针的时候,是不是就表示找到尾了!这里需要理解的是,这个指针的指向是NULL,因为我们是需要开辟新节点的,自然要在最后一个节点后面开辟,所以需要让它下一个节点指向NULL,如图理解(tailcur等价):

//找尾节点
void Traversal(Pointdef** head)
{//判断链表是不是空链表if(*head==NULL){//空链表的话新节点作为第一个节点*head=newspointer;}else{//托付head的指向给cur指针Pointdef* cur = *head;//找尾while (cur->next != NULL){//改变 cur 指针的指向cur = cur->next;}//此时cur的后面一个节点就是要添加节点//这里连接就行了}
}

下面我们进行第二个操作:开辟新节点。新节点的创建不能直接使用原节点的类型去创建,因为这样创建的新节点只是临时变量,为什么呢?因为我们是在函数里面创建的,当函数调用完,里面的变量就全部销毁了。因此我们需要用到动态内存开辟,同时初始化指针,至于为什么要初始化,后面会细说,因此用malloc给新节点开辟一块空间,才能延长新节点生命周期。代码如下:

Pointdef* Greate()
{Pointdef* news = malloc(sizeof(Pointdef));if (news == NULL){perror("malloc");return NULL;}//理解:news指针指向新节点地址,news->next表示对新节点的指针初始化了news->next = NULL;return news;
}

首先我们看这个函数,它的类型是结构体指针类型,为哈?因为我们用这个函数来开辟新节点,那么开辟的空间类型是节点的结构体类型,我们知道链表是用指针连接起来的,那么我们只需要返回一个指针就行了,指针类型肯定是结构体类型,然后用malloc开辟新节点,节点大小是结构体的大小。下面紧接着判断,判断新节点是否开辟成功,news->next=NULL,这句话就是告诉我们这是新节点的尾端,因为new->next是指向下一个节点,也就是NULL。大家肯定想知道为哈不初始化这个新节点的数据域呢?单链表节点的数据域是否初始化,可以自行设定,未初始化的数据域可能出现一些随机值。

下面进行第三个操作:连接这2个节点。在连接前需要判断这个链表的头指针是否存在,否则需要将新节点作为第一个节点,不然会出现严重问题。这里涉及空链表跟链表不存在的区别,如下:

链表不存在:如:head==NULL,链表都不存在,那么一切操作都无用,因此在使用链表时需要检查链表是否存在

空链表:链表存在,头指针为空,如:cur==NULL,它的存在与否需要分情况,比如我需要删除某个节点,那么空链表肯定是不行的,而打印、插入节点这些没什么影响,具体需要根据操作来判断

//增加新节点
void NewPointer(Pointdef** head)
{assert(head);Pointdef* newpointer = Greate();//如果这个头指针的指向是空指针,那么链表是空的,将这个新节点作为新的节点if (*head == NULL){*head = newpointer;}else{//头指针指向交给cur,用cur找尾Pointdef* cur = *head;//找尾while (cur->next != NULL){cur = cur->next;}//连接cur->next = newpointer;}
}

总结:上面代码我们是进行拆分了的,如果看不懂,我们可以用笔一步步记录,理清思路。尾插的总思路就是:先判断链表是否存在,再用函数新增节点,然后判断空链表,最后找尾进行连接。参考完整代码:

//增加新节点
Pointdef* Greate()
{Pointdef* news = malloc(sizeof(Pointdef));if (news == NULL){perror("malloc");return NULL;}//理解:news指针指向新节点地址,new->next标识它是将要接入的最后一个节点,相当于初始化了news->next = NULL;return news;
}//找尾+连接
void NewPointer(Pointdef** head)
{//判断链表是否存在assert(head);Pointdef* newpointer = Greate();//如果这个头指针的指向是空指针,那么链表是空的,将这个新节点作为新的节点if (*head == NULL){*head = newpointer;}else{Pointdef* cur = *head;//找尾while (cur->next == NULL){cur = cur->next;}//连接cur->next = newpointer;}
}
三.4增加节点

新增我们在上面的尾插已经使用过了,我们就不再重复说了,每次操作接收函数返回的指针,就可以控制新增节点的地方,新增节点的函数都是通用的,代码如下:

//增加新节点
Pointdef* Greate()
{Pointdef* news = malloc(sizeof(Pointdef));if (news == NULL){perror("malloc");return NULL;}//理解:news指针指向新节点地址,news->next理解为标识自己是将要接入的最后一个节点,相当于初始化了news->next = NULL;return news;
}
三.5尾删

顾名思义,就是删除最后一个节点,那么我们猜测步骤也就只有2步:先找  再删

我们需要先遍历链表,然后找到最后一个节点,进行删除释放,再把倒数第二个节点的cur->next改为NULL。尾删需要注意几种情况,空链表跟唯一节点的链表,2种情况,跟尾插一样,可能需要更改头指针地址,因此需要使用二级指针。找尾节点跟倒数第二个节点一个指针肯定不够,因此我们需要两个指针,一个来找尾节点,一个来找倒数第二个节点。我们来看思维图,理清双指针的关系:

代码如下:

//尾删
void Taildeletion(Pointdef** head)
{//判断链表是否存在assert(head);//判断是否是空链表assert(*head);//判断一个节点的情况if (((*head)->next) == NULL){//直接释放free(*head);*head = NULL;}else{//找尾Pointdef* cur = *head;while (cur->next){cur = cur->next;}//找倒数第二个节点Pointdef* tail = *head;while (cur->next){tail = cur;cur = cur->next;}//释放尾节点free(cur);cur = NULL;//改变倒数第二个的指向tail->next = NULL;}}
三.6头删

有了尾删的基础,头删也一样需要考虑空链表跟链表不存在的情况,先改变头指针指向,然后直接删就行了,也不需要找头,是不是很简单!代码如下:

//头删
void Headdeletion(Pointdef** head)
{//判断链表是否存在assert(head);//判断是否是空链表assert(*head);//创建指针指向头Pointdef* cur = *head;//改变头指针指向*head = (*head)->next;//释放free(cur);cur = NULL;
}
三.7头插

头插管他是不是空链表!直接搞个节点当头就行了!那么我们需要先开一个新节点,再改变头指针指向,代码如下:

//头插
void HeadPointer(Pointdef** head)
{//判断链表是否存在assert(head);//新开节点Pointdef* cur = Greate();//改头指针跟指向cur->next = *head;*head = cur;
}
三.8查找数据

我们只要给某个节点的数据域初始化,那么就可以通过遍历链表来找这个节点的地址,例如我初始化了某个节点的整型数据域为x,那么就直接遍历就行了!

//查找数据
Pointdef* Find(Pointdef* head , int x)
{Pointdef* cur = head;//查找while (cur){if (cur->data==x){return cur;}else{cur = cur->next;}}//否则没有找到return NULL;
}
三.8插在目标节点前面

之前,我们学习了尾插与头插,那么如果在中间插入新节点呢?我们先遍历链表,找到那个节点,然后在它的前面插入新节点即可(自行选择插入的节点前后,我这里例举插在目标节点前面)。这里需要注意的是如果目标节点是第一个节点,那么就是头插,这里需要改变指针指向,不能是形参,因此需要二级指针。根据对应节点的指针找目标节点,这里我假设cur是目标节点代码如下:

void Middle(Pointdef** head ,Pointdef* cur,int x)
{//注意:cur是目标节点//判断空链表,否则是头插if (*head==NULL){HeadPointer(head);}else{//先开新节点Pointdef* news = Greate();//找要增加的节点位置Pointdef* pc = *head;while (pc->next!=cur){pc = pc->next;}//此时pc指向cur的前一个节点//连接节点pc->next = news;news->next = cur;}
}

三.9插在目标节点后面

这个跟前面插在节点前面的类似,但是更简单。借助指针找到目标节点,在连接就行。代码如下:

//插在节点后面
void Outdle( Pointdef* cur)
{//注意:cur是目标节点assert(cur);//开辟新节点Pointdef* news = Greate();Pointdef* pc = cur->next;//连接cur->next = news;news->next = pc;
}
三.10删除目标节点

考虑空链表跟链表不存在两种情况,然后循环找到目标节点,连接左右节点再删除即可!

//删除目标节点
void Omit(Pointdef** head, Pointdef* cur)
{//cur是要删除的节点指针//判断链表是否存在assert(head);//判断空链表assert(*head);//循环找目标节点Pointdef* pc = *head;while (pc->next = cur){pc = pc->next;}//连接目标节点的左右节点pc->next = cur->next;//先连接再释放free(cur);cur = NULL;
}
四.合并代码

main函数思路流程:

#define _CRT_SECURE_NO_WARNINGS
#include"list.h"
#include <stdlib.h>
#include <stdio.h>
int main()
{int x = 0;Pointdef* head = NULL;//开辟节点、尾插(自动连接)int n = 0;printf("请输入节点个数\n");scanf("%d", &n);for (int i = 1; i <= n; i++){Pointdef* newnode = News(head, i);newnode->next = head;head = newnode;printf("head的地址是%p\n", head);}//遍历打印节点的数据域Printf(head);//头插printf("请输入要插入的头插节点数据\n");scanf("%d", &x);Head(&head, x);//删除节点(根据x找节点)printf("请输入要删除的节点数据\n");scanf("%d", &x);Omit(&head, x);//打印Printf(head);return 0;
}

头文件:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
typedef struct Pointdef
{int data;struct Pointdef* next;
}Pointdef;//开辟新节点、尾插
Pointdef* News(Pointdef* head, int x);
//遍历打印数据域
void Printf(Pointdef* head);
//头插
void Head(Pointdef** head, int x);
//删除节点
void Omit(Pointdef** head, int x);

函数实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include "list.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>//开辟新节点
Pointdef* News(Pointdef* head, int x)
{Pointdef* newnode = (Pointdef*)malloc(sizeof(Pointdef));//判断是否开辟成功if (newnode == NULL){perror("malloc");return NULL;}else{//初始化newnode->data = x;newnode->next = NULL;printf("新增成功\n");return newnode;}
}//遍历打印数据域
void Printf(Pointdef* head)
{//断言空链表assert(head);Pointdef* cur = head;//循环打印while (cur){printf("%d ", cur->data);cur = cur->next;}
}//头插
void Head(Pointdef** head, int x)
{//判断空链表assert(head);//调用开辟节点的函数,此时newspoint指向新开辟的节点Pointdef* newspoint = News(*head, x);//判断空链表,如果是,新节点作为第一个节点if (*head == NULL){*head = newspoint;newspoint->next = NULL;newspoint->data = x;}else{//不是空链表的话直接连接插入到第一个节点Pointdef* cur = *head;*head = newspoint;newspoint->next = cur;newspoint->data = x;}printf("插入成功\n");
}//删除节点
void Omit(Pointdef** head, int x)
{//判断链表是否存在assert(head);//判断空链表assert(*head);//根据x找节点Pointdef* cur = *head;//记录该节点Pointdef* pc = cur->next;//如果只有一个节点if (cur->next == NULL){return;}while ((cur->next)->data != x){cur = cur->next;pc = cur->next;}//连接左右节点cur->next = pc->next;free(pc);pc = NULL;printf("删除成功\n");
}

好了,本篇到此结束,大家记得一键三连哦!几天后我会继续出带头双向循环链表的博文!感谢支持!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

相关文章:

线性数据结构:单向链表

放弃眼高手低&#xff0c;你真正投入学习&#xff0c;会因为找到一个新方法产生成就感&#xff0c;学习不仅是片面的记单词、学高数......只要是提升自己的过程&#xff0c;探索到了未知&#xff0c;就是学习。 考虑到可能有小白在合并代码时出现各种细节问题&#xff0c;本文…...

线程互斥同步

前言&#xff1a; 简单回顾一下上文所学&#xff0c;上文我们最重要核心的工作就是介绍了我们线程自己的LWP和tid究竟是个什么&#xff0c;总结一句话&#xff0c;就是tid是用户视角下所认为的概念&#xff0c;因为在Linux系统中&#xff0c;从来没有线程这一说法&#xff0c;…...

《苍穹外卖》项目学习记录-Day11订单统计

根据起始时间和结束时间&#xff0c;先把begin放入集合中用while循环当begin不等于end的时候&#xff0c;让begin加一天&#xff0c;这样就把这个区间内的时间放到List集合。 查询每天的订单总数也就是查询的时间段是大于当天的开始时间&#xff08;0点0分0秒&#xff09;小于…...

SAP HCM 回溯分析

最近总有人问回溯问题&#xff0c;今天把12年总结的笔记在这共享下&#xff1a; 12年开这个图的时候总是不明白是什么原理&#xff0c;教程看N次&#xff0c;网上资料找一大堆&#xff0c;就是不明白原理&#xff0c;后来为搞明白逻辑&#xff0c;按照教材的数据一样做&#xf…...

JavaScript原型链与继承:优化与扩展的深度探索

在 JavaScript 的世界里&#xff0c;万物皆对象&#xff0c;而每个对象都有一个与之关联的原型对象&#xff0c;这就构成了原型链的基础。原型链&#xff0c;简单来说&#xff0c;是一个由对象的原型相互连接形成的链式结构 。每个对象都有一个内部属性[[Prototype]]&#xff0…...

五子棋对弈

问题描述 "在五子棋的对弈中&#xff0c;友谊的小船说翻就翻&#xff1f;" 不&#xff01;对小蓝和小桥来说&#xff0c;五子棋不仅是棋盘上的较量&#xff0c;更是心与心之间的沟通。这两位挚友秉承着"友谊第一&#xff0c;比赛第二"的宗旨&#xff0c;决…...

vue vscode插件推荐安装

在 VSCode 中开发 Vue&#xff0c;推荐安装以下插件&#xff1a; 核心插件 1. Volar&#xff08;Vue Language Features&#xff09; - Vue 3 官方推荐的开发工具&#xff0c;替代 Vetur。 This extension is deprecated. Use the Vue - Official extension instead. 1.Vue …...

Med-R2:基于循证医学的检索推理框架:提升大语言模型医疗问答能力的新方法

Med-R2 : Crafting Trustworthy LLM Physicians through Retrieval and Reasoning of Evidence-Based Medicine Med-R2框架Why - 这个研究要解决什么现实问题What - 核心发现或论点是什么How - 1. 前人研究的局限性How - 2. 你的创新方法/视角How - 3. 关键数据支持How - 4. 可…...

P7497 四方喝彩 Solution

Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1​,a2​,⋯,an​)&#xff0c;有 m m m 个操作&#xff0c;分四种&#xff1a; add ⁡ ( l , r , v ) \operatorname{add}(l,r,v) add(l,r,v)&#xff1a;对于所有 i ∈ [ l , r ] i \in [l,r…...

EtherCAT主站IGH-- 29 -- IGH之mailbox.h/c文件解析

EtherCAT主站IGH-- 29 -- IGH之mailbox.h/c文件解析 0 预览一 该文件功能`mailbox.c` 文件功能函数预览二 函数功能介绍`mailbox.c` 中主要函数的作用1. `ec_slave_mbox_prepare_send`2. `ec_slave_mbox_prepare_check`3. `ec_slave_mbox_check`4. `ec_slave_mbox_prepare_fetc…...

UI线程用到COM只能选单线程模型

无论用不用UI库&#xff0c;哪怕是用Win32 API手搓UI&#xff0c;UI线程要用COM的话&#xff0c;必须初始化为单线程单元(STA)&#xff0c;即CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);&#xff0c;不能用MULTITHREADTHREADED。 实际上&#xff0c;很多(WPF等)UI库若…...

排序算法--桶排序

核心思想为分区间排序后合并。适用于数据均匀分布在一个范围内&#xff0c;或浮点数排序或范围明确的数据。如果需要处理整数或其他数据范围&#xff0c;可以通过调整BUCKET_RANGE的计算方式实现&#xff0c;例如对[0,100)的整数排序&#xff1a; int index arr[i] / 10; // …...

zsh安装插件

0 zsh不仅在外观上比较美观&#xff0c;而且其具有强大的插件&#xff0c;如果不使用那就亏大了。 官方插件库 https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins 官方插件库并不一定有所有的插件&#xff0c;比如zsh-autosuggestions插件就不再列表里&#xff0c;下面演示zs…...

bypass hcaptcha、hcaptcha逆向

可以过steam&#xff0c;已支持并发&#xff0c;欢迎询问&#xff01; 有事危&#xff0c;ProfessorLuoMing...

python-UnitTest框架笔记

UnitTest框架的基本使用方法 UnitTest框架介绍 框架&#xff1a;framework&#xff0c;为了解决一类事情的功能集合 UnitTest框架&#xff1a;是python自带的单元测试框架 自带的&#xff0c;可以直接使用&#xff0c;不需要格外安装 测试人员用来做自动化测试&#xff0c;作…...

掌握API和控制点(从Java到JNI接口)_35 JNI开发与NDK 03

3、 如何载入 .so档案 VM的角色 由于Android的应用层级类别都是以Java撰写的&#xff0c;这些Java类别转译为Dex型式的Bytecode之后&#xff0c;必须仰赖Dalvik虚拟机器(VM: Virtual Machine)来执行之。 VM在Android平台里&#xff0c;扮演很重要的角色。此外&#xff0c;在执…...

计算机组成原理——存储系统(二)

&#x1f331; "人生最深的裂痕&#xff0c;往往是光照进来的地方。 别怕脚下的荆棘&#xff0c;那是你与平庸划清界限的勋章&#xff1b;别惧眼前的迷雾&#xff0c;星辰永远藏在云层之上。真正的强者不是从未跌倒&#xff0c;而是把每一次踉跄都踏成攀登的阶梯。记住&am…...

CDDIS从2025年2月开始数据迁移

CDDIS 将从 2025 年 2 月开始将我们的网站从 cddis.nasa.gov 迁移到 earthdata.nasa.gov&#xff0c;并于 2025 年 6 月结束。 期间可能对GAMIT联网数据下载造成影响。...

VSCode设置内容字体大小

1、打开VSCode软件&#xff0c;点击左下角的“图标”&#xff0c;选择“Setting”。 在命令面板中的Font Size处选择适合自己的字体大小。 2、对比Font Size值为14与20下的字体大小。...

嵌入式学习---蜂鸣器篇

1. 蜂鸣器分类 蜂鸣器是一种电子发声器件&#xff0c;采用直流电压供电&#xff0c;能够发出声音。广泛应用于计算机、打印机、报警器、电子玩具等电子产品中作为发声部件。一般仅从外形不易分辨蜂鸣器的种类。但是有些蜂鸣器使用广泛&#xff0c;见得多了就很容易分辨。例如常…...

【优先算法】专题——前缀和

目录 一、【模版】前缀和 参考代码&#xff1a; 二、【模版】 二维前缀和 参考代码&#xff1a; 三、寻找数组的中心下标 参考代码&#xff1a; 四、除自身以外数组的乘积 参考代码&#xff1a; 五、和为K的子数组 参考代码&#xff1a; 六、和可被K整除的子数组 参…...

【Linux】使用管道实现一个简易版本的进程池

文章目录 使用管道实现一个简易版本的进程池流程图代码makefileTask.hppProcessPool.cc 程序流程&#xff1a; 使用管道实现一个简易版本的进程池 流程图 代码 makefile ProcessPool:ProcessPool.ccg -o $ $^ -g -stdc11 .PHONY:clean clean:rm -f ProcessPoolTask.hpp #pr…...

使用Express.js和SQLite3构建简单TODO应用的后端API

使用Express.js和SQLite3构建简单TODO应用的后端API 引言环境准备代码解析1. 导入必要的模块2. 创建Express应用实例3. 设置数据库连接4. 初始化数据库表5. 配置中间件6. 定义数据接口7. 定义路由7.1 获取所有TODO项7.2 创建TODO项7.3 更新TODO项7.4 删除TODO项 8. 启动服务器 …...

Vue3 表单:全面解析与最佳实践

Vue3 表单&#xff1a;全面解析与最佳实践 引言 随着前端技术的发展&#xff0c;Vue.js 已经成为最受欢迎的前端框架之一。Vue3 作为 Vue.js 的最新版本&#xff0c;带来了许多改进和新的特性。其中&#xff0c;表单处理是 Vue 应用中不可或缺的一部分。本文将全面解析 Vue3 …...

找不到msvcp140.dll解决方法

您可以尝试以下方案进行修复&#xff0c;看看是否可以解决这个问题&#xff1a; 一、重新注册 msvcp140.dll 运行库文件&#xff1a; “WinR”打开运行&#xff0c;键入&#xff1a;regsvr32 MSVCP140.dll&#xff0c;回车即可&#xff1b; 如果出现找不到该文件的提示&…...

【优先算法】专题——位运算

在讲解位运算之前我们来总结一下常见的位运算 一、常见的位运算 1.基础为运算 << &&#xff1a;有0就是0 >> |&#xff1a;有1就是1 ~ ^&#xff1a;相同为0&#xff0c;相异位1 /无进位相加 2.给一个数 n&#xff0c;确定它的二进制表示…...

【Cadence仿真技巧学习笔记】求解65nm库晶体管参数un, e0, Cox

在设计放大器的第一步就是确定好晶体管参数和直流工作点的选取。通过阅读文献&#xff0c;我了解到L波段低噪声放大器的mos器件最优宽度计算公式为 W o p t . p 3 2 1 ω L C o x R s Q s p W_{opt.p}\frac{3}{2}\frac{1}{\omega LC_{ox}R_{s}Q_{sp}} Wopt.p​23​ωLCox​Rs…...

AI模型升级版0.03

以下是一个升级版的代码实现&#xff0c;结合了最新的技术趋势&#xff0c;例如强化微调&#xff08;Reinforcement Fine-Tuning&#xff09;和一些优化改进&#xff0c;以提升模型的性能和易用性。以下是升级后的代码&#xff1a; 步骤 1&#xff1a;安装必要的库 确保安装了…...

Docker入门篇(Docker基础概念与Linux安装教程)

目录 一、什么是Docker、有什么作用 二、Docker与虚拟机(对比) 三、Docker基础概念 四、CentOS安装Docker 一、从零认识Docker、有什么作用 1.项目部署可能的问题&#xff1a; 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会碰到一些问题&#xff1…...

开源智慧园区管理系统对比其他十种管理软件的优势与应用前景分析

内容概要 在当今数字化快速发展的时代&#xff0c;园区管理软件的选择显得尤为重要。而开源智慧园区管理系统凭借其独特的优势&#xff0c;逐渐成为用户的新宠。与传统管理软件相比&#xff0c;它不仅灵活性高&#xff0c;而且具有更强的可定制性&#xff0c;让各类园区&#…...