數據結構之有關圖的算法(圖的鄰接表示法)

同前篇同樣,本篇意在總結有關圖的一些基本算法node

包括有圖的最小生成樹(Prim,Kruscal),最短路徑(Dijkstra,Floyd),拓撲排序等算法算法

本篇圖的數據結構爲鄰接表表示法數組

 

首先graph.h數據結構

#ifndef __GRAPH_H__
#define __GRAPH_H__

typedef struct graph *Graph;

Graph graph_create(int n);
void graph_destroy(Graph);
void graph_add_edge(Graph g, char u, char v,unsigned int wt);
int graph_vertex_count(Graph);
int graph_edge_count(Graph);
int graph_out_degree(Graph, char svex);
int graph_has_edge(Graph, char svex, char tvex);
void graph_foreach(Graph g, char svex,
    void (*f)(Graph g, char svex, char tvex, void *data),
    void *data);

#endif

cpp文件函數

// 圖.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <stdlib.h>
#include<stdio.h>
#include <string.h>
#include <assert.h>
#include "graph.h"

/* 代碼摘自一位yale前輩 */

struct graph {
    int vexnum;  /* number of vertices */
    int edgenum;  /* number of edges */
    struct successors {
        char vexname;
        char is_sorted;        /* true if list is already sorted */
        int size;        /* number of successors,即出度數*/
        int capacity;    /* number of slots in array,出度數組的長度,當空間不夠,它就會兩倍增長*/
        struct outvexs{
            char vex;
            unsigned int weight;
        } list[1];//出度數組。保存信息有頂點和權值

    } *alist[1];//alist數組至關於頂點鏈表,n個頂點就有n個元素,這裏一樣是爲了動態增長
};

/* create a new graph with n vertices labeled 0..n-1 and no edges */
Graph graph_create(int n)
{
    Graph g;
    int i;

    //新增一個graph空間和n-1個successors指針,算上graph中的一個successors指針就有n個了
    g = (Graph)malloc(sizeof(struct graph) + sizeof(struct successors *) * (n-1));

    //程序中大量使用assert,其做用是若是它的條件返回錯誤,則終止程序執行
    assert(g);

    g->vexnum = n;
    g->edgenum = 0;

    for(i = 0; i < n; i++) {
        //頂點鏈表
        g->alist[i] = (graph::successors*)malloc(sizeof(graph::successors));
        assert(g->alist[i]);

        g->alist[i]->vexname = 'A'+i;
        g->alist[i]->size = 0;
        g->alist[i]->capacity = 1;
        g->alist[i]->is_sorted= 1;
    }
    
    return g;
}

/* free all space used by graph */
void graph_destroy(Graph g)
{
    int i;
    for(i = 0; i < g->vexnum; i++){
        free(g->alist[i]);
    }
    free(g);
}

//找到vex在g->alist數組中的位置
int findVex(Graph g,char vex){
    for(int i=0;i<g->vexnum;i++){
        if(g->alist[i]->vexname==vex)
            return i;
    }
    return -1;
}

/* 爲graph添加邊,這裏是單向邊,僅<u,v> */
void graph_add_edge(Graph g, char u, char v,unsigned int wt)
{
    int s=findVex(g,u);
    int t=findVex(g,v);
    assert(s >= 0);
    assert(s < g->vexnum);
    assert(t >= 0);
    assert(t < g->vexnum);

    /* do we need to grow the list? */
    while(g->alist[s]->size >= g->alist[s]->capacity) {
        g->alist[s]->capacity *= 2;//容量兩倍增長的方式

        g->alist[s] =(graph::successors*)realloc(g->alist[s], sizeof(graph::successors) + sizeof(graph::successors::outvexs) * (g->alist[s]->capacity - 1));
    }

    /* now add the new sink */
    g->alist[s]->list[g->alist[s]->size].vex = v;
    g->alist[s]->list[g->alist[s]->size++].weight = wt;
    g->alist[s]->is_sorted = 0;

    /* bump edge count */
    g->edgenum++;
}

/* return the number of vertices in the graph */
int graph_vertex_count(Graph g)
{
    return g->vexnum;
}

/* return the number of vertices in the graph */
int graph_edge_count(Graph g)
{
    return g->edgenum;
}

/* return the out-degree of a vertex */
int graph_out_degree(Graph g, char vex)
{
    int source = findVex(g,vex);
    assert(source >= 0);
    assert(source < g->vexnum);

    return g->alist[source]->size;
}

/* when we are willing to call bsearch,二分查找 */
#define BSEARCH_THRESHOLD (10)

static int intcmp(const void *a, const void *b)
{
    return (*(const graph::successors::outvexs *)a).vex - (*(const graph::successors::outvexs*)b).vex;
}

/* return 1 if edge (source, sink) exists), 0 otherwise */
int graph_has_edge(Graph g, char svex, char tvex)
{
    int source=findVex(g,svex);;
    int sink=findVex(g,tvex);
    assert(source >= 0);
    assert(source < g->vexnum);
    assert(sink >= 0);
    assert(sink < g->vexnum);

    //若是該頂點出度數超過10,才使用二分查找
    if(graph_out_degree(g, svex) >= BSEARCH_THRESHOLD) {
        if(! g->alist[source]->is_sorted) {
            qsort(g->alist[source]->list,
                    g->alist[source]->size,
                    sizeof(graph::successors::outvexs),
                    intcmp);
        }
        
        /* call bsearch to do binary search for us */
        graph::successors::outvexs *list=(graph::successors::outvexs *) bsearch(&tvex,
                    g->alist[source]->list,
                    g->alist[source]->size,
                    sizeof(graph::successors::outvexs),
                    intcmp);
        if(list)
            return 1;
        else return 0;
    } else {
        /* just do a simple linear search */
        /* we could call lfind for this, but why bother? */
        for(int i = 0; i < g->alist[source]->size; i++) {
            if(g->alist[source]->list[i].vex == tvex) return 1;
        }
        /* else */
        return 0;
    }
}

/* invoke f on all edges (u,v) with source u */
/* supplying data as final parameter to f */
//這裏注意回調函數的使用
void
graph_foreach(Graph g, char svex,
    void (*f)(Graph g, char svex, char tvex, void *data),
    void *data)
{
    int i;
    int source=findVex(g,svex);
    assert(source >= 0);
    assert(source < g->vexnum);

    for(i = 0; i < g->alist[source]->size; i++) {
        f(g, svex, g->alist[source]->list[i].vex, data);
    }
}
#define TEST_SIZE (6)

//static使得本函數本文件可見
 static void
match_sink(Graph g, char svex, char tvex, void *data)
{
    assert(data && tvex == (*(graph::successors::outvexs *) data).vex);
}

 //這個函數有什麼用?
static int node2dot(Graph g)
{
    assert(g != NULL);
    return 0;
}

static void print_edge2dot(Graph g,char source, char sink, void *data)
{
    int svex_pos=findVex(g,source);
    int tvex_pos;
    for(int i=0;i<g->alist[svex_pos]->size;i++)
        if(sink == g->alist[svex_pos]->list[i].vex){
            tvex_pos=i;
            break;
        }
    fprintf(stdout,"<%c->%c>:weight:%d\n",source,sink,g->alist[svex_pos]->list[tvex_pos].weight);
}
//打印全部的邊
static int edge2dot(Graph g)
{
    assert(g != NULL);
    int idx = 0;
    int node_cnt = graph_vertex_count(g);
    for(idx = 0;idx<node_cnt; idx++)
    {
        graph_foreach(g,g->alist[idx]->vexname,print_edge2dot,NULL);
    }
    printf("\n");
    return 0;
}

int graph2dot(Graph g)
{
    fprintf(stdout,"digraph{");
    node2dot(g);
    edge2dot(g);
    fprintf(stdout,"}n");
    return 0;
}

//最小生成樹之Prim,Kruscal算法
//鬆弛操做
struct EdgeType{
    char s,t;
    unsigned int cost;
};

void relaxation(Graph g,EdgeType *dist,char vex,bool* visited){
    int svex_pos=findVex(g,vex);
    assert(svex_pos!=-1);

    for(int j=0;j<g->alist[svex_pos]->size;j++){
        int tvex_pos = findVex(g,g->alist[svex_pos]->list[j].vex);
        assert(tvex_pos!=-1);
        if(visited[tvex_pos]==false && g->alist[svex_pos]->list[j].weight<dist[tvex_pos].cost){
            dist[tvex_pos].cost=g->alist[svex_pos]->list[j].weight;
            dist[tvex_pos].s=vex;
            dist[tvex_pos].t=g->alist[svex_pos]->list[j].vex;
        }
    }
}

const unsigned int INFINITY = -1;

int findMin(EdgeType *dist,bool* visited,int num){
    unsigned int min=INFINITY;
    int pos=-1;
    for(int i=0;i<num;i++)
        if(visited[i]==false && min>dist[i].cost){
            min=dist[i].cost;
            pos=i;
        }
    return pos;
}

//MST之Prim算法
void MST_Prim(Graph g,char beg_vex){
    EdgeType *dist = (EdgeType*)malloc(sizeof(EdgeType)*g->vexnum);//存放MST的邊,MST邊 = vexnum-1
//    memset(dist,0xFF,sizeof(int)*g->vexnum);

    bool *visited=(bool*)malloc(sizeof(bool)*g->vexnum);//是否已添加
    memset(visited,0,sizeof(bool)*g->vexnum);

//    char *prim_vex=(char*)malloc(g->vexnum+1);//prim_vex數組保存添加的結點
    int vexs=0;//記錄已加入點的數目
//    prim_vex[vexs++]=beg_vex;
    int i=findVex(g,beg_vex);
    assert(i!=-1);
    visited[i]=true;
    vexs++;

    relaxation(g,dist,beg_vex,visited);
    while(vexs<g->vexnum){
        int i=findMin(dist,visited,g->vexnum);
        assert(i!=-1);

        visited[i]=true;
        vexs++;

        printf("<%c,%c>:%d\n",dist[i].s,dist[i].t,dist[i].cost);
        
        relaxation(g,dist,g->alist[i]->vexname,visited);
    }
 
    free(dist);
    free(visited);
}

int cmp(const void* a,const void *b){
    return (*(EdgeType *)a).cost - (*(EdgeType *)b).cost;
}

//尋找vex所在樹的根結點
int findRoot(Graph g,int *root,char vex){
    int t=findVex(g,vex);
    while(root[t]>=0)
        t=root[t];
    return t;
}

void MST_Kruscal(Graph g){
    int k=0;
    EdgeType *edges = new EdgeType[g->edgenum];
    for(int i=0;i<g->vexnum;i++){
        for(int j=0;j<g->alist[i]->size;j++){//有向,所以不考慮<u,v>和<v,u>的問題
                edges[k].s=g->alist[i]->vexname;
                edges[k].t=g->alist[i]->list[j].vex;
                edges[k++].cost=g->alist[i]->list[j].weight;
            }
    }
    //全部邊按從小到大排序
    qsort(edges,g->edgenum,sizeof(EdgeType),cmp);

    //root[i]表示頂點i所在的樹的根結點
    int *root=(int*)malloc(sizeof(int)*g->vexnum);//初始全部點屬於不一樣的連通份量
    for(int i=0;i<g->vexnum;i++)//初始化
        root[i]=-1;
    int j=0,i=0;
    //j表示查找第幾條邊,i表示頂點
    while(j<g->edgenum && i<g->vexnum-1){//MST邊數爲頂點數-1
        int svex=findRoot(g,root,edges[j].s);
        int tvex=findRoot(g,root,edges[j].t);
        if(svex != tvex){//若是兩頂點屬於不一樣的連通份量
            root[tvex]=svex;
            i++;
            printf("<%c,%c> ",edges[j].s,edges[j].t);
        }
        j++;
    }

    free(root);
    delete[] edges;
    printf("\n");
}

void relaxation_Dijkstra(Graph g,unsigned int *dist,char** path,char svex,char tvex){
    int i=findVex(g,svex);
    int j=findVex(g,tvex);
    assert(j!=-1 && i!=-1);

    for(int k=0;k<g->alist[j]->size;k++){
        char tvex_pos=findVex(g,g->alist[j]->list[k].vex);
        assert(tvex_pos!=-1);

        if((g->alist[j]->list[k].weight+dist[j])< dist[tvex_pos]){
            dist[tvex_pos]=dist[tvex_pos]+g->alist[j]->list[k].weight;
            int w;
            for(w=0;path[tvex_pos][w]!=-1;w++)//更新路徑
                path[tvex_pos][w]=path[j][w];
            path[tvex_pos][w]=g->alist[j]->list[k].vex;
        }
    }
}

//sp[vexnum]標記源點到該點是否已經是最短路徑
int findMin_Dijkstra(unsigned int dist[],int length,bool sp[]){
    unsigned int min=INFINITY;
    int pos=-1;
    for(int i=0;i<length;i++){
        if(sp[i]==false && dist[i]<min){
            min=dist[i];
            pos=i;
        }
    }
    return pos;
}

//最短路徑
void ShortestPath_Dijkstra(Graph g,char source_vex){
    //最短路徑二維數組,sp_path[j]數組保存頂點j的最短路徑上的點
    char **sp_path=(char**)malloc(sizeof(char*)*g->vexnum);
    for(int i=0;i<g->vexnum;i++){
        sp_path[i]=(char*)malloc(sizeof(char)*g->vexnum);
        memset(sp_path[i],-1,g->vexnum);
    }
    //起始點到其餘頂點的距離
    unsigned int *sp_dist=(unsigned int*)malloc(sizeof(unsigned int)*g->vexnum);
    memset(sp_dist,0xFF,sizeof(unsigned int)*g->vexnum);

    bool *sp_visited=(bool*)malloc(sizeof(bool)*g->vexnum);//標記是否已經是最短
    memset(sp_visited,0,sizeof(bool)*g->vexnum);

    int j=findVex(g,source_vex);
    assert(j!=-1);

    sp_dist[j]=0;
    sp_visited[j]=true;

    relaxation_Dijkstra(g,sp_dist,sp_path,source_vex,source_vex);

    int rounds=1;
    while(rounds<g->vexnum){
        j=findMin_Dijkstra(sp_dist,g->vexnum,sp_visited);
        assert(j!=-1);
        sp_visited[j]=true;
        rounds++;

        relaxation_Dijkstra(g,sp_dist,sp_path,source_vex,g->alist[j]->vexname);    
    }
    for(int i=0;i<g->vexnum;i++){
        printf("Shortest Path from %c to %c :%c",source_vex,g->alist[i]->vexname,source_vex);
        for(j=0;sp_path[i][j]!=-1;j++)
            printf("%c",sp_path[i][j]);
        printf("\n",g->alist[i]->vexname);
    }
    for(int i=0;i<g->vexnum;i++)
        free(sp_path[i]);
    free(sp_path);
    free(sp_dist);
    free(sp_visited);
}

//若P[v][w][u]爲TRUE,則u 是從v 到w 當前求得的最短路徑上的頂點
const int MAX = 10;
bool P[MAX][MAX][MAX];
unsigned int D[MAX][MAX];

 void ShortestPath_Flyod(Graph g){
     /* 使用一個n*n的方陣D,D[s][t]表示<s,t>的最短路徑
      * 可是爲了D[s][t],須要更新n次D矩陣
      * D(k)[s][t]表示通過k次更新後,當前<s,t>的最短路徑,可能最終不是
      * D(-1)[s][t]=edges[s][t]
      * D(k)[s][t]=min{ D(k-1)[s][t],D(k-1)[s][k]+D(k-1)[k][t] }
      * D(k)的計算是嘗試把頂點k加到每對頂點<s,t>之間
     */
    memset(D,0xFF,sizeof(unsigned int)*MAX*MAX);

    for(int i=0;i<g->vexnum;++i){//初始化D矩陣 
        for(int t=0;t<g->alist[i]->size;++t){
            int tvex_pos=findVex(g,g->alist[i]->list[t].vex);
            assert(tvex_pos!=-1);
            D[i][tvex_pos]=g->alist[i]->list[t].weight; //D(-1)
            for(int u=0;u<g->vexnum;++u) 
                P[i][tvex_pos][u]=false;
            if (D[i][tvex_pos]<INFINITY){ /*從v 到w 有直接路徑*/
                P[i][tvex_pos][i]=true;
                P[i][tvex_pos][tvex_pos]=true;
            }
        }
    }

    for(int k=0; k<g->vexnum; ++k){//計算D(k),總共n次,這個循環必定要在最外層
        for(int s=0; s<g->vexnum; ++s){//D(k)[s]
            for(int t=0;t<g->alist[s]->size;++t){//D(k)[s][t]
                int tvex_pos=findVex(g,g->alist[s]->list[t].vex);
                assert(tvex_pos!=-1);
                if(D[s][k]<INFINITY && D[k][tvex_pos]<INFINITY && (D[s][k]+D[k][tvex_pos])<D[s][tvex_pos]){ //若是頂點k屬於<s,t>最短路徑上
                    D[s][tvex_pos]=D[s][k]+D[k][tvex_pos];
                    //更新P[s][t],當前P[s][t]最短路徑上有哪些頂點
                    for(int i=0;i<g->vexnum;++i)
                        P[s][tvex_pos][i]=P[s][k][i] || P[k][tvex_pos][i];
                }
            }
        }
    }
    //輸入每對頂點的最短路徑上的頂點
    for(int s=0;s<g->vexnum;s++)
        for(int t=0;t<g->alist[s]->size;++t){
            printf("The Shortest Path Vertexes of <%c,%c> are:\n",g->alist[s]->vexname,g->alist[s]->list[t].vex);
            int tvex_pos=findVex(g,g->alist[s]->list[t].vex);
            assert(tvex_pos!=-1);

            for(int i=0;i<g->vexnum;i++)
                if(P[s][tvex_pos][i])
                    printf("%c",g->alist[i]->vexname);
            printf("\n");
        }
 }

int _tmain(int argc, _TCHAR* argv[])
{
    Graph g;
    int i;
    int j;

    g = graph_create(TEST_SIZE);


    assert(graph_vertex_count(g) == TEST_SIZE);

    /* check it's empty */
    for(i = 0; i < TEST_SIZE; i++) {
        for(j = 0; j < TEST_SIZE; j++) {
            assert(graph_has_edge(g, g->alist[i]->vexname, g->alist[j]->vexname) == 0);
        }
    }

    /* check it's empty again */
    for(i = 0; i < TEST_SIZE; i++) {
        assert(graph_out_degree(g, g->alist[i]->vexname) == 0);
        graph_foreach(g, g->alist[i]->vexname, match_sink, 0);
    }

    /* check edge count */
    assert(graph_edge_count(g) == 0);

    //添加邊<u,v>,if u<v
    unsigned int weight=0;//權值
    for(i = 0; i < TEST_SIZE; i++) {
        for(j = 0; j < TEST_SIZE; j++) {
            weight = rand()%100 +1;
            if(i < j) graph_add_edge(g, g->alist[i]->vexname, g->alist[j]->vexname,weight);
        }
    }


    for(i = 0; i < TEST_SIZE; i++) {
        for(j = 0; j < TEST_SIZE; j++) {
            assert(graph_has_edge(g, i+'A', j+'A') == (i < j));
        }
    }
    assert(graph_edge_count(g) == (TEST_SIZE*(TEST_SIZE-1)/2));

    //打印圖
    graph2dot(g);

    MST_Prim(g,'A');
    MST_Kruscal(g);
    ShortestPath_Dijkstra(g,'A');
    ShortestPath_Flyod(g);
    /* free it
     * */
    graph_destroy(g);

    return 0;
}
相關文章
相關標籤/搜索