C语言数据结构-链表

C语言数据结构-链表

  • 1.单链表
    • 1.1概念与结构
    • 1.2结点
    • 3.2 链表性质
    • 1.3链表的打印
    • 1.4实现单链表
      • 1.4.1 插入
      • 1.4.2删除
      • 1.4.3查找
      • 1.4.4在指定位置之前插入或删除
      • 1.4.5在指定位置之后插入或删除
      • 1.4.6删除指定位置
      • 1.4.7销毁链表
  • 2.链表的分类
  • 3.双向链表
    • 3.1实现双向链表
      • 3.1.1尾插
      • 3.1.2头插
      • 3.1.3打印
      • 3.1.4尾删
      • 3.1.5头删
      • 3.1.6找到指定位置
      • 3.1.7在指定位置pos之后插入数据
      • 3.1.8删除指定位置的数据
      • 3.1.9销毁链表

1.单链表

1.1概念与结构

数据结构是存储并管理数据。

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

1.2结点

与顺序表不同的是,链表里的每节“车厢”都是独立申请下来的空间,我们称之为“结点”。

结点的组成主要有两个部分:当前结点要保存的数据和保存下一个结点的地址(指针变量)。

链表结点的组成部分:要存储的数据+保存下一个结点地址的指针。
在这里插入图片描述

图中指针变量plist保存的是第一个结点的地址,我们称plist此时“指向”第一个结点,如果我们希望plist“指向”第二个结点时,只需要修改plist保存的内容为0x0012FFA0.

链表中每个结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量来保存下一个结点位置才能从当前结点找到下一结点。

指向下一个结点的地址,就可以找到下一个结点。

在这里插入图片描述
假设当前保存的结点为整型:

typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;//结点数据struct SListNode* next;//指针变量用保存下一个结点的地址,用的是指针,指向下一个节点的指针,下一个结点的类型也是结构体
}SListNode;

3.2 链表性质

1.链式机构在逻辑上是连续的,在物理结构上不一定连续
2.结点一般是从堆上申请的
3/从堆上申请来的空间,是按照一定策略分配出来的,每次申请的空间可能连续,可能不连续。

数组:数据与数据之间的地址是连续的。
链表:数据与数据之间的地址不一定是连续的。

链表也是线性表的一种。

逻辑结构:一定是线性的。
物理结构:不一定是线性的。

结合前面学到的结构体知识,我们可以给出每个结点对应的结构体代码。

当我们想要保存一个整型数据时,实际是向操作系统申请了一块内存,这个内存不仅要保存整型数据,也需要保存下一个结点的地址(当下一个结点为空时保存的地址为空)。

当我们想要从第一个结点走到最后一个结点时,只需要在当前结点拿上下一个结点的地址就可以了。

1.3链表的打印

链表实现从头到尾打印:

listnode.c:

// 单链表打印
void SListPrint(SListNode* plist)
{SListNode* pcur = plist;while (pcur != NULL){printf("%d-> ", pcur->data);pcur = pcur->next;/将pcur移动到下一个结点//把下一个节点的地址给了pcur,就相当于移动到下一个结点了。}printf("NULL\n");

在这里直接使用plist来遍历结果也是一样的,重新创建个pcur指针,是为了避免由于指针指向的改变,导致无法重新找到链表的首结点。

链表同样也有增删改查等操作,接下来我们来实现单链表的头插和尾插

1.4实现单链表

1.4.1 插入

要插入,就要向操作系统申请结点大小的空间,存储要插入的数据。不论是头插还是尾插,都需要向操作系统申请结点大小的一块空间,所有将向操作动态申请一块空间,分装成一个函数:

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc fail!\n");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}

需要注意的是:
传值:形参的改变不会影响实参
传地址:形参的改变影响实参

接受一级指针的地址用二级指针
在这里插入图片描述

// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{assert(pplist);SListNode* newnode = BuySListNode(x);if (*pplist == NULL){//如果pplist为空,那么申请的结点就为首节点,直接将首节点指向*pplist//链表为空*pplist = newnode;}else{//非空SListNode* ptail = *pplist;//防止找不到头节点首先我们要先找到该链表的尾节点。while (ptail->next){ptail = ptail->next;//相当于遍历,挪过去了 .}ptail->next = newnode;}
}

时间复杂度为:O(n)

如果只传一级指针,函数内部修改这个指针并不会影响到函数外部的指针;传二级指针就不同了。二级指针指向的是头指针的地址。在函数内部通过二级指针修改头指针的值,就可以真正改变头指针的值,使得在函数外部也可以看到链表头指针的更新。

在这里插入图片描述

// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{assert(pplist);SListNode* newnode = BuySListNode(x);//申请一个节点.newnode->next = *pplist;//指向第一个节点的指针*pplist = newnode;//现在的头节点是nednode了,将pplist指向newnode。
}

