【数据结构】单链表(详解)

news2024/10/3 8:29:28

【数据结构】单链表(详解)

  • 1.前言
    • 1.1本章节重点
    • 1.2 什么是单链表
    • 1.3 结构体设计
    • 1.4结构体传参
  • 2. SList.h展示
  • 3. SList.c展示
  • 4. 各个接口函数的实现
    • 4.1 尾插
    • 4.2 打印
    • 4.3 头插
      • 4.3.1内存开辟函数
      • 4.3.2插入
    • 4.4 尾删
    • 4.5 头删
    • 4.6 查找
    • 4.7 给定一个位置在这个位置的前面插入数据
    • 4.8 给定一个位置在这个位置的后面插入数据
    • 4.9 给定一个位置删除这个位置的数据
    • 4.10 给定一个位置在删除这个位置前面的数据
    • 4.11 释放空间
  • 5 text.c展示

所属专栏:初始数据结构
博主首页:初阳785
代码托管:chuyang785>
感谢大家的支持,您的点赞和关注是对我最大的支持!!!
博主也会更加的努力,创作出更优质的博文!!
关注我,关注我,关注我,重要的事情说三遍!!!!!!!!

1.前言

前面我们已经用顺序表方式来实现接口函数,但是使用顺序表实现接口函数是有一些缺陷的。

  1. 空间不够了需要增容,增容是要付出代价。
  2. 头部或者中间插入删除数据的时候,需要挪动数据,挪动数据的额时候也是有消耗的。
  3. 避免频繁扩容,我们满了基本上是扩2倍,可能就会导致一定的空间浪费。
  4. 顺序表要求数据从开始位置连续存储那么我们在头部或者中间插入删除数据就需要挪动数据,效率不高。

针对顺序表的缺陷就设计出了单链表。

  1. 按需要申请空间,用不了了就释放空间(更合理的使用了空间)。
  2. 头部或中间插入删除数据,不需要挪动数据。
  3. 不浪费空间。

1.1本章节重点

本章节的重点是熟练掌握二级指针的使用,以及形参和实参之间的关系。

1.2 什么是单链表

所谓链表重在一个链字,顾名思义就是形象认为用一条链子讲各个数据链接起来,顺着条链子就可以找到其他的数据。

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

图像解析:
在这里插入图片描述
最后是以NULL为结束标志的。

1.3 结构体设计

typedef struct SlistNode
{
	int data;//存放数据
	struct SlistNode* next;//存放下一个数据的地址
}SLTNode;

1.4结构体传参

	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);//先随便那个函数结构当作测试用例
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);

注意这里我们传的是plist的地址。而我们的plist同时有时结构体的指针,也就是说我们传过去的是一个二级指针。
至于我们为什么要传二级指针后面我们会讲到。

2. SList.h展示

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLTDateType;
//定义结构体
typedef struct SlistNode
{
	int data;
	struct SlistNode* next;
}SLTNode;
//打印
void SListPrint(SLTNode* phead);
//尾插
void SListPushBack(SLTNode** pphead, SLTDateType x);
//头插
void  SListPushFront(SLTNode** pphead, SLTDateType x);
//尾删
void SListPopBack(SLTNode** pphead);
//头删
void SListPopFront(SLTNode** pphead);
//查找
SLTNode* SListFind(SLTNode* phead, SLTDateType x);
//给定给位置在pos前面插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);
//给定一个位置在pos后面插入
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDateType x);
//给定pos位置删除
void SListErase(SLTNode** pphead, SLTNode* pos);
//给定一个位置删除pos后面的数据
void SListEraseAfter(SLTNode* pos);
//回收空间
void SListDestroy(SLTNode** pphead);

3. SList.c展示

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

SLTNode* BuyListNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}


//打印
void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d-> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
//尾插
void SListPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	//先开辟一开空间
	SLTNode* newnode = BuyListNode(x);
	//判断当没有数据的时候即:NULL
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;

		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
//头插
void  SListPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);

	SLTNode* newnode = BuyListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;

}
//尾删
void SListPopBack(SLTNode** pphead)
{
	assert(pphead);

	assert(*pphead);
	//分情况
	//1.只有一个节点的时候
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;

	}
	//2.有两个及两个以上的节点的时候
	else
	{
		法一:
		SLTNode* tail = *pphead;
		找到尾节点的前一个
		//SLTNode* prev = NULL;
		找到尾插点
		//while (tail->next != NULL)
		//{
		//	prev = tail;
		//	tail = tail->next;
		//}
		//free(tail);
		//tail = NULL;
		//prev->next = NULL;
		
		//法二:
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

}
//头删
void SListPopFront(SLTNode** pphead)
{
	assert(pphead);

	assert(*pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;

}
//查找
SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
	int i = 0;
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}
//给定给位置在pos前面插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);

	SLTNode* newnode = BuyListNode(x);

	SLTNode* posPrev = *pphead;
	if (pos == *pphead)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = newnode;
		newnode->next = pos;
	}
}
//给定一个位置在pos后面插入
void SListInsertAfter( SLTNode* pos, SLTDateType x)
{
	assert(pos);

	SLTNode* newnode = BuyListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//给定pos位置删除
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);

	if (*pphead == pos)
	{
		*pphead = pos->next;
		free(pos);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

void SListEraseAfter(SLTNode* pos)
{
	assert(pos);

	assert(pos->next != NULL);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

//回收空间
void SListDestroy(SLTNode** pphead)
{
	assert(pphead);
	//法1:
	//SLTNode* tail = *pphead;
	//SLTNode* front = tail;
	//while (tail != NULL)
	//{
	//	tail = tail->next;
	//	free(front);
	//	front = tail;
	//}
	//free(front);
	//front = NULL;

	//法二
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

4. 各个接口函数的实现

在进行学习的时候,博主建议小伙伴们结合画图来理解。
画图不仅可以更直观的看出程序是怎么实现的,还可以加深我们对单链表的记忆哦。

4.1 尾插

void SListPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	//SLTNode* newnode = BuyListNode(x);//这里后面分装一个函数来开辟空间
	//先开辟一开空间
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;
	//判断当没有数据的时候即:NULL
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;

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

我们要插入数据,第一步肯定是先开一块空间,里面存放数据和NULL,这就用到了我们的动态内存开辟函数malloc,并把我们开辟好的空间的地址叫做newnode(新节点的意思)
接着就是将他们链接在一起。
这里用到了一个if语句判断我们链表是否有数据,如果没有我们就让我们开辟的一个空间作为我们的第一个数据,然后我们记住他第一个数据的地址记作*pphead。
在这里插入图片描述
而else后面的就是有数据之后就往数据后面插入数据就行了。

现在我们回到刚才的问题:为什么要传二级指针呢?
原因是我们咋插入第一个数据的时候是要改变我们传过来的值的,如果我们只是传plist过来了,我们接受的时候就变成了。
void SListPushBack(SLTNode* pphead, SLTDateType x)
这个时候我们的pphead是形参是实参的临时拷贝,而我们知道改变我们的新参是不能改变实参的,如果要改变实参,就必须通过传地址过来,通过解引用的方法来改变他的值,所以这里如果要想改变plist实参,就必须传plist的地址过来。

4.2 打印

void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d-> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

这里的化就是比较简单的打印,也不需要传二级指针。

4.3 头插

4.3.1内存开辟函数

我们在讨论头插的时候就会想到,我们的尾插和头插总的讲都是插入数据,都是用先创建一个空间,在将他们一一链接起来,所以我们们就换想到能不能分装一个开辟内存的函数,来实现开辟内存呢?

SLTNode* BuyListNode(SLTDateType x)
{
	//开辟一块空间
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

这个样的化就可以避免代码累赘了。

4.3.2插入

void  SListPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	//开辟一块空间
	SLTNode* newnode = BuyListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;

}

我们的头插也是比较好理解的。
在这里插入图片描述

4.4 尾删

void SListPopBack(SLTNode** pphead)
{
	assert(pphead);

	assert(*pphead);
	//分情况
	//1.只有一个节点的时候
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;

	}
	//2.有两个及两个以上的节点的时候
	else
	{
		//法一:
		SLTNode* tail = *pphead;
		//找到尾节点的前一个
		SLTNode* prev = NULL;
		//找到尾插点
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		prev->next = NULL;
		

		法二:
		//while (tail->next->next != NULL)
		//{
		//	tail = tail->next;
		//}
		//free(tail->next);
		//tail->next = NULL;
	}
}

单链表有一个特点就是他是单向的,你只能找到下一个数据,但是你找不到前一个数据。
这个时候我们就要定义一个新的变量(prev)来记住我们要删除的前一个数据地址。
在定义一个(tail)变量来找到我们的尾节点,找到后free,在是prev->next=NULL;
在这里插入图片描述
同样的这里也是要判断是否只剩下最后一个数据的情况,因为如果我们只剩下最后一个的化我们的prev=NULL,而prev->next是对NULL解引用,这明显是一个错误,所以我们的分两种情况,第一种是有两个及上的数据,第二个是只有一个数据的时候。

4.5 头删

void SListPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

头删的化就没什么好说的了。
直接就是将我们的 *pphead向前推进,然后free吊前面的。
同时这里也不许要分情况,因为就算只有一个数据了我们最后 *pphead也是指向NULL的。

4.6 查找

SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
	int i = 0;
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

查找到话就是一一遍历。

4.7 给定一个位置在这个位置的前面插入数据

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	//开辟一块空间
	SLTNode* newnode = BuyListNode(x);
	//当只有一个数据的时候
	if (pos == *pphead)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	//当有两个及以上的数据的时候
	else
	{
		SLTNode* posPrev = *pphead;
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = newnode;
		newnode->next = pos;
	}
}

在这里插入图片描述

这里同样的也是要注意一点就是当只有一个数据的时候
在这里插入图片描述

4.8 给定一个位置在这个位置的后面插入数据

void SListInsertAfter( SLTNode* pos, SLTDateType x)
{
	assert(pos);

	SLTNode* newnode = BuyListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

如果是在后面插入数据的话就不会像上面那样麻烦了,也不用分情况了,因为如果我们在前面一个位置插入的话那么必定会有一个数据,我们就只要往这个数据后面插入即可。

4.9 给定一个位置删除这个位置的数据

void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);

	if (*pphead == pos)
	{
		*pphead = pos->next;
		free(pos);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

如果我们想要删除中间的某个数据并且删除后还要将他们链接起来,前面我们说了,单链表是单向的,只能找到后面的数据,找不到前面的数据,这个时候我们就要定义一个变量来记住我们前面数据的地址,然后将他们链接起来。

注意:这里也是要分两种情况的,和上面的给定一个位置在这个位置的前面插入数据是一样的道理。

4.10 给定一个位置在删除这个位置前面的数据

void SListEraseAfter(SLTNode* pos)
{
	assert(pos);

	assert(pos->next != NULL);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

这里如果我们是删除pos前面的数据的话我们就不用在找到它前面的地址了,而是直接删除后面的数据,因为我们知道单链表是可以找到后面的数据的。

4.11 释放空间

void SListDestroy(SLTNode** pphead)
{
	assert(pphead);
	//法一:
	//SLTNode* tail = *pphead;
	//SLTNode* front = tail;
	//while (tail != NULL)
	//{
	//	tail = tail->next;
	//	free(front);
	//	front = tail;
	//}
	//free(front);
	//front = NULL;

	//法二:
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

5 text.c展示

最后就是我们的text.c源文件了。
我们所写的以上函数接口都是实现功能的,二我们的主函数的一个作用就是测试我们的接口函数是否是正确的。
这里教大家一个小技巧,就是我们没实现一个接口的时候,我们都用一个函数来测试它的功能,这样的好处是我们可以减少我们在调试的时候用到其他的接口函数,这样不仅可能影响到我们的调试,而且还费时。

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
void TestList1()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);

	SListPrint(plist);
}
void TestList2()
{
	SLTNode* plist = NULL;

	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SListPrint(plist);

}


void TestList3()
{
	SLTNode* plist = NULL;

	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);

}

void TestList4()
{
	SLTNode* plist = NULL;

	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);

	SListPrint(plist);


}

void TestList5()
{
	SLTNode* plist = NULL;

	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);


	SLTNode* pos=SListFind(plist, 1);
	int i = 1;
	while (pos)
	{
		printf("第%d个节点:%p->%d\n", i++, pos, pos->data);

		pos= SListFind(pos->next, 1);
	}
}
void TestList6()
{
	SLTNode* plist = NULL;

	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SLTNode* pos = SListFind(plist, 5);

	SListInsert(&plist, pos, 50);

	SListPrint(plist);

}

void TestList7()
{
	SLTNode* plist = NULL;

	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SLTNode* pos = SListFind(plist, 1);

	SListInsertAfter(&plist, pos, 50);

	SListPrint(plist);

}

void TestList8()
{
	SLTNode* plist = NULL;

	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SLTNode* pos = SListFind(plist, 1);
	SListErase(&plist, pos);

	pos = SListFind(plist, 2);
	SListErase(&plist, pos);

	pos = SListFind(plist, 3);
	SListErase(&plist, pos);

	pos = SListFind(plist, 4);
	SListErase(&plist, pos);

	pos = SListFind(plist, 5);
	SListErase(&plist, pos);

	SListPrint(plist);

	SListDestroy(&plist);
}
int main()
{
	//TestList1();
	//TestList2();
	//TestList3();
	//TestList4();
	//TestList5();
	//TestList6();
	//TestList7();
	//TestList8();

	return 0;
}

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

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

