链表 --- C语言实现

news2024/11/24 3:36:30

本篇文章来详细介绍一下数据结构中的链表。

目录

1.链表的概念及结构

2.链表的分类

3.单链表的实现

4.链表的面试题

5.双向链表的实现

6.顺序表和链表的区别


1.链表的概念及结构

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

注意:

  1. 从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续
  2. 现实中的结点一般都是从堆上申请出来的
  3. 从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

2.链表的分类

实际中链表的结构非常多样,以下3种情况组合起来就有8种链表结构,2^3 = 8:
 

1.单项或者双向

 

2.带头或者不带头

 

3.循环或者非循环

 虽然有这么多的链表的结构,但是我们实际中最常用的还是两种结构

 1.无头单向非循环链表

 

2.带头双向循环链表

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

3.单链表的实现

//无头+单行+非循环链表的增删改查实现

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SListNode;

// 动态申请一个节点
SListNode* BuySListNode(SLTDataType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDataType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?因为单链表只能向后访问
void SlistInsertAfter(SListNode* pos, SLTDataType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?因为单链表只能向后访问
void SlistEraseAfter(SListNode* pos);

// 单链表的销毁
void SListDestroy(SListNode** pphead);

//在pos之前插入
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x);

//删除pos位置的值
void SListErase(SListNode** pphead, SListNode* pos);

接口实现:

// 动态申请一个节点
SListNode* BuySListNode(SLTDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	//申请成功
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
// 单链表打印
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
	assert(pplist);//链表为空,pphead也不为空,因为它是头指针plist的地址

	SListNode* newnode = BuySListNode(x);
	//空链表
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
	assert(pplist);//链表为空,pphead也不为空,因为它是头指针plist的地址
	SListNode* newnode = BuySListNode(x);

	newnode->next = *pplist;
	
	*pplist = newnode;
}
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(pplist);//链表为空,pphead也不为空,因为它是头指针plist的地址
	assert(*pplist);//空链表不能尾删

	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		//方法一:
		SListNode* tail = *pplist;
		while (tail->next->next)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
		//方法二
		/*SListNode* tail = *pplist;
		SListNode* prev = *pplist;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;*/
	}
}
// 单链表头删
void SListPopFront(SListNode** pplist)
{
	assert(pplist);//链表为空,pphead也不为空,因为它是头指针plist的地址
	assert(*pplist);//空链表不能头删

	SListNode* del = *pplist;
	*pplist = (*pplist)->next;
	free(del);
	del = NULL;
}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDataType x)
{
	SListNode* cul = plist;
	while (cul)
	{
		if (cul->data == x)
			return cul;
		cul = cul->next;
	}
	return NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?没有地址,找不到,单链表只能找后面的
void SlistInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?没有地址,找不到
void SlistEraseAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next);

	SListNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}

// 单链表的销毁
void SListDestroy(SListNode** pphead)
{
	SListNode* del = *pphead;
	while (*pphead)
	{
		del = *pphead;
		*pphead = (*pphead)->next;
		free(del);
	}
}


//在pos之前插入
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x)
{
	assert(pphead);
	SListNode* newnode = BuySListNode(x);
	if (pos == *pphead)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		SListNode* cur = *pphead;
		while (cur)
		{
			if (cur->next == pos)
			{
				newnode->next = pos;
				cur->next = newnode;
				return;
			}
			cur = cur->next;
		}
	}
}

//删除pos位置的值
void SListErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if (pos == *pphead)
	{
		*pphead = (*pphead)->next;
		free(pos);
	}
	else
	{
		SListNode* cur = *pphead;
		while (cur)
		{
			if (cur->next == pos)
			{
				cur->next = pos->next;
				free(pos);
				return;
			}
			cur = cur->next;
		}
	
	}
}

4.链表的面试题

1.删除链表中等于给定值val的所有结点。OJ链接

2.反转一个单链表。OJ链接

3.给定一个带有头结点head的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。ОJ链接

4.输入一个链表,输出该链表中倒数第k个结点。OJ链接

5.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。OJ链接

6.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前。OJ链接

7.链表的回文结构。OJ链接

8.输入两个链表,找出它们的第一个公共结点。OJ链接

9.给定一个链表,判断链表中是否有环。OJ链接

【思路】

快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾。

【扩展问题】

1.为什么快指针每次走两步,慢指针走一步可以?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在慢指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

2.快指针一次走3步,走4步,...n步行吗?

假设:快指针每次走3步,满指针每次走一步,此时快指针肯定先进环,慢指针后来才进
环。假设慢指针进环时候,快指针的位置如图所示:

此时按照上述方法来绕环移动,每次快指针走3步,慢指针走1步,是永远不会相遇的,快指针刚好将慢指针套圈了,因此不行。
只有快指针走2步,慢指针走一步才可以,因为换的最小长度是1,即使套圈了两个也在相同的位置。

10.给定一个链表,返回链表开始入环的第一个结点。如果链表无环,则返回NULL。OJ链接

结论:

双指针:先让一个指针走一步,一个指针走两步,最终两个指针会在环内相遇。再让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。

证明:

说明:

phead为链表的起始点,E为环入口点,M与判断是否是环的时候相遇点(快慢指针第9题)

设:

环的长度为R,H到E的距离为LE到M的距离为X则:M到E的距离为R-x

在判环时,快慢指针相遇时所走的路径长度:

  • fast: L +X + nR
  • slow: L+ x

注意:

1.当慢指针进入环时,快指针可能已经在环中绕了n圈了,n至少为1因为:快指针先进环走到M的位置,最后又在M的位置与慢指针相遇

2.慢指针进环之后,快指针肯定会在慢指针走一圈之内追上慢指针

因为:慢指针进环后,快慢指针之间的距离最多就是环的长度,而两个指针在移动时,每次它们至今的距离都缩减一步(速度差是1),因此在慢指针移动一圈之前快指针肯定是可以追上慢指针的

而快指针速度是满指针的两倍,因此有如下关系是:2*(L+ X)= L+ X + nR
L+ x = nR
L= nR- x(n为1,2,3,4.......n的大小取决于环的大小,环越小n越大)

极端情况下,假设n = 1,此时:L =R- x

即:一个指针从链表起始位置运行,一个指针从相遇点位置绕环,每次都走一步,两个指针最终会在入口点的位置相遇

11.给定一个链表,每个结点包含一个额外增加的随机指针,该指针可以指向链表中的任何结点
或空结点。

要求返回这个链表的深度拷贝。OJ链接
 

5.双向链表的实现

// 带头+双向+循环链表增删查改实现
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int LTDataType;

typedef struct ListNode
{
	struct ListNode* prev;
	LTDataType data;
	struct ListNode* next;
}LTNode;

//初始化
//void InitListNode(LTNode** phead)//使用二级指针
LTNode* InitListNode();//使用返回值
//打印
void LTPrint(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//判空
bool LTEmpty(LTNode* phead);
//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);

//查找
LTNode* LTFind(LTNode* phead, LTDataType x);

//pos之前插入(与顺序表一致)
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置的值
void LTErase(LTNode* pos);
//释放链表
void LTDestroy(LTNode* phead);

 接口实现:

LTNode* BuyaNewNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->prev = NULL;
	newnode->next = NULL;
	newnode->data = x;
	return newnode;
}

//初始化
LTNode* InitListNode()
{
	LTNode* phead = BuyaNewNode(0);
	phead->next = phead;
	phead->prev = phead;
	return phead; 
}

//打印
void LTPrint(LTNode* phead)
{
	assert(phead);
	printf("gurad<==>");
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<==>", cur->data);
		cur = cur->next;
	}
	printf("gurad\n");
}

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	//LTInsert(phead, x);
	assert(phead);
	LTNode* newnode = BuyaNewNode(x);

	LTNode* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	//LTInsert(phead->next, x);
	assert(phead);
	LTNode* newnode = BuyaNewNode(x);

	newnode->next = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next->prev = newnode;
}

bool LTEmpty(LTNode* phead)
{
	if (phead->next == phead)
	{
		return true;
	}
	else
	{
		return false;
	}
}


//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));//空链表 
	//LTErase(phead->prev);
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;
	free(tail);
	phead->prev = tailprev;
	tailprev->next = phead;
	
}

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));//空链表 
	//LTErase(phead->next);
	LTNode* frist = phead->next;
	LTNode* second = frist->next;
	free(frist);
	phead->next = second;
	second->prev = phead;
}

//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//pos之前插入(与顺序表一致)
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = BuyaNewNode(x);
	LTNode* prev = pos->prev;

	newnode->prev = prev;
	newnode->next = pos;
	prev->next = newnode;
	pos->prev = newnode;
}

//删除pos位置的值
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;

	prev->next = next;
	next->prev = prev;

	free(pos);
}

