Skip to main content

Data Structures 16 - Graph Traversal: Prim's and Dijkstra's Algorithms

Prim's Algorithm

Prim's algorithm is used to find the minimum spanning tree of a weighted undirected graph. Its basic idea is to start from a single vertex and progressively expand the minimum spanning tree until all vertices are included.

1. Data Structures

We need the following data structures:

  • An adjacency matrix representation of the graph
  • An array to store the minimum spanning tree
  • A boolean array to mark vertices already included in the minimum spanning tree

2. Algorithm Steps

  1. Initialization: Choose a vertex as the starting point, set its weight to 0, and set the weights of all other vertices to infinity.
  2. Repeat the following steps until all vertices are included in the minimum spanning tree:
    • Select the vertex with the smallest weight among those not yet included in the minimum spanning tree.
    • Add that vertex to the minimum spanning tree.
    • Update the weights of the vertices adjacent to the selected vertex.

3. Code Implementation

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

#define V 5 // Number of vertices in the graph

// Find the vertex with the minimum key not yet included in the MST
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;
}

// Print the constructed MST
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]]);
}

// Build the MST using Prim's algorithm
void primMST(int graph[V][V]) {
int parent[V]; // Store the constructed MST
int key[V]; // Used to pick the minimum weight edge
bool mstSet[V]; // Mark vertices already included in the MST

// Initialize all keys to infinity
for (int i = 0; i < V; i++)
key[i] = INT_MAX, mstSet[i] = false;

// Pick the first vertex as the starting point
key[0] = 0;
parent[0] = -1; // The starting vertex has no parent

// Build the MST
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's Algorithm

Dijkstra's algorithm is used to solve the single-source shortest path problem in a weighted directed graph. Its basic idea is to start from the source vertex and progressively expand the shortest paths until all vertices have been processed.

1. Data Structures

We need the following data structures:

  • An adjacency matrix representation of the graph
  • An array to store the shortest paths
  • A boolean array to mark processed vertices

2. Algorithm Steps

  1. Initialization: Set the distance of the source vertex to 0 and the distances of all other vertices to infinity.
  2. Repeat the following steps until all vertices have been processed:
    • Select the vertex with the smallest distance among the unprocessed vertices.
    • Mark that vertex as processed.
    • Update the distances of the vertices adjacent to the selected vertex.

3. Code Implementation

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

#define V 9 // Number of vertices in the graph

// Find the unprocessed vertex with the minimum distance
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;
}

// Print the shortest distances from the source to all vertices
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]);
}

// Compute shortest paths from src to all vertices using Dijkstra's algorithm
void dijkstra(int graph[V][V], int src) {
int dist[V]; // Store the shortest distances from the source
bool sptSet[V]; // Mark processed vertices

// Initialize all distances to infinity
for (int i = 0; i < V; i++)
dist[i] = INT_MAX, sptSet[i] = false;

// Distance from source to itself is 0
dist[src] = 0;

// Find the shortest paths from the source to all vertices
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;
}

Written by Professor Min Fan

Code

#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

Output

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