Serie recursiva de estructura y algoritmo de datos

Serie recursiva de estructura y algoritmo de datos

Escribir al frente

Hace unos meses, quise escribir un artículo así para compartir con todos, porque tengo el corazón y la falta de energía, no he establecido lo que he aprendido, así que he estado aprendiendo constantemente por mi cuenta.

Luego me tomó otra semana ordenar y categorizar, y luego tengo este intercambio de 8000 palabras sobre la recursividad. Espero que pueda ayudar a los amigos que están aprendiendo la recursividad. Este artículo es una versión reducida. Puede hacer clic en "Leer el texto original" para ver la versión completa.

Y con el apoyo y la motivación de este artículo, escribiré algunos conceptos difíciles sobre estructuras de datos y algoritmos para simplificar. Si hay errores en el artículo, espero que pueda corregirme y compartir más artículos de calidad para otros.

El siguiente artículo que compartiré es sobre la recursividad. Este artículo no solo comparte todo sobre la recursividad. Creo que es más importante transmitir un pensamiento a cada lector. ¿pensamiento? ¡Sí, eso es correcto! No se puede decir que este artículo incluya las esquinas y las esquinas de la recursividad, pero a través de mi propio estudio teórico y práctica, tengo mi propio conjunto de ideas recursivas.

Qué problemas deberían ser recursivos, qué problemas deberían ser concisos con la recursividad, qué problemas no se pueden resolver mediante recursividad, y la trampa de usar la recursividad para resolver problemas específicos, podemos optimizar aún más la recursividad, estos son el contenido compartido por Xiaolu hoy.

Que es la recursividad

La recursividad, como su nombre lo indica, se llama recursividad si hay recursividad, si hay recursividad o si hay recursividad sin recursividad, se llama "jugar a un pícaro".

Por que aprender recursividad

Ya sea que aprendamos una tecnología o un lenguaje de programación, antes de aprenderlo sabemos lo que nos puede traer y qué tipo de problemas nos puede ayudar a resolver, esto es lo que nos motiva a aprenderlo.

Desde matrices hasta listas vinculadas, tablas hash, algoritmos básicos, etc., hasta después de la recursividad, se siente muy difícil de entender. Creo que todo el mundo se siente así. Al principio fue muy difícil, pero después de experimentar noventa y nueve y ochenta y una dificultades, todavía no entendía los trucos de la recursividad, y luego lo salté naturalmente.

Más tarde, comencé a estudiar las preguntas de LeetCode durante un mes y descubrí que la recursividad tiene un lugar en las estructuras de datos y los algoritmos que gobiernan el país. La mayoría de las preguntas se pueden resolver mediante recursividad, como: recorrido de árbol binario, algoritmo de retroceso, problema de mochila 0-1, recorrido de profundidad primero, algoritmo de retroceso, etc. He ordenado al menos de 20 a 30 preguntas sobre recursividad y he encontrado La importancia de la recursividad, así que tengo que volver a aprender de forma recursiva, todo con el artículo de hoy.

Cómo entender la recursividad

Mi definición del tipo recursivo "pícaro" anterior no le permite comprender exactamente qué es la recursividad, así que tomemos un ejemplo en vivo.

1. Problema

Por ejemplo, a ti, como Xiaolu y a mí, nos gusta hacer cola para cenar en la universidad (como un buen estudiante de tres, ¿cómo puedo hacer este tipo de cosas? Jaja), entonces los compañeros de clase detrás del equipo contaron que había 5 compañeros frente a ellos, por lo que les tocaba el turno. Ahora, debido a que los compañeros del frente seguían saltando en fila, se encontró en este momento, ¿cómo se sentía que se alejaba cada vez más de la ventana de la cocina? En este momento, si quiere saber su número en la cola (siempre que nadie salte en la cola), y resolverlo con pensamiento recursivo, ¿qué debemos hacer?

2. "Aprobar"

