数据结构——堆的介绍以及应用

news2024/11/15 20:36:17

前言:对于数据结构而言,大多存在着对应的物理结构和逻辑结构,而我们一开始介绍的顺序表,链表,栈,队列等的物理结构和逻辑结构还是比较类似的。今天要介绍的堆则有所不同,其物理结构是数组,而逻辑结构是完全二叉树,并且其应用还是比较重要的,我们需要好好掌握其中的知识内容。

注:学习本节知识的前提是了解顺序表结构以及二叉树的基本知识。


目录

一:堆的简单介绍

(1) 概念

(2) 性质

(3) 结构

二:堆的实现

(1) 基本框架的搭建

1. 测试函数(自己设置)

2. 堆的初始化

3. 数据的打印

4. 获取堆顶数据

5. 堆的销毁

6. 数据的交换

(2) 数据入堆的实现 (向上调整算法)

1. 思路

2. 图示

3. 代码实现

(3) 堆顶数据的删除 (向下调整算法)

1. 思路

2. 图示

​编辑3. 代码实现

三:堆的两个经典应用

(1) Top-K问题

1. 简单介绍

2. 实现思路

3. 代码实现

(2) 堆排序

1. 简单介绍

2. 实现思路

3. 图示

4. 代码实现

四:完整代码展示

(1) Test.c

(2) Heap.h

(3) Heap.c


一:堆的简单介绍

(1) 概念

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

(2) 性质

1. 堆中某个节点的值总是不大于或不小于其父节点的值

(eg:  小堆:K(i)<= K(2i+1) 且 K(i)<=K(2i+2)    大堆: K(i)>= K(2i+1) 且 K(i)>=K(2i+2) );
2. 堆总是一棵完全二叉树(具体见下)。

(3) 结构


二:堆的实现

(1) 基本框架的搭建

1. 测试函数(自己设置)

