From 72e6c4323379d9a5984ff800808032b1d2520058 Mon Sep 17 00:00:00 2001
From: Srishti Soni <92056170+shimmer12@users.noreply.github.com>
Date: Wed, 8 Oct 2025 22:56:18 +0530
Subject: [PATCH 1/7] Implement PATRICIA Trie with integer keys
This implementation of a PATRICIA Trie uses fixed-width integers as keys. It includes methods for insertion, searching, and checking if the trie is empty.
---
.../datastructures/trees/PATRICIA_Trie.java | 300 ++++++++++++++++++
1 file changed, 300 insertions(+)
create mode 100644 src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java b/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
new file mode 100644
index 000000000000..a45f54abb9fc
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
@@ -0,0 +1,300 @@
+package com.thealgorithms.datastructures.tries;
+
+import java.math.BigInteger;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Implements a PATRICIA Trie (Practical Algorithm to Retrieve Information Coded
+ * in Alphanumeric) using fixed-width integers (keys).
+ *
+ *
This specific implementation uses the fixed-size 32-bit integer representation
+ * as keys, common in many networking and IP lookup contexts, and relies on
+ * bitwise operations for efficiency.
+ *
+ *
Key characteristics:
+ *
+ * - **Radix-2 Trie:** Works on the binary representation of integer keys.
+ * - **Compacted:** Nodes only exist where branching occurs, compacting unary paths.
+ * - **External Nodes:** All nodes are internal; the key itself is stored in the
+ * leaf/external node found by the search.
+ *
+ */
+public final class PatriciaTrie {
+
+ /**
+ * Represents a node in the Patricia Trie.
+ * All nodes are internal nodes that store the key data at the point of creation,
+ * and their `bitNumber` indicates the bit position to check when traversing.
+ */
+ private static class PatriciaTrieNode {
+ /**
+ * The bit index (1-indexed from MSB) to check for branching at this node.
+ * The index must be greater than that of the parent node.
+ */
+ int bitNumber;
+ /**
+ * The integer key stored at this node. This is the **data** that was inserted
+ * to create this node and acts as a placeholder or final result during search.
+ */
+ int key;
+ /**
+ * Pointer to the next node if the current bit is 0.
+ */
+ PatriciaTrieNode leftChild;
+ /**
+ * Pointer to the next node if the current bit is 1.
+ */
+ PatriciaTrieNode rightChild;
+
+ PatriciaTrieNode(int bitNumber, int key) {
+ this.bitNumber = bitNumber;
+ this.key = key;
+ }
+ }
+
+ private PatriciaTrieNode root;
+ private static final int MAX_BITS = Integer.SIZE; // 32 bits for standard Java int
+
+ /**
+ * Initializes an empty Patricia Trie.
+ */
+ public PatriciaTrie() {
+ this.root = null;
+ }
+
+ /**
+ * Checks if the trie is empty.
+ * @return true if the root is null, false otherwise.
+ */
+ public boolean isEmpty() {
+ return root == null;
+ }
+
+ /**
+ * Resets the trie, setting the root to null.
+ */
+ public void makeEmpty() {
+ root = null;
+ }
+
+ /**
+ * Determines the value of the i-th bit (1-indexed from MSB) of a given key.
+ * Uses efficient bitwise operations.
+ *
+ * @param key The integer key.
+ * @param i The 1-based index of the bit to check (1 is MSB, 32 is LSB).
+ * @return true if the bit is 1, false if the bit is 0.
+ */
+ private boolean getBit(int key, int i) {
+ // Calculate the shift amount: MAX_BITS - i
+ // i=1 (MSB) -> shift 31
+ // i=32 (LSB) -> shift 0
+ int shift = MAX_BITS - i;
+ // Use unsigned right shift (>>>) for predictable results, then mask with 1.
+ return ((key >>> shift) & 1) == 1;
+ }
+
+ /**
+ * Searches for a key in the trie.
+ *
+ * @param key The integer key to search for.
+ * @return true if the key is found, false otherwise.
+ */
+ public boolean search(int key) {
+ if (root == null) {
+ return false;
+ }
+
+ // Search down to the external node
+ PatriciaTrieNode foundNode = searchDown(root, key);
+
+ // Check if the key stored in the found node matches the search key
+ return foundNode.key == key;
+ }
+
+ /**
+ * Traverses the trie to find the external node that is the predecessor
+ * of the key 'k'. This node contains the most similar key currently in the trie.
+ *
+ * @param t The starting node for the search (usually the root).
+ * @param k The key being searched for.
+ * @return The external node where the key comparison should happen.
+ */
+ private PatriciaTrieNode searchDown(PatriciaTrieNode t, int k) {
+ PatriciaTrieNode currentNode = t;
+ PatriciaTrieNode nextNode = t.leftChild; // Start by following the default (0) child
+
+ // The condition nextNode.bitNumber > currentNode.bitNumber is the core
+ // of the Patricia Trie structure. It means we are moving down a tree edge (forward reference).
+ while (nextNode.bitNumber > currentNode.bitNumber) {
+ currentNode = nextNode;
+ // Determine the next child based on the bit at nextNode.bitNumber
+ nextNode = getBit(k, nextNode.bitNumber)
+ ? nextNode.rightChild
+ : nextNode.leftChild;
+ }
+ // When nextNode.bitNumber <= currentNode.bitNumber, we've found an external node
+ // (a back pointer) which holds the best match key.
+ return nextNode;
+ }
+
+ /**
+ * Inserts an integer key into the Patricia Trie.
+ *
+ * @param key The integer key to insert.
+ */
+ public void insert(int key) {
+ root = insert(root, key);
+ }
+
+ /**
+ * Recursive helper method for insertion.
+ *
+ * @param t The current subtree root.
+ * @param element The key to insert.
+ * @return The updated root of the subtree.
+ */
+ private PatriciaTrieNode insert(PatriciaTrieNode t, int element) {
+
+ // 1. Handle Empty Trie (Initial Insertion)
+ if (t == null) {
+ t = new PatriciaTrieNode(0, element); // Bit number 0 for the root/sentinel
+ t.leftChild = t; // Root node links back to itself (left pointer)
+ t.rightChild = null; // Right pointer unused or null
+ return t;
+ }
+
+ // 2. Search for the best match (predecessor)
+ PatriciaTrieNode lastNode = searchDown(t, element);
+
+ // 3. Check for Duplicates
+ if (element == lastNode.key) {
+ System.out.println("Key " + element + " already present.");
+ return t;
+ }
+
+ // 4. Find the first differentiating bit (i)
+ int i = 1;
+ while (getBit(element, i) == getBit(lastNode.key, i) && i < MAX_BITS) {
+ i++;
+ }
+ // If i reached MAX_BITS + 1, the keys are identical (should have been caught above)
+ if (i > MAX_BITS) {
+ throw new IllegalStateException("Keys are identical but duplicate check failed.");
+ }
+
+ // 5. Find the insertion point (parent)
+ // Find the node 'parent' that points to a bit number greater than 'i' or points back
+ PatriciaTrieNode currentNode = t.leftChild;
+ PatriciaTrieNode parent = t;
+
+ while (currentNode.bitNumber > parent.bitNumber && currentNode.bitNumber < i) {
+ parent = currentNode;
+ currentNode = getBit(element, currentNode.bitNumber)
+ ? currentNode.rightChild
+ : currentNode.leftChild;
+ }
+
+ // 6. Create the new internal node
+ PatriciaTrieNode newNode = new PatriciaTrieNode(i, element);
+
+ // Determine the children of the new node (newNode)
+ if (getBit(element, i)) {
+ // New key has 1 at bit i: left child points to the old subtree (currentNode), right child points to self
+ newNode.leftChild = currentNode;
+ newNode.rightChild = newNode;
+ } else {
+ // New key has 0 at bit i: left child points to self, right child points to the old subtree (currentNode)
+ newNode.leftChild = newNode;
+ newNode.rightChild = currentNode;
+ }
+
+ // 7. Link the parent to the new node
+ if (getBit(element, parent.bitNumber)) {
+ // Parent's splitting bit matches the new key's bit: link via right child
+ parent.rightChild = newNode;
+ } else {
+ // Parent's splitting bit doesn't match: link via left child
+ parent.leftChild = newNode;
+ }
+
+ return t;
+ }
+
+ /**
+ * Utility method to print all keys in the trie (in order of insertion discovery).
+ * @param t The root node.
+ */
+ private void printKeys(PatriciaTrieNode t) {
+ if (t == null) {
+ return;
+ }
+
+ PatriciaTrieNode startNode = t.leftChild; // Start at the first meaningful node
+
+ // Use a set to track visited nodes and prevent infinite loop due to back pointers
+ java.util.Set visitedNodes = new java.util.HashSet<>();
+ java.util.Queue queue = new java.util.LinkedList<>();
+
+ // Add the sentinel/root node's left child if it's not the root itself (0 bit)
+ if (startNode != t && startNode != null) {
+ queue.add(startNode);
+ visitedNodes.add(startNode);
+ }
+
+ // Handle the root key if it's the only one
+ if (t.leftChild == t && t.key != 0) {
+ System.out.print(t.key + " ");
+ return;
+ }
+
+ while (!queue.isEmpty()) {
+ PatriciaTrieNode current = queue.poll();
+
+ // The 'key' in a Patricia node is only the data stored at the time of creation.
+ // It is NOT a full traversal output. Traversal requires following the logic.
+ // This traversal is complex due to back pointers. A simpler in-order traversal
+ // that avoids infinite loops by checking bit numbers is typically used.
+
+ // Simplest key extraction for this structure: Recursively find external nodes
+ // by detecting back pointers.
+
+ // Skip if the node is a back pointer (i.e., its child is itself or points "back"
+ // to a node with a smaller or equal bit number).
+ // NOTE: A standard in-order traversal is difficult due to the compressed structure.
+ // We will stick to the basic functionality and provide a simple list of inserted keys
+ // for demonstration in the main method.
+ }
+ }
+
+ // --- Main Driver and Example Usage ---
+
+ public static void main(String[] args) {
+ PatriciaTrie trie = new PatriciaTrie();
+ System.out.println("Patricia Trie Demonstration (Max Bits: " + MAX_BITS + ")");
+
+ // Example integer keys (representing, perhaps, IP addresses or other binary identifiers)
+ int[] keys = {10, 20, 15, 7, 5, 25};
+
+ System.out.println("\n--- Insertion ---");
+ for (int key : keys) {
+ trie.insert(key);
+ System.out.println("Inserted: " + key + " (" + Integer.toBinaryString(key) + ")");
+ }
+
+ System.out.println("\n--- Search ---");
+ // Test existing keys
+ IntStream.of(keys)
+ .forEach(key -> System.out.printf("Search %d: %b\n", key, trie.search(key)));
+
+ // Test non-existing keys
+ System.out.printf("Search %d: %b\n", 100, trie.search(100)); // Non-existent
+ System.out.printf("Search %d: %b\n", 0, trie.search(0)); // Non-existent
+
+ // Test duplicate insertion
+ System.out.println("\n--- Duplicate Insertion ---");
+ trie.insert(20);
+ }
+}
From 89035c37ba41a0bcb091adcf40c5b155cb4d5752 Mon Sep 17 00:00:00 2001
From: Srishti Soni <92056170+shimmer12@users.noreply.github.com>
Date: Wed, 8 Oct 2025 23:01:17 +0530
Subject: [PATCH 2/7] updated PATRICIA_Trie
Removed the printKeys utility method that printed all keys in the trie.
---
.../datastructures/trees/PATRICIA_Trie.java | 51 +------------------
1 file changed, 2 insertions(+), 49 deletions(-)
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java b/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
index a45f54abb9fc..2937352321b0 100644
--- a/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
+++ b/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
@@ -1,8 +1,5 @@
package com.thealgorithms.datastructures.tries;
-import java.math.BigInteger;
-import java.util.Objects;
-import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
@@ -13,6 +10,8 @@
* as keys, common in many networking and IP lookup contexts, and relies on
* bitwise operations for efficiency.
*
+ * Reference: Wikipedia: Radix Tree (Patricia Trie)
+ *
*
Key characteristics:
*
* - **Radix-2 Trie:** Works on the binary representation of integer keys.
@@ -223,52 +222,6 @@ private PatriciaTrieNode insert(PatriciaTrieNode t, int element) {
return t;
}
- /**
- * Utility method to print all keys in the trie (in order of insertion discovery).
- * @param t The root node.
- */
- private void printKeys(PatriciaTrieNode t) {
- if (t == null) {
- return;
- }
-
- PatriciaTrieNode startNode = t.leftChild; // Start at the first meaningful node
-
- // Use a set to track visited nodes and prevent infinite loop due to back pointers
- java.util.Set visitedNodes = new java.util.HashSet<>();
- java.util.Queue queue = new java.util.LinkedList<>();
-
- // Add the sentinel/root node's left child if it's not the root itself (0 bit)
- if (startNode != t && startNode != null) {
- queue.add(startNode);
- visitedNodes.add(startNode);
- }
-
- // Handle the root key if it's the only one
- if (t.leftChild == t && t.key != 0) {
- System.out.print(t.key + " ");
- return;
- }
-
- while (!queue.isEmpty()) {
- PatriciaTrieNode current = queue.poll();
-
- // The 'key' in a Patricia node is only the data stored at the time of creation.
- // It is NOT a full traversal output. Traversal requires following the logic.
- // This traversal is complex due to back pointers. A simpler in-order traversal
- // that avoids infinite loops by checking bit numbers is typically used.
-
- // Simplest key extraction for this structure: Recursively find external nodes
- // by detecting back pointers.
-
- // Skip if the node is a back pointer (i.e., its child is itself or points "back"
- // to a node with a smaller or equal bit number).
- // NOTE: A standard in-order traversal is difficult due to the compressed structure.
- // We will stick to the basic functionality and provide a simple list of inserted keys
- // for demonstration in the main method.
- }
- }
-
// --- Main Driver and Example Usage ---
public static void main(String[] args) {
From f37be2a878da14a8b64148edccb576cb4c9ab9bc Mon Sep 17 00:00:00 2001
From: Srishti Soni <92056170+shimmer12@users.noreply.github.com>
Date: Wed, 8 Oct 2025 23:07:08 +0530
Subject: [PATCH 3/7] Improve documentation in PATRICIA_Trie.java
---
.../datastructures/trees/PATRICIA_Trie.java | 21 ++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java b/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
index 2937352321b0..1580564b1177 100644
--- a/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
+++ b/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
@@ -25,28 +25,33 @@ public final class PatriciaTrie {
/**
* Represents a node in the Patricia Trie.
* All nodes are internal nodes that store the key data at the point of creation,
- * and their `bitNumber` indicates the bit position to check when traversing.
+ * and their {@code bitNumber} indicates the bit position to check when traversing.
*/
private static class PatriciaTrieNode {
/**
- * The bit index (1-indexed from MSB) to check for branching at this node.
- * The index must be greater than that of the parent node.
+ * The bit index (1-indexed from MSB, 1 to 32) to check for branching at this node.
+ * The index must be greater than that of the parent node. A value of 0 is used for the root.
*/
int bitNumber;
/**
- * The integer key stored at this node. This is the **data** that was inserted
- * to create this node and acts as a placeholder or final result during search.
+ * The integer key stored at this node. This key is used for the final comparison
+ * after traversing the trie structure to determine if the key exists.
*/
int key;
/**
- * Pointer to the next node if the current bit is 0.
+ * Pointer to the next node if the current bit being examined is 0.
*/
PatriciaTrieNode leftChild;
/**
- * Pointer to the next node if the current bit is 1.
+ * Pointer to the next node if the current bit being examined is 1.
*/
PatriciaTrieNode rightChild;
+ /**
+ * Constructs a new PatriciaTrieNode.
+ * @param bitNumber The bit index for comparison at this node.
+ * @param key The integer key associated with this node.
+ */
PatriciaTrieNode(int bitNumber, int key) {
this.bitNumber = bitNumber;
this.key = key;
@@ -170,6 +175,8 @@ private PatriciaTrieNode insert(PatriciaTrieNode t, int element) {
// 3. Check for Duplicates
if (element == lastNode.key) {
+ // Note: Printing to stdout within a core algorithm method is generally discouraged
+ // but is kept here for informational context during the insertion process.
System.out.println("Key " + element + " already present.");
return t;
}
From 51c521597b34490fa6a18958cf737ebeb520bcea Mon Sep 17 00:00:00 2001
From: Srishti Soni <92056170+shimmer12@users.noreply.github.com>
Date: Thu, 9 Oct 2025 00:56:47 +0530
Subject: [PATCH 4/7] Rename PATRICIA_Trie.java to PatriciaTrie.java
---
.../trees/{PATRICIA_Trie.java => PatriciaTrie.java} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename src/main/java/com/thealgorithms/datastructures/trees/{PATRICIA_Trie.java => PatriciaTrie.java} (100%)
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java b/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
similarity index 100%
rename from src/main/java/com/thealgorithms/datastructures/trees/PATRICIA_Trie.java
rename to src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
From 701f422d241cbd1a076e39bf06e4ca2c7cc1d514 Mon Sep 17 00:00:00 2001
From: Srishti Soni <92056170+shimmer12@users.noreply.github.com>
Date: Thu, 9 Oct 2025 00:59:25 +0530
Subject: [PATCH 5/7] Update PatriciaTrie.java
From 4e49696e05a01ff01a374269abb789b6db2d06b8 Mon Sep 17 00:00:00 2001
From: Srishti Soni <92056170+shimmer12@users.noreply.github.com>
Date: Thu, 9 Oct 2025 01:06:05 +0530
Subject: [PATCH 6/7] fixes
---
.../datastructures/trees/PatriciaTrie.java | 249 ++++++++----------
1 file changed, 109 insertions(+), 140 deletions(-)
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java b/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
index 1580564b1177..5cfffcc80067 100644
--- a/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
+++ b/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
@@ -1,65 +1,64 @@
-package com.thealgorithms.datastructures.tries;
+package com.thealgorithms.datastructures.trees;
import java.util.stream.IntStream;
/**
* Implements a PATRICIA Trie (Practical Algorithm to Retrieve Information Coded
- * in Alphanumeric) using fixed-width integers (keys).
+ * in Alphanumeric).
*
- * This specific implementation uses the fixed-size 32-bit integer representation
- * as keys, common in many networking and IP lookup contexts, and relies on
- * bitwise operations for efficiency.
+ *
This implementation uses 32-bit integers as keys, which is common in
+ * applications like IP address routing. It relies on efficient bitwise
+ * operations to navigate the tree. PATRICIA Tries are a type of compressed
+ * binary trie (radix tree with radix 2), where nodes are only created at points
+ * of divergence between keys.
*
- *
Reference: Wikipedia: Radix Tree (Patricia Trie)
+ *
Reference: Wikipedia: Radix Tree
*
- *
Key characteristics:
+ *
Key Characteristics:
*
- * - **Radix-2 Trie:** Works on the binary representation of integer keys.
- * - **Compacted:** Nodes only exist where branching occurs, compacting unary paths.
- * - **External Nodes:** All nodes are internal; the key itself is stored in the
- * leaf/external node found by the search.
+ * - Compressed: Nodes with only one child are omitted, saving space.
+ * - Binary (Radix-2): Decisions at each node are based on a single bit from the key.
+ * - Back-Pointers: Traversal ends when a "back-pointer" is found—a link to an ancestor
+ * or to the node itself—indicating the location of the key for final comparison.
*
*/
public final class PatriciaTrie {
/**
- * Represents a node in the Patricia Trie.
- * All nodes are internal nodes that store the key data at the point of creation,
- * and their {@code bitNumber} indicates the bit position to check when traversing.
+ * Represents a node in the Patricia Trie. Each node specifies which bit to
+ * check and stores a key for comparison.
*/
- private static class PatriciaTrieNode {
+ private static class Node {
/**
- * The bit index (1-indexed from MSB, 1 to 32) to check for branching at this node.
- * The index must be greater than that of the parent node. A value of 0 is used for the root.
+ * The bit index to check for branching at this node (1-indexed from MSB, 1 to 32).
+ * This index must be greater than that of the parent node. A value of 0 is
+ * reserved for the root/head node.
*/
int bitNumber;
/**
- * The integer key stored at this node. This key is used for the final comparison
- * after traversing the trie structure to determine if the key exists.
+ * The integer key associated with this node. This key is used for the final
+ * comparison after traversal to confirm an exact match.
*/
int key;
- /**
- * Pointer to the next node if the current bit being examined is 0.
- */
- PatriciaTrieNode leftChild;
- /**
- * Pointer to the next node if the current bit being examined is 1.
- */
- PatriciaTrieNode rightChild;
+ /** Pointer to the next node if the bit being examined is 0. */
+ Node leftChild;
+ /** Pointer to the next node if the bit being examined is 1. */
+ Node rightChild;
/**
- * Constructs a new PatriciaTrieNode.
+ * Constructs a new Node.
+ *
* @param bitNumber The bit index for comparison at this node.
* @param key The integer key associated with this node.
*/
- PatriciaTrieNode(int bitNumber, int key) {
+ Node(int bitNumber, int key) {
this.bitNumber = bitNumber;
this.key = key;
}
}
- private PatriciaTrieNode root;
- private static final int MAX_BITS = Integer.SIZE; // 32 bits for standard Java int
+ private Node root;
+ private static final int MAX_BITS = Integer.SIZE; // 32 bits for a standard Java int
/**
* Initializes an empty Patricia Trie.
@@ -69,192 +68,162 @@ public PatriciaTrie() {
}
/**
- * Checks if the trie is empty.
- * @return true if the root is null, false otherwise.
+ * Checks if the trie contains any keys.
+ * @return {@code true} if the trie is empty, {@code false} otherwise.
*/
public boolean isEmpty() {
return root == null;
}
/**
- * Resets the trie, setting the root to null.
+ * Removes all keys from the trie.
*/
public void makeEmpty() {
root = null;
}
/**
- * Determines the value of the i-th bit (1-indexed from MSB) of a given key.
- * Uses efficient bitwise operations.
+ * Determines the value of the i-th bit of a given key.
*
* @param key The integer key.
- * @param i The 1-based index of the bit to check (1 is MSB, 32 is LSB).
- * @return true if the bit is 1, false if the bit is 0.
+ * @param i The 1-based index of the bit to check (1=MSB, 32=LSB).
+ * @return {@code true} if the bit is 1, {@code false} if it is 0.
*/
private boolean getBit(int key, int i) {
- // Calculate the shift amount: MAX_BITS - i
- // i=1 (MSB) -> shift 31
- // i=32 (LSB) -> shift 0
- int shift = MAX_BITS - i;
- // Use unsigned right shift (>>>) for predictable results, then mask with 1.
- return ((key >>> shift) & 1) == 1;
+ // A 1-based index `i` corresponds to a shift of (MAX_BITS - i).
+ // Example for 32 bits: i=1 (MSB) -> shift 31; i=32 (LSB) -> shift 0.
+ return ((key >>> (MAX_BITS - i)) & 1) == 1;
}
/**
- * Searches for a key in the trie.
+ * Searches for an exact key in the trie.
*
* @param key The integer key to search for.
- * @return true if the key is found, false otherwise.
+ * @return {@code true} if the key is found, {@code false} otherwise.
*/
public boolean search(int key) {
if (root == null) {
return false;
}
-
- // Search down to the external node
- PatriciaTrieNode foundNode = searchDown(root, key);
-
- // Check if the key stored in the found node matches the search key
- return foundNode.key == key;
+ // Find the node containing the best possible match for the key.
+ Node bestMatchNode = findBestMatchNode(root, key);
+ // Confirm if the best match is an exact match.
+ return bestMatchNode.key == key;
}
/**
- * Traverses the trie to find the external node that is the predecessor
- * of the key 'k'. This node contains the most similar key currently in the trie.
+ * Traverses the trie to find the node containing the key most similar to the search key.
*
- * @param t The starting node for the search (usually the root).
- * @param k The key being searched for.
- * @return The external node where the key comparison should happen.
+ * @param startNode The node to begin the search from (usually the root).
+ * @param key The key being searched for.
+ * @return The node containing the best matching key.
*/
- private PatriciaTrieNode searchDown(PatriciaTrieNode t, int k) {
- PatriciaTrieNode currentNode = t;
- PatriciaTrieNode nextNode = t.leftChild; // Start by following the default (0) child
+ private Node findBestMatchNode(Node startNode, int key) {
+ Node currentNode = startNode;
+ Node nextNode = startNode.leftChild;
- // The condition nextNode.bitNumber > currentNode.bitNumber is the core
- // of the Patricia Trie structure. It means we are moving down a tree edge (forward reference).
+ // Traverse down the trie as long as we are following forward pointers.
+ // A forward pointer is indicated by a child's bitNumber being greater
+ // than its parent's bitNumber.
while (nextNode.bitNumber > currentNode.bitNumber) {
currentNode = nextNode;
- // Determine the next child based on the bit at nextNode.bitNumber
- nextNode = getBit(k, nextNode.bitNumber)
- ? nextNode.rightChild
- : nextNode.leftChild;
+ nextNode = getBit(key, nextNode.bitNumber) ? nextNode.rightChild : nextNode.leftChild;
}
- // When nextNode.bitNumber <= currentNode.bitNumber, we've found an external node
- // (a back pointer) which holds the best match key.
+ // The loop terminates upon finding a back-pointer (nextNode.bitNumber <= currentNode.bitNumber),
+ // which points to the node containing the best match.
return nextNode;
}
/**
- * Inserts an integer key into the Patricia Trie.
+ * Inserts an integer key into the Patricia Trie. Does nothing if the key
+ * already exists.
*
* @param key The integer key to insert.
*/
public void insert(int key) {
- root = insert(root, key);
- }
-
- /**
- * Recursive helper method for insertion.
- *
- * @param t The current subtree root.
- * @param element The key to insert.
- * @return The updated root of the subtree.
- */
- private PatriciaTrieNode insert(PatriciaTrieNode t, int element) {
-
- // 1. Handle Empty Trie (Initial Insertion)
- if (t == null) {
- t = new PatriciaTrieNode(0, element); // Bit number 0 for the root/sentinel
- t.leftChild = t; // Root node links back to itself (left pointer)
- t.rightChild = null; // Right pointer unused or null
- return t;
+ // 1. Handle Empty Trie (the very first insertion)
+ if (root == null) {
+ root = new Node(0, key); // bitNumber 0 is a sentinel for the head
+ root.leftChild = root; // The first node points back to itself
+ return;
}
- // 2. Search for the best match (predecessor)
- PatriciaTrieNode lastNode = searchDown(t, element);
+ // 2. Find the best matching key already in the trie
+ Node bestMatchNode = findBestMatchNode(root, key);
// 3. Check for Duplicates
- if (element == lastNode.key) {
- // Note: Printing to stdout within a core algorithm method is generally discouraged
- // but is kept here for informational context during the insertion process.
- System.out.println("Key " + element + " already present.");
- return t;
+ if (key == bestMatchNode.key) {
+ return; // Key already exists, do nothing.
}
- // 4. Find the first differentiating bit (i)
- int i = 1;
- while (getBit(element, i) == getBit(lastNode.key, i) && i < MAX_BITS) {
- i++;
- }
- // If i reached MAX_BITS + 1, the keys are identical (should have been caught above)
- if (i > MAX_BITS) {
- throw new IllegalStateException("Keys are identical but duplicate check failed.");
+ // 4. Find the first bit position where the new key and its best match differ
+ int differingBit = 1;
+ // BUG FIX: The loop must check all bits (i <= MAX_BITS). The original
+ // code (i < MAX_BITS) failed to check the last bit.
+ while (differingBit <= MAX_BITS && getBit(key, differingBit) == getBit(bestMatchNode.key, differingBit)) {
+ differingBit++;
}
- // 5. Find the insertion point (parent)
- // Find the node 'parent' that points to a bit number greater than 'i' or points back
- PatriciaTrieNode currentNode = t.leftChild;
- PatriciaTrieNode parent = t;
+ // This should not happen if the duplicate check is correct, but serves as a safeguard.
+ if (differingBit > MAX_BITS) {
+ throw new IllegalStateException("Keys are different but no differing bit was found.");
+ }
- while (currentNode.bitNumber > parent.bitNumber && currentNode.bitNumber < i) {
- parent = currentNode;
- currentNode = getBit(element, currentNode.bitNumber)
- ? currentNode.rightChild
- : currentNode.leftChild;
+ // 5. Find the correct insertion point by traversing again
+ Node parent = root;
+ Node child = root.leftChild;
+ while (child.bitNumber > parent.bitNumber && child.bitNumber < differingBit) {
+ parent = child;
+ child = getBit(key, child.bitNumber) ? child.rightChild : child.leftChild;
}
- // 6. Create the new internal node
- PatriciaTrieNode newNode = new PatriciaTrieNode(i, element);
+ // 6. Create the new node and link it into the trie
+ Node newNode = new Node(differingBit, key);
- // Determine the children of the new node (newNode)
- if (getBit(element, i)) {
- // New key has 1 at bit i: left child points to the old subtree (currentNode), right child points to self
- newNode.leftChild = currentNode;
+ // Set the children of the new node. One child will be the existing subtree (`child`),
+ // and the other will be a back-pointer to the new node itself.
+ if (getBit(key, differingBit)) { // New key has a '1' at the differing bit
+ newNode.leftChild = child;
newNode.rightChild = newNode;
- } else {
- // New key has 0 at bit i: left child points to self, right child points to the old subtree (currentNode)
+ } else { // New key has a '0' at the differing bit
newNode.leftChild = newNode;
- newNode.rightChild = currentNode;
+ newNode.rightChild = child;
}
- // 7. Link the parent to the new node
- if (getBit(element, parent.bitNumber)) {
- // Parent's splitting bit matches the new key's bit: link via right child
+ // 7. Link the parent to the new node, replacing its old link to `child`
+ if (getBit(key, parent.bitNumber)) {
parent.rightChild = newNode;
} else {
- // Parent's splitting bit doesn't match: link via left child
parent.leftChild = newNode;
}
-
- return t;
}
// --- Main Driver and Example Usage ---
public static void main(String[] args) {
PatriciaTrie trie = new PatriciaTrie();
- System.out.println("Patricia Trie Demonstration (Max Bits: " + MAX_BITS + ")");
+ System.out.println("--- Patricia Trie Demonstration ---");
+ System.out.println("Trie is empty: " + trie.isEmpty());
- // Example integer keys (representing, perhaps, IP addresses or other binary identifiers)
int[] keys = {10, 20, 15, 7, 5, 25};
- System.out.println("\n--- Insertion ---");
+ System.out.println("\n--- Inserting Keys ---");
for (int key : keys) {
+ System.out.printf("Inserting: %3d (%s)%n", key, Integer.toBinaryString(key));
trie.insert(key);
- System.out.println("Inserted: " + key + " (" + Integer.toBinaryString(key) + ")");
}
+ System.out.println("\nTrie is empty: " + trie.isEmpty());
- System.out.println("\n--- Search ---");
- // Test existing keys
- IntStream.of(keys)
- .forEach(key -> System.out.printf("Search %d: %b\n", key, trie.search(key)));
+ System.out.println("\n--- Verifying Existing Keys ---");
+ IntStream.of(keys).forEach(key -> System.out.printf("Search %3d: %s%n", key, trie.search(key) ? "Found" : "Not Found"));
- // Test non-existing keys
- System.out.printf("Search %d: %b\n", 100, trie.search(100)); // Non-existent
- System.out.printf("Search %d: %b\n", 0, trie.search(0)); // Non-existent
+ System.out.println("\n--- Searching for Non-Existing Keys ---");
+ System.out.printf("Search %3d: %s%n", 100, trie.search(100) ? "Found" : "Not Found");
+ System.out.printf("Search %3d: %s%n", 0, trie.search(0) ? "Found" : "Not Found");
- // Test duplicate insertion
- System.out.println("\n--- Duplicate Insertion ---");
- trie.insert(20);
+ System.out.println("\n--- Attempting Duplicate Insertion ---");
+ System.out.println("Inserting 20 again...");
+ trie.insert(20); // Should do nothing
+ System.out.printf("Search %3d: %s%n", 20, trie.search(20) ? "Found" : "Not Found");
}
}
From b657ae362add14c65e15238e17fc7cb87f2dd791 Mon Sep 17 00:00:00 2001
From: Srishti Soni <92056170+shimmer12@users.noreply.github.com>
Date: Thu, 9 Oct 2025 01:12:45 +0530
Subject: [PATCH 7/7] Remove main method and example usage
Removed main method and example usage from PatriciaTrie class.
---
.../datastructures/trees/PatriciaTrie.java | 32 +------------------
1 file changed, 1 insertion(+), 31 deletions(-)
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java b/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
index 5cfffcc80067..fd2645824dea 100644
--- a/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
+++ b/src/main/java/com/thealgorithms/datastructures/trees/PatriciaTrie.java
@@ -158,8 +158,7 @@ public void insert(int key) {
// 4. Find the first bit position where the new key and its best match differ
int differingBit = 1;
- // BUG FIX: The loop must check all bits (i <= MAX_BITS). The original
- // code (i < MAX_BITS) failed to check the last bit.
+ // The loop must check all bits (i <= MAX_BITS).
while (differingBit <= MAX_BITS && getBit(key, differingBit) == getBit(bestMatchNode.key, differingBit)) {
differingBit++;
}
@@ -197,33 +196,4 @@ public void insert(int key) {
parent.leftChild = newNode;
}
}
-
- // --- Main Driver and Example Usage ---
-
- public static void main(String[] args) {
- PatriciaTrie trie = new PatriciaTrie();
- System.out.println("--- Patricia Trie Demonstration ---");
- System.out.println("Trie is empty: " + trie.isEmpty());
-
- int[] keys = {10, 20, 15, 7, 5, 25};
-
- System.out.println("\n--- Inserting Keys ---");
- for (int key : keys) {
- System.out.printf("Inserting: %3d (%s)%n", key, Integer.toBinaryString(key));
- trie.insert(key);
- }
- System.out.println("\nTrie is empty: " + trie.isEmpty());
-
- System.out.println("\n--- Verifying Existing Keys ---");
- IntStream.of(keys).forEach(key -> System.out.printf("Search %3d: %s%n", key, trie.search(key) ? "Found" : "Not Found"));
-
- System.out.println("\n--- Searching for Non-Existing Keys ---");
- System.out.printf("Search %3d: %s%n", 100, trie.search(100) ? "Found" : "Not Found");
- System.out.printf("Search %3d: %s%n", 0, trie.search(0) ? "Found" : "Not Found");
-
- System.out.println("\n--- Attempting Duplicate Insertion ---");
- System.out.println("Inserting 20 again...");
- trie.insert(20); // Should do nothing
- System.out.printf("Search %3d: %s%n", 20, trie.search(20) ? "Found" : "Not Found");
- }
}