Entenda os princípios subjacentes do algoritmo de retrocesso de maneira simples

Link original: Problema de combinação
————————————————

1. Materiais de referência

Pensamentos aleatórios sobre código - Problemas de combinação
leetcode - Questão 77. Combinação

2. Introdução ao Backtracking

Recursão e retrocesso são uma família, e ambos, retrocesso e recursão, andam de mãos dadas.

Backtracking é um subproduto da recursão, enquanto houver recursão haverá retrocesso

1. Introdução à pergunta

Alguns problemas só podem ser pesquisados ​​por força bruta, e não há outra solução mais eficiente, então o algoritmo de retrocesso é escolhido.

2. Cenários aplicáveis ​​do método backtracking

O método de retrocesso geralmente pode resolver os seguintes problemas:

  • Problema de combinação: Encontre um conjunto de k números em N números de acordo com certas regras;
  • Problema de corte: Existem várias maneiras de cortar uma corda de acordo com certas regras;
  • Problema do subconjunto: Quantos subconjuntos elegíveis existem em um conjunto de N números;
  • Problema de arranjo: N números são arranjados de acordo com certas regras, e existem vários métodos de arranjo;
  • Problemas de tabuleiro: N Queens, resolvendo Sudoku e muito mais.

3. Essência do retrocesso

A essência do método de retrocesso é enumerar exaustivamente todas as possibilidades e, em seguida, selecionar a resposta que queremos. Portanto, o desempenho do método de retrocesso não é alto. Para melhorar a eficiência do método de retrocesso, algumas operações de poda podem ser adicionadas.

4. Entenda o retrocesso

Os problemas resolvidos pelo método backtracking podem ser abstraídos em uma estrutura de árvore, porque o método backtracking trata de procurar recursivamente por subconjuntos na coleção . O tamanho da coleção constitui a largura da árvore e a profundidade da recursão constitui a profundidade da árvore.

A abordagem básica do método de retrocesso é a busca em profundidade, que é um algoritmo de busca exaustiva bem organizado que pode evitar buscas repetidas desnecessárias.

algoritmo de retrocessoideia básicaÉ: avance por um caminho, avance se puder, recue se não puder e tente outro caminho.

5. Processo de algoritmo de retrocesso

5.1 Determinar os parâmetros e o valor de retorno da função de retrocesso

  • Nome da função, no algoritmo de retrocesso, o nome da função geralmente é backtracking();
  • Valor de retorno, o valor de retorno da função no algoritmo de retrocesso é geralmente nulo.
  • parâmetro,Os parâmetros exigidos pelo algoritmo de retrocesso não são tão fáceis de determinar de uma só vez quanto a recursão da árvore binária, então a lógica geralmente é escrita primeiro e, em seguida, quaisquer que sejam os parâmetros necessários, basta preencher os parâmetros

O pseudocódigo da função de retrocesso é o seguinte:

void backtracking(参数)

5.2 Determinando as condições de término do método de retrocesso

De um modo geral, quando um nó folha é pesquisado, uma resposta que satisfaz as condições é encontrada e a resposta é armazenada e a recursão dessa camada termina.

O pseudocódigo da condição de término da função de retrocesso é o seguinte:

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

5.3 Processo transversal de busca retroativa

Normalmente, o método de retrocesso é pesquisar recursivamente na coleção, o tamanho da coleção constitui a largura da árvore e a profundidade da recursão constitui a profundidade da árvore.
insira a descrição da imagem aqui

O pseudocódigo do processo de travessia da função de retrocesso é o seguinte:

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

Como pode ser visto na figura,O loop for é usado para travessia horizontal e o backtracking (recursivo) é usado para travessia vertical, para que toda a árvore possa ser percorrida. De um modo geral, procurar nós de folha é encontrar um dos resultados.

Depois de analisar o processo, a estrutura do modelo de algoritmo de retrocesso é a seguinte:

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

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

3. Exemplo de análise

1. Exemplo de código-fonte

Para obter o código-fonte, consulte: Random Thoughts on Code - Question 77. Combination

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 da operação do algoritmo de retrocesso

Link para download do arquivo de origem esquemática
insira a descrição da imagem aqui

Acho que você gosta

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