图的存储和操作

一、图的概念及定义

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;
		}
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值