图之邻接表(存储结构、图的遍历、最小生成树、关键路径、最短路径)

邻接表的存储结构(ADJacency List)

//邻接表(Adjacency List) 
//链表中结点的类型 
typedef struct ArcNode{
	int adjvex; //该弧指向的顶点的位置 
	struct ArcNode *nextarc; //指向下一个弧结点的指针
	InfoType info; //该弧的相关信息的指针 
}ArcNode;
//头结点的类型 
typedef struct VNode{
	ElemType data; //顶点的信息 
	ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 
	int degree; //存放顶点的入度 
}VNode, AdjList[N+1]; //AdjList[0]可以不存放顶点信息 
typedef struct {
	AdjList adjlist; //邻接表 
	int vexnum, arcnum; //图的顶点数和弧数 
}ALGraph;

邻接表的方式构造图(无向图)

//邻接表的方式构造无向图
int LocateVex_AL(ALGraph G, ElemType v)
{ //找到顶点v在邻接表中的位置
	int i;
	for(i = 1; i<=G.vexnum; i++)
	{
		if(v == G.adjlist[i].data)
			return i;
	}
	return -1;
}
void Create_ALGraph(ALGraph &G)
{
	int i, j;
	ElemType v1, v2;
	int a1, a2;
	scanf("%d %d", &G.vexnum, &G.arcnum);//输入顶点数和边数
	//读入顶点信息,同时将顶点下一个指针初始化为空,入度初始化为零 
	for(i = 1;i<=G.vexnum; i++)
	{
		scanf("%d", &G.adjlist[i]);
		G.adjlist[i].firstarc = NULL;
		G.adjlist[i].degree = 0; 
	}	

	//读入边的信息
	for(i = 1; i<=G.arcnum; i++)
	{
		scanf("%d %d", &v1, &v2);
		a1 = LocateVex_AL(G, v1);
		a2 = LocateVex_AL(G, v2);
		
		ArcNode * p;
		p = (ArcNode*)malloc(sizeof(ArcNode)); 
		p->adjvex = a2;
		p->nextarc = G.adjlist[a1].firstarc;
		G.adjlist[a1].firstarc = p;
		G.adjlist[a2].degree++;
		//图为无向图,所以弧要做两次插入 
		ArcNode * q;
		q = (ArcNode*)malloc(sizeof(ArcNode)); 
		q->adjvex = a1;
		q->nextarc = G.adjlist[a2].firstarc;
		G.adjlist[a2].firstarc = q;
		G.adjlist[a1].degree++;
	}
}

图的遍历

一、深度优先遍历(Depth_First Search)

算法思想:
1.从图中某一个顶点v出发,访问此顶点,然后依次从v的未被访问过的邻接顶点出发深度遍历图,直到图中所有与v连通的顶点都被访问过;
2.若此时图中还有顶点未被访问(即非连通图),则另选一个未被访问过的顶点起始点,重复此过程,直到所有顶点都被访问过。

//深度优先搜索
void DFS(ALGraph G, int visited[], int v)
{
	int i;
	int t;
	ArcNode *p;
	p = G.adjlist[v].firstArc;
	printf("%c ", G.adjlist[v].data);
	visited[v] = 1;
	while(p)
	{
		if(visited[p->adjvex] == 0)
			DFS(G, visited, p->adjvex);
		p = p->nextArc;
	}
}

void DFSTravel(ALGraph G)
{
	int i;
	int visited[N]= {0}; //值为1表示该店访问过
	for(i = 0; i<G.vexnum; i++)
	{
		if(visited[i] == 0)
			DFS(G, visited, i);
	}
}

二、广度优先遍历(Broadth_First Search)

