数据结构之《二叉树》(中)

在数据结构之《二叉树》(上)中学习了树的相关概念,还了解的树中的二叉树的顺序结构和链式结构,在本篇中我们将重点学习二叉树中的堆的相关概念与性质,同时试着实现堆中的相关方法,一起加油吧!


1.实现顺序结构二叉树

在实现顺序结构的二叉树中通常把堆使用顺序结构的数组来存储,因此我们先要了解堆的概念与结构

1.1 堆的概念与结构

如果有一个关键码的集合 K  =  {k0 , k1 , k2 , ...,kn−1 } ,把它的所有元素按完全二叉树的顺序存储方式式存储,在⼀个⼀维数组中,并满足: Ki  <=  K2∗i+1 ( Ki >= K2∗i+1 且 Ki  <=  K2∗i+2 ),i = 0、1、2... ,则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

以上的堆的概念简单来说就是堆是完全二叉树,在大堆中根结点为最大的元素,在此之后的每个孩子结点都要小于或者等于它的父结点;在小堆中根结点为最小的元素,在此之后的每个孩子结点都要大于或者等于的父结点

因此通过堆的概念的了解需要知道堆有以下的性质:
• 堆中某个结点的值总是不大于或不小于其父结点的值
• 堆总是一棵完全二叉树

例如以下图示就是大堆,并且将其结点的数据存储到数组当中

以下图示就是小堆,并且将其结点的数据存储到数组当中

1.2二叉树的性质 

在了解的堆的相关概念和结构后,之后我们要来实现堆,因此在此之前还要再了解二叉树的相关性质

💡 二叉树性质
• 对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0 开始编号,则对于序号为 i 的结点有:
1. 若 i>0 ,i 位置结点的双亲序号: \frac{i-1}{2} ; i=0 , i 为根结点编号,无双亲结点
2. 若 2i+1<n ,左孩⼦序号: 2i+1 , 2i+1>=n 否则无左孩子
3. 若 2i+2<n ,右孩⼦序号: 2i+2 , 2i+2>=n 否则无右孩子

对以上的性质来通过一个示例来加深理解

以下就是根据二叉树的性质来求出各节点的孩子结点以及父结点的图示

1.3堆的实现

在了解了堆相关的性质与结构后接下来就可以来试着实现堆
在实现堆的程序中还是将代码分为三个文件

1.堆结构的定义 

//堆结构的定义
typedef int HDataType;
typedef struct Heap
{HDataType* arr;int size;//有效数据个数int capacity;//空间大小
}Heap;

在定义堆的结构中,使用一个结构体来定义堆的结构,里面的成员变量和顺序表中相同arr为数组首元素的指针,size为数组中有效元素的个数,capacity为数组空间的大小

2.堆的初始化

在堆的初始化函数的实现中先要在Heap.h内完成初始化函数的声明

//初始化堆
void HeapInit(Heap* php);

将该函数命名为HeapInit,函数的参数为结构体指针

在完成了函数的声明后就是在Heap.c内完成函数的定义

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

3.堆的插入

 在堆的插入函数的实现中先要在Heap.h内完成插入函数的声明

//堆的插入
void HeapPush(Heap* php,HDataType x);

将该函数命名为HeapPush函数参数有两个,第一个为结构体指针,第二个为要插入的数据

在完成了函数的声明后就是在Heap.c内完成函数的定义

在堆的插入当中我们要实现的是在堆的末尾插入数据,也就是在数组的size-1位置插入新的数据,并且在插入之后由于堆的特性还要将堆中各元素的位置进行调整,使得其变为一个小堆或者大堆

因此在插入函数中我们先要实现向上调整的函数

3.1 向上调整法 

要实现堆中的向上调整结点的函数首先要来分析在调整过程中需要实现哪些步骤,我们来看下面这个堆的示例

在这个小堆中我们先在堆的末尾插入数据为10的结点,在这之后要将该二叉树重新调整为小堆需要哪些操作呢?

首先就是要将新插入的结点10与它的父结点做对比,如果比父结点大就和父结点交换之后再和父结点的父结点比较重复以上操作直到最后父结点为0时就停止;在此过程中如果比父结点小就不进行交换

所以以上的二叉树要调整为小堆就要经过以下的步骤 

在完全向上调整实例的分析后接下来就可以来实现向上调整的代码 

先再Heap.h内对向上调整函数进行声明,通过以上的分析就可以推测出函数的参数有两个,一个为数组的首元素指针,另一个为数组的有效元素个数 

