16-02-2026

árbol radix en php: cómo implementarlo

Despçues de ver lo que es un árbol radix vopy a poner un ejemplo con php.

class RadixNode {
public array $children = [];// string prefix => RadixNode
public mixed $value = null;// valor si termina una palabra aquí
public bool $isEndOfWord = false;
}

class RadixTree {
private RadixNode $root;

public function __construct() {
$this->root = new RadixNode();
}

public function insert(string $key, mixed $value = null): void {
$node = $this->root;
$i = 0;
$keyLength = strlen($key);

while ($i < $keyLength) {
  $matched = false;

  foreach ($node->children as $prefix => $child) {
  $common = $this->commonPrefixLength($key, $i, $prefix);

  if ($common > 0) {
 // Hay coincidencia parcial o total
 if ($common === strlen($prefix)) {
// El prefijo entero coincide → bajamos al hijo
$node = $child;
$i += $common;
 } else {
// Coincidencia parcial → partimos el nodo existente
$this->splitNode($node, $prefix, $common, $child);
// Ahora insertamos el resto en el nuevo nodo intermedio
$newNode = $node->children[substr($key, $i, $common)];
$newNode->children[substr($key, $i + $common)] = new RadixNode();
$node = $newNode->children[substr($key, $i + $common)];
$node->value = $value;
$node->isEndOfWord = true;
return;
 }
 $matched = true;
 break;
  }
  }

  if (!$matched) {
  // No hay coincidencia → nuevo hijo directo
  $remaining = substr($key, $i);
  $node->children[$remaining] = new RadixNode();
  $node = $node->children[$remaining];
  $node->value = $value;
  $node->isEndOfWord = true;
  return;
  }
}

// Llegamos al final de la clave
$node->value = $value;
$node->isEndOfWord = true;
}

private function commonPrefixLength(string $key, int $start, string $prefix): int {
$len = 0;
$max = min(strlen($key) - $start, strlen($prefix));
while ($len < $max && $key[$start + $len] === $prefix[$len]) {
  $len++;
}
return $len;
}

private function splitNode(RadixNode $parent, string $oldPrefix, int $splitAt, RadixNode $child): void {
$commonPart = substr($oldPrefix, 0, $splitAt);
$remainingOld = substr($oldPrefix, $splitAt);

// Creamos nodo intermedio
$midNode = new RadixNode();
$midNode->children[$remainingOld] = $child;

// Reemplazamos el viejo prefijo por el común
unset($parent->children[$oldPrefix]);
$parent->children[$commonPart] = $midNode;
}

public function search(string $key): mixed {
$node = $this->findNode($key);
return $node && $node->isEndOfWord ? $node->value : null;
}

public function startsWith(string $prefix): bool {
return $this->findNode($prefix) !== null;
}

public function getAllWithPrefix(string $prefix): array {
$node = $this->findNode($prefix);
if (!$node) return [];

$results = [];
$this->collectAll($node, $prefix, $results);
return $results;
}

private function findNode(string $key): ?RadixNode {
$node = $this->root;
$i = 0;
$len = strlen($key);

while ($i < $len) {
  $matched = false;
  foreach ($node->children as $p => $child) {
  $common = $this->commonPrefixLength($key, $i, $p);
  if ($common > 0) {
 if ($common < strlen($p)) {
// No coincide todo el prefijo → no existe
return null;
 }
 $node = $child;
 $i += $common;
 $matched = true;
 break;
  }
  }
  if (!$matched) return null;
}
return $node;
}

private function collectAll(RadixNode $node, string $current, array &$results): void {
if ($node->isEndOfWord) {
  $results[$current] = $node->value;
}
foreach ($node->children as $prefix => $child) {
  $this->collectAll($child, $current . $prefix, $results);
}
}
}

// ---------------------- EJEMPLO DE USO ----------------------
$tree = new RadixTree();

$tree->insert("gato", "animal felino");
$tree->insert("gatito", "bebé gato");
$tree->insert("gata", "hembra del gato");
$tree->insert("perro", "mejor amigo del hombre");
$tree->insert("perrito", "cachorro");

echo "Búsqueda exacta:\n";
var_dump($tree->search("gato")); // "animal felino"
var_dump($tree->search("gat"));  // null

echo "\nPrefijos:\n";
var_dump($tree->startsWith("gat"));// true
var_dump($tree->startsWith("gatote")); // false

echo "\nTodas las palabras con prefijo 'gat':\n";
print_r($tree->getAllWithPrefix("gat"));
// Muestra: gato, gatito, gata
  • Esta versión es case-sensitive (distingue mayúsculas/minúsculas). PAra insensitive, convertir todo a minúsculas antes.
  • Para un router HTTP real, muchos usan variantes con parámetros (/users/{id}) → ahí se necesitan extensiones (wildcards, etc.).
  • Librerías recomendadas: https://github.com/MarkBaker/Tries (Trie + RadixTrie), https://github.com/Wilaak/RadixRouter.