带你玩转数据结构-单链表(适合初学者的文章,讲解的很仔细哦)

news2024/12/23 10:42:44

在这里插入图片描述

前言:

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏: 🍔🍟🌯 C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解数据结构中链表的知识,;链表的分类,c语言实现单链表常见接口等.
金句分享:
✨山不向我走来,我便向山走去.✨

目录

  • 前言:
  • 一、链表介绍
    • 1.1 链表结构图:
    • 1.2 链表分类(图解分析)
  • 二、单链表实现:
    • 2.1 链表的"结点"声明:
    • 2.2 "插入"元素操作.
      • 单链表的"尾插":
      • 单链表的"头插"
      • 指定位置之后"插入"新节点
      • "申请新节点"函数
    • 2.3 "删除"元素操作.
      • 单链表的"尾删"
      • 单链表的"头删":
      • 单链表的"删除"指定的目标结点
    • 2.4 "查找"目标结点函数
    • 2.5 单链表的"打印"
    • 2.6 单链表的"销毁"
  • 三、总代码
    • 测试区(test.c)
    • 接口实现区(SList.c)
    • 函数声明区(SList.h)

一、链表介绍

顺序表缺点:

  1. 中间/头部的插入删除,时间复杂度为O(N),因为需要移动数据.
  2. 增容需要申请新空间,特别是异地扩容,拷贝数据,释放旧空间。消耗不小。
  3. 增容不是一次增容到位,而是每次增容后判断是否符合要求,并且增容一般是2倍增容,一次次扩容消耗太大.
  4. 除此之外,还可能有一定的空间浪费。
    例如:当前容量为200,如果有201个待插入数据,势必要增容到400(原来容量的两倍),这样就浪费了199个空间.

我们不妨来看一下链表的存储结构.

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

🌰栗子
单链表的结点声明:

typedef int DateType;
typedef struct SListN0de
{
	DateType Date;//数据域
	struct SListN0de* next;//指针域
}SLTNode;

结点:

1.1 链表结构图:

在这里插入图片描述

通过上图我们不难知道:

  1. 链表在逻辑上是连续的(next指针,指向下一个结点,链接在一起),而物理上可能连续,也可能不连续.
  2. 链表是由一个个结点链接在一起组成的,每个结点其实是malloc在堆区上申请的,所以地址可能连续,也可能不连续.

1.2 链表分类(图解分析)

共有八种链表,我们主要学习不带头单向链表带头双向链表,学会这两种,其它的大同小异,写起来并不苦难.
在这里插入图片描述

单向、双向:

在这里插入图片描述

不带头、带头:
在这里插入图片描述

带头与不带头的区别在于:
带头:链表是通过一个特殊的结点—头结点指向链表的第一个有效结点.
不带头:通过结点指针指向链表的第一个有效结点.

头结点作用:传送门

不须换、循环:
在这里插入图片描述

重点掌握:

  1. 无头单向非循环链表(本篇重点):结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多,因为单链表不能回头,可以考察的地方很多.
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这种结构的链表虽然结构复杂,但是优势也很明显,并且实现起来反而很简单.后续跟着学到了就可以理解了.

在这里插入图片描述

二、单链表实现:

2.1 链表的"结点"声明:

typedef int DateType;
typedef struct SListN0de
{
	DateType Date;//数据域
	struct SListN0de* next;//指针域
}SLTNode;

单链表初始化:
单链表是不需要初始化操作的,只需要创建一个结点指针就行.初始状态:指针指向NULL.

头指针:

SLTNode* plist=NULL;

2.2 "插入"元素操作.

我们需要插入数据时,只需要申请一个结点,将数据放入结点,然后将结点链接起来就行.

单链表的"尾插":

单链表的尾插步骤:

  1. 找尾:
    由于单链表的结点之间不一定是连续存储的,不支持向顺序表那样的随机访问,需要通过遍历才能找到目标结点.
  2. 将最后一个结点的next指向新节点.

图解:
在这里插入图片描述
那如果链表本身就是空链表呢?
此时需要修改头指针,将头指针指向这个新节点.

注意:

  1. 需要传二级指针:
    这点很重要,因为需要修改头指针,而头指针的类型是:SLTNode*,相信友友们学到这里应该知道,如果想要在函数中形参的改变影响实参,则需要传址调用,通过地址来影响实参.
    那么,指针的地址是?二级指针相信友友们应该没有忘记.😂😂😂

  2. 断言,这里需要灵活断言.

在这里插入图片描述

"尾插"函数声明:

void PushBack(SLTNode** pphead, DateType x)

pphead需要断言:
pphead是指向 *pphead(一级指针/头指针)的指针,即值存储的是头指针的地址,只要头指针存在,则不为空.而头指针一定存在.

*phead不能断言:
*phead是头指针,头指针在链表为空时,头指针的值是NULL,所以不能断言.
链表中有数据时,指向第一个结点,值是第一个结点的地址.

  1. 头指针是很重要的一个指针,我们都是通过头指针找到链表的,所以,除了头插需要修改头指针以外,其他插入都不能修改头指针,所以我们需要创建一个临时指针:SLTNode*tail = *pphead代替头指针找尾.

代码:

void PushBack(SLTNode** pphead, DateType x)
{
	assert(pphead);//如果头指针的地址为NULL,则报错.
	SLTNode*tail = *pphead;//创建一个指针代替头指针遍历
	SLTNode* newnode = BuyNode(x);
	//*pphead代表代表头指针,phead表示头指针的地址
	//如果*pphead指向NULL,说明为空链表
	if (*pphead == NULL)
	{
		//这里可以使用tail代替*phead吗?
		//不能,因为这里要改变的是,头结点的指针,需要用二级指针(解引用)来改变
		*pphead = newnode;//空链表是将头指针指向新节点
	}
	else
	{
		//找尾巴,画图解析
		//这里可以使用tail,是因为,要改变的是结构体的内容,只需要用结构体指针(解引用)就行
		while ( tail->next != NULL)//如果该节点的下一个节点是空指针则停止循环
		{
			tail = tail->next;
		}
		tail->next = newnode;//让尾节点的next指向新节点.
	}
}

单链表的"头插"

尾插是不是显得有些麻烦?那我们试着看一下头插.

头插步骤:

  1. 创建一个新节点.
  2. 将新节点指向头指针指向的结点.
  3. 更新头指针(头指针指向新节点)

图解:
在这里插入图片描述
代码实现:

//写法1
void PushFront(SLTNode** pphead, DateType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	//下面两句的顺序不能变
	newnode->next = *pphead;
	*pphead = newnode;
}

写法2:

//写法2
void PushFront(SLTNode** pphead, DateType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	SLTNode* phead = *pphead;//创建一个临时指针,保存一下头指针指向的头结点.
	//顺序随便改
	*pphead = newnode;
	newnode->next = phead;
}

两种方法都比较好理解,也很简单,单链表的头插效率很高,不需要遍历,

指定位置之后"插入"新节点

该函数很简单,只需要通过查找目标结点函数找到目标结点的位置,然后将将新节点链接上去就行了.

步骤:

  1. 将新节点的指针域(next)指向指定结点的下一个结点.
  2. 将指定位置的结点的指针域(next)指向新节点,

图解:
在这里插入图片描述

//使用此函数之前可以先使用,查找目标结点函数(SListFind),找到位置先
void SLTInsertBack( SLTNode* pos, DateType x)
{
	assert(pos);
	SLTNode* newnode = BuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

"申请新节点"函数

新节点都是使用malloc函数动态申请的.函数实现很简单,相信聪明的友友们可以理解,牛牛就不过介绍了.

SLTNode* BuyNode(DateType x)//创建新结点
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);//防止申请结点失败
	newnode->Date = x;
	newnode->next = NULL;
	return newnode;
}

2.3 "删除"元素操作.

因为链表的结点都是动态申请的,所以链表的删除操作需要将目标结点释放,同时为了保护原有的链表结构,需要将受目标结点的其他结点也灵活修改.

单链表的"尾删"

"删除结点"步骤:

  1. 处理特殊情况,如果头指针指向NULL,空链表不能执行删除操作.
  2. 找倒数第二个结点,方法:tail->next->next != NULL因为最后一个结点的next=NULL;
    数据结构记得多画图哦,有助于我们理解.
  3. 先释放尾结点(tail->next),再将倒数第二个结点的next置空NULL
  4. 处理特殊情况:如果链表就只有一个结点,就不存在倒数第二个结点,此时直接释放头结点,并将头结点置空.

图解:
在这里插入图片描述

