数据结构之链表(2),双向链表

目录

前言

一、链表的分类详细

二、双向链表

三、双向链表的实现

四、List.c文件的完整代码

五、使用演示

总结



前言

        接着上一篇单链表来详细说说链表中什么是带头和不带头,“哨兵位”是什么,什么是单向什么是双向,什么是循环和不循环。然后实现完整的双向链表代码。


❤️感谢支持,点赞关注不迷路❤️


一、链表的分类详细

上一篇已经说过,链表总共分为8中结构,常用的就是单链表双链表

这里的单链表全称:不带头单向不循环链表双链表全称:带头双向循环链表

以下是上一篇中提到的8中结构:

注意:因为常用的只有单链表和双链表,将这两种链表结构掌握之后就能够自行实现其他结构的链表。


1.带头和不带:

带头与不带头的区别就是:带头的链表有一个头结点(head),也叫哨兵位。

哨兵位:带头链表的头结点,不存储任何有效元素,仅用于占位,代表链表的头部,功能类似于“放哨”,因此叫哨兵位。

注意:上一篇中的单链表是不带头的,因此不存在头结点的说法,所以上一篇中说的头结点仅仅是为了方便称呼第一个节点的一种不规范叫法,只有带头链表才有头结点的说法。尤其是在链表的分类中。


2.单向和双向

区别:

  1. 结构上:单向链表只有一个指向下一个节点的指针。双向链表有一个指向前一个节点的指针和一个指向后一个节点的指针。
  2. 功能上:单向链表只能单向访问下一个节点。双向链表可以既可以访问下一个节点也可以访问上一个节点。

3.循环和不循环

区别:不循环链表的尾结点next指针会指向空指针。循环链表的尾结点next指针不为空,可以无限循环的访问下一个节点。

注意:循环链表的尾结点的next指针可以指向链表的第一个节点,或者中间的任一节点,甚至尾结点。这都叫循环链表。


二、双向链表

双向链表全称:带头双向循环链表。

结构图如下:

双向链表的结构声明如下:

typedef int LTDataType;
//定义双链表结构
typedef struct ListNode
{
    LTDataType data;//存储数据
    struct ListNode* next;//指向下一个节点
    struct ListNode* prev;//指向上一个节点
}LTNode;

同样,双向链表的实现也分为三个文件:

  1. List.h:双向链表的头文件,包含各种库函数头文件、链表结构、函数的声明。
  2. List.c:用于实现各种接口(函数)。
  3. test.c:用于测试。


三、双向链表的实现

1.List.h文件

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>typedef int LTDataType;
//定义双链表结构
typedef struct ListNode
{LTDataType data;//存储数据struct ListNode* next;//指向下一个节点struct ListNode* prev;//指向上一个节点
}LTNode;//初始化
//第一种:传二级指针
//void LTInit(LTNode** pphead);
//第二种:不传参
LTNode* LTInit();//打印
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);//在指定位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);//删除指定位置的节点
void LTErase(LTNode* pos);//销毁
//第一种:传二级指针
//void LTDesTroy(LTNode** pphead);
//第二种:传一级指针,缺点是需手动将哨兵位置空
void LTDesTroy(LTNode* phead);


2.List.c文件

1.LTBuyNode函数

//申请新节点
LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;//前后指针全部指向自己,自循环newnode->next = newnode->prev = newnode;return newnode;
}

解析:

  1. 功能:用于申请新节点。
  2. 参数:新节点的需数据x
  3. 使用malloc函数申请一个节点空间,判空后将其数据域赋值x。注意,新节点的两个指针都需要指向自己,这样才是一个双向循环链表。
  4. 因为该函数只为其他函数服务,因此可以只在 List.c 文件中定义,无需在 List.h 中声明。


2.LTInit函数

