数据结果(C语言) 7.图

1.图的基本知识

  • 定义

图中的点——》顶点

两顶点的连线—》边

图:Graph=(V,E)

V:顶点(数据元素)的非空有穷集合

E:边的有穷集合,可以为空

  • 无向图
  1. 每条边都是无方向的
  2. 若任意两个顶点都有一条边相连的图称为无向完全图
  3. 无向完全图,n个顶点有几条边?

(n-1)+(n-2)+…+1=n*(n-1)/2

  • 有向图
  1. 每一条边都是有方向的,称为弧

弧头——>弧尾

  1. 任意两个顶点都有方向相反的两条边的图称为有向完全图
  2. 有向完全图,n个顶点有几条边?

2*[(n-1)+(n-2)+…+1]=n*(n-1)

  1. 定义:网是边或弧带权的图
  2. 图的边或弧所具有的相关数称为
  3. 有向网:带权的有向图
  4. 无向网:带权的无向图
  • 子图:

如果一个图的顶点和边都属于另一个图,那么这个图就是另一个图的子图

  • 图的顶点和边的关系

无向图

  1. 邻接点:两点之间有连线,那么这两点是邻接点
  2. 边依附于这条边上的两顶点(或:边和这条边上的两顶点相关联)
  3. 顶点的度:和顶点向关联的边的数量,记为TD(V)
  4. 无向图的边数是各顶点度数和的一半

因为相当于每条边算了两次

    有向图

  1. 邻接点:两点之间有弧,则两点互为邻接点
  2. 弧依附于弧上的两顶点(弧与这两顶点相关联)
  3. 入度和出度

入度:以某顶点为头的弧的数量,称为此顶点的入度

出度:以某顶点为尾的弧的数量,称为此顶点的出度

度:入度+出度

  1. 有向图的弧数=总入度=总出度

因为一个顶点有一个入度,就有另一个顶点有一个出度

入度和等于出度和等于度数和的一半

弧数等于度数和的一半,所以弧数=入度和=出度和

  • 路径
  1. 定义:路径:连续的边构成的有序顶点集合

若是有向图,路径也是有向的

  1. 路径长度:路径上边或弧的长度
  • 回路或环
  1. 定义:第一个顶点到最后一个顶点相同的路径
  2. 简单回路或简单环

除第一个顶点和最后一个顶点外,其余顶点都不重复出现的回路

2.无向无权图的创建(邻接矩阵)以及深度优先遍历和广度优先遍历

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef struct Graph
{
	char* vexs;//顶点
	int** arcs;//边 存边和顶点关系的二维数组
	int vexNum;//顶点数
	int arcNum;//边数
}Graph;

//初始化
Graph* initGraph(int vexNum)
{
	Graph* G = (Graph*)malloc(sizeof(Graph));

	G->vexs = (char*)malloc(sizeof(char) * vexNum);
	G->arcs = (int**)malloc(sizeof(int*) * vexNum);

	for (int i = 0; i < vexNum; i++)
	{
		G->arcs[i] = (int*)malloc(sizeof(int) * vexNum);//二维数组的每行一维数组的首地址
	}
	G->vexNum = vexNum;
	G->arcNum = 0;

	return G;
}

//无向图 邻接矩阵法 创建
void CreateGraph(Graph* G, char* vexs, int* arcs)
{
	for (int i = 0; i < G->vexNum; i++)
	{
		G->vexs[i] = vexs[i];

		for (int j = 0; j < G->vexNum; j++)
		{
			G->arcs[i][j] = *(arcs + i * G->vexNum + j);//G->arcs[i]是二维数组每行一维数组的首地址,可以理解为每行一维数组的数组名,再加[j]就是每行一维数组的第J个元素的元素值

			if (G->arcs[i][j] != 0)
			{
				G->arcNum++;
			}
		}
	}

	G->arcNum /= 2;//无向图
	//有向图则不用除2
}


//图的遍历
//BFS广度优先遍历
typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QueueNode;

typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
}Queue;

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

//释放
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QueueNode* cur = pq->head;
	while (cur)
	{
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
}

//判断是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

//队尾插入
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	newnode->data = x;
	newnode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

//队头删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	QueueNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

//取队头的数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

void BFS(Graph* G, int* visited, int index)//类似树的层次遍历
{
	Queue Q;
	QueueInit(&Q);

	printf("%c\t", G->vexs[index]);
	visited[index] = 1;

	QueuePush(&Q, index);//入队
	while (!QueueEmpty(&Q))
	{
		int i = QueueFront(&Q);//取队头
		QueuePop(&Q);//删队头

		for (int j = 0; j < G->vexNum; j++)
		{
			if (G->arcs[i][j] == 1 && !visited[j])
			{
				printf("%c\t", G->vexs[j]);
				visited[j] = 1;
				QueuePush(&Q, j);//入队
			}
		}
	}
}



//DFS 深度优先遍历
//1.找到一个节点访问
//2.找这个节点可以访问的节点
//3.重复第一步,直到所有节点访问完毕
void DFS(Graph* G, int* visited, int index)//visited标识是否遍历过 index索引,从哪个顶点开始遍历
{
	printf("%c\t", G->vexs[index]);
	visited[index] = 1;

	for (int i = 0; i < G->vexNum; i++)
	{
		if (G->arcs[index][i] == 1 && !visited[i])
		{
			DFS(G, visited, i);
		}
	}
}

int main()
{
	Graph* G = initGraph(5);
	//网(带权)时,表中存放权值,而不是1
	int arcs[5][5] = {
		0,1,1,1,0,
		1,0,1,1,1,
		1,1,0,0,0,
		1,1,0,0,1,
		0,1,0,1,0
	};

	CreateGraph(G, "ABCDE", (int*)arcs);

	int* visited = (int*)malloc(sizeof(int) * G->vexNum);
	for (int i = 0; i < G->vexNum; i++)
	{
		visited[i] = 0;
	}
	DFS(G, visited, 0);
	printf("\n");

	for (int i = 0; i < G->vexNum; i++)
	{
		visited[i] = 0;
	}
	BFS(G, visited, 0);

	return 0;
}

3.邻接表表示法创建图

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_VERTEX 100 //顶点数的最大值

typedef int ArcType;//先将权重默认为int类型
typedef char* VerTexType;//将顶点的值默认为字符串类型

typedef enum
{
	DG,  //有向图
	UDG, //无向图
	DN,  //有向网
	UDN  //无向网
}GraphKind;

//边/弧 的结点
typedef struct node
{
	int adjVex;              //该边指向的这条边邻接点的下标
	union
	{
		struct node* nextEdge;   //指向下一个边结点的指针
		struct node* nextArc;    //指向下一个弧结点的指针
	};
	ArcType weight;          //权重
}Edgenode,ArcNode;//Edgenode---边    ArcNode---弧


//顶点结点
typedef struct vexNode
{
	VerTexType vex;       //顶点的值
	//union
	
		Edgenode* firstEdge;  //指向第一个边结点的指针
		ArcNode* firstArc;    //指向第一个弧结点的指针
	
}VNode, AdjList[MAX_VERTEX];  // AdjList[MAX_VERTEX] 邻接表(存顶点结点的数组)


//邻接表实现的图结构
typedef struct AdjGraph
{
	AdjList vexs;  //顶点数组
	int vexCount;  //顶点数量
	union
	{
		int edgeCount; //图的边数
		int arcCount;  //图的弧数
	};
	GraphKind kind;    //图的类型
}AdjListGraph;

int LocateVex_AdjList(AdjListGraph* G, VerTexType vex);//找到值为vex的顶点的

void CreateUDG_AdjList(AdjListGraph* G);//无向图创建

void CreateDG_AdjList(AdjListGraph* G);//有向图创建

//找到值为vex的顶点的下标,没找到返回-1
int LocateVex_AdjList(AdjListGraph* G, VerTexType vex)
{
	int index = -1;
	for (int i = 0; i < G->vexCount; i++)
	{
		if (strcmp(vex , G->vexs[i].vex) == 0)
		{
			index = i;
			break;
		}
	}

	return index;
}

//无向图创建
void CreateUDG_AdjList(AdjListGraph* G)
{
	G->kind = UDG;

	printf("请输入顶点数量:");
	scanf_s("%d", &(G->vexCount));

	printf("请输入边的数量:");
	scanf_s("%d", &(G->edgeCount));

	printf("请依此输入顶点信息\n");
	for (int i = 0; i < G->vexCount; i++)
	{
		G->vexs[i].vex = (VerTexType)malloc(sizeof(char) * 10);
		printf("顶点%d:", i+1);
		scanf_s("%s", G->vexs[i].vex);

		//初始化邻接表,把边置空
		G->vexs[i].firstEdge = NULL;
	}

	printf("请输入顶点和邻接点信息,构建邻接表:\n");
	for (int j = 0; j < G->edgeCount; j++)
	{
		VerTexType vex1 = (VerTexType)malloc(sizeof(char) * 10);
		VerTexType vex2 = (VerTexType)malloc(sizeof(char) * 10);

		printf("顶点:");
		//vex1 & vex2 是邻接点
		scanf_s("%s", vex1);
		printf("邻接点:");
		scanf_s("%s", vex2);

		int x = LocateVex_AdjList(G, vex1);//vex1在顶点数组的下标
		int y = LocateVex_AdjList(G, vex2);//vex2在顶点数组的下标

		if (x == -1 || y == -1)
		{
			free(vex1);
			free(vex2);
			printf("输入顶点有误\n");
			return;
		}

		//无向图 相当于双向关系
		//因为每个顶点的第一个边都存邻接点
		//所以某边上的两个顶点 互为邻接点
		Edgenode* edgenode = (Edgenode*)malloc(sizeof(edgenode));//边结点(邻接点下标&下一个边结点的指针)
		edgenode->adjVex = x;
		edgenode->nextEdge = G->vexs[y].firstEdge;
		edgenode->weight = 0;//权重置零
		G->vexs[y].firstEdge = edgenode;

		edgenode = (Edgenode*)malloc(sizeof(edgenode));//边结点(邻接点下标&下一个边结点的指针)
		edgenode->adjVex = y;
		edgenode->nextEdge = G->vexs[x].firstEdge;
		edgenode->weight = 0;//权重置零
		G->vexs[x].firstEdge = edgenode;

		free(vex1);
		free(vex2);
	}
}

//有向图创建
void CreateDG_AdjList(AdjListGraph* G)
{
	G->kind = DG;

	printf("请输入顶点数量:");
	scanf_s("%d", &(G->vexCount));

	printf("请输入弧的数量:");
	scanf_s("%d", &(G->arcCount));

	printf("请依此输入顶点信息\n");
	for (int i = 0; i < G->vexCount; i++)
	{
		G->vexs[i].vex = (VerTexType)malloc(sizeof(char) * 10);
		printf("顶点%d:", i + 1);
		scanf_s("%s", G->vexs[i].vex);

		//初始化邻接表,把边置空
		//有邻接表(出度) 和 逆邻接表(入度)
		G->vexs[i].firstArc = NULL;//出度
		G->vexs[i].firstEdge = NULL;//入度
	}

	printf("请输入顶点和邻接点信息,构建邻接表:\n");
	for (int j = 0; j < G->arcCount; j++)
	{
		VerTexType vex1 = (VerTexType)malloc(sizeof(char) * 10);
		VerTexType vex2 = (VerTexType)malloc(sizeof(char) * 10);

		printf("顶点:");
		//vex1 & vex2 是邻接点
		scanf_s("%s", vex1);
		printf("邻接点:");
		scanf_s("%s", vex2);

		//有向网
		//{
		//	int value;//权值
		//	printf("权值");
		//	scanf("%d", &value);
		//}

		int x = LocateVex_AdjList(G, vex1);//vex1在顶点数组的下标
		int y = LocateVex_AdjList(G, vex2);//vex2在顶点数组的下标

		if (x == -1 || y == -1)
		{
			free(vex1);
			free(vex2);
			printf("输入顶点有误\n");
			return;
		}

		//生成边结点的邻接表 (出度)
		ArcNode* arcnode = (ArcNode*)malloc(sizeof(ArcNode));//边结点(邻接点下标&下一个边结点的指针)
		arcnode->adjVex = y;
		arcnode->nextArc = G->vexs[x].firstArc;
		G->vexs[x].firstArc = arcnode;

		arcnode->weight = 0;//权重置零
		//有向网:
		//arcnode->weight = value;

		//生成边结点的邻接表 (入度)
		Edgenode* edgenode = (Edgenode*)malloc(sizeof(edgenode));//边结点(邻接点下标&下一个边结点的指针)
		edgenode->adjVex = x;
		edgenode->nextEdge = G->vexs[y].firstEdge;
		G->vexs[y].firstEdge = edgenode;

		edgenode->weight = 0;//权重置零
		//有向网:
		//edgenode->weight = value;
		

		free(vex1);
		free(vex2);
	}
}

void TestAdjGraph1()
{
	AdjListGraph G;
	CreateUDG_AdjList(&G);

	for (int i = 0; i < G.vexCount; i++)
	{
		VNode vNode = G.vexs[i];//顶点数组存放的每个顶点
		printf("顶点:%s", vNode.vex);

		Edgenode* eNode = vNode.firstEdge;

		while (eNode)//打印链表信息
		{
			printf("-> %d", eNode->adjVex);
			eNode = eNode->nextEdge;
		}

		printf("\n");
	}
}

void TestAdjGraph2()
{
	AdjListGraph G;
	CreateDG_AdjList(&G);

	printf("邻接表:\n");
	for (int i = 0; i < G.vexCount; i++)
	{
		VNode vNode = G.vexs[i];//顶点数组存放的每个顶点
		printf("顶点:%s", vNode.vex);

		ArcNode* arcNode = vNode.firstArc;

		while (arcNode)//打印链表信息
		{
			printf("-> %d", arcNode->adjVex);
			arcNode = arcNode->nextArc;
		}

		printf("\n");
	}

	printf("逆邻接表:\n");
	for (int i = 0; i < G.vexCount; i++)
	{
		VNode vNode = G.vexs[i];//顶点数组存放的每个顶点
		printf("顶点:%s", vNode.vex);

		ArcNode* arcNode = vNode.firstEdge;

		while (arcNode)//打印链表信息
		{
			printf("<- %d", arcNode->adjVex);
			arcNode = arcNode->nextEdge;
		}

		printf("\n");
	}
}

