树和堆的精讲

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary_walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互相支持,蟹蟹!
👑👑👑💎👑👑👑

思维导图:

1:树的相关概念
1.1 在数据结构中数的重要性

首先:树在数据结构中扮演着重要的角色,它是一种非线性的数据结构,可以用来表示层次关系或者具有树状结构的数据。

其次:树的常见应用包括文件系统、组织结构、编译器中的抽象语法树

再者:树有个好处,就是当节点有序的时候(即有序树),那么在这个树上搜索一个 节点 是很快的(log级别),所以,现在的索引一般都是用各种树( 数据库 如mysql大多用B+树)

1.2 树的结构

1.3 树的概念

树是一种 非线性 的数据结构,它是由 n n>=0 )个有限结点组成一个具有层次关系的集合。
有一个 特殊的结点,称为根结点 ,根节点没有前驱结点
除根节点外,其余每一个节点被分成多个不相交的小树 ,其中每一个小树 又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0 个或多个后继
树是递归定义的。
1.4 树的常见术语
节点的度 :一个节点含有的子树的个数称为该节点的度(或者说当前节点的 直接后继 );
如上图: A节点 的度为 6
叶节点或终端节点 :度为 0 的节点称为叶节点(没有直接后继的节点);
如上图: B C H I... 等节点为叶节点
非终端节点或分支节点 :度不为 0 的节点( 除根节点和叶子结点之外的节点
如上图: D E F G... 等节点为分支节点
双亲节点或父节点 :若一个节点含有子节点,则这个节点称为其子节点的父节点(注意 没有父亲节点的说法 ) 如上图: A B 的父节点
孩子节点或子节点 :一个节点含有的子树的根节点称为该节点的子节点; 如上图: B A 的孩子节点
兄弟节点 :具 有相同父节点的 节点互称为兄弟节点; 如上图: B C 是兄弟节点
树的度 :一棵树中, 最大的节点的度 称为树的度; 如上图:树的度为 6
节点的层次 :从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;
树的高度或深度 :树中节点的最大层次; 如上图:树的高度为 4(注意: 高度为3 也是对的 ,从数组下标出发)
堂兄弟节点 双亲在同一层 的节点互为堂兄弟;如上图: H I 互为兄弟节点
节点的祖先 :从根到该节点所经分支上的所有节点;如上图: A 是所有节点的祖先
子孙 :以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是 A 的子孙
森林 :由 m m>0 )棵互不相交的树的集合称为森林;
2:树的应用

文件的目录

组织结构:

3:堆的概念
3.1二叉树的概念

因为堆是与二叉树相关的,所以这里就不得不说一下二叉树

二叉树是一种树形数据结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树有很多种不同的形式,比如满二叉树、完全二叉树、平衡二叉树等

注意:

二叉树不存在度 大于2的节点

二叉树的左子树 和右子树的次序是不能颠倒的(颠倒后是不同的树)

3.3 特殊的二叉树
满二叉树 :一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是 说,如果一个二叉树的层数为K ,且结点总数是 2 ^K -1,则它就是满二叉树。
2. 完全二叉树 :完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为 K 的,有n 个结点的二叉树,当且仅当其每一个结点都与深度为 K 的满二叉树中编号从 1 n 的结点一一对 应时称之为完全二叉树。 要注意的是 满二叉树是一种特殊的完全二叉树
3.3 堆的概念
如果有一个关键码的集合  ,把它的所有元素 按完全二叉树的顺序存储 方式存储 在一个一维数组中,
将根节点最大的堆叫做最大堆或大根堆:双亲节点 大于或者等于任何一个子节点(递归定义
根节点最小的堆叫做最小堆或小根堆。双亲节点 小于或者等于任何一个子节点(递归定义
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
4:堆的结构

首先:无论是一个大堆还是个小堆:在结构上都是有根节点,左子树,右子树

 其次:堆要不是大堆,要不是小堆(有序的)

最后:堆的本质其实还是数组在物理结构上,在逻辑结构上是一个二叉树

typedef int HP_DataType;
typedef struct Heap
{HP_DataType* a;int size;//有效数据个数int capacity;//空间容量}HP;

5: 堆的创建

以建一个小堆为例来分析:

 上调算法的思路分析:

1)关键的如何确定每一次的双亲以及孩子节点的下标

二叉树的性质之一: 下标上  child = 2 * parent + 1 

所以双亲的下标:parent = (child -1) / 2 

2)因为每次进入数据最坏的情况下就是把孩子上调到根节点 此时就是最小堆了

所以结束条件的判断  child > 0

3)因为是建一个小堆:所以 若当前孩子节点 的值 > 双亲节点 的值就进行交换

