图的算法的简单C++实现Dijkstra Floydvoid Prim Kruskal DFSTraverse BFSTraverse

本文介绍了一个图数据结构的C++实现,包括最短路径算法(如Dijkstra和Floyd)、最小生成树算法(如Prim和Kruskal)、深度优先搜索(DFS)及广度优先搜索(BFS)。通过具体代码示例展示了如何初始化图、执行算法并输出结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

头文件:Graph.h

#ifndef GRAPH_H

#define GRAPH_H
#pragma once
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;


typedef vector<vector<int> > Myarray2;
typedef struct  
{
int begin;
int end;
int weight;
}Edge;


class Graph
{
public:



Graph(int num1,int num2):m_VertexNum(num1),m_EdgeNum(num2){};
virtual ~Graph(void){};
bool Initialization(const char *);
void Dijkstra(int index,int *,int*,bool*);//点index到途中各点的最短路径
    void Floyd(void);//图中每个点到另外各点的最短路径
void Prim(int *,int *);//找连通图的最小生成树
void Kruskal(int *);
void print(void);
void DFS(bool*,int i);
void DFSTraverse(bool *);
void BFSTraverse(bool *);
private:
int m_EdgeNum;
int m_VertexNum;
Myarray2 m_AdjArray;
vector<Edge> edges;
};

#endif



#include "Graph.h"
#include <vector>
#include<queue>


using namespace std;


void Graph::print()
{

for (int i = 0;i<m_VertexNum;++i)
{
for(int j = 0;j<m_VertexNum;++j)
cout<<m_AdjArray[i][j]<<' ';


cout<<endl;
}
}
///从图文件中读入图的信息,初始化邻接矩阵和边集
bool Graph::Initialization(const char * filename)
{
Edge e;
ifstream infile1(filename,ios::in);
if (!infile1)
{
cerr<<"open error!"<<endl;
return false;
}


for (int i=0;!infile1.eof();++i)
{
int a;
vector<int> temp;
for (int j = 0;j<m_VertexNum;++j)
{
infile1>>a;
   temp.push_back(a);

if(a != 65535 && j>i)
{
e.begin = i;
e.end = j;
e.weight = a;
edges.push_back(e);
}


}
m_AdjArray.push_back(temp);


     }
//此时得到的边集edges是未经过排序的
    //下面对边集进行排序
bool exchage = true;
for(int i = 1;i<m_EdgeNum && exchage;++i)
{
exchage = false;
for (int j = m_EdgeNum-1;j>i ;j--)
{

           if (edges[j].weight < edges[j-1].weight)
  { 
  //cout<<edges[j].weight<<" "<<edges[j-1].weight;
  swap(edges[j],edges[j-1]);
      exchage = true;
  }
}
}


return true;
}





void Graph::Dijkstra(int index,int *dis,int * prevertex,bool* visit)
{
int k = 0;//记录每次比较中的最小点

   //初始化距离向量dis,前驱向量prevertex,访问向量visit
//dis[i]保存的是从index点到i点的最短路径长度
//prevertex[i]保存的是从index点到i点的最短路径到达i点的上一步经过的是哪个点
for (int v = 0;v<m_VertexNum;++v)
{
dis[v] = m_AdjArray[index][v];
prevertex[v] = index;
visit[v] = false;


}

//源点的visit设为true/////////
visit[index] = true;


//开始循环
for(int v = 0;v<m_VertexNum;v++)
{


  int min = INT_MAX;
           //dis里寻找离index最近的点,其点标号为k
  //即寻找目前dis里最小的值
  for(int w = 0;w<m_VertexNum;w++ )
   {
//只在未访问过的点里找(此时index已经访问了,所以满足visit[w]=false的点一定有w!=index)
    while(!visit[w]  && dis[w]<min )
   {
k = w;//最小点对应位置为k
min = dis[w];
   }


    }
           //将最近点k点的访问标志记为true
   visit[k] = true;
        
  //检查是否需要更改距离表dis,即是否有dis[w]>dis[k]+m_AdjArray[k][w]
  //此时距离表中存储的是index点直接或经过已访问点到各点的最短路径长度
  for (int w = 0;w<m_VertexNum;++w)
  {
  if (!visit[w] && dis[w] > dis[k] +m_AdjArray[k][w])
 {
dis[w] = dis[k] +m_AdjArray[k][w];
prevertex[w] = k;
  }
  }
  //}



}


//算法完毕,输出
//此时dis[i]保存的是从index点到i点的最短路径长度
    //prevertex[i]保存的是从index点到i点的最短路径到达i点的上一步经过的是哪个点
printf("距离向量为:\n");
for (int i = 0;i<m_VertexNum;++i)
{
printf("%d ",dis[i]);
}
printf("\n前驱向量为:\n");
    
for (int i = 0;i<m_VertexNum;++i)
{
printf("%d ",prevertex[i]);
}
printf("\n");


for (int v = 0;v<m_VertexNum;++v)
{
int p = v;
printf("顶点%d到顶点%d的最短路径为:\n%d<--",index,v,v);

while(prevertex[p] != index)
{
printf("%d<--",prevertex[p]);
   p = prevertex[p];
}
printf("%d\n",index);
}






}


