ReviewForJob——拓扑排序+最短路径算法(有权+无权)

【0】README

1)本文旨在给出 拓扑排序+最短路径算法(有权+无权) 的源码实现 和 分析,内容涉及到 邻接表, 拓扑排序, 循环队列,无权最短路径(广度优先搜索,有权最短路径,二叉堆,迪杰斯特拉算法 等知识;

2)其实,说白了:广度优先搜索算法(计算无权最短路径) 是基于 拓扑排序算法的,而 迪杰斯特拉算法(计算有权最短路径) 是基于 广度优先搜索算法或者说是它的变体算法;上述三者不同点在于: 拓扑排序算法 和 广度优先搜索算法 使用了 循环队列, 而迪杰斯特拉算法使用了 二叉堆优先队列作为其各自的工具;相同点在于:他们都使用了 邻接表来表示图;

3)o. m. g. 差点忘记了,如何计算所有点对最短路径?

3.1)邻接表表示的稀疏图:因为 迪杰斯特拉算法是 计算 单源有权最短路径,故运行顶点个数 次用二叉堆优先队列编写 的 迪杰斯特拉算法即可 计算所有点对最短路径;

3.2)邻接矩阵表示的稠密图:floyd 算法,该算法有三层for 循环 ,内两层循环用于遍历 邻接矩阵的每个元素,最外层循环 用于遍历中转节点,在中转节点的作用下,d[i][j] 之间的路径是否可以减小,伪代码如下:

for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++){if(d[i][k]+d[k][j]<d[i][j]){d[i][j]=d[i][k]+d[k][j];path[i][j]=path[i][k];}}

【1】邻接表是图的标准表示方法

https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/review/p216_adjacency_list

0)图的表示方法:稀疏图用邻接表,而稠密图用邻接矩阵,而现实生活中 大多数图都是稀疏的;

1)邻接表的结构体

// 顶点的结构体.
struct Vertex;
typedef struct Vertex* Vertex;
struct Vertex
{// ElementType value; // 要知道顶点是一个标识符,并不是真正的value(而是对value的抽象).int index;Vertex next;
};// 邻接表的结构体
struct AdjList;
typedef struct AdjList* AdjList;
struct AdjList
{int capacity;Vertex* array;
};

2)代码实现如下:

// create vertex with index.
Vertex create(int index)
{Vertex v = (Vertex)malloc(sizeof(struct Vertex));if(v==NULL){Error("failed create() for out of space.");return NULL;}v->index=index;v->next=NULL;return v;
}// 插入 顶点标识符index 到邻接表下标为 start 的位置. 
void insertAdjList(AdjList adjList, int start, int index)
{Vertex temp = adjList->array[start];	while(temp->next){temp = temp->next;}	temp->next = create(index);;	if(temp->next ==NULL){return ;}
}
#include "adjList.h"void main()
{int capacity=7;	AdjList adjList;int row=7, col=3, i, j;int adjArray[7][3] = {{2, 4, 3},{4, 5, 0},{6, 0, 0},{6, 7, 3},{4, 7, 0},{0, 0, 0},{6, 0, 0}};// init adjacency list.adjList = init(7);	if(adjList==NULL){return;}			printf("\n\n\t ====== review for adjacency list ======\n");			for(i=0;i<row;i++){for(j=0;j<col;j++){if(adjArray[i][j]){insertAdjList(adjList, i, adjArray[i][j]);}}}		printAdjList(adjList);
}


【2】拓扑排序

https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/review/p217_topSort

0)定义:拓扑排序是对有向无圈图的顶点的一种排序,它使得如果存在一条从 vi 到 vj 的路径,则在排序中 vj 出现在 vi 的后面;

1)用到的技术: 邻接表, 循环队列, 拓扑排序算法;且使用邻接表的拓扑排序时间复杂度为 O(|E| + |V|);

2)拓扑排序的steps

step1)把入度为0 的顶点入队列;

