mirror of
https://github.com/Estom/notes.git
synced 2026-02-03 02:23:31 +08:00
图算法整理完毕
This commit is contained in:
@@ -2,6 +2,6 @@
|
||||
|
||||
> 每日计划包括三方面的内容:数据结构与算法、基础知识与项目经历、联邦学习与恶意软件
|
||||
|
||||
- [ ] 数据结构预算法——图算法和随机化算法整理完成
|
||||
- [x] 数据结构预算法——图算法和随机化算法整理完成
|
||||
- [ ] 基础知识与项目经历——项目经历整理完成
|
||||
- [ ] 联邦学习与恶意软件——TensorFlow federated学习完成part2
|
||||
|
||||
127
数据结构/5.4 并查集.md
127
数据结构/5.4 并查集.md
@@ -1,5 +1,9 @@
|
||||
# 并查集
|
||||
|
||||
> 参考文献
|
||||
> * [https://zhuanlan.zhihu.com/p/93647900/](https://zhuanlan.zhihu.com/p/93647900/)
|
||||
> * [https://blog.csdn.net/qq_41754350/article/details/81271567](https://blog.csdn.net/qq_41754350/article/details/81271567)
|
||||
|
||||
## 1 概念
|
||||
### 定义
|
||||
并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
|
||||
@@ -8,16 +12,117 @@
|
||||
* 查询(Find):查询两个元素是否在同一个集合中。
|
||||
|
||||
|
||||
## 2 题目——亲戚问题
|
||||
## 2 并查集原理
|
||||
|
||||
> ### 题目背景
|
||||
> * 若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
|
||||
> ### 题目描述
|
||||
> * 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
|
||||
> ### 输入格式
|
||||
> * 第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
|
||||
> * 以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。
|
||||
> * 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
|
||||
> ### 输出格式
|
||||
> * P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
|
||||
### 初始化
|
||||
* 初始化所有的节点的根节点是自己。
|
||||
* 假如有编号为1, 2, 3, ..., n的n个元素,我们用一个数组fa[]来存储每个元素的父节点(因为每个元素有且只有一个父节点,所以这是可行的)。一开始,我们先将它们的父节点设为自己。
|
||||
### 查询
|
||||
* 我们用递归的写法实现对代表元素的查询:一层一层访问父节点,直至根节点(根节点的标志就是父节点是本身)。要判断两个元素是否属于同一个集合,只需要看它们的根节点是否相同即可。
|
||||
### 合并
|
||||
|
||||
* 合并操作也是很简单的,先找到两个集合的代表元素,然后将前者的父节点设为后者即可。当然也可以将后者的父节点设为前者,这里暂时不重要。本文末尾会给出一个更合理的比较方法。
|
||||
|
||||
### 路径压缩
|
||||
|
||||
* 防止多个节点形成递归查询链太长,在查找过程中队路径进行压缩。
|
||||
* 即将每一个当前的节点直接指向其根节点。
|
||||
|
||||
## 3 代码实现
|
||||
```C++
|
||||
class SetUnion{
|
||||
public:
|
||||
vector<int> vec;
|
||||
// 初始化并查集
|
||||
SetUnion(int n){
|
||||
vec=vector<int>(n);
|
||||
for(int i=0;i<n;i++){
|
||||
vec[i]=i;
|
||||
}
|
||||
}
|
||||
// 没有路径压缩的递归查找
|
||||
int find_r(int x){
|
||||
if(x==vec[x])return x;
|
||||
else{
|
||||
return find_r(vec[x]);
|
||||
}
|
||||
}
|
||||
// 合并两个非连通图。
|
||||
void merge(int i,int j){
|
||||
vec[find(i)]=find(j);
|
||||
}
|
||||
// 有路径压缩的递归查找
|
||||
int find(int x){
|
||||
if(x==vec[x]){
|
||||
return x;
|
||||
}
|
||||
else{
|
||||
vec[x]=find(vec[x]);
|
||||
return vec[x];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## 4 题目——亲戚问题
|
||||
|
||||
### 题目背景
|
||||
* 若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
|
||||
### 题目描述
|
||||
* 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
|
||||
### 输入格式
|
||||
* 第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
|
||||
* 以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。
|
||||
* 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
|
||||
### 输出格式
|
||||
* P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
|
||||
|
||||
### 代码实现
|
||||
|
||||
```C++
|
||||
#include <cstdio>
|
||||
#define MAXN 5005
|
||||
int fa[MAXN], rank[MAXN];
|
||||
inline void init(int n)
|
||||
{
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
fa[i] = i;
|
||||
rank[i] = 1;
|
||||
}
|
||||
}
|
||||
int find(int x)
|
||||
{
|
||||
return x == fa[x] ? x : (fa[x] = find(fa[x]));
|
||||
}
|
||||
inline void merge(int i, int j)
|
||||
{
|
||||
int x = find(i), y = find(j);
|
||||
if (rank[x] <= rank[y])
|
||||
fa[x] = y;
|
||||
else
|
||||
fa[y] = x;
|
||||
if (rank[x] == rank[y] && x != y)
|
||||
rank[y]++;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
int n, m, p, x, y;
|
||||
scanf("%d%d%d", &n, &m, &p);
|
||||
init(n);
|
||||
for (int i = 0; i < m; ++i)
|
||||
{
|
||||
scanf("%d%d", &x, &y);
|
||||
merge(x, y);
|
||||
}
|
||||
for (int i = 0; i < p; ++i)
|
||||
{
|
||||
scanf("%d%d", &x, &y);
|
||||
printf("%s\n", find(x) == find(y) ? "Yes" : "No");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
42
数据结构/5.4.cpp
Normal file
42
数据结构/5.4.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include<vector>
|
||||
#include<iostream>
|
||||
using namespace std;
|
||||
|
||||
class SetUnion{
|
||||
public:
|
||||
vector<int> vec;
|
||||
// 初始化并查集
|
||||
SetUnion(int n){
|
||||
vec=vector<int>(n);
|
||||
for(int i=0;i<n;i++){
|
||||
vec[i]=i;
|
||||
}
|
||||
}
|
||||
// 没有路径压缩的递归查找
|
||||
int find_r(int x){
|
||||
if(x==vec[x])return x;
|
||||
else{
|
||||
return find_r(vec[x]);
|
||||
}
|
||||
}
|
||||
// 合并两个非连通图。
|
||||
void merge(int i,int j){
|
||||
vec[find(i)]=find(j);
|
||||
}
|
||||
// 有路径压缩的递归查找
|
||||
int find(int x){
|
||||
if(x==vec[x]){
|
||||
return x;
|
||||
}
|
||||
else{
|
||||
vec[x]=find(vec[x]);
|
||||
return vec[x];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int main(){
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -8,3 +8,10 @@
|
||||
>* 图算法-Kruskal算法
|
||||
|
||||
|
||||
# bellman ford 和 SPFA两个算法等到以后再学吧。我赌十块不需要。等到下次在复习的时候完善这两个算法,已经没时间了
|
||||
|
||||
> 参考文献
|
||||
> * [https://www.cnblogs.com/lfri/p/9521271.html](https://www.cnblogs.com/lfri/p/9521271.html)
|
||||
> * [https://blog.csdn.net/qq_35644234/article/details/61614581](https://blog.csdn.net/qq_35644234/article/details/61614581)
|
||||
|
||||
> 这两个博客够了
|
||||
@@ -10,7 +10,10 @@
|
||||
> 参考文献
|
||||
> [https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html](https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html)
|
||||
## 1 问题描述
|
||||
* Kruskal算法是一种用来寻找**最小生成树的算法**,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。
|
||||
* Kruskal算法是一种用来寻找**最小生成树的算法**,由Joseph Kruskal在1956年发表。
|
||||
* 用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是**贪婪算法**的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。
|
||||
* 当点少,但是关系复杂的时候,使用prim算法,进行点的贪心。
|
||||
* 当点多,但是关系很稀疏的时候,使用kruskal算法那,进行边的贪心
|
||||
|
||||
## 2 算法原理
|
||||
|
||||
@@ -48,63 +51,193 @@
|
||||
## 5 算法实现
|
||||
|
||||
```C++
|
||||
typedef struct
|
||||
{
|
||||
char vertex[VertexNum]; //顶点表
|
||||
int edges[VertexNum][VertexNum]; //邻接矩阵,可看做边表
|
||||
int n,e; //图中当前的顶点数和边数
|
||||
}MGraph;
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
int u; //边的起始顶点
|
||||
int v; //边的终止顶点
|
||||
int w; //边的权值
|
||||
}Edge;
|
||||
#include<iostream>
|
||||
#include<vector>
|
||||
#include<queue>
|
||||
#include<algorithm>
|
||||
using namespace std;
|
||||
|
||||
void kruskal(MGraph G)
|
||||
{
|
||||
int i,j,u1,v1,sn1,sn2,k;
|
||||
int vset[VertexNum]; //辅助数组,判定两个顶点是否连通
|
||||
int E[EdgeNum]; //存放所有的边
|
||||
k=0; //E数组的下标从0开始
|
||||
for (i=0;i<G.n;i++)
|
||||
{
|
||||
for (j=0;j<G.n;j++)
|
||||
{
|
||||
if (G.edges[i][j]!=0 && G.edges[i][j]!=INF)
|
||||
{
|
||||
E[k].u=i;
|
||||
E[k].v=j;
|
||||
E[k].w=G.edges[i][j];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
heapsort(E,k,sizeof(E[0])); //堆排序,按权值从小到大排列
|
||||
for (i=0;i<G.n;i++) //初始化辅助数组
|
||||
{
|
||||
vset[i]=i;
|
||||
}
|
||||
k=1; //生成的边数,最后要刚好为总边数
|
||||
j=0; //E中的下标
|
||||
while (k<G.n)
|
||||
{
|
||||
sn1=vset[E[j].u];
|
||||
sn2=vset[E[j].v]; //得到两顶点属于的集合编号
|
||||
if (sn1!=sn2) //不在同一集合编号内的话,把边加入最小生成树
|
||||
{
|
||||
printf("%d ---> %d, %d",E[j].u,E[j].v,E[j].w);
|
||||
k++;
|
||||
for (i=0;i<G.n;i++)
|
||||
{
|
||||
if (vset[i]==sn2)
|
||||
{
|
||||
vset[i]=sn1;
|
||||
}
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
// 一下算法可以借助一些东西进行改进。
|
||||
class Graph{
|
||||
private:
|
||||
int vertex_num; //图的顶点个数
|
||||
int edge;
|
||||
// 如果顶点的表示是离散的应该加一层映射
|
||||
vector<vector<int>> arc; //邻接矩阵
|
||||
public:
|
||||
// 测试用的默认构造函数
|
||||
Graph();
|
||||
//构造函数,从命令行输入
|
||||
Graph(int vertex_num_,int edge_);
|
||||
//打印所有的边
|
||||
void print_edge();
|
||||
// 打印整个邻接矩阵
|
||||
void print_arc();
|
||||
//求最短路径
|
||||
void Dijkstra(int begin);//单源最短路
|
||||
void Floyd();//多源最短路
|
||||
void Prim(int begin);//最小生成树
|
||||
void Kruskal();//最小生成树
|
||||
};
|
||||
|
||||
Graph::Graph(){
|
||||
this->vertex_num = 7;
|
||||
this->edge=12;
|
||||
this->arc = vector<vector<int>>(vertex_num,vector<int>(vertex_num,INT_MAX));
|
||||
// cout<<vertex_num<<endl;
|
||||
// 初始化一个邻接矩阵.dijskra算法的图。
|
||||
arc[0][1]=12;
|
||||
arc[1][0]=12;
|
||||
arc[0][6]=14;
|
||||
arc[6][0]=14;
|
||||
arc[0][5]=16;
|
||||
arc[5][0]=16;
|
||||
arc[1][2]=10;
|
||||
arc[2][1]=10;
|
||||
arc[1][5]=7;
|
||||
arc[5][1]=7;
|
||||
arc[2][3]=3;
|
||||
arc[3][2]=3;
|
||||
arc[2][4]=5;
|
||||
arc[4][2]=5;
|
||||
arc[2][5]=6;
|
||||
arc[5][2]=6;
|
||||
arc[3][4]=4;
|
||||
arc[4][3]=4;
|
||||
arc[4][5]=2;
|
||||
arc[5][4]=2;
|
||||
arc[4][6]=8;
|
||||
arc[6][4]=8;
|
||||
arc[5][6]=9;
|
||||
arc[6][5]=9;
|
||||
// 对角线自己到自己是0
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
arc[i][i]=0;
|
||||
}
|
||||
}
|
||||
|
||||
// 从命令行输入一个邻接矩阵
|
||||
Graph::Graph(int vertex_num_,int edge_){
|
||||
this->vertex_num=vertex_num_;
|
||||
this->edge=edge_;
|
||||
this->arc=vector<vector<int>>(vertex_num,vector<int>(vertex_num,INT_MAX));
|
||||
int beg=0,end=0,weight=0;
|
||||
for(int i=0;i<edge_;i++){
|
||||
cin>>beg>>end>>weight;
|
||||
arc[beg][end]=weight;
|
||||
// 如果是无向图则需要添加反向的路径
|
||||
arc[end][beg]=weight;
|
||||
}
|
||||
}
|
||||
|
||||
void Graph::print_edge(){
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
for(int j=0;j<vertex_num;j++){
|
||||
if(arc[i][j]<INT_MAX)
|
||||
cout<<"begin:"<<i<<"\tend:"<<j<<"\tweight:"<<arc[i][j]<<endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Graph::print_arc(){
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
for(int j=0;j<vertex_num;j++){
|
||||
cout<<arc[i][j]<<"\t";
|
||||
}
|
||||
cout<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 并查集的设计实现,用来检验是否连通
|
||||
class SetUnion{
|
||||
public:
|
||||
vector<int> vec;
|
||||
// 初始化并查集
|
||||
SetUnion(int n){
|
||||
vec=vector<int>(n);
|
||||
for(int i=0;i<n;i++){
|
||||
vec[i]=i;
|
||||
}
|
||||
}
|
||||
// 没有路径压缩的递归查找
|
||||
int find_r(int x){
|
||||
if(x==vec[x])return x;
|
||||
else{
|
||||
return find_r(vec[x]);
|
||||
}
|
||||
}
|
||||
// 合并两个非连通图。
|
||||
void merge(int i,int j){
|
||||
vec[find(i)]=find(j);
|
||||
}
|
||||
// 有路径压缩的递归查找
|
||||
int find(int x){
|
||||
if(x==vec[x]){
|
||||
return x;
|
||||
}
|
||||
else{
|
||||
vec[x]=find(vec[x]);
|
||||
return vec[x];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
// 构造边的对象
|
||||
struct Edge
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
int weight;
|
||||
Edge(int s,int e,int w){
|
||||
start=s;
|
||||
end=e;
|
||||
weight=w;
|
||||
}
|
||||
// 重写<运算符
|
||||
bool operator<(const Edge& a)const{
|
||||
return a.weight > weight;
|
||||
}
|
||||
};
|
||||
// 贪心选择最小的边。且使他们不连通。
|
||||
void Graph::Kruskal(){
|
||||
// 构建结果集合
|
||||
vector<Edge> result;
|
||||
// 对边初始化并进行排序
|
||||
vector<Edge> vec;
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
for(int j=i+1;j<vertex_num;j++){
|
||||
if(arc[i][j]<INT_MAX){
|
||||
vec.push_back(Edge(i,j,arc[i][j]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
sort(vec.begin(),vec.end());
|
||||
|
||||
// 创建点的并查集
|
||||
SetUnion su(vertex_num);
|
||||
int k=0;
|
||||
for(int i=0;i<vec.size();i++){
|
||||
Edge e = vec[i];
|
||||
if(su.find(e.start)!=su.find(e.end)){
|
||||
result.push_back(e);
|
||||
su.merge(e.start,e.end);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
for(int i=0;i<result.size();i++){
|
||||
cout<<result[i].start<<"\t"<<result[i].end<<"\t"<<result[i].weight<<endl;
|
||||
}
|
||||
}
|
||||
int main(){
|
||||
Graph g;
|
||||
// g.print();
|
||||
// g.Dijkstra(3);
|
||||
// g.Floyd();
|
||||
// g.Prim(2);
|
||||
g.Kruskal();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
>* 图算法-Kruskal算法
|
||||
|
||||
> 参考文献
|
||||
> [https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html](https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html)
|
||||
> * [https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html](https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html)
|
||||
|
||||
## 1 问题分析
|
||||
|
||||
* 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索**最小生成树**。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。
|
||||
* 主要是**顶点的贪心思想**。
|
||||
* 主要思想是**基于顶点的贪心思想**。
|
||||
|
||||
## 2 算法原理
|
||||
|
||||
|
||||
@@ -1,30 +1,10 @@
|
||||
#include<iostream>
|
||||
#include<vector>
|
||||
#include<queue>
|
||||
#include<algorithm>
|
||||
using namespace std;
|
||||
|
||||
// 构造边的对象
|
||||
struct Edge
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
int weight;
|
||||
Edge(int s,int e,int w){
|
||||
start=s;
|
||||
end=e;
|
||||
weight=w;
|
||||
}
|
||||
// 重写<运算符
|
||||
bool operator<(const Edge& a)const{
|
||||
return a.weight < weight;
|
||||
}
|
||||
};
|
||||
// struct cmp{
|
||||
// bool operator()(Edge a,Edge b){
|
||||
// return b.weight > a.weight;
|
||||
// }
|
||||
// };
|
||||
|
||||
// 一下算法可以借助一些东西进行改进。
|
||||
class Graph{
|
||||
private:
|
||||
int vertex_num; //图的顶点个数
|
||||
@@ -43,7 +23,7 @@ public:
|
||||
//求最短路径
|
||||
void Dijkstra(int begin);//单源最短路
|
||||
void Floyd();//多源最短路
|
||||
void Prim();//最小生成树
|
||||
void Prim(int begin);//最小生成树
|
||||
void Kruskal();//最小生成树
|
||||
};
|
||||
|
||||
@@ -153,7 +133,7 @@ void Graph::Dijkstra(int begin){
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 三层for循环。中间节点、起始点、终止点。不断更新表格。
|
||||
void Graph::Floyd(){
|
||||
// 初始化结果集合,一个用来记录最短距离,一个用来记录最短路径。
|
||||
vector<vector<int>> distance(arc);
|
||||
@@ -188,38 +168,46 @@ void Graph::Floyd(){
|
||||
// 经过分析不需要使用优先队列。与dijkstra算法一样。可以使用向量来表示是否选中。
|
||||
// 与dijkstra算法十分相似。唯一的区别是距离更新的方式不同。dijkstra更新使用新的点做为中间节点,计算起始点到该节点的距离,更新最小距离。结果集中保存的是到某个点的最小距离。
|
||||
// 而Prim算法是直接使用新的点和新添加到集合中的点进行更新。如果新添加到结果集合中的点周围有更短的距离,则进行更新。而不是作为转接点。结果集中保存的是某个节点的前一个节点。构成一条边。
|
||||
void Graph::Prim(){
|
||||
// 为了跟Dijkstra统一,这里改成相同的添加方式。
|
||||
void Graph::Prim(int begin){
|
||||
// 最小生成树开始的顶点。表示顶点是否被选中过.-1表示没有被选中。其他值表示它的上一个节点。
|
||||
vector<int> result(vertex_num,-1);
|
||||
// 用来记录到某个节点的距离的向量
|
||||
result[begin]=begin;//表示从当前节点开始.前一个节点时自己。
|
||||
|
||||
// 用来记录到某个节点的距离的向量。使用0表示节点已经计算过,添加到结果当中了。
|
||||
vector<int> distance(vertex_num,INT_MAX);
|
||||
// 用来记录到某个节点的前一个节点向量。dijsktra也可以添加一个前置节点向量,用来保存路径。
|
||||
vector<int> pre_point(vertex_num,-1);
|
||||
|
||||
// 首先选择第一个节点
|
||||
int min_index=2;
|
||||
int min_distance=INT_MAX;
|
||||
result[min_index]=min_index;//表示从当前节点开始.前一个节点时自己。
|
||||
// 初始化刚开始的边集合。从当前点出发的边集合
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
distance[i]=arc[begin][i];
|
||||
pre_point[i]=begin;
|
||||
}
|
||||
|
||||
for(int k=0;k<vertex_num-1;k++){
|
||||
// 以当前节点更新距离结合
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
if(result[i]<0 && arc[min_index][i]<distance[i]){
|
||||
distance[i]=arc[min_index][i];
|
||||
pre_point[i]=min_index;
|
||||
}
|
||||
}
|
||||
min_distance=INT_MAX;
|
||||
int min_index =0;
|
||||
int min_distance=INT_MAX;
|
||||
// 挑选距离最小的节点
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
if(result[i]<0 && distance[i]<min_distance){
|
||||
if(distance[i]!=0 && distance[i]<min_distance){
|
||||
min_distance=distance[i];
|
||||
min_index=i;
|
||||
}
|
||||
}
|
||||
cout<<min_index<<"\t"<<pre_point[min_index]<<endl;
|
||||
// cout<<min_index<<"\t"<<pre_point[min_index]<<endl;
|
||||
// 更新结果集合
|
||||
result[min_index]=pre_point[min_index];
|
||||
distance[min_index]=0;
|
||||
|
||||
// 以当前节点更新距离结合
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
if(distance[i]!=0 && arc[min_index][i]<distance[i]){
|
||||
distance[i]=arc[min_index][i];
|
||||
pre_point[i]=min_index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 输出结果集合
|
||||
@@ -229,14 +217,94 @@ void Graph::Prim(){
|
||||
|
||||
}
|
||||
|
||||
// 并查集的设计实现,用来检验是否连通
|
||||
class SetUnion{
|
||||
public:
|
||||
vector<int> vec;
|
||||
// 初始化并查集
|
||||
SetUnion(int n){
|
||||
vec=vector<int>(n);
|
||||
for(int i=0;i<n;i++){
|
||||
vec[i]=i;
|
||||
}
|
||||
}
|
||||
// 没有路径压缩的递归查找
|
||||
int find_r(int x){
|
||||
if(x==vec[x])return x;
|
||||
else{
|
||||
return find_r(vec[x]);
|
||||
}
|
||||
}
|
||||
// 合并两个非连通图。
|
||||
void merge(int i,int j){
|
||||
vec[find(i)]=find(j);
|
||||
}
|
||||
// 有路径压缩的递归查找
|
||||
int find(int x){
|
||||
if(x==vec[x]){
|
||||
return x;
|
||||
}
|
||||
else{
|
||||
vec[x]=find(vec[x]);
|
||||
return vec[x];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
// 构造边的对象
|
||||
struct Edge
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
int weight;
|
||||
Edge(int s,int e,int w){
|
||||
start=s;
|
||||
end=e;
|
||||
weight=w;
|
||||
}
|
||||
// 重写<运算符
|
||||
bool operator<(const Edge& a)const{
|
||||
return a.weight > weight;
|
||||
}
|
||||
};
|
||||
// 贪心选择最小的边。且使他们不连通。
|
||||
void Graph::Kruskal(){
|
||||
|
||||
// 构建结果集合
|
||||
vector<Edge> result;
|
||||
// 对边初始化并进行排序
|
||||
vector<Edge> vec;
|
||||
for(int i=0;i<vertex_num;i++){
|
||||
for(int j=i+1;j<vertex_num;j++){
|
||||
if(arc[i][j]<INT_MAX){
|
||||
vec.push_back(Edge(i,j,arc[i][j]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
sort(vec.begin(),vec.end());
|
||||
|
||||
// 创建点的并查集
|
||||
SetUnion su(vertex_num);
|
||||
int k=0;
|
||||
for(int i=0;i<vec.size();i++){
|
||||
Edge e = vec[i];
|
||||
if(su.find(e.start)!=su.find(e.end)){
|
||||
result.push_back(e);
|
||||
su.merge(e.start,e.end);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
for(int i=0;i<result.size();i++){
|
||||
cout<<result[i].start<<"\t"<<result[i].end<<"\t"<<result[i].weight<<endl;
|
||||
}
|
||||
}
|
||||
int main(){
|
||||
Graph g;
|
||||
// g.print();
|
||||
// g.Dijkstra(3);
|
||||
// g.Floyd();
|
||||
// g.Prim();
|
||||
// g.Prim(2);
|
||||
// g.Kruskal();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user