数据结构:线性表之-循环双向链表(万字详解)

news2025/1/1 22:32:08

目录

基本概念

1,什么是双向链表

2,与单向链表的区别

双向链表详解

功能展示:

1. 定义链表

2,创建双向链表

3,初始化链表

4,尾插

5,头插

6,尾删

判断链表是否被删空

尾删代码

7,头删

8,pos位置之前插入

优化后的头插

优化后的尾插

9,删除pos位置的节点

优化后的尾删

优化后的头删

10,求链表长度

11,查找链元素

12,销毁链表

成品展示

List.h

List.c

test.c


本文将以写代码思路进行讲述,故中间会出现代码的优化以便梳理思路,渐入佳境

本文分成三个文件:

List.h//函数的声明
List.c//函数的创建
test.c //用于测试文件

基本概念

1,什么是双向链表

双向链表(Doubly Linked List)是一种常见的链表数据结构。它与普通链表的区别在于,每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点,因此可以从任意一个节点开始,双向遍历整个链表。

双向链表的节点通常由三部分组成:数据部分(存储节点的值)、前驱指针(指向前一个节点的指针)和后继指针(指向后一个节点的指针)。相比于单向链表,双向链表可以更方便地进行正反向遍历和节点的插入、删除操作。

双向链表的优点是在某些特定场景下可以更高效地操作链表,缺点是相比于单向链表,需要额外的空间来存储每个节点的前驱指针。

继续遍历一下双向链表的特点和操作:

  1. 特点:

    • 双向链表每个节点有两个指针,可以向前或向后遍历。
    • 可以从任意节点开始向前或向后遍历整个链表。
    • 插入和删除节点的操作相对容易,只需要调整节点的前驱和后继指针即可。
    • 对于双向链表,可以更轻松地实现双向循环链表,即首尾节点相连。
  2. 常见操作:

    • 遍历链表:可以从头节点开始,按照后继指针依次访问每个节点。或者从尾节点开始,按照前驱指针依次访问每个节点。
    • 插入节点:在给定位置之前或之后插入一个新节点,只需要调整前后节点的指针即可。
    • 删除节点:删除给定位置上的节点,同样通过调整前后节点的指针来实现。
    • 查找节点:可以通过遍历链表来查找特定值或者特定位置上的节点。

注意:在使用双向链表时需要注意指针的正确性和更新,以避免出现指针丢失和内存泄漏等问题。

2,与单向链表的区别

双向链表与单向链表的主要区别在于节点中指针的个数和指向方式:

1. 指针个数:双向链表每个节点有两个指针,分别指向前一个节点和后一个节点;而单向链表每个节点只有一个指针,指向下一个节点。
2. 遍历方式:双向链表可以从任意节点开始向前或向后遍历整个链表,而单向链表只能从头节点开始顺序遍历。
3. 插入和删除操作:双向链表的插入和删除操作相对方便,因为可以直接调整前驱和后继节点的指针;而单向链表在进行插入和删除时,需要更多的操作来调整节点之间的链接关系。
4. 空间占用:双向链表相较于单向链表需要额外的空间来存储每个节点的前驱指针。这在一些特定场景下可能会带来一定的开销。
5. 双向性:双向链表可以在节点内部通过前驱指针和后继指针来实现双向的遍历和操作。这使得在某些场景下,双向链表更加方便和高效,例如需要反向遍历链表或者需要在给定节点处进行插入和删除操作。
6. 双向循环性:双向链表可以通过连接首尾节点的前驱和后继指针来形成一个双向循环链表。这使得链表在某些场景下更具灵活性,例如需要循环遍历链表或者实现循环队列等。
7. 内存占用:相对于单向链表,每个节点额外存储一个前驱指针和一个后继指针,使得双向链表在存储上需要更多的内存空间。这是一个需要考虑的因素,尤其是在链表节点数量较大或内存有限的情况下。
8. 双向链表的前向遍历与后向遍历:由于双向链表具有前驱和后继指针,可以方便地进行正向和反向的遍历操作。而单向链表只能从头节点开始进行顺序遍历,无法直接进行反向遍历。
9.  删除操作的效率:相对于单向链表,在双向链表中删除给定节点的效率更高。由于双向链表可以直接通过前驱和后继指针找到目标节点的前后节点,所以删除操作只需要修改前后节点的指针即可,而单向链表需要遍历找到目标节点的前一个节点,才能进行删除操作。
10.  双向链表支持双向迭代器:由于双向链表具有双向性,可以轻松地实现双向迭代器。这使得在某些情况下,可以更加方便地进行双向迭代操作,如双向搜索算法或需要反向遍历的数据结构。
总之,双向链表相较于单向链表具有更强的遍历和操作能力,但在存储上需要额外的空间开销。根据具体需求和场景的不同,选择适合的链表类型是根据实际情况进行权衡和取舍。

