关于图及其操作

3#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<queue>
#define ElemType int
using namespace std;
/*
Graph Vertex Edge
简单路径-在路径序列中,顶点不重复出现的路径成为路径长度
简单回路-除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路
路径长度-路径上边的数目
点到点的距离-从顶点u出发到顶点v的最短路径!!若存在,则路径的长度称为从u到v的距离。若不存在路径,则记该距离为无穷。
无向图-有路径-连通  任意顶点都连通-连通图
有向图-双向都有路 -1条边
非连通图-最多有C n-1 2条边
!强连通图-最少有n条边
生成子图-包含原图的所有顶点
无向图-极大连通分量-包含尽可能多的顶点和边
有向图-极大强连通分量
无向图-生成树-包含全部顶点的一个极小连通子图
带权图=网
带权路径长度=一条路径上所有边的权值之和
无向完全图-无向图中任意两个顶点之间都存在边
有向完全图-有向图中任意两个顶点之间都存在方向相反的两条弧
*/
/*
!!!设图G的邻接矩阵为A(0/1),则A^n的元素A^n[i][j]等于由顶点i到顶点j的长度为n的路径的数目
*/
#define MaxVertexNum 100
#define INF 999999
//邻接矩阵
typedef struct {
	char Vex[MaxVertexNum];
	int Edge[MaxVertexNum][MaxVertexNum];
	int vexnum, arcnum;
}MGraph;
//邻接表
//边/弧   adjacent  a邻近的
typedef struct ArcNode {
	int adjvex;
	struct ArcNode *next;
	int weight;
}ArcNode;
//顶点
typedef struct VNode {
	char data;
	ArcNode *first;
}VNode, AdjList[MaxVertexNum];
//用邻接表存储的图
typedef struct {
	AdjList verteices;
	int vexnum, arcnum;
}ALGraph;
//求图G中顶点x的第一个邻接点,若有则返回顶点号,若没有或不存在x则返回1
int FirstNeighbor_M(MGraph G, int x) {
	if (x < 1 || x > G.vexnum)
		return -1;
	for (int i = 1; i <= G.vexnum; i++) {
		if (G.Edge[x][i] > 0 && G.Edge[x][i] < INF)
			return i;
	}
	return -1;
}
int FirstNeighbor_AL(ALGraph G, int x) {
	if (x < 1 || x > G.vexnum)
		return -1;
	ArcNode *p = G.verteices[x].first;
	if (p != NULL)
		return p->adjvex;
	return -1;
}
//假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1
int NextNeightbor_M(MGraph G, int x, int y) {
	if (x < 1 || x > G.vexnum)
		return -1;
	for (int i = y + 1; i <= G.vexnum; i++) {//注意从y+1开始循环
		if (G.Edge[x][i] > 0 && G.Edge[x][i] < INF)
			return i;
	}
	return -1;
}
int NextNeightbor_AL(ALGraph G, int x, int y) {
	if (x < 1 || x > G.vexnum)
		return -1;
	ArcNode *p = G.verteices[x].first;
	while (p != NULL && p->adjvex != y)
		p = p->next;
	if (p != NULL && p->next != NULL)
		return p->next->adjvex;
	return -1;
}
//从x开始广度优先遍历
bool visited[MaxVertexNum] = { false };
void BFS_M(MGraph G, int x) {//时间复杂度O(|v^2|)
	queue<int> q;
	q.push(x);
	visited[x] = true;
	while (!q.empty()) {
		int temp = q.front();
		q.pop();
		printf("%d\t", temp);
		for (int i = FirstNeighbor_M(G, temp); i != -1; i = NextNeightbor_M(G, temp, i)) {
			if (!visited[i]) {
				q.push(i);
				visited[i] = true;//必须在这vis,不可以放在for里,也不可以提前 因为跟数不一样 图可能有回路
			}
		}
	}
}
void BFS_AL(ALGraph G, int x) {//遍历序列不唯一 跟邻接表有关//时间复杂度:O(|v|+|E|)
	queue<int> q;//注意 类型应为int 若为vnode 则不方便代表此顶点编号 
	q.push(x);
	visited[x] = true;
	while (!empty(q)) {
		int temp = q.front();
		q.pop();
		printf("%d\t", temp);
		for (int i = FirstNeighbor_AL(G, temp); i != -1; i = NextNeightbor_AL(G, temp, i)) {
			if (!visited[i]) {
				q.push(i);
				visited[i] = true;
			}
		}
	}
}
//从x开始深度遍历
void DFS_M_1(MGraph G, int x) {
	if (visited[x])
		return;
	printf("%d\t", x);
	visited[x] = true;
	for (int i = 1; i <= G.vexnum; i++) {
		if (G.Edge[x][i] > 0 && G.Edge[x][i] < INF)
			DFS_M_1(G, i);
	}
}
//另一种写法
void DFS_M(MGraph G, int x) {
	printf("%d\t", x);
	visited[x] = true;
	for (int i = FirstNeighbor_M(G, x); i != -1; i = NextNeightbor_M(G, x, i)) {
		if (!visited[i])
			DFS_M(G, i);
	}
}
void DFS_AL(ALGraph G, int x) {
	printf("%d\t", x);
	visited[x] = true;
	for (int i = FirstNeighbor_AL(G, x); i != -1; i = NextNeightbor_AL(G, x, i)) {
		if (!visited[i]) {
			DFS_AL(G, i);
		}
	}
}

/*最小生成树MST	Minimum-Spanning-Tree ->研究对象为:带权连通无向图
 */
 //Prim算法(普里姆)-O(|V^2|)-适合用于边稠密
 //从某个一个顶点开始构建生成树;每次将代价最小的新顶点纳入生成树,直到所有顶点都纳入为止

 //Kruskal算法(克鲁斯卡尔)-O(|E|log2|E|)-适合用于边稀疏图
 //每次选择一条权值最小的边,使这条边的两头连通(原本已经连通的就不选),直到所有结点都连通

 //最短路劲
 //BFS实现
int d[MaxVertexNum];
int path[MaxVertexNum];
void BFS_MIN_Distance(MGraph G, int u) {
	for (int i = 1; i <= G.vexnum; i++) {
		d[i] = INF;
		path[i] = -1;
	}
	queue<int> q;
	q.push(u);
	visited[u] = true;
	d[u] = 0;
	while (!q.empty()) {
		u = q.front();
		q.pop();
		for (int i = FirstNeighbor_M(G, u); i != -1; i = NextNeightbor_M(G, u, i)) {
			if (!visited[i]) {
				visited[i] = true;
				d[i] = d[u] + 1;
				path[i] = u;
				q.push(i);
			}
		}
	}
}
//Dijkstra算法(迪杰斯特拉)
bool final[MaxVertexNum];
int dist[MaxVertexNum];
int path_[MaxVertexNum];
void Dijkstra(MGraph G, int u) {
	for (int i = 1; i <= G.vexnum; i++) {
		final[i] = false;
		dist[i] = INF;
		path_[i] = -1;
	}
	dist[u] = 0;
	for (int i = 1; i <= G.vexnum; i++) {
		int x = INF;
		int v = -1;
		for (int j = 1; j <= G.vexnum; j++) {
			if (!final[j] && dist[j] < x) {
				x = dist[j];
				v = j;
			}
		}
		if (v == -1)return;
		final[v] = true;
		for (int j = 1; j <= G.vexnum; j++) {
			if (G.Edge[v][j] + dist[v] < dist[j]) {
				dist[j] = G.Edge[v][j] + dist[v];
				path_[j] = v;
			}
		}
	}
}
int A[MaxVertexNum][MaxVertexNum];
int Path[MaxVertexNum][MaxVertexNum];
//Floyd算法(弗洛伊德)【不能解决带有负权回路的图】
void Floyd(MGraph G) {
	for (int i = 1; i <= G.vexnum; i++)
		for (int j = 1; j <= G.vexnum; j++) {
			A[i][j] = G.Edge[i][j];
			Path[i][j] = -1;
		}
	for (int k = 1; k <= G.vexnum; k++) {
		for (int i = 1; i <= G.vexnum; i++) {
			for (int j = 1; j <= G.vexnum; j++) {
				if (A[i][j] > A[i][k] + A[k][j]) {
					A[i][j] = A[i][k] + A[k][j];
					Path[i][j] = k;
				}
			}
		}
	}
	for (int i = 1; i <= G.vexnum; i++) {
		for (int j = 1; j <= G.vexnum; j++) { 
			printf("%d\t", A[i][j]);
		}
		printf("\n");
	}
}

//有向无环图DAG(Directed Acyclic Graph)
/*
即有向图中不存在环
AOV网(Activity On Vertex NetWork )每个aov网都有一个或多个拓扑序列
1.从AOV网中选择一个没有前驱(入度为0)的顶点并输出
2.从网中删除该顶点和所有以它为起点的有向边
3.重复1和2直到当前的AOV网为空或当前网中不存在无前驱的顶点为止
*/
int indegree[MaxVertexNum];
int print[MaxVertexNum];
bool TopologicalSort(ALGraph G) {
	//初始化indegree数组和print数组
	for (int i = 1; i < G.vexnum; i++) {
		print[i] = -1;
		ArcNode *p = G.verteices[i].first;
		while (p != NULL) {
			indegree[p->adjvex]++;
			p = p->next;
		}
	}
	queue<int> q;
	for (int i = 1; i <= G.vexnum; i++)
		if (indegree[i] == 0)
			q.push(i);
	int count = 0;
	while (!q.empty()) {
		print[++count] = q.front();
		ArcNode *p = G.verteices[q.front()].first;
		q.pop();
		while (p != NULL) {
			indegree[p->adjvex]--;
			if (indegree[p->adjvex] == 0)
				q.push(p->adjvex);
			p = p->next;
		}
	}
	if (count < G.vexnum)
		return false;
	else
		return true;
}
//逆拓扑排序
int outdegree[MaxVertexNum];
bool ReTopologicalSort(MGraph G) {
	//初始化
	for (int i = 1; i <= G.vexnum; i++) {
		print[i] = -1;
		for (int j = 1; j <= G.vexnum; j++) {
			if (G.Edge[i][j] > 0 && G.Edge[i][j] < INF)
				outdegree[i]++;
		}
	}
	queue<int> q;
	for (int i = 1; i <= G.vexnum; i++) {
		if (outdegree[i] == 0)
			q.push(i);
	}
	int count = 0;
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		print[++count] = x;
		for (int i = 1; i <= G.vexnum; i++) {
			if (G.Edge[i][x] > 0 && G.Edge[i][x] < INF) {
				outdegree[i]--;
				if (outdegree[i] == 0)
					q.push(i);
			}
		}
	}
	if (count < G.vexnum)
		return false;
	else
		return true;
}

//AOE网(Activity On Edge NetWork)
/*
在带权有向图中,以顶点表示时间,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需的时间),称之为用边表示活动的网络
1.只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始;
2.只有在进入某顶点的各有向边所代表的活动都已结束时,该顶点所代表的事件才能发生
3.有些活动可以并行进行
!仅有一个入度为0的顶点,称为开始顶点(源点),它表示整个工程的开始
!仅有一个出度为0的顶点,称为结束顶点(汇点),它表示整个工程的结束
!从源点到汇点的有向路劲可能有多条,其中具有最大路劲长度的路劲称为!关键路径!-关键路径上的活动称为关键活动!!!
完成整个工程的最短时间就是关键路径的长度
若关键活动不能按时完成,则整个工程的完成时间就会延长
@@@@
事件v k的最早发生时间v e (k)——决定了所有从v k开始的活动能够开工的最早时间
活动a i的最早开始时间e (i)——指该活动弧的起点所表示的事件的最早发生时间
事件v k 的最迟发生时间 v l (k)——指在不推迟整个工程完成的前提下,该事件最迟必须发生的时间
活动a i的最迟开始时间l (i)——指该活动弧的终点所表示的最迟发生时间与该活动所需时间之差
活动a i的时间余量d (i)=l (i)-e (i)——在不增加完成整个工程所需总时间的情况下,活动a i可以拖延的时间
若一个活动的时间余量为零,则说明该活动必须要如期完成,d (i)=0即l (i)=e (i)的活动a i是关键活动
@
可能存在多条关键路径
若关键活动耗时增加 则整个工程的工期将增长
缩短关键活动的时间,可以缩短整个工程的工期
!当缩短到一定程度时,关键活动可能会变成非关键活动
*/

