Difference between Tries and Trees?

Updated: 2024-03-12
3 min read
[]

Despite their similar names, these structures serve different purposes, and understanding their differences is crucial to utilizing them effectively.

Tree

A tree data structure is a collection of entities, called nodes, connected by edges.

Each node contains a value, and a list of references to its child nodes. The first node of the tree is called the root. If we visualize it, a tree data structure resembles an inverted tree, with the root at the top and the leaves (nodes without children) at the bottom.

Trees are hierarchical, non-linear data structures.

They are excellent for representing relationships between objects, and their operations usually have a logarithmic time complexity, making them efficient for search operations.

Let’s create a simple binary tree in Python, where each node can have at most two children:

class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

root = Node(1)
root.left = Node(2)
root.right = Node(3)

Here, we have a tree with the root node storing the value 1. The root node has two children: the left child stores the value 2, and the right child stores the value 3.

Trie

A trie, also known as a prefix tree, is a type of tree that specializes in managing sequences, typically strings. In a trie, every node (except the root) corresponds to a character or a string, and each path down the tree can represent a word or a prefix.

The key characteristic of tries is that they provide a fast retrieval of data. They can check if a word or prefix exists in a dataset in O(M) time, where M is the length of the word.

Here’s a simple Python example of a trie data structure:

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

class Trie:
    def __init__(self):
        self.root = TrieNode()

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

In this example, each node in the trie has a dictionary called children to keep references to its child nodes. The end_of_string flag helps determine if the current concatenation of characters forms a valid word.

Tries vs Trees

Despite their shared properties (being tree-based structures), tries and trees are designed for different use cases.

Data Storage: A general-purpose tree can store any data type—numbers, strings, objects, whereas a trie is specifically used for storing sequences, like strings or arrays.

Node Value: In a tree, each node holds a value. In a trie, nodes themselves don’t hold a value—instead, the value is the path from the root to that node.

Efficiency: Tries are incredibly efficient when it comes to searching for a word or prefix in a dictionary. Trees, on the other hand, are more efficient for a wide range of operations, like searching, inserting, and deleting arbitrary values.

Memory Usage: Tries can use more memory because of references in each node, especially when dealing with a large alphabet. Each node in a trie maintains a collection (often a dictionary or array) of all its child nodes. However, in a binary tree, each node only needs to keep a reference to at most two child nodes.

Lookup Time: Tries have a faster lookup time for certain tasks. For instance, finding a word in a trie takes O(M) time, where M is the length of the word. For a balanced binary search tree, the time complexity would be O(log N), where N is the number of elements in the tree.