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的优点,今 ...
随机推荐
- 里氏替换原则(LSP)
替换原则由MIT计算机科学实验室的Liskov女士在1987年的OOPSLA大会上的一篇文章中提出,主要阐述有关继承的一些原则,故称里氏替换原则. 2002年,Robert C.Martin出版了一本 ...
- Maven json-lib依赖下载不下来解决方案
今天Maven添加依赖时候发现json lib这个包引入之后,死活出不来JSONObject这个类,打开Maven Project视图,发现json-lib这个包没下下来,以前也遇到过类似问题,都是网 ...
- Tomcat8源码笔记(六)连接器Connector分析
根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...
- Perl一行式:处理行号和单词数
perl一行式程序系列文章:Perl一行式 所有行的行号 $ perl -pe '$_ = "$. $_"' file.log $ perl -ne 'print "$. ...
- AFNetworking封装-项目使用
本篇博客是接着上一篇AFNetworking源码解析的后续,如果想对AFNetworking源码有所了解. 请读一下https://www.cnblogs.com/guohai-stronger/p/ ...
- 【转载】访问IIS中网站出现 403.14 - Forbidden报错信息
将网站发布后部署到IIS后,配置完应用程序池以及相关设置项后,在浏览器中访问设置好的网站,出现403.14 - Forbidden的错误信息,从错误信息的提示来看,应该是IIS服务器此网站目录的内容被 ...
- Java 泛型中的PECS原则
在泛型编程时,使用部分限定的形参时,<? super T>和<? extends T>的使用场景容易混淆,PECS原则可以帮助我们很好记住它们: 生产者(Producer)使用 ...
- What does operator “dot” (.) mean?
Question: Given the code : A = [1 2 3; 3 2 1] B = A.^2 The output : B = 1 4 9 9 4 1 But if I do this ...
- C#设计模式之六适配器模式(Adapter Pattern)【结构型】
一.引言 从今天开始我们开始讲[结构型]设计模式,[结构型]设计模式有如下几种:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式.[创建型]的设计模式解决的是对象创建的问题,那[ ...
- SaaS技术栈的走势
本地部署时代 在软件还是“本地部署(on-premise)”的时候,SaaS的版图被大型玩家把持着,几乎所有的垂直领域(营销.支持.销售.人力)都被微软.SAP等大公司的解决方案占据.那时候的用户并没 ...