//广度优先搜索,类似于树的层次遍历
void BFSTravel(ALGraph G)
{
	//利用队列
	int i;
	int visited[N] = {0};
	int Q[N];
	int rear = 0; //队尾指针 
	int front = 0; //队头指针 
	int t; //用来接收出队元素 
	for(i = 0; i<G.vexnum; i++)
	{
		if(visited[i] == 0)
		{
			printf("%c ", G.adjlist[i].data);
			visited[i] = 1;
			Q[rear++] = i;//进队
			while(rear != front)//队不空 
			{
				t = Q[front++];
				ArcNode *p;
				p = (ArcNode*)malloc(sizeof(ArcNode));
				p = G.adjlist[t].firstArc;
				while(p)
				{
					if(visited[p->adjvex]==0)
					{
						printf("%c ", G.adjlist[p->adjvex].data);
						visited[p->adjvex] = 1;
						Q[rear++] = p->adjvex;
					}
					p = p->nextArc;
				}
			}
		}
	}
}

图的常见算法

最小生成树

  • 构造连通网的一棵生成树,使得总的代价最少——最小生成树的问题。
一、普利姆算法(Prim)

算法思想:

  1. 定义一个辅助结构数组 closege[n],记录从U 到 V-U 具有最小代价的边。 对每个还没有加入到生成树的顶点vi ∈V-U , 计算:

    closege[i-1].lowcost = Min{ cost(vi , u) | u∈U  }
    closege[i-1].vexs =  { u | 若 cost(vi  , u ) 最小  }
    
  2. 求 closege[k].lowcost 最小的顶点vk+1,并加入到U中,同时把 边( vk+1 , closege[k].vexs )加入到生成树中;

  3. 重复1,2过程。直到 U = V。

//最小生成树 
//普利姆算法(Prim) 
struct {
	int lowcost; //closedge[i].lowcost表示当前与第i个顶点相连的的权值最小的边
	int vex; //closedeg[i].vex表示与第i个顶点相连的顶点的位置 
}closedge[N];
void MiniTree_Prim(ALGraph G, int v) //从第v个顶点开始
{
	int i;
	int j;
	int visited[N]={0}; //0表示未访问,1表示已访问 
	printf("%c\n", G.adjlist[v].data);
	visited[v] = 1;
	//初始化closedge
	for(i = 0; i<=G.vexnum; i++)
	{
		closedge[i].lowcost = MAX;
		closedge[i].vex = v;
	}
	ArcNode *p;
	p = G.adjlist[v].firstArc;
	while(p)
	{
		closedge[p->adjvex].lowcost = p->info;
		p = p->nextArc;
		while(p)
		{
			closedge[p->adjvex].lowcost = p->info;
			p = p->nextArc;
		}
	}
	//找出closedge中最小的边,输出并进行更新
	for(j = 1; j<G.vexnum; j++)
	{
		int min = MAX;
		int t;
		for(i = 0; i<G.vexnum; i++)
		{
			if(closedge[i].lowcost < min && visited[i] == 0)
			{
				min = closedge[i].lowcost;
				t = i;
			}
		}
		printf("%c,%d,%c\n", G.adjlist[t].data,closedge[t].lowcost, G.adjlist[closedge[t].vex].data);
		visited[t] = 1;
		
		//更新 
		p = G.adjlist[t].firstArc;
		while(p)
		{
			if(closedge[p->adjvex].lowcost>p->info)
			{
				closedge[p->adjvex].lowcost = p->info;
				closedge[p->adjvex].vex = t;
			}
			p = p->nextArc;
		}
	}
}
二、克鲁斯卡尔算法(Kruskal)

算法思想:

  1. 设T=(V,{E})是一个连通图,则令最小生成树的初始状态为只有n个顶点而无边的非连通图 ,即:T=( V,{ } ) 图中每个顶点自成一个连通分量。
  2. 在E中选择代价最小的边。约束条件:若此边依附的顶点落在T的不同连通分量上,将此边加入T中。否则舍去此边另选下一条代价最小的边。
  3. 依次类推,直到T中所有顶点都在同一个连通分量中。

拓扑排序(Topological Sort),AOV-网

算法思想:

  • 在有向图中选一个没有前驱的顶点且输出之。
  • 从图中删除此顶点及所有以它为尾的弧。
  • 重复1.2过程,直到所有顶点均已输出,或当前图中不再有无前驱的顶点为止,此种情况表示图中有环。