//尾删
void PopBack(SLTNode** pphead)
{
	assert(pphead);//二级指针不可能为空,如果为空就一定是传错了
	assert(*pphead);//防止空链表的删除操作
	SLTNode* tail = *pphead;//创建一个指针代替头指针遍历
	if (tail->next == NULL) {
		free(tail);
		tail= NULL;
	}
	else {
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

}

单链表的"头删":

同样,单链表的"头删"也是很简单的操作.
步骤:

  1. 将头结点记录一下.
  2. 将头指针指向第二个结点.
  3. 释放头结点.
void PopFront(SLTNode** pphead)
{
	assert(pphead);//二级指针不可能为空,如果为空就一定是传错了
	assert(*pphead);//防止空链表的删除操作
	SLTNode* head = *pphead;
	*pphead = ( * pphead)->next;
	free(head);
}

思考:
需不需要单独去考虑,如果链表只有一个结点的特殊情况?
在这里插入图片描述
答案:

不需要,因为如果链表只有一个结点,头删头指针指向第二个结点,刚好是指向NULL,也是符合要求的.

单链表的"删除"指定的目标结点

步骤:

  1. 通过查找目标结点函数SListFind(下面牛牛讲了),找到目标结点的地址.
  2. 将目标结点的前驱结点指向目标结点的后继结点.
  3. 释放目标结点.
  4. 特殊情况:如果是头删,需要修改头结点,让其指向第二个结点.

图解:
在这里插入图片描述

代码实现:

//告诉位置(建议配合SListFind函数一起使用),删除第一个出现该值的节点
void SlitErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);//二级指针不可能为空,如果为空就一定是传错了
	assert(*pphead);//防止空链表的删除操作
	assert(pos);
	SLTNode* cur = *pphead;//创建一个指针代替头指针遍历
	if (cur == pos) {//如果目标结点是头结点这种特殊情况
		SLTNode* next = cur->next;
		free(cur);
		*pphead = next;
	}
	else {
		while (cur->next != pos && cur->next != NULL)//遍历寻找目标结点
		{
			cur = cur->next;
		}
		cur->next = pos->next;//将目标结点的前驱指向目标结点的后继
		free(pos);
	}
}

2.4 "查找"目标结点函数

单链表查找目标结点只需要遍历一遍这个链表即可,如果目标结点有多个,则只返回第一个遇到的目标结点,找不到目标结点则返回NULL.
函数很简单,牛牛不过多介绍了.

SLTNode* SListFind(SLTNode* phead, DateType x)
{
	SLTNode* cur = phead;//代替头指针遍历链表
	while (cur)
	{
		if (cur->Date == x)
		{
			return cur;
		}
		cur = cur ->next;
	}
	return NULL;
}

2.5 单链表的"打印"

单链表的打印很简单,遍历打印就行了.

void Print(SLTNode* phead)//链表的打印
{
	//assert(phead);
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->Date);
		cur = cur->next;
	}
	printf("NULL\n\n");
}

2.6 单链表的"销毁"

步骤:

  1. 创建next指针保存要删除节点的下一个结点.
  2. 将要删除的结点释放.
  3. 将要删除的结点更新到next
  4. 继续执行1
//单链表的销毁
void SLTDestroy(SLTNode* phead)//这个函数不会将头指针置空,要使用该函数的人自己置空
{
	SLTNode* del = phead;
	SLTNode* next = phead;//用于记录下一个结点
	while (next)
	{
		next = next->next;
		free(del);
		del = next;

	}
	//保持习惯置空
	next == NULL;
	del = NULL;
}

希望这篇文章对大家有帮助。欢迎小伙伴们私信提意见和提问哦!
最后,小伙伴们的点赞就是给牛牛最大的支持,能不能给牛牛来一个一键三连呢?谢谢支持。
在这里插入图片描述

三、总代码

测试区(test.c)

//test.c 主函数区域,用于测试接口
#include "SList.h"
void test1()
{
	SLTNode* plist=NULL;
	printf("插入0,1,2,3,4,5,6,7,8,9之后:\n");
	PushBack(&plist, 1);
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	PushBack(&plist, 4);
	PushBack(&plist, 5);
	PushBack(&plist, 6);
	PushBack(&plist, 7);
	PushBack(&plist, 8);
	PushBack(&plist, 9);
	//头插
	PushFront(&plist, 0);

	Print(plist);
	printf("尾删一次后:\n");
	PopBack(&plist);
	Print(plist);
	printf("头删一次后:\n");
	PopFront(&plist);
	Print(plist);
	printf("删除第一次出现元素7的结点后:\n");
	SlitErase(&plist, SListFind(plist, 7));
	Print(plist);
	printf("在第一个出现5值的结点后面插入一个值为666的结点\n");
	SLTInsertBack(SListFind(plist, 5), 666);
	Print(plist);
	SLTDestroy(plist);
	plist == NULL;
}
int main()
{
	test1();
	return 0;
}

