<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trie Autocompletado en JavaScript</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #1e1e2e;
color: #cdd6f4;
font-family: 'Segoe UI', system-ui, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
padding: 40px 20px;
}
h1 { font-size: 1.8rem; margin-bottom: 6px; color: #cba6f7; }
.subtitle { font-size: 0.85rem; color: #6c7086; margin-bottom: 30px; }
.search-wrap { position: relative; width: 100%; max-width: 420px; }
#input {
width: 100%;
padding: 14px 18px;
background: #313244;
border: 2px solid #45475a;
border-radius: 10px;
color: #cdd6f4;
font-size: 1.1rem;
outline: none;
transition: border-color 0.2s;
}
#input:focus { border-color: #cba6f7; }
#suggestions {
position: absolute;
top: calc(100% + 6px);
left: 0; right: 0;
background: #313244;
border: 1px solid #45475a;
border-radius: 10px;
overflow: hidden;
display: none;
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
z-index: 10;
}
#suggestions.visible { display: block; }
.suggestion {
padding: 12px 18px;
cursor: pointer;
font-size: 1rem;
border-bottom: 1px solid #45475a;
transition: background 0.1s;
}
.suggestion:last-child { border-bottom: none; }
.suggestion:hover, .suggestion.active { background: #45475a; }
.suggestion mark {
background: none;
color: #cba6f7;
font-weight: bold;
}
.stats {
margin-top: 14px;
font-size: 0.78rem;
color: #6c7086;
}
/* Panel de visualización del Trie */
#code-section {
margin-top: 50px;
width: 100%;
max-width: 700px;
background: #181825;
border-radius: 12px;
padding: 24px;
}
#code-section h2 { font-size: 1rem; color: #89b4fa; margin-bottom: 16px; }
pre {
font-family: 'Consolas', 'Fira Code', monospace;
font-size: 0.82rem;
color: #a6e3a1;
white-space: pre-wrap;
line-height: 1.6;
overflow: auto;
}
</style>
</head>
<body>
<h1>Trie · Autocompletado</h1>
<p class="subtitle">Implementación en JavaScript empieza a escribir</p>
<div class="search-wrap">
<input id="input" type="text" placeholder="Escribe una palabra..." autocomplete="off" spellcheck="false">
<div id="suggestions"></div>
</div>
<div class="stats" id="stats"></div>
<section id="code-section">
<h2>Implementación del Trie</h2>
<pre id="code-display"></pre>
</section>
<script>
// ???????????????????????????????????????????????????????????
// Implementación del Trie
// ???????????????????????????????????????????????????????????
class TrieNode {
constructor() {
this.children = {}; // char ? TrieNode
this.isEnd = false; // true si es final de palabra
}
}
class Trie {
constructor() {
this.root = new TrieNode();
this.wordCount = 0;
}
// Insertar una palabra
insert(word) {
word = word.toLowerCase();
let node = this.root;
for (const ch of word) {
if (!node.children[ch]) {
node.children[ch] = new TrieNode();
}
node = node.children[ch];
}
if (!node.isEnd) {
node.isEnd = true;
this.wordCount++;
}
}
// Buscar si la palabra exacta existe
search(word) {
const node = this._findNode(word.toLowerCase());
return node !== null && node.isEnd;
}
// ¿Existe alguna palabra que empiece por prefix?
startsWith(prefix) {
return this._findNode(prefix.toLowerCase()) !== null;
}
// Devuelve hasta `limit` palabras que empiecen por prefix
autocomplete(prefix, limit = 8) {
prefix = prefix.toLowerCase();
const node = this._findNode(prefix);
if (!node) return [];
const results = [];
this._dfs(node, prefix, results, limit);
return results;
}
// Navegar hasta el nodo del último carácter del prefix
_findNode(prefix) {
let node = this.root;
for (const ch of prefix) {
if (!node.children[ch]) return null;
node = node.children[ch];
}
return node;
}
// DFS desde un nodo acumulando palabras completas
_dfs(node, current, results, limit) {
if (results.length >= limit) return;
if (node.isEnd) results.push(current);
// Ordenar hijos para sugerencias alfabéticas
for (const ch of Object.keys(node.children).sort()) {
if (results.length >= limit) break;
this._dfs(node.children[ch], current + ch, results, limit);
}
}
}
// ???????????????????????????????????????????????????????????
// Diccionario (~100 palabras en español)
// ???????????????????????????????????????????????????????????
const WORDS = [
'abeja','abierto','abogado','abrazo','absoluto','abstracto','academia','acción',
'acento','acero','algoritmo','almacén','ambiente','amigo','amor','análisis',
'aplicación','archivo','arena','arte','base','biblioteca','bucle','caché',
'clase','clave','código','compilar','complejidad','comunicación','condición',
'consola','contenedor','contexto','control','cursor','datos','debug','decisión',
'definición','depuración','desarrollo','diccionario','diseño','docker','dominio',
'editor','elemento','enlace','entrada','entorno','error','estructura','evento',
'excepción','expresión','fichero','flujo','framework','función','generador',
'grafo','herencia','herramienta','imagen','implementar','índice','instancia',
'iteración','javascript','kernel','lenguaje','librería','lista','llamada',
'mapa','memoria','método','módulo','nodo','objeto','operador','parámetro',
'patrón','pila','polimorfismo','proceso','programa','protocolo','proyecto',
'python','queue','recursión','red','referencia','registro','rendimiento',
'repositorio','servidor','sistema','stack','string','tabla','tipo','token',
'variable','vector','versión','función','interfaz','módulo','clase','objeto'
];
const trie = new Trie();
WORDS.forEach(w => trie.insert(w));
// ???????????????????????????????????????????????????????????
// UI
// ???????????????????????????????????????????????????????????
const input = document.getElementById('input');
const suggestBox = document.getElementById('suggestions');
const statsEl = document.getElementById('stats');
let activeIndex = -1;
let currentSugs = [];
function highlight(word, prefix) {
if (!prefix) return word;
const idx = word.toLowerCase().indexOf(prefix.toLowerCase());
if (idx === -1) return word;
return word.slice(0, idx)
+ '<mark>' + word.slice(idx, idx + prefix.length) + '</mark>'
+ word.slice(idx + prefix.length);
}
function showSuggestions(prefix) {
const t0 = performance.now();
currentSugs = trie.autocomplete(prefix, 8);
const elapsed = (performance.now() - t0).toFixed(3);
statsEl.textContent = prefix
? `${currentSugs.length} sugerencia(s) para "${prefix}" · búsqueda en ${elapsed}ms · ${trie.wordCount} palabras indexadas`
: '';
if (!prefix || !currentSugs.length) {
suggestBox.classList.remove('visible');
return;
}
suggestBox.innerHTML = '';
activeIndex = -1;
currentSugs.forEach((word, i) => {
const div = document.createElement('div');
div.className = 'suggestion';
div.innerHTML = highlight(word, prefix);
div.addEventListener('mousedown', e => {
e.preventDefault();
input.value = word;
suggestBox.classList.remove('visible');
statsEl.textContent = `Seleccionado: "${word}"`;
});
div.addEventListener('mouseover', () => {
setActive(i);
});
suggestBox.appendChild(div);
});
suggestBox.classList.add('visible');
}
function setActive(idx) {
const items = suggestBox.querySelectorAll('.suggestion');
items.forEach(el => el.classList.remove('active'));
if (idx >= 0 && idx < items.length) {
items[idx].classList.add('active');
activeIndex = idx;
}
}
input.addEventListener('input', () => showSuggestions(input.value.trim()));
input.addEventListener('blur', () => setTimeout(() => suggestBox.classList.remove('visible'), 150));
input.addEventListener('focus', () => { if (input.value) showSuggestions(input.value.trim()); });
input.addEventListener('keydown', e => {
if (!suggestBox.classList.contains('visible')) return;
if (e.key === 'ArrowDown') {
e.preventDefault();
setActive(Math.min(activeIndex + 1, currentSugs.length - 1));
} else if (e.key === 'ArrowUp') {
e.preventDefault();
setActive(Math.max(activeIndex - 1, 0));
} else if (e.key === 'Enter' && activeIndex >= 0) {
e.preventDefault();
input.value = currentSugs[activeIndex];
suggestBox.classList.remove('visible');
statsEl.textContent = `Seleccionado: "${input.value}"`;
} else if (e.key === 'Escape') {
suggestBox.classList.remove('visible');
}
});
// Mostrar el código de la implementación en el panel inferior
document.getElementById('code-display').textContent = `class TrieNode {
constructor() {
this.children = {}; // char ? TrieNode
this.isEnd = false;
}
}
class Trie {
insert(word) { /* O(m) */ }
search(word) { /* O(m) */ }
startsWith(prefix) { /* O(m) */ }
autocomplete(prefix, limit = 8) {
const node = this._findNode(prefix);
if (!node) return [];
const results = [];
this._dfs(node, prefix, results, limit);
return results;
}
_dfs(node, current, results, limit) {
if (results.length >= limit) return;
if (node.isEnd) results.push(current);
for (const ch of Object.keys(node.children).sort()) {
this._dfs(node.children[ch], current + ch, results, limit);
}
}
}
// m = longitud de la palabra/prefijo
// Complejidad espacial: O(N·L) N palabras, L longitud media`;
</script>
</body>
</html>
Trie para autocompletado en JavaScript
Implementación de la estructura de datos Trie en JavaScript con insert, search, startsWith y autocomplete. Demo interactiva en el navegador: campo de texto con sugerencias en tiempo real sobre un vocabulario de palabras en español. Highlight de la parte escrita y máximo de 8 sugerencias.
Descargar adjuntos
COMPARTE ESTE TUTORIAL
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP