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