//向上调整法
void AdjustUp(HDataType* arr, int child);

在以下的代码当中parent就来表示父结点的数组下标,child就来表示孩子节点的下标,因此在知道孩子结点的下标时要求父结点的下标就可以用到前面提到的二叉树的性质,父结点的下标等于其孩子结点下标减一再除以二

//向上调整法
void AdjustUp(HDataType* arr, int child)
{int parent = (child - 1) / 2;//求父结点下标while (child>0){//小堆时下面的if判断部分就用<//大堆时下面的if判断部分就用>if (arr[child] < arr[parent])//若父结点大于孩子结点就交换{Swap(&arr[child], & arr[parent]);child = parent;parent= (child - 1) / 2;}else{break;//若不符合以上if的条件就退出循环}}
}

例如以上的示例中在以下代码中parent和child的变化就如以下图所示

在以上函数中的Swap是来实现两个数的交换,以下是函数的定义

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

3.2 插入函数代码实现 

在完成了向上调整的代码后接下来就可以来完成堆插入函数的实现

在插入函数中由于php为结构体指针,因此php不能为空,所以要将php进行assert断言
并且在插入之前也要判断数组空间是否满了,满了就要对空间进行调整,在此的调整代码和顺序表中相同
之后再将数据尾插到数组当中,再进行向上调整,最后切记要将size+1

//堆的插入
void HeapPush(Heap* php, HDataType x)
{assert(php);if (php->size == php->capacity)//对数组空间进行调整{int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;Heap* tmp = (Heap*)realloc(php->arr,sizeof(HDataType) * newcapacity);if (tmp == NULL){perror("malloc file");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//进行尾插AdjustUp(php->arr, php->size);//进行向上调整php->size++;
}

4.堆的删除 

在堆的删除函数的实现中先要在Heap.h内完成删除函数的声明

//堆的删除
void HeapPop(Heap* php);

将该函数命名为HeapPop函数的参数是结构体指针

在完成了函数的声明后就是在Heap.c内完成函数的定义

在堆的删除中是将跟结点给删除,在删除过程中是将根结点和最后一个结点交换,之后将数组的有效个数减一这样就将原来的根结点给删除了,之后再进行各结点的调整使其重新变为一个堆

因此在插入函数中我们先要实现向下调整的函数

4.1向下调整法

要实现堆中的向下调整结点的函数首先要来分析在调整过程中需要实现哪些步骤,我们来看下面这个堆的示例

在这个小堆中我们如果已经跟结点删除,在这之后要将该二叉树重新调整为小堆需要哪些操作呢?

 

首先就是要将新的的跟结点70与它的两个孩子结点中小的做对比,如果比该孩子结点大就和该孩子结点交换之后再和该孩子结点的两的孩子结点中小的比较重复以上操作直到最后开始的跟结点为叶子节点时就停止;如果在这过程中比孩子结点中小的那个还小就不进行交换

所以以上的二叉树要调整为小堆就要经过以下的步骤 

在完全向下调整实例的分析后接下来就可以来实现向下调整的代码 

 先再Heap.h内对向上调整函数进行声明,通过以上的分析就可以推测出函数的参数有三个,一个为数组的首元素指针,另一个为数组的有效元素个数 ,最后一个是要向下调整的元素的数组下标

//向下调整法
void AdjustDown(HDataType* arr, int n, int parent);

之后就是在Heap.c内完成函数的定义
在以下的代码当中parent就来表示父结点的数组下标,child就来表示孩子节点的下标,因此在知道父结点的下标时要求孩子结点的下标就可以用到前面提到的二叉树的性质,孩子结点的下标等于其父结点下标乘二再加一或二

while循环中当孩子节点下标大于数组有效个数也就是父节点为叶子节点时就退出循环,因此进入while条件为child<n

//向下调整法
void AdjustDown(HDataType* arr, int n, int parent)
{int child = 2 * parent + 1;//孩子节点的下标while (child<n){//如果为小堆 以下的if判断部分就用>//如果为大堆 以下的if判断部分就用<if (child+1<n && arr[child] > arr[child + 1]){child++;//若为小堆就找孩子节点中小的,大堆就找孩子节点中大的}if (arr[parent] > arr[child])//若孩子节点小于父节点就交换{Swap(&arr[parent], &arr[child]);parent = child;child= 2 * parent + 1;}else{break;//若不符合以上if的条件就退出循环}}
}

