【数据结构】树、二叉树的概念和二叉树的顺序结构及实现

news2024/11/25 9:46:25

目录

  • 前言:
  • 一、树的概念及结构
    • 1.树的概念
    • 2.树的相关概念
    • 3.树的存储
    • 4.树在实际中的运用
  • 二、二叉树概念及结构
    • 1.概念
    • 2.特殊的二叉树
      • (1)满二叉树
      • (2)完全二叉树
    • 3.二叉树的性质
    • 4.二叉树的存储
      • (1)顺序存储
      • (2)链式存储
  • 三、二叉树的顺序结构及实现
    • 1.二叉树的顺序结构
    • 2.堆的概念及结构
    • 3.堆的实现
      • (1)初始化堆
      • (2)销毁堆
      • (3)堆的插入
    • 向上调整算法
      • (4)堆的删除
    • 向下调整算法
      • (5)获取堆顶元素/堆的元素个数/判空
    • 4.堆排序
      • (1)堆排序版本1
      • (2)堆排序版本2——对数组原地排序
      • (3)优化堆排序版本2及时间复杂度的比较
      • (4)TopK问题

前言:

之前我们学习了顺序表、链表以及栈和队列这些数据结构,但这些数据结构都是线性的(一对一)。接下来要学习非线性的数据结构——树(二叉树),相比前面的,树的结构更加复杂,话不多说,直接进入正题吧。

一、树的概念及结构

1.树的概念

树是一种非线性的数据结构,它是一对多(也有可能是一对一) 的关系。它是由n(n>=0)个有限节点组成一个具有层次关系的集合。把它叫做树是因为它看起来 像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

有一个特殊的结点,称为根结点,根节点没有前驱结点
除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i
<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继,因此树是递归定义的。

现实中的树和数据结构中的树,例如:
在这里插入图片描述

注意:树形结构中,子树之间不能有交集,否则就不是树形结构
在这里插入图片描述

2.树的相关概念

树的相关概念是指树的根、分枝和叶子等等之间的联系,与人类的亲缘关系类似
在这里插入图片描述

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A是一对6,所以A的度为6
叶节点或终端结节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支结点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为堂兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;

注意:在计算节点的层次的时候,从最上面的节点往下数可以从0开始,也可以从1开始,但是更推荐从1开始。
因为树可能是空树、只有一个节点的树或者是有多个节点的树,如果从0开始计算,那么节点的层次(或者说树的高度)当为0时到底是空树还是只有一个节点的树不易分清,所以为了方便数树的高度,从1开始比较好。

在这里插入图片描述

3.树的存储

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既要保存值域,也要保存结点和结点之间的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法。(又称左孩子右兄弟表示法

struct TreeNode
{
	int val;
	struct TreeNode* firstchild;
	struct TreeNode* nextbrother;
};

一个节点可以任意指向多个节点,这个节点的firstchild指针指向它的第一个孩子节点(没有则指向空指针),nextbrother指针指向它的兄弟节点(没有则指向空指针)。

在这里插入图片描述
先找到第一个孩子,(例如B是A的第一个孩子)然后此时的第一个孩子再找他的兄弟,直到为空指针结束。

TreeNode* Anode假设为节点A
找A的第一个孩子B节点:TreeNode* child = Anode->firstchild;
while(child)直到空结束
{
…………
child = child->nextbrother;
}

在这里插入图片描述

4.树在实际中的运用

我们平时使用电脑文件系统的目录就是树结构,打开此电脑,有D盘、C盘等,每层又有多个文件。
在这里插入图片描述

二、二叉树概念及结构

1.概念

一棵二叉树是结点的一个有限集合
1.最多两个节点,也可以是1个或者0个
2. 由一个根节点加上两棵别称为左子树右子树的二叉树组成

在这里插入图片描述
从上图可以看出,二叉树不存在度大于2的结点,并且二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序

注意:对于任意的二叉树都是由以下几种情况复合而成的:
在这里插入图片描述
现实中的二叉树:
在这里插入图片描述

2.特殊的二叉树

(1)满二叉树

概念:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为h,且结点总数是2^h - 1 ,则它就是满二叉树。
在这里插入图片描述

每一层都是满的:2^(i-1)个节点(i是某一层的位置)
F(h) = 2 ^ 0 + 2 ^ 1 + ……+2 ^ (h-2) + 2 ^ (h-1)等比数列求和

假设这个二叉树的节点是N个
N = 2 ^ h - 1 => h = log(N+1) (log以2为底)

(2)完全二叉树

概念:完全二叉树是在满二叉树的基础上变化的。假设它的高度是h,那么它的前h-1层是满的,最后一层可能是满的,也可能是不满的,并且它的最后一层必须从左到右是连续的。满二叉树是特殊的完全二叉树。

在这里插入图片描述
因此,完全二叉树的节点总个数是有范围的。当它为满二叉树时,也就是节点总个数最多的情况下,它的节点总个数是2^h - 1;当最后一层只有一个节点,也就是节点总个数最少的情况下,它的节点总个数是2 ^ (h-1),所以节点范围:【2 ^ (h-1) , 2 ^ h - 1】。

3.二叉树的性质

1.如果根节点的层数为1,则一个非空二叉树的第h层最多有2^(h-1)个节点;
2.如果根节点的层数为1,则深度为h的二叉树最多一共有2^h-1个节点;
3.对任何一棵二叉树, 如果度为0其叶结点个数为n0, 度为2的分支结点个数为n2,则满足公式n0=n2+1;
4.若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log(n+1)

4.二叉树的存储

二叉树的存储方式分为顺序存储链式存储

(1)顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

使用数组存储时有一个规律:
在这里插入图片描述

可在任意位置通过下标找到父亲或者孩子
注意:前提是满二叉树或者完全二叉树才能用这个规律

在这里插入图片描述
所以,对于非完全二叉树,使用链式结构存储更适合。

总结:满二叉树或者完全二叉树适合用数组存储

(2)链式存储

二叉树的链式结构是用链表来表示一个二叉树,分为三个作用域(数据域和左右指针域),左右指针来存储左右孩子的地址。
在这里插入图片描述

三、二叉树的顺序结构及实现

1.二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

2.堆的概念及结构

堆:非线性结构的二叉树,更准确的说是完全二叉树。适合用数组存储。

堆分为两种:根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
小堆:树中任意一个父亲都<=孩子(比的是节点的值)
大堆:树中任意一个父亲都>=孩子

在这里插入图片描述

3.堆的实现

(1)初始化堆

初始化堆有两种方式。
第一种:把结构体指向的数组里面置空(没有元素),有效个数和容量置为0

void HPInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

第二种:接收外来数组的大小n,动态开辟大小为n的空间,把数据拷贝到结构体指向的数组里

void HPInitArray(HP* php, HPDataType* a, int n)
{
	assert(php);
	assert(a);
	php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	php->size = n;
	php->capacity = n;
	memcpy(php->a, a, sizeof(HPDataType) * n);
	for (int i = 1; i < n; i++)
	{
		AdjustUp(php->a, i);
	}

AdjustUp 是向上调整算法,下面会分析。

(2)销毁堆

堆的销毁不必多说,与顺序表的销毁相同

void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

(3)堆的插入

堆的插入采用顺序表的尾插(一个一个插入),因为数组的尾插效率高(还有尾删,后面也会用到)。插入数据要考虑扩容,因为前面初始化的时候空间大小为0。这里可以设置一个变量newcapacity,用条件操作符,如果容量为0,就给初始容量4,否则就是原来容量的两倍。扩容完插入新的数据,元素个数++。

void HPPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* ptr = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);
		if (ptr == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = ptr;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);//向上调整
}

难道把外来的数据一个一个插入就行了吗?其实不然,我们插入数据后要使结构体指向的数组变成一个堆,这里要介绍一个算法。

向上调整算法

从最后一个叶子节点开始向上调整。以建小堆为例,插入一个新的数据为最后一个叶子节点,小堆的特点是父节点<=孩子节点,所以要先找到父节点。

公式:parent = (child - 1) / 2

我们知道,堆的实现虽然是二叉树,但它的存储方式本质是数组,空间是连续的,所以可以通过孩子节点下标找到父节点。这时孩子节点可以与父节点比较,如果父节点大于孩子节点,就交换,然后孩子节点向上到达父节点的位置,原来父节点到达它的父节点的位置。注意控制孩子节点的范围,孩子节点不可能是堆顶元素,否则就会越界,所以child>0。如果父子节点大小关系满足小堆特点,就跳出循环,此时该数组就是小堆了。
在这里插入图片描述

向上调整的前提:该数组原来是小堆或者大堆

既然如此,那插入一个数据前怎么知道这个数组已经是小堆或者大堆了呢?其实这里指针所指向的数组原来什么也没有,是一个一个插入数据的,而我每插入一个数据就调整一次,相当于说我要插入下一个数据时前面的数据已经调整为小堆了,那么插入下一个数据我就调整这个数据和它的父节点的关系就行了。

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

(4)堆的删除

删除堆的一个元素采用顺序表的尾删,但是这里要注意的是仅仅删除堆的最后一个元素是没有意义的。假如是小堆,那么它有个特点,它的堆顶元素一定是所以元素里最小的,所以应该删除堆顶元素。可是数组的头删要挪动数据,比较麻烦,所以这里把堆顶元素与最后一个元素交换,然后再尾删,就可以把最小的元素删除了。但是删除之后,堆顶的元素大小就不确定了,此时不一定是小堆。这里要介绍另一个算法。

void HPPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	AdjustDown(php->a, php->size, 0);
}

向下调整算法

从堆顶元素开始往下调整,因为前面的铺垫只改变了堆顶元素,所以堆顶元素是父节点,可以通过以下公式找到它的孩子节点:

左孩子:child = parent * 2 + 1
右孩子:child = parent * 2 + 2

那么问题来了,是选择左孩子还是选择右孩子?这里我们可以默认选择左孩子,然后加一个判断,如果左孩子的值大于右孩子的值,左孩子下标加1,变成右孩子。因为向下调整变成小堆父节点所要交换的节点是两个孩子节点中的较小的。接着比较父节点与孩子节点的大小关系,如果父节点大于孩子节点,就交换(父节点与孩子交换就是与较小的孩子节点交换),然后父节点到达孩子节点的位置,孩子节点到到达它的左孩子节点的位置。不满足条件说明此时是小堆就跳出循环。

注意:这里要控制一个细节,孩子节点下标的范围小于元素个数。同时,如果一个父节点只有一个孩子节点的情况下(只有左孩子),那么这个左孩子的下标加1就越界了。
child + 1 < n ——这个条件成立说明只有左孩子没有右孩子

在这里插入图片描述

void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] < a[child])
		{                         
			child++;
		}
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

(5)获取堆顶元素/堆的元素个数/判空

//获取堆顶元素
HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}
//获取堆的元素个数
int HPSize(HP* php)
{
	assert(php);
	return php->size;
}
//堆的判空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

4.堆排序

我们要把堆里面的数据排序(以升序为例),有两种方式,一个是打印出来有序,另一个是原地使数组有序。

(1)堆排序版本1

把数据一个一个插入堆中,只要数组不为空就取堆顶元素,然后删除堆顶元素,直到把所有元素打印出来为升序。

void HeapSort(int* a, int n)
{
	HP hp;
	HPInit(&hp);
	int i = 0;
	for (i = 0; i < n; i++)
	{
		HPPush(&hp, a[i]);
	}
	HPPrint(&hp);

	while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));
		HPPop(&hp);
	}
	HPDestroy(&hp);
}

这个方法有缺点:
1.频繁的扩容造成空间复杂度的消耗
2.先要有一个堆的数据结构

(2)堆排序版本2——对数组原地排序

假如一个数组进行堆排序后还要使用,此时只对它打印出来排序就不适合了。所以要对数组原地进行堆排序,使数组的内容有序。

例如:
原来的数组:65,100,70,32,50,60
排序后的数组:32,50,60,65,70,100

在排序前我们要建堆,那么是建小堆还是建大堆?
先来建小堆:
以升序为例,我们在前面的堆排序是打印出来的,用向上调整法是建小堆。这里也用建小堆的方法,小堆建成后堆顶的元素是最小的,所以这个元素排在数组的第一个;然后是次小的,依次从前往后排,直到把数组里的元素从前往后全部排完就是升序了。

但是先以建小堆的方法有缺陷:
分析:堆顶的元素是堆当中最小的依次对应数组从前往后排,每次排完一个数据,要从后面的数据中找出次小的,但问题在于排完的那个数据的后一个数据不一定是次小的,所以后面的数据要重新建堆,重新建的堆的堆顶元素就是次小的。而每次重新建堆使时间复杂度变大,造成效率很低。

向上调整法一个数据遍历时时间复杂度为:logN
有N个数据所以建堆的时间复杂度为:N * logN
每排完一个数据都要建堆一次时间复杂度为:N * (N * logN)
相当于建N次小堆

所以这里应该是建大堆,大堆的堆顶是堆中最大的元素,可是它排在数组的第一个位置,怎么让它到数组的最后一个位置呢?

在这步可以采用堆的删除的思路。把堆顶的元素与最后一个元素交换,这时最大的元素就到了它应该在的位置,然后接下来的步骤很关键,不是真的把最后一个元素删除,那最大的元素排在最后不就没了吗?所以这里我们可以定义一个变量end,它指向的是数组的最后一个元素,完成交换后向下调整,调整的范围是0(第一个元素)到end的有效个数,注意,这时end指向的那个元素不在调整的范围内。然后end减1,控制end>0循环,因为最后只有一个元素就不需要调整了,它是最小的。
在这里插入图片描述

//建堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}
	//调整
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}

(3)优化堆排序版本2及时间复杂度的比较

相较于向上调整法建堆,使用向下调整法建堆的时间复杂度更优秀。前面我们都是使用向上调整法建堆,那么用向下调整法是怎么建堆的呢?

我们知道,用向上调整法建堆是从最后一个节点开始,找它的父节点然后进行比较完成建堆。

调整的执行总次数:每层数据个数 * 向上或向下移动的层数

向上调整法时间复杂度的计算
如图:
在这里插入图片描述
向下调整法建堆:
前面使用向下调整法是从堆顶开始向下调整,依次比较。这里不是从堆顶开始,而是从下面开始调整。找到最后一个父节点,(只要是父节点一定有孩子节点),然后进行比较、调整;接着再到前一个父节点进行前面的操作,最终建成堆。

找最后一个父节点:
最后一个孩子节点:child = n-1
有孩子找父亲:(child-1)/ 2
所以最后一个父节点:(n - 1 - 1) / 2

向下调整法时间复杂度的计算
如图:
在这里插入图片描述
通过对比:向上调整与向下调整的总次数相比多了2 ^ (h-1),也就是说多了一层(最后一层)的计算。最后一层占比大约整体的一半,所以向下调整法建堆更好。

void HeapSort(int* a, int n)
{
	//建堆
	//选择向上调整——O(N*log(N))
	/*for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}*/
	//选择向下调整——O(N)
	int i = 0;
	for ( i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//调整
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

(4)TopK问题

假设有N个数据,找出最大的前K个

1.读取文件的前K个数据,在内存数组中建一个小堆
2.再依次读取后面的数据,跟堆顶元素比较,只要比堆顶元素大就替换堆顶元素进堆,然后向下调整
3.读完所有数据,堆里面的数据就是最大的前K个

这个方法很巧妙,建小堆这样堆顶的元素是堆中最小的,只要比它大就交换进堆。大的数据进堆往下沉在堆底,只有堆里面是最大的前K个才没有交换。当堆里面是最大的前K个时,因为是小堆,堆顶是堆中元素最小的,但是它又比不在堆里面的元素都要大。就算刚开始堆里面已经有K-1个元素在所有元素最大的前K个元素的范围内,进行向下调整保持小堆那么必然有一个元素在堆顶且这个元素小于后面的某个数据,读取到后面的那个元素就交换然后向下调整,最大的前K个就找到了。
如果是建大堆,大堆的堆顶元素是堆中最大的,比它大才能进堆,万一刚开始的堆建好后正好堆顶的元素是所有元素中最大的,那岂不是把所有元素都挡住了。

void PrintTopK(const char* file, int k)
{
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("minheap fail");
		return;
	}
	int i = 0;
	for (i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
	}
	for (i = (k - 2) / 2; i >= 0; i--)
	{
		AdjustDown(minheap, k, i);
	}
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (minheap[0] < x)
		{
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}
	for (i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	free(minheap);
	fclose(fout);
}
void CreateNDate()
{
	// 造数据
	int n = 10000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (int i = 0; i < n; ++i)
	{
		int x = rand() % 1000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}
int main()
{
	CreateNDate();
	PrintTopK("data.txt", 5);
	return 0;
}

在这里插入图片描述
感谢观看~

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

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

相关文章

山西电力市场日前价格预测【2023-09-30】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-09-30&#xff09;山西电力市场全天平均日前电价为267.99元/MWh。其中&#xff0c;最高日前电价为485.05元/MWh&#xff0c;预计出现在18: 45。最低日前电价为0.00元/MWh&#xff0c;预计出…

手机搜狗输入法,输入拼音时如何分割拼音,调出“分词“功能,如何微信或QQ使用发送按钮而不是换行?

背景 有时候打字&#xff0c;输入 “xian” 的时候我们的意图是 “xi’an” &#xff08;西安&#xff09;&#xff0c;或者输入 “yue” 的时候希望是 “yu’e”&#xff08;余额&#xff09; 如何输入这个分隔符 ’ 呢&#xff1f; 设置方法 默认页面如图 希望设置成 点…

python安装第三方模块方法

正常情况下安装python第三方模块没啥说的&#xff0c;但是由于python安装模块默认是在外网下载安装&#xff0c;牵扯外网网速问题&#xff0c;所以可以配置下使用国内某镜像源来下载模块 python -m pip install xxxxxxxxxxx 和 pip install xxxxxxxxxx 的命令都可下载安装第三…

Junit单元测试为什么不能有返回值?

这个问题的产生来源于我们老师上节课说的我们班一个男生问他的想法&#xff0c;刚开始听到这个还觉得挺有意思&#xff0c;我之前使用单元测试好像下意识的就将它的返回值写为void,一般都是进行简单的测试&#xff0c;也从没思考过在某个单元测试中调用另一个单元测试&#xff…

2023年【起重信号司索工(建筑特殊工种)】考试总结及起重信号司索工(建筑特殊工种)模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重信号司索工(建筑特殊工种)考试总结是安全生产模拟考试一点通生成的&#xff0c;起重信号司索工(建筑特殊工种)证模拟考试题库是根据起重信号司索工(建筑特殊工种)最新版教材汇编出起重信号司索工(建筑特殊工种)仿…

Muduo网络库之Channel、EPollPoller与EventLoop类【深度解析】

文章目录 前言一、Channel类1、主要成员变量以及函数2、实现原理 二、EPollPoller类1、实现原理 二、EventLoop类1、功能实现SubReactorde的唤醒操作 前言 重新梳理一遍muduo网络库的类与知识点。 Channel、EPollPoller与EventLoop类是muduo库最重要的基础&#xff0c; 他们三…

python-切换镜像源和使用PyCharm进行第三方开源包安装

文章目录 前言python-切换镜像源和使用PyCharm进行第三方开源包安装1. 切换镜像源2. 使用PyCharm进行第三方开源包安装 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每…

【Java 进阶篇】MySQL多表查询:内连接详解

MySQL是一种强大的关系型数据库管理系统&#xff0c;允许您在多个表之间执行复杂的查询操作。本文将重点介绍MySQL中的多表查询中的一种重要类型&#xff1a;内连接&#xff08;INNER JOIN&#xff09;。内连接用于检索满足两个或多个表之间关联条件的行&#xff0c;它能够帮助…

【Leetcode】 501. 二叉搜索树中的众数

给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 如果树中有不止一个众数&#xff0c;可以按 任意顺序 返回。 假定 BST 满足如下定义&#xf…

严重影响Windows使用体验的一些建议

1内存不够用&#xff1a;通过观察我发现我的电脑已经评价到了90%的内存使用率 没有内存什么程序运行起来都会卡的&#xff0c;所以一定要把不用的PROGRAME给他删除掉。特别是那些自动启动的软件&#xff0c;如果实在不行&#xff0c;就把杀毒也给他卸载掉。 不良具体表现&…

人物重识别(ReID):AaP-ReID: Improved Attention-Aware Person Re-identification

论文作者&#xff1a;Vipin Gautam,Shitala Prasad,Sharad Sinha 作者单位&#xff1a;Indian Institute of Technology Goa 论文链接&#xff1a;http://arxiv.org/abs/2309.15780v1 内容简介&#xff1a; 1&#xff09;方向&#xff1a;人物重识别&#xff08;ReID&#…

Acer宏碁笔记本暗影骑士轻刃AN715-51原装出厂Windows10系统工厂模式镜像

系统自带所有驱动、NITROSENSE风扇键盘灯控制中心、Office办公软件、出厂主题壁纸、系统属性Acer宏基专属的LOGO标志、 Acer Care Center、Quick Access等预装程序 下载链接&#xff1a;https://pan.baidu.com/s/1FDCP5EONlk0o12CYFXbhrg?pwdvazt 所需要工具&#xff1a;32G…

word 多级目录的问题

一、多级标题自动编号 --> 制表符 -> 空格 网址&#xff1a; 【Word技巧】2 标题自动编号——将多级列表链接到样式 - YouTube 二、多级列表 --> 正规形式编号 网址&#xff1a;Word 教学 - 定框架&#xff1a;文档格式与多级标题&#xff01; - YouTube 三、目…

2023年【安徽省安全员C证】模拟考试题及安徽省安全员C证实操考试视频

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【安徽省安全员C证】模拟考试题及安徽省安全员C证实操考试视频&#xff0c;包含安徽省安全员C证模拟考试题答案和解析及安徽省安全员C证实操考试视频练习。安全生产模拟考试一点通结合国家安徽省安全员C证考试最…

力扣:113. 路径总和 II(Python3)

题目&#xff1a; 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;…

Netty(三)NIO-进阶

Netty进阶 1. 粘包与半包 1.1 粘包现象 //client端分10次每次发送16字节数据 public void channelActive(ChannelHandlerContext ctx) {for (int i 0; i < 10; i) {ByteBuf buf ctx.alloc().buffer(16);buf.writeBytes(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …

【嵌入式 – GD32开发实战指南(ARM版本)】第2部分 外设篇 - 第1章 温湿度传感器DHT11

1 理论分析 1.1 DHT11介绍 DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。 DHT11传感器包括一个电阻式感湿元件和一个 NTC 测温元件,并与一个高性能…

2、MQ高级

在昨天的练习作业中&#xff0c;我们改造了余额支付功能&#xff0c;在支付成功后利用RabbitMQ通知交易服务&#xff0c;更新业务订单状态为已支付。 但是大家思考一下&#xff0c;如果这里MQ通知失败&#xff0c;支付服务中支付流水显示支付成功&#xff0c;而交易服务中的订单…

C++核心编程--继承篇

4.6、继承 继承是面向对象三大特征之一 有些类与类之间存在特殊的关系&#xff0c;例如下图中&#xff1a; ​ 我们发现&#xff0c;定义这些类的定义时&#xff0c;都拥有上一级的一些共性&#xff0c;还有一些自己的特性。那么我们遇到重复的东西时&#xff0c;就可以考虑使…

【Java 进阶篇】MySQL多表查询之外连接详解

在MySQL数据库中&#xff0c;多表查询是一种常见且强大的功能&#xff0c;允许您在多个表之间执行联接操作&#xff0c;从而检索、过滤和组合数据。在本篇博客中&#xff0c;我们将深入探讨多表查询的一种类型&#xff0c;即外连接&#xff08;Outer Join&#xff09;&#xff…