step2)当队列不为空时,顶点出队,其邻接顶点的入度减1;

step3)每一个邻接顶点的入度减1 后,若其入度为0 则入队列,转向step2;(别忘记在拓扑排序前,还要统计各个顶点的入度,入度数组indegreeArray 作为输入参数传给 拓扑排序算法)

// 拓扑排序
void topSort(AdjList adjList, Queue queue, int* indegreeArray)
{int i;Vertex* array = adjList->array;Vertex temp;int index;int adjVertex;// step1: 把入度为0的顶点放入队列.for(i=0; i<adjList->capacity; i++) // 切记: 这里入队的value(或i) 从 0 开始取.{if(indegreeArray[i]==0) {enQueue(queue, i);}}//step2: 当队列不为空时,一个顶点v出队,并将与v邻接的所有顶点的入度减1.printf("\n\t top sorting result: ");while(!isEmpty(queue)){index = deQueue(queue); // while 循环已经保证了 队列不可能为空.printf("v[%d] ", index+1); // 注意: 这里的index 要加1,因为元素入队是从 0 开始取的,见上面的入队操作.temp = array[index];while(temp->next){adjVertex = temp->next->index; // 因为 temp->next->index 从1 开始取的,indegreeArray[adjVertex-1]--; // adjVertex 要减1, 而indegreeArray数组从0开始取.if(indegreeArray[adjVertex-1]==0) // step3: 把与顶点v(标识符=index)相邻的,且入度为0的顶点放入队列.{enQueue(queue, adjVertex-1); //入队的value(或index) 从 0 开始取.				}temp = temp->next;}}//  循环结束后: 拓扑排序就是顶点出队的顺序.
}

3)拓扑排序的结果不唯一


4)对于上图的分析(Analysis)

A1)对于上图而言,排序结果可以是 V1, V2, V5, V4, V3, V7, V6  也可以是 V1, V2, V5, V4,V7, V3, V6;

A2)千万不要以为拓扑排序的结果就一定顺着箭头的方向走;

5)排序结果如下:



【3】最短路径算法

【3.1】无权最短路径算法(计算节点间的边数)

https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/review/p220_unweighted_shortest_path

0)算法描述: 给定一个无权图,使用某个顶点s作为 起始顶点,我们想要找出从 s 到 所有其他顶点的最短路径;

1)算法所用技术(techs):

tech1)邻接表;

tech2)循环队列:为什么无权最短路径算法要用到循环队列? 因为 要保存遍历到的当前节点的邻接节点,且以 先进先出的顺序输出;

tech3)无权最短路径算法(基于拓扑排序的算法 idea);

tech4)计算无权最短路径的记录表;

// 记录表的表项
struct Entry;
typedef struct Entry* Entry;
struct Entry
{int known;// 出队后设置为1,表示已处理;int dv; // 起始顶点到当前顶点的距离;int pv; // 引起dv 变化的最后顶点;
};// 记录表的数组
struct UnweightedTable;
typedef struct UnweightedTable* UnweightedTable;
struct UnweightedTable
{	int size;	Entry* array;
};

补充)记录表表项中成员的初始化

//allocate the memory for initializing unweighted table
UnweightedTable initUnweightedTable(int size)
{	int i;UnweightedTable table = (UnweightedTable)malloc(sizeof(struct UnweightedTable));if(table==NULL){Error("failed initUnweightedTable() for out of space.");return NULL;}table->size = size;table->array = (Entry*)malloc(size * sizeof(Entry));	if(table->array==NULL){Error("failed initUnweightedTable() for out of space.");return NULL;}for(i=0; i<size; i++){table->array[i] = (Entry)malloc(sizeof(struct Entry));if(table->array[i]==NULL){Error("failed initUnweightedTable() for out of space.");return NULL;}table->array[i]->known = 0; // known=0 or 1表示 未知 或 已知.table->array[i]->dv= INT_MAX; // dv==distance 等于 INT_MAX 表示不可达. 而 dv=0 表示它自己到自己的path==0.table->array[i]->pv = 0; // pv==path 等于 0 表示不可达,因为pv从1开始取。}return table;
}

2)算法步骤(借用了拓扑排序的算法 idea):

