数据结构————链表

news2024/11/25 16:29:20

 一、引言

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

要想学好数据结构 一定要画图 

下面我们要实现链表 首先我们应该仔细想想链表是什么  为什么使用链表 使用链表与顺序表相比有什么不同吗 如果不用有那些不同能  有为什么通过顺序表引入链表  学习链表应该掌握哪些知识  又是什么思想  接下来我们实现链表  链表的基本功能 增删改查   顺序表存在什么缺点呢?接下来我们好好分析以上问题

 二、链表的概念及结构

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

其实就是内存块  需要利用指针来链接起来

在设计链表之前应该先知道链表的样子 在我们假象中的样子 与实际的样子 为了更好的理解代码 我们应该经常画图构思 画图 就是逻辑结构 还有真实的物理图

物理结构:真实的数据在内存中的变化

逻辑结构:我们想象出的箭头。实际上没有cur在动。

三、链表的实现

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;//结构体指针
}SLTNode;

定义结构体的中查找规则是指针类型

1.1链表的打印

void SLTNode(SLTNode* phead)
{
	SLTNode* cur = phead;
	//while(cur->next!=NULL)
	//while(cur!=NULL)
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

指针phead指向第一个结点  phead赋值给cur物理上就是把phead的值给cur 就如同cur有个箭头指向1的结点,cur的值不断被覆盖,也是逻辑上箭头移动的过程

next表示下一个结点的地zhi  循环变量 

phead赋值给cur物理上就是把phead的值给cur 就如同cur有个箭头指向1的结点,cur的值不断被覆盖,也是逻辑上箭头移动的过程

1.1.1链表打印与顺序表打印对比 

void SLPrint(SL* PS)
{
	assert(ps);
	for (int i = 0; i < ps->size; ++i)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

 顺序表

1.1.2链表为什么不断言

如果链表为NULL,那就是指针为NULL,链表是一个一个的结点,每个结点存下一个地址,每个结点就是一个结构以体,就是一个内存。可以打印空的数据。

1.2顺序表为什么断言

顺序表 指针指向结构体,数据不是存在结构体里,数据存在一段空间a,结构体的指针不能为NULL,要么就没法判断了,访问size判断顺序表是不是空,如果指针都是空了,就没办法访问size

如果size是0 就没有数据 没法访问

1.1.3  为什么不能用cur++代替cur=cur->next

由于链表的每一个结点都是单独malloc出来的,只有当malloc一次开辟很多个的时候才能保证空间是连续的,链表不能保证是连续的。

我们知道cur是指针 指向的是下一个结点的地址 要想实现对链表内容的打印 就要一个结点一个结点的访问,每一个结点存下一个结点的地址 通过指针访问每个结点,然后通过cur->next!=NULL判断访问结束。由于最后一个元素没有打印  next下一个结点的地址为NULL

注意:循环语句不能使用cur++ 代替cur=cur->next 由于不能保证是连续的对于顺序表而言 他里面的数据放在一段空间中,他是连续的  可以用通过移动指针来遍历数组的内容  然而 对于一个链表来说 不能保证结点与另一个的地址是连续的 ,因为是malloc开辟的空间 地址可能连续 也可能不连续

1.2、链表的尾插

在实现尾插之前我们应先了解函数的传参  了解了这个 就会很容易的理解链表的尾插 思想类似

接下来我们看几组代码  观察一下有什么不同

1.2.1分析以下函数的调用

void Func(int y)
{
	y = 1;
}

int main()
{
	int x = 0;
	Func(x);
	return 0;
}
void Func(int *p)
{
	*p = 1;
}

int main()
{
	int x = 0;
	Func(&x);
	return 0;
}

void Func(int* ptr)
{
	ptr = (int*)malloc(sizeof(int));
}

int main()
{
	int* px = NULL;
	Func(px);
  free(px);
 

	return 0;
}
void Func(int** pptr)
{
	*pptr = (int*)malloc(sizeof(int));
}

int main()
{
	int* px = NULL;
	Func(&px);

	free(px);

	return 0;
}

  

1.2.2对以上函数解析 

二级指针传参,二级指针接收

从根本上来讨论,地址是不变的,不像是传值调用在内存中开辟的不是同一块空间,就会导致函数用一下就没了  如果打印实参的值 是没有改变的 

注意:想改变int,就是用int*的指针

通过C语言的学习我们很清楚的知道 函数在传参的过程中 形参是实参的临时拷贝  由于局部变量存放在内存的栈区 函数在使用过程中它的生命周期是 调用的时候作用 出作用域销毁 这种情况是传值调用  还有传址调用   实参传地址  形参就应该用指针接收  还应该注意 对于链表插入一个结点  就 既然插入新的结点 就代表着 这个链表是该变的 只要是改变的 如果要传int类型 就应该用int*的指针接收 才能保证函数调用完不会销毁  因为在内存malloc开辟的空间 才可以一直使用 变成自己的   我们通过几组图片 一次对应上方代码  就会很容易理解 

对于修改结点  多了一个少一个这种情况  还需要具体函数  实现具体功能  也就是说  传个二级指针   只是为了在以后实现链表多了一个少了一个问题的基础上  能够合理实现  不至于功能是写的挺好  但是没用到地方  就是传地址挺重要的  为了真正实现功能  

SLTNode* BuySLTNode(SLTDataType x)
{
    assert(pphead);
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
}
void  SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = nnewnode;
	}
	else
	{
		//找尾
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail ->next=newnode;
	}
}