void Graph::Floyd(void)
{
    //初始化路线矩阵
Myarray2 path;//path[v][w]表示从v到w的最短路径必须先经过path[v][w]点(前驱)
    Myarray2 adarr;//
for (int i=0;i<m_VertexNum;++i)
{
vector<int> temp;
for (int j = 0;j<m_VertexNum;++j)
{
temp.push_back(j);


}
path.push_back(temp);


}
   //开始循环
for (int k = 0;k<m_VertexNum;++k)//表示从k中转即观察是否有m_AdjArray[v][w] > m_AdjArray[v][k]+m_AdjArray[k][w]
{
for (int v = 0;v<m_VertexNum;++v)
{
for (int w =0;w<m_VertexNum;++w)
{
if(m_AdjArray[v][w] > m_AdjArray[v][k]+m_AdjArray[k][w])
{
m_AdjArray[v][w] = m_AdjArray[v][k]+m_AdjArray[k][w];
path[v][w] = path[v][k];
}
}
}
}
   //算法完毕,输出
   //此时m_AdjArray[v][w]表示从v到w的最短路径长度(已经不是最初的m_AdjArray了)
   //path[v][w]表示从v到w的最短路径的要先经过path[v][w]点(前驱)
for(int v = 0;v<m_VertexNum;++v)
{
for (int w = 0;w<m_VertexNum;++w)
{
printf("从%d到%d的最短路径为:\n%d->",v,w,v);


int temp = path[v][w];
while(temp!=w)
{
printf("%d->",temp);
temp = path[temp][w];


}
printf("%d\n该路径长度为%d\n",w,m_AdjArray[v][w]);
}
}
}


void Graph::Prim(int *lowcost,int * prevertex)
{
int result = 0;//记录最小生成树总长度
for (int i = 0;i < m_VertexNum;++i)
{
lowcost[i] = m_AdjArray[0][i];//初始化lowcost向量,lowcost[i]表示从所有已选点到第i个点的直接最短距离为lowcost[i]
prevertex[i] = 0;//初始化prevertex向量,prevertex[i]表示所有已选点中prevertex[i]点到第i个点的直接距离最短
}
printf("最小生成树如下:\n");
for (int i = 1;i < m_VertexNum;++i)//除了初始点,循环m_VertexNum-1次,保证最终所有点都被遍历到
{
        int min = INT_MAX;
        int k = 0;
int num = 1;

//每次找lowcost向量中的最小值
while (num < m_VertexNum)
{
if (lowcost[num] != 0 && lowcost[num] < min)
{
min = lowcost[num];
   k = num;
}
++num;


}
result += lowcost[k];
printf("%d->%d:length is:%d\n",prevertex[k],k,lowcost[k]);
lowcost[k] = 0;//表示从已选点到k的最小距离为0即将点k加入已选点
        //由新加入的已选点更新lowcost和prevertex向量
//即观察对于所有未选点(lowcost[j] != 0),是否有lowcost[j] > m_AdjArray[k][j]
//若有,则将相应的prevertex置为k
for (int j = 1;j < m_VertexNum;++j)
{
if (lowcost[j] != 0 &&lowcost[j] > m_AdjArray[k][j])
{
lowcost[j] = m_AdjArray[k][j];
prevertex[j] = k;
}
}


}
printf("最小生成树总长度为:%d\n",result);




}
int findParent(int *parent,int f)
{
while (parent[f]>0)
{
f = parent[f];
}
return f;
}


void Graph::Kruskal(int *parent)
{
  int m,n;
 //*****初始化数组值为0
  memset(parent,0,sizeof(int)*m_EdgeNum);


  for (int i = 0;i<m_EdgeNum;++i)
  {
 m = findParent(parent,edges[i].begin);
 n = findParent(parent,edges[i].end);
 //*****若m与n不相等,说明此边没有与现成生成树形成回路
 if (m!=n)
 {
 parent[m] = n;//将此边的结尾结点放入下标为起点的parent中
               //parent中可达的结点表示最大的不连通的边集合
 printf("(%d,%d) %d\n",edges[i].begin,edges[i].end,edges[i].weight);
 }


  }

}
void Graph::DFS(bool visit[],int i){

visit[i] = true;
printf("%d ",i);
for (int j = 0;j<m_VertexNum;++j)
{   //cout<<m_AdjArray[i][j];
if(m_AdjArray[i][j] != 65535 && !visit[j])
DFS(visit,j);
}
}


