隣接行列から隣接リストの変換


グラフで使われる、隣接行列から隣接リストへの変換関数を作った。有向グラフ、無向グラフ関係なく使える。グラフ G=(E,V)において、隣接行列は、O(V^2) の空間を必要とするが、隣接リストは O(E+V). 辺がそこまで多くないようなグラフで有効。

なお実際この変換関数は O(V^2) なので、入力が与えられているときは直接リストを作るべきだから、使うべき場面はない。何かのためではなく、変換することが目的な時に使える(笑)。ポインターを使ういい練習になった。

listify()はある配列に対して参照渡しをする。listify2()は新しく配列をヒープに動的確保して返す。このほうがオブジェクト指向っぽい。計算量は O(V^2) だけど、作業している分は辺の数 E だけ。

get_edge()は両方のインデックスから辺を見つける。隣接行列だとO(1)でとれるが、これは連結リストなので、一つのノードにつながれている辺が多いほど時間がかかる. 平均で O(E/V).

#include <stdio.h>
#include <stdlib.h>
typedef struct Edge {
    int idx;
    int cost;
    struct Edge* next;
} Edge;

void listify(int n,int mat[n][n], Edge* ls[n], int boundary){
    // initialize list. point next itself.
    int i,j;
    Edge* new;
    for(i=0;i<n;i++){
        ls[i] = malloc(sizeof(Edge));
        ls[i]->idx = -1;        
        ls[i]->next = ls[i];
    }

    for(i = 0; i < n; i++){
        for(j = 0; j < n; j++){
            if(mat[i][j] != boundary){
                // insert cost and key
                new = (Edge *)malloc(sizeof(Edge));
                new->cost = mat[i][j];
                new->idx = j;
                // pointer replace. Nodes are inversively orderd because new node connect to first node.
                new->next = ls[i]->next;
                ls[i]->next = new;
            }
        }
    }
}

Edge **listify2(int n,int mat[n][n], int boundary){
    // Dynamic allocation version.
    // allocate memory of pointers array to heap
    // '*ls[n]' cause warning: return-local-addr, and seg fault.
    Edge** ls=malloc(n * sizeof(Edge*));  
    listify(n,mat,ls,boundary);
    return ls;
}

Edge* get_edge(Edge** ls, int u,int v){
    Edge *edge = ls[u]->next;
    while(edge != ls[u]){
        if(edge->idx == v) return edge;
        edge = edge->next;
    }
    return NULL;
}

#define N 8
#define M 10000
int main(void){
    int edge[N][N]= {{0,1,7,2,M,M,M,M},
                {1,0,M,M,2,4,M,M},
                {7,M,0,M,M,2,3,M},
                {2,M,M,0,M,M,5,M},
                {M,2,M,M,0,1,M,M},
                {M,4,2,M,1,0,M,6},
                {M,M,3,5,M,M,0,2},
                {M,M,M,M,M,6,2,0}};

    Edge *ls[N];
    listify(N,edge,ls,M); // 参照渡し
    Edge **lst2 = listify2(N,edge,M); //配列を動的確保するver.

    Edge *e = get_edge(lst2,0,2);
    printf("%d\n",e->cost); //=> 7
    e = get_edge(ls,0,2);
    printf("%d\n",e->cost); //=> 7
    
}