//释放链表
void LTDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

6.顺序表和链表的区别

链表(双向循环带头链表):

 优点:

  1. 任意位置插入删除O(1)
  2. 按需申请释放空间

 缺点:

  1. 不支持下标随机访问
  2. CPU高速缓存命中率会更低

顺序表:

缺点:

  1. 前面部分插入删除数据,效率是O(N),需要挪动数据。
  2. 空间不够,需要扩容。a、扩容是需要付出代价的 b、一般还会伴随空间浪费。

优点:

  1. 尾插尾删效率不错。
  2. 下标的随机访问。
  3. CPU高速缓存命中率会更高
     
不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持,为O(N)
任意位置插入或者删除元素可能需要搬移元素,效率低O(N)只需要修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入或删除频繁
缓存利用率

 备注:缓存利用率参考存储体系结构 以及 程序的局部性原理。

 数据结构是为了帮助我们跟好的管理内存。内存需要电,关机后就会消失,磁盘存储的数据在关机后也不会消失。

在CPU与内存之间存在寄存器和三级缓存。内存小使用寄存器(一般几个字节),大的使用三级缓存。

CPU读取数据时

  1.  先去看数据是否在缓存,在就叫缓存命中,则直接访问
  2. 不在就不命中,先加载数据到缓存,再访问

因为缓存一次会加载需要的数据以及这个数据旁边的数据,数组是连续存放的,所以缓存利用率高

可以参考:与程序员相关的CPU缓存知识

本篇结束。

 

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

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

相关文章

【C++】多态(举例+详解,超级详细)

本篇文章会对C中的多态进行详解。希望本篇文章会对你有所帮助。 文章目录 一、多态的定义及实现 1、1 多态的概念 1、2 多态的构成条件 1、2、1 虚函数 1、2、2 虚函数的重写 1、2、3 析构函数构成重写特例原因 1、3 多态的实例练习 1、3、1 例1 1、3、2 例2 1、3、3 例3 1、4…

【C语言】指针进阶(2)

接上期文章指针进阶&#xff08;1&#xff09;指针进阶&#xff08;1&#xff09; 目录 1.函数指针 2.函数指针数组 3.指向函数指针数组的指针 4.回调函数 4.1qsort的用法 void*类型的指针介绍 使用qsort对数组进行排序 使用qsort对结构体进行排序&#xff1a; 4.2使用…

【C++】-初步认识和学习继承

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

【CAS6.6源码解析】在IDEA中调试可插拔的supprot模块

CAS源码的casWebApplication启动后&#xff0c;默认只加载最小的支撑系统的模块&#xff0c;很多模块&#xff08;大部分在support包下&#xff09;是需要手动去引入的&#xff08;对新人来说有坑&#xff09;&#xff0c;这里介绍一下如何手动引入这些模块。 文章目录 调试步骤…

TCP通信 -- 文件传输

www 客户端&#xff1a; package com.itheima.b03TCPTest3;import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException;public class Client {public static void main(String[] args) throws IOException {System.out.p…

HTTP超本文传输协议

HTTP超本文传输协议 HTTP简介HTTP请求与响应HTTP请求请求行请求头空行请求体 HTTP响应响应行响应头空行响应体 HTTP请求方法GET和POST之间的区别HTTP为什么是无状态的cookie原理session 原理cookie 和 session 的区别cookie如何设置cookie被禁止后如何使用session HTTP简介 HT…

idea快速运行vue项目

目录 一、前提 二、步骤 安装vue.js插件 添加脚本 进行如下配置 一、前提 安装好node.js环境并初始化完成和安装好依赖 二、步骤 安装vue.js插件 打开idea,然后在File–Settings–Plugins–Makerplace下找到vue.js插件,安装并重启idea 添加脚本 进行如下配置 在Sctipts中根…

【手撕排序算法】---基数排序

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【数据结构初阶&#xff08;C实现&#xff09;】 我们直到一般的排序都是通过关键字的比较和移动这两种操作来进行排序的。 而今天介绍的…

MAC 推送证书不受信任

配置推送证书的时候&#xff0c;一打开就变成不受信任&#xff0c;搜了很多解决版本。 由于苹果修改相关规定&#xff0c;推送证书 打开Apple PKI - Apple 下载AppleWWDRCA文件&#xff0c;选择G4,双击安装之后&#xff0c;证书已经变为受信任。 AppleWWDRCA(Apple Worldwid…

一、大数据技术之Flume(简介)

