diff --git a/src/main/java/com/thealgorithms/Recursion/GenerateSubsets.java b/src/main/java/com/thealgorithms/Recursion/GenerateSubsets.java index 417bf1307790..5eefe3eee671 100644 --- a/src/main/java/com/thealgorithms/Recursion/GenerateSubsets.java +++ b/src/main/java/com/thealgorithms/Recursion/GenerateSubsets.java @@ -5,6 +5,11 @@ import java.util.ArrayList; import java.util.List; +/** + * Finds all permutations of given array + * @author Tuhin Mondal (Git-Tuhin Mondal) + */ + public final class GenerateSubsets { private GenerateSubsets() { diff --git a/src/main/java/com/thealgorithms/Recursion/GenerateUniqueSubsets.java b/src/main/java/com/thealgorithms/Recursion/GenerateUniqueSubsets.java new file mode 100644 index 000000000000..9c8cffa59df8 --- /dev/null +++ b/src/main/java/com/thealgorithms/Recursion/GenerateUniqueSubsets.java @@ -0,0 +1,47 @@ +package com.thealgorithms.Recursion; + +// program to find unique power set of a string + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Finds all permutations of given array + * @author Tuhin Mondal (Git-Tuhin Mondal) + */ + +public final class GenerateUniqueSubsets { + + private GenerateUniqueSubsets() { + throw new UnsupportedOperationException("Utility class"); + } + + public static List subsetRecursion(String str) { + Set ans = doRecursion("", str); + List a = new ArrayList<>(ans.stream().toList()); + Collections.sort(a); + return a; + } + + private static Set doRecursion(String p, String up) { + if (up.isEmpty()) { + Set list = new HashSet<>(); + list.add(p); + return list; + } + + // Taking the character + char ch = up.charAt(0); + // Adding the character in the recursion + Set left = doRecursion(p + ch, up.substring(1)); + // Not adding the character in the recursion + Set right = doRecursion(p, up.substring(1)); + + left.addAll(right); + + return left; + } +} diff --git a/src/main/java/com/thealgorithms/Recursion/TowerOfHanoi.java b/src/main/java/com/thealgorithms/Recursion/TowerOfHanoi.java new file mode 100644 index 000000000000..f127ef74161d --- /dev/null +++ b/src/main/java/com/thealgorithms/Recursion/TowerOfHanoi.java @@ -0,0 +1,32 @@ +package com.thealgorithms.Recursion; + +import java.util.ArrayList; +import java.util.List; + +/** + * Finds all permutations of given array + * @author Tuhin Mondal (Git-Tuhin Mondal) + */ + +public final class TowerOfHanoi { + private TowerOfHanoi() { + throw new UnsupportedOperationException("Utility class"); + } + + public static List towerOfHanoi(int n) { + List arr = new ArrayList<>(); + recursionApproach(n, 'A', 'B', 'C', arr); + return arr; + } + + public static void recursionApproach(int n, char a, char b, char c, List list) { + if (n == 1) { + list.add("Take disk 1 from rod " + a + " to rod " + b); + return; + } + + recursionApproach(n - 1, a, c, b, list); + list.add("Take disk " + n + " from rod " + a + " to rod " + b); + recursionApproach(n - 1, c, b, a, list); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/DES.java b/src/main/java/com/thealgorithms/ciphers/DES.java deleted file mode 100644 index 7f3eed70f3c2..000000000000 --- a/src/main/java/com/thealgorithms/ciphers/DES.java +++ /dev/null @@ -1,250 +0,0 @@ -package com.thealgorithms.ciphers; - -/** - * This class is build to demonstrate the application of the DES-algorithm - * (https://en.wikipedia.org/wiki/Data_Encryption_Standard) on a plain English message. The supplied - * key must be in form of a 64 bit binary String. - */ -public class DES { - - private String key; - private final String[] subKeys; - - private void sanitize(String key) { - int length = key.length(); - if (length != 64) { - throw new IllegalArgumentException("DES key must be supplied as a 64 character binary string"); - } - } - - DES(String key) { - sanitize(key); - this.key = key; - subKeys = getSubkeys(key); - } - - public String getKey() { - return this.key; - } - - public void setKey(String key) { - sanitize(key); - this.key = key; - } - - // Permutation table to convert initial 64-bit key to 56 bit key - private static final int[] PC1 = {57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4}; - - // Lookup table used to shift the initial key, in order to generate the subkeys - private static final int[] KEY_SHIFTS = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; - - // Table to convert the 56 bit subkeys to 48 bit subkeys - private static final int[] PC2 = {14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32}; - - // Initial permutation of each 64 but message block - private static final int[] IP = {58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7}; - - // Expansion table to convert right half of message blocks from 32 bits to 48 bits - private static final int[] EXPANSION = {32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1}; - - // The eight substitution boxes are defined below - private static final int[][] S1 = {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}}; - - private static final int[][] S2 = {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}}; - - private static final int[][] S3 = {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}}; - - private static final int[][] S4 = {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}}; - - private static final int[][] S5 = {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14}, {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}}; - - private static final int[][] S6 = {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11}, {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8}, {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6}, {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}}; - - private static final int[][] S7 = {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6}, {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2}, {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}}; - - private static final int[][] S8 = {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7}, {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8}, {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}; - - private static final int[][][] S = {S1, S2, S3, S4, S5, S6, S7, S8}; - - // Permutation table, used in the Feistel function post s-box usage - static final int[] PERMUTATION = {16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25}; - - // Table used for final inversion of the message box after 16 rounds of Feistel Function - static final int[] IP_INVERSE = {40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25}; - - private String[] getSubkeys(String originalKey) { - StringBuilder permutedKey = new StringBuilder(); // Initial permutation of keys via pc1 - int i; - int j; - for (i = 0; i < 56; i++) { - permutedKey.append(originalKey.charAt(PC1[i] - 1)); - } - String[] subKeys = new String[16]; - String initialPermutedKey = permutedKey.toString(); - String c0 = initialPermutedKey.substring(0, 28); - String d0 = initialPermutedKey.substring(28); - - // We will now operate on the left and right halves of the permutedKey - for (i = 0; i < 16; i++) { - String cN = c0.substring(KEY_SHIFTS[i]) + c0.substring(0, KEY_SHIFTS[i]); - String dN = d0.substring(KEY_SHIFTS[i]) + d0.substring(0, KEY_SHIFTS[i]); - subKeys[i] = cN + dN; - c0 = cN; // Re-assign the values to create running permutation - d0 = dN; - } - - // Let us shrink the keys to 48 bits (well, characters here) using pc2 - for (i = 0; i < 16; i++) { - String key = subKeys[i]; - permutedKey.setLength(0); - for (j = 0; j < 48; j++) { - permutedKey.append(key.charAt(PC2[j] - 1)); - } - subKeys[i] = permutedKey.toString(); - } - - return subKeys; - } - - private String xOR(String a, String b) { - int i; - int l = a.length(); - StringBuilder xor = new StringBuilder(); - for (i = 0; i < l; i++) { - int firstBit = a.charAt(i) - 48; // 48 is '0' in ascii - int secondBit = b.charAt(i) - 48; - xor.append((firstBit ^ secondBit)); - } - return xor.toString(); - } - - private String createPaddedString(String s, int desiredLength, char pad) { - int i; - int l = s.length(); - StringBuilder paddedString = new StringBuilder(); - int diff = desiredLength - l; - for (i = 0; i < diff; i++) { - paddedString.append(pad); - } - return paddedString.toString(); - } - - private String pad(String s, int desiredLength) { - return createPaddedString(s, desiredLength, '0') + s; - } - - private String padLast(String s, int desiredLength) { - return s + createPaddedString(s, desiredLength, '\u0000'); - } - - private String feistel(String messageBlock, String key) { - int i; - StringBuilder expandedKey = new StringBuilder(); - for (i = 0; i < 48; i++) { - expandedKey.append(messageBlock.charAt(EXPANSION[i] - 1)); - } - String mixedKey = xOR(expandedKey.toString(), key); - StringBuilder substitutedString = new StringBuilder(); - - // Let us now use the s-boxes to transform each 6 bit (length here) block to 4 bits - for (i = 0; i < 48; i += 6) { - String block = mixedKey.substring(i, i + 6); - int row = (block.charAt(0) - 48) * 2 + (block.charAt(5) - 48); - int col = (block.charAt(1) - 48) * 8 + (block.charAt(2) - 48) * 4 + (block.charAt(3) - 48) * 2 + (block.charAt(4) - 48); - String substitutedBlock = pad(Integer.toBinaryString(S[i / 6][row][col]), 4); - substitutedString.append(substitutedBlock); - } - - StringBuilder permutedString = new StringBuilder(); - for (i = 0; i < 32; i++) { - permutedString.append(substitutedString.charAt(PERMUTATION[i] - 1)); - } - - return permutedString.toString(); - } - - private String encryptBlock(String message, String[] keys) { - StringBuilder permutedMessage = new StringBuilder(); - int i; - for (i = 0; i < 64; i++) { - permutedMessage.append(message.charAt(IP[i] - 1)); - } - String e0 = permutedMessage.substring(0, 32); - String f0 = permutedMessage.substring(32); - - // Iterate 16 times - for (i = 0; i < 16; i++) { - String eN = f0; // Previous Right block - String fN = xOR(e0, feistel(f0, keys[i])); - e0 = eN; - f0 = fN; - } - - String combinedBlock = f0 + e0; // Reverse the 16th block - permutedMessage.setLength(0); - for (i = 0; i < 64; i++) { - permutedMessage.append(combinedBlock.charAt(IP_INVERSE[i] - 1)); - } - return permutedMessage.toString(); - } - - // To decode, we follow the same process as encoding, but with reversed keys - private String decryptBlock(String message, String[] keys) { - String[] reversedKeys = new String[keys.length]; - for (int i = 0; i < keys.length; i++) { - reversedKeys[i] = keys[keys.length - i - 1]; - } - return encryptBlock(message, reversedKeys); - } - - /** - * @param message Message to be encrypted - * @return The encrypted message, as a binary string - */ - public String encrypt(String message) { - StringBuilder encryptedMessage = new StringBuilder(); - int l = message.length(); - int i; - int j; - if (l % 8 != 0) { - int desiredLength = (l / 8 + 1) * 8; - l = desiredLength; - message = padLast(message, desiredLength); - } - - for (i = 0; i < l; i += 8) { - String block = message.substring(i, i + 8); - StringBuilder bitBlock = new StringBuilder(); - byte[] bytes = block.getBytes(); - for (j = 0; j < 8; j++) { - bitBlock.append(pad(Integer.toBinaryString(bytes[j]), 8)); - } - encryptedMessage.append(encryptBlock(bitBlock.toString(), subKeys)); - } - return encryptedMessage.toString(); - } - - /** - * @param message The encrypted string. Expects it to be a multiple of 64 bits, in binary format - * @return The decrypted String, in plain English - */ - public String decrypt(String message) { - StringBuilder decryptedMessage = new StringBuilder(); - int l = message.length(); - int i; - int j; - if (l % 64 != 0) { - throw new IllegalArgumentException("Encrypted message should be a multiple of 64 characters in length"); - } - for (i = 0; i < l; i += 64) { - String block = message.substring(i, i + 64); - String result = decryptBlock(block, subKeys); - byte[] res = new byte[8]; - for (j = 0; j < 64; j += 8) { - res[j / 8] = (byte) Integer.parseInt(result.substring(j, j + 8), 2); - } - decryptedMessage.append(new String(res)); - } - return decryptedMessage.toString().replace("\0", ""); // Get rid of the null bytes used for padding - } -} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java deleted file mode 100644 index 2b8959ed0136..000000000000 --- a/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.thealgorithms.datastructures.crdt; - -import java.util.HashSet; -import java.util.Set; - -/** - * GSet (Grow-only Set) is a state-based CRDT (Conflict-free Replicated Data Type) - * that allows only the addition of elements and ensures that once an element is added, - * it cannot be removed. The merge operation of two G-Sets is their union. - * This implementation supports adding elements, looking up elements, comparing with other G-Sets, - * and merging with another G-Set to create a new G-Set containing all unique elements from both sets. - * (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) - * - * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) - */ - -public class GSet { - private final Set elements; - - /** - * Constructs an empty G-Set. - */ - public GSet() { - this.elements = new HashSet<>(); - } - - /** - * Adds an element to the G-Set. - * - * @param e the element to be added - */ - public void addElement(T e) { - elements.add(e); - } - - /** - * Checks if the given element is present in the G-Set. - * - * @param e the element to be checked - * @return true if the element is present, false otherwise - */ - public boolean lookup(T e) { - return elements.contains(e); - } - - /** - * Compares the G-Set with another G-Set to check if it is a subset. - * - * @param other the other G-Set to compare with - * @return true if the current G-Set is a subset of the other, false otherwise - */ - public boolean compare(GSet other) { - return other.elements.containsAll(elements); - } - - /** - * Merges the current G-Set with another G-Set, creating a new G-Set - * containing all unique elements from both sets. - * - * @param other the G-Set to merge with - */ - public void merge(GSet other) { - elements.addAll(other.elements); - } -} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/LWWElementSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/LWWElementSet.java deleted file mode 100644 index 2c6ce8a427d1..000000000000 --- a/src/main/java/com/thealgorithms/datastructures/crdt/LWWElementSet.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.thealgorithms.datastructures.crdt; - -import java.util.HashMap; -import java.util.Map; - -/** - * Last-Write-Wins Element Set (LWWElementSet) is a state-based CRDT (Conflict-free Replicated Data Type) - * designed for managing sets in a distributed and concurrent environment. It supports the addition and removal - * of elements, using timestamps to determine the order of operations. The set is split into two subsets: - * the add set for elements to be added and the remove set for elements to be removed. - * - * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) - * @see Conflict-free_replicated_data_type - * @see itakurah (Niklas Hoefflin) - */ - -class Element { - String key; - int timestamp; - Bias bias; - - /** - * Constructs a new Element with the specified key, timestamp and bias. - * - * @param key The key of the element. - * @param timestamp The timestamp associated with the element. - * @param bias The bias of the element (ADDS or REMOVALS). - */ - Element(String key, int timestamp, Bias bias) { - this.key = key; - this.timestamp = timestamp; - this.bias = bias; - } -} - -enum Bias { - /** - * ADDS bias for the add set. - * REMOVALS bias for the remove set. - */ - ADDS, - REMOVALS -} - -class LWWElementSet { - private final Map addSet; - private final Map removeSet; - - /** - * Constructs an empty LWWElementSet. - */ - LWWElementSet() { - this.addSet = new HashMap<>(); - this.removeSet = new HashMap<>(); - } - - /** - * Adds an element to the addSet. - * - * @param e The element to be added. - */ - public void add(Element e) { - addSet.put(e.key, e); - } - - /** - * Removes an element from the removeSet. - * - * @param e The element to be removed. - */ - public void remove(Element e) { - if (lookup(e)) { - removeSet.put(e.key, e); - } - } - - /** - * Checks if an element is in the LWWElementSet by comparing timestamps in the addSet and removeSet. - * - * @param e The element to be checked. - * @return True if the element is present, false otherwise. - */ - public boolean lookup(Element e) { - Element inAddSet = addSet.get(e.key); - Element inRemoveSet = removeSet.get(e.key); - - return (inAddSet != null && (inRemoveSet == null || inAddSet.timestamp > inRemoveSet.timestamp)); - } - - /** - * Compares the LWWElementSet with another LWWElementSet to check if addSet and removeSet are a subset. - * - * @param other The LWWElementSet to compare. - * @return True if the set is subset, false otherwise. - */ - public boolean compare(LWWElementSet other) { - return other.addSet.keySet().containsAll(addSet.keySet()) && other.removeSet.keySet().containsAll(removeSet.keySet()); - } - - /** - * Merges another LWWElementSet into this set by resolving conflicts based on timestamps. - * - * @param other The LWWElementSet to merge. - */ - public void merge(LWWElementSet other) { - for (Element e : other.addSet.values()) { - if (!addSet.containsKey(e.key) || compareTimestamps(addSet.get(e.key), e)) { - addSet.put(e.key, e); - } - } - - for (Element e : other.removeSet.values()) { - if (!removeSet.containsKey(e.key) || compareTimestamps(removeSet.get(e.key), e)) { - removeSet.put(e.key, e); - } - } - } - - /** - * Compares timestamps of two elements based on their bias (ADDS or REMOVALS). - * - * @param e The first element. - * @param other The second element. - * @return True if the first element's timestamp is greater or the bias is ADDS and timestamps are equal. - */ - public boolean compareTimestamps(Element e, Element other) { - if (e.bias != other.bias) { - throw new IllegalArgumentException("Invalid bias value"); - } - Bias bias = e.bias; - int timestampComparison = Integer.compare(e.timestamp, other.timestamp); - - if (timestampComparison == 0) { - return bias != Bias.ADDS; - } - return timestampComparison < 0; - } -} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/ORSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/ORSet.java deleted file mode 100644 index a4cc2ffdd4a6..000000000000 --- a/src/main/java/com/thealgorithms/datastructures/crdt/ORSet.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.thealgorithms.datastructures.crdt; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -/** - * ORSet (Observed-Removed Set) is a state-based CRDT (Conflict-free Replicated Data Type) - * that supports both addition and removal of elements. This particular implementation follows - * the Add-Wins strategy, meaning that in case of conflicting add and remove operations, - * the add operation takes precedence. The merge operation of two OR-Sets ensures that - * elements added at any replica are eventually observed at all replicas. Removed elements, - * once observed, are never reintroduced. - * This OR-Set implementation provides methods for adding elements, removing elements, - * checking for element existence, retrieving the set of elements, comparing with other OR-Sets, - * and merging with another OR-Set to create a new OR-Set containing all unique elements - * from both sets. - * - * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) - * @see Conflict-free_replicated_data_type - * @see itakurah (Niklas Hoefflin) - */ - -public class ORSet { - - private final Set> elements; - private final Set> tombstones; - - /** - * Constructs an empty OR-Set. - */ - public ORSet() { - this.elements = new HashSet<>(); - this.tombstones = new HashSet<>(); - } - - /** - * Checks if the set contains the specified element. - * - * @param element the element to check for - * @return true if the set contains the element, false otherwise - */ - public boolean contains(T element) { - return elements.stream().anyMatch(pair -> pair.getElement().equals(element)); - } - - /** - * Retrieves the elements in the set. - * - * @return a set containing the elements - */ - public Set elements() { - Set result = new HashSet<>(); - elements.forEach(pair -> result.add(pair.getElement())); - return result; - } - - /** - * Adds the specified element to the set. - * - * @param element the element to add - */ - public void add(T element) { - String n = prepare(); - effect(element, n); - } - - /** - * Removes the specified element from the set. - * - * @param element the element to remove - */ - public void remove(T element) { - Set> pairsToRemove = prepare(element); - effect(pairsToRemove); - } - - /** - * Collect all pairs with the specified element. - * - * @param element the element to collect pairs for - * @return a set of pairs with the specified element to be removed - */ - private Set> prepare(T element) { - Set> pairsToRemove = new HashSet<>(); - for (Pair pair : elements) { - if (pair.getElement().equals(element)) { - pairsToRemove.add(pair); - } - } - return pairsToRemove; - } - - /** - * Generates a unique tag for the element. - * - * @return the unique tag - */ - private String prepare() { - return generateUniqueTag(); - } - - /** - * Adds the element with the specified unique tag to the set. - * - * @param element the element to add - * @param n the unique tag associated with the element - */ - private void effect(T element, String n) { - Pair pair = new Pair<>(element, n); - elements.add(pair); - elements.removeAll(tombstones); - } - - /** - * Removes the specified pairs from the set. - * - * @param pairsToRemove the pairs to remove - */ - private void effect(Set> pairsToRemove) { - elements.removeAll(pairsToRemove); - tombstones.addAll(pairsToRemove); - } - - /** - * Generates a unique tag. - * - * @return the unique tag - */ - private String generateUniqueTag() { - return UUID.randomUUID().toString(); - } - - /** - * Compares this Add-Wins OR-Set with another OR-Set to check if elements and tombstones are a subset. - * - * @param other the other OR-Set to compare - * @return true if the sets are subset, false otherwise - */ - public boolean compare(ORSet other) { - Set> union = new HashSet<>(elements); - union.addAll(tombstones); - - Set> otherUnion = new HashSet<>(other.elements); - otherUnion.addAll(other.tombstones); - - return otherUnion.containsAll(union) && other.tombstones.containsAll(tombstones); - } - - /** - * Merges this Add-Wins OR-Set with another OR-Set. - * - * @param other the other OR-Set to merge - */ - public void merge(ORSet other) { - elements.removeAll(other.tombstones); - other.elements.removeAll(tombstones); - elements.addAll(other.elements); - tombstones.addAll(other.tombstones); - } - - /** - * Represents a pair containing an element and a unique tag. - * - * @param the type of the element in the pair - */ - public static class Pair { - private final T element; - private final String uniqueTag; - - /** - * Constructs a pair with the specified element and unique tag. - * - * @param element the element in the pair - * @param uniqueTag the unique tag associated with the element - */ - public Pair(T element, String uniqueTag) { - this.element = element; - this.uniqueTag = uniqueTag; - } - - /** - * Gets the element from the pair. - * - * @return the element - */ - public T getElement() { - return element; - } - } -} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java deleted file mode 100644 index c0ce17b2802b..000000000000 --- a/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.thealgorithms.datastructures.crdt; - -import java.util.HashSet; -import java.util.Set; - -/** - * TwoPhaseSet (2P-Set) is a state-based CRDT (Conflict-free Replicated Data Type) designed for managing sets - * with support for both addition and removal operations in a distributed and concurrent environment. - * It combines two G-Sets (grow-only sets) - one set for additions and another set (tombstone set) for removals. - * Once an element is removed and placed in the tombstone set, it cannot be re-added, adhering to "remove-wins" semantics. - * This implementation supports querying the presence of elements, adding elements, removing elements, - * comparing with other 2P-Sets, and merging two 2P-Sets while preserving the remove-wins semantics. - * (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) - * - * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) - */ - -public class TwoPSet { - private final Set setA; - private final Set setR; - - /** - * Constructs an empty Two-Phase Set. - */ - public TwoPSet() { - this.setA = new HashSet<>(); - this.setR = new HashSet<>(); - } - - /** - * Checks if an element is in the set and has not been removed. - * - * @param element The element to be checked. - * @return True if the element is in the set and has not been removed, otherwise false. - */ - public boolean lookup(T element) { - return setA.contains(element) && !setR.contains(element); - } - - /** - * Adds an element to the set. - * - * @param element The element to be added. - */ - public void add(T element) { - setA.add(element); - } - - /** - * Removes an element from the set. The element will be placed in the tombstone set. - * - * @param element The element to be removed. - */ - public void remove(T element) { - if (lookup(element)) { - setR.add(element); - } - } - - /** - * Compares the current 2P-Set with another 2P-Set. - * - * @param otherSet The other 2P-Set to compare with. - * @return True if both SetA and SetR are subset, otherwise false. - */ - public boolean compare(TwoPSet otherSet) { - return otherSet.setA.containsAll(setA) && otherSet.setR.containsAll(setR); - } - - /** - * Merges the current 2P-Set with another 2P-Set. - * - * @param otherSet The other 2P-Set to merge with. - * @return A new 2P-Set containing the merged elements. - */ - public TwoPSet merge(TwoPSet otherSet) { - TwoPSet mergedSet = new TwoPSet<>(); - mergedSet.setA.addAll(this.setA); - mergedSet.setA.addAll(otherSet.setA); - mergedSet.setR.addAll(this.setR); - mergedSet.setR.addAll(otherSet.setR); - return mergedSet; - } -} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java deleted file mode 100644 index dcdb08ad133e..000000000000 --- a/src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java +++ /dev/null @@ -1,217 +0,0 @@ -package com.thealgorithms.datastructures.graphs; - -import java.util.ArrayList; -import java.util.List; - -/** - * Boruvka's algorithm to find Minimum Spanning Tree - * (https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm) - * - * @author itakurah (https://github.com/itakurah) - */ - -final class BoruvkaAlgorithm { - private BoruvkaAlgorithm() { - } - - /** - * Represents an edge in the graph - */ - static class Edge { - final int src; - final int dest; - final int weight; - - Edge(final int src, final int dest, final int weight) { - this.src = src; - this.dest = dest; - this.weight = weight; - } - } - - /** - * Represents the graph - */ - static class Graph { - final int vertex; - final List edges; - - /** - * Constructor for the graph - * - * @param vertex number of vertices - * @param edges list of edges - */ - Graph(final int vertex, final List edges) { - if (vertex < 0) { - throw new IllegalArgumentException("Number of vertices must be positive"); - } - if (edges == null || edges.isEmpty()) { - throw new IllegalArgumentException("Edges list must not be null or empty"); - } - for (final var edge : edges) { - checkEdgeVertices(edge.src, vertex); - checkEdgeVertices(edge.dest, vertex); - } - - this.vertex = vertex; - this.edges = edges; - } - } - - /** - * Represents a subset for Union-Find operations - */ - private static class Component { - int parent; - int rank; - - Component(final int parent, final int rank) { - this.parent = parent; - this.rank = rank; - } - } - - /** - * Represents the state of Union-Find components and the result list - */ - private static class BoruvkaState { - List result; - Component[] components; - final Graph graph; - - BoruvkaState(final Graph graph) { - this.result = new ArrayList<>(); - this.components = initializeComponents(graph); - this.graph = graph; - } - - /** - * Adds the cheapest edges to the result list and performs Union operation on the subsets. - * - * @param cheapest Array containing the cheapest edge for each subset. - */ - void merge(final Edge[] cheapest) { - for (int i = 0; i < graph.vertex; ++i) { - if (cheapest[i] != null) { - final var component1 = find(components, cheapest[i].src); - final var component2 = find(components, cheapest[i].dest); - - if (component1 != component2) { - result.add(cheapest[i]); - union(components, component1, component2); - } - } - } - } - - /** - * Checks if there are more edges to add to the result list - * - * @return true if there are more edges to add, false otherwise - */ - boolean hasMoreEdgesToAdd() { - return result.size() < graph.vertex - 1; - } - - /** - * Computes the cheapest edges for each subset in the Union-Find structure. - * - * @return an array containing the cheapest edge for each subset. - */ - private Edge[] computeCheapestEdges() { - Edge[] cheapest = new Edge[graph.vertex]; - for (final var edge : graph.edges) { - final var set1 = find(components, edge.src); - final var set2 = find(components, edge.dest); - - if (set1 != set2) { - if (cheapest[set1] == null || edge.weight < cheapest[set1].weight) { - cheapest[set1] = edge; - } - if (cheapest[set2] == null || edge.weight < cheapest[set2].weight) { - cheapest[set2] = edge; - } - } - } - return cheapest; - } - - /** - * Initializes subsets for Union-Find - * - * @param graph the graph - * @return the initialized subsets - */ - private static Component[] initializeComponents(final Graph graph) { - Component[] components = new Component[graph.vertex]; - for (int v = 0; v < graph.vertex; ++v) { - components[v] = new Component(v, 0); - } - return components; - } - } - - /** - * Finds the parent of the subset using path compression - * - * @param components array of subsets - * @param i index of the subset - * @return the parent of the subset - */ - static int find(final Component[] components, final int i) { - if (components[i].parent != i) { - components[i].parent = find(components, components[i].parent); - } - return components[i].parent; - } - - /** - * Performs the Union operation for Union-Find - * - * @param components array of subsets - * @param x index of the first subset - * @param y index of the second subset - */ - static void union(Component[] components, final int x, final int y) { - final int xroot = find(components, x); - final int yroot = find(components, y); - - if (components[xroot].rank < components[yroot].rank) { - components[xroot].parent = yroot; - } else if (components[xroot].rank > components[yroot].rank) { - components[yroot].parent = xroot; - } else { - components[yroot].parent = xroot; - components[xroot].rank++; - } - } - - /** - * Boruvka's algorithm to find the Minimum Spanning Tree - * - * @param graph the graph - * @return list of edges in the Minimum Spanning Tree - */ - static List boruvkaMST(final Graph graph) { - var boruvkaState = new BoruvkaState(graph); - - while (boruvkaState.hasMoreEdgesToAdd()) { - final var cheapest = boruvkaState.computeCheapestEdges(); - boruvkaState.merge(cheapest); - } - return boruvkaState.result; - } - - /** - * Checks if the edge vertices are in a valid range - * - * @param vertex the vertex to check - * @param upperBound the upper bound for the vertex range - */ - private static void checkEdgeVertices(final int vertex, final int upperBound) { - if (vertex < 0 || vertex >= upperBound) { - throw new IllegalArgumentException("Edge vertex out of range"); - } - } -} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java b/src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java deleted file mode 100644 index 52308c23cf1c..000000000000 --- a/src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.thealgorithms.dynamicprogramming; - -import java.util.Arrays; - -/* -Given an array of non-negative integers , partition the array in two subset that -difference in sum of elements for both subset minimum. -Return the minimum difference in sum of these subsets you can achieve. - -Input: array[] = {1, 6, 11, 4} -Output: 0 -Explanation: -Subset1 = {1, 4, 6}, sum of Subset1 = 11 -Subset2 = {11}, sum of Subset2 = 11 - -Input: array[] = {36, 7, 46, 40} -Output: 23 -Explanation: -Subset1 = {7, 46} ; sum of Subset1 = 53 -Subset2 = {36, 40} ; sum of Subset2 = 76 - */ -public final class MinimumSumPartition { - private MinimumSumPartition() { - } - - private static void throwIfInvalidInput(final int[] array) { - if (Arrays.stream(array).anyMatch(a -> a < 0)) { - throw new IllegalArgumentException("Input array should not contain negative number(s)."); - } - } - - public static int minimumSumPartition(final int[] array) { - throwIfInvalidInput(array); - int sum = Arrays.stream(array).sum(); - boolean[] dp = new boolean[sum / 2 + 1]; - dp[0] = true; // Base case , don't select any element from array - - // Find the closest sum of subset array that we can achieve which is closest to half of sum of full array - int closestPartitionSum = 0; - - for (int i = 0; i < array.length; i++) { - for (int j = sum / 2; j > 0; j--) { - if (array[i] <= j) { - dp[j] = dp[j] || dp[j - array[i]]; - } - if (dp[j]) { - closestPartitionSum = Math.max(closestPartitionSum, j); - } - } - } - /* - Difference in sum = Big partition sum - Small partition sum - = ( Total sum - Small partition sum) - Small partition sum - */ - return sum - (2 * closestPartitionSum); - } -} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/PartitionProblem.java b/src/main/java/com/thealgorithms/dynamicprogramming/PartitionProblem.java deleted file mode 100644 index 49c4a0a3a008..000000000000 --- a/src/main/java/com/thealgorithms/dynamicprogramming/PartitionProblem.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @author Md Asif Joardar - * - * Description: The partition problem is a classic problem in computer science - * that asks whether a given set can be partitioned into two subsets such that - * the sum of elements in each subset is the same. - * - * Example: - * Consider nums = {1, 2, 3} - * We can split the array "nums" into two partitions, where each having a sum of 3. - * nums1 = {1, 2} - * nums2 = {3} - * - * The time complexity of the solution is O(n × sum) and requires O(n × sum) space - */ - -package com.thealgorithms.dynamicprogramming; - -import java.util.Arrays; - -public final class PartitionProblem { - private PartitionProblem() { - } - - /** - * Test if a set of integers can be partitioned into two subsets such that the sum of elements - * in each subset is the same. - * - * @param nums the array contains integers. - * @return {@code true} if two subset exists, otherwise {@code false}. - */ - public static boolean partition(int[] nums) { - // calculate the sum of all the elements in the array - int sum = Arrays.stream(nums).sum(); - - // it will return true if the sum is even and the array can be divided into two - // subarrays/subset with equal sum. and here i reuse the SubsetSum class from dynamic - // programming section to check if there is exists a subsetsum into nums[] array same as the - // given sum - return (sum & 1) == 0 && SubsetSum.subsetSum(nums, sum / 2); - } -} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java deleted file mode 100644 index 0c5bc2c5884d..000000000000 --- a/src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.thealgorithms.dynamicprogramming; - -/** - * Find the number of subsets present in the given array with a sum equal to target. - * Based on Solution discussed on - * StackOverflow - * @author Samrat Podder - */ -public final class SubsetCount { - private SubsetCount() { - } - - /** - * Dynamic Programming Implementation. - * Method to find out the number of subsets present in the given array with a sum equal to - * target. Time Complexity is O(n*target) and Space Complexity is O(n*target) - * @param arr is the input array on which subsets are to searched - * @param target is the sum of each element of the subset taken together - * - */ - public static int getCount(int[] arr, int target) { - /* - * Base Cases - If target becomes zero, we have reached the required sum for the subset - * If we reach the end of the array arr then, either if target==arr[end], then we add one to - * the final count Otherwise we add 0 to the final count - */ - int n = arr.length; - int[][] dp = new int[n][target + 1]; - for (int i = 0; i < n; i++) { - dp[i][0] = 1; - } - if (arr[0] <= target) { - dp[0][arr[0]] = 1; - } - for (int t = 1; t <= target; t++) { - for (int idx = 1; idx < n; idx++) { - int notpick = dp[idx - 1][t]; - int pick = 0; - if (arr[idx] <= t) { - pick += dp[idx - 1][target - t]; - } - dp[idx][target] = pick + notpick; - } - } - return dp[n - 1][target]; - } - - /** - * This Method is a Space Optimized version of the getCount(int[], int) method and solves the - * same problem This approach is a bit better in terms of Space Used Time Complexity is - * O(n*target) and Space Complexity is O(target) - * @param arr is the input array on which subsets are to searched - * @param target is the sum of each element of the subset taken together - */ - public static int getCountSO(int[] arr, int target) { - int n = arr.length; - int[] prev = new int[target + 1]; - prev[0] = 1; - if (arr[0] <= target) { - prev[arr[0]] = 1; - } - for (int ind = 1; ind < n; ind++) { - int[] cur = new int[target + 1]; - cur[0] = 1; - for (int t = 1; t <= target; t++) { - int notTaken = prev[t]; - int taken = 0; - if (arr[ind] <= t) { - taken = prev[t - arr[ind]]; - } - cur[t] = notTaken + taken; - } - prev = cur; - } - return prev[target]; - } -} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java deleted file mode 100644 index 3dd41d2fdc0f..000000000000 --- a/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.thealgorithms.dynamicprogramming; - -public final class SubsetSum { - private SubsetSum() { - } - - /** - * Test if a set of integers contains a subset that sums to a given integer. - * - * @param arr the array containing integers. - * @param sum the target sum of the subset. - * @return {@code true} if a subset exists that sums to the given value, otherwise {@code false}. - */ - public static boolean subsetSum(int[] arr, int sum) { - int n = arr.length; - boolean[][] isSum = new boolean[n + 1][sum + 1]; - - // Initialize the first column to true since a sum of 0 can always be achieved with an empty subset. - for (int i = 0; i <= n; i++) { - isSum[i][0] = true; - } - - // Fill the subset sum matrix - for (int i = 1; i <= n; i++) { - for (int j = 1; j <= sum; j++) { - if (arr[i - 1] <= j) { - isSum[i][j] = isSum[i - 1][j] || isSum[i - 1][j - arr[i - 1]]; - } else { - isSum[i][j] = isSum[i - 1][j]; - } - } - } - - return isSum[n][sum]; - } -} diff --git a/src/test/java/com/thealgorithms/Recursion/GenerateUniqueSubsetsTest.java b/src/test/java/com/thealgorithms/Recursion/GenerateUniqueSubsetsTest.java new file mode 100644 index 000000000000..ceaa5081f0c5 --- /dev/null +++ b/src/test/java/com/thealgorithms/Recursion/GenerateUniqueSubsetsTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.Recursion; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public final class GenerateUniqueSubsetsTest { + + @Test + void subsetRecursionTestOne() { + String str = "aba"; + String[] expected = new String[] {"", "a", "aa", "ab", "aba", "b", "ba"}; + + List ans = GenerateUniqueSubsets.subsetRecursion(str); + assertArrayEquals(ans.toArray(), expected); + } + + @Test + void subsetRecursionTestTwo() { + String str = "abba"; + String[] expected = new String[] {"", "a", "aa", "ab", "aba", "abb", "abba", "b", "ba", "bb", "bba"}; + + List ans = GenerateUniqueSubsets.subsetRecursion(str); + assertArrayEquals(ans.toArray(), expected); + } + + @Test + void subsetRecursionTestThree() { + String str = "aaa"; + String[] expected = new String[] {"", "a", "aa", "aaa"}; + + List ans = GenerateUniqueSubsets.subsetRecursion(str); + assertArrayEquals(ans.toArray(), expected); + } +} diff --git a/src/test/java/com/thealgorithms/Recursion/TowerOfHanoiTest.java b/src/test/java/com/thealgorithms/Recursion/TowerOfHanoiTest.java new file mode 100644 index 000000000000..404c195ae400 --- /dev/null +++ b/src/test/java/com/thealgorithms/Recursion/TowerOfHanoiTest.java @@ -0,0 +1,42 @@ +package com.thealgorithms.Recursion; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public final class TowerOfHanoiTest { + + @Test + void hanoiTowerTestOne() { + + int n = 5; + String[] expected = {"Take disk 1 from rod A to rod B", "Take disk 2 from rod A to rod C", "Take disk 1 from rod B to rod C", "Take disk 3 from rod A to rod B", "Take disk 1 from rod C to rod A", "Take disk 2 from rod C to rod B", "Take disk 1 from rod A to rod B", + "Take disk 4 from rod A to rod C", "Take disk 1 from rod B to rod C", "Take disk 2 from rod B to rod A", "Take disk 1 from rod C to rod A", "Take disk 3 from rod B to rod C", "Take disk 1 from rod A to rod B", "Take disk 2 from rod A to rod C", "Take disk 1 from rod B to rod C", + "Take disk 5 from rod A to rod B", "Take disk 1 from rod C to rod A", "Take disk 2 from rod C to rod B", "Take disk 1 from rod A to rod B", "Take disk 3 from rod C to rod A", "Take disk 1 from rod B to rod C", "Take disk 2 from rod B to rod A", "Take disk 1 from rod C to rod A", + "Take disk 4 from rod C to rod B", "Take disk 1 from rod A to rod B", "Take disk 2 from rod A to rod C", "Take disk 1 from rod B to rod C", "Take disk 3 from rod A to rod B", "Take disk 1 from rod C to rod A", "Take disk 2 from rod C to rod B", "Take disk 1 from rod A to rod B"}; + + List actual = TowerOfHanoi.towerOfHanoi(n); + assertArrayEquals(expected, actual.toArray()); + } + + @Test + void hanoiTowerTestTwo() { + + int n = 3; + String[] expected = {"Take disk 1 from rod A to rod B", "Take disk 2 from rod A to rod C", "Take disk 1 from rod B to rod C", "Take disk 3 from rod A to rod B", "Take disk 1 from rod C to rod A", "Take disk 2 from rod C to rod B", "Take disk 1 from rod A to rod B"}; + + List actual = TowerOfHanoi.towerOfHanoi(n); + assertArrayEquals(expected, actual.toArray()); + } + + @Test + void hanoiTowerTestThree() { + + int n = 1; + String[] expected = {"Take disk 1 from rod A to rod B"}; + + List actual = TowerOfHanoi.towerOfHanoi(n); + assertArrayEquals(expected, actual.toArray()); + } +}