概述:

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

单向链表(无头)详细讲解:# 数据结构:线性表之-单向链表(无头)

双向链表详解

功能展示:

typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;

LTNode* BuyListNode(LTDataType x);

//void ListInit(LTNode** pphead);
LTNode* ListInit();

//打印
void ListPrint(LTNode* phead);

//尾插
void ListPushBack(LTNode* phead, LTDataType x);

//头插
void ListPushFront(LTNode* phead, LTDataType x);

//尾删
void ListPopBack(LTNode* phead);

//头删
void ListPopFront(LTNode* phead);

//判断链表是否被删空
bool ListEmptyLTNode(LTNode* phead);

//pos位置之前插入
void ListInsert(LTNode* pos, LTDataType x);

//删除pos位置的节点
void ListErase(LTNode* pos);

//求链表长度
int ListSize(LTNode* phead);

//销毁链表
void ListDestory(LTNode* phead);

1. 定义链表

typedef struct ListNode
{
	struct ListNode* next;//下一节点
	struct ListNode* prev;//上一个节点
	LTDataType data;
}LTNode;

2,创建双向链表

LTNode* BuyListNode(LTDataType x)
{
	//计算连自定义链表大小
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		//判断是否创建成功
		perror("malloc fail");
		exit(-1);
	}
	//对前节点进行赋值
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
}

3,初始化链表

初始化链表使用哨兵位(头节点)前后指针存储的位置为phead本身。

LTNode* ListInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
}

当然用无返回值的函数用二级指针进行初始化,及后续的增删查改
这样会更麻烦,本文全程采用一级指针:

void ListInit(LTNode** pphead)
{
	*pphead = BuyListNode(-1);
	(*pphead)->next = *pphead;
	(*pphead)->prev = *pphead;
}

改变结构体要用结构体指针。
要改变结构体指针要用结构体指针的指针。

4,尾插

先让我们回顾下双向链表的结构:


与单链表的不同是:单链表要尾插时,要从头一次向后访问找到为节点再进行插入
,而双向链表要尾插在head前插入节点即为尾节点。

void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;
	//前一个节点指向新节点
	tail->next = newnode;
	//新节点的prev指向上一个节点
	newnode->prev = tail;
	//新节点存放的下一节点为头节点
	newnode->next = phead;
	//头节点的prev存放新节点,方便尾插
	phead->prev = newnode;
}

5,头插

头部插入为head前一个节点

void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	
	LTNode* newnode = BuyListNode(x);
	LTNode* next = phead->next;
//尾部标号对应图中步骤
	next->prev = newnode;  //1
	newnode->next = next;  //2
	newnode->prev = phead; //3
	phead->next = newnode; //4
//另一种写法
	//phead->next = newnode;
	//newnode->prev = phead;
	//newnode->next = next;
	//next->prev = newnode;
}

6,尾删

尾删即为删除head的前一个节点
切记不可先将head指向尾删节点NULL,否则有可能造成野指针。
还有一种情况,若链表已经被删空,则会出现错误,则应该对删空时进行提醒并终止程序:

判断链表是否被删空

bool ListEmptyLTNode(LTNode* phead)
{
	assert(phead);
	/*
		链表返回只剩头节点(链表已经被删空)为真
		否则为假
	*/
	return phead->next == phead;
}

尾删代码

void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);

	//head的前一个即为尾巴
	LTNode* tail = phead->prev;//head前一个节点
	LTNode* tailPrev = tail->prev;//tail的前一个节点

	free(tail);//删除节点

	//重新链接新尾节点
	tailPrev->next = phead;
	phead->prev = tailPrev;
}

7,头删

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(!ListEmptyLTNode(phead));

	LTNode* next = phead->next;
	LTNode* nnext = next->next;

	free(next);//删除节点

	phead->next = nnext;
	nnext->prev = phead;
}

8,pos位置之前插入

void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);

	//prev newnode pos
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

此时既然已经做到了可以任意位置插入,
那就可以对头插尾插进行复用,简化代码

优化后的头插

void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	ListInsert(phead->next, x);
}

优化后的尾插

void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
	
	ListErase(phead->prev);
}

9,删除pos位置的节点

void ListErase(LTNode* pos)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* next = pos->next;

	prev->next = next;
	next->prev = prev;

	free(pos);
}

此时既然已经做到了可以任意位置插入,
那就可以对头插尾插进行复用,简化代码

优化后的尾删

void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
	
	ListErase(phead->prev);
}

优化后的头删

void ListPopFront(LTNode* phead)
{
	assert(phead);

	assert(!ListEmptyLTNode(phead));

	ListErase(phead->next);
}

10,求链表长度

int ListSize(LTNode* phead)
{
	assert(phead);

	//跳过哨兵位
	LTNode* cur = phead->next;
	//求长度
	int size = 0;
	while (cur!=phead)
	{
		++size;
		cur = cur->next;
	}
	//返回链表长度
	return size;
/*
	不要用头节点存储来计算长度,int符合但其他类型
	例如char,double 都会出现错误
*/
}

test.c中的调用方法:

	printf("存储的元素个数为:%d\n", ListSize(plist));
	//plist为主函数中创建的结构体

11,查找链元素

/*phead为原双链表,x表示被查找元素*/
int ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	/*新建一个指针t,初始化为头指针 head*/
	LTNode* temp = phead->next;
	int i = 1;
	while (temp!=phead)
	{
		if (temp->data == x)
		{
			return i;
		}
		i++;
		temp = temp->next;
	}
	/*程序执行至此处,表示查找失败*/
	return 0;
}

test.c中的调用方法:

	int lf = ListFind(plist, 5);
	if (lf==1)
		printf("找到了%d\n",lf);
	else
		printf("没找到\n");

12,销毁链表

void ListDestory(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	int size = 0;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		ListErase(cur);
		cur = next;
	}
	free(phead);
	//只是将指向plist的指针置空,要置空plist要在Test.c中完成
	phead = NULL;
}

test.c中的使用方法:

	//销毁的为结构体,并不是指向结构体的指针,所以需要对指针置空
	ListDestory(plist);
	plist = NULL;
	if (plist == NULL)
		printf("销毁成功");

成品展示

List.h

#pragma once

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

typedef int LTDataType;

//定义链表
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;

//创建双向链表
LTNode* BuyListNode(LTDataType x);

//链表初始化
//void ListInit(LTNode** pphead);
LTNode* ListInit();

//打印
void ListPrint(LTNode* phead);

//尾插
void ListPushBack(LTNode* phead, LTDataType x);

//头插
void ListPushFront(LTNode* phead, LTDataType x);

//尾删
void ListPopBack(LTNode* phead);

//头删
void ListPopFront(LTNode* phead);

//判断链表是否被删空
bool ListEmptyLTNode(LTNode* phead);

//pos位置之前插入
void ListInsert(LTNode* pos, LTDataType x);

//删除pos位置的节点
void ListErase(LTNode* pos);

//求链表长度
int ListSize(LTNode* phead);

//查找链元素
int ListFind(LTNode* phead, LTDataType x);

//销毁链表
void ListDestory(LTNode* phead);

List.c

#include "List.h"

//创建双向链表
LTNode* BuyListNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
} 

/*
	改变结构体要用结构体指针。
	要改变结构体指针要用结构体指针的指针。
*/
//初始化链表
//void ListInit(LTNode** pphead)
//{
//	*pphead = BuyListNode(-1);
//	(*pphead)->next = *pphead;
//	(*pphead)->prev = *pphead;
//}
LTNode* ListInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
}

