Cómo encontrar la ruta de longitud máxima en un árbol binario T

El camino de longitud máxima en un árbol también se conoce como su diámetro .

Uno de mis algoritmos favoritos le permite encontrar el diámetro de un árbol general, binario o no, y es increíblemente simple y elegante. Tampoco tiene que preocuparse por la eficiencia, ya que se ejecuta en tiempo lineal [matemático] O (N) [/ matemático], ¡lo cual es óptimo!

¿Entonces, cómo funciona?

  1. Elija un nodo arbitrario [math] v [/ math] y arraigue el árbol en ese nodo.
  2. Ejecute una búsqueda de profundidad primero desde [math] v [/ math], para encontrar el nodo [math] u [/ math] que tiene la mayor distancia desde la raíz.
  3. Reroot el árbol en [math] u [/ math].
  4. Ejecute otra búsqueda de profundidad primero desde [math] u [/ math], y nuevamente, encuentre el nodo [math] w [/ math] que está más alejado de [math] u [/ math].
  5. La ruta [math] u [/ math] – [math] w [/ math] es un diámetro del árbol.

¡Eso es! Simple, eficiente y fácil de codificar. Ahora, espera, porque sé que probablemente eres escéptico. ¿Por qué es correcto este algoritmo? Una prueba se puede encontrar aquí.

Finalmente, aquí hay una implementación de muestra en C ++, que se lee en el árbol en formato de lista de borde de la entrada estándar:

#include
#include
usando el espacio de nombres estándar;

#define MAXN 100013
int N;
vector adj [MAXN];
int parent [MAXN];
int d [MAXN];

dfs nulo (int n, int p = -1) {
padre [n] = p;
d [n] = (n == p? 0: 1) + d [p];
para (int v: adj [n]) {
si (v! = p) {
dfs (v, n);
}
}
}

int main () {
cin >> N;
para (int i = 0; i <N – 1; i ++) {
int a, b;
cin >> a >> b;
una-; si-;
adj [a] .push_back (b);
adj [b] .push_back (a);
}

dfs (0, 0);

int f = 0;
para (int i = 0; i <N; i ++) {
if (d [i]> d [f]) ​​{
f = i;
}
}

d [f] = 0;
dfs (f, f);

para (int i = 0; i <N; i ++) {
if (d [i]> d [f]) ​​{
f = i;
}
}

cout << d [f] << endl;
devuelve 0;
}

Llamemos a un camino “hacia abajo” en un árbol (binario) es un camino que comienza en algún vértice y va hacia abajo. Cada ruta en un árbol (binario) es una ruta “hacia abajo”, o una unión de 2 rutas “hacia abajo” comienza en un mismo vértice v y no tiene vértices comunes excepto v.

Para cada vértice v calcularemos la longitud de las rutas “descendentes” más largas que comienzan en v y luego bajan a su hijo izquierdo / derecho. Suma de ellos es la longitud del camino más largo que tiene v como el vértice más bajo (es decir, el más cercano a la raíz).

La siguiente idea anterior pseudoexpresa (con la suposición de que cada borde es de longitud 1):

max_length_path_on_tree = – infinito;

DFS nulo (vértice v) {
DFS (v-> left_child);
DFS (v-> right_child);
max_length_downward_path_go_left (v) = 1 + max (max_length_downward_path_go_left (v-> left_child), max_length_downward_path_go_right (v-> left_child));
// la longitud del camino descendente más largo comienza en v y luego va a su hijo izquierdo
max_length_downward_path_go_right (v) = 1 + max (max_length_downward_path_go_left (v-> right_child), max_length_downward_path_go_right (v-> right_child));
// la longitud del camino descendente más largo comienza en v y luego va a su hijo derecho
max_length_path (v) = max_length_downward_path_go_left (v) + max_length_downward_path_right_left (v);
// longitud de la ruta más larga que tiene x como vértice más bajo
max_length_path_on_tree = max (max_length_path_on_tree, max_length_path (v)); // actualiza la longitud de la ruta más larga en el árbol
}

DFS (T-> raíz); // inicia DFS en la raíz

Para obtener la ruta con esa longitud más larga, simplemente siga 2 rutas hacia abajo en un mismo vértice que sumen esa longitud más larga.