diff --git a/docs/chapter_backtracking/permutations_problem.md b/docs/chapter_backtracking/permutations_problem.md
index 444542ea3..e8d1c1a67 100644
--- a/docs/chapter_backtracking/permutations_problem.md
+++ b/docs/chapter_backtracking/permutations_problem.md
@@ -724,7 +724,7 @@ comments: true
(*selected)[i] = true
*state = append(*state, choice)
// 进行下一轮选择
- backtrackI(state, choices, selected, res)
+ backtrackII(state, choices, selected, res)
// 回退:撤销选择,恢复到之前的状态
(*selected)[i] = false
*state = (*state)[:len(*state)-1]
diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md
index c481e1ada..dad1a80f1 100644
--- a/docs/chapter_sorting/bucket_sort.md
+++ b/docs/chapter_sorting/bucket_sort.md
@@ -406,7 +406,35 @@ comments: true
=== "Ruby"
```ruby title="bucket_sort.rb"
- [class]{}-[func]{bucket_sort}
+ ### 桶排序 ###
+ def bucket_sort(nums)
+ # 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
+ k = nums.length / 2
+ buckets = Array.new(k) { [] }
+
+ # 1. 将数组元素分配到各个桶中
+ nums.each do |num|
+ # 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
+ i = (num * k).to_i
+ # 将 num 添加进桶 i
+ buckets[i] << num
+ end
+
+ # 2. 对各个桶执行排序
+ buckets.each do |bucket|
+ # 使用内置排序函数,也可以替换成其他排序算法
+ bucket.sort!
+ end
+
+ # 3. 遍历桶合并结果
+ i = 0
+ buckets.each do |bucket|
+ bucket.each do |num|
+ nums[i] = num
+ i += 1
+ end
+ end
+ end
```
=== "Zig"
diff --git a/docs/chapter_sorting/heap_sort.md b/docs/chapter_sorting/heap_sort.md
index 209112721..a477dbb04 100644
--- a/docs/chapter_sorting/heap_sort.md
+++ b/docs/chapter_sorting/heap_sort.md
@@ -582,9 +582,38 @@ comments: true
=== "Ruby"
```ruby title="heap_sort.rb"
- [class]{}-[func]{sift_down}
+ ### 堆的长度为 n ,从节点 i 开始,从顶至底堆化 ###
+ def sift_down(nums, n, i)
+ while true
+ # 判断节点 i, l, r 中值最大的节点,记为 ma
+ l = 2 * i + 1
+ r = 2 * i + 2
+ ma = i
+ ma = l if l < n && nums[l] > nums[ma]
+ ma = r if r < n && nums[r] > nums[ma]
+ # 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
+ break if ma == i
+ # 交换两节点
+ nums[i], nums[ma] = nums[ma], nums[i]
+ # 循环向下堆化
+ i = ma
+ end
+ end
- [class]{}-[func]{heap_sort}
+ ### 堆排序 ###
+ def heap_sort(nums)
+ # 建堆操作:堆化除叶节点以外的其他所有节点
+ (nums.length / 2 - 1).downto(0) do |i|
+ sift_down(nums, nums.length, i)
+ end
+ # 从堆中提取最大元素,循环 n-1 轮
+ (nums.length - 1).downto(1) do |i|
+ # 交换根节点与最右叶节点(交换首元素与尾元素)
+ nums[0], nums[i] = nums[i], nums[0]
+ # 以根节点为起点,从顶至底进行堆化
+ sift_down(nums, i, 0)
+ end
+ end
```
=== "Zig"
diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md
index 618a9ae3e..8ec1ed9af 100755
--- a/docs/chapter_sorting/merge_sort.md
+++ b/docs/chapter_sorting/merge_sort.md
@@ -630,9 +630,53 @@ comments: true
=== "Ruby"
```ruby title="merge_sort.rb"
- [class]{}-[func]{merge}
+ ### 合并左子数组和右子数组 ###
+ def merge(nums, left, mid, right)
+ # 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
+ # 创建一个临时数组 tmp,用于存放合并后的结果
+ tmp = Array.new(right - left + 1, 0)
+ # 初始化左子数组和右子数组的起始索引
+ i, j, k = left, mid + 1, 0
+ # 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
+ while i <= mid && j <= right
+ if nums[i] <= nums[j]
+ tmp[k] = nums[i]
+ i += 1
+ else
+ tmp[k] = nums[j]
+ j += 1
+ end
+ k += 1
+ end
+ # 将左子数组和右子数组的剩余元素复制到临时数组中
+ while i <= mid
+ tmp[k] = nums[i]
+ i += 1
+ k += 1
+ end
+ while j <= right
+ tmp[k] = nums[j]
+ j += 1
+ k += 1
+ end
+ # 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
+ (0...tmp.length).each do |k|
+ nums[left + k] = tmp[k]
+ end
+ end
- [class]{}-[func]{merge_sort}
+ ### 归并排序 ###
+ def merge_sort(nums, left, right)
+ # 终止条件
+ # 当子数组长度为 1 时终止递归
+ return if left >= right
+ # 划分阶段
+ mid = (left + right) / 2 # 计算中点
+ merge_sort(nums, left, mid) # 递归左子数组
+ merge_sort(nums, mid + 1, right) # 递归右子数组
+ # 合并阶段
+ merge(nums, left, mid, right)
+ end
```
=== "Zig"
diff --git a/en/docs/chapter_appendix/contribution.md b/en/docs/chapter_appendix/contribution.md
new file mode 100644
index 000000000..02e9cdeb8
--- /dev/null
+++ b/en/docs/chapter_appendix/contribution.md
@@ -0,0 +1,53 @@
+---
+comments: true
+---
+
+# 16.2 Contributing
+
+Due to the limited abilities of the author, some omissions and errors are inevitable in this book. Please understand. If you discover any typos, broken links, missing content, textual ambiguities, unclear explanations, or unreasonable text structures, please assist us in making corrections to provide readers with better quality learning resources.
+
+The GitHub IDs of all [contributors](https://github.com/krahets/hello-algo/graphs/contributors) will be displayed on the repository, web, and PDF versions of the homepage of this book to thank them for their selfless contributions to the open-source community.
+
+!!! success "The charm of open source"
+
+ The interval between two printings of a paper book is often long, making content updates very inconvenient.
+
+ In this open-source book, however, the content update cycle is shortened to just a few days or even hours.
+
+### 1. Content fine-tuning
+
+As shown in the Figure 16-3 , there is an "edit icon" in the upper right corner of each page. You can follow these steps to modify text or code.
+
+1. Click the "edit icon". If prompted to "fork this repository", please agree to do so.
+2. Modify the Markdown source file content, check the accuracy of the content, and try to keep the formatting consistent.
+3. Fill in the modification description at the bottom of the page, then click the "Propose file change" button. After the page redirects, click the "Create pull request" button to initiate the pull request.
+
+{ class="animation-figure" }
+
+
Figure 16-3 Edit page button
+
+Images cannot be directly modified and require the creation of a new [Issue](https://github.com/krahets/hello-algo/issues) or a comment to describe the problem. We will redraw and replace the images as soon as possible.
+
+### 2. Content creation
+
+If you are interested in participating in this open-source project, including translating code into other programming languages or expanding article content, then the following Pull Request workflow needs to be implemented.
+
+1. Log in to GitHub and Fork the [code repository](https://github.com/krahets/hello-algo) of this book to your personal account.
+2. Go to your Forked repository web page and use the `git clone` command to clone the repository to your local machine.
+3. Create content locally and perform complete tests to verify the correctness of the code.
+4. Commit the changes made locally, then push them to the remote repository.
+5. Refresh the repository webpage and click the "Create pull request" button to initiate the pull request.
+
+### 3. Docker deployment
+
+In the `hello-algo` root directory, execute the following Docker script to access the project at `http://localhost:8000`:
+
+```shell
+docker-compose up -d
+```
+
+Use the following command to remove the deployment:
+
+```shell
+docker-compose down
+```
diff --git a/en/docs/chapter_appendix/index.md b/en/docs/chapter_appendix/index.md
new file mode 100644
index 000000000..807cc622a
--- /dev/null
+++ b/en/docs/chapter_appendix/index.md
@@ -0,0 +1,14 @@
+---
+comments: true
+icon: material/help-circle-outline
+---
+
+# Chapter 16. Appendix
+
+{ class="cover-image" }
+
+## Chapter contents
+
+- [16.1 Installation](installation.md)
+- [16.2 Contributing](contribution.md)
+- [16.3 Terminology](terminology.md)
diff --git a/en/docs/chapter_appendix/installation.md b/en/docs/chapter_appendix/installation.md
new file mode 100644
index 000000000..4f92f40ce
--- /dev/null
+++ b/en/docs/chapter_appendix/installation.md
@@ -0,0 +1,76 @@
+---
+comments: true
+---
+
+# 16.1 Installation
+
+## 16.1.1 Install IDE
+
+We recommend using the open-source, lightweight VS Code as your local Integrated Development Environment (IDE). Visit the [VS Code official website](https://code.visualstudio.com/) and choose the version of VS Code appropriate for your operating system to download and install.
+
+{ class="animation-figure" }
+
+ Figure 16-1 Download VS Code from the official website
+
+VS Code has a powerful extension ecosystem, supporting the execution and debugging of most programming languages. For example, after installing the "Python Extension Pack," you can debug Python code. The installation steps are shown in the following figure.
+
+{ class="animation-figure" }
+
+ Figure 16-2 Install VS Code Extension Pack
+
+## 16.1.2 Install language environments
+
+### 1. Python environment
+
+1. Download and install [Miniconda3](https://docs.conda.io/en/latest/miniconda.html), requiring Python 3.10 or newer.
+2. In the VS Code extension marketplace, search for `python` and install the Python Extension Pack.
+3. (Optional) Enter `pip install black` in the command line to install the code formatting tool.
+
+### 2. C/C++ environment
+
+1. Windows systems need to install [MinGW](https://sourceforge.net/projects/mingw-w64/files/) ([Configuration tutorial](https://blog.csdn.net/qq_33698226/article/details/129031241)); MacOS comes with Clang, so no installation is necessary.
+2. In the VS Code extension marketplace, search for `c++` and install the C/C++ Extension Pack.
+3. (Optional) Open the Settings page, search for the `Clang_format_fallback Style` code formatting option, and set it to `{ BasedOnStyle: Microsoft, BreakBeforeBraces: Attach }`.
+
+### 3. Java environment
+
+1. Download and install [OpenJDK](https://jdk.java.net/18/) (version must be > JDK 9).
+2. In the VS Code extension marketplace, search for `java` and install the Extension Pack for Java.
+
+### 4. C# environment
+
+1. Download and install [.Net 8.0](https://dotnet.microsoft.com/en-us/download).
+2. In the VS Code extension marketplace, search for `C# Dev Kit` and install the C# Dev Kit ([Configuration tutorial](https://code.visualstudio.com/docs/csharp/get-started)).
+3. You can also use Visual Studio ([Installation tutorial](https://learn.microsoft.com/zh-cn/visualstudio/install/install-visual-studio?view=vs-2022)).
+
+### 5. Go environment
+
+1. Download and install [go](https://go.dev/dl/).
+2. In the VS Code extension marketplace, search for `go` and install Go.
+3. Press `Ctrl + Shift + P` to call up the command bar, enter go, choose `Go: Install/Update Tools`, select all and install.
+
+### 6. Swift environment
+
+1. Download and install [Swift](https://www.swift.org/download/).
+2. In the VS Code extension marketplace, search for `swift` and install [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang).
+
+### 7. JavaScript environment
+
+1. Download and install [Node.js](https://nodejs.org/en/).
+2. (Optional) In the VS Code extension marketplace, search for `Prettier` and install the code formatting tool.
+
+### 8. TypeScript environment
+
+1. Follow the same installation steps as the JavaScript environment.
+2. Install [TypeScript Execute (tsx)](https://github.com/privatenumber/tsx?tab=readme-ov-file#global-installation).
+3. In the VS Code extension marketplace, search for `typescript` and install [Pretty TypeScript Errors](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors).
+
+### 9. Dart environment
+
+1. Download and install [Dart](https://dart.dev/get-dart).
+2. In the VS Code extension marketplace, search for `dart` and install [Dart](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code).
+
+### 10. Rust environment
+
+1. Download and install [Rust](https://www.rust-lang.org/tools/install).
+2. In the VS Code extension marketplace, search for `rust` and install [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).
diff --git a/en/docs/chapter_appendix/terminology.md b/en/docs/chapter_appendix/terminology.md
new file mode 100644
index 000000000..e92982aaa
--- /dev/null
+++ b/en/docs/chapter_appendix/terminology.md
@@ -0,0 +1,145 @@
+---
+comments: true
+---
+
+# 16.3 Glossary
+
+The Table 16-1 lists the important terms that appear in the book, and it is worth noting the following points.
+
+- It is recommended to remember the English names of the terms to facilitate reading English literature.
+- Some terms have different names in Simplified and Traditional Chinese.
+
+ Table 16-1 Important Terms in Data Structures and Algorithms
+
+
+
+| English | 简体中文 | 繁体中文 |
+| ------------------------------ | -------------- | -------------- |
+| algorithm | 算法 | 演算法 |
+| data structure | 数据结构 | 資料結構 |
+| code | 代码 | 程式碼 |
+| file | 文件 | 檔案 |
+| function | 函数 | 函式 |
+| method | 方法 | 方法 |
+| variable | 变量 | 變數 |
+| asymptotic complexity analysis | 渐近复杂度分析 | 漸近複雜度分析 |
+| time complexity | 时间复杂度 | 時間複雜度 |
+| space complexity | 空间复杂度 | 空間複雜度 |
+| loop | 循环 | 迴圈 |
+| iteration | 迭代 | 迭代 |
+| recursion | 递归 | 遞迴 |
+| tail recursion | 尾递归 | 尾遞迴 |
+| recursion tree | 递归树 | 遞迴樹 |
+| big-$O$ notation | 大 $O$ 记号 | 大 $O$ 記號 |
+| asymptotic upper bound | 渐近上界 | 漸近上界 |
+| sign-magnitude | 原码 | 原碼 |
+| 1’s complement | 反码 | 一補數 |
+| 2’s complement | 补码 | 二補數 |
+| array | 数组 | 陣列 |
+| index | 索引 | 索引 |
+| linked list | 链表 | 鏈結串列 |
+| linked list node, list node | 链表节点 | 鏈結串列節點 |
+| head node | 头节点 | 頭節點 |
+| tail node | 尾节点 | 尾節點 |
+| list | 列表 | 串列 |
+| dynamic array | 动态数组 | 動態陣列 |
+| hard disk | 硬盘 | 硬碟 |
+| random-access memory (RAM) | 内存 | 記憶體 |
+| cache memory | 缓存 | 快取 |
+| cache miss | 缓存未命中 | 快取未命中 |
+| cache hit rate | 缓存命中率 | 快取命中率 |
+| stack | 栈 | 堆疊 |
+| top of the stack | 栈顶 | 堆疊頂 |
+| bottom of the stack | 栈底 | 堆疊底 |
+| queue | 队列 | 佇列 |
+| double-ended queue | 双向队列 | 雙向佇列 |
+| front of the queue | 队首 | 佇列首 |
+| rear of the queue | 队尾 | 佇列尾 |
+| hash table | 哈希表 | 雜湊表 |
+| hash set | 哈希集合 | 雜湊集合 |
+| bucket | 桶 | 桶 |
+| hash function | 哈希函数 | 雜湊函式 |
+| hash collision | 哈希冲突 | 雜湊衝突 |
+| load factor | 负载因子 | 負載因子 |
+| separate chaining | 链式地址 | 鏈結位址 |
+| open addressing | 开放寻址 | 開放定址 |
+| linear probing | 线性探测 | 線性探查 |
+| lazy deletion | 懒删除 | 懶刪除 |
+| binary tree | 二叉树 | 二元樹 |
+| tree node | 树节点 | 樹節點 |
+| left-child node | 左子节点 | 左子節點 |
+| right-child node | 右子节点 | 右子節點 |
+| parent node | 父节点 | 父節點 |
+| left subtree | 左子树 | 左子樹 |
+| right subtree | 右子树 | 右子樹 |
+| root node | 根节点 | 根節點 |
+| leaf node | 叶节点 | 葉節點 |
+| edge | 边 | 邊 |
+| level | 层 | 層 |
+| degree | 度 | 度 |
+| height | 高度 | 高度 |
+| depth | 深度 | 深度 |
+| perfect binary tree | 完美二叉树 | 完美二元樹 |
+| complete binary tree | 完全二叉树 | 完全二元樹 |
+| full binary tree | 完满二叉树 | 完滿二元樹 |
+| balanced binary tree | 平衡二叉树 | 平衡二元樹 |
+| binary search tree | 二叉搜索树 | 二元搜尋樹 |
+| AVL tree | AVL 树 | AVL 樹 |
+| red-black tree | 红黑树 | 紅黑樹 |
+| level-order traversal | 层序遍历 | 層序走訪 |
+| breadth-first traversal | 广度优先遍历 | 廣度優先走訪 |
+| depth-first traversal | 深度优先遍历 | 深度優先走訪 |
+| binary search tree | 二叉搜索树 | 二元搜尋樹 |
+| balanced binary search tree | 平衡二叉搜索树 | 平衡二元搜尋樹 |
+| balance factor | 平衡因子 | 平衡因子 |
+| heap | 堆 | 堆積 |
+| max heap | 大顶堆 | 大頂堆積 |
+| min heap | 小顶堆 | 小頂堆積 |
+| priority queue | 优先队列 | 優先佇列 |
+| heapify | 堆化 | 堆積化 |
+| top-$k$ problem | Top-$k$ 问题 | Top-$k$ 問題 |
+| graph | 图 | 圖 |
+| vertex | 顶点 | 頂點 |
+| undirected graph | 无向图 | 無向圖 |
+| directed graph | 有向图 | 有向圖 |
+| connected graph | 连通图 | 連通圖 |
+| disconnected graph | 非连通图 | 非連通圖 |
+| weighted graph | 有权图 | 有權圖 |
+| adjacency | 邻接 | 鄰接 |
+| path | 路径 | 路徑 |
+| in-degree | 入度 | 入度 |
+| out-degree | 出度 | 出度 |
+| adjacency matrix | 邻接矩阵 | 鄰接矩陣 |
+| adjacency list | 邻接表 | 鄰接表 |
+| breadth-first search | 广度优先搜索 | 廣度優先搜尋 |
+| depth-first search | 深度优先搜索 | 深度優先搜尋 |
+| binary search | 二分查找 | 二分搜尋 |
+| searching algorithm | 搜索算法 | 搜尋演算法 |
+| sorting algorithm | 排序算法 | 排序演算法 |
+| selection sort | 选择排序 | 選擇排序 |
+| bubble sort | 冒泡排序 | 泡沫排序 |
+| insertion sort | 插入排序 | 插入排序 |
+| quick sort | 快速排序 | 快速排序 |
+| merge sort | 归并排序 | 合併排序 |
+| heap sort | 堆排序 | 堆積排序 |
+| bucket sort | 桶排序 | 桶排序 |
+| counting sort | 计数排序 | 計數排序 |
+| radix sort | 基数排序 | 基數排序 |
+| divide and conquer | 分治 | 分治 |
+| hanota problem | 汉诺塔问题 | 河內塔問題 |
+| backtracking algorithm | 回溯算法 | 回溯演算法 |
+| constraint | 约束 | 約束 |
+| solution | 解 | 解 |
+| state | 状态 | 狀態 |
+| pruning | 剪枝 | 剪枝 |
+| permutations problem | 全排列问题 | 全排列問題 |
+| subset-sum problem | 子集和问题 | 子集合問題 |
+| $n$-queens problem | $n$ 皇后问题 | $n$ 皇后問題 |
+| dynamic programming | 动态规划 | 動態規劃 |
+| initial state | 初始状态 | 初始狀態 |
+| state-transition equation | 状态转移方程 | 狀態轉移方程 |
+| knapsack problem | 背包问题 | 背包問題 |
+| edit distance problem | 编辑距离问题 | 編輯距離問題 |
+| greedy algorithm | 贪心算法 | 貪婪演算法 |
+
+
diff --git a/en/docs/chapter_array_and_linkedlist/index.md b/en/docs/chapter_array_and_linkedlist/index.md
index 9a34cb915..fd2e8796b 100644
--- a/en/docs/chapter_array_and_linkedlist/index.md
+++ b/en/docs/chapter_array_and_linkedlist/index.md
@@ -18,5 +18,5 @@ icon: material/view-list-outline
- [4.1 Array](array.md)
- [4.2 Linked list](linked_list.md)
- [4.3 List](list.md)
-- [4.4 Memory and cache](ram_and_cache.md)
+- [4.4 Memory and cache *](ram_and_cache.md)
- [4.5 Summary](summary.md)
diff --git a/en/docs/chapter_backtracking/backtracking_algorithm.md b/en/docs/chapter_backtracking/backtracking_algorithm.md
new file mode 100644
index 000000000..1ed5d0be0
--- /dev/null
+++ b/en/docs/chapter_backtracking/backtracking_algorithm.md
@@ -0,0 +1,1951 @@
+---
+comments: true
+---
+
+# 13.1 Backtracking algorithms
+
+Backtracking algorithm is a method to solve problems by exhaustive search, where the core idea is to start from an initial state and brute force all possible solutions, recording the correct ones until a solution is found or all possible choices are exhausted without finding a solution.
+
+Backtracking typically employs "depth-first search" to traverse the solution space. In the "Binary Tree" chapter, we mentioned that preorder, inorder, and postorder traversals are all depth-first searches. Next, we use preorder traversal to construct a backtracking problem to gradually understand the workings of the backtracking algorithm.
+
+!!! question "Example One"
+
+ Given a binary tree, search and record all nodes with a value of $7$, please return a list of nodes.
+
+For this problem, we traverse this tree in preorder and check if the current node's value is $7$. If it is, we add the node's value to the result list `res`. The relevant process is shown in the following diagram and code:
+
+=== "Python"
+
+ ```python title="preorder_traversal_i_compact.py"
+ def pre_order(root: TreeNode):
+ """前序遍历:例题一"""
+ if root is None:
+ return
+ if root.val == 7:
+ # 记录解
+ res.append(root)
+ pre_order(root.left)
+ pre_order(root.right)
+ ```
+
+=== "C++"
+
+ ```cpp title="preorder_traversal_i_compact.cpp"
+ /* 前序遍历:例题一 */
+ void preOrder(TreeNode *root) {
+ if (root == nullptr) {
+ return;
+ }
+ if (root->val == 7) {
+ // 记录解
+ res.push_back(root);
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ }
+ ```
+
+=== "Java"
+
+ ```java title="preorder_traversal_i_compact.java"
+ /* 前序遍历:例题一 */
+ void preOrder(TreeNode root) {
+ if (root == null) {
+ return;
+ }
+ if (root.val == 7) {
+ // 记录解
+ res.add(root);
+ }
+ preOrder(root.left);
+ preOrder(root.right);
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="preorder_traversal_i_compact.cs"
+ /* 前序遍历:例题一 */
+ void PreOrder(TreeNode? root) {
+ if (root == null) {
+ return;
+ }
+ if (root.val == 7) {
+ // 记录解
+ res.Add(root);
+ }
+ PreOrder(root.left);
+ PreOrder(root.right);
+ }
+ ```
+
+=== "Go"
+
+ ```go title="preorder_traversal_i_compact.go"
+ /* 前序遍历:例题一 */
+ func preOrderI(root *TreeNode, res *[]*TreeNode) {
+ if root == nil {
+ return
+ }
+ if (root.Val).(int) == 7 {
+ // 记录解
+ *res = append(*res, root)
+ }
+ preOrderI(root.Left, res)
+ preOrderI(root.Right, res)
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="preorder_traversal_i_compact.swift"
+ /* 前序遍历:例题一 */
+ func preOrder(root: TreeNode?) {
+ guard let root = root else {
+ return
+ }
+ if root.val == 7 {
+ // 记录解
+ res.append(root)
+ }
+ preOrder(root: root.left)
+ preOrder(root: root.right)
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="preorder_traversal_i_compact.js"
+ /* 前序遍历:例题一 */
+ function preOrder(root, res) {
+ if (root === null) {
+ return;
+ }
+ if (root.val === 7) {
+ // 记录解
+ res.push(root);
+ }
+ preOrder(root.left, res);
+ preOrder(root.right, res);
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="preorder_traversal_i_compact.ts"
+ /* 前序遍历:例题一 */
+ function preOrder(root: TreeNode | null, res: TreeNode[]): void {
+ if (root === null) {
+ return;
+ }
+ if (root.val === 7) {
+ // 记录解
+ res.push(root);
+ }
+ preOrder(root.left, res);
+ preOrder(root.right, res);
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="preorder_traversal_i_compact.dart"
+ /* 前序遍历:例题一 */
+ void preOrder(TreeNode? root, List res) {
+ if (root == null) {
+ return;
+ }
+ if (root.val == 7) {
+ // 记录解
+ res.add(root);
+ }
+ preOrder(root.left, res);
+ preOrder(root.right, res);
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="preorder_traversal_i_compact.rs"
+ /* 前序遍历:例题一 */
+ fn pre_order(res: &mut Vec>>, root: Option>>) {
+ if root.is_none() {
+ return;
+ }
+ if let Some(node) = root {
+ if node.borrow().val == 7 {
+ // 记录解
+ res.push(node.clone());
+ }
+ pre_order(res, node.borrow().left.clone());
+ pre_order(res, node.borrow().right.clone());
+ }
+ }
+ ```
+
+=== "C"
+
+ ```c title="preorder_traversal_i_compact.c"
+ /* 前序遍历:例题一 */
+ void preOrder(TreeNode *root) {
+ if (root == NULL) {
+ return;
+ }
+ if (root->val == 7) {
+ // 记录解
+ res[resSize++] = root;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="preorder_traversal_i_compact.kt"
+ /* 前序遍历:例题一 */
+ fun preOrder(root: TreeNode?) {
+ if (root == null) {
+ return
+ }
+ if (root._val == 7) {
+ // 记录解
+ res!!.add(root)
+ }
+ preOrder(root.left)
+ preOrder(root.right)
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="preorder_traversal_i_compact.rb"
+ [class]{}-[func]{pre_order}
+ ```
+
+=== "Zig"
+
+ ```zig title="preorder_traversal_i_compact.zig"
+ [class]{}-[func]{preOrder}
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+{ class="animation-figure" }
+
+ Figure 13-1 Searching nodes in preorder traversal
+
+## 13.1.1 Trying and retreating
+
+**The reason it is called backtracking is that the algorithm uses a "try" and "retreat" strategy when searching the solution space**. When the algorithm encounters a state where it can no longer progress or fails to achieve a satisfying solution, it undoes the previous choice, reverts to the previous state, and tries other possible choices.
+
+For Example One, visiting each node represents a "try", and passing a leaf node or returning to the parent node's `return` represents "retreat".
+
+It's worth noting that **retreat is not merely about function returns**. We expand slightly on Example One for clarification.
+
+!!! question "Example Two"
+
+ In a binary tree, search for all nodes with a value of $7$ and **please return the paths from the root node to these nodes**.
+
+Based on the code from Example One, we need to use a list `path` to record the visited node paths. When a node with a value of $7$ is reached, we copy `path` and add it to the result list `res`. After the traversal, `res` holds all the solutions. The code is as shown:
+
+=== "Python"
+
+ ```python title="preorder_traversal_ii_compact.py"
+ def pre_order(root: TreeNode):
+ """前序遍历:例题二"""
+ if root is None:
+ return
+ # 尝试
+ path.append(root)
+ if root.val == 7:
+ # 记录解
+ res.append(list(path))
+ pre_order(root.left)
+ pre_order(root.right)
+ # 回退
+ path.pop()
+ ```
+
+=== "C++"
+
+ ```cpp title="preorder_traversal_ii_compact.cpp"
+ /* 前序遍历:例题二 */
+ void preOrder(TreeNode *root) {
+ if (root == nullptr) {
+ return;
+ }
+ // 尝试
+ path.push_back(root);
+ if (root->val == 7) {
+ // 记录解
+ res.push_back(path);
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // 回退
+ path.pop_back();
+ }
+ ```
+
+=== "Java"
+
+ ```java title="preorder_traversal_ii_compact.java"
+ /* 前序遍历:例题二 */
+ void preOrder(TreeNode root) {
+ if (root == null) {
+ return;
+ }
+ // 尝试
+ path.add(root);
+ if (root.val == 7) {
+ // 记录解
+ res.add(new ArrayList<>(path));
+ }
+ preOrder(root.left);
+ preOrder(root.right);
+ // 回退
+ path.remove(path.size() - 1);
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="preorder_traversal_ii_compact.cs"
+ /* 前序遍历:例题二 */
+ void PreOrder(TreeNode? root) {
+ if (root == null) {
+ return;
+ }
+ // 尝试
+ path.Add(root);
+ if (root.val == 7) {
+ // 记录解
+ res.Add(new List(path));
+ }
+ PreOrder(root.left);
+ PreOrder(root.right);
+ // 回退
+ path.RemoveAt(path.Count - 1);
+ }
+ ```
+
+=== "Go"
+
+ ```go title="preorder_traversal_ii_compact.go"
+ /* 前序遍历:例题二 */
+ func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
+ if root == nil {
+ return
+ }
+ // 尝试
+ *path = append(*path, root)
+ if root.Val.(int) == 7 {
+ // 记录解
+ *res = append(*res, append([]*TreeNode{}, *path...))
+ }
+ preOrderII(root.Left, res, path)
+ preOrderII(root.Right, res, path)
+ // 回退
+ *path = (*path)[:len(*path)-1]
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="preorder_traversal_ii_compact.swift"
+ /* 前序遍历:例题二 */
+ func preOrder(root: TreeNode?) {
+ guard let root = root else {
+ return
+ }
+ // 尝试
+ path.append(root)
+ if root.val == 7 {
+ // 记录解
+ res.append(path)
+ }
+ preOrder(root: root.left)
+ preOrder(root: root.right)
+ // 回退
+ path.removeLast()
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="preorder_traversal_ii_compact.js"
+ /* 前序遍历:例题二 */
+ function preOrder(root, path, res) {
+ if (root === null) {
+ return;
+ }
+ // 尝试
+ path.push(root);
+ if (root.val === 7) {
+ // 记录解
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // 回退
+ path.pop();
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="preorder_traversal_ii_compact.ts"
+ /* 前序遍历:例题二 */
+ function preOrder(
+ root: TreeNode | null,
+ path: TreeNode[],
+ res: TreeNode[][]
+ ): void {
+ if (root === null) {
+ return;
+ }
+ // 尝试
+ path.push(root);
+ if (root.val === 7) {
+ // 记录解
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // 回退
+ path.pop();
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="preorder_traversal_ii_compact.dart"
+ /* 前序遍历:例题二 */
+ void preOrder(
+ TreeNode? root,
+ List path,
+ List> res,
+ ) {
+ if (root == null) {
+ return;
+ }
+
+ // 尝试
+ path.add(root);
+ if (root.val == 7) {
+ // 记录解
+ res.add(List.from(path));
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // 回退
+ path.removeLast();
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="preorder_traversal_ii_compact.rs"
+ /* 前序遍历:例题二 */
+ fn pre_order(
+ res: &mut Vec>>>,
+ path: &mut Vec>>,
+ root: Option>>,
+ ) {
+ if root.is_none() {
+ return;
+ }
+ if let Some(node) = root {
+ // 尝试
+ path.push(node.clone());
+ if node.borrow().val == 7 {
+ // 记录解
+ res.push(path.clone());
+ }
+ pre_order(res, path, node.borrow().left.clone());
+ pre_order(res, path, node.borrow().right.clone());
+ // 回退
+ path.remove(path.len() - 1);
+ }
+ }
+ ```
+
+=== "C"
+
+ ```c title="preorder_traversal_ii_compact.c"
+ /* 前序遍历:例题二 */
+ void preOrder(TreeNode *root) {
+ if (root == NULL) {
+ return;
+ }
+ // 尝试
+ path[pathSize++] = root;
+ if (root->val == 7) {
+ // 记录解
+ for (int i = 0; i < pathSize; ++i) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // 回退
+ pathSize--;
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="preorder_traversal_ii_compact.kt"
+ /* 前序遍历:例题二 */
+ fun preOrder(root: TreeNode?) {
+ if (root == null) {
+ return
+ }
+ // 尝试
+ path!!.add(root)
+ if (root._val == 7) {
+ // 记录解
+ res!!.add(path!!.toMutableList())
+ }
+ preOrder(root.left)
+ preOrder(root.right)
+ // 回退
+ path!!.removeAt(path!!.size - 1)
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="preorder_traversal_ii_compact.rb"
+ [class]{}-[func]{pre_order}
+ ```
+
+=== "Zig"
+
+ ```zig title="preorder_traversal_ii_compact.zig"
+ [class]{}-[func]{preOrder}
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+In each "try", we record the path by adding the current node to `path`; before "retreating", we need to pop the node from `path` **to restore the state before this attempt**.
+
+Observe the process shown below, **we can understand trying and retreating as "advancing" and "undoing"**, two operations that are reverse to each other.
+
+=== "<1>"
+ { class="animation-figure" }
+
+=== "<2>"
+ { class="animation-figure" }
+
+=== "<3>"
+ { class="animation-figure" }
+
+=== "<4>"
+ { class="animation-figure" }
+
+=== "<5>"
+ { class="animation-figure" }
+
+=== "<6>"
+ { class="animation-figure" }
+
+=== "<7>"
+ { class="animation-figure" }
+
+=== "<8>"
+ { class="animation-figure" }
+
+=== "<9>"
+ { class="animation-figure" }
+
+=== "<10>"
+ { class="animation-figure" }
+
+=== "<11>"
+ { class="animation-figure" }
+
+ Figure 13-2 Trying and retreating
+
+## 13.1.2 Pruning
+
+Complex backtracking problems usually involve one or more constraints, **which are often used for "pruning"**.
+
+!!! question "Example Three"
+
+ In a binary tree, search for all nodes with a value of $7$ and return the paths from the root to these nodes, **requiring that the paths do not contain nodes with a value of $3$**.
+
+To meet the above constraints, **we need to add a pruning operation**: during the search process, if a node with a value of $3$ is encountered, it returns early, discontinuing further search. The code is as shown:
+
+=== "Python"
+
+ ```python title="preorder_traversal_iii_compact.py"
+ def pre_order(root: TreeNode):
+ """前序遍历:例题三"""
+ # 剪枝
+ if root is None or root.val == 3:
+ return
+ # 尝试
+ path.append(root)
+ if root.val == 7:
+ # 记录解
+ res.append(list(path))
+ pre_order(root.left)
+ pre_order(root.right)
+ # 回退
+ path.pop()
+ ```
+
+=== "C++"
+
+ ```cpp title="preorder_traversal_iii_compact.cpp"
+ /* 前序遍历:例题三 */
+ void preOrder(TreeNode *root) {
+ // 剪枝
+ if (root == nullptr || root->val == 3) {
+ return;
+ }
+ // 尝试
+ path.push_back(root);
+ if (root->val == 7) {
+ // 记录解
+ res.push_back(path);
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // 回退
+ path.pop_back();
+ }
+ ```
+
+=== "Java"
+
+ ```java title="preorder_traversal_iii_compact.java"
+ /* 前序遍历:例题三 */
+ void preOrder(TreeNode root) {
+ // 剪枝
+ if (root == null || root.val == 3) {
+ return;
+ }
+ // 尝试
+ path.add(root);
+ if (root.val == 7) {
+ // 记录解
+ res.add(new ArrayList<>(path));
+ }
+ preOrder(root.left);
+ preOrder(root.right);
+ // 回退
+ path.remove(path.size() - 1);
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="preorder_traversal_iii_compact.cs"
+ /* 前序遍历:例题三 */
+ void PreOrder(TreeNode? root) {
+ // 剪枝
+ if (root == null || root.val == 3) {
+ return;
+ }
+ // 尝试
+ path.Add(root);
+ if (root.val == 7) {
+ // 记录解
+ res.Add(new List(path));
+ }
+ PreOrder(root.left);
+ PreOrder(root.right);
+ // 回退
+ path.RemoveAt(path.Count - 1);
+ }
+ ```
+
+=== "Go"
+
+ ```go title="preorder_traversal_iii_compact.go"
+ /* 前序遍历:例题三 */
+ func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
+ // 剪枝
+ if root == nil || root.Val == 3 {
+ return
+ }
+ // 尝试
+ *path = append(*path, root)
+ if root.Val.(int) == 7 {
+ // 记录解
+ *res = append(*res, append([]*TreeNode{}, *path...))
+ }
+ preOrderIII(root.Left, res, path)
+ preOrderIII(root.Right, res, path)
+ // 回退
+ *path = (*path)[:len(*path)-1]
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="preorder_traversal_iii_compact.swift"
+ /* 前序遍历:例题三 */
+ func preOrder(root: TreeNode?) {
+ // 剪枝
+ guard let root = root, root.val != 3 else {
+ return
+ }
+ // 尝试
+ path.append(root)
+ if root.val == 7 {
+ // 记录解
+ res.append(path)
+ }
+ preOrder(root: root.left)
+ preOrder(root: root.right)
+ // 回退
+ path.removeLast()
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="preorder_traversal_iii_compact.js"
+ /* 前序遍历:例题三 */
+ function preOrder(root, path, res) {
+ // 剪枝
+ if (root === null || root.val === 3) {
+ return;
+ }
+ // 尝试
+ path.push(root);
+ if (root.val === 7) {
+ // 记录解
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // 回退
+ path.pop();
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="preorder_traversal_iii_compact.ts"
+ /* 前序遍历:例题三 */
+ function preOrder(
+ root: TreeNode | null,
+ path: TreeNode[],
+ res: TreeNode[][]
+ ): void {
+ // 剪枝
+ if (root === null || root.val === 3) {
+ return;
+ }
+ // 尝试
+ path.push(root);
+ if (root.val === 7) {
+ // 记录解
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // 回退
+ path.pop();
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="preorder_traversal_iii_compact.dart"
+ /* 前序遍历:例题三 */
+ void preOrder(
+ TreeNode? root,
+ List path,
+ List> res,
+ ) {
+ if (root == null || root.val == 3) {
+ return;
+ }
+
+ // 尝试
+ path.add(root);
+ if (root.val == 7) {
+ // 记录解
+ res.add(List.from(path));
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // 回退
+ path.removeLast();
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="preorder_traversal_iii_compact.rs"
+ /* 前序遍历:例题三 */
+ fn pre_order(
+ res: &mut Vec>>>,
+ path: &mut Vec>>,
+ root: Option>>,
+ ) {
+ // 剪枝
+ if root.is_none() || root.as_ref().unwrap().borrow().val == 3 {
+ return;
+ }
+ if let Some(node) = root {
+ // 尝试
+ path.push(node.clone());
+ if node.borrow().val == 7 {
+ // 记录解
+ res.push(path.clone());
+ }
+ pre_order(res, path, node.borrow().left.clone());
+ pre_order(res, path, node.borrow().right.clone());
+ // 回退
+ path.remove(path.len() - 1);
+ }
+ }
+ ```
+
+=== "C"
+
+ ```c title="preorder_traversal_iii_compact.c"
+ /* 前序遍历:例题三 */
+ void preOrder(TreeNode *root) {
+ // 剪枝
+ if (root == NULL || root->val == 3) {
+ return;
+ }
+ // 尝试
+ path[pathSize++] = root;
+ if (root->val == 7) {
+ // 记录解
+ for (int i = 0; i < pathSize; i++) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // 回退
+ pathSize--;
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="preorder_traversal_iii_compact.kt"
+ /* 前序遍历:例题三 */
+ fun preOrder(root: TreeNode?) {
+ // 剪枝
+ if (root == null || root._val == 3) {
+ return
+ }
+ // 尝试
+ path!!.add(root)
+ if (root._val == 7) {
+ // 记录解
+ res!!.add(path!!.toMutableList())
+ }
+ preOrder(root.left)
+ preOrder(root.right)
+ // 回退
+ path!!.removeAt(path!!.size - 1)
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="preorder_traversal_iii_compact.rb"
+ [class]{}-[func]{pre_order}
+ ```
+
+=== "Zig"
+
+ ```zig title="preorder_traversal_iii_compact.zig"
+ [class]{}-[func]{preOrder}
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+"Pruning" is a very vivid noun. As shown in the diagram below, in the search process, **we "cut off" the search branches that do not meet the constraints**, avoiding many meaningless attempts, thus enhancing the search efficiency.
+
+{ class="animation-figure" }
+
+ Figure 13-3 Pruning based on constraints
+
+## 13.1.3 Framework code
+
+Next, we attempt to distill the main framework of "trying, retreating, and pruning" from backtracking to enhance the code's universality.
+
+In the following framework code, `state` represents the current state of the problem, `choices` represents the choices available under the current state:
+
+=== "Python"
+
+ ```python title=""
+ def backtrack(state: State, choices: list[choice], res: list[state]):
+ """Backtracking algorithm framework"""
+ # Check if it's a solution
+ if is_solution(state):
+ # Record the solution
+ record_solution(state, res)
+ # Stop searching
+ return
+ # Iterate through all choices
+ for choice in choices:
+ # Pruning: check if the choice is valid
+ if is_valid(state, choice):
+ # Try: make a choice, update the state
+ make_choice(state, choice)
+ backtrack(state, choices, res)
+ # Retreat: undo the choice, revert to the previous state
+ undo_choice(state, choice)
+ ```
+
+=== "C++"
+
+ ```cpp title=""
+ /* Backtracking algorithm framework */
+ void backtrack(State *state, vector &choices, vector &res) {
+ // Check if it's a solution
+ if (isSolution(state)) {
+ // Record the solution
+ recordSolution(state, res);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ for (Choice choice : choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Try: make a choice, update the state
+ makeChoice(state, choice);
+ backtrack(state, choices, res);
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "Java"
+
+ ```java title=""
+ /* Backtracking algorithm framework */
+ void backtrack(State state, List choices, List res) {
+ // Check if it's a solution
+ if (isSolution(state)) {
+ // Record the solution
+ recordSolution(state, res);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ for (Choice choice : choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Try: make a choice, update the state
+ makeChoice(state, choice);
+ backtrack(state, choices, res);
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title=""
+ /* Backtracking algorithm framework */
+ void Backtrack(State state, List choices, List res) {
+ // Check if it's a solution
+ if (IsSolution(state)) {
+ // Record the solution
+ RecordSolution(state, res);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ foreach (Choice choice in choices) {
+ // Pruning: check if the choice is valid
+ if (IsValid(state, choice)) {
+ // Try: make a choice, update the state
+ MakeChoice(state, choice);
+ Backtrack(state, choices, res);
+ // Retreat: undo the choice, revert to the previous state
+ UndoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "Go"
+
+ ```go title=""
+ /* Backtracking algorithm framework */
+ func backtrack(state *State, choices []Choice, res *[]State) {
+ // Check if it's a solution
+ if isSolution(state) {
+ // Record the solution
+ recordSolution(state, res)
+ // Stop searching
+ return
+ }
+ // Iterate through all choices
+ for _, choice := range choices {
+ // Pruning: check if the choice is valid
+ if isValid(state, choice) {
+ // Try: make a choice, update the state
+ makeChoice(state, choice)
+ backtrack(state, choices, res)
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, choice)
+ }
+ }
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title=""
+ /* Backtracking algorithm framework */
+ func backtrack(state: inout State, choices: [Choice], res: inout [State]) {
+ // Check if it's a solution
+ if isSolution(state: state) {
+ // Record the solution
+ recordSolution(state: state, res: &res)
+ // Stop searching
+ return
+ }
+ // Iterate through all choices
+ for choice in choices {
+ // Pruning: check if the choice is valid
+ if isValid(state: state, choice: choice) {
+ // Try: make a choice, update the state
+ makeChoice(state: &state, choice: choice)
+ backtrack(state: &state, choices: choices, res: &res)
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state: &state, choice: choice)
+ }
+ }
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title=""
+ /* Backtracking algorithm framework */
+ function backtrack(state, choices, res) {
+ // Check if it's a solution
+ if (isSolution(state)) {
+ // Record the solution
+ recordSolution(state, res);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ for (let choice of choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Try: make a choice, update the state
+ makeChoice(state, choice);
+ backtrack(state, choices, res);
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title=""
+ /* Backtracking algorithm framework */
+ function backtrack(state: State, choices: Choice[], res: State[]): void {
+ // Check if it's a solution
+ if (isSolution(state)) {
+ // Record the solution
+ recordSolution(state, res);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ for (let choice of choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Try: make a choice, update the state
+ makeChoice(state, choice);
+ backtrack(state, choices, res);
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title=""
+ /* Backtracking algorithm framework */
+ void backtrack(State state, List, List res) {
+ // Check if it's a solution
+ if (isSolution(state)) {
+ // Record the solution
+ recordSolution(state, res);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ for (Choice choice in choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Try: make a choice, update the state
+ makeChoice(state, choice);
+ backtrack(state, choices, res);
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title=""
+ /* Backtracking algorithm framework */
+ fn backtrack(state: &mut State, choices: &Vec, res: &mut Vec) {
+ // Check if it's a solution
+ if is_solution(state) {
+ // Record the solution
+ record_solution(state, res);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ for choice in choices {
+ // Pruning: check if the choice is valid
+ if is_valid(state, choice) {
+ // Try: make a choice, update the state
+ make_choice(state, choice);
+ backtrack(state, choices, res);
+ // Retreat: undo the choice, revert to the previous state
+ undo_choice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "C"
+
+ ```c title=""
+ /* Backtracking algorithm framework */
+ void backtrack(State *state, Choice *choices, int numChoices, State *res, int numRes) {
+ // Check if it's a solution
+ if (isSolution(state)) {
+ // Record the solution
+ recordSolution(state, res, numRes);
+ // Stop searching
+ return;
+ }
+ // Iterate through all choices
+ for (int i = 0; i < numChoices; i++) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, &choices[i])) {
+ // Try: make a choice, update the state
+ makeChoice(state, &choices[i]);
+ backtrack(state, choices, numChoices, res, numRes);
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, &choices[i]);
+ }
+ }
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title=""
+ /* Backtracking algorithm framework */
+ fun backtrack(state: State?, choices: List, res: List?) {
+ // Check if it's a solution
+ if (isSolution(state)) {
+ // Record the solution
+ recordSolution(state, res)
+ // Stop searching
+ return
+ }
+ // Iterate through all choices
+ for (choice in choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Try: make a choice, update the state
+ makeChoice(state, choice)
+ backtrack(state, choices, res)
+ // Retreat: undo the choice, revert to the previous state
+ undoChoice(state, choice)
+ }
+ }
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title=""
+
+ ```
+
+=== "Zig"
+
+ ```zig title=""
+
+ ```
+
+Next, we solve Example Three based on the framework code. The `state` is the node traversal path, `choices` are the current node's left and right children, and the result `res` is the list of paths:
+
+=== "Python"
+
+ ```python title="preorder_traversal_iii_template.py"
+ def is_solution(state: list[TreeNode]) -> bool:
+ """判断当前状态是否为解"""
+ return state and state[-1].val == 7
+
+ def record_solution(state: list[TreeNode], res: list[list[TreeNode]]):
+ """记录解"""
+ res.append(list(state))
+
+ def is_valid(state: list[TreeNode], choice: TreeNode) -> bool:
+ """判断在当前状态下,该选择是否合法"""
+ return choice is not None and choice.val != 3
+
+ def make_choice(state: list[TreeNode], choice: TreeNode):
+ """更新状态"""
+ state.append(choice)
+
+ def undo_choice(state: list[TreeNode], choice: TreeNode):
+ """恢复状态"""
+ state.pop()
+
+ def backtrack(
+ state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]]
+ ):
+ """回溯算法:例题三"""
+ # 检查是否为解
+ if is_solution(state):
+ # 记录解
+ record_solution(state, res)
+ # 遍历所有选择
+ for choice in choices:
+ # 剪枝:检查选择是否合法
+ if is_valid(state, choice):
+ # 尝试:做出选择,更新状态
+ make_choice(state, choice)
+ # 进行下一轮选择
+ backtrack(state, [choice.left, choice.right], res)
+ # 回退:撤销选择,恢复到之前的状态
+ undo_choice(state, choice)
+ ```
+
+=== "C++"
+
+ ```cpp title="preorder_traversal_iii_template.cpp"
+ /* 判断当前状态是否为解 */
+ bool isSolution(vector &state) {
+ return !state.empty() && state.back()->val == 7;
+ }
+
+ /* 记录解 */
+ void recordSolution(vector &state, vector> &res) {
+ res.push_back(state);
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ bool isValid(vector &state, TreeNode *choice) {
+ return choice != nullptr && choice->val != 3;
+ }
+
+ /* 更新状态 */
+ void makeChoice(vector &state, TreeNode *choice) {
+ state.push_back(choice);
+ }
+
+ /* 恢复状态 */
+ void undoChoice(vector &state, TreeNode *choice) {
+ state.pop_back();
+ }
+
+ /* 回溯算法:例题三 */
+ void backtrack(vector &state, vector &choices, vector> &res) {
+ // 检查是否为解
+ if (isSolution(state)) {
+ // 记录解
+ recordSolution(state, res);
+ }
+ // 遍历所有选择
+ for (TreeNode *choice : choices) {
+ // 剪枝:检查选择是否合法
+ if (isValid(state, choice)) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state, choice);
+ // 进行下一轮选择
+ vector nextChoices{choice->left, choice->right};
+ backtrack(state, nextChoices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "Java"
+
+ ```java title="preorder_traversal_iii_template.java"
+ /* 判断当前状态是否为解 */
+ boolean isSolution(List state) {
+ return !state.isEmpty() && state.get(state.size() - 1).val == 7;
+ }
+
+ /* 记录解 */
+ void recordSolution(List state, List> res) {
+ res.add(new ArrayList<>(state));
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ boolean isValid(List state, TreeNode choice) {
+ return choice != null && choice.val != 3;
+ }
+
+ /* 更新状态 */
+ void makeChoice(List state, TreeNode choice) {
+ state.add(choice);
+ }
+
+ /* 恢复状态 */
+ void undoChoice(List state, TreeNode choice) {
+ state.remove(state.size() - 1);
+ }
+
+ /* 回溯算法:例题三 */
+ void backtrack(List state, List choices, List> res) {
+ // 检查是否为解
+ if (isSolution(state)) {
+ // 记录解
+ recordSolution(state, res);
+ }
+ // 遍历所有选择
+ for (TreeNode choice : choices) {
+ // 剪枝:检查选择是否合法
+ if (isValid(state, choice)) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state, choice);
+ // 进行下一轮选择
+ backtrack(state, Arrays.asList(choice.left, choice.right), res);
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="preorder_traversal_iii_template.cs"
+ /* 判断当前状态是否为解 */
+ bool IsSolution(List state) {
+ return state.Count != 0 && state[^1].val == 7;
+ }
+
+ /* 记录解 */
+ void RecordSolution(List state, List> res) {
+ res.Add(new List(state));
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ bool IsValid(List state, TreeNode choice) {
+ return choice != null && choice.val != 3;
+ }
+
+ /* 更新状态 */
+ void MakeChoice(List state, TreeNode choice) {
+ state.Add(choice);
+ }
+
+ /* 恢复状态 */
+ void UndoChoice(List state, TreeNode choice) {
+ state.RemoveAt(state.Count - 1);
+ }
+
+ /* 回溯算法:例题三 */
+ void Backtrack(List state, List choices, List> res) {
+ // 检查是否为解
+ if (IsSolution(state)) {
+ // 记录解
+ RecordSolution(state, res);
+ }
+ // 遍历所有选择
+ foreach (TreeNode choice in choices) {
+ // 剪枝:检查选择是否合法
+ if (IsValid(state, choice)) {
+ // 尝试:做出选择,更新状态
+ MakeChoice(state, choice);
+ // 进行下一轮选择
+ Backtrack(state, [choice.left!, choice.right!], res);
+ // 回退:撤销选择,恢复到之前的状态
+ UndoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "Go"
+
+ ```go title="preorder_traversal_iii_template.go"
+ /* 判断当前状态是否为解 */
+ func isSolution(state *[]*TreeNode) bool {
+ return len(*state) != 0 && (*state)[len(*state)-1].Val == 7
+ }
+
+ /* 记录解 */
+ func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) {
+ *res = append(*res, append([]*TreeNode{}, *state...))
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ func isValid(state *[]*TreeNode, choice *TreeNode) bool {
+ return choice != nil && choice.Val != 3
+ }
+
+ /* 更新状态 */
+ func makeChoice(state *[]*TreeNode, choice *TreeNode) {
+ *state = append(*state, choice)
+ }
+
+ /* 恢复状态 */
+ func undoChoice(state *[]*TreeNode, choice *TreeNode) {
+ *state = (*state)[:len(*state)-1]
+ }
+
+ /* 回溯算法:例题三 */
+ func backtrackIII(state *[]*TreeNode, choices *[]*TreeNode, res *[][]*TreeNode) {
+ // 检查是否为解
+ if isSolution(state) {
+ // 记录解
+ recordSolution(state, res)
+ }
+ // 遍历所有选择
+ for _, choice := range *choices {
+ // 剪枝:检查选择是否合法
+ if isValid(state, choice) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state, choice)
+ // 进行下一轮选择
+ temp := make([]*TreeNode, 0)
+ temp = append(temp, choice.Left, choice.Right)
+ backtrackIII(state, &temp, res)
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state, choice)
+ }
+ }
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="preorder_traversal_iii_template.swift"
+ /* 判断当前状态是否为解 */
+ func isSolution(state: [TreeNode]) -> Bool {
+ !state.isEmpty && state.last!.val == 7
+ }
+
+ /* 记录解 */
+ func recordSolution(state: [TreeNode], res: inout [[TreeNode]]) {
+ res.append(state)
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ func isValid(state: [TreeNode], choice: TreeNode?) -> Bool {
+ choice != nil && choice!.val != 3
+ }
+
+ /* 更新状态 */
+ func makeChoice(state: inout [TreeNode], choice: TreeNode) {
+ state.append(choice)
+ }
+
+ /* 恢复状态 */
+ func undoChoice(state: inout [TreeNode], choice: TreeNode) {
+ state.removeLast()
+ }
+
+ /* 回溯算法:例题三 */
+ func backtrack(state: inout [TreeNode], choices: [TreeNode], res: inout [[TreeNode]]) {
+ // 检查是否为解
+ if isSolution(state: state) {
+ recordSolution(state: state, res: &res)
+ }
+ // 遍历所有选择
+ for choice in choices {
+ // 剪枝:检查选择是否合法
+ if isValid(state: state, choice: choice) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state: &state, choice: choice)
+ // 进行下一轮选择
+ backtrack(state: &state, choices: [choice.left, choice.right].compactMap { $0 }, res: &res)
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state: &state, choice: choice)
+ }
+ }
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="preorder_traversal_iii_template.js"
+ /* 判断当前状态是否为解 */
+ function isSolution(state) {
+ return state && state[state.length - 1]?.val === 7;
+ }
+
+ /* 记录解 */
+ function recordSolution(state, res) {
+ res.push([...state]);
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ function isValid(state, choice) {
+ return choice !== null && choice.val !== 3;
+ }
+
+ /* 更新状态 */
+ function makeChoice(state, choice) {
+ state.push(choice);
+ }
+
+ /* 恢复状态 */
+ function undoChoice(state) {
+ state.pop();
+ }
+
+ /* 回溯算法:例题三 */
+ function backtrack(state, choices, res) {
+ // 检查是否为解
+ if (isSolution(state)) {
+ // 记录解
+ recordSolution(state, res);
+ }
+ // 遍历所有选择
+ for (const choice of choices) {
+ // 剪枝:检查选择是否合法
+ if (isValid(state, choice)) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state, choice);
+ // 进行下一轮选择
+ backtrack(state, [choice.left, choice.right], res);
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state);
+ }
+ }
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="preorder_traversal_iii_template.ts"
+ /* 判断当前状态是否为解 */
+ function isSolution(state: TreeNode[]): boolean {
+ return state && state[state.length - 1]?.val === 7;
+ }
+
+ /* 记录解 */
+ function recordSolution(state: TreeNode[], res: TreeNode[][]): void {
+ res.push([...state]);
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ function isValid(state: TreeNode[], choice: TreeNode): boolean {
+ return choice !== null && choice.val !== 3;
+ }
+
+ /* 更新状态 */
+ function makeChoice(state: TreeNode[], choice: TreeNode): void {
+ state.push(choice);
+ }
+
+ /* 恢复状态 */
+ function undoChoice(state: TreeNode[]): void {
+ state.pop();
+ }
+
+ /* 回溯算法:例题三 */
+ function backtrack(
+ state: TreeNode[],
+ choices: TreeNode[],
+ res: TreeNode[][]
+ ): void {
+ // 检查是否为解
+ if (isSolution(state)) {
+ // 记录解
+ recordSolution(state, res);
+ }
+ // 遍历所有选择
+ for (const choice of choices) {
+ // 剪枝:检查选择是否合法
+ if (isValid(state, choice)) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state, choice);
+ // 进行下一轮选择
+ backtrack(state, [choice.left, choice.right], res);
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state);
+ }
+ }
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="preorder_traversal_iii_template.dart"
+ /* 判断当前状态是否为解 */
+ bool isSolution(List state) {
+ return state.isNotEmpty && state.last.val == 7;
+ }
+
+ /* 记录解 */
+ void recordSolution(List state, List> res) {
+ res.add(List.from(state));
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ bool isValid(List state, TreeNode? choice) {
+ return choice != null && choice.val != 3;
+ }
+
+ /* 更新状态 */
+ void makeChoice(List state, TreeNode? choice) {
+ state.add(choice!);
+ }
+
+ /* 恢复状态 */
+ void undoChoice(List state, TreeNode? choice) {
+ state.removeLast();
+ }
+
+ /* 回溯算法:例题三 */
+ void backtrack(
+ List state,
+ List choices,
+ List> res,
+ ) {
+ // 检查是否为解
+ if (isSolution(state)) {
+ // 记录解
+ recordSolution(state, res);
+ }
+ // 遍历所有选择
+ for (TreeNode? choice in choices) {
+ // 剪枝:检查选择是否合法
+ if (isValid(state, choice)) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state, choice);
+ // 进行下一轮选择
+ backtrack(state, [choice!.left, choice.right], res);
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state, choice);
+ }
+ }
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="preorder_traversal_iii_template.rs"
+ /* 判断当前状态是否为解 */
+ fn is_solution(state: &mut Vec>>) -> bool {
+ return !state.is_empty() && state.get(state.len() - 1).unwrap().borrow().val == 7;
+ }
+
+ /* 记录解 */
+ fn record_solution(
+ state: &mut Vec>>,
+ res: &mut Vec>>>,
+ ) {
+ res.push(state.clone());
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ fn is_valid(_: &mut Vec>>, choice: Rc>) -> bool {
+ return choice.borrow().val != 3;
+ }
+
+ /* 更新状态 */
+ fn make_choice(state: &mut Vec>>, choice: Rc>) {
+ state.push(choice);
+ }
+
+ /* 恢复状态 */
+ fn undo_choice(state: &mut Vec>>, _: Rc>) {
+ state.remove(state.len() - 1);
+ }
+
+ /* 回溯算法:例题三 */
+ fn backtrack(
+ state: &mut Vec>>,
+ choices: &mut Vec>>,
+ res: &mut Vec>>>,
+ ) {
+ // 检查是否为解
+ if is_solution(state) {
+ // 记录解
+ record_solution(state, res);
+ }
+ // 遍历所有选择
+ for choice in choices {
+ // 剪枝:检查选择是否合法
+ if is_valid(state, choice.clone()) {
+ // 尝试:做出选择,更新状态
+ make_choice(state, choice.clone());
+ // 进行下一轮选择
+ backtrack(
+ state,
+ &mut vec![
+ choice.borrow().left.clone().unwrap(),
+ choice.borrow().right.clone().unwrap(),
+ ],
+ res,
+ );
+ // 回退:撤销选择,恢复到之前的状态
+ undo_choice(state, choice.clone());
+ }
+ }
+ }
+ ```
+
+=== "C"
+
+ ```c title="preorder_traversal_iii_template.c"
+ /* 判断当前状态是否为解 */
+ bool isSolution(void) {
+ return pathSize > 0 && path[pathSize - 1]->val == 7;
+ }
+
+ /* 记录解 */
+ void recordSolution(void) {
+ for (int i = 0; i < pathSize; i++) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ bool isValid(TreeNode *choice) {
+ return choice != NULL && choice->val != 3;
+ }
+
+ /* 更新状态 */
+ void makeChoice(TreeNode *choice) {
+ path[pathSize++] = choice;
+ }
+
+ /* 恢复状态 */
+ void undoChoice(void) {
+ pathSize--;
+ }
+
+ /* 回溯算法:例题三 */
+ void backtrack(TreeNode *choices[2]) {
+ // 检查是否为解
+ if (isSolution()) {
+ // 记录解
+ recordSolution();
+ }
+ // 遍历所有选择
+ for (int i = 0; i < 2; i++) {
+ TreeNode *choice = choices[i];
+ // 剪枝:检查选择是否合法
+ if (isValid(choice)) {
+ // 尝试:做出选择,更新状态
+ makeChoice(choice);
+ // 进行下一轮选择
+ TreeNode *nextChoices[2] = {choice->left, choice->right};
+ backtrack(nextChoices);
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice();
+ }
+ }
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="preorder_traversal_iii_template.kt"
+ /* 判断当前状态是否为解 */
+ fun isSolution(state: MutableList): Boolean {
+ return state.isNotEmpty() && state[state.size - 1]?._val == 7
+ }
+
+ /* 记录解 */
+ fun recordSolution(state: MutableList?, res: MutableList?>) {
+ res.add(state!!.toMutableList())
+ }
+
+ /* 判断在当前状态下,该选择是否合法 */
+ fun isValid(state: MutableList?, choice: TreeNode?): Boolean {
+ return choice != null && choice._val != 3
+ }
+
+ /* 更新状态 */
+ fun makeChoice(state: MutableList, choice: TreeNode?) {
+ state.add(choice)
+ }
+
+ /* 恢复状态 */
+ fun undoChoice(state: MutableList, choice: TreeNode?) {
+ state.removeLast()
+ }
+
+ /* 回溯算法:例题三 */
+ fun backtrack(
+ state: MutableList,
+ choices: MutableList,
+ res: MutableList?>
+ ) {
+ // 检查是否为解
+ if (isSolution(state)) {
+ // 记录解
+ recordSolution(state, res)
+ }
+ // 遍历所有选择
+ for (choice in choices) {
+ // 剪枝:检查选择是否合法
+ if (isValid(state, choice)) {
+ // 尝试:做出选择,更新状态
+ makeChoice(state, choice)
+ // 进行下一轮选择
+ backtrack(state, mutableListOf(choice!!.left, choice.right), res)
+ // 回退:撤销选择,恢复到之前的状态
+ undoChoice(state, choice)
+ }
+ }
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="preorder_traversal_iii_template.rb"
+ [class]{}-[func]{is_solution}
+
+ [class]{}-[func]{record_solution}
+
+ [class]{}-[func]{is_valid}
+
+ [class]{}-[func]{make_choice}
+
+ [class]{}-[func]{undo_choice}
+
+ [class]{}-[func]{backtrack}
+ ```
+
+=== "Zig"
+
+ ```zig title="preorder_traversal_iii_template.zig"
+ [class]{}-[func]{isSolution}
+
+ [class]{}-[func]{recordSolution}
+
+ [class]{}-[func]{isValid}
+
+ [class]{}-[func]{makeChoice}
+
+ [class]{}-[func]{undoChoice}
+
+ [class]{}-[func]{backtrack}
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+As per the requirements, after finding a node with a value of $7$, the search should continue, **thus the `return` statement after recording the solution should be removed**. The following diagram compares the search processes with and without retaining the `return` statement.
+
+{ class="animation-figure" }
+
+ Figure 13-4 Comparison of retaining and removing the return in the search process
+
+Compared to the implementation based on preorder traversal, the code implementation based on the backtracking algorithm framework seems verbose, but it has better universality. In fact, **many backtracking problems can be solved within this framework**. We just need to define `state` and `choices` according to the specific problem and implement the methods in the framework.
+
+## 13.1.4 Common terminology
+
+To analyze algorithmic problems more clearly, we summarize the meanings of commonly used terminology in backtracking algorithms and provide corresponding examples from Example Three as shown in the Table 13-1 .
+
+ Table 13-1 Common backtracking algorithm terminology
+
+
+
+| Term | Definition | Example Three |
+| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| Solution (solution) | A solution is an answer that satisfies specific conditions of the problem, which may have one or more | All paths from the root node to node $7$ that meet the constraint |
+| Constraint (constraint) | Constraints are conditions in the problem that limit the feasibility of solutions, often used for pruning | Paths do not contain node $3$ |
+| State (state) | State represents the situation of the problem at a certain moment, including choices made | Current visited node path, i.e., `path` node list |
+| Attempt (attempt) | An attempt is the process of exploring the solution space based on available choices, including making choices, updating the state, and checking if it's a solution | Recursively visiting left (right) child nodes, adding nodes to `path`, checking if the node's value is $7$ |
+| Backtracking (backtracking) | Backtracking refers to the action of undoing previous choices and returning to the previous state when encountering states that do not meet the constraints | When passing leaf nodes, ending node visits, encountering nodes with a value of $3$, terminating the search, and function return |
+| Pruning (pruning) | Pruning is a method to avoid meaningless search paths based on the characteristics and constraints of the problem, which can enhance search efficiency | When encountering a node with a value of $3$, no further search is continued |
+
+
+
+!!! tip
+
+ Concepts like problems, solutions, states, etc., are universal, and are involved in divide and conquer, backtracking, dynamic programming, and greedy algorithms, among others.
+
+## 13.1.5 Advantages and limitations
+
+The backtracking algorithm is essentially a depth-first search algorithm that attempts all possible solutions until a satisfying solution is found. The advantage of this method is that it can find all possible solutions, and with reasonable pruning operations, it can be highly efficient.
+
+However, when dealing with large-scale or complex problems, **the operational efficiency of backtracking may be difficult to accept**.
+
+- **Time**: Backtracking algorithms usually need to traverse all possible states in the state space, which can reach exponential or factorial time complexity.
+- **Space**: In recursive calls, it is necessary to save the current state (such as paths, auxiliary variables for pruning, etc.). When the depth is very large, the space requirement may become significant.
+
+Even so, **backtracking remains the best solution for certain search problems and constraint satisfaction problems**. For these problems, since it is unpredictable which choices can generate valid solutions, we must traverse all possible choices. In this case, **the key is how to optimize efficiency**, with common efficiency optimization methods being two types.
+
+- **Pruning**: Avoid searching paths that definitely will not produce a solution, thus saving time and space.
+- **Heuristic search**: Introduce some strategies or estimates during the search process to prioritize the paths that are most likely to produce valid solutions.
+
+## 13.1.6 Typical backtracking problems
+
+Backtracking algorithms can be used to solve many search problems, constraint satisfaction problems, and combinatorial optimization problems.
+
+**Search problems**: The goal of these problems is to find solutions that meet specific conditions.
+
+- Full permutation problem: Given a set, find all possible permutations and combinations of it.
+- Subset sum problem: Given a set and a target sum, find all subsets of the set that sum to the target.
+- Tower of Hanoi problem: Given three rods and a series of different-sized discs, the goal is to move all the discs from one rod to another, moving only one disc at a time, and never placing a larger disc on a smaller one.
+
+**Constraint satisfaction problems**: The goal of these problems is to find solutions that satisfy all the constraints.
+
+- $n$ queens: Place $n$ queens on an $n \times n$ chessboard so that they do not attack each other.
+- Sudoku: Fill a $9 \times 9$ grid with the numbers $1$ to $9$, ensuring that the numbers do not repeat in each row, each column, and each $3 \times 3$ subgrid.
+- Graph coloring problem: Given an undirected graph, color each vertex with the fewest possible colors so that adjacent vertices have different colors.
+
+**Combinatorial optimization problems**: The goal of these problems is to find the optimal solution within a combination space that meets certain conditions.
+
+- 0-1 knapsack problem: Given a set of items and a backpack, each item has a certain value and weight. The goal is to choose items to maximize the total value within the backpack's capacity limit.
+- Traveling salesman problem: In a graph, starting from one point, visit all other points exactly once and then return to the starting point, seeking the shortest path.
+- Maximum clique problem: Given an undirected graph, find the largest complete subgraph, i.e., a subgraph where any two vertices are connected by an edge.
+
+Please note that for many combinatorial optimization problems, backtracking is not the optimal solution.
+
+- The 0-1 knapsack problem is usually solved using dynamic programming to achieve higher time efficiency.
+- The traveling salesman is a well-known NP-Hard problem, commonly solved using genetic algorithms and ant colony algorithms, among others.
+- The maximum clique problem is a classic problem in graph theory, which can be solved using greedy algorithms and other heuristic methods.
diff --git a/en/docs/chapter_backtracking/index.md b/en/docs/chapter_backtracking/index.md
new file mode 100644
index 000000000..7ebd7946e
--- /dev/null
+++ b/en/docs/chapter_backtracking/index.md
@@ -0,0 +1,22 @@
+---
+comments: true
+icon: material/map-marker-path
+---
+
+# Chapter 13. Backtracking
+
+{ class="cover-image" }
+
+!!! abstract
+
+ Like explorers in a maze, we may encounter difficulties on our path forward.
+
+ The power of backtracking allows us to start over, keep trying, and eventually find the exit to the light.
+
+## Chapter contents
+
+- [13.1 Backtracking algorithms](backtracking_algorithm.md)
+- [13.2 Permutation problem](permutations_problem.md)
+- [13.3 Subset sum problem](subset_sum_problem.md)
+- [13.4 n queens problem](n_queens_problem.md)
+- [13.5 Summary](summary.md)
diff --git a/en/docs/chapter_backtracking/n_queens_problem.md b/en/docs/chapter_backtracking/n_queens_problem.md
new file mode 100644
index 000000000..cf8dad3af
--- /dev/null
+++ b/en/docs/chapter_backtracking/n_queens_problem.md
@@ -0,0 +1,732 @@
+---
+comments: true
+---
+
+# 13.4 n queens problem
+
+!!! question
+
+ According to the rules of chess, a queen can attack pieces in the same row, column, or on a diagonal line. Given $n$ queens and an $n \times n$ chessboard, find arrangements where no two queens can attack each other.
+
+As shown in the Figure 13-15 , when $n = 4$, there are two solutions. From the perspective of the backtracking algorithm, an $n \times n$ chessboard has $n^2$ squares, presenting all possible choices `choices`. The state of the chessboard `state` changes continuously as each queen is placed.
+
+{ class="animation-figure" }
+
+ Figure 13-15 Solution to the 4 queens problem
+
+The following image shows the three constraints of this problem: **multiple queens cannot be on the same row, column, or diagonal**. It is important to note that diagonals are divided into the main diagonal `\` and the secondary diagonal `/`.
+
+{ class="animation-figure" }
+
+ Figure 13-16 Constraints of the n queens problem
+
+### 1. Row-by-row placing strategy
+
+As the number of queens equals the number of rows on the chessboard, both being $n$, it is easy to conclude: **each row on the chessboard allows and only allows one queen to be placed**.
+
+This means that we can adopt a row-by-row placing strategy: starting from the first row, place one queen per row until the last row is reached.
+
+The image below shows the row-by-row placing process for the 4 queens problem. Due to space limitations, the image only expands one search branch of the first row, and prunes any placements that do not meet the column and diagonal constraints.
+
+{ class="animation-figure" }
+
+ Figure 13-17 Row-by-row placing strategy
+
+Essentially, **the row-by-row placing strategy serves as a pruning function**, avoiding all search branches that would place multiple queens in the same row.
+
+### 2. Column and diagonal pruning
+
+To satisfy column constraints, we can use a boolean array `cols` of length $n$ to track whether a queen occupies each column. Before each placement decision, `cols` is used to prune the columns that already have queens, and it is dynamically updated during backtracking.
+
+How about the diagonal constraints? Let the row and column indices of a cell on the chessboard be $(row, col)$. By selecting a specific main diagonal, we notice that the difference $row - col$ is the same for all cells on that diagonal, **meaning that $row - col$ is a constant value on that diagonal**.
+
+Thus, if two cells satisfy $row_1 - col_1 = row_2 - col_2$, they are definitely on the same main diagonal. Using this pattern, we can utilize the array `diags1` shown below to track whether a queen is on any main diagonal.
+
+Similarly, **the sum $row + col$ is a constant value for all cells on a secondary diagonal**. We can also use the array `diags2` to handle secondary diagonal constraints.
+
+{ class="animation-figure" }
+
+ Figure 13-18 Handling column and diagonal constraints
+
+### 3. Code implementation
+
+Please note, in an $n$-dimensional matrix, the range of $row - col$ is $[-n + 1, n - 1]$, and the range of $row + col$ is $[0, 2n - 2]$, thus the number of both main and secondary diagonals is $2n - 1$, meaning the length of both arrays `diags1` and `diags2` is $2n - 1$.
+
+=== "Python"
+
+ ```python title="n_queens.py"
+ def backtrack(
+ row: int,
+ n: int,
+ state: list[list[str]],
+ res: list[list[list[str]]],
+ cols: list[bool],
+ diags1: list[bool],
+ diags2: list[bool],
+ ):
+ """回溯算法:n 皇后"""
+ # 当放置完所有行时,记录解
+ if row == n:
+ res.append([list(row) for row in state])
+ return
+ # 遍历所有列
+ for col in range(n):
+ # 计算该格子对应的主对角线和次对角线
+ diag1 = row - col + n - 1
+ diag2 = row + col
+ # 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if not cols[col] and not diags1[diag1] and not diags2[diag2]:
+ # 尝试:将皇后放置在该格子
+ state[row][col] = "Q"
+ cols[col] = diags1[diag1] = diags2[diag2] = True
+ # 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2)
+ # 回退:将该格子恢复为空位
+ state[row][col] = "#"
+ cols[col] = diags1[diag1] = diags2[diag2] = False
+
+ def n_queens(n: int) -> list[list[list[str]]]:
+ """求解 n 皇后"""
+ # 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ state = [["#" for _ in range(n)] for _ in range(n)]
+ cols = [False] * n # 记录列是否有皇后
+ diags1 = [False] * (2 * n - 1) # 记录主对角线上是否有皇后
+ diags2 = [False] * (2 * n - 1) # 记录次对角线上是否有皇后
+ res = []
+ backtrack(0, n, state, res, cols, diags1, diags2)
+
+ return res
+ ```
+
+=== "C++"
+
+ ```cpp title="n_queens.cpp"
+ /* 回溯算法:n 皇后 */
+ void backtrack(int row, int n, vector> &state, vector>> &res, vector &cols,
+ vector &diags1, vector &diags2) {
+ // 当放置完所有行时,记录解
+ if (row == n) {
+ res.push_back(state);
+ return;
+ }
+ // 遍历所有列
+ for (int col = 0; col < n; col++) {
+ // 计算该格子对应的主对角线和次对角线
+ int diag1 = row - col + n - 1;
+ int diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // 尝试:将皇后放置在该格子
+ state[row][col] = "Q";
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state[row][col] = "#";
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ vector>> nQueens(int n) {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ vector> state(n, vector(n, "#"));
+ vector cols(n, false); // 记录列是否有皇后
+ vector diags1(2 * n - 1, false); // 记录主对角线上是否有皇后
+ vector diags2(2 * n - 1, false); // 记录次对角线上是否有皇后
+ vector>> res;
+
+ backtrack(0, n, state, res, cols, diags1, diags2);
+
+ return res;
+ }
+ ```
+
+=== "Java"
+
+ ```java title="n_queens.java"
+ /* 回溯算法:n 皇后 */
+ void backtrack(int row, int n, List> state, List>> res,
+ boolean[] cols, boolean[] diags1, boolean[] diags2) {
+ // 当放置完所有行时,记录解
+ if (row == n) {
+ List> copyState = new ArrayList<>();
+ for (List sRow : state) {
+ copyState.add(new ArrayList<>(sRow));
+ }
+ res.add(copyState);
+ return;
+ }
+ // 遍历所有列
+ for (int col = 0; col < n; col++) {
+ // 计算该格子对应的主对角线和次对角线
+ int diag1 = row - col + n - 1;
+ int diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // 尝试:将皇后放置在该格子
+ state.get(row).set(col, "Q");
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state.get(row).set(col, "#");
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ List>> nQueens(int n) {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ List> state = new ArrayList<>();
+ for (int i = 0; i < n; i++) {
+ List row = new ArrayList<>();
+ for (int j = 0; j < n; j++) {
+ row.add("#");
+ }
+ state.add(row);
+ }
+ boolean[] cols = new boolean[n]; // 记录列是否有皇后
+ boolean[] diags1 = new boolean[2 * n - 1]; // 记录主对角线上是否有皇后
+ boolean[] diags2 = new boolean[2 * n - 1]; // 记录次对角线上是否有皇后
+ List>> res = new ArrayList<>();
+
+ backtrack(0, n, state, res, cols, diags1, diags2);
+
+ return res;
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="n_queens.cs"
+ /* 回溯算法:n 皇后 */
+ void Backtrack(int row, int n, List> state, List>> res,
+ bool[] cols, bool[] diags1, bool[] diags2) {
+ // 当放置完所有行时,记录解
+ if (row == n) {
+ List> copyState = [];
+ foreach (List sRow in state) {
+ copyState.Add(new List(sRow));
+ }
+ res.Add(copyState);
+ return;
+ }
+ // 遍历所有列
+ for (int col = 0; col < n; col++) {
+ // 计算该格子对应的主对角线和次对角线
+ int diag1 = row - col + n - 1;
+ int diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // 尝试:将皇后放置在该格子
+ state[row][col] = "Q";
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // 放置下一行
+ Backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state[row][col] = "#";
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ List>> NQueens(int n) {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ List> state = [];
+ for (int i = 0; i < n; i++) {
+ List row = [];
+ for (int j = 0; j < n; j++) {
+ row.Add("#");
+ }
+ state.Add(row);
+ }
+ bool[] cols = new bool[n]; // 记录列是否有皇后
+ bool[] diags1 = new bool[2 * n - 1]; // 记录主对角线上是否有皇后
+ bool[] diags2 = new bool[2 * n - 1]; // 记录次对角线上是否有皇后
+ List>> res = [];
+
+ Backtrack(0, n, state, res, cols, diags1, diags2);
+
+ return res;
+ }
+ ```
+
+=== "Go"
+
+ ```go title="n_queens.go"
+ /* 回溯算法:n 皇后 */
+ func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) {
+ // 当放置完所有行时,记录解
+ if row == n {
+ newState := make([][]string, len(*state))
+ for i, _ := range newState {
+ newState[i] = make([]string, len((*state)[0]))
+ copy(newState[i], (*state)[i])
+
+ }
+ *res = append(*res, newState)
+ }
+ // 遍历所有列
+ for col := 0; col < n; col++ {
+ // 计算该格子对应的主对角线和次对角线
+ diag1 := row - col + n - 1
+ diag2 := row + col
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if !(*cols)[col] && !(*diags1)[diag1] && !(*diags2)[diag2] {
+ // 尝试:将皇后放置在该格子
+ (*state)[row][col] = "Q"
+ (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true
+ // 放置下一行
+ backtrack(row+1, n, state, res, cols, diags1, diags2)
+ // 回退:将该格子恢复为空位
+ (*state)[row][col] = "#"
+ (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ func nQueens(n int) [][][]string {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ state := make([][]string, n)
+ for i := 0; i < n; i++ {
+ row := make([]string, n)
+ for i := 0; i < n; i++ {
+ row[i] = "#"
+ }
+ state[i] = row
+ }
+ // 记录列是否有皇后
+ cols := make([]bool, n)
+ diags1 := make([]bool, 2*n-1)
+ diags2 := make([]bool, 2*n-1)
+ res := make([][][]string, 0)
+ backtrack(0, n, &state, &res, &cols, &diags1, &diags2)
+ return res
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="n_queens.swift"
+ /* 回溯算法:n 皇后 */
+ func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]]], cols: inout [Bool], diags1: inout [Bool], diags2: inout [Bool]) {
+ // 当放置完所有行时,记录解
+ if row == n {
+ res.append(state)
+ return
+ }
+ // 遍历所有列
+ for col in 0 ..< n {
+ // 计算该格子对应的主对角线和次对角线
+ let diag1 = row - col + n - 1
+ let diag2 = row + col
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if !cols[col] && !diags1[diag1] && !diags2[diag2] {
+ // 尝试:将皇后放置在该格子
+ state[row][col] = "Q"
+ cols[col] = true
+ diags1[diag1] = true
+ diags2[diag2] = true
+ // 放置下一行
+ backtrack(row: row + 1, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
+ // 回退:将该格子恢复为空位
+ state[row][col] = "#"
+ cols[col] = false
+ diags1[diag1] = false
+ diags2[diag2] = false
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ func nQueens(n: Int) -> [[[String]]] {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ var state = Array(repeating: Array(repeating: "#", count: n), count: n)
+ var cols = Array(repeating: false, count: n) // 记录列是否有皇后
+ var diags1 = Array(repeating: false, count: 2 * n - 1) // 记录主对角线上是否有皇后
+ var diags2 = Array(repeating: false, count: 2 * n - 1) // 记录次对角线上是否有皇后
+ var res: [[[String]]] = []
+
+ backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
+
+ return res
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="n_queens.js"
+ /* 回溯算法:n 皇后 */
+ function backtrack(row, n, state, res, cols, diags1, diags2) {
+ // 当放置完所有行时,记录解
+ if (row === n) {
+ res.push(state.map((row) => row.slice()));
+ return;
+ }
+ // 遍历所有列
+ for (let col = 0; col < n; col++) {
+ // 计算该格子对应的主对角线和次对角线
+ const diag1 = row - col + n - 1;
+ const diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // 尝试:将皇后放置在该格子
+ state[row][col] = 'Q';
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state[row][col] = '#';
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ function nQueens(n) {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ const state = Array.from({ length: n }, () => Array(n).fill('#'));
+ const cols = Array(n).fill(false); // 记录列是否有皇后
+ const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线上是否有皇后
+ const diags2 = Array(2 * n - 1).fill(false); // 记录次对角线上是否有皇后
+ const res = [];
+
+ backtrack(0, n, state, res, cols, diags1, diags2);
+ return res;
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="n_queens.ts"
+ /* 回溯算法:n 皇后 */
+ function backtrack(
+ row: number,
+ n: number,
+ state: string[][],
+ res: string[][][],
+ cols: boolean[],
+ diags1: boolean[],
+ diags2: boolean[]
+ ): void {
+ // 当放置完所有行时,记录解
+ if (row === n) {
+ res.push(state.map((row) => row.slice()));
+ return;
+ }
+ // 遍历所有列
+ for (let col = 0; col < n; col++) {
+ // 计算该格子对应的主对角线和次对角线
+ const diag1 = row - col + n - 1;
+ const diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // 尝试:将皇后放置在该格子
+ state[row][col] = 'Q';
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state[row][col] = '#';
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ function nQueens(n: number): string[][][] {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ const state = Array.from({ length: n }, () => Array(n).fill('#'));
+ const cols = Array(n).fill(false); // 记录列是否有皇后
+ const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线上是否有皇后
+ const diags2 = Array(2 * n - 1).fill(false); // 记录次对角线上是否有皇后
+ const res: string[][][] = [];
+
+ backtrack(0, n, state, res, cols, diags1, diags2);
+ return res;
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="n_queens.dart"
+ /* 回溯算法:n 皇后 */
+ void backtrack(
+ int row,
+ int n,
+ List> state,
+ List>> res,
+ List cols,
+ List diags1,
+ List diags2,
+ ) {
+ // 当放置完所有行时,记录解
+ if (row == n) {
+ List> copyState = [];
+ for (List sRow in state) {
+ copyState.add(List.from(sRow));
+ }
+ res.add(copyState);
+ return;
+ }
+ // 遍历所有列
+ for (int col = 0; col < n; col++) {
+ // 计算该格子对应的主对角线和次对角线
+ int diag1 = row - col + n - 1;
+ int diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // 尝试:将皇后放置在该格子
+ state[row][col] = "Q";
+ cols[col] = true;
+ diags1[diag1] = true;
+ diags2[diag2] = true;
+ // 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state[row][col] = "#";
+ cols[col] = false;
+ diags1[diag1] = false;
+ diags2[diag2] = false;
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ List>> nQueens(int n) {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ List> state = List.generate(n, (index) => List.filled(n, "#"));
+ List cols = List.filled(n, false); // 记录列是否有皇后
+ List diags1 = List.filled(2 * n - 1, false); // 记录主对角线上是否有皇后
+ List diags2 = List.filled(2 * n - 1, false); // 记录次对角线上是否有皇后
+ List>> res = [];
+
+ backtrack(0, n, state, res, cols, diags1, diags2);
+
+ return res;
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="n_queens.rs"
+ /* 回溯算法:n 皇后 */
+ fn backtrack(
+ row: usize,
+ n: usize,
+ state: &mut Vec>,
+ res: &mut Vec>>,
+ cols: &mut [bool],
+ diags1: &mut [bool],
+ diags2: &mut [bool],
+ ) {
+ // 当放置完所有行时,记录解
+ if row == n {
+ let mut copy_state: Vec> = Vec::new();
+ for s_row in state.clone() {
+ copy_state.push(s_row);
+ }
+ res.push(copy_state);
+ return;
+ }
+ // 遍历所有列
+ for col in 0..n {
+ // 计算该格子对应的主对角线和次对角线
+ let diag1 = row + n - 1 - col;
+ let diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if !cols[col] && !diags1[diag1] && !diags2[diag2] {
+ // 尝试:将皇后放置在该格子
+ state.get_mut(row).unwrap()[col] = "Q".into();
+ (cols[col], diags1[diag1], diags2[diag2]) = (true, true, true);
+ // 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state.get_mut(row).unwrap()[col] = "#".into();
+ (cols[col], diags1[diag1], diags2[diag2]) = (false, false, false);
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ fn n_queens(n: usize) -> Vec>> {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ let mut state: Vec> = Vec::new();
+ for _ in 0..n {
+ let mut row: Vec = Vec::new();
+ for _ in 0..n {
+ row.push("#".into());
+ }
+ state.push(row);
+ }
+ let mut cols = vec![false; n]; // 记录列是否有皇后
+ let mut diags1 = vec![false; 2 * n - 1]; // 记录主对角线上是否有皇后
+ let mut diags2 = vec![false; 2 * n - 1]; // 记录次对角线上是否有皇后
+ let mut res: Vec>> = Vec::new();
+
+ backtrack(
+ 0,
+ n,
+ &mut state,
+ &mut res,
+ &mut cols,
+ &mut diags1,
+ &mut diags2,
+ );
+
+ res
+ }
+ ```
+
+=== "C"
+
+ ```c title="n_queens.c"
+ /* 回溯算法:n 皇后 */
+ void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE],
+ bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) {
+ // 当放置完所有行时,记录解
+ if (row == n) {
+ res[*resSize] = (char **)malloc(sizeof(char *) * n);
+ for (int i = 0; i < n; ++i) {
+ res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1));
+ strcpy(res[*resSize][i], state[i]);
+ }
+ (*resSize)++;
+ return;
+ }
+ // 遍历所有列
+ for (int col = 0; col < n; col++) {
+ // 计算该格子对应的主对角线和次对角线
+ int diag1 = row - col + n - 1;
+ int diag2 = row + col;
+ // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // 尝试:将皇后放置在该格子
+ state[row][col] = 'Q';
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // 放置下一行
+ backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2);
+ // 回退:将该格子恢复为空位
+ state[row][col] = '#';
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+ }
+
+ /* 求解 n 皇后 */
+ char ***nQueens(int n, int *returnSize) {
+ char state[MAX_SIZE][MAX_SIZE];
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ state[i][j] = '#';
+ }
+ state[i][n] = '\0';
+ }
+ bool cols[MAX_SIZE] = {false}; // 记录列是否有皇后
+ bool diags1[2 * MAX_SIZE - 1] = {false}; // 记录主对角线上是否有皇后
+ bool diags2[2 * MAX_SIZE - 1] = {false}; // 记录次对角线上是否有皇后
+
+ char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE);
+ *returnSize = 0;
+ backtrack(0, n, state, res, returnSize, cols, diags1, diags2);
+ return res;
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="n_queens.kt"
+ /* 回溯算法:n 皇后 */
+ fun backtrack(
+ row: Int,
+ n: Int,
+ state: MutableList>,
+ res: MutableList>?>,
+ cols: BooleanArray,
+ diags1: BooleanArray,
+ diags2: BooleanArray
+ ) {
+ // 当放置完所有行时,记录解
+ if (row == n) {
+ val copyState = mutableListOf>()
+ for (sRow in state) {
+ copyState.add(sRow.toMutableList())
+ }
+ res.add(copyState)
+ return
+ }
+ // 遍历所有列
+ for (col in 0..>?> {
+ // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ val state = mutableListOf>()
+ for (i in 0..()
+ for (j in 0..>?>()
+
+ backtrack(0, n, state, res, cols, diags1, diags2)
+
+ return res
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="n_queens.rb"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{n_queens}
+ ```
+
+=== "Zig"
+
+ ```zig title="n_queens.zig"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{nQueens}
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+Placing $n$ queens row-by-row, considering column constraints, from the first row to the last row there are $n$, $n-1$, $\dots$, $2$, $1$ choices, using $O(n!)$ time. When recording a solution, it is necessary to copy the matrix `state` and add it to `res`, with the copying operation using $O(n^2)$ time. Therefore, **the overall time complexity is $O(n! \cdot n^2)$**. In practice, pruning based on diagonal constraints can significantly reduce the search space, thus often the search efficiency is better than the above time complexity.
+
+Array `state` uses $O(n^2)$ space, and arrays `cols`, `diags1`, and `diags2` each use $O(n)$ space. The maximum recursion depth is $n$, using $O(n)$ stack space. Therefore, **the space complexity is $O(n^2)$**.
diff --git a/en/docs/chapter_backtracking/permutations_problem.md b/en/docs/chapter_backtracking/permutations_problem.md
new file mode 100644
index 000000000..62322c7ab
--- /dev/null
+++ b/en/docs/chapter_backtracking/permutations_problem.md
@@ -0,0 +1,1068 @@
+---
+comments: true
+---
+
+# 13.2 Permutation problem
+
+The permutation problem is a typical application of the backtracking algorithm. It is defined as finding all possible arrangements of elements from a given set (such as an array or string).
+
+The Table 13-2 lists several example data, including the input arrays and their corresponding permutations.
+
+ Table 13-2 Permutation examples
+
+
+
+| Input array | Permutations |
+| :---------- | :----------------------------------------------------------------- |
+| $[1]$ | $[1]$ |
+| $[1, 2]$ | $[1, 2], [2, 1]$ |
+| $[1, 2, 3]$ | $[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]$ |
+
+
+
+## 13.2.1 Cases without equal elements
+
+!!! question
+
+ Enter an integer array without duplicate elements and return all possible permutations.
+
+From the perspective of the backtracking algorithm, **we can imagine the process of generating permutations as a series of choices**. Suppose the input array is $[1, 2, 3]$, if we first choose $1$, then $3$, and finally $2$, we obtain the permutation $[1, 3, 2]$. Backtracking means undoing a choice and then continuing to try other choices.
+
+From the code perspective, the candidate set `choices` contains all elements of the input array, and the state `state` contains elements that have been selected so far. Please note that each element can only be chosen once, **thus all elements in `state` must be unique**.
+
+As shown in the following figure, we can unfold the search process into a recursive tree, where each node represents the current state `state`. Starting from the root node, after three rounds of choices, we reach the leaf nodes, each corresponding to a permutation.
+
+{ class="animation-figure" }
+
+ Figure 13-5 Permutation recursive tree
+
+### 1. Pruning of repeated choices
+
+To ensure that each element is selected only once, we consider introducing a boolean array `selected`, where `selected[i]` indicates whether `choices[i]` has been selected. We base our pruning operations on this array:
+
+- After making the choice `choice[i]`, we set `selected[i]` to $\text{True}$, indicating it has been chosen.
+- When iterating through the choice list `choices`, skip all nodes that have already been selected, i.e., prune.
+
+As shown in the following figure, suppose we choose 1 in the first round, 3 in the second round, and 2 in the third round, we need to prune the branch of element 1 in the second round and elements 1 and 3 in the third round.
+
+{ class="animation-figure" }
+
+ Figure 13-6 Permutation pruning example
+
+Observing the above figure, this pruning operation reduces the search space size from $O(n^n)$ to $O(n!)$.
+
+### 2. Code implementation
+
+After understanding the above information, we can "fill in the blanks" in the framework code. To shorten the overall code, we do not implement individual functions within the framework code separately, but expand them in the `backtrack()` function:
+
+=== "Python"
+
+ ```python title="permutations_i.py"
+ def backtrack(
+ state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
+ ):
+ """回溯算法:全排列 I"""
+ # 当状态长度等于元素数量时,记录解
+ if len(state) == len(choices):
+ res.append(list(state))
+ return
+ # 遍历所有选择
+ for i, choice in enumerate(choices):
+ # 剪枝:不允许重复选择元素
+ if not selected[i]:
+ # 尝试:做出选择,更新状态
+ selected[i] = True
+ state.append(choice)
+ # 进行下一轮选择
+ backtrack(state, choices, selected, res)
+ # 回退:撤销选择,恢复到之前的状态
+ selected[i] = False
+ state.pop()
+
+ def permutations_i(nums: list[int]) -> list[list[int]]:
+ """全排列 I"""
+ res = []
+ backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
+ return res
+ ```
+
+=== "C++"
+
+ ```cpp title="permutations_i.cpp"
+ /* 回溯算法:全排列 I */
+ void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.size() == choices.size()) {
+ res.push_back(state);
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choices.size(); i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state.push_back(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.pop_back();
+ }
+ }
+ }
+
+ /* 全排列 I */
+ vector> permutationsI(vector nums) {
+ vector state;
+ vector selected(nums.size(), false);
+ vector> res;
+ backtrack(state, nums, selected, res);
+ return res;
+ }
+ ```
+
+=== "Java"
+
+ ```java title="permutations_i.java"
+ /* 回溯算法:全排列 I */
+ void backtrack(List state, int[] choices, boolean[] selected, List> res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.size() == choices.length) {
+ res.add(new ArrayList(state));
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choices.length; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state.add(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.remove(state.size() - 1);
+ }
+ }
+ }
+
+ /* 全排列 I */
+ List> permutationsI(int[] nums) {
+ List> res = new ArrayList>();
+ backtrack(new ArrayList(), nums, new boolean[nums.length], res);
+ return res;
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="permutations_i.cs"
+ /* 回溯算法:全排列 I */
+ void Backtrack(List state, int[] choices, bool[] selected, List> res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.Count == choices.Length) {
+ res.Add(new List(state));
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choices.Length; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state.Add(choice);
+ // 进行下一轮选择
+ Backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.RemoveAt(state.Count - 1);
+ }
+ }
+ }
+
+ /* 全排列 I */
+ List> PermutationsI(int[] nums) {
+ List> res = [];
+ Backtrack([], nums, new bool[nums.Length], res);
+ return res;
+ }
+ ```
+
+=== "Go"
+
+ ```go title="permutations_i.go"
+ /* 回溯算法:全排列 I */
+ func backtrackI(state *[]int, choices *[]int, selected *[]bool, res *[][]int) {
+ // 当状态长度等于元素数量时,记录解
+ if len(*state) == len(*choices) {
+ newState := append([]int{}, *state...)
+ *res = append(*res, newState)
+ }
+ // 遍历所有选择
+ for i := 0; i < len(*choices); i++ {
+ choice := (*choices)[i]
+ // 剪枝:不允许重复选择元素
+ if !(*selected)[i] {
+ // 尝试:做出选择,更新状态
+ (*selected)[i] = true
+ *state = append(*state, choice)
+ // 进行下一轮选择
+ backtrackI(state, choices, selected, res)
+ // 回退:撤销选择,恢复到之前的状态
+ (*selected)[i] = false
+ *state = (*state)[:len(*state)-1]
+ }
+ }
+ }
+
+ /* 全排列 I */
+ func permutationsI(nums []int) [][]int {
+ res := make([][]int, 0)
+ state := make([]int, 0)
+ selected := make([]bool, len(nums))
+ backtrackI(&state, &nums, &selected, &res)
+ return res
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="permutations_i.swift"
+ /* 回溯算法:全排列 I */
+ func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) {
+ // 当状态长度等于元素数量时,记录解
+ if state.count == choices.count {
+ res.append(state)
+ return
+ }
+ // 遍历所有选择
+ for (i, choice) in choices.enumerated() {
+ // 剪枝:不允许重复选择元素
+ if !selected[i] {
+ // 尝试:做出选择,更新状态
+ selected[i] = true
+ state.append(choice)
+ // 进行下一轮选择
+ backtrack(state: &state, choices: choices, selected: &selected, res: &res)
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false
+ state.removeLast()
+ }
+ }
+ }
+
+ /* 全排列 I */
+ func permutationsI(nums: [Int]) -> [[Int]] {
+ var state: [Int] = []
+ var selected = Array(repeating: false, count: nums.count)
+ var res: [[Int]] = []
+ backtrack(state: &state, choices: nums, selected: &selected, res: &res)
+ return res
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="permutations_i.js"
+ /* 回溯算法:全排列 I */
+ function backtrack(state, choices, selected, res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.length === choices.length) {
+ res.push([...state]);
+ return;
+ }
+ // 遍历所有选择
+ choices.forEach((choice, i) => {
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state.push(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.pop();
+ }
+ });
+ }
+
+ /* 全排列 I */
+ function permutationsI(nums) {
+ const res = [];
+ backtrack([], nums, Array(nums.length).fill(false), res);
+ return res;
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="permutations_i.ts"
+ /* 回溯算法:全排列 I */
+ function backtrack(
+ state: number[],
+ choices: number[],
+ selected: boolean[],
+ res: number[][]
+ ): void {
+ // 当状态长度等于元素数量时,记录解
+ if (state.length === choices.length) {
+ res.push([...state]);
+ return;
+ }
+ // 遍历所有选择
+ choices.forEach((choice, i) => {
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state.push(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.pop();
+ }
+ });
+ }
+
+ /* 全排列 I */
+ function permutationsI(nums: number[]): number[][] {
+ const res: number[][] = [];
+ backtrack([], nums, Array(nums.length).fill(false), res);
+ return res;
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="permutations_i.dart"
+ /* 回溯算法:全排列 I */
+ void backtrack(
+ List state,
+ List choices,
+ List selected,
+ List> res,
+ ) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.length == choices.length) {
+ res.add(List.from(state));
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choices.length; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state.add(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.removeLast();
+ }
+ }
+ }
+
+ /* 全排列 I */
+ List> permutationsI(List nums) {
+ List> res = [];
+ backtrack([], nums, List.filled(nums.length, false), res);
+ return res;
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="permutations_i.rs"
+ /* 回溯算法:全排列 I */
+ fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) {
+ // 当状态长度等于元素数量时,记录解
+ if state.len() == choices.len() {
+ res.push(state);
+ return;
+ }
+ // 遍历所有选择
+ for i in 0..choices.len() {
+ let choice = choices[i];
+ // 剪枝:不允许重复选择元素
+ if !selected[i] {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state.push(choice);
+ // 进行下一轮选择
+ backtrack(state.clone(), choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.remove(state.len() - 1);
+ }
+ }
+ }
+
+ /* 全排列 I */
+ fn permutations_i(nums: &mut [i32]) -> Vec> {
+ let mut res = Vec::new(); // 状态(子集)
+ backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res);
+ res
+ }
+ ```
+
+=== "C"
+
+ ```c title="permutations_i.c"
+ /* 回溯算法:全排列 I */
+ void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
+ // 当状态长度等于元素数量时,记录解
+ if (stateSize == choicesSize) {
+ res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
+ for (int i = 0; i < choicesSize; i++) {
+ res[*resSize][i] = state[i];
+ }
+ (*resSize)++;
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choicesSize; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true;
+ state[stateSize] = choice;
+ // 进行下一轮选择
+ backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ }
+ }
+ }
+
+ /* 全排列 I */
+ int **permutationsI(int *nums, int numsSize, int *returnSize) {
+ int *state = (int *)malloc(numsSize * sizeof(int));
+ bool *selected = (bool *)malloc(numsSize * sizeof(bool));
+ for (int i = 0; i < numsSize; i++) {
+ selected[i] = false;
+ }
+ int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
+ *returnSize = 0;
+
+ backtrack(state, 0, nums, numsSize, selected, res, returnSize);
+
+ free(state);
+ free(selected);
+
+ return res;
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="permutations_i.kt"
+ /* 回溯算法:全排列 I */
+ fun backtrack(
+ state: MutableList,
+ choices: IntArray,
+ selected: BooleanArray,
+ res: MutableList?>
+ ) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.size == choices.size) {
+ res.add(state.toMutableList())
+ return
+ }
+ // 遍历所有选择
+ for (i in choices.indices) {
+ val choice = choices[i]
+ // 剪枝:不允许重复选择元素
+ if (!selected[i]) {
+ // 尝试:做出选择,更新状态
+ selected[i] = true
+ state.add(choice)
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res)
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false
+ state.removeAt(state.size - 1)
+ }
+ }
+ }
+
+ /* 全排列 I */
+ fun permutationsI(nums: IntArray): MutableList?> {
+ val res = mutableListOf?>()
+ backtrack(mutableListOf(), nums, BooleanArray(nums.size), res)
+ return res
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="permutations_i.rb"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{permutations_i}
+ ```
+
+=== "Zig"
+
+ ```zig title="permutations_i.zig"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{permutationsI}
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+## 13.2.2 Considering cases with equal elements
+
+!!! question
+
+ Enter an integer array, **which may contain duplicate elements**, and return all unique permutations.
+
+Suppose the input array is $[1, 1, 2]$. To differentiate the two duplicate elements $1$, we mark the second $1$ as $\hat{1}$.
+
+As shown in the following figure, half of the permutations generated by the above method are duplicates.
+
+{ class="animation-figure" }
+
+ Figure 13-7 Duplicate permutations
+
+So, how do we eliminate duplicate permutations? Most directly, consider using a hash set to deduplicate permutation results. However, this is not elegant, **as branches generating duplicate permutations are unnecessary and should be identified and pruned in advance**, which can further improve algorithm efficiency.
+
+### 1. Pruning of equal elements
+
+Observing the following figure, in the first round, choosing $1$ or $\hat{1}$ results in identical permutations under both choices, thus we should prune $\hat{1}$.
+
+Similarly, after choosing $2$ in the first round, choosing $1$ and $\hat{1}$ in the second round also produces duplicate branches, so we should also prune $\hat{1}$ in the second round.
+
+Essentially, **our goal is to ensure that multiple equal elements are only selected once in each round of choices**.
+
+{ class="animation-figure" }
+
+ Figure 13-8 Duplicate permutations pruning
+
+### 2. Code implementation
+
+Based on the code from the previous problem, we consider initiating a hash set `duplicated` in each round of choices, used to record elements that have been tried in that round, and prune duplicate elements:
+
+=== "Python"
+
+ ```python title="permutations_ii.py"
+ def backtrack(
+ state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
+ ):
+ """回溯算法:全排列 II"""
+ # 当状态长度等于元素数量时,记录解
+ if len(state) == len(choices):
+ res.append(list(state))
+ return
+ # 遍历所有选择
+ duplicated = set[int]()
+ for i, choice in enumerate(choices):
+ # 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if not selected[i] and choice not in duplicated:
+ # 尝试:做出选择,更新状态
+ duplicated.add(choice) # 记录选择过的元素值
+ selected[i] = True
+ state.append(choice)
+ # 进行下一轮选择
+ backtrack(state, choices, selected, res)
+ # 回退:撤销选择,恢复到之前的状态
+ selected[i] = False
+ state.pop()
+
+ def permutations_ii(nums: list[int]) -> list[list[int]]:
+ """全排列 II"""
+ res = []
+ backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
+ return res
+ ```
+
+=== "C++"
+
+ ```cpp title="permutations_ii.cpp"
+ /* 回溯算法:全排列 II */
+ void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.size() == choices.size()) {
+ res.push_back(state);
+ return;
+ }
+ // 遍历所有选择
+ unordered_set duplicated;
+ for (int i = 0; i < choices.size(); i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && duplicated.find(choice) == duplicated.end()) {
+ // 尝试:做出选择,更新状态
+ duplicated.emplace(choice); // 记录选择过的元素值
+ selected[i] = true;
+ state.push_back(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.pop_back();
+ }
+ }
+ }
+
+ /* 全排列 II */
+ vector> permutationsII(vector nums) {
+ vector state;
+ vector selected(nums.size(), false);
+ vector> res;
+ backtrack(state, nums, selected, res);
+ return res;
+ }
+ ```
+
+=== "Java"
+
+ ```java title="permutations_ii.java"
+ /* 回溯算法:全排列 II */
+ void backtrack(List state, int[] choices, boolean[] selected, List> res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.size() == choices.length) {
+ res.add(new ArrayList(state));
+ return;
+ }
+ // 遍历所有选择
+ Set duplicated = new HashSet();
+ for (int i = 0; i < choices.length; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && !duplicated.contains(choice)) {
+ // 尝试:做出选择,更新状态
+ duplicated.add(choice); // 记录选择过的元素值
+ selected[i] = true;
+ state.add(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.remove(state.size() - 1);
+ }
+ }
+ }
+
+ /* 全排列 II */
+ List> permutationsII(int[] nums) {
+ List> res = new ArrayList>();
+ backtrack(new ArrayList(), nums, new boolean[nums.length], res);
+ return res;
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="permutations_ii.cs"
+ /* 回溯算法:全排列 II */
+ void Backtrack(List state, int[] choices, bool[] selected, List> res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.Count == choices.Length) {
+ res.Add(new List(state));
+ return;
+ }
+ // 遍历所有选择
+ HashSet duplicated = [];
+ for (int i = 0; i < choices.Length; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && !duplicated.Contains(choice)) {
+ // 尝试:做出选择,更新状态
+ duplicated.Add(choice); // 记录选择过的元素值
+ selected[i] = true;
+ state.Add(choice);
+ // 进行下一轮选择
+ Backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.RemoveAt(state.Count - 1);
+ }
+ }
+ }
+
+ /* 全排列 II */
+ List> PermutationsII(int[] nums) {
+ List> res = [];
+ Backtrack([], nums, new bool[nums.Length], res);
+ return res;
+ }
+ ```
+
+=== "Go"
+
+ ```go title="permutations_ii.go"
+ /* 回溯算法:全排列 II */
+ func backtrackII(state *[]int, choices *[]int, selected *[]bool, res *[][]int) {
+ // 当状态长度等于元素数量时,记录解
+ if len(*state) == len(*choices) {
+ newState := append([]int{}, *state...)
+ *res = append(*res, newState)
+ }
+ // 遍历所有选择
+ duplicated := make(map[int]struct{}, 0)
+ for i := 0; i < len(*choices); i++ {
+ choice := (*choices)[i]
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if _, ok := duplicated[choice]; !ok && !(*selected)[i] {
+ // 尝试:做出选择,更新状态
+ // 记录选择过的元素值
+ duplicated[choice] = struct{}{}
+ (*selected)[i] = true
+ *state = append(*state, choice)
+ // 进行下一轮选择
+ backtrackII(state, choices, selected, res)
+ // 回退:撤销选择,恢复到之前的状态
+ (*selected)[i] = false
+ *state = (*state)[:len(*state)-1]
+ }
+ }
+ }
+
+ /* 全排列 II */
+ func permutationsII(nums []int) [][]int {
+ res := make([][]int, 0)
+ state := make([]int, 0)
+ selected := make([]bool, len(nums))
+ backtrackII(&state, &nums, &selected, &res)
+ return res
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="permutations_ii.swift"
+ /* 回溯算法:全排列 II */
+ func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) {
+ // 当状态长度等于元素数量时,记录解
+ if state.count == choices.count {
+ res.append(state)
+ return
+ }
+ // 遍历所有选择
+ var duplicated: Set = []
+ for (i, choice) in choices.enumerated() {
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if !selected[i], !duplicated.contains(choice) {
+ // 尝试:做出选择,更新状态
+ duplicated.insert(choice) // 记录选择过的元素值
+ selected[i] = true
+ state.append(choice)
+ // 进行下一轮选择
+ backtrack(state: &state, choices: choices, selected: &selected, res: &res)
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false
+ state.removeLast()
+ }
+ }
+ }
+
+ /* 全排列 II */
+ func permutationsII(nums: [Int]) -> [[Int]] {
+ var state: [Int] = []
+ var selected = Array(repeating: false, count: nums.count)
+ var res: [[Int]] = []
+ backtrack(state: &state, choices: nums, selected: &selected, res: &res)
+ return res
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="permutations_ii.js"
+ /* 回溯算法:全排列 II */
+ function backtrack(state, choices, selected, res) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.length === choices.length) {
+ res.push([...state]);
+ return;
+ }
+ // 遍历所有选择
+ const duplicated = new Set();
+ choices.forEach((choice, i) => {
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && !duplicated.has(choice)) {
+ // 尝试:做出选择,更新状态
+ duplicated.add(choice); // 记录选择过的元素值
+ selected[i] = true;
+ state.push(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.pop();
+ }
+ });
+ }
+
+ /* 全排列 II */
+ function permutationsII(nums) {
+ const res = [];
+ backtrack([], nums, Array(nums.length).fill(false), res);
+ return res;
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="permutations_ii.ts"
+ /* 回溯算法:全排列 II */
+ function backtrack(
+ state: number[],
+ choices: number[],
+ selected: boolean[],
+ res: number[][]
+ ): void {
+ // 当状态长度等于元素数量时,记录解
+ if (state.length === choices.length) {
+ res.push([...state]);
+ return;
+ }
+ // 遍历所有选择
+ const duplicated = new Set();
+ choices.forEach((choice, i) => {
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && !duplicated.has(choice)) {
+ // 尝试:做出选择,更新状态
+ duplicated.add(choice); // 记录选择过的元素值
+ selected[i] = true;
+ state.push(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.pop();
+ }
+ });
+ }
+
+ /* 全排列 II */
+ function permutationsII(nums: number[]): number[][] {
+ const res: number[][] = [];
+ backtrack([], nums, Array(nums.length).fill(false), res);
+ return res;
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="permutations_ii.dart"
+ /* 回溯算法:全排列 II */
+ void backtrack(
+ List state,
+ List choices,
+ List selected,
+ List> res,
+ ) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.length == choices.length) {
+ res.add(List.from(state));
+ return;
+ }
+ // 遍历所有选择
+ Set duplicated = {};
+ for (int i = 0; i < choices.length; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && !duplicated.contains(choice)) {
+ // 尝试:做出选择,更新状态
+ duplicated.add(choice); // 记录选择过的元素值
+ selected[i] = true;
+ state.add(choice);
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.removeLast();
+ }
+ }
+ }
+
+ /* 全排列 II */
+ List> permutationsII(List nums) {
+ List> res = [];
+ backtrack([], nums, List.filled(nums.length, false), res);
+ return res;
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="permutations_ii.rs"
+ /* 回溯算法:全排列 II */
+ fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) {
+ // 当状态长度等于元素数量时,记录解
+ if state.len() == choices.len() {
+ res.push(state);
+ return;
+ }
+ // 遍历所有选择
+ let mut duplicated = HashSet::::new();
+ for i in 0..choices.len() {
+ let choice = choices[i];
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if !selected[i] && !duplicated.contains(&choice) {
+ // 尝试:做出选择,更新状态
+ duplicated.insert(choice); // 记录选择过的元素值
+ selected[i] = true;
+ state.push(choice);
+ // 进行下一轮选择
+ backtrack(state.clone(), choices, selected, res);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ state.remove(state.len() - 1);
+ }
+ }
+ }
+
+ /* 全排列 II */
+ fn permutations_ii(nums: &mut [i32]) -> Vec> {
+ let mut res = Vec::new();
+ backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res);
+ res
+ }
+ ```
+
+=== "C"
+
+ ```c title="permutations_ii.c"
+ /* 回溯算法:全排列 II */
+ void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
+ // 当状态长度等于元素数量时,记录解
+ if (stateSize == choicesSize) {
+ res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
+ for (int i = 0; i < choicesSize; i++) {
+ res[*resSize][i] = state[i];
+ }
+ (*resSize)++;
+ return;
+ }
+ // 遍历所有选择
+ bool duplicated[MAX_SIZE] = {false};
+ for (int i = 0; i < choicesSize; i++) {
+ int choice = choices[i];
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && !duplicated[choice]) {
+ // 尝试:做出选择,更新状态
+ duplicated[choice] = true; // 记录选择过的元素值
+ selected[i] = true;
+ state[stateSize] = choice;
+ // 进行下一轮选择
+ backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false;
+ }
+ }
+ }
+
+ /* 全排列 II */
+ int **permutationsII(int *nums, int numsSize, int *returnSize) {
+ int *state = (int *)malloc(numsSize * sizeof(int));
+ bool *selected = (bool *)malloc(numsSize * sizeof(bool));
+ for (int i = 0; i < numsSize; i++) {
+ selected[i] = false;
+ }
+ int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
+ *returnSize = 0;
+
+ backtrack(state, 0, nums, numsSize, selected, res, returnSize);
+
+ free(state);
+ free(selected);
+
+ return res;
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="permutations_ii.kt"
+ /* 回溯算法:全排列 II */
+ fun backtrack(
+ state: MutableList,
+ choices: IntArray,
+ selected: BooleanArray,
+ res: MutableList?>
+ ) {
+ // 当状态长度等于元素数量时,记录解
+ if (state.size == choices.size) {
+ res.add(state.toMutableList())
+ return
+ }
+ // 遍历所有选择
+ val duplicated = HashSet()
+ for (i in choices.indices) {
+ val choice = choices[i]
+ // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if (!selected[i] && !duplicated.contains(choice)) {
+ // 尝试:做出选择,更新状态
+ duplicated.add(choice) // 记录选择过的元素值
+ selected[i] = true
+ state.add(choice)
+ // 进行下一轮选择
+ backtrack(state, choices, selected, res)
+ // 回退:撤销选择,恢复到之前的状态
+ selected[i] = false
+ state.removeAt(state.size - 1)
+ }
+ }
+ }
+
+ /* 全排列 II */
+ fun permutationsII(nums: IntArray): MutableList?> {
+ val res = mutableListOf?>()
+ backtrack(mutableListOf(), nums, BooleanArray(nums.size), res)
+ return res
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="permutations_ii.rb"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{permutations_ii}
+ ```
+
+=== "Zig"
+
+ ```zig title="permutations_ii.zig"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{permutationsII}
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+Assuming all elements are distinct from each other, there are $n!$ (factorial) permutations of $n$ elements; when recording results, it is necessary to copy a list of length $n$, using $O(n)$ time. **Thus, the time complexity is $O(n!n)$**.
+
+The maximum recursion depth is $n$, using $O(n)$ frame space. `Selected` uses $O(n)$ space. At any one time, there can be up to $n$ `duplicated`, using $O(n^2)$ space. **Therefore, the space complexity is $O(n^2)$**.
+
+### 3. Comparison of the two pruning methods
+
+Please note, although both `selected` and `duplicated` are used for pruning, their targets are different.
+
+- **Repeated choice pruning**: There is only one `selected` throughout the search process. It records which elements are currently in the state, aiming to prevent an element from appearing repeatedly in `state`.
+- **Equal element pruning**: Each round of choices (each call to the `backtrack` function) contains a `duplicated`. It records which elements have been chosen in the current traversal (`for` loop), aiming to ensure equal elements are selected only once.
+
+The following figure shows the scope of the two pruning conditions. Note, each node in the tree represents a choice, and the nodes from the root to the leaf form a permutation.
+
+{ class="animation-figure" }
+
+ Figure 13-9 Scope of the two pruning conditions
diff --git a/en/docs/chapter_backtracking/subset_sum_problem.md b/en/docs/chapter_backtracking/subset_sum_problem.md
new file mode 100644
index 000000000..9b82f9bf1
--- /dev/null
+++ b/en/docs/chapter_backtracking/subset_sum_problem.md
@@ -0,0 +1,1623 @@
+---
+comments: true
+---
+
+# 13.3 Subset sum problem
+
+## 13.3.1 Case without duplicate elements
+
+!!! question
+
+ Given an array of positive integers `nums` and a target positive integer `target`, find all possible combinations such that the sum of the elements in the combination equals `target`. The given array has no duplicate elements, and each element can be chosen multiple times. Please return these combinations as a list, which should not contain duplicate combinations.
+
+For example, for the input set $\{3, 4, 5\}$ and target integer $9$, the solutions are $\{3, 3, 3\}, \{4, 5\}$. Note the following two points.
+
+- Elements in the input set can be chosen an unlimited number of times.
+- Subsets do not distinguish the order of elements, for example $\{4, 5\}$ and $\{5, 4\}$ are the same subset.
+
+### 1. Reference permutation solution
+
+Similar to the permutation problem, we can imagine the generation of subsets as a series of choices, updating the "element sum" in real-time during the choice process. When the element sum equals `target`, the subset is recorded in the result list.
+
+Unlike the permutation problem, **elements in this problem can be chosen an unlimited number of times**, thus there is no need to use a `selected` boolean list to record whether an element has been chosen. We can make minor modifications to the permutation code to initially solve the problem:
+
+=== "Python"
+
+ ```python title="subset_sum_i_naive.py"
+ def backtrack(
+ state: list[int],
+ target: int,
+ total: int,
+ choices: list[int],
+ res: list[list[int]],
+ ):
+ """回溯算法:子集和 I"""
+ # 子集和等于 target 时,记录解
+ if total == target:
+ res.append(list(state))
+ return
+ # 遍历所有选择
+ for i in range(len(choices)):
+ # 剪枝:若子集和超过 target ,则跳过该选择
+ if total + choices[i] > target:
+ continue
+ # 尝试:做出选择,更新元素和 total
+ state.append(choices[i])
+ # 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res)
+ # 回退:撤销选择,恢复到之前的状态
+ state.pop()
+
+ def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]:
+ """求解子集和 I(包含重复子集)"""
+ state = [] # 状态(子集)
+ total = 0 # 子集和
+ res = [] # 结果列表(子集列表)
+ backtrack(state, target, total, nums, res)
+ return res
+ ```
+
+=== "C++"
+
+ ```cpp title="subset_sum_i_naive.cpp"
+ /* 回溯算法:子集和 I */
+ void backtrack(vector &state, int target, int total, vector &choices, vector> &res) {
+ // 子集和等于 target 时,记录解
+ if (total == target) {
+ res.push_back(state);
+ return;
+ }
+ // 遍历所有选择
+ for (size_t i = 0; i < choices.size(); i++) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.push_back(choices[i]);
+ // 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ state.pop_back();
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ vector> subsetSumINaive(vector &nums, int target) {
+ vector state; // 状态(子集)
+ int total = 0; // 子集和
+ vector> res; // 结果列表(子集列表)
+ backtrack(state, target, total, nums, res);
+ return res;
+ }
+ ```
+
+=== "Java"
+
+ ```java title="subset_sum_i_naive.java"
+ /* 回溯算法:子集和 I */
+ void backtrack(List state, int target, int total, int[] choices, List> res) {
+ // 子集和等于 target 时,记录解
+ if (total == target) {
+ res.add(new ArrayList<>(state));
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choices.length; i++) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.add(choices[i]);
+ // 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ state.remove(state.size() - 1);
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ List> subsetSumINaive(int[] nums, int target) {
+ List state = new ArrayList<>(); // 状态(子集)
+ int total = 0; // 子集和
+ List> res = new ArrayList<>(); // 结果列表(子集列表)
+ backtrack(state, target, total, nums, res);
+ return res;
+ }
+ ```
+
+=== "C#"
+
+ ```csharp title="subset_sum_i_naive.cs"
+ /* 回溯算法:子集和 I */
+ void Backtrack(List state, int target, int total, int[] choices, List> res) {
+ // 子集和等于 target 时,记录解
+ if (total == target) {
+ res.Add(new List(state));
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choices.Length; i++) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.Add(choices[i]);
+ // 进行下一轮选择
+ Backtrack(state, target, total + choices[i], choices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ state.RemoveAt(state.Count - 1);
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ List> SubsetSumINaive(int[] nums, int target) {
+ List state = []; // 状态(子集)
+ int total = 0; // 子集和
+ List> res = []; // 结果列表(子集列表)
+ Backtrack(state, target, total, nums, res);
+ return res;
+ }
+ ```
+
+=== "Go"
+
+ ```go title="subset_sum_i_naive.go"
+ /* 回溯算法:子集和 I */
+ func backtrackSubsetSumINaive(total, target int, state, choices *[]int, res *[][]int) {
+ // 子集和等于 target 时,记录解
+ if target == total {
+ newState := append([]int{}, *state...)
+ *res = append(*res, newState)
+ return
+ }
+ // 遍历所有选择
+ for i := 0; i < len(*choices); i++ {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if total+(*choices)[i] > target {
+ continue
+ }
+ // 尝试:做出选择,更新元素和 total
+ *state = append(*state, (*choices)[i])
+ // 进行下一轮选择
+ backtrackSubsetSumINaive(total+(*choices)[i], target, state, choices, res)
+ // 回退:撤销选择,恢复到之前的状态
+ *state = (*state)[:len(*state)-1]
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ func subsetSumINaive(nums []int, target int) [][]int {
+ state := make([]int, 0) // 状态(子集)
+ total := 0 // 子集和
+ res := make([][]int, 0) // 结果列表(子集列表)
+ backtrackSubsetSumINaive(total, target, &state, &nums, &res)
+ return res
+ }
+ ```
+
+=== "Swift"
+
+ ```swift title="subset_sum_i_naive.swift"
+ /* 回溯算法:子集和 I */
+ func backtrack(state: inout [Int], target: Int, total: Int, choices: [Int], res: inout [[Int]]) {
+ // 子集和等于 target 时,记录解
+ if total == target {
+ res.append(state)
+ return
+ }
+ // 遍历所有选择
+ for i in choices.indices {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if total + choices[i] > target {
+ continue
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.append(choices[i])
+ // 进行下一轮选择
+ backtrack(state: &state, target: target, total: total + choices[i], choices: choices, res: &res)
+ // 回退:撤销选择,恢复到之前的状态
+ state.removeLast()
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ func subsetSumINaive(nums: [Int], target: Int) -> [[Int]] {
+ var state: [Int] = [] // 状态(子集)
+ let total = 0 // 子集和
+ var res: [[Int]] = [] // 结果列表(子集列表)
+ backtrack(state: &state, target: target, total: total, choices: nums, res: &res)
+ return res
+ }
+ ```
+
+=== "JS"
+
+ ```javascript title="subset_sum_i_naive.js"
+ /* 回溯算法:子集和 I */
+ function backtrack(state, target, total, choices, res) {
+ // 子集和等于 target 时,记录解
+ if (total === target) {
+ res.push([...state]);
+ return;
+ }
+ // 遍历所有选择
+ for (let i = 0; i < choices.length; i++) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.push(choices[i]);
+ // 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ state.pop();
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ function subsetSumINaive(nums, target) {
+ const state = []; // 状态(子集)
+ const total = 0; // 子集和
+ const res = []; // 结果列表(子集列表)
+ backtrack(state, target, total, nums, res);
+ return res;
+ }
+ ```
+
+=== "TS"
+
+ ```typescript title="subset_sum_i_naive.ts"
+ /* 回溯算法:子集和 I */
+ function backtrack(
+ state: number[],
+ target: number,
+ total: number,
+ choices: number[],
+ res: number[][]
+ ): void {
+ // 子集和等于 target 时,记录解
+ if (total === target) {
+ res.push([...state]);
+ return;
+ }
+ // 遍历所有选择
+ for (let i = 0; i < choices.length; i++) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.push(choices[i]);
+ // 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ state.pop();
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ function subsetSumINaive(nums: number[], target: number): number[][] {
+ const state = []; // 状态(子集)
+ const total = 0; // 子集和
+ const res = []; // 结果列表(子集列表)
+ backtrack(state, target, total, nums, res);
+ return res;
+ }
+ ```
+
+=== "Dart"
+
+ ```dart title="subset_sum_i_naive.dart"
+ /* 回溯算法:子集和 I */
+ void backtrack(
+ List state,
+ int target,
+ int total,
+ List choices,
+ List> res,
+ ) {
+ // 子集和等于 target 时,记录解
+ if (total == target) {
+ res.add(List.from(state));
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choices.length; i++) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.add(choices[i]);
+ // 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ state.removeLast();
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ List> subsetSumINaive(List nums, int target) {
+ List state = []; // 状态(子集)
+ int total = 0; // 元素和
+ List> res = []; // 结果列表(子集列表)
+ backtrack(state, target, total, nums, res);
+ return res;
+ }
+ ```
+
+=== "Rust"
+
+ ```rust title="subset_sum_i_naive.rs"
+ /* 回溯算法:子集和 I */
+ fn backtrack(
+ mut state: Vec,
+ target: i32,
+ total: i32,
+ choices: &[i32],
+ res: &mut Vec>,
+ ) {
+ // 子集和等于 target 时,记录解
+ if total == target {
+ res.push(state);
+ return;
+ }
+ // 遍历所有选择
+ for i in 0..choices.len() {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if total + choices[i] > target {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.push(choices[i]);
+ // 进行下一轮选择
+ backtrack(state.clone(), target, total + choices[i], choices, res);
+ // 回退:撤销选择,恢复到之前的状态
+ state.pop();
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ fn subset_sum_i_naive(nums: &[i32], target: i32) -> Vec> {
+ let state = Vec::new(); // 状态(子集)
+ let total = 0; // 子集和
+ let mut res = Vec::new(); // 结果列表(子集列表)
+ backtrack(state, target, total, nums, &mut res);
+ res
+ }
+ ```
+
+=== "C"
+
+ ```c title="subset_sum_i_naive.c"
+ /* 回溯算法:子集和 I */
+ void backtrack(int target, int total, int *choices, int choicesSize) {
+ // 子集和等于 target 时,记录解
+ if (total == target) {
+ for (int i = 0; i < stateSize; i++) {
+ res[resSize][i] = state[i];
+ }
+ resColSizes[resSize++] = stateSize;
+ return;
+ }
+ // 遍历所有选择
+ for (int i = 0; i < choicesSize; i++) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // 尝试:做出选择,更新元素和 total
+ state[stateSize++] = choices[i];
+ // 进行下一轮选择
+ backtrack(target, total + choices[i], choices, choicesSize);
+ // 回退:撤销选择,恢复到之前的状态
+ stateSize--;
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ void subsetSumINaive(int *nums, int numsSize, int target) {
+ resSize = 0; // 初始化解的数量为0
+ backtrack(target, 0, nums, numsSize);
+ }
+ ```
+
+=== "Kotlin"
+
+ ```kotlin title="subset_sum_i_naive.kt"
+ /* 回溯算法:子集和 I */
+ fun backtrack(
+ state: MutableList,
+ target: Int,
+ total: Int,
+ choices: IntArray,
+ res: MutableList?>
+ ) {
+ // 子集和等于 target 时,记录解
+ if (total == target) {
+ res.add(state.toMutableList())
+ return
+ }
+ // 遍历所有选择
+ for (i in choices.indices) {
+ // 剪枝:若子集和超过 target ,则跳过该选择
+ if (total + choices[i] > target) {
+ continue
+ }
+ // 尝试:做出选择,更新元素和 total
+ state.add(choices[i])
+ // 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res)
+ // 回退:撤销选择,恢复到之前的状态
+ state.removeAt(state.size - 1)
+ }
+ }
+
+ /* 求解子集和 I(包含重复子集) */
+ fun subsetSumINaive(nums: IntArray, target: Int): MutableList?> {
+ val state = mutableListOf() // 状态(子集)
+ val total = 0 // 子集和
+ val res = mutableListOf?>() // 结果列表(子集列表)
+ backtrack(state, target, total, nums, res)
+ return res
+ }
+ ```
+
+=== "Ruby"
+
+ ```ruby title="subset_sum_i_naive.rb"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{subset_sum_i_naive}
+ ```
+
+=== "Zig"
+
+ ```zig title="subset_sum_i_naive.zig"
+ [class]{}-[func]{backtrack}
+
+ [class]{}-[func]{subsetSumINaive}
+ ```
+
+??? pythontutor "Code Visualization"
+
+