//打印
void ListPrint(LTNode* phead)
{
	assert(phead);
	
	//创建指针进行依次访问
	LTNode* cur = phead->next;

	//当cur循环到头节点时代表已经循环一圈,停止循环
	while (cur!=phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

//尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	
	//head前一个位置插入即为尾插
	ListInsert(phead, x);//pos前一个位置插入
}
//void ListPushBack(LTNode* phead, LTDataType x)
//{
//	assert(phead);
//
//	LTNode* newnode = BuyListNode(x);
//	LTNode* tail = phead->prev;
//	//前一个节点指向新节点
//	tail->next = newnode;
//	//新节点的prev指向上一个节点
//	newnode->prev = tail;
//	//新节点存放的下一节点为头节点
//	newnode->next = phead;
//	//头节点的prev存放新节点,方便尾插
//	phead->prev = newnode;
//}

//头插
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	ListInsert(phead->next, x);
}
//void ListPushFront(LTNode* phead, LTDataType x)
//{
//	assert(phead);
//	
//	LTNode* newnode = BuyListNode(x);
//	LTNode* next = phead->next;
//
//	next->prev = newnode;  //1
//	newnode->next = next;  //2
//	newnode->prev = phead; //3
//	phead->next = newnode; //4
//
//	//phead->next = newnode;
//	//newnode->prev = phead;
//	//newnode->next = next;
//	//next->prev = newnode;
//
//}

//判断链表是否被删空
bool ListEmptyLTNode(LTNode* phead)
{
	assert(phead);

	/*
		链表返回只剩头节点(链表已经被删空)为真
		否则为假
	*/
	return phead->next == phead;
}

//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
	
	ListErase(phead->prev);
}
//void ListPopBack(LTNode* phead)
//{
//	assert(phead);
//	//判断链表是否被删空:为真即为假,为假即为真
//	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
//
//	//head的前一个即为尾巴
//	LTNode* tail = phead->prev;//head前一个节点
//	LTNode* tailPrev = tail->prev;//tail的前一个节点
//
//	free(tail);//删除节点
//
//	//重新链接新尾节点
//	tailPrev->next = phead;
//	phead->prev = tailPrev;
//}


//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);

	assert(!ListEmptyLTNode(phead));

	ListErase(phead->next);
}
//void ListPopFront(LTNode* phead)
//{
//	assert(phead);
//	assert(!ListEmptyLTNode(phead));
//
//	LTNode* next = phead->next;
//	LTNode* nnext = next->next;
//
//	free(next);//删除节点
//
//	phead->next = nnext;
//	nnext->prev = phead;
//}

//pos位置之前插入
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);

	//prev newnode pos
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
	//此时既然已经做到了可以任意位置插入,
	//那就可以对头插尾插进行复用,简化代码
}

//删除pos位置的节点
void ListErase(LTNode* pos)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* next = pos->next;

	prev->next = next;
	next->prev = prev;

	free(pos);
	//此时既然已经做到了可以任意位置插入,
	//那就可以对头删尾插进行复用,简化代码
}

//求链表长度
int ListSize(LTNode* phead)
{
	assert(phead);

	//跳过哨兵位
	LTNode* cur = phead->next;
	//求长度
	int size = 0;
	while (cur!=phead)
	{
		++size;
		cur = cur->next;
	}
	//返回链表长度
	return size;

/*
	不要用头节点存储来计算长度,int符合但其他类型
	例如char,double 都会出现错误
*/
}

/*phead为原双链表,x表示被查找元素*/
int ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	/*新建一个指针t,初始化为头指针 head*/
	LTNode* temp = phead->next;
	int i = 1;
	while (temp!=phead)
	{
		if (temp->data == x)
		{
			return i;
		}
		i++;
		temp = temp->next;
	}
	/*程序执行至此处,表示查找失败*/
	return 0;
}

//销毁链表
void ListDestory(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	int size = 0;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		ListErase(cur);
		cur = next;
	}
	free(phead);
	//只是将指向plist的指针置空,要置空plist要在Test.c中完成
	phead = NULL;
}

test.c

test.c只是用于测试代码,菜单的写法本文将不进行讲解。
但test.c中含有对查找and销毁and存储链元素个数的使用方法进行了示例可以当参考。

#include "List.h"

void Test1()
{
	//LTNode* plist = NULL;
	//ListInit(&plist);
	
	LTNode* plist = ListInit();
	ListPushBack(plist, 0);
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPrint(plist);
	ListPushFront(plist, 10);
	ListPrint(plist);
}