//初始化并读入图
void Initial_M(MGraph &G) {
	for (int i = 0; i < MaxVertexNum; i++)
		for (int j = 0; j < MaxVertexNum; j++)
			G.Edge[i][j] = G.Edge[j][i] = INF;
	for (int i = 0; i < MaxVertexNum; i++)//flyod必须!
		G.Edge[i][i] = 0;
	int flag;
	scanf("%d", &flag);
	scanf("%d %d", &G.vexnum, &G.arcnum);
	for (int i = 0; i < G.arcnum; i++) {
		int v1, v2, w;
		cin >> v1 >> v2 >> w;
		G.Edge[v1][v2] = w;
		if (flag == 0)
			G.Edge[v2][v1] = w;
	}
	cout << "正在初始化邻接矩阵……" << endl;
}
//根据邻接矩阵初始化邻接表
void Initial_AL(MGraph G1, ALGraph &G2) {
	cout << "正在初始化邻接表……" << endl;
	//初始化邻接表
	for (int i = 0; i < MaxVertexNum; i++)
		G2.verteices[i].first = NULL;
	//根据邻接矩阵创造邻接表
	G2.vexnum = G1.vexnum;
	G2.arcnum = G1.arcnum;
	for (int i = 1; i <= G2.vexnum; i++) {
		ArcNode *q = NULL;
		for (int j = 1; j <= G2.vexnum; j++) {
			if (G1.Edge[i][j] > 0 && G1.Edge[i][j] < INF) {
				ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
				p->adjvex = j;
				p->weight = 1;
				p->next = NULL;
				if (G2.verteices[i].first == NULL)
					G2.verteices[i].first = p;
				else
					q->next = p;
				q = p;
			}
		}
	}
}
int main() {
	MGraph G1;
	ALGraph G2;
	Initial_M(G1);
	for (int i = 1; i <= G1.vexnum; i++) {
		if (!visited[i])//vis不可以放在for里!!!
			BFS_M(G1, i);
	}
	cout << "bfs遍历" << endl;
	//重新初始化visited数组
	for (int i = 0; i < MaxVertexNum; i++)
		visited[i] = false;
	for (int i = 1; i <= G1.vexnum; i++) {
		if (!visited[i])
			DFS_M(G1, i);
	}
	cout << "dfs遍历" << endl;

	Initial_AL(G1, G2);
	for (int i = 0; i < MaxVertexNum; i++)
		visited[i] = false;
	for (int i = 1; i <= G2.vexnum; i++)
		if (!visited[i])
			BFS_AL(G2, i);
	cout << "bfs遍历" << endl;
	for (int i = 0; i < MaxVertexNum; i++)
		visited[i] = false;
	for (int i = 1; i <= G2.vexnum; i++)
		if (!visited[i])
			DFS_AL(G2, i);
	cout << "dfs遍历" << endl;


	cout << "\nBFS求解最短路径(只在无权图有效)";
	for (int j = 1; j <= G1.vexnum; j++) {
		//分别求以每个顶点为源头的最短路劲
		for (int i = 0; i < MaxVertexNum; i++)
			visited[i] = false;
		BFS_MIN_Distance(G1, j);
		printf("\n以%2d为源头\t距离\t", j);
		for (int i = 1; i <= G1.vexnum; i++)
			printf("%d\t", d[i]);
		printf("\n\t\t前驱\t");
		for (int i = 1; i <= G1.vexnum; i++)
			printf("%d\t", path[i]);
	}

	cout << "\n\nDijkstra求解最短路径";
	for (int j = 1; j <= G1.vexnum; j++) {
		//分别求以每个顶点为源头的最短路劲
		Dijkstra(G1, j);
		printf("\n以%2d为源头\t距离\t", j);
		for (int i = 1; i <= G1.vexnum; i++)
			printf("%d\t", dist[i]);
		printf("\n\t\t前驱\t");
		for (int i = 1; i <= G1.vexnum; i++)
			printf("%d\t", path_[i]);
	}
	cout << "\n\nFloyd求解最短路径\n";
	Floyd(G1);

	cout << "\n进行拓扑排序……\n";
	if (TopologicalSort(G2)) {
		for (int i = 1; i <= G2.vexnum; i++)
			printf("%d\t", print[i]);
	}
	else
		printf("生成失败");

	cout << "\n进行逆拓扑排序……\n";
	if (ReTopologicalSort(G1)) {
		for (int i = 1; i <= G1.vexnum; i++)
			printf("%d\t", print[i]);
	}
	else
		printf("生成失败");
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值