分享一篇由C语言实现《数据结构》无头无循环单链表
三月,你好,各位csdn uu们好

文章目录
- 前言
- 一、何为单链表
- 二、单链表基本操作(增,删,查,改,销毁,遍历)
- 1.查找与修改、销毁与遍历
- 2.链表插入与删除操作
- 三、单链表 VS 顺序表 且 全部 源码(SLNode.h ) (SLNode.c) ( test.c)
前言
由于上一篇博客顺序表的相关实现其基本操作中,它的查找效率很快,通过下标可以快速存取表中任意一个位置的元素,但是其插入和删除操作因为要移动大量的元素,造成效率极低,时间复杂度达到O(n),那么能用上面结构来存储可以使其效率提高呢?因此在这里引入单链表,单链表的基本操作插入和删除效率喝高很高时间复杂度为O(n),在以查找到节点的前提下.
一、何为单链表
链表 是一种在物理上非连续、 非顺序的数据结构, 由若干 节点 所组成,一个节点连着一个节点。
单链表顾名思义是链表的每一个节点又包含两部分, 一部分是存放数据的变量data, 另一部分是指向下一个节点的指针next。一个节点连着一个节点,每一个节点之间可以想象成有一条绳子拴起来,其实是上一个节点的next指针指向下一个节点,然后下一个的next指针又指向下一个。
线性表的单链表存储结构
typedef int SLDataType;
typedef struct SLNode
{SLDataType data;struct SLNode* next;
}SLNode;
从这个结构中,可以知道节点由存放数据元素的数据域(data)和存放下一个节点地址的指针域(next)组成,假设phead是指向链表中第一个节点的指针,那么该节点的数据域可以用phead->data表示,该节点的指针域用phead->next表示,链表与顺序表按照下标来随机寻找元素不同,
对于链表的其中一个节点A, 只能根据节点A的next指针来找到该节点的下一个节点B, 再根据节点 B的next指针找到下一个节点C……
而链表中的第一个节点被称为头节点, 最后1个节点被称为尾节点, 尾节点的 next指针指向空。后文不用带哨兵位的头节点,哨兵位的头节点数据域里是没有元素。这里的头节点数据域里是有元素的,与哨兵位头节点不同,这里用phead这一结构体指针指向链表第一个节点,phead->next是指向头节点的下一个节点,其实是phead->next存着它下一个节点的地址,可以想象成第一个节点拿着一根绳子把第二个节点拴起来了。
单链表找下一个容易但是它找他的前一个十分困难,需要再回溯一遍,在数据结构中还有一中双向链表可以快速找到前一个元素,但是本文重点描述单链表实现
链表在内存中如何存储的?
接下来我们看一看链表的存储方式。 如果说顺在内存中的存储方式是顺序存储(一个挨着一个的存储), 而链表在内存中的存储 方式则是随机存储(链表在内存中每一个节点存储在不同内存块中,而通过节点next指针链接下一个节点地址,依靠next指针这样连使每一个节点存在联系,使这些零星的空间链接起来)红色为顺序表顺序存储,绿色为链表随机存储
链表物理结构和逻辑结构
物理结构是在内存中真是存在的内存块,结构体指针phead指向第一个节点,然后第一个节点的next指针指向下一个节点地址。其实就是前一个节点的next存着下一个节点地址,这样下去直到链表为空
逻辑结构是想象中有一根线连着两个节点,箭头表示next指针