void Test2()
{
	//LTNode* plist = NULL;
	//ListInit(&plist);

	LTNode* plist = ListInit();
	ListPushFront(plist, 0);
	ListPushFront(plist, 1);
	ListPushFront(plist, 2);
	ListPushFront(plist, 3);
	ListPushFront(plist, 4);
	ListPushBack(plist, 5);
	ListPrint(plist);
	ListPopBack(plist);
	ListPrint(plist);
	ListPopFront(plist);
	ListPrint(plist);

	int lf = ListFind(plist, 5);
	if (lf==1)
		printf("找到了%d\n",lf);
	else
		printf("没找到\n");

	printf("存储的元素个数为:%d\n", ListSize(plist));

	ListDestory(plist);
	plist = NULL;
	if (plist == NULL)
		printf("销毁成功");
}

int main()
{
	Test2();

	return 0;
}

本文到这就结束啦,本文为万字解读,创作不易,若喜欢请留下免费的赞吧!
感谢阅读😊😊😊

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

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

相关文章

怎么用excel管理固定资产

在当今的数字时代&#xff0c;我们已经习惯了使用各种电子工具来提高我们的生产力。其中&#xff0c;Excel无疑是一个强大的工具&#xff0c;它不仅可以帮助我们处理数据&#xff0c;还可以用来进行复杂的计算和分析。然而&#xff0c;你可能不知道的是&#xff0c;Excel也可以…

【已解决】src/spt_python.h:14:20: 致命错误:Python.h:没有那个文件或目录

src/spt_python.h:14:20: 致命错误&#xff1a;Python.h&#xff1a;没有那个文件或目录 问题 其中重点的报错信息 src/spt_python.h:14:20: fatal error: Python.h: No such file or directory 思路 sudo yum install python-devel然后重新安装需要的依赖。 解决 成功。…

DBeaver使用

一、导出表结构 二、导出数据CSV 导出数据时DBeaver并没有导出表结构&#xff0c;所以表结构需要额外保存&#xff1b; 导入数据CSV 导入数据时会因外键、字段长度导致失败&#xff1b;

4. HBase必知必会理论基础篇

HBase必知必会理论基础篇 1.1 HBase简介1.2 HBase 数据模型1.3 HBase整体架构1.4 HBase 读写流程1.4.1 客户端读取流程1.4.2 客户端写入流程 1.5 HBase 客户端常用的方法1.5.1 scan查询1.5.2 get查询1.5.3 put查询1.5.4 delete 查询1.5.5 append 查询1.5.6 increment查询 1.6 H…

从零开始在树莓派上搭建WordPress博客网站并实现公网访问

文章目录 序幕概述1. 安装 PHP2. 安装MySQL数据库3. 安装 Wordpress4. 设置您的 WordPress 数据库设置 MySQL/MariaDB创建 WordPress 数据库 5. WordPress configuration6. 将WordPress站点发布到公网安装相对URL插件修改config.php配置 7. 支持好友链接样式8. 定制主题 序幕 …

今天给大家分享一个绘图的 RGB COLOR TABLE

如果大家觉得有用&#xff0c;就点个赞让更多的人看到吧~

微信支付上新的“分分捐”很暖心,一起吗?

今天在买东西付款后发现&#xff0c;在付款页面发现下方出现了一个公益项目&#xff0c;名为“为乡村儿童送鸡蛋 去捐 0.01元”的提示。去查了一下才知道该功能名叫“分分捐”。 微信分分捐是什么&#xff1f; 当微信支付金额超过 5 元且为非整数的订单&#xff08;红包、转账…

如何激励员工?

如何激励员工&#xff1f;激励就是发现员工的需求&#xff0c;并满足它。 今天想给大家介绍下“马斯洛需求理论”&#xff0c;毕竟想要更好的激励下属创造价值&#xff0c;你首先需要了解他想要什么。对吧。 马斯洛需求理论是美国心理学家马斯洛在上世纪四十年代年提出的。他…

钢建筑模板和木建筑模板的优缺点?

当涉及到建筑模板材料时&#xff0c;钢模板和木模板都是常见的选择。它们各自具有一些优点和缺点&#xff0c;下面是钢模板和木模板的介绍&#xff1a; 钢模板的优点&#xff1a; 强度和稳定性&#xff1a;钢模板具有高强度和稳定性&#xff0c;能够承受较大的荷载和压力&…

人大女王金融硕士——不要成为群羊中盲从的羊,别人疯狂你要冷静

随着社会经济的迅速发展&#xff0c;经济全球化不断扩大&#xff0c;教育体系的完善。越来越多的人追求高学历&#xff0c;通过系统的学习来提升自己的知识储备&#xff0c;增长见识。这也导致每年考研成人逐年增加&#xff0c;录取率也逐年下降。很多选择考研的人&#xff0c;…

做游戏开发需要读研吗?

主观建议版 【是否要读研再工作——不建议】 游戏行业不太看重研究生上学就是为了更好的进入工作&#xff0c;许多人考研就是为了拿到工作的敲门砖。而你现在已经一脚迈入门里面了。对于游戏开发从业者而言&#xff0c;走企业路线的&#xff0c;工作中的经验远比课堂上的知识…

适合小企业的CRM客户管理系统

对于小企业而言&#xff0c;选择一款适合自己的CRM系统可以帮助他们更好地管理客户关系&#xff0c;提高销售业绩&#xff0c;加强市场营销和客户服务等方面的能力。但是&#xff0c;由于市面上CRM系统品牌众多&#xff0c;功能各异&#xff0c;价格不一&#xff0c;小企业适合…

vulnhub靶机Brainpan

主机发现 arp-scan -l 端口扫描 nmap --min-rate 10000 -p- 192.168.21.156 服务扫描 nmap -sV -sT -O -p9999,10000 192.168.21.156 这个地方感到了有点不对劲&#xff0c;python起来的一个web端口 漏洞扫描 nmap --scriptvuln -p9999,10000 192.168.21.156 意料之中&…

java接入apiv3微信小程序支付(以java的eladmin框架为例)

一、需要准备的资料 1.小程序AppID 如&#xff1a;wx2e56f5****** 2.商户号 如&#xff1a;1641****** 3.商户API私钥路径&#xff1a;什么是商户API证书&#xff1f;如何获取商户API证书&#xff1f; 获取文件如下图&#xff1a; 如&#xff1a; 本地路径&#xff1a;E:\Env\e…

计算机提示d3dcompiler43.dll缺失怎么修复,多个解决方法分享

在游戏玩家中&#xff0c;遇到游戏提示找不到 d3dcompiler43.dll 文件的情况并不罕见。这使得许多玩家在启动游戏时感到困扰&#xff0c;因为这意味着他们可能无法正常运行游戏。那么&#xff0c;d3dcompiler43.dll 文件到底是什么呢&#xff1f;为什么游戏会提示找不到它呢&am…

注塑车间是否需要导入MES系统?

判定注塑车间是否需要导入MES系统时&#xff0c;需要谨慎考虑下述几个重要因素&#xff1a; 1.生产加工复杂性和多样化&#xff1a; 倘若注塑车间生产出来的产品品种繁多&#xff0c;涉及到多个工艺过程、不同类型的生产过程和参数设置&#xff0c;那么MES系统能够带去更加精准…

为什么当下MES如此火热,各大制造业工厂都在推行?

MES系统的特点&#xff1a; 1&#xff09;数据采集引擎、整合数据采集渠道&#xff08;RFID、条码设备、PLC、Sensor、IPC、PC等&#xff09;覆盖整个工厂制造现场&#xff0c;保证现场数据的实时、准确、全面的采集&#xff1b; 2&#xff09;打造工厂生产管理系统数据采集基础…

文心一言 VS 讯飞星火 VS chatgpt (90)-- 算法导论8.3 3题

三、用go语言&#xff0c;利用归纳法来证明基数排序是正确的。在你所给出的证明中&#xff0c;在哪里需要假设所用的底层排序算法是稳定的? 文心一言&#xff1a; 基数排序是一种非比较型整数排序算法&#xff0c;其通过在每一位上进行比较来排序。基数排序的一个主要特点是…

不会用手机做二维码?文本、链接码用手机生成的方法

现在很多功能都可以通过手机来实现&#xff0c;用手机制作二维码就是很多小伙伴经常会使用的一个功能&#xff0c;那么在手机上制作文本或者网址二维码是最常见的两种类型。下面就给还会在线制作二维码的小伙伴分享一个工具&#xff0c;通过简单的操作方法就可以快速做出文本码…

华为回击:制裁无法阻挡中国科技创新 | 百能云芯

华为最新推出的Mate 60 Pro手机引发了中国市场的抢购热潮&#xff0c;这一成功的举措为华为带来了信心。华为在这个背景下再度推出两款新机&#xff0c;其中包括高阶版的Mate 60 Pro和折叠式手机Mate X5。这两款手机在首批预购开始后迅速售罄&#xff0c;不仅取得了市场的热烈欢…