【数据结构】堆的实现(简单易懂,超级详细!!!)

news2025/7/21 4:24:50

目录

1、堆的概念及结构

概念

规律 

2、堆的实现

2.1结构设计

2.2接口实现

2.3 初始化 

2.4堆的向下调整算法

主要思想

涉及问题

代码实现

2.5建堆

思想

代码实现

建堆的时间复杂度

2.6 堆的向上调整算法

主要思想

​涉及问题

代码实现

2.7 插入 

2.8删除堆顶元素

2.9取堆顶元素

2.10 堆的大小

2.11 堆是否为空

2.12 堆销毁

3、源代码

1. 头文件

2. 接口函数

3.测试文件


1、堆的概念及结构

概念

        堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

性质1:堆中某个结点的值总是不大于或不小于其父结点的值;
性质2:堆总是一棵完全二叉树。

        将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

        如下,小堆父亲节点总是小于孩子节点,所以小堆的根节点一定是最小的。而大堆父亲节点总是大于孩子节点,所以大堆的根节点一定是最大的

规律 

        既然堆的逻辑结构是完全二叉树,那么它就同样具有完全二叉树的性质。

         对于完全二叉树,若从上至下、从左至右编号,以根节点为0开始,则编号为i的节点,其左孩子节点编号为2i+1,右孩子节点编号为2i+2,父亲节点为 (i-1) / 2。 这个规律非常重要!!!

         我们来验证一下:

        一棵节点个数为N的完全二叉树,其高度为 h = log 2 (N+1)   ,(2为底数)。

        推导过程如下:

        我们来做个简单的题目:

1.下列关键字序列为堆的是?()

A 100,60 ,70 ,50 ,32 ,65
B 60 ,76 ,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

        在这里,由于堆的存储结构数组,且堆是完全二叉树,可以直接如下图A,把后面的移过去,每一层放置可以放的最大节点个数,直到数组内元素放置完。很容易就可以看出 A 是堆。

2、堆的实现

2.1结构设计

        如下,用数组存储数据,实际存储结构是数组。

typedef int HeapDataType;

typedef struct Heap
{
	HeapDataType* a;
	int size;      //已有数据个数
	int capacity;  //容量
}HP;

2.2接口实现

2.3 初始化 

        如下,非常简单,就不多说了。

//初始化
void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = php->size = 0;
}

2.4堆的向下调整算法

主要思想

        现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是同一种堆,才能调整

int array[] = {27,15,19,18,28,34,65,49,25,37};

        我们可以通过画图构建这棵树,然后走一遍向下调整的过程,如下图 (a)是构建的完全二叉树,从(b)中明显看出,根节点的左右子树都是小堆,所以可以调整。而横线下方就是调整的详细步骤,每一次,根节点都和左右孩子节点中,较小的那个孩子交换 :

涉及问题

        在此过程中,我们要明确几个问题:
1、如何找到孩子节点?

2、如何判断该孩子节点是否存在?(比如值为 28 的节点,不存在右孩子)

3、边界条件?什么时候停止调整?

        由于堆逻辑结构是一颗完全二叉树,所以要利用上面完全二叉树的规律。

        1、  我们用 parent、leftchild、rightchild,分别代表父亲节点、左右孩子节点的下标(物理结构是数组)。 根据规律得:  leftchild = parent * 2 + 1 ,rightchild = parent * 2 + 2 。并且任一节点 i ,其父亲节点 parent = (i-1)/2

        2、由于数组一共有n个元素,其下标最大是 n-1,所以孩子节点的下标只要 小于 n ,就是存在的。

        3、首先,由于其子树是小堆,所以如果调整到某个节点,其父亲节点比孩子节点都小,就不需要调整了,那么已经构建成功(相当于做了一半就成功)。

        其次,就是调整到最后一步的情况,如下。每次调整,改变的都是代表父亲节点孩子节点 下标的那几个变量。父亲节点的值和孩子节点的值交换之后,下一轮调整 父亲节点的下标就变成了上一轮孩子节点的下标孩子节点的下标变成当前 父亲节点的孩子节点中 小的那个的下标。所以孩子节点 > n,那么就代表调整到了最下面一层,不需要再调整了。

          小结论: 如上图,向下调整算法前提是,根节点的左右子树都是大堆或者都是小堆。然后调整之后的结果是,根节点调整到了下方,整棵树成了一个堆。    但是,我们深入思考一下,如果现在调整的根节点,也是某个节点的孩子节点呢? 那么,就能得到,向下调整算法实际上是调整的某一个节点 A,要这个节点的左右子树都是同种堆,调整之后,以原 A 位置的节点为根节点的树是一个堆。

        如下,从左边标红的地方开始向下调整,不用管其父亲节点等只需要知道标红节点的左右子树都是同种堆即可,然后调整得到右半边结果。(很重要,要理解!!!这是理解建堆算法的前提!!!

代码实现

        如下,由于一次只需要交换一个父亲节点和一个孩子节点,且孩子节点存储时是相邻的,所以用child 先表示 左孩子节点下标,然后循环内部和右孩子节点比较,如果右孩子节点存在且其值小于左孩子节点的值,那么child++++之后的child节点表示的是右孩子节点。 然后比较,需要交换就交换parent 和 child 代表的下标的内容,然后parent 和 child 更新;如果不需要交换,那么就是调整好的,break。

        传入三个参数,依次是指向堆的数据的指针,堆的长度,根节点。

void Swap(HeapDataType* a, HeapDataType* b)
{
	int c = *a;
	*a = *b;
	*b = c;
}

//向下调整
void AdjustDown(HeapDataType* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;  // 这里和链表里的长链表、短链表有异曲同工之妙,只不过这里只需要用到大的孩子
	while (child < n)
	{
		if (a[child] > a[child + 1] && child + 1 < n)   //这里child+1要<php->size ,不可以=,因为数据个数是比下标大1
		{
			child++;
		}

		if (a[parent] > a[child])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else // 父亲小于等于孩子,所以已经完成调整,直接break
		{
			break;
		}
	}
}

2.5建堆

思想

        建堆写在这里,相信聪明的你已经猜到,需要使用向下调整算法,要使用这个算法,就要有一个前提 —— 要调整的节点,左右子树都是同种堆。

        如下,方法一,我们从根节点开始调整,但是本身这棵树就是乱序的,没有哪一个节点的子树能保证是堆,更不要说是同种堆了。所以这种方法根本不可行。

        既然从一棵树的上方开始调整不行,那么我们就从下方开始调整,如方法二。但是这里开始调整的起始位置也很有讲究,是从最后一个节点开始吗? 显然不是,从上面的小规律已经得知,向下调整算法实际上是调整了开始的那个节点,而一棵树最后一个节点是叶子节点,叶子节点根本没有孩子节点,更不要说子树了,没有什么好调整的。所以我们要从有孩子节点的 节点开始调整。

        如下方法2 ,从圈出来的步骤 1 到步骤5 。步骤一中,值为37 的节点,左右子树都是堆,可以调整这个值为37的节点。后面依次也是。最后完成调整,建队成功。

代码实现

        如下,传入三个参数,依次是堆指针,数组,数组长度。  首先把开辟堆内存储数据的空间,然后把数组的值复制进去,然后建堆。

void HeapCreat(HP* php, HeapDataType* a, int len)
{
	assert(php);
	assert(a);
	php->a = (HeapDataType*)malloc(sizeof(HeapDataType) * len);
	if (php->a == NULL)
	{
		perror("malloc fail::");
		exit(-1);
	}
	memcpy(php->a, a, sizeof(HeapDataType) * len);
	php->size = php->capacity = len;

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

}

建堆的时间复杂度

        由于建堆是从倒数第二行开始,所以 是 h-1 层开始计数。最终结果约等于 N 。所以向下调整算法建堆的时间复杂度是O(N)。

2.6 堆的向上调整算法

主要思想

        向上调整算法是从节点 n 开始,调整 节点n 到根节点 之间的路径上的节点(任一节点到根节点之间的路径唯一)。其结果是,将这条路径上,最大或者最小的节点,放到根节点位置。这个算法的前提是:这条路径,除了节点n,其余节点要满足堆的特性,即有序。(自己用文字总结的,可能存在一些不标准的地方,欢迎指正!)

        比如,现有一个数组,其逻辑结构是一个堆,新加一个元素再数组末尾,并且使用向上调整算法使其保持逻辑结构依然是一个堆。

int array[] = {12,15,19,18,28,34,65,49,25,37};

新增 6 放在数组末尾。

          如下,不难看出,向上调整算法的路径是: 6 — 28 — 15 — 12 。而除了6 所代表的节点,这条路径上,从上往下看,是升序的,所以最后结果是,这条路径上的最小值到了根节点的位置。

涉及问题

         这里的问题无非和向下调整算法类似。找父亲节点也不难,最主要的就是边界条件,即如何确定循环结束条件。通过上面的图片可以看出, child = 0 就结束了,所以控制条件如下面代码所示。

代码实现

        如下,传入两个参数,一个是指向堆的数据的指针,一个是要调整的孩子节点。

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

         此外,也可以通过向上调整算法建堆,当然了,用该算法,要从上面往下建堆,而不是从下往上。 从根节点到最后一个节点依次调整,这样就可以保证,每一条路径,除了n节点之前的节点,都是有序的。如下:

         核心代码如下,前面开辟空间和复制什么的,和向下调整建堆一摸一样,就不写了。

for (int i = 1;i < n;i++)
{
	AdjustUp(a, i);
}

2.7 插入 

        由于存储结构是数组,所以插入要考虑是否扩容。

void HeapPush(HP* php, HeapDataType x)
{
	assert(php);
	//扩容
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HeapDataType* ptr = (HeapDataType*)realloc(php->a, sizeof(HeapDataType) * newcapacity);
		if (ptr == NULL)
		{
			perror("realloc fali::");
			exit(-1);
		}
		php->a = ptr;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);
}

