【数据结构】长篇详解堆,堆的向上/向下调整算法,堆排序及TopK问题
文章目录
- 堆的概念
- 性质
- 图解
- 向上调整算法
- 算法分析
- 代码整体实现
- 向下调整算法
- 算法分析
- 整体代码实现
- 堆的接口实现
- 初始化堆
- 销毁堆
- 插入元素
- 删除元素
- 打印元素
- 判断是否为空
- 取首元素
- 实现堆
- 堆排序
- 创建堆
- 调整堆
- 整合步骤
- TopK问题
堆的概念
堆就是将一组数据所有元素按完全二叉树的顺序存储方式存储在一个一维数组
中,并满足树中每一个父亲节点都要大于其子节点称为大堆(树中每一个父亲节点都要大于其子节点称为小堆)。
性质
①对于大堆(大根堆)来说,堆的顶部也就是数组首元素一定是最大的元素
②对于小堆(小根堆)来说,堆的顶部也就是数组首元素一定是最小的元素
(这两点对于下面的堆排序来说十分重要)
此外,
堆总是一棵完全二叉树
,因为堆本身就是二叉树的一种顺序存储结构的实现模式
注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段
图解
通过图再去对比上面的概念和性质,理解会更加清晰
所谓的存储结构也就数据在内存中真实的存储情况,在一维数组中
而逻辑结构就是我们想象出来的,能够帮助我们理解并且通过这个也是根据二叉树中父节点和子节点之间的下标关系来确定的
①已知父亲节点求子节点
LeftChild = Parent * 2 + 1; //左孩子的节点下标
RightChild = Parent * 2 + 2; //右孩子的节点下标
②已知子节点求父节点
Parent = (Child - 1) / 2; //切记是减1之后再除以2
向上调整算法
向上调整算法主要在堆的插入和堆排序中应用最为广泛
算法分析
对于堆的插入,就是在数组的末尾进行数字的插入,并且在插入数据之后,我们仍要保证现有的结构仍然是一个堆!
如上图,是一个小堆
然后在数组的末尾插入了一个数字,即最后一个孩子节点,但是在插入之后,我们自身的堆结构发生了变化,所以我们必须对堆的结构进行调整.
不难发现,在最后插入一个数之后,其他子树仍然保持了小堆的性质(即父节点的值小于子节点),而正在需要调整的就是该子节点的’祖宗’这条线路,如上图红色箭头一步一步指向的位置,
而利用的公式就是Parent = (Child - 1) / 2;
把新插入的数和它的父节点作比较,如果这个新插入的数小于于父节点,那么就和父节点交换位置
在向上调整代码中,我们需要传入的参数是数组和插入的那个子孩子的节点的下标
void AdjustUp(HeapDataType* a, int child) //child是下标
在实际的不断向上调整中,我们需要用循环来实现代码,并且要合理的设置循环
while (child > 0) //不能设置为parent >= 0 { //Parent = (Child - 1) / 2, 通过这个公式因为parent永远都不可能小于零...}
时间复杂度 -------O(logN)
根据最坏情况来看(比如上图),数据多少层,我们就需要调整多少次,所以次数=高度h
再根据二叉树节点数量和高度的关系可知:
所以可以得到关系: 次数 = h = logN
所以时间复杂度就为:O(logN)
代码整体实现
算法既可以实现小堆也可以大堆,具体看你函数内部符号的控制
整体实现如下:
typedef struct HeapNode
{HeapDataType* a;int size;int capacity;
}HP;
void Swap(HeapDataType* p1, HeapDataType* p2)
{HeapDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
//循环写法
void AdjustUp(HeapDataType* a, int child) //child,parent是下标
{int parent = (child - 1) / 2;while (child > 0){//小堆:判断子节点和父亲结点的大小if (a[child] < a[parent])//大堆:if (a[child] > a[parent]){Swap(&a[child], &a[parent]);//交换孩子和父亲child = parent;parent = (child - 1) / 2;}else{break;}}
}//递归写法
void AdjustUp(HeapDataType* a, int child)
{int parent = (child - 1) / 2;if (child > 0){//小堆:if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;AdjustUp(a, child); //递归}else{return;}}else{return;}
}
向下调整算法
向上调整算法主要在堆的数据删除和堆排序中应用最为广泛
算法分析
对于上图根节点27来说,它的左右子树都是小堆,所以就需要将27不断向下调整,保证其整体还是一个小堆
由此可见,向下调整的前提是左右子树必须是堆
利用的公式就是
LeftChild = Parent * 2 + 1;
RightChild = Parent * 2 + 2;
在每一轮的调整中你都需要比较左右子节点的大小,比如上图就是对于27来说,15和17两个节点,15更小,所以就将15和27进行交换,然后对于19这个子树来说本身就是一个小堆,就可以不用管了,并且15本身也小于19,所以也符合小堆性质,然后继续对左边的子树进行如此的调整
在向下调整代码中,我们需要传入的参数是数组,数组大小和整棵树根节点的下标
void AdjustDown(HeapDataType* a, int size, int parent)
时间复杂度 -------O(logN)
根据最坏情况来看(比如上图),数据多少层,最坏的情况我们就需要向下调整多少次,所以次数=高度h, 再根据二叉树节点数量和高度的关系可知:
所以可以得到关系: 次数 = h = logN
所以时间复杂度就为:O(logN)
整体代码实现
typedef int HeapDataType;typedef struct HeapNode
{HeapDataType* a;int size;int capacity;
}HP;
//转换
void Swap(HeapDataType* p1, HeapDataType* p2)
{HeapDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
//循环写法
void AdjustDown(HeapDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size)//这里的child是左孩子下标,之所以不是child+1<size,是因为{ //如果没有右孩子的话,这次循环将会终止,调整就会进行不彻底//小堆:if (child + 1 < size && a[child + 1] < a[child]){child++;}//小堆:if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;//交换位置}else{break;}}
}
//递归写法
void AdjustDown(HeapDataType* a, int size, int parent)
{int child = parent * 2 + 1;if (child < size) {//小堆:if (child + 1 < size && a[child + 1] < a[child]){child++; //如果右孩子小,那么下标就换成右孩子的下标}//小堆:if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;AdjustDown(a, size, parent); //递归}else{return;}}else{return;}
}
堆的接口实现
接下里,我将把堆的实现过程一步一步实现出来
初始化堆
void HeapInit(HP* hp)
{assert(hp);hp->a = NULL;hp->size = 0;hp->capacity = 0;
}
销毁堆
void HeapDestroy(HP* hp)
{assert(hp);free(hp->a);hp->a = NULL;hp->size = hp->capacity = 0;
}
插入元素
在尾部插入之后,要用
AdjustUp
函数向上调整形成堆
void HeapPush(HP* hp, HPDataType x)
{assert(hp);// 扩容if (hp->size == hp->capacity){int new = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType*tmp=(HPDataType*)realloc(hp->a, sizeof(HPDataType) * new);if (tmp == NULL){perror("realloc fail");exit(-1);}hp->a = tmp;hp->capacity = new;}hp->a[hp->size] = x;hp->size++;AdjustUp(hp->a, hp->size - 1);//插入之后向上调整堆
}
删除元素
一般指删除首元素,至于为什么HeapPop是删除首元素
根本就是因为要弹出尾元素很简单,直接size–不就完了
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);//首元素换到尾部来,然后再size----php->size;AdjustDown(php->a, php->size, 0);//再用AdjustDown函数再来调整堆
}
打印元素
void HeapPrint(HP* php)
{assert(php);for (size_t i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}
判断是否为空
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}
取首元素
HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}
实现堆
int main()
{HP hp;HeapInit(&hp);int a[] = { 20, 11, 28, 31, 111, 52, 34, 16, 7, 9 };for (int i = 0; i < sizeof(a) / sizeof(int); i++){HeapPush(&hp, a[i]);//插入}HeapPop(&hp);//把堆的首元素7删除删除HeapPrint(&hp);printf("堆顶元素:%d\n", HeapTop(&hp));HeapDestroy(&hp);return 0;
}
运行出来的结果:
堆排序
首先堆排有几个关键的步骤
①创建堆 ②调整堆
创建堆
创建堆的方式有两种①向上调整建堆 ②向下调整建堆
①首先我们来看第一种:向上调整建堆
这种方式的原理就是看作最开始堆中只有一个元素,从第一个元素开始就已经在向上调整,然后逐渐像堆中加入元素,随着一个一个元素的加入,也就形成了堆
图解如下:
而代码就是通过AdjustUp函数和一个for循环就可以完成上面步骤
//以前n个数建小堆
for (int i = 0; i < n; ++i)
{AdjustUp(a, i); //a为数组的指针
}
时间复杂度: O(n*logn)
分析:首先我们上面详细分析了AdjustUp()的时间复杂度为O(logn),然后循环了n此每次建堆,所以两者相乘,时间复杂度也就是 n*logn 了
②我们来看第一种:向下调整建堆 -----堆排序中最主要用到的方法
这种建堆的关键就是从倒数第一个非叶子节点开始调(也就是树中最后一个父节点),然后逐渐+1,就可以调整从最后一个父节点开始的每一棵树.
不难发现这样也符合向下调整的前提,即左右子树都是堆
那么我们如何找到最后一个节点的父亲?
就需要用到公式:Parent = (Child - 1) / 2;
图解如下
而代码就是通过AdjustDown函数和一个for循环就可以完成上面步骤
for (int i = ((n-1)-1)/2; i >= 0; --i)
//(n-1)是拿到树最后一个节点,然后再根据公式Parent = (Child - 1) / 2;
{AdjustDown(a, size, i);
}
时间复杂度:O(n)
根据下面的思路
因此建堆的时间复杂度为O(n)
总结😗:其实两种方式建堆之所以时间复杂度有差距,就是因为向下调整建堆可以看作忽略了最后一排的节点,直接从倒数第二排节点开始调整的,而在一棵满二叉树中最后一排的节点其实就占据了整棵树的二分之一,所以相当于向下调整比向上调整少经历了很多的节点
所以实际堆排序中我们更多的使用的是向下调整建堆,因此时间复杂度为O(n)
还有一点需要注意的是:如果你想要
升序
,即从小打大,需要建大堆
.
建了大堆之后,再交换首元素(最大的)和末尾元素,然后把最大的元素不算入堆中的元素,
再进行向下调整
如果你建小堆,当你拿到首元素(最小的元素之后),需要将数组依次前移然后重新建堆,每次都前移然后每次都建堆,时间复杂度直接拉满!!!
同理 如果你想要降序
,即从大打小,需要建小堆
.
调整堆
在堆建好之后,就可以开始调整堆了,比如你是升序,即从小打大,需要建大堆.
建了大堆之后,循环N次 ,进行N次调整堆操作,每一次调整 堆得到的最大值,将此值和数组的最后一个元素进行交换,交换减小数组的长度(最后被减小的那几个值不参与堆的调整),直到最后一个元素,就完成了堆的排序.
如下图,降序—小堆, 展示了其中一个调整过程
整合步骤
综合建堆和调整,完整的堆排序代码就出来了
void HeapSort(int* a, int n)
{// 建堆 (大堆)or (小堆)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; //换下来的最后一个数不计入堆中
}
升序建大堆,降序建小堆很重要!
TopK问题
最后我们再来解决一个堆在实际应用中很重要的Topk问题
通常这是在数据很大的情况下才会使用到的,如世界前500强,全省高考前十等等…
因为如果数据很大,你不可能在内存中创建一个这么大的数组来装下这么多数据,所以就要用topk问题的思路
举个简单的例子:
比如你有1000个数据,你要找前100个大的数据,那么你先随便拿100个数据(无论其大小多少)建小堆,然后另外900个数据依次与堆顶的最小数据进行比较,比它大就替换,然后再调整堆,这样1000个数据都参与了对比,对比了900次,900个最小的被拿走,剩下的100个一定是最大的,再进行堆排序
接下来用文件传输数据的形式进行举例
void CreateNDate()
{// 造数据int n = 10000000;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);
}void TestTopK(const char* filename, int k)
{// 1. 建堆--用a中前k个元素建堆FILE* fout = fopen(filename, "r");if (fout == NULL){perror("fopen fail");return;}int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("malloc fail");return;}for (int i = 0; i < k; i++){fscanf(fout, "%d", &minheap[i]);}// 前k个数建小堆for (int i = (k-2)/2; i >=0 ; --i){AdjustDown(minheap, k, i);}// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (x > minheap[0]){// 替换你进堆minheap[0] = x;AdjustDown(minheap, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", minheap[i]);}printf("\n");fclose(fout);
}int main()
{CreateNDate();TestTopK("data.txt", 5);return 0;
}
相关文章:
【数据结构】长篇详解堆,堆的向上/向下调整算法,堆排序及TopK问题
文章目录 堆的概念性质图解 向上调整算法算法分析代码整体实现 向下调整算法算法分析整体代码实现 堆的接口实现初始化堆销毁堆插入元素删除元素打印元素判断是否为空取首元素实现堆 堆排序创建堆调整堆整合步骤 TopK问题 堆的概念 堆就是将一组数据所有元素按完全二叉树的顺序…...
DAQ高频量化平台:引领Ai高频量化交易模式变革
近年来,数字货币投资市场掀起了一股热潮,以(BTC)为代表的区块链技术带来了巨大的商业变革。数字资产的特点,如无国界、无阶级、无门槛、高流动性和高透明度,吸引了越来越多的人们的关注和认可,创…...
vue3 element plus获取el-cascader级联选择器选中的当前结点的label值 附vue2获取当前label
各位大佬,有时我们在处理级联选择组件数据时,不仅需要拿到id,还需要拿到label名称,但是通常组件直接绑定的是id,所以就需要我们用别的方法去拿到label,此处官方是有这个方法的,具体根据不同的element 版本进行分别处理。 VUE3 e…...
Spring Boot常见面试题
Spring Boot简介 Spring Boot 是由 Pivotal 团队提供,用来简化 Spring 应用创建、开发、部署的框架。它提供了丰富的Spring模块化支持,可以帮助开发者更轻松快捷地构建出企业级应用。Spring Boot通过自动配置功能,降低了复杂性,同…...
分块矩阵求逆
另可参考Block matrix on Wikipedia2018.4.3 补充补充两个参考文献,都是对工科很实用的矩阵手册:D. S. Bernstein, Matrix mathematics: Theory, facts, and formulas with application to linear systems theory. Princeton, NJ: Princeton University …...
Python 文件写入操作
视频版教程 Python3零基础7天入门实战视频教程 w模式是写入,通过write方法写入内容。 # 打开文件 模式w写入,文件不存在,则自动创建 f open("D:/测试3.txt", "w", encoding"UTF-8")# write写入操作 内容写入…...
【Spring Boot系列】- Spring Boot侦听器Listener
【Spring Boot系列】- Spring Boot侦听器Listener 文章目录 【Spring Boot系列】- Spring Boot侦听器Listener一、概述二、监听器Listener分类2.1 监听ServletContext的事件监听器2.2 监听HttpSeesion的事件监听器2.3 监听ServletRequest的事件监听器 三、SpringMVC中的监听器3…...
JavaScript速成课—事件处理
目录 一.事件类型 1.窗口事件 2.表单元素事件 3.图像事件 4.键盘事件 5.鼠标事件 二.JavaScript事件处理的基本机制 三.绑定事件的方法 1.DOM元素绑定 2.JavaScript代码绑定事件 3.监听事件函数绑定 四.JavaScript事件的event对象 1.获取event对象 2.鼠标坐标获取…...
【入门篇】ClickHouse最优秀的开源列式存储数据库
文章目录 一、什么是ClickHouse?OLAP场景的关键特征列式数据库更适合OLAP场景的原因输入/输出CPU 1.1 ClickHouse的定义与发展历程1.2 ClickHouse的版本介绍 二、ClickHouse的主要特性2.1 高性能的列式存储2.2 实时的分析查询2.3 高度可扩展性2.4 数据压缩2.5 SQL支…...
【C++ Exceptions】异常处理的成本
最低成本 exception是C的一部分,编译器必须支持。即使从未使用任何异常处理机制,也必须付出一些空间放置某些数据结构,付出一些时间随时保持那些数据结构的正确性。 第二种成本:来自try语句块 避免非必要的try语句块。 粗略估计&a…...
API接口:原理、实现及应用
API(Application Programming Interface)接口是现代软件开发中不可或缺的一部分。它们提供了一种机制,使得不同的应用程序和服务可以相互通信,共享数据和功能。在这篇文章中,我们将探讨API接口的原理、实现及应用&…...
SpringBoot学习笔记(项目创建,yaml,多环境开发,整合mybatis SMM)
一、SpringBoot入门 1.1 SpringBoot概述 SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程。 Spring程序缺点:配置繁琐,依赖设置繁琐。SpringBoot程序优点:自动装配,…...
Linux内核分析:输入输出,字符与块设备 31-35
CPU 并不直接和设备打交道,它们中间有一个叫作设备控制器(Device Control Unit)的组件,例如硬盘有磁盘控制器、USB 有 USB 控制器、显示器有视频控制器等。这些控制器就像代理商一样,它们知道如何应对硬盘、鼠标、键盘、显示器的行为。 输入输出设备我们大致可以分为两类…...
Linux抓包工具tcpdump
一、介绍 tcpdump是一个抓包工具,用于实时捕获和分析网络流量。它通常在unix和linux操作系统上使用。tcpdump能够捕获流经网络接口的数据包,并显示或保存它们以供进一步分析。它提供有关每个数据包的详细信息,包括源IP地址、目标IP地址、使用…...
Qt消息机制和事件
事件 事件是由Qt或者系统在不同时刻发出的,当敲下鼠标,或者按下键盘,或者当窗口需要重新绘制的时候,就会发出一个相应的事件,一些操作由用户的操作发出,一些则由系统自动发出,如系统定时器事件等。 Qt 中所有事件类都继承于 QEvent。 在事件对象创建完毕后, Qt 将这个…...
LeetCode-739-每日温度-单调栈
题目描述:给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。 题目…...
MyBatis中当实体类中的属性名和表中的字段名不一样,怎么办
方法1: 在mybatis核心配置文件中指定,springboot加载mybatis核心配置文件 springboot项目的一个特点就是0配置,本来就省掉了mybatis的核心配置文件,现在又加回去算什么事,总之这种方式可行但没人这样用 具体操作&…...
Flutter框架和原理剖析
Flutter是Google推出并开源的跨平台开发框架,主打跨平台、高保真、高性能。开发者可以通过Dart语言开发Flutter应用,一套代码同时运行在ios和Android平台。不仅如此,flutter还支持web、桌面、嵌入应用的开发。flutter提供了丰富的组件、接口&…...
NFS:使用 Ansible 自动化配置 NFS 客户端服务端
考试顺便整理博文内容整理涉及使用 Ansible 部署 NFS 客户端和服务端理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃…...
IntelliJ IDEA使用——Debug操作
文章目录 版本说明图标和快捷键查看变量计算表达式条件断点多线程调试 版本说明 当前的IntelliJ IDEA 的版本是2021.2.2(下载IntelliJ IDEA) ps:不同版本一些图标和设置位置可能会存在差异,但应该大部分都差不多。 图标和快捷键…...
uniapp项目实践总结(十八)自定义多列瀑布流组件
导语:有时候展示图片等内容,会遇到图片高度不一致的情况,这时候就不能使用等高双列或多列展示了,这时候会用到瀑布流的页面布局,下面就一起探讨一下瀑布流的实现方法。 目录 准备工作原理分析实战演练案例展示 准备工…...
Ubuntu 22.04LTS + 深度学习环境安装全流程
一、 CUDA Toolkit 安装 1. 选择需要安装的版本(下载地址) 2. 选择自己的系统版本获取下载地址和安装指令 3. 运行安装指令进行安装 wget https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda_12.2.2_535.104.05_linux.run sudo sh cuda_12.2.…...
【lesson7】git的介绍及使用
文章目录 什么是gitgit的历史git使用在gitee上创建仓库git clone HTTPS地址git add .git add 文件名git commit “日志”git pushgit loggit rm 文件名git statusgit pull 什么是git git是版本控制器,那么什么是版本控制器呢? 下面讲个故事为大家讲解一…...
Keepalived+LVS高可用集群
目录 一、keepalived介绍: 二、keepalived工具介绍: (1)管理 LVS 负载均衡软件: (2)支持故障自动切换: (3)实现 LVS 负载调度器、节点服务器的高可用性&…...
AK 9.12 百度Java后端研发B卷 笔试
T1(博弈论) #include <bits/stdc.h>#define endl \nusing namespace std;typedef long long LL;const int N 1e5 10;int n, m, t;void solve() {cin >> n >> m; t n m - 2;if(t & 1) cout << "Yes" << endl;else cout <&l…...
使用Python和XPath解析动态JSON数据
JSON动态数据在Python中扮演着重要的角色,为开发者提供了处理实时和灵活数据的能力。Python作为一种强大的编程语言,提供了丰富的工具和库来处理动态JSON数据使得解析和处理动态JSON数据变得简单和高效。例如,使用内置的json模块,…...
记录一个iOS实现视频分片缓存拖拽快进不能播放的问题
代码现状 首先来看一下我们将视频数据塞给请求的代码 - (void)finishLoadingWithLoadingRequest:(IdiotResourceTask *)task {// printf("哈哈执行到这里执行到到这里\n");printf("哈哈哈数量数量%ld\n", self.taskList.count);//填充信息task.loadingRe…...
如何解决 503 Service Temporarily Unavailable?
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🐅🐾猫头虎建议程序员必备技术栈一览表📖: 🛠️ 全栈技术 Full Stack: 📚…...
keil报错:Flash Download failed - Could not load file‘..\..\Output\Template.axf
keil报错:Flash Download failed - Could not load file’…\Output\Template.axf,如下图所示: 原因是很多.h文件没有定义位置,可以按照下图操作: 而且,如果是想使用压缩包,那一定要关闭keil后…...
从一到无穷大 #16 ByteSeries,思考内存时序数据库的必要性
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。 引言 在[3]中我基于Gorilla讨论了时序数据库设置cache的可行性,最后得出结论&…...
网站建设的硬件支持/北京seo收费
[飞鸟各投林] 为官的家业雕零,富贵的金银散尽。有恩的死里逃生,无情的分明报应。欠命的命已还,欠泪的泪已尽:冤冤相报自非轻,分离聚合皆前定。欲知命短问前生,老来富贵也真侥幸,看破的遁入空门…...
wordpress更换域名所有页面404/企业网站的搜索引擎推广与优化
概述:Spark 程序开发,调试和运行,intellij idea开发Spark java程序。 分两部分,第一部分基于intellij idea开发Spark实例程序并在intellij IDEA中运行Spark程序.第二部分,将开发程序提交到Spark local或者hadoop YARN…...
福州网站制作计划/守游网络推广平台登陆
【下载一键安装软件包】 百度云地址:https://pan.baidu.com/s/1TZqGKtE-46gxW96Ptfp4gA 网址:https://lnmp.org/ 【步骤】 通过第三方远程工具将软件包传入后,使用tar命令解压,进入解压后的文件夹,执行install.sh,如果是无人值守的请在官网生成无人值守脚…...
淘宝网站那个做的/建站平台哪家好
时钟实现实现这个时钟时间需要解决以下三个问题:获得当前时间,并格式化如何可以在页面中显示时间让时间动起来1、获得当前时间,并格式化要获得当前时间,可以使用JavaSctipt的Date对象,默认构造函数会返回当前时间。存储…...
河北网站备案手机号码短信核验/seo国外推广软件
你再主动一点点 我们就有故事了简介curl 是常用的命令行工具,用来请求 Web 服务器。它的名字就是客户端(client)的 URL 工具的意思。它的功能非常强大,命令行参数多达几十种。如果熟练的话,完全可以取代 Postman 这一类的图形界面工具。本文…...
网络运维工程师薪酬/徐州seo代理计费
1、单片机向上位机LabVIEW VISA串口不断发送数据,只要存在帧间隔,VISA就会每次只上来一帧,之后只要调用解析一帧的子VI程序就可以。关键在于如何保存解析过的每一帧数据。簇数组/数组簇?...