mirror of
https://github.com/Estom/notes.git
synced 2026-02-03 02:23:31 +08:00
图算法
This commit is contained in:
@@ -68,4 +68,10 @@
|
||||
* 不能过度使用列表。
|
||||
* 普通的论述直接分段。或者使用>表示引用。
|
||||
* 而不是一直过度使用列表。用来缩进。
|
||||
* 换一个更好的md渲染引擎。
|
||||
* 换一个更好的md渲染引擎。
|
||||
|
||||
### 补充
|
||||
|
||||
* 在写笔记的过程中应该控制好,“知识类”笔记的粒度。不能把一些没用的什么优势劣势啥玩意的也写上。又不是应付考试。你现在要做的是记住原理跟方法,当然就会知道这种思想的优势和劣势。
|
||||
* 粗粒度的笔记开始。尽量只记录关键点和关键点的解析。
|
||||
|
||||
|
||||
45
数据结构/7 图.md
45
数据结构/7 图.md
@@ -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
40
数据结构/7.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@@ -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;
|
||||
}
|
||||
```
|
||||
@@ -1,11 +1,10 @@
|
||||
# 图算法-Dijkstra算法
|
||||
# 图算法-Floyd算法
|
||||
# 图算法-Bellman-Ford算法
|
||||
# 图算法-Prim算法
|
||||
# 图算法-Kruskal算法
|
||||
|
||||
# 图算法-Bellman-Ford算法
|
||||
> 目录
|
||||
>* 图算法-Dijkstra算法
|
||||
>* 图算法-Floyd算法
|
||||
>* 图算法-Bellman-Ford算法
|
||||
>* 图算法-Prim算法
|
||||
>* 图算法-Kruskal算法
|
||||
>* 图算法-Kruskal算法
|
||||
|
||||
|
||||
|
||||
@@ -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 算法原理
|
||||
|
||||
|
||||
@@ -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);
|
||||
//首先将与该起始点相关的所有边的信息:边的起始点和权值,存入辅助数组中相应的位置,例如(1,2)边,adjvex为0,lowcost为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);
|
||||
}
|
||||
|
||||
```
|
||||
224
算法/B类:数据结构算法/1.cpp
Normal file
224
算法/B类:数据结构算法/1.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user