//拓扑排序 (Topological Sort)
void TopologicalSort(ALGraph G)
{
	//统计每个顶点的入度
	//建立栈,存入每个入度为零的顶点
	//当栈不空,删除入度为零以及以它为尾的弧
	int i,t;
	int indegree[N]={0}; //存放入度的数组
	int S[N];
	int top = 0;
	ArcNode *p;
	int count = 0; //记录输出顶点的个数 
	for(i = 0; i<G.vexnum; i++) //统计入度 
	{
		p = G.adjlist[i].firstArc;
		while(p)
		{
			indegree[p->adjvex]++;
			p = p->nextArc;
		}
	}
	for(i = 0; i<G.vexnum; i++) //对入度为零的顶点进栈并访问 
	{
		if(indegree[i] == 0)
		{
			S[top++] = i;
			count++; 
			printf("%c,", G.adjlist[i].data);
		}
	}
	
	while(top != 0) //当栈不空 
	{
		t = S[--top];
		//进行更新
		p = G.adjlist[t].firstArc;
		while(p)
		{
			indegree[p->adjvex]--;
			if(indegree[p->adjvex] == 0)
			{
				S[top++] = p->adjvex; //进栈的同时标记已访问
				count++;
				printf("%c,", G.adjlist[p->adjvex].data);
			}
			p = p->nextArc;
		}
	}
	if(count == G.vexnum)
		printf("\n该图中无回路");
}

关键路径问题,AOE-网

  • 从开始点到完成点的最长路径的长度.即路径上各活动持续时间之和

算法思想:

  1. 输入e条弧,建立AOE-网的存储结构;
  2. 从源点V0出发,令ve[0]=0;按拓扑有序求其余各顶点的ve[i];若得到的序列中顶点的个数小于网中的顶点总数,则不能求关键路径,算法终止;否则执行步骤(3);
  3. 从汇点Vn出发,令vL[n-1]=ve[n-1],按逆拓扑有序求其余各顶点的vL[i];
  4. 根据各顶点的ve和vL,求每条弧s的e[s]和L[s],若满足e[s]=L[s],则为关键活动。
int ve[N] = {0}; //ve[i]表示最早开始时间
int vl[N] = {0}; //vl[i]表示最晚开始时间
int T[N],S[N];
int topS = 0;
int topT = 0;
void TopologicalOrder(ALGraph G)
{
	int i, t;
	int indegree[N] = {0}; 
	ArcNode *p;
	
	for(i = 0; i<G.vexnum; i++) //统计入度 
	{
		p = G.adjlist[i].firstArc;
		while(p)
		{
			indegree[p->adjvex]++;
			p = p->nextArc;
		}
	}
	for(i = 0; i<G.vexnum; i++) //对入度为零的顶点进栈 
	{
		if(indegree[i] == 0)
		{
			S[topS++] = i;
			T[topT++] = i;
		}
	}
	
	while(topS != 0) //当栈不空 
	{
		t = S[--topS];
		p = G.adjlist[t].firstArc;
		while(p) //求出当前最早开始时间,并对入度进行更新 
		{
			if(ve[t] + p->info > ve[p->adjvex])
				ve[p->adjvex] = ve[t]+p->info;
			indegree[p->adjvex]--;
			if(indegree[p->adjvex] == 0)
			{
				S[topS++] = p->adjvex;
				T[topT++] = p->adjvex;
			}
			p = p->nextArc;
		}
	}
}

void CriticalPath(ALGraph G)
{
	int i,t;
	ArcNode *p;
	TopologicalOrder(G);
	t = T[--topT];
	for(i = 0; i<G.vexnum ;i++)
	{
		vl[i] = ve[t];
	}
	
	while(topT != 0)
	{
		t = T[--topT];
		p = G.adjlist[t].firstArc;
		while(p)
		{
			if(vl[p->adjvex]-p->info < vl[t])
				vl[t] = vl[p->adjvex]-p->info;
			p = p->nextArc;
		}
	}
	
	for(i = 0; i<G.vexnum; i++)
	{
		if(ve[i] == vl[i])
			printf("%c,", G.adjlist[i].data);
	}
}

