Comprender los principios subyacentes del algoritmo de backtracking de una manera sencilla

Enlace original: Problema de combinación
————————————————

1. Materiales de referencia

Pensamientos aleatorios sobre el código - Problemas de combinación
leetcode - Pregunta 77. Combinación

2. Introducción al retroceso

La recursión y el retroceso son una familia, y tanto el retroceso como la recursión van de la mano.

El retroceso es un subproducto de la recursión, mientras haya recursión, habrá retroceso

1. Introducción a la pregunta

Algunos problemas solo se pueden buscar por fuerza bruta, y no hay otra solución más eficiente, por lo que se elige el algoritmo de backtracking.

2. Escenarios aplicables del método de backtracking

El método de retroceso generalmente puede resolver los siguientes problemas:

  • Problema de combinación: Encuentra un conjunto de k números en N números de acuerdo con ciertas reglas;
  • Problema de corte: hay varias formas de cortar una cuerda de acuerdo con ciertas reglas;
  • Problema de subconjuntos: ¿Cuántos subconjuntos elegibles hay en un conjunto de N números?
  • Problema de ordenación: los números N se ordenan de acuerdo con ciertas reglas y existen varios métodos de ordenación;
  • Problemas de tablero: N Queens, Resolver Sudoku y más.

3. La esencia del retroceso

La esencia del método de retroceso es enumerar exhaustivamente todas las posibilidades y luego seleccionar la respuesta que queremos. Por lo tanto, el rendimiento del método de backtracking no es alto. Para mejorar la eficiencia del método de retroceso, se pueden agregar algunas operaciones de poda.

4. Comprender el retroceso

Los problemas resueltos por el método de rastreo inverso se pueden resumir en una estructura de árbol, porque el método de rastreo inverso tiene que ver con la búsqueda recursiva de subconjuntos en la colección . El tamaño de la colección constituye el ancho del árbol, y la profundidad de la recursividad constituye el profundidad del árbol.

El enfoque básico del método de rastreo es la búsqueda en profundidad, que es un algoritmo de búsqueda exhaustivo bien organizado que puede evitar búsquedas repetidas innecesarias.

algoritmo de retrocesoidea básicaEs: avanza desde un camino, avanza si puedes, retrocede si no puedes e intenta otro camino.

5. Proceso de algoritmo de retroceso

5.1 Determinar los parámetros y el valor de retorno de la función de retroceso

  • Nombre de la función, en el algoritmo de backtracking, el nombre de la función suele ser backtracking();
  • Valor de retorno, el valor de retorno de la función en el algoritmo de retroceso generalmente es nulo.
  • parámetro,Los parámetros requeridos por el algoritmo de backtracking no son tan fáciles de determinar a la vez como la recursividad del árbol binario, por lo que la lógica generalmente se escribe primero, y luego cualquier parámetro que se necesite, solo complete los parámetros

El pseudocódigo de la función de retroceso es el siguiente:

void backtracking(参数)

5.2 Determinación de las condiciones de terminación del método de backtracking

En términos generales, cuando se busca un nodo hoja, se encuentra una respuesta que satisface las condiciones, se almacena la respuesta y finaliza la recursión de esta capa.

El pseudocódigo de la condición de terminación de la función de retroceso es el siguiente:

if (终止条件) {
    存放结果;
    return;
}

5.3 Proceso transversal de búsqueda retrospectiva

Por lo general, el método de búsqueda inversa es buscar recursivamente en la colección, el tamaño de la colección constituye el ancho del árbol y la profundidad de la recursividad constituye la profundidad del árbol.
inserte la descripción de la imagen aquí

El pseudocódigo del proceso transversal de la función de retroceso es el siguiente:

for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtracking(路径,选择列表); // 递归
    回溯,撤销处理结果
}

Como se puede ver en la figura,El ciclo for se usa para el recorrido horizontal, y el retroceso (recursivo) se usa para el recorrido vertical, de modo que se pueda recorrer todo el árbol. En términos generales, buscar nodos hoja es encontrar uno de los resultados.

Después de analizar el proceso, el marco de la plantilla del algoritmo de backtracking es el siguiente:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

3. Ejemplo de análisis

1. Ejemplo de código fuente

Para obtener el código fuente, consulte: Pensamientos aleatorios sobre el código - Pregunta 77. Combinación

class Solution {
    
    
private:
    vector<vector<int>> result; // 存放符合条件结果的集合
    vector<int> path; // 用来存放符合条件结果
    void backtracking(int n, int k, int startIndex) {
    
    
        if (path.size() == k) {
    
    
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= n; i++) {
    
    
            path.push_back(i); // 处理节点
            backtracking(n, k, i + 1); // 递归
            path.pop_back(); // 回溯,撤销处理的节点
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
    
    
        result.clear(); // 可以不写
        path.clear();   // 可以不写
        backtracking(n, k, 1);
        return result;
    }
};

2. Diagrama esquemático del funcionamiento del algoritmo de backtracking

Enlace de descarga del archivo fuente esquemático
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/m0_37605642/article/details/131724840
Recomendado
Clasificación