邻接表表示的Dijkstra算法|贪婪算法-8

我们建议阅读以下两篇文章,作为这篇文章的先决条件。 1. 贪婪算法|集7(Dijkstra最短路径算法) 2. 图及其表示 我们讨论过 图的邻接矩阵表示的Dijkstra算法及其实现 .矩阵表示的时间复杂度为O(V^2)。本文讨论了邻接表表示的O(ELogV)算法。 如前一篇文章所述,在Dijkstra的算法中,保留了两个集合,一个集合包含SPT(最短路径树)中已包含的顶点列表,另一个集合包含尚未包含的顶点。使用邻接列表表示法,可以在O(V+E)时间内使用 BFS 。其思想是使用 BFS 并使用最小堆存储尚未包含在SPT中的顶点(或尚未确定最短距离的顶点)。Min Heap用作优先级队列,以从尚未包含的顶点集中获取最小距离顶点。对于最小堆,提取最小值和减少键值等操作的时间复杂度为O(LogV)。 以下是详细的步骤。 1) 创建一个大小为V的最小堆,其中V是给定图形中的顶点数。最小堆的每个节点都包含顶点数和顶点的距离值。 2) 以源顶点为根初始化最小堆(分配给源顶点的距离值为0)。指定给所有其他顶点的距离值为INF(无限)。 3) 当Min Heap不为空时,执行以下操作 ….. (a) 从Min Heap中提取具有最小距离值节点的顶点。将提取的顶点设为u。 ….. b) 对于u的每个相邻顶点v,检查v是否在最小堆中。如果v在最小堆中,且距离值大于u-v的权重加上u的距离值,则更新v的距离值。 让我们用下面的例子来理解。设给定的源顶点为0

null

Dijkstra’s Algorithm for Adjacency List Representation

最初,源顶点的距离值为0,所有其他顶点的距离值为INF(无限)。因此,从最小堆中提取源顶点,并更新与0(1和7)相邻的顶点的距离值。最小堆包含除顶点0之外的所有顶点。 绿色的顶点是最终确定最小距离且不在最小堆中的顶点

Dijkstra’s Algorithm for Adjacency List Representation Step 1

由于顶点1的距离值在最小堆中的所有节点中都是最小的,因此会从最小堆中提取该距离值,并更新与1相邻的顶点的距离值(如果a顶点在最小堆中且通过1的距离小于之前的距离,则更新距离)。最小堆包含除顶点0和1之外的所有顶点。

Dijkstra’s Algorithm for Adjacency List Representation Step 2

从最小堆中拾取具有最小距离值的顶点。顶点7已拾取。所以min heap现在包含除0、1和7之外的所有顶点。更新7的相邻顶点的距离值。顶点6和8的距离值变得有限(分别为15和9)。

Dijkstra’s Algorithm for Adjacency List Representation Step 3

拾取与最小堆之间距离最小的顶点。顶点6已拾取。所以min heap现在包含除0、1、7和6之外的所有顶点。更新6的相邻顶点的距离值。顶点5和8的距离值将更新。

Dijkstra’s Algorithm for Adjacency List Representation Step 4

重复上述步骤,直到最小堆不为空。最后,我们得到以下最短路径树。

Dijkstra’s Algorithm for Adjacency List Representation Step 5

C++

// C / C++ program for Dijkstra's
// shortest path algorithm for adjacency
// list representation of graph
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
// A structure to represent a
// node in adjacency list
struct AdjListNode
{
int dest;
int weight;
struct AdjListNode* next;
};
// A structure to represent
// an adjacency list
struct AdjList
{
// Pointer to head node of list
struct AdjListNode *head;
};
// A structure to represent a graph.
// A graph is an array of adjacency lists.
// Size of array will be V (number of
// vertices in graph)
struct Graph
{
int V;
struct AdjList* array;
};
// A utility function to create
// a new adjacency list node
struct AdjListNode* newAdjListNode(
int dest, int weight)
{
struct AdjListNode* newNode =
( struct AdjListNode*)
malloc ( sizeof ( struct AdjListNode));
newNode->dest = dest;
newNode->weight = weight;
newNode->next = NULL;
return newNode;
}
// A utility function that creates
// a graph of V vertices
struct Graph* createGraph( int V)
{
struct Graph* graph = ( struct Graph*)
malloc ( sizeof ( struct Graph));
graph->V = V;
// Create an array of adjacency lists.
// Size of array will be V
graph->array = ( struct AdjList*)
malloc (V * sizeof ( struct AdjList));
// Initialize each adjacency list
// as empty by making head as NULL
for ( int i = 0; i < V; ++i)
graph->array[i].head = NULL;
return graph;
}
// Adds an edge to an undirected graph
void addEdge( struct Graph* graph, int src,
int dest, int weight)
{
// Add an edge from src to dest.
// A new node is added to the adjacency
// list of src.  The node is
// added at the beginning
struct AdjListNode* newNode =
newAdjListNode(dest, weight);
newNode->next = graph->array[src].head;
graph->array[src].head = newNode;
// Since graph is undirected,
// add an edge from dest to src also
newNode = newAdjListNode(src, weight);
newNode->next = graph->array[dest].head;
graph->array[dest].head = newNode;
}
// Structure to represent a min heap node
struct MinHeapNode
{
int v;
int dist;
};
// Structure to represent a min heap
struct MinHeap
{
// Number of heap nodes present currently
int size;
// Capacity of min heap
int capacity;
// This is needed for decreaseKey()
int *pos;
struct MinHeapNode **array;
};
// A utility function to create a
// new Min Heap Node
struct MinHeapNode* newMinHeapNode( int v,
int dist)
{
struct MinHeapNode* minHeapNode =
( struct MinHeapNode*)
malloc ( sizeof ( struct MinHeapNode));
minHeapNode->v = v;
minHeapNode->dist = dist;
return minHeapNode;
}
// A utility function to create a Min Heap
struct MinHeap* createMinHeap( int capacity)
{
struct MinHeap* minHeap =
( struct MinHeap*)
malloc ( sizeof ( struct MinHeap));
minHeap->pos = ( int *) malloc (
capacity * sizeof ( int ));
minHeap->size = 0;
minHeap->capacity = capacity;
minHeap->array =
( struct MinHeapNode**)
malloc (capacity *
sizeof ( struct MinHeapNode*));
return minHeap;
}
// A utility function to swap two
// nodes of min heap.
// Needed for min heapify
void swapMinHeapNode( struct MinHeapNode** a,
struct MinHeapNode** b)
{
struct MinHeapNode* t = *a;
*a = *b;
*b = t;
}
// A standard function to
// heapify at given idx
// This function also updates
// position of nodes when they are swapped.
// Position is needed for decreaseKey()
void minHeapify( struct MinHeap* minHeap,
int idx)
{
int smallest, left, right;
smallest = idx;
left = 2 * idx + 1;
right = 2 * idx + 2;
if (left < minHeap->size &&
minHeap->array[left]->dist <
minHeap->array[smallest]->dist )
smallest = left;
if (right < minHeap->size &&
minHeap->array[right]->dist <
minHeap->array[smallest]->dist )
smallest = right;
if (smallest != idx)
{
// The nodes to be swapped in min heap
MinHeapNode *smallestNode =
minHeap->array[smallest];
MinHeapNode *idxNode =
minHeap->array[idx];
// Swap positions
minHeap->pos[smallestNode->v] = idx;
minHeap->pos[idxNode->v] = smallest;
// Swap nodes
swapMinHeapNode(&minHeap->array[smallest],
&minHeap->array[idx]);
minHeapify(minHeap, smallest);
}
}
// A utility function to check if
// the given minHeap is ampty or not
int isEmpty( struct MinHeap* minHeap)
{
return minHeap->size == 0;
}
// Standard function to extract
// minimum node from heap
struct MinHeapNode* extractMin( struct MinHeap*
minHeap)
{
if (isEmpty(minHeap))
return NULL;
// Store the root node
struct MinHeapNode* root =
minHeap->array[0];
// Replace root node with last node
struct MinHeapNode* lastNode =
minHeap->array[minHeap->size - 1];
minHeap->array[0] = lastNode;
// Update position of last node
minHeap->pos[root->v] = minHeap->size-1;
minHeap->pos[lastNode->v] = 0;
// Reduce heap size and heapify root
--minHeap->size;
minHeapify(minHeap, 0);
return root;
}
// Function to decreasy dist value
// of a given vertex v. This function
// uses pos[] of min heap to get the
// current index of node in min heap
void decreaseKey( struct MinHeap* minHeap,
int v, int dist)
{
// Get the index of v in  heap array
int i = minHeap->pos[v];
// Get the node and update its dist value
minHeap->array[i]->dist = dist;
// Travel up while the complete
// tree is not hepified.
// This is a O(Logn) loop
while (i && minHeap->array[i]->dist <
minHeap->array[(i - 1) / 2]->dist)
{
// Swap this node with its parent
minHeap->pos[minHeap->array[i]->v] =
(i-1)/2;
minHeap->pos[minHeap->array[
(i-1)/2]->v] = i;
swapMinHeapNode(&minHeap->array[i],
&minHeap->array[(i - 1) / 2]);
// move to parent index
i = (i - 1) / 2;
}
}
// A utility function to check if a given vertex
// 'v' is in min heap or not
bool isInMinHeap( struct MinHeap *minHeap, int v)
{
if (minHeap->pos[v] < minHeap->size)
return true ;
return false ;
}
// A utility function used to print the solution
void printArr( int dist[], int n)
{
printf ( "Vertex   Distance from Source" );
for ( int i = 0; i < n; ++i)
printf ( "%d %d" , i, dist[i]);
}
// The main function that calculates
// distances of shortest paths from src to all
// vertices. It is a O(ELogV) function
void dijkstra( struct Graph* graph, int src)
{
// Get the number of vertices in graph
int V = graph->V;
// dist values used to pick
// minimum weight edge in cut
int dist[V];
// minHeap represents set E
struct MinHeap* minHeap = createMinHeap(V);
// Initialize min heap with all
// vertices. dist value of all vertices
for ( int v = 0; v < V; ++v)
{
dist[v] = INT_MAX;
minHeap->array[v] = newMinHeapNode(v,
dist[v]);
minHeap->pos[v] = v;
}
// Make dist value of src vertex
// as 0 so that it is extracted first
minHeap->array[src] =
newMinHeapNode(src, dist[src]);
minHeap->pos[src]   = src;
dist[src] = 0;
decreaseKey(minHeap, src, dist[src]);
// Initially size of min heap is equal to V
minHeap->size = V;
// In the followin loop,
// min heap contains all nodes
// whose shortest distance
// is not yet finalized.
while (!isEmpty(minHeap))
{
// Extract the vertex with
// minimum distance value
struct MinHeapNode* minHeapNode =
extractMin(minHeap);
// Store the extracted vertex number
int u = minHeapNode->v;
// Traverse through all adjacent
// vertices of u (the extracted
// vertex) and update
// their distance values
struct AdjListNode* pCrawl =
graph->array[u].head;
while (pCrawl != NULL)
{
int v = pCrawl->dest;
// If shortest distance to v is
// not finalized yet, and distance to v
// through u is less than its
// previously calculated distance
if (isInMinHeap(minHeap, v) &&
dist[u] != INT_MAX &&
pCrawl->weight + dist[u] < dist[v])
{
dist[v] = dist[u] + pCrawl->weight;
// update distance
// value in min heap also
decreaseKey(minHeap, v, dist[v]);
}
pCrawl = pCrawl->next;
}
}
// print the calculated shortest distances
printArr(dist, V);
}
// Driver program to test above functions
int main()
{
// create the graph given in above fugure
int V = 9;
struct Graph* graph = createGraph(V);
addEdge(graph, 0, 1, 4);
addEdge(graph, 0, 7, 8);
addEdge(graph, 1, 2, 8);
addEdge(graph, 1, 7, 11);
addEdge(graph, 2, 3, 7);
addEdge(graph, 2, 8, 2);
addEdge(graph, 2, 5, 4);
addEdge(graph, 3, 4, 9);
addEdge(graph, 3, 5, 14);
addEdge(graph, 4, 5, 10);
addEdge(graph, 5, 6, 2);
addEdge(graph, 6, 7, 1);
addEdge(graph, 6, 8, 6);
addEdge(graph, 7, 8, 7);
dijkstra(graph, 0);
return 0;
}


Python3

# A Python program for Dijkstra's shortest
# path algorithm for adjacency
# list representation of graph
from collections import defaultdict
import sys
class Heap():
def __init__( self ):
self .array = []
self .size = 0
self .pos = []
def newMinHeapNode( self , v, dist):
minHeapNode = [v, dist]
return minHeapNode
# A utility function to swap two nodes
# of min heap. Needed for min heapify
def swapMinHeapNode( self , a, b):
t = self .array[a]
self .array[a] = self .array[b]
self .array[b] = t
# A standard function to heapify at given idx
# This function also updates position of nodes
# when they are swapped.Position is needed
# for decreaseKey()
def minHeapify( self , idx):
smallest = idx
left = 2 * idx + 1
right = 2 * idx + 2
if (left < self .size and
self .array[left][ 1 ]
< self .array[smallest][ 1 ]):
smallest = left
if (right < self .size and
self .array[right][ 1 ]
< self .array[smallest][ 1 ]):
smallest = right
# The nodes to be swapped in min
# heap if idx is not smallest
if smallest ! = idx:
# Swap positions
self .pos[ self .array[smallest][ 0 ]] = idx
self .pos[ self .array[idx][ 0 ]] = smallest
# Swap nodes
self .swapMinHeapNode(smallest, idx)
self .minHeapify(smallest)
# Standard function to extract minimum
# node from heap
def extractMin( self ):
# Return NULL wif heap is empty
if self .isEmpty() = = True :
return
# Store the root node
root = self .array[ 0 ]
# Replace root node with last node
lastNode = self .array[ self .size - 1 ]
self .array[ 0 ] = lastNode
# Update position of last node
self .pos[lastNode[ 0 ]] = 0
self .pos[root[ 0 ]] = self .size - 1
# Reduce heap size and heapify root
self .size - = 1
self .minHeapify( 0 )
return root
def isEmpty( self ):
return True if self .size = = 0 else False
def decreaseKey( self , v, dist):
# Get the index of v in  heap array
i = self .pos[v]
# Get the node and update its dist value
self .array[i][ 1 ] = dist
# Travel up while the complete tree is
# not hepified. This is a O(Logn) loop
while (i > 0 and self .array[i][ 1 ] <
self .array[(i - 1 ) / / 2 ][ 1 ]):
# Swap this node with its parent
self .pos[ self .array[i][ 0 ] ] = (i - 1 ) / / 2
self .pos[ self .array[(i - 1 ) / / 2 ][ 0 ] ] = i
self .swapMinHeapNode(i, (i - 1 ) / / 2 )
# move to parent index
i = (i - 1 ) / / 2 ;
# A utility function to check if a given
# vertex 'v' is in min heap or not
def isInMinHeap( self , v):
if self .pos[v] < self .size:
return True
return False
def printArr(dist, n):
print ( "Vertex Distance from source" )
for i in range (n):
print ( "%d %d" % (i,dist[i]))
class Graph():
def __init__( self , V):
self .V = V
self .graph = defaultdict( list )
# Adds an edge to an undirected graph
def addEdge( self , src, dest, weight):
# Add an edge from src to dest.  A new node
# is added to the adjacency list of src. The
# node is added at the beginning. The first
# element of the node has the destination
# and the second elements has the weight
newNode = [dest, weight]
self .graph[src].insert( 0 , newNode)
# Since graph is undirected, add an edge
# from dest to src also
newNode = [src, weight]
self .graph[dest].insert( 0 , newNode)
# The main function that calculates distances
# of shortest paths from src to all vertices.
# It is a O(ELogV) function
def dijkstra( self , src):
V = self .V # Get the number of vertices in graph
dist = [] # dist values used to pick minimum
# weight edge in cut
# minHeap represents set E
minHeap = Heap()
#  Initialize min heap with all vertices.
# dist value of all vertices
for v in range (V):
dist.append( 1e7 )
minHeap.array.append( minHeap.
newMinHeapNode(v, dist[v]))
minHeap.pos.append(v)
# Make dist value of src vertex as 0 so
# that it is extracted first
minHeap.pos[src] = src
dist[src] = 0
minHeap.decreaseKey(src, dist[src])
# Initially size of min heap is equal to V
minHeap.size = V;
# In the following loop,
# min heap contains all nodes
# whose shortest distance is not yet finalized.
while minHeap.isEmpty() = = False :
# Extract the vertex
# with minimum distance value
newHeapNode = minHeap.extractMin()
u = newHeapNode[ 0 ]
# Traverse through all adjacent vertices of
# u (the extracted vertex) and update their
# distance values
for pCrawl in self .graph[u]:
v = pCrawl[ 0 ]
# If shortest distance to v is not finalized
# yet, and distance to v through u is less
# than its previously calculated distance
if (minHeap.isInMinHeap(v) and
dist[u] ! = 1e7 and
pCrawl[ 1 ] + dist[u] < dist[v]):
dist[v] = pCrawl[ 1 ] + dist[u]
# update distance value
# in min heap also
minHeap.decreaseKey(v, dist[v])
printArr(dist,V)
# Driver program to test the above functions
graph = Graph( 9 )
graph.addEdge( 0 , 1 , 4 )
graph.addEdge( 0 , 7 , 8 )
graph.addEdge( 1 , 2 , 8 )
graph.addEdge( 1 , 7 , 11 )
graph.addEdge( 2 , 3 , 7 )
graph.addEdge( 2 , 8 , 2 )
graph.addEdge( 2 , 5 , 4 )
graph.addEdge( 3 , 4 , 9 )
graph.addEdge( 3 , 5 , 14 )
graph.addEdge( 4 , 5 , 10 )
graph.addEdge( 5 , 6 , 2 )
graph.addEdge( 6 , 7 , 1 )
graph.addEdge( 6 , 8 , 6 )
graph.addEdge( 7 , 8 , 7 )
graph.dijkstra( 0 )
# This code is contributed by Divyanshu Mehta


