【数据结构】堆的应用(堆排序的实现 + (向上/向下)建堆时间复杂度证明 + TopK问题(笔记总结))

news2025/1/13 10:31:59

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:数据结构
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


【本章内容】

在这里插入图片描述

标题

  • 一、堆排序
    • 1.1 堆排序的思想
    • 1.2 堆排序排升序思路
    • 1.3 建堆
      • 1.31 向上调整建堆
      • 1.32 向上建堆时间复杂度证明
      • 1.33 向下调整建堆
      • 1.34 向下建堆时间复杂度证明
    • 1.4 调整
      • 1.41 调整代码实现
      • 1.42 调整复杂度证明
    • 1.5 完整代码 + 整体时间复杂度
  • 二、TOP-K问题
    • 2.1 什么是TOP-K问题
    • 2.2 TOP-K问题的基本思路
    • 2.3 取最大的前TOP-K
    • 2.4 代码实现

一、堆排序

1.1 堆排序的思想

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

  1. 建堆
    升序:建大堆
    降序:建小堆
  2. 利用堆删除思想来进行排序
    建堆和堆删除中都运用到了向下调整。但是,建堆也可以用向上调整,只是向上调整的时间复杂度高于向下调整(后面有证明过程)

1.2 堆排序排升序思路

在《堆排序的思想》中,总结了升序要建大堆,为什么要建大堆呢?首先大堆的特点是:父亲结点总大于或等于孩子结点。因此建完大堆后,根节点就是最大的,而又要保持升序。所以,我们可以将根结点和尾结点进行交换,这样最大的元素就在最后一个了,然后再以根结点向下调整(注意调整的时候不要再动尾结点了),这样次大的元素又在根结点上了,重复以上操作,即可以用堆排序排升序。

1.3 建堆

1.31 向上调整建堆

  • 向上调整建堆就是从第二层模拟插入的过程
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustUp(int* a, int child)
{
	//找到父亲节点
	int parent = (child - 1) / 2;

	while (child > 0) 
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			//迭代
			child = parent;
			parent = (child - 1) / 2;
		}
		//如果尾插的节点不比其父亲大,就没必要比了
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int n)
{
	//1. 建堆
	//法一:向上调整建堆(大堆)
	for (int i = 1; i < n; i++)
	{
		Adjust(a, i);
	}
}

int main()
{
	int a[] = { 4,7,10,8,1,5,2,3 };
	
	//数组元素个数Asize
	int Asize = sizeof(a) / sizeof(a[0]); 

	//堆排序
	HeapSort(a, Asize);
	
	//打印
	for (int i = 0; i < Asize; i++)
		printf("%d ", a[i]);

	printf("\n");

	return 0;
}

此篇博客详细讲解了建大堆的过程 -->【传送门】

1.32 向上建堆时间复杂度证明

由于一般时间复杂度通常都是看最坏的,因此以满二叉树为例,因为它的结点个数最多。

在这里插入图片描述

向上建堆是从第二层开始模拟插入过程的。现假设二叉树的高度为h,T(N)表示建堆的总次数。那如何列出总次数的式子呢?我们可以拿(每一层结点个数如上图)每一层结点的个数 × 最坏调整次数。例如,从第二层开始向上建堆可以列出 2 1 × 1 2^1×1 21×1,第三层可以列出 2 2 × 2 2^2 × 2 22×2。后面因此类推。所以可以列出以下T(N)的表达式:
T ( N ) = 2 1 × 1 + 2 2 × 2 + 2 3 × 3 + . . . + 2 ( h − 2 ) × ( h − 2 ) + 2 ( h − 1 ) × ( h − 1 ) T(N) = 2^1×1 + 2^2×2 + 2^3×3 + ... + 2^(h-2)×(h-2) + 2^(h-1)×(h-1) T(N)=21×1+22×2+23×3+...+2(h2)×(h2)+2(h1)×(h1)
那该如何化简呢?这就要运用到高中的数学知识 — 错位相减法
在这里插入图片描述
再通过以下结论:
设满二叉树高度为h,总结点个数为N

  • 满二叉树的总结点个数 : N = 2 h − 1 N = 2^h - 1 N=2h1
  • 再根据数学知识化简以上等式: h = l o g N h = logN h=logN (以2为底)

在这里插入图片描述
带入结论得出:向上建堆的时间复杂度为:NlogN

1.33 向下调整建堆

#include <stdio.h>
//堆排序

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

void AdjustDown(int* a, int n, int parent)
{
	//假设左孩子一开始最大
	int child = parent * 2 + 1;
	//当child走到叶子节点循环结束,也就是说它不能超过数组的大小
	while (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;
		}
	}
}

void HeapSort(int* a, int n)
{
	//1. 建堆
	//法二:向下调整建堆(大堆)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		Adjustdown(a, n, i);
	}
// i - 开始向下调整的根的下标
// n - 需要调整的元素个数
}

这里需要注意的是,这里的向下调整并不是从根结点开始的,而是从倒数第一个非叶子结点开始的。为什么呢?因为一开始数组内的元素都是随机的,而向下调整必须要满足左右子树都要有堆的性质。画个图可能会更加清晰点:
在这里插入图片描述
然后再来解释向下调整循环的细节,前头说过了,要从倒数第一个非叶子结点开始向下调整,对于上图来说,非叶结点的第一个结点也就是15,那么问题来了,如何找到15呢,在堆的章节已经说过了父亲结点和孩子结点的下标关系

  1. 如何找到父亲结点:parent = (child - 1)/ 2
  2. 如何找到左孩子结点 :leftchild = parent * 2 + 1
  3. 如何找到右孩子结点:rightchild = parent * 2 + 2(右孩子比左孩子的下标多1)

所以可以通过500的下标找到15,因此循环里的(n - 1 - 1) / 2n - 1代表的是最后一个元素的下标,再-1 /2即找到倒数第一个非叶子结点

1.34 向下建堆时间复杂度证明

和向上建堆的时间复杂度一样,以满二叉树为例

在这里插入图片描述

在向下调整过程中,由于是从倒数第二层结点开始向下调整的,倒数第二层结点总个数不难算出是 2(h-2)。现假设二叉树的高度为h,T(N)表示建堆的总次数。通过每一层结点的个数 × 最坏调整次数列出T(n)表达式如下:
T ( N ) = 2 ( h − 2 ) × 1 + 2 ( h − 3 ) × 2 + . . . + 2 1 × ( h − 2 ) + 2 0 × ( h − 1 ) T(N) = 2^(h-2)×1 + 2^(h-3)×2 + ... + 2^1×(h-2) + 2^0×(h-1) T(N)=2(h2)×1+2(h3)×2+...+21×(h2)+20×(h1)
然后通过错位相减法,过程如下:
在这里插入图片描述
再通过以下结论:
设满二叉树高度为h,总结点个数为N

  • 满二叉树的总结点个数 : N = 2 h − 1 N = 2^h - 1 N=2h1
  • 再根据数学知识化简以上等式: h = l o g N h = logN h=logN (以2为底)

在这里插入图片描述
带入结论得出:向下建堆的时间复杂度为:O(N)

总结:
向上建堆的时间复杂度为:O(NlogN),向下建堆的时间复杂度为:O(N)。因此,建堆时一般都使用向上建堆。

1.4 调整

我们已经完成了建堆,接下来根据《堆排序排升序思路》。将根结点和尾结点进行交换,然后再以根结点向下调整(注意调整的时候不要再动尾结点了),这样次大的元素又在根结点上了,重复以上操作,即可以用堆排序排升序。

1.41 调整代码实现

#include <stdio.h>
//堆排序

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

void AdjustDown(int* a, int n, int parent)
{
	//假设左孩子一开始最大
	int child = parent * 2 + 1;
	//当child走到叶子节点循环结束,也就是说它不能超过数组的大小
	while (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;
		}
	}
}


void HeapSort(int* a, int n)
{
	//1. 建堆
	//法二:向下调整建堆(大堆)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//2. 调整
	int end = n - 1;//最后一个元素
	while (end > 0)
	{
		//首尾交换
		Swap(&a[0], &a[end]);
		//向下调整
		AdjustDown(a, end, 0);
		end--;
	}
	//end是最后一个元素的下标
	//同时也是前面数据的个数
}

💀注意
交换完头尾两个元素后,向下调整的区间不要包括最后一个元素,因为最后一个元素已经是当前所有元素中最大的了

1.42 调整复杂度证明

可以这么想,因为满二叉树最后一层的结点个数占总结点个数的一半,因此可以只看最后一层
T ( N ) = 2 ( h − 1 ) × ( h − 1 ) = N l o g N T(N) = 2^(h-1) × (h-1) = NlogN T(N)=2(h1)×(h1)=NlogN

1.5 完整代码 + 整体时间复杂度

//堆排序
#include <stdio.h>

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

void AdjustDown(int* a, int n, int parent)
{
	//假设左孩子一开始最大
	int child = parent * 2 + 1;
	//当child走到叶子节点循环结束,也就是说它不能超过数组的大小
	while (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;
		}
	}
}

void HeapSort(int* a, int n)
{
	//1. 建堆
	//法二:向下调整建堆(大堆)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//2. 调整
	
	int end = n - 1;//最后一个元素
	while (end > 0)
	{
		//首尾交换
		Swap(&a[0], &a[end]);
		//向下调整
		AdjustDown(a, end, 0);
		end--;
	}
}

int main()
{
	int a[] = { 4,7,10,8,1,5,2,3 };
	
	//数组元素个数Asize
	int Asize = sizeof(a) / sizeof(a[0]); 

	//堆排序
	HeapSort(a, Asize);
	
	//打印
	for (int i = 0; i < Asize; i++)
		printf("%d ", a[i]);

	printf("\n");

	return 0;
}

【结果展示】

在这里插入图片描述

.🎈总结
向上建堆的时间复杂度是O(N),调整的时间复杂度是O(NlogN)。因此,堆排序的时间复杂度为O(Nlog(N)

二、TOP-K问题

2.1 什么是TOP-K问题

  • TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
  • TOP-K在生活中的应用:在班级取成绩前10名、游戏中给段位高的前100的玩家排名等。

2.2 TOP-K问题的基本思路

对于Top-K问题,能想到的最简单直接的方式就是排序。但是,如果数据量非常大,排序就不太可取了(因为数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决。基本思路如下:

  1. 用数据集合中前K个元素来建堆
  • 取前k个最大的元素,则建小堆
  • 取前k个最小的元素,则建大堆
  1. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

2.3 取最大的前TOP-K

假设我们要取最大的前k个(取最小的前k个也是如此),就要建小堆,然后随机放入K个数据。为什么呢?因为小堆的特点是根结点是整个数据中最小的,然后剩余的N - K个元素依次与堆顶元素来比较,如果大于堆顶元素则交换,然后再进行向下调整。最后,重复以上操作,堆中小的数据都已经被替换了,剩下的就是最大的前K

2.4 代码实现

  • 建小堆
#include <stdio.h>
#include <stdlib.h>

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

void AdjustDown(int* a, int n, int parent)
{
	//假设左孩子一开始最大
	int child = parent * 2 + 1;
	//当child走到叶子节点循环结束,也就是说它不能超过数组的大小
	while (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;
		}
	}
}

void GetTopK(int* a, int n, int k)
{
	//1. 建堆
	int* SmallHeap = (int*)malloc(sizeof(int) * k);
	if (SmallHeap == NULL)
		return;
	//2. 随机放入k个数据
	for (int i = 0; i < k; i++)
	{
		SmallHeap[i] = a[i];
	}
	//3. 建小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(SmallHeap, k, i);
	}
}

  • 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
//4. 将剩下的n - k个元素依次和堆顶元素比较
	for (int i = k; i < n; i++)
	{
		//如果n-k个元素中有大于堆顶则交换
		if (a[i] > SmallHeap[0])
		{
			SmallHeap[0] = a[i];
			//交换完后再调整堆
			AdjustDown(SmallHeap, k, 0);
		}
	 }
	//5.打印
	for (int i = 0; i < k; i++)
	{
		printf("%d ", SmallHeap[i]);
	}
	printf("\n");

【完整代码】

// TopK问题:以找出N个数里面最大为例

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
void Swap(int* a1, int* a2)
{
	int tmp = *a1;
	*a1 = *a2;
	*a2 = tmp;
}

//小堆:父亲比孩子小
void AdjustDown(int* a, int n, int parent)
{
	//child一开始最小
	int child = parent * 2 + 1;
	while (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;
		}
	}
}

void PrintTopK(int* a, int n, int k)
{
	//1.建堆
	int* SmallHeap = (int*)malloc(sizeof(int) * k);
	assert(SmallHeap);
	//2. 随机向堆丢入k个数据
	for (int i = 0; i < k; i++)
	{
		SmallHeap[i] = a[i];
	}

	//3.建小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(SmallHeap, k, i);
	}

	//4. 将剩下的n - k个元素依次和堆顶元素比较
	for (int i = k; i < n; i++)
	{
		//如果n-k个元素中有大于堆顶则交换
		if (a[i] > SmallHeap[0])
		{
			SmallHeap[0] = a[i];
			//交换完后再调整堆
			AdjustDown(SmallHeap, k, 0);
		}
	}
	//5.打印
	for (int i = 0; i < k; i++)
	{
		printf("%d ", SmallHeap[i]);
	}
	printf("\n");
}

int main()
{
	int a[] = { 20, 100, 4, 2, 87, 9, 8, 5, 46, 26 };
	int k = 3;
	int n = sizeof(a) / sizeof(a[0]);

	PrintTopK(a, n, k);
	return 0;
}

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

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

相关文章

Redis高可用之3种集群方案对比

Redis集群方案使用建议&#xff1a; Redis cluster&#xff1a;除非是1000个节点以上的超大规模集群&#xff0c;优先考虑使用Redis clustercodis&#xff1a;旧项目如果仍在使用codis&#xff0c;可继续使用&#xff0c;但也推荐迁移到Redis clustertwemproxy&#xff1a;不建…

什么是数字化?企业为什么要数字化转型?

什么是数字化&#xff1f;企业为什么要数字化转型&#xff1f; 深度长文&#xff0c;4000字&#xff0c;融合了很多国内外专业期刊观点&#xff0c;一文讲清到底什么是企业数字化转型&#xff1f;心急的小伙伴可以先看目录&#xff1a; 关于定义——到底什么是“数字化转型”…

Ubuntu18.04通过一根网线与树莓派建立连接,远程操作树莓派,向树莓派传文件

文章目录 前言1 树莓派设置静态IP1.1 不能登录到树莓派的图形化界面1.2 可以登录到树莓派的图形化界面 2 PC端的ubuntu18.04设置静态ip地址2.1 不使用图形化界面操作2.2 使用图形化界面进行操作 3 Putty软件3 FileZilla软件 前言 本篇博客的应用场景&#xff0c;前提条件如下&a…

4.1.2串的存储结构

串的存储结构和线性表类似 串的顺序存储&#xff1a; 缺点就是长度不可变&#xff08;也就是静态数组存储&#xff09; 函数执行结束存储空间自动回收 需要使用free函数回收 串的顺序存储&#xff1a; 方案二的缺点&#xff1a; char[0]存储length&#xff0c;但是由于char的…

智能学习 | MATLAB实现FA-BP多输入单输出回归预测(萤火虫算法优化BP神经网络)

