下列算法中的图的定义请看这篇文章:(56条消息) 最小生成树算法——kruskal和prim算法的c++实现_Aaaverage JOE的博客-CSDN博客https://blog.csdn.net/m0_73961454/article/details/130814908
Dijsktra算法的实现:这是一个单源路径算法,即只能求一个点到其他各点的最短路径
Node* getMindistanceAndUnselectedNode(unordered_map<Node*,int>& distanceMap,set<Node*>& selectedNode) {
int mindistance = INT_MAX;
Node* minNode = NULL;
for (pair<Node*,int> it : distanceMap) {
Node* node = it.first;
int distance = it.second;
if (distance < mindistance && selectedNode.count(node) == 0) {
mindistance = distance;
minNode = node;
}
}
return minNode;
}
unordered_map<Node*, int> Dijsktra(Node* head) {
unordered_map<Node*, int> distanceMap;
if (head == NULL) {
return distanceMap;
}
distanceMap[head] = 0;
set<Node*> selectedNode;
Node* minNode = getMindistanceAndUnselectedNode(distanceMap, selectedNode);
while (minNode != NULL) {
int distance = distanceMap[minNode];
for (Edge* edge : minNode->edges) {
Node* to = edge->to;
if (distanceMap.count(to) == 0) {
distanceMap[to] = distance + edge->weight;
}
distanceMap[to] = fminf(distanceMap[to], distance + edge->weight);
}
selectedNode.insert(minNode);
minNode = getMindistanceAndUnselectedNode(distanceMap, selectedNode);
}
return distanceMap;
}
Dijsktra的优化版(利用了加强堆,即给堆加了个索引):
unordered_map<Node*,int> dijsktraByheap(Node* node,int size) {
unordered_map<Node*, int> result;
if (node == NULL) {
return result;
}
Nodeheap heap(size);
heap.AddorUpdateorIgnore(node, 0);
while (!heap.isEmpty()) {
NodeRecord* temp = heap.pop();
int distance = temp->distance;
node = temp->node;
for (Edge* edge : node->edges) {
Node* to = edge->to;
heap.AddorUpdateorIgnore(to, edge->weight + distance);
}
result[node] = distance;
}
return result;
}
//dijsktra算法的堆优化写法
class NodeRecord {
public:
int distance;
Node* node;
NodeRecord(Node* n,int v) :distance(v), node(n) {}
};
class Nodeheap {
private:
Node** arr;//储存Node*元素的数组
unordered_map<Node*, int> distanceMap;
unordered_map<Node*, int> indexMap;//标记结点在数组中的下标,若不在数组中,则为-1
int heapsize;
public:
Nodeheap(int s) {
heapsize = 0;
arr = new Node*[s]();
}
void swap(int index1, int index2) {
//交换数组中的元素
Node* temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
//交换索引表中的索引
indexMap[arr[index1]] = index2;
indexMap[arr[index2]] = index1;
}
//index位置和其孩子进行比较,若distance大则进行交换
void heapify(int index) {
int left = index * 2 + 1;
while (left < heapsize) {
int min = left + 1 < heapsize && distanceMap[arr[left]] < distanceMap[arr[left + 1]] ? left : left + 1;
min = distanceMap[arr[index]] < distanceMap[arr[min]] ? index : min;
if (index == min) {
return;
}
swap(index, min);
index = min;
left = index * 2 + 1;
}
}
//index位置和其父进行比较,若distance大则进行交换
void heapInsert(int index) {
while (distanceMap[arr[index]] < distanceMap[arr[(index - 1) / 2]]) {
swap(index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
//若不在堆中,直接添加;若在堆中,需要进行比较,若更小,则进行更新;否则什么都不做
void AddorUpdateorIgnore(Node* node, int distance) {
if (!isEntered(node)) {
arr[heapsize] = node;
distanceMap[node] = distance;
indexMap[node] = heapsize;
heapInsert(heapsize++);
return;
}
if (inheap(node) && distanceMap[node] > distance) {
distanceMap[node] = distance;
heapify(indexMap[node]);
}
}
//将此时最小的距离弹出,弹出这个操作就相当于把此结点加入到selectedNodes以及选出minNode了
NodeRecord* pop() {
NodeRecord* res = new NodeRecord(arr[0], distanceMap[arr[0]]);
swap(0, --heapsize);
indexMap[arr[heapsize]] = -1;
distanceMap.erase(arr[heapsize]);
heapify(0);
return res;
}
//判断这个结点是否进过堆中,为了给Add函数使用(如果没有进来过,直接添加)
bool isEntered(Node* node) {
return indexMap.count(node) != 0;
}
//判断这个结点是否在堆中,只有在堆中才有可能进行更新
bool inheap(Node* node) {
return isEntered(node) && indexMap[node] != -1;
}
bool isEmpty() {
return heapsize == 0;
}
};
多源最短路径算法:求图中各顶点到其他顶点的最短路径,当然我们可以调用多个Dijsktra算法,单未免效率过低,故有了floyd算法
floyd算法的实现:
void floyd(int(*G)[MAX_VEX], int(*A)[MAX_VEX], int(*path)[MAX_VEX]) {
if (G == NULL || A == NULL || path == NULL) {
return;
}
for (int i = 0; i < MAX_VEX; i++) {
for (int j = 0; j < MAX_VEX; j++) {
A[i][j] = G[i][j];
path[i][j] = -1;
}
}
for (int v = 0; v < MAX_VEX; v++) {
for (int i = 0; i < MAX_VEX; i++) {
for (int j = 0; j < MAX_VEX; j++) {
if (A[i][j] > A[i][v] + A[v][j]) {
A[i][j] = A[i][v] + A[v][j];
path[i][j] = v;
}
}
}
}
}
这里函数形参讲一下:
这是二维数组的指针,因为数组实质就是在内存中一段连续的空间,二维数组arr就是以一维数组为元素的构成,例如arr[0] = *(arr + 0),arr就是基地址,这样得到的就是二维数组中的第0个一维数组
arr[0][1] = *(*(arr + 0) + 1)
故数组名就是内存中开辟一段连续空间的基地址,地址即是指针,加上数字就会由编译器根据其类型来加上对应的内存编号,比如int* p;p[1] = *(p + 1),这里加的是int类型的4个字节,一个内存单元是一个字节
如何得到所给定结点之间的最短路径:
int GetMindistanceByFloyd(int(*A)[MAX_VEX], int(*path)[MAX_VEX], int i, int j) {
if (path[i][j] == -1) {
return A[i][j];
}
int v = path[i][j];
int val1 = GetMindistanceByFloyd(A, path, i, v);
int val2 = GetMindistanceByFloyd(A, path, v, j);
return val1 + val2;
}