例如以上的示例中在以下代码中parent和child的变化就如以下图所示

 4.2删除函数代码实现

在插入函数中由于php为结构体指针,因此php不能为空,所以要将php进行assert断言
在删除函数中因为要删除的是堆的根结点,所以先将堆的根结点和堆的最后一个节点进行交换,之后再将size-1,这样就可以让数组当中的有效个数减一,使得原来的根结点被删除,之后再将该二叉树使用向下调整重新调整为堆

//堆的删除
void HeapPop(Heap* php)
{assert(php && php->size);php->arr[0] = php->arr[php->size - 1];--php->size;AdjustDown(php->arr, php->size, 0);
}

5.取堆顶的元素 

在堆的取堆顶的元素函数的实现中先要在Heap.h内完成取堆顶的元素函数的声明

//取堆顶的元素
HDataType HeapTop(Heap* php);

将该函数命名为HeapTop函数的参数是结构体指针,函数的返回值是堆跟结点内的数据

在完成了函数的声明后就是在Heap.c内完成函数的定义

//取堆顶的元素
HDataType HeapTop(Heap* php)
{assert(php);return php->arr[0];
}

6.堆的数据个数 

在求堆的数据个数函数的实现中先要在Heap.h内完成堆的数据个数函数的声明

//堆的数据个数
int HeapSize(Heap* php);

将该函数命名为HeapSize函数的参数是结构体指针,函数的返回值是堆的结点结点个数

在完成了函数的声明后就是在Heap.c内完成函数的定义
因为堆是用数组来实现的,所以堆中的结点个数就为数组的有效元素个数

//堆的数据个数
int HeapSize(Heap* php)
{assert(php);return php->size;
}

7.堆的判空

在判断堆是否为空函数的实现中先要在Heap.h内完成判断堆是否为空函数的声明

//堆的判空
bool HeapEmpty(Heap* php);

将该函数命名为HeapEmpty函数的参数是结构体指针,函数的返回类型是布尔类型

在完成了函数的声明后就是在Heap.c内完成函数的定义
在该函数中当当堆为空时就返回true,不为空时返回false

//堆的判空
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}

8.堆的销毁

在堆的销毁函数的实现中先要在Heap.h内完成堆的销毁函数的声明

//销毁堆
void HeapDestory(Heap* php);

将该函数命名为HeapDestory函数的参数是结构体指针

在完成了函数的声明后就是在Heap.c内完成函数的定义

//销毁堆
void HeapDestory(Heap* php)
{assert(php);if (php->arr){free(php->arr);}php->arr = NULL;php->size = php->capacity = 0;
}

 9.堆实现完整代码