1.2.3链表尾插代码解析

tail = tail->newnode;

找尾的特征  是tail->next == NULL

newnode是指针变量 存的是结点的地址

尾插的实质是:尾插的结点要存储新的结点的地址

对于

if (*pphead == NULL)
    {
        *pphead = nnewnode;
    }
    else
    {
        //找尾
        SLTNode* tail = *pphead;
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
     tail ->next=newnode;
    
    }
}   

else以下代码 意思是  如果tail的下一个结点不能空  就一直找下一个结点 直到下一个结点为空  由于要改变结构体 就用结构体的指针  指向newnode 根据图片可知 也不需要利用二级指针   为什么不要改变tail的类型 就是因为该函数的调用 对于实参和形参的改变没有影响 以至于出了作用域销毁 对于链表没有什么影响  只能说函数想要实现的内容不同 要根据具体情况具体分析  最主要和和上面的区别 对于改变谁要搞清楚  上面是改变SLTNode*    就要用SLTNode**的指针接收  改变的是整个链表 这个改变的是newnode

*ppead就是plist的地址 

1.3、链表的头插

void SLTPushFront(SLTNode**pphead,SLTDataType x)
{
    assert(pphead);
	SLTNode* newnode = BuySLTNode(x);
	newnide->next = *pphead;
	*pphead = newnode;
}

  为空插入是合理的就不用断言   传的是二级指针 解引用是原先传进来的那个一级指针的地址 

1.4、链表的尾删

void SLTPopBack(SLTNode** pphead)
{
	//暴力检查
    assert(pphead);
	assert(*pphead);

	//只有一个结点  
	//多个结点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail -> tail->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

 注意

错误代码如下

void SLTPopBack(SLTNode** pphead)
{
	//暴力检查
    assert(pphead);
	assert(*pphead);

	//只有一个结点  
	//多个结点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		free(tail);
		tail NULL;
	}
}

这种代码插入时就会出现下图的情况 

说明 由于释放掉了  前一个结点的next变成野指针  由于释放掉了   通过调试我们也可以发现问题  应该释放tail->next->next  ;避免把前一个结点变成野指针

 

正确写法是找了一个假的尾  避免了空指针的问题

还应该注意链表为空的情况  如果删除一个数据就不合理 所以需要断言  但是 为空插入是合理的就不用断言

 1.5、链表的头删

SLTNode* SLTPopFront(SLTNode** pphead)
{
    assert(pphead);
	assert(*pphead);
	SLTNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}

1.6、链表的查找(修改)

void SLTFind(SLTNode* phead, SLTSDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}


1.7、链表在pos位置之前插入

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead);
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	//找到pos前一个位置
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

  

注意

 默认pos能找到 找不到则出现空指针问题  

pphead是plist的地址  地址不能为空  需要断言

plist 可能为NULL

pphead一定不是NULL

所以pphead需要断言  防止不小心传错 数据结构这段期间的断言与C语言有所不同 要具体问题具体分析    最重要的问题是  空链表可以打印 刻印插入 无需断言 

空链表不能删除所以需要

assert(*pphead)

