📌  相关文章
📜  未加权图中的多源最短路径

📅  最后修改于: 2021-04-29 04:08:44             🧑  作者: Mango

假设有n个城镇通过m条双向道路连接。其中有城镇,设有警察局。我们想找出每个城镇到最近的警察局的距离。如果城镇本身只有1,则距离为0。

例子:

Input : 
Number of Vertices = 6
Number of Edges = 9
Towns with Police Station : 1, 5
Edges:
1 2
1 6
2 6
2 3
3 6
5 4
6 5
3 4
5 3

Output :
1 0
2 1
3 1
4 1
5 0
6 1

幼稚的方法:我们可以遍历顶点,并从每个顶点运行BFS,以从该顶点找到最近的带有警察局的城镇。这将花费O(VE)。

在每个顶点上使用BFS的幼稚方法实现:

C++
// C++ program to demonstrate distance to
// nearest source problem using BFS
// from each vertex
#include 
using namespace std;
#define N 100000 + 1
#define inf 1000000
 
// This array stores the distances of the
// vertices from the nearest source
int dist[N];
 
// a hash array where source[i] = 1
// means vertex i is a source
int source[N];
 
// The BFS Queue
// The pairs are of the form (vertex, distance
// from current source)
deque > BFSQueue;
 
// visited array for remembering visited vertices
int visited[N];
 
// The BFS function
void BFS(vector graph[], int start)
{
    // clearing the queue
    while (!BFSQueue.empty())
        BFSQueue.pop_back();
 
    // push_back starting vertices
    BFSQueue.push_back({ start, 0 });
 
    while (!BFSQueue.empty()) {
 
        int s = BFSQueue.front().first;
        int d = BFSQueue.front().second;
        visited[s] = 1;
        BFSQueue.pop_front();
 
        // stop at the first source we reach during BFS
        if (source[s] == 1) {
            dist[start] = d;
            return;
        }
 
        // Pushing the adjacent unvisited vertices
        // with distance from current source = this
        // vertex's distance  + 1
        for (int i = 0; i < graph[s].size(); i++)
            if (visited[graph[s][i]] == 0)
                BFSQueue.push_back({ graph[s][i], d + 1 });
    }
}
 
// This function calculates the distance of each
// vertex from nearest source
void nearestTown(vector graph[], int n,
                       int sources[], int S)
{
 
    // reseting the source hash array
    for (int i = 1; i <= n; i++)
        source[i] = 0;
    for (int i = 0; i <= S - 1; i++)
        source[sources[i]] = 1;
 
    // loop through all the vertices and run
    // a BFS from each vertex to find the distance
    // to nearest town from it
    for (int i = 1; i <= n; i++) {
        for (int i = 1; i <= n; i++)
            visited[i] = 0;
        BFS(graph, i);
    }
 
    // Printing the distances
    for (int i = 1; i <= n; i++)
        cout << i << " " << dist[i] << endl;
}
 
void addEdge(vector graph[], int u, int v)
{
    graph[u].push_back(v);
    graph[v].push_back(u);
}
 
// Driver Code
int main()
{    // Number of vertices
    int n = 6;
 
    vector graph[n + 1];
 
    // Edges
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 6);
    addEdge(graph, 2, 6);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 6);
    addEdge(graph, 5, 4);
    addEdge(graph, 6, 5);
    addEdge(graph, 3, 4);
    addEdge(graph, 5, 3);
 
    // Sources
    int sources[] = { 1, 5 };
 
    int S = sizeof(sources) / sizeof(sources[0]);
 
    nearestTown(graph, n, sources, S);
 
    return 0;
}


Java
// Java program to demonstrate distance to
// nearest source problem using BFS
// from each vertex
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
 
class Pair
{
    int first, second;
 
    public Pair(int first, int second)
    {
        this.first = first;
        this.second = second;
    }
}
 
