Jul 22, 2011
Jul 22, 2011
N/A Views
MD
warning
この記事は2年以上前に更新されたものです。情報が古くなっている可能性があります。

ダイクストラ法によるグラフ構造の最短経路を解くメモ。

隣接リストを使ったバージョンと隣接行列を使ったバージョン

以下の例題とてグラフを使う

隣接リスト

本当は↑のグラフは無向なんだけど、経路を定義するのが面倒だったので有向で。

PriorityQueueを使うと楽です

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;

public class DijkstraList {

    /** 駅クラス */
    static class Station implements Comparable<Station> {
        String name;
        List<Route> routes = new ArrayList<Route>();
        /** 始点からのコスト */
        int cost = Integer.MAX_VALUE;

        public Station(String name) {
            this.name = name;
        }

        public void addRoute(Station to, int dist) {
            routes.add(new Route(this, to, dist));
        }

        @Override
        public String toString() {
            return name;
        }

        public int compareTo(Station o) {
            return cost - o.cost;
        }
    }

    /** 経路クラス */
    static class Route {
        Station from;
        Station to;
        /** 経路間コスト */
        int cost;

        public Route(Station from, Station to, int dist) {
            this.from = from;
            this.to = to;
            this.cost = dist;
        }
    }

    public static void main(String[] args) {
        Station[] stations = { new Station("横浜"), new Station("武蔵小杉"),
                new Station("品川"), new Station("渋谷"), new Station("新橋"),
                new Station("溜池山王"), };

        stations[0].addRoute(stations[1], 12);
        stations[0].addRoute(stations[2], 28);
        stations[1].addRoute(stations[2], 10);
        stations[1].addRoute(stations[3], 13);
        stations[2].addRoute(stations[3], 11);
        stations[2].addRoute(stations[4], 7);
        stations[3].addRoute(stations[5], 9);
        stations[4].addRoute(stations[5], 4);

        // 始点
        stations[0].cost = 0;

        // PQに挿入
        PriorityQueue<Station> pq = new PriorityQueue<Station>(
                Arrays.asList(stations));
        while (!pq.isEmpty()) {
            Station s = pq.poll();
            for (Route route : s.routes) {
                Station to = route.to;
                int cost = s.cost + route.cost;
                if (cost < to.cost) {
                    pq.remove(to);
                    // コストを更新して入れ替え
                    to.cost = cost;
                    pq.add(to);
                }
            }
        }

        for (Station s : stations) {
            System.out.println(stations[0] + " → " + s + " " + s.cost + "分");
        }
    }

}

実行結果

横浜 → 横浜 0分
横浜 → 武蔵小杉 12分
横浜 → 品川 22分
横浜 → 渋谷 25分
横浜 → 新橋 29分
横浜 → 溜池山王 33分

隣接行列

こっちは無向でやってみる

import java.util.Arrays;

public class DijkstraMatrix {

    public static void main(String[] args) {
        String[] stations = { "横浜", "武蔵小杉", "品川", "渋谷", "新橋", "溜池山王" };
        int n = stations.length;
        // 隣接行列
        int[][] adjacency_matrix = { { 0, 12, 28, 0, 0, 0 },
                { 12, 0, 10, 13, 0, 0 }, { 28, 10, 0, 11, 7, 0 },
                { 0, 13, 11, 0, 0, 9 }, { 0, 0, 7, 0, 0, 4 },
                { 0, 0, 0, 9, 4, 0 } };
        // 始点からのコスト
        int[] cost = new int[n];
        Arrays.fill(cost, Integer.MAX_VALUE);
        // 訪問済みフラグ
        boolean[] visited = new boolean[n];
        Arrays.fill(visited, false);

        // 始点
        cost[0] = 0;

        while (true) {
            // 未訪問で始点からのコストが最小の駅を探す
            int min = Integer.MAX_VALUE;
            int u = -1;
            for (int i = 0; i < n; i++) {
                if (!visited[i] && cost[i] < min) {
                    min = cost[i];
                    u = i;
                }
            }
            if (u == -1) {
                // 全て訪問
                break;
            }
            visited[u] = true;
            // 隣接する駅までのコストを更新
            for (int i = 0; i < n; i++) {
                int w = adjacency_matrix[u][i];
                if (w > 0) {
                    int c = cost[u] + w; // 新しいコスト
                    if (c < cost[i]) {
                        cost[i] = c;
                    }
                }
            }
        }

        for (int i = 0; i < n; i++) {
            System.out.println(stations[0] + " → " + stations[i] + " "
                    + cost[i] + "分");
        }
    }

}

実行結果

横浜 → 横浜 0分
横浜 → 武蔵小杉 12分
横浜 → 品川 22分
横浜 → 渋谷 25分
横浜 → 新橋 29分
横浜 → 溜池山王 33分

どっちを使うべきか

グラフの節(駅)の数をV、辺の数をEとする。

グラフが疎の場合はO(E) = O(V)となり、密の場合はO(E) = O(V^2)となる。

リストの場合と行列の場合のアルゴリズムのオーダーは次のようになる

隣接リスト隣接行列
O((E+V)logV) = O(VlogV)O(V^2+E) = O(V^2)
O((E+V)logV) = O(V^2logV)O(V^2+E) = O(V^2)

ってことで、その場合は隣接リスト、密の場合は隣接行列が適している。

Found a mistake? Update the entry.
Share this article: