From ab5bb8358ae609398f2fc9c0253fb961ee76fc8f Mon Sep 17 00:00:00 2001 From: "sailok.chinta" Date: Thu, 3 Oct 2024 11:28:51 +0530 Subject: [PATCH 1/5] feat: extend Trie implementation from lowercase english alphabets to all ascii characters --- .../datastructures/trees/TrieImp.java | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java b/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java index d166653ff1b4..a23783d3f9af 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java @@ -1,5 +1,6 @@ package com.thealgorithms.datastructures.trees; +import java.util.HashMap; import java.util.Scanner; /** @@ -8,31 +9,33 @@ * @author Dheeraj Kumar Barnwal */ public class TrieImp { - public class TrieNode { - - TrieNode[] child; + char value; + HashMap child; boolean end; - public TrieNode() { - child = new TrieNode[26]; + public TrieNode(char value) { + this.value = value; + child = new HashMap<>(); end = false; } } + private static final char ROOT_NODE_VALUE = '*'; + private final TrieNode root; public TrieImp() { - root = new TrieNode(); + root = new TrieNode(ROOT_NODE_VALUE); } public void insert(String word) { TrieNode currentNode = root; for (int i = 0; i < word.length(); i++) { - TrieNode node = currentNode.child[word.charAt(i) - 'a']; + TrieNode node = currentNode.child.getOrDefault(word.charAt(i), null); if (node == null) { - node = new TrieNode(); - currentNode.child[word.charAt(i) - 'a'] = node; + node = new TrieNode(word.charAt(i)); + currentNode.child.put(word.charAt(i), node); } currentNode = node; } @@ -43,7 +46,7 @@ public boolean search(String word) { TrieNode currentNode = root; for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); - TrieNode node = currentNode.child[ch - 'a']; + TrieNode node = currentNode.child.getOrDefault(ch, null); if (node == null) { return false; } @@ -56,7 +59,7 @@ public boolean delete(String word) { TrieNode currentNode = root; for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); - TrieNode node = currentNode.child[ch - 'a']; + TrieNode node = currentNode.child.getOrDefault(ch, null); if (node == null) { return false; } @@ -73,13 +76,6 @@ public static void sop(String print) { System.out.println(print); } - /** - * Regex to check if word contains only a-z character - */ - public static boolean isValid(String word) { - return word.matches("^[a-z]+$"); - } - public static void main(String[] args) { TrieImp obj = new TrieImp(); String word; @@ -92,20 +88,12 @@ public static void main(String[] args) { switch (t) { case 1: word = scan.next(); - if (isValid(word)) { - obj.insert(word); - } else { - sop("Invalid string: allowed only a-z"); - } + obj.insert(word); break; case 2: word = scan.next(); - boolean resS = false; - if (isValid(word)) { - resS = obj.search(word); - } else { - sop("Invalid string: allowed only a-z"); - } + boolean resS = obj.search(word); + if (resS) { sop("word found"); } else { @@ -115,11 +103,8 @@ public static void main(String[] args) { case 3: word = scan.next(); boolean resD = false; - if (isValid(word)) { - resD = obj.delete(word); - } else { - sop("Invalid string: allowed only a-z"); - } + resD = obj.delete(word); + if (resD) { sop("word got deleted successfully"); } else { From 8f36b05d799ae392ac636cd0f0d996e533bed2e4 Mon Sep 17 00:00:00 2001 From: "sailok.chinta" Date: Sat, 5 Oct 2024 09:38:32 +0530 Subject: [PATCH 2/5] feat: refactor Trie Data Structure - Rename TrieImp Class to Trie - Add countWords, startsWithPrefix, countWordsWithPrefix methods - Add test cases --- .../datastructures/trees/Trie.java | 179 ++++++++++++++++++ .../datastructures/trees/TrieImp.java | 128 ------------- .../datastructures/trees/TrieTest.java | 61 ++++++ 3 files changed, 240 insertions(+), 128 deletions(-) create mode 100644 src/main/java/com/thealgorithms/datastructures/trees/Trie.java delete mode 100644 src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java create mode 100644 src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/trees/Trie.java b/src/main/java/com/thealgorithms/datastructures/trees/Trie.java new file mode 100644 index 000000000000..aeef63bbeb67 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/Trie.java @@ -0,0 +1,179 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.HashMap; + +/** + * TrieNode class holds the data for each node in the trie + */ +class TrieNode { + char value; + HashMap child; + boolean end; + + public TrieNode(char value) { + this.value = value; + this.child = new HashMap<>(); + this.end = false; + } +} + +/** + * Trie Data structure implementation without any libraries + * + * @author Dheeraj Kumar Barnwal + * @author Sailok Chinta + */ +public class Trie { + private static final char ROOT_NODE_VALUE = '*'; + + private final TrieNode root; + + public Trie() { + root = new TrieNode(ROOT_NODE_VALUE); + } + + /** + * Inserts a word into the trie + * + * @param word + */ + public void insert(String word) { + TrieNode currentNode = root; + + for (int i = 0; i < word.length(); i++) { + TrieNode node = currentNode.child.getOrDefault(word.charAt(i), null); + + if (node == null) { + node = new TrieNode(word.charAt(i)); + currentNode.child.put(word.charAt(i), node); + } + + currentNode = node; + } + + currentNode.end = true; + } + + /** + * Searches a word in the trie + * + * @param word + * @return true / false + */ + public boolean search(String word) { + TrieNode currentNode = root; + + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + + TrieNode node = currentNode.child.getOrDefault(ch, null); + if (node == null) { + return false; + } + + currentNode = node; + } + + return currentNode.end; + } + + /** + * Deletes a word in the trie + * + * @param word + * @return true / false + */ + public boolean delete(String word) { + TrieNode currentNode = root; + + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + + TrieNode node = currentNode.child.getOrDefault(ch, null); + if (node == null) { + return false; + } + + currentNode = node; + } + + if (currentNode.end) { + currentNode.end = false; + return true; + } + + return false; + } + + /** + * Counts the number of words in the trie + * + * @return count + */ + public int countWords() { + return countWords(root); + } + + private int countWords(TrieNode node) { + if (node == null) { + return 0; + } + + int count = 0; + if (node.end) { + count++; + } + + for (TrieNode child : node.child.values()) { + count += countWords(child); + } + + return count; + } + + /** + * Check if the prefix exists in the trie + * + * @param prefix + * @return true / false + */ + public boolean startsWithPrefix(String prefix) { + TrieNode currentNode = root; + + for (int i = 0; i < prefix.length(); i++) { + char ch = prefix.charAt(i); + + TrieNode node = currentNode.child.getOrDefault(ch, null); + if (node == null) { + return false; + } + + currentNode = node; + } + + return true; + } + + /** + * Count the number of words starting with the given prefix in the trie + * + * @param prefix + * @return count + */ + public int countWordsWithPrefix(String prefix) { + TrieNode currentNode = root; + + for (int i = 0; i < prefix.length(); i++) { + char ch = prefix.charAt(i); + + TrieNode node = currentNode.child.getOrDefault(ch, null); + if (node == null) { + return 0; + } + + currentNode = node; + } + + return countWords(currentNode); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java b/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java deleted file mode 100644 index a23783d3f9af..000000000000 --- a/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.thealgorithms.datastructures.trees; - -import java.util.HashMap; -import java.util.Scanner; - -/** - * Trie Data structure implementation without any libraries - * - * @author Dheeraj Kumar Barnwal - */ -public class TrieImp { - public class TrieNode { - char value; - HashMap child; - boolean end; - - public TrieNode(char value) { - this.value = value; - child = new HashMap<>(); - end = false; - } - } - - private static final char ROOT_NODE_VALUE = '*'; - - private final TrieNode root; - - public TrieImp() { - root = new TrieNode(ROOT_NODE_VALUE); - } - - public void insert(String word) { - TrieNode currentNode = root; - for (int i = 0; i < word.length(); i++) { - TrieNode node = currentNode.child.getOrDefault(word.charAt(i), null); - if (node == null) { - node = new TrieNode(word.charAt(i)); - currentNode.child.put(word.charAt(i), node); - } - currentNode = node; - } - currentNode.end = true; - } - - public boolean search(String word) { - TrieNode currentNode = root; - for (int i = 0; i < word.length(); i++) { - char ch = word.charAt(i); - TrieNode node = currentNode.child.getOrDefault(ch, null); - if (node == null) { - return false; - } - currentNode = node; - } - return currentNode.end; - } - - public boolean delete(String word) { - TrieNode currentNode = root; - for (int i = 0; i < word.length(); i++) { - char ch = word.charAt(i); - TrieNode node = currentNode.child.getOrDefault(ch, null); - if (node == null) { - return false; - } - currentNode = node; - } - if (currentNode.end) { - currentNode.end = false; - return true; - } - return false; - } - - public static void sop(String print) { - System.out.println(print); - } - - public static void main(String[] args) { - TrieImp obj = new TrieImp(); - String word; - @SuppressWarnings("resource") Scanner scan = new Scanner(System.in); - sop("string should contain only a-z character for all operation"); - while (true) { - sop("1. Insert\n2. Search\n3. Delete\n4. Quit"); - try { - int t = scan.nextInt(); - switch (t) { - case 1: - word = scan.next(); - obj.insert(word); - break; - case 2: - word = scan.next(); - boolean resS = obj.search(word); - - if (resS) { - sop("word found"); - } else { - sop("word not found"); - } - break; - case 3: - word = scan.next(); - boolean resD = false; - resD = obj.delete(word); - - if (resD) { - sop("word got deleted successfully"); - } else { - sop("word not found"); - } - break; - case 4: - sop("Quit successfully"); - System.exit(1); - break; - default: - sop("Input int from 1-4"); - break; - } - } catch (Exception e) { - String badInput = scan.next(); - sop("This is bad input: " + badInput); - } - } - } -} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java new file mode 100644 index 000000000000..2a1af5600fd1 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class TrieTest { + private static final List WORDS = List.of("Apple", "App", "app", "APPLE"); + + @Test + public void testEmptyTrie() { + Trie trie = new Trie(); + assertEquals(0, trie.countWords()); + } + + @Test + public void testInsertIntoTrie() { + Trie trie = createTrie(); + assertEquals(WORDS.size(), trie.countWords()); + } + + @Test + public void testSearchInTrie() { + Trie trie = createTrie(); + assertTrue(trie.search("APPLE")); + assertFalse(trie.search("apple")); + } + + @Test + public void testDeleteFromTrie() { + Trie trie = createTrie(); + trie.delete("APPLE"); + assertFalse(trie.search("APPLE")); + assertEquals(WORDS.size() - 1, trie.countWords()); + } + + @Test + public void testCountWords() { + Trie trie = createTrie(); + assertEquals(WORDS.size(), trie.countWords()); + } + + @Test + public void testStartsWithPrefix() { + Trie trie = createTrie(); + assertTrue(trie.startsWithPrefix("App")); + } + + @Test + public void testCountWordsWithPrefix() { + Trie trie = createTrie(); + assertEquals(2, trie.countWordsWithPrefix("App")); + } + + private Trie createTrie() { + Trie trie = new Trie(); + WORDS.forEach(trie::insert); + return trie; + } +} From 698be5dba44658d15381f15830d7ca7a9849615c Mon Sep 17 00:00:00 2001 From: "sailok.chinta" Date: Sat, 5 Oct 2024 09:39:56 +0530 Subject: [PATCH 3/5] feat: fix checkstyle issues --- .../java/com/thealgorithms/datastructures/trees/Trie.java | 2 +- .../java/com/thealgorithms/datastructures/trees/TrieTest.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/Trie.java b/src/main/java/com/thealgorithms/datastructures/trees/Trie.java index aeef63bbeb67..8b8d29a6e8c6 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/Trie.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/Trie.java @@ -10,7 +10,7 @@ class TrieNode { HashMap child; boolean end; - public TrieNode(char value) { + TrieNode(char value) { this.value = value; this.child = new HashMap<>(); this.end = false; diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java index 2a1af5600fd1..4c19dcb685d7 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java @@ -1,6 +1,8 @@ package com.thealgorithms.datastructures.trees; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; import java.util.List; import org.junit.jupiter.api.Test; From 1ae60a3191f2d1e91ae25152ae7f40f577b4d987 Mon Sep 17 00:00:00 2001 From: "sailok.chinta" Date: Sat, 5 Oct 2024 09:43:23 +0530 Subject: [PATCH 4/5] feat: fix clang-format issues --- .../java/com/thealgorithms/datastructures/trees/TrieTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java index 4c19dcb685d7..884bce1e8252 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java @@ -1,8 +1,8 @@ package com.thealgorithms.datastructures.trees; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import org.junit.jupiter.api.Test; From 9a065e15d4fcc35119d44fb8ba2bf4ca867fe141 Mon Sep 17 00:00:00 2001 From: "sailok.chinta" Date: Sat, 5 Oct 2024 09:48:07 +0530 Subject: [PATCH 5/5] feat: update DIRECTORY.md --- DIRECTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index c272d3865b58..bc5ac6271f6c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -204,7 +204,7 @@ * [SegmentTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/SegmentTree.java) * [SplayTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/SplayTree.java) * [TreeRandomNode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/TreeRandomNode.java) - * [TrieImp](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/TrieImp.java) + * [Trie](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/Trie.java) * [VerticalOrderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversal.java) * [ZigzagTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/ZigzagTraversal.java) * devutils