1. 程式人生 > >c/c++ 有向無環圖 directed acycline graph

c/c++ 有向無環圖 directed acycline graph

rect 如果 拓撲排序 type 第一個 ima 結構 alt lse

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