Heap.h
#define  _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//堆结构的定义
typedef int HDataType;
typedef struct Heap
{HDataType* arr;int size;//有效数据个数int capacity;//空间大小
}Heap;//初始化堆
void HeapInit(Heap* php);
//销毁堆
void HeapDestory(Heap* php);
//堆的插入
void HeapPush(Heap* php,HDataType x);
//堆的删除
void HeapPop(Heap* php);
//取堆顶的元素
HDataType HeapTop(Heap* php);
//堆的数据个数
int HeapSize(Heap* php);
//堆的判空
bool HeapEmpty(Heap* php);
//向上调整法
void AdjustUp(HDataType* arr, int child);
//向下调整法
void AdjustDown(HDataType* arr, int n, int parent);
Heap.c
#include"Heap.h"void Swap(HDataType* p1, HDataType* p2)
{HDataType* tmp = *p1;*p1 = *p2;*p2 = tmp;
}//向上调整法
void AdjustUp(HDataType* arr, int child)
{int parent = (child - 1) / 2;while (child>0){//小堆 <//大堆 >if (arr[child] < arr[parent]){Swap(&arr[child], & arr[parent]);child = parent;parent= (child - 1) / 2;}else{break;}}}//向下调整法
void AdjustDown(HDataType* arr, int n, int parent)
{int child = 2 * parent + 1;while (child<n){//小堆 >//大堆 <if (child+1<n && arr[child] > arr[child + 1]){child++;}if (arr[parent] > arr[child]){Swap(&arr[parent], &arr[child]);parent = child;child= 2 * parent + 1;}else{break;}}
}//初始化堆
void HeapInit(Heap* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}//销毁堆
void HeapDestory(Heap* php)
{assert(php);if (php->arr){free(php->arr);}php->arr = NULL;php->size = php->capacity = 0;
}//堆的插入
void HeapPush(Heap* php, HDataType x)
{assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;Heap* tmp = (Heap*)realloc(php->arr,sizeof(HDataType) * newcapacity);if (tmp == NULL){perror("malloc file");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;AdjustUp(php->arr, php->size);php->size++;
}//堆的删除
void HeapPop(Heap* php)
{assert(php && php->size);php->arr[0] = php->arr[php->size - 1];--php->size;AdjustDown(php->arr, php->size, 0);
}//取堆顶的元素
HDataType HeapTop(Heap* php)
{assert(php);return php->arr[0];
}//堆的数据个数
int HeapSize(Heap* php)
{assert(php);return php->size;
}//堆的判空
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}

 2.堆的应用

1.堆排序

在之前的C语言的学习当中我们实现了冒泡排序,但在算法的复杂度中得出了冒泡排序的时间复杂度为O(n^2),因此其实冒泡排序的效率是不高的。接下来我们就要来学习一种使用堆来实现排序的算法。

在此之前通过学习堆的相关概念知道了在小堆中的根结点是堆中最小的,那么在小堆中只要一直取堆的根结点就可以得到升序的数据,以下就是使用这种方法来实现的堆排序

void HeapSort(int* a, int n)
{Heap hp;for(int i = 0; i < n; i++){HeapPush(&hp,a[i]);}int i = 0;while (!HeapEmpty(&hp)){a[i++] = HeapTop(&hp);HeapPop(&hp);}HeapDestroy(&hp);
}

但是在以上的这种算法中需要将数组中的数据先要先存储在堆当中才能在之后得到堆顶的数据,因此以上这种方法的空间复杂度就为O(n),那么有什么方法能在不申请新的空间下来实现堆排序呢?接下来就来看以下的这种基于原来数组建堆的方法

//堆排序算法
void HeapSort(int* arr, int n)
{for (int i = 0; i < n; i++){AdjustUp(arr, i);}int end = n - 1;while (end > 0){Swap(&arr[end], &arr[0]);AdjustDown(arr, end, 0);end--;}for (int i = 0; i < n; i++){printf("%d ", arr[i]);}
}

在以上这种堆排序中先用向上调整法来将数组通过循环的将数组数组调整为堆,根据之前向上调整的代码在此的建的是小堆。之后的while循环中实现的是将小堆中的跟结点和尾结点进行交换这时数组中最小的元素就排在了数组的末尾之后再进行向下排序就可重新变为小堆,一直重复以上的操作就可以将数组的元素从小到大依次移动到数组末尾,最终原数组就变为升序的了。

那么以上除了使用向上调整建堆外,其实使用向下调整法也可以建堆,以下是使用向下调整法建堆的代码

//堆排序算法
void HeapSort(int* arr, int n)
{for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, n, i);}int end = n - 1;while (end > 0){Swap(&arr[end], &arr[0]);AdjustDown(arr, end, 0);end--;}for (int i = 0; i < n; i++){printf("%d ", arr[i]);}
}

这两种那一这种效率更好呢,这就需要来分析向上建堆和向下建堆的时间复杂度

先来看向上调整法

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个结点不影响最终结果)

以下是各层的结点在向上调整过程中各层结点在调整过程中最坏的情况下移动的层数

需要移动结点总的移动步数为:每层结点个数 * 向上调整次数(第⼀层调整次数为0)

由此可得:
💡 向上调整算法建堆时间复杂度为: O(n ∗ log2 n)

接下来看向下调整法

以下是各层的结点在向下调整过程中各层结点在调整过程中最坏的情况下移动的层数

则需要移动结点总的移动步数为:每层结点个数 * 向下调整次数 

💡 向下调整算法建堆时间复杂度为: O(n) 

通过以上的分析后可以得出在堆排序中使用向下调整法建堆更好

堆排序第二个循环中的向下调整与建堆中的向上调整算法时间复杂度计算一致。因此堆排序的时间复杂度为 O(n + n ∗ log n) ,即 O(n log n)

 

 

2.TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下⼦全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

(1)用数据集合中前K个元素来建堆
      前k个最大的元素,则建小堆
      前k个最小的元素,则建大堆
(2)用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者大的元素

以下这段代码可以实现将大量的整型数据输入到data.txt的文件当中