并且交换完后 双亲 和 孩子继续迭代

child = parent ;

parent = (child -1) / 2  ;

 交换
void Swap(HP_DataType* p1, HP_DataType* p2)
{assert(p1);assert(p2);HP_DataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
向上调整代码:
void Adujust_up(HP_DataType* a,int pos)
{assert(a);int child = pos;int parent = (child - 1) / 2;//下标从0开始while (child > 0){if (a[child] < a[parent]) //进行上调{/*int swap = php->a[child];php->a[child] = php->a[parent];php->a[parent] = swap;*/Swap(&(a[child]), &(a[parent]));//再继续下一轮child = parent;parent = (child - 1) / 2;}else//  同时也避免了 parent == child == 0 进入死循环{break;}}
}
数据进堆的代码:
void HP_push(HP* php, HP_DataType x)
{/*建小根堆1:空间是否有2:插入数据  同时 size++3:是否需要对插入的当前节点与双亲节点调整(递归的定义)*/assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : 2 * (php->capacity);//HP_DataType* tmp = (HP_DataType*)mealloc(php->a,sizeof(HP_DataType) * newcapacity);//扩容用reallocHP_DataType* tmp = (HP_DataType*)realloc(php->a,sizeof(HP_DataType) * newcapacity);//扩容用reallocif (tmp == NULL){perror("malloc fail");return ;}//扩容成功php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;Adujust_up(php->a,php->size);//判断插入的节点是否需要向上调整//Adujust_up(&x,php->size);//判断插入的节点是否需要向上调整php->size++;}
5.2:堆的销毁

这个相比较之下就 so easy 

void HP_Destroy(HP* php)
{assert(php);free(php->a);php->size = 0;php->capacity = 0;
}
5.3:堆的删除(删除堆顶元素)

思路分析:我们一般会这样思考:直接删除堆顶元素不就完了吗,但是删除之后的堆不在满足小堆的结构。若是重新建小堆的话,在时间上的开销是很大滴

我们不妨格局打开,难道我就一定从堆顶这一个位置着手吗?

让堆顶元素与堆尾元素的值进行交换;此时堆的结构发生了改变但是相对于直接删除堆顶元素而言这并不是多多问题,此时我们在对改变的那个分支结构进行上调即可

草图见下:

 和向上调整思维一样:需要考虑双亲与孩子节点的下标

parent = 0;

child = 2 * parent + 1 ; //注意数组下标是从0 开始

 让父亲节点来到孩子节点的位置  parent =  child 

此时孩子节点再继续下走 child = 2 * parent + 1 ; 

注意:

1:采用假设思想 假设左孩子 节点在值最小

2:循环判断条件 孩子节点的下标 要 小于 数组的大小

3:找左右孩子最小节点的时候,若是右孩子节点值小,判断条件是 必须保证右孩子节的存在  child + 1  < num   

向下调整代码:

void Adujust_Down(HP_DataType* a, int num,int parent_i)//必须保证左右子树是小根堆   第三个参数:双亲下标
{/*向下调整:保证此时还是一个小根堆  当 parent > child 进行交换关键是不知道左右孩子谁是最小节点*/assert(a);int parent = parent_i;int child = 2 * parent + 1;  //假设左孩子为最小的孩子节点while (child < num) //保证孩子在数组里面{if (child + 1 < num && a[child + 1] > a[child ]) //child + 1 < num 保证有孩子存在    a[child + 1] < a[child + 1] 右孩子可能是最小节点{child += 1;//更新最小孩子节点  }if (a[parent] < a[child]){Swap(&a[parent], &a[child]);//父 与 子 依次下移parent = child;child = 2 * parent + 1;}elsebreak;父 与 子 依次下移//parent = child;//child = 2 * parent + 1;//	以上2句代码在这err}
}

 堆顶元素删除代码:

void HP_Pop(HP* php)
{/*把堆顶元素与堆尾元素的值进行交换;其次删除最后检查是否需要 进行向下调整注意对堆进行调整的时候必须保证 左右子树是有序堆*/assert(php);assert(!HP_Empty(php));Swap(&php->a[0], &php->a[php->size - 1]);php->size--;Adujust_Down(php->a, php->size,0);//必须保证是小根堆   }
5.4:堆的判空
bool HP_Empty(HP* php)
{assert(php);return php->size == 0;
}
5.5:堆顶元素获取
HP_DataType HP_Top(HP* php)
{assert(php);assert(!HP_Empty(php));return php->a[0];
}
 5.6 完整代码

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>//堆的本质就是数组  注意:数组不一定是堆
/*
成为堆的条件
1:一定是完全二叉树 (包括满二叉树)
2:双亲节点的val > \ < 孩子节点的val(递归定义)
*/typedef int HP_DataType;
typedef struct Heap
{HP_DataType* a;int size;//有效数据个数int capacity;//空间容量}HP;void HP_Init(HP* php);
void HP_Destroy(HP* php);
void HP_push(HP* php,HP_DataType x);
void HP_Pop(HP* php);
bool HP_Empty(HP* php);
int HP_Size(HP* php);
HP_DataType HP_Top(HP* php);
void Swap(HP_DataType* p1, HP_DataType* p2);void Adujust_Down(HP_DataType* a, int num, int parent_i);//必须保证左右子树是小根堆   第三个参数:双亲下标
void Adujust_up(HP_DataType* a, int pos);

Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"void HP_Init(HP* php)
{assert(php);php->a = NULL;php->size= 0;php->capacity = 0;
}
void HP_Destroy(HP* php)
{assert(php);free(php->a);php->size = 0;php->capacity = 0;
}
void Swap(HP_DataType* p1, HP_DataType* p2)
{assert(p1);assert(p2);HP_DataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void Adujust_up(HP_DataType* a,int pos)
{assert(a);int child = pos;int parent = (child - 1) / 2;//下标从0开始while (child > 0){if (a[child] < a[parent]) //进行上调{/*int swap = php->a[child];php->a[child] = php->a[parent];php->a[parent] = swap;*/Swap(&(a[child]), &(a[parent]));//再继续下一轮child = parent;parent = (child - 1) / 2;}else//  同时也避免了 parent == child == 0 进入死循环{break;}}
}void HP_push(HP* php, HP_DataType x)
{/*建小根堆1:空间是否有2:插入数据  同时 size++3:是否需要对插入的当前节点与双亲节点调整(递归的定义)*/assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : 2 * (php->capacity);//HP_DataType* tmp = (HP_DataType*)mealloc(php->a,sizeof(HP_DataType) * newcapacity);//扩容用reallocHP_DataType* tmp = (HP_DataType*)realloc(php->a,sizeof(HP_DataType) * newcapacity);//扩容用reallocif (tmp == NULL){perror("malloc fail");return ;}//扩容成功php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;Adujust_up(php->a,php->size);//判断插入的节点是否需要向上调整//Adujust_up(&x,php->size);//判断插入的节点是否需要向上调整php->size++;}
void Adujust_Down(HP_DataType* a, int num,int parent_i)//必须保证左右子树是小根堆   第三个参数:双亲下标
{/*向下调整:保证此时还是一个小根堆  当 parent > child 进行交换关键是不知道左右孩子谁是最小节点*/assert(a);int parent = parent_i;int child = 2 * parent + 1;  //假设左孩子为最小的孩子节点while (child < num) //保证孩子在数组里面{if (child + 1 < num && a[child + 1] > a[child ]) //child + 1 < num 保证有孩子存在    a[child + 1] < a[child + 1] 右孩子可能是最小节点{child += 1;//更新最小孩子节点  }if (a[parent] < a[child]){Swap(&a[parent], &a[child]);//父 与 子 依次下移parent = child;child = 2 * parent + 1;}elsebreak;父 与 子 依次下移//parent = child;//child = 2 * parent + 1;//	以上2句代码在这err}
}void HP_Pop(HP* php)
{/*把堆顶元素与堆尾元素的值进行交换;其次删除最后检查是否需要 进行向下调整注意对堆进行调整的时候必须保证 左右子树是有序堆*/assert(php);assert(!HP_Empty(php));Swap(&php->a[0], &php->a[php->size - 1]);php->size--;Adujust_Down(php->a, php->size,0);//必须保证是小根堆   }
bool HP_Empty(HP* php)
{assert(php);return php->size == 0;
}
int HP_Size(HP* php)
{assert(php);return php->size;
}
HP_DataType HP_Top(HP* php)
{assert(php);assert(!HP_Empty(php));return php->a[0];
}
6:与堆相关算法的时间复杂度以及证明
6.1 下调算法来建堆

下调的时间复杂度 O(N)

证明如下:

下面就以最坏的情况来分析:满二叉树

6.2 上调算法来建堆

对应时间复杂度: N * (log N)     注意: N:节点个数

证明如下:

 综合来看的话,虽然上调和下调的算法都可以完成建堆,但是在效率:下调 的算法更友好

7: 堆的应用
7.1堆排序

通过上面时间复杂度的解析想必堆在排序问题上自然也是占上风的。

7.1.1 把数据排成升序

若是想把一组数据排成升序我们到底是建大堆还是建小堆???

 可能有些人会说,自然是建小堆了,这样每次从堆顶取出的元素永远都是当前堆中最小 的元素。确实是没有问题,但是每取出堆顶的元素后,堆中的数据不再是满足父与子之间的关系了,这就需要对堆来进行调整,也就是对余下的 N-1个元素重新建堆

我们可以建大堆:

1)首先把数组的数据进行调整,建成一个大堆,采用下调的方法

 从第一个非叶子节点开始向下调整:孩子节点值 大于双亲节点值就进行调整;

直至调整到根节点为止

2)堆顶元素与堆尾元素进行交换,因为堆顶元素是当前堆中最大的了

3):再对余下的n-1个数进行调整找出最大,放在倒数 第二个位置

其实就是把每次堆顶元素进行头插,最终就变成升序数组

数组原来数据:a[ ] = {9,5,11,15,12,13}

运行结果:

升序排列的结果:

代码:

void Heap_Qsort(int* a, int num)
{/*构造一个升序的堆1:需要先建一个大堆2:堆顶与堆尾交换3:下调*/assert(a);int pos = 0;//把数组排成一个大堆for (pos = (num - 1 - 1) / 2; pos >= 0; pos--){Adujust_Down(a, num, pos);}// 堆顶与堆尾交换 &&  对非堆尾元素进行调整for (pos = num - 1; pos >= 0; pos--){Swap(&a[0], &a[pos]);// 依次选出堆顶最大的元素Adujust_Down(a, pos, 0);//此时pos 即表示 堆尾也表示 每次去掉堆尾元素的个数}}

7.1.2 把数据排成降序

相信有了前面升序排列的思维,再来这个降序排列应该是不在话下。

分析:

首先把数组整成一个小堆

其次就是堆顶与堆尾元素进行交换;交换之后在对非堆尾的元素进行下调

排成小堆的结果:

降序的结果:

 

对应代码:可以复用上次升序的结果注意,有些地方需要改动

7.2经典的TopK问题

Top-K 问题是一类常见的算法问题,其中目的是从一组元素中找到排名前K的元素。具体来说,对于给定的一组数据。Top-K 问题要求找到其中最大(或最小)的K个元素。

生活中 的栗子:

 当数据量非常大的时候(100万亿级别),我们就不能对这100万亿的数据全部进行排序了,

这是为什么呢?

因为内存的空间是非常有限的,这100万亿的数不会存储在内存上,而是在磁盘上,我们需要从文件里面进行读写 的操作。

 所以说我们可以先对100万亿里面的前 K 个数据进行建堆,注意此时堆的大小就是固定的,就只存储K 个数据;

其次依次从余下的数据里面取出与堆顶元素进行比较,若满足指定的条件,则把当数据进行与堆顶元素 的交换

最后:交换之后再对堆中数据进行调整;之后就是重复以上操作,直至数组里面余下数据与堆顶元素比较完之后,堆中的K个数据就是所求

求最大的前三个数据:

首先对前3 数据进行建一个小堆,注意这里不能建大堆(若是建大堆的话,可能最大的数据在前三个数,其余2个数据在余下的 N-K个数里面,这样就不能搞了)

若是大于堆尾元素就替换掉当前的堆尾元素,并对当前堆中数据进行建小堆 的调整

草图见下:

 代码:

void Print_TopK(int k ,int*a,int num_a)
{/*TopK 终极问题:当数据足够大的时候,面临空间的问题:直接减堆解决不了问题只能对前K个数先建堆 ==》在对后 n-k个数据依次进行与堆顶数据判断,是否取代当前堆顶元素 ==》 取代后需要重新调整*///找最大的K个数int* topk_arr = (int*)malloc(sizeof(int) * k);if (topk_arr == NULL){return;}// 前 K个数写到topK_arr这个数组里面int i = 0;for (i; i < k; i++){topk_arr[i] = a[i];}//对前K个数进行小堆的建立int pos = k;for (pos = (k - 1 - 1) / 2; pos >= 0; pos--){Adujust_Down(topk_arr, pos, 0);}//依次判断是否替换堆顶元素for (i = k; i < num_a; i++){if (a[i] > topk_arr[0]){Swap(&a[i], &topk_arr[0]);Adujust_Down(topk_arr, k, 0);}}//对最大的前K个数降序输出  for (i = k - 1; i >= 0; i--){Swap(&topk_arr[0], &topk_arr[i]);  //注意堆尾下标每次是不同的Adujust_Down(topk_arr, i, 0); }for ( i = 0; i < k; i++){printf("%d\n", topk_arr[i]);}}

 运行结果:

结语:

关于堆这一结构在我们日常生活中应用是非常广泛的,当然了对于这个TopK的问题,在面试中也是不可避免的!以上就是我share 的内容了,对于这块的知识体系着实不太好理解,难度相比之前的链表也不在一个层次,我们需要做到物理结构与逻辑结构的双向结合,当然了画图自然是必不可少(对于我这种小白而言,脑子转不过来)。希望此篇博客可以对你有些帮助,要是觉得不错的话,还希望各位大佬们多多支持(这篇博客也是倾注了不少尽力)。

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

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

相关文章

告别你的朝九晚五的工作。

告别你的朝九晚五的工作。 以下是6个网站&#xff0c;你可以从任何地方获得100美元到2000美元的报酬&#xff1a; &#xff08;第3个网站最正规&#xff09; 1. Honeygain 分享未使用的互联网带宽换取现金。 功能包括推荐系统、JumpTask模式、成就和每日幸运抽奖。 非常适…

机构如何搭建一个在线课程教学平台?

随着数字化教育的兴起&#xff0c;越来越多的教育机构开始考虑建立自己的在线课程教学平台。这一趋势不仅顺应了时代的发展&#xff0c;而且为教育行业带来了诸多便利和优势。构建一个在线教学平台可以帮助机构拓宽服务范围、提升教学质量、增强学生体验&#xff0c;并且能够有…

optuna,一个好用的Python机器学习自动化超参数优化库

🏷️个人主页:鼠鼠我捏,要死了捏的主页 🏷️付费专栏:Python专栏 🏷️个人学习笔记,若有缺误,欢迎评论区指正 前言 超参数优化是机器学习中的重要问题,它涉及在训练模型时选择最优的超参数组合,以提高模型的性能和泛化能力。Optuna是一个用于自动化超参数优化的…

美容小程序:让预约更简单,服务更贴心

在当今繁忙的生活节奏中&#xff0c;美容预约常常令人感到繁琐和疲惫。为了解决这个问题&#xff0c;许多美容院和SPA中心已经开始采用美容小程序来简化预约流程&#xff0c;并提供更加贴心的服务。在这篇文章中&#xff0c;我们将引导您了解如何制作一个美容小程序&#xff0c…

2024 年 2 月 TIOBE 指数:最流行的 10 种编程语言

Go 进入了 TIOBE 指数的前 10 名&#xff0c;这是谷歌编程语言有史以来的最高位置。 在 2024 年 2 月的 TIOBE 软件最受欢迎的编程语言列表中&#xff0c;Python、C 和 C 保持了它们的领先地位&#xff08;图 A&#xff09;。TIOBE 的专有积分系统考虑了根据多种大型搜索引擎&…

Tailscale实现内网穿透、异地组网、远程访问

文章目录 Tailscale简介主要功能适用场景使用Tailscale的优势如何开始使用Tailscale总结参考资料注册登录Tailscale账号并下载客户端禁用秘钥过期简单使用设备添加 - 组网Linux安装Tailscale,实现设备添加Tailscale 中的 DERP 简介什么是 DERP?DERP 的优势DERP 的工作原理DER…

初识KMP算法

目录 1.KMP算法的介绍 2.next数组 3.总结 1.KMP算法的介绍 首先我们会疑惑&#xff0c;什么是KMP算法&#xff1f;这个算法是用来干什么的&#xff1f; KMP&#xff08;Knuth-Morris-Pratt&#xff09;算法是一种用于字符串匹配的经典算法&#xff0c;它的目标是在一个主文本…

MySQL数据库基础(七):DDL数据表操作

文章目录 DDL数据表操作 一、数据表的基本操作 1、数据表的创建 2、查询已创建数据表 3、修改数据表信息 ① 数据表字段添加 ② 修改字段名称或字段类型 ③ 删除某个字段 ④ 修改数据表名称 4、删除数据表 二、字段类型详解 1、整数类型 2、浮点类型 3、日期类型…

机器学习入门--门控循环单元(GRU)原理与实践

GRU模型 随着深度学习领域的快速发展&#xff0c;循环神经网络&#xff08;RNN&#xff09;已成为自然语言处理&#xff08;NLP&#xff09;等领域中常用的模型之一。但是&#xff0c;在RNN中&#xff0c;如果时间步数较大&#xff0c;会导致梯度消失或爆炸的问题&#xff0c;…

蓝桥杯嵌入式STM32G431RBT6知识点(主观题部分)

目录 1 前置准备 1.1 Keil 1.1.1 编译器版本及微库 1.1.2 添加官方提供的LCD及I2C文件 1.2 CubeMX 1.2.1 时钟树 1.2.2 其他 1.2.3 明确CubeMX路径&#xff0c;放置芯片包 2 GPIO 2.1 实验1&#xff1a;LED1-LED8循环亮灭 ​编辑 2.2 实验2&#xff1a…

Gitlab CI/CD docker命令报错:/usr/bin/bash: line 136: docker:command not found

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

深入实战:ElasticSearch的Rest API与迭代器模式在高效查询中的应用

在我们公司&#xff0c;大多数Java开发工程师在项目中都有使用Elasticsearch的经验。通常&#xff0c;他们会通过引入第三方工具包或使用Elasticsearch Client等方式来进行数据查询。然而&#xff0c;当涉及到基于Elasticsearch Rest API的/_sql?formatjson接口时&#xff0c;…

2 物理层(三):数据传输的方式,同步传输和异步传输

目录 1 数据的传输方式1.1 并行传输1.2 串行传输 2 同步传输和异步传输2.1 同步传输2.2 异步传输2.3 同步和异步传输对比 1 数据的传输方式 在数据通信中&#xff0c;数据传输方式有并行传输和串行传输两种 1.1 并行传输 定义&#xff1a;并行传输是指数据以成组的方式在多个…

NC 输出模板自定义变量使用加减乘除余等公式计算时无法显示结果的问题处理办法

NC 输出模板自定义变量使用加减乘除余等公式计算时无法显示结果的问题处理办法 比如&#xff0c;求两个字段的差&#xff0c;如果这样写&#xff0c;模板打印输出的时候&#xff0c;是不会显示有值的&#xff1a; sub(vouchercreditamount, voucherdebitamount) 或者 voucherc…

picker选择器-年月日选择

从底部弹起的滚动选择器。支持五种选择器&#xff0c;通过mode来区分&#xff0c;分别是普通选择器&#xff0c;多列选择器&#xff0c;时间选择器&#xff0c;日期选择器&#xff0c;省市区选择器&#xff0c;默认是普通选择器。 学习一下日期选择器 平台差异说明 日期选择默…

K8s进阶之路-控制器无状态服务:

RC/RS/Deployment 控制器 deployment无状态&#xff08;最常用&#xff09;&#xff1a; nginx和Apache statefulset有状态&#xff1a; mysql和redis damonset初始化 job一次性任务 cronjob任务计划 1无状态&#xff1a;不会对本地环境产生依赖如&#xff1a;nginx和Apache …

Kubernetes基础(二十二)-k8s持久化存储详解

1 volume 1.1 介绍 在容器中的磁盘文件是短暂的&#xff0c;当容器崩溃时&#xff0c;Kubelet会重新启动容器&#xff0c;但容器运行时产生的数据文件都将会丢失&#xff0c;之后容器会以最干净的状态启动。另外&#xff0c;当一个Pod运行多个容器时&#xff0c;各个容器可能…

新版Java面试专题视频教程——框架篇

新版Java面试专题视频教程——框架篇 框架篇 01-框架篇介绍02-Spring-单例bean是线程安全的吗03-Spring-AOP相关面试题04-Spring-事务失效的场景05-Spring-bean的生命周期5.1 BeanDefinition 06-Spring-bean的循环依赖(循环引用)6.1 一般对象的循环依…

【C++】类与对象的项目实践 — 日期管理工具

类与对象的实践 项目背景项目需求项目实现1 日期结构设计2 构造函数2.1 全缺省构造函数2.2 拷贝构造函数2.3 析构函数 3 赋值运算符重载3.1 重载3.2 重载重载前置 和 后置 4 关系操作符重载5 工具方法5.1 计算日期差5.2 日期转换为字符串5.3 通过字符串构建对象 完整源代码Dat…

云数贸云生活中心:用云生活理念引领社会和谐发展

在数字经济的浪潮下&#xff0c;云数贸云生活中心不仅在科技进步与文明程度上作出了积极贡献&#xff0c;更在推动社会和谐、承担企业社会责任方面展现出了模范作用。通过与“草根互助爱心社区”的紧密合作&#xff0c;云数贸云生活中心正致力于构建一个更加和谐、互助的社会环…