2017-01-31 20 views
19

私はダイクストラのアルゴリズムについてCLRSの第3版(p。662)を読んでいます。ここで私は理解していない本から一部です: - 具体的には、E = o(V^2/lg V) -ダイクストラアルゴリズム。最小優先度キューとしての最小ヒープ

をグラフが十分に希薄である場合、我々はバイナリ分、ヒープを最小プライオリティキューを実装することにより、アルゴリズムを改善することができます。

グラフが疎なのはなぜですか?ここで


別の部分である:

各DECREASE-KEY操作は時間O(log V)を要し、かつ が最もEなどの操作で残っています。

From 1 to 6

私は1から6までの最短経路を計算し、最小ヒープ・アプローチを使用したい:

は私のグラフは次のようになりますと仮定します。最初に、すべてのノードを最小優先度キューに追加します。最小ヒープを構築した後、最小ノードはソースノードです(それ自身との距離は0です)。私はそれを抽出して、すべての隣人の距離を更新します。

次に、新しい最小限のヒープを作成するには、最小距離のノードでdecreaseKeyと呼ぶ必要があります。しかし、どのように私は一定の時間内にその指標を知ることができますか?

ノード

private static class Node implements Comparable<Node> { 

    final int key; 
    int distance = Integer.MAX_VALUE; 
    Node prev = null; 

    public Node(int key) { 
     this.key = key; 
    } 

    @Override 
    public int compareTo(Node o) { 
     if (distance < o.distance) { 
      return -1; 
     } else if (distance > o.distance) { 
      return 1; 
     } else { 
      return 0; 
     } 
    } 

    @Override 
    public String toString() { 
     return "key=" + key + " distance=" + distance; 
    } 

    @Override 
    public int hashCode() { 
     return key; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) { 
      return true; 
     } 
     if (!(obj instanceof Node)) { 
      return false; 
     } 
     Node other = (Node) obj; 
     return key == other.key; 
    } 
} 

MinPriorityQueue

public static class MinPriorityQueue { 

    private Node[] array; 
    private int heapSize; 

    public MinPriorityQueue(Node[] array) { 
     this.array = array; 
     this.heapSize = this.array.length; 
    } 

    public Node extractMin() { 
     Node temp = array[0]; 
     swap(0, heapSize - 1, array); 
     heapSize--; 
     sink(0); 
     return temp; 
    } 

    public boolean isEmpty() { 
     return heapSize == 0; 
    } 

    public void buildMinHeap() { 
     for (int i = heapSize/2 - 1; i >= 0; i--) { 
      sink(i); 
     } 
    } 

    public void decreaseKey(int index, Node key) { 
     if (key.compareTo(array[index]) >= 0) { 
      throw new IllegalArgumentException("the new key must be greater than the current key"); 
     } 
     array[index] = key; 
     while (index > 0 && array[index].compareTo(array[parentIndex(index)]) < 0) { 
      swap(index, parentIndex(index), array); 
      index = parentIndex(index); 
     } 
    } 

    private int parentIndex(int index) { 
     return (index - 1)/2; 
    } 

    private int left(int index) { 
     return 2 * index + 1; 
    } 

    private int right(int index) { 
     return 2 * index + 2; 
    } 

    private void sink(int index) { 
     int smallestIndex = index; 
     int left = left(index); 
     int right = right(index); 
     if (left < heapSize && array[left].compareTo(array[smallestIndex]) < 0) { 
      smallestIndex = left; 
     } 
     if (right < heapSize && array[right].compareTo(array[smallestIndex]) < 0) { 
      smallestIndex = right; 
     } 
     if (index != smallestIndex) { 
      swap(smallestIndex, index, array); 
      sink(smallestIndex); 
     } 
    } 

    public Node min() { 
     return array[0]; 
    } 

    private void swap(int i, int j, Node[] array) { 
     Node temp = array[i]; 
     array[i] = array[j]; 
     array[j] = temp; 
    } 

} 

答えて

11

なぜグラフが疎すべきですか?

ダイクストラのアルゴリズムの実行時間は、基礎となるデータ構造とグラフの形状(エッジと頂点)の組み合わせによって決まります。

例えば、リンクリストを使用するには、O(V²)時間が必要です。つまり、頂点の数にのみ依存します。 ヒープを使用するには、O((V + E) log V)が必要です。つまり、頂点の数とエッジの数の両方に依存します。

