DS:二叉树的顺序结构及堆的实现

news/发布时间2024/5/14 15:02:25

                                       创作不易,兄弟们给个三连!!

一、二叉树的顺序存储

      顺序结构指的是利用数组来存储,一般只适用于表示完全二叉树,原因如上图,存储不完全二叉树会造成空间上的浪费,有的人又会问,为什么图中空的位置不能存储呢??原因是我们需要根据数组的下标关系才能访问到对应的节点!!有以下两个下标关系公式:

1、父亲找孩子:leftchild=parent*2+1,rightchild=parent*2+2

2、孩子找父亲:parent=(child-1)/2   要注意,这边无论用左孩子算还是右孩子算都是可以的,因为一般俩说,(child-1)/2 由于int类型向下取整的特点,所以得到的结果都是一样的!!

      所以我们想要上面这种方式去访问节点,并且还不希望有大量的空间浪费,现实中只有堆才会使用数组存储,二叉树的顺序存储中在物理上是一个数组,再逻辑上是一颗二叉树!!

二、堆的概念及结构

    现实中我们把堆(类似完全二叉树)使用顺序结构来存储,要注意这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分区。

   如果有一个关键码的集合k,我们将他的全部元素按照完全二叉树的存储逻辑放在一个一维数组中,则成为堆,根节点最大的堆叫做大堆,根节点最小的堆叫做小堆。 

堆的性质:

1、堆中某个节点的值总是不大于或不小于其父节点的值

2、堆总是一颗完全二叉树

注意:并不一定有序 

三、堆的实现

假设我们实现小堆

3.1 相关结构体的创建

跟顺序表的形式是一样的,但是换了个名字

typedef int HPDataType;
typedef struct Heap
{HPDataType * a;int size;int capacity;
}Heap;

3.2 堆的初始化

void HeapInit(Heap* php)
{assert(php);php->a = NULL;php->capacity = php->size = 0;
}

3.3 堆的插入

堆的插入很简单,但是我们要保证堆插入后还能维持堆的形状

所以我们在插入后,还要进行向上调整,也就是孩子要根据下标关系找到自己的父亲去比较,小就交换

void HeapPush(Heap* php, HPDataType x)
{assert(php);//首先要判断是否需要扩容if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDataType* temp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newcapacity);if (temp == NULL){perror("malloc fail");exit(1);}//扩容成功php->a = temp;php->capacity = newcapacity;}//扩容后,我们插入这个元素并size++php->a[php->size++] = x;//但是插入之后可能会破坏堆的结构,所以我们需要这个元素和他的父辈进行逐个比较, AdjustUp(php->a,php->size-1);//封装一个向上调整函数,传入数组和新加元素的下标
}

3.4 向上调整算法

void AdjustUp(HPDataType* a, int child)
{assert(a);//通过孩子找父亲  parent=(child-1)/2int parent = (child - 1) / 2;//孩子和父亲开始比较,如果孩子小,就交换,如果孩子大,退出循环while (child>0)//如果孩子变成了根节点,就没有必要再找了,因为已经没有父母了//如果用parent>=0来判断,那么由于(0-1)/2是-1/2,取整后还是0,就会一直死循环,所以必须用孩子来当循环条件{if (a[child] < a[parent])//孩子小,交换{Swap(&a[child], &a[parent]);//但是交换过后,可能还需要继续往上比,所以我们要让原来的父亲变成孩子,然后再找新的父亲进行比较child = parent;parent = (child - 1) / 2;}else//孩子大,退出break;}
}

注:这里的向上调整算法和后面向下调整算法我们都不用跟堆有关的接口,原因就是这个算法的运用范围很广,可以用在堆排序以及top-k问题中!!

3.5 交换函数

void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType temp = *p1;*p1 = *p2;*p2 = temp;
}

3.6 堆的删除

         一般来说,如果直接删除堆的最后一个元素,其实是没什么意义的,一行代码就可以搞定,没必要封装什么函数,所以这里的堆的删除指的是删除根部的元素!!

        

