Dijkstra算法(邻接表表示的算法实现|贪婪算法S8)

本文概述

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

文章图片
最初, 对于所有其他顶点, 源顶点的距离值为0, INF为无穷大。因此, 从"最小堆"中提取源顶点, 并更新与0(1和7)相邻的顶点的距离值。最小堆包含除顶点0以外的所有顶点。
【Dijkstra算法(邻接表表示的算法实现|贪婪算法S8)】绿色的顶点是已确定最小距离且不在最小堆中的顶点
Dijkstra算法(邻接表表示的算法实现|贪婪算法S8)

文章图片
由于顶点1的距离值在最小堆中的所有节点中最小, 因此从最小堆中提取顶点, 并更新与1相邻的顶点的距离值(如果顶点在最小堆中并且到1的距离小于以前的距离)。最小堆包含除顶点0和1之外的所有顶点。
Dijkstra算法(邻接表表示的算法实现|贪婪算法S8)

文章图片
从最小堆中选取最小距离值的顶点。选择了顶点7。因此, 最小堆现在包含除0、1和7以外的所有顶点。更新相邻顶点7的距离值。顶点6和8的距离值变得有限(分别为15和9)。
Dijkstra算法(邻接表表示的算法实现|贪婪算法S8)

文章图片
选择与最小堆的距离最小的顶点。选择了顶点6。因此, 最小堆现在包含除0、1、7和6以外的所有顶点。更新相邻顶点6的距离值。更新顶点5和8的距离值。
Dijkstra算法(邻接表表示的算法实现|贪婪算法S8)

文章图片
重复上述步骤, 直到最小堆不为空为止。最后, 我们得到以下最短路径树。
Dijkstra算法(邻接表表示的算法实现|贪婪算法S8)

文章图片
Dijkstra的最短路径算法,使用邻接表表示的贪婪算法实现代码如下:
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 { intv; 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 inheap 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 ( "VertexDistance from Source\n" ); for ( int i = 0; i < n; ++i) printf ( "%d \t\t %d\n" , i, dist[i]); } //The main function that calulates //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; }

python
# 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 inheap 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\tDistance from source" for i in range (n): print "%d\t\t%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 calulates 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(sys.maxint) 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] ! = sys.maxint 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

输出如下:
VertexDistance from Source 00 14 212 319 421 511 69 78 814

Dijkstra算法时间复杂度:上面的代码/算法的时间复杂度看起来是O(V ^ 2), 因为有两个嵌套的while循环。如果仔细观察, 可以观察到内部循环中的语句执行了O(V + E)次(类似于BFS)。内部循环具有reduceKey()操作, 该操作需要O(LogV)时间。因此, 总体时间复杂度为O(E + V)* O(LogV), 即O((E + V)* LogV)= O(ELogV)
请注意, 以上代码将Binary Heap用于Priority Queue实现。使用斐波那契堆可以将时间复杂度降低到O(E + VLogV)。原因是, 斐波那契堆需要O(1)时间来进行减键操作, 而二进制堆需要O(Logn)时间。该算法是一个贪婪算法,使用优先队列是该类型算法的典型形式。
Dijkstra的最短路径算法笔记:
  1. 该代码计算最短距离, 但不计算路径信息。我们可以创建一个父数组, 在距离更新时更新父数组(例如总理的实施), 并使用它显示从源到不同顶点的最短路径。
  2. 该代码用于无向图, 相同的dijekstra函数也可用于有向图。
  3. 该代码查找从源到所有顶点的最短距离。如果我们在最短的距离只关心从源到单个目标, 我们可以break for循环时所拾取最小距离顶点等于目标(算法的步骤3.A)。
  4. Dijkstra算法不适用于负边缘为负的图表。对于负负边的图形, Bellman–Ford算法可以使用, 我们将在单独的文章中进行讨论。
    Dijkstra最短路径算法中的打印路径 
    Dijkstra使用STL中的集合的最短路径算法
参考文献:
Clifford Stein, Thomas H.Cormen, Charles E.Leiserson, Ronald L.
Sanjoy Dasgupta, Christos Papadimitriou, Umesh Vazirani的算法
如果发现任何不正确的地方, 或者想分享有关上述主题的更多信息, 请写评论。

    推荐阅读