Deletion in Trie Data Structure

This operation is used to delete strings from the Trie data structure. There are three cases when deleting a word from Trie.

  1. The deleted word is a prefix of other words in Trie.
  2. The deleted word shares a common prefix with other words in Trie.
  3. The deleted word does not share any common prefix with other words in Trie.

3.1 The deleted word is a prefix of other words in Trie.

As shown in the following figure, the deleted word “an” share a complete prefix with another word “and” and “ant“.

Deletion of word which is a prefix of other words in Trie


An easy solution to perform a delete operation for this case is to just decrement the wordCount by 1 at the ending node of the word.

3.2 The deleted word shares a common prefix with other words in Trie.

As shown in the following figure, the deleted word “and” has some common prefixes with other words ‘ant’. They share the prefix ‘an’.

Deletion of word which shares a common prefix with other words in Trie


The solution for this case is to delete all the nodes starting from the end of the prefix to the last character of the given word.

3.3 The deleted word does not share any common prefix with other words in Trie.

As shown in the following figure, the word “geek” does not share any common prefix with any other words.

The solution for this case is just to delete all the nodes.

Below is the implementation that handles all the above cases:

C++
bool delete_key(TrieNode* root, string& word)
{
    TrieNode* currentNode = root;
    TrieNode* lastBranchNode = NULL;
    char lastBrachChar = 'a';

    for (auto c : word) {
        if (currentNode->childNode[c - 'a'] == NULL) {
            return false;
        }
        else {
            int count = 0;
            for (int i = 0; i < 26; i++) {
                if (currentNode->childNode[i] != NULL)
                    count++;
            }

            if (count > 1) {
                lastBranchNode = currentNode;
                lastBrachChar = c;
            }
            currentNode = currentNode->childNode[c - 'a'];
        }
    }

    int count = 0;
    for (int i = 0; i < 26; i++) {
        if (currentNode->childNode[i] != NULL)
            count++;
    }

    // Case 1: The deleted word is a prefix of other words
    // in Trie.
    if (count > 0) {
        currentNode->wordCount--;
        return true;
    }

    // Case 2: The deleted word shares a common prefix with
    // other words in Trie.
    if (lastBranchNode != NULL) {
        lastBranchNode->childNode[lastBrachChar] = NULL;
        return true;
    }
    // Case 3: The deleted word does not share any common
    // prefix with other words in Trie.
    else {
        root->childNode[word[0]] = NULL;
        return true;
    }
}
Java
public class TrieNode {
    TrieNode[] childNode;
    int wordCount;

    public TrieNode() {
        this.childNode = new TrieNode[26];
        this.wordCount = 0;
    }
}

public class Trie {
    TrieNode root;

    public Trie() {
        this.root = new TrieNode();
    }

    public boolean deleteKey(String word) {
        TrieNode currentNode = root;
        TrieNode lastBranchNode = null;
        char lastBranchChar = 'a';

        for (char c : word.toCharArray()) {
            if (currentNode.childNode[c - 'a'] == null) {
                // If the current node has no child, the word is not present
                return false;
            } else {
                int count = 0;
                // Count the number of non-null child nodes
                for (int i = 0; i < 26; i++) {
                    if (currentNode.childNode[i] != null)
                        count++;
                }

                if (count > 1) {
                    // If there are more than one child, store the last branch information
                    lastBranchNode = currentNode;
                    lastBranchChar = c;
                }
                currentNode = currentNode.childNode[c - 'a'];
            }
        }

        int count = 0;
        // Count the number of non-null child nodes at the last character
        for (int i = 0; i < 26; i++) {
            if (currentNode.childNode[i] != null)
                count++;
        }

        // Case 1: The deleted word is a prefix of other words in Trie.
        if (count > 0) {
            // Decrement the word count and indicate successful deletion
            currentNode.wordCount--;
            return true;
        }

        // Case 2: The deleted word shares a common prefix with other words in Trie.
        if (lastBranchNode != null) {
            // Remove the link to the deleted word
            lastBranchNode.childNode[lastBranchChar - 'a'] = null;
            return true;
        }
        // Case 3: The deleted word does not share any common prefix with other words in Trie.
        else {
            // Remove the link to the deleted word from the root
            root.childNode[word.charAt(0) - 'a'] = null;
            return true;
        }
    }
}
Python3
def delete_key(root, word):
    current_node = root
    last_branch_node = None
    last_branch_char = 'a'

    # loop through each character in the word
    for c in word:
        # if the current node doesn't have a child with the current character,
        # return False as the word is not present in Trie
        if current_node.childNode[ord(c) - ord('a')] is None:
            return False
        else:
            count = 0
            # count the number of children nodes of the current node
            for i in range(26):
                if current_node.childNode[i] is not None:
                    count += 1

            # if the count of children is more than 1,
            # store the node and the current character
            if count > 1:
                last_branch_node = current_node
                last_branch_char = c

            current_node = current_node.childNode[ord(c) - ord('a')]

    count = 0
    # count the number of children nodes of the current node
    for i in range(26):
        if current_node.childNode[i] is not None:
            count += 1

    # Case 1: The deleted word is a prefix of other words in Trie
    if count > 0:
        current_node.wordCount -= 1
        return True

    # Case 2: The deleted word shares a common prefix with other words in Trie
    if last_branch_node is not None:
        last_branch_node.childNode[ord(last_branch_char) - ord('a')] = None
        return True

    # Case 3: The deleted word does not share any common prefix with other words in Trie
    else:
        root.childNode[ord(word[0]) - ord('a')] = None
        return True
C#
public bool delete_key(TrieNode root, string word)
{
    TrieNode current_node = root;
    TrieNode last_branch_node = null;
    char last_branch_char = 'a';

    // loop through each character in the word
    foreach (char c in word)
    {
        // if the current node doesn't have a child with the current character,
        // return False as the word is not present in Trie
        if (current_node.childNode[c - 'a'] == null)
        {
            return false;
        }
        else
        {
            int count = 0;
            // count the number of children nodes of the current node
            for (int i = 0; i < 26; i++)
            {
                if (current_node.childNode[i] != null)
                {
                    count++;
                }
            }

            // if the count of children is more than 1,
            // store the node and the current character
            if (count > 1)
            {
                last_branch_node = current_node;
                last_branch_char = c;
            }

            current_node = current_node.childNode[c - 'a'];
        }
    }

    int wordCount = 0;
    // count the number of children nodes of the current node
    for (int i = 0; i < 26; i++)
    {
        if (current_node.childNode[i] != null)
        {
            wordCount++;
        }
    }

    // Case 1: The deleted word is a prefix of other words in Trie
    if (wordCount > 0)
    {
        current_node.wordCount--;
        return true;
    }

    // Case 2: The deleted word shares a common prefix with other words in Trie
    if (last_branch_node != null)
    {
        last_branch_node.childNode[last_branch_char - 'a'] = null;
        return true;
    }

    // Case 3: The deleted word does not share any common prefix with other words in Trie
    else
    {
        root.childNode[word[0] - 'a'] = null;
        return true;
    }
}

//This code is contributed by shivamsharma215
Javascript
function delete_key(root, word) {
    let currentNode = root;
    let lastBranchNode = null;
    let lastBrachChar = 'a';

    for (let c of word) {
        if (currentNode.childNode[c.charCodeAt(0) - 'a'.charCodeAt(0)] === null) {
            return false;
        } else {
            let count = 0;
            for (let i = 0; i < 26; i++) {
                if (currentNode.childNode[i] !== null) {
                    count++;
                }
            }

            if (count > 1) {
                lastBranchNode = currentNode;
                lastBrachChar = c;
            }
            currentNode = currentNode.childNode[c.charCodeAt(0) - 'a'.charCodeAt(0)];
        }
    }

    let count = 0;
    for (let i = 0; i < 26; i++) {
        if (currentNode.childNode[i] !== null) {
            count++;
        }
    }

    // Case 1: The deleted word is a prefix of other words
    // in Trie.
    if (count > 0) {
        currentNode.wordCount--;
        return true;
    }

    // Case 2: The deleted word shares a common prefix with
    // other words in Trie.
    if (lastBranchNode !== null) {
        lastBranchNode.childNode[lastBrachChar] = null;
        return true;
    }
    // Case 3: The deleted word does not share any common
    // prefix with other words in Trie.
    else {
        root.childNode[word[0].charCodeAt(0) - 'a'.charCodeAt(0)] = null;
        return true;
    }
}

//This code is contributed by shivamsharma215

Trie Data Structure Tutorial

The trie data structure, also known as a prefix tree, is a tree-like data structure used for efficient retrieval of key-value pairs. It is commonly used for implementing dictionaries and autocomplete features, making it a fundamental component in many search algorithms. In this article, we will explore all about Trie data structures in detail.

Trie Data Structure

Table of Content

  • What is Trie Data Structure?
  • What is need of Trie Data Structure?
  • Advantages of Trie Data Structure over a Hash Table
  • Properties of a Trie Data Structure
  • How does Trie Data Structure work?
  • Representation of Trie Node
  • Basic Operations on Trie Data Structure
  • Insertion in Trie Data Structure
  • Searching in Trie Data Structure
  • Deletion in Trie Data Structure
  • Implement Trie Data Structure?
  • Complexity Analysis of Trie Data Structure
  • Applications of Trie data structure
  • Advantages of Trie data structure
  • Disadvantages of Trie data structure
  • Top Interview problems on Trie data structure

Similar Reads

What is Trie Data Structure?

Trie data structure is defined as a Tree based data structure that is used for storing some collection of strings and performing efficient search operations on them. The word Trie is derived from reTRIEval, which means finding something or obtaining it....

What is need of Trie Data Structure?

A Trie data structure is used for storing and retrieval of data and the same operations could be done using another data structure which is Hash Table but Trie data structure can perform these operations more efficiently than a Hash Table. Moreover, Trie has its own advantage over the Hash table. A Trie data structure can be used for prefix-based searching whereas a Hash table can’t be used in the same way....

Advantages of Trie Data Structure over a Hash Table:

The A trie data structure has the following advantages over a hash table:...

Properties of a Trie Data Structure

Below are some important properties of the Trie data structure:...

How does Trie Data Structure work?

Trie data structure can contain any number of characters including alphabets, numbers, and special characters. But for this article, we will discuss strings with characters a-z. Therefore, only 26 pointers need for every node, where the 0th index represents ‘a’ and the 25th index represents ‘z’ characters....

Representation of Trie Node:

Every Trie node consists of a character pointer array or hashmap and a flag to represent if the word is ending at that node or not. But if the words contain only lower-case letters (i.e. a-z), then we can define Trie Node with an array instead of a hashmap....

Basic Operations on Trie Data Structure:

InsertionSearchDeletion...

1. Insertion in Trie Data Structure:

This operation is used to insert new strings into the Trie data structure. Let us see how this works:...

2. Searching in Trie Data Structure:

Search operation in Trie is performed in a similar way as the insertion operation but the only difference is that whenever we find that the array of pointers in curr node does not point to the current character of the word then return false instead of creating a new node for that current character of the word....

3. Deletion in Trie Data Structure

This operation is used to delete strings from the Trie data structure. There are three cases when deleting a word from Trie....

Implement Trie Data Structure?

Algorithm:...

Complexity Analysis of Trie Data Structure

OperationTime ComplexityAuxiliary SpaceInsertionO(n)O(n*m)SearchingO(n)O(1)DeletionO(n)O(1)...

Applications of Trie data structure:

1. Autocomplete Feature: Autocomplete provides suggestions based on what you type in the search box. Trie data structure is used to implement autocomplete functionality....

Advantages of Trie data structure:

Trie allows us to input and finds strings in O(l) time, where l is the length of a single word. It is faster as compared to both hash tables and binary search trees.It provides alphabetical filtering of entries by the key of the node and hence makes it easier to print all words in alphabetical order.Trie takes less space when compared to BST because the keys are not explicitly saved instead each key requires just an amortized fixed amount of space to be stored.Prefix search/Longest prefix matching can be efficiently done with the help of trie data structure.Since trie doesn’t need any hash function for its implementation so they are generally faster than hash tables for small keys like integers and pointers.Tries support ordered iteration whereas iteration in a hash table will result in pseudorandom order given by the hash function which is usually more cumbersome.Deletion is also a straightforward algorithm with O(l) as its time complexity, where l is the length of the word to be deleted....

Disadvantages of Trie data structure:

The main disadvantage of the trie is that it takes a lot of memory to store all the strings. For each node, we have too many node pointers which are equal to the no of characters in the worst case.An efficiently constructed hash table(i.e. a good hash function and a reasonable load factor) has O(1) as lookup time which is way faster than O(l) in the case of a trie, where l is the length of the string....

Top Interview problems on Trie data structure:

S.noProblemPractice1Implement Trie (Prefix Tree)Link2Word Break ProblemLink3BoggleLink4Longest Common Prefix using TrieLink5Find the maximum subarray XOR in a given arrayLink6Count of distinct substrings of a stringLink7Find shortest unique prefix for every word in a given list Link8Count inversions in an arrayLink...

Frequently asked questions (FAQs) about Trie Data Structure:

Is trie an advanced data structure?...

Conclusion:

Our discussion so far has led us to the conclusion that the Trie data structure is a Tree based data structure that is used for storing some collection of strings and performing efficient search operations on them and we have also discussed the various advantage and applications of trie data structure....