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

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:28 +08:00
commit 29cfe479ab
432 changed files with 68491 additions and 0 deletions
+18
View File
@@ -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} )
+119
View File
@@ -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;
}
+142
View File
@@ -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;
}
+202
View File
@@ -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;
}
+155
View File
@@ -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;
}
+108
View File
@@ -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;
}
+74
View File
@@ -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;
}
+78
View File
@@ -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)._
*
* Kruskals 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
* doesnt 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;
}