1.8、链表在pos位置之后删除

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(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=NULL;由于形参的改变 对于实参没有影响  以至于 不需要  其实也可以传SLTNode**pos; 但是形式上不美观  可以在操作的时候 在将其置空 

1.9、链表在pos位置之后插入

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

 

1.10、链表在pos位置删除

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

四、完整代码 

 SList.h  SList.c text.c   

//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);
void SLTPopBack(SLTNode** PPhead);
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLTErase(SLTNode** pphead, SLTNode* pos);
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
void SLTEraseAfter(SLTNode* pos);
//SList.c
#define _CRT_SECURE_NO_WARNINGS 
#include"SList.h"
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	//while(cur->next!=NULL)
	//while(cur!=NULL)
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
}
void  SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next=newnode;
	}
}

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead)
{
	//暴力检查
	assert(*pphead);
	//只有一个结点  
	//多个结点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}
void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);
	SLTNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead);
	
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	//找到pos前一个位置
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		//找到pos前一个位置
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//pos位置后面删除
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}
//text.c
#define _CRT_SECURE_NO_WARNINGS 
#include"SList.h"
void TestSList1()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	printf("链表的尾插\n");
	SLTPrint(plist);
	SLTPushFront(&plist, 8);
	printf("链表的头插\n");
	SLTPrint(plist);
	SLTPopFront(&plist);
	printf("链表的头删\n");
	SLTPrint(plist);
	SLTPopBack(&plist);
	printf("链表的尾删\n");
	SLTPrint(plist);
	// 值为2那个节点  *2
	SLTNode* ret = SLTFind(plist, 2);
	ret->data *= 8;
	printf("链表的查找并修改\n");
	SLTPrint(plist);
	SLTInsert(&plist, ret, 66);
	printf("链表的某位置之前插入\n");
	SLTPrint(plist);

	SLTInsertAfter(ret, 88);
	printf("链表的某位置之后插入\n");
	SLTPrint(plist);
	SLTEraseAfter(ret);
	printf("链表的某位置之后删除\n");
	SLTPrint(plist);
	SLTErase(&plist, ret);
	ret = NULL;
	printf("链表的某位删除\n");
	SLTPrint(plist);
 }
int main()
{
	TestSList1();
	return 0;
}

五、运行结果 

 谢谢观看  

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

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

相关文章

【网络原理】深入理解关于HTTP协议和报文的格式以及重要的属性

前言 &#x1f31f;&#x1f31f;本期讲解关于HTTP协议的重要的机制~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

【MyBatis源码】CacheKey缓存键的原理分析

文章目录 Mybatis缓存设计缓存KEY的设计CacheKey类主体CacheKey组成CacheKey如何保证缓存key的唯一性 Mybatis缓存设计 MyBatis 每秒过滤众多数据库查询操作&#xff0c;这对 MyBatis 缓存键的设计提出了很高的要求。MyBatis缓存键要满足以下几点。 无碰撞&#xff1a;必须保证…

面试题:JVM(二)

1. 面试题 简述 Java 类加载机制?&#xff08;百度&#xff09; JVM类加载机制 &#xff08;滴滴&#xff09; JVM中类加载机制&#xff0c;类加载过程&#xff0c;什么是双亲委派模型&#xff1f; &#xff08;腾讯&#xff09; JVM的类加载机制是什么&#xff1f; &#x…

数据库的使用02:SQLServer的连接字符串、备份、还原、SQL监视相关设置

目录 一、连接字符串 【本地连接字符串】 【远程连接字符串】 二、备份 三、还原 &#xff08;1&#xff09;还原数据库-bak、btn文件 &#xff08;2&#xff09;附加数据库mdf文件 四、SQL监视器的使用 一、连接字符串 【本地连接字符串】 server DESKTOP-FTH2P3S; Da…

【2024工业图像异常检测文献】UCAD: 使用对比学习提示的无监督连续异常检测方法

Unsupervised Continual Anomaly Detection with Contrastively-learned Prompt 1、Background 无监督异常检测&#xff08;UAD&#xff09;专注于在没有先验知识或标记实例的情况下识别数据中的不寻常模式或异常值&#xff0c;仅依赖于“正常”数据的内在分布&#xff08;Cha…

【算法】Floyd多源最短路径算法

目录 一、概念 二、思路 三、代码 一、概念 在前面的学习中&#xff0c;我们已经接触了Dijkstra、Bellman-Ford等单源最短路径算法。但首先我们要知道何为单源最短路径&#xff0c;何为多源最短路径 单源最短路径&#xff1a;从图中选取一点&#xff0c;求这个点到图中其他…

[C++]——哈希(附源码)

目录 ​编辑 ​编辑 一、前言 二、正文 2.1 unorder系列关联式容器 2.1.1 unordered_map 2.1.1.1 unorderer_map的介绍 ①unordered_map的构造 ②unordered_map的容量 ③unordered_map的迭代器 ④unordered_map的元素访问 ⑤unordered_map的查询 ⑥unordered_map的修改操…

使用Ubuntu快速部署MinIO对象存储

想拥有自己的私有云存储&#xff0c;安全可靠又高效&#xff1f;MinIO是你的理想选择&#xff01;这篇文章将手把手教你如何在Ubuntu 22.04服务器上部署MinIO&#xff0c;并使用Nginx反向代理和Let’s Encrypt证书进行安全加固。 即使你是新手&#xff0c;也能轻松完成&#xf…