int main()
{
	//TestAdjGraph1();
	TestAdjGraph2();

	return 0;
}

4.最小生成树

        (1)prim(普里姆)算法

  1. MST:(v,E)是一个连通图,那么假设U是V的一个非空子集,如果(u,v)是一条最小权值的边,那么如果u属于U,v属于V,那么必有(u,v)包含在最小生成树里
  2. Prim:找点法,我们从集合中根据边的大小找点
  3. 怎样实现prim算法:
  1. 记录当前U集合的状态
  2. 选择最小边以及顶点加入到U集合中
    #include<stdio.h>
    #include<stdlib.h>
    #include<assert.h>
    #include<stdbool.h>
    
    //图顶点之间不通,那么邻接矩阵的值为MAX
    //如果顶点是自己本身,那么值为零
    
    #define MAX 32767
    
    typedef struct Graph
    {
    	char* vexs;//顶点
    	int** arcs;//边 存边和顶点关系的二维数组
    	int vexNum;//顶点数
    	int arcNum;//边数
    }Graph;
    
    typedef struct Edge
    {
    	char vex;
    	int weight;
    }Edge;
    
    //当weight等于0 时,代表顶点已经加入到U集合中
    Edge* initEdge(Graph* G, int index)
    {
    	Edge* edge = (Edge*)malloc(sizeof(Edge) * G->vexNum);
    
    	for (int i = 0; i < G->vexNum; i++)
    	{
    		edge[i].vex = G->vexs[index];
    		edge[i].weight = G->arcs[index][i];
    	}
    
    	return edge;
    }
    
    int getMinEdge(Edge* edge, Graph* G)
    {
    	int index;
    	int min = MAX;
    
    	for (int i = 0; i < G->vexNum; i++)
    	{
    		if (edge[i].weight != 0 && min > edge[i].weight)
    		{
    			min = edge[i].weight;
    			index = i;
    		}
    	}
    
    	return index;
    }
    
    void prim(Graph* G, int index)
    {
    	int min;
    	Edge* edge = initEdge(G, index);
    
    	//因为第一个顶点已经进入edge,所以次数是G->vexNum-1次
    	for (int i = 0; i < G->vexNum - 1; i++)
    	{
    		min = getMinEdge(edge, G);
    		printf("v%c-->v%c,weight=%d\n", edge[min].vex, G->vexs[min], edge[min].weight);
    
    		edge[min].weight = 0;//标识已经进入
    
    		for (int j = 0; j < G->vexNum; j++)
    		{
    			printf("%d\n", edge[j].weight);
    			printf("%d\n", G->arcs[min][j]);
    			//更新最小边
    			if (G->arcs[min][j] < edge[j].weight)
    			{
    				edge[j].weight = G->arcs[min][j];
    				edge[j].vex = G->vexs[min];
    			}
    		}
    	}
    }
    
    //初始化
    Graph* initGraph(int vexNum)
    {
    	Graph* G = (Graph*)malloc(sizeof(Graph));
    
    	G->vexs = (char*)malloc(sizeof(char) * vexNum);
    	G->arcs = (int**)malloc(sizeof(int*) * vexNum);
    
    	for (int i = 0; i < vexNum; i++)
    	{
    		G->arcs[i] = (int*)malloc(sizeof(int) * vexNum);//二维数组的每行一维数组的首地址
    	}
    	G->vexNum = vexNum;
    	G->arcNum = 0;
    
    	return G;
    }
    
    //无向图 邻接矩阵法 创建
    void CreateGraph(Graph* G, char* vexs, int* arcs)
    {
    	for (int i = 0; i < G->vexNum; i++)
    	{
    		G->vexs[i] = vexs[i];
    
    		for (int j = 0; j < G->vexNum; j++)
    		{
    			G->arcs[i][j] = *(arcs + i * G->vexNum + j);//G->arcs[i]是二维数组每行一维数组的首地址,可以理解为每行一维数组的数组名,再加[j]就是每行一维数组的第J个元素的元素值
    
    			if (G->arcs[i][j] != 0 && G->arcs[i][j] != MAX)
    			{
    				G->arcNum++;
    			}
    		}
    	}
    
    	G->arcNum /= 2;//无向图
    	//有向图则不用除2
    }
    
    
    
    
    //DFS 深度优先遍历
    //1.找到一个节点访问
    //2.找这个节点可以访问的节点
    //3.重复第一步,直到所有节点访问完毕
    void DFS(Graph* G, int* visited, int index)//visited标识是否遍历过 index索引,从哪个顶点开始遍历
    {
    	printf("%c\t", G->vexs[index]);
    	visited[index] = 1;
    
    	for (int i = 0; i < G->vexNum; i++)
    	{
    		if (G->arcs[index][i] > 0 && G->arcs[index][i] < MAX && !visited[i])
    		{
    			DFS(G, visited, i);
    		}
    	}
    }
    
    int main()
    {
    	Graph* G = initGraph(6);
    	//网(带权)时,表中存放权值,而不是1
    	int arcs[6][6] = {
    		0,6,1,5,MAX,MAX,
    		6,0,5,MAX,3,MAX,
    		1,5,0,5,6,4,
    		5,MAX,5,0,MAX,2,
    		MAX,3,6,MAX,0,6,
    		MAX,MAX,4,2,6,0
    	};
    
    	CreateGraph(G, "123456", arcs);
    
    	int* visited = (int*)malloc(sizeof(int) * G->vexNum);
    	for (int i = 0; i < G->vexNum; i++)
    	{
    		visited[i] = 0;
    	}
    	DFS(G, visited, 0);
    	printf("\n");
    
    	prim(G, 0);
    
    	return 0;
    }

        (2)kruskal(克鲁斯卡尔)算法

  1. 克鲁斯卡尔找边法

对边排序

选边

  1. 怎样实现:
  1. 维护一个边的数组并排序
  2. 判断图是否连通?

需要一个辅助的数组,来记录当前索引顶点属于哪个连通分量

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

#define MAX 32767

typedef struct Graph
{
	char* vexs;//顶点
	int** arcs;//边 存边和顶点关系的二维数组
	int vexNum;//顶点数
	int arcNum;//边数
}Graph;

typedef struct Edge
{
	int strat;  //起点索引
	int end;	//终点索引
	int weight;
}Edge;

Edge* initEdge(Graph* G)
{
	int index = 0;
	Edge* edge = (Edge*)malloc(sizeof(Edge) * G->arcNum);

	for (int i = 0; i < G->vexNum; i++)
	{
		for (int j = i + 1; j < G->vexNum; j++)
		{
			if (G->arcs[i][j] != MAX)
			{
				edge[index].strat = i;
				edge[index].end = j;

				edge[index].weight = G->arcs[i][j];

				index++;
			}
		}
	}

	return edge;
}

//排序
void sortEdge(Edge* edge, Graph* G)
{
	Edge temp;
	for (int i = 0; i < G->arcNum; i++)
	{
		for (int j = 0; j < G->arcNum - i - 1; j++)
		{
			if (edge[j].weight > edge[j + 1].weight)
			{
				temp = edge[j];
				edge[j] = edge[j + 1];
				edge[j + 1] = temp;
			}
		}
	}
}

void kruskal(Graph* G)
{
	//记录当前索引顶点属于哪个连通分量
	int* connected = (int*)malloc(sizeof(int) * G->vexNum);
	for (int i = 0; i < G->vexNum; i++)
	{
		connected[i] = i;
	}

	Edge* edge = initEdge(G);
	sortEdge(edge, G);

	for (int i = 0; i < G->arcNum; i++)
	{
		int start = connected[edge[i].strat];
		int end = connected[edge[i].end];

		if (start != end)
		{
			printf("v%c-->v%c  weight = %d\n", G->vexs[edge[i].strat], G->vexs[edge[i].end], edge[i].weight);

			for (int j = 0; j < G->vexNum; j++)
			{
				if (connected[j] == end)
				{
					connected[j] = start;
				}
			}
		}
	}
}


//初始化
Graph* initGraph(int vexNum)
{
	Graph* G = (Graph*)malloc(sizeof(Graph));

	G->vexs = (char*)malloc(sizeof(char) * vexNum);
	G->arcs = (int**)malloc(sizeof(int*) * vexNum);

	for (int i = 0; i < vexNum; i++)
	{
		G->arcs[i] = (int*)malloc(sizeof(int) * vexNum);//二维数组的每行一维数组的首地址
	}
	G->vexNum = vexNum;
	G->arcNum = 0;

	return G;
}

//无向图 邻接矩阵法 创建
void CreateGraph(Graph* G, char* vexs, int* arcs)
{
	for (int i = 0; i < G->vexNum; i++)
	{
		G->vexs[i] = vexs[i];

		for (int j = 0; j < G->vexNum; j++)
		{
			G->arcs[i][j] = *(arcs + i * G->vexNum + j);//G->arcs[i]是二维数组每行一维数组的首地址,可以理解为每行一维数组的数组名,再加[j]就是每行一维数组的第J个元素的元素值

			if (G->arcs[i][j] != 0 && G->arcs[i][j] != MAX)
			{
				G->arcNum++;
			}
		}
	}

	G->arcNum /= 2;//无向图
	//有向图则不用除2
}

//DFS 深度优先遍历
//1.找到一个节点访问
//2.找这个节点可以访问的节点
//3.重复第一步,直到所有节点访问完毕
void DFS(Graph* G, int* visited, int index)//visited标识是否遍历过 index索引,从哪个顶点开始遍历
{
	printf("%c\t", G->vexs[index]);
	visited[index] = 1;

	for (int i = 0; i < G->vexNum; i++)
	{
		if (G->arcs[index][i] > 0 && G->arcs[index][i]<MAX && !visited[i])
		{
			DFS(G, visited, i);
		}
	}
}

int main()
{
	Graph* G = initGraph(6);
	//网(带权)时,表中存放权值,而不是1
	int arcs[6][6] = {
		0,6,1,5,MAX,MAX,
		6,0,5,MAX,3,MAX,
		1,5,0,5,6,4,
		5,MAX,5,0,MAX,2,
		MAX,3,6,MAX,0,6,
		MAX,MAX,4,2,6,0
	};

	CreateGraph(G, "123456", arcs);

	int* visited = (int*)malloc(sizeof(int) * G->vexNum);
	for (int i = 0; i < G->vexNum; i++)
	{
		visited[i] = 0;
	}
	DFS(G, visited, 0);
	printf("\n");

	kruskal(G);
	
	return 0;
}

5.最短路径

        (1)dijkstra(迪杰斯特拉)算法

  1. S数组:记录了目标顶点到其他顶点的最短路径是否求得

        P数组:记录了目标定点到其他顶点的最短路径的前驱结点

        D数组:记录了目标顶点到其他顶点的最短路径的长度

 

  1. 初始化

X:V1

V1 V2 V3 V4 V5 V6 V7

  1. 1  2  3  4  5  6

S    1  0  0  0  0  0  0

P   -1  0 -1 -1  -1 0  0

D    0 12  oo oo oo 16 14

  1. Dijkstra算法:每次从D数组中选取一条最短的边,把这条边的终点加到X集合当中,此时存在中转点,看通过中转点达到终点的距离是否小于当前的最小距离

如果小于,则选择中转路线

X:V1  V2

V1 V2 V3 V4 V5 V6 V7

0  1  2  3  4  5  6

S    1  1  0  0  0  0  0

P   -1  0  1 -1 -1  0  0

D    0 12 22 oo oo 16 14

X:V1  V2  V7

V1 V2 V3 V4 V5 V6 V7

0  1  2  3  4  5  6

S    1  1  0  0  0  0  1

P   -1  0  1 -1  6  0  0

D    0 12 22 oo 22 16 14

X:V1  V2  V7  V6

V1 V2 V3 V4 V5 V6 V7

0  1  2  3  4  5  6

S    1  1  0  0  0  1  1

P   -1  0  1 -1  5  0  0

D    0 12 22 oo 18 16 14

X:V1  V2  V7  V6  V5

V1 V2 V3 V4 V5 V6 V7

0  1  2  3  4  5  6

S    1  1  0  0  1  1  1

P   -1  0  1  4  5  0  0

D    0 12 22 22 18 16 14

X:V1  V2  V7  V6  V5  V3

V1 V2 V3 V4 V5 V6 V7

0  1  2  3  4  5  6

S    1  1  1  0  1  1  1

P   -1  0  1  4  5  0  0

D    0 12 22 22 18 16 14

完成

X:V1  V2  V7  V6  V5  V3  V4

V1 V2 V3 V4 V5 V6 V7

0  1  2  3  4  5  6

S    1  1  1  1  1  1  1

P   -1  0  1  4  5  0  0

D    0 12 22 22 18 16 14

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

#define MAX 32767

typedef struct Graph
{
	char* vexs;//顶点
	int** arcs;//边 存边和顶点关系的二维数组
	int vexNum;//顶点数
	int arcNum;//边数
}Graph;

//初始化
Graph* initGraph(int vexNum)
{
	Graph* G = (Graph*)malloc(sizeof(Graph));

	G->vexs = (char*)malloc(sizeof(char) * vexNum);
	G->arcs = (int**)malloc(sizeof(int*) * vexNum);

	for (int i = 0; i < vexNum; i++)
	{
		G->arcs[i] = (int*)malloc(sizeof(int) * vexNum);//二维数组的每行一维数组的首地址
	}
	G->vexNum = vexNum;
	G->arcNum = 0;

	return G;
}

//无向图 邻接矩阵法 创建
void CreateGraph(Graph* G, char* vexs, int* arcs)
{
	for (int i = 0; i < G->vexNum; i++)
	{
		G->vexs[i] = vexs[i];

		for (int j = 0; j < G->vexNum; j++)
		{
			G->arcs[i][j] = *(arcs + i * G->vexNum + j);//G->arcs[i]是二维数组每行一维数组的首地址,可以理解为每行一维数组的数组名,再加[j]就是每行一维数组的第J个元素的元素值

			if (G->arcs[i][j] != 0 && G->arcs[i][j] < MAX)
			{
				G->arcNum++;
			}
		}
	}

	G->arcNum /= 2;//无向图
	//有向图则不用除2
}