void Graph::DFSTraverse(bool visit[]){
//初始化访问矩阵
for(int i = 0;i<m_VertexNum;++i)
visit[i] = false;


for(int i = 0;i<m_VertexNum;++i)

if(!visit[i])//对未访问的顶点调用DFS,如果是连通图,只会执行一次
DFS(visit,i);
}


}


void Graph::BFSTraverse(bool visit[]){
//初始化访问矩阵
for(int i = 0;i<m_VertexNum;++i)
visit[i] = false;


queue<int> vertex;


for (int i = 0;i<m_VertexNum;++i)
{
if (!visit[i])
{
            visit[i] = true;
printf("%d ",i);
vertex.push(i);

while(!vertex.empty())
{   
i = vertex.front();
vertex.pop();
   for (int j = 0;j<m_VertexNum;++j)
   {
  if (m_AdjArray[i][j] != 65535 && !visit[j])
 {
printf("%d ",j);
vertex.push(j);
visit[j] = true;
  }
   }


   }
    }
    


}


}

本演示程序中,要求以邻接表作为的存储结构。中顶点数据类型为字符型,在提示信息下由用户输入。边的信息由用户输入弧头和弧尾元素。<br><br><br>为实现上述程序功能,以线性链表表示集合。为此,需要两个抽象数据类型:线性表和集合。<br>1. 线性表的抽象数据类型定义为:<br> ADT ALGraph{<br> 数据对象V:V是具有相同特性的数据元素的集合,称为顶点集。<br> 数据关系R1:R={VR}<br>VR={<v,w>|v,w V且P(v,w),<v,w>表示从v到w的弧,为此P(v,w)定义了弧<v,w> 的意义或信息}<br> 基本操作P: <br>void CreateAdjList(ALGraph& G)<br> 操作结果:根据相应的提示信息构造一个。<br> int LocateVex(&G,char u)<br> 初始条件:G存在,u和G中顶点有相同特征。<br> 操作结果:若G中存在顶点u,则返回该顶点在中位置;否则返回-1。<br>int FirstAdjVex(ALGraph G,int v)<br> 初始条件:G存在,v是G中某个顶点。<br> 操作结果:返回v第一个邻接顶点。若v在G中没有邻接顶点,返回-1。<br>int NextAdjVex(ALGraph G,int v,int w)<br> 初始条件:G存在,v是G中某个顶点,w是v的邻接顶点。<br> 操作结果:返回v的(相对于w的)第一个邻接顶点。若不存在,返回-1。<br>void DFS(ALGraph &G,int v)<br> 初始条件:G存在。<br> 操作结果:从顶点v出发,对进行深度优先遍历。<br>void DFSTraverse(ALGraph *G)<br> 初始条件:G存在。<br> 操作结果:对进行深度优先遍历。<br>void BFSTraverse(ALGraph *G)<br> 初始条件:G存在。<br> 操作结果:对进行广度优先遍历。<br> }ADT ALGraph<br>2. 队列的抽象数据类型定义为:<br> ADT Queue{<br> 数据对象D:D={ | QNodeSet,i=1,2,…,n,n 0 }<br> 数据关系R2:R2={< , >| , D1, i=2,…,n}<br> 约定其中 端为队列头, 为队列尾<br> 基本操作P:<br> InitQueue (*Q)<br> 操作结果:构造一个空队列Q<br> EnQueue (*Q,e)<br> 初始条件:队列Q已存在<br> 操作结果:插入元素e为Q的新的队尾元素<br> DeQueue (*Q)<br> 初始条件:Q为非空队列<br> 操作结果:删除Q的队头元素,并返回其值<br> QueueEmpty (*Q)<br> 操作结果:队为空,则返回0;否则,返回1<br> }ADT Queue<br><br><br>的基本操作设置如下:<br>void CreateAdjList(ALGraph& G) //构建<br>int LocateVex(ALGraph &G,char u) //返回u在G中的位置<br>int FirstAdjVex(ALGraph G,int v) //返回v的第一个邻接节点<br>int NextAdjVex(ALGraph G,int v,int w) //返回v的相对w的下一个邻接节点<br>void DFS(ALGraph &G,int v) //从顶点v开始深度优先遍历<br>void DFSTraverse(ALGraph *G) //深度优先遍历<br>void BFSTraverse(ALGraph *G) //广度优先遍历<br><br>队列的基本操作设置如下:(具体操作上一次上机题中已经涉及,故此处不再详述)<br>void InitQueue(LinkQueue &Q) <br>void EnQueue(LinkQueue& Q,int e)<br>int DeQueue( LinkQueue& Q)<br>int QueueEmpty(Queue *Q)<br><br>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值