本文将介绍数据结构图论部分中常见的算法

单源最短路径问题(用来计算一个点到其他所有顶点的最短路径)
Dijkstra(n*n)
1. 初始化: 先找出从源点V0到各终点Vk的直达路径(V0,Vk), 即通过一条弧到达的路径
2. 选择: 从这些路径中找出一条长度最短的路径(V0,u)
3. 更新: 然后对其余各条路径进行适当的调整
   若在图中存在弧(u,Vk),  且(Vo,u,Vk)<(Vo,Vk), 则以路径(Vo,u,Vk) 代替(Vo,Vk)
4. 把V分成两组:
   (1) S: 已求出最短路径的顶点的集合
   (2) T=V-S: 尚未确定最短路径的顶点集合
算法步骤:
1.初始时令S={V0}, T={其余顶点}
2.T中顶点对应的距离值用辅助数组D存放
   D[i] 初值: 若<V0,Vi>存在, 则为其权值; 否则为无穷  注: 从源点V0开始
3.从T中选取一个其距离值最小的顶点Vj, 加入S
4.对T中顶点的距离值进行修改: 若加进Vj 作中间顶点, 从V0到Vi的距离值比不加Vj的路径要短, 则修改此距离值
重复上述步骤, 直到S=V为止
代码实现:
辅助数组: 
-- 数组bool S[n]: 记录相应顶点是否已被确定最短距离
-- 数组D[n]: 记录源点到相应顶点路径长度
-- 数组Path[n]: 记录相应顶点的前驱顶点
1. 从D(T集合中)找最小值v   2. 将最小值对应的顶点, 加入S   3.更新D数组, Path数组
注:找最小值的时候要从D数组中为false的点中找,  并且不能为源点

#include<iostream>
using namespace std;
#define MVNum 20
#define MAXNUM 32767
typedef char VerTexType;  //顶点类型
typedef int ArcType;      //弧类型 
typedef struct{VerTexType vexs[MVNum];   //vexs代表顶点,arcs代表邻接矩阵ArcType arcs[MVNum][MVNum];int vexnum,arcnum;  //vexnum表示图中顶点的数量,arcnum表示图中边的数量
}AMGraph;
int LocateVex(AMGraph G,VerTexType v){for(int i=0;i<G.vexnum;i++){if(v==G.vexs[i]){return i;}} return -1;
}
void CreateUDG(AMGraph &G){cin>>G.vexnum>>G.arcnum;for(int i=0;i<G.vexnum;i++){cin>>G.vexs[i];}for(int i=0;i<G.vexnum;i++){for(int j=0;j<G.vexnum;j++){G.arcs[i][j]=MAXNUM;}}VerTexType v1,v2;ArcType w;for(int k=0;k<G.arcnum;k++){cin>>v1>>v2>>w;int i=LocateVex(G,v1);int j=LocateVex(G,v2);G.arcs[i][j]=w;}
}
bool S[MVNum];
ArcType D[MVNum];
int Path[MVNum];
void ShortestPath_Dijkstra(AMGraph G,VerTexType u){int k=LocateVex(G,u);for(int i=0;i<G.vexnum;i++){S[i]=false;D[i]=G.arcs[k][i];if(D[i]<MAXNUM){Path[i]=k;}else{Path[i]=-1;}}S[k]=true;int min,v;for(int i=1;i<G.vexnum;i++){min=MAXNUM;for(int j=0;j<G.vexnum;j++){if(j!=k){if(!S[j]&&D[j]<min){min=D[j];v=j;}}}S[v]=true;for(int j=0;j<G.vexnum;j++){if(!S[j]&&D[v]+G.arcs[v][j]<D[j]){D[j]=D[v]+G.arcs[v][j];Path[j]=v; }}}}
int main(){AMGraph G;CreateUDG(G);char u;cin>>u;ShortestPath_Dijkstra(G,u);for(int i=0;i<G.vexnum;i++){cout<<D[i]<<" ";}return 0;
}

所有顶点间的最短路径
方法一:每次以一个顶点源点, 重复执行Dijkstra算法n 次
方法二:floyd 算法(n*n*n)
以上两种方法的时间复杂度是一样的
1. 初始时设置一个n阶方阵, 令其对角线元素为0, 若存在弧<Vi,Vj>, 则对应元素为权值; 否则为无穷
2. 逐步试着在原直接路径中增加中间节点, 若加入中间节点后路径变短, 则修改之; 否则, 维持原值. 所有顶点试探完毕, 算法结束
代码实现:
path 数组初始时为-1, 存储的是前驱节点
邻接矩阵, 对角线存储的是0, 有权值就存权值, 没有权值就存储无穷

#include<iostream>
using namespace std;
#define MVNum 20
#define MAXNUM 32767
typedef char VerTexType;  //顶点类型
typedef int ArcType;      //弧类型 
typedef struct{VerTexType vexs[MVNum];   //vexs代表顶点,arcs代表邻接矩阵ArcType arcs[MVNum][MVNum];int vexnum,arcnum;  //vexnum表示图中顶点的数量,arcnum表示图中边的数量
}AMGraph;
int LocateVex(AMGraph G,VerTexType v){for(int i=0;i<G.vexnum;i++){if(v==G.vexs[i]){return i;}} return -1;
}
void CreateUDG(AMGraph &G){cin>>G.vexnum>>G.arcnum;for(int i=0;i<G.vexnum;i++){cin>>G.vexs[i];}for(int i=0;i<G.vexnum;i++){for(int j=0;j<G.vexnum;j++){if(i!=j){G.arcs[i][j]=MAXNUM;}else{G.arcs[i][j]=0;}}}VerTexType v1,v2;ArcType w;for(int k=0;k<G.arcnum;k++){cin>>v1>>v2>>w;int i=LocateVex(G,v1);int j=LocateVex(G,v2);G.arcs[i][j]=w;}
}
ArcType D[MVNum][MVNum];
int P[MVNum][MVNum];
void ShortestPath_Floyd(AMGraph G){for(int i=0;i<G.vexnum;i++){for(int j=0;j<G.vexnum;j++){D[i][j]=G.arcs[i][j];if(D[i][j]<MAXNUM&&i!=j){P[i][j]=i;}else{P[i][j]=-1;}	 }}for(int k=0;k<G.vexnum;k++){  //我咧个n次尝试 for(int i=0;i<G.vexnum;i++){for(int j=0;j<G.vexnum;j++){if(D[i][j]>D[i][k]+D[k][j]){D[i][j]=D[i][k]+D[k][j];P[i][j]=P[k][j];  //从上一个Path二维表里面找,永远存储的是距离终点最近的前驱顶点(因为k是按顺序进行尝试的) }}	}}
}
void Print(int n){for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(D[i][j]<MAXNUM){printf("%4d(%2d)",D[i][j],P[i][j]);}else{printf("%4c(%2d)",'#',P[i][j]);}}printf("\n");}
} 
int main(){AMGraph G;CreateUDG(G);int n=G.vexnum;ShortestPath_Floyd(G);Print(n);return 0;
}

 总结来说第一种算法就是先找最小值, 第二种算法就是n次尝试

关键路径问题:拓扑排序
把工程计划表示为边表示活动的网络, 即AOE网, 用顶点表示事件, 弧表示活动, 弧的权表示活动持续时间
事件(顶点)表示在它之前的活动已经完成,在它之后的活动可以开始
源点:入度为0的顶点   汇点:出度为0的顶点
关键路径----路径长度最长的路径
路径长度----路径上各活动持续时间之和
ve(vj)----表示事件vj的最早发生时间
vl(vj)----表示事件vj的最迟发生时间
e(i)----表示活动ai的最早开始时间   l(i)----表示活动ai的最迟开始时间
l(i)-e(i)----表示完成活动ai的时间余量
关键活动----关键路径上的活动,即l(i)==e(i)(即l(i)-e(i)==0) 的活动
如何找到l(i) == e(i) 的关键路径?
    设活动ai用弧<j,k>表示,其持续时间记为:Wj,k
    则有:(1) e(i)==ve(j)           -> 直接查弧尾的最早发生时间
             (2) l(i) = vl(k) - Wj,k   -> 直接查弧头的最迟发生时间- 权值w
如何求ve(j) 和 vl(j) ?
事件j的最早发生时间?
(1) 从ve(1) = 0 开始向前递推
     ve(j) = Max{ve(i)+Wi,j},<i,j>->T, 2=<j<=n
     其中T是所有以j为头的弧的集合