JAVA

import java.io.*;
import java.util.*;
class GFG {
static class AdjListNode {
int vertex, weight;
AdjListNode( int v, int w)
{
vertex = v;
weight = w;
}
int getVertex() { return vertex; }
int getWeight() { return weight; }
}
// Function to find the shortest distance of all the
// vertices from the source vertex S.
public static int [] dijkstra(
int V, ArrayList<ArrayList<AdjListNode> > graph,
int source)
{
int [] distance = new int [V];
for ( int i = 0 ; i < V; i++)
distance[i] = Integer.MAX_VALUE;
distance = 0 ;
PriorityQueue<AdjListNode> pq = new PriorityQueue<>(
(v1, v2) -> v1.getWeight() - v2.getWeight());
pq.add( new AdjListNode(source, 0 ));
while (pq.size() > 0 ) {
AdjListNode current = pq.poll();
for (AdjListNode n :
graph.get(current.getVertex())) {
if (distance[current.getVertex()]
+ n.getWeight()
< distance[n.getVertex()]) {
distance[n.getVertex()]
= n.getWeight()
+ distance[current.getVertex()];
pq.add( new AdjListNode(
n.getVertex(),
distance[n.getVertex()]));
}
}
}
// If you want to calculate distance from source to
// a particular target, you can return
// distance[target]
return distance;
}
public static void main(String[] args)
{
int V = 9 ;
ArrayList<ArrayList<AdjListNode> > graph
= new ArrayList<>();
for ( int i = 0 ; i < V; i++) {
graph.add( new ArrayList<>());
}
int source = 0 ;
graph.get( 0 ).add( new AdjListNode( 1 , 4 ));
graph.get( 0 ).add( new AdjListNode( 7 , 8 ));
graph.get( 1 ).add( new AdjListNode( 2 , 8 ));
graph.get( 1 ).add( new AdjListNode( 7 , 11 ));
graph.get( 1 ).add( new AdjListNode( 0 , 7 ));
graph.get( 2 ).add( new AdjListNode( 1 , 8 ));
graph.get( 2 ).add( new AdjListNode( 3 , 7 ));
graph.get( 2 ).add( new AdjListNode( 8 , 2 ));
graph.get( 2 ).add( new AdjListNode( 5 , 4 ));
graph.get( 3 ).add( new AdjListNode( 2 , 7 ));
graph.get( 3 ).add( new AdjListNode( 4 , 9 ));
graph.get( 3 ).add( new AdjListNode( 5 , 14 ));
graph.get( 4 ).add( new AdjListNode( 3 , 9 ));
graph.get( 4 ).add( new AdjListNode( 5 , 10 ));
graph.get( 5 ).add( new AdjListNode( 4 , 10 ));
graph.get( 5 ).add( new AdjListNode( 6 , 2 ));
graph.get( 6 ).add( new AdjListNode( 5 , 2 ));
graph.get( 6 ).add( new AdjListNode( 7 , 1 ));
graph.get( 6 ).add( new AdjListNode( 8 , 6 ));
graph.get( 7 ).add( new AdjListNode( 0 , 8 ));
graph.get( 7 ).add( new AdjListNode( 1 , 11 ));
graph.get( 7 ).add( new AdjListNode( 6 , 1 ));
graph.get( 7 ).add( new AdjListNode( 8 , 7 ));
graph.get( 8 ).add( new AdjListNode( 2 , 2 ));
graph.get( 8 ).add( new AdjListNode( 6 , 6 ));
graph.get( 8 ).add( new AdjListNode( 7 , 1 ));
int [] distance = dijkstra(V, graph, source);
// Printing the Output
System.out.println( "Vertex  "
+ "  Distance from Source" );
for ( int i = 0 ; i < V; i++) {
System.out.println(i + "             "
+ distance[i]);
}
}
}


输出:

Vertex   Distance from Source0                01                42                123                194                215                116                97                88                14

时间复杂性: 上述代码/算法的时间复杂度看起来是O(V^2),因为有两个嵌套的while循环。如果我们仔细观察,我们可以观察到内部循环中的语句执行O(V+E)次(类似于BFS)。内部循环有decreaseKey()操作,需要O(LogV)时间。所以总的时间复杂度是O(E+V)*O(LogV),也就是O((E+V)*LogV)=O(ELogV) 注意,上面的代码使用二进制堆实现优先级队列。使用Fibonacci堆可以将时间复杂度降低到O(E+VLogV)。原因是,斐波那契堆减少密钥操作需要O(1)时间,而二进制堆需要O(Logn)时间。 笔记:

  1. 代码计算最短距离,但不计算路径信息。我们可以创建一个父数组,在距离更新时更新父数组(如 prim的实现 )并使用它显示从源到不同顶点的最短路径。
  2. 该代码用于无向图,同样的dijekstra函数也可用于有向图。
  3. 该代码查找从源到所有顶点的最短距离。如果我们只对从源到单个目标的最短距离感兴趣,我们可以在拾取的最小距离顶点等于目标时打破for循环(算法步骤3.a)。
  4. Dijkstra算法不适用于具有负权边的图。对于具有负权边的图, 贝尔曼-福特算法 可以使用,我们将很快作为一个单独的帖子进行讨论。 Dijkstra最短路径算法中的打印路径 基于STL集合的Dijkstra最短路径算法

参考资料: Clifford Stein、Thomas H.Cormen、Charles E.Leiserson、Ronald L。 Sanjoy Dasgupta、Christos Papadimitriou、Umesh Vazirani的算法

如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享