📜  有向无环图中的最长路径

📅  最后修改于: 2022-05-13 01:57:54.294000             🧑  作者: Mango

有向无环图中的最长路径

给定一个加权有向 A循环图 ( DAG ) 和其中的源顶点 s,找出给定图中从 s 到所有其他顶点的最长距离。
一般图的最长路径问题不像最短路径问题那样容易,因为最长路径问题没有最优子结构性质。实际上,对于一般图,最长路径问题是 NP-Hard 问题。然而,最长路径问题对于有向无环图具有线性时间解。这个想法类似于有向无环图中最短路径的线性时间解决方案。我们使用拓扑排序。
我们将到所有顶点的距离初始化为负无穷,将到源的距离初始化为 0,然后我们找到图的拓扑排序。图的拓扑排序表示图的线性排序(见下图(b)是图(a)的线性表示)。一旦我们有了拓扑顺序(或线性表示),我们就按拓扑顺序一个一个地处理所有顶点。对于每个正在处理的顶点,我们使用当前顶点的距离更新其相邻的距离。
下图显示了寻找最长路径的逐步过程。

最长路径

以下是查找最长距离的完整算法。
1)初始化 dist[] = {NINF, NINF, ....} 和 dist[s] = 0 其中 s 是源顶点。这里 NINF 表示负无限。
2)创建所有顶点的拓扑顺序。
3)按照拓扑顺序对每个顶点 u 执行以下操作。
………..对 u 的每个相邻顶点 v 执行以下操作
……………… if (dist[v] < dist[u] + weight(u, v))
…………………………dist[v] = dist[u] + weight(u, v)

以下是上述算法的 C++ 实现。

CPP
// A C++ program to find single source longest distances
// in a DAG
#include 
#include 
#include 
#include 
#define NINF INT_MIN
using namespace std;
 
// Graph is represented using adjacency list. Every
// node of adjacency list contains vertex number of
// the vertex to which edge connects. It also
// contains weight of the edge
class AdjListNode {
    int v;
    int weight;
   
public:
    AdjListNode(int _v, int _w)
    {
        v = _v;
        weight = _w;
    }
    int getV() { return v; }
    int getWeight() { return weight; }
};
   
// Class to represent a graph using adjacency list
// representation
class Graph {
    int V; // No. of vertices'
   
    // Pointer to an array containing adjacency lists
    list* adj;
   
    // A function used by longestPath
    void topologicalSortUtil(int v, bool visited[],
                             stack& Stack);
   
public:
    Graph(int V); // Constructor
    ~Graph(); // Destructor
 
    // function to add an edge to graph
    void addEdge(int u, int v, int weight);
   
    // Finds longest distances from given source vertex
    void longestPath(int s);
};
   
Graph::Graph(int V) // Constructor
{
    this->V = V;
    adj = new list[V];
}
 
Graph::~Graph() // Destructor
{
    delete [] adj;
}
 
 
void Graph::addEdge(int u, int v, int weight)
{
    AdjListNode node(v, weight);
    adj[u].push_back(node); // Add v to u's list
}
   
// A recursive function used by longestPath. See below
// link for details
// https:// www.geeksforgeeks.org/topological-sorting/
void Graph::topologicalSortUtil(int v, bool visited[],
                                stack& Stack)
{
    // Mark the current node as visited
    visited[v] = true;
   
    // Recur for all the vertices adjacent to this vertex
    list::iterator i;
    for (i = adj[v].begin(); i != adj[v].end(); ++i) {
        AdjListNode node = *i;
        if (!visited[node.getV()])
            topologicalSortUtil(node.getV(), visited, Stack);
    }
   
    // Push current vertex to stack which stores topological
    // sort
    Stack.push(v);
}
   
// The function to find longest distances from a given vertex.
// It uses recursive topologicalSortUtil() to get topological
// sorting.
void Graph::longestPath(int s)
{
    stack Stack;
    int dist[V];
   
    // Mark all the vertices as not visited
    bool* visited = new bool[V];
    for (int i = 0; i < V; i++)
        visited[i] = false;
   
    // Call the recursive helper function to store Topological
    // Sort starting from all vertices one by one
    for (int i = 0; i < V; i++)
        if (visited[i] == false)
            topologicalSortUtil(i, visited, Stack);
   
    // Initialize distances to all vertices as infinite and
    // distance to source as 0
    for (int i = 0; i < V; i++)
        dist[i] = NINF;
    dist[s] = 0;
    // Process vertices in topological order
    while (Stack.empty() == false) {
        // Get the next vertex from topological order
        int u = Stack.top();
        Stack.pop();
   
        // Update distances of all adjacent vertices
        list::iterator i;
        if (dist[u] != NINF) {
            for (i = adj[u].begin(); i != adj[u].end(); ++i){
             
                if (dist[i->getV()] < dist[u] + i->getWeight())
                    dist[i->getV()] = dist[u] + i->getWeight();
            }
        }
    }
   
    // Print the calculated longest distances
    for (int i = 0; i < V; i++)
        (dist[i] == NINF) ? cout << "INF " : cout << dist[i] << " ";
     
    delete [] visited;
}
   
// Driver program to test above functions
int main()
{
    // Create a graph given in the above diagram.
    // Here vertex numbers are 0, 1, 2, 3, 4, 5 with
    // following mappings:
    // 0=r, 1=s, 2=t, 3=x, 4=y, 5=z
    Graph g(6);
    g.addEdge(0, 1, 5);
    g.addEdge(0, 2, 3);
    g.addEdge(1, 3, 6);
    g.addEdge(1, 2, 2);
    g.addEdge(2, 4, 4);
    g.addEdge(2, 5, 2);
    g.addEdge(2, 3, 7);
    g.addEdge(3, 5, 1);
    g.addEdge(3, 4, -1);
    g.addEdge(4, 5, -2);
   
    int s = 1;
    cout << "Following are longest distances from "
            "source vertex "
         << s << " \n";
    g.longestPath(s);
   
    return 0;
}


