Skip to content

Commit 4e49696

Browse files
authored
fixes
1 parent 701f422 commit 4e49696

File tree

1 file changed

+109
-140
lines changed

1 file changed

+109
-140
lines changed
Lines changed: 109 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,64 @@
1-
package com.thealgorithms.datastructures.tries;
1+
package com.thealgorithms.datastructures.trees;
22

33
import 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
*/
2325
public 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("\nTrie 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

Comments
 (0)