//初始化
//第一种:传二级指针
//void LTInit(LTNode** pphead)
//{
//	assert(pphead);
//
//	//创建一个头结点(哨兵位)
//	*pphead = LTBuyNode(-1);
//}//第二种:不传参
LTNode* LTInit()
{return LTBuyNode(-1); 
}

解析:

  1. 该函数有两个版本,第二种为优化后的版本,为的是保持接口的一致性,因为双向链表的其它功能函数都只是传一级指针。为了避免混淆,优化为不传参版本。
  2. 功能:初始化为哨兵位(头结点)。
  3. 第一种:需要传参版本,传二级指针,因为只有二级指针才能修改一级指针的指向。因此这里使用二级指针进行初始化赋值。-1可以改为任何值,因为哨兵位存储的数据为无效数据。
  4. 第二种:不传参版本,直接创建并返回哨兵位的地址。
  5. 使用上的区别:第一种需要自己定义一个双向链表类型的空指针,然后使用调用该函数进行传参初始化,注意传参时需&。第二种自己创建一个双向链表类型的空指针并调用该函数进行赋值。


3.LTPrint函数

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

解析:

  1. 功能:打印链表的所有节点数据。
  2. 参数:链表的头结点(哨兵位)。
  3. 首先断言头结点不能为空,创建一个 pcur 指针指向第一个有效节点,
  4. 然后进行循环遍历时,因为该链表是循环链表,因此循环一遍的判定是 pcur 指向的节点不是头结点。循环体里就是打印数据然后让pcur走向下一个节点。打印完后换行即可。


4.LTPushBack函数

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先修改新节点的指针指向newnode->next = phead;//next指针指向哨兵位newnode->prev = phead->prev;//prev指针指向尾节点//再修改旧尾节点和哨兵位phead->prev->next = newnode;//旧尾节点的next指针指向新尾节点phead->prev = newnode;//哨兵位后指针指向新尾节点
}

解析:

  1. 功能:在链表的尾部插入一个节点
  2. 参数:头结点(哨兵位)和需要插入的节点数据
  3. 首先断言头结点不为空,申请新节点,然后就需要改变新节点和受影响节点的指针指向。这里需要注意修改的前后顺序,
  4. 先修改新节点的指针指向,新节点的 next 指针应该指向头结点,因为是循环链表,原尾结点可以通过头结点找到,即 phead->prev 指向的就是原来的尾结点,将新节点的 prev 指针指向原尾结点。
  5. 修改完新节点就需要修改头结点和原尾结点的指针指向,原尾结点依旧可以通过头结点的 prev 指针找到,将其 next 指针指向新节点。最后就是头结点的 prev 指针需要指向新节点。


5.LTPushFront函数

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先还是先修改新节点的指向newnode->next = phead->next;newnode->prev = phead;//然后修改旧头结点和哨兵位phead->next->prev = newnode;phead->next = newnode;
}

解析:

  1. 功能:在第一个有效节点之前插入数据,也就是头结点(哨兵位)之后
  2. 参数:头结点(哨兵位)和需插入的节点数据、
  3. 首先头插为什么不是在头结点前面插入数据,很简单,头结点的 prev 指针就是指向尾结点的,所以如果在头结点之前插入数据,其实就是尾插。
  4. 实现过程,开头还是断言和申请新节点,然后先修改新节点的前后指针指向,原第一个有效节点可以通过头结点的 next 指针找到。修改完新节点后就修改原第一节点的prve指针,使其指向新节点,最后修改头结点的 next 指针也指向新节点。


6.LTEmpty函数

//判空
//只有哨兵位节点说明链表为空
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}

解析:

  1. 功能:判断链表是不是空链表,也就是判断链表是不是只有头结点(哨兵位)
  2. 参数:头结点(哨兵位)
  3. 该函数主要为后续删除链表节点时判断链表是否为空链表,实现简单,断言头结点后,直接判断头结点的下一个节点是不是还是头结点即可。


7.LTPopBack函数

