1.图的基本知识
- 定义
图中的点——》顶点
两顶点的连线—》边
图:Graph=(V,E)
V:顶点(数据元素)的非空有穷集合
E:边的有穷集合,可以为空
- 无向图
- 每条边都是无方向的
- 若任意两个顶点都有一条边相连的图称为无向完全图
- 无向完全图,n个顶点有几条边?
(n-1)+(n-2)+…+1=n*(n-1)/2
- 有向图
- 每一条边都是有方向的,称为弧
弧头——>弧尾
- 任意两个顶点都有方向相反的两条边的图称为有向完全图
- 有向完全图,n个顶点有几条边?
2*[(n-1)+(n-2)+…+1]=n*(n-1)
- 网
- 定义:网是边或弧带权的图
- 图的边或弧所具有的相关数称为权
- 有向网:带权的有向图
- 无向网:带权的无向图
- 子图:
如果一个图的顶点和边都属于另一个图,那么这个图就是另一个图的子图
- 图的顶点和边的关系
无向图:
- 邻接点:两点之间有连线,那么这两点是邻接点
- 边依附于这条边上的两顶点(或:边和这条边上的两顶点相关联)
- 顶点的度:和顶点向关联的边的数量,记为TD(V)
- 无向图的边数是各顶点度数和的一半
因为相当于每条边算了两次
有向图:
- 邻接点:两点之间有弧,则两点互为邻接点
- 弧依附于弧上的两顶点(弧与这两顶点相关联)
- 入度和出度
入度:以某顶点为头的弧的数量,称为此顶点的入度
出度:以某顶点为尾的弧的数量,称为此顶点的出度
度:入度+出度
- 有向图的弧数=总入度=总出度
因为一个顶点有一个入度,就有另一个顶点有一个出度
入度和等于出度和等于度数和的一半
弧数等于度数和的一半,所以弧数=入度和=出度和
- 路径
- 定义:路径:连续的边构成的有序顶点集合
若是有向图,路径也是有向的
- 路径长度:路径上边或弧的长度
- 回路或环
- 定义:第一个顶点到最后一个顶点相同的路径
- 简单回路或简单环
除第一个顶点和最后一个顶点外,其余顶点都不重复出现的回路
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(普里姆)算法
- MST:(v,E)是一个连通图,那么假设U是V的一个非空子集,如果(u,v)是一条最小权值的边,那么如果u属于U,v属于V,那么必有(u,v)包含在最小生成树里
- Prim:找点法,我们从集合中根据边的大小找点
- 怎样实现prim算法:
- 记录当前U集合的状态
- 选择最小边以及顶点加入到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(克鲁斯卡尔)算法
- 克鲁斯卡尔找边法
对边排序
选边
- 怎样实现:
- 维护一个边的数组并排序
- 判断图是否连通?
需要一个辅助的数组,来记录当前索引顶点属于哪个连通分量
#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(迪杰斯特拉)算法
- S数组:记录了目标顶点到其他顶点的最短路径是否求得
P数组:记录了目标定点到其他顶点的最短路径的前驱结点
D数组:记录了目标顶点到其他顶点的最短路径的长度
- 初始化
X:V1
V1 V2 V3 V4 V5 V6 V7
- 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
- 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(弗洛伊德)算法
求每一对顶点间的最短距离
- d数组:保存了两点的最短路径长度
p数组:保存了两点之前最短路径的前驱
- 核心:试探法,通过加入不同的点进行中转,选择出最优解
- 模拟floyd算法的过程
-
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.拓扑排序
- AOV—网
- 图的顶点表示任务
- 图的弧表示先后依赖关系
- 拓扑排序概念:拓扑排序就是将AOV中的顶点排成一个线性序列,如果vi->vj有弧的话,那么vi 必然在vj前面
- 拓扑排序用来判断图中是否有环,看排序序列中是否包含图中所有顶点,若排序序列中顶点个数小于图中顶点个数,则说明有环
- 拓扑排序流程
- 找出没有前驱的点,输出它,剪掉以它为出发点的所有边
- 重复第一步,直到图中不存在没有前驱的点
-
-
拓扑序列: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.关键路径
- AOE网:
事件-》顶点 有向无环图
活动-》弧
- 如何实现
- 拓扑排序
- 计算指标
- 事件指标
最早开始时间 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;
}