Java
// A Java program to find single source longest distances
// in a DAG
import java.util.*;
class GFG
{
   
  // Graph is represented using adjacency list. Every
  // node of adjacency list contains vertex number of
  // the vertex to which edge connects. It also
  // contains weight of the edge
  static class AdjListNode {
    int v;
    int weight;
 
    AdjListNode(int _v, int _w)
    {
      v = _v;
      weight = _w;
    }
    int getV() { return v; }
    int getWeight() { return weight; }
  }
 
  // Class to represent a graph using adjacency list
  // representation
  static class Graph {
    int V; // No. of vertices'
 
    // Pointer to an array containing adjacency lists
    ArrayList> adj;
 
    Graph(int V) // Constructor
    {
      this.V = V;
      adj = new ArrayList>(V);
 
      for(int i = 0; i < V; i++){
        adj.add(new ArrayList());
      }
    }
 
    void addEdge(int u, int v, int weight)
    {
      AdjListNode node = new AdjListNode(v, weight);
      adj.get(u).add(node); // Add v to u's list
    }
 
    // A recursive function used by longestPath. See below
    // link for details
    // https:// www.geeksforgeeks.org/topological-sorting/
    void topologicalSortUtil(int v, boolean visited[],
                             Stack stack)
    {
      // Mark the current node as visited
      visited[v] = true;
 
      // Recur for all the vertices adjacent to this vertex
      for (int i = 0; i stack = new Stack();
      int dist[] = new int[V];
 
      // Mark all the vertices as not visited
      boolean visited[] = new boolean[V];
      for (int i = 0; i < V; i++)
        visited[i] = false;
 
      // Call the recursive helper function to store Topological
      // Sort starting from all vertices one by one
      for (int i = 0; i < V; i++)
        if (visited[i] == false)
          topologicalSortUtil(i, visited, stack);
 
      // Initialize distances to all vertices as infinite and
      // distance to source as 0
      for (int i = 0; i < V; i++)
        dist[i] = Integer.MIN_VALUE;
 
      dist[s] = 0;
       
      // Process vertices in topological order
      while (stack.isEmpty() == false)
      {
         
        // Get the next vertex from topological order
        int u = stack.peek();
        stack.pop();
 
        // Update distances of all adjacent vertices ;
        if (dist[u] != Integer.MIN_VALUE)
        {
          for (int i = 0; i


Python3
# A recursive function used by longestPath. See below
# link for details
# https:#www.geeksforgeeks.org/topological-sorting/
def topologicalSortUtil(v):
    global Stack, visited, adj
    visited[v] = True
 
    # Recur for all the vertices adjacent to this vertex
    # list::iterator i
    for i in adj[v]:
        if (not visited[i[0]]):
            topologicalSortUtil(i[0])
 
    # Push current vertex to stack which stores topological
    # sort
    Stack.append(v)
 
# The function to find longest distances from a given vertex.
# It uses recursive topologicalSortUtil() to get topological
# sorting.
def longestPath(s):
    global Stack, visited, adj, V
    dist = [-10**9 for i in range(V)]
 
    # Call the recursive helper function to store Topological
    # Sort starting from all vertices one by one
    for i in range(V):
        if (visited[i] == False):
            topologicalSortUtil(i)
    # print(Stack)
 
    # Initialize distances to all vertices as infinite and
    # distance to source as 0
    dist[s] = 0
    # Stack.append(1)
 
    # Process vertices in topological order
    while (len(Stack) > 0):
       
        # Get the next vertex from topological order
        u = Stack[-1]
        del Stack[-1]
        #print(u)
 
        # Update distances of all adjacent vertices
        # list::iterator i
        if (dist[u] != 10**9):
            for i in adj[u]:
                # print(u, i)
                if (dist[i[0]] < dist[u] + i[1]):
                    dist[i[0]] = dist[u] + i[1]
 
    # Print calculated longest distances
    # print(dist)
    for i in range(V):
        print("INF ",end="") if (dist[i] == -10**9) else print(dist[i],end=" ")
 
# Driver code
if __name__ == '__main__':
    V, Stack, visited = 6, [], [False for i in range(7)]
    adj = [[] for i in range(7)]
     
    # Create a graph given in the above diagram.
    # Here vertex numbers are 0, 1, 2, 3, 4, 5 with
    # following mappings:
    # 0=r, 1=s, 2=t, 3=x, 4=y, 5=z
    adj[0].append([1, 5])
    adj[0].append([2, 3])
    adj[1].append([3, 6])
    adj[1].append([2, 2])
    adj[2].append([4, 4])
    adj[2].append([5, 2])
    adj[2].append([3, 7])
    adj[3].append([5, 1])
    adj[3].append([4, -1])
    adj[4].append([5, -2])
 
    s = 1
    print("Following are longest distances from source vertex ",s)
    longestPath(s)
 
    # This code is contributed by mohit kumar 29.


输出:

Following are longest distances from source vertex 1
INF 0 2 9 8 10

时间复杂度:拓扑排序的时间复杂度为 O(V+E)。找到拓扑顺序后,算法处理所有顶点,并且对于每个顶点,它对所有相邻顶点运行一个循环。图中的相邻顶点总数为 O(E)。所以内部循环运行 O(V+E) 次。因此,该算法的整体时间复杂度为 O(V+E)。
练习:上述解决方案打印最长的距离,也将代码扩展到打印路径。

微焦点