Dijkstra

인터넷에 돌아다니는 자바와 파이선 구현의 차이점이 있다.

  • Java : 인접 행열 사용, 힙을 사용하지 않음
  • Python : 인접 리스트 사용, 힙 사용

알고리즘 성능 면에서 인접리스트, 힙 이 압도적으로 유리하다. 힙을 사용하면 다음 노드를 선택할 때 비용이 O(1) 이기 때문이다. 이 차이로 문제 해결이 갈리기도 한다.
아마도 자바 샘플 코드는 다익스트라 개념 자체에 집중하여 코드를 작성 하느라 그러는 것 같다. 자바에서 인접리스트, 힙, SimpleMap 것을 구현하려면 이것저것 머리 아픈게 많기는 하다.
반면 파이선은 그럴 필요가 전혀 없다. heapq 와 tuple 이 특별한 교육 없이 코드에 녹아진다.

Java Dijkstra 구현 (인접리스트, Heap 사용)

Head 와 edge 를 이용해서 linked list 를 구현한 이유는 컬렉션을 일절 사용하지 못하게 제한했기 때문이다. 그래서 heap 도 직접 구현했다.

static void djikstra() {
    long[] costarr = new long[N + 1];
    for (int i = 0; i < N + 1; i++) {
        costarr[i] = Long.MAX_VALUE;
    }

    heap = new long[M][2];
    heapSize = 0;

    heapPush(0, 1);
    costarr[1] = 0;
    boolean[] visited = new boolean[N + 1];
    while (heapSize > 0) {
        int from = (int) heapPop()[1];
        if (!visited[from]) {
            visited[from] = true;
            for (Edge e = map[from].head; e != null; e = e.next) {
                if (visited[e.dest] || e.cost == 0)
                    continue;
                long cost = costarr[from] + e.cost;
                if (cost < costarr[e.dest]) {
                    costarr[e.dest] = cost;
                    heapPush(cost, e.dest);
                }
            }
        }
    }
}

private class Edge {
    int to;
    int cost;
    Edge next;
}

private class Head {
    Edge head;
    Edge tail;
    void add(Edge e) {
        if (head == null) {
            head = e;
            tail = e;
        } else {
            tail.next = e;
            tail = e;
        }
    }
}

static long heap[][];
static int heapSize = 0;
static int MAX_SIZE = 500000;

static void heapPush(long cost, int dest) {
    long[] heapItem = new long[2];
    heapItem[0] = cost;
    heapItem[1] = dest;
    heapPush(heapItem);
}

static void heapPush(long[] value) {
    if (heapSize + 1 > MAX_SIZE) {
        return;
    }

    heap[heapSize] = value;

    int current = heapSize;
    while (current > 0 && heap[current][0] < heap[(current - 1) / 2][0]) {
        long[] temp = heap[(current - 1) / 2];
        heap[(current - 1) / 2] = heap[current];
        heap[current] = temp;
        current = (current - 1) / 2;
    }

    heapSize = heapSize + 1;
}

static long[] heapPop() {
    if (heapSize <= 0) {
        return null;
    }

    long[] value = heap[0];
    heapSize = heapSize - 1;

    heap[0] = heap[heapSize];

    int current = 0;
    while (current < heapSize && current * 2 + 1 < heapSize) {
        int child;
        if (current * 2 + 2 >= heapSize) {
            child = current * 2 + 1;
        } else {
            child = heap[current * 2 + 1][0] < heap[current * 2 + 2][0] ? current * 2 + 1 : current * 2 + 2;
        }

        if (heap[current][0] < heap[child][0]) {
            break;
        }

        long[] temp = heap[current];
        heap[current] = heap[child];
        heap[child] = temp;

        current = child;
    }
    return value;
}