c/c++ 有向无环图 directed acycline graph
c/c++ 有向无环图 directed acycline graph
概念:
图中点与点之间的线是有方向的,图中不存在环。用邻接表的方式,实现的图。
名词:
- 顶点的入度:到这个顶点的线的数量。
- 顶点的出度:从这个顶点出发的线的数量。
实现思路:
1,计算出每个顶点的入度,存放到辅助数组cnt中
2,找到入度为0的顶点集合。
3,从入度为0的顶点集合,拿出一个顶点,这个顶点就是第一个顶点(不唯一)。
4,找到与以3处顶点为出发点的顶点,然后把这些顶点的入度减一,减一后发现如果入度为0了,更新辅助数组cnt
5,重复2-4
难点:
辅助数组cnt的作用:
- 刚开始是存放每个顶点的入度
- 找到入度为0的顶点后,入栈;出栈的元素就是找到的顶点,发现入度为0的顶点后,继续入栈,然后出栈...
辅助数组cnt的运用,建议用gdb,多debug几次,就能明白了。
图为下图1:
图1

graph_link.h
#ifndef __graph_link__
#define __graph_link__
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <memory.h>
#define default_vertex_size 10
#define T char
//边的结构
typedef struct Edge{
//顶点的下标
int idx;
//指向下一个边的指针
struct Edge* link;
}Edge;
//顶点的结构
typedef struct Vertex{
//顶点的值
T data;
//边
Edge* adj;
}Vertex;
//图的结构
typedef struct GraphLink{
int MaxVertices;
int NumVertices;
int NumEdges;
Vertex* nodeTable;
}GraphLink;
//初始化图
void init_graph_link(GraphLink* g);
//显示图
void show_graph_link(GraphLink* g);
//插入顶点
void insert_vertex(GraphLink* g, T v);
//插入边尾插
void insert_edge_tail(GraphLink* g, T v1, T v2);
//插入边头插
void insert_edge_head(GraphLink* g, T v1, T v2);
//取得指定顶点的第一个后序顶点
int get_first_neighbor(GraphLink* g, T v);
//取得指定顶点v1的临街顶点v2的第一个后序顶点
int get_next_neighbor(GraphLink* g, T v1, T v2);
//拓扑排序
void topo_sort(GraphLink* g);
#endif
graph_link.c
#include "graph_link.h"
//初始化图
void init_graph_link(GraphLink* g){
g->MaxVertices = default_vertex_size;
g->NumVertices = g->NumEdges = 0;
g->nodeTable = (Vertex*)malloc(sizeof(Vertex) * g->MaxVertices);
assert(NULL != g->nodeTable);
for(int i = 0; i < g->MaxVertices; ++i){
g->nodeTable[i].adj = NULL;
}
}
//显示图
void show_graph_link(GraphLink* g){
if(NULL == g)return;
for(int i = 0; i < g->NumVertices; ++i){
printf("%d %c->", i, g->nodeTable[i].data);
Edge* p = g->nodeTable[i].adj;
while(NULL != p){
printf("%d->", p->idx);
p = p->link;
}
printf(" NULL\n");
}
}
//插入顶点
void insert_vertex(GraphLink* g, T v){
if(g->NumVertices >= g->MaxVertices)return;
g->nodeTable[g->NumVertices++].data = v;
}
//查找顶点的index
int getVertexIndex(GraphLink* g, T v){
for(int i = 0; i < g->NumVertices; ++i){
if(v == g->nodeTable[i].data)return i;
}
return -1;
}
//创建边
void buyEdge(Edge** e, int idx){
Edge* p = (Edge*)malloc(sizeof(Edge));
p->idx = idx;
p->link = NULL;
if(NULL == *e){
*e = p;
}
else{
Edge* tmp = *e;
while(tmp->link != NULL){
tmp = tmp->link;
}
tmp->link = p;
}
}
//插入边(尾插)
void insert_edge_tail(GraphLink* g, T v1, T v2){
int p1 = getVertexIndex(g, v1);
int p2 = getVertexIndex(g, v2);
if(p1 == -1 || p2 == -1)return;
buyEdge(&(g->nodeTable[p1].adj), p2);
g->NumEdges++;
buyEdge(&(g->nodeTable[p2].adj), p1);
g->NumEdges++;
}
//插入边(头插)
void insert_edge_head(GraphLink* g, T v1, T v2){
int p1 = getVertexIndex(g, v1);
int p2 = getVertexIndex(g, v2);
if(p1 == -1 || p2 == -1)return;
Edge* p = (Edge*)malloc(sizeof(Edge));
p->idx = p2;
p->link = g->nodeTable[p1].adj;
g->nodeTable[p1].adj = p;
/*
p = (Edge*)malloc(sizeof(Edge));
p->idx = p1;
p->link = g->nodeTable[p2].adj;
g->nodeTable[p2].adj = p;
*/
}
//取得指定顶点的第一个后序顶点
int get_first_neighbor(GraphLink* g, T v){
int i = getVertexIndex(g, v);
if (-1 == i)return -1;
Edge* p = g->nodeTable[i].adj;
if(NULL != p)
return p->idx;
else
return -1;
}
//取得指定顶点v1的临街顶点v2的第一个后序顶点
int get_next_neighbor(GraphLink* g, T ve1, T ve2){
int v1 = getVertexIndex(g, ve1);
int v2 = getVertexIndex(g, ve2);
if(v1 == -1 || v2 == -1)return -1;
Edge* t = g->nodeTable[v1].adj;
while(t != NULL && t->idx != v2){
t = t->link;
}
if(NULL != t && t->link != NULL){
return t->link->idx;
}
return -1;
}
//拓扑排序
void topo_sort(GraphLink* g){
int n = g->NumVertices;
//表示各个顶点的入度,先都初始化为0
int* cnt = (int*)malloc(sizeof(int) * n);
assert(NULL != cnt);
for(int i = 0; i < n; ++i){
cnt[i] = 0;
}
Edge* p;
//算出各个顶点的入度
for(int i = 0; i < n; ++i){
p = g->nodeTable[i].adj;
while(p != NULL){
cnt[p->idx]++;
p = p->link;
}
}
int top = -1;
for(int i = 0; i < n; ++i){
if(cnt[i] == 0){
//入度为0的顶点入栈(模拟入栈)
cnt[i] = top; //push
top = i;
}
}
int v,w;
for(int i = 0; i < n; ++i){
if(top == -1)return;//有回路存在
v = top; //模拟出栈
top = cnt[top];
printf("%c->", g->nodeTable[v].data);
w = get_first_neighbor(g, g->nodeTable[v].data);
while(-1 != w){
if(--cnt[w] == 0){
//入度为0的顶点入栈(模拟入栈)
cnt[w] = top;
top = w;
}
w = get_next_neighbor(g,g->nodeTable[v].data,g->nodeTable[w].data);
}
}
free(cnt);
}
graph_linkmain.c
#include "graph_link.h"
int main(){
GraphLink gl;
//初始化图
init_graph_link(&gl);
//插入节点
insert_vertex(&gl, 'A');
insert_vertex(&gl, 'B');
insert_vertex(&gl, 'C');
insert_vertex(&gl, 'D');
insert_vertex(&gl, 'E');
insert_vertex(&gl, 'F');
//插入边(头插)
insert_edge_head(&gl, 'A', 'B');
insert_edge_head(&gl, 'A', 'C');
insert_edge_head(&gl, 'A', 'D');
insert_edge_head(&gl, 'C', 'B');
insert_edge_head(&gl, 'C', 'E');
insert_edge_head(&gl, 'D', 'E');
insert_edge_head(&gl, 'F', 'D');
insert_edge_head(&gl, 'F', 'E');
//显示图
show_graph_link(&gl);
//拓扑排序
topo_sort(&gl);
printf("\n");
}
编译方法:gcc -g graph_link.c graph_linkmain.c
c/c++ 有向无环图 directed acycline graph的更多相关文章
- C#实现有向无环图(DAG)拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在 ...
- 【学习笔记】有向无环图上的DP
手动博客搬家: 本文发表于20180716 10:49:04, 原地址https://blog.csdn.net/suncongbo/article/details/81061378 首先,感谢以下几 ...
- 大数据工作流任务调度--有向无环图(DAG)之拓扑排序
点击上方蓝字关注DolphinScheduler(海豚调度) |作者:代立冬 |编辑:闫利帅 回顾基础知识: 图的遍历 图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点 ...
- 图->有向无环图->拓扑排序
文字描述 关于有向无环图的基础定义: 一个无环的有向图称为有向无环图,简称DAG图(directed acycline graph).DAG图是一类较有向树更一般的特殊有向图. 举个例子说明有向无环图 ...
- 拓扑排序-有向无环图(DAG, Directed Acyclic Graph)
条件: 1.每个顶点出现且只出现一次. 2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面. 有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说. 一 ...
- 有向无环图的应用—AOV网 和 拓扑排序
有向无环图:无环的有向图,简称 DAG (Directed Acycline Graph) 图. 一个有向图的生成树是一个有向树,一个非连通有向图的若干强连通分量生成若干有向树,这些有向数形成生成森林 ...
- javascript实现有向无环图中任意两点最短路径的dijistra算法
有向无环图 一个无环的有向图称做有向无环图(directed acycline praph).简称DAG 图.DAG 图是一类较有向树更一般的特殊有向图, dijistra算法 摘自 http://w ...
- 算法精解:DAG有向无环图
DAG是公认的下一代区块链的标志.本文从算法基础去研究分析DAG算法,以及它是如何运用到区块链中,解决了当前区块链的哪些问题. 关键字:DAG,有向无环图,算法,背包,深度优先搜索,栈,BlockCh ...
- [转帖]MerkleDAG全面解析 一文读懂什么是默克尔有向无环图
MerkleDAG全面解析 一文读懂什么是默克尔有向无环图 2018-08-16 15:58区块链/技术 MerkleDAG作为IPFS的核心数据结构,它融合了Merkle Tree和DAG的优点,今 ...
随机推荐
- Hadoop-HDFS
HDFS - 写文件 1.客户端将文件写入本地磁盘的N#x4E34;时文件中 2.当临时文件大小达到一个block大小时,HDFS client通知NameNode,申请写入文件 3.NameNode ...
- YARN集群的mapreduce测试(一)
hadoop集群搭建中配置了mapreduce的别名是yarn [hadoop@master01 hadoop]$ mv mapred-site.xml.template mapred-site.xm ...
- [译]如何在.NET Core中使用System.Drawing?
你大概知道System.Drawing,它是一个执行图形相关任务的流行的API,同时它也不属于.NET Core的一部分.最初是把.NET Core作为云端框架设计的,它不包含非云端相关API.另一方 ...
- [转]Angular开发(十八)-路由的基本认识
angular router https://angular.io/guide/router 本文转自:https://blog.csdn.net/kuangshp128/article/detail ...
- [Linux] nginx管理员指南基本功能
1.运行时控制Nginx进程 NGINX有一个主进程和一个或多个工作进程. 如果启用了缓存,则缓存加载器和缓存管理器进程也会在启动时运行. 主进程的主要目的是读取和评估配置文件,以及维护工作进程. 工 ...
- [android] 表格布局和绝对布局
/*****************2016年4月28日 更新*************************************/ 知乎:为什么Android没有像iOS一样提供autolay ...
- 汇编语言--微机CPU的指令系统(五)(字符串操作指令)
(11)字符串操作指令 字符串操作指令的实质是对一片连续存储单元进行处理,这片存储单元是由隐含指针DS:SI或ES:DI来指定的.字符串操作指令可对内存单元按字节.字或双字进行处理,并能根据操作对象的 ...
- JavaScript主流框架3月趋势总结
原文: What’s New in JavaScript Frameworks-March 2018 译者: Fundebug 为了保证可读性,本文采用意译而非直译.另外,本文版权归原作者所有,翻译仅 ...
- jquery中each中使用break和continue
在jquery中each中直接使用break或者continue会提示:必须在循环中使用.会报错不能直接使用. 但是,是不是就不能用呢,答案是的,但是换种方法可以达到相同的效果: 可以只用return ...
- mysql日期时间函数
日期时间函数1.取得当前日期时间SELECT NOW(), SYSDATE(),CURRENT_TIMESTAMP() FROM DUAL2.取得当前日期SELECT CURDATE(),CURREN ...