mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 04:44:04 +00:00
docs: add example code for Trie
This commit is contained in:
parent
e4ebf5fae1
commit
aab0c87da0
|
@ -112,15 +112,11 @@ export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNod
|
|||
* @example
|
||||
* // Find elements in a range
|
||||
* const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
||||
*
|
||||
* // Helper function to find elements in range
|
||||
* const findElementsInRange = (min: number, max: number): number[] => {
|
||||
* return bst.search(node => node.key >= min && node.key <= max, false, node => node.key);
|
||||
* };
|
||||
*
|
||||
* // Assertions
|
||||
* console.log(findElementsInRange(4, 12)); // [10, 5, 7, 12]
|
||||
* console.log(findElementsInRange(15, 20)); // [15, 18]
|
||||
* console.log(bst.search(new Range(5, 10))); // [10, 5, 7]
|
||||
* console.log(bst.search(new Range(4, 12))); // [10, 12, 5, 7]
|
||||
* console.log(bst.search(new Range(4, 12, true, false))); // [10, 5, 7]
|
||||
* console.log(bst.search(new Range(15, 20))); // [15, 18]
|
||||
* console.log(bst.search(new Range(15, 20, false))); // [18]
|
||||
* @example
|
||||
* // Find lowest common ancestor
|
||||
* const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
||||
|
|
|
@ -92,13 +92,106 @@ export class TrieNode {
|
|||
* 9. Spell Check: Checking the spelling of words.
|
||||
* 10. IP Routing: Used in certain types of IP routing algorithms.
|
||||
* 11. Text Word Frequency Count: Counting and storing the frequency of words in a large amount of text data.
|
||||
* @example
|
||||
* // Autocomplete: Prefix validation and checking
|
||||
* const autocomplete = new Trie<string>([
|
||||
* 'gmail.com',
|
||||
* 'gmail.co.nz',
|
||||
* 'gmail.co.jp',
|
||||
* 'yahoo.com',
|
||||
* 'outlook.com'
|
||||
* ]);
|
||||
*
|
||||
* // Get all completions for a prefix
|
||||
* const gmailCompletions = autocomplete.getWords('gmail');
|
||||
* console.log(gmailCompletions); // ['gmail.com','gmail.co.nz','gmail.co.jp']
|
||||
* @example
|
||||
* // File System Path Operations
|
||||
* const fileSystem = new Trie<string>([
|
||||
* '/home/user/documents/file1.txt',
|
||||
* '/home/user/documents/file2.txt',
|
||||
* '/home/user/pictures/photo.jpg',
|
||||
* '/home/user/pictures/vacation/',
|
||||
* '/home/user/downloads'
|
||||
* ]);
|
||||
*
|
||||
* // Find common directory prefix
|
||||
* console.log(fileSystem.getLongestCommonPrefix()); // '/home/user/'
|
||||
*
|
||||
* // List all files in a directory
|
||||
* const documentsFiles = fileSystem.getWords('/home/user/documents/');
|
||||
* console.log(documentsFiles); // ['/home/user/documents/file1.txt', '/home/user/documents/file2.txt']
|
||||
* @example
|
||||
* // Autocomplete: Basic word suggestions
|
||||
* // Create a trie for autocomplete
|
||||
* const autocomplete = new Trie<string>([
|
||||
* 'function',
|
||||
* 'functional',
|
||||
* 'functions',
|
||||
* 'class',
|
||||
* 'classes',
|
||||
* 'classical',
|
||||
* 'closure',
|
||||
* 'const',
|
||||
* 'constructor'
|
||||
* ]);
|
||||
*
|
||||
* // Test autocomplete with different prefixes
|
||||
* console.log(autocomplete.getWords('fun')); // ['functional', 'functions','function']
|
||||
* console.log(autocomplete.getWords('cla')); // ['classes', 'classical','class', ]
|
||||
* console.log(autocomplete.getWords('con')); // ['constructor', 'const']
|
||||
*
|
||||
* // Test with non-matching prefix
|
||||
* console.log(autocomplete.getWords('xyz')); // []
|
||||
* @example
|
||||
* // Dictionary: Case-insensitive word lookup
|
||||
* // Create a case-insensitive dictionary
|
||||
* const dictionary = new Trie<string>([], { caseSensitive: false });
|
||||
*
|
||||
* // Add words with mixed casing
|
||||
* dictionary.add('Hello');
|
||||
* dictionary.add('WORLD');
|
||||
* dictionary.add('JavaScript');
|
||||
*
|
||||
* // Test lookups with different casings
|
||||
* console.log(dictionary.has('hello')); // true
|
||||
* console.log(dictionary.has('HELLO')); // true
|
||||
* console.log(dictionary.has('Hello')); // true
|
||||
* console.log(dictionary.has('javascript')); // true
|
||||
* console.log(dictionary.has('JAVASCRIPT')); // true
|
||||
* @example
|
||||
* // IP Address Routing Table
|
||||
* // Add IP address prefixes and their corresponding routes
|
||||
* const routes = {
|
||||
* '192.168.1': 'LAN_SUBNET_1',
|
||||
* '192.168.2': 'LAN_SUBNET_2',
|
||||
* '10.0.0': 'PRIVATE_NETWORK_1',
|
||||
* '10.0.1': 'PRIVATE_NETWORK_2'
|
||||
* };
|
||||
*
|
||||
* const ipRoutingTable = new Trie<string>(Object.keys(routes));
|
||||
*
|
||||
* // Check IP address prefix matching
|
||||
* console.log(ipRoutingTable.hasPrefix('192.168.1')); // true
|
||||
* console.log(ipRoutingTable.hasPrefix('192.168.2')); // true
|
||||
*
|
||||
* // Validate IP address belongs to subnet
|
||||
* const ip = '192.168.1.100';
|
||||
* const subnet = ip.split('.').slice(0, 3).join('.');
|
||||
* console.log(ipRoutingTable.hasPrefix(subnet)); // true
|
||||
*/
|
||||
export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
||||
/**
|
||||
* The constructor function for the Trie class.
|
||||
* @param words: Iterable string Initialize the trie with a set of words
|
||||
* @param options?: TrieOptions Allow the user to pass in options for the trie
|
||||
* @return This
|
||||
* The constructor initializes a Trie data structure with optional options and words provided as
|
||||
* input.
|
||||
* @param {Iterable<string> | Iterable<R>} words - The `words` parameter in the constructor is an
|
||||
* iterable containing either strings or elements of type `R`. It is used to initialize the Trie with
|
||||
* a list of words or elements. If no `words` are provided, an empty iterable is used as the default
|
||||
* value.
|
||||
* @param [options] - The `options` parameter in the constructor is an optional object that can
|
||||
* contain configuration options for the Trie data structure. One of the options it can have is
|
||||
* `caseSensitive`, which is a boolean value indicating whether the Trie should be case-sensitive or
|
||||
* not. If `caseSensitive` is set to `
|
||||
*/
|
||||
constructor(words: Iterable<string> | Iterable<R> = [], options?: TrieOptions<R>) {
|
||||
super(options);
|
||||
|
@ -107,13 +200,7 @@ export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
|||
if (caseSensitive !== undefined) this._caseSensitive = caseSensitive;
|
||||
}
|
||||
if (words) {
|
||||
for (const word of words) {
|
||||
if (this.toElementFn) {
|
||||
this.add(this.toElementFn(word as R));
|
||||
} else {
|
||||
this.add(word as string);
|
||||
}
|
||||
}
|
||||
this.addMany(words);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,6 +262,30 @@ export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
|||
return isNewWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n * l)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `addMany` function in TypeScript takes an iterable of strings or elements of type R, converts
|
||||
* them using a provided function if available, and adds them to a data structure while returning an
|
||||
* array of boolean values indicating success.
|
||||
* @param {Iterable<string> | Iterable<R>} words - The `words` parameter in the `addMany` function is
|
||||
* an iterable that contains either strings or elements of type `R`.
|
||||
* @returns The `addMany` method returns an array of boolean values indicating whether each word in
|
||||
* the input iterable was successfully added to the data structure.
|
||||
*/
|
||||
addMany(words: Iterable<string> | Iterable<R> = []): boolean[] {
|
||||
const ans: boolean[] = [];
|
||||
for (const word of words) {
|
||||
if (this.toElementFn) {
|
||||
ans.push(this.add(this.toElementFn(word as R)));
|
||||
} else {
|
||||
ans.push(this.add(word as string));
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(l), where l is the length of the input word.
|
||||
* Space Complexity: O(1) - Constant space.
|
||||
|
|
|
@ -932,3 +932,154 @@ describe('Trie class', () => {
|
|||
expect(trieB.hasPrefix('ap')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Trie basic', () => {
|
||||
test('Dictionary: Basic word lookup functionality', () => {
|
||||
// Initialize a new Trie and add dictionary words
|
||||
const dictionary = new Trie<string>();
|
||||
const words = ['apple', 'app', 'application', 'approve', 'bread', 'break'];
|
||||
words.forEach(word => dictionary.add(word));
|
||||
|
||||
// Test exact word matches
|
||||
expect(dictionary.has('apple')).toBe(true);
|
||||
expect(dictionary.has('app')).toBe(true);
|
||||
expect(dictionary.has('bread')).toBe(true);
|
||||
|
||||
// Test non-existent words
|
||||
expect(dictionary.has('appl')).toBe(false);
|
||||
expect(dictionary.has('breaking')).toBe(false);
|
||||
|
||||
// Verify dictionary size
|
||||
expect(dictionary.size).toBe(words.length);
|
||||
});
|
||||
|
||||
test('Autocomplete: Limited suggestions with max results', () => {
|
||||
const autocomplete = new Trie<string>();
|
||||
|
||||
// Add city names
|
||||
const cities = ['New York', 'New Orleans', 'New Delhi', 'New Jersey', 'New Mexico', 'New Hampshire'];
|
||||
|
||||
cities.forEach(city => autocomplete.add(city));
|
||||
|
||||
// Get limited number of suggestions
|
||||
const maxSuggestions = 3;
|
||||
const suggestions = autocomplete.getWords('New', maxSuggestions);
|
||||
|
||||
expect(suggestions.length).toBe(maxSuggestions);
|
||||
suggestions.forEach(suggestion => {
|
||||
expect(suggestion.startsWith('New')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Dictionary: Word removal and updates', () => {
|
||||
const dictionary = new Trie<string>();
|
||||
|
||||
// Add initial words
|
||||
dictionary.add('delete');
|
||||
dictionary.add('deletion');
|
||||
dictionary.add('deleted');
|
||||
|
||||
// Verify initial state
|
||||
expect(dictionary.has('delete')).toBe(true);
|
||||
expect(dictionary.size).toBe(3);
|
||||
|
||||
// Remove a word
|
||||
const deleted = dictionary.delete('delete');
|
||||
expect(deleted).toBe(true);
|
||||
expect(dictionary.has('delete')).toBe(false);
|
||||
expect(dictionary.has('deletion')).toBe(true);
|
||||
expect(dictionary.has('deleted')).toBe(true);
|
||||
expect(dictionary.size).toBe(2);
|
||||
|
||||
// Try to remove non-existent word
|
||||
expect(dictionary.delete('notexist')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('classic use', () => {
|
||||
test('@example Autocomplete: Prefix validation and checking', () => {
|
||||
const autocomplete = new Trie<string>(['gmail.com', 'gmail.co.nz', 'gmail.co.jp', 'yahoo.com', 'outlook.com']);
|
||||
|
||||
// Get all completions for a prefix
|
||||
const gmailCompletions = autocomplete.getWords('gmail');
|
||||
expect(gmailCompletions).toEqual(['gmail.com', 'gmail.co.nz', 'gmail.co.jp']);
|
||||
});
|
||||
|
||||
test('@example File System Path Operations', () => {
|
||||
const fileSystem = new Trie<string>([
|
||||
'/home/user/documents/file1.txt',
|
||||
'/home/user/documents/file2.txt',
|
||||
'/home/user/pictures/photo.jpg',
|
||||
'/home/user/pictures/vacation/',
|
||||
'/home/user/downloads'
|
||||
]);
|
||||
|
||||
// Find common directory prefix
|
||||
expect(fileSystem.getLongestCommonPrefix()).toBe('/home/user/');
|
||||
|
||||
// List all files in a directory
|
||||
const documentsFiles = fileSystem.getWords('/home/user/documents/');
|
||||
expect(documentsFiles).toEqual(['/home/user/documents/file1.txt', '/home/user/documents/file2.txt']);
|
||||
});
|
||||
|
||||
test('@example Autocomplete: Basic word suggestions', () => {
|
||||
// Create a trie for autocomplete
|
||||
const autocomplete = new Trie<string>([
|
||||
'function',
|
||||
'functional',
|
||||
'functions',
|
||||
'class',
|
||||
'classes',
|
||||
'classical',
|
||||
'closure',
|
||||
'const',
|
||||
'constructor'
|
||||
]);
|
||||
|
||||
// Test autocomplete with different prefixes
|
||||
expect(autocomplete.getWords('fun')).toEqual(['functional', 'functions', 'function']);
|
||||
expect(autocomplete.getWords('cla')).toEqual(['classes', 'classical', 'class']);
|
||||
expect(autocomplete.getWords('con')).toEqual(['constructor', 'const']);
|
||||
|
||||
// Test with non-matching prefix
|
||||
expect(autocomplete.getWords('xyz')).toEqual([]);
|
||||
});
|
||||
|
||||
test('@example Dictionary: Case-insensitive word lookup', () => {
|
||||
// Create a case-insensitive dictionary
|
||||
const dictionary = new Trie<string>([], { caseSensitive: false });
|
||||
|
||||
// Add words with mixed casing
|
||||
dictionary.add('Hello');
|
||||
dictionary.add('WORLD');
|
||||
dictionary.add('JavaScript');
|
||||
|
||||
// Test lookups with different casings
|
||||
expect(dictionary.has('hello')).toBe(true);
|
||||
expect(dictionary.has('HELLO')).toBe(true);
|
||||
expect(dictionary.has('Hello')).toBe(true);
|
||||
expect(dictionary.has('javascript')).toBe(true);
|
||||
expect(dictionary.has('JAVASCRIPT')).toBe(true);
|
||||
});
|
||||
|
||||
test('@example IP Address Routing Table', () => {
|
||||
// Add IP address prefixes and their corresponding routes
|
||||
const routes = {
|
||||
'192.168.1': 'LAN_SUBNET_1',
|
||||
'192.168.2': 'LAN_SUBNET_2',
|
||||
'10.0.0': 'PRIVATE_NETWORK_1',
|
||||
'10.0.1': 'PRIVATE_NETWORK_2'
|
||||
};
|
||||
|
||||
const ipRoutingTable = new Trie<string>(Object.keys(routes));
|
||||
|
||||
// Check IP address prefix matching
|
||||
expect(ipRoutingTable.hasPrefix('192.168.1')).toBe(true);
|
||||
expect(ipRoutingTable.hasPrefix('192.168.2')).toBe(true);
|
||||
|
||||
// Validate IP address belongs to subnet
|
||||
const ip = '192.168.1.100';
|
||||
const subnet = ip.split('.').slice(0, 3).join('.');
|
||||
expect(ipRoutingTable.hasPrefix(subnet)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue