【数据结构】双链表的相关操作(声明结构体成员、初始化、判空、增、删、查)

news2024/10/2 12:26:43

双链表

  • 双链表的特点
  • 声明双链表的结构体成员
  • 双链表的初始化
    • 带头结点的双链表初始化
    • 不带头结点的双链表初始化
    • 调用双链表的初始化
  • 双链表的判空
    • 带头结点的双链表判空
    • 不带头结点的双链表判空
  • 双链表的插入(按值插入)
  • 头插法建立双链表
    • 带头结点的头插法
      • 每次调用头插法只能插入一个结点
      • 每次调用头插法可插入任意个结点
      • 调用头插法建立带头结点的双链表的完整代码(可以直接赋值运行的)
    • 不带头结点的头插法
      • 每次调用只能插入一个结点
      • 每次调用可以插入任意个结点
      • 调用头插法建立不带头结点的双链表的完整代码(可以直接赋值运行的)
  • 尾插法建立双链表
    • 带头结点的尾插法
      • 每次调用只能插入一个结点
        • 我自己能理解的实现尾插法的思路
        • 我的思路的完整代码,可以运行的
      • 教材用的实现尾插法的代码(我看不懂)
    • 不带头结点的尾插法
      • 我自己的思路
  • 打印双链表
    • 二级目录
      • 三级目录

双链表的特点

1. 双链表在创建的过程中,每个结点都需要一个数据域和两个指针域,所以它的存储密度比之单链表更低一些。
2. 可进可退,解决了单链表无法逆向搜索的问题。

声明双链表的结构体成员

typedef struct DNode {
	ElemType data;
	struct DNode *prior, *next;
}DNode, *DLinklist;

// DNode *就等价于DLinklist			之前文章有解释过

DNode *就等价于DLinklist

双链表的初始化

双链表的每个结点都有两个指针域,一个指向前驱结点,一个指向后继结点。头结点的prior永远指向NULL

带头结点的双链表初始化

有几个需要注意的地方:
	1. DLinklist&L是头指针,而不是头结点,这是两东西。
	2. 头结点不存储数据,只起到一个标记作用,方便对链表进行操作。
	3. 头指针指向头结点,用于记录链表的起始位置。
	4. 给&L分配内存空间,是因为这是带头结点的双链表,而头指针指向头结点,所以实际上是给头结点分配内存空间,创建头结点。
	5. 不带头结点的双链表,不用创建头结点,自然也不用分配内存空间,直接创建第一个结点即可。
	6. 注意:原先我一直疑惑的地方是,为啥带头结点就要分配内存空间,不带头结点的双链表哪怕不需要头结点,也要给第一个结点分配内
	存空间,但是初始化代码的时候并没有写,后来我发现,不是没写,而是不应该在初始化链表的时候给链表的第一个实际结点分配内存空
	间,此时是为了初始化一个空链表,后续给链表插入结点的时候,才会开始分配内存空间。而此时分配的,仅仅只是头结点的内存空间,
	并不是第一个实际结点的,带就分配,不带就不分配,就这么个意思。
bool initDList(DLinklist&L) {
	L=(DNode *)malloc(sizeof(DNode));
	/**
	* L本身是头指针,带头结点时,它指向头结点的指针域,所以可以把L看作是头结点,指针域为NULL,说明这是个空指针。
	* 注意:空指针本身不占用内存空间。
	* 指针变量在内存中占用一定的空间,但当指针被赋值为NULL时,它指向的内存地址为空,不指向任何有效的内存空间。
	* 因此,空指针并不占用额外的内存空间
	*/
	if(L==NULL) {		// 空指针不占用内存空间,可用此来判断内存不足,给头结点分配存储空间失败
		return false;
	}
	// 头结点的prior永远指向NULL
	// 头指针L指向头结点,存储头结点的地址;L->prior指向头结点的前指针域,存储前指针域的地址,为NULL。
	L->prior=NULL;		
	// 头结点之后还没有结点,初始化链表这句必须写,因为要创建个空表,以防被内存中残留数据污染		
	L->next=NULL;		
	//  头指针L指向头结点,存储头结点的地址;L->next指向头结点的后指针域,存储后指针域的地址,为NULL。		
	return true;
}

不带头结点的双链表初始化

void initDList(DLinklist&L) {
	// 由于不带头结点,所以头指针L直接指向双链表的第一个实际结点,此时链表为空,所以指向NULL,可看作是个空指针,空指针的前、后指针不指向任何有效的内存空间,自然不用再区分next、prior
	L=NULL;
}

调用双链表的初始化

int main() {
	DLinklistL;
	// 初始化双链表
	initDList(L);
}

双链表的判空

带头结点的双链表判空

1. 带头结点的双链表,由于头结点的前指针永远指向NULL,所以不用管。
2. 主要是头结点的后指针,看它后面有没有结点,没结点时它应该指向NULL。

在这里插入图片描述

bool Empty(DLinklist L) {
	if(L->next==NULL) {
		return true;
	}else {
		return false;
	}
	// 这个if判断可以简写成:
	return L->next==NULL;
}

不带头结点的双链表判空

bool Empty(DLinklist L) {
	return L==NULL;
}

双链表的插入(按值插入)

1. 这是按值插入,不是按位插入,只要在找到链表中某个已存在的结点,在其后面分配存储空间,创建一个新结点,改变前、后指针指向,以及
新结点的数据域,就可以实现插入了,这算是后插。
2. 此处只是说明了插入的逻辑,代码并不完整,也还没有分带不带头结点
1. DNode *p表示声明一个指针变量p,它指向下一结点的指针域。
2. p=NULL表示p指针所在的结点的指针域为NULL,它不指向任何有效的地址,说明p是个空指针。
	- 为什么要判断指针p和指针s是否是空指针?
		1. 因为该函数是在找到p结点之后,通过改变p结点原先的指向,来实现插入的。
		2. 如果p是个空指针,它的后指针域没有任何指向,那我在它后面插入一个新结点,新结点的后指针域就没有赋值指向了。
		3. 并且,一般链表的最后一个结点的后面是NULL,肯定不能在NULL后面再插入新结点,要插也最后是在最后一个实际结点后面插
		4. 参数s表示指针s,它所指向的结点是我要插入的新结点,一个新结点,都要插入一个新结点了,肯定不能是个空指针啊,如果是个
		空指针,那这个链接岂不是断链了。
3. p->next=NULL:p->next表示指针p所在的结点的指针域,它存储下一结点的地址,指向下一结点的指针域。p-next=NULL就表示指针p指向
的结点的下一结点是NULL。
4. 把指针变量p指向的结点看作是p结点,那它的下一结点就是紧跟其后的一个结点,即p结点—NULL。
5. 首先明确,p是用DNode *去声明的,所以p一定是个指针,那一个指针,它会指向一个结点,就可以把p看作是p结点,p=NULL就是说p结点本
身是NULL。
6. 其次,p->next就可以看作是p结点的下一结点是NULL
bool InsertNextNode(DNode *p, DNode *s) {
	if(p==NULL || s==NULL) {
		return false;
	}
	/**
	* s指针所指向的结点(简称s结点)的指针域指向p指针所指向的结点(简称p结点)的指针域指向的结点,即:s结点指向p结点的下一个结点,
	* 此时顺序是p—p下一结点,s—p下一结点
	*/
	s->next=p->next;		
	/**
	*  由于是双链表,每个结点都有前指针和后指针,p结点如果是最后一个结点,说明它后面没有结点了,指向NULL。
	* 它的后指针指向NULL,这没问题,这样子的双链表才是完整的。
	* 但是NULL没有前、后指针啊,所以在操作结点的前指针指向的时候,要加个判断,当前结点下一结点不是NULL才能操作其前指针
	*/
	if(p->next!=NULL) {
		/**
		* p->next表示p结点的下一结点(简称q结点),
		* p->next->prior表示q结点的前指针域指向的结点,
		* 赋值s就表示q结点的指针域指向指针s所指向的结点,即s结点。
		* 此时顺序为:p—q,s—q
		*/
		p->next->prior=s;		
	}
	// 现在只要把p和s再连上就好了
	p->next=s;
	s->prior=p;
	return true;
}

头插法建立双链表

需求:把一个数组中的数据,插入到初始的双链表中,如果当前双链表带头结点,那么每次都是插在头结点的后面,这样每次插入的新结点都是
链表的第一个结点。
如果当前双链表不带头结点,那么每次插入都应该让头指针L直接指向当前新插入的这个结点。
这便是头插法。
带头:
1. 带头结点的初始双链表:头指针L—头结点—NULL
2. 插入1个需要的参数:要建立的链表&L,要插入的元素e
3. 插入n个需要的参数:要建立的链表&L,要插入的多个元素e[],要插入的结点个数n
不带头:头指针L—NULL
4. 带头结点的初始双链表:头指针L—NULL
5. 需要的参数:要建立的链表&L,要插入的元素e

带头结点的头插法

每次调用头插法只能插入一个结点

DLinklist head_insert(DLinklist &L, int e) {
	// 创建新结点,分配内存空间
	DLinklist *s = (DLinklist *)malloc(sizeof(DLinklist));
	/**
	* 头指针L指向头结点,L->next指向头结点的指针域,头结点的指针域指向头结点的下一结点,
	* 即L可看作是头结点,L->next就是头结点的下一结点。
	* 1、头结点指向创建的新结点s,即在头结点之后插入新结点,
	* 2、此时头结点原指向的结点就要变成s结点的下一结点
	* 3、此时后指针的指向已全部完成,双链表还要再操作一下前指针的指向:
	* 		- s结点的前指针指向头结点,头结点原下一结点的前指针指向s结点
	* 4、但是要注意一个问题,如果是第一次插入,双链表此时是空的,头结点指向NULL,插入的新结点s是链表中最后一个结点,
	* 只要操作其后指针即可,而其下一结点的无前指针无需操作,所以为了代码的健壮性要加个判断
	*/
	s->data=e;
	s->next=L->next;
	if(L->next!=NULL) {
			L->next->prior=s;
	}
	L->next=s;
	s->prior=L;	
}

每次调用头插法可插入任意个结点

1. 首先要给分配内存空间,有多少个n,就分配多少个。
2. 由于插入的n个,每个的前指针和后指针还要链接起来,所以最好当作1个来操作,然后通过循环的方式,变成n个。
3. i是数组的下标,从0开始表示数组中第一个元素,假设n=3那就是插入3个结点,必须从i=0开始循环,那就要i<n或者i<=n-1才能正好取3个。
DLinklist head_insert_mult(DLinklist &L, int e[], int n) {
	for(int i=0;i<n;i++) {		
		DNode *s=(DNode *)malloc(sizeof(DNode));
		s->next=L->next;
		s->data=e[i];
		if(L->next!=NULL) {
			L->next->prior=s;
		}
		s->prior=L;
		L->next=s;
	}
	return L
}

调用头插法建立带头结点的双链表的完整代码(可以直接赋值运行的)

在这里插入图片描述

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

// 声明结构体成员
typedef struct DNode {
	ElemType data;
	struct DNode *prior, *next;
}DNode, *DLinklist;

// 初始化一个带头结点的双链表 
DLinklist InitList(DLinklist &L) {
	L=(DNode *)malloc(sizeof(DNode));
	if(L==NULL) {
		return NULL;
	}
	L->next=NULL; 
	L->prior=NULL;
	return L;
}

// 头插法建立双链表(单个)
DLinklist head_insert(DLinklist &L,int e) {
	DNode *s=(DNode *)malloc(sizeof(DNode));
	s->data=e;
	s->next=L->next;
	if(L->next!=NULL)
		L->next->prior=s;
	L->next=s;
	s->prior=L;
	return L;
}

// 头插法建立双链表(多个) 
DLinklist head_insert_mult(DLinklist &L, int e[], int n) {
	for(int i=0;i<=n-1;i++) {
		DNode *s=(DNode *)malloc(sizeof(DNode));
		s->next=L->next;
		s->data=e[i];
		if(L->next!=NULL) {
			L->next->prior=s;
		}
		s->prior=L;
		L->next=s;
	}
	return L;
}

// 打印双链表 
void printDLinkList(DLinklist L)
{
    DNode* p = L;
    while(p)
    {
        printf("%d ", p->data);
        p = p->next;
    }
}

int main() {
	DLinklist L;
	InitList(L);
	head_insert(L,2);	
	head_insert(L,3);
	int arr[5]={1,2,3,4,5};
	head_insert_mult(L,arr,3); 
	// 顺序为 头结点 3 2 1 3 2 
	printDLinkList(L); 
	return 0;
}

不带头结点的头插法

由于没有头结点了,所以操作头指针L直接指向双链表第一个实际结点

每次调用只能插入一个结点

DLinklist head_insert_NoT(DLinklist &L, int e) {
	DNode *s=(DNode *)malloc(sizeof(DNode));
	s->next=L;
	s->data=e;
	if(L!=NULL) {
		L->prior=s;
	}
	L=s;	
	s->prior=L;
	return L;
}

每次调用可以插入任意个结点

DLinklist head_insert_NoT_mult(DLinklist &TL, int e[], int n) {
	for(int i=0;i<n;i++) {
		DNode *s=(DNode *)malloc(sizeof(DNode));
		s->data=e[i];		// 此时双链表只有一个NULL,由TL指向
		s->next=TL;			// s指针所在的结点(简称s结点)的指针域为头指针TL的指向,也就是说s结点下一个是NULL:s—NULL
		if(TL!=NULL) {		
			TL->prior=s;	// 由于是头茬,所以每次TL原先指向的那个结点的前指针都要指向新插入的s
		}
		TL=s;		// TL是头指针,每次都要指向新插入的s指针所指向的结点
		s->prior=NULL;		// 由于s指针是以头插的形式新插入的,它就是第一个结点,前面没有任何东西,前指针指向NULL
	}
	return TL;
 }

调用头插法建立不带头结点的双链表的完整代码(可以直接赋值运行的)

在这里插入图片描述

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

// 声明结构体成员
typedef struct DNode {
	ElemType data;
	struct DNode *prior, *next;
}DNode, *DLinklist;

// 初始化一个带头结点的双链表 
DLinklist InitList(DLinklist &L) {
	L=NULL;
	return L;
}

DLinklist head_insert_NoT(DLinklist &L, int e) {
	DNode *s=(DNode *)malloc(sizeof(DNode));
	s->next=L;
	s->data=e;
	if(L!=NULL) {
		L->prior=s;
	}
	L=s;	
	s->prior=L;
	return L;
}

DLinklist head_insert_NoT_mult(DLinklist &TL, int e[], int n) {
	for(int i=0;i<n;i++) {
		DNode *s=(DNode *)malloc(sizeof(DNode));
		s->data=e[i];		
		s->next=TL;		
		if(TL!=NULL) {		
			TL->prior=s;	
		}
		TL=s;		
		s->prior=NULL;		
	}
	return TL;
 }

// 打印双链表 
void printDLinkList(DLinklist L)
{
    DNode* p = L;
    while(p)
    {
        printf("%d ", p->data);
        p = p->next;
    }
}

int main() {
	DLinklist L;
	InitList_NoT(L);
	head_insert_NoT(L,2); 
	head_insert_NoT(L,21); 
	head_insert_NoT(L,22);
	int arr1[5]={2,3,4,7,8};
	head_insert_NoT_mult(L,arr1,5);
	printDLinkList(L); 
	return 0;
}

尾插法建立双链表

尾插就是:每次都在双链表的末尾再插入一个结点,新结点后面不会再有其他结点,应该是指向NULL的。

带头:
1. 插入一个需要的参数:双链表L、要插入的元素e
不带头:
2、

带头结点的尾插法

每次调用只能插入一个结点

我自己能理解的实现尾插法的思路
带头结点:那就是头结点—新结点—NULL
尾插法思路:
1. 要在双链表的最后一个结点后面插入,那肯定要找到最后一个结点。而最后一个结点后面没结点,指向NULL,所以可以根据这个来判断。
		DNode *tail;		// 这是我声明的指向尾结点指针变量tail
		if(tail->next==NULL)		// 说明tail指针目前已经是指向尾结点
2. 双链表是个链表,它不具有随机存取的特点,所以要找到尾结点,只能从第一个结点开始按序遍历
		tail=L;				// tail指针要指向头指针L指向的结点,即头结点,由此开始遍历
		while(tail->next!=NULL)
			tail=tail->next;				// 如果tail指针当前指向的结点的下个结点不是NULL,那就指向下一个,循环遍历 
		// 此处应该是跳出循环的操作了,而跳出循环就说明tail指针已经指向尾结点了
3. 要插入新结点,肯定要创建并分配存储空间,先让新结点指向尾结点的下一结点,再让尾结点指向新结点,然后让新结点的前指针指向尾结点
		s->next=tail->next;
		tail->next=s;
		s->prior=tail;
DLinklist tail_insert(DLinklist &L, int e) {
	DNode *tail=L;		// 声明一个指针变量tail表示尾指针,赋值L表示初始值指向头结点
	while(tail->next!=NULL) {
		tail=tail->next;
	}
	DNode *s=(DNode *)malloc(sizeof(DNode));
	s->next=tail->next;
	s->data=e;
	tail->next=s;
	s->prior=tail;
	// 因为是尾插法,所以最后s一定指向NULL,tail->next-prior是不存在的不用考虑 
}
我的思路的完整代码,可以运行的

在这里插入图片描述

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

typedef struct DNode {
	int data;
	struct DNode *prior;
	struct DNode *next;
}DNode,*DLinklist;

DLinklist InitList(DLinklist &L) {
	L=(DNode *)malloc(sizeof(DNode));
	if(L==NULL) {
		return NULL;
	}
	L->next=NULL; 
	L->prior=NULL;
	return L;
} 

DLinklist tail_insert(DLinklist &L, int e) {
	DNode *tail=L;		
	while(tail->next!=NULL) {
		tail=tail->next;
	}
	DNode *s=(DNode *)malloc(sizeof(DNode));
	s->next=tail->next;
	s->data=e;
	tail->next=s;
	s->prior=tail;
}

// 打印双链表 
void printDLinkList(DLinklist L)
{
    DNode* p = L;
    while(p)
    {
        printf("%d ", p->data);
        p = p->next;
    }
}

int main() {
	DLinklist L;
	InitList(L);
	tail_insert(L,2); 
	tail_insert(L,21); 
	tail_insert(L,22);
	printDLinkList(L); 
	return 0;
}
DLinklist tail_insert(DLinklist &L, int e) {
	DNode *s=(DNode *)malloc(sizeof(DNode));
	s-next=L->next;
}

教材用的实现尾插法的代码(我看不懂)

不带头结点的尾插法

我自己的思路

由于此处是双链表不带头结点,那么此时头指针L指向双链表的第一个实际结点,所以当双链表为空时,L=NULL,但如果双链表不为空,L自然也不会是NULL,就要通过遍历去获取双链表的尾结点,所以要做个if判断
DLinklist tail_insert_NoT(DLinkList &L,int e) {
	DNode *s=(DNode *)malloc(sizeof(DNode));
	s->data=e;
	// 这两句没有就无法运行
	s->next = NULL;
	s->prior = NULL;
	/**
	* 初始化的时候L=NULL,头结点是NULL,所以tail指针指向NULL,
	* 但是只要插入过结点,再调用该函数进行尾插,头指针L就不再是指向NULL,而是指向一个实际结点,
	* 此时就要通过遍历去获取该链表的尾结点,过程同上
	*/
	DNode *tail;		
	if(L==NULL) {		// 不带头的双链表为空时,L指向的就是第一个实际结点,插入的新结点s就应该是由L指向的:L=s,第一个结点的前指针用于指向NULL
		L=s;
		s->prior=NULL;
	}else {
		while(tail->next!=NULL) {
			tail=tail->next;
		}
		s->next=tail->next;
		tail->next=s;
		s->prior=tail;
	}
	return L;		
}

打印双链表

插入结点之后,想看看当前双链表的结点顺序,可以用以下代码打印输出
void PrintDLinkList(DLinklist L) {	
	/**
	* 声明一个指针变量p让它指向头指针L指向的结点,
	* 如果是带头结点的双链表,就指向头结点;
	* 如果是不带的,就指向双链表的第一个实际结点。
	* 此处我写L是为了区分终端打印效果,如果不想要头结点,写成:DNode *p=L->next
	*/
	DNode *p=L;
	while(p!=NULL) {
		printf("%d", p->data);
		p=p->next;
	}
}

二级目录

三级目录

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

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

相关文章

每日一题 1488. 避免洪水泛滥(中单,贪心,二分)

思路&#xff1a; 当某一天为晴天&#xff0c;可以选择抽水时&#xff0c;我们是不知道要抽哪一个的&#xff0c;最优解应该是抽接下来最近的要发洪水的湖泊&#xff0c;所以我们先把晴天的坐标保存下来&#xff0c;需要用的时候再拿出来需要注意的是&#xff0c;只有晴天发生…

【MATLAB源码-第46期】基于matlab的OFDM系统多径数目对比,有无CP(循环前缀)对比,有无信道均衡对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OFDM&#xff08;正交频分复用&#xff09;是一种频域上的多载波调制技术&#xff0c;经常用于高速数据通信中。以下是关于多径数目、有无CP&#xff08;循环前缀&#xff09;以及有无信道均衡在OFDM系统中对误码率的影响&am…

Python对接海康威视机器视觉工业相机

一、下载MVS客户端 海康机器人-机器视觉-下载中心 二、解压并安装MVS客户端 三、找到MVS示例代码&#xff08;代码在MVS的安装位置&#xff09; 工业相机只允许单条连接&#xff0c;也就是说MVS如果连接了相机&#xff0c;python代码就无法获取数据&#xff0c;此时必须退出M…

计算机毕业设计 大学生选修选课系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

pinctrl子系统 - 架构和结构体关系(四)

一&#xff0c;pinctrl的引入 由于SoC系统越来越复杂、集成度越来越高&#xff0c;SoC中pin的数量也越来越多、功能也越来越复杂&#xff0c;这就对如何管理、使用这些pins提出了挑战。因此&#xff0c;用于管理这些pins的硬件模块&#xff08;pin controller&#xff09;就出…

MySQL读写分离之一主一从

原理 MySQL 的主从复制&#xff0c;是基于二进制日志&#xff08; binlog &#xff09;实现的。 准备 主机 角色 用户名 密码 192.168.2.3 master root newPwd520 192.168.2.4 slave root newPwd520 主从复制的搭建&#xff0c;可以参考 MYSQL的主从复制-CSDN博客 一主一从读…

【K8S】集群中部署nginx应用 运行手写yaml文件报错排查过程

文章目录 ❌报错信息&#x1f50e;排查过程✅问题解决 ❌报错信息 提取报错信息【 unknown field “spec.selector.replicas”】【 unknown field “spec.selector.template”】 [rootmaster ~]# kubectl apply -f nginx-deployment.yaml Error from server (BadRequest): erro…

CCF CSP认证 历年题目自练Day30

题目一 试题编号&#xff1a; 202203-1 试题名称&#xff1a; 未初始化警告 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 题目背景 一个未经初始化的变量&#xff0c;里面存储的值可能是任意的。因此直接使用未初始化的变量&#xff0c;比…

通过API接口进行商品价格监控,可以按照以下步骤进行操作

要实现通过API接口进行商品价格监控&#xff0c;可以按照以下步骤进行操作&#xff1a; 申请平台账号并选择API接口&#xff1a;根据需要的功能&#xff0c;选择相应的API接口&#xff0c;例如商品API接口、店铺API接口、订单API接口等&#xff0c;这一步骤通常需要我们在相应…

MyBatis(中)

1、动态sql&#xff1a; 1、if标签&#xff1a; mapper接口: //if 标签 多条件查询List<Car> selectByMultiConditional(Param("brand") String brand,Param("guidePrice") Double guidePrice,Param("carType") String carType); map…

ATF(TF-A)之UBSAN动态代码分析

安全之安全(security)博客目录导读 目录 一、UBSAN简介 二、TF-A中UBSAN配置选项 一、UBSAN简介 未定义行为消毒器(Undefined Behavior Sanitizer&#xff0c;UBSAN)是Linux内核的未定义行为动态检测器。 详细信息可参考&#xff1a;https://github.com/google/kernel-sanit…

3D 生成重建007-Fantasia3D和Magic3d两阶段玩转文生3D

3D生成重建3D 生成重建007-Fantasia3D和magic3d 文章目录 0 论文工作1 论文方法1.1 magic3d1.2 Fantasia3D 2 效果2.1 magic3d2.2 fantasia3d 0 论文工作 两篇论文都是两阶段法进行文生3d&#xff0c;其中fantasia3D主要对形状和外表进行解耦&#xff0c;然后先对geometry进行…

第五章 图

第五章 图 图的基本概念图的应用背景图的定义和术语 图的存储结构邻接矩阵邻接表 图的遍历连通图的深度优先搜索连通图的广度优先搜索 图的应用最小生成树拓扑排序 小试牛刀 图的基本概念 图结构中&#xff0c;任意两个结点之间都可能相关&#xff1b;而在树中&#xff0c;结点…

接口自动化测试,完整入门篇

1. 什么是接口测试 顾名思义&#xff0c;接口测试是对系统或组件之间的接口进行测试&#xff0c;主要是校验数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及相互逻辑依赖关系。其中接口协议分为HTTP,WebService,Dubbo,Thrift,Socket等类型&#xff0c;测试类型又主…

Web安全基础:常见的Web安全威胁及防御方法 |青训营

Web安全基础&#xff1a;常见的Web安全威胁及防御方法 在现代Web开发中&#xff0c;安全性至关重要。Web应用面临各种潜在的威胁&#xff0c;包括跨站脚本&#xff08;XSS&#xff09;、跨站请求伪造&#xff08;CSRF&#xff09;等。了解这些威胁以及如何防御它们&#xff0c…

c语言练习87:合并两个有序数组

合并两个有序数组 合并两个有序数组https://leetcode.cn/problems/merge-sorted-array/ 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff…

Excel 自动提取某一列不重复值

IFERROR(INDEX($A$1:$A$14,MATCH(0,COUNTIF($C$1:C1,$A$1:$A$14),0)),"")注意&#xff1a;C1要空置&#xff0c;从C2输入公式 参考&#xff1a; https://blog.csdn.net/STR_Liang/article/details/105182654 https://zhuanlan.zhihu.com/p/55219017?utm_id0

超越平凡:Topaz Photo AI for Mac带您领略人工智能降噪的魅力

在这个充满噪点和高频信息的时代&#xff0c;照片和视频的降噪成为了一个重要而迫切的需求。Mac用户现在有了一个强大的新工具——Topaz Photo AI for Mac&#xff0c;这是一款利用人工智能技术进行降噪和优化的软件。通过这款软件&#xff0c;您可以轻松地改善图像质量&#x…

呈现高效的软件测试技术 助力软件研发提升10倍质量

像大多数软件工程一样&#xff0c;软件测试是一门艺术。在过去十年中&#xff0c;自动化测试是测试软件的最佳方式。计算机可在瞬间运行数百个测试&#xff0c;而这样的测试集使公司能自信地每天发布数十个版本的软件。有大量资源(书籍、教程和在线课程)可用于解释如何进行自动…

金蝶EAS代码执行漏洞

【漏洞概述】 金蝶 EAS 及 EAS Cloud 是金蝶软件公司推出的一套企业级应用软件套件&#xff0c;旨在帮助企业实现全面的管理和业务流程优化。 【漏洞介绍】 金蝶 EAS 及 EAS Cloud 存在远程代码执行漏洞 【影响版本】 金蝶 EAS 8.0&#xff0c;8.1&#xff0c;8.2&#xf…