class GFG{
 
static final int N = 100000 + 1;
static final int inf = 1000000;
 
// This array stores the distances of the
// vertices from the nearest source
static int[] dist = new int[N];
 
// a hash array where source[i] = 1
// means vertex i is a source
static int[] source = new int[N];
 
// The BFS Queue
// The pairs are of the form (vertex, distance
// from current source)
static Deque BFSQueue = new LinkedList<>();
// deque > BFSQueue;
 
// visited array for remembering visited vertices
static int[] visited = new int[N];
 
// The BFS function
static void BFS(ArrayList[] graph, int start)
{
     
    // Clearing the queue
    while (!BFSQueue.isEmpty())
        BFSQueue.removeLast();
 
    // push_back starting vertices
    BFSQueue.add(new Pair(start, 0));
 
    while (!BFSQueue.isEmpty())
    {
        int s = BFSQueue.peekFirst().first;
        int d = BFSQueue.peekFirst().second;
        visited[s] = 1;
        BFSQueue.removeFirst();
 
        // Stop at the first source
        // we reach during BFS
        if (source[s] == 1)
        {
            dist[start] = d;
            return;
        }
 
        // Pushing the adjacent unvisited vertices
        // with distance from current source = this
        // vertex's distance + 1
        for(int i = 0; i < graph[s].size(); i++)
            if (visited[graph[s].get(i)] == 0)
                BFSQueue.add(new Pair(
                    graph[s].get(i), d + 1));
    }
}
 
// This function calculates the distance of each
// vertex from nearest source
static void nearestTown(ArrayList[] graph,
                        int n, int sources[], int S)
{
     
    // Reseting the source hash array
    for(int i = 1; i <= n; i++)
        source[i] = 0;
    for(int i = 0; i <= S - 1; i++)
        source[sources[i]] = 1;
 
    // Loop through all the vertices and run
    // a BFS from each vertex to find the distance
    // to nearest town from it
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
            visited[j] = 0;
             
        BFS(graph, i);
    }
 
    // Printing the distances
    for(int i = 1; i <= n; i++)
        System.out.println(i + " " + dist[i]);
}
 
static void addEdge(ArrayList[] graph,
                    int u, int v)
{
    graph[u].add(v);
    graph[v].add(u);
}
 
// Driver Code
public static void main(String[] args)
{
     
    // Number of vertices
    int n = 6;
 
    @SuppressWarnings("unchecked")
    ArrayList[] graph = new ArrayList[n + 1];
    Arrays.fill(graph, new ArrayList<>());
 
    // Edges
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 6);
    addEdge(graph, 2, 6);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 6);
    addEdge(graph, 5, 4);
    addEdge(graph, 6, 5);
    addEdge(graph, 3, 4);
    addEdge(graph, 5, 3);
 
    // Sources
    int sources[] = { 1, 5 };
 
    int S = sources.length;
 
    nearestTown(graph, n, sources, S);
}
}
 
// This code is contributed by sanjeev2552


Python3
# Python3 program to demonstrate distance to
# nearest source problem using BFS
# from each vertex
 
N = 100001
inf = 1000000
  
# This array stores the distances of the
# vertices from the nearest source
dist = [0 for i in range(N)];
  
# a hash array where source[i] = 1
# means vertex i is a source
source = [0 for i in range(N)];
  
# The BFS Queue
# The pairs are of the form (vertex, distance
# from current source)
BFSQueue = []
  
# visited array for remembering visited vertices
visited = [0 for i in range(N)];
  
# The BFS function
def BFS(graph, start):
 
    # clearing the queue
    while (len(BFSQueue) != 0):
        BFSQueue.pop();
  
    # append starting vertices
    BFSQueue.append([ start, 0 ]);
  
    while (len(BFSQueue) != 0):
  
        s = BFSQueue[0][0];
        d = BFSQueue[0][1];
 
        visited[s] = 1;
        BFSQueue.pop(0);
  
        # stop at the first source we reach during BFS
        if (source[s] == 1):
            dist[start] = d;
            return;
          
        # Pushing the adjacent unvisited vertices
        # with distance from current source = this
        # vertex's distance  + 1
        for i in range(len(graph[s])):
         
            if (visited[graph[s][i]] == 0):
                BFSQueue.append([ graph[s][i], d + 1 ]);
      
# This function calculates the distance of each
# vertex from nearest source
def nearestTown(graph, n, sources, S):
    global source, dist
     
    # reseting the source hash array
    for i in range(1, n + 1):
        source[i] = 0;
         
    for i in range(S):
        source[sources[i]] = 1;
  
    # loop through all the vertices and run
    # a BFS from each vertex to find the distance
    # to nearest town from it
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            visited[j] = 0;
        BFS(graph, i);
  
    # Printing the distances
    for i in range(1, n + 1):
         
        print('{} {}'.format(i,dist[i]))
          
