跳到主要内容

数据结构16-图的遍历-Prim算法与Dijkstra算法

Prim算法

Prim算法用于求解加权无向图的最小生成树。它的基本思想是从一个顶点开始,逐步扩展最小生成树,直到包括所有顶点。

1. 数据结构

我们需要以下数据结构:

  • 图的邻接矩阵表示
  • 存储最小生成树的数组
  • 标记已包含在最小生成树中的顶点的布尔数组

2. 算法步骤

  1. 初始化:选择一个顶点作为起点,设置其权重为0,其他顶点的权重为无穷大。
  2. 重复以下步骤直到所有顶点都包含在最小生成树中:
    • 从未包含在最小生成树中的顶点中选择权重最小的顶点。
    • 将该顶点加入最小生成树。
    • 更新与该顶点相邻的顶点的权重。

3. 代码实现

#include <stdio.h>
#include <limits.h>
#include <stdbool.h>

#define V 5 // 图的顶点数

// 找到权值最小且未包含在最小生成树中的顶点
int minKey(int key[], bool mstSet[]) {
int min = INT_MAX, min_index;

for (int v = 0; v < V; v++)
if (mstSet[v] == false && key[v] < min)
min = key[v], min_index = v;

return min_index;
}

// 打印构建的最小生成树
void printMST(int parent[], int graph[V][V]) {
printf("Edge \tWeight\n");
for (int i = 1; i < V; i++)
printf("%d - %d \t%d \n", parent[i], i, graph[i][parent[i]]);
}

// 利用Prim算法构建最小生成树
void primMST(int graph[V][V]) {
int parent[V]; // 存储构建的最小生成树
int key[V]; // 用于选择最小权重的边
bool mstSet[V]; // 标记已包含在最小生成树中的顶点

// 初始化所有键值为无穷大
for (int i = 0; i < V; i++)
key[i] = INT_MAX, mstSet[i] = false;

// 选择第一个顶点作为起点
key[0] = 0;
parent[0] = -1; // 起点没有父节点

// 构建最小生成树
for (int count = 0; count < V - 1; count++) {
int u = minKey(key, mstSet);

mstSet[u] = true;

for (int v = 0; v < V; v++)
if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v])
parent[v] = u, key[v] = graph[u][v];
}

printMST(parent, graph);
}

int main() {
int graph[V][V] = {{0, 2, 0, 6, 0},
{2, 0, 3, 8, 5},
{0, 3, 0, 0, 7},
{6, 8, 0, 0, 9},
{0, 5, 7, 9, 0}};

primMST(graph);

return 0;
}

Dijkstra算法

Dijkstra算法用于求解加权有向图的单源最短路径问题。它的基本思想是从起点开始,逐步扩展最短路径,直到所有顶点都被处理。

1. 数据结构

我们需要以下数据结构:

  • 图的邻接矩阵表示
  • 存储最短路径的数组
  • 标记已处理顶点的布尔数组

2. 算法步骤

  1. 初始化:设置起点的距离为0,其他顶点的距离为无穷大。
  2. 重复以下步骤直到所有顶点都被处理:
    • 从未处理的顶点中选择距离最小的顶点。
    • 将该顶点标记为已处理。
    • 更新与该顶点相邻的顶点的距离。

3. 代码实现

#include <stdio.h>
#include <limits.h>
#include <stdbool.h>

#define V 9 // 图的顶点数

// 找到距离最小且未处理的顶点
int minDistance(int dist[], bool sptSet[]) {
int min = INT_MAX, min_index;

for (int v = 0; v < V; v++)
if (sptSet[v] == false && dist[v] <= min)
min = dist[v], min_index = v;

return min_index;
}

// 打印从起点到其他所有顶点的最短距离
void printSolution(int dist[]) {
printf("Vertex \t Distance from Source\n");
for (int i = 0; i < V; i++)
printf("%d \t\t %d\n", i, dist[i]);
}

