Compressed Tries
Last Updated :
11 Apr, 2024
A trie is a data structure that stores strings like a tree data structure. The maximum number of children in a node is equal to the size of the alphabet. One can easily print letters in alphabetical order which isn’t possible with hashing.
Properties of Trie:
- It’s a multi-way tree.
- Each node has from 1 to N children.
- Each leaf node corresponds to the stored string, which is a chain of characters on a path from the root to its side.
Types of Trie:
Compressed Trie:
Tries with nodes of degree at least 2. It is accomplished by compressing the nodes of the standard trie. It is also known as Radix Tries. It is used to achieve space optimization
Since the nodes are compressed. Let’s visually compare the structure of the Standard tree and the compressed tree for a better approach. In terms of memory, a compressed trie tree uses very few amounts of nodes which gives a huge memory advantage(especially for long) strings with long common prefixes. In terms of speed, a regular trie tree would be slightly faster because its operations don’t involve any string operations, they are simple loops.
In the below image, the left tree is a Standard trie, the right tree is a compressed trie.
Implementation:
A standard trie node looks like this:
C++
class Node {
public: Node* children[26];
bool isWordEnd;
};
Java
class node {
node[] children = new node[26];
boolean isWordEnd;
}
Python3
class node:
def __init__(self):
self.node = [None]*26
self.isWordEnd=False
C#
class Node {
Node[] children = new Node[26];
bool isWordEnd;
}
Javascript
// JavaScript Implementation
class Node {
constructor() {
this.nodes = new Array(26).fill(null);
this.isWordEnd = false;
}
}
But for a compressed trie, redesigning of the tree will be as follows, in the general trie, an edge ‘a’ is denoted by this particular element in the array of references, but in the compressed trie, “An edge ‘face’ is denoted by this particular element in the array of references”. The code is-:
C++
#include <iostream>
#include <string>
class Node {
public:
static const int SYMBOLS = 26;
Node* children[SYMBOLS];
std::string edgeLabel[SYMBOLS];
bool isEnd;
Node(bool isEnd = false) : isEnd(isEnd) {
for (int i = 0; i < SYMBOLS; ++i) {
children[i] = nullptr;
}
}
};
int main() {
// Example usage
Node* exampleNode = new Node(true);
// Accessing children array
if (exampleNode->children['a' - 'a'] == nullptr) {
// Do something
}
// Accessing edgeLabel array
if (exampleNode->edgeLabel['a' - 'a'].empty()) {
// Do something
}
delete exampleNode; // Don't forget to free the allocated memory
return 0;
}
Java
class node {
node[] children = new node[26];
StringBuilder[] edgeLabel = new StringBuilder[26];
boolean isEnd;
}
Python3
class node:
def __init__(self):
self.children = [None]*26
sefl.edgeLabel = [None]*26
self.isEnd=False
C#
using System.Text;
public class Node
{
public Node[] Children = new Node[26];
public StringBuilder[] EdgeLabel = new StringBuilder[26];
public bool IsEnd;
}
Javascript
class Node {
constructor(isEnd = false) {
this.SYMBOLS = 26;
this.children = new Array(this.SYMBOLS).fill(null);
this.edgeLabel = new Array(this.SYMBOLS).fill('');
this.isEnd = isEnd;
}
}
// Example usage
let exampleNode = new Node(true);
// Accessing children array
if (exampleNode.children['a'.charCodeAt(0) - 'a'.charCodeAt(0)] === null) {
// Do something
}
// Accessing edgeLabel array
if (exampleNode.edgeLabel['a'.charCodeAt(0) - 'a'.charCodeAt(0)] === '') {
// Do something
}
// Don't forget to free the allocated memory (JavaScript manages memory automatically)
// No need for 'delete' in JavaScript
// To mimic deletion in JavaScript, you can set the variable to null:
exampleNode = null;
// Sarojmcy2e
Node in Compressed Trie:
C++
class CompressedNode {
public:
int bitNumber;
int data;
CompressedNode* leftChild;
CompressedNode* rightChild;
};
Java
class CompressedNode {
int bitNumber;
int data;
CompressedNode leftChild, rightChild;
}
Python3
class node:
def __init__(self):
self.bitNumber=0
self.data=None
self.leftChild, self.rightChild=None,None
C#
public class CompressedNode
{
public int bitNumber;
public int data;
public CompressedNode leftChild;
public CompressedNode rightChild;
}
JavaScript
class CompressedNode {
constructor(bitNumber, data) {
this.bitNumber = bitNumber;
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
}
//this code is contributed by Utkarsh
Class Compressed trie:
C++
#include <iostream>
class CompressedNode {
// Root Node
private:
CompressedNode* root;
public:
// Constructor
CompressedNode() { root = nullptr; }
// Function to check if empty
bool isEmpty() { return root == nullptr; }
// Function to clear
void makeEmpty() { root = nullptr; }
};
int main() {
// Example usage
CompressedNode compressedNode;
// Check if empty
std::cout << "Is empty: " << (compressedNode.isEmpty() ? "true" : "false") << std::endl;
// Clear the node
compressedNode.makeEmpty();
return 0;
}
Java
class CompressedNode {
// Root Node
private CompressedNode root;
private static final int MaxBits = 10;
// Constructor
public CompressedNode() { root = null; }
// Function to check if empty
public boolean isEmpty() { return root == null; }
// Function to clear
public void makeEmpty() { root = null; }
}
Python3
class CompressedNode {
#Root Node
root=CompressedNode()
MaxBits = 10
#Constructor
def __init__(self):
self.root = None
#Function to check if empty
def isEmpty(self):
return self.root == None
#Function to clear
def makeEmpty(self):
self.root = None
C#
public class CompressedNode
{
// Root Node
private CompressedNode root;
private const int MaxBits = 10;
// Constructor
public CompressedNode()
{
root = null;
}
// Function to check if empty
public bool IsEmpty()
{
return root == null;
}
// Function to clear
public void MakeEmpty()
{
root = null;
}
}
// This code is contributed by Utkarsh
Javascript
class CompressedNode {
constructor() {
// Root Node
this.root = null;
}
// Function to check if empty
isEmpty() {
return this.root === null;
}
// Function to clear
makeEmpty() {
this.root = null;
}
}
// Example usage
const compressedNode = new CompressedNode();
// Check if empty
console.log("Is empty:", compressedNode.isEmpty() ? "true" : "false");
// Clear the node
compressedNode.makeEmpty();
Searching in Compressed Trie:
Searching in a compressed Trie tree is much like searching. Here, instead of comparing a single character, we compare strings.
C++
// Function to search a key k in the trie
bool search(int k)
{
// Find the number of bits
int numOfBits = (int)(log2(k)) + 1;
// If error occurs
if (numOfBits > MaxBits) {
cout << "Error : Number too large" << endl;
return false;
}
// Search Node
CompressedNode* searchNode = search(root, k);
// If the data matches
if (searchNode->data == k)
return true;
// Else return false
else
return false;
}
Java
// Function to search a key k
// in the trie
public boolean search(int k)
{
// Find the number of bits
int numOfBits = (int)(Math.log(k) / Math.log(2)) + 1;
// If error occurs
if (numOfBits > MaxBits) {
System.out.println("Error : Number too large");
return false;
}
// Search Node
CompressedNode searchNode = search(root, k);
// If the data matches
if (searchNode.data == k)
return true;
// Else return false
else
return false;
}
Python3
# Function to search a key k
# in the trie
import math
def search(k):
# Find the number of bits
numOfBits = int(math.log2(k)) + 1
# If error occurs
if (numOfBits > MaxBits):
print("Error : Number too large")
return False
# Search Node
searchNode = search(root, k)
# If the data matches
if (searchNode.data == k):
return True
# Else return false
else:
return False
C#
// Function to search a key k
// in the trie
public bool Search(int k)
{
// Find the number of bits
int numOfBits = (int)(Math.Log(k) / Math.Log(2)) + 1;
// If error occurs
if (numOfBits > MaxBits) {
Console.WriteLine("Error : Number too large");
return false;
}
// Search Node
CompressedNode searchNode = Search(root, k);
// If the data matches
if (searchNode.data == k)
return true;
// Else return false
else
return false;
}
Javascript
// Function to search a key k in the trie
function search(k) {
// Find the number of bits
const numOfBits = Math.floor(Math.log2(k)) + 1;
// If error occurs
if (numOfBits > MaxBits) {
console.log("Error : Number too large");
return false;
}
// Search Node
const searchNode = search(root, k);
// If the data matches
if (searchNode.data == k) {
return true;
}
// Else return false
else {
return false;
}
}
Inserting an element in Compressed Trie:
C++
// C++ code addition
struct CompressedNode {
int bitNumber;
int data;
CompressedNode* leftChild;
CompressedNode* rightChild;
};
// Function to implement the insert
// functionality in the trie
CompressedNode* insert(
CompressedNode* t, int ele)
{
CompressedNode* current;
CompressedNode* parent;
CompressedNode* lastNode;
CompressedNode* newNode;
int i;
// If Node is NULL
if (t == nullptr) {
t = new CompressedNode();
t->bitNumber = 0;
t->data = ele;
t->leftChild = t;
t->rightChild = nullptr;
return t;
}
// Search the key ele
lastNode = search(t, ele);
// If already present key
if (ele == lastNode->data) {
std::cout << "Error : key is already present\n";
return t;
}
for (i = 1; bit(ele, i) == bit(lastNode->data, i); i++)
;
current = t->leftChild;
parent = t;
while (current->bitNumber > parent->bitNumber
&& current->bitNumber < i) {
parent = current;
current = (bit(ele, current->bitNumber))
? current->rightChild
: current->leftChild;
}
newNode = new CompressedNode();
newNode->bitNumber = i;
newNode->data = ele;
newNode->leftChild = bit(ele, i) ? current : newNode;
newNode->rightChild = bit(ele, i) ? newNode : current;
if (current == parent->leftChild)
parent->leftChild = newNode;
else
parent->rightChild = newNode;
return t;
}
// The code is contributed by Arushi Goel.
Java
// Function to implement the insert
// functionality in the trie
private CompressedNode insert(
CompressedNode t, int ele)
{
CompressedNode current, parent;
CompressedNode lastNode, newNode;
int i;
// If Node is NULL
if (t == null) {
t = new CompressedNode();
t.bitNumber = 0;
t.data = ele;
t.leftChild = t;
t.rightChild = null;
return t;
}
// Search the key ele
lastNode = search(t, ele);
// If already present key
if (ele == lastNode.data) {
System.out.println(
"Error : key is already present\n");
return t;
}
for (i = 1; bit(ele, i) == bit(lastNode.data, i); i++)
;
current = t.leftChild;
parent = t;
while (current.bitNumber > parent.bitNumber
&& current.bitNumber < i) {
parent = current;
current = (bit(ele, current.bitNumber))
? current.rightChild
: current.leftChild;
}
newNode = new CompressedNode();
newNode.bitNumber = i;
newNode.data = ele;
newNode.leftChild = bit(ele, i) ? current : newNode;
newNode.rightChild = bit(ele, i) ? newNode : current;
if (current == parent.leftChild)
parent.leftChild = newNode;
else
parent.rightChild = newNode;
return t;
}
Python3
# Function to implement the insert
# functionality in the trie
def insert( t, ele):
# If Node is None
if (t == None) :
t = CompressedNode()
t.bitNumber = 0
t.data = ele
t.leftChild = t
t.rightChild = None
return t
# Search the key ele
lastNode = search(t, ele)
# If already present key
if (ele == lastNode.data) :
print(
"Error : key is already present")
return t
i=1
while(bit(ele, i) == bit(lastNode.data, i)):
i+=1
current = t.leftChild
parent = t
while (current.bitNumber > parent.bitNumber and current.bitNumber < i) :
parent = current
current = current.rightChild if (bit(ele, current.bitNumber)) else current.leftChild
newNode = CompressedNode()
newNode.bitNumber = i
newNode.data = ele
newNode.leftChild = current if bit(ele, i) else newNode
newNode.rightChild = newNode if bit(ele, i) else current
if (current == parent.leftChild):
parent.leftChild = newNode
else:
parent.rightChild = newNode
return t
C#
private CompressedNode Insert(CompressedNode t, int ele)
{
CompressedNode current, parent, lastNode, newNode;
int i;
// If node is null
if (t == null)
{
t = new CompressedNode();
t.bitNumber = 0;
t.data = ele;
t.leftChild = t;
t.rightChild = null;
return t;
}
// Search for the key ele
lastNode = Search(t, ele);
// If key is already present
if (ele == lastNode.data)
{
Console.WriteLine("Error: key is already present");
return t;
}
// Find the first bit of difference between lastNode and new node
for (i = 1; Bit(ele, i) == Bit(lastNode.data, i); i++);
current = t.leftChild;
parent = t;
// Traverse down the tree to find the position to insert the new node
while (current.bitNumber > parent.bitNumber && current.bitNumber < i)
{
parent = current;
current = (Bit(ele, current.bitNumber)) ? current.rightChild : current.leftChild;
}
// Create a new node to be inserted
newNode = new CompressedNode();
newNode.bitNumber = i;
newNode.data = ele;
newNode.leftChild = Bit(ele, i) ? current : newNode;
newNode.rightChild = Bit(ele, i) ? newNode : current;
// Update the parent of the new node
if (current == parent.leftChild)
parent.leftChild = newNode;
else
parent.rightChild = newNode;
return t;
}
// Function to search a key k in the trie
public bool Search(int k)
{
int numOfBits = (int)(Math.Log(k) / Math.Log(2)) + 1;
if (numOfBits > MaxBits)
{
Console.WriteLine("Error: Number too large");
return false;
}
CompressedNode searchNode = Search(root, k);
if (searchNode.data == k)
return true;
else
return false;
}
Javascript
// Function to implement the insert functionality in the trie
function insert(t, ele) {
// If Node is null
if (t == null) {
t = new CompressedNode();
t.bitNumber = 0;
t.data = ele;
t.leftChild = t;
t.rightChild = null;
return t;
}
// Search the key ele
var lastNode = search(t, ele);
// If already present key
if (ele == lastNode.data) {
console.log("Error : key is already present");
return t;
}
var i = 1;
while (bit(ele, i) == bit(lastNode.data, i)) {
i++;
}
var current = t.leftChild;
var parent = t;
while (current.bitNumber > parent.bitNumber && current.bitNumber < i) {
parent = current;
current = current.rightChild ? current.rightChild : current.leftChild;
}
var newNode = new CompressedNode();
newNode.bitNumber = i;
newNode.data = ele;
newNode.leftChild = bit(ele, i) ? current : newNode;
newNode.rightChild = bit(ele, i) ? newNode : current;
if (current == parent.leftChild) {
parent.leftChild = newNode;
} else {
parent.rightChild = newNode;
}
return t;
}
Below is the program to implement all functionality of the compressed Trie:
C++
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
// Node class
class Node {
static const int SYMBOLS = 26;
public:
vector<Node*> children;
vector<string> edgeLabel;
bool isEnd;
Node(bool isEnd) : isEnd(isEnd) {
children.resize(SYMBOLS, nullptr);
edgeLabel.resize(SYMBOLS);
}
};
// Trie class
class Trie {
private:
Node* root;
char CASE;
// Function that creates new string
// from an existing string starting
// from the given index
string strCopy(string str, int index) {
string result;
for (int i = index; i < str.length(); ++i) {
result += str[i];
}
return result;
}
// Function to print the word
// starting from the given node
void printUtil(Node* node, string str) {
if (node->isEnd) {
cout << str << endl;
}
for (int i = 0; i < node->edgeLabel.size(); ++i) {
if (!node->edgeLabel[i].empty()) {
string temp = str + node->edgeLabel[i];
printUtil(node->children[i], temp);
}
}
}
public:
Trie(char CASE = 'a') : CASE(CASE) {
root = new Node(false);
}
// Function to insert a word in
// the compressed trie
void insert(string word) {
Node* trav = root;
int i = 0;
while (i < word.length() && !trav->edgeLabel[word[i] - CASE].empty()) {
int index = word[i] - CASE;
int j = 0;
string label = trav->edgeLabel[index];
while (j < label.length() && i < word.length() && label[j] == word[i]) {
++i;
++j;
}
if (j == label.length()) {
trav = trav->children[index];
} else {
if (i == word.length()) {
Node* existingChild = trav->children[index];
Node* newChild = new Node(true);
string remainingLabel = label.substr(j);
trav->children[index] = newChild;
newChild->children[remainingLabel[0] - CASE] = existingChild;
newChild->edgeLabel[remainingLabel[0] - CASE] = remainingLabel;
} else {
string remainingLabel = label.substr(j);
Node* newChild = new Node(false);
string remainingWord = word.substr(i);
Node* temp = trav->children[index];
trav->children[index] = newChild;
newChild->edgeLabel[remainingLabel[0] - CASE] = remainingLabel;
newChild->children[remainingLabel[0] - CASE] = temp;
newChild->edgeLabel[remainingWord[0] - CASE] = remainingWord;
newChild->children[remainingWord[0] - CASE] = new Node(true);
}
return;
}
}
if (i < word.length()) {
trav->edgeLabel[word[i] - CASE] = strCopy(word, i);
trav->children[word[i] - CASE] = new Node(true);
} else {
trav->isEnd = true;
}
}
// Function to print the Trie
void print() {
printUtil(root, "");
}
// Function to search a word
bool search(string word) {
int i = 0;
Node* trav = root;
while (i < word.length() && !trav->edgeLabel[word[i] - CASE].empty()) {
int index = word[i] - CASE;
string label = trav->edgeLabel[index];
int j = 0;
while (i < word.length() && j < label.length()) {
if (word[i] != label[j]) {
return false;
}
i++;
j++;
}
if (j == label.length() && i <= word.length()) {
trav = trav->children[index];
} else {
return false;
}
}
return i == word.length() && trav->isEnd;
}
// Function to search the prefix
bool startsWith(string prefix) {
int i = 0;
Node* trav = root;
while (i < prefix.length() && !trav->edgeLabel[prefix[i] - CASE].empty()) {
int index = prefix[i] - CASE;
string label = trav->edgeLabel[index];
int j = 0;
while (i < prefix.length() && j < label.length()) {
if (prefix[i] != label[j]) {
return false;
}
i++;
j++;
}
if (j == label.length() && i <= prefix.length()) {
trav = trav->children[index];
} else {
return true;
}
}
return i == prefix.length();
}
};
// Driver code
int main() {
Trie trie;
// Insert words
trie.insert("facebook");
trie.insert("face");
trie.insert("this");
trie.insert("there");
trie.insert("then");
// Print inserted words
trie.print();
// Check if these words
// are present or not
cout << boolalpha;
cout << trie.search("there") << endl;
cout << trie.search("therein") << endl;
cout << trie.startsWith("th") << endl;
cout << trie.startsWith("fab") << endl;
return 0;
}
//This code is contributed by Aman.
Java
// Java program to implement the
// Compressed Trie
class Trie {
// Root Node
private final Node root = new Node(false);
// 'a' for lower, 'A' for upper
private final char CASE;
// Default case
public Trie() { CASE = 'a'; }
// Constructor accepting the
// starting symbol
public Trie(char CASE)
{
this.CASE = CASE;
}
// Function to insert a word in
// the compressed trie
public void insert(String word)
{
// Store the root
Node trav = root;
int i = 0;
// Iterate i less than word
// length
while (i < word.length()
&& trav.edgeLabel[word.charAt(i) - CASE]
!= null) {
// Find the index
int index = word.charAt(i) - CASE, j = 0;
StringBuilder label = trav.edgeLabel[index];
// Iterate till j is less
// than label length
while (j < label.length() && i < word.length()
&& label.charAt(j) == word.charAt(i)) {
++i;
++j;
}
// If is the same as the
// label length
if (j == label.length()) {
trav = trav.children[index];
}
else {
// Inserting a prefix of
// the existing word
if (i == word.length()) {
Node existingChild
= trav.children[index];
Node newChild = new Node(true);
StringBuilder remainingLabel
= strCopy(label, j);
// Making "facebook"
// as "face"
label.setLength(j);
// New node for "face"
trav.children[index] = newChild;
newChild
.children[remainingLabel.charAt(0)
- CASE]
= existingChild;
newChild
.edgeLabel[remainingLabel.charAt(0)
- CASE]
= remainingLabel;
}
else {
// Inserting word which has
// a partial match with
// existing word
StringBuilder remainingLabel
= strCopy(label, j);
Node newChild = new Node(false);
StringBuilder remainingWord
= strCopy(word, i);
// Store the trav in
// temp node
Node temp = trav.children[index];
label.setLength(j);
trav.children[index] = newChild;
newChild
.edgeLabel[remainingLabel.charAt(0)
- CASE]
= remainingLabel;
newChild
.children[remainingLabel.charAt(0)
- CASE]
= temp;
newChild
.edgeLabel[remainingWord.charAt(0)
- CASE]
= remainingWord;
newChild
.children[remainingWord.charAt(0)
- CASE]
= new Node(true);
}
return;
}
}
// Insert new node for new word
if (i < word.length()) {
trav.edgeLabel[word.charAt(i) - CASE]
= strCopy(word, i);
trav.children[word.charAt(i) - CASE]
= new Node(true);
}
else {
// Insert "there" when "therein"
// and "thereafter" are existing
trav.isEnd = true;
}
}
// Function that creates new String
// from an existing string starting
// from the given index
private StringBuilder strCopy(
CharSequence str, int index)
{
StringBuilder result
= new StringBuilder(100);
while (index != str.length()) {
result.append(str.charAt(index++));
}
return result;
}
// Function to print the Trie
public void print()
{
printUtil(root, new StringBuilder());
}
// Function to print the word
// starting from the given node
private void printUtil(
Node node, StringBuilder str)
{
if (node.isEnd) {
System.out.println(str);
}
for (int i = 0;
i < node.edgeLabel.length; ++i) {
// If edgeLabel is not
// NULL
if (node.edgeLabel[i] != null) {
int length = str.length();
str = str.append(node.edgeLabel[i]);
printUtil(node.children[i], str);
str = str.delete(length, str.length());
}
}
}
// Function to search a word
public boolean search(String word)
{
int i = 0;
// Stores the root
Node trav = root;
while (i < word.length()
&& trav.edgeLabel[word.charAt(i) - CASE]
!= null) {
int index = word.charAt(i) - CASE;
StringBuilder label = trav.edgeLabel[index];
int j = 0;
while (i < word.length()
&& j < label.length()) {
// Character mismatch
if (word.charAt(i) != label.charAt(j)) {
return false;
}
++i;
++j;
}
if (j == label.length() && i <= word.length()) {
// Traverse further
trav = trav.children[index];
}
else {
// Edge label is larger
// than target word
// searching for "face"
// when tree has "facebook"
return false;
}
}
// Target word fully traversed
// and current node is word
return i == word.length() && trav.isEnd;
}
// Function to search the prefix
public boolean startsWith(String prefix)
{
int i = 0;
// Stores the root
Node trav = root;
while (i < prefix.length()
&& trav.edgeLabel[prefix.charAt(i) - CASE]
!= null) {
int index = prefix.charAt(i) - CASE;
StringBuilder label = trav.edgeLabel[index];
int j = 0;
while (i < prefix.length()
&& j < label.length()) {
// Character mismatch
if (prefix.charAt(i) != label.charAt(j)) {
return false;
}
++i;
++j;
}
if (j == label.length()
&& i <= prefix.length()) {
// Traverse further
trav = trav.children[index];
}
else {
// Edge label is larger
// than target word,
// which is fine
return true;
}
}
return i == prefix.length();
}
}
// Node class
class Node {
// Number of symbols
private final static int SYMBOLS = 26;
Node[] children = new Node[SYMBOLS];
StringBuilder[] edgeLabel = new StringBuilder[SYMBOLS];
boolean isEnd;
// Function to check if the end
// of the string is reached
public Node(boolean isEnd)
{
this.isEnd = isEnd;
}
}
class GFG {
// Driver Code
public static void main(String[] args)
{
Trie trie = new Trie();
// Insert words
trie.insert("facebook");
trie.insert("face");
trie.insert("this");
trie.insert("there");
trie.insert("then");
// Print inserted words
trie.print();
// Check if these words
// are present or not
System.out.println(
trie.search("there"));
System.out.println(
trie.search("therein"));
System.out.println(
trie.startsWith("th"));
System.out.println(
trie.startsWith("fab"));
}
}
Python3
class TrieNode:
SYMBOLS = 26
def __init__(self, is_end=False):
self.children = [None] * self.SYMBOLS
self.edge_label = [None] * self.SYMBOLS
self.is_end = is_end
class Trie:
def __init__(self, case='a'):
self.root = TrieNode()
self.CASE = case
def insert(self, word):
trav = self.root
i = 0
while i < len(word) and trav.edge_label[ord(word[i]) - ord(self.CASE)] is not None:
index = ord(word[i]) - ord(self.CASE)
j = 0
label = trav.edge_label[index]
while j < len(label) and i < len(word) and label[j] == word[i]:
i += 1
j += 1
if j == len(label):
trav = trav.children[index]
else:
if i == len(word):
existing_child = trav.children[index]
new_child = TrieNode(is_end=True)
remaining_label = label[j:]
trav.children[index] = new_child
new_child.children[ord(remaining_label[0]) - ord(self.CASE)] = existing_child
new_child.edge_label[ord(remaining_label[0]) - ord(self.CASE)] = remaining_label
else:
remaining_label = label[j:]
new_child = TrieNode(is_end=False)
remaining_word = word[i:]
temp = trav.children[index]
trav.children[index] = new_child
new_child.edge_label[ord(remaining_label[0]) - ord(self.CASE)] = remaining_label
new_child.children[ord(remaining_label[0]) - ord(self.CASE)] = temp
new_child.edge_label[ord(remaining_word[0]) - ord(self.CASE)] = remaining_word
new_child.children[ord(remaining_word[0]) - ord(self.CASE)] = TrieNode(is_end=True)
return
if i < len(word):
trav.edge_label[ord(word[i]) - ord(self.CASE)] = word[i:]
trav.children[ord(word[i]) - ord(self.CASE)] = TrieNode(is_end=True)
else:
trav.is_end = True
def print_trie(self):
self._print_util(self.root, "")
def _print_util(self, node, string):
if node.is_end:
print(string)
for i in range(len(node.edge_label)):
if node.edge_label[i]:
temp = string + node.edge_label[i]
self._print_util(node.children[i], temp)
def search(self, word):
i = 0
trav = self.root
while i < len(word) and trav.edge_label[ord(word[i]) - ord(self.CASE)]:
index = ord(word[i]) - ord(self.CASE)
label = trav.edge_label[index]
j = 0
while i < len(word) and j < len(label):
if word[i] != label[j]:
return False
i += 1
j += 1
if j == len(label) and i <= len(word):
trav = trav.children[index]
else:
return False
return i == len(word) and trav.is_end
def starts_with(self, prefix):
i = 0
trav = self.root
while i < len(prefix) and trav.edge_label[ord(prefix[i]) - ord(self.CASE)]:
index = ord(prefix[i]) - ord(self.CASE)
label = trav.edge_label[index]
j = 0
while i < len(prefix) and j < len(label):
if prefix[i] != label[j]:
return False
i += 1
j += 1
if j == len(label) and i <= len(prefix):
trav = trav.children[index]
else:
return True
return i == len(prefix)
# Driver code
if __name__ == "__main__":
trie = Trie()
# Insert words
trie.insert("facebook")
trie.insert("face")
trie.insert("this")
trie.insert("there")
trie.insert("then")
# Print inserted words
trie.print_trie()
# Check if these words are present or not
print(trie.search("there"))
print(trie.search("therein"))
print(trie.starts_with("th"))
print(trie.starts_with("fab"))
JavaScript
// Trie class
class Trie {
constructor() {
// Root node
this.root = new Node(false);
// Case for characters ('a' or 'A' for lower or upper case)
this.CASE = 'a'; // Default case
}
// Function to insert a word into the compressed trie
insert(word) {
let trav = this.root;
let i = 0;
while (i < word.length && trav.edgeLabel[word.charCodeAt(i) - this.CASE.charCodeAt(0)] !== null) {
let index = word.charCodeAt(i) - this.CASE.charCodeAt(0);
let j = 0;
let label = trav.edgeLabel[index];
while (j < label.length && i < word.length && label.charAt(j) === word.charAt(i)) {
++i;
++j;
}
if (j === label.length) {
trav = trav.children[index];
} else {
if (i === word.length) {
let existingChild = trav.children[index];
let newChild = new Node(true);
let remainingLabel = label.substring(j);
label = label.substring(0, j);
trav.children[index] = newChild;
newChild.children[remainingLabel.charCodeAt(0) - this.CASE.charCodeAt(0)] = existingChild;
newChild.edgeLabel[remainingLabel.charCodeAt(0) - this.CASE.charCodeAt(0)] = remainingLabel;
} else {
let remainingLabel = label.substring(j);
let newChild = new Node(false);
let remainingWord = word.substring(i);
let temp = trav.children[index];
label = label.substring(0, j);
trav.children[index] = newChild;
newChild.children[remainingLabel.charCodeAt(0) - this.CASE.charCodeAt(0)] = temp;
newChild.edgeLabel[remainingLabel.charCodeAt(0) - this.CASE.charCodeAt(0)] = remainingLabel;
newChild.children[remainingWord.charCodeAt(0) - this.CASE.charCodeAt(0)] = new Node(true);
newChild.edgeLabel[remainingWord.charCodeAt(0) - this.CASE.charCodeAt(0)] = remainingWord;
}
return;
}
}
if (i < word.length) {
trav.edgeLabel[word.charCodeAt(i) - this.CASE.charCodeAt(0)] = word.substring(i);
trav.children[word.charCodeAt(i) - this.CASE.charCodeAt(0)] = new Node(true);
} else {
trav.isEnd = true;
}
}
// Function to print the trie
print() {
this.printUtil(this.root, '');
}
// Function to print the word starting from the given node
printUtil(node, str) {
if (node.isEnd) {
console.log(str);
}
for (let i = 0; i < node.edgeLabel.length; ++i) {
if (node.edgeLabel[i] !== null) {
let length = str.length;
str += node.edgeLabel[i];
this.printUtil(node.children[i], str);
str = str.substring(0, length);
}
}
}
// Function to search a word
search(word) {
let i = 0;
let trav = this.root;
while (i < word.length && trav.edgeLabel[word.charCodeAt(i) - this.CASE.charCodeAt(0)] !== null) {
let index = word.charCodeAt(i) - this.CASE.charCodeAt(0);
let label = trav.edgeLabel[index];
let j = 0;
while (i < word.length && j < label.length) {
if (word.charAt(i) !== label.charAt(j)) {
return false;
}
++i;
++j;
}
if (j === label.length && i <= word.length) {
trav = trav.children[index];
} else {
return false;
}
}
return i === word.length && trav.isEnd;
}
// Function to search the prefix
startsWith(prefix) {
let i = 0;
let trav = this.root;
while (i < prefix.length && trav.edgeLabel[prefix.charCodeAt(i) - this.CASE.charCodeAt(0)] !== null) {
let index = prefix.charCodeAt(i) - this.CASE.charCodeAt(0);
let label = trav.edgeLabel[index];
let j = 0;
while (i < prefix.length && j < label.length) {
if (prefix.charAt(i) !== label.charAt(j)) {
return false;
}
++i;
++j;
}
if (j === label.length && i <= prefix.length) {
trav = trav.children[index];
} else {
return true;
}
}
return i === prefix.length;
}
}
// Node class
class Node {
constructor(isEnd) {
// Number of symbols
const SYMBOLS = 26;
this.children = Array(SYMBOLS).fill(null);
this.edgeLabel = Array(SYMBOLS).fill(null);
this.isEnd = isEnd;
}
}
// Driver code
let trie = new Trie();
// Insert words
trie.insert("facebook");
trie.insert("face");
trie.insert("this");
trie.insert("there");
trie.insert("then");
// Print inserted words
trie.print();
// Check if these words are present or not
console.log(trie.search("there"));
console.log(trie.search("therein"));
console.log(trie.startsWith("th"));
console.log(trie.startsWith("fab"));
Outputface
facebook
then
there
this
true
false
true
false
Share your thoughts in the comments
Please Login to comment...