void HeapPop(Heap* php)//一般来说,堆中的删除指的是删除根位置的数据
//如果直接删除根然后往前挪动一位,那么亲缘关系就会十分混乱,为了能够尽量在调整中减少对关系的改变
//我们将根部元素与最后一个元素进行交换之后再删除,此时的根是原先的最后一个元素
//然后将该元素进行向下调整(封装一个函数,传入数组、元素个数、)
{assert(php);assert(!HeapEmpty(php));//为空的话没有删除的必要Swap(&php->a[0], &php->a[php->size - 1]);php->size--;//开始向下调整AdjustDown(php->a, php->size,0);
}

3.7 向下调整算法

void AdjustDown(HPDataType* a, int n,int parent)
{assert(a);//此时根部为原来的最后一个元素,往下比较//即通过父亲去找到自己的孩子,如果孩子比自己小,就得交换位置,如果孩子比自己大,就退出//但是因为父亲有一个左孩子parent*2+1,右孩子parent*2+2,我们选择孩子中较小的和自己交换int child = parent * 2 + 1;//假设左孩子比右孩子小while (child<n)//当child超出个数的时候结束{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;}elsebreak;//如果孩子大于等于父亲,直接退出}
}

       在上述算法中,我们应用了先假设再推翻的方法,一开始我们先假设左孩子比较小,然后我们再给个条件判断,如果左孩子大于右孩子,假设不成立,再推翻,这样可以保证我们的child变量一定是较小的孩子!! 

       虽然这里的parent很明显是从a[0]开始,好像不需要专门去传一个parent的参数,但是这也是为了之后的堆排序做准备!

3.8 取堆顶的数据

HPDataType HeapTop(Heap* php)
{assert(php);assert(!HeapEmpty(php));//为空的话没有取的必要return php->a[0];
}

3.9 堆的数据个数

int HeapSize(Heap* php)
{assert(php);return php->size;
}

3.10 堆的判空

bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}

3.11 堆的销毁

void HeapDestory(Heap* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}

3.12 堆的打印(测试)

我们要实现堆的打印,利用我们之前封装的函数,每获取一次堆顶元素就删除一次,直到堆删完就可以获取全部的元素了!!

#include"Heap.h"
int main()//该方法实现堆的顺序打印
{Heap hp;HeapInit(&hp);int a[] = { 55,100,70,32,50,60 };for (int i = 0; i < sizeof(a) / sizeof(int); i++)HeapPush(&hp, a[i]);//不断进堆while (!HeapEmpty(&hp)){int top = HeapTop(&hp);printf("%d\n", top);HeapPop(&hp);}HeapDestory(&hp);return 0;
}

前面只是先创建一个堆,从while循环开始才是实现对堆的打印!!

运行结果 :32 50 55 60 70 100

          我们发现了一个情况:按道理来说堆只有父子节点之间有大小关系,兄弟之间没有的,但是我们最后打印出来的结果却完成了排序!!!下面我们来进行分析

     总之任何一个堆,我们都可以通过不断地pop去实现它的顺序打印!!堆排序后面会介绍!

四、堆实现的全部代码

4.1 Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int HPDataType;
typedef struct Heap
{HPDataType * a;int size;int capacity;
}Heap;void Swap(HPDataType* p1, HPDataType* p2);//实现父亲和孩子的交换
void AdjustUp(HPDataType* a, int child);//向上调整算法// 堆的初始化
void HeapInit(Heap* php);
// 堆的插入
void HeapPush(Heap* php, HPDataType x);
// 堆的删除
void HeapPop(Heap* php);
// 取堆顶的数据
HPDataType HeapTop(Heap* php);
// 堆的数据个数
int HeapSize(Heap* php);
// 堆的判空
bool HeapEmpty(Heap* php);
// 堆的销毁
void HeapDestory(Heap* php);

4.2 Heap.c

#include"Heap.h"
//当前实现小堆
void HeapInit(Heap* php)
{assert(php);php->a = NULL;php->capacity = php->size = 0;
}void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType temp = *p1;*p1 = *p2;*p2 = temp;
}void AdjustUp(HPDataType* a, int child)
{assert(a);//通过孩子找父亲  parent=(child-1)/2int parent = (child - 1) / 2;//孩子和父亲开始比较,如果孩子小,就交换,如果孩子大,退出循环while (child>0)//如果孩子变成了根节点,就没有必要再找了,因为已经没有父母了//如果用parent>=0来判断,那么由于(0-1)/2是-1/2,取整后还是0,就会一直死循环,所以必须用孩子来当循环条件{if (a[child] < a[parent])//孩子小,交换{Swap(&a[child], &a[parent]);//但是交换过后,可能还需要继续往上比,所以我们要让原来的父亲变成孩子,然后再找新的父亲进行比较child = parent;parent = (child - 1) / 2;}else//孩子大,退出break;}
}void AdjustDown(HPDataType* a, int n,int parent)
{assert(a);//此时根部为原来的最后一个元素,往下比较//即通过父亲去找到自己的孩子,如果孩子比自己小,就得交换位置,如果孩子比自己大,就退出//但是因为父亲有一个左孩子parent*2+1,右孩子parent*2+2,我们选择孩子中较小的和自己交换int child = parent * 2 + 1;//假设左孩子比右孩子小while (child<n)//当child超出个数的时候结束{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;}elsebreak;//如果孩子大于等于父亲,直接退出}
}void HeapPush(Heap* php, HPDataType x)
{assert(php);//首先要判断是否需要扩容if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDataType* temp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newcapacity);if (temp == NULL){perror("malloc fail");exit(1);}//扩容成功php->a = temp;php->capacity = newcapacity;}//扩容后,我们插入这个元素并size++php->a[php->size++] = x;//但是插入之后可能会破坏堆的结构,所以我们需要这个元素和他的父辈进行逐个比较, AdjustUp(php->a,php->size-1);//封装一个向上调整函数,传入数组和新加元素的下标
}void HeapPop(Heap* php)//一般来说,堆中的删除指的是删除根位置的数据
//如果直接删除根然后往前挪动一位,那么亲缘关系就会十分混乱,为了能够尽量在调整中减少对关系的改变
//我们将根部元素与最后一个元素进行交换之后再删除,此时的根是原先的最后一个元素
//然后将该元素进行向下调整(封装一个函数,传入数组、元素个数、)
{assert(php);assert(!HeapEmpty(php));//为空的话没有删除的必要Swap(&php->a[0], &php->a[php->size - 1]);php->size--;//开始向下调整AdjustDown(php->a, php->size,0);
}HPDataType HeapTop(Heap* php)
{assert(php);assert(!HeapEmpty(php));//为空的话没有取的必要return php->a[0];
}int HeapSize(Heap* php)
{assert(php);return php->size;
}bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}void HeapDestory(Heap* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}

4.3 test.c(测试)

#include"Heap.h"
int main()//该方法实现堆的顺序打印
{Heap hp;HeapInit(&hp);int a[] = { 55,100,70,32,50,60 };for (int i = 0; i < sizeof(a) / sizeof(int); i++)HeapPush(&hp, a[i]);//不断进堆while (!HeapEmpty(&hp)){int top = HeapTop(&hp);printf("%d\n", top);HeapPop(&hp);}HeapDestory(&hp);return 0;
}

五、堆的应用

5.1 堆排序

要对数组排序前,我们要用堆排序,首先要建堆!

大家看看之前堆的打印时的测试代码逻辑的方法

就是我们得到一个数组,就先建堆,然后先把数组push进去,再pop出来,是可以实现有序的

但是现在我们的需求不是打印出来,而是将他排好序后放进数组里,所以们可以这么写:

void HeapSort(int* a, int n)
{HP hp;HeapInit(&hp);// N*logNfor (int i = 0; i < n; ++i){HeapPush(&hp, a[i]);}// N*logNint i = 0;while (!HeapEmpty(&hp)){int top = HeapTop(&hp);a[i++] = top;HeapPop(&hp);}HeapDestroy(&hp);
}

 这个方法固然是可以的,但是很麻烦,原因如下:

