Dijkstra算法在求最短距离和更新结点距离时的循环遍历可以使用一下三种方式优化:
①图的存储方式改为邻接链表,求最短距离采用堆排序的方法,c语言实现
②图的存储方式改为邻接链表,求最短距离采用STL中的set,c++实现
③图的村粗方式改为邻接链表,求最短距离采用STL中的priority_queue,c++实现
堆排序优化:
理解的难点在于minHeap中pos的功能。pos数组时刻更新每个结点在最小堆中的位置。
decreaseKey函数需要pos数组直接找到v结点在最小堆中的位置来修改v的距离。
时间复杂度: 边的遍历类似BFS时间复杂度为O(V+E),dereaseKey时间复杂度O(LogV),总和O((E+V)*LogV)=O(ELogV)
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdbool.h>
struct AdjListNode
{
int dest;
int weight;
struct AdjListNode * next;
};
struct AdjList {
struct AdjListNode * head;
};
struct Graph
{
int VNum;
struct AdjList * array;
};
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;
}
struct Graph * createGraph(int VNum)
{
struct Graph * graph = (struct Graph *)malloc(sizeof(struct Graph));
graph->VNum = VNum;
graph->array = (struct AdjList *)malloc(VNum*sizeof(struct AdjList));
for (int i = 0;i < VNum;i++)
graph->array[i].head = NULL;
return graph;
}
void addEdge(struct Graph * graph, int src, int dest, int weight)
{
struct AdjListNode * newNode = newAdjListNode(dest, weight);
newNode->next = graph->array[src].head;
graph->array[src].head = newNode;
newNode = newAdjListNode(src, weight);
newNode->next = graph->array[dest].head;
graph->array[dest].head = newNode;
}
struct MinHeapNode {
int dest;
int dist;
};
struct MinHeap {
int size;
int capacity;
int * pos;
struct MinHeapNode ** array;
};
struct MinHeapNode * newMinHeapNode(int dest, int dist)
{
struct MinHeapNode * minHeapNode =
(struct MinHeapNode *)malloc(sizeof(struct MinHeapNode));
minHeapNode->dest = dest;
minHeapNode->dist = dist;
return minHeapNode;
}
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;
}
void swapMinHeapNode(struct MinHeapNode ** a, struct MinHeapNode ** b)
{
struct MinHeapNode * t = *a;
*a = *b;
*b = t;
}
void minHeapify(struct MinHeap * minHeap, int parent)
{
int child;
struct MinHeapNode * temp = minHeap->array[parent];
child = 2 * parent + 1;
while (parent < minHeap->size / 2)
{
if (child + 1 < minHeap->size &&
minHeap->array[child]->dist > minHeap->array[child + 1]->dist)
child++;
if (minHeap->array[child]->dist >= temp->dist)
break;
minHeap->pos[minHeap->array[child]->dest] = parent;
minHeap->array[parent] = minHeap->array[child];
parent = child;
child = 2 * parent + 1;
}
minHeap->pos[temp->dest] = parent;
minHeap->array[parent] = temp;
}
int isEmpty(struct MinHeap * minHeap)
{
return minHeap->size == 0;
}
struct MinHeapNode * extractMin(struct MinHeap * minHeap)
{
if (isEmpty(minHeap))
return NULL;
int lastpos = minHeap->size - 1;
minHeap->pos[minHeap->array[0]->dest] = lastpos;
minHeap->pos[minHeap->array[lastpos]->dest] = 0;
swapMinHeapNode(&minHeap->array[0], &minHeap->array[lastpos]);
minHeap->size--;
minHeapify(minHeap, 0);
return minHeap->array[lastpos];
}
void decreaseKey(struct MinHeap * minHeap, int dest, int dist)
{
int i = minHeap->pos[dest];
minHeap->array[i] -> dist = dist;
struct MinHeapNode * temp = minHeap->array[i];
while (i && temp->dist < minHeap->array[(i - 1) / 2]->dist)
{
minHeap->pos[minHeap->array[(i - 1) / 2]->dest] = i;
minHeap->array[i] = minHeap->array[(i - 1) / 2];
i = (i - 1) / 2;
}
minHeap->pos[temp->dest] = i;
minHeap->array[i] = temp;
}
bool isInMinHeap(struct MinHeap * minHeap, int dest)
{
return minHeap->pos[dest] < minHeap->size;
}
void printSolution(int dist[], int path[], int src,int V)
{
printf("Vertex Distance from Source\n");
for (int i = 0;i < V;i++)
{
printf("%d\t\t%d\t", i, dist[i]);
for (int u = i;u != src;u = path[u])
printf("v%d <- ", u);
printf("v%d\n", src);
}
}
void dijkstra(struct Graph * graph, int src)
{
int vNum = graph->VNum;
int * path = (int *)malloc(vNum*sizeof(int));
int * dist = (int *)malloc(vNum*sizeof(int));
struct MinHeap * minHeap = createMinHeap(vNum);
for (int v = 0;v < vNum;v++)
{
dist[v] = INT_MAX;
minHeap->array[v] = newMinHeapNode(v, dist[v]);
minHeap->pos[v] = v;
}
path[src] = src;
dist[src] = 0;
decreaseKey(minHeap, src, dist[src]);
minHeap->size = vNum;
while (!isEmpty(minHeap))
{
struct MinHeapNode * minHeapNode = extractMin(minHeap);
int u = minHeapNode->dest;
struct AdjListNode * pCrawl = graph->array[u].head;
while (pCrawl)
{
int v = pCrawl->dest;
if (isInMinHeap(minHeap, v) &&
dist[u] != INT_MAX && pCrawl->weight + dist[u] < dist[v])
{
path[v] = u;
dist[v] = pCrawl->weight + dist[u];
decreaseKey(minHeap, v, dist[v]);
}
pCrawl = pCrawl->next;
}
}
printSolution(dist,path,src, vNum);
}
int main()
{
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;
}
②set优化:
STL中的set很适合最短距离,查找和插入的时间复杂度也都是对数的,总的时间复杂度也为O(ELogV)
#include <iostream>
#include <list>
#include <vector>
#include <set>
using namespace std;
class Graph {
int V;
list<pair<int, int>>*adj;
void printSolution(vector<int> & dist, vector<int> path, int src);
public:
Graph(int V);
void addEdge(int u, int v, int w);
void shortestPath(int s);
};
Graph::Graph(int V)
{
this->V = V;
adj = new list<pair<int, int>>[V];
}
void Graph::addEdge(int u, int v, int w)
{
adj[u].push_back(make_pair(v, w));
adj[v].push_back(make_pair(u, w));
}
void Graph::printSolution(vector<int> & dist, vector<int> path, int src)
{
cout << "Vertex Distance from Source path" << endl;
for (int i = 0; i < V; ++i)
{
cout << i << "\t\t" << dist[i] << "\t\t";
for (int u = i;u != src;u = path[u])
cout << 'v' << u << " <- ";
cout << 'v' << src << endl;
}
}
void Graph::shortestPath(int src)
{
set<pair<int, int>> setds;
vector<int> dist(V, INT_MAX);
vector<int> path(V);
setds.insert(make_pair(0, src));
dist[src] = 0;
path[src] = src;
while (!setds.empty())
{
pair<int, int> temp = *(setds.begin());
setds.erase(setds.begin());
int u = temp.second;
list<pair<int, int>>::iterator i;
for (i = adj[u].begin(); i != adj[u].end();++i)
{
int v = (*i).first;
int weight = (*i).second;
if (dist[v] > dist[u] + weight)
{
if (dist[v] != INT_MAX)
setds.erase(setds.find(make_pair(dist[v], v)));
dist[v] = dist[u] + weight;
setds.insert(make_pair(dist[v], v));
path[v] = u;
}
}
}
printSolution(dist, path, src);
}
int main()
{
int V = 9;
Graph g(V);
g.addEdge(0, 1, 4);
g.addEdge(0, 7, 8);
g.addEdge(1, 2, 8);
g.addEdge(1, 7, 11);
g.addEdge(2, 3, 7);
g.addEdge(2, 8, 2);
g.addEdge(2, 5, 4);
g.addEdge(3, 4, 9);
g.addEdge(3, 5, 14);
g.addEdge(4, 5, 10);
g.addEdge(5, 6, 2);
g.addEdge(6, 7, 1);
g.addEdge(6, 8, 6);
g.addEdge(7, 8, 7);
g.shortestPath(7);
return 0;
}
③c语言堆排序的c++版,利用priority_queue优先队列实现与堆排序相同的思路方案。
不过STL封装好的priority_queue不具备遍历、修改和随机删除。为了算法的稳定还是利用了visited数组判断结点是否已经被访问过
时间复杂度同为O(ELogV)
#include <iostream>
#include <list>
#include <queue>
#include <functional>
using namespace std;
typedef pair<int, int> iPair;
class Graph
{
int vNum;
list<iPair> * adj;
void printSolution(vector<int> & dist, vector<int> path, int src);
public:
Graph(int V);
void addEdge(int u, int v, int w);
void shortestPath(int src);
};
Graph::Graph(int V)
{
this->vNum = V;
adj = new list<iPair>[V];
}
void Graph::addEdge(int u, int v, int w)
{
adj[u].push_back(make_pair(v, w));
adj[v].push_back(make_pair(u, w));
}
void Graph::printSolution(vector<int> & dist, vector<int> path, int src)
{
cout << "Vertex Distance from Source path" << endl;
for (int i = 0; i < vNum; ++i)
{
cout << i << "\t\t" << dist[i] << "\t\t";
for (int u = i;u != src;u = path[u])
cout << 'v' << u << " <- ";
cout << 'v' << src << endl;
}
}
void Graph::shortestPath(int src)
{
priority_queue<iPair, vector<iPair>, greater<iPair>> pq;
vector<int> dist(vNum, INT_MAX);
vector<int> path(vNum);
vector<int> visited(vNum,false);
pq.push(make_pair(0, src));
dist[src] = 0;
path[src] = src;
while (!pq.empty())
{
int u = pq.top().second;
pq.pop();
if (visited[u])
continue;
visited[u] = true;
list<iPair>::iterator i;
for (i = adj[u].begin();i != adj[u].end();i++)
{
int v = (*i).first;
int weight = (*i).second;
if (dist[v] > dist[u] + weight)
{
path[v] = u;
dist[v] = dist[u] + weight;
pq.push(make_pair(dist[v], v));
}
}
}
printSolution(dist, path, src);
}
int main()
{
int V = 9;
Graph g(V);
g.addEdge(0, 1, 4);
g.addEdge(0, 7, 8);
g.addEdge(1, 2, 8);
g.addEdge(1, 7, 11);
g.addEdge(2, 3, 7);
g.addEdge(2, 8, 2);
g.addEdge(2, 5, 4);
g.addEdge(3, 4, 9);
g.addEdge(3, 5, 14);
g.addEdge(4, 5, 10);
g.addEdge(5, 6, 2);
g.addEdge(6, 7, 1);
g.addEdge(6, 8, 6);
g.addEdge(7, 8, 7);
g.shortestPath(0);
return 0;
}