图算法

This commit is contained in:
yinkanglong_lab
2021-04-05 14:19:29 +08:00
parent 8ec32f0d25
commit 9233a697e6
9 changed files with 617 additions and 410 deletions

View File

@@ -68,4 +68,10 @@
* 不能过度使用列表。
* 普通的论述直接分段。或者使用>表示引用。
* 而不是一直过度使用列表。用来缩进。
* 换一个更好的md渲染引擎。
* 换一个更好的md渲染引擎。
### 补充
* 在写笔记的过程中应该控制好,“知识类”笔记的粒度。不能把一些没用的什么优势劣势啥玩意的也写上。又不是应付考试。你现在要做的是记住原理跟方法,当然就会知道这种思想的优势和劣势。
* 粗粒度的笔记开始。尽量只记录关键点和关键点的解析。

View File

@@ -76,6 +76,51 @@
### 创建
* 图的邻接链表实现方法
```C++
// 图的邻接链表实现方法
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
const int N = 1e5+1;
struct Edge
{
int to;
int w;
Edge(int t = 0, int w_ = 0) : to(t), w(w_) {}
};
// edge向量的数组
vector<Edge> H1[N];
// edge向量的向量查找需要遍历某个顶点所有边的集合
vector<vector<Edge>> H2;
// edge映射的向量查找会更快
vector<unordered_map<int, int>> H3;
int main()
{
H2.resize(N + 1);
H3.resize(N + 1);
for (int i = 1; i <= N; ++i)
{
int a, b, c;
cin >> a >> b >> c;
H1[a].push_back(Edge(b,c));
H2[a].push_back(Edge(b, c));
H3[a][b] = c;
}
getchar();
return 0;
}
```
### 搜索——广度优先搜索
* 广度优先搜索是一种图遍历算法,它从根节点开始遍历图并探索所有相邻节点。 然后,它选择最近的节点并浏览所有未探测的节点。 对于每个最近的节点,该算法遵循相同的过程,直到找到目标为止。

40
数据结构/7.cpp Normal file
View File

@@ -0,0 +1,40 @@
// 图的邻接链表实现方法
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
const int N = 1e5+1;
struct Edge
{
int to;
int w;
Edge(int t = 0, int w_ = 0) : to(t), w(w_) {}
};
// edge向量的数组
vector<Edge> H1[N];
// edge向量的向量查找需要遍历某个顶点所有边的集合
vector<vector<Edge>> H2;
// edge映射的向量查找会更快
vector<unordered_map<int, int>> H3;
int main()
{
H2.resize(N + 1);
H3.resize(N + 1);
for (int i = 1; i <= N; ++i)
{
int a, b, c;
cin >> a >> b >> c;
H1[a].push_back(Edge(b,c));
H2[a].push_back(Edge(b, c));
H3[a][b] = c;
}
getchar();
return 0;
}

View File

@@ -14,7 +14,7 @@
## 1 问题分析
* 最短路径算法。用于**计算一个节点到其他节点的最短路径。**
* 最短路径算法。用于**单源最短路算法**
* Dijkstra算法算是**贪心思想**实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。
@@ -76,96 +76,136 @@
时间复杂度$O(n^2)$
## 5 算法实现
```
```C++
#include<iostream>
#include<sstream>
#include<vector>
using namespace std;
const int Max=100;
string Int_to_String(int n)//int转换string
{
ostringstream stream;
stream<<n; //n为int类型
return stream.str();
}
class MGraph{
public:
MGraph(){
}
MGraph(int n,int e);
~MGraph(){
}
public:
int vertex[Max];
int arc[Max][Max];
int vertexNum,arcNum;
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();//最小生成树
void Kruskal();//最小生成树
};
MGraph::MGraph(int n,int e){
int i,j;
vertexNum=n;
arcNum=e;
for(int i=0;i<vertexNum;i++)
vertex[i]=i;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
arc[i][j]=10000;
cout<<"请输入图中各边的情况:"<<endl;
for(int k=0;k<e;k++){
cin>>i>>j;
cin>>arc[i][j];
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;
}
}
void Dijkstra(MGraph G,int v){
int dist[Max],s[Max];
string path[Max];
for(int i=0;i<G.vertexNum;i++)
{
dist[i]=G.arc[v][i];
if(dist[i]!=10000)
path[i]=Int_to_String(G.vertex[v])+"->"+Int_to_String(G.vertex[i]);
else
path[i]="";
// 从命令行输入一个邻接矩阵
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;
}
s[0]=v;
dist[v]=0;
int num=1;
for(int i=0;i<G.vertexNum;i++)
{
int t=10000,k;
for(int j=0;j<G.vertexNum;j++)
{
if(dist[j]<t&&dist[j]!=0)
{
t=dist[j];
k=j;
}
}
cout<<"终点为:"<<k<<" 最短路径长度为:"<<dist[k]<<" 过程:"<<path[k]<<endl;
s[num++]=k;
for(int j=0;j<G.vertexNum;j++){
if(dist[j]!=0&&dist[j]>(dist[k]+G.arc[k][j])){
dist[j]=dist[k]+G.arc[k][j];
path[j]=path[k]+"->"+Int_to_String(j);
}
}
dist[k]=0;
if(num==G.vertexNum)
break;
}
cout<<"找到终点的顺序为:"<<endl;
for(int i=1;i<num;i++)
cout<<s[i]<<" ";
cout<<endl;
}
int main(){
int n,e,v;
cout<<"输入起点下标:"<<endl;
cin>>v;
cout<<"输入图的顶点数和边数:"<<endl;
cin>>n>>e;
MGraph G(n,e);
Dijkstra(G,v);
return 0;
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;
}
}
void Graph::Dijkstra(int begin){
// 初始化结果集,到自己是0
vector<int> result(vertex_num,INT_MAX);
result[begin]=0;
// 初始化距离集合,每次从中挑选
vector<int> distance(vertex_num,INT_MAX);
for(int i=0;i<vertex_num;i++){
distance[i]=arc[begin][i];
}
for(int k=0;k<vertex_num-1;k++){
// 从中挑选非零的最小值使用0表示已经挑选到结果集合中。
int min_distance=INT_MAX;
int min_index=0;
for(int i=0;i<vertex_num;i++){
if(distance[i]!=0 && distance[i]<min_distance){
min_distance=distance[i];
min_index=i;
}
}
// 添加到结果集合中并更新删除distance集合。
cout<<min_index<<endl;
result[min_index]=min_distance;
distance[min_index]=0;
// 使用min_index更新距离集合
for(int i=0;i<vertex_num;i++){
if(distance[i]!=0 && arc[min_index][i]<INT_MAX){
distance[i]=min(distance[i],min_distance+arc[min_index][i]);
}
}
}
for(int i=0;i<vertex_num;i++){
cout<<"end:"<<i<<"\tweight:"<<result[i]<<endl;
}
}
```

View File

@@ -13,7 +13,8 @@
> * [https://blog.csdn.net/jeffleo/article/details/53349825](https://blog.csdn.net/jeffleo/article/details/53349825)
## 1 问题分析
* Floyd算法是一个经典的**动态规划算法**它又被称为插点法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。Floyd算法是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,算法目标是寻找从点i到点j的最短路径。
* Floyd算法是一个经典的**动态规划算法**它又被称为插点法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。
* Floyd算法是一种利用动态规划的思想寻找给定的加权图中**多源点最短路径的算法** ,算法目标是寻找从点i到点j的最短路径。
## 2 算法原理
@@ -66,223 +67,181 @@
```C++
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
#define len 100
#define INF 999999
struct Dis {
string path;
int value;
bool visit;
Dis() {
visit = false;
value = 0;
path = "";
}
};
class Graph{
// 内部类
private:
// 邻接表中表对应的链表的顶点
class ENode{
public:
int vex; // 顶点
int weight; // 权重
ENode *nextEdge; // 指向下一条弧
};
// 邻接表中表的顶点
class VNode{
public:
char data; // 顶点信息
ENode *firstEdge; // 指向第一条依付该顶点的弧
};
// 私有成员
private:
int n; // 节点个数
int e; // 边的个数
VNode mVexs[len];
public:
Graph(){
ENode *node1, *node2;
n = 7;
e = 12;
// 设置节点为默认数值
string nodes = "ABCDEFG";
// 输入节点
for(int i=0; i < n; i++){
mVexs[i].data = nodes[i];
mVexs[i].firstEdge = NULL;
}
// 设置边为默认值
char edges[][2] = {
{'A', 'B'},
{'A', 'F'},
{'A', 'G'},
{'B', 'C'},
{'B', 'F'},
{'C', 'D'},
{'C', 'E'},
{'C', 'F'},
{'D', 'E'},
{'E', 'F'},
{'E', 'G'},
{'F', 'G'}
};
// 边的权重
int weights[len] = {12, 16, 14, 10, 7, 3, 5, 6, 4, 2, 8, 9};
// 初始化邻接表的边
for(int i=0; i < e; i++){
int start = get_Node_Index(edges[i][0]);
int end = get_Node_Index(edges[i][1]);
// 初始化 node1
node1 = new ENode();
node1->vex = end;
node1->weight = weights[i];
node1->nextEdge = NULL;
// 将 node 添加到 start 所在链表的末尾
if(mVexs[start].firstEdge == NULL){
mVexs[start].firstEdge = node1;
}
else{
linkLast(mVexs[start].firstEdge, node1);
}
// 初始化 node2
node2 = new ENode();
node2->vex = start;
node2->weight = weights[i];
node2->nextEdge = NULL;
// 将 node 添加到 end 所在链表的末尾
if(mVexs[end].firstEdge == NULL){
mVexs[end].firstEdge = node2;
}
else{
linkLast(mVexs[end].firstEdge, node2);
}
}
}
// 相邻节点链接子函数
void linkLast(ENode*p1, ENode*p2){
ENode*p = p1;
while(p->nextEdge){
p = p->nextEdge;
}
p->nextEdge = p2;
}
// 返回顶点下标
int get_Node_Index(char number){
for(int i=0; i < n; i++){
if(number == mVexs[i].data){
return i;
}
}
return -1; //这句话永远不会执行的
}
// 输出邻接表
void print(){
for(int i=0; i < n; i ++){
cout<<mVexs[i].data;
ENode *temp = mVexs[i].firstEdge;
while(temp){
cout<<" -> "<<temp->vex;
temp = temp->nextEdge;
}
cout<<endl;
}
cout<<endl;
}
// 得到两个节点之间的权重
int getWeight(int m, int n){
ENode *enode = mVexs[m].firstEdge;
while(enode){
if(enode->vex == n){
return enode->weight;
}
enode = enode->nextEdge;
}
return INF;
}
// 弗洛伊德算法
void floyd(){
int dist[n][n]; // 距离矩阵
int path[7][7]; // 路径矩阵, 7为节点数目
int i, j, k;
int temp;
// 初始化权重
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
if(i == j){
dist[i][j] = 0;
}
else{
dist[i][j] = getWeight(i, j);
}
path[i][j] = i;
}
}
// floyd 算法开始
for(k = 0; k < n; k++){
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
temp = (dist[i][k] == INF || dist[k][j] == INF)? INF : (dist[i][k] + dist[k][j]);
if(temp < dist[i][j]){
dist[i][j] = temp;
path[i][j] = path[k][j];
}
}
}
}
// 打印出两点之间最短距离 + 路径
for(i = 0; i < n-1; i++){
for(j = i+1; j < n; j++){
if(dist[i][j] < 10){
cout<<mVexs[i].data<<" -> "<<mVexs[j].data<<": "<<dist[i][j]<<" , 路径为: ";
}
else{
cout<<mVexs[i].data<<" -> "<<mVexs[j].data<<": "<<dist[i][j]<<" , 路径为: ";
}
getPath(i, j, path);
cout<<endl;
}
cout<<endl;
}
// 输出路径矩阵观察, 可用此矩阵自己用笔演算一下路径查找过程
// for(i = 0; i < n; i++){
// for(j = 0; j < n; j++){
// cout<<path[i][j]<<" ";
// }
// cout<<endl;
// }
}
// 递归实现得到节点之间最短路径
void getPath(int start, int end, int path[][7]){
if(path[start][end] == start){
cout<<mVexs[start].data<<" "<<mVexs[end].data<<" ";
}
else{
getPath(start, path[start][end], path);
cout<<mVexs[end].data<<" ";
}
}
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();
};
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;
}
}
void Graph::Dijkstra(int begin){
// 初始化结果集,到自己是0
vector<int> result(vertex_num,INT_MAX);
result[begin]=0;
// 初始化距离集合,每次从中挑选
vector<int> distance(vertex_num,INT_MAX);
for(int i=0;i<vertex_num;i++){
distance[i]=arc[begin][i];
}
for(int k=0;k<vertex_num-1;k++){
// 从中挑选非零的最小值使用0表示已经挑选到结果集合中。
int min_distance=INT_MAX;
int min_index=0;
for(int i=0;i<vertex_num;i++){
if(distance[i]!=0 && distance[i]<min_distance){
min_distance=distance[i];
min_index=i;
}
}
// 添加到结果集合中并更新删除distance集合。
cout<<min_index<<endl;
result[min_index]=min_distance;
distance[min_index]=0;
// 使用min_index更新距离集合
for(int i=0;i<vertex_num;i++){
if(distance[i]!=0 && arc[min_index][i]<INT_MAX){
distance[i]=min(distance[i],min_distance+arc[min_index][i]);
}
}
}
for(int i=0;i<vertex_num;i++){
cout<<"end:"<<i<<"\tweight:"<<result[i]<<endl;
}
}
void Graph::Floyd(){
// 初始化结果集合,一个用来记录最短距离,一个用来记录最短路径。
vector<vector<int>> distance(arc);
vector<vector<int>> path(vertex_num,vector<int>(vertex_num,0));
int i,j,k;
int temp;
for(k=0;k<vertex_num;k++){
for(i=0;i<vertex_num;i++){
for(j=0;j<vertex_num;j++){
temp=INT_MAX;
if(distance[i][k]<INT_MAX && distance[k][j]<INT_MAX){
temp=distance[i][k]+distance[k][j];
}
if(temp<distance[i][j]){
distance[i][j]=temp;
path[i][j]=k;
}
}
}
}
for(i=0;i<vertex_num;i++){
for(j=0;j<vertex_num;j++){
cout<<distance[i][j]<<"\t";
}
cout<<endl;
}
}
int main(){
Graph g;
// 输出邻接表
// g.print();
// 弗洛伊德算法
g.floyd();
// g.Dijkstra(3);
g.Floyd();
return 0;
}
```

View File

@@ -1,11 +1,10 @@
# 图算法-Dijkstra算法
# 图算法-Floyd算法
# 图算法-Bellman-Ford算法
# 图算法-Prim算法
# 图算法-Kruskal算法
# 图算法-Bellman-Ford算法
> 目录
>* 图算法-Dijkstra算法
>* 图算法-Floyd算法
>* 图算法-Bellman-Ford算法
>* 图算法-Prim算法
>* 图算法-Kruskal算法
>* 图算法-Kruskal算法

View File

@@ -10,7 +10,7 @@
> 参考文献
> [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算法在图中存在相同权值的边时也有效。
## 2 算法原理

View File

@@ -11,7 +11,8 @@
## 1 问题分析
普里姆算法Prim算法图论中的一种算法可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中不但包括了连通图里的所有顶点英语Vertex (graph theory)),且其所有边的权值之和亦为最小。
* 普里姆算法Prim算法图论中的一种算法可在加权连通图里搜索**最小生成树**。意即由此算法搜索到的边子集所构成的树中不但包括了连通图里的所有顶点英语Vertex (graph theory)),且其所有边的权值之和亦为最小。
* 主要是**顶点的贪心思想**。
## 2 算法原理
@@ -116,112 +117,5 @@
## 5 算法实现
```C++
#include <stdio.h>
#include <stdlib.h>
#define VertexType int
#define VRType int
#define MAX_VERtEX_NUM 20
#define InfoType char
#define INFINITY 65535
typedef struct {
VRType adj; //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。
InfoType * info; //弧额外含有的信息指针
}ArcCell,AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];
typedef struct {
VertexType vexs[MAX_VERtEX_NUM]; //存储图中顶点数据
AdjMatrix arcs; //二维数组,记录顶点之间的关系
int vexnum,arcnum; //记录图的顶点数和弧(边)数
}MGraph;
//根据顶点本身数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph G,VertexType v){
int i=0;
//遍历一维数组找到变量v
for (; i<G.vexnum; i++) {
if (G.vexs[i]==v) {
return i;
}
}
return -1;
}
//构造无向网
void CreateUDN(MGraph* G){
scanf("%d,%d",&(G->vexnum),&(G->arcnum));
for (int i=0; i<G->vexnum; i++) {
scanf("%d",&(G->vexs[i]));
}
for (int i=0; i<G->vexnum; i++) {
for (int j=0; j<G->vexnum; j++) {
G->arcs[i][j].adj=INFINITY;
G->arcs[i][j].info=NULL;
}
}
for (int i=0; i<G->arcnum; i++) {
int v1,v2,w;
scanf("%d,%d,%d",&v1,&v2,&w);
int m=LocateVex(*G, v1);
int n=LocateVex(*G, v2);
if (m==-1 ||n==-1) {
printf("no this vertex\n");
return;
}
G->arcs[n][m].adj=w;
G->arcs[m][n].adj=w;
}
}
//辅助数组,用于每次筛选出权值最小的边的邻接点
typedef struct {
VertexType adjvex;//记录权值最小的边的起始点
VRType lowcost;//记录该边的权值
}closedge[MAX_VERtEX_NUM];
closedge theclose;//创建一个全局数组,因为每个函数中都会使用到
//在辅助数组中找出权值最小的边的数组下标,就可以间接找到此边的终点顶点。
int minimun(MGraph G,closedge close){
int min=INFINITY;
int min_i=-1;
for (int i=0; i<G.vexnum; i++) {
//权值为0说明顶点已经归入最小生成树中然后每次和min变量进行比较最后找出最小的。
if (close[i].lowcost>0 && close[i].lowcost < min) {
min=close[i].lowcost;
min_i=i;
}
}
//返回最小权值所在的数组下标
return min_i;
}
//普里姆算法函数G为无向网u为在网中选择的任意顶点作为起始点
void miniSpanTreePrim(MGraph G,VertexType u){
//找到该起始点在顶点数组中的位置下标
int k=LocateVex(G, u);
//首先将与该起始点相关的所有边的信息边的起始点和权值存入辅助数组中相应的位置例如12adjvex为0lowcost为6存入theclose[1]中辅助数组的下标表示该边的顶点2
for (int i=0; i<G.vexnum; i++) {
if (i !=k) {
theclose[i].adjvex=k;
theclose[i].lowcost=G.arcs[k][i].adj;
}
}
//由于起始点已经归为最小生成树所以辅助数组对应位置的权值为0这样遍历时就不会被选中
theclose[k].lowcost=0;
//选择下一个点,并更新辅助数组中的信息
for (int i=1; i<G.vexnum; i++) {
//找出权值最小的边所在数组下标
k=minimun(G, theclose);
//输出选择的路径
printf("v%d v%d\n",G.vexs[theclose[k].adjvex],G.vexs[k]);
//归入最小生成树的顶点的辅助数组中的权值设为0
theclose[k].lowcost=0;
//信息辅助数组中存储的信息,由于此时树中新加入了一个顶点,需要判断,由此顶点出发,到达其它各顶点的权值是否比之前记录的权值还要小,如果还小,则更新
for (int j=0; j<G.vexnum; j++) {
if (G.arcs[k][j].adj<theclose[j].lowcost) {
theclose[j].adjvex=k;
theclose[j].lowcost=G.arcs[k][j].adj;
}
}
}
printf("\n");
}
int main(){
MGraph G;
CreateUDN(&G);
miniSpanTreePrim(G, 1);
}
```

View File

@@ -0,0 +1,224 @@
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
// 构造边的对象
struct Edge
{
int start;
int end;
int weight;
Edge(int s,int e,int w){
start=s;
end=e;
weight=2;
}
// 重写<运算符
bool operator<(const Edge& a)const{
return a.weight < weight;
}
};
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();//最小生成树
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;
}
}
void Graph::Dijkstra(int begin){
// 初始化结果集,到自己是0
vector<int> result(vertex_num,INT_MAX);
result[begin]=0;
// 初始化距离集合,每次从中挑选
vector<int> distance(vertex_num,INT_MAX);
for(int i=0;i<vertex_num;i++){
distance[i]=arc[begin][i];
}
for(int k=0;k<vertex_num-1;k++){
// 从中挑选非零的最小值使用0表示已经挑选到结果集合中。
int min_distance=INT_MAX;
int min_index=0;
for(int i=0;i<vertex_num;i++){
if(distance[i]!=0 && distance[i]<min_distance){
min_distance=distance[i];
min_index=i;
}
}
// 添加到结果集合中并更新删除distance集合。
cout<<min_index<<endl;
result[min_index]=min_distance;
distance[min_index]=0;
// 使用min_index更新距离集合
for(int i=0;i<vertex_num;i++){
if(distance[i]!=0 && arc[min_index][i]<INT_MAX){
distance[i]=min(distance[i],min_distance+arc[min_index][i]);
}
}
}
for(int i=0;i<vertex_num;i++){
cout<<"end:"<<i<<"\tweight:"<<result[i]<<endl;
}
}
void Graph::Floyd(){
// 初始化结果集合,一个用来记录最短距离,一个用来记录最短路径。
vector<vector<int>> distance(arc);
vector<vector<int>> path(vertex_num,vector<int>(vertex_num,0));
int i,j,k;
int temp;
// 三层for循环。k表示中间点。i表示起点j表示中点。如果经过起始点小于直接点则更新。
// 可以进行优化例如i||j==k的时候可以不用运算直接continue。
for(k=0;k<vertex_num;k++){
for(i=0;i<vertex_num;i++){
for(j=0;j<vertex_num;j++){
temp=INT_MAX;
if(distance[i][k]<INT_MAX && distance[k][j]<INT_MAX){
temp=distance[i][k]+distance[k][j];
}
if(temp<distance[i][j]){
distance[i][j]=temp;
path[i][j]=k;
}
}
}
}
for(i=0;i<vertex_num;i++){
for(j=0;j<vertex_num;j++){
cout<<distance[i][j]<<"\t";
}
cout<<endl;
}
}
// 使用优先队列,挑选结果集合
void Graph::Prim(){
// 最小生成树开始的顶点。
int start = 2;
// 初始化结果集合,边的集合。
vector<Edge> result;
// 初始化优先队列,用来挑选满足要求的最小的边
priority_queue<Edge,vector<Edge>> pri_edge;
for(int k=0;k<vertex_num;k++){
// 添加边
for(int i=0;i<vertex_num;i++){
// 检查i是不是已经被选中
for(auto e:result){
if(i==e.start || i==e.end){
continue;
}
}
if(i!=start && arc[start][i]<INT_MAX){
pri_edge.push(Edge(start,i,arc[start][i]));
}
}
// 挑选边.并将end点作为下一个边的扩展。继续添加边
Edge e = pri_edge.top();
pri_edge.pop();
result.push_back(e);
start = e.end;
}
// 显示结果
for(auto e:result){
cout<<e.start<<"\t"<<e.end<<"\t"<<e.weight<<endl;
}
}
int main(){
Graph g;
// g.print();
// g.Dijkstra(3);
// g.Floyd();
g.Prim();
return 0;
}