第1章 Flume概述 1.1 Flume定义 Flume是Cloudera提供的一个高可用的&#xff0c;高可靠的&#xff0c;分布式的海量日志采集、聚合和传输的系统。Flume基于流式架构&#xff0c;灵活简单。 1.2 Flume基础架构 Flume组成架构如下图所示。 1.2.1 Agent Agent是一个JVM进程&…

每日一题——反转链表

题目 给定一个单链表的头结点pHead(该头节点是有值的&#xff0c;比如在下图&#xff0c;它的val是1)&#xff0c;长度为n&#xff0c;反转该链表后&#xff0c;返回新链表的表头。 数据范围&#xff1a;0≤n≤1000 要求&#xff1a;空间复杂度 O(1) &#xff0c;时间复杂度 O…

C# Modbus通信从入门到精通(21)——Modbus TCP协议原理

Modbus TCP是走网口的&#xff0c;也可以在同一时间内有多个从站访问主站&#xff0c;并且通过Modbus事务处理标识来区分同一时刻的不同Modbus事务&#xff0c;这是区别于Modbus ASCII和Modbus RTU的地方。 1、访问模式&#xff1a; Modbus客户端通常输入Modbus服务器的IP地址…

科技与人元宇宙论坛跨界对话

近来&#xff0c;“元宇宙”成为热门话题&#xff0c;越来越频繁地出现在人们的视野里。大家都在谈论它&#xff0c;但似 乎还没有一个被所有人认同的定义。元宇宙究竟是什么&#xff1f;未来它会对我们的工作和生活带来什么样 的改变&#xff1f;当谈论虚拟现实&#xff08;VR…

代码随想录| 图论04 查并集 ●查并集理论知识 ●1971.寻找图中是否存在路径 ●684.冗余连接 ●685.冗余连接II

#查并集理论知识 并查集用处&#xff1a;解决连通性问题 将两个元素添加到一个集合中。判断两个元素在不在同一个集合 思路&#xff1a;将三个元素A&#xff0c;B&#xff0c;C &#xff08;分别是数字&#xff09;放在同一个集合&#xff0c;其实就是将三个元素连通在一起&a…

【Kubernetes资源篇】ingress-nginx最佳实践详解

文章目录 一、Ingress Controller理论知识1、Ingress Controller、Ingress简介2、四层代理与七层代理的区别3、Ingress Controller中封装Nginx&#xff0c;为什么不直接用Nginx呢&#xff1f;4、Ingress Controller代理K8S内部Pod流程 二、实践&#xff1a;部署Ingress Control…

Raki的读paper小记:LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

Abstract&Introduction&Related Work 研究任务 对大模型进行部分微调 已有方法和相关工作 现有技术通常通过扩展模型深度引入推理延迟&#xff08;Houlsby 等人&#xff0c;2019&#xff1b;Rebuffi 等人&#xff0c;2017&#xff09;&#xff0c;或通过减少模型可用序…

Python实现HBA混合蝙蝠智能算法优化卷积神经网络回归模型(CNN回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝙蝠算法是2010年杨教授基于群体智能提出的启发式搜索算法&#xff0c;是一种搜索全局最优解的有效方法…

集成了32位Cortex®M0内核XMC1302T038X0200AB、XMC1302Q040X0200AB 32MHz 200KB 闪存 工业MCU

XMC1000 32位工业 MCU 将 ARM Cortex™-M0 核心与领先的 65nm 制造工艺相结合&#xff0c;克服了目前 8 位设计的局限。XMC1000系列让目前的 8 位用户有机会享受 32 位的功耗&#xff0c;同时不在价格或易用性上做出妥协。XMC1000 在其细分市场提供最为广泛的闪存产品线&#x…

人工智能相关笔记

这近一年来&#xff0c;我在国科大修了&#xff1a;人工智能导论、机器学习与模式识别、语义网络与知识图谱、深度学习、强化学习……这几门专业课&#xff0c;由于发现了它们彼此之间有重复的知识点&#xff0c;真想把他们融会贯通一下&#xff0c;至少写个提纲挈领的东西给自…

xcode15启动IOS远程调试

1.用数据线连接IPhone到macOS 2.打开xcode15,然后点击Window->Devices and Simulators 3.选中左边的Devices可看到已连接的IPhone,然后点击Connect via network使其选中. 选择后,左边的IPhone设备的右边出现一个地球图标,表示成功通过网络连接到IPhone 现在可断开数据线的…