数据结构与算法(第四章-2)

目录

二、图的应用

1.拓扑排序

(1)基于DFS的拓扑排序(无法识别回路)

原理:

(2)基于BFS的拓扑排序(可识别回路)

原理:

2.关键路径

3.最短路径

(1)Dijkstra算法

(2)Floyd算法

4.最小生成树

(1)Prim算法

 (2)Kruskal算法


二、图的应用

1.拓扑排序

(1)基于DFS的拓扑排序(无法识别回路)

void TopSort_DFS(Graph&G) 
{
    for (int i=0;i<G.Vernum();i++)//将图中所有顶点的标志位初始化为false 
    G.visited[i]=false;
    for (int i=0;i<G.Vernum();i++)
    if(G.visited[i]==false) Do_TopSort (G.i);//深度优先搜索
}
void Do_TopSort(Graph&G,int V)
{
	G.visited[V]=true;
	for(int w=G.Firstadj(V);w!=-l;w=G.Nextadj(V,w) )
    if(G.visited[w]==false) Do_TopSort(G,w);
    cout<<V<<"\t";
}

原理:

此算法得到逆拓扑排序,即从后往前看时后面节点只受之前节点的影响。

对于一个节点来说,其它节点与其关系有两种:该节点指向别的,或别的节点指向该节点。

DFS过程中,第一种关系的节点会被遍历到,故当这次DFS结束,开始下一次DFS时,下一次的节点与此次节点只有可能是第二种关系,所以之后的DFS序列往后放符合逆拓扑序列。

因为是逆拓扑排序,所以在递归返回时再输出。逆拓扑可用栈调正。

(2)基于BFS的拓扑排序(可识别回路)

void TopSort_Queue(Graph&G)
{
    int i;
    int*indegree=new int[G.Vernum()];//保存顶点的入度
	for(i=0;i<G.Vernum();i++)
        indegre[i]=G.InDegree[i];
    for(i=0;i<G.Vernum();i++)//初始化Visited数组
        G.Visited[i]=false;
	queue <int> Q;
	for(i=0;i<G.Vernum();i++)//图中入度为0的顶点入队
        if(indegree[i]==0) Q.push(i);
	while(!Q.Isempty())//如果队列中还有顶点
	{
	    int V=Q.pop();
		cout<<V <<"\t";
		G.visited[V]=true;
		for(int w=G.Firstadj(V);w!=-l;w=G.Nextadj(V,w)) 
		{
		    indegree[w]--;
            if(indegree[w] ==0) Q.push(w);
	    }
    }
	cout<<endl;
    for(i=0;i<G.Vernum();i++) 
	{
        if(G.visited[i]==false) 
	    {
            cout<<"此图存在回路"<<endl;
            break;
    	}
    }
}

原理:

每次输出入度为零的节点,由于入度为0,剩余的节点不可能优先级更高(或说指向它),所以它只受之前输出节点的影响,它后面的节点可能受到它的影响。每次输出节点后要更新剩余被它指向节点的入度。

2.关键路径

bool CriticalPath(Graph&G)
{
    int*ve=new int[G. Vernum()];//事件最早发生时间数组
	queue <int> Q;//存储入度为0的顶点
    stack <int> S;//用于实现逆拓扑序列的栈
    int cnt=0;//cnt用于对顶点计数 
	int v,w;
    int*indegree=new int[G.VerNum()];
    for(w=0;w<G.Vernum();w++)//保存顶点的入度
    indegree [w] =G. InDegree[w];
	for(w=0;w <G.Vernum();w++)//初始化事件最早发生时间
	ve[w]=0;
	for(w=0;w<G.Vernum();w++)//图中入度为0的顶点人队
	if(indegree[w]==0) Q.push(w);//取出一个入度为0的顶点
	while(!Q.Isempty()) 
	{
	    v=Q.pop();
		S.push(v);//顶点v人栈,以便得到逆拓扑序列
		cnt++;
		for(w=G.Firstadj(v);w!=-1;w=G.Nextadj(v,w))
		{
			inegree[w]--;
			if(indegree[w]==0) Q.push(w);
			if (ve[v]+G.weight(v,w)>ve[w])
			ve[w]=ve[v]+G.weight(v,w);
		}
	} 
    if(cnt<G.Vernum())
    {
	    delete []ve;
		cout<<"此图存在回路"<<endl;
		return false;
	}
	int*vl=new int[G.Vernum()];//事件最迟发生时间数组
	v=S.pop()//取出栈顶,栈顶为汇点
    for(w=0;w<G.Vernum();w++)//初始化事件最迟发生时间
        vl[w]=ve[v];
    while(!S.Isempty())
	{
		v=S.pop();
		for (w=G.FirstAdj(v);w!=-1;w=G.Nextadj(v,w))
		if(vl[w]-G.weight(v,w)<vl[v])
		    vl[v]=vl[w]-G.weight(v.w);
	}    
	int e,l;//e和1分别为活动最早开始时间和最迟开始时间
    for(v=0;v<G.Vernum();v++) 
	{//求e、1以及关键路径
	    for (w=G.Firstadj(v);w!=-1;w=G.Nextadj(v,w))
		{
		   e=ve[v];
		   l=vl[w]-G.weight(v,w);
		   if(e==l)
		       cout<<"<"<<v<<<","<<w<<">";
		}   
	}
	delete[]ve;
	delete[]vl;
	return true;
}

3.最短路径

(1)Dijkstra算法

void Dijkstra(Graph&G,int*dist,int*pre,int s) 
{
    int i,j,n=G. Vernum();
	bool*S=new bool [n]//最短路径终点集S
	for(i=0;i<n;i++) 
	{//初始化dist、pre、s
        S[i] =false;
		if(i! =s&&G.weight (s,i)==0)
            dist[i]=maxValue;
        else
            dist[i]=G.weight(s,i);
		if(i!=s&&dist[i]<maxValue)
		    pre[i]=s;
        else
		    pre[i]=-1;
    }
    S[s]=true;
	int Min,v;
	for(i=0;i<n-1;i++)
    {
        Min=maxValue;
        v=s;
       for(j=0:j<n;j++) 
       {
            if(S[j]==false&&dist[j]<Min)
		    {
                v=j;
			    Min=dist[j];
	        }
	    }
        S[v]=true;//将顶点v加入到集合S中
	    for(j=0;j<n;j++) //修改
	    { 
            if (S[j]==false&&G.weight(v,j)!=0&&dist[v]+G.weight(v.j)<dist(j])
                dist[j]-dist[v]+G.weight(v.j);
		    pre[j]=v;
        }
    }
}

(2)Floyd算法

void MidNode(int i,intj,int**path)
{//输出中间过渡点
    if(path[i][j]!=-1&&i!=path[i][j]) 
	{
        MidNode(i,path[i][j],path);
		cout<<path[i][j]<<"->";
		MidNode(path[i][j],j,path);
    }
void Output_AllPaths(intn,int**path)
{//输出任意对顶点之间的最短路径
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(i!=j&&path[i][j]!=-1) 
			{
                cout<<i<<"->";
                MidNode(i,j,path);
                cout<<j<<endl;
            }
            else
            cout<<i<<"->"<<j<<"之间无路可达"<<endl; 
}
//用Floyd算法求任意对顶点之间的最短距离 
void Floyd(Graph&G,int **dist,int **path)
{
	int i,j,k,n=G.Vernum();
	for(k=0;k<n;k++) //k为中间过渡点
	{
        for(i=0;i<n;i++)
            for(j=0;j<n;j++) 
			{
                if(dist[i][k]+dist[k][j]<dist[i][j]])
                {
					dist[i][j]=dist[i][k]+dist[k][j];
					path[i][j]=k;
				}
			}
	}
    Output_AIlPaths(n,path);//输出任意对顶点之间的最短路径
} 

4.最小生成树

(1)Prim算法

适用于边稠密的图

//利用Prim算法求带权连通图的最小生成树,用MST来存储最小生成树的边
void Prim (Graph&G,int s,Edge*MST)
{
    int i,j,n=G.Vertnum();
	Edge *MST_Edge=new Edge[n];//辅助数组MST_Edge
	for (i=0;i<n;i++) //初始化MST_Edge数组
	{
        MST_Edge [i] .from=i;
		MST_Edge [i] .to=s;
		if (i!=s&&G.weight(i,s) ==0)
            MST_Edge[i].weight=maxValue;
        else
            MST_Edge[i].weight=G.weight(i,s);
    }
	int v, Min;
	for(i=0;i<n-1;i++) 
	{//找到U中顶点到V-U中顶点权值最小的边,并记录顶点
        Min=maxValue;
		v=0;
		for(j=0;j<m;j++)
		    if(MST_Edge[j].weight!=0&&MST_Edge[j].weight<Min) 
			{
                Min=MST_Edge[j].weight;
				v=j;
		    }
    //将顶点v加入集合U中
	MST[i].from=v;
	MST[i].to=MST_Edge[v].to;
	MST[i].weight=Min;
	MST[i]_Edge[v].weight=0;
    //修改辅助数组中与v关联的边的权值
	for(j=0;j<n;j++)
        if(G.weight(v.j)!=0&&G.weight(v.j)<MSTST_Edge[j] .weight) 
		{
            MST_Edge[j].to=v;
			MST_Edge[j].weight=G.weight(v.j);
        }
    }
}

 (2)Kruskal算法

#include<stdilb.h>
int cmp (constt void *a,const void*b) 
{
    Edge*c= (Edge*) a;
	Edge*d= (Edge*) b;
	if (c-> weight! =d-> weight) 
	return c-> weight - d-> weight;
	else return d-> from-c-> from;
//用kruskal算法求带权连通图的最小生成树,用MST来存储最小生成树的边
void Kruskal (Graph&G, Edge*MST) 
{
    n=G. VerticesNumO;
    int*Vset=new int [n] ;
    Edge*E=new Edge [G. Edgnum()];//记录图的所有边
	int i,j;
	int EdgeCnt=0;
	for (i=0;i<n;i++) 
	{ //将图中所有的边存储在数组E中
        for(j=G.Firstadj(i);j!=-1;j=G.Nextadj(i,j))
            if (i <j) 
			{
                [EdgeCnt] .from=i;
				[EdgeCnt] .to=j;
				[EdgeCnt++] .weight=G.weight (i.j);
			}
        Vset[i]=i;
    }
    qsort(E,EdgeCnt,sizeof(E[0]),cmp);//使用快速排序按照权值从小到大排序
    int cnt=0;//生成的边数
    i=0;//E的下标
    int x,y;
	while(cnt <n-1) 
	{
	    x=Vset[E[i].from];
		y=Vset[E [i].to];
		if(x!=y)//不在同一个连通分量中
		{
		    MST[cnt++]=E[i];
			for(j=0;j<n;j++)
			    if(Vset[j]==y)  Vset[j]=x;//合并
		}
		i++;
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值