1、每次都要建立一个新的堆,然后再销毁,比较麻烦,而且空间复杂度比较高 

2、我通过把数组放进变成堆,还要再把堆拷贝到数组中,数据的拷贝是很繁琐的!!

所以我们要思考一种方式避免数据的拷贝,所以就有了向上调整建堆和向下调整建堆的方法了!!

也就是我们在原数组的基础上直接建堆,然后向下调整排序即可,下面会详细介绍

5.1.1 向上调整建堆

 假设数组有n个元素

for (int i = 1; i < n; i++)
{AdjustUp(a, i);
}

5.1.2 向下调整建堆

for (int i = (n-1-1)/2; i >= 0; i--)
{AdjustDown(a, n, i);
}

5.1.3 堆排序的实现

那我们究竟选择向下建堆好还是向下建堆好呢??我们来分析一下

所以我们发现向上调整建堆的时间复杂度大概是N*logN,而向下调整建堆的时间复杂度是N

其实们在推导的时候也能发现,向上调整建堆是节点多的情况调整得多,节点少的情况调整的少,次数是多*多+少*少 ,而向下调整建堆是节点多的情况调整得少,节点少的情况调整的多,次数是多*少+少*多,显然是向下调整建堆是更有优势的!!

     接下去我们建好堆,就要想着怎么去排序了,我们思考一下,之前我们对堆的打印时,不断pop打印出来有序结果的原因是什么??原因就是pop函数里的向下调整算法!!每一次交换根节点和尾节点,将每个节点进行向下调整,最后就可以得到有序的

 

 因为我们之前实现的向下调整算法是小堆的,所以我们这边来实现一个降序的堆排序算法

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;}
}

 

 如果我们想实现升序,将向下调整算法按照大堆的规则改一下就行 

向下调整算法和向上调整算法的空间复杂度都是(logN) 

堆排序中,建堆的时间复杂度是o(N),排序的时间复杂度是(N*logN)所以堆排序的总时间复杂度是N*logN

5.2 TOP-K问题

Top-k问题:即求数据中前k个最大的元素或者是最小的元素,一般情况下的数据量都比较大!

比如:专业前10名、世界五百强、富豪榜前十

堆排序能够帮助我们在大量数据中筛选出最好的几个。

5.2.1 思路

        比如说我们要从1000个学生的成绩中找到前10个分数最高的,方法就是将所有的数据放在一个数组里,直接建大堆,然后pop9次就可以找到了(pop中的向下调整算法可以使得每次pop出去的都是最大值,然后pop9次的原因是因为第10次就可以直接去获取堆顶元素即可)

但是有些情况,上述思路解决不了,分析:

5.2.2 通过数组验证TOP-K

