Un método para encontrar el ancestro común más bajo (LCA) de dos nodos en un árbol enraizado es almacenando el [matemático] 2 ^ k [/ matemático] th padre de cada vértice.
Supongamos que para cada nodo en el árbol, conocemos dos cantidades:
- La profundidad del nodo (es decir, la distancia del nodo desde la raíz)
- El [matemático] 2 ^ k [/ matemático] th padre del nodo para todos [matemático] k [/ matemático] hasta [matemático] \ log N [/ matemático]
La profundidad y el [matemático] 2 ^ 0 [/ matemático] th padre se pueden calcular fácilmente usando un dfs:
- ¿Dónde se puede encontrar una foto y detalles biográficos de Burton Howard Bloom, inventor del filtro Bloom?
- ¿Qué sabes sobre el algoritmo de búsqueda de Fiverr?
- Crear un algoritmo simple me lleva horas, ¿cómo puedo ser más rápido?
- Cómo encontrar factorial de un número en O (logn) complejidad de tiempo
- ¿Qué algoritmos se utilizan para el proceso de aprendizaje automático de Yandex?
char seen[NMAX]; int depth[NMAX]; int parent[LGNMAX][NMAX]; vector adjlist[NMAX]; memset(seen, 0, sizeof seen); memset(parent, -1, sizeof parent); void dfs(int u) { seen[u] = 1; for (int i = 0; i < adjlist[u].size(); ++i) { int v = adjlist[u][i]; if (seen[v]) continue; depth[v] = depth[u] + 1; parent[0][v] = u; dfs(v); } }
Una vez que se encuentra el [math] 2 ^ 0 [/ math] th parent, el resto de la matriz padre se puede completar fácilmente.
for (int i = 1; i < LGNMAX; ++i) { for (int j = 0; j < N; ++j) { if (parent[i - 1][j] != -1) { parent[i][j] = parent[i - 1][parent[i - 1][j]]; } } }
Explicaré cómo encontrar el LCA usando estas cantidades en [math] O (\ log N) [/ math] por consulta. Como ejemplo, considere el siguiente gráfico donde queremos encontrar el LCA de los vértices [matemática] u = 8 [/ matemática] y [matemática] v = 9 [/ matemática].
Lo primero a tener en cuenta es que la profundidad del LCA será menor o igual que la profundidad de ambos vértices. Por lo tanto, si las profundidades de los dos vértices no son iguales, podemos igualarlos subiendo el árbol de ascendencia del vértice adicional tanto como sea necesario. Podemos usar la
parent[][]
para hacer esto en el tiempo [matemático] O (\ log N) [/ matemático], esencialmente mirando la representación binaria de la altura que tenemos que saltar:
int getparent(int u, int k) { for (int i = 0; i >= 1; } return u; }
En este caso, subimos a 7 de 9.
En esta etapa, si los dos vértices considerados son los mismos, ya hemos terminado (habría sido el caso si empezáramos con 7 y 9, por ejemplo). Si no, necesitamos encontrar el número más pequeño [matemática] x [/ matemática] de tal manera que la [matemática] x [/ matemática] th padre de [matemática] u [/ matemática] sea la misma que la [matemática] x [ / math] th padre de [math] v [/ math].
Para hacer esto, podemos usar la parent[][]
nuevamente. Encuentre el valor más grande de [math] k [/ math] de modo que [math] 2 ^ k [/ math] th padre de [math] u [/ math] y [math] v [/ math] sean diferentes, y actualice [math] u [/ math] y [math] v [/ math] para que sean esos padres. Continúe hasta [math] k = 0 [/ math], y es fácil ver que los vértices con los que terminamos estarán justo debajo del LCA.
Nuevamente, esencialmente estamos construyendo la distancia de los vértices al LCA bit por bit. Aquí está la función LCA completa que incluye el ajuste de altura:
int LCA(int u, int v) { // Adjust the heights if (depth[u] > depth[v]) { u = getparent(u, depth[u] - depth[v]); } else if (depth[v] > depth[u]) { v = getparent(v, depth[v] - depth[u]); } // If the vertices are now same, we are done if (u == v) return u; // Go as far up as you can such that vertices are different for (int i = LGNMAX - 1; i >= 0; i--) { if (parent[i][u] != parent[i][v]) { u = parent[i][u]; v = parent[i][v]; } } // Parent of u (or v) is now LCA return parent[0][u]; }
La búsqueda de profundidad primero toma [matemática] O (N) [/ matemática] tiempo, y el siguiente cálculo de matriz principal toma [matemática] O (N \ log N) [/ matemática]. Tanto las
getparent()
como LCA()
deben ejecutarse como máximo una vez por consulta y tomar [math] O (\ log N) [/ math] cada una. Por lo tanto, la complejidad temporal general de la solución es [matemática] O ((N + Q) \ log N) [/ matemática] y la complejidad del espacio es [matemática] O (N \ log N) [/ matemática]. Existen algoritmos que pueden encontrar LCA en espacio y tiempo lineal con un preprocesamiento diferente, pero son conceptualmente más difíciles y este debería ser suficiente para la mayoría de las aplicaciones de programación competitivas. En cualquier caso, aquí hay un enlace con una explicación de la solución [matemática] O (N) [/ matemática]: Consulta mínima de rango y Ancestro común más bajo