In JavaScript, search algorithms are commonly used for searching and processing large datasets efficiently. Two examples of efficient search algorithms are the trie and the suffix tree.
A trie, also known as a prefix tree, is a tree-like data structure used for efficient retrieval of keys from a large set of strings. In a trie, each node represents a prefix of one or more strings. The root node represents an empty string, and each child node represents a single character. By traversing the tree, one can retrieve all strings that start with a given prefix. Tries can be used for a wide range of applications, such as autocompletion and spell checking.
Here is an example implementation of a trie in JavaScript:
class TrieNode {
constructor() {
this.children = {};
this.isEndOfWord = false;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let node = this.root;
for (let i = 0; i < word.length; i++) {
const char = word[i];
if (!node.children[char]) {
node.children[char] = new TrieNode();
}
node = node.children[char];
}
node.isEndOfWord = true;
}
search(word) {
let node = this.root;
for (let i = 0; i < word.length; i++) {
const char = word[i];
if (!node.children[char]) {
return false;
}
node = node.children[char];
}
return node.isEndOfWord;
}
}
A suffix tree, on the other hand, is a tree-like data structure used for efficient substring searching in a larger string. In a suffix tree, each leaf node represents a suffix of the larger string, and each internal node represents a substring that is a concatenation of its children nodes. Suffix trees can be used for a wide range of applications, such as finding the longest repeated substring, finding the longest palindrome, and constructing a generalized suffix tree for multiple strings.
Here is an example implementation of a suffix tree in JavaScript using Ukkonen’s algorithm:
class SuffixTreeNode {
constructor(start, end) {
this.start = start;
this.end = end;
this.children = {};
this.link = null;
}
get length() {
return this.end - this.start;
}
addChild(node, text) {
this.children[text[node.start]] = node;
}
getChild(char) {
return this.children[char];
}
hasChild(char) {
return this.children[char] !== undefined;
}
}
class SuffixTree {
constructor(text) {
this.text = text;
this.root = new SuffixTreeNode(-1, -1);
this.root.link = this.root;
let activeNode = this.root;
let activeLength = 0;
this.remainingSuffixCount = 0;
this.lastNewNode = null;
for (let i = 0; i < text.length; i++) {
this.extend(i, activeNode, activeLength);
}
}
extend(i, activeNode, activeLength) {
let lastNewNode = null;
this.remainingSuffixCount++;
while (this.remainingSuffixCount > 0) {
if (activeLength === 0) {
this.activeEdge = i;
}
if (!activeNode.hasChild(this.text[this.activeEdge])) {
const newNode = new SuffixTreeNode(i, this.text.length);
activeNode.addChild(newNode, this.text);
if (lastNewNode !== null) {
lastNewNode.link = activeNode
}
}
}
}
}