def addEdge(graph, u, v):
 
    graph[u].append(v);
    graph[v].append(u);
  
# Driver Code
if __name__=='__main__':
     
    # Number of vertices
    n = 6
  
    graph = [[] for i in range(n + 1)];
  
    # Edges
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 6);
    addEdge(graph, 2, 6);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 6);
    addEdge(graph, 5, 4);
    addEdge(graph, 6, 5);
    addEdge(graph, 3, 4);
    addEdge(graph, 5, 3);
  
    # Sources
    sources = [ 1, 5 ]
  
    S = len(sources)
  
    nearestTown(graph, n, sources, S);
  
# This code is contributed by rutvik_56


C++
// C++ program to demonstrate
// multi-source BFS
#include 
using namespace std;
#define N 100000 + 1
#define inf 1000000
 
// This array stores the distances of the vertices
// from the nearest source
int dist[N];
 
// This Set contains the vertices not yet visited in
// increasing order of distance from the nearest source
// calculated till now
set > Q;
 
// Util function for Multi-Source BFS
void multiSourceBFSUtil(vector graph[], int s)
{
    set >::iterator it;
    int i;
    for (i = 0; i < graph[s].size(); i++) {
        int v = graph[s][i];
        if (dist[s] + 1 < dist[v]) {
 
            // If a shorter path to a vertex is
            // found than the currently stored
            // distance replace it in the Q
            it = Q.find({ dist[v], v });
            Q.erase(it);
            dist[v] = dist[s] + 1;
            Q.insert({ dist[v], v });
        }
    }
 
    // Stop when the Q is empty -> All
    // vertices have been visited. And we only
    // visit a vertex when we are sure that a
    // shorter path to that vertex is not
    // possible
    if (Q.size() == 0)
        return;
 
    // Go to the first vertex in Q
    // and remove it from the Q
    it = Q.begin();
    int next = it->second;
    Q.erase(it);
 
    multiSourceBFSUtil(graph, next);
}
 
// This function calculates the distance of
// each vertex from nearest source
void multiSourceBFS(vector graph[], int n,
                          int sources[], int S)
{
    // a hash array where source[i] = 1
    // means vertex i is a source
    int source[n + 1];
 
    for (int i = 1; i <= n; i++)
        source[i] = 0;
    for (int i = 0; i <= S - 1; i++)
        source[sources[i]] = 1;
 
    for (int i = 1; i <= n; i++) {
        if (source[i]) {
            dist[i] = 0;
            Q.insert({ 0, i });
        }
        else {
            dist[i] = inf;
            Q.insert({ inf, i });
        }
    }
 
    set >::iterator itr;
 
    // Get the vertex with lowest distance,
    itr = Q.begin();
 
    // currently one of the souces with distance = 0
    int start = itr->second;
 
    multiSourceBFSUtil(graph, start);
 
    // Printing the distances
    for (int i = 1; i <= n; i++)
        cout << i << " " << dist[i] << endl;
}
 
void addEdge(vector graph[], int u, int v)
{
    graph[u].push_back(v);
    graph[v].push_back(u);
}
 
// Driver Code
int main()
{
    // Number of vertices
    int n = 6;
 
    vector graph[n + 1];
 
    // Edges
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 6);
    addEdge(graph, 2, 6);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 6);
    addEdge(graph, 5, 4);
    addEdge(graph, 6, 5);
    addEdge(graph, 3, 4);
    addEdge(graph, 5, 3);
 
    // Sources
    int sources[] = { 1, 5 };
 
    int S = sizeof(sources) / sizeof(sources[0]);
 
    multiSourceBFS(graph, n, sources, S);
 
    return 0;
}


C++
// C++ program to demonstrate Multi-source BFS
#include
using namespace std;
#define N 1000000
 
// This array stores the distances of the vertices
// from the nearest source
int dist[N];
 
//This boolean array is true if the current vertex
// is visited otherwise it is false
bool visited[N];
 
 
void addEdge(vector graph[], int u, int v)
{
    //Function to add 2 edges in an undirected graph
    graph[u].push_back(v);
    graph[v].push_back(u);
}
 
// Multisource BFS Function
void Multisource_BFS(vector graph[],queueq)
{
    while(!q.empty())
    {
        int k = q.front();
        q.pop();
     
        for(auto i:graph[k])
        {
            if(!visited[i])
            {
     
                // Pushing the adjacent unvisited vertices
                // with distance from current source = this
                // vertex's distance + 1
                q.push(i);
                dist[i] = dist[k] + 1;
                visited[i] = true;
            }
        }
    }
}
 
 
// This function calculates the distance of each
// vertex from nearest source
void nearestTown(vector graph[],int n,int sources[],int s)
{
    //Create a queue for BFS
    queue q;
 
    //Mark all the source vertices as visited and enqueue it
    for(int i = 0;i < s; i++)
    {
        q.push(sources[i]);
        visited[sources[i]]=true;
    }
 
    Multisource_BFS(graph,q);
 
 
    //Printing the distances
    for(int i = 1; i <= n; i++)
    {
        cout<< i << " " << dist[i] << endl;
    }
 
}
 
 
// Driver code
int main()
{    
    // Number of vertices
    int n = 6;
 
    vector graph[n + 1];
 
    // Edges
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 6);
    addEdge(graph, 2, 6);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 6);
    addEdge(graph, 5, 4);
    addEdge(graph, 6, 5);
    addEdge(graph, 3, 4);
    addEdge(graph, 5, 3);
 
    // Sources
    int sources[] = { 1, 5 };
 
    int S = sizeof(sources) / sizeof(sources[0]);
 
    nearestTown(graph, n, sources, S);
 
    return 0;
}


输出:

1 0
2 1
3 1
4 1
5 0
6 1

时间复杂度: O(VE)

高效的方法更好的方法是以改进的方式使用Djikstra算法。让我们将其中一个源视为原始源,将其他源视为顶点,其原始路径的成本路径为0。因此,我们将所有源推入距离= 0的Djikstra队列中,将其余顶点推入距离= infinity的其余顶点。现在,使用Dijkstra算法计算出的每个顶点到原始源的最小距离实际上就是到最近源的距离。

说明: C++实现使用了一组对(到源的距离,顶点),这些对根据到源的距离进行排序。最初,该集合包含距离= 0的源和距离= infinity的所有其他顶点。
在每个步骤中,我们将到距源(即集合的第一个元素)(源本身在第一步中的距离= 0)具有最小距离d的顶点。我们遍历所有相邻顶点,如果任何顶点的距离大于d +1,我们将其在集合中的条目替换为新的距离。然后我们从集合中删除当前顶点。我们继续执行此操作,直到集合为空。
这个想法是,到集合顶部的顶点不可能有比当前路径短的路径,因为任何其他路径都将是更长路径(> =它的长度)和非负路径长度(除非我们将其相加)的总和正在考虑负面影响)。
由于所有源的距离都为0,因此从一开始,相邻的非源顶点的距离将为1。所有顶点的距离将为距其最近源的距离。

实施有效方法:

C++

// C++ program to demonstrate
// multi-source BFS
#include 
using namespace std;
#define N 100000 + 1
#define inf 1000000
 
// This array stores the distances of the vertices
// from the nearest source
int dist[N];
 
// This Set contains the vertices not yet visited in
// increasing order of distance from the nearest source
// calculated till now
set > Q;
 
// Util function for Multi-Source BFS
void multiSourceBFSUtil(vector graph[], int s)
{
    set >::iterator it;
    int i;
    for (i = 0; i < graph[s].size(); i++) {
        int v = graph[s][i];
        if (dist[s] + 1 < dist[v]) {
 
            // If a shorter path to a vertex is
            // found than the currently stored
            // distance replace it in the Q
            it = Q.find({ dist[v], v });
            Q.erase(it);
            dist[v] = dist[s] + 1;
            Q.insert({ dist[v], v });
        }
    }
 
    // Stop when the Q is empty -> All
    // vertices have been visited. And we only
    // visit a vertex when we are sure that a
    // shorter path to that vertex is not
    // possible
    if (Q.size() == 0)
        return;
 
    // Go to the first vertex in Q
    // and remove it from the Q
    it = Q.begin();
    int next = it->second;
    Q.erase(it);
 
    multiSourceBFSUtil(graph, next);
}
 