//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//先保存尾结点和倒数第二个节点LTNode* del = phead->prev;LTNode* prev = del->prev;//再修改倒数第二节点和哨兵位节点指向prev->next = phead;phead->prev = prev;//释放尾结点free(del);del = NULL;
}

解析:

  1. 功能:删除尾结点
  2. 参数:头结点(哨兵位)
  3. 首先断言判断头结点是否为空指针以及链表是否为空链表,然后创建 del 指针保存尾结点,创建 prev 指针保存倒数第二个节点,避免修改完受影响节点的前后指针指向后找不到尾结点,
  4. 然后修改倒数第二个节点的 next 指针指向头结点,以及头结点的 prev 指针指向倒数第二个节点。最后释放尾结点(del),最后释放尾结点 del 即可。


8.LTPopFront函数

//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//还是先保存第一个有效节点LTNode* del = phead->next;del->next->prev = phead;//修改第二个有效节点指向phead->next = del->next;//修该哨兵位指向//释放free(del);del = NULL;
}

解析:

  1. 功能:删除第一个有效节点,也就是头结点(哨兵位)的下一个节点
  2. 参数:头结点(哨兵位)
  3. 还是先断言判断头结点和链表是否为空,创建 del 指针保存第一个有效节点,然后先通过 del指针找到并修改第二个有效节点的 prev 指针,使其指向头结点,再修改头结点的 next 指针指向第二个节点。最后就可以直接释放第一个节点 del 即可。

9.LTFind函数

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

解析:

  1. 功能:根据指定数据查找对应的节点,然后返回该节点的地址,主要是配合后面两个函数使用
  2. 有了上面函数实现的经验,查找函数实现简单,循环遍历,循环链表循环一遍的标志是下一节点为头结点,找到就返回节点的地址,找不到返回空指针。


10.LTInsert函数

//在指定位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);//先修改新节点朝向newnode->next = pos->next;newnode->prev = pos;//在修改受影响的两个节点朝向,注意顺序pos->next->prev = newnode;pos->next = newnode;
}

解析:

  1. 功能:在指定位置之后插入数据,这个指定位置需要通过 LTFind 函数指定。
  2. 双向链表插入数据其实都差不多,先修改新节点的前后指针,再修改受影响节点的指针。这里不再赘述。


11.LTErase函数

//删除指定位置的节点
void LTErase(LTNode* pos)
{assert(pos);//先修改pos的前后节点pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}

解析:

  1. 功能:删除指定位置的节点
  2. 这个实现更加简单,通过 pos 指针先修改前后节点的指针指向,然后就可以直接释放掉自己。


11.LTDesTroy函数

//销毁
//第一种:传二级指针
//void LTDesTroy(LTNode** pphead)
//{
//	assert(pphead && *pphead);
//
//	LTNode* pcur = (*pphead)->next;
//	while (pcur != *pphead)
//	{
//		LTNode* Next = pcur->next;
//		free(pcur);
//		pcur = Next;
//	}
//
//	free(*pphead);
//	*pphead = NULL;
//	pcur = NULL;
//}//第二种:传一级指针,缺点是需手动将哨兵位置空
void LTDesTroy(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){LTNode* Next = pcur->next;free(pcur);pcur = Next;}free(phead);phead = pcur = NULL;
}

解析:

  1. 销毁函数有两种,第二种为优化后的版本,同样也是为了接口的一致性,避免使用时混淆,因此传一级指针合适,但是带来的缺点就是需要手动将哨兵位置空。
  2. 两个版本的函数实现方式大致相同,循环遍历,在释放当前节点之前要先使用 Next 指针保存下一节点的地址。
  3. 第一个版本缺点是需要传二级指针,但是不需要手动将哨兵位置空,函数内部就可以实现置空,第二版本优点就是同其他接口一样,只需要传一级指针,缺点就是在调用完该函数后需要手动将哨兵位置空。


四、List.c文件的完整代码

#include "List.h"//申请新节点
LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;//前后指针全部指向自己,自循环newnode->next = newnode->prev = newnode;return newnode;
}//初始化
//第一种:传二级指针
//void LTInit(LTNode** pphead)
//{
//	assert(pphead);
//
//	//创建一个头结点(哨兵位)
//	*pphead = LTBuyNode(-1);
//}//第二种:不传参
LTNode* LTInit()
{return LTBuyNode(-1);
}//打印
void LTPrint(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先修改新节点的指针指向newnode->next = phead;//next指针指向哨兵位newnode->prev = phead->prev;//prev指针指向尾节点//再修改旧尾节点和哨兵位phead->prev->next = newnode;//旧尾节点的next指针指向新尾节点phead->prev = newnode;//哨兵位后指针指向新尾节点
}//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先还是先修改新节点的指向newnode->next = phead->next;newnode->prev = phead;//然后修改旧头结点和哨兵位phead->next->prev = newnode;phead->next = newnode;
}//判空
//只有哨兵位节点说明链表为空
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//先保存尾结点和倒数第二个节点LTNode* del = phead->prev;LTNode* prev = del->prev;//再修改倒数第二节点和哨兵位节点指向prev->next = phead;phead->prev = prev;//释放尾结点free(del);del = NULL;
}//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//还是先保存第一个有效节点LTNode* del = phead->next;del->next->prev = phead;//修改第二个有效节点指向phead->next = del->next;//修该哨兵位指向//释放free(del);del = NULL;
}//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;//循环遍历while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}//在指定位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);//先修改新节点朝向newnode->next = pos->next;newnode->prev = pos;//在修改受影响的两个节点朝向,注意顺序pos->next->prev = newnode;pos->next = newnode;
}//删除指定位置的节点
void LTErase(LTNode* pos)
{assert(pos);//先修改pos的前后节点pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}//销毁
//第一种:传二级指针
//void LTDesTroy(LTNode** pphead)
//{
//	assert(pphead && *pphead);
//
//	LTNode* pcur = (*pphead)->next;
//	while (pcur != *pphead)
//	{
//		LTNode* Next = pcur->next;
//		free(pcur);
//		pcur = Next;
//	}
//
//	free(*pphead);
//	*pphead = NULL;
//	pcur = NULL;
//}//第二种:传一级指针,缺点是需手动将哨兵位置空
void LTDesTroy(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){LTNode* Next = pcur->next;free(pcur);pcur = Next;}free(phead);phead = pcur = NULL;
}


五、使用演示

使用测试文件 test.c 进行演示:

#include "List.h"void ListTest1()
{//创建哨兵位(头结点)LTNode* plist = LTInit();//尾插4个节点LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);printf("尾插:");LTPrint(plist);//头插4个节点LTPushFront(plist, 11);LTPushFront(plist, 12);LTPushFront(plist, 13);LTPushFront(plist, 14);printf("头插:");LTPrint(plist);//在1后面插入一个数据LTInsert(LTFind(plist, 1), 66);printf("在1后面插入66:");LTPrint(plist);//尾删LTPopBack(plist);printf("尾删:");LTPrint(plist);//头删LTPopFront(plist);printf("头删:");LTPrint(plist);//删除11LTErase(LTFind(plist, 11));printf("删除11:");LTPrint(plist);//销毁LTDesTroy(plist);plist = NULL;//需要手动置空printf("链表已销毁\n");}int main()
{ListTest1();return 0;
}

运行结果:


总结

        以上就是本文的全部内容,感谢支持。

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

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

相关文章

数据结构-栈(理解版)

一、栈的定义 相信大家对于栈或多或少有一些了解&#xff0c;可能大多数人会告诉你栈是一种先进后出的数据结构。这其实说了跟没说一样(❁◡❁)&#xff01;当然&#xff08;last in&#xff0c;first out&#xff09;是栈最有特色的性质。 这里可以给大家一些比较好理解的例…

大模型增量训练--基于transformer制作一个大模型聊天机器人

针对夸夸闲聊数据集&#xff0c;利用UniLM模型进行模型训练及测试&#xff0c;更深入地了解预训练语言模型的使用方法&#xff0c;完成一个生成式闲聊机器人任务。 项目主要结构如下&#xff1a; data 存放数据的文件夹 dirty_word.txt 敏感词数据douban_kuakua_qa.txt 原始语…

sqlserver迁移数据库文件存储位置

业务背景&#xff1a;由于C盘爆满&#xff0c;需要将数据库文件迁移到别处比如D盘 下面以某一个数据库转移为示例&#xff1a;&#xff08;可以用SSMS工具&#xff0c;新建查询配合使用&#xff09; 1.查询数据库文件存储路径 sql语句&#xff1a; -- 查询路径 USE QiangTes…

HarmonyOS/OpenHarmony 离线加载web资源,并实现web资源更新

关键词&#xff1a;h5离线包加载、h5离线包更新、沙箱 在上一篇文章中&#xff0c;我们已经介绍了如何将 rawfile 资源文件中的文件数据拷贝到沙箱下&#xff0c;那么该篇文章将介绍如何加载该沙箱目录下的文件资源&#xff08;此处以打包后的web资源为例&#xff09;&#xf…

Android 12系统源码_输入系统(三)输入事件的加工和分发

前言 上一篇文章我们具体分析了InputManagerService的构造方法和start方法&#xff0c;知道IMS的start方法经过层层调用&#xff0c;最终会触发Navite层InputDispatcher的start方法和InputReader的start方法。InputDispatcher的start方法会启动一个名为InputDispatcher的线程&…

linux驱动编程——等待队列

一、等待队列 可实现调用read函数时阻塞等。 1、流程 &#xff08;1&#xff09;初始化等待队列头&#xff08;带参宏&#xff09; init_waitqueue_head(q) 等待队列头wq数据类型&#xff1a; wait_queue_head_t&#xff0c;等待条件condition&#xff1a;int型变量。 &…

(c++)内存四区:1.代码区2.全局区(静态区)3.栈区4.堆区

//内存四区&#xff1a;1.代码区 2.全局区 3.栈区 4.堆区 1.放在代码区的有&#xff1a;1.写的代码&#xff1a;只读的、共享的、存放的二进制机器指令、由操作系统直接管理 2.放在全局区的有&#xff1a;1.全局的&#xff08;变量或常量&#xff09; 2.静态的&#xff0…

rdp远程桌面服务协议概述

rdp远程桌面服务协议概述 什么是远程桌面服务远程桌面服务的通信过程及功能 建立连接资源重定向与用户体验断开连接 远程桌面服务的协议架构 核心协议与基础通信虚拟通道与扩展协议协议协作与层次划分协议的可扩展性协议扩展与性能优化 总结参考 rdp远程桌面服务协议概述 对于…

SpringBoot(Java)实现MQTT连接(本地Mosquitto)通讯调试

1.工作及使用背景 工作中需要跟收集各种硬件或传感器数据用于Web展示及统计计算分析&#xff0c;如电表、流量计、泵、控制器等物联网设备。 目前的思路及解决策略是使用力控或者杰控等组态软件实现数据的转储&#xff08;也会涉及收费问题&#xff09;&#xff0c;通过组态软件…

鸿蒙开发(NEXT/API 12)【应用间消息通信】手机侧应用开发

在手机侧与穿戴设备侧构建应用到应用的通信隧道&#xff0c;用于收发应用自定义的报文消息以及文件。实现手机应用和穿戴设备应用间的交互&#xff0c;为用户提供分布式场景和体验。比如手机应用发送音频文件到穿戴设备侧应用&#xff0c;实现在穿戴设备侧应用上播放音乐&#…

BUG——IMX6ULL编译正点原子Linux内核报错

最初编译的是正点原子改过的Linux内核&#xff0c;可能是版本问题&#xff0c;一直报错&#xff0c;无法成功编译。然后换成NXP官方Linux内核6.6版本&#xff0c;初始编译虽然也报各种错&#xff0c;但都是缺少库或相关工具&#xff0c;全部安装后就可以成功编译出镜像了&#…

Leetcode 740. 删除并获得点数

原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums &#xff0c;你可以对它进行一些操作。 每次操作中&#xff0c;选择任意一个 nums[i] &#xff0c;删除它并获得 nums[i] 的点数。之后&#xff0c;你必须删除 所有 等于 nums[i] - 1 和…

cscode搭建vue项目

创建前安装环境 ctrlj弹出终端 window需要管理员运行并且授权 node -v # 显示版本号&#xff0c;说明 node 已经装好 npm -v # 显示版本号&#xff0c;说明 npm 可以使用 # 安装cnpm npm install -g cnpm --registryhttps://registry.npm.taobao.org cnpm -v # 显示版本号&a…

【hot100-java】【合并两个有序链表】

记忆中&#xff0c;两个指针合并即可。 建立哨兵节点dum /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { t…

Docker更换阿里容器镜像源

以Mac为例&#xff0c; 一、获取阿里容器镜像加速器地址 访问阿里云官网https://cn.aliyun.com/ 登录阿里云&#xff0c;没有账号就注册一个 登录完成后在搜索框搜索&#xff0c;容器镜像服务&#xff0c;并打开 点击管理控制台&#xff0c;进入管理控制台 左侧点击镜像加速…

【Python入门】20个Python自动化脚本,解放双手、事半功倍

如果你正在学习Python&#xff0c;那么你需要的话可以&#xff0c;点击这里&#x1f449;Python重磅福利&#xff1a;入门&进阶全套学习资料、电子书、软件包、项目源码等等免费分享&#xff01; 在当今的快节奏工作环境中&#xff0c;自动化不再是一种奢侈&#xff0c;而是…

下一代性能怪兽RTX 5090最新规格更新与Blackwell架构解析

据悉&#xff0c;目前各家AIC厂商已经陆续收到NVIDIA的相关资料&#xff0c;RTX 5090、RTX 5080已经正式进入开案阶段&#xff0c;也就是厂商们开始设计各自的产品方案了。不出意外&#xff0c;年初的CES 2025上会看到RTX 5090/5080的发布。 作为NVIDIA的新一代GPU&#xff0c…

【车联网安全】车端知识调研

一、CAN总线&#xff1a; 1、定义&#xff1a; CAN 总线相当于汽车的神经网络&#xff0c;连接车内各控制系统,其通信采用广播机制&#xff0c;各连接部件均可收发控制消息&#xff0c;通信效率高&#xff0c;可确保通信实时性。当前市场上的汽车至少拥有一个CAN网络&#xff0…

如何进行“服务器内部错误”的诊断 | OceanBase诊断案例

本文作者&#xff1a;任仲禹&#xff0c;爱可生数据库高级工程师&#xff0c;擅长故障分析和性能优化。 的OMS迁移工具具备丰富的功能。但在实际运维场景中&#xff0c;我们可能会遇到各种问题&#xff0c;其中“服务器内部错误”便是一个较为棘手的问题&#xff0c;因为界面上…

【易上手快捷开发新框架技术】nicegui标签组件lable用法庖丁解牛深度解读和示例源代码IDE运行和调试通过截图为证

传奇开心果微博文系列 序言一、标签组件lable最基本用法示例1.在网页上显示出 Hello World 的标签示例2. 使用 style 参数改变标签样式示例 二、标签组件lable更多用法示例1. 添加按钮动态修改标签文字2. 点击按钮动态改变标签内容、颜色、大小和粗细示例代码3. 添加开关组件动…