void PrintTopK(int* a, int n, int k)
{//建前k个建小堆for (int i = (k - 1 - 1) / 2; i >= 0; i--)AdjustDown(a, k, i);//将剩余n个数据不断与堆顶元素比较,大就交换,然后向下调整for (int i = k; i < n; i++){if (a[i] > a[0]){a[0] = a[i];//直接覆盖就行,不用交换AdjustDown(a, k, 0);}}//打印for(int i=0;i<k;i++)printf("%d ", a[i]);
}void TestTopk()
{int n = 10000;int* a = (int*)malloc(sizeof(int) * n);srand((unsigned int)time(NULL));for (size_t i = 0; i < n; ++i){a[i] = rand() % 1000000;//随机数范围0-999999}
// 为了能够方便找到这些数a[5] = 1000000 + 1;a[1231] = 1000000 + 2;a[531] = 1000000 + 3;a[5121] = 1000000 + 4;a[115] = 1000000 + 5;a[2335] = 1000000 + 6;a[9999] = 1000000 + 7;a[76] = 1000000 + 8;a[423] = 1000000 + 9;a[3144] = 1000000 + 10;PrintTopK(a, n, 10);
}int main()
{TestTopk();return 0;
}

5.2.3 通过文件验证TOP-K

其实用数组的方法,并不能有效地模拟,我们可以尝试用文件的方式来验证

void CreateNDate()
{// 造数据int n = 10000;srand((unsigned int)time(NULL));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (size_t i = 0; i < n; ++i){int x = rand() % 1000000;fprintf(fin, "%d\n", x);//将随机数写进文件}fclose(fin);
}void PrintTopK(int k)
{const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen fail");return;}int* kminheap = (int*)malloc(sizeof(int) * k);if (kminheap == NULL){perror("malloc fail");return;}for (int i = 0; i < k; i++){fscanf(fout, "%d", &kminheap[i]);//从文件读取数据}// 建小堆for (int i = (k - 1 - 1) / 2; i >= 0; i--){AdjustDown(kminheap, k, i);}int val = 0;while (!feof(fout))//feof是文件结束的标识,如果返回1,则说明文件结束{fscanf(fout, "%d", &val);//fscaf的光标闪动到原先的位置,所以会从k的位置开始读if (val > kminheap[0]){kminheap[0] = val;AdjustDown(kminheap, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", kminheap[i]);}printf("\n");
}
int main()//该方法实现堆的顺序打印
{CreateNDate();PrintTopK(5);return 0;
}

友友们上述代码有不理解的,看看博主关于文件操作里的函数介绍:

C语言:文件操作详解-CSDN博客

 不太好找,所以我们可以先注释创造数据的文件,然后再文件中修该出5个最大数,然后再执行一次函数

以上就是通过数组验证top和利用文件验证tok的方法!!

 

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

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

相关文章

grid新建主从一对多

目录 总结一、步骤前端1.第一步-编写tabs的modelBody2.第二步编辑表扩展js 后端--重写表的add和Update方法1.第一步 总结 编写tabs的modelBody后编辑表扩展js在重写后端partial的Service 一、步骤 前端 1.第一步-编写tabs的modelBody 复制下面代码该改的改 <template&…

外包干了10个月,技术退步明显...

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

【RPG Maker MV 仿新仙剑 战斗场景UI (一)】

RPG Maker MV 仿新仙剑 战斗场景UI 一 战斗场景制作原版仙剑战斗UI原版RPG Maker MV战斗UI启航战斗菜单 战斗场景制作 RPG Maker 中战斗场景的UI是比较经典的日式RPG的UI布局&#xff0c;现在尝试将它变成仙剑这样的布局看看。。。 原版仙剑战斗UI 这里只截图了开始的战斗UI…

如何在IDEA中使用固定公网地址SSH远程连接服务器开发环境

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xf…

【网站项目】服装定制系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

ELAdmin 隐藏添加编辑按钮

使用场景 做了一个监控模块&#xff0c;数据都是定时生成的&#xff0c;所以不需要手动添加和编辑功能。 顶部不显示 可以使用 true 或者 false 控制现实隐藏 created() {this.crud.optShow {add: false,edit: false,del: true,download: true,reset: true}},如果没有 crea…

【JVM篇】ThreadLocal中为什么要使用弱引用

文章目录 &#x1f354;ThreadLocal中为什么要使用弱引用⭐总结 &#x1f354;ThreadLocal中为什么要使用弱引用 ThreadLocal可以在线程中存放线程的本地变量&#xff0c;保证数据的线程安全 ThreadLocal是这样子保存对象的&#xff1a; 在每个线程中&#xff0c;存放了一个…

机器学习——聚类问题

&#x1f4d5;参考&#xff1a;西瓜书ysu老师课件博客&#xff08;3&#xff09;聚类算法之DBSCAN算法 - 知乎 (zhihu.com) 目录 1.聚类任务 2.聚类算法的实现 2.1 划分式聚类方法 2.1.1 k均值算法 k均值算法基本原理&#xff1a; k均值算法算法流程&#xff1a; 2.2 基于…

