数据结构和算法(十 三): 图的遍历,最小生成树,最短路径,拓扑路径

深度优先遍历

深度优化遍历(DepthFirstSearch),也有称为深度优化搜索,简称为DFS。

// 邻接表的深度有限递归算法

#define TRUE 1
#define FALSE 0
#define MAX 256

typedef int Boolean;	// 这里我们定义Boolean为布尔类型,其值为TRUE或FALSE
Boolean visited[MAX];	// 访问标志的数组

void DFS(GraphAdjList GL, int i)
{
	EdgeNode *p;
	
	visited[i] = TRUE;
	printf("%c " GL->adjList[i].data);
	p = GL->adjList[i].firstEdge;
	
	while(p)
	{
		if( !visited[p->adjvex] )
		{
			DFS(GL, p->adjvex);
		}
		p = p->next;
	}
}

// 邻接表的深度遍历操作
void DFSTraverse(GraphAdjList GL)
{
	int i;
	
	for( i=0; i < GL->numVertexes; i++ )
	{
		visited[i] = FALSE;		// 初始化所有顶点状态都是未访问过状态
	}
	
	for( i=0; i < GL->numVertexes; i++ )
	{
		if( !visited[i] )		// 若是连通图,只会执行一次
		{
			DFS(GL, i);
		}
	}
}
// 邻接矩阵的深度有限递归算法

#define TRUE 1
#define FALSE 0
#define MAX 256

typedef int Boolean;	// 这里我们定义Boolean为布尔类型,其值为TRUE或FALSE
Boolean visited[MAX];	// 访问标志的数组

void DFS(MGraph G, int i)
{
	int j;
	
	visited[j] = TRUE;			// 访问过的顶点设置为TRUE
	printf("%c ", G.vexs[i]);	// 打印顶点
	for( j=0; j < G.numVertexes; j++ )
	{
		if( G.arc[i][j]==1 && !visited[j] )
		{
			DFS(G, j);			// 对为访问的邻接顶点递归调用
		}
	}
}

// 邻接矩阵的深度遍历操作
void DFSTraverse(MGraph G)
{
	int i;
	
	for( i=0; i < G.numVertexes; i++ )
	{
		visited[i] = FALSE;		// 初始化所有顶点状态都是未访问过状态
	}
	
	for( i=0; i < G.numVertexes; i++ )
	{
		if( !visited[i] )		// 若是连通图,只会执行一次
		{
			DFS(G, i);
		}
	}
}

广度优先遍历(BreadthFirstSearch),又称为广度优先搜索,简称BFS。

// 邻接矩阵的广度遍历算法
void BFSTraverse(MGraph G)
{
	int i, j;
	Queue Q;
	
	for( i=0; i < G.numVertexes; i++ )
	{
		visited[i] = FALSE;
	}
	
	initQueue( &Q );
	
	for( i=0; i < G.numVertexes; i++ )
	{
		if( !visited[i] )
		{
			printf("%c ", G.vex[i]);
			visited[i] = TRUE;
			EnQueue(&Q, i);
			
			while( !QueueEmpty(Q) )
			{
				DeQueue(&Q, &i);
				for( j=0; j < G.numVertexes; j++ )
				{
					if( G.art[i][j]==1 && !visited[j] )
					{
						printf("%c ", G.vex[j]);
						visited[j] = TRUE;
						EnQueue(&Q, j);
					}
				}
			}
		}
	}
}

最小生成树

// Prim算法生成最小生成树
void MiniSpanTree_Prim(MGraph G)
{
	int min, i, j, k;
	int adjvex[MAXVEX];		// 保存相关顶点下标
	int lowcost[MAXVEX];	// 保存相关顶点间边的权值
	
	lowcost[0] = 0;			// V0作为最小生成树的根开始遍历,权值为0
	adjvex[0] = 0;			// V0第一个加入
	
	// 初始化操作
	for( i=1; i < G.numVertexes; i++ )
	{
		lowcost[i] = G.arc[0][i];	// 将邻接矩阵第0行所有权值先加入数组
		adjvex[i] = 0;				// 初始化全部先为V0的下标
	}
	
	// 真正构造最小生成树的过程
	for( i=1; i < G.numVertexes; i++ )
	{
		min = INFINITY;		// 初始化最小权值为65535等不可能数值
		j = 1;
		k = 0;
		
		// 遍历全部顶点
		while( j < G.numVertexes )
		{
			// 找出lowcost数组已存储的最小权值
			if( lowcost[j]!=0 && lowcost[j] < min )
			{
				min = lowcost[j];
				k = j;		// 将发现的最小权值的下标存入k,以待使用。
			}
			j++;
		}
		
		// 打印当前顶点边中权值最小的边
		printf("(%d,%d)", adjvex[k], k);
		lowcost[k] = 0;		// 将当前顶点的权值设置为0,表示此顶点已经完成任务,进行下一个顶点的遍历
		
		// 邻接矩阵k行逐个遍历全部顶点
		for( j=1; j < G.numVertexes; j++ )
		{
			if( lowcost[j]!=0 && G.arc[k][j] < lowcost[j] )
			{
				lowcost[j] = G.arc[k][j];
				adjvex[j] = k;	
			}
		}
	}
}

