Hackernoon how to implement trie (prefix tree) – blind 75 leetcode questions

In the realm of data structures and algorithms, the Trie, also known as a Prefix Tree, stands out as a powerful and efficient tool for various applications. In this article, we’ll delve deep into the implementation of Trie, exploring its structure, functionalities, and practical use cases. Moreover, we’ll tackle 75 LeetCode questions related to Trie, ensuring you’re well-equipped to handle blind challenges.

Understanding Trie

An Overview

Before we dive into the intricacies of Trie implementation, let’s establish a solid understanding of what a Trie is and why it’s a valuable data structure.

What is a Trie?

A Trie, short for retrieval tree or prefix tree, is a tree-like data structure that is used to store an associative array where keys are usually strings. Unlike other data structures like arrays, linked lists, or binary search trees, Tries are specifically designed for handling strings efficiently.

Structure of a Trie

A Trie comprises nodes, each representing a single character of a string. The nodes are connected in a hierarchical fashion, forming a tree structure. The root node represents an empty string, and each subsequent node corresponds to a character in the string.

Advantages of Using a Trie

Tries offer several advantages, making them suitable for various applications

Efficient Search Operations

Tries provide quick and efficient search operations, especially when dealing with large datasets.

Prefix Matching

Tries excel in prefix matching, making them ideal for autocomplete and dictionary applications.

Space Efficiency

Despite their apparent overhead, Tries can be space-efficient, especially when storing a large number of strings with common prefixes.

Dynamic Operations

Tries support dynamic operations like insertion and deletion efficiently.

Implementing a Trie

Step by Step

Now that we have a foundational understanding of Trie, let’s move on to the practical aspects of implementing it. We’ll break down the process into manageable steps to ensure clarity.

Define the TrieNode Class

python
class TrieNode:
def __init__(self):
self.children = {}
self.is_end_of_word = False

The TrieNode class represents a single node in the Trie. It contains a dictionary (children) to store links to the next level of nodes and a boolean flag (is_end_of_word) to indicate whether the current node represents the end of a word.

Initialize the Trie

python
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
# Implementation of insertion will be covered in the next step
pass

The Trie class initializes with a root node. We will now focus on the insert method, responsible for adding words to the Trie.

Insertion Operation

python
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end_of_word = True

The insert method iterates through each character of the word and creates nodes as needed. The last node is marked as the end of the word.

Search Operation

python
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end_of_word

The search method checks if a given word exists in the Trie. It returns True if the word is present and False otherwise.

Prefix Matching

python
def starts_with_prefix(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True

The starts_with_prefix method checks if there are any words in the Trie that start with a given prefix.

Practical Application

Blind 75 LeetCode Questions

With the basics of Trie implementation covered, let’s take our understanding to the next level by solving 75 LeetCode questions related to Trie. These questions will not only test your knowledge but also enhance your problem-solving skills.

LeetCode Question 1

Implement Trie (Prefix Tree)

Problem Statement: Implement a Trie with insert, search, and startsWith methods.

Solution

python
# Implementation of Trie with insert, search, and starts_with_prefix methods
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end_of_word = True

def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end_of_word

def starts_with_prefix(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True

This solution demonstrates the implementation of a Trie class with the required methods.

Word Search II

Problem Statement: Given a 2D board and a list of words from the dictionary, find all words in the board.

Solution

python
def find_words(board, words):
trie = Trie()
for word in words:
trie.insert(word)
result = set()

def dfs(node, i, j, path):
char = board[i][j]
curr_node = node.children.get(char, None)

if not curr_node:
return

path += char
if curr_node.is_end_of_word:
result.add(path)

board[i][j] = “#” # Mark the cell as visited

# Explore neighbors
directions = [(-1, 0), (1, 0), (0, –1), (0, 1)]
for di, dj in directions:
ni, nj = i + di, j + dj
if 0 <= ni < len(board) and 0 <= nj < len(board[0]) and board[ni][nj] != “#”:
dfs(curr_node, ni, nj, path)

board[i][j] = char # Revert the cell to its original state

for i in range(len(board)):
for j in range(len(board[0])):
dfs(trie.root, i, j, “”)

return list(result)

This solution uses Trie to efficiently search for words in a 2D board.

Longest Word in Dictionary

Problem Statement

Given a list of words, find the longest word made of other words in the list.

Solution

python
def longest_word(words):
trie = Trie()
for word in sorted(words):
if trie.starts_with_prefix(word[:-1]):
trie.insert(word)
result = “”

Leave a Reply

Your email address will not be published. Required fields are marked *