最短路径问题

一、迪杰斯特拉算法(Dijkstra)
  • 目的:求源点到其余各顶点的最短路径

算法思想:

  1. 1.定义D[i]:表示当前所找到的从源点V0到Vi的最短路径的长度。初态:D[i] = G.arcs[v0][vi] 即弧的权值。
  2. D[j] = Min( D[i] ) (i=1,…., n-1)。显然:Vj 就是从V0出发最早到达的长度最短的顶点。
  3. 下一条最短路径为 D[k] = min(D[k] , D[j]+G.arcs[j][k]);
  4. 同理,求出接下来的每个最短路径。
//迪杰斯特拉算法(Dijkstra),求v0到各顶点的最短距离 
void ShortestPath_DIJ(ALGraph G, int v0)
{
	//从v0求直接到其他顶点的距离,其中最小的为最短距离
	//再从该点找直接相连的
	int i, j, t;
	int visited[N] = {0};
	int D[N]; //D[i]表示当前v0到第i个顶点的最短距离 
	int P[N] = {0}; //P[i]表示v0到第i个顶点最短路径所经过的上一个顶点 
	ArcNode *p;
	int min;
	for(i = 0; i<G.vexnum; i++)
		D[i] = MAX;
	
	p = G.adjlist[v0].firstArc;
	while(p)
	{
		D[p->adjvex] = p->info;
		P[p->adjvex] = v0;
		p = p->nextArc;
	}
	D[v0] = 0; //v0到v0点的距离为0 
	for(i = 1; i<G.vexnum; i++)
	{
		//找出D[]中的最小值D[t]
		min = MAX;
		for(j = 0; j<G.vexnum; j++)
		{
			if(D[j] < min && visited[j] == 0)
			{
				min = D[j];
				t = j;
			}
		}
		visited[t] = 1;
		p = G.adjlist[t].firstArc;
		while(p)
		{
			if(D[t] + p->info < D[p->adjvex] && visited[p->adjvex] == 0)
			{
				D[p->adjvex] = D[t] + p->info;
				P[p->adjvex] = t;
			}
			p = p->nextArc;
		}
	}
	
	for(i = 0; i<G.vexnum; i++)
	{
		printf("%d, %d\n",D[i], P[i]);	
	}
}
一、弗洛伊德算法(Floyd)
  • 目的:求每一对顶点之间的最短路径
//弗洛伊德算法(Floyd),求每一对顶点之间的最短距离 
void ShortestPath_FLOYD(ALGraph G)
{
	int i, j, k;
	int D[N][N]; //D[i][j]表示当前第i个顶点到第j个顶点的最短距离
	int P[N][N] = {0}; //P[i][j]表示第i个顶点到第j个顶点最短路径所经过的上一个顶点
	ArcNode *p;
	//初始化D[][]
	for(i = 0;i<G.vexnum; i++)
	{
		for(j = 0; j<G.vexnum; j++)
			D[i][j] = MAX;
	}
	for(i = 0; i<G.vexnum; i++)
	{
		p = G.adjlist[i].firstArc;
		while(p)
		{
			D[i][p->adjvex] = p->info;
			P[i][p->adjvex] = i;
			p = p->nextArc;
		}
		D[i][i] = 0;
	}
	
	for(i = 0;i<G.vexnum; i++)
	{
		for(j = 0; j<G.vexnum; j++)
		{
			for(k = 0; k<G.vexnum; k++)
			{
				if(D[j][i] + D[i][k] < D[j][k])
				{
					D[j][k] = D[j][i] + D[i][k];
					P[j][k] = i;
				}
			}
		}
	}
	for(i = 0;i<G.vexnum; i++)
	{
		for(j = 0; j<G.vexnum; j++)
		{
			printf("%d, %d\n", D[i][j],P[i][j]);
		}
	}
}

你可能感兴趣的:(c语言数据结构)