void CreateNDate()
{// 造数据int n = 100000;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) % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}

 接下来就来实现解决TOP-K问题的代码,以下是实现的是得出数据结合中前K个最大的元素

void topk()
{int k=0;printf("k:");scanf("%d", &k);//读取文件中的前k个数据FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen fail");exit(1);}int* pa = (int*)malloc(k * sizeof(int));if (pa == NULL){perror("malloc fail");exit(2);}for (int i = 0; i < k; i++){fscanf(pf, "%d", &pa[i]);}//使用前k个数据建堆for (int i = (k - 1 - 1) / 2; i >= 0; i--){AdjustDown(pa, k, i);}int tmp=0;//循环将k个数据之后的数据堆中最小的元素比较,若比这个元素大就交换while (fscanf(pf, "%d", &tmp) != EOF){if (tmp > pa[0]){pa[0] = tmp;AdjustDown(pa, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", pa[i]);}fclose(pf);pf = NULL;}


 

 

以上就是二叉树(中)的全部内容了,接下来在二叉树(下)将继续学习二叉树的知识,在下一篇中我们将重点学习链式结构的二叉树的相关知识,未完待续…… 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构:带索引的双链表IDL

IDLindexed double list 如图&#xff0c;下方是一个双链表&#xff0c;上方是索引。索引储存为结构体数组&#xff0c;结构体内包括一个指针&#xff0c;和长度。 假设索引只有一个&#xff0c;这时&#xff0c;它应该指向双链表的中间&#xff0c;这样才能提高搜索效率。称…

MyBatis 框架的两大缺点及解决方案

MyBatis 框架的两大缺点及解决方案 1. SQL 编写负担重1.1 缺点概述1.2 解决方案 2. 数据库移植性差2.1 缺点概述2.2 解决方案 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; MyBatis 作为一款广受欢迎的 Java 持久层框架&#xff0c;尽管其…

吴恩达机器学习作业-ex7(主成分分析)

data1 导入库&#xff0c;读取数据&#xff0c;并进行可视化数据 import numpy as np import scipy.io as sio import matplotlib.pyplot as plt#读取数据 path "./ex7data1.mat" data sio.loadmat(path) # print(data.keys()) X data.get("X") # pri…

『C++实战项目 负载均衡式在线OJ』一、项目介绍与效果展示(持续更新)

文章目录 一、项目介绍二、开发环境三、第三方库四、相关技术五、项目整体框架代码目录框架 代码仓库连接 点击这里✈ 一、项目介绍 本项目是实现一个仿 leetcode 的 OJ (Online-Judge&#xff09;系统。更准确的说应该称之为leetcode 的裁剪版。因为本项目只实现了leetcode中…

《计算机网络》(第8版)第9章 无线网络和移动网络 复习笔记

第 9 章 无线网络和移动网络 一、无线局域网 WLAN 1 无线局域网的组成 无线局域网提供移动接入的功能&#xff0c;可分为两大类&#xff1a;有固定基础设施的和无固定基础设 施的。 &#xff08;1&#xff09;IEEE 802.11 IEEE 802.11 是无线以太网的标准&#xff0c;是有固定…

【保姆级系列:锐捷模拟器的下载安装使用全套教程】

保姆级系列&#xff1a;锐捷模拟器的下载安装使用全套教程 1.介绍2.下载3.安装4.实践教程5.验证 1.介绍 锐捷目前可以通过EVE-NG来模拟自己家的路由器&#xff0c;交换机&#xff0c;防火墙。实现方式是把自己家的镜像导入到EVE-ng里面来运行。下面主要就是介绍如何下载镜像和…

【初阶数据结构题目】10. 链表的回文结构

链表的回文结构 点击链接做题 思路1&#xff1a;创建新的数组&#xff0c;遍历原链表&#xff0c;遍历原链表&#xff0c;将链表节点中的值放入数组中&#xff0c;在数组中判断是否为回文结构。 例如&#xff1a; 排序前&#xff1a;1->2->2->1 设置数组来存储链表&a…

1、爬⾍概述

1. 什么是爬虫&#xff1f; 爬虫&#xff08;Web Crawler&#xff09;是一种通过编写程序自动访问并提取互联网上数据的技术。爬虫可以帮助我们在浏览网页时自动收集和保存一些有用的数据&#xff0c;例如图片、视频和文本信息。简单来说&#xff0c;爬虫就是自动化的浏览器。…

react-native从入门到实战系列教程-React Native Elements

react-native的ui框架内网真的是屈指可数&#xff0c;能用的成熟的几乎找不到。不像web端的繁荣景象&#xff0c;可以用荒凉来形容不为过。 京东的nutui说也支持react-native,官网及其简陋。尝试了未成功运行&#xff0c;可能是项目类型不同&#xff0c;对比其他类型的ui库都分…

Flink中上游DataStream到下游DataStream的内置分区策略及自定义分区策略

目录 全局分区器GlobalPartitioner 广播分区器BroadcastPartitioner 哈希分区器BinaryHashPartitioner 轮询分区器RebalancePartitioner 重缩放分区器RescalePartitioner 随机分区器ShufflePartitioner 转发分区器ForwardPartitioner 键组分区器KeyGroupStreamPartitio…

【java基础】徒手写Hello, World!程序

文章目录 前提&#xff1a;java环境变量配置使用vscode编写helloworld解析 前提&#xff1a;java环境变量配置 https://blog.csdn.net/xzzteach/article/details/140869188 使用vscode编写helloworld code .为什么用code看下图 报错了&#xff01;&#xff01;&#xff01;&…

样式与特效(3)——实现一个测算页面

这次我们使用前端实现一个简单的游戏页面,理论上可以增加很多玩法&#xff0c;&#xff0c;但是这里为了加深前端的样式和JS点击事件&#xff0c;用该案例做练习。 首先需要掌握手机端的自适应&#xff0c;我们是只做手机端玩家页面 。需要允许自适应手机端页面&#xff0c; 用…

24年电赛——自动行驶小车(H题)MSPM0G3507-编码电机驱动与通用PID

一、编码电机驱动 编码电机的详情可以查看此篇文章&#xff1a; stm32平衡小车--&#xff08;1&#xff09;JGB-520减速电机tb6612&#xff08;附测试代码&#xff09;_jgb520-CSDN博客 简单来说&#xff0c;编码电机的驱动主要是给一个 PWM 和一个正负级就能驱动。PWM 的大小…

9-springCloud集成nacos config

本文介绍spring cloud集成nacos config的过程。 0、环境 jdk 1.8maven 3.8.1Idea 2021.1nacos 2.0.3 1、项目结构 根项目nacos-config-sample下有两个module&#xff0c;这两个module分别是两个springboot项目&#xff0c;都从nacos中获取连接mysql的连接参数。我们开工。 …

羌活基因组--文献精读-36

The chromosome-scale assembly of the Notopterygium incisum genome provides insight into the structural diversity of coumarins 羌活&#xff08;Notopterygium incisum&#xff09;基因组的染色体级别组装为香豆素的结构多样性提供了新的见解 摘要 香豆素是由苯丙素途…

学生信息管理系统(Python+PySimpleGUI+MySQL)

吐槽一下 经过一段时间学习pymysql的经历&#xff0c;我深刻的体会到了pymysql的不靠谱之处&#xff1b; 就是在使用int型传参&#xff0c;我写的sql语句中格式化%d了之后&#xff0c;我在要传入的数据传递的每一步的去强制转换了&#xff0c;但是他还是会报错&#xff0c;说我…

决策树可解释性分析

决策树可解释性分析 决策树是一种广泛使用的机器学习算法&#xff0c;以其直观的结构和可解释性而闻名。在许多应用场景中&#xff0c;尤其是金融、医疗等领域&#xff0c;模型的可解释性至关重要。本文将从决策路径、节点信息、特征重要性等多个方面分析决策树的可解释性&…

k8s集群的资源发布方式(滚动/蓝绿/灰度发布)及声明式管理方法

目录 1.常见的发布方式 2.滚动发布 3.蓝绿发布 4.实现金丝雀发布&#xff08;Canary Release&#xff09; 5.声明式管理方法 1.常见的发布方式 蓝绿发布:两套环境交替升级&#xff0c;旧版本保留一定时间便于回滚优点&#xff1a;用户无感知&#xff0c;部署和回滚速度较…

如何通过前端表格控件实现自动化报表?

背景 最近伙伴客户的项目经理遇见一个问题&#xff0c;他们在给甲方做自动化报表工具&#xff0c;项目已经基本做好了&#xff0c;但拿给最终甲方&#xff0c;业务人员不太买账&#xff0c;项目经理为此也是天天抓狂&#xff0c;没有想到合适的应对方案。 现阶段主要面临的问…

chrome/edge浏览器插件开发入门与加载使用

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、插件与普通前端项目二、开发插件——manifest.json三、插件使用edge浏览器中使用/加载插件chrome浏览器中使用/加载插件 总结 前言 chrome插件的出现&#xff0c;初衷可能是为了方便用户更好地控制浏览器&#xff0c…