【数据结构】建堆的方式、堆排序以及TopK问题

news2024/11/19 9:29:10

建堆的方式、堆排序以及TopK问题

  • 1、建堆的两种方式
    • 1.1 向上调整建堆
    • 1.2 向下调整建堆
  • 2、堆排序
  • 3、TopK问题
  • 4、建堆、堆排序、TopK问题全部代码

1、建堆的两种方式

我们知道,堆是二叉树的一种,二叉树的建立是借助结构体与数组完成的(通过在结构体中创建动态开辟的数组变量储存堆中的元素)。
除了借助结构体外,有没有其他方式,直接建堆。

1.1 向上调整建堆

//交换函数
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//向上调整函数
typedef int HPDataType;
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;  //找到堆最后一个数的父亲的下标
	while (child > 0)   //孩子的下标等于0时,说明堆从最后一个数一路向上比较,已经到达堆顶了
	{
		//小根堆,任意孩子的值要大于父节点的值,不是的话则要向上调整
		if (a[child] < a[parent])   //改为>,这个堆结构就成为大堆了
		{
			Swap(&a[child], &a[parent]);

			//修正父亲与孩子的下标,通过循环不断比较,直到成为堆的形状
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

int main()
{
	int a[] = { 15,1,19,25,8,34,65,4,27,7 };

   //建堆---向上调整建堆---O(N*LogN)
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = 1; i < n; ++i)
	{
		AdjustUp(a, i);
	}

	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

打印结果
在这里插入图片描述
在这里插入图片描述
1.打印结果将一串无序的数组建立成为小堆。只要将向上调整中的if语句中的小于符号改成大于符号,建成的就是大堆。
2.向上调整建堆从第二个节点开始向上调整(即数组下标为1的节点),因为第一个节点是根节点,根节点没有父节点,再往上就没有节点了。第二个节点向上调整完毕,接着第三个节点,然后第四个节点、第五个节点,直到最后一个节点向上调整完毕。
3.向上调整建堆的时间复杂度
在这里插入图片描述
可以看到,在一棵树中,越往下的节点,向上调整的次数越多,并且越往下的层级包含的节点数越多,所以越往下的层级每个节点向上调整的次数加起来是越大的。由上述式子计算,最后一层所有节点总共需要向上调整的次数最大为(N+1) * ((log(N+1)-1)/2),即最后一层需要向上调整的时间复杂度为O(N * log(N))。因为最后一层的时间复杂度已经是最大的,前面所有层级加起来的时间复杂度也不会超过,所以向上调整总的时间复杂度为O(N * log(N))。

1.2 向下调整建堆

//交换函数
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//向下调整函数
typedef int HPDataType;
void AdjustDown(HPDataType* a, int n, int parent)
{
	int minChild = parent * 2 + 1; //先默认左边的孩子是整个小根堆中次小的孩子
	while (minChild < n)
	{
		//与右孩子比较一下,找出小的那个孩子的下标
		if (minChild + 1 < n && a[minChild + 1] < a[minChild])
		{
			minChild++;
		}
		//找到次小的孩子后将其与父节点比较
		if (a[minChild] < a[parent])
		{
			Swap(&a[minChild], &a[parent]);
			//修正父亲与孩子的下标,通过循环不断比较,直到成为堆的形状
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

}

int main()
{
	int a[] = { 15,1,19,25,8,34,65,4,27,7 };

	//建堆---向下调整建堆---O(N)
	//向下调整建堆要从倒数的第一个非叶子节点开始向下调整,一直调整到根节点位置
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

打印结果
在这里插入图片描述
在这里插入图片描述
1.打印结果将一串无序的数组建立成为小堆。只要将向下调整中的两个if语句中的小于符号改成大于符号,建成的就是大堆。
2.向下调整建堆从倒数的第一个非叶子节点开始向下调整,因为叶子节点往下没有子节点了,不用在往下调整了。倒数第一个非叶子节点调整完毕,接着倒数第二个非叶子节点向下调整,然后倒数第三个非叶子节点向下调整,一直到根节点位置向下调整。(n-1-1)/2 是在找倒数第一个非叶子节点。
3.向下调整时间复杂度
在这里插入图片描述
由上述式子算出,向下调整建堆最坏需要向下调整N-log(N+1)次,因此向下调整建堆的时间复杂度为O(N)。

总结: 经由上述可知,向下调整建堆的时间复杂度与向上调整建堆的时间复杂度相比,向下调整建堆所需时间更少,因此在没有结构体构造堆的情况下,选择向下调整直接建堆。

2、堆排序

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

//向下调整函数
typedef int HPDataType;
void AdjustDown(HPDataType* a, int n, int parent)
{
	int minChild = parent * 2 + 1; //先默认左边的孩子是整个小根堆中次小的孩子
	while (minChild < n)
	{
		//与右孩子比较一下,找出小的那个孩子的下标
		if (minChild + 1 < n && a[minChild + 1] < a[minChild])
		{
			minChild++;
		}
		//找到次小的孩子后将其与父节点比较
		if (a[minChild] < a[parent])
		{
			Swap(&a[minChild], &a[parent]);
			//修正父亲与孩子的下标,通过循环不断比较,直到成为堆的形状
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

}

//堆排序函数
//大思路:选择排序,依次选数,从后往前排
//升序---先建大堆,建堆完毕后,此时最大的数在第一位,把第一个和最后一个的位置进行交换,
// 交换完毕后,把最后一个不看做堆里面的,进行向下调整,选出次大的。后续依次类似处理。

//降序---先建小堆,建堆完毕后,此时最小的数在第一位,把第一个和最后一个的位置进行交换,
//交换完毕后,把最后一个不看做堆里面的,进行向下调整,选出次小的。后续依次类似处理。
void HeapSort(int* a, int n)
{
	//建堆---向下调整建堆---O(N)
	//向下调整建堆要从倒数的第一个非叶子节点开始向下调整,一直调整到根节点位置
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	//选数
	int i = 1;
	while (i < n)
	{
		Swap(&a[0], &a[n - i]);  //交换第一个数跟最后一个数
		AdjustDown(a, n - i, 0);
		++i;
	}
}

int main()
{
	int a[] = { 15,1,19,25,8,34,65,4,27,7 };
	HeapSort(a, sizeof(a) / sizeof(int));

	for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

打印结果
在这里插入图片描述
在这里插入图片描述
1.打印结果将一串无序的数组建立成为降序的小堆。
2.堆排序的思路:
大思路:选择排序,依次选数,从后往前排

2.1升序—先建大堆,建堆完毕后,此时最大的数在第一位,把第一个和最后一个的位置进行交换,
交换完毕后,把最后一个不看做堆里面的,进行向下调整,选出次大的。后续依次类似处理。
2.2降序—先建小堆,建堆完毕后,此时最小的数在第一位,把第一个和最后一个的位置进行交换,
交换完毕后,把最后一个不看做堆里面的,进行向下调整,选出次小的。后续依次类似处理。

3、TopK问题

TopK问题就是在N个数中选出最大的前K个数或者最小的前K个数来。
这个问题有两种思路,例如选出最大的前K个数,
思路1,建大堆(用向下调整建堆的方式),将根节点与尾节点进行交换(此时根节点上的数为最大的数),交换后不将尾节点算入堆中,剩余的N-1个数重新建堆,重复K次,这样最大的前K个数就被筛选出来了。
时间复杂度为O(N+(logN)* K)。
思路2,建小堆(用向下调整建堆的方式),用N个数中的前K个数建一个K个数的小堆,剩余的N-K个数一一与堆顶比较,只要比堆顶大就入队,入堆后再重新向下调整建成小堆。当比较完毕后,小堆中的K个数就是在N个数中选出的最大前K个数。时间复杂度为K+(log(k)) *(N-K)。

分析思路1与思路2的优劣: 当N很大,K很小的时候,选第二种方式,例如当N=100亿,K=100,100亿个整数有400亿个字节(Byte),一个G有1024 *1024 *1024(Byte)=1073741824个字节(Byte)≈1000000000=10亿字节(Byte),所以当N=100亿时,要占用的空间为400÷10=40个G,所占内存过大,所以一般选用思路2的方式,即:
在N个数中选出最大的前K个数-----建小堆
在N个数中选出最小的前K个数-----建大堆

在文件中先写入10个数,选取这十个数中最大的前3个数,代码如下:

void PrintTopK(const char* filename, int k)
{
	assert(filename);

	FILE* fout = fopen(filename, "r");   //r--->在Test,c这个源文件路径下打开一个本身已经存在的叫Data.txt的文件,将这个文件的地址赋值给文件指针
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	int* minHeap = (int*)malloc(sizeof(int) * k);
	if (minHeap == NULL)
	{
		perror("malloc fail");
		return;
	}
	//如何读取前k个数
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &minHeap[i]);
	}

	//建堆,将读取的前k个数建成小堆,向下调整建堆
	for (int j = (k - 1 - 1) / 2; j >= 0; --j)
	{
		AdjustDown(minHeap, k, j);
	}
	//继续读取N-k个数
	int val = 0;
	while (fscanf(fout, "%d", &val) != EOF)
	{
		if (val > minHeap[0])
		{
			minHeap[0] = val;
			AdjustDown(minHeap, k, 0);
		}
	}

	for (int i = 0; i < k; ++i)
	{
		printf("%d ", minHeap[i]);
	}

	free(minHeap);
	fclose(fout);
}

//top k问题  N个数,找出这N个数中最大的前k个数
int main()
{
	const char* filename = "Data.txt";
	int k = 3;
	PrintTopK(filename, k);

	return 0;
}

1.Fopen:打开文件,Fclose:关闭文件,fscanf:从文件中读取数据(即从文件中输入数据到屏幕上,sanf是从键盘上输入数据到屏幕上)
2.fscanf(fout, “%d”, &val);从fout中以 %d的类型读取数据到变量val中。
3.已经存在的data.txt文件中10个数为 0 3 11 2 4 1 100 2 200 5。文件放在与Tsetc同一级的目录下。
4.打印结果为
在这里插入图片描述
前3个最大的数被选出来了,被选出来的数并不是有序的,这里有序只不过是巧合而已。
5. 向下调整函数的实现被写在了Heap.c中,在第一节中,向下调整的实现已经写过了,这里就不搬上来了。

在文件中随机生成10000个数(1~10000中的任意数,包含了重复的数),选出其中最大的前10个数来,如何实现?
这个只要多写一个在文件中随机生成数的函数就行,代码如下。

//在文件中随机数的生成
void CreateDataFile(const char* filename, int k)
{
	assert(filename);
	FILE* fin = fopen(filename, "w"); //w--->在test.c这个源文件路径下创建一个test.txt的文件,如果这个文件已经存在,则将
	                                  //这个文件里的内容全部销毁,相当于重新创建了这个文件。
	if (fin == NULL)
	{
		perror("fin fail");
		return;
	}
	srand(time(0));
	for (int i = 0; i < k; ++i)
	{
		fprintf(fin, "%d\n", rand()%10000);
	}

	fclose(fin);
}

void PrintTopK(const char* filename, int k)
{
	assert(filename);

	FILE* fout = fopen(filename, "r");  //r--->在Test,c这个源文件路径下打开一个本身已经存在的叫Data.txt的文件,将这个文件的地址赋值给文件指针
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	int* minHeap = (int*)malloc(sizeof(int) * k);
	if (minHeap == NULL)
	{
		perror("malloc fail");
		return;
	}
	//如何读取前k个数
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &minHeap[i]);
	}

	//建堆,将读取的前k个数建成小堆
	for (int j = (k - 1 - 1) / 2; j >= 0; --j)
	{
		AdjustDown(minHeap, k, j);
	}
	//继续读取N-k个数
	int val = 0;
	while (fscanf(fout, "%d", &val) != EOF)
	{
		if (val > minHeap[0])
		{
			minHeap[0] = val;
			AdjustDown(minHeap, k, 0);
		}
	}

	for (int i = 0; i < k; ++i)
	{
		printf("%d ", minHeap[i]);
	}

	free(minHeap);
	fclose(fout);
}

int main()
{

	const char* filename = "Data.txt";
	int N = 10000;
	int k = 10;

	CreateDataFile(filename, N);
	PrintTopK(filename, k);
	
	return 0;
}

1.void CreateDataFile(const char* filename, int k);---->在文件中生成随机数
2.如何生成随机数?
rand()函数能够生成不同的伪随机数,如果要生成0到10000之间的伪随机数,则rand()%10000,这样生成的伪随机数就是0~9999。不过如果直接使用rand(),每次生成的伪随机数都是一样的,比如第一次生成的是1 4 99 3……,下一次运行程序生成的还是1 4 99 3……,因此每次使用rand()前都要先使用srand()初始化一遍,如果srand()中的参数是固定的话,每次初始化的值也是一样的,即每次调用rand()生成的伪随机数还是一样的,因此需要srand()中的参数不是固定的,这样每次调用rand()时,生成的伪随机数才能不是一样的。刚好,电脑上流动的时间是在变化的,运用时间戳函数time(),能够将每时每刻的时间转换为time_t类型的值,不同时间调用time(),生成的值都不同,因此这个值作为srand()的参数,随着srand()中的参数变化,每次初始化的值也不一样了,rand()生成的伪随机数也不一样了。
time()的头文件是time.h
3.fprintf(fin, “%d\n”, rand()%10000);—>将参数3以%d的类型写进文件指针fin指向的文件。
打印结果
在这里插入图片描述
在这里插入图片描述
上述文本文件是在文件中随机生成的1000个数中截取的一部份,运行结果是这一万个数种最大的前十个数。因为随机生成的数有重复的,因此最大的前十个数也有重复的。
4.如何验证这个程序的正确性?
答:首相将随机生成数函数屏蔽,然后在之前已经生成的随机数种中手动加入十个比这一万个数都大的数,如果程序运行后,能把这手动添加的数全部选出来,说明程序无误。现在加入 10001 10002 10003……10010。
在这里插入图片描述

int main()
{

	const char* filename = "Data.txt";
	int N = 10000;
	int k = 10;

	//CreateDataFile(filename, N);
	PrintTopK(filename, k);

	return 0;
}

打印结果
在这里插入图片描述
手动加入的十个最大的数全部被挑选出来,说明程序无误。

4、建堆、堆排序、TopK问题全部代码

Heap.h部分:写函数的声明,函数的头文件。

#pragma once

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

//堆是完全二叉树
//堆的二叉树用数组表示,在数组的顺序从上至下,从左至右
//小根堆,任何节点的值小于等于孩子的值
//大根堆,任何节点的值大于等于孩子的值
//数组下标计算父子关系的公式
//leftchild = parent*2 + 1   左孩子的数组下标都是奇数
//rightchild = parent*2 + 2  右孩子的数组下标都是偶数
//parent = (child - 1)/2


typedef int HPDataType;

//向上调整
void AdjustUp(HPDataType* a, int child);

//向下调整
void AdjustDown(HPDataType* a, int n, int parent);

//交换函数
void Swap(HPDataType* p1, HPDataType* p2);

//在文件中随机数的生成
void CreateDataFile(const char* filename, int k);

//TopK函数
void PrintTopK(const char* filename, int k);

Heap.c部分:各自定义函数的实现

#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"

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 (child > 0)   //孩子的下标等于0时,说明堆从最后一个数一路向上比较,已经到达堆顶了
	{
		//小根堆,任意孩子的值要大于父节点的值,不是的话则要向上调整
		if (a[child] < a[parent])   //改为>,这个堆结构就成为大堆了
		{
			Swap(&a[child], &a[parent]);

			//修正父亲与孩子的下标,通过循环不断比较,直到成为堆的形状
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}


//向下调整函数
void AdjustDown(HPDataType* a, int n, int parent)
{
	int minChild = parent * 2 + 1; //先默认左边的孩子是整个小根堆中次小的孩子
	while (minChild < n)
	{
		//与右孩子比较一下,找出小的那个孩子的下标
		if (minChild + 1 < n && a[minChild + 1] < a[minChild])
		{
			minChild++;
		}
		//找到次小的孩子后将其与父节点比较
		if (a[minChild] < a[parent])
		{
			Swap(&a[minChild], &a[parent]);
			//修正父亲与孩子的下标,通过循环不断比较,直到成为堆的形状
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

}


//在文件中随机数的生成
void CreateDataFile(const char* filename, int k)
{
	assert(filename);
	FILE* fin = fopen(filename, "w"); //w--->在test.c这个源文件路径下创建一个test.txt的文件,如果这个文件已经存在,则将
	//这个文件里的内容全部销毁,相当于重新创建了这个文件。
	if (fin == NULL)
	{
		perror("fin fail");
		return;
	}
	srand(time(0));
	for (int i = 0; i < k; ++i)
	{
		fprintf(fin, "%d\n", rand() % 10000);
	}

	fclose(fin);
}


//TopK函数
void PrintTopK(const char* filename, int k)
{
	assert(filename);

	FILE* fout = fopen(filename, "r");  //r--->在Test,c这个源文件路径下打开一个本身已经存在的叫Data.txt的文件,将这个文件的地址赋值给文件指针
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	int* minHeap = (int*)malloc(sizeof(int) * k);
	if (minHeap == NULL)
	{
		perror("malloc fail");
		return;
	}
	//如何读取前k个数
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &minHeap[i]);
	}

	//建堆,将读取的前k个数建成小堆
	for (int j = (k - 1 - 1) / 2; j >= 0; --j)
	{
		AdjustDown(minHeap, k, j);
	}
	//继续读取N-k个数
	int val = 0;
	while (fscanf(fout, "%d", &val) != EOF)
	{
		if (val > minHeap[0])
		{
			minHeap[0] = val;
			AdjustDown(minHeap, k, 0);
		}
	}

	for (int i = 0; i < k; ++i)
	{
		printf("%d ", minHeap[i]);
	}

	free(minHeap);
	fclose(fout);
}

Test.c部分:主函数放在这,在主函数中调用各自定义函数。在实现各函数时,可以用来测试各函数的功能。

#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"

//向上调整建堆
int main()
{
	int a[] = { 15,1,19,25,8,34,65,4,27,7 };

    //建堆---向上调整建堆---O(N*LogN)
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = 1; i < n; ++i)
	{
		AdjustUp(a, i);
	}

	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

//向下调整建堆
int main()
{
	int a[] = { 15,1,19,25,8,34,65,4,27,7 };

	//建堆---向下调整建堆---O(N)
	//向下调整建堆要从倒数的第一个非叶子节点开始向下调整,一直调整到根节点位置
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}


//堆排序函数
//大思路:选择排序,依次选数,从后往前排
//升序---先建大堆,建堆完毕后,此时最大的数在第一位,把第一个和最后一个的位置进行交换,
// 交换完毕后,把最后一个不看做堆里面的,进行向下调整,选出次大的。后续依次类似处理。

//降序---先建小堆,建堆完毕后,此时最小的数在第一位,把第一个和最后一个的位置进行交换,
//交换完毕后,把最后一个不看做堆里面的,进行向下调整,选出次小的。后续依次类似处理。
void HeapSort(int* a, int n)
{
	//建堆---向下调整建堆---O(N)
	//向下调整建堆要从倒数的第一个非叶子节点开始向下调整,一直调整到根节点位置
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	//选数
	int i = 1;
	while (i < n)
	{
		Swap(&a[0], &a[n - i]);  //交换第一个数跟最后一个数
		AdjustDown(a, n - i, 0);
		++i;
	}
}
int main()
{
	int a[] = { 15,1,19,25,8,34,65,4,27,7 };
	HeapSort(a, sizeof(a) / sizeof(int));

	for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}




//top k问题  N个数,找出这N个数中最大的前k个数
int main()
{
	const char* filename = "Data.txt";
	int k = 3;
	PrintTopK(filename, k);

	return 0;
}

//在文件中随机生成10000个数,然后找出其中前十个最大数。
int main()
{

	const char* filename = "Data.txt";
	int N = 10000;
	int k = 10;

	CreateDataFile(filename, N);
	PrintTopK(filename, k);

	return 0;
}

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

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

相关文章

扫描电镜下的人体感官结构,超震撼

我们对自己的感官最熟悉不过了&#xff0c;但是如果把这些器官放大一千倍一万倍&#xff0c;你还能分辨出来吗&#xff1f;能做到这一点的不是普通的光学显微镜&#xff0c;而是电子显微镜。 电子显微镜可以将物体放大近300000倍&#xff0c;其分辨率可达1纳米&#xff08;10-9…

Minio学习

目录 一、概述 1、Minio介绍 2、Minio的基础概念 3、Minio安装 3.1、Docker容器中安装 3.2、Windows运行安装 4、分布式Minio优势 数据保护 高可用 一致性 5、Minio客户端使用 6、SpringBoot工程引入Minio 一、概述 Minio分布式文件系统。 Minio是一个基于Apache…

客服回复差评的话术模板

当店铺出现差评时&#xff0c;客服首先要去与客户进行沟通&#xff0c;帮助客户解决问题&#xff0c;尽可能去消除差评&#xff1b;如果客户不愿意沟通&#xff0c;无法消除差评的情况下&#xff0c;客服也要及时对差评进行回复。 前言 对于开网店的店主来说&#xff0c;客户…

如何搭建一个自己的音乐服务器

点赞再看&#xff0c;动力无限。 微信搜「 程序猿阿朗 」。 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录&#xff0c;有很多知识点和系列文章。 最近发现&#xff0c;经常用的网易云音乐&#xff0c;有很多歌曲下架了&#xff0c;能听的越来越少了&#xff1b;…

【电气安全】安科瑞电气火灾监控系统在江苏某大学中设计与应用

摘要&#xff1a;本文以安科瑞电气火灾系统在江苏大学科技园的应用为案例&#xff0c;介绍电气火灾系统实现对现场设备的系统集成&#xff0c;数据的采集、传输以及存储&#xff0c;验证了该系统的功能及实用性。 关键词&#xff1a;江苏大学科技园&#xff1b;电气火灾系统&a…

【数据结构】Map和Set

目录 一、JDK中的Map和Set 1.1Map接口的使用 &#xff08;1&#xff09;元素的添加操作 &#xff08;2&#xff09;在Map集合中查询特定的值 &#xff08;3&#xff09;删除Map中指定的value和key &#xff08;4&#xff09;Map集合的遍历 1.2Set集合的应用 集合java.u…

RK3588平台开发系列讲解(Thermal篇)Thermal的设备树配置

平台内核版本安卓版本RK3588Linux 5.10Android12🚀返回专栏总目录 文章目录 一、Tsadc 配置二、cooling device配置2.1、CPU配置2.2、 GPU配置三、 Thermal Zone配置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍Thermal的设备树配置方法。 一、Tsadc 配…

【GD32F427开发板试用】+demo的正确打开方式(一)

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;四季的温度 这个系列用于汇总我在使用GD32时遇到的问题&#xff0c;为大家排坑 MDK环境准备 打开https://aijishu.com/a/1060000000356925&a…

搜索与图论-树与图的深度优先遍历

文章目录一、树与图的深度优先遍历1. 构造2. 遍历3. 具体实现详见例题——树的重心二、树与图的深度优先遍历例题——树的重心具体实现1. 样例演示2. 实现思路3. 代码注解4. 实现代码DFS 深度优先遍历详见搜索与图论-DFS 一、树与图的深度优先遍历 数与图的深度优先遍历与 DF…

JDK8新特性超详细总结

JDK8新特性 ​ Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c;Java 仍是企业和开发人员的首选开发平台 课程内容的介绍 了解Java发展史Lambda表达式…

DNS轮询是什么

参考文章&#xff1a;DNS轮询是什么&#xff1f; DNS轮询简单来说就是一个域名解析多个IP地址。那么为什么会有DNS轮询呢&#xff1f;一般DNS轮询用于访问量较大的网站&#xff0c;由于一台服务器无法承载&#xff0c;所以准备了多台服务器组成集群做负载均衡&#xff0c;这些服…

使用Mac编写Thirft的HelloWorld项目

前言 最近在hive新增Thirft接口&#xff0c;于是学习了一下Thirft的使用 步骤 准备工作 brew install thrift安装thrift创建maven项目并引入依赖 <dependency><groupId>org.apache.thrift</groupId><artifactId>libthrift</artifactId><v…

led护眼灯哪种品牌质量好?2022什么牌子的护眼灯最好推荐

现在绝大部分台灯都是使用led灯珠作为发光源&#xff0c;这不仅仅是led灯珠本身的优秀&#xff0c;也更是市场的选择&#xff0c;无论是光线照度、显色性&#xff0c;还是亮度、色温的可调控性&#xff0c;led护眼灯都有非常明显的优势&#xff0c;对保护眼睛起到非常大的作用。…

微信小程序开发平台

微信小程序开发平台顾名思义就是一个可以开发小程序的地方。 微信小程序开发平台&#xff1a;【电脑浏览器输入3M.FKW.COM了解详情】 适合群体&#xff1a;企业、机构、个体户 微信小程序开发平台方式&#xff1a; 自建——可以通过套用小程序模板&#xff0c;利用拖拽式小…

ADSP-21489的开发详解:VDSP+自己编程写代码开发(3-可能出现的故障和解决办法)

如果连接失败怎么办&#xff1f;软件报错&#xff0c;无法进入上图所示的状态&#xff1f;不用担心&#xff0c;ADI 的 Visual DSP软件提供了自带的 TEST 功能&#xff0c;能通过软件分析&#xff0c;得出故障原因&#xff0c;并排除。 发现 ADI 仿真器连不上开发板&#xff0c…

浏览器执行过程与V8引擎执行原理(无惧面试)

前言&#xff1a;一文带你理解&#xff0c;浏览器渲染过程以及V8引擎背后执行原理&#xff0c;让你知道页面在浏览器上显示出来背后都做了什么事情。这将是一个js高级系列第一篇&#xff0c;这将会为之后的闭包&#xff0c;作用域链&#xff0c;原型等让人迷惑的知识打基础。感…

使用IDEA 进行 安卓开发

使用IDEA 进行 安卓开发 前言 以前使用Eclipse 进行 Android 开发&#xff0c;感觉对新手来说实在是太不友好了&#xff0c;又是Android SDK&#xff0c;有是配 gradle 等一系列的东西&#xff0c;对与新手而言&#xff0c;总是遗漏。或者版本对不上&#xff0c;一堆问题&…

.net core 读取配置的几种方式

json配置文件示例 {"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"Account": {"username": "zhangsan","password":&quo…

arXiv2022-12 | FLIP:Scaling Language-Image Pre-training via Masking

凯明团队新作。 论文地址&#xff1a;https://arxiv.org/abs/2212.00794 一、问题 Even using high-end infrastructures, the wall-clock training time is still a major bottleneck hindering explorations on scaling vision-language learning. 即使使用高端的基础设施…

人工智能:声纹相关基础概念介绍

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️个人荣誉&#xff1a; 数据库领域优质创作者&#x1f3c6;&#x…