图-拓扑排序+练习题

前言:图-拓扑排序的学习和实现笔记

参考文章:https://www.jianshu.com/p/b59db381561a

有向无环图(DAG)

在学习拓扑排序之前,先了解一个概念有向无环图(DAG)

有向无环图DAG的定义:有向无环图指的是一个无回路的有向图。

如果有一个非有向无环图,且A点出发向B经C可回到A,形成一个环。

将从C到A的边方向改为从A到C,则变成有向无环图,比如下面这张图的话那么就是一张有向无环图

如果是下面这种多了一条4->1的边长的话,那么类似下面这种的话就是非DAG图,也就是非有向无环图

知识点:有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。

什么是AOV网

它的全称是Activity On Vertex NetWork,用顶点表示的活动的网

而在AOV网中,都是用DAG图(有向无环图)表示一个工程。

其中的顶点表示活动,有向边<Vi,Vj>表示活动Vi必须先于活动Vj进行,如下图所示,这种就是一个AOV网表示的一个工程

这里举个例子如下所示,类似这种的话在AOV网中,其中构成了一个环路,那么就不能称之为一个AOV网

什么是拓扑排序

总结下拓扑排序的作用,简而言之其实就是判断一个有向图是否是无环图,但是在AOV网中通过拓扑排序,我们可以实现排序工程中的每个顶点之间的活动顺序,上面的例子经过排序过后就如下所示

在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG,Directed Acyclic Graph)的所有顶点的线性序列。

且该序列必须满足下面两个条件:

  • 每个顶点出现且只出现一次。

  • 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。

如何手动写出有向无环图的拓扑排序

一个知识点:通常,一个有向无环图可以有一个或多个拓扑排序序列。

  • 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。

  • 从图中删除该顶点和所有以它为起点的有向边。

  • 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。

代码实现有向无环图的拓扑排序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define OK 1
#define ERROR 0
#define MAX 32767
typedef int Status;
typedef struct _Graph
{
int vexNum; // 顶点数量
int arcNum; // 边长数量
int** arcs; // 边长的0或者1,大小就是arcsNum*arcsNum
char* vexs; // 顶点的名称
}Graph, *PGraph;
// 图的初始化
Graph* initGraph(int vexNum)
{
Graph* pGraph = NULL;
if (pGraph == NULL)
{
pGraph = (Graph*)malloc(sizeof(Graph));
memset(pGraph, 0, sizeof(Graph));
if (pGraph == NULL)
return NULL;
pGraph->vexNum = vexNum; // 顶点的数量
pGraph->vexs = (char*)malloc(sizeof(pGraph->vexNum));
memset(pGraph->vexs, 0, sizeof(pGraph->vexNum));
pGraph->arcNum = 0; // 初始化为0,因为到时候craeteGraph的时候完了之后再进行最终边数的计算
pGraph->arcs = (int**)malloc(sizeof(int*)* pGraph->vexNum);
memset(pGraph->arcs, sizeof(int*)* pGraph->vexNum, 0);
// 一个图中每个顶点都有对应的n个顶点的边数
for (int i = 0; i < pGraph->vexNum; i++)
{
pGraph->arcs[i] = malloc(sizeof(int)* pGraph->vexNum);
memset(pGraph->arcs[i], 0, sizeof(int)*pGraph->vexNum);
}
}
return pGraph;
}
// 顶点和边之间的关系初始化
Status createGraph(Graph** pGraph, char* vexs, int* arcs)
{
if (*pGraph == NULL)
return ERROR;
for (int i = 0; i<(*pGraph)->vexNum; i++)
{
*((*pGraph)->vexs + i) = *(vexs + i);
for (int j = 0; j<(*pGraph)->vexNum; j++)
{
(*pGraph)->arcs[i][j] = *(arcs + i*((*pGraph)->vexNum) + j);
printf("%d ", (*pGraph)->arcs[i][j]);
if ((*pGraph)->arcs[i][j] == 1)
(*pGraph)->arcNum++;
}
printf("\n");
}
// 无向图的边数还需要除以2
(*pGraph)->arcNum /= 2;
return OK;
}
// 深度优先遍历
Status DFS(Graph* pGraph, int* iVisitedArray, int visitedIndex)
{
if (pGraph == NULL)
return ERROR;
iVisitedArray[visitedIndex] = 1;
printf("%c ", pGraph->vexs[visitedIndex]);
for (int i = 0; i<pGraph->vexNum; i++)
{
if (pGraph->arcs[visitedIndex][i] == 1 && !iVisitedArray[i])
DFS(pGraph, iVisitedArray, i);
}
return OK;
}
void getTopoSort(Graph* pGraph)
{
int iFlag; // 标识符
int visitedArray[5] = { 0 };
for (int m = 0;m<pGraph->vexNum;m++)
{
for (int i = 0; i < pGraph->vexNum; i++)
{
iFlag = 1;
if (!visitedArray[i])
{
for (int j = 0; j < pGraph->vexNum; j++)
{
if (pGraph->arcs[j][i] != 0 && pGraph->arcs[j][i] != MAX)
{
iFlag = 0; break;
}
}
if (iFlag)
{
for (int k = 0; k < pGraph->vexNum; k++){ if (pGraph->arcs[i][k] == 1)pGraph->arcs[i][k]--; }
printf("输出顶点%c\n", pGraph->vexs[i]);
visitedArray[i] = 1;
break;
}
}
}
}
}
int main()
{
// 要初始化的矩阵
int visited[5] = { 0 };
int initArcs[5][5] =
{
0,1,MAX,1,MAX,
MAX,0,1,1,MAX,
MAX,MAX,0,MAX,1,
MAX,MAX,1,0,1,
MAX,MAX,MAX,MAX,0
};
char initVexs[6] = "12345";
Graph* pGraph = initGraph(5); // 要初始化的顶点的数量
createGraph(&pGraph, initVexs, (int*)initArcs);
getTopoSort(pGraph);
return 0;
}

练习题

问题描述:有一串数字1到5,按照下面的关于顺序的要求,重新排列并打印出来。要求如下:2在5前出现,3在2前出现,4在1前出现,1在3前出现。

看到题目就知道是进行构造相关的无环图dag,那么这里可以通过dfs或者bfs来进行实现,我这里的话就通过dfs来进行实现,go代码如下

思路就是先构造对应的主键的map,然后通过遍历map来进行判断是否能够构造出对应的完整连续的dag来,可以的话则成功

package main
import (
"fmt"
)
// 问题描述:有一串数字1到5,按照下面的关于顺序的要求,重新排列并打印出来。要求如下:2在5前出现,3在2前出现,4在1前出现,1在3前出现
var gmap2 map[string]string = map[string]string{
"2": "5",
"3": "2",
"4": "1",
"1": "3",
}
//反转数组顺序
func reverse(arr []string) {
for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
arr[i], arr[j] = arr[j], arr[i]
}
}
var q []string
var visited_ map[string]bool
func tapo_sort3(k string) {
if !visited_[k] {
visited_[k] = true
q = append(q, k)
if gmap2[gmap2[k]] != "" {
q = append(q, gmap2[k])
tapo_sort3(gmap2[gmap2[k]])
}
}
}
func main() {
for k, _ := range gmap2 {
fmt.Printf("\t %s\n", k)
q = make([]string, 0)
visited_ = make(map[string]bool, 0)
tapo_sort3(k)
// reverse(q)
fmt.Printf("topusort: %v \n", q)
}
}

posted @   zpchcbd  阅读(326)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
历史上的今天:
2020-04-13 WeCenter v3.3.4 多个前台反序列化漏洞挖掘
点击右上角即可分享
微信分享提示