事件i的最晚发生时间?
(2) 从vl(n)==ve(n) 开始向后向后递推
     vl(i)=Min{vl(j)-Wi,j}, <i,j>->S,1<=i<=n-1
     其中S是所有以i为尾的弧的集合
1.第一个是从源点往右算  2. 第二个是从汇点往左算 

生成树:所有定点均由边连接在一起, 但不存在回路的图
    - 一个图可以有许多棵不同的生成树
    - 所有生成树具有以下共同特点
1. 生成树的顶点个数与图的顶点个数相同
2. 生成树 是图的极小连通子图,去掉一条边则非联通
3. 一个有n个顶点的连通图的生成树有n-1 条边
4. 在生成树中再加一条边必然形成回路
5. 生成树中任意两个顶点间的路径是唯一的
最小生成树:给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那颗生成树称为该网的最小生成树,也叫最小代价生成树
已上两种定义简单来说就是:1.生成树就是连通但是内部没有环,有n-1条边.  2. 最小生成树就是权值最小的生成树
MST性质:
在生成树的构造过程中,图中n个顶点分属两个集合:
     - 已落在生成树上的顶点集:  U
     - 尚未落在生成树上的顶点集:  V-U
接下来则应在所有连通U中顶点和V-U中顶点的边种选取权值最小的边(作为生成树的一条边,前提是不能有回路)
Prim 算法
设N={V,E} 是连通网, TE是N上最小生成树中边的集合        {a,b} a 代表点, b代表边
1. 初始令U={u0},{u0->V},TE={ }
2. 在所有u->U, v->V-U的边(u,v)->E中,找一条代价最小的边(u0,v0). 注: 就是权值最小的边
3. 将(u0,v0) 这条边并入集合 TE, 同时 v0这个点并入U
4. 重复上述操作直至U=V为止, 则T=(V,TE) 为N 的最小生成树  (意思就是把所有的点都选进集合U, 所以前面写的是U=V)

通俗解释:在U集合中选一个点和在U-V集合中选一个点, 使得这个权值的最小, 然后把弧上的顶点加入到集合U中, 直到U=T集合,得到的连通图就是最小生成树