//DFS 深度优先遍历
//1.找到一个节点访问
//2.找这个节点可以访问的节点
//3.重复第一步,直到所有节点访问完毕
void DFS(Graph* G, int* visited, int index)//visited标识是否遍历过 index索引,从哪个顶点开始遍历
{
	printf("%c\t", G->vexs[index]);
	visited[index] = 1;

	for (int i = 0; i < G->vexNum; i++)
	{
		if (G->arcs[index][i] > 1 && G->arcs[index][i] < MAX && !visited[i])
		{
			DFS(G, visited, i);
		}
	}
}

//dijkstra算法实现

int getmin(int* d, int* s, Graph* G)
{
	int min = MAX;
	int index;

	for (int i = 0; i < G->vexNum; i++)
	{
		if (!s[i] && d[i] < min)//最短路径未求得时 找最小
		{
			min = d[i];
			index = i;
		}
	}

	return index;
}

void dijkstra(Graph* G, int index)
{
	int* s = (int*)malloc(sizeof(int) * G->vexNum);//记录了目标顶点到其他顶点的最短路径是否求得
	int* p = (int*)malloc(sizeof(int) * G->vexNum);//记录了目标定点到其他顶点的最短路径的前驱结点
	int* d = (int*)malloc(sizeof(int) * G->vexNum);//记录了目标顶点到其他顶点的最短路径的长度

	//初始化
	for (int i = 0; i < G->vexNum; i++)
	{
		if (G->arcs[index][i] > 0 && G->arcs[index][i] < MAX)//顶点与目标顶点间有边
		{
			d[i] = G->arcs[index][i];
			p[i] = index;
		}
		else
		{
			d[i] = MAX;
			p[i] = -1;
		}
		if (i == index)
		{
			s[i] = 1;
			d[i] = 0;
		}
		else
		{
			s[i] = 0;
		}
	}
	//for (int i = 0; i < G->vexNum; i++)
	//{
	//	if (i == index)
	//	{
	//		s[i] = 1;
	//	}
	//	else
	//	{
	//		s[i] = 0;
	//	}
	//}
	//for (int i = 0; i < G->vexNum; i++)
	//{
	//	if (G->arcs[index][i] > 0 && G->arcs[index][i] < MAX)//顶点与目标顶点间有边
	//	{
	//		p[i] = index;
	//	}
	//	else
	//	{
	//		p[i] = -1;
	//	}
	//}
	//for (int i = 0; i < G->vexNum; i++)
	//{
	//	if (G->arcs[index][i] > 0 && G->arcs[index][i] < MAX)//顶点与目标顶点间有边
	//	{
	//		d[i] = G->arcs[index][i];
	//	}
	//	else
	//	{
	//		d[i] = MAX;
	//	}

	//	if (i == index)
	//	{
	//		d[i] = 0;//自己到自己的距离为0
	//	}
	//}

	for (int i = 0; i < G->vexNum - 1; i++)
	{
		int index1 = getmin(d, s, G);
		s[index1] = 1;//它的最短路径已经找到,将其作为中转点
		
		for (int j = 0; j < G->vexNum; j++)
		{
			if (!s[j] && d[index1] + G->arcs[index1][j] < d[j])
			{
				d[j] = d[index1] + G->arcs[index1][j];
				p[j] = index1;
			}
		}

	}
	for (int i = 0; i < G->vexNum; i++)
	{
		printf("%d %d %d\n", s[i], p[i], d[i]);
	}
}

int main()
{
	Graph* G = initGraph(7);
	//网(带权)时,表中存放权值,而不是1
	int arcs[7][7] = {
		0,12,MAX,MAX,MAX,16,14,
		12,0,10,MAX,MAX,7,MAX,
		MAX,10,0,3,5,6,MAX,
		MAX,MAX,3,0,4,MAX,MAX,
		MAX,MAX,5,4,0,2,8,
		16,7,6,MAX,2,0,9,
		14,MAX,MAX,MAX,8,9,0
	};

	CreateGraph(G, "1234567", (int*)arcs);

	int* visited = (int*)malloc(sizeof(int) * G->vexNum);
	for (int i = 0; i < G->vexNum; i++)
	{
		visited[i] = 0;
	}
	DFS(G, visited, 0);
	printf("\n");

	dijkstra(G, 0);

	return 0;
}

        (2)flold(弗洛伊德)算法

求每一对顶点间的最短距离

  1. d数组:保存了两点的最短路径长度

p数组:保存了两点之前最短路径的前驱

  1. 核心:试探法,通过加入不同的点进行中转,选择出最优解
  2. 模拟floyd算法的过程
  3. d数组

    V1

    V2

    V3

    V4

    0

    V1

    0

    1

    MAX

    3

    1

    V2

    1

    0

    2

    2

    2

    V3

    MAX

    2

    0

    8

    3

    V4

    3

    2

    8

    0

    p数组

    V1

    V2

    V3

    V4

    0

    V1

    -1

    0

    -1

    0

    1

    V2

    1

    -1

    1

    1

    2

    V3

    -1

    2

    -1

    2

    3

    V4

    3

    3

    3

    -1

    V1加入,以V1为起点和V1为终点的边不需要考虑

    对角线也不需要考虑

    d数组

    V1

    V2

    V3

    V4

    0

    V1

    0

    1

    MAX

    3

    1

    V2

    1

    0

    2

    2

    2

    V3

    MAX

    2

    0

    8

    3

    V4

    3

    2

    8

    0

    看V2-V1-V3

    V2-V1-V4 : 4>2不需要改

    V3-V1-V2: MAX>2 不需要改

    V3-V1-V4: MAX>8 不需要改

    V4-V1-V2: 4>2不需要改

    V4-V1-V3  MAX>8 不需要改

    p数组

    V1

    V2

    V3

    V4

    0

    V1

    -1

    0

    -1

    0

    1

    V2

    1

    -1

    1

    1

    2

    V3

    -1

    2

    -1

    2

    3

    V4

    3

    3

    3

    -1

    P数组没有发生改变

    V2加入 以V2为起点和V2为终点的边不需要考虑

    对角线也不需要考虑

    d数组

    V1

    V2

    V3

    V4

    0

    V1

    0

    1

    3

    3

    1

    V2

    1

    0

    2

    2

    2

    V3

    3

    2

    0

    4

    3

    V4

    3

    2

    4

    0

    V1-V2-V3: 3<MAX 改变,p数组也需要改变

    V1-V2-V4: 3=3 不需要改变

    V3-V2-V1: 3<MAX 改变,p数组也需要改变

    V3-V2-V4: 4<8 改变,p数组也需要改变

    V4-V2-V1: 3=3 不需要改变

    V4-V2-V3: 4<8 改变,p数组也需要改变

    p数组

    V1

    V2

    V3

    V4

    0

    V1

    -1

    0

    1

    0

    1

    V2

    1

    -1

    1

    1

    2

    V3

    1

    2

    -1

    1

    3

    V4

    3

    3

    1

    0

    V3加入 以V3为起点和V3为终点的边不需要考虑

    对角线也不需要考虑

    d数组

    V1

    V2

    V3

    V4

    0

    V1

    0

    1

    3

    3

    1

    V2

    1

    0

    2

    2

    2

    V3

    3

    2

    0

    4

    3

    V4

    3

    2

    4

    0

    V1-V3-V2: MAX>1 不需要改变

    V1-V3-V4: MAX>1 不需要改变

    V2-V3-V1: MAX>1 不需要改变

    V2-V3-V4: 10>2  不需要改变

    V4-V3-V1: MAX>3 不需要改变

    V4-V3-V2: 10>2  不需要改变

    p数组

    V1

    V2

    V3

    V4

    0

    V1

    -1

    0

    1

    0

    1

    V2

    1

    -1

    1

    1

    2

    V3

    1

    2

    -1

    1

    3

    V4

    3

    3

    1

    0

    P数组不需要改变

    V4加入 以V4为起点和V4为终点的边不需要考虑

    对角线也不需要考虑

    d数组

    V1

    V2

    V3

    V4

    0

    V1

    0

    1

    3

    3

    1

    V2

    1

    0

    2

    2

    2

    V3

    3

    2

    0

    4

    3

    V4

    3

    2

    4

    0

    V1-V4-V2: 5>1  不需要改变

    V1-V4-V3: 11>3 不需要改变

    V2-V4-V1: 5>1  不需要改变

    V2-V4-V3: 10>2 不需要改变

    V3-V4-V1: 11>3 不需要改变

    V3-V4-V2: 10>2 不需要改变

    p数组

    V1

    V2

    V3

    V4

    0

    V1

    -1

    0

    1

    0

    1

    V2

    1

    -1

    1

    1

    2

    V3

    1

    2

    -1

    1

    3

    V4

    3

    3

    1

    0

    P数组不用改变

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