相关文章

今天面了个字节跳动拿35K出来的,真是砂纸擦屁股,给我露了一手啊

今年的金三银四已经快要结束了&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0c…

为什么选择学习python?

对于编程初学者来说&#xff0c;python更加简单易学&#xff0c;便于初学者入门~ 学Python之前&#xff1a;这玩意真有传说中那么好么&#xff1f; 学Python之后&#xff1a;唉呀妈呀&#xff0c;真香~ 别人花2天时间处理的Excel数据&#xff0c;你用Python 只花1小时&#…

若依移动端Ruoyi-App——字典使用

1. 引入dict 将若依前后端分离中的dict文件夹拷贝到api的system里 2.在页面中引入方法 import { getDicts } from "/api/system/dict/data"; 3. 前端 <span>{{statusType}}</span> 4. 加载数据字典 export default {data() {return {statusOptions…

LeetCode:剑指 Offer 58 - II. 左旋转字符串

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;剑指 Offer 58 - II. 左旋转字符串 题目描述&#xff1a;字符串的左旋…

正则表达式 - 电话号码

正则表达式是描述一组字符串特征的模式&#xff0c;用来匹配特定的字符串。 一、需求 写一个正则表达式匹配电话号码&#xff0c;并且括号、连字符或点号都是可选的。假定合规数据只包含以下15种匹配模式之一&#xff1a; xxxxxxx 8277019xxx.xxxx 82…

【Microsoft Edge】详解 Edge 的扩展程序

Edge 扩展程序 安装与卸载显示与隐藏开启与关闭导入与导出导出导入操作好处与优势 安装与卸载 可从 https://microsoftedge.microsoft.com/addons?hlzh-CN 获取你想要的扩展程序。 也可以使用 Chrome Web Store &#xff0c;但出于某些原因&#xff0c;需要科学上网。 获取后…

初赛-排列组合概念

1.加法原理: 完成一个工程可以有n类办法&#xff0c;a[i](1<i<n) 代表第i类方法的数目。 那么完成这件事共有 S a[1]a[2]...a[n]种不同的方法。 2.乘法原理: 完成一个工程需要分n个步骤&#xff0c;a[i](1<i<n) 代表第i个步骤的不同方法数目。 那么完成这件…

DnsAddressResolverGroupFactory.create(DnsAddressResolverGroupFactory.java:33)

今天处理了一个dubb升级造成的包冲突&#xff0c;记录下&#xff0c;希望能帮到一些有同样问题的小伙伴&#xff0c;并且将自己解决包冲突的想法总结下 背景&#xff1a; 因为dubbo出现安全漏洞&#xff0c;公司的安全组要求将dubbo的版本由2.7.3-SNAPSHOT 升级到 2.7.3.7-RE…

Windows 10, version 22H2 (2023年3月) 简体中文版、英文版下载

Windows 10, version 22H2 (updated March 2023) 简体中文版、英文版下载 Windows 10 22H2 2023 年 3 月更新 Windows 10 是微软公司推出的一款操作系统&#xff0c;是 Windows 系列操作系统的最新版本&#xff0c;于2015年7月正式发布。Windows 10 被设计为既适用于桌面电脑和…

ActiveMQ使用(二):在JavaScript中使用mqtt.js

ActiveMQ使用(二):在JavaScript中使用mqtt.js 1. 环境准备 jQuery-1.10 下载地址:https://www.jsdelivr.com/package/npm/jquery-1.10.2?tabfilesmqtt.js 4.3.7: 下载地址:https://www.jsdelivr.com/package/npm/mqtt 2. 相关代码 <!DOCTYPE html> <html lang&q…

引用【C++初阶】

目录 一、引用 1、概念 2、特性 3、常引用 4、使用常景 5、传值与传引用的效率性能比较 6、指针和引用的区别 一、引用 1、概念 引用不是新定义一个变量&#xff0c;而是给已存在的变量取一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的…

线程方法,生命周期,线程状态,Synchronized,

package com.hspedu.method;/*** author 韩顺平* version 1.0*/ public class ThreadMethod01 {public static void main(String[] args) throws InterruptedException {//测试相关的方法T t new T();t.setName("老韩");t.setPriority(Thread.MIN_PRIORITY);//设置优…

初识网络安全应急响应

初识网络安全应急响应 1.应急响应的基本能力2.应急响应的基本流程 1.应急响应的基本能力 一、数据采集、存储和检索 能对全流量数据协议进行还原&#xff1b;能对还原的数据进行存储&#xff1b;能对存储的数据快速检索。 二、事件发现 能发现高级可持续威胁&#xff08;AP…

【Redis】Redis十大数据类型—集合set

介绍 set类型是一个无序并唯一的键值集合&#xff0c;set的存储顺序不会按照插入的先后顺序进行存储。 一个集合最多可以存储2^32-1个元素。 set类型除了支持集合内的增删改查&#xff0c;同时还支持多个集合取交集、并集、差集。 Set 类型和 List 类型的区别如下&#xff1…

cgroups 实践

CPU 限制 1. 创建测试的 cgroup 在 /sys/fs/cgroup/cpu 下面创建自己的 cgroups 测试目录&#xff0c;该目录会自动创建一些文件&#xff0c;对进程或者线程的控制都可以通过修改这些文件内容自动完成。 tasks&#xff1a;被 cgroup 管理的进程 cpu.cfs_period_us&#xff1a…

多线程(九):JUC组件

在来时juc组件前&#xff0c;我们先把上一章遗漏的部分给补上。 synchronized 实现策略&#xff1a;锁升级&#xff1a; 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁 还有一个 &#xff1a; 锁消除 锁消除即删除不必要的加锁操作。JVM在运行时&#xff0c;对一些“…

C++内联函数详解

目录 如何定义内联函数&#xff1f; 内联函数的优势 内联函数的缺点 内联函数的使用场景 示例代码 总结 在C中&#xff0c;内联函数是一种特殊的函数&#xff0c;它的作用是在程序编译时将函数的代码直接嵌入到调用该函数的代码中&#xff0c;从而避免了函数调用的开销。这…

C语言 非本地跳转实现native层TryCatch

前言 最近研究native hook的技术&#xff0c;了解到了这个非本地跳转&#xff0c;本文就是介绍他&#xff0c;对于解决native crash非常有用。 非本地跳转介绍 C语言的本地跳转是指goto、break、continue等语句&#xff0c;但是这个语句最大局限就是只能实现函数内部的跳转。…

深度学习 - 40. N-Gram 采样与 Session 数据获取 For EGES

目录 一.引言 二.订单数据预处理 1.数据样例 2.订单数据处理 3.用户 Session 构建 三.构造 sku_id 游走序列 1.获取完整 Session List 2.统计 sku_id 转移词频 3.构建 sku_id 图 4.游走构造 sku 序列 四.商品侧信息预处理 1.读取商品信息 2.Left Join 匹配侧信息 …

三甲医院体检管理系统源码PEIS ,体检总检建议、体检套餐、各种模板

PEIS体检管理系统源码 本套PEIS医院体检管理系统源码&#xff0c;采用C#语言开发&#xff0c;C/S架构&#xff0c;前台开发工具为Vs2012&#xff0c;后台数据库采用oracle大型数据库。有演示。 文末获取联系 PEIS体检管理系统适用于大中型医院的独立体检中心、干部保健处、民营…