// 利用Dijkstra算法计算从src到所有顶点的最短路径
void dijkstra(int graph[V][V], int src) {
int dist[V]; // 存储从起点到各顶点的最短距离
bool sptSet[V]; // 标记已处理顶点

// 初始化所有距离为无穷大
for (int i = 0; i < V; i++)
dist[i] = INT_MAX, sptSet[i] = false;

// 起点的距离为0
dist[src] = 0;

// 找到从起点到所有顶点的最短路径
for (int count = 0; count < V - 1; count++) {
int u = minDistance(dist, sptSet);

sptSet[u] = true;

for (int v = 0; v < V; v++)
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v])
dist[v] = dist[u] + graph[u][v];
}

printSolution(dist);
}

int main() {
int graph[V][V] = {{0, 4, 0, 0, 0, 0, 0, 8, 0},
{4, 0, 8, 0, 0, 0, 0, 11, 0},
{0, 8, 0, 7, 0, 4, 0, 0, 2},
{0, 0, 7, 0, 9, 14, 0, 0, 0},
{0, 0, 0, 9, 0, 10, 0, 0, 0},
{0, 0, 4, 14, 10, 0, 2, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 6},
{8, 11, 0, 0, 0, 0, 1, 0, 7},
{0, 0, 2, 0, 0, 0, 6, 7, 0}};

dijkstra(graph, 0);

return 0;
}

闵帆老师写的

代码

#include <stdio.h>
#include <malloc.h>

#define MAX_DISTANCE 10000

/**
* The structure of a Net.
*/
typedef struct Net {
int** weights;
int numNodes;
} Net, *NetPtr;

/**
* Initialize a Net.
*/
NetPtr initNet(int paraSize, int** paraData) {
int i, j;
NetPtr resultPtr = (NetPtr)malloc(sizeof(Net));
resultPtr -> numNodes = paraSize;

//Two stage space allocation.
resultPtr->weights = (int**)malloc(paraSize * sizeof(int*));
for (i = 0; i < paraSize; i ++) {
resultPtr -> weights[i] = (int*)malloc(paraSize * sizeof(int));
for (j = 0; j < paraSize; j ++) {
resultPtr -> weights[i][j] = paraData[i][j];
}//Of for j
}//Of for i

return resultPtr;
}//Of initNet

/**
* The Prim algorithm for spanning tree, or the Dijkstra algorithm for nearest path.
* @param paraAlgorithm 0 for Dijkstra, 1 for Prim
* @return The total cost of the tree.
*/
int dijkstraOrPrim(NetPtr paraPtr, int paraAlgorithm) {
int i, j, minDistance, tempBestNode, resultCost;
int source = 0;
int numNodes = paraPtr->numNodes;
int *distanceArray = (int*)malloc(numNodes * sizeof(int));
int *parentArray = (int*)malloc(numNodes * sizeof(int));
//Essentially boolean
int *visitedArray = (int*)malloc(numNodes * sizeof(int));

// Step 1. Initialize. Any node can be the source.
for (i = 0; i < numNodes; i++) {
distanceArray[i] = paraPtr->weights[source][i];
parentArray[i] = source;
visitedArray[i] = 0;
} // Of for i
distanceArray[source] = 0;
parentArray[source] = -1;
visitedArray[source] = 1;

// Step 2. Main loops.
tempBestNode = -1;
for (i = 0; i < numNodes - 1; i++) {
// Step 2.1 Find out the best next node.
minDistance = MAX_DISTANCE;
for (j = 0; j < numNodes; j++) {
// This node is visited.
if (visitedArray[j]) {
continue;
} // Of if

if (minDistance > distanceArray[j]) {
minDistance = distanceArray[j];
tempBestNode = j;
} // Of if
} // Of for j

visitedArray[tempBestNode] = 1;

// Step 2.2 Prepare for the next round.
for (j = 0; j < numNodes; j++) {
// This node is visited.
if (visitedArray[j]) {
continue;
} // Of if

// This node cannot be reached.
if (paraPtr->weights[tempBestNode][j] >= MAX_DISTANCE) {
continue;
} // Of if

// Attention: the difference between Dijkstra and Prim algorithms.
if (paraAlgorithm == 0) {
if (distanceArray[j] > distanceArray[tempBestNode] + paraPtr->weights[tempBestNode][j]) {
// Change the distance.
distanceArray[j] = distanceArray[tempBestNode] + paraPtr->weights[tempBestNode][j];
// Change the parent.
parentArray[j] = tempBestNode;
} // Of if
} else {
if (distanceArray[j] > paraPtr->weights[tempBestNode][j]) {
// Change the distance.
distanceArray[j] = paraPtr->weights[tempBestNode][j];
// Change the parent.
parentArray[j] = tempBestNode;
} // Of if
}//Of if
} // Of for j
} // Of for i

printf("the parent of each node: ");
for (i = 0; i < numNodes; i++) {
printf("%d, ", parentArray[i]);
} // Of for i

if (paraAlgorithm == 0) {
printf("From node 0, path length to all nodes are: ");
for (i = 0; i < numNodes; i++) {
printf("%d (%d), ", i, distanceArray[i]);
} // Of for i
} else {
resultCost = 0;
for (i = 0; i < numNodes; i++) {
resultCost += distanceArray[i];
printf("cost of node %d is %d, total = %d\r\n", i, distanceArray[i], resultCost);
} // Of for i
printf("Finally, the total cost is %d.\r\n ", resultCost);
}//Of if

// Step 3. Output for debug.
printf("\r\n");

return resultCost;
}// Of dijkstraOrPrim

