使用二叉堆实现 Dijkstra 算法

Posted

技术标签:

【中文标题】使用二叉堆实现 Dijkstra 算法【英文标题】:Implementing Dijkstra's algorithm using Binary Heaps 【发布时间】:2018-05-16 17:21:34 【问题描述】:

您将如何使用二进制堆实现 Dijkstra 算法?我的目标是运行时间为 O(M log N)。

假设一个王国有 N 个城市,这个王国有 M 条火车路线,S 是首都。

第一个输入是 N M S 后跟一个由 M 分隔的三元组(U、V 和 D)的列表,这意味着有一条从城市 U 到城市 V 的火车路线需要 D 天。请注意,这条火车路线只能从城市 U 到 V,不能从 V 到 U。

输出是包含 N 个整数的空格分隔列表的一行,其中第 I 个整数是从城市 I 到城市 S 的最少旅行天数。如果不可能从城市 I 到城市旅行S,第I个整数输出 - 1。

如果样本输入是这样的:

 4 4 4
 1 4 1
 3 1 2
 3 4 4
 4 2 1

那么输出是:

 1 -1 3 0

这是另一个例子:

 5 8 2
 3 2 2
 2 3 2
 2 5 2
 5 2 2
 4 2 2
 2 4 2
 1 4 2
 2 1 2

输出是:

 4 0 2 2 2

我的目标是尝试使用二进制堆来解决这个问题,但我在这样做时遇到了麻烦。我现在正在使用邻接列表,我会看看我是否可以在上面发布代码,但如果你能帮助我,它真的会有所帮助。

感谢您的所有帮助。

编辑:这是我使用邻接列表的代码。

//import static jdk.nashorn.internal.runtime.regexp.joni.Syntax.Java;

导入 java.util.Scanner;

公共类 Dijkstra public static void main(String[] args)

int N, M, S;

Scanner scan = new Scanner(System.in);
N = scan.nextInt(); // # cities
M = scan.nextInt(); // # train routes
S = scan.nextInt(); // capital city
// System.out.println(N + " " + M + " " + S);


// NOW THE ARRAYS
int [][] A = new int[50010][60]; // the neighbors of each city
int [][] W = new int[50010][60]; // the weights of going to neighbors
int []   deg = new int[50010]; // the degree of the city
// The limits are 50,010 and 60 because the problem statement said that there are at most
// 50,000 cities, and we just added 10 just to be sure. We have 60 because the maximum number of
// train routes is 50, and we just added 10 to that.

// with each incoming vertex/city, we will at first initialize the degree to be 0
for(int i = 1; i <=N; ++i) 
  deg[i] = 0;  // initialize the degree of each vertex to 0


// this is for each of the train routes
for(int i = 1; i <= M; ++i) 
  int u, v, w;
  u = scan.nextInt(); // origin
  v = scan.nextInt(); // destination
  w = scan.nextInt(); // # days
  // System.out.println(u + " " + v + " " + w);

  // WITH THE ARRAYS
  A[u][deg[u]] = v; // adding an edge (u,v) to the graph where u is origin and deg[u] is weight
  W[u][deg[u]] = w; // set its weight to w, the number of days it takes
  deg[u]++; // increase degree of vertex u by 1


//for(int i = 1; i <= N; ++i) 
//  System.out.println("vertex:" + i + "'s neighbors");
//  for(int j = 0; j < deg[i]; ++j) 
//    System.out.println(A[i][j] + " " + W[i][j]);
//  
//




// compute distance from U (origin) to S (capital city) by Dijkstra's algorithm
// Dijkstra's algorithm: find the shortest path distance from each vertex to the capital
for(int U = 1; U <= N; ++U) 

  // INITIALIZATION
  int[] visited = new int[50010]; // create an empty array w/ max # cities space for cities that are visited
  int[] dist = new int[50010]; // create an empty array w/ max # cities space for distance of each city
  // loop that goes through the arrays and fills in values up to N number of cities
  for(int V = 1; V <= N; ++V) 
    dist[V] = 100000000; // set the distance of the city to the capital to be the maximum possible number
    visited[V] = 0; // set the cities that are visited to be 0
  

  // ACTUAL ALGORITHM
  dist[U] = 0; // set the distance of the city to be 0

  for(int k = 1; k <= N; ++k) 
    //find an unvisited vertex with minimum distance
    int min = 100000000;
    int minVertex = 1;

    for(int i = 1; i<=N; ++i) 
      // if the city has not been visited and the distance from it to the capital is less than the minimum
      if(visited[i] == 0 && dist[i] < min) 
        min = dist[i]; // set the new minimum to be this distance
        minVertex = i; // set the minimum vertex to be this number
      
    

    visited[minVertex] = 1; // set this value to 1 to show that the city has been visited

    // relax the edges that are adjacent to minVertex to update the shortest path distance to
    // neighbors of minVertex
    for(int j = 0; j < deg[minVertex]; ++j)  // this is updating the minimum weight of the city
      // A[minVertex][j] is the j-th neighbor of minVertex
      // W[minVertex][j] is the weight of the corresponding edge
      int newDist = dist[minVertex] + W[minVertex][j];
      if (newDist < dist[A[minVertex][j]]) 
        dist[A[minVertex][j]] = newDist;
      
    
  

  if(dist[S] == 100000000)  // if the distance of this city is still the maximum, it does not have a connection
    System.out.print("-1 ");
  
  else  // if it has a distance less than max, it means there is a minimum distance and we will print that
    System.out.print(dist[S] + " ");
  



System.out.println("");

【问题讨论】:

作为本网站的一般规则,您应该在问题中发布一些代码,向我们展示您已经尝试过的内容。 你没有说你的程序应该找到什么。 @ubadub 对不起,我是这个网站的新手。我发布了上面的代码 @MattTimmermans 我应该输出一行,其中包含以空格分隔的 N 个整数列表,其中第 I 个整数是从城市 I 到城市 S(首都城市)。如果无法从城市 I 到城市 S,则输出第 I 个整数 -1。 倒转火车路线,计算从 S 到 all I 所需的时间。 【参考方案1】:

要获得这种时间复杂度,您需要使用利用堆的次优先级队列(它不是完全排序的结构)。更多信息请看here。

使用堆的想法是,您将任何节点的所有后继者放入优先队列中,这是在堆的帮助下实现的,并从队列中删除成本最低的节点。因为堆结构没有完全排序并且在堆/队列中插入一个节点是~logN,你也可以得到更多关于这个here的信息

【讨论】:

以上是关于使用二叉堆实现 Dijkstra 算法的主要内容,如果未能解决你的问题,请参考以下文章

P4779 模板单源最短路径(标准版)二叉堆优化的 Dijkstra

二叉堆的实现

Java数据结构与算法解析(十四)——二叉堆

Java数据结构与算法解析(十四)——二叉堆

C++ 不知树系列之二叉堆排序(递归和非递归实现上沉下沉算法)

算法与数据结构堆排序是什么鬼?