chore: import upstream snapshot with attribution
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
|
||||
# with full pathname. RELATIVE may makes it easier to extract an executable name
|
||||
# automatically.
|
||||
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
|
||||
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
|
||||
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
|
||||
foreach( testsourcefile ${APP_SOURCES} )
|
||||
# I used a simple string replace, to cut off .cpp.
|
||||
string( REPLACE ".cpp" "" testname ${testsourcefile} )
|
||||
add_executable( ${testname} ${testsourcefile} )
|
||||
|
||||
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
|
||||
if(OpenMP_CXX_FOUND)
|
||||
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
|
||||
endif()
|
||||
install(TARGETS ${testname} DESTINATION "bin/greedy_algorithms")
|
||||
|
||||
endforeach( testsourcefile ${APP_SOURCES} )
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @file binary_addition.cpp
|
||||
* @brief Adds two binary numbers and outputs resulting string
|
||||
*
|
||||
* @details The algorithm for adding two binary strings works by processing them
|
||||
* from right to left, similar to manual addition. It starts by determining the
|
||||
* longer string's length to ensure both strings are fully traversed. For each
|
||||
* pair of corresponding bits and any carry from the previous addition, it
|
||||
* calculates the sum. If the sum exceeds 1, a carry is generated for the next
|
||||
* bit. The results for each bit are collected in a result string, which is
|
||||
* reversed at the end to present the final binary sum correctly. Additionally,
|
||||
* the function validates the input to ensure that only valid binary strings
|
||||
* (containing only '0' and '1') are processed. If invalid input is detected,
|
||||
* it returns an empty string.
|
||||
* @author [Muhammad Junaid Khalid](https://github.com/mjk22071998)
|
||||
*/
|
||||
|
||||
#include <algorithm> /// for reverse function
|
||||
#include <cassert> /// for tests
|
||||
#include <iostream> /// for input and outputs
|
||||
#include <string> /// for string class
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Greedy Algorithms
|
||||
*/
|
||||
namespace greedy_algorithms {
|
||||
/**
|
||||
* @brief A class to perform binary addition of two binary strings.
|
||||
*/
|
||||
class BinaryAddition {
|
||||
public:
|
||||
/**
|
||||
* @brief Adds two binary strings and returns the result as a binary string.
|
||||
* @param a The first binary string.
|
||||
* @param b The second binary string.
|
||||
* @return The sum of the two binary strings as a binary string, or an empty
|
||||
* string if either input string contains non-binary characters.
|
||||
*/
|
||||
std::string addBinary(const std::string& a, const std::string& b) {
|
||||
if (!isValidBinaryString(a) || !isValidBinaryString(b)) {
|
||||
return ""; // Return empty string if input contains non-binary
|
||||
// characters
|
||||
}
|
||||
|
||||
std::string result;
|
||||
int carry = 0;
|
||||
int maxLength = std::max(a.size(), b.size());
|
||||
|
||||
// Traverse both strings from the end to the beginning
|
||||
for (int i = 0; i < maxLength; ++i) {
|
||||
// Get the current bits from both strings, if available
|
||||
int bitA = (i < a.size()) ? (a[a.size() - 1 - i] - '0') : 0;
|
||||
int bitB = (i < b.size()) ? (b[b.size() - 1 - i] - '0') : 0;
|
||||
|
||||
// Calculate the sum of bits and carry
|
||||
int sum = bitA + bitB + carry;
|
||||
carry = sum / 2; // Determine the carry for the next bit
|
||||
result.push_back((sum % 2) +
|
||||
'0'); // Append the sum's current bit to result
|
||||
}
|
||||
if (carry) {
|
||||
result.push_back('1');
|
||||
}
|
||||
std::reverse(result.begin(), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Validates whether a string contains only binary characters (0 or 1).
|
||||
* @param str The string to validate.
|
||||
* @return true if the string is binary, false otherwise.
|
||||
*/
|
||||
bool isValidBinaryString(const std::string& str) const {
|
||||
return std::all_of(str.begin(), str.end(),
|
||||
[](char c) { return c == '0' || c == '1'; });
|
||||
}
|
||||
};
|
||||
} // namespace greedy_algorithms
|
||||
|
||||
/**
|
||||
* @brief run self test implementation.
|
||||
* @returns void
|
||||
*/
|
||||
static void tests() {
|
||||
greedy_algorithms::BinaryAddition binaryAddition;
|
||||
|
||||
// Valid binary string tests
|
||||
assert(binaryAddition.addBinary("1010", "1101") == "10111");
|
||||
assert(binaryAddition.addBinary("1111", "1111") == "11110");
|
||||
assert(binaryAddition.addBinary("101", "11") == "1000");
|
||||
assert(binaryAddition.addBinary("0", "0") == "0");
|
||||
assert(binaryAddition.addBinary("1111", "1111") == "11110");
|
||||
assert(binaryAddition.addBinary("0", "10101") == "10101");
|
||||
assert(binaryAddition.addBinary("10101", "0") == "10101");
|
||||
assert(binaryAddition.addBinary("101010101010101010101010101010",
|
||||
"110110110110110110110110110110") ==
|
||||
"1100001100001100001100001100000");
|
||||
assert(binaryAddition.addBinary("1", "11111111") == "100000000");
|
||||
assert(binaryAddition.addBinary("10101010", "01010101") == "11111111");
|
||||
|
||||
// Invalid binary string tests (should return empty string)
|
||||
assert(binaryAddition.addBinary("10102", "1101") == "");
|
||||
assert(binaryAddition.addBinary("ABC", "1101") == "");
|
||||
assert(binaryAddition.addBinary("1010", "1102") == "");
|
||||
assert(binaryAddition.addBinary("111", "1x1") == "");
|
||||
assert(binaryAddition.addBinary("1x1", "111") == "");
|
||||
assert(binaryAddition.addBinary("1234", "1101") == "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief main function
|
||||
* @returns 0 on successful exit
|
||||
*/
|
||||
int main() {
|
||||
tests(); /// To execute tests
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* @author [Jason Nardoni](https://github.com/JNardoni)
|
||||
* @file
|
||||
*
|
||||
* @brief
|
||||
* [Borůvkas Algorithm](https://en.wikipedia.org/wiki/Borůvka's_algorithm) to
|
||||
*find the Minimum Spanning Tree
|
||||
*
|
||||
*
|
||||
* @details
|
||||
* Boruvka's algorithm is a greepy algorithm to find the MST by starting with
|
||||
*small trees, and combining them to build bigger ones.
|
||||
* 1. Creates a group for every vertex.
|
||||
* 2. looks through each edge of every vertex for the smallest weight. Keeps
|
||||
*track of the smallest edge for each of the current groups.
|
||||
* 3. Combine each group with the group it shares its smallest edge, adding the
|
||||
*smallest edge to the MST.
|
||||
* 4. Repeat step 2-3 until all vertices are combined into a single group.
|
||||
*
|
||||
* It assumes that the graph is connected. Non-connected edges can be
|
||||
*represented using 0 or INT_MAX
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <climits> /// for INT_MAX
|
||||
#include <iostream> /// for IO operations
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace greedy_algorithms
|
||||
* @brief Greedy Algorithms
|
||||
*/
|
||||
namespace greedy_algorithms {
|
||||
/**
|
||||
* @namespace boruvkas_minimum_spanning_tree
|
||||
* @brief Functions for the [Borůvkas
|
||||
* Algorithm](https://en.wikipedia.org/wiki/Borůvka's_algorithm) implementation
|
||||
*/
|
||||
namespace boruvkas_minimum_spanning_tree {
|
||||
/**
|
||||
* @brief Recursively returns the vertex's parent at the root of the tree
|
||||
* @param parent the array that will be checked
|
||||
* @param v vertex to find parent of
|
||||
* @returns the parent of the vertex
|
||||
*/
|
||||
int findParent(std::vector<std::pair<int, int>> parent, const int v) {
|
||||
if (parent[v].first != v) {
|
||||
parent[v].first = findParent(parent, parent[v].first);
|
||||
}
|
||||
|
||||
return parent[v].first;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the implementation of boruvka's algorithm
|
||||
* @param adj a graph adjancency matrix stored as 2d vectors.
|
||||
* @returns the MST as 2d vectors
|
||||
*/
|
||||
std::vector<std::vector<int>> boruvkas(std::vector<std::vector<int>> adj) {
|
||||
size_t size = adj.size();
|
||||
size_t total_groups = size;
|
||||
|
||||
if (size <= 1) {
|
||||
return adj;
|
||||
}
|
||||
|
||||
// Stores the current Minimum Spanning Tree. As groups are combined, they
|
||||
// are added to the MST
|
||||
std::vector<std::vector<int>> MST(size, std::vector<int>(size, INT_MAX));
|
||||
for (int i = 0; i < size; i++) {
|
||||
MST[i][i] = 0;
|
||||
}
|
||||
|
||||
// Step 1: Create a group for each vertex
|
||||
|
||||
// Stores the parent of the vertex and its current depth, both initialized
|
||||
// to 0
|
||||
std::vector<std::pair<int, int>> parent(size, std::make_pair(0, 0));
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
parent[i].first =
|
||||
i; // Sets parent of each vertex to itself, depth remains 0
|
||||
}
|
||||
|
||||
// Repeat until all are in a single group
|
||||
while (total_groups > 1) {
|
||||
std::vector<std::pair<int, int>> smallest_edge(
|
||||
size, std::make_pair(-1, -1)); // Pairing: start node, end node
|
||||
|
||||
// Step 2: Look throught each vertex for its smallest edge, only using
|
||||
// the right half of the adj matrix
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = i + 1; j < size; j++) {
|
||||
if (adj[i][j] == INT_MAX || adj[i][j] == 0) { // No connection
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finds the parents of the start and end points to make sure
|
||||
// they arent in the same group
|
||||
int parentA = findParent(parent, i);
|
||||
int parentB = findParent(parent, j);
|
||||
|
||||
if (parentA != parentB) {
|
||||
// Grabs the start and end points for the first groups
|
||||
// current smallest edge
|
||||
int start = smallest_edge[parentA].first;
|
||||
int end = smallest_edge[parentA].second;
|
||||
|
||||
// If there is no current smallest edge, or the new edge is
|
||||
// smaller, records the new smallest
|
||||
if (start == -1 || adj[i][j] < adj[start][end]) {
|
||||
smallest_edge[parentA].first = i;
|
||||
smallest_edge[parentA].second = j;
|
||||
}
|
||||
|
||||
// Does the same for the second group
|
||||
start = smallest_edge[parentB].first;
|
||||
end = smallest_edge[parentB].second;
|
||||
|
||||
if (start == -1 || adj[j][i] < adj[start][end]) {
|
||||
smallest_edge[parentB].first = j;
|
||||
smallest_edge[parentB].second = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Combine the groups based off their smallest edge
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
// Makes sure the smallest edge exists
|
||||
if (smallest_edge[i].first != -1) {
|
||||
// Start and end points for the groups smallest edge
|
||||
int start = smallest_edge[i].first;
|
||||
int end = smallest_edge[i].second;
|
||||
|
||||
// Parents of the two groups - A is always itself
|
||||
int parentA = i;
|
||||
int parentB = findParent(parent, end);
|
||||
|
||||
// Makes sure the two nodes dont share the same parent. Would
|
||||
// happen if the two groups have been
|
||||
// merged previously through a common shortest edge
|
||||
if (parentA == parentB) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Tries to balance the trees as much as possible as they are
|
||||
// merged. The parent of the shallower
|
||||
// tree will be pointed to the parent of the deeper tree.
|
||||
if (parent[parentA].second < parent[parentB].second) {
|
||||
parent[parentB].first = parentA; // New parent
|
||||
parent[parentB].second++; // Increase depth
|
||||
} else {
|
||||
parent[parentA].first = parentB;
|
||||
parent[parentA].second++;
|
||||
}
|
||||
// Add the connection to the MST, using both halves of the adj
|
||||
// matrix
|
||||
MST[start][end] = adj[start][end];
|
||||
MST[end][start] = adj[end][start];
|
||||
total_groups--; // one fewer group
|
||||
}
|
||||
}
|
||||
}
|
||||
return MST;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief counts the sum of edges in the given tree
|
||||
* @param adj 2D vector adjacency matrix
|
||||
* @returns the int size of the tree
|
||||
*/
|
||||
int test_findGraphSum(std::vector<std::vector<int>> adj) {
|
||||
size_t size = adj.size();
|
||||
int sum = 0;
|
||||
|
||||
// Moves through one side of the adj matrix, counting the sums of each edge
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = i + 1; j < size; j++) {
|
||||
if (adj[i][j] < INT_MAX) {
|
||||
sum += adj[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
} // namespace boruvkas_minimum_spanning_tree
|
||||
} // namespace greedy_algorithms
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void tests() {
|
||||
std::cout << "Starting tests...\n\n";
|
||||
std::vector<std::vector<int>> graph = {
|
||||
{0, 5, INT_MAX, 3, INT_MAX}, {5, 0, 2, INT_MAX, 5},
|
||||
{INT_MAX, 2, 0, INT_MAX, 3}, {3, INT_MAX, INT_MAX, 0, INT_MAX},
|
||||
{INT_MAX, 5, 3, INT_MAX, 0},
|
||||
};
|
||||
std::vector<std::vector<int>> MST =
|
||||
greedy_algorithms::boruvkas_minimum_spanning_tree::boruvkas(graph);
|
||||
assert(greedy_algorithms::boruvkas_minimum_spanning_tree::test_findGraphSum(
|
||||
MST) == 13);
|
||||
std::cout << "1st test passed!" << std::endl;
|
||||
|
||||
graph = {{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}};
|
||||
MST = greedy_algorithms::boruvkas_minimum_spanning_tree::boruvkas(graph);
|
||||
assert(greedy_algorithms::boruvkas_minimum_spanning_tree::test_findGraphSum(
|
||||
MST) == 16);
|
||||
std::cout << "2nd test passed!" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
tests(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @file digit_separation.cpp
|
||||
* @brief Separates digits from numbers in forward and reverse order
|
||||
* @see https://www.log2base2.com/c-examples/loop/split-a-number-into-digits-in-c.html
|
||||
* @details The DigitSeparation class provides two methods to separate the
|
||||
* digits of large integers: digitSeparationReverseOrder and
|
||||
* digitSeparationForwardOrder. The digitSeparationReverseOrder method extracts
|
||||
* digits by repeatedly applying the modulus operation (% 10) to isolate the
|
||||
* last digit, then divides the number by 10 to remove it. This process
|
||||
* continues until the entire number is broken down into its digits, which are
|
||||
* stored in reverse order. If the number is zero, the method directly returns a
|
||||
* vector containing {0} to handle this edge case. Negative numbers are handled
|
||||
* by taking the absolute value, ensuring consistent behavior regardless of the
|
||||
* sign.
|
||||
* @author [Muhammad Junaid Khalid](https://github.com/mjk22071998)
|
||||
*/
|
||||
|
||||
#include <algorithm> /// For reveresing the vector
|
||||
#include <cassert> /// For assert() function to check for errors
|
||||
#include <cmath> /// For abs() function
|
||||
#include <cstdint> /// For int64_t data type to handle large numbers
|
||||
#include <iostream> /// For input/output operations
|
||||
#include <vector> /// For std::vector to store separated digits
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Greedy Algorithms
|
||||
*/
|
||||
namespace greedy_algorithms {
|
||||
|
||||
/**
|
||||
* @brief A class that provides methods to separate the digits of a large
|
||||
* positive number.
|
||||
*/
|
||||
class DigitSeparation {
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor for the DigitSeparation class.
|
||||
*/
|
||||
DigitSeparation() {}
|
||||
|
||||
/**
|
||||
* @brief Implementation of digitSeparationReverseOrder method.
|
||||
*
|
||||
* @param largeNumber The large number to separate digits from.
|
||||
* @return A vector of digits in reverse order.
|
||||
*/
|
||||
std::vector<std::int64_t> digitSeparationReverseOrder(
|
||||
std::int64_t largeNumber) const {
|
||||
std::vector<std::int64_t> result;
|
||||
if (largeNumber != 0) {
|
||||
while (largeNumber != 0) {
|
||||
result.push_back(std::abs(largeNumber % 10));
|
||||
largeNumber /= 10;
|
||||
}
|
||||
} else {
|
||||
result.push_back(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of digitSeparationForwardOrder method.
|
||||
*
|
||||
* @param largeNumber The large number to separate digits from.
|
||||
* @return A vector of digits in forward order.
|
||||
*/
|
||||
std::vector<std::int64_t> digitSeparationForwardOrder(
|
||||
std::int64_t largeNumber) const {
|
||||
std::vector<std::int64_t> result =
|
||||
digitSeparationReverseOrder(largeNumber);
|
||||
std::reverse(result.begin(), result.end());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace greedy_algorithms
|
||||
|
||||
/**
|
||||
* @brief self test implementation
|
||||
* @return void
|
||||
*/
|
||||
static void tests() {
|
||||
greedy_algorithms::DigitSeparation ds;
|
||||
|
||||
// Test case: Positive number
|
||||
std::int64_t number = 1234567890;
|
||||
std::vector<std::int64_t> expectedReverse = {0, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
std::vector<std::int64_t> expectedForward = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
|
||||
std::vector<std::int64_t> reverseOrder =
|
||||
ds.digitSeparationReverseOrder(number);
|
||||
assert(reverseOrder == expectedReverse);
|
||||
std::vector<std::int64_t> forwardOrder =
|
||||
ds.digitSeparationForwardOrder(number);
|
||||
assert(forwardOrder == expectedForward);
|
||||
|
||||
// Test case: Single digit number
|
||||
number = 5;
|
||||
expectedReverse = {5};
|
||||
expectedForward = {5};
|
||||
reverseOrder = ds.digitSeparationReverseOrder(number);
|
||||
assert(reverseOrder == expectedReverse);
|
||||
forwardOrder = ds.digitSeparationForwardOrder(number);
|
||||
assert(forwardOrder == expectedForward);
|
||||
|
||||
// Test case: Zero
|
||||
number = 0;
|
||||
expectedReverse = {0};
|
||||
expectedForward = {0};
|
||||
reverseOrder = ds.digitSeparationReverseOrder(number);
|
||||
assert(reverseOrder == expectedReverse);
|
||||
forwardOrder = ds.digitSeparationForwardOrder(number);
|
||||
assert(forwardOrder == expectedForward);
|
||||
|
||||
// Test case: Large number
|
||||
number = 987654321012345;
|
||||
expectedReverse = {5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
expectedForward = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5};
|
||||
reverseOrder = ds.digitSeparationReverseOrder(number);
|
||||
assert(reverseOrder == expectedReverse);
|
||||
forwardOrder = ds.digitSeparationForwardOrder(number);
|
||||
assert(forwardOrder == expectedForward);
|
||||
|
||||
// Test case: Negative number
|
||||
number = -987654321012345;
|
||||
expectedReverse = {5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
expectedForward = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5};
|
||||
reverseOrder = ds.digitSeparationReverseOrder(number);
|
||||
assert(reverseOrder == expectedReverse);
|
||||
forwardOrder = ds.digitSeparationForwardOrder(number);
|
||||
assert(forwardOrder == expectedForward);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief main function
|
||||
* @return 0 on successful exit
|
||||
*/
|
||||
int main() {
|
||||
tests(); // run self test implementation
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) algorithm
|
||||
* implementation
|
||||
* @details
|
||||
* _Quote from Wikipedia._
|
||||
*
|
||||
* **Dijkstra's algorithm** is an algorithm for finding the
|
||||
* shortest paths between nodes in a weighted graph, which may represent, for
|
||||
* example, road networks. It was conceived by computer scientist Edsger W.
|
||||
* Dijkstra in 1956 and published three years later.
|
||||
*
|
||||
* @author [David Leal](https://github.com/Panquesito7)
|
||||
* @author [Arpan Jain](https://github.com/arpanjain97)
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <climits> /// for INT_MAX
|
||||
#include <iostream> /// for IO operations
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Greedy Algorithms
|
||||
*/
|
||||
namespace greedy_algorithms {
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Functions for the [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) algorithm implementation
|
||||
*/
|
||||
namespace dijkstra {
|
||||
/**
|
||||
* @brief Wrapper class for storing a graph
|
||||
*/
|
||||
class Graph {
|
||||
public:
|
||||
int vertexNum = 0;
|
||||
std::vector<std::vector<int>> edges{};
|
||||
|
||||
/**
|
||||
* @brief Constructs a graph
|
||||
* @param V number of vertices of the graph
|
||||
*/
|
||||
explicit Graph(const int V) {
|
||||
// Initialize the array edges
|
||||
this->edges = std::vector<std::vector<int>>(V, std::vector<int>(V, 0));
|
||||
for (int i = 0; i < V; i++) {
|
||||
edges[i] = std::vector<int>(V, 0);
|
||||
}
|
||||
|
||||
// Fills the array with zeros
|
||||
for (int i = 0; i < V; i++) {
|
||||
for (int j = 0; j < V; j++) {
|
||||
edges[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this->vertexNum = V;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an edge to the graph
|
||||
* @param src the graph the edge should be added to
|
||||
* @param dst the position where the edge should be added to
|
||||
* @param weight the weight of the edge that should be added
|
||||
* @returns void
|
||||
*/
|
||||
void add_edge(int src, int dst, int weight) {
|
||||
this->edges[src][dst] = weight;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility function that finds
|
||||
* the vertex with the minimum distance in `mdist`.
|
||||
*
|
||||
* @param mdist array of distances to each vertex
|
||||
* @param vset array indicating inclusion in the shortest path tree
|
||||
* @param V the number of vertices in the graph
|
||||
* @returns index of the vertex with the minimum distance
|
||||
*/
|
||||
int minimum_distance(std::vector<int> mdist, std::vector<bool> vset, int V) {
|
||||
int minVal = INT_MAX, minInd = 0;
|
||||
for (int i = 0; i < V; i++) {
|
||||
if (!vset[i] && (mdist[i] < minVal)) {
|
||||
minVal = mdist[i];
|
||||
minInd = i;
|
||||
}
|
||||
}
|
||||
|
||||
return minInd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to print the distances to vertices.
|
||||
*
|
||||
* This function prints the distances to each vertex in a tabular format. If the
|
||||
* distance is equal to INT_MAX, it is displayed as "INF".
|
||||
*
|
||||
* @param dist An array representing the distances to each vertex.
|
||||
* @param V The number of vertices in the graph.
|
||||
* @return void
|
||||
*/
|
||||
void print(std::vector<int> dist, int V) {
|
||||
std::cout << "\nVertex Distance\n";
|
||||
for (int i = 0; i < V; i++) {
|
||||
if (dist[i] < INT_MAX) {
|
||||
std::cout << i << "\t" << dist[i] << "\n";
|
||||
}
|
||||
else {
|
||||
std::cout << i << "\tINF" << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main function that finds the shortest path from a given source
|
||||
* to all other vertices using Dijkstra's Algorithm.
|
||||
* @note This doesn't work on negative weights.
|
||||
* @param graph the graph to be processed
|
||||
* @param src the source of the given vertex
|
||||
* @returns void
|
||||
*/
|
||||
void dijkstra(Graph graph, int src) {
|
||||
int V = graph.vertexNum;
|
||||
std::vector<int> mdist{}; // Stores updated distances to the vertex
|
||||
std::vector<bool> vset{}; // `vset[i]` is true if the vertex `i` is included in the shortest path tree
|
||||
|
||||
// Initialize `mdist and `vset`. Set the distance of the source as zero
|
||||
for (int i = 0; i < V; i++) {
|
||||
mdist[i] = INT_MAX;
|
||||
vset[i] = false;
|
||||
}
|
||||
|
||||
mdist[src] = 0;
|
||||
|
||||
// iterate to find the shortest path
|
||||
for (int count = 0; count < V - 1; count++) {
|
||||
int u = minimum_distance(mdist, vset, V);
|
||||
|
||||
vset[u] = true;
|
||||
|
||||
for (int v = 0; v < V; v++) {
|
||||
if (!vset[v] && graph.edges[u][v] &&
|
||||
mdist[u] + graph.edges[u][v] < mdist[v]) {
|
||||
mdist[v] = mdist[u] + graph.edges[u][v];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print(mdist, V);
|
||||
}
|
||||
} // namespace dijkstra
|
||||
} // namespace greedy_algorithms
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void tests() {
|
||||
greedy_algorithms::dijkstra::Graph graph(8);
|
||||
|
||||
// 1st test.
|
||||
graph.add_edge(6, 2, 4);
|
||||
graph.add_edge(2, 6, 4);
|
||||
|
||||
assert(graph.edges[6][2] == 4);
|
||||
|
||||
// 2nd test.
|
||||
graph.add_edge(0, 1, 1);
|
||||
graph.add_edge(1, 0, 1);
|
||||
|
||||
assert(graph.edges[0][1] == 1);
|
||||
|
||||
// 3rd test.
|
||||
graph.add_edge(0, 2, 7);
|
||||
graph.add_edge(2, 0, 7);
|
||||
graph.add_edge(1, 2, 1);
|
||||
graph.add_edge(2, 1, 1);
|
||||
|
||||
assert(graph.edges[0][2] == 7);
|
||||
|
||||
// 4th test.
|
||||
graph.add_edge(1, 3, 3);
|
||||
graph.add_edge(3, 1, 3);
|
||||
graph.add_edge(1, 4, 2);
|
||||
graph.add_edge(4, 1, 2);
|
||||
graph.add_edge(2, 3, 2);
|
||||
|
||||
assert(graph.edges[1][3] == 3);
|
||||
|
||||
std::cout << "All tests have successfully passed!\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
tests(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Gale Shapley
|
||||
* Algorithm](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm)
|
||||
* @details
|
||||
* This implementation utilizes the Gale-Shapley algorithm to find stable
|
||||
* matches.
|
||||
*
|
||||
* **Gale Shapley Algorithm** aims to find a stable matching between two equally
|
||||
* sized sets of elements given an ordinal preference for each element. The
|
||||
* algorithm was introduced by David Gale and Lloyd Shapley in 1962.
|
||||
*
|
||||
* Reference:
|
||||
* [Wikipedia](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm)
|
||||
* [Wikipedia](https://en.wikipedia.org/wiki/Stable_matching_problem)
|
||||
*
|
||||
* @author [B Karthik](https://github.com/BKarthik7)
|
||||
*/
|
||||
|
||||
#include <algorithm> /// for std::find
|
||||
#include <cassert> /// for assert
|
||||
#include <cstdint> /// for std::uint32_t
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Greedy Algorithms
|
||||
*/
|
||||
namespace greedy_algorithms {
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Functions for the Gale-Shapley Algorithm
|
||||
*/
|
||||
namespace stable_matching {
|
||||
/**
|
||||
* @brief The main function that finds the stable matching between two sets of
|
||||
* elements using the Gale-Shapley Algorithm.
|
||||
* @note This doesn't work on negative preferences. the preferences should be
|
||||
* continuous integers starting from 0 to number of preferences - 1.
|
||||
* @param primary_preferences the preferences of the primary set should be a 2D
|
||||
* vector
|
||||
* @param secondary_preferences the preferences of the secondary set should be a
|
||||
* 2D vector
|
||||
* @returns matches the stable matching between the two sets
|
||||
*/
|
||||
std::vector<std::uint32_t> gale_shapley(
|
||||
const std::vector<std::vector<std::uint32_t>>& secondary_preferences,
|
||||
const std::vector<std::vector<std::uint32_t>>& primary_preferences) {
|
||||
std::uint32_t num_elements = secondary_preferences.size();
|
||||
std::vector<std::uint32_t> matches(num_elements, -1);
|
||||
std::vector<bool> is_free_primary(num_elements, true);
|
||||
std::vector<std::uint32_t> proposal_index(
|
||||
num_elements,
|
||||
0); // Tracks the next secondary to propose for each primary
|
||||
|
||||
while (true) {
|
||||
int free_primary_index = -1;
|
||||
|
||||
// Find the next free primary
|
||||
for (std::uint32_t i = 0; i < num_elements; i++) {
|
||||
if (is_free_primary[i]) {
|
||||
free_primary_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no free primary is found, break the loop
|
||||
if (free_primary_index == -1)
|
||||
break;
|
||||
|
||||
// Get the next secondary to propose
|
||||
std::uint32_t secondary_to_propose =
|
||||
primary_preferences[free_primary_index]
|
||||
[proposal_index[free_primary_index]];
|
||||
proposal_index[free_primary_index]++;
|
||||
|
||||
// Get the current match of the secondary
|
||||
std::uint32_t current_match = matches[secondary_to_propose];
|
||||
|
||||
// If the secondary is free, match them
|
||||
if (current_match == -1) {
|
||||
matches[secondary_to_propose] = free_primary_index;
|
||||
is_free_primary[free_primary_index] = false;
|
||||
} else {
|
||||
// Determine if the current match should be replaced
|
||||
auto new_proposer_rank =
|
||||
std::find(secondary_preferences[secondary_to_propose].begin(),
|
||||
secondary_preferences[secondary_to_propose].end(),
|
||||
free_primary_index);
|
||||
auto current_match_rank =
|
||||
std::find(secondary_preferences[secondary_to_propose].begin(),
|
||||
secondary_preferences[secondary_to_propose].end(),
|
||||
current_match);
|
||||
|
||||
// If the new proposer is preferred over the current match
|
||||
if (new_proposer_rank < current_match_rank) {
|
||||
matches[secondary_to_propose] = free_primary_index;
|
||||
is_free_primary[free_primary_index] = false;
|
||||
is_free_primary[current_match] =
|
||||
true; // Current match is now free
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
} // namespace stable_matching
|
||||
} // namespace greedy_algorithms
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void tests() {
|
||||
// Test Case 1
|
||||
std::vector<std::vector<std::uint32_t>> primary_preferences = {
|
||||
{0, 1, 2, 3}, {2, 1, 3, 0}, {1, 2, 0, 3}, {3, 0, 1, 2}};
|
||||
std::vector<std::vector<std::uint32_t>> secondary_preferences = {
|
||||
{1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}};
|
||||
assert(greedy_algorithms::stable_matching::gale_shapley(
|
||||
secondary_preferences, primary_preferences) ==
|
||||
std::vector<std::uint32_t>({0, 2, 1, 3}));
|
||||
|
||||
// Test Case 2
|
||||
primary_preferences = {
|
||||
{0, 2, 1, 3}, {2, 3, 0, 1}, {3, 1, 2, 0}, {2, 1, 0, 3}};
|
||||
secondary_preferences = {
|
||||
{1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}};
|
||||
assert(greedy_algorithms::stable_matching::gale_shapley(
|
||||
secondary_preferences, primary_preferences) ==
|
||||
std::vector<std::uint32_t>({0, 3, 1, 2}));
|
||||
|
||||
// Test Case 3
|
||||
primary_preferences = {{0, 1, 2}, {2, 1, 0}, {1, 2, 0}};
|
||||
secondary_preferences = {{1, 0, 2}, {2, 0, 1}, {0, 2, 1}};
|
||||
assert(greedy_algorithms::stable_matching::gale_shapley(
|
||||
secondary_preferences, primary_preferences) ==
|
||||
std::vector<std::uint32_t>({0, 2, 1}));
|
||||
|
||||
// Test Case 4
|
||||
primary_preferences = {};
|
||||
secondary_preferences = {};
|
||||
assert(greedy_algorithms::stable_matching::gale_shapley(
|
||||
secondary_preferences, primary_preferences) ==
|
||||
std::vector<std::uint32_t>({}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
tests(); // Run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// C++ program for Huffman Coding
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
using namespace std;
|
||||
|
||||
// A Huffman tree node
|
||||
struct MinHeapNode {
|
||||
// One of the input characters
|
||||
char data;
|
||||
|
||||
// Frequency of the character
|
||||
unsigned freq;
|
||||
|
||||
// Left and right child
|
||||
MinHeapNode *left, *right;
|
||||
|
||||
MinHeapNode(char data, unsigned freq)
|
||||
|
||||
{
|
||||
left = right = NULL;
|
||||
this->data = data;
|
||||
this->freq = freq;
|
||||
}
|
||||
};
|
||||
|
||||
void deleteAll(const MinHeapNode* const root) {
|
||||
if (root) {
|
||||
deleteAll(root->left);
|
||||
deleteAll(root->right);
|
||||
delete root;
|
||||
}
|
||||
}
|
||||
|
||||
// For comparison of
|
||||
// two heap nodes (needed in min heap)
|
||||
struct compare {
|
||||
bool operator()(const MinHeapNode* const l,
|
||||
const MinHeapNode* const r) const {
|
||||
return l->freq > r->freq;
|
||||
}
|
||||
};
|
||||
|
||||
// Prints huffman codes from
|
||||
// the root of Huffman Tree.
|
||||
void printCodes(struct MinHeapNode* root, const string& str) {
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
if (root->data != '$')
|
||||
cout << root->data << ": " << str << "\n";
|
||||
|
||||
printCodes(root->left, str + "0");
|
||||
printCodes(root->right, str + "1");
|
||||
}
|
||||
|
||||
// The main function that builds a Huffman Tree and
|
||||
// print codes by traversing the built Huffman Tree
|
||||
void HuffmanCodes(const char data[], const int freq[], int size) {
|
||||
struct MinHeapNode *left, *right;
|
||||
|
||||
// Create a min heap & inserts all characters of data[]
|
||||
priority_queue<MinHeapNode*, vector<MinHeapNode*>, compare> minHeap;
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
minHeap.push(new MinHeapNode(data[i], freq[i]));
|
||||
|
||||
// Iterate while size of heap doesn't become 1
|
||||
while (minHeap.size() != 1) {
|
||||
// Extract the two minimum
|
||||
// freq items from min heap
|
||||
left = minHeap.top();
|
||||
minHeap.pop();
|
||||
|
||||
right = minHeap.top();
|
||||
minHeap.pop();
|
||||
|
||||
// Create a new internal node with
|
||||
// frequency equal to the sum of the
|
||||
// two nodes frequencies. Make the
|
||||
// two extracted node as left and right children
|
||||
// of this new node. Add this node
|
||||
// to the min heap '$' is a special value
|
||||
// for internal nodes, not used
|
||||
auto* const top = new MinHeapNode('$', left->freq + right->freq);
|
||||
|
||||
top->left = left;
|
||||
top->right = right;
|
||||
|
||||
minHeap.push(top);
|
||||
}
|
||||
|
||||
// Print Huffman codes using
|
||||
// the Huffman tree built above
|
||||
printCodes(minHeap.top(), "");
|
||||
deleteAll(minHeap.top());
|
||||
}
|
||||
|
||||
// Driver program to test above functions
|
||||
int main() {
|
||||
char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
int freq[] = {5, 9, 12, 13, 16, 45};
|
||||
|
||||
int size = sizeof(arr) / sizeof(arr[0]);
|
||||
|
||||
HuffmanCodes(arr, freq, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Jumping Game](https://leetcode.com/problems/jump-game/)
|
||||
* algorithm implementation
|
||||
* @details
|
||||
*
|
||||
* Given an array of non-negative integers, you are initially positioned at the
|
||||
* first index of the array. Each element in the array represents your maximum
|
||||
* jump length at that position. Determine if you are able to reach the last
|
||||
* index. This solution takes in input as a vector and output as a boolean to
|
||||
* check if you can reach the last position. We name the indices good and bad
|
||||
* based on whether we can reach the destination if we start at that position.
|
||||
* We initialize the last index as lastPos.
|
||||
* Here, we start from the end of the array and check if we can ever reach the
|
||||
* first index. We check if the sum of the index and the maximum jump count
|
||||
* given is greater than or equal to the lastPos. If yes, then that is the last
|
||||
* position you can reach starting from the back. After the end of the loop, if
|
||||
* we reach the lastPos as 0, then the destination can be reached from the start
|
||||
* position.
|
||||
*
|
||||
* @author [Rakshaa Viswanathan](https://github.com/rakshaa2000)
|
||||
* @author [David Leal](https://github.com/Panquesito7)
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for std::cout
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Greedy Algorithms
|
||||
*/
|
||||
namespace greedy_algorithms {
|
||||
/**
|
||||
* @brief Checks whether the given element (default is `1`) can jump to the last
|
||||
* index.
|
||||
* @param nums array of numbers containing the maximum jump (in steps) from that
|
||||
* index
|
||||
* @returns true if the index can be reached
|
||||
* @returns false if the index can NOT be reached
|
||||
*/
|
||||
bool can_jump(const std::vector<int> &nums) {
|
||||
size_t lastPos = nums.size() - 1;
|
||||
for (size_t i = lastPos; i != static_cast<size_t>(-1); i--) {
|
||||
if (i + nums[i] >= lastPos) {
|
||||
lastPos = i;
|
||||
}
|
||||
}
|
||||
return lastPos == 0;
|
||||
}
|
||||
} // namespace greedy_algorithms
|
||||
|
||||
/**
|
||||
* @brief Function to test the above algorithm
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
assert(greedy_algorithms::can_jump(std::vector<int>({4, 3, 1, 0, 5})));
|
||||
assert(!greedy_algorithms::can_jump(std::vector<int>({3, 2, 1, 0, 4})));
|
||||
assert(greedy_algorithms::can_jump(std::vector<int>({5, 9, 4, 7, 15, 3})));
|
||||
assert(!greedy_algorithms::can_jump(std::vector<int>({1, 0, 5, 8, 12})));
|
||||
assert(greedy_algorithms::can_jump(std::vector<int>({2, 1, 4, 7})));
|
||||
|
||||
std::cout << "All tests have successfully passed!\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
struct Item {
|
||||
int weight;
|
||||
int profit;
|
||||
};
|
||||
|
||||
float profitPerUnit(Item x) { return (float)x.profit / (float)x.weight; }
|
||||
|
||||
int partition(Item arr[], int low, int high) {
|
||||
Item pivot = arr[high]; // pivot
|
||||
int i = (low - 1); // Index of smaller element
|
||||
|
||||
for (int j = low; j < high; j++) {
|
||||
// If current element is smaller than or
|
||||
// equal to pivot
|
||||
if (profitPerUnit(arr[j]) <= profitPerUnit(pivot)) {
|
||||
i++; // increment index of smaller element
|
||||
Item temp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = temp;
|
||||
}
|
||||
}
|
||||
Item temp = arr[i + 1];
|
||||
arr[i + 1] = arr[high];
|
||||
arr[high] = temp;
|
||||
return (i + 1);
|
||||
}
|
||||
|
||||
void quickSort(Item arr[], int low, int high) {
|
||||
if (low < high) {
|
||||
int p = partition(arr, low, high);
|
||||
|
||||
quickSort(arr, low, p - 1);
|
||||
quickSort(arr, p + 1, high);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
cout << "\nEnter the capacity of the knapsack : ";
|
||||
float capacity;
|
||||
cin >> capacity;
|
||||
cout << "\n Enter the number of Items : ";
|
||||
int n;
|
||||
cin >> n;
|
||||
Item *itemArray = new Item[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
cout << "\nEnter the weight and profit of item " << i + 1 << " : ";
|
||||
cin >> itemArray[i].weight;
|
||||
cin >> itemArray[i].profit;
|
||||
}
|
||||
|
||||
quickSort(itemArray, 0, n - 1);
|
||||
|
||||
// show(itemArray, n);
|
||||
|
||||
float maxProfit = 0;
|
||||
int i = n;
|
||||
while (capacity > 0 && --i >= 0) {
|
||||
if (capacity >= itemArray[i].weight) {
|
||||
maxProfit += itemArray[i].profit;
|
||||
capacity -= itemArray[i].weight;
|
||||
cout << "\n\t" << itemArray[i].weight << "\t"
|
||||
<< itemArray[i].profit;
|
||||
} else {
|
||||
maxProfit += profitPerUnit(itemArray[i]) * capacity;
|
||||
cout << "\n\t" << capacity << "\t"
|
||||
<< profitPerUnit(itemArray[i]) * capacity;
|
||||
capacity = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cout << "\nMax Profit : " << maxProfit;
|
||||
delete[] itemArray;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Kruskals Minimum Spanning
|
||||
* Tree](https://www.simplilearn.com/tutorials/data-structure-tutorial/kruskal-algorithm)
|
||||
* implementation
|
||||
*
|
||||
* @details
|
||||
* _Quoted from
|
||||
* [Simplilearn](https://www.simplilearn.com/tutorials/data-structure-tutorial/kruskal-algorithm)._
|
||||
*
|
||||
* Kruskal’s algorithm is the concept that is introduced in the graph theory of
|
||||
* discrete mathematics. It is used to discover the shortest path between two
|
||||
* points in a connected weighted graph. This algorithm converts a given graph
|
||||
* into the forest, considering each node as a separate tree. These trees can
|
||||
* only link to each other if the edge connecting them has a low value and
|
||||
* doesn’t generate a cycle in MST structure.
|
||||
*
|
||||
* @author [coleman2246](https://github.com/coleman2246)
|
||||
*/
|
||||
|
||||
#include <array> /// for array
|
||||
#include <iostream> /// for IO operations
|
||||
#include <limits> /// for numeric limits
|
||||
#include <cstdint> /// for uint32_t
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Greedy Algorithms
|
||||
*/
|
||||
namespace greedy_algorithms {
|
||||
/**
|
||||
* @brief Finds the minimum edge of the given graph.
|
||||
* @param infinity Defines the infinity of the graph
|
||||
* @param graph The graph that will be used to find the edge
|
||||
* @returns void
|
||||
*/
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
void findMinimumEdge(const T &infinity,
|
||||
const std::array<std::array<T, N>, M> &graph) {
|
||||
if (N != M) {
|
||||
std::cout << "\nWrong input passed. Provided array has dimensions " << N
|
||||
<< "x" << M << ". Please provide a square matrix.\n";
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < graph.size(); i++) {
|
||||
int min = infinity;
|
||||
int minIndex = 0;
|
||||
for (int j = 0; j < graph.size(); j++) {
|
||||
if (i != j && graph[i][j] != 0 && graph[i][j] < min) {
|
||||
min = graph[i][j];
|
||||
minIndex = j;
|
||||
}
|
||||
}
|
||||
std::cout << i << " - " << minIndex << "\t" << graph[i][minIndex]
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
} // namespace greedy_algorithms
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
/**
|
||||
* define a large value for int
|
||||
* define a large value for float
|
||||
* define a large value for double
|
||||
* define a large value for uint32_t
|
||||
*/
|
||||
constexpr int INFINITY_INT = std::numeric_limits<int>::max();
|
||||
constexpr float INFINITY_FLOAT = std::numeric_limits<float>::max();
|
||||
constexpr double INFINITY_DOUBLE = std::numeric_limits<double>::max();
|
||||
constexpr uint32_t INFINITY_UINT32 = UINT32_MAX;
|
||||
|
||||
// Test case with integer values
|
||||
std::cout << "\nTest Case 1 :\n";
|
||||
std::array<std::array<int, 6>, 6> graph1{
|
||||
0, 4, 1, 4, INFINITY_INT, INFINITY_INT,
|
||||
4, 0, 3, 8, 3, INFINITY_INT,
|
||||
1, 3, 0, INFINITY_INT, 1, INFINITY_INT,
|
||||
4, 8, INFINITY_INT, 0, 5, 7,
|
||||
INFINITY_INT, 3, 1, 5, 0, INFINITY_INT,
|
||||
INFINITY_INT, INFINITY_INT, INFINITY_INT, 7, INFINITY_INT, 0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, graph1);
|
||||
|
||||
// Test case with floating values
|
||||
std::cout << "\nTest Case 2 :\n";
|
||||
std::array<std::array<float, 3>, 3> graph2{
|
||||
0.0f, 2.5f, INFINITY_FLOAT,
|
||||
2.5f, 0.0f, 3.2f,
|
||||
INFINITY_FLOAT, 3.2f, 0.0f};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_FLOAT, graph2);
|
||||
|
||||
// Test case with double values
|
||||
std::cout << "\nTest Case 3 :\n";
|
||||
std::array<std::array<double, 5>, 5> graph3{
|
||||
0.0, 10.5, INFINITY_DOUBLE, 6.7, 3.3,
|
||||
10.5, 0.0, 8.1, 15.4, INFINITY_DOUBLE,
|
||||
INFINITY_DOUBLE, 8.1, 0.0, INFINITY_DOUBLE, 7.8,
|
||||
6.7, 15.4, INFINITY_DOUBLE, 0.0, 9.9,
|
||||
3.3, INFINITY_DOUBLE, 7.8, 9.9, 0.0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_DOUBLE, graph3);
|
||||
|
||||
// Test Case with negative weights
|
||||
std::cout << "\nTest Case 4 :\n";
|
||||
std::array<std::array<int, 3>, 3> graph_neg{
|
||||
0, -2, 4,
|
||||
-2, 0, 3,
|
||||
4, 3, 0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, graph_neg);
|
||||
|
||||
// Test Case with Self-Loops
|
||||
std::cout << "\nTest Case 5 :\n";
|
||||
std::array<std::array<int, 3>, 3> graph_self_loop{
|
||||
2, 1, INFINITY_INT,
|
||||
INFINITY_INT, 0, 4,
|
||||
INFINITY_INT, 4, 0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, graph_self_loop);
|
||||
|
||||
// Test Case with no edges
|
||||
std::cout << "\nTest Case 6 :\n";
|
||||
std::array<std::array<int, 4>, 4> no_edges{
|
||||
0, INFINITY_INT, INFINITY_INT, INFINITY_INT,
|
||||
INFINITY_INT, 0, INFINITY_INT, INFINITY_INT,
|
||||
INFINITY_INT, INFINITY_INT, 0, INFINITY_INT,
|
||||
INFINITY_INT, INFINITY_INT, INFINITY_INT, 0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, no_edges);
|
||||
|
||||
// Test Case with a non-connected graph
|
||||
std::cout << "\nTest Case 7:\n";
|
||||
std::array<std::array<int, 4>, 4> partial_graph{
|
||||
0, 2, INFINITY_INT, 6,
|
||||
2, 0, 3, INFINITY_INT,
|
||||
INFINITY_INT, 3, 0, 4,
|
||||
6, INFINITY_INT, 4, 0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, partial_graph);
|
||||
|
||||
// Test Case with Directed weighted graph. The Krushkal algorithm does not give
|
||||
// optimal answer
|
||||
std::cout << "\nTest Case 8:\n";
|
||||
std::array<std::array<int, 4>, 4> directed_graph{
|
||||
0, 3, 7, INFINITY_INT, // Vertex 0 has edges to Vertex 1 and Vertex 2
|
||||
INFINITY_INT, 0, 2, 5, // Vertex 1 has edges to Vertex 2 and Vertex 3
|
||||
INFINITY_INT, INFINITY_INT, 0, 1, // Vertex 2 has an edge to Vertex 3
|
||||
INFINITY_INT, INFINITY_INT, INFINITY_INT, 0}; // Vertex 3 has no outgoing edges
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, directed_graph);
|
||||
|
||||
// Test case with wrong input passed
|
||||
std::cout << "\nTest Case 9:\n";
|
||||
std::array<std::array<int, 4>, 3> graph9{
|
||||
0, 5, 5, 5,
|
||||
5, 0, 5, 5,
|
||||
5, 5, 5, 5};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, graph9);
|
||||
|
||||
// Test case with all the same values between every edge
|
||||
std::cout << "\nTest Case 10:\n";
|
||||
std::array<std::array<int, 5>, 5> graph10{
|
||||
0, 5, 5, 5, 5,
|
||||
5, 0, 5, 5, 5,
|
||||
5, 5, 0, 5, 5,
|
||||
5, 5, 5, 0, 5,
|
||||
5, 5, 5, 5, 0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_INT, graph10);
|
||||
|
||||
// Test Case with uint32_t values
|
||||
std::cout << "\nTest Case 11 :\n";
|
||||
std::array<std::array<uint32_t, 4>, 4> graph_uint32{
|
||||
0, 5, INFINITY_UINT32, 9,
|
||||
5, 0, 2, INFINITY_UINT32,
|
||||
INFINITY_UINT32, 2, 0, 6,
|
||||
9, INFINITY_UINT32, 6, 0};
|
||||
greedy_algorithms::findMinimumEdge(INFINITY_UINT32, graph_uint32);
|
||||
|
||||
std::cout << "\nAll tests have successfully passed!\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
|
||||
int main() {
|
||||
test(); // run Self-test implementation
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
#define V 4
|
||||
#define INFINITY 99999
|
||||
|
||||
int graph[V][V] = {{0, 5, 1, 2}, {5, 0, 3, 3}, {1, 3, 0, 4}, {2, 3, 4, 0}};
|
||||
|
||||
struct mst {
|
||||
bool visited;
|
||||
int key;
|
||||
int near;
|
||||
};
|
||||
|
||||
mst MST_Array[V];
|
||||
|
||||
void initilize() {
|
||||
for (int i = 0; i < V; i++) {
|
||||
MST_Array[i].visited = false;
|
||||
MST_Array[i].key = INFINITY; // considering INFINITY as inifinity
|
||||
MST_Array[i].near = i;
|
||||
}
|
||||
|
||||
MST_Array[0].key = 0;
|
||||
}
|
||||
|
||||
void updateNear() {
|
||||
for (int v = 0; v < V; v++) {
|
||||
int min = INFINITY;
|
||||
int minIndex = 0;
|
||||
for (int i = 0; i < V; i++) {
|
||||
if (MST_Array[i].key < min && MST_Array[i].visited == false &&
|
||||
MST_Array[i].key != INFINITY) {
|
||||
min = MST_Array[i].key;
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
MST_Array[minIndex].visited = true;
|
||||
|
||||
for (int i = 0; i < V; i++) {
|
||||
if (graph[minIndex][i] != 0 && graph[minIndex][i] < INFINITY) {
|
||||
if (graph[minIndex][i] < MST_Array[i].key) {
|
||||
MST_Array[i].key = graph[minIndex][i];
|
||||
MST_Array[i].near = minIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void show() {
|
||||
for (int i = 0; i < V; i++) {
|
||||
cout << i << " - " << MST_Array[i].near << "\t"
|
||||
<< graph[i][MST_Array[i].near] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
initilize();
|
||||
updateNear();
|
||||
show();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user