#include<stdio.h>
#define max 32767
typedef struct
{char *date;int **array;int nodenumber,edgenumber;
} Graph;
struct close   //保存节点的权值和字符
{char date;int wed;
};
int LocateNode(Graph L,char ch)
{for(int i=0; i<L.nodenumber; i++)if(L.date[i]==ch) return i;return -1;
}
void CreatUDG(Graph &L)
{scanf("%d %d",&L.nodenumber,&L.edgenumber);//输入图的节点数和边数getchar();L.date=new char[L.nodenumber];   //节点数组L.array=new int*[L.nodenumber];  //邻接矩阵for(int i=0; i<L.nodenumber; i++){L.date[i]=getchar();getchar();L.array[i]=new int[L.nodenumber];}for(int i=0; i<L.nodenumber; i++)for(int j=0; j<L.nodenumber; j++)L.array[i][j]=max;for(int k=0; k<L.edgenumber; k++){char ch1,ch2;int w;scanf("%c %c %d",&ch1,&ch2,&w);getchar();int i=LocateNode(L,ch1);int j=LocateNode(L,ch2);L.array[i][j]=w;L.array[j][i]=w;}
}
int min(struct close *clos,int n)
{int k;for(int i=1; i<n; i++)if(clos[k].wed==0||(clos[i].wed!=0&&clos[i].wed<clos[k].wed)) k=i;return k;
}
void Prim(Graph L,char v)
{struct close clos[L.nodenumber];int k=LocateNode(L,v);for(int i=0; i<L.nodenumber; i++){clos[i].date=v; 	//类似于一种前驱节点的感觉clos[i].wed=L.array[k][i];}//起始节点与其他节点建立联系clos[k].wed=0; //将起始节点的权值设置为0,表示起始节点已经被选中for(int i=1; i<L.nodenumber; i++){k=min(clos,L.nodenumber);char u0=clos[k].date;   //存储的是k的前驱节点char v0=L.date[k];      //索引k下的节点字符printf("(%c,%c)\n",u0,v0);clos[k].wed=0;          //将选中节点的权值设置为0,表示已经加入到最小生成树for(int j=0; j<L.nodenumber; j++)  //这个循环遍历图中的所有节点。变量 j 表示当前正在检查的节点的索引//如果当前被选中的节点(索引为k),到其他(未被选入最小生成树)节点的权值比原来更小,那么if(L.array[k][j]<clos[j].wed)  {clos[j].date=L.date[k];  //更新前驱// 更新 clos 数组中节点 j 的字符为当前选中节点的字符clos[j].wed=L.array[k][j]; //更新权值//更新 clos 数组中节点 j 的权值为当前选中节点与节点 j 之间的边的权重}}
}
int main(){Graph L;CreatUDG(L);Prim(L,L.date[0]);  //传到Prim函数集合U中起始节点U0return 0;
}

 Kruskal算法
1.设 连通网 N=(V,E), 令最小生成树初始化状态为只有n个顶点而无边的非联通图 T={V,{ }}, 每个顶点自成一个联通分量
2. 在E中选取代价最小的边, 若该边依附的顶点落在 T中不同的连通分量上(即: 不能成环), 则将此边加入到T中; 否则, 舍去此边, 选取下一条代价最小的边
3.依次类推, 直至T中所有顶点都在同一连通分量上为止
注:通俗来讲就是, 直接把所有顶点都先加入到生成树T中, 然后从所有的边中一次找最短, 次短的边....进行连通,(但是不能成环)

注:最小生成树可能有很多个,同时Kruskal算法在寻找最小边的时候, 可以使用小顶堆{priority_queue<int,vector<int>,greater<int>> pq}, s所以Kruskal算法的时间复杂度就是堆排序的时间复杂度
两种算法比较:
算法名         Prim算法                     Kruskal算法
算法思想      选择点                         选择边
时间复杂度   O(n^2)(n为顶点数)     O(eloge)(e为边数)
适应范围      (稠密图)(点多的)          稀疏图(边多的)

#include<algorithm>
#include<iostream>
using namesapce std;
#define MVNum 20    //定点数的最大值
#define MANum 20    //边数的最大值
typedef char VerTexType;
typedef int ArcType;
typedef struct{VerTexType vexs[MVNum];int vexnum,arcnum;
}AMGraph;
typedef struct{VerTexType Head; //边的起始点VerTexType Tail; //边的终止点ArcType lowcost; //边上的权值
}Edge; //边的类型
Edge edges[MANum] //存储边信息的辅助数组
//辅助数组 VexSet,记录每个顶点所在联通分量编号
int VexSet[MVNum];
//确定顶点v在G中位置, 即顶点数组的下标
int LocateVex(AMGraph G,VerTexType v){for(int i=0;i<G.vexnum;i++){if(v==G.vexs[i]){return i;}}return -1;
}
void CreateAMGraph(AMGraph &G){cin>>G.vexnum>>G.arcnum;for (int i = 0; i <G.vexnum; i+){cin>>G.vexs[i];}VerTexType v1,v2;ArcType w;for(int k=0;k<G.arcnum;k++){cin>>v1>>v2>>w;edges[k].Head=v1;edges[k].Tail=v2;edges[k].lowcost=w;}
}
//按边上的权值Lowcost进行排序
bool cmp(Edge a,Edge b){if(a.lowcost<b.lowcost){return true;}
}
void MiniSpanTree_Kruskal(AMGraph G){//按边上的权值Lowcost进行排序sort(edges,edges+G.arcnum,cmp);//初始化,每个顶点都是一个独立的连通分量for(int i=0;i<G.vexnum;i++){VexSet[i]=i;}//反复执行,获取G.vexnum-1边for(int i=0;i<G.arcnum;i++){int v1=LocateVex(G.edges[i].Head); //边的起始顶点存储位置的序号,那个顶点    通俗讲:就是找顶点存储在数组的那个位置int v2=LocateVex(G.edges[i].Tail); //边的终止顶点存储位置的序号,那个顶点int vs1=VexSet[v1];  //边的起始顶点所在联通分量的编号int vs1=VexSet[V2];  //边的终止顶点所在连通分量的编号if(vs1!=vs2){printf("(%c,%c)\n",edges[i].Head,edges[i].Tail);//同时将起始顶点所在联通分量和终止顶点所在的连通分量进行(合并)统一编号,变为起始顶点所在连通分量//经过多次统一编号之后, 每个连通分量中就会包含多个顶点for(int j=0;j<G.vexnum;j++){if(VexSet[j]==vs2){VexSet[j]=vs1;}}}}
}
int main(){AMGraph G;CreateAMGraph(G);MiniSpanTree_Kruskal(G)return 0;
}

 感谢大家的点赞和关注,你们的支持是我创作的动力!

 

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

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

相关文章

vue-i18n 插件打包解析失效问题记录

vue-i18n 插件打包解析失效问题记录 开发环境中没有问题的&#xff0c;但打包发布之后就不行了&#xff0c;显示的就是模板字符串 // An highlighted block const messages {en: {step: {stepDesc1: Scan,stepDesc2: Analyze,stepDesc3: Result}},zh: {step: {stepDesc1: 扫描…

数据可视化 - 单子图

一、认识单子图 import matplotlib.pyplot as plt import numpy as np import pandas as pdplt.figure(num单子图, figsize(12, 8), facecolorw) # 中文字体 plt.rcParams[font.sans-serif] KaiTi # 负号显示 plt.rcParams[axes.unicode_minus] False# 2行&#xff0c;1列&a…

服务器上设置了代理之后,服务器可以访问外网,但是不能访问服务器本地。如何解决

你在服务器上设置了代理后&#xff0c;发现&#xff1a; 可以访问外网不能访问服务器本地地址&#xff08;如 localhost、127.0.0.1、内网IP&#xff09; 这是代理设置中常见的问题&#xff0c;尤其是当你设置了全局 HTTP/HTTPS 代理时。本地访问也会被强制走代理&#xff0c…

mysql启动报错:Can‘t connect to local MySQL server through socket

文章目录 一、报错内容二、解决方法 一、报错内容 在linux上启动mysql时报错 [rootlocalhost bin]# ./mysql -u root -p Enter password: ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock (2)执行以上命令后报错&#xff0c;并且也…

C# Avalonia 绑定模式 Mode 的区别,它们的应用场景

C# Avalonia 绑定模式 Mode 的区别&#xff0c;它们的应用场景 文章目录 1. **Default&#xff08;默认模式&#xff09;**2. **OneTime&#xff08;一次性绑定&#xff09;**3. **OneWay&#xff08;单向绑定&#xff09;**4. **TwoWay&#xff08;双向绑定&#xff09;**5. *…

【OpenGL学习】(七)纹理单元

【OpenGL学习】&#xff08;七&#xff09;纹理单元 OpenGL的纹理单元&#xff08;Texture Unit&#xff09;是GPU中用于管理和组织纹理资源的逻辑单元&#xff0c;它允许开发者在渲染过程中同时使用多个纹理&#xff0c;并通过采样器&#xff08;Sampler&#xff09;在着色器…

Ubuntu 下降 Linux Kernel 的版本备忘

此处以 ubuntu 22.04 为示例系统&#xff0c;来降低其 Linux kernel 的版本。 1. 降低 Linux kernel 版本 在 Ubuntu 22.04 上降低 Linux 内核版本的步骤如下所示。 步骤 1&#xff1a;检查当前内核版本 uname -r 确认当前运行的内核版本。 步骤 2&#xff1a;查看已安装的…

Python 数据分析与机器学习入门 (八):用 Scikit-Learn 跑通第一个机器学习模型

引言&#xff1a;初识 Scikit-Learn Scikit-learn 是 Python 机器学习领域的黄金标准库。它构建在 NumPy, SciPy 和 Matplotlib 之上&#xff0c;提供了大量用于分类、回归、聚类和降维等任务的算法。Scikit-learn 广受欢迎的原因在于其三大核心优势&#xff1a; 一致的 API 设…

FPGA芯片的配置方法

FPGA芯片的配置方法 文章目录 FPGA芯片的配置方法1. FPGA配置概述2. 主动配置模式3. 被动配置模式4. JTAG配置模式5. 总结 1. FPGA配置概述 当我们在PC机上的FPGA软件集成开发环境中完成我们的设计后&#xff0c;必须通过某种形式将其映射到FPGA芯片硬件中&#xff0c;这样FPG…

通过python+openCV实现对图片中箭头方向的判断

在项目中遇到一个需求,需要对图片中的箭头方向进行判断,本来是使用YOLOv8算法来实现的,但是发现YOLO的效果对箭头的识别效果很差,不管是分类算法还是检测算法,效果都不理想,因此试一试通过openCV对箭头方向进行判断,发现效果还可以。 下面附上完整的代码和原理。 文章目…

React 第六十六节Router中 StaticRouter使用详解及注意事项

前言 StaticRouter 是 React Router 为服务器端渲染&#xff08;SSR&#xff09;提供的专用路由组件。它允许在服务器环境中处理路由逻辑&#xff0c;确保服务器和客户端渲染结果一致。下面我将详细解释其用途、原理并提供完整的代码示例。 一、StaticRouter 的核心用途 服务…

嵌入模型与大语言模型的区别:从结构到应用的深度解析

嵌入模型与大语言模型的区别&#xff1a;从结构到应用的深度解析 在当今自然语言处理&#xff08;NLP&#xff09;技术蓬勃发展的背景下&#xff0c;嵌入模型&#xff08;Embedding Model&#xff09; 和 大语言模型&#xff08;Large Language Model, LLM&#xff09; 成为了…

el-date-picker赋值不成功

vue使用element 的时间组件el-date-picker赋值不成功&#xff0c;点击后才回显数据 解决: 组件未渲染完成之前赋值了&#xff0c;在onMounted函数内赋值&#xff0c;或者在确保组件已经渲染后赋值

深入浅出JavaScript中的私有变量与特权方法

深入浅出JavaScript中的私有变量与特权方法&#xff1a;封装的艺术 在JavaScript的开发实践中&#xff0c;私有变量和特权方法是实现数据封装和代码安全性的核心工具。它们不仅帮助我们隐藏敏感数据&#xff0c;还能通过闭包和作用域机制构建更健壮的代码结构。本文将从基础概…

ReactNative【实战系列教程】我的小红书 2 -- 快捷登录、手机号密码登录

最终效果 技术要点 用户协议 – 打开本地浏览器 点击后&#xff0c;直接打开本地浏览器浏览网页 // 最终需修改为 《用户协议》 的网址Linking.openURL("https://www.baidu.com");手机号输入框的 344 展示 onChangeText{(text: string) > {setPhone(formatPhone(…

【赵渝强老师】OceanBase数据库从零开始:Oracle模式

这里我们来介绍一下新上线的课程《OceanBase数据库从零开始&#xff1a;Oracle模式》&#xff0c;本门课程共11章。 视频讲解如下 【赵渝强老师】OceanBase从零开始&#xff08;Oracle模式&#xff09; 下面详细介绍一下每一章的主要内容&#xff1a; 第01章-OceanBase的体系…

Flink核心功能与运行流程详解

目录 一、背景 二、图构建 三、任务执行流程&#xff08;yarn per-job模式&#xff09; 3.1 Flink组件 3.2 执行流程 四、分布式调度 4.1 TM的slot 4.2 TM的slot的CPU与内存 4.3 节点的部署 4.4 节点的状态 4.5 节点部署流程 五、数据传输 5.1 内存分配 5.2 传输…

linux 操作docker的基本命令docker仓库

基本操作命令 docker run --nametest-host -itd centos7.6 /bin/bash 通过镜像创建容器 登录容器 [rootdocker101 ~]# docker exec -it test-host /bin/bash &#xff08;exec是执行&#xff0c;i是交互式。t叫tty&#xff09; 或者container id [rootdocker101 ~]# doc…

Netty学习路线图 - 第四阶段:Netty基础应用

Netty学习路线图 - 第四阶段&#xff1a;Netty基础应用 &#x1f4da; Netty学习系列之四 本文是Netty学习路线的第四篇&#xff0c;我们将用大白话讲解Netty的基础应用&#xff0c;带你从理论走向实践。 写在前面 大家好&#xff01;在前面三篇文章中&#xff0c;我们学习了J…

开源项目推荐:MCP Registry——管理MCP服务器的利器

探索MCP Registry:未来模型上下文协议的核心注册服务 随着人工智能技术的迅速发展,机器学习模型的管理和配置变得愈发重要。今天,我们将探索一个颇具潜力的开源项目——MCP Registry。这是一个由社区驱动的注册服务,专为模型上下文协议(Model Context Protocol,简称MCP)…