最短路径的O(ElgV)的解法。
使用邻接表存储图,使用堆操作选取下一个最小路径点。
本题的难度并不在最短路径本身这个算法,而是在于堆的操作:
1 使用双重指针操作堆的节点,可以省去直接复制操作堆节点,提高效率,并且这才是有效操作动态地址数据的方法,不用双重指针,我思考了下,觉得更加不好做。
2 使用一个数组记录当前顶点在堆中的位置,相当于一个hash表了,可以需要的时候,直接从表中查找表示顶点的堆节点在堆中的位置,要记得更新节点时维护好这个表。
3 释放内存的时候注意,弹出堆的节点可以马上释放,不过注意不要双重释放内存了
记得曾经看到网上有人说堆排序是百万年薪的算法,不过现在看来光是堆排序是非常简单的算法了,会堆排序应该得达不到百万年薪吧,因为现在的算法高手应该都能随手写出堆排序的算法。
但是如本题这样灵活运用堆还是十分困难的。
参考:http://www.geeksforgeeks.org/greedy-algorithms-set-7-dijkstras-algorithm-for-adjacency-list-representation/
#include <stdio.h>
#include <limits>
class Dijsktra_Adjacency_List_and_Head
{
struct Node
{
int des, weight;
Node *next;
Node(int d, int w) : des(d), weight(w), next(NULL) {}
~Node()
{
if (next) delete next;
next = NULL;
}
};
struct AdjList
{
Node *head;
AdjList() : head(NULL) {}
~AdjList()
{
if (head) delete head;
head = NULL;
}
};
struct Graph
{
int V;
AdjList *arr;
Graph(int v) : V(v)
{
arr = new AdjList[v];
}
~Graph()
{
if (arr) delete [] arr;
arr = NULL;
}
};
void addEdge(Graph *graph, int src, int des, int wei)
{
Node *n = new Node(des, wei);
n->next = graph->arr[src].head;
graph->arr[src].head = n;
n = new Node(src, wei);
n->next = graph->arr[des].head;
graph->arr[des].head = n;
}
struct heapNode
{
int v, dist;
heapNode(int v1, int d):v(v1), dist(d){}
};
struct minHeap
{
int size, capacity;
int *pos;//记录顶点的位置
//双重指针:直接操作地址,而不是复制数据
heapNode **arr;
explicit minHeap(int c = 0) : capacity(c), size(0)
{
pos = new int[capacity];
arr = new heapNode*[capacity];//创建存储空间
}
~minHeap()
{
if (pos) delete [] pos;
for (int i = 0; i < size; i++)
{
if (arr[i]) delete arr[i], arr[i] = NULL;
}
if (arr) delete [] arr;
}
};
//双重指针的作用:这里只需要交换了地址,并不需要直接交换整个heapNode数据结构
void swapMinHeapNode(heapNode **a, heapNode **b)
{
heapNode *c = *a;
*a = *b;
*b = c;
}
void minheapify(minHeap *heap, int idx)
{
int smallest, left, right;
smallest = idx;
left = idx * 2 + 1;
right = idx * 2 + 2;//错误+2写成+1
if (left < heap->size &&
heap->arr[left]->dist < heap->arr[smallest]->dist)
{
smallest = left;
}
//很难调试的bug:下面不能加else,因为要选择三种中最小的
if (right < heap->size &&
heap->arr[right]->dist < heap->arr[smallest]->dist)
{
smallest = right;
}
if (smallest != idx)
{
heapNode *smallestNode = heap->arr[smallest];
heapNode *idxNode = heap->arr[idx];
heap->pos[smallestNode->v] = idx;
heap->pos[idxNode->v] = smallest;
swapMinHeapNode(&heap->arr[smallest], &heap->arr[idx]);
minheapify(heap, smallest);
}
}
bool isEmpty(minHeap *heap)
{
return heap->size == 0;
}
heapNode *extraMin(minHeap *heap)
{
if (isEmpty(heap)) return NULL;
heapNode *root = heap->arr[0];
heapNode *lastNode = heap->arr[heap->size-1];
heap->arr[0] = lastNode;
//swapMinHeapNode(&root, &lastNode);
heap->pos[root->v] = heap->size-1;
heap->pos[lastNode->v] = 0;
--heap->size;
minheapify(heap, 0);
return root;
}
void decreaseKey(minHeap *heap, int v, int dist)
{
int i = heap->pos[v];
heap->arr[i]->dist = dist;
while (i && heap->arr[i]->dist < heap->arr[(i-1)/2]->dist)
{
heap->pos[heap->arr[i]->v] = (i-1)/2;
heap->pos[heap->arr[(i-1)/2]->v] = i;
swapMinHeapNode(&heap->arr[i], &heap->arr[(i-1)/2]);
i = (i-1)/2;
}
}
bool isInMinHeap(minHeap *heap, int v)
{
if (heap->pos[v] < heap->size) return true;
return false;
}
void printArr(int dist[], int n)
{
printf("Vertex Distance from Source\n");
for (int i = 0; i < n; ++i)
printf("%d \t\t %d\n", i, dist[i]);
}
void dijkstra(Graph *graph, int src, int dist[])
{
int V = graph->V;
minHeap *heap = new minHeap(V);
for (int i = 0; i < V; i++)
{
dist[i] = INT_MAX;
heap->arr[i] = new heapNode(i, dist[i]);
heap->pos[i] = i;
}
dist[src] = 0;
decreaseKey(heap, src, dist[src]);
heap->size = V;
while (!isEmpty(heap))
{
heapNode *hn = extraMin(heap);
int u = hn->v;
delete hn;
hn = NULL;
Node *pCrawl = graph->arr[u].head;
while (pCrawl)
{
int v = pCrawl->des;
if (isInMinHeap(heap, v) && dist[u] != INT_MAX
&& pCrawl->weight + dist[u] < dist[v])
{
dist[v] = pCrawl->weight + dist[u];
decreaseKey(heap, v, dist[v]);
}
pCrawl = pCrawl->next;
}
}
delete heap;
}
public:
Dijsktra_Adjacency_List_and_Head()
{
// create the graph given in above fugure
int V = 9;
struct Graph* graph = new Graph(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);
int *dist = (int *) malloc(sizeof(int) * V);
dijkstra(graph, 0, dist);
printArr(dist, V);
free(dist);
delete graph;
}
};