特殊二叉树——堆

🌈一、堆的基本概念

1.堆:非线性结构,是完全二叉树
2.堆分为大堆和小堆。
大堆:树中任意一个父亲都大于等于孩子,根节点值大于等于其所有子孙节点的值。
小堆:树中任意一个父亲都小于等于孩子,根节点值小于等于其所有子孙节点的值。
3.“同辈”即同一层数据间的大小顺序不做要求。
4.堆的物理结构即实际内存中存储的结构是顺序表,但逻辑结构是树。
在这里插入图片描述5.堆特性:堆中最有用的数据是堆的根节点。小堆的根是整棵树的最小值,大堆的根是整棵树的最大值.
6.堆的用途:①topk问题 ②堆排序

🌈二、实现一个小堆(大堆同理)

☀️(1)头文件test.h:

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int HpDataType;
typedef struct  {HpDataType* a;int size;int capacity;
}HP;
void Swap(HpDataType* p1, HpDataType* p2);
void AdjustUp(HpDataType* php, int x);
void HeapPrint(HP* php);
void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HpDataType x);
void AdjustDown(HpDataType* a, int n, int parent);
void HeapPop(HP* php);
HpDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);

☀️(2)函数实现heap.c:

🎈1.初始化、销毁函数:

#define _CRT_SECURE_NO_WARNINGS
#include"test.h"
void HeapInit(HP* php) {assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}
void HeapDestroy(HP* php) {assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}

🎈2.打印、交换数值函数:

void Swap(HpDataType* p1, HpDataType* p2) {HpDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void HeapPrint(HP* php) {assert(php);for (int i = 0;i < php->size;i++) {printf("%d ", php->a[i]);}printf("\n");
}

Swap函数细节分析:
Swap函数:用来交换父亲节点和孩子节点的值:
在这里插入图片描述

🎈3.插入、向上调整函数:

现在有一个堆,对其插入一个值:
插入位置:插入到根节点会破坏堆的结构;插入到树的中间部分的话我怎么知道哪里是中间部分,难不成还要先算一下树的大小?只能插入到最高层的末尾位置,即数组的末尾处,然后一层一层向上调整成一个正确的堆。
步骤一:首先需要判断该堆的容量是否足够插入一个节点,容量不够就扩容。
步骤二:根据插入的值判断是否需要对树进行调整。
eg:该情况下,若插入90,不需要对树进行调整:
在这里插入图片描述
若插入50,需要对树进行调整:
在这里插入图片描述
调整部位:右子树即50的“祖先”,不调整左子树15及其“子代”。
如何调整:一层一层地和“父亲”换位置,直到符合大小排序。
调整完的样子:
在这里插入图片描述

代码部分:


void AdjustUp(HpDataType* a, int child) {int parent = (child - 1) / 2;//为什么要用循环?//因为不止调整一次,有可能调整好多次直到child走到了根节点while (child > 0) {if (a[child] < a[parent]) {swap(&a[child], &a[parent]);//为什么还要加取地址符号&?//因为要对该空间的值进行更改,传值调用不会改变空间上的值,只有传地址才行child = parent;parent = (child - 1) / 2;}elsebreak;}
}
void HeapPush(HP* php, HpDataType x) {assert(php);//步骤一:扩容,放值if (php->size == php->capacity) {int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HpDataType* tmp = (HpDataType*)realloc(php->a, sizeof(HpDataType) * newCapacity);if (tmp == NULL) {perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;//步骤二:调整//命名含义:每次插入的位置都是最后一层最右边的节点,//调整过程是一层一层向上进行的,因此叫adjust upAdjustUp(php->a, php->size - 1);//函数参数://参数一:需要知道在那棵树中调整=>数组指针即a;//参数二:需要知道调整哪个节点=>下标即size-1;
}

细节分析:
1.关于AdjustUp函数名和参数:
在这里插入图片描述
2.关于AdjustUp函数内部:
在这里插入图片描述

🎈4.删除、向下调整函数:

删除位置:基于堆的特性,堆中最有用的数据是根上的数据,因此删除也是删除根的数据。
如何删除:
👻错误方法:直接挪动后面的数据覆盖,这样会破坏堆结构,得到的不一定是堆,如图:在这里插入图片描述
挪动覆盖根,关系全乱了,原先的兄弟关系被迫变成了父子或长辈晚辈关系,不一定稳定(不一定符合大小顺序),就不一定是堆了。
👻正确方法:最下面一层的最后一个数8和根位置的数据2交换,然后删除掉最后一层最后一个数据(–size就删除了),然后从根节点向下调整。下一层的最小的数据和根数据交换,直到走到最下面一层就调整成了正确的堆结构。每删除(pop)一次就能得到堆中最小的数。
👻背后原理:不破坏树原本的结构,左右子树依旧是小堆。
在这里插入图片描述
注:
向下调整前提:左右子树是堆;
向上调整前提:前面的数据是堆。

代码实现:

void AdjustDown(HpDataType* a, int n, int parent) {int child = parent * 2 + 1;//while循环作用:1.实现从上到下一层一层调整 //2.child>n时说明已经调整到最后一层,停止循环while (child < n) {//当有两个孩子时,需要选出较小的孩子与父节点比较//如果只有一个孩子,那child下标就是parent*2+1if (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;}//只要某一层的孩子节点值大于父节点值,就停止循环,//这是基于除了根节点其他地方都符合堆结构的前提elsebreak;}
}
void HeapPop(HP* php) {assert(php);assert(php->size > 0);swap(&php->a[0], &php->a[php->size - 1]);--php->size;AdjustDown(php->a, php->size, 0);//参数分析://参数一:结构体中的数组指针,指向堆//参数二:堆的大小,计算出的孩子序号数与堆大小进行比较就可以知道是否有该孩子//参数三:父节点下标,从该位置开始向下调整
}

细节分析(关于AdjustDown函数):
①参数分析:
在这里插入图片描述

②while循环作用:
在这里插入图片描述

③孩子节点可能有一个或两个,如何选出值较小的那个?
在这里插入图片描述
当没有“child+1<n”条件时,如果父节点只有一个左孩子,那么a[child+1]就会造成越界访问。

④向上调整或向下调整的前提是除了被调整节点,其他部位都符合堆,即左右子树是堆:
在这里插入图片描述

🎈5.根节点函数、判空函数:

HpDataType HeapTop(HP* php) {assert(php);assert(php->size > 0);return php->a[0];
}
bool HeapEmpty(HP* php) {assert(php);return php->size == 0;
}

☀️(3)测试test.c:

🎈1.测试插入:

测试思路:一个数组本身不是堆,但当我将每一个元素都push进去后,它就变成了堆。通过打印看是否建堆成功。

#define _CRT_SECURE_NO_WARNINGS
#include"test.h"
int main() {int a[] = { 65,100,70,32,50,60 };HP hp;HeapInit(&hp);for (int i = 0;i < sizeof(a) / sizeof(a[0]);i++) {HeapPush(&hp, a[i]);}HeapPrint(&hp);HeapDestroy(&hp);return 0;
}

打印结果:
在这里插入图片描述
从物理结构转变为逻辑结构:建堆成功。
在这里插入图片描述

🎈2.测试从小到大对堆中的值排序:

测试思路:先通过push函数将数组建成堆结构,然后通过top函数得到根节点值(最小值),最后pop掉根节点,将剩下的数调整成堆结构,继续得到根节点值(次小值)。

#define _CRT_SECURE_NO_WARNINGS
#include"test.h"
int main() {int a[] = { 65,100,70,32,50,60 };HP hp;HeapInit(&hp);for (int i = 0;i < sizeof(a) / sizeof(a[0]);i++) {HeapPush(&hp, a[i]);}HeapPrint(&hp);while (!HeapEmpty(&hp)) {printf("%d ", HeapTop(&hp));HeapPop(&hp);}HeapDestroy(&hp);return 0;
}

打印结果:
在这里插入图片描述
由此可以看出,将一组数据建堆是对改组数据进行粗略的排序;将堆的根一个一个取出才能得到准确的大小关系。

思考:如何实现大堆?

AdjustUp函数和AdjustDown函数中,把if中的大于小于号换一下即可。

🌈三、堆结构基础上实现堆排序

给出一个数组,将数组中的数据一个一个放进搭建好的小堆结构中,再将每次pop出来的最小值(根节点数据)从左到右放进原数组,从而排成升序。

☀️HeapSort函数:

void HeapSort(int* a, int n) {HP hp;HeapInit(&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);
}

测试将一组数排成升序:

int main() {int a[] = { 2,3,5,7,4,6,8,65,100,70,32,50,60 };HeapSort(a, sizeof(a) / sizeof(a[0]));return 0;
}

结果(通过监视看内存):
在这里插入图片描述
在这里插入图片描述
思考:如果要将改组数据排成降序呢?
法一:得到pop出来的最小值(根节点)后,从原数组最后面往前放置。
法二:建大堆,将每次pop出的最大值(根节点)从前往后放进原数组。
👻注:这种写法的缺陷:
1.需要先搭建一个完整的堆结构:先初始化,再有插入向上调整,删除向下调整等等;
2.空间复杂度的消耗:被排序的原数组占据了一部分空间,将原数组的值插入进堆中又占用了相同大小的空间,造成空间浪费。

🌈四、无堆结构,在数组上原地建堆

☀️法一:向上调整建堆

1.思路:从前往后依次将数组的值插入进堆,并向上调整。不需要初始化与销毁,只需要AdjustUp函数。
2.代码:
heap.c函数文件:

void AdjustUp(int* a, int child) {int parent = (child - 1) / 2;while (child > 0) {if (a[parent] > a[child]) {Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}
void Swap(int* a, int* b) {int tmp = *a;*a = *b;*b = tmp;
}
void HeapSort(int* a, int n) {for (int i = 0;i < n;i++) {AdjustUp(a, i);}
}

test.c测试文件:
测试将一组数组中的无序的数排成小堆

#define _CRT_SECURE_NO_WARNINGS
#include"test.h"
int main() {int a[] = { 70,65,100,32,50,60 };HeapSort(a, sizeof(a) / sizeof(int));for (int i = 0;i < sizeof(a) / sizeof(int);i++) {printf("%d ", a[i]);}
}

测试结果:
在这里插入图片描述
原地建堆成功。

☀️法二:向下调整建堆

1.思路:从最后一个节点的父节点(倒数第一个父节点)开始,向下调整至正确大小顺序;然后指针向前移动,指向倒数第二个父节点,向下调整;直至从根节点向下调整。
2.代码:
heap.c函数文件:

void AdjustDown(int* a, int size,int i) {int parent = i;int child = parent * 2 + 1;while (child < size) {if (child + 1 < size && a[child] > a[child + 1]) {child++;}if (a[parent] > a[child]) {Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}elsebreak;}
}
void Swap(int* a, int* b) {int tmp = *a;*a = *b;*b = tmp;
}
void HeapSort(int* a, int n) {for (int i = (n - 1 - 1) / 2;i >= 0;i--) {AdjustDown(a, n, i);}
}

细节:
为何有child+1<size,没有的话会怎样?
在这里插入图片描述

堆的节点不一定都有两个孩子,如果倒数第一个父节点只有一个左孩子,则child+1就越界了。因此要确保有两个孩子的情况下才能比较两个孩子谁大谁小,只有一个孩子时,child就储存这一个孩子节点的下标。
test.c测试文件:
测试将一组数组中的无序的数排成小堆

#define _CRT_SECURE_NO_WARNINGS
#include"test.h"
int main() {int a[] = { 70,65,100,32,50,60 };HeapSort(a, sizeof(a) / sizeof(int));for (int i = 0;i < sizeof(a) / sizeof(int);i++) {printf("%d ", a[i]);}
}

测试结果:
在这里插入图片描述

☀️比较向上和向下调整建堆

1.计算向下调整建堆时间复杂度:O(N)=N-logN
在这里插入图片描述
在这里插入图片描述

2.向上调整建堆时间复杂度:N*logN
在这里插入图片描述
在这里插入图片描述
3.比较结果:向下调整时间复杂度更低,效率更高。
更直观的理解方法:对于向上调整而言,节点最多的那一层(最高层)需要向上调整的次数最多(有h层就调整h-1次),即大数乘大数;对于向下调整而言,节点最多的那一层(最高层)需要向下调整的次数最少(最高层调整0次),即大数乘小数。因此相比较而言,向下调整建堆时间复杂度更低。

🌈五、无堆结构,对数组进行堆排序

思路:先通过向上或向下调整建堆,在没有堆结构的基础上让数组先成为堆,再通过pop的思想每次得到根节点,即为当前的最大值(最小值)。步骤为:建堆->pop。

☀️升序降序分别建什么堆?

假设现在要将一组数排成升序:
在这里插入图片描述
👻如果在已经有堆结构的基础上排升序,法一是建小堆,pop出来的数是从小到大的,从左到右排列在新开辟的数组空间中,从而将数组排成升序。法二是建大堆,pop出来的数是从大到小的,只需要从空间的右边往左边排,即可排成降序。

👻现在没有堆结构,并且直接在原数组上建堆,意味着不可以新开辟空间,此时需要仔细斟酌排序对应建什么堆:
🎈1.错误方法:排升序建小堆:
第一步:建成小堆后,得到物理结构与逻辑结构,如下:
在这里插入图片描述
在这里插入图片描述
第二步:pop出根节点,得到最小值32,并且刚好在数组最左边。
第三步:用剩下的数据得到次小值。
问题来了:如何排除掉第一个数据?
如果直接移动指针,将下标为1的节点作为根节点,则有破坏堆结构的风险,如图:
在这里插入图片描述
如果将剩下的数重新建堆,则代价太大。
因此排升序不可以建小堆。
🎈2.正确方法:建大堆,每次pop出的数和数组最右边一个数交换,再向下调整从而得出次大的数,从右往左放置从大到小的数,从而排成升序。
具体过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
🎈3.结论:排升序,建大堆;排降序,建小堆。
🎈4.为何有堆结构时可以随意建大小堆,无堆结构时有限制?
本质上是因为:有堆结构时,可以将数据从原先的混乱数组中转移到一片新的空间,只需要将大小堆和放置先后顺序匹配就可排成任意顺序(排升序:如果建大堆,从右往左放,如果建小堆,从左往右放;排降序:如果建大堆,从左往右放,如果建小堆,从右往左放)。无堆结构时,从混乱的一组数据到堆结构再到有序结构,始终是在同一块空间上进行操作的,为了不破坏堆结构,只能用pop和向下调整的方式,因此对什么时候建什么堆有限制(升序建大堆,降序建小堆)。

☀️代码(将数组排成降序)

方法:让数组中的数据一个一个插入到堆的最后面,再向上调整,从而建堆。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Swap(int* a, int* b) {int tmp = *a;*a = *b;*b = tmp;
}
void AdjustDown(int* a, int size, int i) {int parent = i;int child = parent * 2 + 1;while (child < size) {if (child + 1 < size && a[child] > a[child + 1]) {child++;}if (a[parent] > a[child]) {Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}elsebreak;}
}void HeapSort(int* a, int n) {for (int i = (n - 1 - 1) / 2;i >= 0;i--) {AdjustDown(a, n, i);}int end = n - 1;while (end >= 0) {Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}
int main() {int a[] = { 70,65,100,32,50,60 };HeapSort(a, sizeof(a) / sizeof(int));for (int i = 0;i < sizeof(a) / sizeof(int);i++) {printf("%d ", a[i]);}
}

测试结果:
在这里插入图片描述

☀️应用:TopK问题

👻问题背景:一共10000个数,要找出最大的前10个。
👻程序设计:
1.随机生成10000个值小于10000的数
2.随机10挑个数改成大于10000的值(为了方便检查测试结果,如果最终得到的10个数就是这些大于10000的值的话,则说明筛选成功)
👻解决方案:
先对这10000个数的前10个数建小堆,然后后面的9990个数依次和堆顶数据比较,大于堆顶数据的话则顶替这个堆顶数据进堆。
问:为何要对前10个数据建小堆,建大堆会怎样?
答:假设这10000个数中最大的数恰好在前10个中,则建了大堆后,根节点一开始的值就是最大值,后面的9990个数就无法进堆,最终只有根节点的最大值是准确的,剩下的9个值找不到。而建小堆的话,最大的前10个数来了都可以畅通无阻的进堆,然后再向下调整至正确位置,最终堆中剩下的数就是最大的前10个。

代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include<time.h>
#include<stdlib.h>
void Swap(int* a, int* b) {int tmp = *a;*a = *b;*b = tmp;
}
void AdjustDown(int* a, int k, int parent) {int child = parent * 2 + 1;while (child<k) {if (child + 1 < k && a[child + 1] < a[child]) {child++;}if (a[parent] > a[child]) {Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else {break;}}
}
void PrintTopK(int* a, int n, int k) {//对前10个数据建小堆for (int i = (k-2)/2;i >=0 ;i--) {AdjustDown(a, k, i);}//将剩下9990个数据依次与堆顶元素比较//若大于堆顶,则替换堆顶进堆,并向下调整for (int i = 0;i < n - k;i++) {if (a[k + i] > a[0]) {Swap(&a[k + i], &a[0]);AdjustDown(a, k, 0);}}//将前k个数据打印出来for (int i = 0;i < k;i++) {printf("%d ", a[i]);}
}
void TeatTopK() {int n = 10000;int k = 10;srand(time(0));int* a = (int*)malloc(sizeof(int) * n);for (size_t i = 0;i < n;i++) {a[i] = rand() % 10000;}a[10] = 10001;a[29] = 10002;a[300] = 10003;a[790] = 10004;a[1440] = 10005;a[4990] = 10006;a[6900] = 10007;a[6930] = 10008;a[8999] = 10009;a[9999] = 10010;//先展示一下最初始的前10个数for (int i = 0;i < k;i++) {printf("%d ", a[i]);}printf("\n");//再展示筛选出来的前10个数PrintTopK(a, n, k);
}
int main() {TeatTopK();
}

测试结果:
由于不对前10个最大的数有顺序要求,因此每次运行完程序得到的前10个数的顺序不一样。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

兼容jlink OB arm仿真器使用(杜邦线过长导致烧写总是失败)

一、兼容jlink OB的使用&#xff1a; 1、设置中要选择jlink&#xff1b; 2、模式选择SWD模式&#xff08;接三根线&#xff09;&#xff1b; 二、杜邦线过长导致stm32的stlink烧写总是失败 用ST-link烧写提示的错误信息有&#xff1a; Error while accessing a target reso…

Redis中的数据结构

文章目录 第1关&#xff1a;Redis中的数据结构 第1关&#xff1a;Redis中的数据结构 这是上篇文章的第一关&#xff0c;只不过本篇是代码按行做的&#xff0c;方便一下大家使用。 代码如下&#xff1a; redis-cliset hello redislpush educoder-list hellorpush educoder-lis…

51单片机制作数字频率计

文章目录 简介设计思路工作原理Proteus软件仿真软件程序实验现象测量误差和范围总结 简介 数字频率计是能实现对周期性变化信号频率测量的仪器。传统的频率计通常是用很多的逻辑电路和时序电路来实现的&#xff0c;这种电路一般运行较慢&#xff0c;而且测量频率的范围较小。这…

【SpringCloud】注册中心和Ribbon负载均衡

SpringCloud 1.Eureka注册中心 1.1 Eureka的作用 注册中心拉取服务负载均衡远程调用 order-service得知user-service实例地址流程&#xff1a; user-service服务实例启动后&#xff0c;将自己的信息注册到eureka-server&#xff08;Eureka服务端&#xff09;&#xff0c;称…

redis主从复制模式和哨兵机制

目录 第一章、主从复制模式1.1&#xff09;Redis 主从复制模式介绍1.2&#xff09;Redis 主从复制实现、 第二章、哨兵机制2.1&#xff09;容灾处理之哨兵2.2&#xff09;Sentinel 配置 第一章、主从复制模式 1.1&#xff09;Redis 主从复制模式介绍 ①单点故障&#xff1a;数…

honle电源维修UV电源控制器EVG EPS40C-HMI

好乐UV电源控制器维修&#xff1b;honle控制器维修&#xff1b;UV电源维修MUC-Steuermodul 2 LΛmpen D-82166 主要维修型号&#xff1a; EVG EPS 60/120、EVG EPS 100、EVG EPS200、EVG EPS 220、EVG EPS 340、EVG EPS40C-HMI、EVG EPS60 HONLE好乐uv电源维修故障包括&#…

申请开通QMT量化需要多少资金?免费开通!

最近量化交易在市场上大火&#xff0c;很多投资者想要参与进来。QMT量化软件是目前市场上一款比较常见并且强大的量化软件。那开通QMT量化交易软件需要多少资金&#xff1f; QMT量化交易软件是一种专门用于量化交易的工具&#xff0c;它能够帮助投资者通过程序化交易策略进行股…

如何解决“该公众号提供的服务出现故障,请稍后再试”

出现“该公众号提供的服务出现故障&#xff0c;请稍后再试” &#xff0c; 或者是出现 “公众号接口出现异常&#xff0c;请加入微信群接收接口报警” 的提问&#xff0c; 出现这个一般是开发者自身服务器出现问题导致的。 本文我来教大家如何排查。 第一步&#xff1a;加入告…

【力扣周赛】第 115 场双周赛(⭐优化背包DP)(TODO)

文章目录 竞赛链接Q1&#xff1a;2899. 上一个遍历的整数&#x1f4a9;&#xff08;阅读理解题&#xff0c;按题意模拟&#xff09;Q2&#xff1a;2900. 最长相邻不相等子序列 I&#xff08;贪心&#xff09;Q3&#xff1a;2901. 最长相邻不相等子序列 II&#xff08;类似 最长…

算法通关村第一关—白银挑战—链表高频面试算法题—查找两个链表的第一个公共子节点

文章目录 查找两个链表的第一个公共子节点&#xff08;1&#xff09;暴力求解法&#xff08;2&#xff09;使用哈希Hash⭐&#xff08;3&#xff09;使用集合⭐ - 与Hash类似&#xff08;4&#xff09;使用栈⭐&#xff08;5&#xff09;仍有更多方法&#xff0c;作者尚未理解&…

【小布_ORACLE笔记】Part11-1--RMAN Backups

Oracle的数据备份于恢复RMAN Backups 学习第11章需要掌握&#xff1a; 一.RMAN的备份类型 二.使用backup命令创建备份集 三.创建备份文件 四.备份归档日志文件 五.使用RMAN的copy命令创建镜像拷贝 文章目录 Oracle的数据备份于恢复RMAN Backups1.RMAN Backup Concepts&#x…

LeetCode | 965. 单值二叉树

LeetCode | 965. 单值二叉树 OJ链接 首先判断树为不为空&#xff0c;为空直接true然后判断左子树的val&#xff0c;和根的val相不相同再判断右子树的val&#xff0c;和根的val相不相同最后递归左子树和右子树 bool isUnivalTree(struct TreeNode* root) {if(root NULL)retur…

Python解释器的安装【侯小啾python领航班系列(一)】

Python解释器的安装【侯小啾python领航班系列(一)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

深入Spring Security魔幻山谷-获取认证机制核心原理讲解(新版)

文/朱季谦 这是一个古老的传说。 在神秘的Web系统世界里&#xff0c;有一座名为Spring Security的山谷&#xff0c;它高耸入云&#xff0c;蔓延千里&#xff0c;鸟飞不过&#xff0c;兽攀不了。这座山谷只有一条逼仄的道路可通。然而&#xff0c;若要通过这条道路前往另一头的…

Vue + Element ui 实现动态表单,包括新增行/删除行/动态表单验证/提交功能

原创/朱季谦 最近通过Vue Element ui实现了动态表单功能&#xff0c;该功能还包括了动态表单新增行、删除行、动态表单验证、动态表单提交功能&#xff0c;趁热打铁&#xff0c;将开发心得记录下来&#xff0c;方便以后再遇到类似功能时&#xff0c;直接拿来应用。 简化的页…

zabbix6.4.0配置邮件及企微机器人群聊告警

一、邮件告警 根据公司邮箱自行配置&#xff0c;电子邮件、用户账号密码填自己的邮箱账号密码 动作本次使用的默认的&#xff0c;如果为了更加美观可自行修改。 二、企业微信机器人告警 首先在企微上创建群聊&#xff0c;之后添加群聊机器人 将地址复制&#xff0c;后面用 …

MATLAB 模型参考自适应控制 - Model Reference Adaptive Control

系列文章目录 文章目录 系列文章目录前言一、参考模型二、扰动与不确定性模型三、直接 MRAC名义模型参数更新间接 MRAC估计器模型和控制器增益参数更新学习修正参考文献 前言 模型参考自适应控制模块计算控制动作&#xff0c;使不确定的受控系统跟踪给定参考被控对象模型的行为…

【LeetCode刷题笔记】102. 二叉树的层序遍历

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

kkFileView 从源码编译最新安装包

目录 一、前言二、拉取 kkFileView 最新代码三、kkFileView 打包 一、前言 kkFileView 是一个开源的附件在线预览项目&#xff0c;可以让你的项目方便的在线预览附件&#xff0c;包括比如&#xff1a;doc、docx、pdf、xml、xls、xlsx、ppt、pptx、zip、png、jpg、txt、mp4等常…

测评补单助力亚马逊,速卖通,国际站卖家抢占市场,提升转化和评分

想要快速提升商品的销量&#xff0c;测评补单这种方法见效是最快的。特别是新品上线&#xff0c;缺少用户评价&#xff0c;转化率不好&#xff0c;很多商家新品上线都会做测评补单&#xff0c;搞些商品好评&#xff0c;不但可以提升转化&#xff0c;同时在平台也可以获得更多展…