克鲁斯卡尔算法


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

// Kruskal算法生成最小生成树
void MiniSpanTree_Kruskal(MGraph G)
{
	int i, n, m;
	Edge edges[MAGEDGE];	// 定义边集数组克鲁斯卡尔算法

	int parent[MAXVEX];		// 定义parent数组用来判断边与边是否形成环路
	
	for( i=0; i < G.numVertexes; i++ )
	{
		parent[i] = 0;
	}
	
	for( i=0; i < G.numEdges; i++ )
	{
		n = Find(parent, edges[i].begin);	// 4 2 0 1 5 3 8 6 6 6 7
		m = Find(parent, edges[i].end);		// 7 8 1 5 8 7 6 6 6 7 7
		
		if( n != m )		// 如果n==m,则形成环路,不满足!
		{
			parent[n] = m;	// 将此边的结尾顶点放入下标为起点的parent数组中,表示此顶点已经在生成树集合中
			printf("(%d, %d) %d ", edges[i].begin, edges[i].end, edges[i].weight);
		}
	}
}

最短路径

迪杰斯特拉算法原理

#define MAXVEX	9
#define	INFINITY	65535

typedef	int	Patharc[MAXVEX];			// 用于存储最短路径下标的数组
typedef int	ShortPathTable[MAXVEX];		// 用于存储到各点最短路径的权值和

void ShortestPath_Dijkstar(MGraph G, int V0, Patharc *P, ShortPathTable *D)
{
	int v, w, k, min;
	int final[MAXVEX];		// final[w] = 1 表示已经求得顶点V0到Vw的最短路径
	
	// 初始化数据
	for( v=0; v < G.numVertexes; v++ )
	{
		final[v] = 0;				// 全部顶点初始化为未找到最短路径
		(*D)[V] = G.arc[V0][v];		// 将与V0点有连线的顶点加上权值
		(*P)[V] = 0;				// 初始化路径数组P为0
	}
	(*D)[V0] = 0;		// V0至V0的路径为0
	final[V0] = 1;		// V0至V0不需要求路径
	
	// 开始主循环,每次求得V0到某个V顶点的最短路径
	for( v=1; v < G.numVertexes; v++ )
	{
		min = INFINITY;
		for( w=0; w < G.numVertexes; w++ )
		{
			if( !final[w] && (*D)[w]<min )
			{
				k = w;
				min = (*D)[w];
			}
		}
		final[k] = 1;	// 将目前找到的最近的顶点置1
		
		// 修正当前最短路径及距离
		for( w=0; w < G.numVextexes; w++ )
		{
			// 如果经过v顶点的路径比现在这条路径的长度短的话,更新!
			if( !final[w] && (min+G.arc[k][w] < (*D)[w]) )
			{
				(*D)[w] = min + G.arc[k][w];	// 修改当前路径长度
				(*p)[w] = k;					// 存放前驱顶点
			}
		}
	}
}

弗洛伊德算法

#define MAXVEX	9
#define INFINITY	65535

typedef int Pathmatirx[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];

void ShortestPath_Floyd(MGraph G, Pathmatirx *P, ShortPathTable *D)
{
	int v, w, k;
	
	// 初始化D和P
	for( v=0; v < G.numVertexes; v++ )
	{
		for( w=0; w < G.numVertexes; w++ )
		{
			(*D)[v][w] = G.matirx[v][w];
			(*P)[v][w] = w;
		}
	}
	
	// 优美的弗洛伊德算法
	for( k=0; k < G.numVertexes; k++ )
	{
		for( v=0; v < G.numVertexes; v++ )
		{
			for( w=0; w < G.numVertexes; w++ )
			{
				if( (*D)[v][w] > (*D)[v][k] + (*D)[k][w] )
				{
					(*D)[v][w] = (*D)[v][k] + (*D)[k][w];
					(*P)[v][w] = (*P)[v][k];		// 请思考:这里换成(*P)[k][w]可以吗?为什么?
				}
			}
		}
	}
}

拓扑排序:

// 边表结点声明
typedef struct EdgeNode
{
	int adjvex;
	struct EdgeNode *next;
}EdgeNode;

// 顶点表结点声明
typedef struct VertexNode
{
	int in;			// 顶点入度
	int data;
	EdgeNode *firstedge;
}VertexNode, AdjList[MAXVEX];

typedef struct
{
	AdjList adjList;
	int numVertexes, numEdges;
}graphAdjList, *GraphAdjList;

