@@ -1334,12 +1328,6 @@ comments: true
end
```
-=== "Zig"
-
- ```zig title="array_binary_tree.zig"
- [class]{ArrayBinaryTree}-[func]{}
- ```
-
??? pythontutor "可视化运行"
diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md
index 3648fe73f..e2ee21221 100644
--- a/docs/chapter_tree/avl_tree.md
+++ b/docs/chapter_tree/avl_tree.md
@@ -236,12 +236,6 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二
end
```
-=== "Zig"
-
- ```zig title=""
-
- ```
-
“节点高度”是指从该节点到它的最远叶节点的距离,即所经过的“边”的数量。需要特别注意的是,叶节点的高度为 $0$ ,而空节点的高度为 $-1$ 。我们将创建两个工具函数,分别用于获取和更新节点的高度:
=== "Python"
@@ -481,23 +475,6 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二
end
```
-=== "Zig"
-
- ```zig title="avl_tree.zig"
- // 获取节点高度
- fn height(self: *Self, node: ?*inc.TreeNode(T)) i32 {
- _ = self;
- // 空节点高度为 -1 ,叶节点高度为 0
- return if (node == null) -1 else node.?.height;
- }
-
- // 更新节点高度
- fn updateHeight(self: *Self, node: ?*inc.TreeNode(T)) void {
- // 节点高度等于最高子树高度 + 1
- node.?.height = @max(self.height(node.?.left), self.height(node.?.right)) + 1;
- }
- ```
-
### 2. 节点平衡因子
节点的平衡因子(balance factor)定义为节点左子树的高度减去右子树的高度,同时规定空节点的平衡因子为 $0$ 。我们同样将获取节点平衡因子的功能封装成函数,方便后续使用:
@@ -669,18 +646,6 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二
end
```
-=== "Zig"
-
- ```zig title="avl_tree.zig"
- // 获取平衡因子
- fn balanceFactor(self: *Self, node: ?*inc.TreeNode(T)) i32 {
- // 空节点平衡因子为 0
- if (node == null) return 0;
- // 节点平衡因子 = 左子树高度 - 右子树高度
- return self.height(node.?.left) - self.height(node.?.right);
- }
- ```
-
!!! tip
设平衡因子为 $f$ ,则一棵 AVL 树的任意节点的平衡因子皆满足 $-1 \le f \le 1$ 。
@@ -956,24 +921,6 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中
end
```
-=== "Zig"
-
- ```zig title="avl_tree.zig"
- // 右旋操作
- fn rightRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) {
- var child = node.?.left;
- var grandChild = child.?.right;
- // 以 child 为原点,将 node 向右旋转
- child.?.right = node;
- node.?.left = grandChild;
- // 更新节点高度
- self.updateHeight(node);
- self.updateHeight(child);
- // 返回旋转后子树的根节点
- return child;
- }
- ```
-
### 2. 左旋
相应地,如果考虑上述失衡二叉树的“镜像”,则需要执行图 7-28 所示的“左旋”操作。
@@ -1229,24 +1176,6 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中
end
```
-=== "Zig"
-
- ```zig title="avl_tree.zig"
- // 左旋操作
- fn leftRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) {
- var child = node.?.right;
- var grandChild = child.?.left;
- // 以 child 为原点,将 node 向左旋转
- child.?.left = node;
- node.?.right = grandChild;
- // 更新节点高度
- self.updateHeight(node);
- self.updateHeight(child);
- // 返回旋转后子树的根节点
- return child;
- }
- ```
-
### 3. 先左旋后右旋
对于图 7-30 中的失衡节点 3 ,仅使用左旋或右旋都无法使子树恢复平衡。此时需要先对 `child` 执行“左旋”,再对 `node` 执行“右旋”。
@@ -1730,40 +1659,6 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中
end
```
-=== "Zig"
-
- ```zig title="avl_tree.zig"
- // 执行旋转操作,使该子树重新恢复平衡
- fn rotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) {
- // 获取节点 node 的平衡因子
- var balance_factor = self.balanceFactor(node);
- // 左偏树
- if (balance_factor > 1) {
- if (self.balanceFactor(node.?.left) >= 0) {
- // 右旋
- return self.rightRotate(node);
- } else {
- // 先左旋后右旋
- node.?.left = self.leftRotate(node.?.left);
- return self.rightRotate(node);
- }
- }
- // 右偏树
- if (balance_factor < -1) {
- if (self.balanceFactor(node.?.right) <= 0) {
- // 左旋
- return self.leftRotate(node);
- } else {
- // 先右旋后左旋
- node.?.right = self.rightRotate(node.?.right);
- return self.leftRotate(node);
- }
- }
- // 平衡树,无须旋转,直接返回
- return node;
- }
- ```
-
## 7.5.3 AVL 树常用操作
### 1. 插入节点
@@ -2142,38 +2037,6 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区
end
```
-=== "Zig"
-
- ```zig title="avl_tree.zig"
- // 插入节点
- fn insert(self: *Self, val: T) !void {
- self.root = (try self.insertHelper(self.root, val)).?;
- }
-
- // 递归插入节点(辅助方法)
- fn insertHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) !?*inc.TreeNode(T) {
- var node = node_;
- if (node == null) {
- var tmp_node = try self.mem_allocator.create(inc.TreeNode(T));
- tmp_node.init(val);
- return tmp_node;
- }
- // 1. 查找插入位置并插入节点
- if (val < node.?.val) {
- node.?.left = try self.insertHelper(node.?.left, val);
- } else if (val > node.?.val) {
- node.?.right = try self.insertHelper(node.?.right, val);
- } else {
- return node; // 重复节点不插入,直接返回
- }
- self.updateHeight(node); // 更新节点高度
- // 2. 执行旋转操作,使该子树重新恢复平衡
- node = self.rotate(node);
- // 返回子树的根节点
- return node;
- }
- ```
-
### 2. 删除节点
类似地,在二叉搜索树的删除节点方法的基础上,需要从底至顶执行旋转操作,使所有失衡节点恢复平衡。代码如下所示:
@@ -2775,51 +2638,6 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区
end
```
-=== "Zig"
-
- ```zig title="avl_tree.zig"
- // 删除节点
- fn remove(self: *Self, val: T) void {
- self.root = self.removeHelper(self.root, val).?;
- }
-
- // 递归删除节点(辅助方法)
- fn removeHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) ?*inc.TreeNode(T) {
- var node = node_;
- if (node == null) return null;
- // 1. 查找节点并删除
- if (val < node.?.val) {
- node.?.left = self.removeHelper(node.?.left, val);
- } else if (val > node.?.val) {
- node.?.right = self.removeHelper(node.?.right, val);
- } else {
- if (node.?.left == null or node.?.right == null) {
- var child = if (node.?.left != null) node.?.left else node.?.right;
- // 子节点数量 = 0 ,直接删除 node 并返回
- if (child == null) {
- return null;
- // 子节点数量 = 1 ,直接删除 node
- } else {
- node = child;
- }
- } else {
- // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点
- var temp = node.?.right;
- while (temp.?.left != null) {
- temp = temp.?.left;
- }
- node.?.right = self.removeHelper(node.?.right, temp.?.val);
- node.?.val = temp.?.val;
- }
- }
- self.updateHeight(node); // 更新节点高度
- // 2. 执行旋转操作,使该子树重新恢复平衡
- node = self.rotate(node);
- // 返回子树的根节点
- return node;
- }
- ```
-
### 3. 查找节点
AVL 树的节点查找操作与二叉搜索树一致,在此不再赘述。
diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md
index 0602ab231..1cdd5bcc2 100755
--- a/docs/chapter_tree/binary_search_tree.md
+++ b/docs/chapter_tree/binary_search_tree.md
@@ -338,30 +338,6 @@ comments: true
end
```
-=== "Zig"
-
- ```zig title="binary_search_tree.zig"
- // 查找节点
- fn search(self: *Self, num: T) ?*inc.TreeNode(T) {
- var cur = self.root;
- // 循环查找,越过叶节点后跳出
- while (cur != null) {
- // 目标节点在 cur 的右子树中
- if (cur.?.val < num) {
- cur = cur.?.right;
- // 目标节点在 cur 的左子树中
- } else if (cur.?.val > num) {
- cur = cur.?.left;
- // 找到目标节点,跳出循环
- } else {
- break;
- }
- }
- // 返回目标节点
- return cur;
- }
- ```
-
??? pythontutor "可视化运行"
@@ -826,42 +802,6 @@ comments: true
end
```
-=== "Zig"
-
- ```zig title="binary_search_tree.zig"
- // 插入节点
- fn insert(self: *Self, num: T) !void {
- // 若树为空,则初始化根节点
- if (self.root == null) {
- self.root = try self.mem_allocator.create(inc.TreeNode(T));
- return;
- }
- var cur = self.root;
- var pre: ?*inc.TreeNode(T) = null;
- // 循环查找,越过叶节点后跳出
- while (cur != null) {
- // 找到重复节点,直接返回
- if (cur.?.val == num) return;
- pre = cur;
- // 插入位置在 cur 的右子树中
- if (cur.?.val < num) {
- cur = cur.?.right;
- // 插入位置在 cur 的左子树中
- } else {
- cur = cur.?.left;
- }
- }
- // 插入节点
- var node = try self.mem_allocator.create(inc.TreeNode(T));
- node.init(num);
- if (pre.?.val < num) {
- pre.?.right = node;
- } else {
- pre.?.left = node;
- }
- }
- ```
-
??? pythontutor "可视化运行"
@@ -1648,56 +1588,6 @@ comments: true
end
```
-=== "Zig"
-
- ```zig title="binary_search_tree.zig"
- // 删除节点
- fn remove(self: *Self, num: T) void {
- // 若树为空,直接提前返回
- if (self.root == null) return;
- var cur = self.root;
- var pre: ?*inc.TreeNode(T) = null;
- // 循环查找,越过叶节点后跳出
- while (cur != null) {
- // 找到待删除节点,跳出循环
- if (cur.?.val == num) break;
- pre = cur;
- // 待删除节点在 cur 的右子树中
- if (cur.?.val < num) {
- cur = cur.?.right;
- // 待删除节点在 cur 的左子树中
- } else {
- cur = cur.?.left;
- }
- }
- // 若无待删除节点,则直接返回
- if (cur == null) return;
- // 子节点数量 = 0 or 1
- if (cur.?.left == null or cur.?.right == null) {
- // 当子节点数量 = 0 / 1 时, child = null / 该子节点
- var child = if (cur.?.left != null) cur.?.left else cur.?.right;
- // 删除节点 cur
- if (pre.?.left == cur) {
- pre.?.left = child;
- } else {
- pre.?.right = child;
- }
- // 子节点数量 = 2
- } else {
- // 获取中序遍历中 cur 的下一个节点
- var tmp = cur.?.right;
- while (tmp.?.left != null) {
- tmp = tmp.?.left;
- }
- var tmp_val = tmp.?.val;
- // 递归删除节点 tmp
- self.remove(tmp.?.val);
- // 用 tmp 覆盖 cur
- cur.?.val = tmp_val;
- }
- }
- ```
-
??? pythontutor "可视化运行"
diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md
index 84e788d8e..93548fd37 100644
--- a/docs/chapter_tree/binary_tree.md
+++ b/docs/chapter_tree/binary_tree.md
@@ -205,12 +205,6 @@ comments: true
end
```
-=== "Zig"
-
- ```zig title=""
-
- ```
-
每个节点都有两个引用(指针),分别指向左子节点(left-child node)和右子节点(right-child node),该节点被称为这两个子节点的父节点(parent node)。当给定一个二叉树的节点时,我们将该节点的左子节点及其以下节点形成的树称为该节点的左子树(left subtree),同理可得右子树(right subtree)。
**在二叉树中,除叶节点外,其他所有节点都包含子节点和非空子树**。如图 7-1 所示,如果将“节点 2”视为父节点,则其左子节点和右子节点分别是“节点 4”和“节点 5”,左子树是“节点 4 及其以下节点形成的树”,右子树是“节点 5 及其以下节点形成的树”。
@@ -463,12 +457,6 @@ comments: true
n2.right = n5
```
-=== "Zig"
-
- ```zig title="binary_tree.zig"
-
- ```
-
??? pythontutor "可视化运行"
@@ -638,12 +626,6 @@ comments: true
n1.left = n2
```
-=== "Zig"
-
- ```zig title="binary_tree.zig"
-
- ```
-
??? pythontutor "可视化运行"
diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md
index d0a90e11c..9dbbc550a 100755
--- a/docs/chapter_tree/binary_tree_traversal.md
+++ b/docs/chapter_tree/binary_tree_traversal.md
@@ -334,38 +334,6 @@ comments: true
end
```
-=== "Zig"
-
- ```zig title="binary_tree_bfs.zig"
- // 层序遍历
- fn levelOrder(comptime T: type, mem_allocator: std.mem.Allocator, root: *inc.TreeNode(T)) !std.ArrayList(T) {
- // 初始化队列,加入根节点
- const L = std.TailQueue(*inc.TreeNode(T));
- var queue = L{};
- var root_node = try mem_allocator.create(L.Node);
- root_node.data = root;
- queue.append(root_node);
- // 初始化一个列表,用于保存遍历序列
- var list = std.ArrayList(T).init(std.heap.page_allocator);
- while (queue.len > 0) {
- var queue_node = queue.popFirst().?; // 队列出队
- var node = queue_node.data;
- try list.append(node.val); // 保存节点值
- if (node.left != null) {
- var tmp_node = try mem_allocator.create(L.Node);
- tmp_node.data = node.left.?;
- queue.append(tmp_node); // 左子节点入队
- }
- if (node.right != null) {
- var tmp_node = try mem_allocator.create(L.Node);
- tmp_node.data = node.right.?;
- queue.append(tmp_node); // 右子节点入队
- }
- }
- return list;
- }
- ```
-
??? pythontutor "可视化运行"
@@ -851,37 +819,6 @@ comments: true
end
```
-=== "Zig"
-
- ```zig title="binary_tree_dfs.zig"
- // 前序遍历
- fn preOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void {
- if (root == null) return;
- // 访问优先级:根节点 -> 左子树 -> 右子树
- try list.append(root.?.val);
- try preOrder(T, root.?.left);
- try preOrder(T, root.?.right);
- }
-
- // 中序遍历
- fn inOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void {
- if (root == null) return;
- // 访问优先级:左子树 -> 根节点 -> 右子树
- try inOrder(T, root.?.left);
- try list.append(root.?.val);
- try inOrder(T, root.?.right);
- }
-
- // 后序遍历
- fn postOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void {
- if (root == null) return;
- // 访问优先级:左子树 -> 右子树 -> 根节点
- try postOrder(T, root.?.left);
- try postOrder(T, root.?.right);
- try list.append(root.?.val);
- }
- ```
-
??? pythontutor "可视化运行"
diff --git a/en/docs/chapter_appendix/contribution.md b/en/docs/chapter_appendix/contribution.md
index 924351851..339629089 100644
--- a/en/docs/chapter_appendix/contribution.md
+++ b/en/docs/chapter_appendix/contribution.md
@@ -2,45 +2,45 @@
comments: true
---
-# 16.2 Contributing
+# 16.2 Contributing Together
-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.
+Due to limited capacity, there may be inevitable omissions and errors in this book. We appreciate your understanding and are grateful for your help in correcting them. If you discover typos, broken links, missing content, ambiguous wording, unclear explanations, or structural issues, please help us make corrections to provide readers with higher-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.
+The GitHub IDs of all [contributors](https://github.com/krahets/hello-algo/graphs/contributors) will be displayed on the homepage of the book repository, the web version, and the PDF version to acknowledge their selfless contributions to the open source community.
-!!! success "The charm of open source"
+!!! 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.
+ The interval between two printings of a physical book is often quite long, making content updates very inconvenient.
-### 1. Content fine-tuning
+ In this open source book, the time for content updates has been shortened to just days or even hours.
-As shown in 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. Minor Content Adjustments
-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.
+As shown in Figure 16-3, there is an "edit icon" in the top-right corner of each page. You can modify text or code by following these steps.
-{ class="animation-figure" }
+1. Click the "edit icon". If you encounter a prompt asking you to "Fork this repository", please approve the operation.
+2. Modify the content of the Markdown source file, verify the correctness of the content, and maintain consistent formatting as much as possible.
+3. Fill in a description of your changes at the bottom of the page, then click the "Propose file change" button. After the page transitions, click the "Create pull request" button to submit your pull request.
-
Figure 16-3 Edit page button
+{ class="animation-figure" }
-Figures 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 figures as soon as possible.
+
Figure 16-3 Page edit button
-### 2. Content creation
+Images cannot be directly modified. Please describe the issue by creating a new [Issue](https://github.com/krahets/hello-algo/issues) or leaving a comment. We will promptly redraw and replace the images.
-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.
+### 2. Content Creation
-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.
+If you are interested in contributing to this open source project, including translating code into other programming languages or expanding article content, you will need to follow the Pull Request workflow below.
-### 3. Docker deployment
+1. Log in to GitHub and Fork the book's [code repository](https://github.com/krahets/hello-algo) to your personal account.
+2. Enter your forked repository webpage and use the `git clone` command to clone the repository to your local machine.
+3. Create content locally and conduct comprehensive tests to verify code correctness.
+4. Commit your local changes and push them to the remote repository.
+5. Refresh the repository webpage and click the "Create pull request" button to submit your pull request.
-In the `hello-algo` root directory, execute the following Docker script to access the project at `http://localhost:8000`:
+### 3. Docker Deployment
+
+From the root directory of `hello-algo`, run the following Docker script to access the project at `http://localhost:8000`:
```shell
docker-compose up -d
diff --git a/en/docs/chapter_appendix/index.md b/en/docs/chapter_appendix/index.md
index 807cc622a..fdc1e490c 100644
--- a/en/docs/chapter_appendix/index.md
+++ b/en/docs/chapter_appendix/index.md
@@ -9,6 +9,6 @@ icon: material/help-circle-outline
## Chapter contents
-- [16.1 Installation](installation.md)
-- [16.2 Contributing](contribution.md)
-- [16.3 Terminology](terminology.md)
+- [16.1 Programming Environment Installation](installation.md)
+- [16.2 Contributing Together](contribution.md)
+- [16.3 Terminology Table](terminology.md)
diff --git a/en/docs/chapter_appendix/installation.md b/en/docs/chapter_appendix/installation.md
index fc8b3b357..be665333d 100644
--- a/en/docs/chapter_appendix/installation.md
+++ b/en/docs/chapter_appendix/installation.md
@@ -2,75 +2,75 @@
comments: true
---
-# 16.1 Installation
+# 16.1 Programming Environment Installation
-## 16.1.1 Install IDE
+## 16.1.1 Installing 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.
+We recommend using the open-source and lightweight VS Code as the local integrated development environment (IDE). Visit the [VS Code official website](https://code.visualstudio.com/), and download and install the appropriate version of VS Code according to your operating system.
-{ class="animation-figure" }
+{ class="animation-figure" }
-
Figure 16-1 Download VS Code from the official website
+
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 Figure 16-2.
+VS Code has a powerful ecosystem of extensions that supports running and debugging most programming languages. For example, after installing the "Python Extension Pack" extension, you can debug Python code. The installation steps are shown in the following figure.
-{ class="animation-figure" }
+{ class="animation-figure" }
-
Figure 16-2 Install VS Code Extension Pack
+
Figure 16-2 Install VS Code Extensions
-## 16.1.2 Install language environments
+## 16.1.2 Installing Language Environments
-### 1. Python environment
+### 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.
+1. Download and install [Miniconda3](https://docs.conda.io/en/latest/miniconda.html), which requires Python 3.10 or newer.
+2. Search for `python` in the VS Code extension marketplace and install the Python Extension Pack.
+3. (Optional) Enter `pip install black` on the command line to install the code formatter.
-### 2. C/C++ environment
+### 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.
+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 built-in and does not require installation.
+2. Search for `c++` in the VS Code extension marketplace 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
+### 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.
+2. Search for `java` in the VS Code extension marketplace and install the Extension Pack for Java.
-### 4. C# environment
+### 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)).
+2. Search for `C# Dev Kit` in the VS Code extension marketplace and install 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
+### 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.
+1. Download and install [Go](https://go.dev/dl/).
+2. Search for `go` in the VS Code extension marketplace and install Go.
+3. Press `Ctrl + Shift + P` to open the command palette, type `go`, select `Go: Install/Update Tools`, check all options and install.
-### 6. Swift environment
+### 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).
+2. Search for `swift` in the VS Code extension marketplace and install [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang).
-### 7. JavaScript environment
+### 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.
+2. (Optional) Search for `Prettier` in the VS Code extension marketplace and install the code formatter.
-### 8. TypeScript environment
+### 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).
+3. Search for `typescript` in the VS Code extension marketplace and install [Pretty TypeScript Errors](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors).
-### 9. Dart environment
+### 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).
+2. Search for `dart` in the VS Code extension marketplace and install [Dart](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code).
-### 10. Rust environment
+### 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).
+2. Search for `rust` in the VS Code extension marketplace 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
index e8e0957e0..085304a38 100644
--- a/en/docs/chapter_appendix/terminology.md
+++ b/en/docs/chapter_appendix/terminology.md
@@ -2,19 +2,19 @@
comments: true
---
-# 16.3 Glossary
+# 16.3 Terminology Table
-Table 16-1 lists the important terms that appear in the book, and it is worth noting the following points.
+The following table lists important terms that appear in this book. 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.
+- We recommend remembering the English names of terms to help with reading English literature.
+- Some terms have different names in Simplified Chinese and Traditional Chinese.
Table 16-1 Important Terms in Data Structures and Algorithms
-| English | 简体中文 | 繁体中文 |
-| ------------------------------ | -------------- | -------------- |
+| English | Simplified Chinese | Traditional Chinese |
+| ------------------------------ | ------------------ | ------------------- |
| algorithm | 算法 | 演算法 |
| data structure | 数据结构 | 資料結構 |
| code | 代码 | 程式碼 |
diff --git a/en/docs/chapter_array_and_linkedlist/array.md b/en/docs/chapter_array_and_linkedlist/array.md
index 4494f3044..569536196 100755
--- a/en/docs/chapter_array_and_linkedlist/array.md
+++ b/en/docs/chapter_array_and_linkedlist/array.md
@@ -4,17 +4,17 @@ comments: true
# 4.1 Array
-An array is a linear data structure that operates as a lineup of similar items, stored together in a computer's memory in contiguous spaces. It's like a sequence that maintains organized storage. Each item in this lineup has its unique 'spot' known as an index. Please refer to Figure 4-1 to observe how arrays work and grasp these key terms.
+An array is a linear data structure that stores elements of the same type in contiguous memory space. The position of an element in the array is called the element's index. Figure 4-1 illustrates the main concepts and storage method of arrays.
{ class="animation-figure" }
Figure 4-1 Array definition and storage method
-## 4.1.1 Common operations on arrays
+## 4.1.1 Common Array Operations
-### 1. Initializing arrays
+### 1. Initializing Arrays
-Arrays can be initialized in two ways depending on the needs: either without initial values or with specified initial values. When initial values are not specified, most programming languages will set the array elements to $0$:
+We can choose between two array initialization methods based on our needs: without initial values or with given initial values. When no initial values are specified, most programming languages will initialize array elements to $0$:
=== "Python"
@@ -31,7 +31,7 @@ Arrays can be initialized in two ways depending on the needs: either without ini
// Stored on stack
int arr[5];
int nums[5] = { 1, 3, 2, 5, 4 };
- // Stored on heap (manual memory release needed)
+ // Stored on heap (requires manual memory release)
int* arr1 = new int[5];
int* nums1 = new int[5] { 1, 3, 2, 5, 4 };
```
@@ -57,9 +57,9 @@ Arrays can be initialized in two ways depending on the needs: either without ini
```go title="array.go"
/* Initialize array */
var arr [5]int
- // In Go, specifying the length ([5]int) denotes an array, while not specifying it ([]int) denotes a slice.
- // Since Go's arrays are designed to have compile-time fixed length, only constants can be used to specify the length.
- // For convenience in implementing the extend() method, the Slice will be considered as an Array here.
+ // In Go, specifying length ([5]int) creates an array; not specifying length ([]int) creates a slice
+ // Since Go's arrays are designed to have their length determined at compile time, only constants can be used to specify the length
+ // For convenience in implementing the extend() method, slices are treated as arrays below
nums := []int{1, 3, 2, 5, 4}
```
@@ -101,10 +101,10 @@ Arrays can be initialized in two ways depending on the needs: either without ini
/* Initialize array */
let arr: [i32; 5] = [0; 5]; // [0, 0, 0, 0, 0]
let slice: &[i32] = &[0; 5];
- // In Rust, specifying the length ([i32; 5]) denotes an array, while not specifying it (&[i32]) denotes a slice.
- // Since Rust's arrays are designed to have compile-time fixed length, only constants can be used to specify the length.
- // Vectors are generally used as dynamic arrays in Rust.
- // For convenience in implementing the extend() method, the vector will be considered as an array here.
+ // In Rust, specifying length ([i32; 5]) creates an array; not specifying length (&[i32]) creates a slice
+ // Since Rust's arrays are designed to have their length determined at compile time, only constants can be used to specify the length
+ // Vector is the type generally used as a dynamic array in Rust
+ // For convenience in implementing the extend() method, vectors are treated as arrays below
let nums: Vec = vec![1, 3, 2, 5, 4];
```
@@ -119,37 +119,44 @@ Arrays can be initialized in two ways depending on the needs: either without ini
=== "Kotlin"
```kotlin title="array.kt"
-
+ /* Initialize array */
+ var arr = IntArray(5) // { 0, 0, 0, 0, 0 }
+ var nums = intArrayOf(1, 3, 2, 5, 4)
```
-=== "Zig"
+=== "Ruby"
- ```zig title="array.zig"
- // Initialize array
- var arr = [_]i32{0} ** 5; // { 0, 0, 0, 0, 0 }
- var nums = [_]i32{ 1, 3, 2, 5, 4 };
+ ```ruby title="array.rb"
+ # Initialize array
+ arr = Array.new(5, 0)
+ nums = [1, 3, 2, 5, 4]
```
-### 2. Accessing elements
+??? pythontutor "Code Visualization"
-Elements in an array are stored in contiguous memory spaces, making it simpler to compute each element's memory address. The formula shown in the Figure below aids in determining an element's memory address, utilizing the array's memory address (specifically, the first element's address) and the element's index. This computation streamlines direct access to the desired element.
+
+
+
+### 2. Accessing Elements
+
+Array elements are stored in contiguous memory space, which means calculating the memory address of array elements is very easy. Given the array's memory address (the memory address of the first element) and an element's index, we can use the formula shown in Figure 4-2 to calculate the element's memory address and directly access that element.
{ class="animation-figure" }
Figure 4-2 Memory address calculation for array elements
-As observed in Figure 4-2, array indexing conventionally begins at $0$. While this might appear counterintuitive, considering counting usually starts at $1$, within the address calculation formula, **an index is essentially an offset from the memory address**. For the first element's address, this offset is $0$, validating its index as $0$.
+Observing Figure 4-2, we find that the first element of an array has an index of $0$, which may seem counterintuitive since counting from $1$ would be more natural. However, from the perspective of the address calculation formula, **an index is essentially an offset from the memory address**. The address offset of the first element is $0$, so it is reasonable for its index to be $0$.
-Accessing elements in an array is highly efficient, allowing us to randomly access any element in $O(1)$ time.
+Accessing elements in an array is highly efficient; we can randomly access any element in the array in $O(1)$ time.
=== "Python"
```python title="array.py"
def random_access(nums: list[int]) -> int:
- """Random access to elements"""
+ """Random access to element"""
# Randomly select a number from the interval [0, len(nums)-1]
random_index = random.randint(0, len(nums) - 1)
- # Retrieve and return a random element
+ # Retrieve and return the random element
random_num = nums[random_index]
return random_num
```
@@ -157,11 +164,11 @@ Accessing elements in an array is highly efficient, allowing us to randomly acce
=== "C++"
```cpp title="array.cpp"
- /* Random access to elements */
+ /* Random access to element */
int randomAccess(int *nums, int size) {
- // Randomly select a number in the range [0, size)
+ // Randomly select a number from interval [0, size)
int randomIndex = rand() % size;
- // Retrieve and return a random element
+ // Retrieve and return the random element
int randomNum = nums[randomIndex];
return randomNum;
}
@@ -170,11 +177,11 @@ Accessing elements in an array is highly efficient, allowing us to randomly acce
=== "Java"
```java title="array.java"
- /* Random access to elements */
+ /* Random access to element */
int randomAccess(int[] nums) {
// Randomly select a number in the interval [0, nums.length)
int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length);
- // Retrieve and return a random element
+ // Retrieve and return the random element
int randomNum = nums[randomIndex];
return randomNum;
}
@@ -183,101 +190,166 @@ Accessing elements in an array is highly efficient, allowing us to randomly acce
=== "C#"
```csharp title="array.cs"
- [class]{array}-[func]{RandomAccess}
+ /* Random access to element */
+ int RandomAccess(int[] nums) {
+ Random random = new();
+ // Randomly select a number in interval [0, nums.Length)
+ int randomIndex = random.Next(nums.Length);
+ // Retrieve and return the random element
+ int randomNum = nums[randomIndex];
+ return randomNum;
+ }
```
=== "Go"
```go title="array.go"
- [class]{}-[func]{randomAccess}
+ /* Random access to element */
+ func randomAccess(nums []int) (randomNum int) {
+ // Randomly select a number in the interval [0, nums.length)
+ randomIndex := rand.Intn(len(nums))
+ // Retrieve and return the random element
+ randomNum = nums[randomIndex]
+ return
+ }
```
=== "Swift"
```swift title="array.swift"
- [class]{}-[func]{randomAccess}
+ /* Random access to element */
+ func randomAccess(nums: [Int]) -> Int {
+ // Randomly select a number in interval [0, nums.count)
+ let randomIndex = nums.indices.randomElement()!
+ // Retrieve and return the random element
+ let randomNum = nums[randomIndex]
+ return randomNum
+ }
```
=== "JS"
```javascript title="array.js"
- [class]{}-[func]{randomAccess}
+ /* Random access to element */
+ function randomAccess(nums) {
+ // Randomly select a number in the interval [0, nums.length)
+ const random_index = Math.floor(Math.random() * nums.length);
+ // Retrieve and return the random element
+ const random_num = nums[random_index];
+ return random_num;
+ }
```
=== "TS"
```typescript title="array.ts"
- [class]{}-[func]{randomAccess}
+ /* Random access to element */
+ function randomAccess(nums: number[]): number {
+ // Randomly select a number in the interval [0, nums.length)
+ const random_index = Math.floor(Math.random() * nums.length);
+ // Retrieve and return the random element
+ const random_num = nums[random_index];
+ return random_num;
+ }
```
=== "Dart"
```dart title="array.dart"
- [class]{}-[func]{randomAccess}
+ /* Random access to element */
+ int randomAccess(List nums) {
+ // Randomly select a number in the interval [0, nums.length)
+ int randomIndex = Random().nextInt(nums.length);
+ // Retrieve and return the random element
+ int randomNum = nums[randomIndex];
+ return randomNum;
+ }
```
=== "Rust"
```rust title="array.rs"
- [class]{}-[func]{random_access}
+ /* Random access to element */
+ fn random_access(nums: &[i32]) -> i32 {
+ // Randomly select a number in interval [0, nums.len())
+ let random_index = rand::thread_rng().gen_range(0..nums.len());
+ // Retrieve and return the random element
+ let random_num = nums[random_index];
+ random_num
+ }
```
=== "C"
```c title="array.c"
- [class]{}-[func]{randomAccess}
+ /* Random access to element */
+ int randomAccess(int *nums, int size) {
+ // Randomly select a number from interval [0, size)
+ int randomIndex = rand() % size;
+ // Retrieve and return the random element
+ int randomNum = nums[randomIndex];
+ return randomNum;
+ }
```
=== "Kotlin"
```kotlin title="array.kt"
- [class]{}-[func]{randomAccess}
+ /* Random access to element */
+ fun randomAccess(nums: IntArray): Int {
+ // Randomly select a number in interval [0, nums.size)
+ val randomIndex = ThreadLocalRandom.current().nextInt(0, nums.size)
+ // Retrieve and return the random element
+ val randomNum = nums[randomIndex]
+ return randomNum
+ }
```
=== "Ruby"
```ruby title="array.rb"
- [class]{}-[func]{random_access}
+ ### Random access element ###
+ def random_access(nums)
+ # Randomly select a number in the interval [0, nums.length)
+ random_index = Random.rand(0...nums.length)
+
+ # Retrieve and return the random element
+ nums[random_index]
+ end
```
-=== "Zig"
+### 3. Inserting Elements
- ```zig title="array.zig"
- [class]{}-[func]{randomAccess}
- ```
+Array elements are stored "tightly adjacent" in memory, with no space between them to store any additional data. As shown in Figure 4-3, if we want to insert an element in the middle of an array, we need to shift all elements after that position backward by one position, and then assign the value to that index.
-### 3. Inserting elements
+{ class="animation-figure" }
-Array elements are tightly packed in memory, with no space available to accommodate additional data between them. As illustrated in Figure 4-3, inserting an element in the middle of an array requires shifting all subsequent elements back by one position to create room for the new element.
+
Figure 4-3 Example of inserting an element into an array
-{ class="animation-figure" }
-
-
Figure 4-3 Array element insertion example
-
-It's important to note that due to the fixed length of an array, inserting an element will unavoidably result in the loss of the last element in the array. Solutions to address this issue will be explored in the "List" chapter.
+It is worth noting that since the length of an array is fixed, inserting an element will inevitably cause the element at the end of the array to be "lost". We will leave the solution to this problem for discussion in the "List" chapter.
=== "Python"
```python title="array.py"
def insert(nums: list[int], num: int, index: int):
- """Insert element num at `index`"""
- # Move all elements after `index` one position backward
+ """Insert element num at index index in the array"""
+ # Move all elements at and after index index backward by one position
for i in range(len(nums) - 1, index, -1):
nums[i] = nums[i - 1]
- # Assign num to the element at index
+ # Assign num to the element at index index
nums[index] = num
```
=== "C++"
```cpp title="array.cpp"
- /* Insert element num at `index` */
+ /* Insert element num at index index in the array */
void insert(int *nums, int size, int num, int index) {
- // Move all elements after `index` one position backward
+ // Move all elements at and after index index backward by one position
for (int i = size - 1; i > index; i--) {
nums[i] = nums[i - 1];
}
- // Assign num to the element at index
+ // Assign num to the element at index index
nums[index] = num;
}
```
@@ -285,13 +357,13 @@ It's important to note that due to the fixed length of an array, inserting an el
=== "Java"
```java title="array.java"
- /* Insert element num at `index` */
+ /* Insert element num at index index in the array */
void insert(int[] nums, int num, int index) {
- // Move all elements after `index` one position backward
+ // Move all elements at and after index index backward by one position
for (int i = nums.length - 1; i > index; i--) {
nums[i] = nums[i - 1];
}
- // Assign num to the element at index
+ // Assign num to the element at index index
nums[index] = num;
}
```
@@ -299,85 +371,160 @@ It's important to note that due to the fixed length of an array, inserting an el
=== "C#"
```csharp title="array.cs"
- [class]{array}-[func]{Insert}
+ /* Insert element num at index index in the array */
+ void Insert(int[] nums, int num, int index) {
+ // Move all elements at and after index index backward by one position
+ for (int i = nums.Length - 1; i > index; i--) {
+ nums[i] = nums[i - 1];
+ }
+ // Assign num to the element at index index
+ nums[index] = num;
+ }
```
=== "Go"
```go title="array.go"
- [class]{}-[func]{insert}
+ /* Insert element num at index index in the array */
+ func insert(nums []int, num int, index int) {
+ // Move all elements at and after index index backward by one position
+ for i := len(nums) - 1; i > index; i-- {
+ nums[i] = nums[i-1]
+ }
+ // Assign num to the element at index index
+ nums[index] = num
+ }
```
=== "Swift"
```swift title="array.swift"
- [class]{}-[func]{insert}
+ /* Insert element num at index index in the array */
+ func insert(nums: inout [Int], num: Int, index: Int) {
+ // Move all elements at and after index index backward by one position
+ for i in nums.indices.dropFirst(index).reversed() {
+ nums[i] = nums[i - 1]
+ }
+ // Assign num to the element at index index
+ nums[index] = num
+ }
```
=== "JS"
```javascript title="array.js"
- [class]{}-[func]{insert}
+ /* Insert element num at index index in the array */
+ function insert(nums, num, index) {
+ // Move all elements at and after index index backward by one position
+ for (let i = nums.length - 1; i > index; i--) {
+ nums[i] = nums[i - 1];
+ }
+ // Assign num to the element at index index
+ nums[index] = num;
+ }
```
=== "TS"
```typescript title="array.ts"
- [class]{}-[func]{insert}
+ /* Insert element num at index index in the array */
+ function insert(nums: number[], num: number, index: number): void {
+ // Move all elements at and after index index backward by one position
+ for (let i = nums.length - 1; i > index; i--) {
+ nums[i] = nums[i - 1];
+ }
+ // Assign num to the element at index index
+ nums[index] = num;
+ }
```
=== "Dart"
```dart title="array.dart"
- [class]{}-[func]{insert}
+ /* Insert element _num at array index index */
+ void insert(List nums, int _num, int index) {
+ // Move all elements at and after index index backward by one position
+ for (var i = nums.length - 1; i > index; i--) {
+ nums[i] = nums[i - 1];
+ }
+ // Assign _num to element at index
+ nums[index] = _num;
+ }
```
=== "Rust"
```rust title="array.rs"
- [class]{}-[func]{insert}
+ /* Insert element num at index index in the array */
+ fn insert(nums: &mut [i32], num: i32, index: usize) {
+ // Move all elements at and after index index backward by one position
+ for i in (index + 1..nums.len()).rev() {
+ nums[i] = nums[i - 1];
+ }
+ // Assign num to the element at index index
+ nums[index] = num;
+ }
```
=== "C"
```c title="array.c"
- [class]{}-[func]{insert}
+ /* Insert element num at index index in the array */
+ void insert(int *nums, int size, int num, int index) {
+ // Move all elements at and after index index backward by one position
+ for (int i = size - 1; i > index; i--) {
+ nums[i] = nums[i - 1];
+ }
+ // Assign num to the element at index index
+ nums[index] = num;
+ }
```
=== "Kotlin"
```kotlin title="array.kt"
- [class]{}-[func]{insert}
+ /* Insert element num at index index in the array */
+ fun insert(nums: IntArray, num: Int, index: Int) {
+ // Move all elements at and after index index backward by one position
+ for (i in nums.size - 1 downTo index + 1) {
+ nums[i] = nums[i - 1]
+ }
+ // Assign num to the element at index index
+ nums[index] = num
+ }
```
=== "Ruby"
```ruby title="array.rb"
- [class]{}-[func]{insert}
+ ### Insert element num at index in array ###
+ def insert(nums, num, index)
+ # Move all elements at and after index index backward by one position
+ for i in (nums.length - 1).downto(index + 1)
+ nums[i] = nums[i - 1]
+ end
+
+ # Assign num to the element at index index
+ nums[index] = num
+ end
```
-=== "Zig"
+### 4. Removing Elements
- ```zig title="array.zig"
- [class]{}-[func]{insert}
- ```
+Similarly, as shown in Figure 4-4, to delete the element at index $i$, we need to shift all elements after index $i$ forward by one position.
-### 4. Deleting elements
+{ class="animation-figure" }
-Similarly, as depicted in Figure 4-4, to delete an element at index $i$, all elements following index $i$ must be moved forward by one position.
+
Figure 4-4 Example of removing an element from an array
-{ class="animation-figure" }
-
-
Figure 4-4 Array element deletion example
-
-Please note that after deletion, the former last element becomes "meaningless," hence requiring no specific modification.
+Note that after the deletion is complete, the original last element becomes "meaningless", so we do not need to specifically modify it.
=== "Python"
```python title="array.py"
def remove(nums: list[int], index: int):
- """Remove the element at `index`"""
- # Move all elements after `index` one position forward
+ """Remove the element at index index"""
+ # Move all elements after index index forward by one position
for i in range(index, len(nums) - 1):
nums[i] = nums[i + 1]
```
@@ -385,9 +532,9 @@ Please note that after deletion, the former last element becomes "meaningless,"
=== "C++"
```cpp title="array.cpp"
- /* Remove the element at `index` */
+ /* Remove the element at index index */
void remove(int *nums, int size, int index) {
- // Move all elements after `index` one position forward
+ // Move all elements after index index forward by one position
for (int i = index; i < size - 1; i++) {
nums[i] = nums[i + 1];
}
@@ -397,9 +544,9 @@ Please note that after deletion, the former last element becomes "meaningless,"
=== "Java"
```java title="array.java"
- /* Remove the element at `index` */
+ /* Remove the element at index index */
void remove(int[] nums, int index) {
- // Move all elements after `index` one position forward
+ // Move all elements after index index forward by one position
for (int i = index; i < nums.length - 1; i++) {
nums[i] = nums[i + 1];
}
@@ -409,78 +556,133 @@ Please note that after deletion, the former last element becomes "meaningless,"
=== "C#"
```csharp title="array.cs"
- [class]{array}-[func]{Remove}
+ /* Remove the element at index index */
+ void Remove(int[] nums, int index) {
+ // Move all elements after index index forward by one position
+ for (int i = index; i < nums.Length - 1; i++) {
+ nums[i] = nums[i + 1];
+ }
+ }
```
=== "Go"
```go title="array.go"
- [class]{}-[func]{remove}
+ /* Remove the element at index index */
+ func remove(nums []int, index int) {
+ // Move all elements after index index forward by one position
+ for i := index; i < len(nums)-1; i++ {
+ nums[i] = nums[i+1]
+ }
+ }
```
=== "Swift"
```swift title="array.swift"
- [class]{}-[func]{remove}
+ /* Remove the element at index index */
+ func remove(nums: inout [Int], index: Int) {
+ // Move all elements after index index forward by one position
+ for i in nums.indices.dropFirst(index).dropLast() {
+ nums[i] = nums[i + 1]
+ }
+ }
```
=== "JS"
```javascript title="array.js"
- [class]{}-[func]{remove}
+ /* Remove the element at index index */
+ function remove(nums, index) {
+ // Move all elements after index index forward by one position
+ for (let i = index; i < nums.length - 1; i++) {
+ nums[i] = nums[i + 1];
+ }
+ }
```
=== "TS"
```typescript title="array.ts"
- [class]{}-[func]{remove}
+ /* Remove the element at index index */
+ function remove(nums: number[], index: number): void {
+ // Move all elements after index index forward by one position
+ for (let i = index; i < nums.length - 1; i++) {
+ nums[i] = nums[i + 1];
+ }
+ }
```
=== "Dart"
```dart title="array.dart"
- [class]{}-[func]{remove}
+ /* Remove the element at index index */
+ void remove(List nums, int index) {
+ // Move all elements after index index forward by one position
+ for (var i = index; i < nums.length - 1; i++) {
+ nums[i] = nums[i + 1];
+ }
+ }
```
=== "Rust"
```rust title="array.rs"
- [class]{}-[func]{remove}
+ /* Remove the element at index index */
+ fn remove(nums: &mut [i32], index: usize) {
+ // Move all elements after index index forward by one position
+ for i in index..nums.len() - 1 {
+ nums[i] = nums[i + 1];
+ }
+ }
```
=== "C"
```c title="array.c"
- [class]{}-[func]{removeItem}
+ /* Remove the element at index index */
+ // Note: stdio.h occupies the remove keyword
+ void removeItem(int *nums, int size, int index) {
+ // Move all elements after index index forward by one position
+ for (int i = index; i < size - 1; i++) {
+ nums[i] = nums[i + 1];
+ }
+ }
```
=== "Kotlin"
```kotlin title="array.kt"
- [class]{}-[func]{remove}
+ /* Remove the element at index index */
+ fun remove(nums: IntArray, index: Int) {
+ // Move all elements after index index forward by one position
+ for (i in index.. nums) {
+ int count = 0;
+ // Traverse array by index
+ for (var i = 0; i < nums.length; i++) {
+ count += nums[i];
+ }
+ // Direct traversal of array elements
+ for (int _num in nums) {
+ count += _num;
+ }
+ // Traverse array using forEach method
+ nums.forEach((_num) {
+ count += _num;
+ });
+ }
```
=== "Rust"
```rust title="array.rs"
- [class]{}-[func]{traverse}
+ /* Traverse array */
+ fn traverse(nums: &[i32]) {
+ let mut _count = 0;
+ // Traverse array by index
+ for i in 0..nums.len() {
+ _count += nums[i];
+ }
+ // Direct traversal of array elements
+ _count = 0;
+ for &num in nums {
+ _count += num;
+ }
+ }
```
=== "C"
```c title="array.c"
- [class]{}-[func]{traverse}
+ /* Traverse array */
+ void traverse(int *nums, int size) {
+ int count = 0;
+ // Traverse array by index
+ for (int i = 0; i < size; i++) {
+ count += nums[i];
+ }
+ }
```
=== "Kotlin"
```kotlin title="array.kt"
- [class]{}-[func]{traverse}
+ /* Traverse array */
+ fun traverse(nums: IntArray) {
+ var count = 0
+ // Traverse array by index
+ for (i in nums.indices) {
+ count += nums[i]
+ }
+ // Direct traversal of array elements
+ for (j in nums) {
+ count += j
+ }
+ }
```
=== "Ruby"
```ruby title="array.rb"
- [class]{}-[func]{traverse}
+ ### Traverse array ###
+ def traverse(nums)
+ count = 0
+
+ # Traverse array by index
+ for i in 0...nums.length
+ count += nums[i]
+ end
+
+ # Direct traversal of array elements
+ for num in nums
+ count += num
+ end
+ end
```
-=== "Zig"
+### 6. Finding Elements
- ```zig title="array.zig"
- [class]{}-[func]{traverse}
- ```
+Finding a specified element in an array requires traversing the array and checking whether the element value matches in each iteration; if it matches, output the corresponding index.
-### 6. Finding elements
-
-Locating a specific element within an array involves iterating through the array, checking each element to determine if it matches the desired value.
-
-Because arrays are linear data structures, this operation is commonly referred to as "linear search."
+Since an array is a linear data structure, the above search operation is called a "linear search".
=== "Python"
```python title="array.py"
def find(nums: list[int], target: int) -> int:
- """Search for a specified element in the array"""
+ """Find the specified element in the array"""
for i in range(len(nums)):
if nums[i] == target:
return i
@@ -616,7 +936,7 @@ Because arrays are linear data structures, this operation is commonly referred t
=== "C++"
```cpp title="array.cpp"
- /* Search for a specified element in the array */
+ /* Find the specified element in the array */
int find(int *nums, int size, int target) {
for (int i = 0; i < size; i++) {
if (nums[i] == target)
@@ -629,7 +949,7 @@ Because arrays are linear data structures, this operation is commonly referred t
=== "Java"
```java title="array.java"
- /* Search for a specified element in the array */
+ /* Find the specified element in the array */
int find(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target)
@@ -642,86 +962,154 @@ Because arrays are linear data structures, this operation is commonly referred t
=== "C#"
```csharp title="array.cs"
- [class]{array}-[func]{Find}
+ /* Find the specified element in the array */
+ int Find(int[] nums, int target) {
+ for (int i = 0; i < nums.Length; i++) {
+ if (nums[i] == target)
+ return i;
+ }
+ return -1;
+ }
```
=== "Go"
```go title="array.go"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ func find(nums []int, target int) (index int) {
+ index = -1
+ for i := 0; i < len(nums); i++ {
+ if nums[i] == target {
+ index = i
+ break
+ }
+ }
+ return
+ }
```
=== "Swift"
```swift title="array.swift"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ func find(nums: [Int], target: Int) -> Int {
+ for i in nums.indices {
+ if nums[i] == target {
+ return i
+ }
+ }
+ return -1
+ }
```
=== "JS"
```javascript title="array.js"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ function find(nums, target) {
+ for (let i = 0; i < nums.length; i++) {
+ if (nums[i] === target) return i;
+ }
+ return -1;
+ }
```
=== "TS"
```typescript title="array.ts"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ function find(nums: number[], target: number): number {
+ for (let i = 0; i < nums.length; i++) {
+ if (nums[i] === target) {
+ return i;
+ }
+ }
+ return -1;
+ }
```
=== "Dart"
```dart title="array.dart"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ int find(List nums, int target) {
+ for (var i = 0; i < nums.length; i++) {
+ if (nums[i] == target) return i;
+ }
+ return -1;
+ }
```
=== "Rust"
```rust title="array.rs"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ fn find(nums: &[i32], target: i32) -> Option {
+ for i in 0..nums.len() {
+ if nums[i] == target {
+ return Some(i);
+ }
+ }
+ None
+ }
```
=== "C"
```c title="array.c"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ int find(int *nums, int size, int target) {
+ for (int i = 0; i < size; i++) {
+ if (nums[i] == target)
+ return i;
+ }
+ return -1;
+ }
```
=== "Kotlin"
```kotlin title="array.kt"
- [class]{}-[func]{find}
+ /* Find the specified element in the array */
+ fun find(nums: IntArray, target: Int): Int {
+ for (i in nums.indices) {
+ if (nums[i] == target)
+ return i
+ }
+ return -1
+ }
```
=== "Ruby"
```ruby title="array.rb"
- [class]{}-[func]{find}
+ ### Find specified element in array ###
+ def find(nums, target)
+ for i in 0...nums.length
+ return i if nums[i] == target
+ end
+
+ -1
+ end
```
-=== "Zig"
+### 7. Expanding Arrays
- ```zig title="array.zig"
- [class]{}-[func]{find}
- ```
+In complex system environments, programs cannot guarantee that the memory space after an array is available, making it unsafe to expand the array's capacity. Therefore, in most programming languages, **the length of an array is immutable**.
-### 7. Expanding arrays
-
-In complex system environments, ensuring the availability of memory space after an array for safe capacity extension becomes challenging. Consequently, in most programming languages, **the length of an array is immutable**.
-
-To expand an array, it's necessary to create a larger array and then copy the elements from the original array. This operation has a time complexity of $O(n)$ and can be time-consuming for large arrays. The code are as follows:
+If we want to expand an array, we need to create a new, larger array and then copy the original array elements to the new array one by one. This is an $O(n)$ operation, which is very time-consuming when the array is large. The code is shown below:
=== "Python"
```python title="array.py"
def extend(nums: list[int], enlarge: int) -> list[int]:
"""Extend array length"""
- # Initialize an extended length array
+ # Initialize an array with extended length
res = [0] * (len(nums) + enlarge)
# Copy all elements from the original array to the new array
for i in range(len(nums)):
res[i] = nums[i]
- # Return the new array after expansion
+ # Return the extended new array
return res
```
@@ -730,7 +1118,7 @@ To expand an array, it's necessary to create a larger array and then copy the e
```cpp title="array.cpp"
/* Extend array length */
int *extend(int *nums, int size, int enlarge) {
- // Initialize an extended length array
+ // Initialize an array with extended length
int *res = new int[size + enlarge];
// Copy all elements from the original array to the new array
for (int i = 0; i < size; i++) {
@@ -738,7 +1126,7 @@ To expand an array, it's necessary to create a larger array and then copy the e
}
// Free memory
delete[] nums;
- // Return the new array after expansion
+ // Return the extended new array
return res;
}
```
@@ -748,13 +1136,13 @@ To expand an array, it's necessary to create a larger array and then copy the e
```java title="array.java"
/* Extend array length */
int[] extend(int[] nums, int enlarge) {
- // Initialize an extended length array
+ // Initialize an array with extended length
int[] res = new int[nums.length + enlarge];
// Copy all elements from the original array to the new array
for (int i = 0; i < nums.length; i++) {
res[i] = nums[i];
}
- // Return the new array after expansion
+ // Return the extended new array
return res;
}
```
@@ -762,89 +1150,194 @@ To expand an array, it's necessary to create a larger array and then copy the e
=== "C#"
```csharp title="array.cs"
- [class]{array}-[func]{Extend}
+ /* Extend array length */
+ int[] Extend(int[] nums, int enlarge) {
+ // Initialize an array with extended length
+ int[] res = new int[nums.Length + enlarge];
+ // Copy all elements from the original array to the new array
+ for (int i = 0; i < nums.Length; i++) {
+ res[i] = nums[i];
+ }
+ // Return the extended new array
+ return res;
+ }
```
=== "Go"
```go title="array.go"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ func extend(nums []int, enlarge int) []int {
+ // Initialize an array with extended length
+ res := make([]int, len(nums)+enlarge)
+ // Copy all elements from the original array to the new array
+ for i, num := range nums {
+ res[i] = num
+ }
+ // Return the extended new array
+ return res
+ }
```
=== "Swift"
```swift title="array.swift"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ func extend(nums: [Int], enlarge: Int) -> [Int] {
+ // Initialize an array with extended length
+ var res = Array(repeating: 0, count: nums.count + enlarge)
+ // Copy all elements from the original array to the new array
+ for i in nums.indices {
+ res[i] = nums[i]
+ }
+ // Return the extended new array
+ return res
+ }
```
=== "JS"
```javascript title="array.js"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ // Note: JavaScript's Array is dynamic array, can be directly expanded
+ // For learning purposes, this function treats Array as fixed-length array
+ function extend(nums, enlarge) {
+ // Initialize an array with extended length
+ const res = new Array(nums.length + enlarge).fill(0);
+ // Copy all elements from the original array to the new array
+ for (let i = 0; i < nums.length; i++) {
+ res[i] = nums[i];
+ }
+ // Return the extended new array
+ return res;
+ }
```
=== "TS"
```typescript title="array.ts"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ // Note: TypeScript's Array is dynamic array, can be directly expanded
+ // For learning purposes, this function treats Array as fixed-length array
+ function extend(nums: number[], enlarge: number): number[] {
+ // Initialize an array with extended length
+ const res = new Array(nums.length + enlarge).fill(0);
+ // Copy all elements from the original array to the new array
+ for (let i = 0; i < nums.length; i++) {
+ res[i] = nums[i];
+ }
+ // Return the extended new array
+ return res;
+ }
```
=== "Dart"
```dart title="array.dart"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ List extend(List nums, int enlarge) {
+ // Initialize an array with extended length
+ List res = List.filled(nums.length + enlarge, 0);
+ // Copy all elements from the original array to the new array
+ for (var i = 0; i < nums.length; i++) {
+ res[i] = nums[i];
+ }
+ // Return the extended new array
+ return res;
+ }
```
=== "Rust"
```rust title="array.rs"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ fn extend(nums: &[i32], enlarge: usize) -> Vec {
+ // Initialize an array with extended length
+ let mut res: Vec = vec![0; nums.len() + enlarge];
+ // Copy all elements from original array to new
+ res[0..nums.len()].copy_from_slice(nums);
+
+ // Return the extended new array
+ res
+ }
```
=== "C"
```c title="array.c"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ int *extend(int *nums, int size, int enlarge) {
+ // Initialize an array with extended length
+ int *res = (int *)malloc(sizeof(int) * (size + enlarge));
+ // Copy all elements from the original array to the new array
+ for (int i = 0; i < size; i++) {
+ res[i] = nums[i];
+ }
+ // Initialize expanded space
+ for (int i = size; i < size + enlarge; i++) {
+ res[i] = 0;
+ }
+ // Return the extended new array
+ return res;
+ }
```
=== "Kotlin"
```kotlin title="array.kt"
- [class]{}-[func]{extend}
+ /* Extend array length */
+ fun extend(nums: IntArray, enlarge: Int): IntArray {
+ // Initialize an array with extended length
+ val res = IntArray(nums.size + enlarge)
+ // Copy all elements from the original array to the new array
+ for (i in nums.indices) {
+ res[i] = nums[i]
+ }
+ // Return the extended new array
+ return res
+ }
```
=== "Ruby"
```ruby title="array.rb"
- [class]{}-[func]{extend}
+ ### Extend array length ###
+ # Note: Ruby's Array is dynamic array, can be directly expanded
+ # For learning purposes, this function treats Array as fixed-length array
+ def extend(nums, enlarge)
+ # Initialize an array with extended length
+ res = Array.new(nums.length + enlarge, 0)
+
+ # Copy all elements from the original array to the new array
+ for i in 0...nums.length
+ res[i] = nums[i]
+ end
+
+ # Return the extended new array
+ res
+ end
```
-=== "Zig"
+## 4.1.2 Advantages and Limitations of Arrays
- ```zig title="array.zig"
- [class]{}-[func]{extend}
- ```
+Arrays are stored in contiguous memory space with elements of the same type. This approach contains rich prior information that the system can use to optimize the efficiency of data structure operations.
-## 4.1.2 Advantages and limitations of arrays
+- **High space efficiency**: Arrays allocate contiguous memory blocks for data without additional structural overhead.
+- **Support for random access**: Arrays allow accessing any element in $O(1)$ time.
+- **Cache locality**: When accessing array elements, the computer not only loads the element but also caches the surrounding data, thereby leveraging the cache to improve the execution speed of subsequent operations.
-Arrays are stored in contiguous memory spaces and consist of elements of the same type. This approach provides substantial prior information that systems can leverage to optimize the efficiency of data structure operations.
+Contiguous space storage is a double-edged sword with the following limitations:
-- **High space efficiency**: Arrays allocate a contiguous block of memory for data, eliminating the need for additional structural overhead.
-- **Support for random access**: Arrays allow $O(1)$ time access to any element.
-- **Cache locality**: When accessing array elements, the computer not only loads them but also caches the surrounding data, utilizing high-speed cache to enchance subsequent operation speeds.
+- **Low insertion and deletion efficiency**: When an array has many elements, insertion and deletion operations require shifting a large number of elements.
+- **Immutable length**: After an array is initialized, its length is fixed. Expanding the array requires copying all data to a new array, which is very costly.
+- **Space waste**: If the allocated size of an array exceeds what is actually needed, the extra space is wasted.
-However, continuous space storage is a double-edged sword, with the following limitations:
+## 4.1.3 Typical Applications of Arrays
-- **Low efficiency in insertion and deletion**: As arrays accumulate many elements, inserting or deleting elements requires shifting a large number of elements.
-- **Fixed length**: The length of an array is fixed after initialization. Expanding an array requires copying all data to a new array, incurring significant costs.
-- **Space wastage**: If the allocated array size exceeds the what is necessary, the extra space is wasted.
+Arrays are a fundamental and common data structure, frequently used in various algorithms and for implementing various complex data structures.
-## 4.1.3 Typical applications of arrays
-
-Arrays are fundamental and widely used data structures. They find frequent application in various algorithms and serve in the implementation of complex data structures.
-
-- **Random access**: Arrays are ideal for storing data when random sampling is required. By generating a random sequence based on indices, we can achieve random sampling efficiently.
-- **Sorting and searching**: Arrays are the most commonly used data structure for sorting and searching algorithms. Techniques like quick sort, merge sort, binary search, etc., are primarily operate on arrays.
-- **Lookup tables**: Arrays serve as efficient lookup tables for quick element or relationship retrieval. For instance, mapping characters to ASCII codes becomes seamless by using the ASCII code values as indices and storing corresponding elements in the array.
-- **Machine learning**: Within the domain of neural networks, arrays play a pivotal role in executing crucial linear algebra operations involving vectors, matrices, and tensors. Arrays serve as the primary and most extensively used data structure in neural network programming.
-- **Data structure implementation**: Arrays serve as the building blocks for implementing various data structures like stacks, queues, hash tables, heaps, graphs, etc. For instance, the adjacency matrix representation of a graph is essentially a two-dimensional array.
+- **Random access**: If we want to randomly sample some items, we can use an array to store them and generate a random sequence to implement random sampling based on indices.
+- **Sorting and searching**: Arrays are the most commonly used data structure for sorting and searching algorithms. Quick sort, merge sort, binary search, and others are primarily performed on arrays.
+- **Lookup tables**: When we need to quickly find an element or its corresponding relationship, we can use an array as a lookup table. For example, if we want to implement a mapping from characters to ASCII codes, we can use the ASCII code value of a character as an index, with the corresponding element stored at that position in the array.
+- **Machine learning**: Neural networks make extensive use of linear algebra operations between vectors, matrices, and tensors, all of which are constructed in the form of arrays. Arrays are the most commonly used data structure in neural network programming.
+- **Data structure implementation**: Arrays can be used to implement stacks, queues, hash tables, heaps, graphs, and other data structures. For example, the adjacency matrix representation of a graph is essentially a two-dimensional array.
diff --git a/en/docs/chapter_array_and_linkedlist/index.md b/en/docs/chapter_array_and_linkedlist/index.md
index fd2e8796b..906b5521e 100644
--- a/en/docs/chapter_array_and_linkedlist/index.md
+++ b/en/docs/chapter_array_and_linkedlist/index.md
@@ -3,20 +3,20 @@ comments: true
icon: material/view-list-outline
---
-# Chapter 4. Arrays and linked lists
+# Chapter 4. Array and Linked List
-{ class="cover-image" }
+{ class="cover-image" }
!!! abstract
- The world of data structures resembles a sturdy brick wall.
+ The world of data structures is like a solid brick wall.
- In arrays, envision bricks snugly aligned, each resting seamlessly beside the next, creating a unified formation. Meanwhile, in linked lists, these bricks disperse freely, embraced by vines gracefully knitting connections between them.
+ Array bricks are neatly arranged, tightly packed one by one. Linked list bricks are scattered everywhere, with connecting vines freely weaving through the gaps between bricks.
## Chapter contents
- [4.1 Array](array.md)
-- [4.2 Linked list](linked_list.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_array_and_linkedlist/linked_list.md b/en/docs/chapter_array_and_linkedlist/linked_list.md
index 22e0ac6ec..91283010e 100755
--- a/en/docs/chapter_array_and_linkedlist/linked_list.md
+++ b/en/docs/chapter_array_and_linkedlist/linked_list.md
@@ -2,25 +2,25 @@
comments: true
---
-# 4.2 Linked list
+# 4.2 Linked List
-Memory space is a shared resource among all programs. In a complex system environment, available memory can be dispersed throughout the memory space. We understand that the memory allocated for an array must be continuous. However, for very large arrays, finding a sufficiently large contiguous memory space might be challenging. This is where the flexible advantage of linked lists becomes evident.
+Memory space is a shared resource for all programs. In a complex system runtime environment, available memory space may be scattered throughout the memory. We know that the memory space for storing an array must be contiguous, and when the array is very large, the memory may not be able to provide such a large contiguous space. This is where the flexibility advantage of linked lists becomes apparent.
-A linked list is a linear data structure in which each element is a node object, and the nodes are interconnected through "references". These references hold the memory addresses of subsequent nodes, enabling navigation from one node to the next.
+A linked list is a linear data structure in which each element is a node object, and the nodes are connected through "references". A reference records the memory address of the next node, through which the next node can be accessed from the current node.
-The design of linked lists allows for their nodes to be distributed across memory locations without requiring contiguous memory addresses.
+The design of linked lists allows nodes to be stored scattered throughout the memory, and their memory addresses do not need to be contiguous.
{ class="animation-figure" }
Figure 4-5 Linked list definition and storage method
-As shown in Figure 4-5, we see that the basic building block of a linked list is the node object. Each node comprises two key components: the node's "value" and a "reference" to the next node.
+Observing Figure 4-5, the basic unit of a linked list is a node object. Each node contains two pieces of data: the node's "value" and a "reference" to the next node.
-- The first node in a linked list is the "head node", and the final one is the "tail node".
-- The tail node points to "null", designated as `null` in Java, `nullptr` in C++, and `None` in Python.
-- In languages that support pointers, like C, C++, Go, and Rust, this "reference" is typically implemented as a "pointer".
+- The first node of a linked list is called the "head node", and the last node is called the "tail node".
+- The tail node points to "null", which is denoted as `null`, `nullptr`, and `None` in Java, C++, and Python, respectively.
+- In languages that support pointers, such as C, C++, Go, and Rust, the aforementioned "reference" should be replaced with "pointer".
-As the code below illustrates, a `ListNode` in a linked list, besides holding a value, must also maintain an additional reference (or pointer). Therefore, **a linked list occupies more memory space than an array when storing the same quantity of data.**.
+As shown in the following code, a linked list node `ListNode` contains not only a value but also an additional reference (pointer). Therefore, **linked lists occupy more memory space than arrays when storing the same amount of data**.
=== "Python"
@@ -168,39 +168,39 @@ As the code below illustrates, a `ListNode` in a linked list, besides holding a
=== "Kotlin"
```kotlin title=""
-
- ```
-
-=== "Zig"
-
- ```zig title=""
- // Linked list node class
- pub fn ListNode(comptime T: type) type {
- return struct {
- const Self = @This();
-
- val: T = 0, // Node value
- next: ?*Self = null, // Pointer to the next node
-
- // Constructor
- pub fn init(self: *Self, x: i32) void {
- self.val = x;
- self.next = null;
- }
- };
+ /* Linked list node class */
+ // Constructor
+ class ListNode(x: Int) {
+ val _val: Int = x // Node value
+ val next: ListNode? = null // Reference to the next node
}
```
-## 4.2.1 Common operations on linked lists
+=== "Ruby"
-### 1. Initializing a linked list
+ ```ruby title=""
+ # Linked list node class
+ class ListNode
+ attr_accessor :val # Node value
+ attr_accessor :next # Reference to the next node
-Constructing a linked list is a two-step process: first, initializing each node object, and second, forming the reference links between the nodes. After initialization, we can traverse all nodes sequentially from the head node by following the `next` reference.
+ def initialize(val=0, next_node=nil)
+ @val = val
+ @next = next_node
+ end
+ end
+ ```
+
+## 4.2.1 Common Linked List Operations
+
+### 1. Initializing a Linked List
+
+Building a linked list involves two steps: first, initializing each node object; second, constructing the reference relationships between nodes. Once initialization is complete, we can traverse all nodes starting from the head node of the linked list through the reference `next`.
=== "Python"
```python title="linked_list.py"
- # Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4
+ # Initialize linked list 1 -> 3 -> 2 -> 5 -> 4
# Initialize each node
n0 = ListNode(1)
n1 = ListNode(3)
@@ -217,7 +217,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "C++"
```cpp title="linked_list.cpp"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
ListNode* n0 = new ListNode(1);
ListNode* n1 = new ListNode(3);
@@ -234,7 +234,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "Java"
```java title="linked_list.java"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
ListNode n0 = new ListNode(1);
ListNode n1 = new ListNode(3);
@@ -251,7 +251,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "C#"
```csharp title="linked_list.cs"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
ListNode n0 = new(1);
ListNode n1 = new(3);
@@ -268,7 +268,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "Go"
```go title="linked_list.go"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
n0 := NewListNode(1)
n1 := NewListNode(3)
@@ -285,7 +285,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "Swift"
```swift title="linked_list.swift"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
let n0 = ListNode(x: 1)
let n1 = ListNode(x: 3)
@@ -302,7 +302,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "JS"
```javascript title="linked_list.js"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
const n0 = new ListNode(1);
const n1 = new ListNode(3);
@@ -319,7 +319,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "TS"
```typescript title="linked_list.ts"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
const n0 = new ListNode(1);
const n1 = new ListNode(3);
@@ -336,7 +336,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "Dart"
```dart title="linked_list.dart"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */\
// Initialize each node
ListNode n0 = ListNode(1);
ListNode n1 = ListNode(3);
@@ -353,7 +353,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "Rust"
```rust title="linked_list.rs"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
let n0 = Rc::new(RefCell::new(ListNode { val: 1, next: None }));
let n1 = Rc::new(RefCell::new(ListNode { val: 3, next: None }));
@@ -371,7 +371,7 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "C"
```c title="linked_list.c"
- /* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
ListNode* n0 = newListNode(1);
ListNode* n1 = newListNode(3);
@@ -388,37 +388,53 @@ Constructing a linked list is a two-step process: first, initializing each node
=== "Kotlin"
```kotlin title="linked_list.kt"
-
- ```
-
-=== "Zig"
-
- ```zig title="linked_list.zig"
- // Initialize linked list
+ /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */
// Initialize each node
- var n0 = inc.ListNode(i32){.val = 1};
- var n1 = inc.ListNode(i32){.val = 3};
- var n2 = inc.ListNode(i32){.val = 2};
- var n3 = inc.ListNode(i32){.val = 5};
- var n4 = inc.ListNode(i32){.val = 4};
+ val n0 = ListNode(1)
+ val n1 = ListNode(3)
+ val n2 = ListNode(2)
+ val n3 = ListNode(5)
+ val n4 = ListNode(4)
// Build references between nodes
- n0.next = &n1;
- n1.next = &n2;
- n2.next = &n3;
- n3.next = &n4;
+ n0.next = n1;
+ n1.next = n2;
+ n2.next = n3;
+ n3.next = n4;
```
-The array as a whole is a variable, for instance, the array `nums` includes elements like `nums[0]`, `nums[1]`, and so on, whereas a linked list is made up of several distinct node objects. **We typically refer to a linked list by its head node**, for example, the linked list in the previous code snippet is referred to as `n0`.
+=== "Ruby"
-### 2. Inserting nodes
+ ```ruby title="linked_list.rb"
+ # Initialize linked list 1 -> 3 -> 2 -> 5 -> 4
+ # Initialize each node
+ n0 = ListNode.new(1)
+ n1 = ListNode.new(3)
+ n2 = ListNode.new(2)
+ n3 = ListNode.new(5)
+ n4 = ListNode.new(4)
+ # Build references between nodes
+ n0.next = n1
+ n1.next = n2
+ n2.next = n3
+ n3.next = n4
+ ```
-Inserting a node into a linked list is very easy. As shown in Figure 4-6, let's assume we aim to insert a new node `P` between two adjacent nodes `n0` and `n1`. **This can be achieved by simply modifying two node references (pointers)**, with a time complexity of $O(1)$.
+??? pythontutor "Code Visualization"
-By comparison, inserting an element into an array has a time complexity of $O(n)$, which becomes less efficient when dealing with large data volumes.
+
+
-{ class="animation-figure" }
+An array is a single variable; for example, an array `nums` contains elements `nums[0]`, `nums[1]`, etc. A linked list, however, is composed of multiple independent node objects. **We typically use the head node as the reference to the linked list**; for example, the linked list in the above code can be referred to as linked list `n0`.
-
Figure 4-6 Linked list node insertion example
+### 2. Inserting a Node
+
+Inserting a node in a linked list is very easy. As shown in Figure 4-6, suppose we want to insert a new node `P` between two adjacent nodes `n0` and `n1`. **We only need to change two node references (pointers)**, with a time complexity of $O(1)$.
+
+In contrast, the time complexity of inserting an element in an array is $O(n)$, which is inefficient when dealing with large amounts of data.
+
+{ class="animation-figure" }
+
+
Figure 4-6 Example of inserting a node into a linked list
=== "Python"
@@ -455,78 +471,124 @@ By comparison, inserting an element into an array has a time complexity of $O(n)
=== "C#"
```csharp title="linked_list.cs"
- [class]{linked_list}-[func]{Insert}
+ /* Insert node P after node n0 in the linked list */
+ void Insert(ListNode n0, ListNode P) {
+ ListNode? n1 = n0.next;
+ P.next = n1;
+ n0.next = P;
+ }
```
=== "Go"
```go title="linked_list.go"
- [class]{}-[func]{insertNode}
+ /* Insert node P after node n0 in the linked list */
+ func insertNode(n0 *ListNode, P *ListNode) {
+ n1 := n0.Next
+ P.Next = n1
+ n0.Next = P
+ }
```
=== "Swift"
```swift title="linked_list.swift"
- [class]{}-[func]{insert}
+ /* Insert node P after node n0 in the linked list */
+ func insert(n0: ListNode, P: ListNode) {
+ let n1 = n0.next
+ P.next = n1
+ n0.next = P
+ }
```
=== "JS"
```javascript title="linked_list.js"
- [class]{}-[func]{insert}
+ /* Insert node P after node n0 in the linked list */
+ function insert(n0, P) {
+ const n1 = n0.next;
+ P.next = n1;
+ n0.next = P;
+ }
```
=== "TS"
```typescript title="linked_list.ts"
- [class]{}-[func]{insert}
+ /* Insert node P after node n0 in the linked list */
+ function insert(n0: ListNode, P: ListNode): void {
+ const n1 = n0.next;
+ P.next = n1;
+ n0.next = P;
+ }
```
=== "Dart"
```dart title="linked_list.dart"
- [class]{}-[func]{insert}
+ /* Insert node P after node n0 in the linked list */
+ void insert(ListNode n0, ListNode P) {
+ ListNode? n1 = n0.next;
+ P.next = n1;
+ n0.next = P;
+ }
```
=== "Rust"
```rust title="linked_list.rs"
- [class]{}-[func]{insert}
+ /* Insert node P after node n0 in the linked list */
+ #[allow(non_snake_case)]
+ pub fn insert(n0: &Rc>>, P: Rc>>) {
+ let n1 = n0.borrow_mut().next.take();
+ P.borrow_mut().next = n1;
+ n0.borrow_mut().next = Some(P);
+ }
```
=== "C"
```c title="linked_list.c"
- [class]{}-[func]{insert}
+ /* Insert node P after node n0 in the linked list */
+ void insert(ListNode *n0, ListNode *P) {
+ ListNode *n1 = n0->next;
+ P->next = n1;
+ n0->next = P;
+ }
```
=== "Kotlin"
```kotlin title="linked_list.kt"
- [class]{}-[func]{insert}
+ /* Insert node P after node n0 in the linked list */
+ fun insert(n0: ListNode?, p: ListNode?) {
+ val n1 = n0?.next
+ p?.next = n1
+ n0?.next = p
+ }
```
=== "Ruby"
```ruby title="linked_list.rb"
- [class]{}-[func]{insert}
+ ### Insert node _p after node n0 in linked list ###
+ # Ruby's `p` is a built-in function, `P` is a constant, so use `_p` instead
+ def insert(n0, _p)
+ n1 = n0.next
+ _p.next = n1
+ n0.next = _p
+ end
```
-=== "Zig"
+### 3. Removing a Node
- ```zig title="linked_list.zig"
- [class]{}-[func]{insert}
- ```
+As shown in Figure 4-7, removing a node in a linked list is also very convenient. **We only need to change one node's reference (pointer)**.
-### 3. Deleting nodes
+Note that although node `P` still points to `n1` after the deletion operation is complete, the linked list can no longer access `P` when traversing, which means `P` no longer belongs to this linked list.
-As shown in Figure 4-7, deleting a node from a linked list is also very easy, **involving only the modification of a single node's reference (pointer)**.
+{ class="animation-figure" }
-It's important to note that even though node `P` continues to point to `n1` after being deleted, it becomes inaccessible during linked list traversal. This effectively means that `P` is no longer a part of the linked list.
-
-{ class="animation-figure" }
-
-
Figure 4-7 Linked list node deletion
+
Figure 4-7 Removing a node from a linked list
=== "Python"
@@ -574,78 +636,157 @@ It's important to note that even though node `P` continues to point to `n1` afte
=== "C#"
```csharp title="linked_list.cs"
- [class]{linked_list}-[func]{Remove}
+ /* Remove the first node after node n0 in the linked list */
+ void Remove(ListNode n0) {
+ if (n0.next == null)
+ return;
+ // n0 -> P -> n1
+ ListNode P = n0.next;
+ ListNode? n1 = P.next;
+ n0.next = n1;
+ }
```
=== "Go"
```go title="linked_list.go"
- [class]{}-[func]{removeItem}
+ /* Remove the first node after node n0 in the linked list */
+ func removeItem(n0 *ListNode) {
+ if n0.Next == nil {
+ return
+ }
+ // n0 -> P -> n1
+ P := n0.Next
+ n1 := P.Next
+ n0.Next = n1
+ }
```
=== "Swift"
```swift title="linked_list.swift"
- [class]{}-[func]{remove}
+ /* Remove the first node after node n0 in the linked list */
+ func remove(n0: ListNode) {
+ if n0.next == nil {
+ return
+ }
+ // n0 -> P -> n1
+ let P = n0.next
+ let n1 = P?.next
+ n0.next = n1
+ }
```
=== "JS"
```javascript title="linked_list.js"
- [class]{}-[func]{remove}
+ /* Remove the first node after node n0 in the linked list */
+ function remove(n0) {
+ if (!n0.next) return;
+ // n0 -> P -> n1
+ const P = n0.next;
+ const n1 = P.next;
+ n0.next = n1;
+ }
```
=== "TS"
```typescript title="linked_list.ts"
- [class]{}-[func]{remove}
+ /* Remove the first node after node n0 in the linked list */
+ function remove(n0: ListNode): void {
+ if (!n0.next) {
+ return;
+ }
+ // n0 -> P -> n1
+ const P = n0.next;
+ const n1 = P.next;
+ n0.next = n1;
+ }
```
=== "Dart"
```dart title="linked_list.dart"
- [class]{}-[func]{remove}
+ /* Remove the first node after node n0 in the linked list */
+ void remove(ListNode n0) {
+ if (n0.next == null) return;
+ // n0 -> P -> n1
+ ListNode P = n0.next!;
+ ListNode? n1 = P.next;
+ n0.next = n1;
+ }
```
=== "Rust"
```rust title="linked_list.rs"
- [class]{}-[func]{remove}
+ /* Remove the first node after node n0 in the linked list */
+ #[allow(non_snake_case)]
+ pub fn remove(n0: &Rc>>) {
+ // n0 -> P -> n1
+ let P = n0.borrow_mut().next.take();
+ if let Some(node) = P {
+ let n1 = node.borrow_mut().next.take();
+ n0.borrow_mut().next = n1;
+ }
+ }
```
=== "C"
```c title="linked_list.c"
- [class]{}-[func]{removeItem}
+ /* Remove the first node after node n0 in the linked list */
+ // Note: stdio.h occupies the remove keyword
+ void removeItem(ListNode *n0) {
+ if (!n0->next)
+ return;
+ // n0 -> P -> n1
+ ListNode *P = n0->next;
+ ListNode *n1 = P->next;
+ n0->next = n1;
+ // Free memory
+ free(P);
+ }
```
=== "Kotlin"
```kotlin title="linked_list.kt"
- [class]{}-[func]{remove}
+ /* Remove the first node after node n0 in the linked list */
+ fun remove(n0: ListNode?) {
+ if (n0?.next == null)
+ return
+ // n0 -> P -> n1
+ val p = n0.next
+ val n1 = p?.next
+ n0.next = n1
+ }
```
=== "Ruby"
```ruby title="linked_list.rb"
- [class]{}-[func]{remove}
+ ### Delete first node after node n0 in linked list ###
+ def remove(n0)
+ return if n0.next.nil?
+
+ # n0 -> remove_node -> n1
+ remove_node = n0.next
+ n1 = remove_node.next
+ n0.next = n1
+ end
```
-=== "Zig"
+### 4. Accessing a Node
- ```zig title="linked_list.zig"
- [class]{}-[func]{remove}
- ```
-
-### 4. Accessing nodes
-
-**Accessing nodes in a linked list is less efficient**. As previously mentioned, any element in an array can be accessed in $O(1)$ time. In contrast, with a linked list, the program involves starting from the head node and sequentially traversing through the nodes until the desired node is found. In other words, to access the $i$-th node in a linked list, the program must iterate through $i - 1$ nodes, resulting in a time complexity of $O(n)$.
+**Accessing nodes in a linked list is less efficient**. As mentioned in the previous section, we can access any element in an array in $O(1)$ time. This is not the case with linked lists. The program needs to start from the head node and traverse backward one by one until the target node is found. That is, accessing the $i$-th node in a linked list requires $i - 1$ iterations, with a time complexity of $O(n)$.
=== "Python"
```python title="linked_list.py"
def access(head: ListNode, index: int) -> ListNode | None:
- """Access the node at `index` in the linked list"""
+ """Access the node at index index in the linked list"""
for _ in range(index):
if not head:
return None
@@ -656,7 +797,7 @@ It's important to note that even though node `P` continues to point to `n1` afte
=== "C++"
```cpp title="linked_list.cpp"
- /* Access the node at `index` in the linked list */
+ /* Access the node at index index in the linked list */
ListNode *access(ListNode *head, int index) {
for (int i = 0; i < index; i++) {
if (head == nullptr)
@@ -670,7 +811,7 @@ It's important to note that even though node `P` continues to point to `n1` afte
=== "Java"
```java title="linked_list.java"
- /* Access the node at `index` in the linked list */
+ /* Access the node at index index in the linked list */
ListNode access(ListNode head, int index) {
for (int i = 0; i < index; i++) {
if (head == null)
@@ -684,78 +825,167 @@ It's important to note that even though node `P` continues to point to `n1` afte
=== "C#"
```csharp title="linked_list.cs"
- [class]{linked_list}-[func]{Access}
+ /* Access the node at index index in the linked list */
+ ListNode? Access(ListNode? head, int index) {
+ for (int i = 0; i < index; i++) {
+ if (head == null)
+ return null;
+ head = head.next;
+ }
+ return head;
+ }
```
=== "Go"
```go title="linked_list.go"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ func access(head *ListNode, index int) *ListNode {
+ for i := 0; i < index; i++ {
+ if head == nil {
+ return nil
+ }
+ head = head.Next
+ }
+ return head
+ }
```
=== "Swift"
```swift title="linked_list.swift"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ func access(head: ListNode, index: Int) -> ListNode? {
+ var head: ListNode? = head
+ for _ in 0 ..< index {
+ if head == nil {
+ return nil
+ }
+ head = head?.next
+ }
+ return head
+ }
```
=== "JS"
```javascript title="linked_list.js"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ function access(head, index) {
+ for (let i = 0; i < index; i++) {
+ if (!head) {
+ return null;
+ }
+ head = head.next;
+ }
+ return head;
+ }
```
=== "TS"
```typescript title="linked_list.ts"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ function access(head: ListNode | null, index: number): ListNode | null {
+ for (let i = 0; i < index; i++) {
+ if (!head) {
+ return null;
+ }
+ head = head.next;
+ }
+ return head;
+ }
```
=== "Dart"
```dart title="linked_list.dart"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ ListNode? access(ListNode? head, int index) {
+ for (var i = 0; i < index; i++) {
+ if (head == null) return null;
+ head = head.next;
+ }
+ return head;
+ }
```
=== "Rust"
```rust title="linked_list.rs"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ pub fn access(head: Rc>>, index: i32) -> Option>>> {
+ fn dfs(
+ head: Option<&Rc>>>,
+ index: i32,
+ ) -> Option>>> {
+ if index <= 0 {
+ return head.cloned();
+ }
+
+ if let Some(node) = head {
+ dfs(node.borrow().next.as_ref(), index - 1)
+ } else {
+ None
+ }
+ }
+
+ dfs(Some(head).as_ref(), index)
+ }
```
=== "C"
```c title="linked_list.c"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ ListNode *access(ListNode *head, int index) {
+ for (int i = 0; i < index; i++) {
+ if (head == NULL)
+ return NULL;
+ head = head->next;
+ }
+ return head;
+ }
```
=== "Kotlin"
```kotlin title="linked_list.kt"
- [class]{}-[func]{access}
+ /* Access the node at index index in the linked list */
+ fun access(head: ListNode?, index: Int): ListNode? {
+ var h = head
+ for (i in 0.. int:
- """Search for the first node with value target in the linked list"""
+ """Find the first node with value target in the linked list"""
index = 0
while head:
if head.val == target:
@@ -768,7 +998,7 @@ Traverse the linked list to locate a node whose value matches `target`, and then
=== "C++"
```cpp title="linked_list.cpp"
- /* Search for the first node with value target in the linked list */
+ /* Find the first node with value target in the linked list */
int find(ListNode *head, int target) {
int index = 0;
while (head != nullptr) {
@@ -784,7 +1014,7 @@ Traverse the linked list to locate a node whose value matches `target`, and then
=== "Java"
```java title="linked_list.java"
- /* Search for the first node with value target in the linked list */
+ /* Find the first node with value target in the linked list */
int find(ListNode head, int target) {
int index = 0;
while (head != null) {
@@ -800,111 +1030,216 @@ Traverse the linked list to locate a node whose value matches `target`, and then
=== "C#"
```csharp title="linked_list.cs"
- [class]{linked_list}-[func]{Find}
+ /* Find the first node with value target in the linked list */
+ int Find(ListNode? head, int target) {
+ int index = 0;
+ while (head != null) {
+ if (head.val == target)
+ return index;
+ head = head.next;
+ index++;
+ }
+ return -1;
+ }
```
=== "Go"
```go title="linked_list.go"
- [class]{}-[func]{findNode}
+ /* Find the first node with value target in the linked list */
+ func findNode(head *ListNode, target int) int {
+ index := 0
+ for head != nil {
+ if head.Val == target {
+ return index
+ }
+ head = head.Next
+ index++
+ }
+ return -1
+ }
```
=== "Swift"
```swift title="linked_list.swift"
- [class]{}-[func]{find}
+ /* Find the first node with value target in the linked list */
+ func find(head: ListNode, target: Int) -> Int {
+ var head: ListNode? = head
+ var index = 0
+ while head != nil {
+ if head?.val == target {
+ return index
+ }
+ head = head?.next
+ index += 1
+ }
+ return -1
+ }
```
=== "JS"
```javascript title="linked_list.js"
- [class]{}-[func]{find}
+ /* Find the first node with value target in the linked list */
+ function find(head, target) {
+ let index = 0;
+ while (head !== null) {
+ if (head.val === target) {
+ return index;
+ }
+ head = head.next;
+ index += 1;
+ }
+ return -1;
+ }
```
=== "TS"
```typescript title="linked_list.ts"
- [class]{}-[func]{find}
+ /* Find the first node with value target in the linked list */
+ function find(head: ListNode | null, target: number): number {
+ let index = 0;
+ while (head !== null) {
+ if (head.val === target) {
+ return index;
+ }
+ head = head.next;
+ index += 1;
+ }
+ return -1;
+ }
```
=== "Dart"
```dart title="linked_list.dart"
- [class]{}-[func]{find}
+ /* Find the first node with value target in the linked list */
+ int find(ListNode? head, int target) {
+ int index = 0;
+ while (head != null) {
+ if (head.val == target) {
+ return index;
+ }
+ head = head.next;
+ index++;
+ }
+ return -1;
+ }
```
=== "Rust"
```rust title="linked_list.rs"
- [class]{}-[func]{find}
+ /* Find the first node with value target in the linked list */
+ pub fn find(head: Rc>>, target: T) -> i32 {
+ fn find(head: Option<&Rc>>>, target: T, idx: i32) -> i32 {
+ if let Some(node) = head {
+ if node.borrow().val == target {
+ return idx;
+ }
+ return find(node.borrow().next.as_ref(), target, idx + 1);
+ } else {
+ -1
+ }
+ }
+
+ find(Some(head).as_ref(), target, 0)
+ }
```
=== "C"
```c title="linked_list.c"
- [class]{}-[func]{find}
+ /* Find the first node with value target in the linked list */
+ int find(ListNode *head, int target) {
+ int index = 0;
+ while (head) {
+ if (head->val == target)
+ return index;
+ head = head->next;
+ index++;
+ }
+ return -1;
+ }
```
=== "Kotlin"
```kotlin title="linked_list.kt"
- [class]{}-[func]{find}
+ /* Find the first node with value target in the linked list */
+ fun find(head: ListNode?, target: Int): Int {
+ var index = 0
+ var h = head
+ while (h != null) {
+ if (h._val == target)
+ return index
+ h = h.next
+ index++
+ }
+ return -1
+ }
```
=== "Ruby"
```ruby title="linked_list.rb"
- [class]{}-[func]{find}
+ ### Find first node with value target in linked list ###
+ def find(head, target)
+ index = 0
+ while head
+ return index if head.val == target
+ head = head.next
+ index += 1
+ end
+
+ -1
+ end
```
-=== "Zig"
+## 4.2.2 Arrays vs. Linked Lists
- ```zig title="linked_list.zig"
- [class]{}-[func]{find}
- ```
+Table 4-1 summarizes the characteristics of arrays and linked lists and compares their operational efficiencies. Since they employ two opposite storage strategies, their various properties and operational efficiencies also exhibit contrasting characteristics.
-## 4.2.2 Arrays vs. linked lists
-
-Table 4-1 summarizes the characteristics of arrays and linked lists, and it also compares their efficiencies in various operations. Because they utilize opposing storage strategies, their respective properties and operational efficiencies exhibit distinct contrasts.
-
-
Table 4-1 Efficiency comparison of arrays and linked lists
+
Table 4-1 Comparison of array and linked list efficiencies
-| | Arrays | Linked Lists |
-| ------------------ | ------------------------------------------------ | ----------------------- |
-| Storage | Contiguous Memory Space | Dispersed Memory Space |
-| Capacity Expansion | Fixed Length | Flexible Expansion |
-| Memory Efficiency | Less Memory per Element, Potential Space Wastage | More Memory per Element |
-| Accessing Elements | $O(1)$ | $O(n)$ |
-| Adding Elements | $O(n)$ | $O(1)$ |
-| Deleting Elements | $O(n)$ | $O(1)$ |
+| | Array | Linked List |
+| ---------------------- | --------------------------------------------- | -------------------------- |
+| Storage method | Contiguous memory space | Scattered memory space |
+| Capacity expansion | Immutable length | Flexible expansion |
+| Memory efficiency | Elements occupy less memory, but space may be wasted | Elements occupy more memory |
+| Accessing an element | $O(1)$ | $O(n)$ |
+| Adding an element | $O(n)$ | $O(1)$ |
+| Removing an element | $O(n)$ | $O(1)$ |
-## 4.2.3 Common types of linked lists
+## 4.2.3 Common Types of Linked Lists
-As shown in Figure 4-8, there are three common types of linked lists.
+As shown in Figure 4-8, there are three common types of linked lists:
-- **Singly linked list**: This is the standard linked list described earlier. Nodes in a singly linked list include a value and a reference to the next node. The first node is known as the head node, and the last node, which points to null (`None`), is the tail node.
-- **Circular linked list**: This is formed when the tail node of a singly linked list points back to the head node, creating a loop. In a circular linked list, any node can function as the head node.
-- **Doubly linked list**: In contrast to a singly linked list, a doubly linked list maintains references in two directions. Each node contains references (pointer) to both its successor (the next node) and predecessor (the previous node). Although doubly linked lists offer more flexibility for traversing in either direction, they also consume more memory space.
+- **Singly linked list**: This is the ordinary linked list introduced earlier. The nodes of a singly linked list contain a value and a reference to the next node. We call the first node the head node and the last node the tail node, which points to null `None`.
+- **Circular linked list**: If we make the tail node of a singly linked list point to the head node (connecting the tail to the head), we get a circular linked list. In a circular linked list, any node can be viewed as the head node.
+- **Doubly linked list**: Compared to a singly linked list, a doubly linked list records references in both directions. The node definition of a doubly linked list includes references to both the successor node (next node) and the predecessor node (previous node). Compared to a singly linked list, a doubly linked list is more flexible and can traverse the linked list in both directions, but it also requires more memory space.
=== "Python"
```python title=""
class ListNode:
- """Bidirectional linked list node class"""
+ """Doubly linked list node class"""
def __init__(self, val: int):
self.val: int = val # Node value
self.next: ListNode | None = None # Reference to the successor node
- self.prev: ListNode | None = None # Reference to a predecessor node
+ self.prev: ListNode | None = None # Reference to the predecessor node
```
=== "C++"
```cpp title=""
- /* Bidirectional linked list node structure */
+ /* Doubly linked list node structure */
struct ListNode {
int val; // Node value
ListNode *next; // Pointer to the successor node
@@ -916,10 +1251,10 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "Java"
```java title=""
- /* Bidirectional linked list node class */
+ /* Doubly linked list node class */
class ListNode {
int val; // Node value
- ListNode next; // Reference to the next node
+ ListNode next; // Reference to the successor node
ListNode prev; // Reference to the predecessor node
ListNode(int x) { val = x; } // Constructor
}
@@ -928,10 +1263,10 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "C#"
```csharp title=""
- /* Bidirectional linked list node class */
+ /* Doubly linked list node class */
class ListNode(int x) { // Constructor
int val = x; // Node value
- ListNode next; // Reference to the next node
+ ListNode next; // Reference to the successor node
ListNode prev; // Reference to the predecessor node
}
```
@@ -939,14 +1274,14 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "Go"
```go title=""
- /* Bidirectional linked list node structure */
+ /* Doubly linked list node structure */
type DoublyListNode struct {
Val int // Node value
Next *DoublyListNode // Pointer to the successor node
Prev *DoublyListNode // Pointer to the predecessor node
}
- // NewDoublyListNode initialization
+ // NewDoublyListNode Initialization
func NewDoublyListNode(val int) *DoublyListNode {
return &DoublyListNode{
Val: val,
@@ -959,10 +1294,10 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "Swift"
```swift title=""
- /* Bidirectional linked list node class */
+ /* Doubly linked list node class */
class ListNode {
var val: Int // Node value
- var next: ListNode? // Reference to the next node
+ var next: ListNode? // Reference to the successor node
var prev: ListNode? // Reference to the predecessor node
init(x: Int) { // Constructor
@@ -974,7 +1309,7 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "JS"
```javascript title=""
- /* Bidirectional linked list node class */
+ /* Doubly linked list node class */
class ListNode {
constructor(val, next, prev) {
this.val = val === undefined ? 0 : val; // Node value
@@ -987,7 +1322,7 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "TS"
```typescript title=""
- /* Bidirectional linked list node class */
+ /* Doubly linked list node class */
class ListNode {
val: number;
next: ListNode | null;
@@ -1003,11 +1338,11 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "Dart"
```dart title=""
- /* Bidirectional linked list node class */
+ /* Doubly linked list node class */
class ListNode {
int val; // Node value
- ListNode next; // Reference to the next node
- ListNode prev; // Reference to the predecessor node
+ ListNode? next; // Reference to the successor node
+ ListNode? prev; // Reference to the predecessor node
ListNode(this.val, [this.next, this.prev]); // Constructor
}
```
@@ -1018,15 +1353,15 @@ As shown in Figure 4-8, there are three common types of linked lists.
use std::rc::Rc;
use std::cell::RefCell;
- /* Bidirectional linked list node type */
+ /* Doubly linked list node type */
#[derive(Debug)]
struct ListNode {
val: i32, // Node value
- next: Option>>, // Pointer to successor node
- prev: Option>>, // Pointer to predecessor node
+ next: Option>>, // Pointer to the successor node
+ prev: Option>>, // Pointer to the predecessor node
}
- /* Constructors */
+ /* Constructor */
impl ListNode {
fn new(val: i32) -> Self {
ListNode {
@@ -1041,16 +1376,16 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "C"
```c title=""
- /* Bidirectional linked list node structure */
+ /* Doubly linked list node structure */
typedef struct ListNode {
int val; // Node value
struct ListNode *next; // Pointer to the successor node
struct ListNode *prev; // Pointer to the predecessor node
} ListNode;
- /* Constructors */
+ /* Constructor */
ListNode *newListNode(int val) {
- ListNode *node, *next;
+ ListNode *node;
node = (ListNode *) malloc(sizeof(ListNode));
node->val = val;
node->next = NULL;
@@ -1062,50 +1397,51 @@ As shown in Figure 4-8, there are three common types of linked lists.
=== "Kotlin"
```kotlin title=""
-
+ /* Doubly linked list node class */
+ // Constructor
+ class ListNode(x: Int) {
+ val _val: Int = x // Node value
+ val next: ListNode? = null // Reference to the successor node
+ val prev: ListNode? = null // Reference to the predecessor node
+ }
```
-=== "Zig"
+=== "Ruby"
- ```zig title=""
- // Bidirectional linked list node class
- pub fn ListNode(comptime T: type) type {
- return struct {
- const Self = @This();
+ ```ruby title=""
+ # Doubly linked list node class
+ class ListNode
+ attr_accessor :val # Node value
+ attr_accessor :next # Reference to the successor node
+ attr_accessor :prev # Reference to the predecessor node
- val: T = 0, // Node value
- next: ?*Self = null, // Pointer to the successor node
- prev: ?*Self = null, // Pointer to the predecessor node
-
- // Constructor
- pub fn init(self: *Self, x: i32) void {
- self.val = x;
- self.next = null;
- self.prev = null;
- }
- };
- }
+ def initialize(val=0, next_node=nil, prev_node=nil)
+ @val = val
+ @next = next_node
+ @prev = prev_node
+ end
+ end
```
{ class="animation-figure" }
Figure 4-8 Common types of linked lists
-## 4.2.4 Typical applications of linked lists
+## 4.2.4 Typical Applications of Linked Lists
-Singly linked lists are frequently utilized in implementing stacks, queues, hash tables, and graphs.
+Singly linked lists are commonly used to implement stacks, queues, hash tables, and graphs.
-- **Stacks and queues**: In singly linked lists, if insertions and deletions occur at the same end, it behaves like a stack (last-in-first-out). Conversely, if insertions are at one end and deletions at the other, it functions like a queue (first-in-first-out).
-- **Hash tables**: Linked lists are used in chaining, a popular method for resolving hash collisions. Here, all collided elements are grouped into a linked list.
-- **Graphs**: Adjacency lists, a standard method for graph representation, associate each graph vertex with a linked list. This list contains elements that represent vertices connected to the corresponding vertex.
+- **Stacks and queues**: When insertion and deletion operations both occur at one end of the linked list, it exhibits last-in-first-out characteristics, corresponding to a stack. When insertion operations occur at one end of the linked list and deletion operations occur at the other end, it exhibits first-in-first-out characteristics, corresponding to a queue.
+- **Hash tables**: Separate chaining is one of the mainstream solutions for resolving hash collisions. In this approach, all colliding elements are placed in a linked list.
+- **Graphs**: An adjacency list is a common way to represent a graph, where each vertex in the graph is associated with a linked list, and each element in the linked list represents another vertex connected to that vertex.
-Doubly linked lists are ideal for scenarios requiring rapid access to preceding and succeeding elements.
+Doubly linked lists are commonly used in scenarios where quick access to the previous and next elements is needed.
-- **Advanced data structures**: In structures like red-black trees and B-trees, accessing a node's parent is essential. This is achieved by incorporating a reference to the parent node in each node, akin to a doubly linked list.
-- **Browser history**: In web browsers, doubly linked lists facilitate navigating the history of visited pages when users click forward or back.
-- **LRU algorithm**: Doubly linked lists are apt for Least Recently Used (LRU) cache eviction algorithms, enabling swift identification of the least recently used data and facilitating fast node addition and removal.
+- **Advanced data structures**: For example, in red-black trees and B-trees, we need to access the parent node of a node, which can be achieved by saving a reference to the parent node in the node, similar to a doubly linked list.
+- **Browser history**: In web browsers, when a user clicks the forward or backward button, the browser needs to know the previous and next web pages the user visited. The characteristics of doubly linked lists make this operation simple.
+- **LRU algorithm**: In cache eviction (LRU) algorithms, we need to quickly find the least recently used data and support quick addition and deletion of nodes. Using a doubly linked list is very suitable for this.
-Circular linked lists are ideal for applications that require periodic operations, such as resource scheduling in operating systems.
+Circular linked lists are commonly used in scenarios that require periodic operations, such as operating system resource scheduling.
-- **Round-robin scheduling algorithm**: In operating systems, the round-robin scheduling algorithm is a common CPU scheduling method, requiring cycling through a group of processes. Each process is assigned a time slice, and upon expiration, the CPU rotates to the next process. This cyclical operation can be efficiently realized using a circular linked list, allowing for a fair and time-shared system among all processes.
-- **Data buffers**: Circular linked lists are also used in data buffers, like in audio and video players, where the data stream is divided into multiple buffer blocks arranged in a circular fashion for seamless playback.
+- **Round-robin scheduling algorithm**: In operating systems, round-robin scheduling is a common CPU scheduling algorithm that needs to cycle through a set of processes. Each process is assigned a time slice, and when the time slice expires, the CPU switches to the next process. This cyclic operation can be implemented using a circular linked list.
+- **Data buffers**: In some data buffer implementations, circular linked lists may also be used. For example, in audio and video players, the data stream may be divided into multiple buffer blocks and placed in a circular linked list to achieve seamless playback.
diff --git a/en/docs/chapter_array_and_linkedlist/list.md b/en/docs/chapter_array_and_linkedlist/list.md
index 7dbc02836..ac64cbfef 100755
--- a/en/docs/chapter_array_and_linkedlist/list.md
+++ b/en/docs/chapter_array_and_linkedlist/list.md
@@ -4,27 +4,27 @@ comments: true
# 4.3 List
-A list is an abstract data structure concept that represents an ordered collection of elements, supporting operations such as element access, modification, addition, deletion, and traversal, without requiring users to consider capacity limitations. Lists can be implemented based on linked lists or arrays.
+A list is an abstract data structure concept that represents an ordered collection of elements, supporting operations such as element access, modification, insertion, deletion, and traversal, without requiring users to consider capacity limitations. Lists can be implemented based on linked lists or arrays.
-- A linked list inherently serves as a list, supporting operations for adding, deleting, searching, and modifying elements, with the flexibility to dynamically adjust its size.
-- Arrays also support these operations, but due to their immutable length, they can be considered as a list with a length limit.
+- A linked list can naturally be viewed as a list, supporting element insertion, deletion, search, and modification operations, and can flexibly expand dynamically.
+- An array also supports element insertion, deletion, search, and modification, but since its length is immutable, it can only be viewed as a list with length limitations.
-When implementing lists using arrays, **the immutability of length reduces the practicality of the list**. This is because predicting the amount of data to be stored in advance is often challenging, making it difficult to choose an appropriate list length. If the length is too small, it may not meet the requirements; if too large, it may waste memory space.
+When implementing lists using arrays, **the immutable length property reduces the practicality of the list**. This is because we usually cannot determine in advance how much data we need to store, making it difficult to choose an appropriate list length. If the length is too small, it may fail to meet usage requirements; if the length is too large, it will waste memory space.
-To solve this problem, we can implement lists using a dynamic array. It inherits the advantages of arrays and can dynamically expand during program execution.
+To solve this problem, we can use a dynamic array to implement a list. It inherits all the advantages of arrays and can dynamically expand during program execution.
-In fact, **many programming languages' standard libraries implement lists using dynamic arrays**, such as Python's `list`, Java's `ArrayList`, C++'s `vector`, and C#'s `List`. In the following discussion, we will consider "list" and "dynamic array" as synonymous concepts.
+In fact, **the lists provided in the standard libraries of many programming languages are implemented based on dynamic arrays**, such as `list` in Python, `ArrayList` in Java, `vector` in C++, and `List` in C#. In the following discussion, we will treat "list" and "dynamic array" as equivalent concepts.
-## 4.3.1 Common list operations
+## 4.3.1 Common List Operations
-### 1. Initializing a list
+### 1. Initialize a List
-We typically use two initialization methods: "without initial values" and "with initial values".
+We typically use two initialization methods: "without initial values" and "with initial values":
=== "Python"
```python title="list.py"
- # Initialize list
+ # Initialize a list
# Without initial values
nums1: list[int] = []
# With initial values
@@ -34,8 +34,8 @@ We typically use two initialization methods: "without initial values" and "with
=== "C++"
```cpp title="list.cpp"
- /* Initialize list */
- // Note, in C++ the vector is the equivalent of nums described here
+ /* Initialize a list */
+ // Note that vector in C++ is equivalent to nums as described in this article
// Without initial values
vector nums1;
// With initial values
@@ -45,10 +45,10 @@ We typically use two initialization methods: "without initial values" and "with
=== "Java"
```java title="list.java"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
List nums1 = new ArrayList<>();
- // With initial values (note the element type should be the wrapper class Integer[] for int[])
+ // With initial values (note that array elements should use the wrapper class Integer[] instead of int[])
Integer[] numbers = new Integer[] { 1, 3, 2, 5, 4 };
List nums = new ArrayList<>(Arrays.asList(numbers));
```
@@ -56,7 +56,7 @@ We typically use two initialization methods: "without initial values" and "with
=== "C#"
```csharp title="list.cs"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
List nums1 = [];
// With initial values
@@ -67,7 +67,7 @@ We typically use two initialization methods: "without initial values" and "with
=== "Go"
```go title="list_test.go"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
nums1 := []int{}
// With initial values
@@ -77,7 +77,7 @@ We typically use two initialization methods: "without initial values" and "with
=== "Swift"
```swift title="list.swift"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
let nums1: [Int] = []
// With initial values
@@ -87,7 +87,7 @@ We typically use two initialization methods: "without initial values" and "with
=== "JS"
```javascript title="list.js"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
const nums1 = [];
// With initial values
@@ -97,7 +97,7 @@ We typically use two initialization methods: "without initial values" and "with
=== "TS"
```typescript title="list.ts"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
const nums1: number[] = [];
// With initial values
@@ -107,7 +107,7 @@ We typically use two initialization methods: "without initial values" and "with
=== "Dart"
```dart title="list.dart"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
List nums1 = [];
// With initial values
@@ -117,7 +117,7 @@ We typically use two initialization methods: "without initial values" and "with
=== "Rust"
```rust title="list.rs"
- /* Initialize list */
+ /* Initialize a list */
// Without initial values
let nums1: Vec = Vec::new();
// With initial values
@@ -133,119 +133,130 @@ We typically use two initialization methods: "without initial values" and "with
=== "Kotlin"
```kotlin title="list.kt"
-
+ /* Initialize a list */
+ // Without initial values
+ var nums1 = listOf()
+ // With initial values
+ var numbers = arrayOf(1, 3, 2, 5, 4)
+ var nums = numbers.toMutableList()
```
-=== "Zig"
+=== "Ruby"
- ```zig title="list.zig"
- // Initialize list
- var nums = std.ArrayList(i32).init(std.heap.page_allocator);
- defer nums.deinit();
- try nums.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 });
+ ```ruby title="list.rb"
+ # Initialize a list
+ # Without initial values
+ nums1 = []
+ # With initial values
+ nums = [1, 3, 2, 5, 4]
```
-### 2. Accessing elements
+??? pythontutor "Code Visualization"
-Lists are essentially arrays, thus they can access and update elements in $O(1)$ time, which is very efficient.
+
+
+
+### 2. Access Elements
+
+Since a list is essentially an array, we can access and update elements in $O(1)$ time complexity, which is very efficient.
=== "Python"
```python title="list.py"
- # Access elements
- num: int = nums[1] # Access the element at index 1
+ # Access an element
+ num: int = nums[1] # Access element at index 1
- # Update elements
- nums[1] = 0 # Update the element at index 1 to 0
+ # Update an element
+ nums[1] = 0 # Update element at index 1 to 0
```
=== "C++"
```cpp title="list.cpp"
- /* Access elements */
- int num = nums[1]; // Access the element at index 1
+ /* Access an element */
+ int num = nums[1]; // Access element at index 1
- /* Update elements */
- nums[1] = 0; // Update the element at index 1 to 0
+ /* Update an element */
+ nums[1] = 0; // Update element at index 1 to 0
```
=== "Java"
```java title="list.java"
- /* Access elements */
- int num = nums.get(1); // Access the element at index 1
+ /* Access an element */
+ int num = nums.get(1); // Access element at index 1
- /* Update elements */
- nums.set(1, 0); // Update the element at index 1 to 0
+ /* Update an element */
+ nums.set(1, 0); // Update element at index 1 to 0
```
=== "C#"
```csharp title="list.cs"
- /* Access elements */
- int num = nums[1]; // Access the element at index 1
+ /* Access an element */
+ int num = nums[1]; // Access element at index 1
- /* Update elements */
- nums[1] = 0; // Update the element at index 1 to 0
+ /* Update an element */
+ nums[1] = 0; // Update element at index 1 to 0
```
=== "Go"
```go title="list_test.go"
- /* Access elements */
- num := nums[1] // Access the element at index 1
+ /* Access an element */
+ num := nums[1] // Access element at index 1
- /* Update elements */
- nums[1] = 0 // Update the element at index 1 to 0
+ /* Update an element */
+ nums[1] = 0 // Update element at index 1 to 0
```
=== "Swift"
```swift title="list.swift"
- /* Access elements */
- let num = nums[1] // Access the element at index 1
+ /* Access an element */
+ let num = nums[1] // Access element at index 1
- /* Update elements */
- nums[1] = 0 // Update the element at index 1 to 0
+ /* Update an element */
+ nums[1] = 0 // Update element at index 1 to 0
```
=== "JS"
```javascript title="list.js"
- /* Access elements */
- const num = nums[1]; // Access the element at index 1
+ /* Access an element */
+ const num = nums[1]; // Access element at index 1
- /* Update elements */
- nums[1] = 0; // Update the element at index 1 to 0
+ /* Update an element */
+ nums[1] = 0; // Update element at index 1 to 0
```
=== "TS"
```typescript title="list.ts"
- /* Access elements */
- const num: number = nums[1]; // Access the element at index 1
+ /* Access an element */
+ const num: number = nums[1]; // Access element at index 1
- /* Update elements */
- nums[1] = 0; // Update the element at index 1 to 0
+ /* Update an element */
+ nums[1] = 0; // Update element at index 1 to 0
```
=== "Dart"
```dart title="list.dart"
- /* Access elements */
- int num = nums[1]; // Access the element at index 1
+ /* Access an element */
+ int num = nums[1]; // Access element at index 1
- /* Update elements */
- nums[1] = 0; // Update the element at index 1 to 0
+ /* Update an element */
+ nums[1] = 0; // Update element at index 1 to 0
```
=== "Rust"
```rust title="list.rs"
- /* Access elements */
- let num: i32 = nums[1]; // Access the element at index 1
- /* Update elements */
- nums[1] = 0; // Update the element at index 1 to 0
+ /* Access an element */
+ let num: i32 = nums[1]; // Access element at index 1
+ /* Update an element */
+ nums[1] = 0; // Update element at index 1 to 0
```
=== "C"
@@ -257,221 +268,228 @@ Lists are essentially arrays, thus they can access and update elements in $O(1)$
=== "Kotlin"
```kotlin title="list.kt"
-
+ /* Access an element */
+ val num = nums[1] // Access element at index 1
+ /* Update an element */
+ nums[1] = 0 // Update element at index 1 to 0
```
-=== "Zig"
+=== "Ruby"
- ```zig title="list.zig"
- // Access elements
- var num = nums.items[1]; // Access the element at index 1
-
- // Update elements
- nums.items[1] = 0; // Update the element at index 1 to 0
+ ```ruby title="list.rb"
+ # Access an element
+ num = nums[1] # Access element at index 1
+ # Update an element
+ nums[1] = 0 # Update element at index 1 to 0
```
-### 3. Inserting and removing elements
+??? pythontutor "Code Visualization"
-Compared to arrays, lists offer more flexibility in adding and removing elements. While adding elements to the end of a list is an $O(1)$ operation, the efficiency of inserting and removing elements elsewhere in the list remains the same as in arrays, with a time complexity of $O(n)$.
+
+
+
+### 3. Insert and Delete Elements
+
+Compared to arrays, lists can freely add and delete elements. Adding an element at the end of a list has a time complexity of $O(1)$, but inserting and deleting elements still have the same efficiency as arrays, with a time complexity of $O(n)$.
=== "Python"
```python title="list.py"
- # Clear list
+ # Clear the list
nums.clear()
- # Append elements at the end
+ # Add elements at the end
nums.append(1)
nums.append(3)
nums.append(2)
nums.append(5)
nums.append(4)
- # Insert element in the middle
+ # Insert an element in the middle
nums.insert(3, 6) # Insert number 6 at index 3
- # Remove elements
- nums.pop(3) # Remove the element at index 3
+ # Delete an element
+ nums.pop(3) # Delete element at index 3
```
=== "C++"
```cpp title="list.cpp"
- /* Clear list */
+ /* Clear the list */
nums.clear();
- /* Append elements at the end */
+ /* Add elements at the end */
nums.push_back(1);
nums.push_back(3);
nums.push_back(2);
nums.push_back(5);
nums.push_back(4);
- /* Insert element in the middle */
+ /* Insert an element in the middle */
nums.insert(nums.begin() + 3, 6); // Insert number 6 at index 3
- /* Remove elements */
- nums.erase(nums.begin() + 3); // Remove the element at index 3
+ /* Delete an element */
+ nums.erase(nums.begin() + 3); // Delete element at index 3
```
=== "Java"
```java title="list.java"
- /* Clear list */
+ /* Clear the list */
nums.clear();
- /* Append elements at the end */
+ /* Add elements at the end */
nums.add(1);
nums.add(3);
nums.add(2);
nums.add(5);
nums.add(4);
- /* Insert element in the middle */
+ /* Insert an element in the middle */
nums.add(3, 6); // Insert number 6 at index 3
- /* Remove elements */
- nums.remove(3); // Remove the element at index 3
+ /* Delete an element */
+ nums.remove(3); // Delete element at index 3
```
=== "C#"
```csharp title="list.cs"
- /* Clear list */
+ /* Clear the list */
nums.Clear();
- /* Append elements at the end */
+ /* Add elements at the end */
nums.Add(1);
nums.Add(3);
nums.Add(2);
nums.Add(5);
nums.Add(4);
- /* Insert element in the middle */
- nums.Insert(3, 6);
+ /* Insert an element in the middle */
+ nums.Insert(3, 6); // Insert number 6 at index 3
- /* Remove elements */
- nums.RemoveAt(3);
+ /* Delete an element */
+ nums.RemoveAt(3); // Delete element at index 3
```
=== "Go"
```go title="list_test.go"
- /* Clear list */
+ /* Clear the list */
nums = nil
- /* Append elements at the end */
+ /* Add elements at the end */
nums = append(nums, 1)
nums = append(nums, 3)
nums = append(nums, 2)
nums = append(nums, 5)
nums = append(nums, 4)
- /* Insert element in the middle */
+ /* Insert an element in the middle */
nums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert number 6 at index 3
- /* Remove elements */
- nums = append(nums[:3], nums[4:]...) // Remove the element at index 3
+ /* Delete an element */
+ nums = append(nums[:3], nums[4:]...) // Delete element at index 3
```
=== "Swift"
```swift title="list.swift"
- /* Clear list */
+ /* Clear the list */
nums.removeAll()
- /* Append elements at the end */
+ /* Add elements at the end */
nums.append(1)
nums.append(3)
nums.append(2)
nums.append(5)
nums.append(4)
- /* Insert element in the middle */
+ /* Insert an element in the middle */
nums.insert(6, at: 3) // Insert number 6 at index 3
- /* Remove elements */
- nums.remove(at: 3) // Remove the element at index 3
+ /* Delete an element */
+ nums.remove(at: 3) // Delete element at index 3
```
=== "JS"
```javascript title="list.js"
- /* Clear list */
+ /* Clear the list */
nums.length = 0;
- /* Append elements at the end */
+ /* Add elements at the end */
nums.push(1);
nums.push(3);
nums.push(2);
nums.push(5);
nums.push(4);
- /* Insert element in the middle */
- nums.splice(3, 0, 6);
+ /* Insert an element in the middle */
+ nums.splice(3, 0, 6); // Insert number 6 at index 3
- /* Remove elements */
- nums.splice(3, 1);
+ /* Delete an element */
+ nums.splice(3, 1); // Delete element at index 3
```
=== "TS"
```typescript title="list.ts"
- /* Clear list */
+ /* Clear the list */
nums.length = 0;
- /* Append elements at the end */
+ /* Add elements at the end */
nums.push(1);
nums.push(3);
nums.push(2);
nums.push(5);
nums.push(4);
- /* Insert element in the middle */
- nums.splice(3, 0, 6);
+ /* Insert an element in the middle */
+ nums.splice(3, 0, 6); // Insert number 6 at index 3
- /* Remove elements */
- nums.splice(3, 1);
+ /* Delete an element */
+ nums.splice(3, 1); // Delete element at index 3
```
=== "Dart"
```dart title="list.dart"
- /* Clear list */
+ /* Clear the list */
nums.clear();
- /* Append elements at the end */
+ /* Add elements at the end */
nums.add(1);
nums.add(3);
nums.add(2);
nums.add(5);
nums.add(4);
- /* Insert element in the middle */
+ /* Insert an element in the middle */
nums.insert(3, 6); // Insert number 6 at index 3
- /* Remove elements */
- nums.removeAt(3); // Remove the element at index 3
+ /* Delete an element */
+ nums.removeAt(3); // Delete element at index 3
```
=== "Rust"
```rust title="list.rs"
- /* Clear list */
+ /* Clear the list */
nums.clear();
- /* Append elements at the end */
+ /* Add elements at the end */
nums.push(1);
nums.push(3);
nums.push(2);
nums.push(5);
nums.push(4);
- /* Insert element in the middle */
+ /* Insert an element in the middle */
nums.insert(3, 6); // Insert number 6 at index 3
- /* Remove elements */
- nums.remove(3); // Remove the element at index 3
+ /* Delete an element */
+ nums.remove(3); // Delete element at index 3
```
=== "C"
@@ -483,42 +501,61 @@ Compared to arrays, lists offer more flexibility in adding and removing elements
=== "Kotlin"
```kotlin title="list.kt"
+ /* Clear the list */
+ nums.clear();
+ /* Add elements at the end */
+ nums.add(1);
+ nums.add(3);
+ nums.add(2);
+ nums.add(5);
+ nums.add(4);
+
+ /* Insert an element in the middle */
+ nums.add(3, 6); // Insert number 6 at index 3
+
+ /* Delete an element */
+ nums.remove(3); // Delete element at index 3
```
-=== "Zig"
+=== "Ruby"
- ```zig title="list.zig"
- // Clear list
- nums.clearRetainingCapacity();
+ ```ruby title="list.rb"
+ # Clear the list
+ nums.clear
- // Append elements at the end
- try nums.append(1);
- try nums.append(3);
- try nums.append(2);
- try nums.append(5);
- try nums.append(4);
+ # Add elements at the end
+ nums << 1
+ nums << 3
+ nums << 2
+ nums << 5
+ nums << 4
- // Insert element in the middle
- try nums.insert(3, 6); // Insert number 6 at index 3
+ # Insert an element in the middle
+ nums.insert(3, 6) # Insert number 6 at index 3
- // Remove elements
- _ = nums.orderedRemove(3); // Remove the element at index 3
+ # Delete an element
+ nums.delete_at(3) # Delete element at index 3
```
-### 4. Iterating the list
+??? pythontutor "Code Visualization"
-Similar to arrays, lists can be iterated either by using indices or by directly iterating through each element.
+
+
+
+### 4. Traverse a List
+
+Like arrays, lists can be traversed by index or by directly iterating through elements.
=== "Python"
```python title="list.py"
- # Iterate through the list by index
+ # Traverse the list by index
count = 0
for i in range(len(nums)):
count += nums[i]
- # Iterate directly through list elements
+ # Traverse list elements directly
for num in nums:
count += num
```
@@ -526,13 +563,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "C++"
```cpp title="list.cpp"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
int count = 0;
for (int i = 0; i < nums.size(); i++) {
count += nums[i];
}
- /* Iterate directly through list elements */
+ /* Traverse list elements directly */
count = 0;
for (int num : nums) {
count += num;
@@ -542,13 +579,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "Java"
```java title="list.java"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
int count = 0;
for (int i = 0; i < nums.size(); i++) {
count += nums.get(i);
}
- /* Iterate directly through list elements */
+ /* Traverse list elements directly */
for (int num : nums) {
count += num;
}
@@ -557,13 +594,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "C#"
```csharp title="list.cs"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
int count = 0;
for (int i = 0; i < nums.Count; i++) {
count += nums[i];
}
- /* Iterate directly through list elements */
+ /* Traverse list elements directly */
count = 0;
foreach (int num in nums) {
count += num;
@@ -573,13 +610,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "Go"
```go title="list_test.go"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
count := 0
for i := 0; i < len(nums); i++ {
count += nums[i]
}
- /* Iterate directly through list elements */
+ /* Traverse list elements directly */
count = 0
for _, num := range nums {
count += num
@@ -589,13 +626,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "Swift"
```swift title="list.swift"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
var count = 0
for i in nums.indices {
count += nums[i]
}
- /* Iterate directly through list elements */
+ /* Traverse list elements directly */
count = 0
for num in nums {
count += num
@@ -605,13 +642,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "JS"
```javascript title="list.js"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
let count = 0;
for (let i = 0; i < nums.length; i++) {
count += nums[i];
}
- /* Iterate directly through list elements */
+ /* Traverse list elements directly */
count = 0;
for (const num of nums) {
count += num;
@@ -621,13 +658,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "TS"
```typescript title="list.ts"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
let count = 0;
for (let i = 0; i < nums.length; i++) {
count += nums[i];
}
- /* Iterate directly through list elements */
+ /* Traverse list elements directly */
count = 0;
for (const num of nums) {
count += num;
@@ -637,13 +674,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "Dart"
```dart title="list.dart"
- /* Iterate through the list by index */
+ /* Traverse the list by index */
int count = 0;
for (var i = 0; i < nums.length; i++) {
count += nums[i];
}
-
- /* Iterate directly through list elements */
+
+ /* Traverse list elements directly */
count = 0;
for (var num in nums) {
count += num;
@@ -653,13 +690,13 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "Rust"
```rust title="list.rs"
- // Iterate through the list by index
+ // Traverse the list by index
let mut _count = 0;
for i in 0..nums.len() {
_count += nums[i];
}
- // Iterate directly through list elements
+ // Traverse list elements directly
_count = 0;
for num in &nums {
_count += num;
@@ -675,36 +712,49 @@ Similar to arrays, lists can be iterated either by using indices or by directly
=== "Kotlin"
```kotlin title="list.kt"
-
- ```
-
-=== "Zig"
-
- ```zig title="list.zig"
- // Iterate through the list by index
- var count: i32 = 0;
- var i: i32 = 0;
- while (i < nums.items.len) : (i += 1) {
- count += nums[i];
+ /* Traverse the list by index */
+ var count = 0
+ for (i in nums.indices) {
+ count += nums[i]
}
- // Iterate directly through list elements
- count = 0;
- for (nums.items) |num| {
- count += num;
+ /* Traverse list elements directly */
+ for (num in nums) {
+ count += num
}
```
-### 5. Concatenating lists
+=== "Ruby"
-Given a new list `nums1`, we can append it to the end of the original list.
+ ```ruby title="list.rb"
+ # Traverse the list by index
+ count = 0
+ for i in 0...nums.length
+ count += nums[i]
+ end
+
+ # Traverse list elements directly
+ count = 0
+ for num in nums
+ count += num
+ end
+ ```
+
+??? pythontutor "Code Visualization"
+
+
+
+
+### 5. Concatenate Lists
+
+Given a new list `nums1`, we can concatenate it to the end of the original list.
=== "Python"
```python title="list.py"
# Concatenate two lists
nums1: list[int] = [6, 8, 7, 10, 9]
- nums += nums1 # Concatenate nums1 to the end of nums
+ nums += nums1 # Concatenate list nums1 to the end of nums
```
=== "C++"
@@ -712,7 +762,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```cpp title="list.cpp"
/* Concatenate two lists */
vector nums1 = { 6, 8, 7, 10, 9 };
- // Concatenate nums1 to the end of nums
+ // Concatenate list nums1 to the end of nums
nums.insert(nums.end(), nums1.begin(), nums1.end());
```
@@ -721,7 +771,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```java title="list.java"
/* Concatenate two lists */
List nums1 = new ArrayList<>(Arrays.asList(new Integer[] { 6, 8, 7, 10, 9 }));
- nums.addAll(nums1); // Concatenate nums1 to the end of nums
+ nums.addAll(nums1); // Concatenate list nums1 to the end of nums
```
=== "C#"
@@ -729,7 +779,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```csharp title="list.cs"
/* Concatenate two lists */
List nums1 = [6, 8, 7, 10, 9];
- nums.AddRange(nums1); // Concatenate nums1 to the end of nums
+ nums.AddRange(nums1); // Concatenate list nums1 to the end of nums
```
=== "Go"
@@ -737,7 +787,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```go title="list_test.go"
/* Concatenate two lists */
nums1 := []int{6, 8, 7, 10, 9}
- nums = append(nums, nums1...) // Concatenate nums1 to the end of nums
+ nums = append(nums, nums1...) // Concatenate list nums1 to the end of nums
```
=== "Swift"
@@ -745,7 +795,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```swift title="list.swift"
/* Concatenate two lists */
let nums1 = [6, 8, 7, 10, 9]
- nums.append(contentsOf: nums1) // Concatenate nums1 to the end of nums
+ nums.append(contentsOf: nums1) // Concatenate list nums1 to the end of nums
```
=== "JS"
@@ -753,7 +803,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```javascript title="list.js"
/* Concatenate two lists */
const nums1 = [6, 8, 7, 10, 9];
- nums.push(...nums1); // Concatenate nums1 to the end of nums
+ nums.push(...nums1); // Concatenate list nums1 to the end of nums
```
=== "TS"
@@ -761,7 +811,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```typescript title="list.ts"
/* Concatenate two lists */
const nums1: number[] = [6, 8, 7, 10, 9];
- nums.push(...nums1); // Concatenate nums1 to the end of nums
+ nums.push(...nums1); // Concatenate list nums1 to the end of nums
```
=== "Dart"
@@ -769,7 +819,7 @@ Given a new list `nums1`, we can append it to the end of the original list.
```dart title="list.dart"
/* Concatenate two lists */
List nums1 = [6, 8, 7, 10, 9];
- nums.addAll(nums1); // Concatenate nums1 to the end of nums
+ nums.addAll(nums1); // Concatenate list nums1 to the end of nums
```
=== "Rust"
@@ -789,91 +839,96 @@ Given a new list `nums1`, we can append it to the end of the original list.
=== "Kotlin"
```kotlin title="list.kt"
-
+ /* Concatenate two lists */
+ val nums1 = intArrayOf(6, 8, 7, 10, 9).toMutableList()
+ nums.addAll(nums1) // Concatenate list nums1 to the end of nums
```
-=== "Zig"
+=== "Ruby"
- ```zig title="list.zig"
- // Concatenate two lists
- var nums1 = std.ArrayList(i32).init(std.heap.page_allocator);
- defer nums1.deinit();
- try nums1.appendSlice(&[_]i32{ 6, 8, 7, 10, 9 });
- try nums.insertSlice(nums.items.len, nums1.items); // Concatenate nums1 to the end of nums
+ ```ruby title="list.rb"
+ # Concatenate two lists
+ nums1 = [6, 8, 7, 10, 9]
+ nums += nums1
```
-### 6. Sorting the list
+??? pythontutor "Code Visualization"
-Once the list is sorted, we can employ algorithms commonly used in array-related algorithm problems, such as "binary search" and "two-pointer" algorithms.
+
+
+
+### 6. Sort a List
+
+After sorting a list, we can use "binary search" and "two-pointer" algorithms, which are frequently tested in array algorithm problems.
=== "Python"
```python title="list.py"
- # Sort the list
- nums.sort() # After sorting, the list elements are in ascending order
+ # Sort a list
+ nums.sort() # After sorting, list elements are arranged from smallest to largest
```
=== "C++"
```cpp title="list.cpp"
- /* Sort the list */
- sort(nums.begin(), nums.end()); // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ sort(nums.begin(), nums.end()); // After sorting, list elements are arranged from smallest to largest
```
=== "Java"
```java title="list.java"
- /* Sort the list */
- Collections.sort(nums); // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ Collections.sort(nums); // After sorting, list elements are arranged from smallest to largest
```
=== "C#"
```csharp title="list.cs"
- /* Sort the list */
- nums.Sort(); // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ nums.Sort(); // After sorting, list elements are arranged from smallest to largest
```
=== "Go"
```go title="list_test.go"
- /* Sort the list */
- sort.Ints(nums) // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ sort.Ints(nums) // After sorting, list elements are arranged from smallest to largest
```
=== "Swift"
```swift title="list.swift"
- /* Sort the list */
- nums.sort() // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ nums.sort() // After sorting, list elements are arranged from smallest to largest
```
=== "JS"
```javascript title="list.js"
- /* Sort the list */
- nums.sort((a, b) => a - b); // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ nums.sort((a, b) => a - b); // After sorting, list elements are arranged from smallest to largest
```
=== "TS"
```typescript title="list.ts"
- /* Sort the list */
- nums.sort((a, b) => a - b); // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ nums.sort((a, b) => a - b); // After sorting, list elements are arranged from smallest to largest
```
=== "Dart"
```dart title="list.dart"
- /* Sort the list */
- nums.sort(); // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ nums.sort(); // After sorting, list elements are arranged from smallest to largest
```
=== "Rust"
```rust title="list.rs"
- /* Sort the list */
- nums.sort(); // After sorting, the list elements are in ascending order
+ /* Sort a list */
+ nums.sort(); // After sorting, list elements are arranged from smallest to largest
```
=== "C"
@@ -885,25 +940,31 @@ Once the list is sorted, we can employ algorithms commonly used in array-related
=== "Kotlin"
```kotlin title="list.kt"
-
+ /* Sort a list */
+ nums.sort() // After sorting, list elements are arranged from smallest to largest
```
-=== "Zig"
+=== "Ruby"
- ```zig title="list.zig"
- // Sort the list
- std.sort.sort(i32, nums.items, {}, comptime std.sort.asc(i32));
+ ```ruby title="list.rb"
+ # Sort a list
+ nums = nums.sort { |a, b| a <=> b } # After sorting, list elements are arranged from smallest to largest
```
-## 4.3.2 List implementation
+??? pythontutor "Code Visualization"
-Many programming languages come with built-in lists, including Java, C++, Python, etc. Their implementations tend to be intricate, featuring carefully considered settings for various parameters, like initial capacity and expansion factors. Readers who are curious can delve into the source code for further learning.
+
+
-To enhance our understanding of how lists work, we will attempt to implement a simplified version of a list, focusing on three crucial design aspects:
+## 4.3.2 List Implementation
-- **Initial capacity**: Choose a reasonable initial capacity for the array. In this example, we choose 10 as the initial capacity.
-- **Size recording**: Declare a variable `size` to record the current number of elements in the list, updating in real-time with element insertion and deletion. With this variable, we can locate the end of the list and determine whether expansion is needed.
-- **Expansion mechanism**: If the list reaches full capacity upon an element insertion, an expansion process is required. This involves creating a larger array based on the expansion factor, and then transferring all elements from the current array to the new one. In this example, we stipulate that the array size should double with each expansion.
+Many programming languages have built-in lists, such as Java, C++, and Python. Their implementations are quite complex, and the parameters are carefully considered, such as initial capacity, expansion multiples, and so on. Interested readers can consult the source code to learn more.
+
+To deepen our understanding of how lists work, we attempt to implement a simple list with three key design considerations:
+
+- **Initial capacity**: Select a reasonable initial capacity for the underlying array. In this example, we choose 10 as the initial capacity.
+- **Size tracking**: Declare a variable `size` to record the current number of elements in the list and update it in real-time as elements are inserted and deleted. Based on this variable, we can locate the end of the list and determine whether expansion is needed.
+- **Expansion mechanism**: When the list capacity is full upon inserting an element, we need to expand. We create a larger array based on the expansion multiple and then move all elements from the current array to the new array in order. In this example, we specify that the array should be expanded to 2 times its previous size each time.
=== "Python"
@@ -916,7 +977,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
self._capacity: int = 10 # List capacity
self._arr: list[int] = [0] * self._capacity # Array (stores list elements)
self._size: int = 0 # List length (current number of elements)
- self._extend_ratio: int = 2 # Multiple for each list expansion
+ self._extend_ratio: int = 2 # Multiple by which the list capacity is extended each time
def size(self) -> int:
"""Get list length (current number of elements)"""
@@ -941,7 +1002,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
def add(self, num: int):
"""Add element at the end"""
- # When the number of elements exceeds capacity, trigger the expansion mechanism
+ # When the number of elements exceeds capacity, trigger the extension mechanism
if self.size() == self.capacity():
self.extend_capacity()
self._arr[self._size] = num
@@ -951,10 +1012,10 @@ To enhance our understanding of how lists work, we will attempt to implement a s
"""Insert element in the middle"""
if index < 0 or index >= self._size:
raise IndexError("Index out of bounds")
- # When the number of elements exceeds capacity, trigger the expansion mechanism
+ # When the number of elements exceeds capacity, trigger the extension mechanism
if self._size == self.capacity():
self.extend_capacity()
- # Move all elements after `index` one position backward
+ # Move all elements at and after index index backward by one position
for j in range(self._size - 1, index - 1, -1):
self._arr[j + 1] = self._arr[j]
self._arr[index] = num
@@ -966,7 +1027,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
if index < 0 or index >= self._size:
raise IndexError("Index out of bounds")
num = self._arr[index]
- # Move all elements after `index` one position forward
+ # Move all elements after index index forward by one position
for j in range(index, self._size - 1):
self._arr[j] = self._arr[j + 1]
# Update the number of elements
@@ -975,14 +1036,14 @@ To enhance our understanding of how lists work, we will attempt to implement a s
return num
def extend_capacity(self):
- """Extend list"""
- # Create a new array of _extend_ratio times the length of the original array and copy the original array to the new array
+ """Extend list capacity"""
+ # Create a new array with length _extend_ratio times the original array, and copy the original array to the new array
self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1)
# Update list capacity
self._capacity = len(self._arr)
def to_array(self) -> list[int]:
- """Return a list of valid lengths"""
+ """Return list with valid length"""
return self._arr[: self._size]
```
@@ -995,7 +1056,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
int *arr; // Array (stores list elements)
int arrCapacity = 10; // List capacity
int arrSize = 0; // List length (current number of elements)
- int extendRatio = 2; // Multiple for each list expansion
+ int extendRatio = 2; // Multiple by which the list capacity is extended each time
public:
/* Constructor */
@@ -1018,7 +1079,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
return arrCapacity;
}
- /* Access element */
+ /* Update element */
int get(int index) {
// If the index is out of bounds, throw an exception, as below
if (index < 0 || index >= size())
@@ -1026,16 +1087,16 @@ To enhance our understanding of how lists work, we will attempt to implement a s
return arr[index];
}
- /* Update element */
+ /* Add elements at the end */
void set(int index, int num) {
if (index < 0 || index >= size())
throw out_of_range("Index out of bounds");
arr[index] = num;
}
- /* Add element at the end */
+ /* Direct traversal of list elements */
void add(int num) {
- // When the number of elements exceeds capacity, trigger the expansion mechanism
+ // When the number of elements exceeds capacity, trigger the extension mechanism
if (size() == capacity())
extendCapacity();
arr[size()] = num;
@@ -1043,14 +1104,14 @@ To enhance our understanding of how lists work, we will attempt to implement a s
arrSize++;
}
- /* Insert element in the middle */
+ /* Sort list */
void insert(int index, int num) {
if (index < 0 || index >= size())
throw out_of_range("Index out of bounds");
- // When the number of elements exceeds capacity, trigger the expansion mechanism
+ // When the number of elements exceeds capacity, trigger the extension mechanism
if (size() == capacity())
extendCapacity();
- // Move all elements after `index` one position backward
+ // Move all elements after index index forward by one position
for (int j = size() - 1; j >= index; j--) {
arr[j + 1] = arr[j];
}
@@ -1064,7 +1125,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
if (index < 0 || index >= size())
throw out_of_range("Index out of bounds");
int num = arr[index];
- // Move all elements after `index` one position forward
+ // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array
for (int j = index; j < size() - 1; j++) {
arr[j] = arr[j + 1];
}
@@ -1074,9 +1135,9 @@ To enhance our understanding of how lists work, we will attempt to implement a s
return num;
}
- /* Extend list */
+ /* Driver Code */
void extendCapacity() {
- // Create a new array with a length multiple of the original array by extendRatio
+ // Create a new array with length extendRatio times the original array
int newCapacity = capacity() * extendRatio;
int *tmp = arr;
arr = new int[newCapacity];
@@ -1089,9 +1150,9 @@ To enhance our understanding of how lists work, we will attempt to implement a s
arrCapacity = newCapacity;
}
- /* Convert the list to a Vector for printing */
+ /* Convert list to Vector for printing */
vector toVector() {
- // Only convert elements within valid length range
+ // Elements enqueue
vector vec(size());
for (int i = 0; i < size(); i++) {
vec[i] = arr[i];
@@ -1109,7 +1170,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
private int[] arr; // Array (stores list elements)
private int capacity = 10; // List capacity
private int size = 0; // List length (current number of elements)
- private int extendRatio = 2; // Multiple for each list expansion
+ private int extendRatio = 2; // Multiple by which the list capacity is extended each time
/* Constructor */
public MyList() {
@@ -1126,7 +1187,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
return capacity;
}
- /* Access element */
+ /* Update element */
public int get(int index) {
// If the index is out of bounds, throw an exception, as below
if (index < 0 || index >= size)
@@ -1134,16 +1195,16 @@ To enhance our understanding of how lists work, we will attempt to implement a s
return arr[index];
}
- /* Update element */
+ /* Add elements at the end */
public void set(int index, int num) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index out of bounds");
arr[index] = num;
}
- /* Add element at the end */
+ /* Direct traversal of list elements */
public void add(int num) {
- // When the number of elements exceeds capacity, trigger the expansion mechanism
+ // When the number of elements exceeds capacity, trigger the extension mechanism
if (size == capacity())
extendCapacity();
arr[size] = num;
@@ -1151,14 +1212,14 @@ To enhance our understanding of how lists work, we will attempt to implement a s
size++;
}
- /* Insert element in the middle */
+ /* Sort list */
public void insert(int index, int num) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index out of bounds");
- // When the number of elements exceeds capacity, trigger the expansion mechanism
+ // When the number of elements exceeds capacity, trigger the extension mechanism
if (size == capacity())
extendCapacity();
- // Move all elements after `index` one position backward
+ // Move all elements after index index forward by one position
for (int j = size - 1; j >= index; j--) {
arr[j + 1] = arr[j];
}
@@ -1172,7 +1233,7 @@ To enhance our understanding of how lists work, we will attempt to implement a s
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index out of bounds");
int num = arr[index];
- // Move all elements after `index` one position forward
+ // Move all elements after index forward by one position
for (int j = index; j < size - 1; j++) {
arr[j] = arr[j + 1];
}
@@ -1182,18 +1243,18 @@ To enhance our understanding of how lists work, we will attempt to implement a s
return num;
}
- /* Extend list */
+ /* Driver Code */
public void extendCapacity() {
- // Create a new array with a length multiple of the original array by extendRatio, and copy the original array to the new array
+ // Create a new array with length extendRatio times the original array and copy the original array to the new array
arr = Arrays.copyOf(arr, capacity() * extendRatio);
- // Update list capacity
+ // Add elements at the end
capacity = arr.length;
}
- /* Convert the list to an array */
+ /* Convert list to array */
public int[] toArray() {
int size = size();
- // Only convert elements within valid length range
+ // Elements enqueue
int[] arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = get(i);
@@ -1206,65 +1267,1030 @@ To enhance our understanding of how lists work, we will attempt to implement a s
=== "C#"
```csharp title="my_list.cs"
- [class]{MyList}-[func]{}
+ /* List class */
+ class MyList {
+ private int[] arr; // Array (stores list elements)
+ private int arrCapacity = 10; // List capacity
+ private int arrSize = 0; // List length (current number of elements)
+ private readonly int extendRatio = 2; // Multiple by which the list capacity is extended each time
+
+ /* Constructor */
+ public MyList() {
+ arr = new int[arrCapacity];
+ }
+
+ /* Get list length (current number of elements) */
+ public int Size() {
+ return arrSize;
+ }
+
+ /* Get list capacity */
+ public int Capacity() {
+ return arrCapacity;
+ }
+
+ /* Update element */
+ public int Get(int index) {
+ // If the index is out of bounds, throw an exception, as below
+ if (index < 0 || index >= arrSize)
+ throw new IndexOutOfRangeException("Index out of bounds");
+ return arr[index];
+ }
+
+ /* Add elements at the end */
+ public void Set(int index, int num) {
+ if (index < 0 || index >= arrSize)
+ throw new IndexOutOfRangeException("Index out of bounds");
+ arr[index] = num;
+ }
+
+ /* Direct traversal of list elements */
+ public void Add(int num) {
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (arrSize == arrCapacity)
+ ExtendCapacity();
+ arr[arrSize] = num;
+ // Update the number of elements
+ arrSize++;
+ }
+
+ /* Sort list */
+ public void Insert(int index, int num) {
+ if (index < 0 || index >= arrSize)
+ throw new IndexOutOfRangeException("Index out of bounds");
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (arrSize == arrCapacity)
+ ExtendCapacity();
+ // Move all elements after index index forward by one position
+ for (int j = arrSize - 1; j >= index; j--) {
+ arr[j + 1] = arr[j];
+ }
+ arr[index] = num;
+ // Update the number of elements
+ arrSize++;
+ }
+
+ /* Remove element */
+ public int Remove(int index) {
+ if (index < 0 || index >= arrSize)
+ throw new IndexOutOfRangeException("Index out of bounds");
+ int num = arr[index];
+ // Move all elements after index forward by one position
+ for (int j = index; j < arrSize - 1; j++) {
+ arr[j] = arr[j + 1];
+ }
+ // Update the number of elements
+ arrSize--;
+ // Return the removed element
+ return num;
+ }
+
+ /* Driver Code */
+ public void ExtendCapacity() {
+ // Create new array of length arrCapacity * extendRatio and copy original array to new array
+ Array.Resize(ref arr, arrCapacity * extendRatio);
+ // Add elements at the end
+ arrCapacity = arr.Length;
+ }
+
+ /* Convert list to array */
+ public int[] ToArray() {
+ // Elements enqueue
+ int[] arr = new int[arrSize];
+ for (int i = 0; i < arrSize; i++) {
+ arr[i] = Get(i);
+ }
+ return arr;
+ }
+ }
```
=== "Go"
```go title="my_list.go"
- [class]{myList}-[func]{}
+ /* List class */
+ type myList struct {
+ arrCapacity int
+ arr []int
+ arrSize int
+ extendRatio int
+ }
+
+ /* Constructor */
+ func newMyList() *myList {
+ return &myList{
+ arrCapacity: 10, // List capacity
+ arr: make([]int, 10), // Array (stores list elements)
+ arrSize: 0, // List length (current number of elements)
+ extendRatio: 2, // Multiple by which the list capacity is extended each time
+ }
+ }
+
+ /* Get list length (current number of elements) */
+ func (l *myList) size() int {
+ return l.arrSize
+ }
+
+ /* Get list capacity */
+ func (l *myList) capacity() int {
+ return l.arrCapacity
+ }
+
+ /* Update element */
+ func (l *myList) get(index int) int {
+ // If the index is out of bounds, throw an exception, as below
+ if index < 0 || index >= l.arrSize {
+ panic("Index out of bounds")
+ }
+ return l.arr[index]
+ }
+
+ /* Add elements at the end */
+ func (l *myList) set(num, index int) {
+ if index < 0 || index >= l.arrSize {
+ panic("Index out of bounds")
+ }
+ l.arr[index] = num
+ }
+
+ /* Direct traversal of list elements */
+ func (l *myList) add(num int) {
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if l.arrSize == l.arrCapacity {
+ l.extendCapacity()
+ }
+ l.arr[l.arrSize] = num
+ // Update the number of elements
+ l.arrSize++
+ }
+
+ /* Sort list */
+ func (l *myList) insert(num, index int) {
+ if index < 0 || index >= l.arrSize {
+ panic("Index out of bounds")
+ }
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if l.arrSize == l.arrCapacity {
+ l.extendCapacity()
+ }
+ // Move all elements after index index forward by one position
+ for j := l.arrSize - 1; j >= index; j-- {
+ l.arr[j+1] = l.arr[j]
+ }
+ l.arr[index] = num
+ // Update the number of elements
+ l.arrSize++
+ }
+
+ /* Remove element */
+ func (l *myList) remove(index int) int {
+ if index < 0 || index >= l.arrSize {
+ panic("Index out of bounds")
+ }
+ num := l.arr[index]
+ // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array
+ for j := index; j < l.arrSize-1; j++ {
+ l.arr[j] = l.arr[j+1]
+ }
+ // Update the number of elements
+ l.arrSize--
+ // Return the removed element
+ return num
+ }
+
+ /* Driver Code */
+ func (l *myList) extendCapacity() {
+ // Create a new array with length extendRatio times the original array and copy the original array to the new array
+ l.arr = append(l.arr, make([]int, l.arrCapacity*(l.extendRatio-1))...)
+ // Add elements at the end
+ l.arrCapacity = len(l.arr)
+ }
+
+ /* Return list with valid length */
+ func (l *myList) toArray() []int {
+ // Elements enqueue
+ return l.arr[:l.arrSize]
+ }
```
=== "Swift"
```swift title="my_list.swift"
- [class]{MyList}-[func]{}
+ /* List class */
+ class MyList {
+ private var arr: [Int] // Array (stores list elements)
+ private var _capacity: Int // List capacity
+ private var _size: Int // List length (current number of elements)
+ private let extendRatio: Int // Multiple by which the list capacity is extended each time
+
+ /* Constructor */
+ init() {
+ _capacity = 10
+ _size = 0
+ extendRatio = 2
+ arr = Array(repeating: 0, count: _capacity)
+ }
+
+ /* Get list length (current number of elements) */
+ func size() -> Int {
+ _size
+ }
+
+ /* Get list capacity */
+ func capacity() -> Int {
+ _capacity
+ }
+
+ /* Update element */
+ func get(index: Int) -> Int {
+ // Throw error if index out of bounds, same below
+ if index < 0 || index >= size() {
+ fatalError("Index out of bounds")
+ }
+ return arr[index]
+ }
+
+ /* Add elements at the end */
+ func set(index: Int, num: Int) {
+ if index < 0 || index >= size() {
+ fatalError("Index out of bounds")
+ }
+ arr[index] = num
+ }
+
+ /* Direct traversal of list elements */
+ func add(num: Int) {
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if size() == capacity() {
+ extendCapacity()
+ }
+ arr[size()] = num
+ // Update the number of elements
+ _size += 1
+ }
+
+ /* Sort list */
+ func insert(index: Int, num: Int) {
+ if index < 0 || index >= size() {
+ fatalError("Index out of bounds")
+ }
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if size() == capacity() {
+ extendCapacity()
+ }
+ // Move all elements after index index forward by one position
+ for j in (index ..< size()).reversed() {
+ arr[j + 1] = arr[j]
+ }
+ arr[index] = num
+ // Update the number of elements
+ _size += 1
+ }
+
+ /* Remove element */
+ @discardableResult
+ func remove(index: Int) -> Int {
+ if index < 0 || index >= size() {
+ fatalError("Index out of bounds")
+ }
+ let num = arr[index]
+ // Move all elements after index forward by one position
+ for j in index ..< (size() - 1) {
+ arr[j] = arr[j + 1]
+ }
+ // Update the number of elements
+ _size -= 1
+ // Return the removed element
+ return num
+ }
+
+ /* Driver Code */
+ func extendCapacity() {
+ // Create a new array with length extendRatio times the original array and copy the original array to the new array
+ arr = arr + Array(repeating: 0, count: capacity() * (extendRatio - 1))
+ // Add elements at the end
+ _capacity = arr.count
+ }
+
+ /* Convert list to array */
+ func toArray() -> [Int] {
+ Array(arr.prefix(size()))
+ }
+ }
```
=== "JS"
```javascript title="my_list.js"
- [class]{MyList}-[func]{}
+ /* List class */
+ class MyList {
+ #arr = new Array(); // Array (stores list elements)
+ #capacity = 10; // List capacity
+ #size = 0; // List length (current number of elements)
+ #extendRatio = 2; // Multiple by which the list capacity is extended each time
+
+ /* Constructor */
+ constructor() {
+ this.#arr = new Array(this.#capacity);
+ }
+
+ /* Get list length (current number of elements) */
+ size() {
+ return this.#size;
+ }
+
+ /* Get list capacity */
+ capacity() {
+ return this.#capacity;
+ }
+
+ /* Update element */
+ get(index) {
+ // If the index is out of bounds, throw an exception, as below
+ if (index < 0 || index >= this.#size) throw new Error('Index out of bounds');
+ return this.#arr[index];
+ }
+
+ /* Add elements at the end */
+ set(index, num) {
+ if (index < 0 || index >= this.#size) throw new Error('Index out of bounds');
+ this.#arr[index] = num;
+ }
+
+ /* Direct traversal of list elements */
+ add(num) {
+ // If length equals capacity, need to expand
+ if (this.#size === this.#capacity) {
+ this.extendCapacity();
+ }
+ // Add new element to end of list
+ this.#arr[this.#size] = num;
+ this.#size++;
+ }
+
+ /* Sort list */
+ insert(index, num) {
+ if (index < 0 || index >= this.#size) throw new Error('Index out of bounds');
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (this.#size === this.#capacity) {
+ this.extendCapacity();
+ }
+ // Move all elements after index index forward by one position
+ for (let j = this.#size - 1; j >= index; j--) {
+ this.#arr[j + 1] = this.#arr[j];
+ }
+ // Update the number of elements
+ this.#arr[index] = num;
+ this.#size++;
+ }
+
+ /* Remove element */
+ remove(index) {
+ if (index < 0 || index >= this.#size) throw new Error('Index out of bounds');
+ let num = this.#arr[index];
+ // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array
+ for (let j = index; j < this.#size - 1; j++) {
+ this.#arr[j] = this.#arr[j + 1];
+ }
+ // Update the number of elements
+ this.#size--;
+ // Return the removed element
+ return num;
+ }
+
+ /* Driver Code */
+ extendCapacity() {
+ // Create a new array with length extendRatio times the original array and copy the original array to the new array
+ this.#arr = this.#arr.concat(
+ new Array(this.capacity() * (this.#extendRatio - 1))
+ );
+ // Add elements at the end
+ this.#capacity = this.#arr.length;
+ }
+
+ /* Convert list to array */
+ toArray() {
+ let size = this.size();
+ // Elements enqueue
+ const arr = new Array(size);
+ for (let i = 0; i < size; i++) {
+ arr[i] = this.get(i);
+ }
+ return arr;
+ }
+ }
```
=== "TS"
```typescript title="my_list.ts"
- [class]{MyList}-[func]{}
+ /* List class */
+ class MyList {
+ private arr: Array; // Array (stores list elements)
+ private _capacity: number = 10; // List capacity
+ private _size: number = 0; // List length (current number of elements)
+ private extendRatio: number = 2; // Multiple by which the list capacity is extended each time
+
+ /* Constructor */
+ constructor() {
+ this.arr = new Array(this._capacity);
+ }
+
+ /* Get list length (current number of elements) */
+ public size(): number {
+ return this._size;
+ }
+
+ /* Get list capacity */
+ public capacity(): number {
+ return this._capacity;
+ }
+
+ /* Update element */
+ public get(index: number): number {
+ // If the index is out of bounds, throw an exception, as below
+ if (index < 0 || index >= this._size) throw new Error('Index out of bounds');
+ return this.arr[index];
+ }
+
+ /* Add elements at the end */
+ public set(index: number, num: number): void {
+ if (index < 0 || index >= this._size) throw new Error('Index out of bounds');
+ this.arr[index] = num;
+ }
+
+ /* Direct traversal of list elements */
+ public add(num: number): void {
+ // If length equals capacity, need to expand
+ if (this._size === this._capacity) this.extendCapacity();
+ // Add new element to end of list
+ this.arr[this._size] = num;
+ this._size++;
+ }
+
+ /* Sort list */
+ public insert(index: number, num: number): void {
+ if (index < 0 || index >= this._size) throw new Error('Index out of bounds');
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (this._size === this._capacity) {
+ this.extendCapacity();
+ }
+ // Move all elements after index index forward by one position
+ for (let j = this._size - 1; j >= index; j--) {
+ this.arr[j + 1] = this.arr[j];
+ }
+ // Update the number of elements
+ this.arr[index] = num;
+ this._size++;
+ }
+
+ /* Remove element */
+ public remove(index: number): number {
+ if (index < 0 || index >= this._size) throw new Error('Index out of bounds');
+ let num = this.arr[index];
+ // Move all elements after index forward by one position
+ for (let j = index; j < this._size - 1; j++) {
+ this.arr[j] = this.arr[j + 1];
+ }
+ // Update the number of elements
+ this._size--;
+ // Return the removed element
+ return num;
+ }
+
+ /* Driver Code */
+ public extendCapacity(): void {
+ // Create new array of length size and copy original array to new array
+ this.arr = this.arr.concat(
+ new Array(this.capacity() * (this.extendRatio - 1))
+ );
+ // Add elements at the end
+ this._capacity = this.arr.length;
+ }
+
+ /* Convert list to array */
+ public toArray(): number[] {
+ let size = this.size();
+ // Elements enqueue
+ const arr = new Array(size);
+ for (let i = 0; i < size; i++) {
+ arr[i] = this.get(i);
+ }
+ return arr;
+ }
+ }
```
=== "Dart"
```dart title="my_list.dart"
- [class]{MyList}-[func]{}
+ /* List class */
+ class MyList {
+ late List _arr; // Array (stores list elements)
+ int _capacity = 10; // List capacity
+ int _size = 0; // List length (current number of elements)
+ int _extendRatio = 2; // Multiple by which the list capacity is extended each time
+
+ /* Constructor */
+ MyList() {
+ _arr = List.filled(_capacity, 0);
+ }
+
+ /* Get list length (current number of elements) */
+ int size() => _size;
+
+ /* Get list capacity */
+ int capacity() => _capacity;
+
+ /* Update element */
+ int get(int index) {
+ if (index >= _size) throw RangeError('Index out of bounds');
+ return _arr[index];
+ }
+
+ /* Add elements at the end */
+ void set(int index, int _num) {
+ if (index >= _size) throw RangeError('Index out of bounds');
+ _arr[index] = _num;
+ }
+
+ /* Direct traversal of list elements */
+ void add(int _num) {
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (_size == _capacity) extendCapacity();
+ _arr[_size] = _num;
+ // Update the number of elements
+ _size++;
+ }
+
+ /* Sort list */
+ void insert(int index, int _num) {
+ if (index >= _size) throw RangeError('Index out of bounds');
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (_size == _capacity) extendCapacity();
+ // Move all elements after index index forward by one position
+ for (var j = _size - 1; j >= index; j--) {
+ _arr[j + 1] = _arr[j];
+ }
+ _arr[index] = _num;
+ // Update the number of elements
+ _size++;
+ }
+
+ /* Remove element */
+ int remove(int index) {
+ if (index >= _size) throw RangeError('Index out of bounds');
+ int _num = _arr[index];
+ // Move all elements after index forward by one position
+ for (var j = index; j < _size - 1; j++) {
+ _arr[j] = _arr[j + 1];
+ }
+ // Update the number of elements
+ _size--;
+ // Return the removed element
+ return _num;
+ }
+
+ /* Driver Code */
+ void extendCapacity() {
+ // Create new array with length _extendRatio times original array
+ final _newNums = List.filled(_capacity * _extendRatio, 0);
+ // Copy original array to new array
+ List.copyRange(_newNums, 0, _arr);
+ // Update _arr reference
+ _arr = _newNums;
+ // Add elements at the end
+ _capacity = _arr.length;
+ }
+
+ /* Convert list to array */
+ List toArray() {
+ List arr = [];
+ for (var i = 0; i < _size; i++) {
+ arr.add(get(i));
+ }
+ return arr;
+ }
+ }
```
=== "Rust"
```rust title="my_list.rs"
- [class]{MyList}-[func]{}
+ /* List class */
+ #[allow(dead_code)]
+ struct MyList {
+ arr: Vec, // Array (stores list elements)
+ capacity: usize, // List capacity
+ size: usize, // List length (current number of elements)
+ extend_ratio: usize, // Multiple by which the list capacity is extended each time
+ }
+
+ #[allow(unused, unused_comparisons)]
+ impl MyList {
+ /* Constructor */
+ pub fn new(capacity: usize) -> Self {
+ let mut vec = vec![0; capacity];
+ Self {
+ arr: vec,
+ capacity,
+ size: 0,
+ extend_ratio: 2,
+ }
+ }
+
+ /* Get list length (current number of elements) */
+ pub fn size(&self) -> usize {
+ return self.size;
+ }
+
+ /* Get list capacity */
+ pub fn capacity(&self) -> usize {
+ return self.capacity;
+ }
+
+ /* Update element */
+ pub fn get(&self, index: usize) -> i32 {
+ // If the index is out of bounds, throw an exception, as below
+ if index >= self.size {
+ panic!("Index out of bounds")
+ };
+ return self.arr[index];
+ }
+
+ /* Add elements at the end */
+ pub fn set(&mut self, index: usize, num: i32) {
+ if index >= self.size {
+ panic!("Index out of bounds")
+ };
+ self.arr[index] = num;
+ }
+
+ /* Direct traversal of list elements */
+ pub fn add(&mut self, num: i32) {
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if self.size == self.capacity() {
+ self.extend_capacity();
+ }
+ self.arr[self.size] = num;
+ // Update the number of elements
+ self.size += 1;
+ }
+
+ /* Sort list */
+ pub fn insert(&mut self, index: usize, num: i32) {
+ if index >= self.size() {
+ panic!("Index out of bounds")
+ };
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if self.size == self.capacity() {
+ self.extend_capacity();
+ }
+ // Move all elements after index index forward by one position
+ for j in (index..self.size).rev() {
+ self.arr[j + 1] = self.arr[j];
+ }
+ self.arr[index] = num;
+ // Update the number of elements
+ self.size += 1;
+ }
+
+ /* Remove element */
+ pub fn remove(&mut self, index: usize) -> i32 {
+ if index >= self.size() {
+ panic!("Index out of bounds")
+ };
+ let num = self.arr[index];
+ // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array
+ for j in index..self.size - 1 {
+ self.arr[j] = self.arr[j + 1];
+ }
+ // Update the number of elements
+ self.size -= 1;
+ // Return the removed element
+ return num;
+ }
+
+ /* Driver Code */
+ pub fn extend_capacity(&mut self) {
+ // Create new array with length extend_ratio times original, copy original array to new array
+ let new_capacity = self.capacity * self.extend_ratio;
+ self.arr.resize(new_capacity, 0);
+ // Add elements at the end
+ self.capacity = new_capacity;
+ }
+
+ /* Convert list to array */
+ pub fn to_array(&self) -> Vec {
+ // Elements enqueue
+ let mut arr = Vec::new();
+ for i in 0..self.size {
+ arr.push(self.get(i));
+ }
+ arr
+ }
+ }
```
=== "C"
```c title="my_list.c"
- [class]{MyList}-[func]{}
+ /* List class */
+ typedef struct {
+ int *arr; // Array (stores list elements)
+ int capacity; // List capacity
+ int size; // List size
+ int extendRatio; // List expansion multiplier
+ } MyList;
+
+ /* Constructor */
+ MyList *newMyList() {
+ MyList *nums = malloc(sizeof(MyList));
+ nums->capacity = 10;
+ nums->arr = malloc(sizeof(int) * nums->capacity);
+ nums->size = 0;
+ nums->extendRatio = 2;
+ return nums;
+ }
+
+ /* Destructor */
+ void delMyList(MyList *nums) {
+ free(nums->arr);
+ free(nums);
+ }
+
+ /* Get list length */
+ int size(MyList *nums) {
+ return nums->size;
+ }
+
+ /* Get list capacity */
+ int capacity(MyList *nums) {
+ return nums->capacity;
+ }
+
+ /* Update element */
+ int get(MyList *nums, int index) {
+ assert(index >= 0 && index < nums->size);
+ return nums->arr[index];
+ }
+
+ /* Add elements at the end */
+ void set(MyList *nums, int index, int num) {
+ assert(index >= 0 && index < nums->size);
+ nums->arr[index] = num;
+ }
+
+ /* Direct traversal of list elements */
+ void add(MyList *nums, int num) {
+ if (size(nums) == capacity(nums)) {
+ extendCapacity(nums); // Expand capacity
+ }
+ nums->arr[size(nums)] = num;
+ nums->size++;
+ }
+
+ /* Sort list */
+ void insert(MyList *nums, int index, int num) {
+ assert(index >= 0 && index < size(nums));
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (size(nums) == capacity(nums)) {
+ extendCapacity(nums); // Expand capacity
+ }
+ for (int i = size(nums); i > index; --i) {
+ nums->arr[i] = nums->arr[i - 1];
+ }
+ nums->arr[index] = num;
+ nums->size++;
+ }
+
+ /* Remove element */
+ // Note: stdio.h occupies the remove keyword
+ int removeItem(MyList *nums, int index) {
+ assert(index >= 0 && index < size(nums));
+ int num = nums->arr[index];
+ for (int i = index; i < size(nums) - 1; i++) {
+ nums->arr[i] = nums->arr[i + 1];
+ }
+ nums->size--;
+ return num;
+ }
+
+ /* Driver Code */
+ void extendCapacity(MyList *nums) {
+ // Allocate space first
+ int newCapacity = capacity(nums) * nums->extendRatio;
+ int *extend = (int *)malloc(sizeof(int) * newCapacity);
+ int *temp = nums->arr;
+
+ // Copy old data to new data
+ for (int i = 0; i < size(nums); i++)
+ extend[i] = nums->arr[i];
+
+ // Free old data
+ free(temp);
+
+ // Update new data
+ nums->arr = extend;
+ nums->capacity = newCapacity;
+ }
+
+ /* Convert list to Array for printing */
+ int *toArray(MyList *nums) {
+ return nums->arr;
+ }
```
=== "Kotlin"
```kotlin title="my_list.kt"
- [class]{MyList}-[func]{}
+ /* List class */
+ class MyList {
+ private var arr: IntArray = intArrayOf() // Array (stores list elements)
+ private var capacity: Int = 10 // List capacity
+ private var size: Int = 0 // List length (current number of elements)
+ private var extendRatio: Int = 2 // Multiple by which the list capacity is extended each time
+
+ /* Constructor */
+ init {
+ arr = IntArray(capacity)
+ }
+
+ /* Get list length (current number of elements) */
+ fun size(): Int {
+ return size
+ }
+
+ /* Get list capacity */
+ fun capacity(): Int {
+ return capacity
+ }
+
+ /* Update element */
+ fun get(index: Int): Int {
+ // If the index is out of bounds, throw an exception, as below
+ if (index < 0 || index >= size)
+ throw IndexOutOfBoundsException("Index out of bounds")
+ return arr[index]
+ }
+
+ /* Add elements at the end */
+ fun set(index: Int, num: Int) {
+ if (index < 0 || index >= size)
+ throw IndexOutOfBoundsException("Index out of bounds")
+ arr[index] = num
+ }
+
+ /* Direct traversal of list elements */
+ fun add(num: Int) {
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (size == capacity())
+ extendCapacity()
+ arr[size] = num
+ // Update the number of elements
+ size++
+ }
+
+ /* Sort list */
+ fun insert(index: Int, num: Int) {
+ if (index < 0 || index >= size)
+ throw IndexOutOfBoundsException("Index out of bounds")
+ // When the number of elements exceeds capacity, trigger the extension mechanism
+ if (size == capacity())
+ extendCapacity()
+ // Move all elements after index index forward by one position
+ for (j in size - 1 downTo index)
+ arr[j + 1] = arr[j]
+ arr[index] = num
+ // Update the number of elements
+ size++
+ }
+
+ /* Remove element */
+ fun remove(index: Int): Int {
+ if (index < 0 || index >= size)
+ throw IndexOutOfBoundsException("Index out of bounds")
+ val num = arr[index]
+ // Move all elements after index forward by one position
+ for (j in index..= size
+ @arr[index]
+ end
+
+ ### Access element ###
+ def set(index, num)
+ raise IndexError, "Index out of bounds" if index < 0 || index >= size
+ @arr[index] = num
+ end
+
+ ### Add element at end ###
+ def add(num)
+ # When the number of elements exceeds capacity, trigger the extension mechanism
+ extend_capacity if size == capacity
+ @arr[size] = num
+
+ # Update the number of elements
+ @size += 1
+ end
+
+ ### Insert element in middle ###
+ def insert(index, num)
+ raise IndexError, "Index out of bounds" if index < 0 || index >= size
+
+ # When the number of elements exceeds capacity, trigger the extension mechanism
+ extend_capacity if size == capacity
+
+ # Move all elements after index index forward by one position
+ for j in (size - 1).downto(index)
+ @arr[j + 1] = @arr[j]
+ end
+ @arr[index] = num
+
+ # Update the number of elements
+ @size += 1
+ end
+
+ ### Delete element ###
+ def remove(index)
+ raise IndexError, "Index out of bounds" if index < 0 || index >= size
+ num = @arr[index]
+
+ # Move all elements after index forward by one position
+ for j in index...size
+ @arr[j] = @arr[j + 1]
+ end
+
+ # Update the number of elements
+ @size -= 1
+
+ # Return the removed element
+ num
+ end
+
+ ### Expand list capacity ###
+ def extend_capacity
+ # Create new array with length extend_ratio times original, copy original array to new array
+ arr = @arr.dup + Array.new(capacity * (@extend_ratio - 1))
+ # Add elements at the end
+ @capacity = arr.length
+ end
+
+ ### Convert list to array ###
+ def to_array
+ sz = size
+ # Elements enqueue
+ arr = Array.new(sz)
+ for i in 0...sz
+ arr[i] = get(i)
+ end
+ arr
+ end
+ end
```
diff --git a/en/docs/chapter_array_and_linkedlist/ram_and_cache.md b/en/docs/chapter_array_and_linkedlist/ram_and_cache.md
index 031a451a0..50be50078 100644
--- a/en/docs/chapter_array_and_linkedlist/ram_and_cache.md
+++ b/en/docs/chapter_array_and_linkedlist/ram_and_cache.md
@@ -2,82 +2,82 @@
comments: true
---
-# 4.4 Memory and cache *
+# 4.4 Random-Access Memory and Cache *
-In the first two sections of this chapter, we explored arrays and linked lists, two fundamental data structures that represent "continuous storage" and "dispersed storage," respectively.
+In the first two sections of this chapter, we explored arrays and linked lists, two fundamental and important data structures that represent "contiguous storage" and "distributed storage" as two physical structures, respectively.
-In fact, **the physical structure largely determines how efficiently a program utilizes memory and cache**, which in turn affects the overall performance of the algorithm.
+In fact, **physical structure largely determines the efficiency with which programs utilize memory and cache**, which in turn affects the overall performance of algorithmic programs.
-## 4.4.1 Computer storage devices
+## 4.4.1 Computer Storage Devices
-There are three types of storage devices in computers: hard disk, random-access memory (RAM), and cache memory. The following table shows their respective roles and performance characteristics in computer systems.
+Computers include three types of storage devices: hard disk, random-access memory (RAM), and cache memory. The following table shows their different roles and performance characteristics in a computer system.
-
Table 4-2 Computer storage devices
+
Table 4-2 Computer Storage Devices
-| | Hard Disk | Memory | Cache |
-| ----------- | -------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
-| Usage | Long-term storage of data, including OS, programs, files, etc. | Temporary storage of currently running programs and data being processed | Stores frequently accessed data and instructions, reducing the number of CPU accesses to memory |
-| Volatility | Data is not lost after power off | Data is lost after power off | Data is lost after power off |
-| Capacity | Larger, TB level | Smaller, GB level | Very small, MB level |
-| Speed | Slower, several hundred to thousands MB/s | Faster, several tens of GB/s | Very fast, several tens to hundreds of GB/s |
-| Price (USD) | Cheaper, a few cents / GB | More expensive, a few dollars / GB | Very expensive, priced with CPU |
+| | Hard Disk | RAM | Cache |
+| -------------- | ------------------------------------------------------------- | ------------------------------------------------ | -------------------------------------------------------------- |
+| Purpose | Long-term storage of data, including operating systems, programs, and files | Temporary storage of currently running programs and data being processed | Storage of frequently accessed data and instructions to reduce CPU's accesses to memory |
+| Volatility | Data is not lost after power-off | Data is lost after power-off | Data is lost after power-off |
+| Capacity | Large, on the order of terabytes (TB) | Small, on the order of gigabytes (GB) | Very small, on the order of megabytes (MB) |
+| Speed | Slow, hundreds to thousands of MB/s | Fast, tens of GB/s | Very fast, tens to hundreds of GB/s |
+| Cost (USD/GB) | Inexpensive, fractions of a dollar to a few dollars per GB | Expensive, tens to hundreds of dollars per GB | Very expensive, priced as part of the CPU package |
-The computer storage system can be visualized as a pyramid, as shown in Figure 4-9. The storage devices at the top of the pyramid are faster, have smaller capacities, and are more expensive. This multi-level design is not accidental, but a deliberate outcome of careful consideration by computer scientists and engineers.
+We can imagine the computer storage system as a pyramid structure as shown in the diagram below. Storage devices closer to the top of the pyramid are faster, have smaller capacity, and are more expensive. This multi-layered design is not by accident, but rather the result of careful consideration by computer scientists and engineers.
-- **Replacing hard disks with memory is challenging**. Firstly, data in memory is lost after power off, making it unsuitable for long-term data storage; secondly, memory is significantly more expensive than hard disks, limiting its feasibility for widespread use in the consumer market.
-- **Caches face a trade-off between large capacity and high speed**. As the capacity of L1, L2, and L3 caches increases, their physical size grows, increasing the distance from the CPU core. This results in longer data transfer times and higher access latency. With current technology, a multi-level cache structure provides the optimal balance between capacity, speed, and cost.
+- **Hard disk cannot be easily replaced by RAM**. First, data in memory is lost after power-off, making it unsuitable for long-term data storage. Second, memory is tens of times more expensive than hard disk, which makes it difficult to popularize in the consumer market.
+- **Cache cannot simultaneously achieve large capacity and high speed**. As the capacity of L1, L2, and L3 caches increases, their physical size becomes larger, and the physical distance between them and the CPU core increases, resulting in longer data transmission time and higher element access latency. With current technology, the multi-layered cache structure represents the best balance point between capacity, speed, and cost.
-{ class="animation-figure" }
+{ class="animation-figure" }
-
Figure 4-9 Computer storage system
+
Figure 4-9 Computer Storage System
!!! tip
- The storage hierarchy in computers reflects a careful balance between speed, capacity, and cost. This type of trade-off is common across various industries, where finding the optimal balance between benefits and limitations is essential.
+ The storage hierarchy of computers embodies a delicate balance among speed, capacity, and cost. In fact, such trade-offs are common across all industrial fields, requiring us to find the optimal balance point between different advantages and constraints.
-Overall, **hard disks provide long-term storage for large volumes of data, memory serves as temporary storage for data being processed during program execution, and cache stores frequently accessed data and instructions to enhance execution efficiency**. Together, they ensure the efficient operation of computer systems.
+In summary, **hard disk is used for long-term storage of large amounts of data, RAM is used for temporary storage of data being processed during program execution, and cache is used for storage of frequently accessed data and instructions**, to improve program execution efficiency. The three work together to ensure efficient operation of the computer system.
-As shown in Figure 4-10, during program execution, data is read from the hard disk into memory for CPU computation. The cache, acting as an extension of the CPU, **intelligently preloads data from memory**, enabling faster data access for the CPU. This greatly improves program execution efficiency while reducing reliance on slower memory.
+As shown in the diagram below, during program execution, data is read from the hard disk into RAM for CPU computation. Cache can be viewed as part of the CPU, **it intelligently loads data from RAM**, providing the CPU with high-speed data reading, thereby significantly improving program execution efficiency and reducing reliance on slower RAM.
-{ class="animation-figure" }
+{ class="animation-figure" }
-
Figure 4-10 Data flow between hard disk, memory, and cache
+
Figure 4-10 Data Flow Among Hard Disk, RAM, and Cache
-## 4.4.2 Memory efficiency of data structures
+## 4.4.2 Memory Efficiency of Data Structures
-In terms of memory space utilization, arrays and linked lists have their advantages and limitations.
+In terms of memory space utilization, arrays and linked lists each have advantages and limitations.
-On one hand, **memory is limited and cannot be shared by multiple programs**, so optimizing space usage in data structures is crucial. Arrays are space-efficient because their elements are tightly packed, without requiring extra memory for references (pointers) as in linked lists. However, arrays require pre-allocating a contiguous block of memory, which can lead to waste if the allocated space exceeds the actual need. Expanding an array also incurs additional time and space overhead. In contrast, linked lists allocate and free memory dynamically for each node, offering greater flexibility at the cost of additional memory for pointers.
+On one hand, **memory is limited, and the same memory cannot be shared by multiple programs**, so we hope data structures can utilize space as efficiently as possible. Array elements are tightly packed and do not require additional space to store references (pointers) between linked list nodes, thus having higher space efficiency. However, arrays need to allocate sufficient contiguous memory space at once, which may lead to memory waste, and array expansion requires additional time and space costs. In comparison, linked lists perform dynamic memory allocation and deallocation on a "node" basis, providing greater flexibility.
-On the other hand, during program execution, **repeated memory allocation and deallocation increase memory fragmentation**, reducing memory utilization efficiency. Arrays, due to their continuous storage method, are relatively less likely to cause memory fragmentation. In contrast, linked lists store elements in non-contiguous locations, and frequent insertions and deletions can exacerbate memory fragmentation.
+On the other hand, during program execution, **as memory is repeatedly allocated and freed, the degree of fragmentation of free memory becomes increasingly severe**, leading to reduced memory utilization efficiency. Arrays, due to their contiguous storage approach, are relatively less prone to memory fragmentation. Conversely, linked list elements are distributed in storage, and frequent insertion and deletion operations are more likely to cause memory fragmentation.
-## 4.4.3 Cache efficiency of data structures
+## 4.4.3 Cache Efficiency of Data Structures
-Although caches are much smaller in space capacity than memory, they are much faster and play a crucial role in program execution speed. Due to their limited capacity, caches can only store a subset of frequently accessed data. When the CPU attempts to access data not present in the cache, a cache miss occurs, requiring the CPU to retrieve the needed data from slower memory, which can impact performance.
+Although cache has much smaller space capacity than memory, it is much faster than memory and plays a crucial role in program execution speed. Since cache capacity is limited and can only store a small portion of frequently accessed data, when the CPU attempts to access data that is not in the cache, a cache miss occurs, and the CPU must load the required data from the slower memory.
-Clearly, **the fewer the cache misses, the higher the CPU's data read-write efficiency**, and the better the program performance. The proportion of successful data retrieval from the cache by the CPU is called the cache hit rate, a metric often used to measure cache efficiency.
+Clearly, **the fewer "cache misses," the higher the efficiency of CPU data reads and writes**, and the better the program performance. We call the proportion of data that the CPU successfully obtains from the cache the cache hit rate, a metric typically used to measure cache efficiency.
-To achieve higher efficiency, caches adopt the following data loading mechanisms.
+To achieve the highest efficiency possible, cache employs the following data loading mechanisms.
-- **Cache lines**: Caches operate by storing and loading data in units called cache lines, rather than individual bytes. This approach improves efficiency by transferring larger blocks of data at once.
-- **Prefetch mechanism**: Processors predict data access patterns (e.g., sequential or fixed-stride access) and preload data into the cache based on these patterns to increase the cache hit rate.
-- **Spatial locality**: When a specific piece of data is accessed, nearby data is likely to be accessed soon. To leverage this, caches load adjacent data along with the requested data, improving hit rates.
-- **Temporal locality**: If data is accessed, it's likely to be accessed again in the near future. Caches use this principle to retain recently accessed data to improve the hit rate.
+- **Cache lines**: The cache does not store and load data on a byte-by-byte basis, but rather as cache lines. Compared to byte-by-byte transmission, cache line transmission is more efficient.
+- **Prefetching mechanism**: The processor attempts to predict data access patterns (e.g., sequential access, fixed-stride jumping access, etc.) and loads data into the cache according to specific patterns, thereby improving hit rate.
+- **Spatial locality**: If a piece of data is accessed, nearby data may also be accessed in the near future. Therefore, when the cache loads a particular piece of data, it also loads nearby data to improve hit rate.
+- **Temporal locality**: If a piece of data is accessed, it is likely to be accessed again in the near future. Cache leverages this principle by retaining recently accessed data to improve hit rate.
-In fact, **arrays and linked lists have different cache utilization efficiencies**, which is mainly reflected in the following aspects.
+In fact, **arrays and linked lists have different efficiencies in utilizing cache**, manifested in the following aspects.
-- **Occupied space**: Linked list elements take up more space than array elements, resulting in less effective data being held in the cache.
-- **Cache lines**: Linked list data is scattered throughout the memory, and cache is "loaded by row", so the proportion of invalid data loaded is higher.
-- **Prefetch mechanism**: The data access pattern of arrays is more "predictable" than that of linked lists, that is, it is easier for the system to guess the data that is about to be loaded.
-- **Spatial locality**: Arrays are stored in a continuous memory space, so data near the data being loaded is more likely to be accessed soon.
+- **Space occupied**: Linked list elements occupy more space than array elements, resulting in fewer effective data in the cache.
+- **Cache lines**: Linked list data are scattered throughout memory, while cache loads "by lines," so the proportion of invalid data loaded is higher.
+- **Prefetching mechanism**: Arrays have more "predictable" data access patterns than linked lists, making it easier for the system to guess which data will be loaded next.
+- **Spatial locality**: Arrays are stored in centralized memory space, so data near loaded data is more likely to be accessed soon.
-Overall, **arrays have a higher cache hit rate and are generally more efficient in operation than linked lists**. This makes data structures based on arrays more popular in solving algorithmic problems.
+Overall, **arrays have higher cache hit rates, thus they usually outperform linked lists in operation efficiency**. This makes data structures implemented based on arrays more popular when solving algorithmic problems.
-It should be noted that **high cache efficiency does not mean that arrays are always better than linked lists**. The choice of data structure should depend on specific application requirements. For example, both arrays and linked lists can implement the "stack" data structure (which will be detailed in the next chapter), but they are suitable for different scenarios.
+It is important to note that **high cache efficiency does not mean arrays are superior to linked lists in all cases**. In practical applications, which data structure to choose should be determined based on specific requirements. For example, both arrays and linked lists can implement the "stack" data structure (which will be discussed in detail in the next chapter), but they are suitable for different scenarios.
-- In algorithm problems, we tend to choose stacks based on arrays because they provide higher operational efficiency and random access capabilities, with the only cost being the need to pre-allocate a certain amount of memory space for the array.
-- If the data volume is very large, highly dynamic, and the expected size of the stack is difficult to estimate, then a stack based on a linked list is a better choice. Linked lists can distribute a large amount of data in different parts of the memory and avoid the additional overhead of array expansion.
+- When solving algorithm problems, we tend to prefer stack implementations based on arrays, because they provide higher operation efficiency and the ability of random access, at the cost of needing to pre-allocate a certain amount of memory space for the array.
+- If the data volume is very large, the dynamic nature is high, and the expected size of the stack is difficult to estimate, then a stack implementation based on linked lists is more suitable. Linked lists can distribute large amounts of data across different parts of memory and avoid the additional overhead produced by array expansion.
diff --git a/en/docs/chapter_array_and_linkedlist/summary.md b/en/docs/chapter_array_and_linkedlist/summary.md
index e2201dee2..ecdbb6350 100644
--- a/en/docs/chapter_array_and_linkedlist/summary.md
+++ b/en/docs/chapter_array_and_linkedlist/summary.md
@@ -4,82 +4,87 @@ comments: true
# 4.5 Summary
-### 1. Key review
+### 1. Key Review
-- Arrays and linked lists are two basic data structures, representing two storage methods in computer memory: contiguous space storage and non-contiguous space storage. Their characteristics complement each other.
-- Arrays support random access and use less memory; however, they are inefficient in inserting and deleting elements and have a fixed length after initialization.
-- Linked lists implement efficient node insertion and deletion through changing references (pointers) and can flexibly adjust their length; however, they have lower node access efficiency and consume more memory.
-- Common types of linked lists include singly linked lists, circular linked lists, and doubly linked lists, each with its own application scenarios.
-- Lists are ordered collections of elements that support addition, deletion, and modification, typically implemented based on dynamic arrays, retaining the advantages of arrays while allowing flexible length adjustment.
-- The advent of lists significantly enhanced the practicality of arrays but may lead to some memory space wastage.
-- During program execution, data is mainly stored in memory. Arrays provide higher memory space efficiency, while linked lists are more flexible in memory usage.
-- Caches provide fast data access to CPUs through mechanisms like cache lines, prefetching, spatial locality, and temporal locality, significantly enhancing program execution efficiency.
-- Due to higher cache hit rates, arrays are generally more efficient than linked lists. When choosing a data structure, the appropriate choice should be made based on specific needs and scenarios.
+- Arrays and linked lists are two fundamental data structures, representing two different ways data can be stored in computer memory: contiguous memory storage and scattered memory storage. The characteristics of the two complement each other.
+- Arrays support random access and use less memory; however, inserting and deleting elements is inefficient, and the length is immutable after initialization.
+- Linked lists achieve efficient insertion and deletion of nodes by modifying references (pointers), and can flexibly adjust length; however, node access is inefficient and memory consumption is higher. Common linked list types include singly linked lists, circular linked lists, and doubly linked lists.
+- A list is an ordered collection of elements that supports insertion, deletion, search, and modification, typically implemented based on dynamic arrays. It retains the advantages of arrays while allowing flexible adjustment of length.
+- The emergence of lists has greatly improved the practicality of arrays, but may result in some wasted memory space.
+- During program execution, data is primarily stored in memory. Arrays provide higher memory space efficiency, while linked lists offer greater flexibility in memory usage.
+- Caches provide fast data access to the CPU through mechanisms such as cache lines, prefetching, and spatial and temporal locality, significantly improving program execution efficiency.
+- Because arrays have higher cache hit rates, they are generally more efficient than linked lists. When choosing a data structure, appropriate selection should be made based on specific requirements and scenarios.
### 2. Q & A
-**Q**: Does storing arrays on the stack versus the heap affect time and space efficiency?
+**Q**: Does storing an array on the stack versus on the heap affect time efficiency and space efficiency?
-Arrays stored on both the stack and heap are stored in contiguous memory spaces, and data operation efficiency is essentially the same. However, stacks and heaps have their own characteristics, leading to the following differences.
+Arrays stored on the stack and on the heap are both stored in contiguous memory space, so data operation efficiency is basically the same. However, the stack and heap have their own characteristics, leading to the following differences.
-1. Allocation and release efficiency: The stack is a smaller memory block, allocated automatically by the compiler; the heap memory is relatively larger and can be dynamically allocated in the code, more prone to fragmentation. Therefore, allocation and release operations on the heap are generally slower than on the stack.
-2. Size limitation: Stack memory is relatively small, while the heap size is generally limited by available memory. Therefore, the heap is more suitable for storing large arrays.
-3. Flexibility: The size of arrays on the stack needs to be determined at compile-time, while the size of arrays on the heap can be dynamically determined at runtime.
+1. Allocation and deallocation efficiency: The stack is a relatively small piece of memory, with allocation automatically handled by the compiler; the heap is relatively larger and can be dynamically allocated in code, more prone to fragmentation. Therefore, allocation and deallocation operations on the heap are usually slower than on the stack.
+2. Size limitations: Stack memory is relatively small, and the heap size is generally limited by available memory. Therefore, the heap is more suitable for storing large arrays.
+3. Flexibility: The size of an array on the stack must be determined at compile time, while the size of an array on the heap can be determined dynamically at runtime.
-**Q**: Why do arrays require elements of the same type, while linked lists do not emphasize same-type elements?
+**Q**: Why do arrays require elements of the same type, while linked lists do not emphasize this requirement?
-Linked lists consist of nodes connected by references (pointers), and each node can store data of different types, such as int, double, string, object, etc.
+Linked lists are composed of nodes, with nodes connected through references (pointers), and each node can store different types of data, such as `int`, `double`, `string`, `object`, etc.
-In contrast, array elements must be of the same type, allowing the calculation of offsets to access the corresponding element positions. For example, an array containing both int and long types, with single elements occupying 4 bytes and 8 bytes respectively, cannot use the following formula to calculate offsets, as the array contains elements of two different lengths.
+In contrast, array elements must be of the same type, so that the corresponding element position can be obtained by calculating the offset. For example, if an array contains both `int` and `long` types, with individual elements occupying 4 bytes and 8 bytes respectively, then the following formula cannot be used to calculate the offset, because the array contains two different "element lengths".
```shell
-# Element memory address = array memory address + element length * element index
+# Element Memory Address = Array Memory Address (first Element Memory address) + Element Length * Element Index
```
-**Q**: After deleting a node, is it necessary to set `P.next` to `None`?
+**Q**: After deleting node `P`, do we need to set `P.next` to `None`?
-Not modifying `P.next` is also acceptable. From the perspective of the linked list, traversing from the head node to the tail node will no longer encounter `P`. This means that node `P` has been effectively removed from the list, and where `P` points no longer affects the list.
+It is not necessary to modify `P.next`. From the perspective of the linked list, traversing from the head node to the tail node will no longer encounter `P`. This means that node `P` has been removed from the linked list, and it doesn't matter where node `P` points to at this time—it won't affect the linked list.
-From a garbage collection perspective, for languages with automatic garbage collection mechanisms like Java, Python, and Go, whether node `P` is collected depends on whether there are still references pointing to it, not on the value of `P.next`. In languages like C and C++, we need to manually free the node's memory.
+From a data structures and algorithms perspective (problem-solving), not disconnecting the pointer doesn't matter as long as the program logic is correct. From the perspective of standard libraries, disconnecting is safer and the logic is clearer. If not disconnected, assuming the deleted node is not properly reclaimed, it may affect the memory reclamation of its successor nodes.
-**Q**: In linked lists, the time complexity for insertion and deletion operations is `O(1)`. But searching for the element before insertion or deletion takes `O(n)` time, so why isn't the time complexity `O(n)`?
+**Q**: In a linked list, the time complexity of insertion and deletion operations is $O(1)$. However, both insertion and deletion require $O(n)$ time to find the element; why isn't the time complexity $O(n)$?
-If an element is searched first and then deleted, the time complexity is indeed `O(n)`. However, the `O(1)` advantage of linked lists in insertion and deletion can be realized in other applications. For example, in the implementation of double-ended queues using linked lists, we maintain pointers always pointing to the head and tail nodes, making each insertion and deletion operation `O(1)`.
+If the element is first found and then deleted, the time complexity is indeed $O(n)$. However, the advantage of $O(1)$ insertion and deletion in linked lists can be demonstrated in other applications. For example, a deque is well-suited for linked list implementation, where we maintain pointer variables always pointing to the head and tail nodes, with each insertion and deletion operation being $O(1)$.
-**Q**: In the figure "Linked List Definition and Storage Method", do the light blue storage nodes occupy a single memory address, or do they share half with the node value?
+**Q**: In the diagram "Linked List Definition and Storage Methods", does the light blue pointer node occupy a single memory address, or does it share equally with the node value?
-The figure is just a qualitative representation; quantitative analysis depends on specific situations.
+This diagram is a qualitative representation; a quantitative representation requires analysis based on the specific situation.
-- Different types of node values occupy different amounts of space, such as int, long, double, and object instances.
-- The memory space occupied by pointer variables depends on the operating system and compilation environment used, usually 8 bytes or 4 bytes.
+- Different types of node values occupy different amounts of space, such as `int`, `long`, `double`, and instance objects, etc.
+- The amount of memory space occupied by pointer variables depends on the operating system and compilation environment used, usually 8 bytes or 4 bytes.
-**Q**: Is adding elements to the end of a list always `O(1)`?
+**Q**: Is appending an element at the end of a list always $O(1)$?
-If adding an element exceeds the list length, the list needs to be expanded first. The system will request a new memory block and move all elements of the original list over, in which case the time complexity becomes `O(n)`.
+If appending an element exceeds the list length, the list must first be expanded before adding. The system allocates a new block of memory and moves all elements from the original list to it, in which case the time complexity becomes $O(n)$.
-**Q**: The statement "The emergence of lists greatly improves the practicality of arrays, but may lead to some memory space wastage" - does this refer to the memory occupied by additional variables like capacity, length, and expansion multiplier?
+**Q**: "The emergence of lists has greatly improved the practicality of arrays, but may result in some wasted memory space"—does this space waste refer to the memory occupied by additional variables such as capacity, length, and expansion factor?
-The space wastage here mainly refers to two aspects: on the one hand, lists are set with an initial length, which we may not always need; on the other hand, to prevent frequent expansion, expansion usually multiplies by a coefficient, such as $\times 1.5$. This results in many empty slots, which we typically cannot fully fill.
+This space waste mainly has two aspects: on one hand, lists typically set an initial length, which we may not need to fully utilize; on the other hand, to prevent frequent expansion, expansion generally multiplies by a coefficient, such as $\times 1.5$. As a result, there will be many empty positions that we typically cannot completely fill.
-**Q**: In Python, after initializing `n = [1, 2, 3]`, the addresses of these 3 elements are contiguous, but initializing `m = [2, 1, 3]` shows that each element's `id` is not consecutive but identical to those in `n`. If the addresses of these elements are not contiguous, is `m` still an array?
+**Q**: In Python, after initializing `n = [1, 2, 3]`, the addresses of these 3 elements are contiguous, but initializing `m = [2, 1, 3]` reveals that each element's id is not continuous; rather, they are the same as those in `n`. Since the addresses of these elements are not contiguous, is `m` still an array?
-If we replace list elements with linked list nodes `n = [n1, n2, n3, n4, n5]`, these 5 node objects are also typically dispersed throughout memory. However, given a list index, we can still access the node's memory address in `O(1)` time, thereby accessing the corresponding node. This is because the array stores references to the nodes, not the nodes themselves.
+If we replace list elements with linked list nodes `n = [n1, n2, n3, n4, n5]`, usually these 5 node objects are also scattered throughout memory. However, given a list index, we can still obtain the node memory address in $O(1)$ time, thereby accessing the corresponding node. This is because the array stores references to nodes, not the nodes themselves.
-Unlike many languages, in Python, numbers are also wrapped as objects, and lists store references to these numbers, not the numbers themselves. Therefore, we find that the same number in two arrays has the same `id`, and these numbers' memory addresses need not be contiguous.
+Unlike many languages, numbers in Python are wrapped as objects, and lists store not the numbers themselves, but references to the numbers. Therefore, we find that the same numbers in two arrays have the same id, and the memory addresses of these numbers need not be contiguous.
-**Q**: The `std::list` in C++ STL has already implemented a doubly linked list, but it seems that some algorithm books don't directly use it. Is there any limitation?
+**Q**: C++ STL has `std::list` which has already implemented a doubly linked list, but it seems that some algorithm books don't use it directly. Is there a limitation?
-On the one hand, we often prefer to use arrays to implement algorithms, only using linked lists when necessary, mainly for two reasons.
+On one hand, we often prefer to use arrays for implementing algorithms and only use linked lists when necessary, mainly for two reasons.
-- Space overhead: Since each element requires two additional pointers (one for the previous element and one for the next), `std::list` usually occupies more space than `std::vector`.
-- Cache unfriendly: As the data is not stored continuously, `std::list` has a lower cache utilization rate. Generally, `std::vector` performs better.
+- Space overhead: Since each element requires two additional pointers (one for the previous element and one for the next element), `std::list` typically consumes more space than `std::vector`.
+- Cache unfriendliness: Since data is not stored contiguously, `std::list` has lower cache utilization. In general, `std::vector` has better performance.
-On the other hand, linked lists are primarily necessary for binary trees and graphs. Stacks and queues are often implemented using the programming language's `stack` and `queue` classes, rather than linked lists.
+On the other hand, cases where linked lists are necessary mainly involve binary trees and graphs. Stacks and queues usually use the `stack` and `queue` provided by the programming language, rather than linked lists.
-**Q**: Does initializing a list `res = [0] * self.size()` result in each element of `res` referencing the same address?
+**Q**: Does the operation `res = [[0]] * n` create a 2D list where each `[0]` is independent?
-No. However, this issue arises with two-dimensional arrays, for example, initializing a two-dimensional list `res = [[0]] * self.size()` would reference the same list `[0]` multiple times.
+No, they are not independent. In this 2D list, all the `[0]` are actually references to the same object. If we modify one element, we will find that all corresponding elements change accordingly.
-**Q**: In deleting a node, is it necessary to break the reference to its successor node?
+If we want each `[0]` in the 2D list to be independent, we can use `res = [[0] for _ in range(n)]` to achieve this. The principle of this approach is to initialize $n$ independent `[0]` list objects.
-From the perspective of data structures and algorithms (problem-solving), it's okay not to break the link, as long as the program's logic is correct. From the perspective of standard libraries, breaking the link is safer and more logically clear. If the link is not broken, and the deleted node is not properly recycled, it could affect the recycling of the successor node's memory.
+**Q**: Does the operation `res = [0] * n` create a list where each integer 0 is independent?
+
+In this list, all integer 0s are references to the same object. This is because Python uses a caching mechanism for small integers (typically -5 to 256) to maximize object reuse and improve performance.
+
+Although they point to the same object, we can still independently modify each element in the list. This is because Python integers are "immutable objects". When we modify an element, we are actually switching to a reference of another object, rather than changing the original object itself.
+
+However, when list elements are "mutable objects" (such as lists, dictionaries, or class instances), modifying an element directly changes the object itself, and all elements referencing that object will have the same change.
diff --git a/en/docs/chapter_backtracking/backtracking_algorithm.md b/en/docs/chapter_backtracking/backtracking_algorithm.md
index d7e073462..522e66bc6 100644
--- a/en/docs/chapter_backtracking/backtracking_algorithm.md
+++ b/en/docs/chapter_backtracking/backtracking_algorithm.md
@@ -2,23 +2,23 @@
comments: true
---
-# 13.1 Backtracking algorithms
+# 13.1 Backtracking Algorithm
-Backtracking algorithm is a method to solve problems by exhaustive search. Its core concept is to start from an initial state and brutally search for all possible solutions. The algorithm records the correct ones until a solution is found or all possible solutions have been tried but no solution can be found.
+The backtracking algorithm is a method for solving problems through exhaustive search. Its core idea is to start from an initial state and exhaustively search all possible solutions. When a correct solution is found, it is recorded. This process continues until a solution is found or all possible choices have been tried without finding a solution.
-Backtracking typically employs "depth-first search" to traverse the solution space. In the "Binary tree" chapter, we mentioned that pre-order, in-order, and post-order traversals are all depth-first searches. Next, we are going to use pre-order traversal to solve a backtracking problem. This helps us to understand how the algorithm works gradually.
+The backtracking algorithm typically employs "depth-first search" to traverse the solution space. In the "Binary Tree" chapter, we mentioned that preorder, inorder, and postorder traversals all belong to depth-first search. Next, we will construct a backtracking problem using preorder traversal to progressively understand how the backtracking algorithm works.
-!!! question "Example One"
+!!! question "Example 1"
- Given a binary tree, search and record all nodes with a value of $7$ and return them in a list.
+ Given a binary tree, search and record all nodes with value $7$, and return a list of these nodes.
-To solve this problem, we traverse this tree in pre-order 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 process is shown in Figure 13-1:
+For this problem, we perform a preorder traversal of the tree and check whether the current node's value is $7$. If it is, we add the node to the result list `res`. The relevant implementation is shown in the following figure and code:
=== "Python"
```python title="preorder_traversal_i_compact.py"
def pre_order(root: TreeNode):
- """Pre-order traversal: Example one"""
+ """Preorder traversal: Example 1"""
if root is None:
return
if root.val == 7:
@@ -31,7 +31,7 @@ To solve this problem, we traverse this tree in pre-order and check if the curre
=== "C++"
```cpp title="preorder_traversal_i_compact.cpp"
- /* Pre-order traversal: Example one */
+ /* Preorder traversal: Example 1 */
void preOrder(TreeNode *root) {
if (root == nullptr) {
return;
@@ -48,7 +48,7 @@ To solve this problem, we traverse this tree in pre-order and check if the curre
=== "Java"
```java title="preorder_traversal_i_compact.java"
- /* Pre-order traversal: Example one */
+ /* Preorder traversal: Example 1 */
void preOrder(TreeNode root) {
if (root == null) {
return;
@@ -65,92 +65,196 @@ To solve this problem, we traverse this tree in pre-order and check if the curre
=== "C#"
```csharp title="preorder_traversal_i_compact.cs"
- [class]{preorder_traversal_i_compact}-[func]{PreOrder}
+ /* Preorder traversal: Example 1 */
+ void PreOrder(TreeNode? root) {
+ if (root == null) {
+ return;
+ }
+ if (root.val == 7) {
+ // Record solution
+ res.Add(root);
+ }
+ PreOrder(root.left);
+ PreOrder(root.right);
+ }
```
=== "Go"
```go title="preorder_traversal_i_compact.go"
- [class]{}-[func]{preOrderI}
+ /* Preorder traversal: Example 1 */
+ func preOrderI(root *TreeNode, res *[]*TreeNode) {
+ if root == nil {
+ return
+ }
+ if (root.Val).(int) == 7 {
+ // Record solution
+ *res = append(*res, root)
+ }
+ preOrderI(root.Left, res)
+ preOrderI(root.Right, res)
+ }
```
=== "Swift"
```swift title="preorder_traversal_i_compact.swift"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 1 */
+ func preOrder(root: TreeNode?) {
+ guard let root = root else {
+ return
+ }
+ if root.val == 7 {
+ // Record solution
+ res.append(root)
+ }
+ preOrder(root: root.left)
+ preOrder(root: root.right)
+ }
```
=== "JS"
```javascript title="preorder_traversal_i_compact.js"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 1 */
+ function preOrder(root, res) {
+ if (root === null) {
+ return;
+ }
+ if (root.val === 7) {
+ // Record solution
+ res.push(root);
+ }
+ preOrder(root.left, res);
+ preOrder(root.right, res);
+ }
```
=== "TS"
```typescript title="preorder_traversal_i_compact.ts"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 1 */
+ function preOrder(root: TreeNode | null, res: TreeNode[]): void {
+ if (root === null) {
+ return;
+ }
+ if (root.val === 7) {
+ // Record solution
+ res.push(root);
+ }
+ preOrder(root.left, res);
+ preOrder(root.right, res);
+ }
```
=== "Dart"
```dart title="preorder_traversal_i_compact.dart"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 1 */
+ void preOrder(TreeNode? root, List res) {
+ if (root == null) {
+ return;
+ }
+ if (root.val == 7) {
+ // Record solution
+ res.add(root);
+ }
+ preOrder(root.left, res);
+ preOrder(root.right, res);
+ }
```
=== "Rust"
```rust title="preorder_traversal_i_compact.rs"
- [class]{}-[func]{pre_order}
+ /* Preorder traversal: Example 1 */
+ fn pre_order(res: &mut Vec>>, root: Option<&Rc>>) {
+ if root.is_none() {
+ return;
+ }
+ if let Some(node) = root {
+ if node.borrow().val == 7 {
+ // Record solution
+ res.push(node.clone());
+ }
+ pre_order(res, node.borrow().left.as_ref());
+ pre_order(res, node.borrow().right.as_ref());
+ }
+ }
```
=== "C"
```c title="preorder_traversal_i_compact.c"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 1 */
+ void preOrder(TreeNode *root) {
+ if (root == NULL) {
+ return;
+ }
+ if (root->val == 7) {
+ // Record solution
+ res[resSize++] = root;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ }
```
=== "Kotlin"
```kotlin title="preorder_traversal_i_compact.kt"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 1 */
+ fun preOrder(root: TreeNode?) {
+ if (root == null) {
+ return
+ }
+ if (root._val == 7) {
+ // Record solution
+ res!!.add(root)
+ }
+ preOrder(root.left)
+ preOrder(root.right)
+ }
```
=== "Ruby"
```ruby title="preorder_traversal_i_compact.rb"
- [class]{}-[func]{pre_order}
+ ### Pre-order traversal: example 1 ###
+ def pre_order(root)
+ return unless root
+
+ # Record solution
+ $res << root if root.val == 7
+
+ pre_order(root.left)
+ pre_order(root.right)
+ end
```
-=== "Zig"
+{ class="animation-figure" }
- ```zig title="preorder_traversal_i_compact.zig"
- [class]{}-[func]{preOrder}
- ```
+
Figure 13-1 Search for nodes in preorder traversal
-{ class="animation-figure" }
+## 13.1.1 Attempt and Backtrack
-
Figure 13-1 Searching nodes in pre-order traversal
+**The reason it is called a backtracking algorithm is that it employs "attempt" and "backtrack" strategies when searching the solution space**. When the algorithm encounters a state where it cannot continue forward or cannot find a solution that satisfies the constraints, it will undo the previous choice, return to a previous state, and try other possible choices.
-## 13.1.1 Trial and retreat
+For Example 1, visiting each node represents an "attempt", while skipping over a leaf node or a function `return` from the parent node represents a "backtrack".
-**It is called a backtracking algorithm because it uses a "trial" and "retreat" strategy when searching the solution space**. During the search, whenever it encounters a state where it can no longer proceed to obtain a satisfying solution, it undoes the previous choice and reverts to the previous state so that other possible choices can be chosen for the next attempt.
+It is worth noting that **backtracking is not limited to function returns alone**. To illustrate this, let's extend Example 1 slightly.
-In Example One, visiting each node starts a "trial". And passing a leaf node or the `return` statement to going back to the parent node suggests "retreat".
+!!! question "Example 2"
-It's worth noting that **retreat is not merely about function returns**. We'll expand slightly on Example One question to explain what it means.
+ In a binary tree, search all nodes with value $7$, **and return the paths from the root node to these nodes**.
-!!! question "Example Two"
-
- In a binary tree, search for all nodes with a value of $7$ and for all matching nodes, **please return the paths from the root node to that node**.
-
-Based on the code from Example One, we need to use a list called `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:
+Based on the code from Example 1, we need to use a list `path` to record the visited node path. When we reach a node with value $7$, we copy `path` and add it to the result list `res`. After traversal is complete, `res` contains all the solutions. The code is as follows:
=== "Python"
```python title="preorder_traversal_ii_compact.py"
def pre_order(root: TreeNode):
- """Pre-order traversal: Example two"""
+ """Preorder traversal: Example 2"""
if root is None:
return
# Attempt
@@ -160,14 +264,14 @@ Based on the code from Example One, we need to use a list called `path` to recor
res.append(list(path))
pre_order(root.left)
pre_order(root.right)
- # Retract
+ # Backtrack
path.pop()
```
=== "C++"
```cpp title="preorder_traversal_ii_compact.cpp"
- /* Pre-order traversal: Example two */
+ /* Preorder traversal: Example 2 */
void preOrder(TreeNode *root) {
if (root == nullptr) {
return;
@@ -180,7 +284,7 @@ Based on the code from Example One, we need to use a list called `path` to recor
}
preOrder(root->left);
preOrder(root->right);
- // Retract
+ // Backtrack
path.pop_back();
}
```
@@ -188,7 +292,7 @@ Based on the code from Example One, we need to use a list called `path` to recor
=== "Java"
```java title="preorder_traversal_ii_compact.java"
- /* Pre-order traversal: Example two */
+ /* Preorder traversal: Example 2 */
void preOrder(TreeNode root) {
if (root == null) {
return;
@@ -201,7 +305,7 @@ Based on the code from Example One, we need to use a list called `path` to recor
}
preOrder(root.left);
preOrder(root.right);
- // Retract
+ // Backtrack
path.remove(path.size() - 1);
}
```
@@ -209,75 +313,237 @@ Based on the code from Example One, we need to use a list called `path` to recor
=== "C#"
```csharp title="preorder_traversal_ii_compact.cs"
- [class]{preorder_traversal_ii_compact}-[func]{PreOrder}
+ /* Preorder traversal: Example 2 */
+ void PreOrder(TreeNode? root) {
+ if (root == null) {
+ return;
+ }
+ // Attempt
+ path.Add(root);
+ if (root.val == 7) {
+ // Record solution
+ res.Add(new List(path));
+ }
+ PreOrder(root.left);
+ PreOrder(root.right);
+ // Backtrack
+ path.RemoveAt(path.Count - 1);
+ }
```
=== "Go"
```go title="preorder_traversal_ii_compact.go"
- [class]{}-[func]{preOrderII}
+ /* Preorder traversal: Example 2 */
+ func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
+ if root == nil {
+ return
+ }
+ // Attempt
+ *path = append(*path, root)
+ if root.Val.(int) == 7 {
+ // Record solution
+ *res = append(*res, append([]*TreeNode{}, *path...))
+ }
+ preOrderII(root.Left, res, path)
+ preOrderII(root.Right, res, path)
+ // Backtrack
+ *path = (*path)[:len(*path)-1]
+ }
```
=== "Swift"
```swift title="preorder_traversal_ii_compact.swift"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 2 */
+ func preOrder(root: TreeNode?) {
+ guard let root = root else {
+ return
+ }
+ // Attempt
+ path.append(root)
+ if root.val == 7 {
+ // Record solution
+ res.append(path)
+ }
+ preOrder(root: root.left)
+ preOrder(root: root.right)
+ // Backtrack
+ path.removeLast()
+ }
```
=== "JS"
```javascript title="preorder_traversal_ii_compact.js"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 2 */
+ function preOrder(root, path, res) {
+ if (root === null) {
+ return;
+ }
+ // Attempt
+ path.push(root);
+ if (root.val === 7) {
+ // Record solution
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // Backtrack
+ path.pop();
+ }
```
=== "TS"
```typescript title="preorder_traversal_ii_compact.ts"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 2 */
+ function preOrder(
+ root: TreeNode | null,
+ path: TreeNode[],
+ res: TreeNode[][]
+ ): void {
+ if (root === null) {
+ return;
+ }
+ // Attempt
+ path.push(root);
+ if (root.val === 7) {
+ // Record solution
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // Backtrack
+ path.pop();
+ }
```
=== "Dart"
```dart title="preorder_traversal_ii_compact.dart"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 2 */
+ void preOrder(
+ TreeNode? root,
+ List path,
+ List> res,
+ ) {
+ if (root == null) {
+ return;
+ }
+
+ // Attempt
+ path.add(root);
+ if (root.val == 7) {
+ // Record solution
+ res.add(List.from(path));
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // Backtrack
+ path.removeLast();
+ }
```
=== "Rust"
```rust title="preorder_traversal_ii_compact.rs"
- [class]{}-[func]{pre_order}
+ /* Preorder traversal: Example 2 */
+ fn pre_order(
+ res: &mut Vec>>>,
+ path: &mut Vec>>,
+ root: Option<&Rc>>,
+ ) {
+ if root.is_none() {
+ return;
+ }
+ if let Some(node) = root {
+ // Attempt
+ path.push(node.clone());
+ if node.borrow().val == 7 {
+ // Record solution
+ res.push(path.clone());
+ }
+ pre_order(res, path, node.borrow().left.as_ref());
+ pre_order(res, path, node.borrow().right.as_ref());
+ // Backtrack
+ path.pop();
+ }
+ }
```
=== "C"
```c title="preorder_traversal_ii_compact.c"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 2 */
+ void preOrder(TreeNode *root) {
+ if (root == NULL) {
+ return;
+ }
+ // Attempt
+ path[pathSize++] = root;
+ if (root->val == 7) {
+ // Record solution
+ for (int i = 0; i < pathSize; ++i) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // Backtrack
+ pathSize--;
+ }
```
=== "Kotlin"
```kotlin title="preorder_traversal_ii_compact.kt"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 2 */
+ fun preOrder(root: TreeNode?) {
+ if (root == null) {
+ return
+ }
+ // Attempt
+ path!!.add(root)
+ if (root._val == 7) {
+ // Record solution
+ res!!.add(path!!.toMutableList())
+ }
+ preOrder(root.left)
+ preOrder(root.right)
+ // Backtrack
+ path!!.removeAt(path!!.size - 1)
+ }
```
=== "Ruby"
```ruby title="preorder_traversal_ii_compact.rb"
- [class]{}-[func]{pre_order}
+ ### Pre-order traversal: example 2 ###
+ def pre_order(root)
+ return unless root
+
+ # Attempt
+ $path << root
+
+ # Record solution
+ $res << $path.dup if root.val == 7
+
+ pre_order(root.left)
+ pre_order(root.right)
+
+ # Backtrack
+ $path.pop
+ end
```
-=== "Zig"
+In each "attempt", we record the path by adding the current node to `path`; before "backtracking", we need to remove the node from `path`, **to restore the state before this attempt**.
- ```zig title="preorder_traversal_ii_compact.zig"
- [class]{}-[func]{preOrder}
- ```
-
-In each "trial", we record the path by adding the current node to `path`. Whenever we need to "retreat", we pop the node from `path` **to restore the state prior to this failed attempt**.
-
-By observing the process shown in Figure 13-2, **the trial is like "advancing", and retreat is like "undoing"**. The later pairs can be seen as a reverse operation to their counterpart.
+Observing the process shown in the following figure, **we can understand attempt and backtrack as "advance" and "undo"**, two operations that are the reverse of each other.
=== "<1>"
- { class="animation-figure" }
+ { class="animation-figure" }
=== "<2>"
{ class="animation-figure" }
@@ -309,23 +575,23 @@ By observing the process shown in Figure 13-2, **the trial is like "advancing",
=== "<11>"
{ class="animation-figure" }
-
Figure 13-2 Trying and retreating
+
Figure 13-2 Attempt and backtrack
-## 13.1.2 Prune
+## 13.1.2 Pruning
-Complex backtracking problems usually involve one or more constraints, **which are often used for "pruning"**.
+Complex backtracking problems usually contain one or more constraints. **Constraints can typically be used for "pruning"**.
-!!! question "Example Three"
+!!! question "Example 3"
- In a binary tree, search for all nodes with a value of $7$ and return the paths from the root to these nodes, **with restriction that the paths do not contain nodes with a value of $3$**.
+ In a binary tree, search all nodes with value $7$ and return the paths from the root node to these nodes, **but require that the paths do not contain nodes with value $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 aborts further searching down through the path immediately. The code is as shown:
+To satisfy the above constraints, **we need to add pruning operations**: during the search process, if we encounter a node with value $3$, we return early and do not continue searching. The code is as follows:
=== "Python"
```python title="preorder_traversal_iii_compact.py"
def pre_order(root: TreeNode):
- """Pre-order traversal: Example three"""
+ """Preorder traversal: Example 3"""
# Pruning
if root is None or root.val == 3:
return
@@ -336,14 +602,14 @@ To meet the above constraints, **we need to add a pruning operation**: during th
res.append(list(path))
pre_order(root.left)
pre_order(root.right)
- # Retract
+ # Backtrack
path.pop()
```
=== "C++"
```cpp title="preorder_traversal_iii_compact.cpp"
- /* Pre-order traversal: Example three */
+ /* Preorder traversal: Example 3 */
void preOrder(TreeNode *root) {
// Pruning
if (root == nullptr || root->val == 3) {
@@ -357,7 +623,7 @@ To meet the above constraints, **we need to add a pruning operation**: during th
}
preOrder(root->left);
preOrder(root->right);
- // Retract
+ // Backtrack
path.pop_back();
}
```
@@ -365,7 +631,7 @@ To meet the above constraints, **we need to add a pruning operation**: during th
=== "Java"
```java title="preorder_traversal_iii_compact.java"
- /* Pre-order traversal: Example three */
+ /* Preorder traversal: Example 3 */
void preOrder(TreeNode root) {
// Pruning
if (root == null || root.val == 3) {
@@ -379,7 +645,7 @@ To meet the above constraints, **we need to add a pruning operation**: during th
}
preOrder(root.left);
preOrder(root.right);
- // Retract
+ // Backtrack
path.remove(path.size() - 1);
}
```
@@ -387,100 +653,271 @@ To meet the above constraints, **we need to add a pruning operation**: during th
=== "C#"
```csharp title="preorder_traversal_iii_compact.cs"
- [class]{preorder_traversal_iii_compact}-[func]{PreOrder}
+ /* Preorder traversal: Example 3 */
+ void PreOrder(TreeNode? root) {
+ // Pruning
+ if (root == null || root.val == 3) {
+ return;
+ }
+ // Attempt
+ path.Add(root);
+ if (root.val == 7) {
+ // Record solution
+ res.Add(new List(path));
+ }
+ PreOrder(root.left);
+ PreOrder(root.right);
+ // Backtrack
+ path.RemoveAt(path.Count - 1);
+ }
```
=== "Go"
```go title="preorder_traversal_iii_compact.go"
- [class]{}-[func]{preOrderIII}
+ /* Preorder traversal: Example 3 */
+ func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
+ // Pruning
+ if root == nil || root.Val == 3 {
+ return
+ }
+ // Attempt
+ *path = append(*path, root)
+ if root.Val.(int) == 7 {
+ // Record solution
+ *res = append(*res, append([]*TreeNode{}, *path...))
+ }
+ preOrderIII(root.Left, res, path)
+ preOrderIII(root.Right, res, path)
+ // Backtrack
+ *path = (*path)[:len(*path)-1]
+ }
```
=== "Swift"
```swift title="preorder_traversal_iii_compact.swift"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 3 */
+ func preOrder(root: TreeNode?) {
+ // Pruning
+ guard let root = root, root.val != 3 else {
+ return
+ }
+ // Attempt
+ path.append(root)
+ if root.val == 7 {
+ // Record solution
+ res.append(path)
+ }
+ preOrder(root: root.left)
+ preOrder(root: root.right)
+ // Backtrack
+ path.removeLast()
+ }
```
=== "JS"
```javascript title="preorder_traversal_iii_compact.js"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 3 */
+ function preOrder(root, path, res) {
+ // Pruning
+ if (root === null || root.val === 3) {
+ return;
+ }
+ // Attempt
+ path.push(root);
+ if (root.val === 7) {
+ // Record solution
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // Backtrack
+ path.pop();
+ }
```
=== "TS"
```typescript title="preorder_traversal_iii_compact.ts"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 3 */
+ function preOrder(
+ root: TreeNode | null,
+ path: TreeNode[],
+ res: TreeNode[][]
+ ): void {
+ // Pruning
+ if (root === null || root.val === 3) {
+ return;
+ }
+ // Attempt
+ path.push(root);
+ if (root.val === 7) {
+ // Record solution
+ res.push([...path]);
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // Backtrack
+ path.pop();
+ }
```
=== "Dart"
```dart title="preorder_traversal_iii_compact.dart"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 3 */
+ void preOrder(
+ TreeNode? root,
+ List path,
+ List> res,
+ ) {
+ if (root == null || root.val == 3) {
+ return;
+ }
+
+ // Attempt
+ path.add(root);
+ if (root.val == 7) {
+ // Record solution
+ res.add(List.from(path));
+ }
+ preOrder(root.left, path, res);
+ preOrder(root.right, path, res);
+ // Backtrack
+ path.removeLast();
+ }
```
=== "Rust"
```rust title="preorder_traversal_iii_compact.rs"
- [class]{}-[func]{pre_order}
+ /* Preorder traversal: Example 3 */
+ fn pre_order(
+ res: &mut Vec>>>,
+ path: &mut Vec>>,
+ root: Option<&Rc>>,
+ ) {
+ // Pruning
+ if root.is_none() || root.as_ref().unwrap().borrow().val == 3 {
+ return;
+ }
+ if let Some(node) = root {
+ // Attempt
+ path.push(node.clone());
+ if node.borrow().val == 7 {
+ // Record solution
+ res.push(path.clone());
+ }
+ pre_order(res, path, node.borrow().left.as_ref());
+ pre_order(res, path, node.borrow().right.as_ref());
+ // Backtrack
+ path.pop();
+ }
+ }
```
=== "C"
```c title="preorder_traversal_iii_compact.c"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 3 */
+ void preOrder(TreeNode *root) {
+ // Pruning
+ if (root == NULL || root->val == 3) {
+ return;
+ }
+ // Attempt
+ path[pathSize++] = root;
+ if (root->val == 7) {
+ // Record solution
+ for (int i = 0; i < pathSize; i++) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // Backtrack
+ pathSize--;
+ }
```
=== "Kotlin"
```kotlin title="preorder_traversal_iii_compact.kt"
- [class]{}-[func]{preOrder}
+ /* Preorder traversal: Example 3 */
+ fun preOrder(root: TreeNode?) {
+ // Pruning
+ if (root == null || root._val == 3) {
+ return
+ }
+ // Attempt
+ path!!.add(root)
+ if (root._val == 7) {
+ // Record solution
+ res!!.add(path!!.toMutableList())
+ }
+ preOrder(root.left)
+ preOrder(root.right)
+ // Backtrack
+ path!!.removeAt(path!!.size - 1)
+ }
```
=== "Ruby"
```ruby title="preorder_traversal_iii_compact.rb"
- [class]{}-[func]{pre_order}
+ ### Pre-order traversal: example 3 ###
+ def pre_order(root)
+ # Pruning
+ return if !root || root.val == 3
+
+ # Attempt
+ $path.append(root)
+
+ # Record solution
+ $res << $path.dup if root.val == 7
+
+ pre_order(root.left)
+ pre_order(root.right)
+
+ # Backtrack
+ $path.pop
+ end
```
-=== "Zig"
+"Pruning" is a vivid term. As shown in the following figure, during the search process, **we "prune" search branches that do not satisfy the constraints**, avoiding many meaningless attempts and thus improving search efficiency.
- ```zig title="preorder_traversal_iii_compact.zig"
- [class]{}-[func]{preOrder}
- ```
+{ class="animation-figure" }
-"Pruning" is a very vivid noun. As shown in Figure 13-3, in the search process, **we "cut off" the search branches that do not meet the constraints**. It avoids further unnecessary attempts, thus enhances the search efficiency.
+
Figure 13-3 Pruning according to constraints
-{ class="animation-figure" }
+## 13.1.3 Framework Code
-
Figure 13-3 Pruning based on constraints
+Next, we attempt to extract the main framework of backtracking's "attempt, backtrack, and pruning", to improve code generality.
-## 13.1.3 Framework code
-
-Now, let's try to distill the main framework of "trial, retreat, and prune" 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:
+In the following framework code, `state` represents the current state of the problem, and `choices` represents the choices available in 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
+ # Check if it is a solution
if is_solution(state):
# Record the solution
record_solution(state, res)
# Stop searching
return
- # Iterate through all choices
+ # Traverse all choices
for choice in choices:
- # Prune: check if the choice is valid
+ # Pruning: check if the choice is valid
if is_valid(state, choice):
- # Trial: make a choice, update the state
+ # Attempt: make a choice and update the state
make_choice(state, choice)
backtrack(state, choices, res)
- # Retreat: undo the choice, revert to the previous state
+ # Backtrack: undo the choice and restore to the previous state
undo_choice(state, choice)
```
@@ -489,21 +926,21 @@ In the following framework code, `state` represents the current state of the pro
```cpp title=""
/* Backtracking algorithm framework */
void backtrack(State *state, vector &choices, vector &res) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record the solution
recordSolution(state, res);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
for (Choice choice : choices) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, choice);
backtrack(state, choices, res);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, choice);
}
}
@@ -515,21 +952,21 @@ In the following framework code, `state` represents the current state of the pro
```java title=""
/* Backtracking algorithm framework */
void backtrack(State state, List choices, List res) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record the solution
recordSolution(state, res);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
for (Choice choice : choices) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, choice);
backtrack(state, choices, res);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, choice);
}
}
@@ -541,21 +978,21 @@ In the following framework code, `state` represents the current state of the pro
```csharp title=""
/* Backtracking algorithm framework */
void Backtrack(State state, List choices, List res) {
- // Check if it's a solution
+ // Check if it is a solution
if (IsSolution(state)) {
// Record the solution
RecordSolution(state, res);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
foreach (Choice choice in choices) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (IsValid(state, choice)) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
MakeChoice(state, choice);
Backtrack(state, choices, res);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
UndoChoice(state, choice);
}
}
@@ -567,21 +1004,21 @@ In the following framework code, `state` represents the current state of the pro
```go title=""
/* Backtracking algorithm framework */
func backtrack(state *State, choices []Choice, res *[]State) {
- // Check if it's a solution
+ // Check if it is a solution
if isSolution(state) {
// Record the solution
recordSolution(state, res)
// Stop searching
return
}
- // Iterate through all choices
+ // Traverse all choices
for _, choice := range choices {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if isValid(state, choice) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, choice)
backtrack(state, choices, res)
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, choice)
}
}
@@ -593,21 +1030,21 @@ In the following framework code, `state` represents the current state of the pro
```swift title=""
/* Backtracking algorithm framework */
func backtrack(state: inout State, choices: [Choice], res: inout [State]) {
- // Check if it's a solution
+ // Check if it is a solution
if isSolution(state: state) {
// Record the solution
recordSolution(state: state, res: &res)
// Stop searching
return
}
- // Iterate through all choices
+ // Traverse all choices
for choice in choices {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if isValid(state: state, choice: choice) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state: &state, choice: choice)
backtrack(state: &state, choices: choices, res: &res)
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state: &state, choice: choice)
}
}
@@ -619,21 +1056,21 @@ In the following framework code, `state` represents the current state of the pro
```javascript title=""
/* Backtracking algorithm framework */
function backtrack(state, choices, res) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record the solution
recordSolution(state, res);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
for (let choice of choices) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, choice);
backtrack(state, choices, res);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, choice);
}
}
@@ -645,21 +1082,21 @@ In the following framework code, `state` represents the current state of the pro
```typescript title=""
/* Backtracking algorithm framework */
function backtrack(state: State, choices: Choice[], res: State[]): void {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record the solution
recordSolution(state, res);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
for (let choice of choices) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, choice);
backtrack(state, choices, res);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, choice);
}
}
@@ -671,21 +1108,21 @@ In the following framework code, `state` represents the current state of the pro
```dart title=""
/* Backtracking algorithm framework */
void backtrack(State state, List, List res) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record the solution
recordSolution(state, res);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
for (Choice choice in choices) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, choice);
backtrack(state, choices, res);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, choice);
}
}
@@ -697,21 +1134,21 @@ In the following framework code, `state` represents the current state of the pro
```rust title=""
/* Backtracking algorithm framework */
fn backtrack(state: &mut State, choices: &Vec, res: &mut Vec) {
- // Check if it's a solution
+ // Check if it is a solution
if is_solution(state) {
// Record the solution
record_solution(state, res);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
for choice in choices {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if is_valid(state, choice) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
make_choice(state, choice);
backtrack(state, choices, res);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undo_choice(state, choice);
}
}
@@ -723,21 +1160,21 @@ In the following framework code, `state` represents the current state of the pro
```c title=""
/* Backtracking algorithm framework */
void backtrack(State *state, Choice *choices, int numChoices, State *res, int numRes) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record the solution
recordSolution(state, res, numRes);
// Stop searching
return;
}
- // Iterate through all choices
+ // Traverse all choices
for (int i = 0; i < numChoices; i++) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (isValid(state, &choices[i])) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, &choices[i]);
backtrack(state, choices, numChoices, res, numRes);
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, &choices[i]);
}
}
@@ -749,21 +1186,21 @@ In the following framework code, `state` represents the current state of the pro
```kotlin title=""
/* Backtracking algorithm framework */
fun backtrack(state: State?, choices: List, res: List?) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record the solution
recordSolution(state, res)
// Stop searching
return
}
- // Iterate through all choices
+ // Traverse all choices
for (choice in choices) {
- // Prune: check if the choice is valid
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Trial: make a choice, update the state
+ // Attempt: make a choice and update the state
makeChoice(state, choice)
backtrack(state, choices, res)
- // Retreat: undo the choice, revert to the previous state
+ // Backtrack: undo the choice and restore to the previous state
undoChoice(state, choice)
}
}
@@ -773,22 +1210,36 @@ In the following framework code, `state` represents the current state of the pro
=== "Ruby"
```ruby title=""
+ ### Backtracking algorithm framework ###
+ def backtrack(state, choices, res)
+ # Check if it is a solution
+ if is_solution?(state)
+ # Record the solution
+ record_solution(state, res)
+ return
+ end
+ # Traverse all choices
+ for choice in choices
+ # Pruning: check if the choice is valid
+ if is_valid?(state, choice)
+ # Attempt: make a choice and update the state
+ make_choice(state, choice)
+ backtrack(state, choices, res)
+ # Backtrack: undo the choice and restore to the previous state
+ undo_choice(state, choice)
+ end
+ end
+ end
```
-=== "Zig"
-
- ```zig title=""
-
- ```
-
-Now, we are able to solve Example Three using 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:
+Next, we solve Example 3 based on the framework code. The state `state` is the node traversal path, the choices `choices` are the left and right child nodes of the current node, and the result `res` is a list of paths:
=== "Python"
```python title="preorder_traversal_iii_template.py"
def is_solution(state: list[TreeNode]) -> bool:
- """Determine if the current state is a solution"""
+ """Check if the current state is a solution"""
return state and state[-1].val == 7
def record_solution(state: list[TreeNode], res: list[list[TreeNode]]):
@@ -796,7 +1247,7 @@ Now, we are able to solve Example Three using the framework code. The `state` is
res.append(list(state))
def is_valid(state: list[TreeNode], choice: TreeNode) -> bool:
- """Determine if the choice is legal under the current state"""
+ """Check if the choice is valid under the current state"""
return choice is not None and choice.val != 3
def make_choice(state: list[TreeNode], choice: TreeNode):
@@ -810,27 +1261,27 @@ Now, we are able to solve Example Three using the framework code. The `state` is
def backtrack(
state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]]
):
- """Backtracking algorithm: Example three"""
- # Check if it's a solution
+ """Backtracking algorithm: Example 3"""
+ # Check if it is a solution
if is_solution(state):
# Record solution
record_solution(state, res)
# Traverse all choices
for choice in choices:
- # Pruning: check if the choice is legal
+ # Pruning: check if the choice is valid
if is_valid(state, choice):
- # Attempt: make a choice, update the state
+ # Attempt: make choice, update state
make_choice(state, choice)
# Proceed to the next round of selection
backtrack(state, [choice.left, choice.right], res)
- # Retract: undo the choice, restore to the previous state
+ # Backtrack: undo choice, restore to previous state
undo_choice(state, choice)
```
=== "C++"
```cpp title="preorder_traversal_iii_template.cpp"
- /* Determine if the current state is a solution */
+ /* Check if the current state is a solution */
bool isSolution(vector &state) {
return !state.empty() && state.back()->val == 7;
}
@@ -840,7 +1291,7 @@ Now, we are able to solve Example Three using the framework code. The `state` is
res.push_back(state);
}
- /* Determine if the choice is legal under the current state */
+ /* Check if the choice is valid under the current state */
bool isValid(vector &state, TreeNode *choice) {
return choice != nullptr && choice->val != 3;
}
@@ -855,23 +1306,23 @@ Now, we are able to solve Example Three using the framework code. The `state` is
state.pop_back();
}
- /* Backtracking algorithm: Example three */
+ /* Backtracking algorithm: Example 3 */
void backtrack(vector &state, vector &choices, vector> &res) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record solution
recordSolution(state, res);
}
// Traverse all choices
for (TreeNode *choice : choices) {
- // Pruning: check if the choice is legal
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Attempt: make a choice, update the state
+ // Attempt: make choice, update state
makeChoice(state, choice);
// Proceed to the next round of selection
vector nextChoices{choice->left, choice->right};
backtrack(state, nextChoices, res);
- // Retract: undo the choice, restore to the previous state
+ // Backtrack: undo choice, restore to previous state
undoChoice(state, choice);
}
}
@@ -881,7 +1332,7 @@ Now, we are able to solve Example Three using the framework code. The `state` is
=== "Java"
```java title="preorder_traversal_iii_template.java"
- /* Determine if the current state is a solution */
+ /* Check if the current state is a solution */
boolean isSolution(List state) {
return !state.isEmpty() && state.get(state.size() - 1).val == 7;
}
@@ -891,7 +1342,7 @@ Now, we are able to solve Example Three using the framework code. The `state` is
res.add(new ArrayList<>(state));
}
- /* Determine if the choice is legal under the current state */
+ /* Check if the choice is valid under the current state */
boolean isValid(List state, TreeNode choice) {
return choice != null && choice.val != 3;
}
@@ -906,22 +1357,22 @@ Now, we are able to solve Example Three using the framework code. The `state` is
state.remove(state.size() - 1);
}
- /* Backtracking algorithm: Example three */
+ /* Backtracking algorithm: Example 3 */
void backtrack(List state, List choices, List> res) {
- // Check if it's a solution
+ // Check if it is a solution
if (isSolution(state)) {
// Record solution
recordSolution(state, res);
}
// Traverse all choices
for (TreeNode choice : choices) {
- // Pruning: check if the choice is legal
+ // Pruning: check if the choice is valid
if (isValid(state, choice)) {
- // Attempt: make a choice, update the state
+ // Attempt: make choice, update state
makeChoice(state, choice);
// Proceed to the next round of selection
backtrack(state, Arrays.asList(choice.left, choice.right), res);
- // Retract: undo the choice, restore to the previous state
+ // Backtrack: undo choice, restore to previous state
undoChoice(state, choice);
}
}
@@ -931,248 +1382,602 @@ Now, we are able to solve Example Three using the framework code. The `state` is
=== "C#"
```csharp title="preorder_traversal_iii_template.cs"
- [class]{preorder_traversal_iii_template}-[func]{IsSolution}
+ /* Check if the current state is a solution */
+ bool IsSolution(List state) {
+ return state.Count != 0 && state[^1].val == 7;
+ }
- [class]{preorder_traversal_iii_template}-[func]{RecordSolution}
+ /* Record solution */
+ void RecordSolution(List state, List> res) {
+ res.Add(new List(state));
+ }
- [class]{preorder_traversal_iii_template}-[func]{IsValid}
+ /* Check if the choice is valid under the current state */
+ bool IsValid(List state, TreeNode choice) {
+ return choice != null && choice.val != 3;
+ }
- [class]{preorder_traversal_iii_template}-[func]{MakeChoice}
+ /* Update state */
+ void MakeChoice(List state, TreeNode choice) {
+ state.Add(choice);
+ }
- [class]{preorder_traversal_iii_template}-[func]{UndoChoice}
+ /* Restore state */
+ void UndoChoice(List state, TreeNode choice) {
+ state.RemoveAt(state.Count - 1);
+ }
- [class]{preorder_traversal_iii_template}-[func]{Backtrack}
+ /* Backtracking algorithm: Example 3 */
+ void Backtrack(List state, List choices, List> res) {
+ // Check if it is a solution
+ if (IsSolution(state)) {
+ // Record solution
+ RecordSolution(state, res);
+ }
+ // Traverse all choices
+ foreach (TreeNode choice in choices) {
+ // Pruning: check if the choice is valid
+ if (IsValid(state, choice)) {
+ // Attempt: make choice, update state
+ MakeChoice(state, choice);
+ // Proceed to the next round of selection
+ Backtrack(state, [choice.left!, choice.right!], res);
+ // Backtrack: undo choice, restore to previous state
+ UndoChoice(state, choice);
+ }
+ }
+ }
```
=== "Go"
```go title="preorder_traversal_iii_template.go"
- [class]{}-[func]{isSolution}
+ /* Check if the current state is a solution */
+ func isSolution(state *[]*TreeNode) bool {
+ return len(*state) != 0 && (*state)[len(*state)-1].Val == 7
+ }
- [class]{}-[func]{recordSolution}
+ /* Record solution */
+ func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) {
+ *res = append(*res, append([]*TreeNode{}, *state...))
+ }
- [class]{}-[func]{isValid}
+ /* Check if the choice is valid under the current state */
+ func isValid(state *[]*TreeNode, choice *TreeNode) bool {
+ return choice != nil && choice.Val != 3
+ }
- [class]{}-[func]{makeChoice}
+ /* Update state */
+ func makeChoice(state *[]*TreeNode, choice *TreeNode) {
+ *state = append(*state, choice)
+ }
- [class]{}-[func]{undoChoice}
+ /* Restore state */
+ func undoChoice(state *[]*TreeNode, choice *TreeNode) {
+ *state = (*state)[:len(*state)-1]
+ }
- [class]{}-[func]{backtrackIII}
+ /* Backtracking algorithm: Example 3 */
+ func backtrackIII(state *[]*TreeNode, choices *[]*TreeNode, res *[][]*TreeNode) {
+ // Check if it is a solution
+ if isSolution(state) {
+ // Record solution
+ recordSolution(state, res)
+ }
+ // Traverse all choices
+ for _, choice := range *choices {
+ // Pruning: check if the choice is valid
+ if isValid(state, choice) {
+ // Attempt: make choice, update state
+ makeChoice(state, choice)
+ // Proceed to the next round of selection
+ temp := make([]*TreeNode, 0)
+ temp = append(temp, choice.Left, choice.Right)
+ backtrackIII(state, &temp, res)
+ // Backtrack: undo choice, restore to previous state
+ undoChoice(state, choice)
+ }
+ }
+ }
```
=== "Swift"
```swift title="preorder_traversal_iii_template.swift"
- [class]{}-[func]{isSolution}
+ /* Check if the current state is a solution */
+ func isSolution(state: [TreeNode]) -> Bool {
+ !state.isEmpty && state.last!.val == 7
+ }
- [class]{}-[func]{recordSolution}
+ /* Record solution */
+ func recordSolution(state: [TreeNode], res: inout [[TreeNode]]) {
+ res.append(state)
+ }
- [class]{}-[func]{isValid}
+ /* Check if the choice is valid under the current state */
+ func isValid(state: [TreeNode], choice: TreeNode?) -> Bool {
+ choice != nil && choice!.val != 3
+ }
- [class]{}-[func]{makeChoice}
+ /* Update state */
+ func makeChoice(state: inout [TreeNode], choice: TreeNode) {
+ state.append(choice)
+ }
- [class]{}-[func]{undoChoice}
+ /* Restore state */
+ func undoChoice(state: inout [TreeNode], choice: TreeNode) {
+ state.removeLast()
+ }
- [class]{}-[func]{backtrack}
+ /* Backtracking algorithm: Example 3 */
+ func backtrack(state: inout [TreeNode], choices: [TreeNode], res: inout [[TreeNode]]) {
+ // Check if it is a solution
+ if isSolution(state: state) {
+ recordSolution(state: state, res: &res)
+ }
+ // Traverse all choices
+ for choice in choices {
+ // Pruning: check if the choice is valid
+ if isValid(state: state, choice: choice) {
+ // Attempt: make choice, update state
+ makeChoice(state: &state, choice: choice)
+ // Proceed to the next round of selection
+ backtrack(state: &state, choices: [choice.left, choice.right].compactMap { $0 }, res: &res)
+ // Backtrack: undo choice, restore to previous state
+ undoChoice(state: &state, choice: choice)
+ }
+ }
+ }
```
=== "JS"
```javascript title="preorder_traversal_iii_template.js"
- [class]{}-[func]{isSolution}
+ /* Check if the current state is a solution */
+ function isSolution(state) {
+ return state && state[state.length - 1]?.val === 7;
+ }
- [class]{}-[func]{recordSolution}
+ /* Record solution */
+ function recordSolution(state, res) {
+ res.push([...state]);
+ }
- [class]{}-[func]{isValid}
+ /* Check if the choice is valid under the current state */
+ function isValid(state, choice) {
+ return choice !== null && choice.val !== 3;
+ }
- [class]{}-[func]{makeChoice}
+ /* Update state */
+ function makeChoice(state, choice) {
+ state.push(choice);
+ }
- [class]{}-[func]{undoChoice}
+ /* Restore state */
+ function undoChoice(state) {
+ state.pop();
+ }
- [class]{}-[func]{backtrack}
+ /* Backtracking algorithm: Example 3 */
+ function backtrack(state, choices, res) {
+ // Check if it is a solution
+ if (isSolution(state)) {
+ // Record solution
+ recordSolution(state, res);
+ }
+ // Traverse all choices
+ for (const choice of choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Attempt: make choice, update state
+ makeChoice(state, choice);
+ // Proceed to the next round of selection
+ backtrack(state, [choice.left, choice.right], res);
+ // Backtrack: undo choice, restore to previous state
+ undoChoice(state);
+ }
+ }
+ }
```
=== "TS"
```typescript title="preorder_traversal_iii_template.ts"
- [class]{}-[func]{isSolution}
+ /* Check if the current state is a solution */
+ function isSolution(state: TreeNode[]): boolean {
+ return state && state[state.length - 1]?.val === 7;
+ }
- [class]{}-[func]{recordSolution}
+ /* Record solution */
+ function recordSolution(state: TreeNode[], res: TreeNode[][]): void {
+ res.push([...state]);
+ }
- [class]{}-[func]{isValid}
+ /* Check if the choice is valid under the current state */
+ function isValid(state: TreeNode[], choice: TreeNode): boolean {
+ return choice !== null && choice.val !== 3;
+ }
- [class]{}-[func]{makeChoice}
+ /* Update state */
+ function makeChoice(state: TreeNode[], choice: TreeNode): void {
+ state.push(choice);
+ }
- [class]{}-[func]{undoChoice}
+ /* Restore state */
+ function undoChoice(state: TreeNode[]): void {
+ state.pop();
+ }
- [class]{}-[func]{backtrack}
+ /* Backtracking algorithm: Example 3 */
+ function backtrack(
+ state: TreeNode[],
+ choices: TreeNode[],
+ res: TreeNode[][]
+ ): void {
+ // Check if it is a solution
+ if (isSolution(state)) {
+ // Record solution
+ recordSolution(state, res);
+ }
+ // Traverse all choices
+ for (const choice of choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Attempt: make choice, update state
+ makeChoice(state, choice);
+ // Proceed to the next round of selection
+ backtrack(state, [choice.left, choice.right], res);
+ // Backtrack: undo choice, restore to previous state
+ undoChoice(state);
+ }
+ }
+ }
```
=== "Dart"
```dart title="preorder_traversal_iii_template.dart"
- [class]{}-[func]{isSolution}
+ /* Check if the current state is a solution */
+ bool isSolution(List state) {
+ return state.isNotEmpty && state.last.val == 7;
+ }
- [class]{}-[func]{recordSolution}
+ /* Record solution */
+ void recordSolution(List state, List> res) {
+ res.add(List.from(state));
+ }
- [class]{}-[func]{isValid}
+ /* Check if the choice is valid under the current state */
+ bool isValid(List state, TreeNode? choice) {
+ return choice != null && choice.val != 3;
+ }
- [class]{}-[func]{makeChoice}
+ /* Update state */
+ void makeChoice(List state, TreeNode? choice) {
+ state.add(choice!);
+ }
- [class]{}-[func]{undoChoice}
+ /* Restore state */
+ void undoChoice(List state, TreeNode? choice) {
+ state.removeLast();
+ }
- [class]{}-[func]{backtrack}
+ /* Backtracking algorithm: Example 3 */
+ void backtrack(
+ List state,
+ List choices,
+ List> res,
+ ) {
+ // Check if it is a solution
+ if (isSolution(state)) {
+ // Record solution
+ recordSolution(state, res);
+ }
+ // Traverse all choices
+ for (TreeNode? choice in choices) {
+ // Pruning: check if the choice is valid
+ if (isValid(state, choice)) {
+ // Attempt: make choice, update state
+ makeChoice(state, choice);
+ // Proceed to the next round of selection
+ backtrack(state, [choice!.left, choice.right], res);
+ // Backtrack: undo choice, restore to previous state
+ undoChoice(state, choice);
+ }
+ }
+ }
```
=== "Rust"
```rust title="preorder_traversal_iii_template.rs"
- [class]{}-[func]{is_solution}
+ /* Check if the current state is a solution */
+ fn is_solution(state: &mut Vec>>) -> bool {
+ return !state.is_empty() && state.last().unwrap().borrow().val == 7;
+ }
- [class]{}-[func]{record_solution}
+ /* Record solution */
+ fn record_solution(
+ state: &mut Vec>>,
+ res: &mut Vec>>>,
+ ) {
+ res.push(state.clone());
+ }
- [class]{}-[func]{is_valid}
+ /* Check if the choice is valid under the current state */
+ fn is_valid(_: &mut Vec>>, choice: Option<&Rc>>) -> bool {
+ return choice.is_some() && choice.unwrap().borrow().val != 3;
+ }
- [class]{}-[func]{make_choice}
+ /* Update state */
+ fn make_choice(state: &mut Vec>>, choice: Rc>) {
+ state.push(choice);
+ }
- [class]{}-[func]{undo_choice}
+ /* Restore state */
+ fn undo_choice(state: &mut Vec>>, _: Rc>) {
+ state.pop();
+ }
- [class]{}-[func]{backtrack}
+ /* Backtracking algorithm: Example 3 */
+ fn backtrack(
+ state: &mut Vec>>,
+ choices: &Vec