Algorithme de retour en arrière
"L'algorithme de retour en arrière" est une méthode de résolution de problèmes par épuisement. Son idée principale est de partir d'un état initial, de rechercher violemment toutes les solutions possibles, et lorsque la bonne solution est rencontrée, de l'enregistrer jusqu'à ce qu'elle soit trouvée. la solution ne peut pas être trouvée après avoir essayé toutes les options possibles.
Les algorithmes de backtracking utilisent généralement une « recherche en profondeur d'abord » pour parcourir l'espace de solution. Dans le chapitre sur l'arbre binaire, nous avons mentionné que les parcours pré-ordre, dans l'ordre et post-ordre sont des recherches en profondeur d'abord. Ensuite, nous utilisons le parcours de précommande pour construire un problème de retour en arrière et comprendre progressivement le principe de fonctionnement de l'algorithme de retour en arrière.
!!!question "Exemple de question 1"
给定一个二叉树,搜索并记录所有值为 $7$ 的节点,请返回节点列表。
Pour cette question, nous parcourons l'arbre en pré-ordre et déterminons si la valeur du nœud actuel est 7 77 , si tel est le cas, ajoutez la valeur du nœud à la liste des résultatsres
. L'implémentation du processus concerné est illustrée dans la figure ci-dessous et dans le code suivant.
=== « Python »
```python title="preorder_traversal_i_compact.py"
[class]{}-[func]{pre_order}
```
=== « C++ »
```cpp title="preorder_traversal_i_compact.cpp"
[class]{}-[func]{preOrder}
```
=== « Java »
```java title="preorder_traversal_i_compact.java"
[class]{preorder_traversal_i_compact}-[func]{preOrder}
```
=== « C# »
```csharp title="preorder_traversal_i_compact.cs"
[class]{preorder_traversal_i_compact}-[func]{preOrder}
```
=== « Partez »
```go title="preorder_traversal_i_compact.go"
[class]{}-[func]{preOrderI}
```
=== « Rapide »
```swift title="preorder_traversal_i_compact.swift"
[class]{}-[func]{preOrder}
```
=== "JS"
```javascript title="preorder_traversal_i_compact.js"
[class]{}-[func]{preOrder}
```
=== « TS »
```typescript title="preorder_traversal_i_compact.ts"
[class]{}-[func]{preOrder}
```
=== « Fléchette »
```dart title="preorder_traversal_i_compact.dart"
[class]{}-[func]{preOrder}
```
=== « Rouille »
```rust title="preorder_traversal_i_compact.rs"
[class]{}-[func]{pre_order}
```
=== « C »
```c title="preorder_traversal_i_compact.c"
[class]{}-[func]{preOrder}
```
=== "Zig"
```zig title="preorder_traversal_i_compact.zig"
[class]{}-[func]{preOrder}
```
Essayez de revenir en arrière
La raison pour laquelle on l'appelle algorithme de backtracking est que l'algorithme adopte la stratégie « essayer » et « backoff » lors de la recherche dans l'espace de solutions . Lorsque l'algorithme rencontre un état au cours du processus de recherche qui ne peut pas avancer ou ne peut pas obtenir une solution satisfaisant les conditions, il annulera le choix de l'étape précédente, reviendra à l'état précédent et essaiera d'autres choix possibles.
Pour l'exemple 1, accéder à chaque nœud représente une « tentative », tandis que traverser un nœud feuille ou revenir à un nœud parent return
représente un « retour en arrière ».
Il convient de noter que le repli n'inclut pas seulement les retours de fonctions . Pour expliquer cela, nous développons légèrement l’exemple 1.
!!! question "Exemple de question deux"
在二叉树中搜索所有值为 $7$ 的节点,**请返回根节点到这些节点的路径**。
Sur la base du code de l'exemple 1, nous devons utiliser une liste path
pour enregistrer les chemins des nœuds visités. Lors de l'accès à la valeur 7 7Lorsque le nœud est 7 , copiez-lepath
et ajoutez-le à la liste des résultatsres
. Une fois le parcours terminé,res
toutes les solutions sont enregistrées dans .
=== « Python »
```python title="preorder_traversal_ii_compact.py"
[class]{}-[func]{pre_order}
```
=== « C++ »
```cpp title="preorder_traversal_ii_compact.cpp"
[class]{}-[func]{preOrder}
```
=== « Java »
```java title="preorder_traversal_ii_compact.java"
[class]{preorder_traversal_ii_compact}-[func]{preOrder}
```
=== « C# »
```csharp title="preorder_traversal_ii_compact.cs"
[class]{preorder_traversal_ii_compact}-[func]{preOrder}
```
=== « Partez »
```go title="preorder_traversal_ii_compact.go"
[class]{}-[func]{preOrderII}
```
=== « Rapide »
```swift title="preorder_traversal_ii_compact.swift"
[class]{}-[func]{preOrder}
```
=== "JS"
```javascript title="preorder_traversal_ii_compact.js"
[class]{}-[func]{preOrder}
```
=== « TS »
```typescript title="preorder_traversal_ii_compact.ts"
[class]{}-[func]{preOrder}
```
=== « Fléchette »
```dart title="preorder_traversal_ii_compact.dart"
[class]{}-[func]{preOrder}
```
=== « Rouille »
```rust title="preorder_traversal_ii_compact.rs"
[class]{}-[func]{pre_order}
```
=== « C »
```c title="preorder_traversal_ii_compact.c"
[class]{}-[func]{preOrder}
```
=== "Zig"
```zig title="preorder_traversal_ii_compact.zig"
[class]{}-[func]{preOrder}
```
À chaque "essai", nous enregistrons le chemin en path
ajoutant ; avant de "revenir en arrière", nous devons path
faire sortir le nœud pour restaurer l'état avant cette tentative .
En observant le processus illustré dans la figure ci-dessous, nous pouvons comprendre try and rollback comme "forward" et "undo" . Les deux opérations sont inverses l'une de l'autre.
=== « <1> »
=== « <2> »
=== « <3> »
=== « <4> »
=== « <5> »
=== « <6> »
=== « <7> »
=== « <8> »
=== « <9> »
=== « <10> »
=== « <11> »