/**
* Construct a sample net.
* Revised from testGraphTranverse().
*/
NetPtr constructSampleNet() {
int i, j;
int myGraph[6][6] = {
{0, 6, 1, 5, 0, 0},
{6, 0, 5, 0, 3, 0},
{1, 5, 0, 5, 6, 4},
{5, 0, 5, 0, 0, 2},
{0, 3, 6, 0, 0, 6},
{0, 0, 4, 2, 6, 0}};
int** tempPtr;
int numNodes = 6;
printf("Preparing data\r\n");

tempPtr = (int**)malloc(numNodes * sizeof(int*));
for (i = 0; i < numNodes; i ++) {
tempPtr[i] = (int*)malloc(numNodes * sizeof(int));
}//Of for i

for (i = 0; i < numNodes; i ++) {
for (j = 0; j < numNodes; j ++) {
if (myGraph[i][j] == 0) {
tempPtr[i][j] = MAX_DISTANCE;
} else {
tempPtr[i][j] = myGraph[i][j];
}//Of if
}//Of for j
}//Of for i

printf("Data ready\r\n");

NetPtr resultNetPtr = initNet(numNodes, tempPtr);
return resultNetPtr;
}//Of constructSampleNet

/**
* Test the Prim algorithm.
*/
void testPrim() {
NetPtr tempNetPtr = constructSampleNet();
printf("=====Dijkstra algorithm=====\r\n");
dijkstraOrPrim(tempNetPtr, 0);
printf("=====Prim algorithm=====\r\n");
dijkstraOrPrim(tempNetPtr, 1);
}//Of testPrim

/**
* The entrance.
*/
int main(){
testPrim();
return 1;
}//Of main

运行结果

Preparing data
Data ready
=====Dijkstra algorithm=====
the parent of each node: -1, 0, 0, 0, 2, 2, From node 0, path length to all nodes are: 0 (0), 1 (6), 2 (1), 3 (5), 4 (7), 5 (5),
=====Prim algorithm=====
the parent of each node: -1, 2, 0, 5, 1, 2, cost of node 0 is 0, total = 0
cost of node 1 is 5, total = 5
cost of node 2 is 1, total = 6
cost of node 3 is 2, total = 8
cost of node 4 is 3, total = 11
cost of node 5 is 4, total = 15
Finally, the total cost is 15.

Press any key to continue