一、图的概念及定义
1.图G由两个集合V和E组成,记为G=(V,E),其中V是顶点的有限集合,E是连接V中两个不同的顶点的边的有限集合。如果E中的顶点对是有序的,即E中的每条边都是有方向的,则称G为有向图。如果顶点对是无序对,则称G是无向图。一般情况下,图G的边集合记为E(G),顶点集合记为V(G)。
2.设G是一个无向图,v是V(G)集合中的元素,即v是G中的一个点,E(G)中以v为端点的边的个数,称为顶点v的度。若G是有向图,则v的出度是 以v为始点的边的个数,v的入度是以v为终点的边的个数。
3.设G是图,若存在一条从vi到vj的路径,则称vi与vj可及,若G是无向图,且V(G)中任意两点都可及,则称G是连通图。若G为有向图,且对于V(G)中任意两点都可及,则称G为强连通图。
二、图的存储结构及其操作代码
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#define maxsize 1000
using namespace std;
typedef struct Edge{//边结点的结构体
int VerAdj;//邻接顶点的序号,用自然数编号
int cost;//边的权值
struct Edge* link;//指向下一个边结点的指针
}Edge;
typedef struct Vertex{//顶点表中的结点的结构体
int VerName;//顶点的名称
Edge* adjacent;//边链表头指针
}Vertex;
struct Graph{
Vertex Head[maxsize];
int numEdges,Graphsize;
}G;
int locatevex(Graph &G,int v){
int i;
for(i=0;i<G.Graphsize;i++)
if(G.Head[i].VerName==v) return i;
return -1;
}
void create_graph(struct Graph& G){ //创建有向图
int i,j,k;
int v1,v2;
Edge *p;
scanf("%d %d",&G.Graphsize,&G.numEdges);
for(i=1;i<=G.Graphsize;i++){
G.Head[i].VerName=i;
G.Head[i].adjacent=NULL;
}
//printf("请输入图的边的信息:\n");
for(k=1;k<=G.numEdges;k++){
//printf("please input the %d th edge:\n",k);
scanf("%d %d",&v1,&v2);
p=new Edge;
p->VerAdj=v2;
p->link=NULL;
Edge *q=G.Head[v1].adjacent;
if(q==NULL) G.Head[v1].adjacent=p;
else{
while(q->link!=NULL){
q=q->link;
}
q->link=p;
}
}
}
void create_graph2(struct Graph& G){ //创建无向图
int n,m,s;
int i,j,k;
int v1,v2,Cost;
Edge *p,*r;
scanf("%d %d",&n,&m);
G.Graphsize=n;
G.numEdges=m;
for(i=0;i<G.Graphsize;i++){
G.Head[i].VerName=i;
G.Head[i].adjacent=NULL;
}
for(k=1;k<=G.numEdges;k++){
scanf("%d %d %d",&v1,&v2,&Cost);
p=new Edge;
p->VerAdj=v2;
p->cost=Cost;
p->link=NULL;
Edge *q=G.Head[v1].adjacent;
if(q==NULL) G.Head[v1].adjacent=p;
else{
while(q->link!=NULL){
q=q->link;
}
q->link=p;
}
r=new Edge;
r->VerAdj=v1;
r->cost=Cost;
r->link=NULL;
Edge* r0=G.Head[v2].adjacent;
if(r0==NULL) G.Head[v2].adjacent=r;
else{
while(r0->link!=NULL)
r0=r0->link;
r0->link=r;
}
}
}
void print(Graph G){
int i;
Edge* p;
printf("图的邻接表表示:\n");
for(i=1;i<=G.Graphsize;i++){
printf("\n%d",G.Head[i].VerName);
// for(p=G.Head[i].adjacent;p;p=p->link)
// printf("-->%d",p->VerAdj);
p=G.Head[i].adjacent;
if(!p) printf("-->NULL");
while(p!=NULL){
printf("-->%d",p->VerAdj);
p=p->link;
}
}
printf("\n");
}
int visited[1000];//标志顶点是否被访问的辅助数组
int dist[maxsize],path[maxsize]; //diatance是从某点到其他点的最短路径长度,path是记录从某点到该点的前驱结点
void DepthFirstSearch(int v){// 适用于连通图的深度优先搜索
//深度优先递归遍历算法
cout<<G.Head[v].VerName<<" "; //输出数据
visited[v]=1;//访问过后标记为1
Edge* p;
for(p=G.Head[v].adjacent;p;p=p->link){//将该节点所有的邻接结点全部访问,再访问邻接节点的所有邻接节点
if(visited[p->VerAdj]!=1)
DepthFirstSearch(p->VerAdj);
}
}
void DFS(struct Graph G,int v){
//DFS算法2
cout<<G.Head[v].VerName<<" ";
visited[v]=1;
Edge* p;
p=G.Head[v].adjacent;
while(p){
if(visited[p->VerAdj]!=1)
DFS(G,p->VerAdj);
p=p->link;
}
}
void DFS_Main(struct Graph G){//适用于所有情况的图,非联通的图也适用
//DFS主函数
int i;
for(i=1;i<=G.Graphsize;i++){
visited[i]=0;
}
for(i=1;i<=G.Graphsize;i++){
if(visited[i]==0)
DFS(G,i);
}
}
void DFS_2(struct Graph G,int v){//DFS 的非递归算法
stack<int> S; //建栈
int i;
Edge* p;
for(i=1;i<=G.Graphsize;i++){//初始化
visited[i]=0;
}
S.push(v); //将第一个开始访问的结点入栈
while(!S.empty()){
v=S.top();
S.pop(); //只要栈不空就弹栈
if(visited[v]==0){ //visited[v]=0说明该结点未被访问
cout<<G.Head[v].VerName;
visited[v]=1; //访问过后标记为1
p=G.Head[v].adjacent;
while(p){ //将这个结点的所有未被访问的相邻结点入栈
if(visited[p->VerAdj]==0){
S.push(p->VerAdj);
}
p=p->link;
}
}
}
}
void BFS(struct Graph G,int v){//图的广度优先搜索
queue<int> Q; //使用队列实现
int i;
Edge* p;
for(i=1;i<=G.Graphsize;i++){//初始化,均未被访问
visited[i]=0;
}
cout<<G.Head[v].VerName<<" "; //从第v个节点开始访问
visited[v]=1;//访问过后标记为1
Q.push(v);//访问过后的结点入队
while(!Q.empty()){ //只要队列不空,出队
v=Q.front();
Q.pop();
p=G.Head[v].adjacent;
// while(p){ //将第v个节点的所有未被访问的邻接节点访问并将其入队
// if(visited[p->VerAdj]==0){
// cout<<G.Head[p->VerAdj].VerName;
// visited[p->VerAdj]=1;
// Q.push(p->VerAdj);
// }
// p=p->link;
// }
for(p=G.Head[v].adjacent;p;p=p->link)//同上
if(visited[p->VerAdj]==0){
Q.push(p->VerAdj);
visited[p->VerAdj]=1;
}
}
}
void ShortestPath(struct Graph G,int v){ //适用于无权图
//计算从结点v到其他各顶点的最短路径,v表示起始点
queue<int> Q; //用队列实现层次遍历
int i,k,u;
Edge* p;
for(i=1;i<=G.Graphsize;i++){ //初始化均为-1
dist[i]=-1;path[i]=-1;
}
dist[v]=0;//起始点的路径长为0
Q.push(v); //起始点入队
while(!Q.empty()){ //只要队列不空,取队头元素遍历其邻接点
u=Q.front();
Q.pop();
for(p=G.Head[u].adjacent;p;p=p->link){ //遍历邻接点,若邻接点未被访问则继续入队
k=p->VerAdj; //k保留u邻接点的序号
if(dist[k]==-1){ //-1表示未被访问
Q.push(k);
dist[k]=dist[u]+1; //无权图路径的权默认为1
path[k]=u; //u是k的前驱结点
}
}
}
// for(i=1;i<=G.Graphsize;i++) cout<<path[i]<<" ";
// for(i=1;i<=G.Graphsize;i++) cout<<dist[i]<<" ";
}
void Dijkstra_ShortestPath(struct Graph G,int v){ //迪杰斯特拉算法求有权图的最短路径
//迪杰斯特拉算法只支持正权图,若有负环则该算法失效
int i,max=99999,mindist,j,u,k; //i,j是计数器,max是最长路径,mindiatance是最短路径
Edge* p;
for(i=1;i<=G.Graphsize;i++){//初始化:disti均为max,表示一开始的路径长为无穷
dist[i]=max;
path[i]=-1; //path标记为-1表示没有前驱
visited[i]=0;//用来记录i是否已经计算完
}
dist[v]=0;//起始点的距离是0
visited[v]=1; //起始点已经访问过,表示操作完毕
for(j=1;j<G.Graphsize;j++){ //循环1:求起始点到其他点的最短路径
mindist=max; //设在j下的最短路为mindist
for(i=1;i<=G.Graphsize;i++)
if(dist[i]<mindist&&visited[i]==0){ //寻找此时v到各点的最短路径,即要访问的结点u
mindist=dist[i];
u=i;//u是下一个访问的结点
}
visited[u]=1;
for(p=G.Head[u].adjacent;p;p=p->link){ //对u的邻接点进行更新
k=p->VerAdj;
if(dist[u]+p->cost<dist[k]){ //更新v到u邻接点的路径长
dist[k]=dist[u]+p->cost; //如果新路径长小于原路径长,则新路径替代原路径
path[k]=u;//记录k的前驱结点为u
}
}
}
}
void Bellmanford(struct Graph G,int s){//Bellmamford 算法可以解决图中有负环的情况。
int i,max=999999,u,v;
bool relaxed;
Edge* p;
for(i=1;i<=G.Graphsize;i++){
dist[i]=max;
}
dist[s]=0;
for(i=1;i<=G.Graphsize-1;i++){
relaxed=false; //松弛标记为false
for(u=1;u<=G.Graphsize;u++){
for(p=G.Head[u].adjacent;p;p=p->link){
v=p->VerAdj;
if(dist[u]+p->cost<dist[v]){//做松弛操作,更新路径长度
dist[v]=dist[u]+p->cost;
relaxed=true; //有更新操作,松弛标记赋值true
}
}
}
if(!relaxed) break; //如果循环中没有做松弛操作,则路径已经优化完成
}
if(i==G.Graphsize) printf("有负环");
}
void SPFA(struct Graph G,int v){ //SPFA算法
int mark[maxsize];
int i,u,k,max=999999;
Edge* p;
queue<int> Q;
for(i=1;i<=G.Graphsize;i++)
{
dist[i]=max;
}
dist[v]=0;
Q.push(v);
mark[v]=1;
while(!Q.empty())
{
u=Q.front(); Q.pop(); mark[u]=0;
for(p=G.Head[u].adjacent;p;p=p->link)
{
k=p->VerAdj;
if(dist[u]+p->cost<dist[k])
{//松弛
dist[k]=dist[u]+p->cost;
if(!mark[k])
{
Q.push(k);
mark[k]=1;
}
}
}
}
}
int A[50][50],Path[50][50],edge[50][50];
void AllLength(){ //floyd算法,求每对顶点之间的最短路径
int n=G.Graphsize,max=9999;
int i,j,k;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
A[i][j]=edge[i][j];
if(i!=j&&A[i][j]<max) Path[i][j]=i;
else Path[i][j]=-1;
}
}
// for(k=0;k<n;k++)
// for(i=0;i<n;i++)
// if(i!=k)
// for(j=0;j<n;j++)
// if(j!=k&&j!=i&&A[i][k]<max&&A[k][j]<max&&A[i][k]+A[k][j]<A[i][j]){
// A[i][j]=A[i][k]+A[k][j];
// Path[i][j]=Path[k][j];
// }
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(A[i][k]+A[k][j]<A[i][j])
A[i][j]=A[i][k]+A[k][j];
}
struct closedge{//存最小邻边的值
int Lowcost;//存某顶点的最小加权邻值
int Vex;//上一个顶点
}closedge[maxsize];
struct TE{ //最小支撑树边的集合
int head;//边的始点
int tail;//边的终点
int cost;//边的开销
}TE[maxsize];
void Prim_method(int *edge[],int n){ //普利姆算法 (邻接矩阵存储法)
int i,max=9999,min;
int count,j,v;
for(i=1;i<=n;i++)
{
closedge[i].Lowcost=edge[1][i];
closedge[i].Vex=1; //表示还未进入最小支撑树点的集合,标记为1
}
count=1;
for(i=2;i<=n;i++)
{
min=max;
v=0;
for(j=1;j<=n;j++)
{
if(closedge[j].Vex!=-1&&closedge[j].Lowcost<min)
{
v=j;
min=closedge[j].Lowcost;
}
}
if(v!=0)
{
TE[count].head=closedge[v].Vex;
TE[count].tail=v;
TE[count].cost=closedge[v].Lowcost;
count++;
closedge[v].Lowcost=0;
closedge[v].Vex=-1;
for(j=1;j<=n;j++)
{
if(closedge[j].Vex!=-1&&edge[v][j]<closedge[j].Lowcost)
{
closedge[j].Lowcost=edge[v][j];
closedge[j].Vex=v;
}
}
}
}
}
int lowcost[maxsize],vex[maxsize];
void Prim_method2(struct Graph G){//普利姆算法(邻接表存储)
int i,j,k,n=G.Graphsize;
int max=9999999,ldist,u,v;
Edge *p;
for(i=1;i<=n;i++)
{//初始化
lowcost[i]=max;
visited[i]=0;//visited记录点i是否在最小支撑树中
vex[i]=-1; //vex[i]是i的前驱结点
}
visited[1]=1;lowcost[1]=0;
for(p=G.Head[1].adjacent;p;p=p->link)
{//先将第一个点的邻接点访问
k=p->VerAdj;
lowcost[k]=p->cost;
vex[k]=1;
}
for(j=1;j<n;j++)
{
ldist=max; //确定轻边
for(i=1;i<=n;i++)
{
if(lowcost[i]<ldist&&visited[i]==0)
{
ldist=lowcost[i];
u=i;
}
}
visited[u]=1;
for(p=G.Head[u].adjacent;p;p=p->link)
{
v=p->VerAdj;
if(p->cost<lowcost[v]&&visited[v]==0)
{
lowcost[v]=p->cost;
vex[v]=u;
}
}
}
}
bool cmp(struct TE a,struct TE b){
return a.cost<b.cost;
}
struct TE T[maxsize],E[maxsize];
struct TE Kruskal_method(int n,int m){
int i,j,v,u;
sort(T,T+m,cmp);
for(i=1;i<=n;i++) make_set(i);
for(i=1,j=0;i<=m;i++){
int u=T[i].head,v=T[i].tail;
if(find(u)!=find(v)){
E[++j]=T[i];Union(u,v);
}
if(j==n-1) break;
}
int sum=0;
for(i=1;i<=j;i++) sum+=E[i].cost;
cout<<sum<<endl;
}
//Kruskal算法
//边集定义部分
struct edge {
int u,v; //边的两个端点编号
int cost; //边权
}E[maxsize]; //最多有MAXV条边
bool cmp(edge a,edge b){
return a.cost < b.cost;
}
//并查集部分
int father[maxsize]; //并查集数组
int findFather(int x){ //并查集查询函数
int a=x;
while(x != father[x]){
x = father[x];
}
//路径压缩
while(a != father[a]){
int z = a;
a = father[a];
father[x] = x;
}
return x;
}
//kruskal函数返回最小生成树的边权之和,参数n为顶点个数,m为图的边数
int kruskal(int n,int m){
//ans为所求边权之和,Num_Edge为当前生成树的边数
int ans = 0, Num_Edge = 0;
for(int i=1; i<= n;i++){ //假设题目中顶点范围为[1,n]
father[i] = i; //并查集初始化
}
sort(E,E+m,cmp); //所有边按边权从小到大排序
for(int i=0;i<m;i++){ //枚举所有边
int faU = findFather(E[i].u); //查询测试边所在的集合根结点
int faV = findFather(E[i].v);
if(faU != faV){ //如果不在一个集合
father[faU] = faV; //合并集合(即把测试边加入最小生成树中)
ans += E[i].cost; //边权之和增加测试边的边权
Num_Edge++; //生成树的边数+1
if(Num_Edge == n-1) //边数等于顶点数减一时结束算法
break;
}
}
if(Num_Edge != n-1)
return -1; //无法连通,返回-1
else
return ans; //返回最小生成树的边权之和
}
int count_[maxsize];//记录所顶点的入度
void TopoOrder(struct Graph G){// 拓扑排序算法:AOV网
int n=G.Graphsize,i,j,k;
Edge* p;
for(i=1;i<=n;i++){
count_[i]=0;
}
for(i=1;i<=n;i++){
for(p=G.Head[i].adjacent;p;p=p->link)
count_[p->VerAdj]++;
}
stack<int> S;
for(i=1;i<=n;i++){
if(count_[i]==0) S.push(i);
}
for(i=1;i<=n;i++){
if(S.empty()) cout<<"有回路!\n";
j=S.top();S.pop();
cout<<j<<" ";
for(p=G.Head[j].adjacent;p;p=p->link){
k=p->VerAdj;
count_[k]--;
if(count_[k]==0) S.push(k);
}
}
}
void CriticalPath(struct Graph G){//关键路径:AOE网
int i,j,k,n=G.Graphsize;
int ve[maxsize],vl[maxsize];//ve[]是最早发生时间,vl[]是最迟发生时间
Edge* p;
//计算事件的最早发生时间
for(i=1;i<=n;i++) ve[i]=0;
for(i=1;i<=n-1;i++)
{
for(p=G.Head[i].adjacent;p;p=p->link)
{
k=p->VerAdj;
if(ve[i]+p->cost>ve[k])
ve[k]=ve[i]+p->cost;
}
}
//计算事件最迟发生时间
for(i=1;i<=n;i++) vl[i]=ve[n];
for(i=n-1;i>=1;i--)
{
for(p=G.Head[i].adjacent;p;p=p->link)
{
k=p->VerAdj;
if(vl[k]-p->cost<vl[i])
vl[i]=vl[k]-p->cost;
}
}
for(i=1;i<=n;i++)
{
for(p=G.Head[i].adjacent;p;p=p->link)
{
k=p->VerAdj;
int e=ve[i];
int l=vl[k]-p->cost;
if(l==e) cout<<"<i,k> is Critical Activity!"<<endl;
}
}
}
int WSM[50][50];
void Warshall_method(){//计算可及矩阵的算法
int i,j,k,n=G.Graphsize;
// int **WSM=new int*[n];
Edge* p=new Edge;
cout<<" 4 ";
for(j=0;j<n;j++)
{
// WSM[j]=new int[n];
p=G.Head[j].adjacent;
cout<<" 5 ";
for(k=0;k<n;k++)
{
if(j==k) WSM[j][k]=1;
else WSM[j][k]=0;
}
while(p!=NULL)
{
WSM[j][p->VerAdj]=1;
p=p->link;
}
}
cout<<"3";
for(k=0;k<n;k++)
for(i=0;i<n;i++)
if(WSM[i][k]==1)
for(j=0;j<n;j++)
{
WSM[i][j]=(WSM[i][j]||WSM[k][j]);
}
cout<<"可及矩阵是:\n";
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
cout<<WSM[i][j]<<" ";
}
cout<<endl;
}
delete p;
//return WSM;
}
void Tranclo()
{
int n=G.Graphsize;
int *BREACH=new int[n];
vector< vector<int> > reach;
int j=0,i;
for(i=0;i<n;i++) BREACH[i]=0;
for(i=n-1;i>0;i--)
{
vector<int> arr;
BREACH[i]=1;
arr.push_back(i);//初始化reach[i]={i}
Edge* p;
for(p=G.Head[i].adjacent;p;p=p->link)
{
j=p->VerAdj;
if(BREACH[j]==0)
{
vector<int> r;
r=reach[n-j-1];//找到邻接点所存储的位置
for(int b=0;b<r.size();b++)
{
if(BREACH[r[b]]==0)
{
arr.push_back(r[b]);
BREACH[r[b]]=1;
}
}
}
}
for(int c=0;c<arr.size();c++)
{
BREACH[arr[c]]=0;
}
reach.push_back(arr);
}
for(i=0;i<reach.size();i++)
{
vector<int> tem=reach[i];
cout<<n-1-i<<"的可及顶点集合:\n";
for(j=0;j<tem.size();j++)
{
cout<<tem[j]<<",";
}
cout<<endl;
}
}
void All_Component(struct Graph G)
{
int n=G.Graphsize;
int *markedList=new int[n];
//int **WSM;
cout<<"1";
Warshall_method();//求图的可及矩阵
cout<<"2";
struct Node
{
int Vername;
Node* link;
};
for(int i=0;i<n;i++)
{
markedList[i]=0;
}
int t=0;
for(int i=0;i<n;i++)
{
if(markedList[i]==0)
{
Node *scList=new Node;
t++;
markedList[i]=1;
scList->Vername=i;
scList->link=NULL;
Node *q=new Node;
q=scList;
for(int j=0;j<n;j++)
{
if(i!=j&&WSM[i][j]==1&&WSM[j][i]==1)
{
markedList[j]=1;
Node* p=new Node;
p->Vername=j;
p->link=NULL;
q->link=p;
q=p;
}
}
cout<<"第"<<t<<"个连通分量:";
while(scList!=NULL)
{
cout<<scList->Vername<<" ";
q=scList;
scList=scList->link;
delete q;
}
cout<<endl;
}
}
}