// This function calculates the distance of
// each vertex from nearest source
void multiSourceBFS(vector graph[], int n,
                          int sources[], int S)
{
    // a hash array where source[i] = 1
    // means vertex i is a source
    int source[n + 1];
 
    for (int i = 1; i <= n; i++)
        source[i] = 0;
    for (int i = 0; i <= S - 1; i++)
        source[sources[i]] = 1;
 
    for (int i = 1; i <= n; i++) {
        if (source[i]) {
            dist[i] = 0;
            Q.insert({ 0, i });
        }
        else {
            dist[i] = inf;
            Q.insert({ inf, i });
        }
    }
 
    set >::iterator itr;
 
    // Get the vertex with lowest distance,
    itr = Q.begin();
 
    // currently one of the souces with distance = 0
    int start = itr->second;
 
    multiSourceBFSUtil(graph, start);
 
    // Printing the distances
    for (int i = 1; i <= n; i++)
        cout << i << " " << dist[i] << endl;
}
 
void addEdge(vector graph[], int u, int v)
{
    graph[u].push_back(v);
    graph[v].push_back(u);
}
 
// Driver Code
int main()
{
    // Number of vertices
    int n = 6;
 
    vector graph[n + 1];
 
    // Edges
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 6);
    addEdge(graph, 2, 6);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 6);
    addEdge(graph, 5, 4);
    addEdge(graph, 6, 5);
    addEdge(graph, 3, 4);
    addEdge(graph, 5, 3);
 
    // Sources
    int sources[] = { 1, 5 };
 
    int S = sizeof(sources) / sizeof(sources[0]);
 
    multiSourceBFS(graph, n, sources, S);
 
    return 0;
}

输出:

1 0
2 1
3 1
4 1
5 0
6 1

时间复杂度O(E.logV)

更有效的方法:一种更好的方法是使用多源BFS,它是对BFS的修改。我们将所有源顶点放在队列中的第一位,而不是标准BFS情况下的单个顶点。首先将访问所有源顶点。之后,它将访问与所有源顶点相距1的顶点,然后与所有源顶点相距2的顶点,依此类推。

下面是上述方法的实现:

C++

// C++ program to demonstrate Multi-source BFS
#include
using namespace std;
#define N 1000000
 
// This array stores the distances of the vertices
// from the nearest source
int dist[N];
 
//This boolean array is true if the current vertex
// is visited otherwise it is false
bool visited[N];
 
 
void addEdge(vector graph[], int u, int v)
{
    //Function to add 2 edges in an undirected graph
    graph[u].push_back(v);
    graph[v].push_back(u);
}
 
// Multisource BFS Function
void Multisource_BFS(vector graph[],queueq)
{
    while(!q.empty())
    {
        int k = q.front();
        q.pop();
     
        for(auto i:graph[k])
        {
            if(!visited[i])
            {
     
                // Pushing the adjacent unvisited vertices
                // with distance from current source = this
                // vertex's distance + 1
                q.push(i);
                dist[i] = dist[k] + 1;
                visited[i] = true;
            }
        }
    }
}
 
 
// This function calculates the distance of each
// vertex from nearest source
void nearestTown(vector graph[],int n,int sources[],int s)
{
    //Create a queue for BFS
    queue q;
 
    //Mark all the source vertices as visited and enqueue it
    for(int i = 0;i < s; i++)
    {
        q.push(sources[i]);
        visited[sources[i]]=true;
    }
 
    Multisource_BFS(graph,q);
 
 
    //Printing the distances
    for(int i = 1; i <= n; i++)
    {
        cout<< i << " " << dist[i] << endl;
    }
 
}
 
 
// Driver code
int main()
{    
    // Number of vertices
    int n = 6;
 
    vector graph[n + 1];
 
    // Edges
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 6);
    addEdge(graph, 2, 6);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 6);
    addEdge(graph, 5, 4);
    addEdge(graph, 6, 5);
    addEdge(graph, 3, 4);
    addEdge(graph, 5, 3);
 
    // Sources
    int sources[] = { 1, 5 };
 
    int S = sizeof(sources) / sizeof(sources[0]);
 
    nearestTown(graph, n, sources, S);
 
    return 0;
}

输出:

1 0
2 1
3 1
4 1
5 0
6 1

时间复杂度O(V + E)

想要从精选的最佳视频中学习并解决问题,请查看有关从基础到高级C++的C++基础课程以及有关语言和STL的C++ STL课程。要完成从学习语言到DS Algo等的更多准备工作,请参阅“完整面试准备课程”