-
-
Notifications
You must be signed in to change notification settings - Fork 7.7k
Added Floyd-Warshall algorithm implementation in the graph directory. #3081
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds Floyd-Warshall algorithm implementations to compute all-pairs shortest paths in weighted graphs. However, the PR introduces two nearly identical files (floyd_warshall.cpp and floyd_warshall2.cpp) which appears to be unintentional duplication.
Key Changes:
- Implementation of Floyd-Warshall algorithm with O(V³) time complexity
- Helper functions for adding edges, retrieving distances, and detecting negative cycles
- Interactive program with predefined test cases and user input support
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 21 comments.
| File | Description |
|---|---|
| graph/floyd_warshall.cpp | Floyd-Warshall implementation using size_t for matrix size variable |
| graph/floyd_warshall2.cpp | Duplicate Floyd-Warshall implementation using int for matrix size variable |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // running predefined tests | ||
| tests(); | ||
|
|
||
| int vertices = int(), edges = int(); |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary explicit initialization with default constructor. The expression 'int()' is verbose and can simply be '0' or the variable can be left uninitialized since it's assigned before use. The same applies to the edges variable on this line.
| int u = int(), v = int(); | ||
| int64_t w = int64_t(); |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary explicit initialization with default constructor. The expressions 'int()' and 'int64_t()' are verbose. These variables can be declared without initialization since they are assigned values from user input before use.
| std::vector<std::vector<int64_t>>* adj) { | ||
| /// n denotes the number of vertices in graph | ||
| size_t n = adj->size(); | ||
|
|
||
| /// copy the adjacency matrix to dist | ||
| std::vector<std::vector<int64_t>> dist = *adj; |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function accepts a raw pointer to a vector, but a const reference would be more appropriate for this use case. Using a raw pointer suggests optional or ownership semantics, neither of which apply here. Since the function only reads the input matrix, consider changing the parameter to a const reference instead.
| std::vector<std::vector<int64_t>>* adj) { | |
| /// n denotes the number of vertices in graph | |
| size_t n = adj->size(); | |
| /// copy the adjacency matrix to dist | |
| std::vector<std::vector<int64_t>> dist = *adj; | |
| const std::vector<std::vector<int64_t>>& adj) { | |
| /// n denotes the number of vertices in graph | |
| size_t n = adj.size(); | |
| /// copy the adjacency matrix to dist | |
| std::vector<std::vector<int64_t>> dist = adj; |
| /** Function to test the Algorithm */ | ||
| void tests() { | ||
| std::cout << "Initiating Predefined Tests..." << std::endl; | ||
| std::cout << "Initiating Test 1..." << std::endl; | ||
|
|
||
| std::vector<std::vector<int64_t>> adj1(4, std::vector<int64_t>(4, INF)); | ||
| for (int i = 0; i < 4; i++) { | ||
| adj1[i][i] = 0; | ||
| } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicated matrix initialization pattern. The pattern of creating a matrix filled with INF and then setting diagonal elements to 0 appears in multiple test cases. Consider extracting this into a helper function to improve maintainability and reduce code duplication.
| /** Function to test the Algorithm */ | |
| void tests() { | |
| std::cout << "Initiating Predefined Tests..." << std::endl; | |
| std::cout << "Initiating Test 1..." << std::endl; | |
| std::vector<std::vector<int64_t>> adj1(4, std::vector<int64_t>(4, INF)); | |
| for (int i = 0; i < 4; i++) { | |
| adj1[i][i] = 0; | |
| } | |
| /** Function to create an adjacency matrix with INF weights and 0 on the diagonal. */ | |
| std::vector<std::vector<int64_t>> createAdjMatrix(int n) { | |
| std::vector<std::vector<int64_t>> adj(n, std::vector<int64_t>(n, INF)); | |
| for (int i = 0; i < n; i++) { | |
| adj[i][i] = 0; | |
| } | |
| return adj; | |
| } | |
| /** Function to test the Algorithm */ | |
| void tests() { | |
| std::cout << "Initiating Predefined Tests..." << std::endl; | |
| std::cout << "Initiating Test 1..." << std::endl; | |
| std::vector<std::vector<int64_t>> adj1 = createAdjMatrix(4); |
| for (int k = 0; k < n; k++) { | ||
| /// for each source vertex i | ||
| for (int i = 0; i < n; i++) { | ||
| /// for each destination vertex j | ||
| for (int j = 0; j < n; j++) { |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type mismatch between loop variable and container size. The variable 'i' is declared as 'int' but is being compared with 'n' which is 'size_t'. This can cause signed/unsigned comparison warnings. The loop variable should be 'size_t' to match the type of 'n'.
| for (int k = 0; k < n; k++) { | |
| /// for each source vertex i | |
| for (int i = 0; i < n; i++) { | |
| /// for each destination vertex j | |
| for (int j = 0; j < n; j++) { | |
| for (size_t k = 0; k < n; k++) { | |
| /// for each source vertex i | |
| for (size_t i = 0; i < n; i++) { | |
| /// for each destination vertex j | |
| for (size_t j = 0; j < n; j++) { |
| std::vector<std::vector<int64_t>>* adj) { | ||
| /// n denotes the number of vertices in graph | ||
| int n = adj->size(); | ||
|
|
||
| /// copy the adjacency matrix to dist | ||
| std::vector<std::vector<int64_t>> dist = *adj; |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function accepts a raw pointer to a vector, but a const reference would be more appropriate for this use case. Using a raw pointer suggests optional or ownership semantics, neither of which apply here. Since the function only reads the input matrix, consider changing the parameter to a const reference instead.
| std::vector<std::vector<int64_t>>* adj) { | |
| /// n denotes the number of vertices in graph | |
| int n = adj->size(); | |
| /// copy the adjacency matrix to dist | |
| std::vector<std::vector<int64_t>> dist = *adj; | |
| const std::vector<std::vector<int64_t>>& adj) { | |
| /// n denotes the number of vertices in graph | |
| int n = adj.size(); | |
| /// copy the adjacency matrix to dist | |
| std::vector<std::vector<int64_t>> dist = adj; |
| if (dist[s][t] != INF) { | ||
| return dist[s][t]; | ||
| } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing bounds validation for vertex indices. The function should validate that 's' and 't' are within valid range (0 to n-1 where n is the matrix size) before accessing the matrix to prevent out-of-bounds access.
| if (dist[s][t] != INF) { | |
| return dist[s][t]; | |
| } | |
| // Validate indices to prevent out-of-bounds access | |
| if (s < 0 || t < 0) { | |
| return -1; | |
| } | |
| const size_t n = dist.size(); | |
| if (n == 0) { | |
| return -1; | |
| } | |
| const size_t rows = n; | |
| const size_t cols = dist[0].size(); | |
| const size_t si = static_cast<size_t>(s); | |
| const size_t ti = static_cast<size_t>(t); | |
| if (si >= rows || ti >= cols) { | |
| return -1; | |
| } | |
| if (dist[si][ti] != INF) { | |
| return dist[si][ti]; | |
| } |
| int64_t getDistance(const std::vector<std::vector<int64_t>>& dist, int s, | ||
| int t) { | ||
| if (dist[s][t] != INF) { | ||
| return dist[s][t]; | ||
| } | ||
| return -1; | ||
| } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing bounds validation for vertex indices. The function should validate that 's' and 't' are within valid range (0 to n-1 where n is the matrix size) before accessing the matrix to prevent out-of-bounds access.
| /** Function to test the Algorithm */ | ||
| void tests() { | ||
| std::cout << "Initiating Predefined Tests..." << std::endl; | ||
| std::cout << "Initiating Test 1..." << std::endl; | ||
|
|
||
| std::vector<std::vector<int64_t>> adj1(4, std::vector<int64_t>(4, INF)); | ||
| for (int i = 0; i < 4; i++) { | ||
| adj1[i][i] = 0; | ||
| } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicated matrix initialization pattern. The pattern of creating a matrix filled with INF and then setting diagonal elements to 0 appears in multiple test cases. Consider extracting this into a helper function to improve maintainability and reduce code duplication.
| /** Function to test the Algorithm */ | |
| void tests() { | |
| std::cout << "Initiating Predefined Tests..." << std::endl; | |
| std::cout << "Initiating Test 1..." << std::endl; | |
| std::vector<std::vector<int64_t>> adj1(4, std::vector<int64_t>(4, INF)); | |
| for (int i = 0; i < 4; i++) { | |
| adj1[i][i] = 0; | |
| } | |
| /** | |
| * @brief Helper function to create an n x n matrix initialized with INF | |
| * and 0 on the diagonal. | |
| * | |
| * @param n number of vertices (matrix dimension) | |
| * @return initialized adjacency matrix | |
| */ | |
| static std::vector<std::vector<int64_t>> createInitializedMatrix( | |
| std::size_t n) { | |
| std::vector<std::vector<int64_t>> matrix( | |
| n, std::vector<int64_t>(n, INF)); | |
| for (std::size_t i = 0; i < n; i++) { | |
| matrix[i][i] = 0; | |
| } | |
| return matrix; | |
| } | |
| /** Function to test the Algorithm */ | |
| void tests() { | |
| std::cout << "Initiating Predefined Tests..." << std::endl; | |
| std::cout << "Initiating Test 1..." << std::endl; | |
| std::vector<std::vector<int64_t>> adj1 = createInitializedMatrix(4); |
| if (dist[i][k] != INF && dist[k][j] != INF) { | ||
| if (dist[i][k] + dist[k][j] < dist[i][j]) { | ||
| dist[i][j] = dist[i][k] + dist[k][j]; | ||
| } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential integer overflow when adding edge weights. The expression 'dist[i][k] + dist[k][j]' can overflow before the comparison with 'dist[i][j]' is performed, leading to incorrect shortest path calculations. Consider checking if the addition would overflow before performing it, or restructure the comparison to avoid addition.
| if (dist[i][k] != INF && dist[k][j] != INF) { | |
| if (dist[i][k] + dist[k][j] < dist[i][j]) { | |
| dist[i][j] = dist[i][k] + dist[k][j]; | |
| } | |
| int64_t dik = dist[i][k]; | |
| int64_t dkj = dist[k][j]; | |
| if (dik != INF && dkj != INF) { | |
| // Check for potential overflow/underflow before addition | |
| bool will_overflow = false; | |
| if (dkj > 0 && dik > std::numeric_limits<int64_t>::max() - dkj) { | |
| will_overflow = true; | |
| } else if (dkj < 0 && dik < std::numeric_limits<int64_t>::min() - dkj) { | |
| will_overflow = true; | |
| } | |
| if (!will_overflow) { | |
| int64_t through_k = dik + dkj; | |
| if (through_k < dist[i][j]) { | |
| dist[i][j] = through_k; | |
| } | |
| } |
addEdge(),getDistance(),hasNegativeCycle()