2.8删除堆顶元素

        把堆尾元素放到堆顶元素,然后去除堆尾元素(这里直接size--),再向下调整即可。因为原本就是一个堆,现在堆顶元素变了,所以直接向下调整。

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

2.9取堆顶元素

HeapDataType HeapTop(HP* php)
{
	assert(php);
	assert(!php->a);
	return php->a[0];
}

2.10 堆的大小

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

2.11 堆是否为空

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

2.12 堆销毁

//销毁
void HeapDestory(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

3、源代码

1. 头文件

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

typedef int HeapDataType;

typedef struct Heap
{
	HeapDataType* a;
	int size;      //已有数据个数
	int capacity;  //容量
}HP;

//初始化
void HeapInit(HP* php);
//插入
void HeapPush(HP* php, HeapDataType x);
//删除堆顶元素
void HeapPop(HP* php);
//销毁
void HeapDestory(HP* php);
//向上调整
void AdjustUp(HeapDataType* a, int child);
void AdjustDown(HeapDataType* a, int n);
//建堆
void HeapCreat(HP* php, HeapDataType* a, int len);
//打印
void Print(HP* php);
//取堆顶元素
HeapDataType HeapTop(HP* php);
int HeapSize(HP* php);
bool HeapEmpty(HP* php);

2. 接口函数

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"


//初始化
void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = php->size = 0;
}

//插入
void HeapPush(HP* php, HeapDataType x)
{
	assert(php);
	//扩容
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HeapDataType* ptr = (HeapDataType*)realloc(php->a, sizeof(HeapDataType) * newcapacity);
		if (ptr == NULL)
		{
			perror("realloc fali::");
			exit(-1);
		}
		php->a = ptr;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);
}



//销毁
void HeapDestory(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

void Swap(HeapDataType* a, HeapDataType* b)
{
	int c = *a;
	*a = *b;
	*b = c;
}

//向上调整
void AdjustUp(HeapDataType* a, int child)
{
	assert(a);
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

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

//向下调整
void AdjustDown(HeapDataType* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;  // 这里和链表里的长链表、短链表有异曲同工之妙,只不过这里只需要用到大的孩子
	while (child < n)
	{
		if (a[child] > a[child + 1] && child + 1 < n)   //这里child+1要<php->size ,不可以=,因为数据个数是比下标大1
		{
			child++;
		}

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

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

HeapDataType HeapTop(HP* php)
{
	assert(php);
	assert(!php->a);
	return php->a[0];
}

void HeapCreat(HP* php, HeapDataType* a, int len)
{
	assert(php);
	assert(a);
	php->a = (HeapDataType*)malloc(sizeof(HeapDataType) * len);
	if (php->a == NULL)
	{
		perror("malloc fail::");
		exit(-1);
	}
	memcpy(php->a, a, sizeof(HeapDataType) * len);
	php->size = php->capacity = len;

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

}


int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

3.测试文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

Test1()
{
	int arr[] = { 10,78,99,45,67,45,0,77,34 };
	HP hp;
	HeapInit(&hp);
	for (int i = 0;i < sizeof(arr) / sizeof(arr[0]);i++)
	{
		HeapPush(&hp, arr[i]);
	}
	HeapPop(&hp);
	Print(&hp);
}


Test2()
{
	int arr[] = { 10,78,99,45,67,45,0,77,34 };
	HP hp;
	HeapInit(&hp);
	HeapCreat(&hp, arr, sizeof(arr) / sizeof(arr[0]));
	Print(&hp);
}



int main()
{
	//Test1();
	//Test2();
	return 0;
}

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

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

相关文章

【HDU No. 2586】 树上距离 How far away ?

【HDU No. 2586】 树上距离 How far away &#xff1f; 杭电 OJ 题目地址 【题意】 有n 栋房屋&#xff0c;由一些双向道路连接起来。 每两栋房屋之间都有一条独特的简单道路&#xff08;“简单”意味着不可以通过两条道路去一个地方&#xff09;。人们每天总是喜欢这样问&a…

CUDA——向量化内存

许多 CUDA 内核受带宽限制&#xff0c;新硬件中触发器与带宽的比率增加导致更多带宽受限内核。 这使得采取措施缓解代码中的带宽瓶颈变得非常重要。 在本文中&#xff0c;我将向您展示如何在 CUDA C/C 中使用矢量加载和存储来帮助提高带宽利用率&#xff0c;同时减少执行指令的…

【附源码】计算机毕业设计JAVA疫情社区志愿者组织的资源管理平台

【附源码】计算机毕业设计JAVA疫情社区志愿者组织的资源管理平台 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#…

Rust权威指南配套手把手笔记

Rust权威指南配套手把手笔记 持续更新ing 共20章&#xff0c;110小节 P1 1.1 - 简介 06:46 P2 1.2 - 安装 Rust 03:18 P3 1.3 - Hello World 04:11 P4 1.4 - Hello Cargo 07:49 P5 2.1 - 猜数游戏&#xff1a;一次猜测 11:47 P6 2.2 - 猜数游戏&#xff1a;生成神秘数字 …

【强化学习论文合集】ICLR-2021 强化学习论文

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题。 本专栏整理了近几年国际顶级会议中,涉及强化学习(Rein…

Grad-CAM

其实还是关于yolo的 利用Grad-CAM解释目标检测框架 研究者研究了视觉物体检测器的可解释性问题。具体来说&#xff0c;研究者在YOLO目标检测器的示例中演示了如何将Grad-CAM集成到模型架构中并分析结果。最后展示了如何计算个体检测的基于归因的解释&#xff0c;并发现结果的归…

Biotin-PEG2-alkyne|紫外线可裂解生物素-二聚乙二醇-炔烃|提供光谱图

试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a; 紫外线可切割生物素-PEG2-炔烃含有紫外线可切割碎片(containsa UV cleavable Fragemnt)&#xff0c;试剂通过点击化学与含叠氮化物的分子反应。点击化学生物素标记试剂包含各种点…

深入浅出PyTorch——PyTorch可视化

1. 可视化网络结构 在复杂的网络结构中确定每一层的输入结构&#xff0c;方便我们在短时间内完成debug 1.1 使用print函数打印模型基础信息 使用ResNet18的结构进行展示 import torchvision.models as models model models.resnet18() print(model)#打印结果 ResNet((conv1)…

算法学习 | 深度优先搜索~一条道走到黑

目录 员工的重要性 图像渲染 岛屿的周长 被围绕的区域 岛屿数量 深度优先搜索(Depth First Search)&#xff1a;深度优先搜索属于图算法的一种&#xff0c;其过程主要是对每一个可能的分支路径深入到不能再深入到为止&#xff0c;而且每个节点只能访问一次。深度优先搜…

[毕业设计]机器学习的运动目标跟踪-opencv

目录 前言 课题背景和意义 实现技术思路 第一步&#xff1a;创建单目标追踪器 第二步&#xff1a;读取视频的第一帧 第三步&#xff1a;在第一帧中定位物体 第四步&#xff1a;初始化多目标追踪器 实现效果图样例 前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边…

leetcode 907. Sum of Subarray Minimums(子数组最小值的和)

所有子数组的最小值求和。 思路&#xff1a; 最容易想到的就是用DFS找出所有子数组&#xff0c;然后每个子数组找最小值&#xff0c;再求和。但显然不是最优的。 因为费尽心思找到了一堆子数组&#xff0c;它们的最小值竟然是相同的&#xff0c; 是不是有种直接用这个最小值乘…

Alkyne-PEG-Biotin,Alk-PEG-Biotin,炔烃-聚乙二醇-生物素试剂供应

英文&#xff1a;Alkyne-PEG-Biotin&#xff0c;Alk-PEG-Biotin 中文&#xff1a;炔烃-聚乙二醇-生物素 CAS编号&#xff1a;N/A 所属分类&#xff1a;Alkyne PEG Biotin PEG 分子量&#xff1a;可定制&#xff0c;生物素-聚乙二醇5-炔烃、生物素-PEG 20-炔烃 、Biotin-PEG…

HCIA 访问控制列表ACL

一、前言 ACL又称访问控制列表&#xff0c;其实这个东西在很多地方都有用&#xff0c;可能名字不太一样但原理和功能都差不太多&#xff0c;比如服务器、防火墙&#xff0c;都有类似的东西&#xff0c;功能其实也就是“过滤”掉不想收到的数据包。为什么不想收到一些数据包呢&…

C++ 测试框架 Gtest学习——qt版本

目录标题一、参考文档二、获取Gtest三、使用&#xff08;一&#xff09;qt项目导入Gtest&#xff08;二&#xff09;修改pro文件&#xff08;三&#xff09;一个简单的例子&#xff08;四&#xff09;EXPECT&#xff08;期望&#xff09;和ASSERT&#xff08;断言&#xff09;介…

ImportError: cannot import name ‘xxx‘ from ‘xxx‘关于python导包的问题

github clone下来的代码&#xff0c;在矩池云跑的好好的&#xff0c;在自己电脑跑却报错。 ImportError: cannot import name ‘helper’ from ‘utils’ (D:\anaconda\envs\TF2.1\lib\site-packages\utils_init_.py) 搜了网上&#xff0c;说加路径 import sys sys.path.appe…

力控关节机器人(关节扭矩传感器力控)

力控机器人本质上属于协作机器人中的一种&#xff0c;其每个关节都带有力矩传感器&#xff1b; [1] 广泛应用在工业、医疗、新零售领域或智能厨房行业。 Franka Emika&#xff1a; 力控机器人每个关节都带有力矩传感器 力矩传感器提供了一种提高机器人力控性能的途径。 更加…

[毕业设计]基于机器视觉的车辆速度检测与识别算法

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过和节省时间与精力投…

面板平滑转换回归(PSTR)分析案例实现

建模过程包括三个阶段&#xff1a;表述&#xff0c;估计和评估&#xff0c;本文帮助用户进行模型表述、估计&#xff0c;进行PSTR模型评估。 最近我们被客户要求撰写关于PSTR的研究报告&#xff0c;包括一些图形和统计输出。 在程序包中实现了集群依赖性和异方差性一致性检验…

电脑删除的照片怎么找回来?总结了四种方法

照片被删除似乎是常有的事情&#xff0c;如果是重要的照片被删了&#xff0c;想要办法恢复才是最重要的。而对于删除的照片您是如何恢复的呢&#xff1f;这里总结了几种恢复方法&#xff0c;根据自己的需要选择恢复方法&#xff0c;不出意料的话&#xff0c;按照下面的方法你将…

以分割栅格为例实现FME模板的方案优化

一、利用FME分割栅格 &#xff08;一&#xff09;问题的产生 对于FME使用者来说&#xff0c;利用FME完成栅格的批量分割是一件极为平常且容易的事情。只需要输入栅格和确定分割方案就可以实现利用FME对栅格数据的分割&#xff0c;再配合FME的“扇出”功能&#xff0c;就能够实…