Download presentation
0
Honors Track: Competitive Programming & Problem Solving Tries
Frank Maurix
1
Contents What is a trie? When to use tries
Implementation and some operations Alternatives for implementation Compression Suffix tree
2
What is a trie? Data Structure Digital tree, radix tree, prefix tree Stores set of strings (dictionary) Characters as nodes Position reflects prefix represented
3
When to use tries Pros O(L) and O(L*A) operations Form of radix sort x for all words with same prefix Suffix tree Cons O(N*A) space complexity Horrible for floating point numbers Not a standard library N = Number of nodes L = Length of the word A = Size of the alphabet
4
Implementation Keep track of root Array of children
Store the number of children Alphabet = A, B,…, Z Uppercase only Change character into value 0,…,25 int c = someChar – 'A'; // - 'a' for lowercase
5
Implementation import java.util.*; public class ScaryProblem { TrieNode root; //Root of the trie void solve() { root = new TrieNode(null, false, null); //here is the place where you should do some magic with tries } public static void main(String[] args) { new ScaryProblem().solve(); class TrieNode { TrieNode[] children = new TrieNode[26]; Character ch; //last char of prefix, null for root TrieNode parent; //pointer to parent boolean inDictionary; //Prefix of this node in the dictionary? int nOC = 0; //Number of children TrieNode(Character ch, boolean used, TrieNode newParent) {...}
6
Operations Searching Insertion Word deletion Prefix deletion Retrieving in sorted order
7
Searching char[] word = {'N', 'A', 'S', 'A'}; root.search(word, 0); //Alternative: word as String and use word.charAt(index) class TrieNode { TrieNode search(char[] word, int index) { //index should be 0 on initial call if (index == word.length - 1) { //Node found or final node doesn’t exist return children[word[index] - 'A']; } else if (children[word[index] - 'A'] == null) { //Node doesn't exist return null; } else { //Keep searching return children[word[index] - 'A'].search(word, index + 1); }
8
Insertion Inserting nodes may be necessary, but doesn't need to be Example 1: inserting “SPACES”
9
Insertion Inserting nodes may be necessary, but doesn't need to be Example 1: inserting “SPACES”
10
Insertion Inserting nodes may be necessary, but doesn't need to be Example 1: inserting “SPACES”
11
Insertion Inserting nodes may be necessary, but doesn't need to be Example 1: inserting “SPACES”
12
Insertion Inserting nodes may be necessary, but doesn't need to be Example 1: inserting “SPACES”
13
Insertion Inserting nodes may be necessary, but doesn't need to be Example 1: inserting “SPACES”
14
Insertion Inserting nodes may be necessary, but doesn't need to be Example 2: inserting “NSA”
15
Insertion Inserting nodes may be necessary, but doesn't need to be Example 2: inserting “NSA”
16
Insertion Inserting nodes may be necessary, but doesn't need to be Example 2: inserting “NSA”
17
Insertion Inserting nodes may be necessary, but doesn't need to be Example 2: inserting “NSA”
18
Insertion Inserting nodes may be necessary, but doesn't need to be Example 2: inserting “NSA”
19
Insertion char[] word = {'N', 'S', 'A'}; root.insert(word, 0); class TrieNode { void insert(char[] word, int index) { //index 0 on initial call if (children[word[index]-'A'] == null) { //Next node doesn’t exist nOC++; if (index == word.length - 1) { children[word[index]-'A'] = new TrieNode(word[index], true, this); } else { children[word[index]-'A'] = new TrieNode(word[index], false, this); children[word[index]-'A'].insert(word, index + 1); } } else if (index == word.length - 1) { children[word[index] - 'A'].inDictionary = true; children[word[index] - 'A'].insert(word, index + 1);
20
Deletion Search for corresponding node
Set inDictionary for corresponding node to false If the node isn’t a leaf, you’re done Else, one or more nodes can be removed Removing the nodes Don’t delete the root If the current node is a leaf and not in the dictionary Remove the node Recursive call to the parent and repeat
21
Deletion Example 1: deleting “SPACES”
22
Deletion Example 1: deleting “SPACES”
23
Deletion Example 1: deleting “SPACES”
24
Deletion Example 2: deleting “NSA”
25
Deletion Example 2: deleting “NSA”
26
Deletion Example 2: deleting “NSA”
27
Deletion Example 2: deleting “NSA”
28
Deletion Example 2: deleting “NSA”
29
Deletion void removeWord(char[] word) { TrieNode result = root.search(word, 0); if (result == null) { //word not in trie } else if (result.nOC == 0) { //node is a leaf result.trieCleanup(); } else { //node isn’t a leaf result.inDictionary = false; } class TrieNode { void trieCleanup() { //Delete current node & check if parent should be deleted if (parent != null) { //Never delete the root parent.children[ch - 'A'] = null; parent.nOC--; if (parent.nOC == 0 && !parent.inDictionary) { parent.trieCleanup();
30
Prefix deletion Example: deleting all words with prefix ‘SPA’
31
Prefix deletion Example: deleting all words with prefix ‘SPA’
32
Prefix deletion Example: deleting all words with prefix ‘SPA’
33
Prefix deletion Example: deleting all words with prefix ‘SPA’
34
Prefix deletion Example: deleting all words with prefix ‘SPA’
35
Prefix deletion Example: deleting all words with prefix ‘SPA’
36
Prefix deletion void removePrefix(char[] word) { TrieNode result = root.search(word, 0); if (result != null) { result.trieCleanup(); }
37
Retrieving in alphabetical order
Pre-order tree traversal Only report prefixes in dictionary traverse(root); void traverse(TrieNode node) { if (node.inDictionary) { report(node); //Or other fancy stuff } for (int i = 0; i < 26; i++) { if (node.children[i] != null) { traverse(node.children[i]); To get prefix without too much extra time: When inserting, store prefix only for the node you insert (to stay in O(L)/O(A*L))
38
Alphabetic successor of a node
If node isn’t a leaf Find minimum on the subtree rooted at node Stop as soon as you find a word in the dictionary Else If parent has a node with index higher than index of the current, then find minimum like before on that node Else repeat for parent
39
Alternatives for storing children
A is the size of the alphabet L is the length of the word N is the number of nodes Operation Array HashMap LinkedList Insertion O(A . L) O(L)* O(A . L) sorted; O(L) unsorted Deletion O(L) O(L) (search already done) Search Trie traversal O(N)** O(N)** sorted, O(A . log(A) . N)** unsorted * : assuming simple uniform hashing * Expected O(L) time. Worst case O(A . L) ** Assuming report function takes O(1) time
40
Array vs HashMap vs LinkedList
+ Simple + Small constants + Good for simple alphabet - A pain with complex alphabet - Always a lot of space + Works with complex alphabet + Fast expected time - Worst case still slow - Worst case more space than array - Constants worse than array + Low space usage + Works with complex alphabet + Small constants - Very slow
41
Compression Merge nodes Adapt operations appropriately
Why compress: less data usage! How to adapt search?
42
Compression After deletion, more compression may be possible Insertion after compression: Possibly split a node, insert and compress
43
Suffix tree Take all suffixes of a word Insert all into a trie Offers many fast string operations Worst case O(L2) nodes All suffixes of BANANA BANANA ANANA NANA ANA NA A
44
Applications of suffix trees
Number of occurrences of a pattern in a text Search for the pattern, only consider that subtree Result = number of nodes in that subtree with inDictionary = true Longest Common Substring of two strings Insert both strings into a suffix tree For each node, store which strings represent them Find deepest node represented by both strings
45
Questions
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.