Maven 下载配置 详解 我的学习笔记

Maven 下载配置 详解 我的学习笔记 一、Maven 简介二、maven安装配置三、maven基本使用四、idea配置mavenidea配置maven环境maven坐标idea创建maven项目配置Maven-Helper插件 五、依赖管理 一、Maven 简介 Apache Maven 是一个项目管理和构建工具&#xff0c;它基于项目对象模型…

一文带你了解,全国职业院校技能大赛老年护理与保健赛项如何备赛

老年护理与保健&#xff0c;作为2023年全国职业院校技能大赛的新增赛项&#xff0c;紧密贴合党的二十大精神&#xff0c;致力于加速健康与养老产业的蓬勃发展&#xff0c;并深化医养康养结合的服务模式。此赛项不仅承载着立德树人的教育使命&#xff0c;更通过竞赛的引领作用&a…

STM32ZET6-USART使用

一、原理说明 STM32自带通讯接口 通讯目的 通信方式&#xff1a; 全双工&#xff1a;通信时可以双方同时通信。 半双工&#xff1a;通信时同一时间只能一个设备发送数据&#xff0c;其他设备接收。 单工&#xff1a;只能一个设备发送到另一个设备&#xff0c;例如USART只有…

电话语音机器人,是由哪些功能构成?

电话语音机器人是自动电话销售、筛选意向客户的&#xff0c;只要录入好行业话术&#xff0c;导入要拨打的手机号&#xff0c;机器人就可以上岗工作了。 电话语音机器人组成部分&#xff1a; 1、语音识别器&#xff0c;主要作用&#xff1a;识别客户讲话内容&#xff0c;从而做…

理解 WordPress | 第二篇:结构化分析

WordPress 专题致力于从 0 到 1 搞懂、用熟这种可视化建站工具。 第一阶段主要是理解。 第二阶段开始实践个人博客、企业官网、独立站的建设。 如果感兴趣&#xff0c;点个关注吧&#xff0c;防止迷路。 WordPress 的内容和功能结构可以按照层级来划分&#xff0c;这种层次化的…

vue3项目history模式部署404处理,使用 historyApiFallback 中间件支持单页面应用路由

vue3项目history模式部署404处理&#xff0c;使用 historyApiFallback 中间件支持单页面应用路由 在现代的 web 开发中&#xff0c;单页面应用&#xff08;SPA&#xff09;变得越来越流行。这类应用通常依赖于客户端路由来提供流畅的用户体验&#xff0c;但在服务器端&#xf…

计算机毕业设计Hadoop+PySpark深度学习游戏推荐系统 游戏可视化 游戏数据分析 游戏爬虫 Scrapy 机器学习 人工智能 大数据毕设

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

PHP电商供应链ERP管理系统小程序源码

&#x1f680;电商供应链大揭秘&#xff01;ERP管理系统如何重塑你的商业版图✨ &#x1f50d; 什么是电商供应链ERP管理系统&#xff1f; 电商供应链ERP管理系统是一款基于FastAdminThinkPHP开发的系统。该系统可满足电商企业管理自身进销存&#xff0c;帮助中小型电商企业管…

全参微调与LoRA的区别,及7种LoRA变种方法解析

随着LLM的发展和应用&#xff0c;在LLM的预训练模型基础上做微调&#xff0c;使其适用于自己的业务场景的研究越来越多。与全参数SFT相比LoRA是在冻结LLM本身参数的基础上&#xff0c;在旁路增加两个可学习的矩阵&#xff0c;用于训练和学习&#xff0c;最后推理是LLM输出和可学…

ubuntu工具 -- ubuntu服务器临时没有网络,急需联网下载东西怎么办? 使用手机提供网络

问题 ubuntu服务器配置经常遇到临时需要网络下载文件需求, 通过有线连接又来不及 解决方法 使用手机usb为ubuntu服务器提供网络 先在ubuntu上运行 ifconfig 查看当前的网络接口, 一会看看多了哪个网口 1. 手机端操作 先使用usb数据线将手机连接到服务器上 打开手机的usb共享…

一文快速预览经典深度学习模型(一)——CNN、RNN、LSTM、Transformer、ViT

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要简要并通俗地介绍了几种经典的深度学习模型&#xff0c;如CNN、RNN、LSTM、Transformer、ViT&#xff08;Vision Transformer&#xff09;等&#xff0c;便于大家初探深度学习的相关知识&#xff0c;并更好地理解深度学…

【D3.js in Action 3 精译_038】4.2 D3 折线图的绘制方法及曲线插值处理

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…