爱上JVM——常见问题(一):JVM组成

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&…

寒假作业-day5

1>现有无序序列数组为23,24,12,5,33,5347&#xff0c;请使用以下排序实现编程 函数1:请使用冒泡排序实现升序排序 函数2:请使用简单选择排序实现升序排序 函数3:请使用直接插入排序实现升序排序 函数4:请使用插入排序实现升序排序 代码&#xff1a; #include<stdio.h&g…

chatGPT免费AI交互产品的应用

概述 前提条件及结果&#xff1a; 1、会翻墙&#xff0c;别问我为什么&#xff1b; 2、无需注册chatgpt账号&#xff1b; 3、通过注册第三方账号来免费无限次使用chatgpt&#xff1b; 工具&#xff1a; 翻墙工具&#xff0c;请自行搜索度娘&#xff0c;不用来问我。 第三…

Java实现停车场收费系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 停车位模块2.2 车辆模块2.3 停车收费模块2.4 IC卡模块2.5 IC卡挂失模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 停车场表3.2.2 车辆表3.2.3 停车收费表3.2.4 IC 卡表3.2.5 IC 卡挂失表 四、系统实现五、核心代码…

Spring Boot 笔记 012 创建接口_添加文章分类

1.1.1 实体类添加校验 package com.geji.pojo;import jakarta.validation.constraints.NotEmpty; import lombok.Data;import java.time.LocalDateTime;Data public class Category {private Integer id;//主键IDNotEmptyprivate String categoryName;//分类名称NotEmptypriva…

深入探索Pandas读写XML文件的完整指南与实战read_xml、to_xml【第79篇—读写XML文件】

深入探索Pandas读写XML文件的完整指南与实战read_xml、to_xml XML&#xff08;eXtensible Markup Language&#xff09;是一种常见的数据交换格式&#xff0c;广泛应用于各种应用程序和领域。在数据处理中&#xff0c;Pandas是一个强大的工具&#xff0c;它提供了read_xml和to…

C# CAD2016 判断多边形的方向正时针或逆时针旋转

方法一&#xff1a;基于相邻顶点相对位置判断顺时针排列 // 计算当前子序列是否为顺时针排列 for (int i 1; i < outerPoints.Count; i) {int index (startVertexIndex i) % outerPoints.Count;int prevIndex (startVertexIndex i - 1) % outerPoints.Count;Point2d c…

java SSM新闻管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM新闻管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S…

面试经典150题——矩阵置零

​"Dream it. Wish it. Do it." - Unknown 1. 题目描述 2. 题目分析与解析 2.1 思路一——暴力求解 思路一很简单&#xff0c;就是尝试遍历矩阵的所有元素&#xff0c;如果发现值等于0&#xff0c;就把当前行与当前列的值分别置为0。同时我们需要注意&#xff0c;…

java面试微服务篇

目录 目录 SpringCloud Spring Cloud 的5大组件 服务注册 Eureka Nacos Eureka和Nacos的对比 负载均衡 负载均衡流程 Ribbon负载均衡策略 自定义负载均衡策略 熔断、降级 服务雪崩 服务降级 服务熔断 服务监控 为什么需要监控 服务监控的组件 skywalking 业务…

Linux--编译器-gcc/g++使用

目录 前言 1.看一段样例 2.程序的翻译过程 1.第一个阶段&#xff1a;预处理 2.第二个阶段&#xff1a;编译 3.第三个阶段&#xff1a;汇编 4.第四个阶段&#xff1a;链接 3.程序的编译为什么是这个样子&#xff1f; 4. 关于编译器 5.链接&#xff08;动静态链接&#x…

[Flink04] Flink部署实践

Flink部署支持三种模式&#xff1a;本地部署、Standalone部署、Flink on Yarn部署。 独立&#xff08;Standalone&#xff09;模式由Flink自身提供资源&#xff0c;无需其他框架&#xff0c;这种方式降低了和其他第三方资源框架的耦合性&#xff0c;独立性非常强。但Flink 是大…
推荐文章