数据结构【单链表】

news2024/11/18 15:47:24

文章目录

  • 前言
  • 单链表的概念
  • 单链表接口的实现
    • 头文件(`SLinkList.h`)
    • 单链表的创建
      • 创建链表节点
    • 关于传参
    • 单链表的顺序插入
      • 关于`assert`对象
      • 尾插
        • 正常情况(链表不为空)
        • 特殊情况(链表为`NULL`)
        • 代码
      • 头插
    • 单链表的顺序删除
      • `assert`对象
      • 尾删
        • 正常情况
        • 特殊情况(链表只有一个节点)
      • 头删
    • 单链表的打印
    • 单链表的查找
    • 单链表的指定插入
      • 指定位置前插入
        • 正常情况
        • 特殊情况(是同一个节点)
      • 指定位置之后的插入
    • 删除指定节点
      • 正常情况
        • 特殊情况(`pos == *pphead`)
    • 销毁链表
  • 完整代码
    • 头文件(`SLinkList.h`)
    • 源文件(`SLinkList.c`)
  • 结语

前言

我们前面学习了顺序表(点击此处跳转到顺序表博客),那么它有什么问题呢?

1.每次在头或者指定位置前插入数据,都需要将数据往后挪动一位,这样太浪费时间了。
2.内存每次都是以2倍增长,这样或多或少会造成空间浪费。
假设我们有一个顺序表来存放班级学生的信息,假设这个班的50个学生刚好填满了这个顺序表,如果这时来了个新同学要加入这个班级,那这个顺序表就需要以2倍的形式扩容,这样顺序表的大小就从原来的50,扩容到了100;但是我只加了一个新同学,这样就有49个数据的空间被浪费掉了

这时候用单链表就可以解决空间浪费的问。

单链表的概念

单链表(Singly Linked List)是线性表的一种,它的特点是数据元素是以节点的形式存储在内存中,它在物理结构上是不连续的。
每个节点都是由一个数据域和一个指针域组成的,数据域用来存放数据元素,指针域则用来指向该节点的后一个节点。所以这种结构就使得单链表在逻辑结构上是连续的。
在单链表中,节点是通过指针链接起来的,这些指针指向每个节点的后继节点。类似于下图
在这里插入图片描述
此外,单链表的最后一个节点的指针指向NULL,这表示没有更多的节点了

单链表接口的实现

头文件(SLinkList.h)

#pragma once

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

typedef int SLLDATATYPE;

typedef struct SLListNode
{
	SLLDATATYPE data;
	struct SLListNode* next;
}SLListNode;

//打印
void SLListPrint(SLListNode* phead);
//销毁
void SLListDestroy(SLListNode** pphead);
//新节点
SLListNode* SLListNewNode(SLLDATATYPE x);

//尾插
void SLListPushBack(SLListNode** pphead, SLLDATATYPE x);
//尾删
void SLListPopBack(SLListNode** pphead);

//头插
void SLListPushFront(SLListNode** pphead, SLLDATATYPE x);
//头删
void SLListPopFront(SLListNode** pphead);

//查找
SLListNode* SLListFind(SLListNode* phead, SLLDATATYPE x);

//在指定位置之前插入
void SLListInsert(SLListNode** pphead, SLListNode* pos, SLLDATATYPE x);
//在指定位置之后插入
void SLListInsertAfter(SLListNode* pos, SLLDATATYPE x);

//删除指定节点
void SLListErase(SLListNode** pphead, SLListNode* pos);

单链表的创建

因为创造单链表,其实是创造一个节点,所以在创建的时候就会给他赋我们要给的值。
因为每个节点都是独立的一块空间,所以我们就可以使用malloc函数来开辟空间。

	SLListNode* plist = (SLListNode*)malloc(sizeof(SLListNode));
	plist->data = x;//x为自己给的值
	plist->next = NULL;

创建链表节点

根据上文我们就可以独立封装一个函数,这样也是为了方便和避免代码有过多的重复。

//创建节点
SLListNode* SLListNewNode(SLLDATATYPE x)
{
	SLListNode* tmp = (SLListNode*)malloc(sizeof(SLListNode));//创造一个新的节点,大小为这个节点(struct)的大小
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	tmp->data = x;
	tmp->next = NULL;
	return tmp;
}