EがVに比べて十分小さい場合は(E << V²/logVのように)、ヒープを使用する方が効率的になります。

次に、最小距離のノードでdecreaseKeyと呼ぶ必要があります。しかし、どのように私は一定の時間内にその指標を知ることができますか?

バイナリヒープを使用している場合は、extractMin常にO(log V)時間で実行され、あなたの最低距離(別称、キー)を持つノードを提供します。あなたは常に、最低の距離を持つ要素になります配列H、(我々は1から数え慣例により)配列H[1]の最初の要素としてバイナリ分ヒープを実装している場合たとえば

、その発見それはO(1)しかかかりません。

しかし、後の各extractMininsertまたはdecreaseKeyあなたは結果的にトップに最低距離のノードを移動し、swimまたはsinkがヒープ状態を復元するために実行する必要があります。これにはO(log V)が必要です。

本書で触れたように、ヒープとキーのキー間のマッピングを維持することもできます。「 の頂点と対応するヒープ要素が相互にハンドルを維持していることを確認する」(6.5節で簡単に説明します) )。

4

はあなたが7(0 - > 6)を持っているあなたのグラフは、あなたのケースで頂点(ノード)で構成されているとしましょうとの縁。

ノードモデル:これらは、以下のモデルで表される

public class Vertex{ 
     final private String id; 
     final private String name; 


     public Vertex(String id, String name) { 
       this.id = id; 
       this.name = name; 
     } 
     public String getId() { 
       return id; 
     } 

     public String getName() { 
       return name; 
     } 

     @Override 
     public int hashCode() { 
       final int prime = 31; 
       int result = 1; 
       result = prime * result + ((id == null) ? 0 : id.hashCode()); 
       return result; 
     } 

     @Override 
     public boolean equals(Object obj) { 
       if (this == obj) 
         return true; 
       if (obj == null) 
         return false; 
       if (getClass() != obj.getClass()) 
         return false; 
       Vertex other = (Vertex) obj; 
       if (id == null) { 
         if (other.id != null) 
           return false; 
       } else if (!id.equals(other.id)) 
         return false; 
       return true; 
     } 

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

} 

とエッジは、このモデルで存在するであろう。エッジ

public class Edge { 
     private final String id; 
     private final Vertex source; 
     private final Vertex destination; 
     private final int weight; 

     public Edge(String id, Vertex source, Vertex destination, int weight) { 
       this.id = id; 
       this.source = source; 
       this.destination = destination; 
       this.weight = weight; 
     } 

     public String getId() { 
       return id; 
     } 
     public Vertex getDestination() { 
       return destination; 
     } 

     public Vertex getSource() { 
       return source; 
     } 
     public int getWeight() { 
       return weight; 
     } 

     @Override 
     public String toString() { 
       return source + " " + destination; 
     } 


} 

グラフ(ノード+エッジ)は、このクラスによって提示されます:グラフ

public class Graph { 
     private final List<Vertex> vertexes; 
     private final List<Edge> edges; 

     public Graph(List<Vertex> vertexes, List<Edge> edges) { 
       this.vertexes = vertexes; 
       this.edges = edges; 
     } 

     public List<Vertex> getVertexes() { 
       return vertexes; 
     } 

     public List<Edge> getEdges() { 
       return edges; 
     } 



} 

これはダイクストラのアルゴリズムの簡単な実装です。これは、任意のパフォーマンスの最適化を使用していません:

public class DijkstraAlgorithm { 

     private final List<Vertex> nodes; 
     private final List<Edge> edges; 
     private Set<Vertex> settledNodes; 
     private Set<Vertex> unSettledNodes; 
     private Map<Vertex, Vertex> predecessors; 
     private Map<Vertex, Integer> distance; 

     public DijkstraAlgorithm(Graph graph) { 
       // create a copy of the array so that we can operate on this array 
       this.nodes = new ArrayList<Vertex>(graph.getVertexes()); 
       this.edges = new ArrayList<Edge>(graph.getEdges()); 
     } 

     public void execute(Vertex source) { 
       settledNodes = new HashSet<Vertex>(); 
       unSettledNodes = new HashSet<Vertex>(); 
       distance = new HashMap<Vertex, Integer>(); 
       predecessors = new HashMap<Vertex, Vertex>(); 
       distance.put(source, 0); 
       unSettledNodes.add(source); 
       while (unSettledNodes.size() > 0) { 
         Vertex node = getMinimum(unSettledNodes); 
         settledNodes.add(node); 
         unSettledNodes.remove(node); 
         findMinimalDistances(node); 
       } 
     } 