与顺序表一样,链表同样有增删查改基本操作,并且插入和删除同样有头、尾、任意位置操作。且还有销毁操作
二、单链表基本操作(增,删,查,改,销毁,遍历)
1.查找与修改、销毁与遍历
查找
在查找元素时, 链表不像数组那样可以通过下标快速进行定位, 只能从 头节点开始向后一个一个节点逐一查找,查找到就返回改节点,找不到则返回空,如pos为3,从以phead指针指向第一个节点,第一个节点数据域不为3,通过第一个节点next指针指向第二个,然后第三个,找到为止。链表中的数据只能按顺序进行访问, 最坏的时间复杂度是O(n) 。
//查找
SLNode* SLNodeFind(SLNode* phead, SLDataType x)
{SLNode* cur = phead;while (cur != NULL){if (cur->data != x){cur = cur->next;}else{return cur;}}return NULL;
}
不考虑查找节点的过程, 链表的更新过程会像数组那样简单, 直接 把旧数据替换成新数据即可。修改就是将找到的这一节点的数据域修改即可
代码如下
if (ret){//修改ret节点的值ret->data *= 2;}
链表销毁
销毁时注意释放上一个节点时要找到下一个节点地址,同时将该节点置空
代码实现如下
//销毁
void SLNodeDestory(SLNode** pphead)
{assert(*pphead);SLNode* cur = *pphead;SLNode* next = NULL;while (cur->next != NULL){next = cur->next;free(cur);cur = next;}free(cur);cur = NULL;/*free(next);next = NULL;*/*pphead = NULL;}
链表遍历
直接上代码
//遍历
void SLNodeprint(SLNode* phead)
{SLNode* cur = phead;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}
2.链表插入与删除操作
插入三种情况
1、尾部插入 2、头部插入 3、中间插入
在插入之前先创建一个要插入的节点,开创新节点之后返回该节点地址,新节点数据域为x,指针域为空
代码如下
//创建一个新节点
SLNode* BuyNewnode(SLDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;//1newnode->next = NULL;return newnode;
}
尾插
尾部插入, 是最简单的情况, 把最后一个节点的next指针指向新插入的 节点即可。
代码如下
//尾插
void SLNodePushBack(SLNode** pphead, SLDataType x)
{assert(pphead);SLNode* newnode = BuyNewnode(x);if (*pphead == NULL){*pphead = newnode;//1}else{SLNode* tail = *pphead;while (tail->next != NULL)//如果将判断改为cur != NULL,那么到最后一个节点时将NULL给cur,后面插入的时候就不能找到下一个节点,直接为空了{tail = tail->next;}tail->next = newnode;}
}
头插
头部插入, 可以分成两步。
1、 把新节点的next指针指向原先的头节点。
2 、把新节点变为链表的头节点。
代码如下
//头插
void SLNodePushFront(SLNode** pphead, SLDataType x)
{assert(pphead);SLNode* newnode = BuyNewnode(x);newnode->next = *pphead;*pphead = newnode;
}
任意位置(pos)插入
任意位置插入, 同样分为两步。
1、 新节点的next指针, 指向插入位置的节点。
2、 插入位置前置节点的next指针, 指向新节点。
代码 如下
//pos前面插入
void SLNodeInsert(SLNode** pphead, SLNode*pos,SLDataType x)
{assert(pphead);SLNode* newnode = BuyNewnode(x);SLNode* prev = *pphead;if (pos == *pphead){SLNodePushFront(pphead, x);//复用头插}else{while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}}
链表插入只要内存空间允许, 能够插入链表的元素是无穷无尽的, 不需要像数组 那样考虑扩容的问题。
链表删除操作同样分为尾删、头删、任意位置(pos)删除
尾删
尾部删除, 是最简单的情况, 把倒数第2个节点的next指针指向空。
代码如下
//尾删
void SLNodePopBack(SLNode** pphead)
{assert(pphead);assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLNode* tail = *pphead;SLNode* prev = NULL;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}
头删
头部删除, 把链表的头节点设为原先头节点的next指针即 可。
代码如下
//头删
void SLNodePopFront(SLNode** pphead)
{assert(pphead);assert(*pphead);SLNode* first = *pphead;*pphead = first->next;free(first);first = NULL;
}
任意位置删除
任意位置删除,把要删除节点的前置节点的next指针 指向要 删除元素的下一个节点即可。
代码如下
//删除pos位置节点
void SLNodeErase(SLNode** pphead, SLNode* pos)
{assert(pphead);if (*pphead == pos){*pphead = pos->next;free(pos);pos = NULL;//这里置空无意义,形参改变不会影响实参,在外面调用它的那个函数置空}else{SLNode* prev = *pphead;while (prev->next!=pos){prev = prev->next;}prev->next = pos->next;free(pos);}
}
删除注意两点
1、链表删空
2、被删除的节点需要释放且置空
链表的插入和删除时间复杂度是多少嘞?
如果不考虑插入、 删除操作之前 查找元素的过程, 只考虑纯粹的插入和删除操作, 时间复杂度都 是O(1)
三、单链表 VS 顺序表 且 全部 源码(SLNode.h ) (SLNode.c) ( test.c)
那么思考一下顺序表和 链表都属于线性的数据结构, 用哪一个更好呢? 顺序表和链表没有绝对的好与坏,顺序和 链表各有千秋。
对比一下顺序表和链表相关操作的性能。
从表格可以看出, 顺序表的优势在于能
够快速定位元素,就这一优势就特别如在有序且数据个数范围特别大时,用二分效率高。而链表的优势在于能够灵活地进行插入和删除操作。
SLNode.h
#include<stdlib.h>#include<assert.h>
#include<stdio.h>
typedef int SLDataType;
typedef struct SLNode
{SLDataType data;struct SLNode* next;
}SLNode;//遍历打印
void SLNodeprint(SLNode* phead);
//尾插
void SLNodePushBack(SLNode** pphead, SLDataType x);
//尾删
void SLNodePopBack(SLNode** pphead);
//头插
void SLNodePushFront(SLNode** pphead, SLDataType x);
//头删
void SLNodePopFront(SLNode** pphead);
//销毁
void SLNodeDestory(SLNode** pphead);
//查找
SLNode* SLNodeFind(SLNode* phead, SLDataType x);
//在pos位置前插入
void SLNodeInsert(SLNode** pphead, SLNode* pos, SLDataType x);
//在pos位置后插入
void SLNodeInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x);
//删除pos节点
void SLNodeErase(SLNode** pphead, SLNode* pos);
//节点pos后一个节点
void SLNodePopAfter(SLNode** pphead, SLNode* pos);
SLNode.c
#include"SLNode.h"//遍历
void SLNodeprint(SLNode* phead)
{SLNode* cur = phead;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}//创建一个新节点
SLNode* BuyNewnode(SLDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;//1newnode->next = NULL;return newnode;
}//尾插
void SLNodePushBack(SLNode** pphead, SLDataType x)
{assert(pphead);SLNode* newnode = BuyNewnode(x);if (*pphead == NULL){*pphead = newnode;//1}else{SLNode* tail = *pphead;while (tail->next != NULL)//如果将判断改为cur != NULL,那么到最后一个节点时将NULL给cur,后面插入的时候就不能找到下一个节点,直接为空了{tail = tail->next;}tail->next = newnode;}
}//尾删
void SLNodePopBack(SLNode** pphead)
{assert(pphead);assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLNode* tail = *pphead;SLNode* prev = NULL;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}//头插
void SLNodePushFront(SLNode** pphead, SLDataType x)
{assert(pphead);SLNode* newnode = BuyNewnode(x);newnode->next = *pphead;*pphead = newnode;
}//头删
void SLNodePopFront(SLNode** pphead)
{assert(pphead);assert(*pphead);SLNode* first = *pphead;*pphead = first->next;free(first);first = NULL;
}//销毁
void SLNodeDestory(SLNode** pphead)
{assert(*pphead);SLNode* cur = *pphead;SLNode* next = NULL;while (cur->next != NULL){next = cur->next;free(cur);cur = next;}free(cur);cur = NULL;/*free(next);next = NULL;*/*pphead = NULL;}//查找
SLNode* SLNodeFind(SLNode* phead, SLDataType x)
{SLNode* cur = phead;while (cur != NULL){if (cur->data != x){cur = cur->next;}else{return cur;}}return NULL;
}//pos前面插入
void SLNodeInsert(SLNode** pphead, SLNode*pos,SLDataType x)
{assert(pphead);SLNode* newnode = BuyNewnode(x);SLNode* prev = *pphead;if (pos == *pphead){SLNodePushFront(pphead, x);}else{while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}}//在pos位置后面插入x
void SLNodeInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x)
{SLNode* newnode = BuyNewnode(x);newnode->next = pos->next;pos->next = newnode;
}//删除pos位置节点
void SLNodeErase(SLNode** pphead, SLNode* pos)
{assert(pphead);if (*pphead == pos){*pphead = pos->next;free(pos);pos = NULL;//这里置空无意义,形参改变不会影响实参,在外面调用它的那个函数置空}else{SLNode* prev = *pphead;while (prev->next!=pos){prev = prev->next;}prev->next = pos->next;free(pos);}
}//在pos位置之后删除
void SLNodePopAfter(SLNode** pphead, SLNode* pos)
{assert(pos->next);SLNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}
test.c
#include"SLNode.h"void test()
{SLNode* plist = NULL;//尾插SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);SLNodeprint(plist);//头插SLNodePushFront(&plist, 10);SLNodeprint(plist);SLNodeDestory(&plist);
}void test1()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);SLNodePushFront(&plist, 10);SLNodeprint(plist);//尾删SLNodePopBack(&plist);SLNodeprint(plist);SLNodePopBack(&plist);SLNodePopBack(&plist);/*SLNodePopBack(&plist);*///SLNodePopBack(&plist);SLNodeprint(plist);//头删SLNodePopFront(&plist);SLNodeprint(plist);SLNodePopFront(&plist);SLNodeprint(plist);SLNodePopFront(&plist);SLNodeprint(plist);SLNodeDestory(&plist);
}void test2()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);SLNodePushFront(&plist, 10);SLNodeprint(plist);//查找SLNode* ret = SLNodeFind(plist, 4);if (ret){//在ret位置前插入30SLNodeInsert(&plist, ret, 30);}SLNodeprint(plist);if (ret){//修改ret节点的值ret->data *= 2;}SLNodeprint(plist);if (ret){//在ret节点后面插入一个值为节点SLNodeInsertAfter(&plist, ret, 999);}SLNodeprint(plist);//if (ret)//{// //删除ret节点// SLNodeErase(&plist, ret);// //ret = NULL;//}//SLNodeprint(plist);if (ret){//删除ret节点后面的那个节点SLNodePopAfter(&plist, ret);}SLNodeprint(plist);
}
int main()
{//test();//test1();test2();return 0;
}
注意:这篇文章是由c语言实现的,而从上文很多接口实现时可以发现,有**pphead,*phead,*plist为结构体指针初始化为空,后面在向链表中插入节点时,由于要改变链表内容,那么就需要将链表的地址传过去,在插入接口以二级指针指向链表地址,通过解引用就可以改变链表。这里插入一点传参问题,
值传递:改变形参不可改变实参,地址传递:,改变形参可以改变实参。而断言assert(pphead)则是因为在插入时需要有链表才可以对链表进行操作,如果在SLNodePushBack()这个接口函数test()在传参时,传的是空地址,该接口就无法对链表进行操作。而在删除时同样如此,还需要断言链表内容是否为空,链表中是否有节点assert(*pphead),链表为空就不能再删除了。如果不用断言判断,用if判断链表是否为空的话,如果代码太多,一时间看不出哪里出的问题,还需要通过调试才能发现,而用断言则能给你报一些基础的错误,这时会发觉断言真好用。
迎来了三月
相关文章:
分享一篇由C语言实现《数据结构》无头无循环单链表
三月,你好,各位csdn uu们好 文章目录前言一、何为单链表二、单链表基本操作(增,删,查,改,销毁,遍历)1.查找与修改、销毁与遍历2.链表插入与删除操作三、单链表 VS 顺序表…...
C盘爆满?两个超简单的解决办法
我们在使用电脑的过程中,经常容易出现C盘爆红,反而其他盘还有大量可用空间的情况。为什么会这样呢?其实主要就两种原因:一是电脑使用习惯不好,不管什么软件都默认安装在C盘,大文件又喜欢放在桌面࿰…...
ThreadLocal
ThreadLocalThreadLocalMapgetsetremove内存泄漏key用强/弱引用entry继承了弱引用ThreadLocal 一个对象的所有线程会共享其全局变量——>线程不安全 解决方式: 方式一:同步机制,加锁(时间换空间) 方式二:…...
Java基础:JDK7-时间Date
JDK7以前时间相关类 1.Date Date date new Date(); , sout(date)得到的是现在所处位置的时间 Date date new Date(0L); , sout(date)得到的是时间原点也就是1970年1月1日08:00(东八区). date.setTime(1000L); sout(date)得到的是时间原点后一秒钟的时间 long time date.g…...
什么是IP地址?
IP协议中还有一个非常重要的内容,那就是给因特网上的每台计算机和其它设备都规定了一种地址,叫做“IP 地址”。由于有这种地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。…...
4年经验之谈,什么是接口测试?怎样做接口测试?
一、什么是接口?【文末学习资源分享】赶紧嫖!冲!!!! 接口测试主要用于外部系统与系统之间以及内部各个子系统之间的交互点,定义特定的交互点,然后通过这些交互点来,通过…...
普通指针扫盲
一、什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。 CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里,数据对象是指存储在内存 中的一个指定数据…...
深度学习笔记:神经网络权重确定初始值方法
神经网络权重不可为相同的值,比如都为0,因为如果这样网络正向传播输出和反向传播结果对于各权重都完全一样,导致设置多个权重和设一个权重毫无区别。我们需要使用随机数作为网络权重 实验程序 在以下实验中,我们使用5层神经网络…...
关于 python 的异常使用说明 (python 的文件和异常)
文章目录异常1. 处理异常 ZeroDivisionError 异常2. 使用 try-except 代码块3. 使用异常避免崩溃4. else 代码块5. 处理 FileNotFoundError 异常6. 分析文本7. 失败时一声不吭异常 pyhong 使用被异常成为异常的特殊对象来管理程序执行期间发生的错误。 每当发生让 python 不知所…...
Spark RDD持久化
RDD Cache缓存 RDD通过Cache或者Persist方法将前面的计算结果缓存,默认情况下会把数据以序列化的形式缓存在JVM的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供…...
【Linux】Linux系统安装Python3和pip3
1.说明 一般来说Linux会自带Python环境,可能是Python3或者Python2,可能有pip也可能没有pip,所以有时候需要自己安装指定的Python版本。Linux系统下的安装方式都大同小异,基本上都是下载安装包然后编译一下,再创建好软…...
用java进行base64加密
首先定义一组密钥,加密和解密使用同一组密钥private final String key "hahahahahaha";也可以随机生成密钥/*** 生成随机密钥* param keySize 密钥大小推荐128 256* return* throws NoSuchAlgorithmException*/public static String generateSecret(int keySize) th…...
torch函数合集
torch.tensor() 原型:torch.tensor(data, dtypeNone, deviceNone, requires_gradFalse) 功能:其中data可以是:list,tuple,NumPy,ndarray等其他类型,torch.tensor会从data中的数据部分做拷贝(而不是直接引用),根据原始数据类型生成相应类型的torch.Tenso…...
AcWing算法提高课-3.1.2信使
宣传一下算法提高课整理 <— CSDN个人主页:更好的阅读体验 <— 题目传送门点这里 题目描述 战争时期,前线有 nnn 个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。 信使负责在哨所之间传递信息,当然,…...
Paddle OCR Win 11下的安装和简单使用教程
Paddle OCR Win 11下的安装和简单使用教程 对于中文的识别,可以考虑直接使用Paddle OCR,识别准确率和部署都相对比较方便。 环境搭建 目前PaddlePaddle 发布到v2.4,先下载paddlepaddle,再下载paddleocr。根据自己设备操作系统进…...
杂谈:数组index问题和对象key问题
面试题一: var arr [1, 2, 3, 4] 问:arr[1] ?; arr[1] ?答:arr[1] 2; arr[1] 2 这里可以再分为两个问题: 1、数组赋值 var arr [1, 2, 3, 4]arr[1] 10; // 数字场景 arr[10] 1; // 字符串场景 arr[a] 1; // 字符串…...
三天Golang快速入门—Slice切片
三天Golang快速入门—Slice切片Slice切片切片原理切片遍历append函数操作切片append添加append追加多个切片中删除元素切片合并string和slice的联系Slice切片 切片原理 由三个部分构成,指针、长度、容量指针:指向slice第一个元素对应的数组元素的地址长…...
腾讯会议演示者视图/演讲者视图
前言 使用腾讯会议共享PPT时,腾讯会议支持共享用户使用演示者视图/演讲者视图,而会议其他成员可以看到正常的放映视图。下面以Win10系统和Office为例,介绍使用步骤。值得一提的是,该方法同时适用于单显示屏和多显示屏。 腾讯会议…...
【C++】类与对象(一)
文章目录1、面向过程和面向对象初步认识2、类的引入3、类的定义4、类的访问限定符5、类的作用域6、类的实例化7、计算类对象的大小8、this指针9、 C语言和C实现Stack的对比1、面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题…...
JavaScript基本语法
本文提到的绝大多数语法都是与Java不同的语法,相同的就不会赘述了.JavaScript的三种引入方式内部js<body><script>alert(hello);</script> </body>行内js<body><div onclick"alert(hello)">这是一个div 点击一下试试</div>…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...