// 拓扑排序算法
// 若GL无回路,则输出拓扑排序序列并返回OK,否则返回ERROR
Status TopologicalSort(GraphAdjList GL)
{
	EdgeNode *e;
	int i, k, gettop;
	int top = 0;		// 用于栈指针下标索引
	int count = 0;		// 用于统计输出顶点的个数
	int *stack;			// 用于存储入度为0的顶点
	
	stack = (int *)malloc(GL->numVertexes * sizeof(int));
	
	for( i=0; i < GL->numVertexes; i++ )
	{
		if( 0 == GL->adjList[i].in )
		{
			stack[++top] = i;	// 将度为0的顶点下标入栈
		}
	}
	
	while( 0 != top )
	{
		gettop = stack[top--];	// 出栈
		printf("%d -> ", GL->adjList[gettop].data);
		count++;				
		
		for( e=GL->adjList[gettop].firstedge; e; e=e->next )
		{
			k = e->adjvex;
			// 注意:下边这个if条件是分析整个程序的要点!
			// 将k号顶点邻接点的入度-1,因为他的前驱已经消除
			// 接着判断-1后入度是否为0,如果为0则也入栈
			if( !(--GL->adjList[k].in) )	
			{
				stack[++top] = k;
			}
		}
	}
	
	if( count < GL->numVertexes )	// 如果count小于顶点数,说明存在环
	{
		return ERROR;
	}
	else
	{
		return OK;
	}
}

关键路径


// 边表结点声明
typedef struct EdgeNode
{
	int adjvex;
	struct EdgeNode *next;
}EdgeNode;

// 顶点表结点声明
typedef struct VertexNode
{
	int in;			// 顶点入度
	int data;
	EdgeNode *firstedge;
}VertexNode, AdjList[MAXVEX];

typedef struct
{
	AdjList adjList;
	int numVertexes, numEdges;
}graphAdjList, *GraphAdjList;

int *etv, *ltv;
int *stack2;			// 用于存储拓扑序列的栈
int top2;				// 用于stack2的栈顶指针

// 拓扑排序算法
// 若GL无回路,则输出拓扑排序序列并返回OK,否则返回ERROR
Status TopologicalSort(GraphAdjList GL)
{
	EdgeNode *e;
	int i, k, gettop;
	int top = 0;		// 用于栈指针下标索引
	int count = 0;		// 用于统计输出顶点的个数
	int *stack;			// 用于存储入度为0的顶点
	
	stack = (int *)malloc(GL->numVertexes * sizeof(int));
	
	for( i=0; i < GL->numVertexes; i++ )
	{
		if( 0 == GL->adjList[i].in )
		{
			stack[++top] = i;	// 将度为0的顶点下标入栈
		}
	}
	
	// 初始化etv都为0
	top2 = 0;
	etv = (int *)malloc(GL->numVertexes*sizeof(int));
	for( i=0; i < GL->numVertexes; i++ )
	{
		etv[i] = 0;
	}
	stack2 = (int *)malloc(GL->numVertexes*sizeof(int));
	
	while( 0 != top )
	{
		gettop = stack[top--];		// 出栈
		// printf("%d -> ", GL->adjList[gettop].data); 
		stack2[++top2] = gettop;	// 保存拓扑序列顺序 C1 C2 C3 C4 .... C9
		count++;				
		
		for( e=GL->adjList[gettop].firstedge; e; e=e->next )
		{
			k = e->adjvex;
			// 注意:下边这个if条件是分析整个程序的要点!
			// 将k号顶点邻接点的入度-1,因为他的前驱已经消除
			// 接着判断-1后入度是否为0,如果为0则也入栈
			if( !(--GL->adjList[k].in) )	
			{
				stack[++top] = k;
			}
			
			if( (etv[gettop]+e->weight) > etv[k] )
			{
				etv[k] = etv[gettop] + e->weight;
			}
		}
	}
	
	if( count < GL->numVertexes )	// 如果count小于顶点数,说明存在环
	{
		return ERROR;
	}
	else
	{
		return OK;
	}
}

// 求关键路径,GL为有向图,输出GL的各项关键活动
void CriticalPath(GraphAdjList GL)
{
	EdgeNode *e;
	int i, gettop, k, j;
	int ete, lte;
	
	// 调用改进后的拓扑排序,求出etv和stack2的值
	TopologicalSort(GL);
	
	// 初始化ltv都为汇点的时间
	ltv = (int *)malloc(GL->numVertexes*sizeof(int));
	for( i=0; i < GL->numVertexes; i++ )
	{
		ltv[i] = etv[GL->numVertexes-1];
	}
	
	// 从汇点倒过来逐个计算ltv
	while( 0 != top2 )
	{
		gettop = stack2[top2--];	// 注意,第一个出栈是汇点
		for( e=GL->adjList[gettop].firstedge; e; e=e->next )
		{
			k = e->adjvex;
			if( (ltv[k] - e->weight) < ltv[gettop] )
			{
				ltv[gettop] = ltv[k] - e->weight;
			}
		}
	}
	
	// 通过etv和ltv求ete和lte
	for( j=0; j < GL->numVertexes; j++ )
	{
		for( e=GL->adjList[j].firstedge; e; e=e->next )
		{
			k = e->adjvex;
			ete = etv[j];
			lte = ltv[k] - e->weight;
			
			if( ete == lte )
			{
				printf("<v%d,v%d> length: %d , ", GL->adjList[j].data, GL->adjList[k].data, e->weight );
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_41543888/article/details/96482732