diff --git a/src/main/java/com/thealgorithms/others/ArrayRotation.java b/src/main/java/com/thealgorithms/others/ArrayRotation.java new file mode 100644 index 000000000000..7e3f93d3a9d0 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/ArrayRotation.java @@ -0,0 +1,78 @@ +package com.thealgorithms.others; + +/** + * Array Rotation Utility + * + * Supports: + * 1. Left Rotation + * 2. Right Rotation + * + * Approach: + * Reversal Algorithm + * + * Time Complexity: O(n) + * Space Complexity: O(1) + */ +public final class ArrayRotation { + + private ArrayRotation() { + } + + /** + * Rotates the array to the right by k positions. + * + * @param nums the input array + * @param k number of rotations + */ + public static void rotateRight(int[] nums, int k) { + int n = nums.length; + + if (n == 0) { + return; + } + + k = k % n; + + reverse(nums, 0, n - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + } + + /** + * Rotates the array to the left by k positions. + * + * @param nums the input array + * @param k number of rotations + */ + public static void rotateLeft(int[] nums, int k) { + int n = nums.length; + + if (n == 0) { + return; + } + + k = k % n; + + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + reverse(nums, 0, n - 1); + } + + /** + * Reverses elements between start and end indices. + * + * @param nums the input array + * @param start starting index + * @param end ending index + */ + private static void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + + start++; + end--; + } + } +} diff --git a/src/main/java/com/thealgorithms/others/Trie.java b/src/main/java/com/thealgorithms/others/Trie.java new file mode 100644 index 000000000000..339451b00a41 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Trie.java @@ -0,0 +1,162 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +import java.util.List; + +public class Trie { + + private static final int ALPHABET_SIZE = 26; + + // ---------------- NODE ---------------- + + static class TrieNode { + + TrieNode[] children = new TrieNode[ALPHABET_SIZE]; + + boolean isWordEnd; + } + + // ---------------- ROOT ---------------- + + private final TrieNode root = new TrieNode(); + + // ---------------- INSERT ---------------- + + public void insert(String word) { + + word = normalize(word); + + TrieNode current = root; + + for (char c : word.toCharArray()) { + + int index = c - 'a'; + + if (current.children[index] == null) { + current.children[index] = new TrieNode(); + } + + current = current.children[index]; + } + + current.isWordEnd = true; + } + + // ---------------- SEARCH ---------------- + + public boolean search(String word) { + + word = normalize(word); + + TrieNode current = root; + + for (char c : word.toCharArray()) { + + int index = c - 'a'; + + if (current.children[index] == null) { + return false; + } + + current = current.children[index]; + } + + return current.isWordEnd; + } + + // ---------------- PREFIX ---------------- + + public boolean startsWith(String prefix) { + + prefix = normalize(prefix); + + return findNode(prefix) != null; + } + + // ---------------- AUTOCOMPLETE ---------------- + + public List suggestions(String prefix) { + + prefix = normalize(prefix); + + List result = new ArrayList<>(); + + TrieNode node = findNode(prefix); + + // Prefix doesn't exist + if (node == null) { + return result; + } + + StringBuilder currentWord = new StringBuilder(prefix); + + collectSuggestions(node, currentWord, result); + + return result; + } + + // Recursive autocomplete + private void collectSuggestions(TrieNode node, StringBuilder currentWord, List result) { + + // Current word is complete + if (node.isWordEnd) { + result.add(currentWord.toString()); + } + + // Check all 26 possible characters + for (int i = 0; i < ALPHABET_SIZE; i++) { + + if (node.children[i] != null) { + + // Add character + currentWord.append((char) ('a' + i)); + + // Go to next node + collectSuggestions(node.children[i], currentWord, result); + + // Backtrack + currentWord.deleteCharAt(currentWord.length() - 1); + } + } + } + + // ---------------- FIND NODE ---------------- + + private TrieNode findNode(String text) { + + TrieNode current = root; + + for (char c : text.toCharArray()) { + + int index = c - 'a'; + + if (current.children[index] == null) { + return null; + } + + current = current.children[index]; + } + + return current; + } + + // ---------------- NORMALIZE ---------------- + + private String normalize(String text) { + + if (text == null || text.isBlank()) { + throw new IllegalArgumentException("Word cannot be empty"); + } + + text = text.toLowerCase().trim(); + + for (char c : text.toCharArray()) { + + if (c < 'a' || c > 'z') { + throw new IllegalArgumentException("Only a-z characters are allowed"); + } + } + + return text; + } +} diff --git a/src/test/java/com/thealgorithms/others/ArrayRotationTest.java b/src/test/java/com/thealgorithms/others/ArrayRotationTest.java new file mode 100644 index 000000000000..f43448483021 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/ArrayRotationTest.java @@ -0,0 +1,62 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class ArrayRotationTest { + + @Test + void shouldRotateArrayRightByTwoPositions() { + int[] values = {1, 2, 3, 4, 5}; + + ArrayRotation.rotateRight(values, 2); + + assertArrayEquals(new int[] {4, 5, 1, 2, 3}, values); + } + + @Test + void shouldRotateArrayLeftByTwoPositions() { + int[] values = {1, 2, 3, 4, 5}; + + ArrayRotation.rotateLeft(values, 2); + + assertArrayEquals(new int[] {3, 4, 5, 1, 2}, values); + } + + @Test + void shouldHandleRotationGreaterThanArrayLength() { + int[] values = {10, 20, 30, 40}; + + ArrayRotation.rotateRight(values, 6); + + assertArrayEquals(new int[] {30, 40, 10, 20}, values); + } + + @Test + void shouldKeepSingleElementArrayUnchanged() { + int[] values = {99}; + + ArrayRotation.rotateLeft(values, 5); + + assertArrayEquals(new int[] {99}, values); + } + + @Test + void shouldHandleEmptyArrayWithoutErrors() { + int[] values = {}; + + ArrayRotation.rotateRight(values, 3); + + assertArrayEquals(new int[] {}, values); + } + + @Test + void shouldReturnOriginalArrayWhenRotationIsZero() { + int[] values = {7, 8, 9}; + + ArrayRotation.rotateLeft(values, 0); + + assertArrayEquals(new int[] {7, 8, 9}, values); + } +}