#define MAX 32767

typedef struct Graph
{
	char* vexs;//顶点
	int** arcs;//边 存边和顶点关系的二维数组
	int vexNum;//顶点数
	int arcNum;//边数
}Graph;

//初始化
Graph* initGraph(int vexNum)
{
	Graph* G = (Graph*)malloc(sizeof(Graph));

	G->vexs = (char*)malloc(sizeof(char) * vexNum);
	G->arcs = (int**)malloc(sizeof(int*) * vexNum);

	for (int i = 0; i < vexNum; i++)
	{
		G->arcs[i] = (int*)malloc(sizeof(int) * vexNum);//二维数组的每行一维数组的首地址
	}
	G->vexNum = vexNum;
	G->arcNum = 0;

	return G;
}

//无向图 邻接矩阵法 创建
void CreateGraph(Graph* G, char* vexs, int* arcs)
{
	for (int i = 0; i < G->vexNum; i++)
	{
		G->vexs[i] = vexs[i];

		for (int j = 0; j < G->vexNum; j++)
		{
			G->arcs[i][j] = *(arcs + i * G->vexNum + j);//G->arcs[i]是二维数组每行一维数组的首地址,可以理解为每行一维数组的数组名,再加[j]就是每行一维数组的第J个元素的元素值

			if (G->arcs[i][j] != 0 && G->arcs[i][j] > MAX)
			{
				G->arcNum++;
			}
		}
	}

	G->arcNum /= 2;//无向图
	//有向图则不用除2
}

//图的遍历
//DFS 深度优先遍历
//1.找到一个节点访问
//2.找这个节点可以访问的节点
//3.重复第一步,直到所有节点访问完毕
void DFS(Graph* G, int* visited, int index)//visited标识是否遍历过 index索引,从哪个顶点开始遍历
{
	printf("%c\t", G->vexs[index]);
	visited[index] = 1;

	for (int i = 0; i < G->vexNum; i++)
	{
		if (G->arcs[index][i] > 0 && G->arcs[index][i] < MAX && !visited[i])
		{
			DFS(G, visited, i);
		}
	}
}

//实现floyd算法
void floyd(Graph* G)
{
	int d[G->vexNum][G->vexNum];//保存了两点的最短路径长度
	int p[G->vexNum][G->vexNum];//保存了两点之前最短路径的前驱
	
	//初始化 
	for(int i = 0; i < G->vexNum; i++)
	{
		for(int j = 0; j < G->vexNum; j++)
		{
			d[i][j] = G->arcs[i][j];
			
			if(G->arcs[i][j] > 0 && G->arcs[i][j] < MAX)//i j间有边 && 自己和自己没边 
			{
				p[i][j] = i;
			}
			else
			{
				p[i][j] = -1;
			}
		}
	} 
	
	for(int i = 0; i < G->vexNum; i++)
	{
		for(int j = 0; j < G->vexNum; j++)
		{
			for(int k = 0; k < G->vexNum; k++)
			{
				if(d[j][i]+d[i][k] < d[j][k])//i为中转点的下标 
				{
					d[j][k] = d[j][i]+d[i][k];
					p[j][k] = i;//p[j][k]=p[i][k]?相等 不理解 
				}
			}
		}
	}
	
	for(int i = 0; i < G->vexNum; i++)
	{
		for(int j = 0; j < G->vexNum; j++)
		{
			printf("%d ",d[i][j]);
		}
		printf("\n");
	} 
	
	printf("\n");
	
	for(int i = 0; i < G->vexNum; i++)
	{
		for(int j = 0; j < G->vexNum; j++)
		{
			printf("%d ",p[i][j]);
		}
		printf("\n");
	} 
}

int main()
{
	Graph* G = initGraph(4);
	//网(带权)时,表中存放权值,而不是1
	int arcs[4][4] = {
		0,1,MAX,3,
		1,0,2,2,
		MAX,2,0,8,
		3,2,8,0
	};

	CreateGraph(G, "1234", (int*)arcs);

	int* visited = (int*)malloc(sizeof(int) * G->vexNum);
	for (int i = 0; i < G->vexNum; i++)
	{
		visited[i] = 0;
	}
	DFS(G, visited, 0);
	printf("\n");

	floyd(G);

	return 0;
}

 6.拓扑排序

  1. AOV—网
  1. 图的顶点表示任务
  2. 图的弧表示先后依赖关系
  1. 拓扑排序概念:拓扑排序就是将AOV中的顶点排成一个线性序列,如果vi->vj有弧的话,那么vi 必然在vj前面
  2. 拓扑排序用来判断图中是否有环,看排序序列中是否包含图中所有顶点,若排序序列中顶点个数小于图中顶点个数,则说明有环
  3. 拓扑排序流程
  1. 找出没有前驱的点,输出它,剪掉以它为出发点的所有边
  2. 重复第一步,直到图中不存在没有前驱的点
  3.        

     

  4. 拓扑序列:V6  V1  V4  V3  V5  V2

    出度

    1

    2

    3

    4

    5

    6

    1

    0

    1

    1

    1

    0

    0

    2

    0

    0

    0

    0

    0

    0

    3

    0

    1

    0

    0

    1

    0

    4

    0

    0

    0

    0

    1

    0

    5

    0

    0

    0

    0

    0

    0

    6

    0

    0

    0

    1

    1

    0

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef struct Graph
{
	char* vexs;//顶点
	int** arcs;//边 存边和顶点关系的二维数组
	int vexNum;//顶点数
	int arcNum;//边数
}Graph;

//创建一个栈
typedef struct Node
{
	int data;
	struct Node* next;
}Node;

//栈的初始化
//带头节点
Node* initStack()
{
	Node* stack = (Node*)malloc(sizeof(Node));

	stack->data = 0;
	stack->next = NULL;

	return stack;
}

//入栈
void push(Node* stack, int data)
{
	Node* node = (Node*)malloc(sizeof(Node));
	node->data = data;
	//头插
	node->next = stack->next;
	stack->next = node;
	stack->data++;
}

bool isEmpty(Node* stack)
{
	return stack->next == NULL;
}

//出栈
int pop(Node* stack)
{
	if (!isEmpty(stack))
	{
		Node* node = stack->next;
		//free(stack->next);? 为什么free就报错
		stack->next = node->next; 
		stack->data--;

		return node->data;
	}
	else
	{
		return -1;
	}
}

//得到图中各个顶点的入度
int* findInDegrees(Graph* G)
{
	int* inDegrees = (int*)malloc(sizeof(int) * G->vexNum);
	//初始化
	for (int i = 0; i < G->vexNum; i++)
	{
		inDegrees[i] = 0;
	}

	//计算入度 存入inDegrees数组中
	for (int i = 0; i < G->vexNum; i++)
	{
		for (int j = 0; j < G->vexNum; j++)
		{
			if (G->arcs[i][j])
			{
				inDegrees[j] = inDegrees[j] + 1;
			}
		}
	}
	return inDegrees;
}

//拓扑排序实现
void topologicalSort(Graph* G)
{
	//得到图中每个顶点的入度
	int* inDegrees = findInDegrees(G);
	
	//保存最后拓扑排序的序列
	int* top = (int*)malloc(sizeof(int) * G->vexNum);
	int index = 0;

	Node* stack = initStack();//建栈  栈中存储的为整个过程中 入度为0的顶点

	for (int i = 0; i < G->vexNum; i++)
	{
		//入度为0,没有前驱
		if (inDegrees[i] == 0)
		{
			push(stack, i);//入栈
		}
	}

	while (!isEmpty(stack))
	{
		int vex = pop(stack);//出栈
		top[index++] = vex;

		for (int i = 0; i < G->vexNum; i++)
		{
			//删除 此时出栈节点(入度为0,没有前驱) 的所有弧
			if (G->arcs[vex][i])
			{
				inDegrees[i] = inDegrees[i] - 1;//删除弧就是,尾顶点的入度减1

				if (inDegrees[i] == 0)//如果此点 的入度变为0 入栈
				{
					push(stack, i);
				}
			}
		}
	}
	for (int i = 0; i < index; i++)
	{
		printf("%c ", G->vexs[top[i]]);
	}
	printf("\n");
}

//初始化
Graph* initGraph(int vexNum)
{
	Graph* G = (Graph*)malloc(sizeof(Graph));

	G->vexs = (char*)malloc(sizeof(char) * vexNum);
	G->arcs = (int**)malloc(sizeof(int*) * vexNum);

	for (int i = 0; i < vexNum; i++)
	{
		G->arcs[i] = (int*)malloc(sizeof(int) * vexNum);//二维数组的每行一维数组的首地址
	}
	G->vexNum = vexNum;
	G->arcNum = 0;

	return G;
}

//无向图 邻接矩阵法 创建
void CreateGraph(Graph* G, char* vexs, int* arcs)
{
	for (int i = 0; i < G->vexNum; i++)
	{
		G->vexs[i] = vexs[i];

		for (int j = 0; j < G->vexNum; j++)
		{
			G->arcs[i][j] = *(arcs + i * G->vexNum + j);//G->arcs[i]是二维数组每行一维数组的首地址,可以理解为每行一维数组的数组名,再加[j]就是每行一维数组的第J个元素的元素值

			if (G->arcs[i][j] != 0)
			{
				G->arcNum++;
			}
		}
	}

	G->arcNum /= 2;//无向图
	//有向图则不用除2
}


//图的遍历
//DFS 深度优先遍历
//1.找到一个节点访问
//2.找这个节点可以访问的节点
//3.重复第一步,直到所有节点访问完毕
void DFS(Graph* G, int* visited, int index)//visited标识是否遍历过 index索引,从哪个顶点开始遍历
{
	printf("%c\t", G->vexs[index]);
	visited[index] = 1;

	for (int i = 0; i < G->vexNum; i++)
	{
		if (G->arcs[index][i] == 1 && !visited[i])
		{
			DFS(G, visited, i);
		}
	}
}

int main()
{
	Graph* G = initGraph(6);
	//网(带权)时,表中存放权值,而不是1
	int arcs[6][6] = {
		0,1,1,1,0,0,
		0,0,0,0,0,0,
		0,1,0,0,1,0,
		0,0,0,0,1,0,
		0,0,0,0,0,0,
		0,0,0,1,1,0
	};

	CreateGraph(G, "123456", (int*)arcs);

	int* visited = (int*)malloc(sizeof(int) * G->vexNum);
	for (int i = 0; i < G->vexNum; i++)
	{
		visited[i] = 0;
	}
	DFS(G, visited, 0);
	printf("\n");

	topologicalSort(G);

	return 0;
}

 7.关键路径

  1. AOE网:

事件-》顶点  有向无环图

活动-》弧

  1. 如何实现
  1. 拓扑排序
  2. 计算指标
    • 事件指标

        最早开始时间 Vi(early) 前推后

             X是i的前驱

             Vi(early)=MAX{Vx(e)+weight(x,i),x<i}

        最晚开始时间 Vi(late) 后推前

             X是i的后继

               Vi(late)=Min{Vx(l)-weight(i,x),x>i}

    • 活动指标

        最早开始时间 Li(early)

             Li(e)=Vstrat(e)

        最晚开始时间 Li(late)

             Li(late)=Vend(late)-weight

        L(l)-L(e)时间余量=0 时 为关键活动

 

时间指标

拓扑排序

early

Late

V0

0

0

V1

6

6

V2

4

6

V3

5

8

V4

7

7

V5

7

10

V6

16

16

V7

14

14

V8

18

18

活动指标

early

Late

A1

0

0

A2

0

2

A3

0

3

A4

6

6

A5

4

6

A6

5

2

A7

7

7

A8

7

7

A9

7

10

A10

16

16

A11

14

14

 

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

#define MAX 65535

typedef struct Graph
{
	char* vexs;//顶点
	int** arcs;//边 存边和顶点关系的二维数组
	int vexNum;//顶点数
	int arcNum;//边数
}Graph;

//创建一个栈
typedef struct Node
{
	int data;
	struct Node* next;
}Node;

//栈的初始化
//带头节点
Node* initStack()
{
	Node* stack = (Node*)malloc(sizeof(Node));

	stack->data = 0;
	stack->next = NULL;

	return stack;
}

//入栈
void push(Node* stack, int data)
{
	Node* node = (Node*)malloc(sizeof(Node));
	node->data = data;
	//头插
	node->next = stack->next;
	stack->next = node;
	stack->data++;
}

bool isEmpty(Node* stack)
{
	return stack->next == NULL;
}

//出栈
int pop(Node* stack)
{
	if (!isEmpty(stack))
	{
		Node* node = stack->next;
		//free(stack->next);? 为什么free就报错
		stack->next = node->next;
		stack->data--;

		return node->data;
	}
	else
	{
		return -1;
	}
}

//得到图中各个顶点的入度
int* findInDegrees(Graph* G)
{
	int* inDegrees = (int*)malloc(sizeof(int) * G->vexNum);
	//初始化
	for (int i = 0; i < G->vexNum; i++)
	{
		inDegrees[i] = 0;
	}

	//计算入度 存入inDegrees数组中
	for (int i = 0; i < G->vexNum; i++)
	{
		for (int j = 0; j < G->vexNum; j++)
		{
			if (G->arcs[i][j] > 0 && G->arcs[i][j] < MAX)
			{
				inDegrees[j] = inDegrees[j] + 1;
			}
		}
	}
	return inDegrees;
}