step1)起始顶点进队,设置其 dv,pv 等于 0;

step2)队列不为空,顶点出队;出队顶点的known设置为1,表明已处理;

step3)遍历出队顶点的每一个邻接顶点,若 邻接顶点的dv 等于 -1 (表不可达),则该邻接顶点进队,设置其 dv,pv;转向step2;

//计算 startVertex顶点 到其他顶点的无权最短路径
void unweighted_shortest_path(AdjList adj, UnweightedTable table, int startVertex, Queue queue)
{		int capacity=adj->capacity;Vertex* arrayVertex = adj->array;Vertex temp;Entry* arrayEntry = table->array;int index; // 顶点标识符(从0开始取)int adjVertex;//step1(初始状态): startVertex 顶点进队.enQueue(queue, startVertex-1); // 切记: 这里入队的value(或i) 从 0 开始取.	arrayEntry[startVertex-1]->dv=0;arrayEntry[startVertex-1]->pv=0;// 初始状态over.// step2: 出队. 并将其出队顶点的邻接顶点进队.while(!isEmpty(queue)){index = deQueue(queue);  // index从0开始取,因为出队value从0开始取,不需要减1.arrayEntry[index]->known=1; // 出队后,将其 known 设置为1.temp = arrayVertex[index];while(temp->next) {adjVertex = temp->next->index; // 邻接节点标识符adjVertex 从1开始取.if(arrayEntry[adjVertex-1]->dv == INT_MAX) // 注意: 下标是adjVertex-1, 且 dv==INT_MAX 表明 index 到 adjVertex 还处于不可达状态,所以adjVertex入队.{enQueue(queue, adjVertex-1); // 入队的value 从 0 开始取,所以减1.arrayEntry[adjVertex-1]->dv = arrayEntry[index]->dv + 1; arrayEntry[adjVertex-1]->pv=index+1; // index 从0开始取,所以index加1.}			temp = temp->next;}		}	
} 


Attention)上述搜索图的算法被称为“广度优先搜索”,因为要遍历出队顶点的每一个邻接顶点,注意是每一个,所以叫做广度;


【3.2】有权最短路径算法(迪杰斯特拉算法)

https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/review/p224_dijkstra_weighted_shortest_path

0)算法描述:给定一个有权图,使用某个顶点s作为 起始顶点,我们想要找出从 s 到 所有其他顶点的有权最短路径;

1)算法所用技术(techs):

tech1)邻接表;

tech2)二叉堆(优先队列);堆中节点类型为 HeapNode(vertex 和 weight);为什么迪杰斯特拉算法要用到二叉堆? 因为 需要保存 当前节点的所有邻接顶点,且 有最小weight 的 邻接顶点先输出,这可以通过小根堆来实现,deleteMin() 就是一个输出的过程;

// 二叉堆的节点类型的结构体.
struct HeapNode;
typedef struct HeapNode* HeapNode;
struct HeapNode
{int vertex;int weight;
};// 二叉堆的结构体.
struct BinaryHeap;
typedef struct BinaryHeap* BinaryHeap;
struct BinaryHeap
{int capacity;int size;HeapNode array;
};

tech3)迪杰斯特拉算法;

tech4)计算有权最短路径的记录表(同无权的记录表)

// 邻接表的表项结构体.
struct Entry;
typedef struct Entry* Entry;
struct Entry
{int known;int dv;int pv;
};// 有权路径记录表的结构体.
struct WeightedTable;
typedef struct WeightedTable* WeightedTable;struct WeightedTable
{	int size;	Entry* array;
};

