1+ package com .thealgorithms .datastructures .trees ;
2+
3+ /**
4+ * Ternary Search Tree implementation for efficient string storage and retrieval.
5+ *
6+ * A Ternary Search Tree (TST) is a data structure that combines the time efficiency
7+ * of digital tries with the space efficiency of binary search trees.
8+ *
9+ * Time Complexity:
10+ * - Insert: O(log n) average, O(n) worst case
11+ * - Search: O(log n) average, O(n) worst case
12+ * - Delete: O(log n) average, O(n) worst case
13+ *
14+ * Space Complexity: O(n) where n is the number of characters
15+ *
16+ * @see <a href="https://en.wikipedia.org/wiki/Ternary_search_tree">Ternary Search Tree</a>
17+ * @author JeevanYewale
18+ */
19+ public final class TernarySearchTree {
20+
21+ private Node root ;
22+
23+ private static class Node {
24+ char data ;
25+ boolean isEnd ;
26+ Node left , middle , right ;
27+
28+ Node (char data ) {
29+ this .data = data ;
30+ this .isEnd = false ;
31+ }
32+ }
33+
34+ /**
35+ * Inserts a word into the ternary search tree.
36+ *
37+ * @param word the word to insert
38+ * @throws IllegalArgumentException if word is null or empty
39+ */
40+ public void insert (String word ) {
41+ if (word == null || word .isEmpty ()) {
42+ throw new IllegalArgumentException ("Word cannot be null or empty" );
43+ }
44+ root = insert (root , word , 0 );
45+ }
46+
47+ private Node insert (Node node , String word , int index ) {
48+ char c = word .charAt (index );
49+
50+ if (node == null ) {
51+ node = new Node (c );
52+ }
53+
54+ if (c < node .data ) {
55+ node .left = insert (node .left , word , index );
56+ } else if (c > node .data ) {
57+ node .right = insert (node .right , word , index );
58+ } else {
59+ if (index < word .length () - 1 ) {
60+ node .middle = insert (node .middle , word , index + 1 );
61+ } else {
62+ node .isEnd = true ;
63+ }
64+ }
65+
66+ return node ;
67+ }
68+
69+ /**
70+ * Searches for a word in the ternary search tree.
71+ *
72+ * @param word the word to search for
73+ * @return true if the word exists, false otherwise
74+ * @throws IllegalArgumentException if word is null or empty
75+ */
76+ public boolean search (String word ) {
77+ if (word == null || word .isEmpty ()) {
78+ throw new IllegalArgumentException ("Word cannot be null or empty" );
79+ }
80+ return search (root , word , 0 );
81+ }
82+
83+ private boolean search (Node node , String word , int index ) {
84+ if (node == null ) {
85+ return false ;
86+ }
87+
88+ char c = word .charAt (index );
89+
90+ if (c < node .data ) {
91+ return search (node .left , word , index );
92+ } else if (c > node .data ) {
93+ return search (node .right , word , index );
94+ } else {
95+ if (index == word .length () - 1 ) {
96+ return node .isEnd ;
97+ }
98+ return search (node .middle , word , index + 1 );
99+ }
100+ }
101+
102+ /**
103+ * Checks if any word in the tree starts with the given prefix.
104+ *
105+ * @param prefix the prefix to search for
106+ * @return true if any word starts with the prefix, false otherwise
107+ * @throws IllegalArgumentException if prefix is null or empty
108+ */
109+ public boolean startsWith (String prefix ) {
110+ if (prefix == null || prefix .isEmpty ()) {
111+ throw new IllegalArgumentException ("Prefix cannot be null or empty" );
112+ }
113+ return startsWith (root , prefix , 0 );
114+ }
115+
116+ private boolean startsWith (Node node , String prefix , int index ) {
117+ if (node == null ) {
118+ return false ;
119+ }
120+
121+ if (index == prefix .length ()) {
122+ return true ;
123+ }
124+
125+ char c = prefix .charAt (index );
126+
127+ if (c < node .data ) {
128+ return startsWith (node .left , prefix , index );
129+ } else if (c > node .data ) {
130+ return startsWith (node .right , prefix , index );
131+ } else {
132+ return startsWith (node .middle , prefix , index + 1 );
133+ }
134+ }
135+
136+ /**
137+ * Deletes a word from the ternary search tree.
138+ *
139+ * @param word the word to delete
140+ * @return true if the word was deleted, false if it didn't exist
141+ * @throws IllegalArgumentException if word is null or empty
142+ */
143+ public boolean delete (String word ) {
144+ if (word == null || word .isEmpty ()) {
145+ throw new IllegalArgumentException ("Word cannot be null or empty" );
146+ }
147+
148+ if (!search (word )) {
149+ return false ;
150+ }
151+
152+ root = delete (root , word , 0 );
153+ return true ;
154+ }
155+
156+ private Node delete (Node node , String word , int index ) {
157+ if (node == null ) {
158+ return null ;
159+ }
160+
161+ char c = word .charAt (index );
162+
163+ if (c < node .data ) {
164+ node .left = delete (node .left , word , index );
165+ } else if (c > node .data ) {
166+ node .right = delete (node .right , word , index );
167+ } else {
168+ if (index == word .length () - 1 ) {
169+ node .isEnd = false ;
170+ } else {
171+ node .middle = delete (node .middle , word , index + 1 );
172+ }
173+ }
174+
175+ // Remove node if it's not needed
176+ if (!node .isEnd && node .left == null && node .middle == null && node .right == null ) {
177+ return null ;
178+ }
179+
180+ return node ;
181+ }
182+
183+ /**
184+ * Checks if the tree is empty.
185+ *
186+ * @return true if the tree is empty, false otherwise
187+ */
188+ public boolean isEmpty () {
189+ return root == null ;
190+ }
191+ }
0 commit comments