【数据结构】单向链表(真正的零基础)
放弃眼高手低,你真正投入学习,会因为找到一个新方法产生成就感,学习不仅是片面的记单词、学高数......只要是提升自己的过程,探索到了未知,就是学习。
目录
一.链表的理解
二.链表的分类(重点理解)
三.无头单向非循环链表
三.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将第一个节点的地址传给cur,cur是指向下一个节点的地址的,当cur到达最后一个节点时,此时cur的指向还是最后一个节点的地址,而cur->next已经是指向空指针了,就少进入了循环一次。cur是通过cur->next来改变cur的指向的,而cur->next已经指向下一个节点了。这个我们结合2张图理解(物理图示与逻辑图示):
三.3尾插
我们从字面上理解,就是在当前最后一个节点后面再插入新节点!再把新节点与尾节点连接起来,那么我们需要用3个操作:找尾节点 开辟新节点 连接节点 (最后会对3个步骤汇总代码讲思路)
先解释一下为什么要用二级指针?因为涉及空链表跟链表存在的讨论,需要更改地址的问题,
*head表示头指针本身,用来判断空指针(也就是空链表),空链表也就是*head==NULL
head表示头指针的地址,用来判断链表的存在与否,链表不存在也就是head==NULL
下面我们进行第一个操作,找尾节点。我们已经知道如何遍历链表了!既然指针指向下一个节点的地址,那么当这个指针是空指针的时候,是不是就表示找到尾了!这里需要理解的是,这个指针的指向是NULL,因为我们是需要开辟新节点的,自然要在最后一个节点后面开辟,所以需要让它下一个节点指向NULL,如图理解(tail跟cur等价):
//找尾节点
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");
}
好了,本篇到此结束,大家记得一键三连哦!几天后我会继续出带头双向循环链表的博文!感谢支持!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
相关文章:
【数据结构】单向链表(真正的零基础)
放弃眼高手低,你真正投入学习,会因为找到一个新方法产生成就感,学习不仅是片面的记单词、学高数......只要是提升自己的过程,探索到了未知,就是学习。 目录 一.链表的理解 二.链表的分类(重点理解…...
8. k8s二进制集群之Kubectl部署
创建kubectl证书请求文件生成admin证书文件复制admin证书到指定目录生成kubeconfig配置文件接下来完成kubectl配置文件的角色绑定【扩展】kubectl命令补全操作继续上一篇文章《k8s二进制集群之Kube ApiServer部署》下面介绍一下k8s中的命令行管理工具kubectl。 通过kubectl可以…...
115,【7】 攻防世界 web fileinclude
进入靶场 试着访问了几个文件,都没得到信息,f12看看源码 还真有 <?php // 检查是否开启了错误显示功能 // ini_get 函数用于获取 PHP 配置选项的值,这里检查 display_errors 选项是否开启 if( !ini_get(display_errors) ) {// 如果错误…...
RabbitMQ 从入门到精通:从工作模式到集群部署实战(二)
接上篇:《RabbitMQ 从入门到精通:从工作模式到集群部署实战(一)》 链接 文章目录 4.安装RabbitMQ Messaging Topology Operator 裸金属环境部署RabbitMQ部署单实例部署集群 4.安装RabbitMQ Messaging Topology Operator 使用 cer…...
【MySQL】MySQL经典面试题深度解析
文章目录 一、MySQL与C的深度结合1.1 为什么C项目需要MySQL?1.2 典型应用场景 二、基础概念面试题精讲2.1 存储引擎对比2.2 索引原理 三、C专项面试题解析3.1 连接池实现3.2 预处理语句3.3 批量操作优化 四、高级应用面试题剖析4.1 事务隔离级别4.2 锁机制详解4.3 查…...
小程序-基础加强
前言 这一节把基础加强讲完 1. 导入需要用到的小程序项目 2. 初步安装和使用vant组件库 这里还可以扫描二维码 其中步骤四没什么用 右键选择最后一个 在开始之前,我们的项目根目录得有package.json 没有的话,我们就初始化一个 但是我们没有npm这个…...
vscode+CMake+Debug实现 及权限不足等诸多问题汇总
环境说明 有空再补充 直接贴两个json tasks.json {"version": "2.0.0","tasks": [{"label": "cmake","type": "shell","command": "cmake","args": ["../"…...
零基础Vue入门6——Vue router
本节重点: 路由定义路由跳转 前面几节学习的都是单页面的功能(都在专栏里面https://blog.csdn.net/zhanggongzichu/category_12883540.html),涉及到项目研发都是有很多页面的,这里就需要用到路由(vue route…...
【疑海破局】一个注解引发的线上事故
【疑海破局】一个注解引发的线上事故 1、问题背景 在不久前一个阳光明媚的上午,我的思绪正在代码中游走、双手正在键盘上飞舞。突然,公司内部通讯工具上,我被拉进了一个临时工作群,只见群中产品、运营、运维、测试等关键人员全部严阵以待,我就知道大的可能要来了。果不其…...
C语言:函数栈帧的创建和销毁
目录 1.什么是函数栈帧2.理解函数栈帧能解决什么问题3.函数栈帧的创建和销毁的过程解析3.1 什么是栈3.2 认识相关寄存器和汇编指令3.3 解析函数栈帧的创建和销毁过程3.3.1 准备环境3.3.2 函数的调用堆栈3.3.3 转到反汇编3.3.4 函数栈帧的创建和销毁 1.什么是函数栈帧 在写C语言…...
IDEA启动项目慢问题处理
IDEA启动项目慢问题处理 一、问题现象二、问题排查排查点1:idea内存排查点2:应用内存排查点3:shorten command lineclasspath filejar manifest 排查点4:jstack排查 三、问题定位 一、问题现象 多模块工程,启动模块为…...
Denavit-Hartenberg DH MDH坐标系
Denavit-Hartenberg坐标系及其规则详解 6轴协作机器人的MDH模型详细图_6轴mdh-CSDN博客 N轴机械臂的MDH正向建模,及python算法_mdh建模-CSDN博客 运动学3-----正向运动学 | 鱼香ROS 机器人学:MDH建模 - 哆啦美 - 博客园 机械臂学习——标准DH法和改进MDH…...
Unity 快速入门 1 - 界面操作
本项目将快速介绍 Unity 6的基本操作和功能,下载附件的项目,解压到硬盘,例如 D:\Unity Projects\, 注意整个文件路径中只有英文、空格或数字,不要有中文或其他特殊符合。 1. 打开Unity Hub,点击右上角的 O…...
美国网络司令部军事网络指挥框架战略转型与挑战分析
文章目录 前言一、框架核心内容:从分散到集中,构建标准化作战体系二、指挥体系重构:权责明晰与集中化管控三、风险管理创新:从被动防御到主动备战四、对美军网络作战的影响总结 前言 2024年9月,美国网络司令部发布《国…...
9-收纳的知识
[ComponentOf(typeof(xxx))]组件描述,表示是哪个实体的组件 [EntitySystemOf(typeof(xxx))] 系统描述 [Event(SceneType.Demo)] 定义事件,在指定场景的指定事件发生后触发 [ChildOf(typeof(ComputersComponent))] 标明是谁的子实体 [ResponseType(na…...
Linux 压缩打包
Linux压缩打包 文章目录 Linux压缩打包压缩的意义和原理压缩的意义压缩的原理压缩与解压缩的好处压缩打包命令.zipzip 命令用法unzip 的用法.gzgzip 的用法gunzip 的用法.bz2bzip2 的用法bunzip2 的用法.xzxz 命令用法tar04-Linux压缩打包课后习题压缩的意义和原理 压缩的意义…...
排序算法--堆排序
堆排序是一种高效的排序算法,适合大规模数据排序,尤其适用于需要实时获取最大(或最小)值的场景。 // 交换两个元素的值 void swap(int* a, int* b) {int temp *a;*a *b;*b temp; }// 调整堆,使其满足堆的性质 void …...
51c视觉~CV~合集10
我自己的原文哦~ https://blog.51cto.com/whaosoft/13241694 一、CV创建自定义图像滤镜 热图滤镜 这组滤镜提供了各种不同的艺术和风格化光学图像捕捉方法。例如,热滤镜会将图像转换为“热图”,而卡通滤镜则提供生动的图像,这些图像看起来…...
【数据结构】(6) LinkedList 链表
一、什么是链表 1、链表与顺序表对比 不同点LinkedListArrayList物理存储上不连续连续随机访问效率O(N)O(1)插入、删除效率O(1)O(N) 3、链表的分类 链表根据结构分类,可分为单向/双向、无头结点/有头节点、非循环/循环链表,这三组每组各取…...
使用 Axios 获取用户数据并渲染——个人信息设置
目录 1. HTML 部分(前端页面结构) HTML 结构解析: 2. JavaScript 部分(信息渲染逻辑) JavaScript 解析: 3. 完整流程 4. 总结 5. 适用场景 本文将介绍如何通过 Axios 从服务器获取用户信息࿰…...
【hudi】基于hive2.1.1的编译hudi-1.0.0源码
hudi版本1.0.0 需要使用较低版本的hive,编译hudi只需要修改下类即可: org.apache.hudi.hadoop.hive.HoodieCombineHiveInputFormat 一、复制org.apache.hadoop.hive.common.StringInternUtils 找个hive2.3.9的源码包,创建包路径,…...
物联网领域的MQTT协议,优势和应用场景
MQTT(Message Queuing Telemetry Transport)作为轻量级发布/订阅协议,凭借其低带宽消耗、低功耗与高扩展性,已成为物联网通信的事实标准。其核心优势包括:基于TCP/IP的异步通信机制、支持QoS(服务质量&…...
MyBatis 调优指南:释放持久层性能潜力
MyBatis 作为一款优秀的持久层框架,以其灵活性和易用性深受开发者喜爱。然而,随着应用规模扩大和数据量增长,MyBatis 的性能问题也逐渐显现。本文将深入探讨 MyBatis 调优策略,帮助您释放持久层性能潜力。 一、 SQL 语句优化 避免…...
Unity扩展编辑器使用整理(一)
准备工作 在Unity工程中新建Editor文件夹存放编辑器脚本, Unity中其他的特殊文件夹可以参考官方文档链接,如下: Unity - 手册:保留文件夹名称参考 (unity3d.com) 一、菜单栏扩展 1.增加顶部菜单栏选项 使用MenuItemÿ…...
注册中心不知选哪个?Zookeeper、Eureka、Nacos、Consul和Etcd 5种全方位剖析对比
本文给大家讲解 5 种常用的注册中心,对比其流程和原理,无论是面试还是技术选型,都非常有帮助。 对于注册中心,在写这篇文章前,我其实只对 ETCD 有比较深入的了解,但是对于 Zookeeper 和其他的注册中心了解甚…...
Windows下怎么安装FFFmpeg呢?
在Windows下使用Open-webui报错,说Couldnt find ffmpeg or avconv,解决open-webui报错Couldn‘t find ffmpeg or avconv-CSDN博客于是尝试解决问题,那么Windows下怎么安装FFFmpeg呢? 尝试了两种方法。 第一种方法pip安装(失败&…...
CSS 基础:层叠、优先级与继承
CSS 基础:层叠、优先级与继承 一、层叠(Cascade)示例:层叠的顺序 二、优先级(Specificity)优先级规则示例:优先级的比较 三、继承(Inheritance)哪些属性会被继承…...
《翻转组件库之发布》
背景 继《翻转组件库之打包》_杨晓风-linda的博客-CSDN博客之后,组件库已经可以正常构建,那如何像elementUI等组件库那样,用npm安装,按照既定的用法使用即可呢?本篇便为你揭晓 资料相关 1、npm官方文档:…...
Spring Boot + Spring AI快速体验
Spring AI快速体验 1 什么是Spring AI 主要功能 2 快速开始 2.1 版本说明2.2 配置文件2.3 pom依赖 2.3.1 spring maven仓库2.3.2 核心依赖 2.4 定义ChatClient2.5 启动类2.6 测试 3 参考链接 1 什么是Spring AI Spring AI是Spring的一个子项目,是Spring专门面向于…...
windows linux常用基础命令
windows基础命令 cd …/ (访问D盘 直接D: 进入目录cd…\baidudu) color 2 改变颜色 dir 浏览当前目录中有什么内容 例如 dir windows可以浏览windows中有什么文件 cls 清屏 cd windows 可以跳转到c盘目录的下面 cd…/可以返回到上一级目录 ./当前目录 cd \ 直…...
ZooKeeper单节点详细部署流程
ZooKeeper单节点详细部署流程 文章目录 ZooKeeper单节点详细部署流程 一.下载稳定版本**ZooKeeper**二进制安装包二.安装并启动**ZooKeeper**1.安装**ZooKeeper**2.配置并启动**ZooKeeper** ZooKeeper 版本与 JDK 兼容性3.检查启动状态4.配置环境变量 三.可视化工具管理**Zooke…...
【AI日记】25.02.06
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI kaggle 比赛:Backpack Prediction Challenge 读书 书名:理解公司:产权、激励与治理作者:张维迎下图感想:哲学家、思想家比如卢梭…...
税费学习之:附加税费
好的!我将从 **税收本质、历史沿革、用途逻辑、企业影响** 四个维度综合分析,用项目管理中的实际场景说明为什么需要缴纳附加税费。 --- ### **一、附加税费的本质与构成** #### **1. 定义** 附加税费是 **以增值税、消费税为基数征收的附加税**&…...
数据库开发常识(10.6)——SQL性能判断标准及索引误区(1)
10.6. 数据库开发常识 作为一名专业数据库开发人员,不但需要掌握数据库开发相关的语法和功能实现,还要掌握专业数据库开发的常识。这样,才能在保量完成工作任务的同时,也保质的完成工作任务,避免了为应用的日后维护埋下性能和稳定性方面的隐患。可遗憾的是,现实中,很大…...
网络原理一>数据链路层协议->以太网协议
目录 以太网协议的结构:类型:ARP请求应答报文:CRC:MTU: 为什么需要mac地址:mac地址和IP地址的区别: 以太网协议的结构: 以太网是数据链路层和物理层的主要协议 源IP,目的IP就不多说…...
Android 约束布局ConstraintLayout整体链式打包居中显示
Android 用约束布局ConstraintLayout实现将多个控件视作一个整体居中显示,使用 app:layout_constraintHorizontal_chainStyle"packed"实现 chain 除了链条方向有横向和竖向区分外, chain链条上的模式有 3种 spread - 元素将被展开&#…...
云计算行业分析
云计算作为数字经济的核心基础设施,未来十年将持续重塑全球科技格局,并渗透到几乎所有行业的数字化转型中。 一、云计算的发展潜力 1. 技术融合驱动爆发式创新 AI与云计算的深度耦合 - **智能云服务**:云厂商将提供预训练模型、自动化ML工…...
深入浅出DeepSeek LLM 以长远主义拓展开源语言模型
深入浅出地讲解DeepSeek LLM 以长远主义拓展开源语言模型 🌟 1. 什么是 DeepSeek LLM? 大家想象一下,你在游戏里要打造一个超级英雄角色,选择最强的装备、技能点和升级策略。那么,DeepSeek LLM 就是 AI 界的“超级英雄…...
用Python获取股票数据并实现未来收盘价的预测
获取数据 先用下面这段代码获取上证指数的历史数据,得到的csv文件数据,为后面训练模型用的 import akshare as ak import pandas as pd# 获取上证指数历史数据 df ak.stock_zh_index_daily(symbol"sh000001")# 将数据保存到本地CSV文件 df.…...
[openwrt]openwrt slaac only模式下部分终端无法获取到IPv6 DNS
问题描述 OpenWrt 中,如果启用了 RA 单播(ra_unicast),但部分终端无法获取到 DNS 信息 问题分析 RA 单播的局限性 并非所有终端都完全支持通过单播接收 RA 消息。部分终端可能无法正确解析单播 RA 中的 RDNSS(Recursive DNS Server)选项,从而导致无法获取 DNS 信息。终…...
【redis】数据类型之list
Redis的List数据类型是一个双向链表,支持在链表的头部(left)和尾部(right)进行元素的插入(push)和弹出(pop)操作。这使得List既可以用作栈(stack)…...
电脑连接wifi但是浏览器打开不了网页,使用手机热点能正常使用
电脑连接wifi但是浏览器打开不了网页,使用手机热点能正常使用 打开控制面板 打开网络和Internet(查看网络状态和任务) 点击更改适配器设置 双击WLAN 点击属性并双击打开Internet 协议版本4(TCP/IPv4) 将自动…...
el-table中的某个字段最多显示两行,超出部分显示“...详情”,怎么办
文章目录 背景需求需求分析 解决方案在线体验灵感来源我的实现方案 总结 背景 需求 比如,有如下一个表格,请你实现它: 要求: 最多显示两行超出部分显示为:“…详情”点击详情,展开全部内容 说明&#x…...
Vue el-input密码输入框 按住显示密码,松开显示*;阻止浏览器密码回填,自写密码输入框;校验输入非汉字内容;文本框聚焦到内容末尾;
输入框功能集合 <template><div style"padding: 10px"><!-- 密码输入框 --><el-input:type"inputType"v-model"password"placeholder"请输入密码"auto-complete"new-password"id"pwd"style…...
尚硅谷课程【笔记】——大数据之Shell【一】
课程视频:【【尚硅谷】Shell脚本从入门到实战】 一、Shell概述 为什么要学习Shell? 1)需要看懂运维人员的Shell程序 2)偶尔编写一些简单的Shell程序来管理集群、提高开发效率 什么是Shell? 1)Shell是一…...
4年测试|20-30K|金山办公|大模型测开3轮面经
一面时间:面试时长一小时左右 二面时间:面试时长基本满一小时 HR面时间:面试流程上全部结束了,内容如下: 前言: 岗位:自己想投递base珠海,金山办公的HR捞了下。why choose Zhuha…...
【负载均衡式在线OJ】实现负载均衡
目录 管理服务器 增加负载 && 减少负载 重置负载 && 获得负载 负载均衡 添加配置信息 什么是负载均衡 如何实现? 管理服务器 增加负载 && 减少负载 客户端访问一次服务器,负载就加1。客户端结束访问服务器,…...
网络安全-防御 第一次作业(由于防火墙只成功启动了一次未补截图)
防火墙安全策略课堂实验报告 一、拓扑 本实验拓扑包含预启动设备、DMZ区域(含OA Server和Web Server)、防火墙(FW1)、Trust区域(含办公区PC和生产区PC)等。具体IP地址及连接关系如给定拓扑图所示…...
大数据挖掘--两个角度理解相似度计算理论
文章目录 0 相似度计算可以转换成什么问题1 集合相似度的应用1.1 集合相似度1.1文档相似度1.2 协同过滤用户-用户协同过滤物品-物品协同过滤 1.2 文档的shingling--将文档表示成集合1.2.1 k-shingling1.2.2 基于停用词的 shingling 1.3 最小哈希签名1.4 局部敏感哈希算法&#…...
【Mybatis Plus】JSqlParser解析sql语句
【Mybatis Plus】JSqlParser解析sql语句 【一】JSqlParser 是什么【二】JSqlParser 的安装步骤【三】使用场景【1】sql语句解析【2】SQL 语句转换【3】SQL 语句生成【4】SQL 语句验证 【四】在使用 JSqlParser 时,如何处理 SQL 注入攻击?【1】使用预编译…...