时间复杂度为:O(1)

1.4.2删除

// 单链表的尾删
void SListPopBack(SListNode** pplist)
{assert(pplist&&*pplist);//链表不能为空,就是指向链表第一个结点的指针不能为空//要删除尾节点,就需要找到尾节点,需要遍历。if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else {//需要定义一个游标,将尾节点的前一个结点保存下来,然后直接将NULL指向游标,就是删除了。SListNode* pcur = NULL;SListNode* ptail = *pplist;while (ptail->next){//赋值pcur = ptail;ptail = ptail->next;}pcur->next = NULL;free(ptail);ptail = NULL;}
}
// 单链表头删
void SListPopFront(SListNode** pplist)//** pplist是第一个结点
{assert(pplist && *pplist);//传过来的参数不能为空,链表不能为空SListNode* next = (*pplist)->next;//先将下一个结点保存下来。free(*pplist);//释放掉原来的头节点*pplist = next;
}

1.4.3查找

// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)//不要求形参发生改变,所以传一级指针就可以了
{SListNode* pcur = plist;while (pcur){if (pcur->data == x){return pcur;}pcur=pcur->next;//相当于++}return NULL;//未找到
}

1.4.4在指定位置之前插入或删除

在指定位置之前插入会改变pos之前和pos位置的结点,所以需要将po之前的结点保存下来。

// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x)//需要改变形参,传入二级指针
{assert(pos&&pplist);if (pos == *pplist)//头插{SListPushFront(pplist, x);}else {SListNode* newnode = BuySListNode(x);SListNode* prev = *pplist;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}

在这里插入图片描述

1.4.5在指定位置之后插入或删除

//插入
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x)//不需要知道头结点也可以。
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}
//删除
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos)
{assert(pos&&pos->next);SListNode* pre = pos->next;pos->next = pre->next;free(pre);pre = NULL;
}

1.4.6删除指定位置

// 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos)
{assert(pos&&pplist);//头删if (pos==*pplist){SListPopFront(pplist);}else {SListNode* pre = *pplist;//保存头结点while (pre->next != pos)//向后遍历{pre = pre->next;}pre->next = pos->next;//将pre->next指向pos->next,就将pos删除了。free(pos);//释放pos的内存pos = NULL;}
}

在这里插入图片描述

1.4.7销毁链表

//销毁链表
void SLTDestroy(SListNode** pphead)
{SListNode* pcur = *pphead;while (pcur){SListNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

2.链表的分类

链表的结构非常多样:
在这里插入图片描述
链表的说明:
在这里插入图片描述
①单向或双向:
单向是只能从左往右遍历;
双向是既能从左往右遍历,也能从右往左遍历。

双向链表有三个,指向前一个结点的指针(前驱结点),指向后一个结点的指针(后继结点)和数据域。

②带头或者不带头:
在前面说的单链表中的“头结点”,该头结点是链表的首节点(第一个结点),实际这样的称呼是错误的,因为链表中存在一种链表叫做带头链表(不是指链表里第一个有效的结点),这里的头结点指的是,哨兵位(不保存任何有效数据,只是占位置的)。

如带头链表中,只有头结点,那么我们就称该链表为空。

带头链表的意义就是:占位置,不需要判断链表的头是否为空。

③循环或者不循环
循环链表的尾结点不为空,指向第一个有效的结点。

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:单链表和双向带头循环链表

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

3.双向链表

在这里插入图片描述

3.1实现双向链表

结点由三个部分组成:
前驱结点、后驱结点和数据。

3.1.1尾插

在这里插入图片描述

//尾插
void LTPushBack(LTNode* phead, LTDatatype x)
{//为什么这里传的是一级指针?//pphead不会发生改变,哨兵位的地址不会改变。//不需要传地址过去,一级指针//如果哨兵位发生改变,就传二级指针assert(phead);LTNode* newnode = BuyListNode(x);//phead phead->prev newnodenewnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;
}

3.1.2头插

在这里插入图片描述

//头插
void LTPushFront(LTNode* phead, LTDatatype x)
{assert(phead);//phead phead->next newnodeLTNode* newnode = BuyListNode(x);//先改变d1newnode->prev = phead;newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;
}

3.1.3打印

//打印
void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;//不等于哨兵位就继续向下遍历while (pcur != phead){printf("%d ->", pcur->data);pcur = pcur->next;}printf("\n");
}

3.1.4尾删

首先需要判断链表是否为空:

//判断是否为空
bool LTEmpty(LTNode* phead)
{assert(phead);//判断哨兵位的下一个是不是指向它自己//如果指向它自己说明链表为空return phead->next == phead;//如果return返回的是真,传给下面函数的是真
}

在这里插入图片描述

//尾删
void LTPopBack(LTNode* phead)
{//传过来的是真,就是phead->next==phead,就说明已经遍历结束,给前面加!表示已经为空。assert(!LTEmpty(phead));//判断是否为空LTNode* del = phead->prev;//phead del->prev deldel->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;
}

3.1.5头删

在这里插入图片描述

//头删
void LTPopFront(LTNode* phead)
{
//assert(!LTEmpty(phead));LTNode* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}

3.1.6找到指定位置

//找到指定位置
LTNode* LTFind(LTNode* phead, LTDatatype x)
{//需要遍历LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

3.1.7在指定位置pos之后插入数据

在这里插入图片描述

//在指定位置pos之后插入数据
void LTInsert(LTNode* pos, LTDatatype x)
{assert(pos);LTNode* newnode = BuyListNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}

3.1.8删除指定位置的数据

//在指定位置pos删除数据
void LTErase(LTNode* pos)
{assert(pos);pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}

3.1.9销毁链表

//销毁
void LTDesTroy(LTNode** pphead)
{LTNode* pcur = (*pphead)->next;while (pcur != *pphead){//释放之前把pcur的下一个结点先保存起来LTNode* next = pcur->next;free(pcur);pcur = next;}free(*pphead);*pphead = NULL;
}

#4.源代码

list.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int LTDatatype;typedef struct ListNode {LTDatatype data;struct ListNode* next;struct ListNode* prev;
}LTNode;//申请一个结点
LTNode* BuyListNode(LTDatatype x);
//初始化
LTNode* LTInit();//调用
//void LTinit(LTNode** pphead);//销尽量也传一级,这是保持接口的一致性。
//销毁
//void LTDesTroy(LTNode**phead);//要将哨兵位也销毁掉,所以要传二级指针
void LTDesTroy(LTNode* phead);
//打印
void LTPrint(LTNode* phead);//判断是否为空
bool LTEmpty(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDatatype x);
//头插
void LTPushFront(LTNode* phead, LTDatatype x);//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);//在指定位置pos之后插入数据
void LTInsert(LTNode* pos, LTDatatype x);//在指定位置pos删除数据
void LTErase(LTNode* pos);//找到指定位置
LTNode* LTFind(LTNode* phead, LTDatatype x);

list.c

#define _CRT_SECURE_NO_WARNINGS 1#include"list.h"//初始化
LTNode* LTInit()
{//创建头结点//LTNode* phead = (LTNode*)malloc(sizeof(LTNode));//phead->data = -1;//phead->next = phead->prev;//需要将头结点指向它自己,就成为了带头双向循环链表//phead->prev = phead;LTNode* phead = BuyListNode(-1);return phead;//通过返回的方式
}//销毁
//void LTDesTroy(LTNode** pphead)
//{
//	LTNode* pcur = (*pphead)->next;
//	while (pcur != *pphead)
//	{
//		//释放之前把pcur的下一个结点先保存起来
//		LTNode* next = pcur->next;
//		free(pcur);
//		pcur = next;
//	}
//	free(*pphead);
//	*pphead = NULL;
//}
void LTDesTroy(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = NULL;
}
//我把我的博客链接给你,你给我写。
//void LTinit(LTNode** pphead)
//{
//	assert(pphead);
//	*pphead = (LTNode*)malloc(sizeof(LTNode));
//	(*pphead)->data = -1;
//	(*pphead)->next = (*pphead)->prev = *pphead;
//
//}//创建一个新的结点
LTNode* BuyListNode(LTDatatype x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL){perror("malloc fail!\n");exit(1);}node->data = x;node->next = node->prev = node;return node;
}//打印
void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;//不等于哨兵位就继续向下遍历while (pcur != phead){printf("%d ->", pcur->data);pcur = pcur->next;}printf("\n");
}//判断是否为空
bool LTEmpty(LTNode* phead)
{assert(phead);//判断哨兵位的下一个是不是指向它自己//如果指向它自己说明链表为空return phead->next == phead;
}//尾插
void LTPushBack(LTNode* phead, LTDatatype x)
{//为什么这里传的是一级指针?//pphead不会发生改变,哨兵位的地址不会改变。//不需要传地址过去,一级指针//如果哨兵位发生改变,就传二级指针assert(phead);LTNode* newnode = BuyListNode(x);//phead phead->prev newnodenewnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;
}//头插
void LTPushFront(LTNode* phead, LTDatatype x)
{assert(phead);//phead phead->next newnodeLTNode* newnode = BuyListNode(x);//先改变d1newnode->prev = phead;newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;
}//尾删
void LTPopBack(LTNode* phead)
{//不用判断是否为空assert(!LTEmpty(phead));//判断是否为空LTNode* del = phead->prev;//phead del->prev deldel->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;
}//头删
void LTPopFront(LTNode* phead)
{assert(!LTEmpty(phead));LTNode* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}//在指定位置pos之后插入数据
void LTInsert(LTNode* pos, LTDatatype x)
{assert(pos);LTNode* newnode = BuyListNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}//找到指定位置
LTNode* LTFind(LTNode* phead, LTDatatype x)
{//需要遍历LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}//在指定位置pos删除数据
void LTErase(LTNode* pos)
{assert(pos);pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"list.h"void test()
{LTNode* plist = LTInit();//创建一个双向带头循环链表。/*LTNode* plist = NULL;LTInit(&plist);*///传一级指针的地址,实参的改变会影响实参。//上面两种方式都可以。LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPushBack(plist, 5);LTPrint(plist);//LTPopFront(plist);//LTPrint(plist);//LTNode* find = LTFind(plist, 2);//if (find == NULL)//{//	printf("未找到!\n");//}//else {//	printf("找到了!\n");//}//LTInsert(find, 99);//LTPrint(plist);//LTErase(find);//LTPrint(plist);LTDesTroy(plist);plist = NULL;//传一级需要手动将plist置为空。
}
int main()
{test();return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringCloud详细教程】-04-服务容错--Sentinel

精品专题&#xff1a; 01.《C语言从不挂科到高绩点》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. 《SpringBoot详细教程》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12789841.html?spm1001.20…

【Python中while循环】

一、深拷贝、浅拷贝 1、需求 1&#xff09;拷贝原列表产生一个新列表 2&#xff09;想让两个列表完全独立开&#xff08;针对改操作&#xff0c;读的操作不改变&#xff09; 要满足上述的条件&#xff0c;只能使用深拷贝 2、如何拷贝列表 1&#xff09;直接赋值 # 定义一个…

在 Mac(ARM 架构)上安装 JDK 8 环境

文章目录 步骤 1&#xff1a;检查系统版本步骤 2&#xff1a;下载支持 ARM 的 JDK 8步骤 3&#xff1a;安装 JDK步骤 4&#xff1a;配置环境变量步骤 5&#xff1a;验证安装步骤 6&#xff1a;注意事项步骤7&#xff1a;查看Java的安装路径 在 Mac&#xff08;ARM 架构&#xf…

对比C++,Rust在内存安全上做的努力

简介 近年来&#xff0c;越来越多的组织表示&#xff0c;如果新项目在技术选型时需要使用系统级开发语言&#xff0c;那么不要选择使用C/C这种内存不安全的系统语言&#xff0c;推荐使用内存安全的Rust作为替代。 谷歌也声称&#xff0c;Android 的安全漏洞&#xff0c;从 20…

小程序基础:流程。

一、前言 该文章是个人的学习笔记&#xff0c;是学习了黑马程序的微信小程序开发视频后写的笔记&#xff0c;将老师所讲的内容重点汇总起来&#xff0c;目的是为了方便自己日后回顾&#xff0c;同时也方便大家学习和了解小程序的开发 想要入门小程序&#xff0c;那么看这一篇文…

【漏洞复现】CVE-2020-13925

漏洞信息 NVD - CVE-2020-13925 Similar to CVE-2020-1956, Kylin has one more restful API which concatenates the API inputs into OS commands and then executes them on the server; while the reported API misses necessary input validation, which causes the hac…

数据结构 (11)串的基本概念

一、串的定义 1.串是由一个或者多个字符组成的有限序列&#xff0c;一般记为&#xff1a;sa1a2…an&#xff08;n≥0&#xff09;。其中&#xff0c;s是串的名称&#xff0c;用单括号括起来的字符序列是串的值&#xff1b;ai&#xff08;1≤i≤n&#xff09;可以是字母、数字或…

LLM PPT Translator

LLM PPT Translator 引言Github 地址UI PreviewTranslated Result Samples 引言 周末开发了1个PowerPoint文档翻译工具&#xff0c;上传PowerPoint文档&#xff0c;指定想翻译的目标语言&#xff0c;通过LLM的能力将文档翻译成目标语言的文档。 Github 地址 https://github.…

Python数据分析实例五、US 大选捐款数据分析

美国联邦选举委员会 (FEC) 公布了对政治竞选活动的贡献数据。这包括投稿人姓名、职业和雇主、地址和投款金额。2012 年美国总统大选的贡献数据以单个 150 MB 的 CSV 文件P00000001-ALL.csv形式提供,该文件可以通过以下pandas.read_csv加载: import pandas as pdfec = pd.r…

3.http模块

文章目录 [TOC](文章目录) 1、什么是http模块&#xff1f;1.1.作用1.2.服务器相关概念1.2.创建基本的web服务器-实现的核心步骤和代码1.2.1导入http模块1.2.2.req 请求对象 1.3.根据不同的url地址 响应不同的html内容1.4.案例-clock时钟的web服务器 1、什么是http模块&#xff…

【Nginx】核心概念与安装配置解释

文章目录 1. 概述2. 核心概念2.1.Http服务器2.2.反向代理2.3. 负载均衡 3. 安装与配置3.1.安装3.2.配置文件解释3.2.1.全局配置块3.2.2.HTTP 配置块3.2.3.Server 块3.2.4.Location 块3.2.5.upstream3.2.6. mine.type文件 3.3.多虚拟主机配置 4. 总结 1. 概述 Nginx是我们常用的…

uniapp开发微信小程序笔记8-uniapp使用vant框架

前言&#xff1a;其实用uni-app开发微信小程序的首选不应该是vant&#xff0c;因为vant没有专门给uni-app设置专栏&#xff0c;可以看到目前Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本&#xff0c;并由社区团队维护 React 版本和支付宝小程序版本。 但是我之前维…

IDEA2024创建一个spingboot项目

以下是创建一个基本的 Spring Boot 项目的步骤和示例&#xff1a; 初始化一个springboot工程其实有许多方法&#xff0c;笔者这里挑了一个最快捷的方式搭建一个项目。我们直接通过官方平台&#xff08;start.spring.io&#xff09;进行配置&#xff0c;然后下载压缩包就可以获取…

Easyexcel(7-自定义样式)

相关文章链接 Easyexcel&#xff08;1-注解使用&#xff09;Easyexcel&#xff08;2-文件读取&#xff09;Easyexcel&#xff08;3-文件导出&#xff09;Easyexcel&#xff08;4-模板文件&#xff09;Easyexcel&#xff08;5-自定义列宽&#xff09;Easyexcel&#xff08;6-单…

北京航空航天大学多模态自适应攀岩机器人:突破复杂地形挑战

近年来&#xff0c;地外天体探测任务的需求显著增加&#xff0c;尤其是在月球、火星等崎岖地形的探索中&#xff0c;攀岩机器人凭借其灵活性和稳定性成为重要工具。然而&#xff0c;传统攀爬技术在面对复杂地形时仍面临诸多挑战&#xff0c;如附着装置的适应性不足、柔顺性较低…

【Java】二叉树:数据海洋中灯塔式结构探秘(上)

个人主页 &#x1f339;&#xff1a;喜欢做梦 二叉树中有一个树&#xff0c;我们可以猜到他和树有关&#xff0c;那我们先了解一下什么是树&#xff0c;在来了解一下二叉树 一&#x1f35d;、树型结构 1&#x1f368;.什么是树型结构&#xff1f; 树是一种非线性的数据结构&…

深度学习基础01_深度学习概述参数初始化激活函数

目录 一、深度学习概述 二、神经网络 1、感知神经网络 2、人工神经元 1.构建 2.组成 3.数学表示 3、深入神经网络 1.基本结构 2.网络构建 3.全连接神经网络 三、数据处理 四、参数初始化 1、固定值初始化 1.全零初始化 2.全1初始化 3.任意常数初始化 2、随机…

从Full-Text Search全文检索到RAG检索增强

从Full-Text Search全文检索到RAG检索增强 时光飞逝&#xff0c;转眼间六年过去了&#xff0c;六年前铁蛋优化单表千万级数据查询性能的场景依然历历在目&#xff0c;铁蛋也从最开始做CRUD转行去了大数据平台开发&#xff0c;混迹包装开源的业务&#xff0c;机缘巧合下做了实时…

C++ 优先算法 —— 无重复字符的最长子串(滑动窗口)

目录 题目&#xff1a; 无重复字符的最长子串 1. 题目解析 2. 算法原理 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口&#xff08;同向双指针&#xff09; 3. 代码实现 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口 题目&#xff1a; 无重复字符的最长子串 1. 题目解析 题目截图&#xff1a; 此题所说的…

【pyspark学习从入门到精通19】机器学习库_2

目录 估计器 分类 回归 聚类 管道 估计器 估计器可以被看作是需要估算的统计模型&#xff0c;以便对您的观测值进行预测或分类。 如果从抽象的 Estimator 类派生&#xff0c;新模型必须实现 .fit(...) 方法&#xff0c;该方法根据在 DataFrame 中找到的数据以及一些默认或…