接口实现区(SList.c)


#include "SList.h"

SLTNode* BuyNode(DateType x)//创建新结点
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);
	newnode->Date = x;
	newnode->next = NULL;
	return newnode;
}
void PushBack(SLTNode** pphead, DateType x)
{
	assert(pphead);
	SLTNode*tail = *pphead;//创建一个指针代替头指针遍历
	SLTNode* newnode = BuyNode(x);
	//cur代表代表头指针,phead表示头指针的地址
	//如果cur指向NULL,说明为空链表
	if (*pphead == NULL)
	{
		//这里可以使用tail代替*phead吗?
		//不能,因为这里要改变的是,头结点的指针,需要用二级指针(解引用)来改变
		*pphead = newnode;//空链表是将头指针指向新节点
	}
	else
	{
		//找尾巴,画图解析
		//这里可以使用tail,是因为,要改变的是结构体的内容,只需要用结构体指针(解引用)就行
		while ( tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}


//头插(错误示例)
//void PushFront(SLTNode** pphead, DateType x)
//{
//	assert(pphead);
//	SLTNode* phead = *pphead;
//	SLTNode* newnode = BuyNode(x);
//	//下面两句的顺序不能变,除非再创一个结点保phead
//	newnode->next = phead;
//	phead= newnode;
//}
// 
正确写法1
//void PushFront(SLTNode** pphead, DateType x)
//{
//	assert(pphead);
//	SLTNode* newnode = BuyNode(x);
//	//下面两句的顺序不能变,除非再创一个结点保phead
//	newnode->next = *pphead;
//	*pphead = newnode;
//}

//写法2
void PushFront(SLTNode** pphead, DateType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	SLTNode* phead = *pphead;
	//顺序随便改
	*pphead = newnode;
	newnode->next = phead;
}
//尾删
void PopBack(SLTNode** pphead)
{
	assert(pphead);//二级指针不可能为空,如果为空就一定是传错了
	assert(*pphead);//防止空链表的删除操作
	SLTNode* tail = *pphead;//创建一个指针代替头指针遍历
	if (tail->next == NULL) {
		free(tail);
		tail= NULL;

	}
	else {
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

}
void PopFront(SLTNode** pphead)
{
	assert(pphead);//二级指针不可能为空,如果为空就一定是传错了
	assert(*pphead);//防止空链表的删除操作
	SLTNode* head = *pphead;
	*pphead = ( * pphead)->next;
	free(head);
}

SLTNode* SListFind(SLTNode* phead, DateType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->Date == x)
		{
			return cur;
		}
		cur = cur ->next;
	}
	printf("找不到:\n");
	return NULL;
}
void SlitErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);//二级指针不可能为空,如果为空就一定是传错了
	assert(*pphead);//防止空链表的删除操作
	assert(pos);
	SLTNode* cur = *pphead;//创建一个指针代替头指针遍历
	if (cur == pos) {//如果目标结点时头结点
		SLTNode* next = cur->next;
		free(cur);
		*pphead = next;
	}
	else {
		while (cur->next != pos && cur->next != NULL)//遍历寻找目标结点
		{
			cur = cur->next;
		}
		cur->next = pos->next;//将目标结点的前驱指向目标结点的后继
		free(pos);
	}
}

void SLTInsertBack( SLTNode* pos, DateType x)
{
	assert(pos);
	SLTNode* newnode = BuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;

}

void Print(SLTNode* phead)//链表的打印
{
	//assert(phead);
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->Date);
		cur = cur->next;
	}
	printf("NULL\n\n");
}


void SLTDestroy(SLTNode* phead)//这个函数不会将头指针置空,要使用该函数的人自己置空
{
	SLTNode* del = phead;
	SLTNode* next = phead;//用于记录下一个结点
	while (next)
	{
		next = next->next;
		free(del);
		del = next;

	}
	//保持习惯置空
	next == NULL;
	del = NULL;
}