#include "Heap.h"
void HeapTest1(int* arr,int n)//堆相关接口函数的测试
{
	HP hp;//定义一个堆
	HeapInit(&hp);//初始化堆

	int i = 0;
	for (i = 0;i < n;i++)
	{
		HeapPush(&hp, arr[i]);//将数组元素入堆
	}
	HeapPrint(&hp, n);//打印堆
	printf("%d\n", HeapTop(&hp));//打印堆顶数据
	HeapPop(&hp);
	HeapPop(&hp);
	HeapPop(&hp);//删除三次堆内的数据
	HeapPrint(&hp, (&hp)->size);//删除后再打印
	printf("%d\n", HeapTop(&hp));//打印堆顶数据
}
int main()
{
	int arr[] = { 70,33,28,40,55,60,59,20,44 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	HeapTest1(arr, sz);//测试堆的一些接口函数
	return 0;
}

2. 堆的初始化

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

3. 数据的打印

void HeapPrint(HP* hp,int n)
{
	int i = 0;
	for (i = 0;i < n;i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}

4. 获取堆顶数据

HPDataType HeapTop(HP* hp)
{
	assert(hp);
	return hp->a[0];
}

5. 堆的销毁

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

6. 数据的交换

void Swap(HPDataType* px, HPDataType* py)
{
	HPDataType tmp = *px;
	*px = *py;
	*py = tmp;
}

如果实现了顺序表的结构,完成大致框架难度就不大,接下来就要实现堆这个数据结构与顺序表不同之处。


(2) 数据入堆的实现 (向上调整算法)

入堆其实就是将一个数据加在数组最后一个元素后面,依旧需要扩容,插入数据。

void HeapPush(HP* hp, HPDataType x)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		int newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		hp->a = tmp;
		hp->capacity = newCapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	//AdjustUp(hp->a,hp->size - 1);//向上调整,建堆(需要传递最后一个元素的下标)
}

但是,由堆的第一个性质我们知道堆是有特殊结构的,那么在插入数据之后堆的结构很有可能会被破坏(使得不是一个堆了),所以入堆操作不仅仅需要将数据插入数组,我们还要对插入数据之后的数组进行调整,使保证它的堆结构,那么就要涉及下面的向上调整算法了。

向上调整算法

1. 思路

要实现向上调整算法,首先要明确其思路,有以下三点

(1)首先我们要清楚向上调整的对象:

从插入数据的第一个父节点开始,一直到祖先节点的一条路径上的所有父节点

(2)其次是向上调整的条件:
当子节点小于或大于(由堆的大小决定)对应的父节点时,就进行交换调整

(3)最后是要明确停止调整的条件,有两个条件会停止向下调整:

1. 当祖先节点作为子节点时;   2. 调整路径上遇到子节点与父节点不满足对应大小关系时

2. 图示

3. 代码实现

void AdjustUp(HPDataType* a,int child)//向上调整算法
{
	int parent = (child - 1) / 2;  //利用到了二叉树中以知父节点求子节点的公式
	//当满足两个条件就停止向上调整
	while (child > 0)  //1.child移动到祖先节点
	{
		//相当于子节点小于父节点,开始向上调整
		if (a[child] < a[parent]) //这里小于就调表示原本是小堆, 如果原本是大堆, 改成大于号即可
		{
			Swap(&a[child], &a[parent]); //交换
			child = parent; //child向上移动
			parent = (child - 1) / 2;  //继续计算对应的父节点
		}
		else
		{
			break;   //2.子节点不小于(或大于)父节点(交换关系不满足)
		}
	}
}

(3) 堆顶数据的删除 (向下调整算法)

此出的pop函数是对堆顶数据的删除(即数组的第一个元素),但在删除之后,堆的结构会大变,使其不再是一个堆,所以在删除时我们要想到一定的方法使得既能删除堆顶数据,又能够保持堆的结构不变。

解决方法:

1. 将堆顶的数据跟数组最后一个数据交换

2. 然后删除数组最后一个数据

3. 再进行向下调整算法

完成这三步就可以很好地实现堆顶数据的删除了。那这里又要涉及一个向下调整算法的实现,具体见下。

void HeapPop(HP* hp)//删除堆顶元素
{
	assert(hp);
	assert(hp->size > 0);
	Swap(&hp->a[0], &hp->a[hp->size - 1]); //先交换
	hp->size--; //删除最后一个数据
	AdjustDown(hp->a, hp->size, 0); //调堆:从堆顶开始向下调整(需要传递数组元素个数,堆顶元素下标)
}

向下调整算法:

1. 思路

向下调整算法与向上调整算法类似,完成以下两步即可:

(1) 调整过程的实现

1. 将交换后新的祖先节点作为父节点,比较它两个子节点的大小,原来是大堆,就选择较大的子节点,小堆就选择较小的子节点

2.  将父节点与选择好的子节点进行比较,满足需求的大小关系就进行交换

(3) 调整停止的条件:
1. 所得新的子节点下标超过数组下标

2. 调整过程中子节点与父节点不满足交换的大小关系

2. 图示

3. 代码实现

void AdjustDown(HPDataType* a, int n, int parent)//向下调整
{
	int child = parent * 2 + 1; //以知父节点算子节点的公式
	//当满足两个条件就停止向下调整
	while (child < n)//1.下标超过数组元素个数
	{
		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//2.父子节点大小关系不符合时
		{
			break;
		}
	}
}

三:堆的两个经典应用

(1) Top-K问题

1. 简单介绍

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

2. 实现思路

Top-K问题的解决方法有很多,其中最好的一种就是利用堆的结构特性,使用堆解决。具体思路(eg:选N个数中最大的K个数):

(1) 要选N个数中最大的K个数,先用N个数的前K个数建立小堆

(2) 再将剩余的N-K个数与堆顶的元素进行比较,如果比堆顶元素大就进行替换,再调整堆维持原来的小堆结构(这样可以将更大的数沉在二叉树结构的下部)

最后堆中剩余的K个数就是要选的k个数。

3. 代码实现

//(1)topK问题:选取N个元素中最大(小)的K个
//解决方法:选小建大堆,选大建小堆,剩余元素再与堆顶元素进行比较 
void TopKPrint(HPDataType* a, int n, int k)
{
	HP hp;//定义堆
	HeapInit(&hp);

	//1.选大数建小堆,将n个数的前k个数建成小堆
	for (int i = 0;i < k;i++)
	{
		HeapPush(&hp, a[i]);//将前k个数插入堆中,建堆
	}

	//2.将n个数中剩余的n-k个数依次与堆顶的元素进行比较,更大就入堆,再调整堆
	for (int i = k;i < n;i++)
	{
		if (a[i] > HeapTop(&hp))
		{
			HeapPop(&hp);//删除堆顶元素
			HeapPush(&hp, a[i]);//再入堆(push函数中涉及了调堆的过程)
		}
	}
	//3.打印
	HeapPrint(&hp, k);
	HeapDestroy(&hp);
}

void TesttopK()//选出n个数中最大的10个数(测试)
{
	int n = 10000;
	int* a = (int*)malloc(sizeof(int) * n);
	if (a == NULL)
	{
		printf("realloc fail\n");
		exit(-1);
	}
	srand(time(0));
	for (int i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000; //产生随机的n个数一定比1000000小
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;
	TopKPrint(a, n, 10);
}

int main()
{
    TestTopK();
    return 0;
}

(2) 堆排序

1. 简单介绍

 堆排序其实就是一种利用堆的结构进行的排序算法。

2. 实现思路:

(1) 排降序建小堆(利用类似二叉树子树递归的思想)

(2) 将堆顶元素与堆(数组)的最后一个元素进行交换(此时最后一个元素就是最小的一个)

(3) 不将最后一个元素看成堆内的元素,调整堆

(4) 循环进行上述操作,直至排序完毕

3. 图示

 

 4. 代码实现

//堆排序,排降序建小堆 (1.建堆 + 2.调堆)
void HeapSort(HPDataType* a, int n)
{
	//1.将数组内元素从最后一个非叶子节点开始一直向前,将各个子树利用向下调整算法构建成堆   O(N)
	int i = 0;
	//(n-1-1)/2表示最后一个非叶子节点的下标,根节点也要执行(i=0不可少)
	for (i = (n - 1 - 1) / 2;i >= 0;i--)
	{
		AdjustDown(a, n, i);
	}
	//2.利用堆删除的思想,将堆顶元素(最小)与最后一个元素进行交换,然后不将最后一个元素看成堆内元素
	//再通过向下调整,选出次小的一个,继续交换,循环执行,直到排到第一个元素为止   O(N*logN)
	for (i = n - 1;i > 0;i--)
	{
		Swap(&a[0], &a[i]);
		AdjustDown(a, i, 0);//注意堆的最后一个元素由i的变化而变化,即实参不是n-1而是i!
	}
}

四:完整代码展示

(1) Test.c

#include "Heap.h"
void HeapTest1(int* arr,int n)
{
	HP hp;//定义一个堆
	HeapInit(&hp);//初始化堆

	int i = 0;
	for (i = 0;i < n;i++)
	{
		HeapPush(&hp, arr[i]);//将数组元素入堆
	}
	HeapPrint(&hp, (&hp)->size);//打印堆内数据
	printf("%d\n", HeapTop(&hp));//打印堆顶数据
	HeapPop(&hp);
	HeapPop(&hp);
	HeapPop(&hp);//删除三次堆内的数据
	HeapPrint(&hp, (&hp)->size);//删除后再打印
	printf("%d\n", HeapTop(&hp));//打印堆顶数据
}

void TesttopK()//选出n个数中最大的10个数
{
	int n = 10000;
	int* a = (int*)malloc(sizeof(int) * n);
	if (a == NULL)
	{
		printf("realloc fail\n");
		exit(-1);
	}
	srand(time(0));
	for (size_t i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000;
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;
	TopKPrint(a, n, 10);
}

int main()
{
	int arr[] = { 70,33,28,40,55,60,59,20,44 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0;i < 9;i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	HeapTest1(arr, sz);//测试堆的一些接口函数
	TesttopK();//测试topK问题
	HeapSort(arr, sz);//堆排序
	for (int i = 0;i < 9;i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

(2) Heap.h

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

//定义堆内数据的数据类型
typedef int HPDataType;

//定义一个数组结构作为堆的物理结构(堆的实际结构是一颗完全二叉树)
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
} HP;

//接口函数的定义
void HeapInit(HP* hp);//初始化
void HeapPrint(HP* hp,int n);//打印
void HeapPush(HP* hp, HPDataType x);//向堆内插入数据,需要利用向上调整算法保证堆的性质
void HeapPop(HP* hp);//删除堆顶元素,需要利用向下调整算法保证堆的性质
void AdjustDown(HPDataType* a, int n, int parent);//向下调整算法
void AdjustUp(HPDataType* a, int child);//向上调整算法
HPDataType HeapTop(HP* hp);//获取堆顶数据
void HeapDestroy(HP* hp);//销毁堆

//堆的应用
void TesttopK();//测试topK问题
void TopKPrint(HPDataType* a, int n, int k);//topK问题的实现
void HeapSort(HPDataType* a, int n);//堆排序的实现

(3) Heap.c

#include "Heap.h"

void Swap(HPDataType* px, HPDataType* py)
{
	HPDataType tmp = *px;
	*px = *py;
	*py = tmp;
}

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

void HeapPrint(HP* hp,int n)
{
	int i = 0;
	for (i = 0;i < n;i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}

void AdjustUp(HPDataType* a,int child)//向上调整算法
{
	int parent = (child - 1) / 2; //利用到了二叉树中以知父节点求子节点的公式
	//当满足两个条件就停止向上调整
	while (child > 0)//1.child移动到祖先节点
	{
		//相当于子节点小于父节点,开始向上调整
		if (a[child] < a[parent])//这里小于就调表示原本是小堆, 如果原本是大堆, 改成大于号即可
		{
			Swap(&a[child], &a[parent]);//交换
			child = parent;//child向上移动
			parent = (child - 1) / 2;//继续计算对应的父节点
		}
		else
		{
			break;//2.子节点不小于(或大于)父节点
		}
	}
}

void AdjustDown(HPDataType* a, int n, int parent)//向下调整
{
	int child = parent * 2 + 1; //以知父节点算子节点的公式
	//当满足两个条件就停止向下调整
	while (child < n)//1.下标超过数组元素个数
	{
		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//2.父子节点大小关系不符合时
		{
			break;
		}
	}
}

void HeapPush(HP* hp, HPDataType x)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		int newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		hp->a = tmp;
		hp->capacity = newCapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	AdjustUp(hp->a,hp->size - 1);//向上调整
}

void HeapPop(HP* hp)//删除堆顶元素
{
	assert(hp);
	assert(hp->size > 0);
	Swap(&hp->a[0], &hp->a[hp->size - 1]); //先交换
	hp->size--; //删除最后一个数据
	AdjustDown(hp->a, hp->size, 0); //从堆顶开始向下调整(调堆)
}

HPDataType HeapTop(HP* hp)
{
	assert(hp);
	return hp->a[0];
}

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

//堆的两个应用:  (1)topK问题   (2)堆排序

//(1)topK问题:选取N个元素中最大(小)的K个
//解决方法:选小建大堆,选大建小堆,剩余元素再与堆顶元素进行比较 
void TopKPrint(HPDataType* a, int n, int k)
{
	HP hp;//定义堆
	HeapInit(&hp);

	//1.选大数建小堆,将n个数的前k个数建成小堆
	for (int i = 0;i < k;i++)
	{
		HeapPush(&hp, a[i]);//将前k个数插入堆中,建堆
	}

	//2.将n个数中剩余的n-k个数依次与堆顶的元素进行比较,更大就入堆,再调整堆
	for (int i = k;i < n;i++)
	{
		if (a[i] > HeapTop(&hp))
		{
			HeapPop(&hp);//删除堆顶元素
			HeapPush(&hp, a[i]);//再入堆(push函数中涉及了调堆的过程)
		}
	}
	//3.打印
	HeapPrint(&hp, k);
	HeapDestroy(&hp);
}

//(2)堆排序,排降序建小堆,再排序;排升序建大堆,再排序  (1.建堆 + 2.调堆)
void HeapSort(HPDataType* a, int n)
{
	//1.将数组内元素从最后一个非叶子节点开始,将各个子树利用向下调整算法构建成堆   O(N)
	int i = 0;
	//(n-1-1)/2表示最后一个非叶子节点的下标,根节点也要执行(i=0不可少)
	for (i = (n - 1 - 1) / 2;i >= 0;i--)
	{
		AdjustDown(a, n, i);
	}
	//2.利用堆删除的思想,将堆顶元素(最小)与最后一个元素进行交换,然后不将最后一个元素看成堆内元素
	//再通过向下调整,选出次小的一个,继续交换,循环执行,直到排到第一个元素为止   O(N*logN)
	for (i = n - 1;i > 0;i--)
	{
		Swap(&a[0], &a[i]);
		AdjustDown(a, i, 0);//注意堆的最后一个元素由i的变化而变化,即实参不是n-1而是i!
	}
}

总结:

在本次分享的内容中,最重要的就是其中的向上向下调整算法,理解这两步,其他只要明白了思路实现起来也简单,那这次的分享就到这里结束,希望对各位有些许帮助,再见。

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

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

相关文章

JS前端基于canvas给图片添加水印,并下载带有水印的图片

基于canvas给图片添加水印实现效果图图片添加水印的步骤1.获取图片路径&#xff0c;将图片转换为canvas2.canvas画布上绘制文字水印3.水印绘制完成后&#xff0c;将canvas转换为图片格式4.水印绘制完成后&#xff0c;将canvas下载为图片完整代码总结1、在utils.js 封装添加水印…

POE交换机全方位解读(中)

POE供电距离到底怎么算 只针对符合IEEE802.3af/at 标准PoE设备 ① 网线对供电距离的影响 首先我们先来看下表IEEE802.af和IEEE802.3at标准中对Cat5e网线要求&#xff1a; 说明&#xff1a;Type 1 value和Type 2 value 分别指IEEE802.3af和IEEE802.3at的要求。 从表中可以看出&a…

PCB电路板单面板和双面板的区别和共同点

PCB电路板可以分为单面板、双面板和多面板&#xff0c;我们常用的主要是单面板和双面板&#xff0c;那么单面板和双面板有哪些区别呢&#xff1f;在了解二者区别前&#xff0c;沐渥小编先给大家介绍一下什么是单面板和双面板。 单面板是指单面的线路板&#xff0c;元器件在一面…

如何实现报表集成?(四)——权限集成

在上一篇&#xff0c;我们介绍了报表工具的资源集成&#xff0c;基本知道了报表工具链接、模块、页面和移动端如何实现集成。 这一篇&#xff0c;我们看下如何做权限集成。使用第三方系统的资源权限验证 实际上往往存在多个系统需要统一权限认证&#xff0c;用户要求将某个系统…

PixelLib图像分割

文章目录前言一、PixelLib依赖安装二、实例模型训练前言 图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。它是由图像处理到图像分析的关键步骤。 传统的图像分割方法主要分以下几类&#xff1a;基于阈值的分割方法、基于区域的分割方…

Mybatis核心原理梳理

文章目录Mybatis的简单使用Mybatis组件名词介绍Mybatis主要工作流程Mybatis如何控制事务Mybatis中事务的生命周期一二级缓存分别如何生效一二级缓存分别如何失效一级缓存的实体可能会被修改Mybatis中的已经存在PooledDataSource连接池为啥还选择Durid等为啥连接close之后被没有…

如何获取 WWDC 视频对应的官方源代码?

零 概览 每年的 WWDC&#xff08;The Apple Worldwide Developers Conference&#xff09; 是 Apple 开发者的盛大节日&#xff0c;我们可以从 WWDC 海量官方视频中学到大量的知识。 不过&#xff0c;有些视频仅包含一些“惨不忍睹”&#xff08;由于网络质量差等原因&#…

【C++】C++ 入门(二)(引用)

目录 一、前言 二、引用 1、引用的概念 2、引用特性 3、使用场景 3.1、做参数 3.2、做返回值 4、传值、传引用效率比较 值和引用作为参数的性能比较 值和引用作为返回值类型的性能比较 5、常引用 6、引用和指针的区别 一、前言 上一篇文章我们讲解了 C 的命名空间…

IDEA快速生成实体类(加注释)

步骤&#xff1a; 1、点击右侧的datesource图标&#xff0c;要是没有该图标&#xff0c;请去自行百度 2、点击 号 3、选择 datasource 4、选择 mysql 1、填写一个连接名&#xff0c;随便填什么都行 2、不用选择&#xff0c;默认就行 3、填写数据库连接的 IP地址&#xff0c;比…

Android 时间工具类

最近总结了一下时间相关的用法&#xff0c;如下。 1、日期转换为字符串 默认"yyyy-MM-dd HH:mm:ss" 2、任意类型日期字符串转时间 3、获取当前对应格式的日期 4、获取当前对应格式的日期 默认"yyyyMMddHHmmssSSS" 5、计算该天是星期几 6、获取星期几…

XSS - 进阶篇(蓝莲花的基本使用)

数据来源 本文仅用于信息安全的学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 xss漏洞接收平台-蓝莲花&#xff1a; 1&#xff09;下载并安装Phpstudy&#xff08;安…

分享157个ASP源码,总有一款适合您

ASP源码 分享157个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 157个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1_IF9pFQX4NM-kmJyIAGBQQ?pwdcb55 提取码&#x…

RBAC简介

RBAC BAC基于角色的访问控制&#xff0c;RBAC认为权限授权的过程可以抽象地概括为&#xff1a;Who是否可以对What进行How的访问操作 RBAC简介 基于角色的权限访问控制模型 在RBAC模型里面&#xff0c;有3个基础组成部分&#xff0c;分别是&#xff1a;用户、角色和权限。RB…

微信公众号小程序怎么做?

​微信公众号小程序在当下已经成为人们日常生活中不可或缺的工具&#xff0c;在用户体验方面也做得很好&#xff0c;不仅可以实现沟通和交流&#xff0c;还可以通过微信公众号进行在线预约服务。那么关于微信公众号小程序怎么做&#xff0c;下面就给大家说说。 1、注册微信公众…

Cadence PCB仿真 使用 Allegro PCB SI 元器件端口设置的PDN分析功能介绍图文教程

🏡《总目录》   🏡《分目录》 目录 1,概述2,启动方法3,功能介绍3.1,元器件设置列表(Device)3.2,端口设置列表(Ports)4,总结1,概述 在进行PDN分析时需要对电源网络涉及到的所有元器件的指定端口的参数进行配置。本文介绍PDN网络元器件端口设置的功能。 2,启动…

【寒假day3】leetcode刷题

&#x1f308;一、选择题 ❤第1题&#xff1a;关于重载函数,&#xff08; &#xff09;说明是正确的。 A: 函数名相同&#xff0c;参数类型或个数不同 B: 函数名相同&#xff0c;返回值类型不同 C: 函数名相同&#xff0c;函数内部实现不同 D: 函数名称不同答案&#xff1a…

数据挖掘-特征选择方法:方差过滤,相关性过滤

目录特征选择1、Filter过滤法方差过滤1&#xff0c;消除方差为0的特征2&#xff0c;只留下一半的特征3&#xff0c;特征是二分类时2、相关性过滤法2.1 卡方过滤2.2 F检验2.3 互信息法3、 Embedded嵌入法4、Wrapper包装法5、总结特征选择 数据预处理完成后&#xff0c;就进入特…

常见网络报文数据包格式

当我们应用程序用TCP传输数据的时候&#xff0c;数据被送入协议栈中&#xff0c;然后逐个通过每一层&#xff0c;知道最后到物理层数据转换成比特流&#xff0c;送入网络。而再这个过程中&#xff0c;每一层都会对要发送的数据加一些首部信息。整个过程如下图。以太网帧格式以太…

Mybatis框架(二)再遇Mybatis之Mybatis配置文件与映射文件

本文是本人专栏【Java开发后端系列框架】里的文章&#xff0c;文章根据各框架官网与网上资料加上本人工作经验&#xff0c;进行修改总结发布在这个专栏&#xff0c;主要目的是用于自我提升&#xff0c;不用于获取利益。如果系列文章能到帮到您本人将感到荣幸&#xff0c;如果有…

2023年3月5日DAMA-CDGA/CDGP数据治理认证考试报名入口

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…