//拓扑排序实现
int* topologicalSort(Graph* G)
{
	//得到图中每个顶点的入度
	int* inDegrees = findInDegrees(G);

	//保存最后拓扑排序的序列
	int* top = (int*)malloc(sizeof(int) * G->vexNum);
	int index = 0;

	Node* stack = initStack();//建栈  栈中存储的为整个过程中 入度为0的顶点

	for (int i = 0; i < G->vexNum; i++)
	{
		//入度为0,没有前驱
		if (inDegrees[i] == 0)
		{
			push(stack, i);//入栈
		}
	}

	while (!isEmpty(stack))
	{
		int vex = pop(stack);//出栈
		top[index++] = vex;

		for (int i = 0; i < G->vexNum; i++)
		{
			//删除 此时出栈节点(入度为0,没有前驱) 的所有弧
			if (G->arcs[vex][i] > 0 && G->arcs[vex][i] < MAX)
			{
				inDegrees[i] = inDegrees[i] - 1;//删除弧就是,尾顶点的入度减1

				if (inDegrees[i] == 0)//如果此点 的入度变为0 入栈
				{
					push(stack, i);
				}
			}
		}
	}
	for (int i = 0; i < index; i++)
	{
		printf("%c ", G->vexs[top[i]]);
	}
	printf("\n");

	return top;
}

//初始化
Graph* initGraph(int vexNum)
{
	Graph* G = (Graph*)malloc(sizeof(Graph));

	G->vexs = (char*)malloc(sizeof(char) * vexNum);
	G->arcs = (int**)malloc(sizeof(int*) * vexNum);

	for (int i = 0; i < vexNum; i++)
	{
		G->arcs[i] = (int*)malloc(sizeof(int) * vexNum);//二维数组的每行一维数组的首地址
	}
	G->vexNum = vexNum;
	G->arcNum = 0;

	return G;
}

//无向图 邻接矩阵法 创建
void CreateGraph(Graph* G, char* vexs, int* arcs)
{
	for (int i = 0; i < G->vexNum; i++)
	{
		G->vexs[i] = vexs[i];

		for (int j = 0; j < G->vexNum; j++)
		{
			G->arcs[i][j] = *(arcs + i * G->vexNum + j);//G->arcs[i]是二维数组每行一维数组的首地址,可以理解为每行一维数组的数组名,再加[j]就是每行一维数组的第J个元素的元素值

			if (G->arcs[i][j] > 0 && G->arcs[i][j] < MAX)
			{
				G->arcNum++;
			}
		}
	}
	//有向图则不用除2
}


//图的遍历
//DFS 深度优先遍历
//1.找到一个节点访问
//2.找这个节点可以访问的节点
//3.重复第一步,直到所有节点访问完毕
void DFS(Graph* G, int* visited, int index)//visited标识是否遍历过 index索引,从哪个顶点开始遍历
{
	printf("%c ", G->vexs[index]);
	visited[index] = 1;

	for (int i = 0; i < G->vexNum; i++)
	{
		if (G->arcs[index][i] > 0 && G->arcs[index][i] < MAX && !visited[i])
		{
			DFS(G, visited, i);
		}
	}
}


//找到顶点在拓扑排序中的序号
int getIndex(int* top, Graph* G, int i)
{
	int j;
	for (j = 0; j < G->vexNum; j++)
	{
		if (top[j] == i)
		{
			break;
		}
	}
	return j;
}
//关键路径实现
void critialPath(Graph* G)
{
	int* top = topologicalSort(G);
	int* early = (int*)malloc(sizeof(int) * G->vexNum);//时间指标——最早发生时间
	int* late = (int*)malloc(sizeof(int) * G->vexNum);//时间指标——最晚发生时间

	//初始化
	for (int i = 0; i < G->vexNum; i++)
	{
		early[i] = 0;
		late[i] = 0;
	}

	//计算最早发生时间
	for (int i = 0; i < G->vexNum; i++)
	{
		int max = 0;
		for (int j = 0; j < G->vexNum; j++)
		{
			//j到(拓扑序列中i对应的序号顶点)有弧
			if (G->arcs[j][top[i]]> 0 && G->arcs[j][top[i]] < MAX)
			{
				int index = getIndex(top, G, j);//j在拓扑序列中的序号
				if (early[index] + G->arcs[j][top[i]] > max)
				{
					max = early[index] + G->arcs[j][top[i]];
				}
			}
		}
		early[i] = max;
	}

	for (int i = 0; i < G->vexNum; i++)
	{
		printf("%d ", early[i]);
	}
	printf("\n");

	//最后一位的late的值 等于 early的值
	late[(G->vexNum) - 1] = early[(G->vexNum) - 1];

	//计算最晚发生时间
	for (int i = G->vexNum - 2; i >= 0; i--)
	{
		int min = MAX;

		for (int j = 0; j < G->vexNum; j++)
		{
			//(拓扑序列中i对应的序号顶点)到j有弧
			if (G->arcs[top[i]][j] > 0 && G->arcs[top[i]][j] < MAX)
			{
				int index = getIndex(top, G, j);
				if (late[index] - G->arcs[top[i]][j] < min)
				{
					min = late[index] - G->arcs[top[i]][j];
				}
			}
		}
		late[i] = min;
	}

	for (int i = 0; i < G->vexNum; i++)
	{
		printf("%d ", late[i]);
	}
	printf("\n");

	//关键路径
	for (int i = 0; i < G->vexNum; i++)
	{
		for (int j = 0; j < G->vexNum; j++)
		{
			if (G->arcs[i][j] > 0 && G->arcs[i][j] < MAX)
			{
				//转换为拓扑排序的序号
				int start = getIndex(top, G, i);
				int end = getIndex(top, G, j);

				if (late[end] - G->arcs[i][j] == early[start])
				{
					printf("start = %d   end = %d\n", i, j);
				}
			}
		}
	}
}

int main()
{
	Graph* G = initGraph(9);
	//网(带权)时,表中存放权值,而不是1
	int arcs[9][9] = {
		0,6,4,5,MAX,MAX,MAX,MAX,MAX,
		MAX,0,MAX,MAX,1,MAX,MAX,MAX,MAX,
		MAX,MAX,0,MAX,1,MAX,MAX,MAX,MAX,
		MAX,MAX,MAX,0,MAX,2,MAX,MAX,MAX,
		MAX,MAX,MAX,MAX,0,MAX,9,7,MAX,
		MAX,MAX,MAX,MAX,MAX,0,MAX,4,MAX,
		MAX,MAX,MAX,MAX,MAX,MAX,0,MAX,2,
		MAX,MAX,MAX,MAX,MAX,MAX,MAX,0,4,
		MAX,MAX,MAX,MAX,MAX,MAX,MAX,MAX,0
	};

	CreateGraph(G, "012345678", (int*)arcs);

	int* visited = (int*)malloc(sizeof(int) * G->vexNum);
	for (int i = 0; i < G->vexNum; i++)
	{
		visited[i] = 0;
	}
	DFS(G, visited, 0);
	printf("\n");

	//topologicalSort(G);
	critialPath(G);

	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值