这样我们创造单链表(创建第一个节点)就可以这样优化

	SLListNode* plist = SLListNewNode(x); //x为自己给的值

当然我们也可以直接给NULL;

SLListNode* plist = NULL;

关于传参

因为plist的类型是SLLNode*,所以当我们把plist传过去,用SLLNode*来接收的话,其实是在进行传值调用,我们都知道,传值调用其实是将实参临时拷贝一份给形参,这时候对形参操作并不会影响到实参。

所以当我们要对单链表plist进行更改的时候,应该传plist的地址,那么我们接收的类型就要用SLListNode**

当我们只是要查找、打印的时候,就可以进行传址调用。

单链表的顺序插入

关于assert对象

我们需要的断言对象应该是类型为SLListNode**的接收参数。
因为我们对单链表进行操作就会解引用,所以当接收参数为NULL时,我们解引用单链表就会报错。

尾插

尾插分为两种情况,第一种就是正常情况,第二种是这个链表为NULL

正常情况(链表不为空)

第一步:创建一个新节点
第二步:找到单链表的尾节点(next指向NULL的节点),这我们直接循环就可以了
第三步:将尾节点的next指针指向新节点

特殊情况(链表为NULL)

这就很简单了,直接将新节点赋值给链表就可以了

代码
//尾插
void SLListPushBack(SLListNode** pphead, SLLDATATYPE x)
{
	//两个p代表二级指针(这看个人心情)
	assert(pphead);
	SLListNode* NewNode = SLListNewNode(x);

	//链表为空
	if (*pphead == NULL)
	{
		*pphead = NewNode;
	}
	//链表不为空
	else
	{
		SLListNode* ptail = *pphead;
		//找尾
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		ptail->next = NewNode;
	}
}

头插

头插就很简单了。
创建一个新节点 ——> 将新节点的next指针指向头节点——> 让新节点成为新的头节点。
而且不需要考虑节点是否为空,因为我解引用的是新节点,不会对空节点(NULL)进行解引用。
代码如下:

//头插
void SLListPushFront(SLListNode** pphead, SLLDATATYPE x)
{
	assert(pphead);
	SLListNode* NewNode = SLListNewNode(x);
	NewNode->next = *pphead;//将NewNode的next 指向*pphead
	*pphead = NewNode;
}

单链表的顺序删除

assert对象

这时候就有多加一个 *pphead(没有节点了我还删什么呢?),这样也能防止我们对NULL进行解引用。

尾删

这时候就又要分为两个情况了,一种正常情况,另一种就是链表只有一个节点。
为什么会有第二种情况,我们待会再说。

正常情况

思路:首先我们看到是要找到尾节点的,我们找到尾节点后将它释放并置为NULL;那么只有就有一个问题,我尾节点的前一个节点的next是指向尾节点,如果没对其进行处理,就会造成野指针

所以我们要创建两个指针(ptail、pcur),ptail个当尾节点,pcur当尾节点的前一个节点,这样,我们将尾节点释放并置为NULL后,直接将pcur的next指针指向被置为NULL的ptail就可以了

		SLListNode* pcur = *pphead;//尾节点的前一个节点
		SLListNode* ptail = *pphead;//要被删除的尾节点
		//找尾
		while (ptail->next)
		{
			pcur = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		ptail = NULL;
		pcur->next = ptail;

这样就又有一个问题,如果链表只有一个节点怎么办?
看上面的代码,我们能看到当链表只有一个节点的的时候,我们将节点赋给了ptailpcur,这时候ptail->next = NULL,循环条件就为假,就没有进行循环,直接将ptail释放并置为了NULL,但是只有我们在对pcur进行解引用的时候就出现问题了,ptail已经置为NULL了,已经将链表置为NULL了,我们这时候对pcur进行了解引用,这是什么?这不就是对NULL解引用吗。
所以我们就有了下面这个方案。

特殊情况(链表只有一个节点)

直接对这个节点进行释放。
完整代码如下:

void SLListPopBack(SLListNode** pphead)
{
	assert(pphead && *pphead);
	//特殊情况(只有一个节点)
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//正常情况
	else
	{
		SLListNode* pcur = *pphead;//尾节点的前一个节点
		SLListNode* ptail = *pphead;//要被删除的尾节点
		//找尾
		while (ptail->next)
		{
			pcur = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		ptail = NULL;
		pcur->next = ptail;
	}
}

头删

头删就很简单了。
创建一个指针pcur用来接收*pphead ——> *pphead = (*pphead)->next ——> 释放掉pcur
补充:'->‘这个操作符的优先级比’*'高,所以我们要先用括号括起来
代码如下:

//头删
void SLListPopFront(SLListNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	SLListNode* pcur = *pphead;
	*pphead = (*pphead)->next;// '->'这个操作符的优先级比'*'高,所以我们要先用括号括起来
	free(pcur);
}

单链表的打印

只要给循环一个限制条件就可以了

//打印
void SLListPrint(SLListNode* phead)
{
	while (phead)
	{
		printf("%d -> ", phead->data);
		phead = phead->next;
	}
	printf("NULL");
}

单链表的查找

查找我们分为两个情况,找到和没找到。
找到:返回被找到数据的节点。
没找到:返回NULL。

//查找
SLListNode* SLListFind(SLListNode* phead, SLLDATATYPE x)
{
	SLListNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
			return pcur;
		pcur = pcur->next;
	}
	return NULL;
}

有了这个函数,我们就可以实现在指定位置的增加和删除了。

单链表的指定插入

pos也要用assert断言

指定位置前插入

分为两种情况,pos*pphead不是头一个节点和是一个节点

正常情况
  • 第一步:找到pos(指定位置)的前一个节点
  • 第二步:将NewNode的next指向pos;
  • 第三步:将pos的前一个节点的next指向NewNode;
		//正常情况(pos和*pphead不是同一个节点)
		SLListNode* pcur = *pphead;
		//找到pos的前一个节点
		while (pcur->next != pos)
		{
			pcur = pcur->next;

		}

		NewNode->next = pos;
		pcur->next = NewNode;
特殊情况(是同一个节点)

指定位置和头节点是同一个节点,那么这时候在前面进行插入,这个行为是什么?
这不就是头插吗?
所以我们直接调用头插函数就可以了。

完整代码:

//在指定位置之前插入
void SLListInsert(SLListNode** pphead, SLListNode* pos, SLLDATATYPE x)
{
	assert(pphead && *pphead);//如果*pphead为NULL,那肯定找不到pos
	assert(pos);
	SLListNode* NewNode = SLListNewNode(x);
	//特殊情况(pos == *pphead)
	if (pos == *pphead)
	{
		//这就是头插
		SLListPushFront(pphead, x);
	}
	else
	{
		//正常情况(pos和*pphead不是同一个节点)
		SLListNode* pcur = *pphead;
		//找到pos的前一个节点
		while (pcur->next != pos)
		{
			pcur = pcur->next;

		}

		NewNode->next = pos;
		pcur->next = NewNode;
	}
}

指定位置之后的插入

我们就不需要传递plist了,因为我们不需要找到pos的前一个指针了。
我们直接改变pos的next指针就可以了。

//在指定位置之后插入
void SLListInsertAfter(SLListNode* pos, SLLDATATYPE x)
{
	assert(pos);

	SLListNode* NewNode = SLListNewNode(x);
	NewNode->next = pos->next;
	pos->next = NewNode;
	
}

删除指定节点

删除指定节点有两种情况,正常情况(pos和*pphead不是同一个节点)和特殊情况(pos == *pphead)

正常情况

  • 第一步:找到pos的前一个节点
  • 第二步:将pos前一个节点的next指向pos的next
  • 第三步:释放pos并将其置为NULL;
		//正常情况(pos和*pphead不是同一个节点)
		SLListNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = pos->next;
		free(pos);
		pos = NULL;
特殊情况(pos == *pphead)

这直接调用头删函数就可以了

完整代码:

//删除指定节点
void SLListErase(SLListNode** pphead, SLListNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	//特殊情况(pos == *pphead)
	if (pos == *pphead)
	{
		//直接调用头删
		SLListPopFront(pphead);
	}
	else
	{
		//正常情况(pos和*pphead不是同一个节点)
		SLListNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

销毁链表

这里销毁链表不能像销毁顺序表一样直接free掉就可以了,因为链表是以节点的形式存储的(每个节点都是独立的空间),所以我们需要从头节点一直释放到尾节点
代码:

//销毁
void SLListDestroy(SLListNode** pphead)
{
	assert(pphead && *pphead);
	SLListNode* pcur = *pphead;
	while (pcur)
	{
		SLListNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

完整代码

头文件(SLinkList.h)

#pragma once

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

typedef int SLLDATATYPE;

typedef struct SLListNode
{
	SLLDATATYPE data;
	struct SLListNode* next;
}SLListNode;

//打印
void SLListPrint(SLListNode* phead);
//销毁
void SLListDestroy(SLListNode** pphead);
//新节点
SLListNode* SLListNewNode(SLLDATATYPE x);

//尾插
void SLListPushBack(SLListNode** pphead, SLLDATATYPE x);
//尾删
void SLListPopBack(SLListNode** pphead);

//头插
void SLListPushFront(SLListNode** pphead, SLLDATATYPE x);
//头删
void SLListPopFront(SLListNode** pphead);

//查找
SLListNode* SLListFind(SLListNode* phead, SLLDATATYPE x);

//在指定位置之前插入
void SLListInsert(SLListNode** pphead, SLListNode* pos, SLLDATATYPE x);
//在指定位置之后插入
void SLListInsertAfter(SLListNode* pos, SLLDATATYPE x);

//删除指定节点
void SLListErase(SLListNode** pphead, SLListNode* pos);

源文件(SLinkList.c)

#include"SLinkList.h"

//打印
void SLListPrint(SLListNode* phead)
{
	while (phead)
	{
		printf("%d -> ", phead->data);
		phead = phead->next;
	}
	printf("NULL");
}

//销毁
void SLListDestroy(SLListNode** pphead)
{
	assert(pphead && *pphead);
	SLListNode* pcur = *pphead;
	while (pcur)
	{
		SLListNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

//创建节点
SLListNode* SLListNewNode(SLLDATATYPE x)
{
	SLListNode* tmp = (SLListNode*)malloc(sizeof(SLListNode));//创造一个新的节点,大小为这个节点(struct)的大小
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	tmp->data = x;
	tmp->next = NULL;
	return tmp;
}

//尾插
void SLListPushBack(SLListNode** pphead, SLLDATATYPE x)
{
	//两个p代表二级指针(这看个人心情)
	assert(pphead);
	SLListNode* NewNode = SLListNewNode(x);

	//链表为空
	if (*pphead == NULL)
	{
		*pphead = NewNode;
	}
	//链表不为空
	else
	{
		SLListNode* ptail = *pphead;
		//找尾
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		ptail->next = NewNode;
	}
}
//尾删
void SLListPopBack(SLListNode** pphead)
{
	assert(pphead && *pphead);
	//特殊情况(只有一个节点)
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//正常情况
	else
	{
		SLListNode* pcur = *pphead;//尾节点的前一个节点
		SLListNode* ptail = *pphead;//要被删除的尾节点
		//找尾
		while (ptail->next)
		{
			pcur = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		ptail = NULL;
		pcur->next = ptail;
	}
}

//头插
void SLListPushFront(SLListNode** pphead, SLLDATATYPE x)
{
	assert(pphead);
	SLListNode* NewNode = SLListNewNode(x);
	NewNode->next = *pphead;//将NewNode的next 指向*pphead
	*pphead = NewNode;
}

//头删
void SLListPopFront(SLListNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	SLListNode* pcur = *pphead;
	*pphead = (*pphead)->next;// '->'这个操作符的优先级比'*'高,所以我们要先用括号括起来
	free(pcur);
}

//查找
SLListNode* SLListFind(SLListNode* phead, SLLDATATYPE x)
{
	SLListNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
			return pcur;
		pcur = pcur->next;
	}
	return NULL;
}

//在指定位置之前插入
void SLListInsert(SLListNode** pphead, SLListNode* pos, SLLDATATYPE x)
{
	assert(pphead && *pphead);//如果*pphead为NULL,那肯定找不到pos
	assert(pos);
	SLListNode* NewNode = SLListNewNode(x);
	//特殊情况(pos == *pphead)
	if (pos == *pphead)
	{
		//这就是头插
		SLListPushFront(pphead, x);
	}
	else
	{
		//正常情况(pos和*pphead不是同一个节点)
		SLListNode* pcur = *pphead;
		//找到pos的前一个节点
		while (pcur->next != pos)
		{
			pcur = pcur->next;

		}

		NewNode->next = pos;
		pcur->next = NewNode;
	}
}
//在指定位置之后插入
void SLListInsertAfter(SLListNode* pos, SLLDATATYPE x)
{
	assert(pos);

	SLListNode* NewNode = SLListNewNode(x);
	NewNode->next = pos->next;
	pos->next = NewNode;
}

//删除指定节点
void SLListErase(SLListNode** pphead, SLListNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	//特殊情况(pos == *pphead)
	if (pos == *pphead)
	{
		//直接调用头删
		SLListPopFront(pphead);
	}
	else
	{
		//正常情况(pos和*pphead不是同一个节点)
		SLListNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

结语

最后感谢您能阅读完此片文章,如果有任何建议或纠正欢迎在评论区留言。

如果您认为这篇文章对您有所收获,点一个小小的赞就是我创作的巨大动力,谢谢!!!

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

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

相关文章

多联机分户计费控制系统

中央空调多联机分户计费控制系统&#xff0c;针对国内常见几种品牌的多联机空调系统实行&#xff0c;远程控制与计费管理。系统采用MQTT网络协议&#xff0c;以订阅/发布模式实行设备感知&#xff0c;实现对室外机、室内机的状态监测、实时故障报警、累计分摊费用的实时数据传导…

基于JAVA的Dubbo 实现的各种限流算法

在基于 Java 的 Dubbo 实现中&#xff0c;限流&#xff08;Rate Limiting&#xff09;同样是一个关键的需求。Dubbo 是阿里巴巴开源的一款高性能 Java RPC 框架&#xff0c;广泛应用于分布式服务架构中。实现限流可以帮助服务在高并发场景下保持稳定性和可靠性。以下是几种常见…

【el-table 实现自定义单选】

el-table 实现自定义单选 示例图片代码 示例图片 代码 row-click"singleElection"<el-table-columnalign"center"label"选择"><template slot-scope"scope"><el-radio:key"scope.row.id"v-model"templa…

在ARM开发板上,栈大小设置为2MB(常用设置)里面存放的数据

系列文章目录 在ARM开发板上&#xff0c;栈大小设置为2MB&#xff08;常用设置&#xff09;里面存放的数据 在ARM开发板上&#xff0c;栈大小设置为2MB&#xff08;常用设置&#xff09;里面存放的数据 系列文章目录 在ARM开发板上&#xff0c;栈&#xff08;Stack&#xff09;…

thingML的学习——什么是thingML

今天开始建模的学习&#xff0c;thingML是建模的一种工具 &#xff0c;也可以理解为一种建模语言&#xff0c;有自己的语法和语义。 ThingML 支持的多种平台和通信协议&#xff0c;如UART、I2C、MQTT、WebSocket、REST、ROS、Bluetooth、BLE和Zwave&#xff0c;通过插件机制&a…

目标检测网络:YOLOv3 模型复现

目录 YOLOv3 网络架构 YOLOv3 检测流程 YOLOv3 网络搭建 YOLOv3 网络架构 论文原址&#xff1a;https://arxiv.org/pdf/1804.02767 Code&#xff1a;https://github.com/ultralytics/yolov3 YOLO官方模型总览&#xff1a;https://pjreddie.com/darknet/yolo/#google_vign…

K8S中YAML案例

目录 案例&#xff1a;自主式创建service并关联上面的pod 案例&#xff1a;部署redis 案例&#xff1a;部署myapp 案例&#xff1a;部署MySQL数据库 总结 1.K8S集群中访问流向 K8S集群外部&#xff1a;客户端——nodeIP&#xff1a;nodeport——通过target port——podIP…

LiteOS-A内核中的procfs文件系统分析

一、 procfs介绍 procfs是类UNIX操作系统中进程文件系统&#xff08;process file system&#xff09;的缩写&#xff0c;主要用于通过内核访问进程信息和系统信息&#xff0c;以及可以修改内核参数改变系统行为。需要注意的是&#xff0c;procfs文件系统是一个虚拟文件系统&a…

AI副业:3天涨粉10w+,这些人凭什么这么火? (附教程案例)

大家好&#xff0c;我是向阳 今天给大家分享一下免费的 AI 插画工具 & 某书图文涨粉项目 某书上有这么一类笔记&#xff1a;精美的插话 几句提供情绪价值的文案 这类笔记可以说几乎是发一篇爆一篇&#xff01;获赞涨粉如喝水&#xff0c;并且评论区有不少人求图和求教程…

利用Axure模板快速设计,可视化大屏信息大屏,含近200例资源和各类部件

模板类别&#xff1a; **通用模板&#xff1a;**提供基础的布局和设计元素&#xff0c;适用于各种场景。 **行业特定模板&#xff1a;**如农业、医院、销售、能源、物流、政府机关等&#xff0c;针对不同行业提供专业模板。 **数据展示模板&#xff1a;**包括大数据驾驶舱、统…

正点原子LWIP学习笔记(一)lwIP入门

lwIP入门 一、lwIP简介&#xff08;了解&#xff09;二、lwIP结构框图&#xff08;了解&#xff09;三、如何学习lwIP&#xff08;熟悉&#xff09; 一、lwIP简介&#xff08;了解&#xff09; lwIP是一个小型开源的TCP/IP协议栈 阉割的TCP/IP协议 TCP/IP协议栈结构&#xff0…

什么是 UUID,uuid

文章目录 一、是什么二、为什么三、怎么用 标题&#xff1a;深入探讨UUID&#xff1a;全球唯一标识符的秘密 一、是什么 在当今数字化时代&#xff0c;唯一标识符&#xff08;UUID&#xff09;在计算机科学领域扮演着重要的角色。UUID是一种用于标识信息的唯一字符串&#xff0…

【数据结构】树、森林与二叉树的转换 |树的存储 |双亲表示法 |孩子表示法 |孩子兄弟表示法

&#x1f4d6;专栏文章&#xff1a;数据结构学习笔记 &#x1faaa;作者主页&#xff1a;格乐斯 前言 树的存储 双亲表示法孩子表示法孩子兄弟表示法 树、森林与二叉树的转换 树的存储 双亲表示法 首先给每个结点编号&#xff0c;再将这些结点的双亲结点的序号存储起来&a…

从零开始实现自己的串口调试助手(1) - ui界面搭建

UI 界面搭建 ui界面整体演示 ui对象拆分 更多的细节就不方便展开了&#xff0c;下面有提示完成ui设计的提示 在创建工程前 记得把编码改为utf-8 ui设计技巧: ctrl 鼠标左键实现拖动实现复制粘贴 groupBox &#xff1a; 带标题的文本框 栅格布局 -- 只有一个控件的时候会铺满…

C++使用范围for语句处理多维数组

循环访问二维数组时出现如下问题&#xff1a; error: invalid range expression of type int *; no viable begin function availablefor (auto col : row){^ ~~~ 1 error generated.问题复现 初始化数组 constexpr size_t rowCnt 3, colCnt 4; int ia[rowCnt][colCnt]; fo…

【网站项目】SpringBoot380百天公司财务管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

AD162A低功耗语音解码芯片,助眠耳机方案推荐—云信通讯

睡眠一直是很多人所追寻的目标&#xff0c;无论是因为工作压力过大&#xff0c;还是日常烦扰的思绪&#xff0c;一个好的睡眠质量对每个人来说都至关重要。为了解决这个问题&#xff0c;一些科技公司开发了高品质的助眠耳机&#xff0c;通过音乐和声音来帮助入睡&#xff0c;为…

618好物推荐,每一款都是心头好

618买什么&#xff1f;&#xff01;是不是又要开始剁手了&#xff1f;&#xff01;买贵不如买对&#xff0c;收下这份好物推荐&#xff0c;无论你是有购物需求还是观望中&#xff0c;无论是日常好物还是智能家电&#xff0c;无论你是追求平价还是实用&#xff0c;这一篇好物推荐…

eNSP-集线器(hub)连接局域网

一、拓扑结构搭建 二、主机配置 pc1、pc2、pc3 三、测试 Hub相当于大家共享一条线路(类似于电线搭电)&#xff0c;线路上的所有的设备都会接收同样的信息。

【MySQL02】【 InnoDB 记录存储结构】

文章目录 一、前言二、InnoDB 行格式1. COMPACT 行格式1.1 记录的额外信息1.2 记录的真实数据1.3 综上 2. REDUNDANT 行格式2.1 字段长度偏移列表2.2 记录头信息 3. DYNAMIC 行格式和 COMPPESED 行格式 三、InnoDB 数据页结构1. File Header (文件头部)2. Page Header (页面头部…