//allocate the memory for initializing unweighted table(计算有权路径的记录表项的初始化)
WeightedTable initWeightedTable(int size)
{	int i;WeightedTable table = (WeightedTable)malloc(sizeof(struct WeightedTable));if(table==NULL){Error("failed initUnweightedTable() for out of space.");return NULL;}table->size = size;table->array = (Entry*)malloc(size * sizeof(Entry));	if(table->array==NULL){Error("failed initUnweightedTable() for out of space.");return NULL;}for(i=0; i<size; i++){table->array[i] = (Entry)malloc(sizeof(struct Entry));if(table->array[i]==NULL){Error("failed initUnweightedTable() for out of space.");return NULL;}table->array[i]->known = 0; // known 等于 0 or 1 表示 未知 or 已知.table->array[i]->dv= INT_MAX; // dv==distance 等于 INT_MAX 表示不可达.(有权路径表示weight)table->array[i]->pv = 0; // pv==path 等于 0 也表示不可达.}return table;
} 

2)算法步骤

step1)从未知(known==0)顶点中选取 dv 最小的顶点作为初始顶点;初始顶点对应的HeapNode节点类型插入堆(insert操作);

step2)堆不为空,执行deleteMin()操作;设置被删除顶点为已知(其known=1);遍历被删除顶点的每一个邻接顶点;

step2.1)如其未知(known==0)

step2.1.1)且若更新后的权值(路径长)比更新前的权值(路径长)小的话

step2.1.1.1)构建该节点的HeapNode节点类型并插入堆且更新路径长;

补充)迪杰斯特拉算法的结束标志是:当 所有顶点的状态都是已知(known==1)的时候,算法结束;
//计算 startVertex顶点 到其他顶点的无权最短路径
// adj:邻接表(图的标准表示方法), table: 计算有权最短路径的配置表,heap:用于选取最小权值的邻接顶点的小根堆.
void dijkstra(AdjList adj, WeightedTable table, int startVertex, BinaryHeap heap)
{		int capacity=adj->capacity;Vertex* arrayVertex = adj->array;Vertex temp;Entry* arrayEntry = table->array;int index; // 顶点标识符(从0开始取)int adjVertex;struct HeapNode node;int weight;int i=0; // 记录已知顶点个数( known == 1 的 个数).//step1(初始状态): startVertex 顶点插入堆. startVertex 从1 开始取.node.vertex=startVertex-1; // 插入堆的 node.vertex 从 0 开始取,所以startVertex-1.node.weight=0;insert(heap, node); // 插入堆.arrayEntry[startVertex-1]->dv=0;arrayEntry[startVertex-1]->pv=0;// 初始状态over.// step2: 堆不为空,执行 deleteMin操作. 并将被删除顶点的邻接顶点插入堆.while(!isEmpty(heap)){		if(i == capacity) // 当所有 顶点都 设置为 已知(known)时,退出循环.{break;}index = deleteMin(heap).vertex;  // index表示邻接表下标,从0开始取,参见插入堆的操作.arrayEntry[index]->known=1; // 从堆取出后,将其 known 设置为1.i++; // 记录已知顶点个数( known == 1 的 个数).temp = arrayVertex[index];		while(temp->next) {adjVertex = temp->next->index; // 邻接节点标识符adjVertex 从1开始取.weight = temp->next->weight; // 取出该邻接顶点的权值.if(arrayEntry[adjVertex-1]->known == 0) // 注意: 下标是adjVertex-1, 且known==0 表明 adjVertex顶点还处于未知状态,所以adjVertex插入堆.{if(arrayEntry[adjVertex-1]->dv > arrayEntry[index]->dv + weight ) // [key code] 当当前权值和 比 之前权值和 小的时候 才更新,否则不更新.{node.vertex=adjVertex-1; // 插入堆的 node.vertex 从 0 开始取.node.weight=weight;insert(heap, node); // 插入堆.arrayEntry[adjVertex-1]->dv = arrayEntry[index]->dv + weight; // [also key code]arrayEntry[adjVertex-1]->pv=index+1; // index 从0开始取,所以index加1.	}}			temp = temp->next;}printWeightedtable(table, 1); // 取消这行注释可以 follow 迪杰斯特拉 alg 的运行过程.}	
} 

