1- package com .thealgorithms .datastructures .tries ;
1+ package com .thealgorithms .datastructures .trees ;
22
33import java .util .stream .IntStream ;
44
55/**
66 * Implements a PATRICIA Trie (Practical Algorithm to Retrieve Information Coded
7- * in Alphanumeric) using fixed-width integers (keys) .
7+ * in Alphanumeric).
88 *
9- * <p>This specific implementation uses the fixed-size 32-bit integer representation
10- * as keys, common in many networking and IP lookup contexts, and relies on
11- * bitwise operations for efficiency.
9+ * <p>This implementation uses 32-bit integers as keys, which is common in
10+ * applications like IP address routing. It relies on efficient bitwise
11+ * operations to navigate the tree. PATRICIA Tries are a type of compressed
12+ * binary trie (radix tree with radix 2), where nodes are only created at points
13+ * of divergence between keys.
1214 *
13- * <p>Reference: <a href="https://en.wikipedia.org/wiki/Radix_tree">Wikipedia: Radix Tree (Patricia Trie) </a>
15+ * <p>Reference: <a href="https://en.wikipedia.org/wiki/Radix_tree">Wikipedia: Radix Tree</a>
1416 *
15- * <p>Key characteristics:
17+ * <p><b> Key Characteristics:</b>
1618 * <ul>
17- * <li>**Radix-2 Trie:** Works on the binary representation of integer keys .</li>
18- * <li>**Compacted:** Nodes only exist where branching occurs, compacting unary paths .</li>
19- * <li>**External Nodes:** All nodes are internal; the key itself is stored in the
20- * leaf/external node found by the search .</li>
19+ * <li><b>Compressed:</b> Nodes with only one child are omitted, saving space .</li>
20+ * <li><b>Binary (Radix-2):</b> Decisions at each node are based on a single bit from the key .</li>
21+ * <li><b>Back-Pointers:</b> Traversal ends when a "back-pointer" is found—a link to an ancestor
22+ * or to the node itself—indicating the location of the key for final comparison .</li>
2123 * </ul>
2224 */
2325public final class PatriciaTrie {
2426
2527 /**
26- * Represents a node in the Patricia Trie.
27- * All nodes are internal nodes that store the key data at the point of creation,
28- * and their {@code bitNumber} indicates the bit position to check when traversing.
28+ * Represents a node in the Patricia Trie. Each node specifies which bit to
29+ * check and stores a key for comparison.
2930 */
30- private static class PatriciaTrieNode {
31+ private static class Node {
3132 /**
32- * The bit index (1-indexed from MSB, 1 to 32) to check for branching at this node.
33- * The index must be greater than that of the parent node. A value of 0 is used for the root.
33+ * The bit index to check for branching at this node (1-indexed from MSB, 1 to 32).
34+ * This index must be greater than that of the parent node. A value of 0 is
35+ * reserved for the root/head node.
3436 */
3537 int bitNumber ;
3638 /**
37- * The integer key stored at this node. This key is used for the final comparison
38- * after traversing the trie structure to determine if the key exists .
39+ * The integer key associated with this node. This key is used for the final
40+ * comparison after traversal to confirm an exact match .
3941 */
4042 int key ;
41- /**
42- * Pointer to the next node if the current bit being examined is 0.
43- */
44- PatriciaTrieNode leftChild ;
45- /**
46- * Pointer to the next node if the current bit being examined is 1.
47- */
48- PatriciaTrieNode rightChild ;
43+ /** Pointer to the next node if the bit being examined is 0. */
44+ Node leftChild ;
45+ /** Pointer to the next node if the bit being examined is 1. */
46+ Node rightChild ;
4947
5048 /**
51- * Constructs a new PatriciaTrieNode.
49+ * Constructs a new Node.
50+ *
5251 * @param bitNumber The bit index for comparison at this node.
5352 * @param key The integer key associated with this node.
5453 */
55- PatriciaTrieNode (int bitNumber , int key ) {
54+ Node (int bitNumber , int key ) {
5655 this .bitNumber = bitNumber ;
5756 this .key = key ;
5857 }
5958 }
6059
61- private PatriciaTrieNode root ;
62- private static final int MAX_BITS = Integer .SIZE ; // 32 bits for standard Java int
60+ private Node root ;
61+ private static final int MAX_BITS = Integer .SIZE ; // 32 bits for a standard Java int
6362
6463 /**
6564 * Initializes an empty Patricia Trie.
@@ -69,192 +68,162 @@ public PatriciaTrie() {
6968 }
7069
7170 /**
72- * Checks if the trie is empty .
73- * @return true if the root is null, false otherwise.
71+ * Checks if the trie contains any keys .
72+ * @return {@code true} if the trie is empty, {@code false} otherwise.
7473 */
7574 public boolean isEmpty () {
7675 return root == null ;
7776 }
7877
7978 /**
80- * Resets the trie, setting the root to null .
79+ * Removes all keys from the trie .
8180 */
8281 public void makeEmpty () {
8382 root = null ;
8483 }
8584
8685 /**
87- * Determines the value of the i-th bit (1-indexed from MSB) of a given key.
88- * Uses efficient bitwise operations.
86+ * Determines the value of the i-th bit of a given key.
8987 *
9088 * @param key The integer key.
91- * @param i The 1-based index of the bit to check (1 is MSB, 32 is LSB).
92- * @return true if the bit is 1, false if the bit is 0.
89+ * @param i The 1-based index of the bit to check (1= MSB, 32= LSB).
90+ * @return {@code true} if the bit is 1, {@code false} if it is 0.
9391 */
9492 private boolean getBit (int key , int i ) {
95- // Calculate the shift amount: MAX_BITS - i
96- // i=1 (MSB) -> shift 31
97- // i=32 (LSB) -> shift 0
98- int shift = MAX_BITS - i ;
99- // Use unsigned right shift (>>>) for predictable results, then mask with 1.
100- return ((key >>> shift ) & 1 ) == 1 ;
93+ // A 1-based index `i` corresponds to a shift of (MAX_BITS - i).
94+ // Example for 32 bits: i=1 (MSB) -> shift 31; i=32 (LSB) -> shift 0.
95+ return ((key >>> (MAX_BITS - i )) & 1 ) == 1 ;
10196 }
10297
10398 /**
104- * Searches for a key in the trie.
99+ * Searches for an exact key in the trie.
105100 *
106101 * @param key The integer key to search for.
107- * @return true if the key is found, false otherwise.
102+ * @return {@code true} if the key is found, {@code false} otherwise.
108103 */
109104 public boolean search (int key ) {
110105 if (root == null ) {
111106 return false ;
112107 }
113-
114- // Search down to the external node
115- PatriciaTrieNode foundNode = searchDown (root , key );
116-
117- // Check if the key stored in the found node matches the search key
118- return foundNode .key == key ;
108+ // Find the node containing the best possible match for the key.
109+ Node bestMatchNode = findBestMatchNode (root , key );
110+ // Confirm if the best match is an exact match.
111+ return bestMatchNode .key == key ;
119112 }
120113
121114 /**
122- * Traverses the trie to find the external node that is the predecessor
123- * of the key 'k'. This node contains the most similar key currently in the trie.
115+ * Traverses the trie to find the node containing the key most similar to the search key.
124116 *
125- * @param t The starting node for the search (usually the root).
126- * @param k The key being searched for.
127- * @return The external node where the key comparison should happen .
117+ * @param startNode The node to begin the search from (usually the root).
118+ * @param key The key being searched for.
119+ * @return The node containing the best matching key .
128120 */
129- private PatriciaTrieNode searchDown ( PatriciaTrieNode t , int k ) {
130- PatriciaTrieNode currentNode = t ;
131- PatriciaTrieNode nextNode = t .leftChild ; // Start by following the default (0) child
121+ private Node findBestMatchNode ( Node startNode , int key ) {
122+ Node currentNode = startNode ;
123+ Node nextNode = startNode .leftChild ;
132124
133- // The condition nextNode.bitNumber > currentNode.bitNumber is the core
134- // of the Patricia Trie structure. It means we are moving down a tree edge (forward reference).
125+ // Traverse down the trie as long as we are following forward pointers.
126+ // A forward pointer is indicated by a child's bitNumber being greater
127+ // than its parent's bitNumber.
135128 while (nextNode .bitNumber > currentNode .bitNumber ) {
136129 currentNode = nextNode ;
137- // Determine the next child based on the bit at nextNode.bitNumber
138- nextNode = getBit (k , nextNode .bitNumber )
139- ? nextNode .rightChild
140- : nextNode .leftChild ;
130+ nextNode = getBit (key , nextNode .bitNumber ) ? nextNode .rightChild : nextNode .leftChild ;
141131 }
142- // When nextNode.bitNumber <= currentNode.bitNumber, we've found an external node
143- // (a back pointer) which holds the best match key .
132+ // The loop terminates upon finding a back-pointer ( nextNode.bitNumber <= currentNode.bitNumber),
133+ // which points to the node containing the best match.
144134 return nextNode ;
145135 }
146136
147137 /**
148- * Inserts an integer key into the Patricia Trie.
138+ * Inserts an integer key into the Patricia Trie. Does nothing if the key
139+ * already exists.
149140 *
150141 * @param key The integer key to insert.
151142 */
152143 public void insert (int key ) {
153- root = insert (root , key );
154- }
155-
156- /**
157- * Recursive helper method for insertion.
158- *
159- * @param t The current subtree root.
160- * @param element The key to insert.
161- * @return The updated root of the subtree.
162- */
163- private PatriciaTrieNode insert (PatriciaTrieNode t , int element ) {
164-
165- // 1. Handle Empty Trie (Initial Insertion)
166- if (t == null ) {
167- t = new PatriciaTrieNode (0 , element ); // Bit number 0 for the root/sentinel
168- t .leftChild = t ; // Root node links back to itself (left pointer)
169- t .rightChild = null ; // Right pointer unused or null
170- return t ;
144+ // 1. Handle Empty Trie (the very first insertion)
145+ if (root == null ) {
146+ root = new Node (0 , key ); // bitNumber 0 is a sentinel for the head
147+ root .leftChild = root ; // The first node points back to itself
148+ return ;
171149 }
172150
173- // 2. Search for the best match (predecessor)
174- PatriciaTrieNode lastNode = searchDown ( t , element );
151+ // 2. Find the best matching key already in the trie
152+ Node bestMatchNode = findBestMatchNode ( root , key );
175153
176154 // 3. Check for Duplicates
177- if (element == lastNode .key ) {
178- // Note: Printing to stdout within a core algorithm method is generally discouraged
179- // but is kept here for informational context during the insertion process.
180- System .out .println ("Key " + element + " already present." );
181- return t ;
155+ if (key == bestMatchNode .key ) {
156+ return ; // Key already exists, do nothing.
182157 }
183158
184- // 4. Find the first differentiating bit (i)
185- int i = 1 ;
186- while (getBit (element , i ) == getBit (lastNode .key , i ) && i < MAX_BITS ) {
187- i ++;
188- }
189- // If i reached MAX_BITS + 1, the keys are identical (should have been caught above)
190- if (i > MAX_BITS ) {
191- throw new IllegalStateException ("Keys are identical but duplicate check failed." );
159+ // 4. Find the first bit position where the new key and its best match differ
160+ int differingBit = 1 ;
161+ // BUG FIX: The loop must check all bits (i <= MAX_BITS). The original
162+ // code (i < MAX_BITS) failed to check the last bit.
163+ while (differingBit <= MAX_BITS && getBit (key , differingBit ) == getBit (bestMatchNode .key , differingBit )) {
164+ differingBit ++;
192165 }
193166
194- // 5. Find the insertion point (parent)
195- // Find the node 'parent' that points to a bit number greater than 'i' or points back
196- PatriciaTrieNode currentNode = t . leftChild ;
197- PatriciaTrieNode parent = t ;
167+ // This should not happen if the duplicate check is correct, but serves as a safeguard.
168+ if ( differingBit > MAX_BITS ) {
169+ throw new IllegalStateException ( "Keys are different but no differing bit was found." ) ;
170+ }
198171
199- while (currentNode .bitNumber > parent .bitNumber && currentNode .bitNumber < i ) {
200- parent = currentNode ;
201- currentNode = getBit (element , currentNode .bitNumber )
202- ? currentNode .rightChild
203- : currentNode .leftChild ;
172+ // 5. Find the correct insertion point by traversing again
173+ Node parent = root ;
174+ Node child = root .leftChild ;
175+ while (child .bitNumber > parent .bitNumber && child .bitNumber < differingBit ) {
176+ parent = child ;
177+ child = getBit (key , child .bitNumber ) ? child .rightChild : child .leftChild ;
204178 }
205179
206- // 6. Create the new internal node
207- PatriciaTrieNode newNode = new PatriciaTrieNode ( i , element );
180+ // 6. Create the new node and link it into the trie
181+ Node newNode = new Node ( differingBit , key );
208182
209- // Determine the children of the new node (newNode)
210- if ( getBit ( element , i )) {
211- // New key has 1 at bit i: left child points to the old subtree (currentNode), right child points to self
212- newNode .leftChild = currentNode ;
183+ // Set the children of the new node. One child will be the existing subtree (`child`),
184+ // and the other will be a back-pointer to the new node itself.
185+ if ( getBit ( key , differingBit )) { // New key has a '1' at the differing bit
186+ newNode .leftChild = child ;
213187 newNode .rightChild = newNode ;
214- } else {
215- // New key has 0 at bit i: left child points to self, right child points to the old subtree (currentNode)
188+ } else { // New key has a '0' at the differing bit
216189 newNode .leftChild = newNode ;
217- newNode .rightChild = currentNode ;
190+ newNode .rightChild = child ;
218191 }
219192
220- // 7. Link the parent to the new node
221- if (getBit (element , parent .bitNumber )) {
222- // Parent's splitting bit matches the new key's bit: link via right child
193+ // 7. Link the parent to the new node, replacing its old link to `child`
194+ if (getBit (key , parent .bitNumber )) {
223195 parent .rightChild = newNode ;
224196 } else {
225- // Parent's splitting bit doesn't match: link via left child
226197 parent .leftChild = newNode ;
227198 }
228-
229- return t ;
230199 }
231200
232201 // --- Main Driver and Example Usage ---
233202
234203 public static void main (String [] args ) {
235204 PatriciaTrie trie = new PatriciaTrie ();
236- System .out .println ("Patricia Trie Demonstration (Max Bits: " + MAX_BITS + ")" );
205+ System .out .println ("--- Patricia Trie Demonstration ---" );
206+ System .out .println ("Trie is empty: " + trie .isEmpty ());
237207
238- // Example integer keys (representing, perhaps, IP addresses or other binary identifiers)
239208 int [] keys = {10 , 20 , 15 , 7 , 5 , 25 };
240209
241- System .out .println ("\n --- Insertion ---" );
210+ System .out .println ("\n --- Inserting Keys ---" );
242211 for (int key : keys ) {
212+ System .out .printf ("Inserting: %3d (%s)%n" , key , Integer .toBinaryString (key ));
243213 trie .insert (key );
244- System .out .println ("Inserted: " + key + " (" + Integer .toBinaryString (key ) + ")" );
245214 }
215+ System .out .println ("\n Trie is empty: " + trie .isEmpty ());
246216
247- System .out .println ("\n --- Search ---" );
248- // Test existing keys
249- IntStream .of (keys )
250- .forEach (key -> System .out .printf ("Search %d: %b\n " , key , trie .search (key )));
217+ System .out .println ("\n --- Verifying Existing Keys ---" );
218+ IntStream .of (keys ).forEach (key -> System .out .printf ("Search %3d: %s%n" , key , trie .search (key ) ? "Found" : "Not Found" ));
251219
252- // Test non-existing keys
253- System .out .printf ("Search %d : %b \ n " , 100 , trie .search (100 )); // Non-existent
254- System .out .printf ("Search %d : %b \ n " , 0 , trie .search (0 )); // Non-existent
220+ System . out . println ( " \n --- Searching for Non-Existing Keys ---" );
221+ System .out .printf ("Search %3d : %s% n" , 100 , trie .search (100 ) ? "Found" : "Not Found" );
222+ System .out .printf ("Search %3d : %s% n" , 0 , trie .search (0 ) ? "Found" : "Not Found" );
255223
256- // Test duplicate insertion
257- System .out .println ("\n --- Duplicate Insertion ---" );
258- trie .insert (20 );
224+ System .out .println ("\n --- Attempting Duplicate Insertion ---" );
225+ System .out .println ("Inserting 20 again..." );
226+ trie .insert (20 ); // Should do nothing
227+ System .out .printf ("Search %3d: %s%n" , 20 , trie .search (20 ) ? "Found" : "Not Found" );
259228 }
260229}
0 commit comments