数据结构之二叉树的超详细讲解(2)--(堆的概念和结构的实现,堆排序和堆排序的应用)

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

数据结构之二叉树的超详细讲解(2)--(堆的概念和结构的实现,堆排序和堆排序的应用)

收录于专栏【数据结构初阶】
本专栏旨在分享学习数据结构学习的一点学习笔记,欢迎大家在评论区交流讨论💌

之前发布过数据结构之二叉树的超详细讲解(1)--(树和二叉树的概念和结构) ,今天重点讲解堆的概念和结构的实现,堆排序和堆排序的应用,感兴趣的宝子们赶紧点赞收藏起来吧!💓💓💓

目录

1.二叉树的顺序结构及实现 

1.1二叉树的顺序结构

1.2 堆的概念及结构 

1.3堆算法的实现

1.3.1堆结构的实现

1.3.2堆操作函数 

1.3.2.1堆的初始化

1.3.2.2堆的销毁

1.3.2.3堆顶和堆尾元素的交换

1.3.2.4堆的调整算法

1.3.2.4.1向上调整建堆(以小根堆为例)

1.3.2.4.2向下调整建堆(以小根堆为例)

1.3.2.5数据进堆

1.3.2.6堆顶元素出堆

1.3.2.7输出堆顶元素

1.3.2.8堆的判空

练习:

2.堆排序

2.1向上调整建堆

2.1.1小根堆

2.1.2大顶堆

2.2向下调整建堆

2.2.1小根堆

2.2.2大根堆 

2.3堆排序的完整代码参考:

2.4建堆的时间复杂度 

2.4.1向下调整建堆的时间复杂度: 

2.4.1向上调整建堆的时间复杂度: 

2.5堆排序的应用-- TOP-K问题

方法一:

方法二:

 参考代码:

Heap.h:

Heap.c:

text.c:


1.二叉树的顺序结构及实现 

1.1二叉树的顺序结构

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

1.2 堆的概念及结构 

如果有一个关键码的集合K = { k0,k1,k2,,,,,,,,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: ki<=k2*i 且 ki <= k2*i+2 (k >= k2*i+1 且ki >= k2*i+2) i = 0,1, 2…,则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

这里运用了二叉树性质五:

堆的性质:

堆中某个结点的值总是不大于或不小于其父结点的值;

堆总是一棵完全二叉树。 

如下图所示: 

1.3堆算法的实现

1.3.1堆结构的实现

之前说到过,堆就是完全二叉树, 把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,所以我们这里使用动态数组的方式建堆,类似于使用动态数组构建顺序表

顺序表的构建和基本操作-CSDN博客

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

 a就是我们的一个动态数组

size是我们堆中的元素个数

capacity是我们堆的容量,方便后面动态开辟空间

HPDataType方便后面我们进行类型的修改,比如我们不需要整形了,而是char类型,直接修改HPDataType就可以了

1.3.2堆操作函数 

//堆顶和堆尾元素的交换
void Swap(HPDataType* p1, HPDataType* p2);
//向上调正算法
void AdjustUp(HPDataType* a, int child);
//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent);
//堆的初始化
void HPInit(HP* php);
//堆的销毁
void HPDestroy(HP* php);
//数据近堆
void HPPush(HP* php, HPDataType x);
//堆顶元素出堆
void HPPop(HP* php);
//输出队顶元素
HPDataType HPTop(HP* php);
//堆的判空
bool HPEmpty(HP* php);
1.3.2.1堆的初始化
void HPInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = php->capacity = 0;
}
1.3.2.2堆的销毁
void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

注意:

free释放掉a之后,还需要将a置为NULL 

1.3.2.3堆顶和堆尾元素的交换
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

为后面的调整算法算法埋下铺垫 

1.3.2.4堆的调整算法
1.3.2.4.1向上调整建堆(以小根堆为例)

如下图所示(以小根堆为例):

我们将新插入的数据不断的与它的parent节点进行比较 

代码展示:

void AdjustUp(HPDataType* a, int child)
{
	// 初始条件
	// 中间过程
	// 结束条件
	int parent = (child - 1) / 2;
	//while (parent >= 0)
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

注意:

根据我们之前学过的二叉树的性质,左child可以通过(child-1)/2和右孩子(child-2)/2来找到它的母亲节点,因为编译器的向下取整,所以我们直接使用int parent = (child - 1) / 2;来解决,不需要判断左右孩子

如下图 :

这里我们的循环停止条件有两种:

第一种:while (parent >= 0) 

第一种需要注意:当我们的parent = 0时,假设当时child还是比parent小,child  = parent,parent = (child - 1) / 2;还是会进入下一次循环,此时child = parent = 0,由于存在if的判断循环会结束,不会造成死循环现象,属于歪打正着

第二种就严谨很多:

第二种采用child作为循环条件的判断,当我们的child大于0,就会一直进行交换直到child等于0时,即为堆顶元素时,循环停止

1.3.2.4.2向下调整建堆(以小根堆为例)


 

注意:

向下调整建堆需要以27为根的左右子树都满足小堆的性质,只有根节点不满足.因此只需要将根节点往下调,往下调时需要与最小的那个值进行比较

代码展示:

void AdjustDown(HPDataType* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // child >= n说明孩子不存在,调整到叶子了
	{
		// 找出小的那个孩子
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

这里我们使用假设法,先假设左孩子小,然后进入循环,进入循环后进行判断,因为我们需要将较小的孩子与我们的parent进行交换,如果它本来就小,那就直接跳过,然后交换parent和child,如图所示:

1.3.2.5数据进堆

这里需要注意,数据进堆时,我们需要对进堆的数据进行调整,这样我们进堆结束后,即建堆完毕

代码展示:

void HPPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}
1.3.2.6堆顶元素出堆
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);
}

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。这样我们向下调整时两边都是小根堆,满足向下调整的条件

1.3.2.7输出堆顶元素
HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}
1.3.2.8堆的判空
bool HPEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

练习:

1.下列关键字序列为堆的是:()
A 100, 60, 70, 50, 32, 65
B 60, 70, 65, 50, 32, 100
C 65, 100, 70, 32, 50, 60
D 70, 65, 100, 32, 50, 60
E 32, 50, 100, 70, 65, 60
F 50, 100, 70, 65, 60, 32
2.已知小根堆为8, 15, 10, 21, 34, 16, 12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次
数是()。
A 1
B 2
C 3
D 4
3.一组记录排序码为(5 11 7 2 3 17), 则利用堆排序方法建立的初始堆为
A(11 5 7 2 3 17)
B(11 5 7 2 17 3)
C(17 11 7 2 3 5)
D(17 11 7 5 3 2)
E(17 7 11 3 5 2)
F(17 7 11 3 2 5)
4.最小堆[0, 3, 2, 5, 7, 4, 6, 8], 在删除堆顶元素0之后,其结果是()
A[3,2,5,7,4,6,8]
B[2,3,5,7,4,6,8]
C[2,3,4,5,7,8,6]
D[2,3,4,5,6,7,8]

解析:

第一题:我们上面说到过,堆其实就是完全二叉树,所以我们直接构建完全二叉树:

只有A选项满足大堆的需求

第二题:

第三题:

只有c选项满足大顶堆的要求

第四题:

删除后,堆的调整如下:

2.堆排序

2.1向上调整建堆

2.1.1小根堆

	for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		HPPush(&hp, a[i]);
	}

 对每一次插入堆中的数据进行调整建堆

测试数据:

    int a[] = { 4,2,8,1,5,6,9,7,3,2,23,55,232,66,222,33,7,1,66,3333,999 }; 

	while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));
		//a[i++] = HPTop(&hp);
		HPPop(&hp);
	}
	printf("\n");

根据小根堆的性质:它的堆顶元素一定是数组中最小的数据,我们将堆顶数据输出,在重新建堆,直到堆中没有数据,这样就可以实现堆排序 

输出结果:

2.1.2大顶堆

建立大顶堆只需要改变我们之前写的向上向下调整代码:

向上调整代码:

void AdjustUp(HPDataType* a, int child)
{
	// 初始条件
	// 中间过程
	// 结束条件
	int parent = (child - 1) / 2;
	//while (parent >= 0)
	while (child > 0)
	{
		//如果孩子比parent大
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

再向上调整代码中,我们只需要将if的判断改为>就可以了 

向下调整代码:

void AdjustDown(HPDataType* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // child >= n说明孩子不存在,调整到叶子了
	{
		// 找出小的那个孩子
		//找出大的那个孩子
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}
		//如果孩子比母亲大
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

 再向下调整代码中,我们需要调整两处,一个是我们需要找大的那个孩子,将<改为>,还有我们交换的条件需要孩子比母亲大,也是将<改为>

测试数据和代码不变:

所以你只需要会小根堆,大顶堆只需要改变三个地方 

2.2向下调整建堆

再实际应用中,我们建堆并不会使用向上调整建堆,因为时间复杂度不够低,但向上调整建堆更容易理解,大家可以根据自己的情况进行选择,这里我还是推荐向下调整建堆

	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

 

我们这里第一个非叶子节点即最后一个节点的母亲节点:

也就是(n - 1 - 1) / 2,如图所示:

2.2.1小根堆

测试数据    int a[] = { 4,2,8,1,5,6,9,7,2,7,9 };

2.2.2大根堆 

数据和小根堆一样:

2.3堆排序的完整代码参考:

void HeapSort(int* a, int n)
{

	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		printf("%d ", a[0]);
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}



int main()
{
	int a[] = { 4,2,8,1,5,6,9,7,2,7,9 };
	HeapSort(a, sizeof(a) / sizeof(int));
	
	return 0;
}

2.4建堆的时间复杂度 

 我们上面说到过,向下建堆的时间复杂度是优于向上建堆的,这里我们具体分析一下:

2.4.1向下调整建堆的时间复杂度: 

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个结点不影响最终结果):

这里我们是向下调整建堆,是从第一个非叶子节点开始一直到第一个节点的向下调整,如下图所示: 

根据上面的图,我们可以很简单的得到下面的等式: 

再根据我们高中学过的错位相减法,进一步化简: 

因此:向下建堆的时间复杂度为O(N)。 

2.4.1向上调整建堆的时间复杂度: 

因此向上调整建堆的时间复杂度为N*logN 

总结:

 向下调整:节点数量少的层*调整次数多的层 

 向上调整:节点数量多的层*调整次数多的层

 向下建堆的时间复杂度为O(N)。 

上调整建堆的时间复杂度为N*logN 

2.5堆排序的应用-- TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

再比如:大家都玩过王者荣耀吧,金标或者国标都需要取前100名玩家,再几亿玩家中找出战力最高的100名玩家,如果你用快排的话,电脑估计要转冒烟了,但TOP-K可以很好的解决这个问题

方法一:

建立一个N个数的大堆,TOP K次 

这个方法是可行的,不过不够完美,因为再TOP K问题中,N往往是很大的,这样你建堆的一个内存就很大,算法的空间损耗会很大 

方法二:

用前K个数建一个小堆

 

 建堆的时间复杂度为O(K),然后还进行了(N-K)比较,所以总的时间复杂度为O(K + (N-K)*log(K)),假设是最坏的情况,每一次比较都需要向下调整,因为K是远小于N的,所以K,logK可以忽略不记,总时间复杂度为O(N),也就是上亿的数据,也能再秒之内完成 

测试方法二:

创建数据:

void CreateNDate()
{
	// 造数据
	int n = 100000;
	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() + i) % 10000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

输出结果:

创造一百完个随机数据

TOP-K求解:

void TestHeap3()
{
	int k;
	printf("请输入k>:");
	scanf("%d", &k);
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc fail");
		return;
	}
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	// 读取文件中前k个数
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}

	// 建K个数的小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}

	// 读取剩下的N-K个数
	int x = 0;
	while (fscanf(fout, "%d", &x) > 0)
	{
		if (x > kminheap[0])
		{
			kminheap[0] = x;
			AdjustDown(kminheap, k, 0);
		}
	}

	printf("最大前%d个数:", k);
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
}

手动更改十个数据大于10000000的数,查看结果:

 输出结果:

 参考代码:

Heap.h:

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


typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;
//堆顶和堆尾元素的交换
void Swap(HPDataType* p1, HPDataType* p2);
//向上调正算法
void AdjustUp(HPDataType* a, int child);
//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent);
//堆的初始化
void HPInit(HP* php);
//堆的销毁
void HPDestroy(HP* php);
//数据近堆
void HPPush(HP* php, HPDataType x);
//堆顶元素出堆
void HPPop(HP* php);
//输出队顶元素
HPDataType HPTop(HP* php);
//堆的判空
bool HPEmpty(HP* php);

Heap.c:

#define _CRT_SECURE_NO_WARNINGS 1


#include"Heap.h"

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

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

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustUp(HPDataType* a, int child)
{
	// 初始条件
	// 中间过程
	// 结束条件
	int parent = (child - 1) / 2;
	//while (parent >= 0)
	while (child > 0)
	{
		//如果孩子比parent大
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HPPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

void AdjustDown(HPDataType* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // child >= n说明孩子不存在,调整到叶子了
	{
		// 找出小的那个孩子
		//找出大的那个孩子
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}
		//如果孩子比母亲大
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// logN
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);
}

HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}

bool HPEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

text.c:

#define _CRT_SECURE_NO_WARNINGS 1

#include <time.h>
#include"Heap.h"

void TestHeap1()
{
	int a[] = { 4,2,8,1,5,6,9,7,3,2,23,55,232,66,222,33,7,1,66,3333,999 };
	HP hp;
	HPInit(&hp);
	for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		HPPush(&hp, a[i]);
	}

	int i = 0;
	while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));
		//a[i++] = HPTop(&hp);
		HPPop(&hp);
	}
	printf("\n");

	// 找出最大的前k个
	/*int k = 0;
	scanf("%d", &k);
	while (k--)
	{
		printf("%d ", HPTop(&hp));
		HPPop(&hp);
	}
	printf("\n");*/

	HPDestroy(&hp);
}

// 堆排序    O(N*logN)
// 冒泡排序  O(N^2) 
void HeapSort(int* a, int n)
{

	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		printf("%d ", a[0]);
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}


void CreateNDate()
{
	// 造数据
	int n = 100000;
	srand((unsigned int)time(NULL));
	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() + i) % 10000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

void TestHeap3()
{
	int k;
	printf("请输入k>:");
	scanf("%d", &k);
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc fail");
		return;
	}
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	// 读取文件中前k个数
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}

	// 建K个数的小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}

	// 读取剩下的N-K个数
	int x = 0;
	while (fscanf(fout, "%d", &x) > 0)
	{
		if (x > kminheap[0])
		{
			kminheap[0] = x;
			AdjustDown(kminheap, k, 0);
		}
	}

	printf("最大前%d个数:", k);
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
}




int main()
{

	
	//CreateNDate();

	TestHeap3();

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IC解析之TPS1HB08-Q1

目录 1.主要参数2. 接口定义3. 工作原理分析高低边驱动的作用TPS1HB08-Q1架构TPS1HB08-Q1典型应用电路参数设置 4.总结 1.主要参数 2. 接口定义 其中&#xff0c;不同的IC版本在故障反馈引脚有所差异&#xff0c;A/B版本则为ILIM功能&#xff0c;F版本则为FLT功能&#xff0c;两…

GpuMall智算云:QwenLM/Qwen1.5/Qwen1.5-7B-Chat

Qwen 是阿里巴巴集团 Qwen 团队的大型语言模型和大型多模态模型系列&#xff0c;现在大型语言模型已经升级到 Qwen1.5 版本。 GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall,面向AI开发者的GPU云平台 无论是语言模型还是多模态模型&#xff0c;都在大规模的多语言和多模…

【Python】 从Python列表中获取唯一值

基本原理 在Python中&#xff0c;列表是一种非常灵活的数据结构&#xff0c;它允许存储不同类型的元素。然而&#xff0c;有时我们可能需要从列表中提取唯一的值&#xff0c;即去除重复的元素。这在处理数据集或进行数据分析时尤其有用。Python提供了几种方法来实现这一目标。…

甘肃省大学生志愿服务西部计划报名流程及免冠证件照处理

在甘肃省&#xff0c;大学生志愿服务西部计划是一项旨在鼓励和引导大学生参与西部地区社会服务与发展的重要项目。随着2024年报名季的到来&#xff0c;许多有志青年正准备投身这一有意义的事业。本文将详细介绍报名流程&#xff0c;并提供免冠证件照的处理技巧&#xff0c;帮助…

人工智能——什么是摩尔定律以及它如何影响人工智能?

1. 概述 摩尔定律是现代技术发展中一个至关重要的基石。它预言了微芯片上晶体管的数量大约每两年翻一番&#xff0c;这一现象导致了计算能力的指数级增长。在过去的50多年里&#xff0c;这一定律一直是推动技术进步的强大动力&#xff0c;并且对人工智能领域产生了深远的影响。…

市面上前 11 名的 Android 数据恢复软件

Android数据恢复软件是恢复无意中删除的文件或文件夹的必要工具。该软件还将帮助您恢复丢失或损坏的信息。本文介绍提供数据备份和磁盘克隆选项的程序&#xff0c;这些选项有助于在Android设备上恢复文件的过程。 如果您正在寻找一种有效的方法来恢复图像&#xff0c;文档&…

宝塔Nginx设置图片访问跨域

宝塔Nginx设置图片访问跨域 针对某一个站点设置 找到对应的站点点击 “设置” 增加对应header设置 代码&#xff1a; location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)${#允许跨域add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Headers X-Requeste…

解密MySQL中的临时表:探究临时表的神奇用途

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 解密MySQL中的临时表&#xff1a;探究临时表的神奇用途 前言临时表的定义与分类创建与使用临时表临时表的操作与管理优化与性能提升注意事项与最佳实践 前言 在数据库管理中&#xff0c;临时表是一个…

Kali的基本扫描命令

nmap -sP 192.168.10.0/24 //扫描10网段存活的主机 nmap -P 192.168.10.142 //扫描主机开放的端口 nmap -sS 192.168.10.142 //TCP半开扫描 nmap -sT 192.168.10.142 //TCP全开扫描 nmap -O 192.168.10.142 //扫描靶机的操作系统类型 nmap -sV 192.168.10.142 //扫描开放端口对…

记一次绕过宝塔防火墙的BC站渗透

0x00 信息收集 由于主站存在云waf 一测就封 且初步测试不存在能用得上的洞 所以转战分站 希望能通过分站获得有价值的信息 这是一个查询代理帐号的站 url输入admin 自动跳转至后台 看这个参数 猜测可能是thinkCMF 0x01 getshell thinkcmf正好有一个RCE 可以尝试一下 ?afetc…

和可被k整除的子数组 ---- 前缀和

题目链接 题目: 分析: 补充知识 1. 同余定理: (a-b) % p 0即a-b能被p整除, > a % p b % p 2. c, java中 [负数 % 正数] 的结果是负数, 想要得到正确结果 > (a%pp)%p这道题和<和为k的子数组>类似, 利用前缀和的思想, 计算以i结尾的所有子数组, 前缀和为sum[i] …

React项目知识积累(四)

1.useMemo( ) 在 React 中&#xff0c;useMemo 是一个 Hook&#xff0c;用于记忆计算结果&#xff0c;只有当依赖项之一发生变化时&#xff0c;才会重新计算。这有助于避免不必要的计算和渲染&#xff0c;从而提高应用程序的性能。 基本语法如下&#xff1a; const memoized…

【ai】pycharm安装langchain 相关module

pycharm module install 【Python学习 】一篇文章教你PyCharm如何快速安装module 【python】pycharm如何安装python的模块包版本 2024.1.2 RC2 找到当前的虚拟项目 找到解释器 我现在配置为专门为openai-start 准备的3.10 版本+ 号可以找到模块

linux经典定时任务

在使用时记得替换为自己的脚本路径。请在相应的脚本第一行加上#!/bin/bash&#xff0c;否则脚本在定时任务中无法执行。 1、在每天凌晨2点执行 0 2 * * * /bin/sh bashup.sh 2、每天执行两次 下面的示例命令将在每天上午5点和下午5点执行。您可以通过逗号分隔指定多个时间戳…

基于双差分值和RR间隔处理的心电信号R峰检测算法(MATLAB R2018A)

心电信号中的R峰是确定心率和节律、以及检测其它波形特征点&#xff08;图1A&#xff09;的基础。R峰的准确检测是心率变异性分析、心拍分割和心律失常识别重要的处理步骤。 现有的心电信号R峰检测方法主要为基于规则的决策法和基于深度学习的检测方法。基于规则的决策法通常对…

全域运营平台的优缺点各有哪些?听听使用者怎么说!

作为多个创业者交流群内的热点话题&#xff0c;关于全域运营平台优缺点的分析和点评不断涌现&#xff0c;为许多创业者更多信息的同时&#xff0c;也让他们的选择过程变得非常艰难。而在众多的全域运营平台中&#xff0c;被分析和点评次数最多的&#xff0c;当属全域运营平台。…

Excel分类汇总,5个做法,提高数据处理效率!

在日常的工作中&#xff0c;我们经常需要使用Excel中的各种功能&#xff0c;Excel分类汇总功能无疑是数据分析和报告制作中的一把利器&#xff0c;它极大地提高了数据处理的效率和准确性。在现代商业环境中&#xff0c;数据无处不在&#xff0c;而如何从这些数据中提取有效信息…

勇于创新,勤于探索 —— 我的创作纪念日

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

一文了解ai问答机器人:特点、应用、影响

很多人都听过ai问答机器人这个词&#xff0c;也许对于大部分人来说&#xff0c;对它的印象就是智能&#xff01;这是不可置疑的。你在生活中肯定也接触了不少的ai问答机器人。但是关于ai问答机器人&#xff0c;你是否了解它的特点、应用领域和对人类未来的影响呢&#xff1f;Lo…

【Python数据分析】基于自回归积分滑动平均模型的疫情分析报告 附完整python代码

资源地址&#xff1a;Python数据分析大作业 2000字 图文分析文档 疫情分析完整python代码 数据分析 数据来自法国疫情数据 资源地址&#xff1a;Python数据分析大作业 2000字 图文分析文档 疫情分析完整python代码 代码详解 完整代码文件 主要是对时间序列数据进行分析和预…