【补充】以HeapNode 为数据类型的二叉堆优先队列

0)为什么我要提及这个二叉堆?因为 这个二叉堆 和他家的不一样,其数据类型不是 单纯的 int, 而是一个结构体类型,我们只需要在 二叉堆中 将 HeapNode 类型定义为 ElementType 即可。

1)intro: 迪杰斯特拉法用到了 二叉堆优先队列,而二叉堆优先队列 的 数组(节点)类型是 HeapNode;

2)结构体介绍

// 二叉堆的节点类型的结构体.
struct HeapNode;
typedef struct HeapNode* HeapNode;
struct HeapNode
{int vertex;int weight;
};// 二叉堆的结构体.
struct BinaryHeap;
typedef struct BinaryHeap* BinaryHeap;
struct BinaryHeap
{int capacity;int size;HeapNode array;
};

3)二叉堆优先队列的 操作(重点关注其 基于上滤的插入操作 和 基于下滤的deleteMin操作)

// judge whether the heap is empty or not.
int isEmpty(BinaryHeap heap)
{return heap->size == 0;
}// build binary heap with capacity.
BinaryHeap buildHeap(int capacity)
{BinaryHeap heap = (BinaryHeap)malloc(sizeof(struct BinaryHeap));if(!heap){Error("failed buildHeap() for out of space.");return NULL;}heap->capacity = capacity;heap->size = 0; // startup of the heap is 1. so ++size when insert.// allocate memory for heap->array.heap->array = (ElementType*)malloc(sizeof(ElementType) * capacity);if(!heap->array){Error("failed buildHeap() for out of space.");return NULL;}heap->array[0].weight = INT_MIN; // heap->array starts from 1 not 0, so let heap->array[0] = INT_MIN.return heap;
}// 插入操作 based on 上滤操作.
void insert(BinaryHeap heap, ElementType data)
{if(heap->size == heap->capacity-1){Error("failed insert() for heap is full.");}	percolateUp(heap, data);
}// 上滤操作(key operation)
void percolateUp(BinaryHeap heap, ElementType data)
{int i;// 必须将size++.for(i=++heap->size; data.weight < heap->array[i/2].weight; i/=2){heap->array[i] = heap->array[i/2];}heap->array[i] = data;
}// delete minimal from heap based on percolateDown().
ElementType deleteMin(BinaryHeap heap)
{		ElementType temp;if(heap->size==0){Error("failed deleteMin() for the heap is empty");return temp;}	swap(&heap->array[1], &heap->array[heap->size--]); // 将二叉堆的根和二叉堆的最后一个元素交换,size--。percolateDown(heap, 1); // 执行下滤操作.return heap->array[heap->size+1];
}// 下滤操作(key operation)
void percolateDown(BinaryHeap heap, int i)
{int child;ElementType temp;	for(temp=heap->array[i]; (child=leftChild(i))<=heap->size; i=child){				if(child<heap->size && heap->array[child].weight > heap->array[child+1].weight){child++;}if(temp.weight > heap->array[child].weight) // 比较是和 temp=heap->array[index] 进行比较.{			heap->array[i] = heap->array[child];}else{					break;}}heap->array[i] = temp;	
}int leftChild(int index)
{return 2*index;
}// swap a and b.
void swap(ElementType* a, ElementType* b)
{ElementType t;t = *a;*a = *b;*b = t;
}void printBinaryHeap(ElementType *array, int size)
{int i;for(i=1; i<=size; i++){printf("\n\t heap->array[%d] = ", i);		printf("%d", array[i].weight);		}printf("\n");
} 


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

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

相关文章

Spring boot (5):Spring data jpa 的使用

总结&#xff1a; jpa是什么&#xff0c;spring data jpa是什么&#xff1f; jpa是一套规范&#xff0c;不是一套产品。jpa是一套规范&#xff0c;不是一套产品。 spring data jpa是spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架&#xff0c;提供了包括增删改等在…

Java Map集合面试题汇总

转载自 Java Map集合面试题汇总1、 你都知道哪些常用的Map集合?2、Collection集合接口和Map接口有什么关系&#xff1f;3、HashMap是线程安全的吗&#xff1f;线程安全的Map都有哪些&#xff1f;性能最好的是哪个&#xff1f;4、使用HashMap有什么性能问题吗&#xff1f;5、Ha…

ReviewForJob——二叉堆优先队列的实现(三种堆节点类型——int + struct HeapNode + struct HeapNode*)

【0】README 1&#xff09;本文旨在给出 二叉堆优先队列的实现 的代码实现和分析&#xff0c; 而堆节点类型 不外乎三种&#xff1a; 一&#xff0c; 基本类型如int&#xff1b; 二&#xff0c;结构体类型 struct HeapNode&#xff1b; 三&#xff0c;结构体指针类型 struct H…

Spring boot(六):如何优雅的使用mybatis

总结 hibernate 和 mybatis 的区别 hibernate的特点是所有的sql都用java代码生成&#xff0c;不用跳出程序去&#xff08;看&#xff09;sql&#xff0c;发展到最顶端就是Spring data jpa了。 mybatis初期使用比较麻烦&#xff0c;需要各种配置文件、实体类、dao层映射关联、还…

Java中创建String的两道面试题及详解

转载自 Java中创建String的两道面试题及详解 我们知道创建一个String类型的变量一般有以下两种方法&#xff1a; String str1 "abcd";String str2 new String("abcd"); 那么为什么会存在这两种创建方式呢&#xff0c;它们在内存中的表现形式各有什么区别…

ReviewForJob——最小生成树(prim + kruskal)源码实现和分析

【0】README 1&#xff09;本文旨在给出 ReviewForJob——最小生成树&#xff08;prim kruskal&#xff09;源码实现和分析&#xff0c; 还会对其用到的 技术 做介绍&#xff1b; 2&#xff09;最小生成树是对无向图而言的&#xff1a;一个无向图G 的最小生成树是一个连通图…

Spring boot(七):Spring boot+ mybatis 多数据源最简解决方案

多数据源一般解决哪些问题&#xff1f;主从模式或者业务比较复杂需要连接不同的分库来支持业务。 直接上代码。 配置文件 pom包依赖&#xff0c;该依赖的依赖。主要是数据库这边的配置&#xff1a; mybatis.config-locationsclasspath:mybatis/mybatis-config.xmlspring.da…

Java:关于main方法的10道面试题

转载自 Java&#xff1a;关于main方法的10道面试题 1.main方法是做什么用的&#xff1f; 2.不用main方法如何运行一个类&#xff1f; 3.main方法如何传递参数&#xff1f;传递参数的类型是什么&#xff1f;能不能改变该参数类型&#xff1f; 4.main方法为什么是静态的&#xff…

ReviewForJob——深度优先搜索的应用

【0】README 1&#xff09;本文旨在 介绍 ReviewForJob——深度优先搜索的应用 及其 源码实现 &#xff1b; 2&#xff09;搜索树的技术分为广度优先搜索 和 深度优先搜索&#xff1a;而广度优先搜索&#xff0c;我们前面利用 广度优先搜索计算无权最短路径已经做过分析了&am…

Spring boot(八):RabbitMQ详解

RabbitMQ介绍 RabbitMQ既一个消息队列&#xff0c;主要用来实现应用程序的异步和解耦&#xff0c;同时也能起到消息缓冲&#xff0c;消息分发的作用。 消息中间件在互联网公司的使用中越来越多。消息中间件最主要的作用是解耦&#xff0c;中间件最标准的用法师生产者生产消息传…

关于Jvm知识看这一篇就够了

转载自 关于Jvm知识看这一篇就够了2016年左右的时候读了周志明《深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践》&#xff0c;读完之后受益匪浅&#xff0c;让我对Java虚拟机有了一个完整的认识&#xff0c;这是Jvm书籍中最好的读物之一。后来结合实际工作中遇到的问题…

ReviewForJob——算法设计技巧(贪婪算法+分治算法+动态规划)

【0】README 1&#xff09;本文旨在介绍算法设计技巧包括 贪婪算法、分治算法、动态规划 以及相关的荔枝等&#xff1b; 【1】贪婪算法 1&#xff09;intro&#xff1a; 贪婪算法是分阶段进行的&#xff0c;在每个阶段&#xff0c;可以认为所做的决定是最好的&#xff0c;而…

Spring boot(九):定时任务

在我们的项目开发过程中&#xff0c;进场需要定时任务来帮助我们做一些内容&#xff0c;springboot默认已经帮我们实行了&#xff0c;只要天剑相应的注解就可以实现。 1、pom包配置 pom包里面只需要引入springboot starter包即可 <dependencies><dependency><…

jvm系列(一):java类的加载机制

转载自 jvm系列(一):java类的加载机制1、什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中&#xff0c;将其放在运行时数据区的方法区内&#xff0c;然后在堆区创建一个 java.lang.Class对象&#xff0c;用来封装类在方法区内的数据结构。类的加载的最…

Springboot(十):邮件服务

发送邮件应该是网站的必备功能之一&#xff0c;什么注册验证&#xff0c;忘记密码或者是给用户发送营销信息。最早期的时候我们会使用javaMail相关api来写邮件相关代码&#xff0c;后来spring退出了javaMailSender 更加简化了邮件发送的过程&#xff0c;在之后springboot 对此进…

How to install plugin for Eclipse from .zip

these contents are reshiped from 如何在eclipse中安装.zip插件 1.make sure your .zip file is an valid Eclipse Plugin Note: that means: your .zip file contains folders features and plugins, like this:for new version Eclipse Plugin, it may also include anothe…

jvm系列(二):JVM内存结构

转载自 jvm系列(二):JVM内存结构所有的Java开发人员可能会遇到这样的困惑&#xff1f;我该为堆内存设置多大空间呢&#xff1f;OutOfMemoryError的异常到底涉及到运行时数据的哪块区域&#xff1f;该怎么解决呢&#xff1f;其实如果你经常解决服务器性能问题&#xff0c;那么这…

jvm系列(三):GC算法 垃圾收集器

转载自 jvm系列(三):GC算法 垃圾收集器 概述 垃圾收集 Garbage Collection 通常被称为“GC”&#xff0c;它诞生于1960年 MIT 的 Lisp 语言&#xff0c;经过半个多世纪&#xff0c;目前已经十分成熟了。 jvm 中&#xff0c;程序计数器、虚拟机栈、本地方法栈都是随线程而生随线…

Spring boot(十二):Spring boot 如何测试、打包、部署

博文引用&#xff1a;springboot(十二)&#xff1a;springboot如何测试打包部署 开发阶段 单元测试 Spring boot对单元测试的支持已经很完善了。 1 在pom包中添加Spring-boot-starter-test包引用 <dependency><groupId>org.springframework.boot</groupId&g…

Java虚拟机详解----常用JVM配置参数

【声明】 欢迎转载&#xff0c;但请保留文章原始出处→_→ 生命壹号&#xff1a;http://www.cnblogs.com/smyhvae/ 文章来源&#xff1a;http://www.cnblogs.com/smyhvae/p/4736162.html 联系方式&#xff1a;smyhvae163.com 本文主要内容&#xff1a; Trace跟踪参数堆的…