智能学习 | MATLAB实现FA-BP多输入单输出回归预测(萤火虫算法优化BP神经网络) 目录 智能学习 | MATLAB实现FA-BP多输入单输出回归预测(萤火虫算法优化BP神经网络)预测效果基本介绍程序设计参考资料预测效果 基本介绍 MATLAB实现FA-BP多输入单输出回归预测(萤火虫算法优化B…

基于铜锁构建Web在线加密工具库

基于铜锁构建Web在线加密工具库 搭建运行环境 实验⼿册中的实验都是以 docker 和 docker-compose 环境为主&#xff0c;基于 Ubuntu 20.04 容器镜像。 初始化项目 首先利用 IDE 创建一个 tongsuo_web 的空项目&#xff0c;接下来我们所有的文件都会创建在该项目中&#xff0…

学习系统编程No.19【进程间通信之控制进程】

引言&#xff1a; 北京时间&#xff1a;2023/4/13/8:00&#xff0c;早八人&#xff0c;早八魂&#xff0c;时间不怎么充足&#xff0c;磨磨引言刚好&#xff0c;学习Linux和Linux有关的系统级知识已经许久了&#xff0c;在不知不觉之中&#xff0c;发现自己已经更到了第19篇&a…

3D视觉之深度相机方案

随着机器视觉&#xff0c;自动驾驶等颠覆性的技术逐步发展&#xff0c;采用 3D 相机进行物体识别&#xff0c;行为识别&#xff0c;场景 建模的相关应用越来越多&#xff0c;可以说 3D 相机就是终端和机器人的眼睛。 3D 相机 3D 相机又称之为深度相机&#xff0c;顾名思义&…

openDriver开源插件main.js源码分析

、基础要求 阅读本文章需要对以下知识有相关的了解 Threejs 3D渲染引擎dat.gui界面控制插件webgl三维绘图协议、着色器相关知识UV坐标、XYZ惯性坐标XODR文件格式 拓展 自动驾驶场景仿真标准&#xff08;一&#xff09;- OpenDRIVE - 知乎 《OpenDRIVE1.6规格文档》3_open…

2023年SICWGHS两大高含金量商赛组队招募中

想参加商赛&#xff0c;问了周围一圈朋友&#xff0c;不是没时间就是没兴趣&#xff0c;找个靠谱的队友怎么这么难&#xff1f; 相信这是不少商赛热爱者的共同烦恼&#xff0c;别急&#xff01;翰林来承包你的“找队友”任务&#xff0c;各路学霸等你来pick&#xff01; 两大…

屏幕亮度调节工具:Simple Screen Shade Mac

Simple Screen Shade Mac版是mac上一款优秀的屏幕颜色亮度调节工具&#xff0c;能够让我们Mac电脑的显示器背景变暗&#xff0c;这样可以保护你眼睛的健康并保持舒适。Simple Screen Shade 旨在实现最大程度的简单性和易用性。你可以设置灰色背景以减轻明亮鲜艳的色彩&#xff…

上海亚商投顾:沪指跌近2%险守3300点 AI概念股集体重挫

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数今日放量调整&#xff0c;深成指午后跌超2%&#xff0c;沪指、创业板指均跌近2%&#xff0c;科创50指数跌…

java-word模板转化为pdf

文章目录 一、引入包1.1在pom引入1.2 因为我们的项目是打包成jar,所以以上方法在本地idea运行没有问题&#xff0c;linux系统不行1.2.1解决方法11.2.2解决方法2 二、配置文件--License.xml--去除水印2.1 license.xml直接放到resources的根目录下即可2.2 工具类 三、调用效果 一…

部署架构 因为单体架构痛点 升级到微服务架构

如图为单体部署 痛点 多人协作可能产生很多的回归测试 代码管理复杂度提升 软件包升级会导致增加测试次数 举例 单体电商 1增加功能(增加小程序平台) 2 并发增加 出现 1 代码复用 2 系统间相互调用 3 接口不仅要对外服务&#xff0c;也得对内提供接口 4 数据分析功…

OTG是什么意思?

OTG是什么意思&#xff1f; OTG是怎么样实现的&#xff1f; TYPE-C接口的手机如何实现同时充电OTG功能&#xff1f; OTG是什么意思&#xff1f; OTG是On-The-Go的缩写&#xff0c;是一项新兴技术&#xff0c;主要应用于不同的设备或移动设备间的联接&#xff0c;进行数据交…

腾讯安全助力高校信息安全建设,护航湾区教育高质量发展

2023年4月20日-21日&#xff0c;首届大湾区信息网络安全大会在广州市长隆国际会展中心隆重召开。会议以“同筑安全屏障&#xff0c;共赢湾区未来”为主题&#xff0c;旨在响应国家安全战略&#xff0c;推动粤港澳大湾区信息网络安全的建设和发展&#xff0c;保障经济社会稳定运…

「OceanBase 4.1 体验」|国产分布式数据库不好用?别再打脸了

文章目录 分布式数据库分布式数据库有哪些&#xff1f;OceanBase4.1安装部署Index Skip Scan总结 随着互联网的高速发展和数据量的爆炸式增长&#xff0c;如何能够高效、可靠、安全地存储海量数据成为了每个企业的重要课题。 分布式数据库 分布式数据库通常是由多个独立的数据…

无人机各个类型介绍

为了执行&#xff0c;无人机可能由类似的元件制成&#xff0c;但无论是它们的能力&#xff0c;还是由什么组成的&#xff0c;它们都在某种程度上有所不同。大多数无人机都是为了执行特定任务而制造的&#xff0c;因此以特定的方式建造&#xff0c;以适应它们将要使用的环境。 …

docker-mysql-主从设计

一、docker主从 1.新建主从镜像 docker run -p 3307:3306 --name mysql -e MYSQL_ROOT_PASSWORDroot -d mysql:5.7.28 docker run -p 3308:3306 --name mysqlslave -e MYSQL_ROOT_PASSWORDroot -d mysql:5.7.282.分别进入两个容器&#xff0c;修改配置文件 #1.进入容器 $ do…

Java8新特性【函数式接口、Lambda表达、Stream流】

一、Lambda表达式 Java8是Java语言自JDK1.5以后的一个重大的里程碑版本&#xff0c;因为它增加了很多新特性&#xff0c;这些新特性会改变编程的风格和解决问题的方式。 例如&#xff1a;日期时间API、Lambda表达式&#xff08;λ&#xff09;、Stream API(操作集合)、方法引用…