函数声明区(SList.h)

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int DateType;
typedef struct SListN0de
{
	DateType Date;
	struct SListN0de* next;
}SLTNode;

//尾插
void PushBack(SLTNode** pphead, DateType x);
//尾删
void PopBack(SLTNode** pphead);
//头插
void PushFront(SLTNode** pphead, DateType x);
//头删
void PopFront(SLTNode** pphead);

//告诉值,返回结点的地址
SLTNode* SListFind(SLTNode* phead, DateType x);

//告诉位置(建议配合SListFind函数一起使用),删除第一个出现该值的节点

void SlitErase(SLTNode** pphead, SLTNode* pos);

//告诉位置,在位置后面插入
void SLTInsertBack( SLTNode* pos, DateType x);

struct SListN0de* BuyNode(DateType x);//创建新节点

void Print(SLTNode* phead);//链表的打印


// 单链表的销毁
void SLTDestroy(SLTNode* phead);

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

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

相关文章

界面控件Telerik UI for WinForms使用指南 - 数据绑定 填充(二)

Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件&#xff0c;所有的UI for WinForms控件都具有完整的主题支持&#xff0c;可以轻松地帮助开发人员在桌面和平板电脑应用程序提供一致美观的下一代用户体验。 Telerik UI for WinForms组件为可视化任何类…

使用【SD-WEBUI】插件生成同一张图包含多个人物:分区域的提示词

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;潜变量成对&#xff08;Latent Couple&#xff09;&#xff08;1.1&#xff09;可自组LoRA&#xff08;Composable LoRA&#xff09; &#xff08;二&#xff09;分区扩散&#xff08;Multi Diffusion&#…

测试用例常见设计方法

1.基于需求的设计方法 主要从以下方面进行思考&#xff1a; 2.等价类法 将输入的数据等价划分成几个类&#xff0c;从每个类里面选出一个测试用例&#xff0c;如果这个测试用例通过&#xff0c;说明这一个类的测试用例都通过 有效等价类&#xff1a;满足输入数据要求的类 无…

rosbag相关进阶操作

一些很好用的网站 时间戳在线转换网页 旋转矩阵、四元数、绕轴旋转、欧拉角在线转换网页 四元数、欧拉角可视化在线转换网页 一、按时间截取bag 使用如下代码&#xff1a; rosbag filter 原始包名.bag 截取后的包名.bag "t.to_sec() > 开始时间 and t.to_sec() <…

如何导出windows平台下cloudflare warp内部存的私钥和token

结论&#xff1a;管理员身份运行 mimikatz&#xff1a;https://github.com/gentilkiwi/mimikatz/releases/tag/2.2.0-20220919 然后输入&#xff1a; privilege::debug &#xff08;提升权限到&#xff1a;NT-AUTHORITY\SYSTEM&#xff09;以及sekurlsa::credman 就能看到&…

免费使用GPT4.0?搭载多模态的全新New Bing开放使用教程(文末送书)

目录 1 微软发布新必应2 支持文本生成图像3 支持多模态回答4 历史记录和回答导出5 支持插件化导入本期图书推广 1 微软发布新必应 5月4日&#xff0c;微软基于ChatGPT的搜索引擎New Bing发布了一次大规模更新&#xff0c;并宣布已开放给所有用户&#xff0c;现在无需再排队等待…

前端技术搭建弹珠小游戏(内附源码)

The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了小人逃脱&#xff0c;当然很多伙伴再评论区提出了想法&#xff0c;后续我们会考虑实现的&#xff0c;今天还是继续按照我们原定的节…

sentinel 随笔 3-降级处理

0. 像喝点东西&#xff0c;但不知道喝什么 先来段源码&#xff0c;看一下 我们在dashboard 录入的降级规则&#xff0c;都映射到哪些字段上 package com.alibaba.csp.sentinel.slots.block.degrade;public class DegradeRule extends AbstractRule {public DegradeRule(String…

redis(1)---redis的安装以及五大基本类型的使用

一)认识NoSQL 格式化数据VS格式化 1)SQL关系型数据库&#xff0c;在关系型数据库里面表中的字段是有限制的&#xff0c;况且最好不要随意删除修改表结构&#xff0c;存入到关系型数据库中的数据&#xff0c;都是结构化的数据&#xff0c;对于这些数据都是必须要有格式化的要求&…

sentinel 随笔 2-流控

0. 想要个半个月的旅游 最近发现算法比较有意思一些&#xff0c;什么企业框架都是看不完的… 书接 FlowSlot 1. FlowRuleChecker.checkFlow() : 配置的规则校验类 sentinel 并没有对这个Checker进行抽象的设计&#xff0c;第一次看有些别扭… package com.alibaba.csp.sent…

01- 综述 (目标检测)

要点&#xff1a; 区分One-stage detector 和 Two-stage detector 参考链接&#xff1a;深度学习目标检测最全综述 - 爱码网 详细模型解读参考&#xff1a;目标检测简介 - 知乎 一 目标检测分类 1.1 发展历程 检测网络发布历程&#xff1a; 1.2 检测模型分类 2014年后目标…

C嘎嘎~~ [类 下篇之运算符重载]

类 下篇 之运算符重载 5.赋值运算符重载5.1运算符重载5.1.1 运算符的概念5..1.2 重载运费符的位置5.1.3运算符重载的实质 5.2 赋值运算符重载5.2.1深刻理解---编译器生成的默认赋值运算符重载5.2.2深刻理解---拷贝构造和赋值运算符重载5.2.3深刻理解---传参和返回值用引用修饰…

CUDA error: device-side assert triggered CUDA kernel errors might be asynchronously reported at some

问题描述&#xff1a; 在修改代码时&#xff0c;出现入下报错。 发生异常: RuntimeError CUDA error: device-side assert triggered CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below might be incorrect. For debuggi…

Android Switch开关按钮使用和自定义样式(系列教程五)

Switch开关按钮简介 Switch开关按钮是Android中的基本控件之一&#xff0c;其本质上也是一个按钮&#xff0c;具有开和关两种展示状态。 Switch开关按钮基本使用 在布局文件中定义开关按钮&#xff1a; <LinearLayoutandroid:layout_width"300dp"android:layo…

Vivado安装后添加器件库

1.前言 通常安装Vivado时&#xff0c;由于软件完整安装的空间需求过于庞大&#xff0c;一般只会选择一部分器件进行安装。而随着学习和工作的进展&#xff0c;遇到新的赛灵思朋友是成长的里程碑&#xff0c;也是综合不能通过的绊脚石。 今天有幸认识了一位新的赛灵思朋友——…

云原生: istio+dapr构建多运行时服务网格

2020 年&#xff0c;Bilgin Ibryam 提出了 Multi-Runtime&#xff08;多运行时&#xff09;的理念&#xff0c;对基于 Sidecar 模式的各种产品形态进行了实践总结和理论升华。那到底什么是多运行时呢&#xff1f;首先还是得从分布式应用的四大类基本需求讲起。简单来讲任何分布…

刷题练习3

文章目录 题目一分析题解 题目二分析第一种第二种 题解第一种方法代码第二种方法代码 题目一 题目链接 描述 读入一个字符串str&#xff0c;输出字符串str中的连续最长的数字串 输入描述&#xff1a; 个测试输入包含1个测试用例&#xff0c;一个字符串str&#xff0c;长度不超…

牛顿迭代法解超越方程

牛顿迭代法解超越方程 L g T 2 2 π t a n h ( 2 π L d ) L\frac{gT^2}{2\pi}tanh(\frac{2\pi}{L}d) L2πgT2​tanh(L2π​d) 方程&#xff1a; f ( L ) L − g T 2 2 π t a n h ( 2 π L d ) 0 f(L)L-\frac{gT^2}{2\pi}tanh(\frac{2\pi}{L}d)0 f(L)L−2πgT2​tanh(L2π…

~项目启动~

rmq是什么&#xff1f; "rmq" 可能指的是 "RabbitMQ"&#xff0c;它是一种开源消息队列软件&#xff0c;采用 AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议&#xff0c;可以用于支持异步处理、任务分发、解耦合等应用场景。Rabbit…

仪表检测与读数(一):仪表检测

基于YOLOv4的仪表检测 前言YOLOv4源码下载数据集处理与模型训练模型性能测试 前言 本系列是想记录一下自己实现的一种用于仪表检测与读数的方法&#xff0c;首先方法仅针对于单指针仪表和单行显示的数字仪表进行了检测与读数方法的设计。方法的整体思路是&#xff1a;第一步对拍…