Entonces preguntó qué compañero de clase estaba al frente, y los compañeros de frente no solo estaban ahí, por lo que el compañero de frente le preguntó quién estaba frente a él, hasta que el segundo compañero de frente le preguntó quién era el primer compañero que estaba cocinando El número uno en el equipo (un poco vergonzoso). El compañero de clase que estaba cocinando dijo impaciente, ¿no viste que yo fui el primero en cocinar? Este proceso es en realidad un proceso de "pasar" en recursividad.

3. "Volver"

Entonces el segundo compañero que estaba cocinando en frente le dijo impacientemente al tercer compañero que yo era el segundo ¿No vi a un chico frente a mí que estaba cocinando? Luego el tercero se pasa al cuarto, y luego más tarde, hasta que la persona frente al compañero de clase que se aleja gradualmente de la ventana le dice en qué posición se encuentra, él conoce su posición actual en el equipo. Este proceso puede entenderse como el proceso de "recursividad" en recursividad.

4. Condiciones de rescisión

“El compañero de clase que cena dijo con impaciencia, ¿no viste que fui el primero en cenar?” En recursividad, lo llamamos condición de terminación.

5. Cómo entender la recursividad

Aunque el problema es el análisis recursivo, cuando se expresa en programas, no llame al código recursivo en el cerebro capa por capa para pensar en ello. Esto puede hacer que caiga completamente en el proceso de "recursivo". No puedo retroceder, estas son cosas que dejamos que haga la computadora.

Entonces, ¿cómo entendemos la recursividad cuando escribimos programas? Solo buscamos la relación entre los problemas, protegiendo los detalles de la recursividad, ver (5) análisis para más detalles.

Satisfacer la condición recursiva

¿Qué tipo de problemas se pueden resolver mediante recursividad? ¿Cuáles son las características y existen condiciones de juicio?

1. ¿Se puede resolver un problema en múltiples subproblemas?

Quiere saber en qué parte del equipo se encuentra y desglosar el problema en varias subpreguntas como "dónde están todos los miembros del equipo".

2. ¿El problema es el mismo que el subproblema?

Si desea saber su ubicación actual, debe preguntar dónde está la persona que está al frente. Si la persona que está al frente quiere saber dónde está, debe conocer la ubicación de la persona que está frente a él. Por lo tanto, la solución del problema y el subproblema son los mismos y satisfacen la segunda condición.

3. ¿Existe alguna condición de rescisión para el problema?

El primer alumno que está cocinando dice que es la primera persona del equipo. Esta es la llamada condición de terminación. Una vez que se encuentra la condición de terminación, comienza el proceso de "devolución".

Clasificación recursiva

Haciendo muchas preguntas, resolviendo diferentes problemas según la recursividad, se derivan varias formas de resolver y pensar. La razón para categorizarlo es comprender mejor el papel de la recursividad en diferentes problemas, tales como: la relación entre cada capa de recursividad, cálculo y enumeración recursiva de todas las situaciones y recursividad frente a problemas de selectividad. Aunque se divide en varias categorías, la naturaleza de la recursividad es inmutable.

※ Categoría 1: tipo de cálculo recursivo

1. Cálculo capa por capa

Computación capa por capa, como su nombre indica, los problemas que pueden resolverse mediante recursividad se pueden dividir en múltiples subproblemas. Podemos abstraer cada subproblema en una capa, y la relación entre los subproblemas se puede expresar como la relación entre capas. Usamos la fórmula recursiva para expresar el cálculo a través de la relación de cálculo entre las capas, y finalmente obtenemos el valor del resultado después de las capas de recursividad.

▉ Ejemplo:

Tomemos el ejemplo de hacer cola para cenar arriba para ilustrar que nuestro subproblema ha sido analizado, es decir, quiero saber la posición actual en el equipo, que es preguntarme dónde está la persona de delante más uno es mi la posición actual del equipo, esta como capa. La persona al frente quiere saber su posición actual y necesita usar la misma solución que otra capa.

¿Cuál es la relación entre las capas (cuál es la relación entre mi posición actual en el equipo y la posición de la persona que tengo delante)? En este momento dirás que actualmente es +1. Esto es fácil de averiguar para la mayoría de la gente, ahora que la relación está determinada, es fácil escribir código recursivo a través de la fórmula recursiva.


1// f(n) 为我所在的当前层
2// f(n-1) 为我前边的人所在的当前层
3// + 1 是层与层之间的计算关系
4f(n) = f(n-1) + 1

▉ Resumen:

Llamé al tipo anterior de problema recursivo "tipo de cálculo recursivo" "tipo de cálculo en capas".

▉ Aprender unos de otros:

El problema de buscar la edad también es un problema del tipo de cálculo en capas, intente analizarlo usted mismo (debe intentar pensarlo usted mismo y codificarlo a mano para comprender mejor las habilidades recursivas).

Pregunta 1: Hay 5 personas sentadas juntas y preguntaron cuántos años tiene la 5. Dijo que es 2 años mayor que la cuarta persona. Cuando se le preguntó cuántos años tenía la cuarta persona, dijo que tenía 2 años más que la tercera persona. Cuando se le preguntó cuántos años tenía la tercera persona, dijo que tenía 2 años más que la segunda persona. Cuando se le preguntó cuántos años tenía la segunda persona, dijo que tenía 2 años más que la primera persona. Finalmente, le pregunté a la primera persona y me dijo que tenía 10 años. Escriba un programa para averiguar la edad correspondiente al ingresar el número de personas.

Problema dos : la lista enlazada individualmente genera el valor del nodo desde el final hasta la primera vez, que se realiza mediante recursividad.

2. Cálculo paralelo

Cálculo paralelo, como su nombre lo indica, la solución al problema es obtener el resultado mediante cálculo paralelo recursivo. No existe una relación de cálculo segura entre capas, simplemente cambie los valores de los parámetros de entrada.

▉ Ejemplo:

El tipo de pregunta más clásico es la secuencia de Fibonacci. Observe tal conjunto de datos 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ..., excepto por el primer y segundo dato, el resto de los datos es igual a la suma del primero dos datos (por ejemplo: 2 = 1 + 1, 8 = 3 + 5, 34 = 21 + 13). Puede intentar analizarlo usted mismo según los pasos de "satisfacer las tres condiciones de recursividad" y "cómo escribir código recursivo".

También hago un pequeño análisis aquí.

1) El primer paso: Primero determine si el problema se puede dividir en múltiples subproblemas. Lo he analizado arriba. Excepto por el primer y segundo datos, los otros datos son la suma de los dos primeros datos. Entonces, ¿cómo conoces los dos primeros datos? La misma solución es la suma de sus dos primeros números.

2) El segundo paso: Encuentre la condición de terminación, si continúa encontrando la suma de los dos primeros números, hasta que los primeros tres datos 0, 1, 1. Si el primer 1 se encuentra de forma recursiva, los datos anteriores no son suficientes, por lo que esta es también la condición de terminación que encontramos.

3) El tercer paso: ahora que hemos encontrado las condiciones y relaciones de terminación, no es difícil escribir la fórmula de recurrencia f (n) = f (n-1) + f (n-2) (n es el número requerido Valor numérico).

4) Convertido a código recursivo de la siguiente manera:


1function f(n) {
2    // 终止条件
3    if(n == 0) return 0;
4    if(n == 1) return 1;
5    // 递推公式
6    return f(n-1) + f(n-2);
7}

▉ Resumen:

Resumo la pregunta anterior como un tipo de cálculo paralelo. También se puede clasificar como una especie de cálculo en capas, pero se cambia + 1 para agregar una recursividad de la función f en sí (para decirlo sin rodeos, el resultado de la recursividad también es un valor exacto).

El llamado cálculo paralelo f (n-1) yf (n-2) no se molestan entre sí y calculan sus respectivos valores de forma recursiva. Finalmente, sumamos el valor del resultado calculado que es el resultado que más queremos.

▉ Aprender unos de otros:

Pregunta: Una rana puede saltar 1 paso o 2 pasos a la vez. Encuentra el número total de métodos de salto que la rana salta en un paso de n niveles.

※ Categoría 2: tipo de enumeración recursiva

La aplicación más común de la enumeración recursiva es el algoritmo de retroceso, que enumera todas las situaciones posibles ¿Cómo enumerar todas las situaciones? Enumerar mediante técnicas de programación recursiva. Entonces, ¿qué es el algoritmo de retroceso? Por ejemplo, caminando por el laberinto, caminando desde la entrada hasta la salida, si encuentra un callejón sin salida, debe regresar, regresar a la intersección anterior y luego tomar otra bifurcación. Repita el método anterior hasta que encuentre la salida .

Los problemas más clásicos de los algoritmos de retroceso son el recorrido en profundidad primero, el problema de las ocho reinas, etc., que se utilizan ampliamente. A continuación se toma el problema de las ocho reinas como ejemplo para analizar. Otros algoritmos de retroceso que utilizan la enumeración recursiva son muy simples.

Problema de las ocho reinas

En la cuadrícula de 8 x 8, coloque ocho reinas (piezas de ajedrez), y la condición es que no puedan haber dos reinas (piezas de ajedrez) en la misma fila, columna o línea diagonal. Pregunte cuántos tipos de ubicaciones hay en el camino ?

Serie recursiva de estructura y algoritmo de datos
Figura (1) Situación correcta

Serie recursiva de estructura y algoritmo de datos
Figura (2) Situación de error

▉ Análisis de problemas:

Si desea asegurarse de que dos reinas (fichas) no pueden estar en la misma fila, la misma columna o en la misma barra, debe enumerar todas las ubicaciones de las reinas (fichas) una por una y luego establecer las condiciones en filtrar las condiciones que cumplen las condiciones.


▉ Idea de algoritmo:

Después de analizar el problema con claridad, ¿cómo podemos implementar un algoritmo de retroceso recursivo para enumerar todas las apariciones de las ocho reinas (piezas de ajedrez)?

1) En la cuadrícula de 8 X 8, primero colocamos la primera reina (peón) en la primera columna de la primera fila (es decir, coordenadas: (0,0)).

2) Luego colocamos la segunda reina (peón) en la segunda fila, la colocamos en la primera columna y luego juzgamos si hay otra reina en la misma fila, columna y barra. Si existe, la posición es inapropiada, luego colóquela en la siguiente columna y luego juzgue si cumple con las condiciones que establecemos.

3) Después de que la segunda reina (peón) encuentre una posición adecuada, coloque el tercer peón en la tercera fila y coloque las ocho reinas en la posición adecuada sucesivamente.

4) Esto es solo una posibilidad, porque la primera reina que configuré es una posición fija, en la posición (0,0) de las coordenadas de la cuadrícula, entonces, ¿cómo enumerar todos los casos? Luego cambiamos constantemente la posición de la primera reina, la posición de la segunda reina ..., podemos enumerar todas las situaciones. Si eres como yo, después de leer esta pregunta, si aún eres un poco ignorante, analiza el código directamente.

▉ Implementación del código:

Aunque el código está implementado en javascript, creo que la lógica básica del código de los amigos que han aprendido a programar pueden entender. De acuerdo con las tres condiciones satisfechas por el análisis recursivo resumido anteriormente y los pasos de cómo escribir código recursivo, analice el problema de las ocho reinas paso a paso.

1. Descomponga el problema en múltiples subproblemas

En el análisis de código y el análisis de pensamiento de algoritmos anteriores, podemos saber aproximadamente cómo descomponer el problema.Enumerar todas las situaciones de satisfacción de las ocho reinas (piezas de ajedrez) se puede descomponer en el subproblema de encontrar primero cada situación de satisfacción. Por ejemplo, la idea del algoritmo de cada subproblema son los cuatro pasos enumerados anteriormente.

2. Conoce las condiciones de rescisión

Cuando se alcanza la octava línea, la recursión finaliza.


1// 终止条件
2if(row === 8){
3    // 打印第 n 种满足的情况
4    console.log(result)
5    n++;
6    return;
7}

3. Escribe la fórmula de recurrencia

La función isOkCulomn () juzga si la posición encontrada satisface las condiciones (no puede estar en la misma fila, columna o barra). Si se cumplen las condiciones, devolvemos verdadero, ingresamos el juicio if y se pasa el número de filas más uno para recursar la posición de reina de la siguiente fila. Hasta que la recursividad encuentre la posición de condición de terminación, columna ++, coloque la reina en la primera fila a la siguiente posición, continúe la recursión y enumere todas las posibles situaciones de colocación.


 1// 每一列的判断
 2for(let column = 0; column < 8; column++){
 3    // 判断当前的列位置是否合适
 4    if(isOkCulomn(row,column)){
 5        // 保存皇后的位置
 6        result[row] = column;
 7        // 对下一行寻找数据
 8        cal8queens(row + 1);
 9    }
10    // 此循环结束后,继续遍历下一种情况,就会形成一种枚举所有可能性
11}

 1// 判断当前列是否合适
 2const isOkCulomn = (row,column) =>{
 3    // 左上角列的位置
 4    let leftcolumn = column - 1;
 5    // 右上角列的位置
 6    let rightcolumn = column + 1;
 7
 8    for(let i = row - 1;i >= 0; i--){
 9        // 判断当前格子正上方是否有重复
10        if(result[i] === column) return false;
11
12        // 判断当前格子左上角是否有重复
13        if(leftcolumn >= 0){
14            if(result[i] === leftcolumn) return false;
15        }
16
17        // 判断当前格式右上角是否有重复
18        if(leftcolumn < 8){
19            if(result[i] === rightcolumn) return false;
20        }
21
22        // 继续遍历
23        leftcolumn --;
24        rightcolumn ++;
25    }
26    return true;
27}

4. Convertir a código recursivo


 1// 变量
 2// result 为数组,下标为行,数组中存储的是每一行中皇后的存储的列的位置。
 3// row 行  
 4// column 列
 5// n 计数满足条件的多少种
 6var result = [];
 7let n = 0
 8const cal8queens = (row) =>{
 9    // 终止条件
10    if(row === 8){
11        console.log(result)
12        n++;
13        return;
14    }
15    // 每一列的判断
16    for(let column = 0; column < 8; column++){
17        // 判断当前的列位置是否合适
18        if(isOkCulomn(row,column)){
19            // 保存皇后的位置
20            result[row] = column;
21            // 对下一行寻找数据
22            cal8queens(row + 1);
23        }
24        // 此循环结束后,继续遍历下一种情况,就会形成一种枚举所有可能性
25    }
26}
27
28// 判断当前列是否合适
29const isOkCulomn = (row,column) =>{
30    // 设置左上角
31    let leftcolumn = column - 1;
32    let rightcolumn = column + 1;
33
34    for(let i = row - 1;i >= 0; i--){
35        // 判断当前格子正上方是否有重复
36        if(result[i] === column) return false;
37
38        // 判断当前格子左上角是否有重复
39        if(leftcolumn >= 0){
40            if(result[i] === leftcolumn) return false;
41        }
42
43        // 判断当前格式右上角是否有重复
44        if(leftcolumn < 8){
45            if(result[i] === rightcolumn) return false;
46        }
47
48        // 继续遍历
49        leftcolumn --;
50        rightcolumn ++;
51    }
52    return true;
53}
54
55// 递归打印所有情况
56const print = (result)=>{
57    for(let i = 0;i < 8; i++){
58        for(let j = 0;j < 8; j++){
59            if(result[i] === j){
60                console.log('Q' + ' ')
61            }else{
62                console.log('*' + ' ')
63            }
64        }
65    }
66}
67
68// 测试
69cal8queens(0);
70console.log(n)

▉ Resumen

El problema de las ocho reinas mencionadas anteriormente es usar la recursividad para enumerar todas las situaciones, y luego establecer condiciones a partir de ellas, y solo filtrar las opciones que cumplen las condiciones. Se recomienda leer el código anterior varias veces y practicarlo usted mismo. Al principio, resolví el problema de las ocho reinas. Lo miré durante mucho tiempo para entender cómo la recursividad jugaba un papel complicado.

▉ Aprender unos de otros:

Si quieres practicar tus manos, puedes implementar el recorrido en profundidad primero en la siguiente figura. Esto no es difícil de entender. Puedes intentar escribirlo tú mismo. Subí el código a mi Github.

※ Categoría 3: tipo de selección recursiva

En el llamado tipo de selección recursiva, cada subproblema tiene que enfrentarse a una elección y buscar la solución óptima. Algunos amigos dirán que la programación dinámica para la solución óptima es la más adecuada, sí, sí, pero enumerando recursivamente todos los casos a través de la selección, estableciendo las condiciones y encontrando la solución óptima al problema también se puede lograr, así que ¿qué hay de mí? Este tipo de problema se clasifica como un problema de selección recursiva.

0-1 problema de mochila

El problema de la mochila 0-1 es familiar para los amigos que lo han conocido. De hecho, este problema también pertenece a una especie de algoritmo de retroceso, no mucho sin sentido, directamente sobre el problema. Hay una mochila, el peso total de carga de la mochila es Wkg. Ahora tenemos n elementos, cada uno de los cuales tiene pesos diferentes y es indivisible. Ahora esperamos seleccionar algunos elementos y cargarlos en la mochila. ¿Cómo maximizar el peso total de los artículos en la mochila sin exceder el peso de la mochila?

▉ Análisis de problemas:

Si está confundido con este tema, no importa, lo analizaremos un poco. Si tenemos dos estados para cada elemento, hay 2 ^ n tipos de métodos de instalación totales ¿Cómo no podemos repetir estas posibilidades?

▉ Idea de algoritmo:

Podemos organizar los elementos en orden, y todo el problema se divide en n etapas, y cada etapa corresponde a cómo elegir un elemento. Primero procese el primer elemento, elija cargarlo o no, y luego procese los elementos restantes de forma recursiva.

▉ Implementación del código:


 1// 用来存储背包中承受的最大重量
 2var max = Number.MIN_VALUE;
 3// i: 对第 i 个物品做出选择
 4// currentw: 当前背包的总重量
 5// goods:数组,存储每个物品的质量
 6// n: 物品的数量
 7// weight: 背包应承受的重量
 8const f = (i, currentw, goods, n, weight) => {
 9    // 终止条件
10    if(currentw === weight || i === n){
11        if(currentw > max){
12            // 保存满足条件的最大值
13            max = currentw;
14        }
15        return ;
16    }
17
18    // 选择跳过当前物品不装入背包
19    f(i+1, currentw, goods, n, weight)
20
21    // 将当前物品装入背包
22    // 判断当前物品装入背包之前是否超过背包的重量,如果已经超过当前背包重量,就不要就继续装了
23    if(currentw + goods[i] <= weight){
24        f(i+1 ,currentw + goods[i], goods, n, weight)
25    }
26}
27
28let a = [2,2,4,6,3]
29f(0,0,a,5,10)
30console.log(max)

Desventajas de la recursividad

Aunque el uso de la recursividad es muy conciso, también tiene muchas deficiencias, y también es donde necesitamos atención adicional y optimización en el uso.

1. Desbordamiento de pila recursivo

▉ Comprender el desbordamiento de pila

1) La esencia de la recursividad es el proceso de llamarse repetidamente a sí mismo. ¿Qué es? Por supuesto que es una función. Bueno, hay parámetros y algunas variables declaradas localmente en la función. Creo que muchos amigos solo usan la función y no saben cómo se almacenan las variables en la función. No importa, cuando escuches mi análisis, estarás ahí.

2) Las variables de la función se almacenan en la pila del sistema La estructura de datos de la pila se caracteriza por el primero en entrar, el último en salir y el último en entrar, primero en salir. El uso de variables en una función cambia con el ciclo de vida de la función. Cuando ejecutamos una función, las variables de la función seguirán siendo empujadas a la pila. Cuando la función se ejecuta y se destruye, los elementos de la pila se sacarán de la pila a su vez. Todavía no entiendo, no importa, mira el diagrama a continuación.

3) Una vez que entendemos el proceso anterior, volvemos a la recursividad. Nuestra llamada recursiva es llamarse a sí misma en una función, y la función actual no se destruye, porque la función actual está recursivamente en la ejecución de sí misma, por lo que el proceso recursivo , la función Variables en la pila se ha empujado continuamente a la pila, debido a que la pila de nuestro sistema o el espacio de pila de la máquina virtual es muy pequeña, cuando la pila está llena y luego presionada, hará que la pila se desborde.


1// 函数
2function f(n){
3    var a = 1;
4    var b = 2;
5    return a + b;
6}

Serie recursiva de estructura y algoritmo de datos

▉ Solución

Entonces, ¿cómo resolvemos esta situación?

Por lo general, establecemos la profundidad de la recursividad, pero el simple entendimiento es que si la recursividad excede la profundidad que establecimos, saldremos y ya no recurriremos. O el ejemplo de hacer cola para cenar, de la siguiente manera:


 1// 表示递归深度变量
 2let depth = 0;
 3
 4function f(n){
 5    depth++;
 6    // 如果超过递归深度,抛出错误
 7    if(depth > 1000) throw 'error';
 8    // 终止条件
 9    if(n == 1) retun 1;
10    // 递推公式
11    return f(n-1) + 1;
12}

2. Repetir elementos de forma recursiva

En algunos problemas recursivos, hay un problema de cálculos repetidos, como encontrar la secuencia de Fibonacci. Dibujemos el árbol recursivo como se muestra en la figura siguiente. Encontraremos que hay muchos cálculos recursivos repetidos. Los cálculos repetidos llevarán a un tiempo elevado complejidad del programa, y ​​es exponencial, lo que conduce a la ineficiencia de nuestro programa.

Serie recursiva de estructura y algoritmo de datos

▉ Solución

¿Cómo deberíamos resolver el problema de la doble contabilización? Algunos amigos lo pensaron. Guardamos el valor calculado. Antes de cada cálculo recursivo, verifiquemos si los datos guardados contienen los datos. Si los hay, los usaremos directamente. Si no, lo calculamos y lo guardamos. Generalmente usamos una tabla hash para guardar. (La denominada tabla hash tiene la forma de pares clave-valor, como mapa)


 1// 斐波那契数列改进后
 2let map = new Map();
 3function f(n) {
 4    // 终止条件
 5    if(n == 0) return 0;
 6    if(n == 1) return 1;
 7
 8    // 如果散列表中存在当前计算的值,就直接返回,不再进行递归计算
 9    if(map.has(n)){
10        return map.get(n);
11     }
12
13    // 递推公式
14    let num = f(n-1) + f(n-2);
15    // 将当前的值保存到散列表中
16    map.set(n,num)
17    return num;
18}

3. Gran complejidad espacial recursiva

Debido a que el almacenamiento de variables de función durante la recursividad requiere espacio de pila adicional, cuando la profundidad de recursividad es muy profunda, se requiere memoria adicional para ocupar mucho espacio, por lo que la recursividad tiene una complejidad de espacio muy alta.

Lo ultimo que decir

Al final, podría decir que es un poco de sangre de gallina, mucha gente colapsará cuando se encuentre con la recursividad, como yo, jaja. No importa qué dificultades encuentres en el futuro, no les temas, pero trátalas como un desafío. Cuando superes capas de dificultades después de una larga batalla y finalmente superes los desafíos, estarás agradecido de haber no se rindió ante las dificultades.

Me siento profundamente por esto. A veces me siento muy impotente ante el problema. Aunque no tengo una buena universidad, no tengo buenos recursos y nadie que se concentre en guiarte, siempre creo que todo esto es un regalo de Dios. el libro del desafío que publiqué, continuaré trabajando duro para escribir más artículos de alta calidad.

Si te sientes bien, ¡puedes agregarle una pierna de pollo al venado! ¡Gracias de nuevo por ser un granjero de códigos no reconciliado!

Supongo que te gusta

Origin blog.51cto.com/15064450/2602375
Recomendado
Clasificación