diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d663edd --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Java +*.class +*.log +*.jar +*.war +*.ear +# Build directories +target/ +build/ +out/ +bin/ +# Editor temporary files +*.swp +*~ +# IDE files +.idea/ +.vscode/ +*.iml +*.iws +*.ipr +# Other +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/Search suggestion system/README.md b/Search suggestion system/README.md new file mode 100644 index 0000000..c99b399 --- /dev/null +++ b/Search suggestion system/README.md @@ -0,0 +1,64 @@ +# Java Search Suggestions System (Trie) + +This is a simple and efficient command-line implementation of a search suggestions system (autocomplete) in Java. It uses a **Trie (Prefix Tree)** data structure to store a dictionary of words and find all words that match a given prefix. + +This project is a clean, single-file implementation suitable for demonstrating the practical application of a Trie. + +## Features + +* **Trie-Based:** Uses a `TrieNode` class with a `HashMap` for efficient, case-insensitive prefix lookups. +* **Case-Insensitive:** All inserted words and prefixes are handled in lowercase. +* **Interactive:** Includes a `main` method that allows you to load a dictionary and test prefixes in real-time. +* **Loads from File:** Reads its dictionary from an external `dictionary.txt` file. + +## How to Compile and Run + +This project consists of a single Java file and a dictionary text file. + +**1. Compile the Code:** +Open your terminal in the project directory and run: + +```bash +javac SearchSuggestions.java +``` + +**2. Run the Program:** +After compiling, run the program: + +```bash +java SearchSuggestions +``` + +**3. Example Session:** +The program will first load the words from `dictionary.txt`. + +```text +Loading dictionary from 'dictionary.txt'... +Successfully loaded 10 words. + +Enter prefix to search (or 'q' to quit): pro +Suggestions for 'pro': + -> profile + -> program + -> project + -> python + +Enter prefix to search (or 'q' to quit): app +Suggestions for 'app': + -> app + -> apple + -> apply + -> application + +Enter prefix to search (or 'q' to quit): q +Goodbye! +``` + +## How It Works + +* `TrieNode`: A private inner class holds a `Map` for its children and a `boolean` to mark the end of a word. +* `insert(String word)`: Iterates through each character of the word, creating new nodes in the Trie as needed. +* `getSuggestions(String prefix)`: + 1. Traverses the Trie to the node corresponding to the last character of the prefix. + 2. If the prefix path doesn't exist, it returns an empty list. + 3. If it does, it calls a recursive helper `findAllWords()` to explore all child paths from that node and collect all complete words. \ No newline at end of file diff --git a/Search suggestion system/SearchSuggestions.java b/Search suggestion system/SearchSuggestions.java new file mode 100644 index 0000000..af8ea0a --- /dev/null +++ b/Search suggestion system/SearchSuggestions.java @@ -0,0 +1,130 @@ +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; + +public class SearchSuggestions { + + private class TrieNode { + Map children = new HashMap<>(); + boolean isEndOfWord; + } + + private final TrieNode root; + + public SearchSuggestions() { + root = new TrieNode(); + } + + public void insert(String word) { + if (word == null || word.isEmpty()) { + return; + } + TrieNode node = root; + for (char c : word.toLowerCase().toCharArray()) { + node = node.children.computeIfAbsent(c, k -> new TrieNode()); + } + node.isEndOfWord = true; + } + + public List getSuggestions(String prefix) { + if (prefix == null) { + return Collections.emptyList(); + } + String lowerPrefix = prefix.toLowerCase(); + TrieNode node = root; + + for (char c : lowerPrefix.toCharArray()) { + node = node.children.get(c); + if (node == null) { + return Collections.emptyList(); + } + } + + List suggestions = new ArrayList<>(); + findAllWords(node, new StringBuilder(lowerPrefix), suggestions); + return suggestions; + } + + private void findAllWords(TrieNode node, StringBuilder currentWord, List suggestions) { + if (node.isEndOfWord) { + suggestions.add(currentWord.toString()); + } + + for (Map.Entry entry : node.children.entrySet()) { + currentWord.append(entry.getKey()); + findAllWords(entry.getValue(), currentWord, suggestions); + currentWord.setLength(currentWord.length() - 1); // Backtrack + } + } + + /** + * Loads words from a specified dictionary file into the Trie. + */ + private static int loadDictionary(SearchSuggestions trie, String filename) { + int wordCount = 0; + // The dictionary file is inside the same folder, so the path is simple. + try (BufferedReader br = new BufferedReader(new FileReader(filename))) { + String line; + while ((line = br.readLine()) != null) { + if (!line.trim().isEmpty()) { + trie.insert(line.trim()); + wordCount++; + } + } + } catch (FileNotFoundException e) { + System.err.println("Error: Dictionary file not found: " + filename); + } catch (IOException e) { + System.err.println("Error: Failed to read dictionary file: " + e.getMessage()); + } + return wordCount; + } + + public static void main(String[] args) { + SearchSuggestions trie = new SearchSuggestions(); + + // Looks for the file in the same directory as the .java file. + // When running, you must be "inside" this folder. + String dictionaryFile = "dictionary.txt"; + System.out.println("Loading dictionary from '" + dictionaryFile + "'..."); + + int wordCount = loadDictionary(trie, dictionaryFile); + + if (wordCount == 0) { + System.err.println("Dictionary is empty or could not be loaded. Exiting."); + return; + } + + System.out.println("Successfully loaded " + wordCount + " words."); + + try (Scanner scanner = new Scanner(System.in)) { + + while (true) { + System.out.print("\nEnter prefix to search (or 'q' to quit): "); + String prefix = scanner.nextLine(); + + if (prefix.equalsIgnoreCase("q") || prefix.isEmpty()) { + break; + } + + List suggestions = trie.getSuggestions(prefix); + + if (suggestions.isEmpty()) { + System.out.println("No suggestions found for '" + prefix + "'."); + } else { + System.out.println("Suggestions for '" + prefix + "':"); + for (String suggestion : suggestions) { + System.out.println(" -> " + suggestion); + } + } + } + } + System.out.println("Goodbye!"); + } +} \ No newline at end of file diff --git a/Search suggestion system/dictionary.txt b/Search suggestion system/dictionary.txt new file mode 100644 index 0000000..9cb4a70 --- /dev/null +++ b/Search suggestion system/dictionary.txt @@ -0,0 +1,22 @@ +apple +app +application +apply +apricot +banana +band +bank +bat +project +program +profile +java +javascript +react +node +sql +system +search +suggestion +trie +tree \ No newline at end of file