Algoritmos: ¿Cuáles son los detalles en la implementación de un algoritmo de ancestro común más bajo O (N log N)?

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:

  1. La profundidad del nodo (es decir, la distancia del nodo desde la raíz)
  2. 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:

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

En lugar de repetir aquí, le sugiero que consulte este conocido artículo Algorithm Tutorials solución fácil en O (N logN, O (logN), explica todo lo que está buscando.

More Interesting

¿Cuáles son las características de los árboles de coníferas y cuáles son algunas plantas / árboles con aspectos similares?

¿Qué algoritmos de aprendizaje funcionan basados ​​en datos desequilibrados (eventos raros)?

¿Cómo se puede explicar el algoritmo para la conversión de un número binario a un hexadecimal (código fuente incluido)?

¿Cuál es el mejor método para aprender efectivamente ciencia de datos / aprendizaje automático desde cero para un estudiante promedio de Ingeniería (idiomas, algoritmos, tutoriales, etc.)?

¿Puede la longitud de un comando Mathematica o Wolfram | Alpha ser una aproximación aproximada de su complejidad de Kolmogorov?

¿Por qué no usar Dijkstra o Bellman-Ford para encontrar el camino más corto entre dos personas en Facebook y por qué no usar BFS bidireccional en DVR o LSR?

¿Cómo funciona la función Rolling Hash utilizada en el algoritmo Rabin Karp?

¿Cómo funciona un algoritmo de 'aprendizaje de representación'?

¿Por qué se ha reducido la participación de Instagram después de la actualización del algoritmo?

¿Cuál es el vínculo entre los algoritmos de optimización y las distribuciones de probabilidad?

¿Cuál es una manera simple de implementar la paginación en una matriz en Javascript?

¿Son los algoritmos de detección de imágenes el futuro de Silicon Valley?

¿Sigue siendo necesario convertir una solución dp memorable en una iterativa?

¿Debería centrarme en aprender más idiomas o algoritmos y estructuras de datos?

Ayúdame con el problema TopCoder SRM - 599, div - I, level - 3?