     private void findMinimalDistances(Vertex node) { 
       List<Vertex> adjacentNodes = getNeighbors(node); 
       for (Vertex target : adjacentNodes) { 
         if (getShortestDistance(target) > getShortestDistance(node) 
             + getDistance(node, target)) { 
           distance.put(target, getShortestDistance(node) 
               + getDistance(node, target)); 
           predecessors.put(target, node); 
           unSettledNodes.add(target); 
         } 
       } 

     } 

     private int getDistance(Vertex node, Vertex target) { 
       for (Edge edge : edges) { 
         if (edge.getSource().equals(node) 
             && edge.getDestination().equals(target)) { 
           return edge.getWeight(); 
         } 
       } 
       throw new RuntimeException("Should not happen"); 
     } 

     private List<Vertex> getNeighbors(Vertex node) { 
       List<Vertex> neighbors = new ArrayList<Vertex>(); 
       for (Edge edge : edges) { 
         if (edge.getSource().equals(node) 
             && !isSettled(edge.getDestination())) { 
           neighbors.add(edge.getDestination()); 
         } 
       } 
       return neighbors; 
     } 

     private Vertex getMinimum(Set<Vertex> vertexes) { 
       Vertex minimum = null; 
       for (Vertex vertex : vertexes) { 
         if (minimum == null) { 
           minimum = vertex; 
         } else { 
           if (getShortestDistance(vertex) < getShortestDistance(minimum)) { 
             minimum = vertex; 
           } 
         } 
       } 
       return minimum; 
     } 

     private boolean isSettled(Vertex vertex) { 
       return settledNodes.contains(vertex); 
     } 

     private int getShortestDistance(Vertex destination) { 
       Integer d = distance.get(destination); 
       if (d == null) { 
         return Integer.MAX_VALUE; 
       } else { 
         return d; 
       } 
     } 

     /* 
     * This method returns the path from the source to the selected target and 
     * NULL if no path exists 
     */ 
     public LinkedList<Vertex> getPath(Vertex target) { 
       LinkedList<Vertex> path = new LinkedList<Vertex>(); 
       Vertex step = target; 
       // check if a path exists 
       if (predecessors.get(step) == null) { 
         return null; 
       } 
       path.add(step); 
       while (predecessors.get(step) != null) { 
         step = predecessors.get(step); 
         path.add(step); 
       } 
       // Put it into the correct order 
       Collections.reverse(path); 
       return path; 
     } 

} 

はその後テストクラスを作成し、グラフの値を追加します。

public class TestDijkstraAlgorithm { 

     private List<Vertex> nodes; 
     private List<Edge> edges; 

     @Test 
     public void testExcute() { 
       nodes = new ArrayList<Vertex>(); 
       edges = new ArrayList<Edge>(); 
       for (int i = 0; i < 11; i++) { 
         Vertex location = new Vertex("Node_" + i, "Node_" + i); 
         nodes.add(location); 
       } 

       addLane("Edge_0", 0, 1, 5); 
       addLane("Edge_1", 0, 2, 40); 
       addLane("Edge_2", 0, 3, 21); 
       addLane("Edge_3", 2, 3, 13); 
       addLane("Edge_4", 2, 4, 19); 
       addLane("Edge_5", 4, 5, 32); 
       addLane("Edge_6", 3, 5, 41); 
       addLane("Edge_7", 4, 6, 14); 
       addLane("Edge_8", 5, 6, 8); 


       // Lets check from location Loc_1 to Loc_10 
       Graph graph = new Graph(nodes, edges); 
       DijkstraAlgorithm dijkstra = new DijkstraAlgorithm(graph); 
       dijkstra.execute(nodes.get(0)); 
       LinkedList<Vertex> path = dijkstra.getPath(nodes.get(10)); 

       assertNotNull(path); 
       assertTrue(path.size() > 0); 

       for (Vertex vertex : path) { 
         System.out.println(vertex); 
       } 

     } 

     private void addLane(String laneId, int sourceLocNo, int destLocNo, 
         int duration) { 
       Edge lane = new Edge(laneId,nodes.get(sourceLocNo), nodes.get(destLocNo), duration); 
       edges.add(lane); 
     } 
} 
+0

'private final List nodes;'は使用されず、 'assertNotNull(path);でテストが失敗します。 –

関連する問題