diff --git a/en/CONTRIBUTING.md b/en/CONTRIBUTING.md index 5e226da57..80b34b858 100644 --- a/en/CONTRIBUTING.md +++ b/en/CONTRIBUTING.md @@ -1,134 +1,28 @@ -# Contributing guidelines for Chinese-to-English +# Contributing to the English edition -We are working on translating "Hello Algo" from Chinese to English with the following approach: +Help make *Hello Algo* accurate, clear, and enjoyable for readers learning data structures and algorithms. Corrections to explanations, terminology, code comments, examples, and links are welcome. -1. **AI translation**: Carry out an initial pass of translations using large language models. -2. **Human optimization**: Manually refine the machine-generated outputs to ensure authenticity and accuracy. -3. **Pull request review**: The optimized translation will be double checked by the reviewers through GitHub pull request workflow. -4. Repeat steps `2.` and `3.` for further improvements. +For the general contribution workflow, see [Contributing](https://www.hello-algo.com/en/chapter_appendix/contribution/). You can use the edit button on a book page for a small correction, or fork the repository, make and test your changes locally, and submit a pull request. -translation_pipeline +## Translating and reviewing a chapter -## Join us +Use the current Simplified Chinese source as the authority for meaning. Read the complete passage and its examples before changing a sentence. The English should convey the full explanation in natural English, including qualifications and assumptions. -We're seeking contributors who meet the following criteria. +1. Translate the complete document, including headings, tables, figure captions, callouts, questions, and code comments. Preserve the algorithms, example data, formulas, links, and code references. +2. Compare the English with the Chinese, checking technical meaning, completeness, terminology, and the steps in each example. +3. Read the English on its own. Make it clear and natural for a beginner, then check any revisions against the Chinese again. +4. Ask another reviewer to check the whole document in context. Resolve differences in interpretation before treating the translation as ready. -- **Technical background**: Strong foundation in computer science, particularly in data structures and algorithms. -- **Language skills**: Native proficiency in Chinese with professional-level English, or native English. -- **Available time**: Dedicated to contributing to open-source projects with a willingness to engage in long-term translation efforts. +Use clear American English and sentence case for headings. Prefer direct explanations to word-for-word renderings of Chinese sentence structure. Use the same term consistently, and distinguish concepts such as full, perfect, and complete binary trees by their definitions. See the [terminology table](https://www.hello-algo.com/en/chapter_appendix/terminology/) for reference. -That is, our contributors are computer scientists, engineers, and students from different linguistic backgrounds, and their objectives have different focal points: +If the Chinese source appears incorrect or ambiguous, describe the issue in the pull request. A translation should not silently change the algorithm or introduce a new technical claim. Likewise, keep example strings unchanged when they are program data, even if those strings contain Chinese characters. -- **Native Chinese with professional working English**: Ensuring translation accuracy and consistency between CN and EN versions. -- **Native English**: Enhance the authenticity and fluency of the English content to flow naturally and to be engaging. +## Checking the result -> [!note] -> If you are interested in joining us, don't hesitate to contact me via krahetx@gmail.com or WeChat `krahets-jyd`. -> -> We use this [Notion page](https://hello-algo.notion.site/chinese-to-english) to track progress and assign tasks. Please visit it for more details. +Review the rendered page as well as the Markdown. Check that formulas, emphasis, tables, images, links, code tabs, and PythonTutor examples still work. Translate the comments and explanatory output inside PythonTutor examples without deleting explanations to shorten the links. -## Translation process +Figure and table numbers are generated during the build; keep their placeholders intact. Chapter exercise pages are also generated: changes to exercise wording belong in the source exercise data and translation inputs in the companion [workspace repository](https://github.com/krahets/hello-algo-workspace), rather than directly in the generated pages. -> [!important] -> Before diving in, ensure you're comfortable with the GitHub pull request workflow and have read the "Translation standards" and "Pseudo-code for translation" below. +Keep a pull request focused on a complete document when possible; a small correction can be smaller. Explain what changed and how you checked it. Automated checks help find missing content and structural or code changes, but do not replace a careful language review. -1. **Task assignment**: Self-assign a task in the Notion workspace. -2. **Translation**: Optimize the translation on your local PC, referring to the “Translation Pseudo-Code” section below for more details. -3. **Peer review**: Carefully review your changes before submitting a Pull Request (PR). The PR will be merged into the main branch after approval from two reviewers. - -## Translation standards - -> [!tip] -> **The "Accuracy" and "Authenticity" are primarily handled by native Chinese speakers and native English speakers, respectively.** -> -> In some instances, "Accuracy (consistency)" and "Authenticity" represent a trade-off, where optimizing one aspect could significantly affect the other. In such cases, please leave a comment in the pull request for discussion. - -**Accuracy**: - -- Maintain consistency in terminology across translations by referring to the [Terminology](https://www.hello-algo.com/chapter_appendix/terminology/) section. -- Prioritize technical accuracy and maintain the tone and style of the Chinese version. -- Always take into account the content and context of the Chinese version to ensure modifications are accurate and comprehensive. - -**Authenticity**: - -- Translations should flow naturally and fluently, adhering to English expression conventions. -- Always consider the context of the content to harmonize the article. -- Be aware of cultural differences between Chinese and English. For instance, Chinese "pinyin" does not exist in English. -- If the optimized sentence could alter the original meaning, please add a comment for discussion. - -**Formatting**: - -- Figures and tables will be automatically numbered during deployment, so DO NOT manually number them. -- Each PR should cover at least one complete document to ensure manageable review sizes, except for bug fixes. - -**Review**: - -- During the review, prioritize evaluating the changes, consulting the surrounding context as needed. -- Learning from each other's perspectives can lead to better translations and more cohesive results. - -## Translation pseudo-code - -The following pseudo-code models the steps in a typical translation process. - -```python -def optimize_translation(markdown_texts, lang_skill): - """Optimize the translation""" - for sentence in markdown_texts: - """Accuracy is handled primarily by native Chinese speakers""" - if lang_skill is "Native Chinese + Professional working English": - if is_accurate_Chinese_to_English(sentence): - continue - # Optimize the accuracy - result = refine_accuracy(sentence) - - """ - Authenticity is handled primarily by native English speakers - and secondarily by native Chinese speakers - """ - if is_authentic_English(sentence): - continue - # Optimize the authenticity - result = refine_authenticity(sentence) - # Add comments in the PR if it may break consistency - if break_consistency(result): - add_comment(description) - - pull_request = submit_pull_request(markdown_texts) - # The PR will be merged after approved by >= 2 reviewers - while count_approvals(pull_request) < 2: - continue - merge(pull_request) -``` - -The following pseudo-code is for the reviewers: - -```python -def review_pull_requests(pull_request, lang_skill): - """Review the PR""" - # Loop through all the changes in the PR - while is_anything_left_to_review(pull_request): - change = get_next_change(pull_request) - - """Accuracy is handled primarily by native Chinese speakers""" - if lang_skill is "Native Chinese + Professional working English": - # Check the accuracy(consistency) between CN and EN versions - if is_accurate_Chinese_to_English(change): - continue - # Optimize the accuracy(consistency) - result = refine_accuracy(change) - # Add comments in the PR - add_comment(result) - - """ - Authenticity is handled primarily by native English speakers - and secondarily by native Chinese speakers - """ - if is_authentic_English(change): - continue - # Optimize the authenticity if it is not authentic English - result = refine_authenticity(change) - # Add comments in the PR - add_comment(result) - - approve(pull_request) -``` +If you find a problem in an image, open an [issue](https://github.com/krahets/hello-algo/issues) or leave a comment describing it so the image can be updated. diff --git a/en/README.md b/en/README.md index ce941b7cc..f115066f8 100644 --- a/en/README.md +++ b/en/README.md @@ -4,9 +4,9 @@

- hello-algo-typing-svg + hello-algo-typing-svg
- Data structures and algorithms tutorial with animated illustrations and ready-to-run code + A data structures and algorithms tutorial with animated illustrations and code you can run with one click

@@ -38,7 +38,7 @@

- 简体中文 + 简体中文 | 繁體中文 | @@ -51,41 +51,37 @@ 한국어

-## The book +## About this book -This open-source project aims to create a free and beginner-friendly crash course for data structures and algorithms. +This project aims to create a free, open-source introduction to data structures and algorithms that is accessible to beginners. -- Animated illustrations, easy-to-understand content, and a smooth learning curve help beginners explore the "knowledge map" of data structures and algorithms. -- Run code with just one click, helping readers improve their programming skills and understand the working principle of algorithms and the underlying implementation of data structures. -- Promoting learning by teaching, feel free to ask questions and share insights. Let's grow together through discussion. +- Animated illustrations throughout the book, clear explanations, and a gradual learning curve guide beginners through data structures and algorithms. +- Code that runs with one click helps readers build programming skills through practice and understand how algorithms work and how data structures are implemented. +- Readers are encouraged to learn together. Ask questions and share insights in the comments so everyone can benefit from the discussion. -If you find this book helpful, please give it a Star :star: to support us, thank you! +If you find this book helpful, please support it by clicking Star :star: at the top right of the page. Thank you! ## Endorsements -> "An easy-to-understand book on data structures and algorithms, which guides readers to learn by minds-on and hands-on. Strongly recommended for algorithm beginners!" +> “An accessible introduction to data structures and algorithms that encourages readers to learn by thinking and doing. Highly recommended for anyone new to algorithms.” > -> **—— Junhui Deng, Professor, Department of computer science and technology, Tsinghua University** +> **— Junhui Deng, Professor, Department of Computer Science, Tsinghua University** -> "If I had 'Hello Algo' when I was learning data structures and algorithms, it would have been 10 times easier!" +> “If I had had *Hello Algo* when I was learning data structures and algorithms, it would have been ten times easier!” > -> **—— Mu Li, Senior Principal Scientist, Amazon** +> **— Mu Li, Senior Principal Scientist, Amazon** ## Contributing -> [!Important] -> -> Welcome to contribute to Chinese-to-English translation! For more information please see [CONTRIBUTING.md](CONTRIBUTING.md). +This open-source book is continually being updated. You are welcome to join the project and help improve the learning material for readers. -This open-source book is continuously being updated, and we welcome your participation in this project to provide better learning content for readers. +- [Content corrections](https://www.hello-algo.com/en/chapter_appendix/contribution/): Help fix or report grammatical errors, missing content, ambiguous wording, broken links, and code bugs in the comments. +- [Code ports](https://github.com/krahets/hello-algo/issues/15): Contributions in different programming languages are welcome. The book supports 12 languages, including Python, Java, C++, Go, and JavaScript. +- Translation and review: Help translate and proofread the book so more readers can learn data structures and algorithms in their own language. -- [Content Correction](https://www.hello-algo.com/en/chapter_appendix/contribution/): Please help us correct or point out mistakes in the comments section such as grammatical errors, missing content, ambiguities, invalid links, or code bugs. -- [Code Transpilation](https://github.com/krahets/hello-algo/issues/15): We look forward to your contributions in various programming languages. We currently support 12 languages including Python, Java, C++, Go, and JavaScript. -- Translation Review: Welcome to contribute to multilingual translation and review work, helping more readers learn data structures and algorithms in their native languages. +Feedback and suggestions are welcome. If you have questions, open an issue or contact `krahets-jyd` on WeChat. -We welcome your valuable suggestions and feedback. If you have any questions, please submit Issues or reach out via WeChat: `krahets-jyd`. - -We would like to dedicate our thanks to all the contributors of this book. It is their selfless dedication that has made this book better. They are: +Thank you to everyone who has contributed to this open-source book. Their generous efforts have made it better:

diff --git a/en/codes/Dockerfile b/en/codes/Dockerfile new file mode 100644 index 000000000..7885225de --- /dev/null +++ b/en/codes/Dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu:latest + +# Use Aliyun mirrors for Ubuntu packages +RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \ + sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \ + sed -i 's/ports.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list + +RUN apt-get update && apt-get install -y wget + +# Install the requested language environments +ARG LANGS +RUN for LANG in $LANGS; do \ + case $LANG in \ + python) \ + apt-get install -y python3.10 && \ + update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1 ;; \ + cpp) \ + apt-get install -y g++ gdb ;; \ + java) \ + apt-get install -y openjdk-17-jdk ;; \ + csharp) \ + wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \ + dpkg -i packages-microsoft-prod.deb && \ + apt-get update && \ + apt-get install -y dotnet-sdk-8.0 ;; \ + # More languages... + *) \ + echo "Warning: No installation workflow for $LANG" ;; \ + esac \ + done + +WORKDIR /codes +COPY ./ ./ + +CMD ["/bin/bash"] diff --git a/en/codes/c/.gitignore b/en/codes/c/.gitignore index 698ee4e21..69bf8da3b 100644 --- a/en/codes/c/.gitignore +++ b/en/codes/c/.gitignore @@ -1,8 +1,8 @@ # Ignore all * -# Unignore all with extensions +# Unignore all files with extensions !*.* -# Unignore all dirs +# Unignore all directories !*/ *.dSYM/ diff --git a/en/codes/c/chapter_array_and_linkedlist/array.c b/en/codes/c/chapter_array_and_linkedlist/array.c index 269a528c1..a1aabfaf8 100644 --- a/en/codes/c/chapter_array_and_linkedlist/array.c +++ b/en/codes/c/chapter_array_and_linkedlist/array.c @@ -6,60 +6,60 @@ #include "../utils/common.h" -/* Random access to element */ +/* Access an element at a random index */ int randomAccess(int *nums, int size) { - // Randomly select a number from interval [0, size) + // Choose a random number in [0, size) int randomIndex = rand() % size; - // Retrieve and return the random element + // Get and return the randomly selected element int randomNum = nums[randomIndex]; return randomNum; } -/* Extend array length */ +/* Extend the array length */ int *extend(int *nums, int size, int enlarge) { - // Initialize an array with extended length + // Initialize an array with the 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 + // Initialize the newly added space for (int i = size; i < size + enlarge; i++) { res[i] = 0; } - // Return the extended new array + // Return the new, extended array return res; } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for (int i = size - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num; } -/* Remove the element at index index */ -// Note: stdio.h occupies the remove keyword +/* Remove the element at index `index` */ +// Note: stdio.h already declares a function named remove void removeItem(int *nums, int size, int index) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (int i = index; i < size - 1; i++) { nums[i] = nums[i + 1]; } } -/* Traverse array */ +/* Traverse the array */ void traverse(int *nums, int size) { int count = 0; - // Traverse array by index + // Traverse the array by index for (int i = 0; i < size; i++) { count += nums[i]; } } -/* Find the specified element in the array */ +/* Find a specified element in the array */ int find(int *nums, int size, int target) { for (int i = 0; i < size; i++) { if (nums[i] == target) @@ -68,12 +68,12 @@ int find(int *nums, int size, int target) { return -1; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize the random seed once */ + /* Initialize the random seed (only once) */ srand((unsigned)time(NULL)); - /* Initialize array */ + /* Initialize the array */ int size = 5; int arr[5]; printf("Array arr = "); @@ -83,33 +83,33 @@ int main() { printf("Array nums = "); printArray(nums, size); - /* Insert element */ + /* Random access */ int randomNum = randomAccess(nums, size); - printf("Get random element %d from nums", randomNum); + printf("Random element selected from nums: %d", randomNum); - /* Traverse array */ + /* Extend the length */ int enlarge = 3; int *res = extend(nums, size, enlarge); size += enlarge; - printf("Extend array length to 8, resulting in nums = "); + printf("Extend the array length to 8, giving nums = "); printArray(res, size); - /* Insert element */ + /* Insert an element */ insert(res, size, 6, 3); - printf("Insert number 6 at index 3, resulting in nums = "); + printf("Insert the number 6 at index 3, giving nums = "); printArray(res, size); - /* Remove element */ + /* Remove an element */ removeItem(res, size, 2); - printf("Remove element at index 2, resulting in nums = "); + printf("Remove the element at index 2, giving nums = "); printArray(res, size); - /* Traverse array */ + /* Traverse the array */ traverse(res, size); - /* Find element */ + /* Find an element */ int index = find(res, size, 3); - printf("Find element 3 in res, index = %d\n", index); + printf("Find element 3 in res; its index = %d\n", index); /* Free memory */ free(res); diff --git a/en/codes/c/chapter_array_and_linkedlist/linked_list.c b/en/codes/c/chapter_array_and_linkedlist/linked_list.c index e9238f724..90d37a284 100644 --- a/en/codes/c/chapter_array_and_linkedlist/linked_list.c +++ b/en/codes/c/chapter_array_and_linkedlist/linked_list.c @@ -14,7 +14,7 @@ void insert(ListNode *n0, ListNode *P) { } /* Remove the first node after node n0 in the linked list */ -// Note: stdio.h occupies the remove keyword +// Note: stdio.h already declares a function named remove void removeItem(ListNode *n0) { if (!n0->next) return; @@ -26,7 +26,7 @@ void removeItem(ListNode *n0) { free(P); } -/* Access the node at index 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) @@ -48,40 +48,40 @@ int find(ListNode *head, int target) { return -1; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize linked list */ - // Initialize each node + /* Initialize the linked list */ + // Initialize the individual nodes ListNode *n0 = newListNode(1); ListNode *n1 = newListNode(3); ListNode *n2 = newListNode(2); ListNode *n3 = newListNode(5); ListNode *n4 = newListNode(4); - // Build references between nodes + // Set up references between the nodes n0->next = n1; n1->next = n2; n2->next = n3; n3->next = n4; - printf("Initialized linked list is\r\n"); + printf("Initial linked list:\r\n"); printLinkedList(n0); - /* Insert node */ + /* Insert a node */ insert(n0, newListNode(0)); - printf("Linked list after node insertion is\r\n"); + printf("Linked list after inserting a node:\r\n"); printLinkedList(n0); - /* Remove node */ + /* Remove a node */ removeItem(n0); - printf("Linked list after node deletion is\r\n"); + printf("Linked list after removing a node:\r\n"); printLinkedList(n0); - /* Access node */ + /* Access a node */ ListNode *node = access(n0, 3); - printf("Value of node at index 3 in linked list = %d\r\n", node->val); + printf("Value of the linked list node at index 3 = %d\r\n", node->val); - /* Search node */ + /* Find a node */ int index = find(n0, 2); - printf("Index of node with value 2 in linked list = %d\r\n", index); + printf("Index of the linked list node with value 2 = %d\r\n", index); // Free memory freeMemoryLinkedList(n0); diff --git a/en/codes/c/chapter_array_and_linkedlist/my_list.c b/en/codes/c/chapter_array_and_linkedlist/my_list.c index 52d22b857..e8d9d64b9 100644 --- a/en/codes/c/chapter_array_and_linkedlist/my_list.c +++ b/en/codes/c/chapter_array_and_linkedlist/my_list.c @@ -6,12 +6,12 @@ #include "../utils/common.h" -/* List class */ +/* List structure */ typedef struct { - int *arr; // Array (stores list elements) + int *arr; // Array (stores the list elements) int capacity; // List capacity int size; // List size - int extendRatio; // List expansion multiplier + int extendRatio; // List capacity growth factor } MyList; void extendCapacity(MyList *nums); @@ -32,43 +32,43 @@ void delMyList(MyList *nums) { free(nums); } -/* Get list length */ +/* Get the list size */ int size(MyList *nums) { return nums->size; } -/* Get list capacity */ +/* Get the list capacity */ int capacity(MyList *nums) { return nums->capacity; } -/* Update element */ +/* Access an element */ int get(MyList *nums, int index) { assert(index >= 0 && index < nums->size); return nums->arr[index]; } -/* Add elements at the end */ +/* Update an element */ void set(MyList *nums, int index, int num) { assert(index >= 0 && index < nums->size); nums->arr[index] = num; } -/* Direct traversal of list elements */ +/* Append an element at the end */ void add(MyList *nums, int num) { if (size(nums) == capacity(nums)) { - extendCapacity(nums); // Expand capacity + extendCapacity(nums); // Grow the capacity } nums->arr[size(nums)] = num; nums->size++; } -/* Sort list */ +/* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if (size(nums) == capacity(nums)) { - extendCapacity(nums); // Expand capacity + extendCapacity(nums); // Grow the capacity } for (int i = size(nums); i > index; --i) { nums->arr[i] = nums->arr[i - 1]; @@ -77,8 +77,8 @@ void insert(MyList *nums, int index, int num) { nums->size++; } -/* Remove element */ -// Note: stdio.h occupies the remove keyword +/* Remove an element */ +// Note: stdio.h already declares a function named remove int removeItem(MyList *nums, int index) { assert(index >= 0 && index < size(nums)); int num = nums->arr[index]; @@ -89,35 +89,35 @@ int removeItem(MyList *nums, int index) { return num; } -/* Driver Code */ +/* Grow the list capacity */ void extendCapacity(MyList *nums) { - // Allocate space first + // Allocate new 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 + // Copy the old data into the new array for (int i = 0; i < size(nums); i++) extend[i] = nums->arr[i]; - // Free old data + // Free the old array free(temp); - // Update new data + // Update the array pointer and capacity nums->arr = extend; nums->capacity = newCapacity; } -/* Convert list to Array for printing */ +/* Convert the list to an array for printing */ int *toArray(MyList *nums) { return nums->arr; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize list */ + /* Initialize the list */ MyList *nums = newMyList(); - /* Direct traversal of list elements */ + /* Append an element at the end */ add(nums, 1); add(nums, 3); add(nums, 2); @@ -125,38 +125,38 @@ int main() { add(nums, 4); printf("List nums = "); printArray(toArray(nums), size(nums)); - printf("Capacity = %d, Length = %d\n", capacity(nums), size(nums)); + printf("Capacity = %d, size = %d\n", capacity(nums), size(nums)); - /* Sort list */ + /* Insert an element in the middle */ insert(nums, 3, 6); - printf("Insert number 6 at index 3, resulting in nums = "); + printf("Insert the number 6 at index 3, giving nums = "); printArray(toArray(nums), size(nums)); - /* Remove element */ + /* Remove an element */ removeItem(nums, 3); - printf("Remove element at index 3, resulting in nums = "); + printf("Remove the element at index 3, giving nums = "); printArray(toArray(nums), size(nums)); - /* Update element */ + /* Access an element */ int num = get(nums, 1); - printf("Access element at index 1, get num = %d\n", num); + printf("Access the element at index 1; num = %d\n", num); - /* Add elements at the end */ + /* Update an element */ set(nums, 1, 0); - printf("Update element at index 1 to 0, resulting in nums = "); + printf("Update the element at index 1 to 0, giving nums = "); printArray(toArray(nums), size(nums)); - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for (int i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth add(nums, i); } - printf("List nums after expansion = "); + printf("List after capacity growth, nums = "); printArray(toArray(nums), size(nums)); - printf("Capacity = %d, Length = %d\n", capacity(nums), size(nums)); + printf("Capacity = %d, size = %d\n", capacity(nums), size(nums)); - /* Free allocated memory */ + /* Free the allocated memory */ delMyList(nums); return 0; diff --git a/en/codes/c/chapter_backtracking/n_queens.c b/en/codes/c/chapter_backtracking/n_queens.c index 4acd49ab3..1912e4620 100644 --- a/en/codes/c/chapter_backtracking/n_queens.c +++ b/en/codes/c/chapter_backtracking/n_queens.c @@ -8,10 +8,10 @@ #define MAX_SIZE 100 -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE], bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if (row == n) { res[*resSize] = (char **)malloc(sizeof(char *) * n); for (int i = 0; i < n; ++i) { @@ -21,38 +21,38 @@ void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int (*resSize)++; return; } - // Traverse all columns + // Iterate over all columns for (int col = 0; col < n; col++) { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square int diag1 = row - col + n - 1; int diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = 'Q'; cols[col] = diags1[diag1] = diags2[diag2] = true; - // Place the next row + // Place a queen in the next row backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = '#'; cols[col] = diags1[diag1] = diags2[diag2] = false; } } } -/* Solve N queens */ +/* Solve the n-queens problem */ char ***nQueens(int n, int *returnSize) { char state[MAX_SIZE][MAX_SIZE]; - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { state[i][j] = '#'; } state[i][n] = '\0'; } - bool cols[MAX_SIZE] = {false}; // Record whether there is a queen in the column - bool diags1[2 * MAX_SIZE - 1] = {false}; // Record whether there is a queen on the main diagonal - bool diags2[2 * MAX_SIZE - 1] = {false}; // Record whether there is a queen on the anti-diagonal + bool cols[MAX_SIZE] = {false}; // Track whether a column contains a queen + bool diags1[2 * MAX_SIZE - 1] = {false}; // Track whether a main diagonal contains a queen + bool diags2[2 * MAX_SIZE - 1] = {false}; // Track whether an anti-diagonal contains a queen char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE); *returnSize = 0; @@ -60,14 +60,14 @@ char ***nQueens(int n, int *returnSize) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int n = 4; int returnSize; char ***res = nQueens(n, &returnSize); - printf("Input board size is %d\n", n); - printf("Total queen placement solutions: %d\n", returnSize); + printf("Board width and height: %d\n", n); + printf("Number of queen placement solutions: %d\n", returnSize); for (int i = 0; i < returnSize; ++i) { for (int j = 0; j < n; ++j) { printf("["); diff --git a/en/codes/c/chapter_backtracking/permutations_i.c b/en/codes/c/chapter_backtracking/permutations_i.c index 48a6d8451..264f9704c 100644 --- a/en/codes/c/chapter_backtracking/permutations_i.c +++ b/en/codes/c/chapter_backtracking/permutations_i.c @@ -6,12 +6,12 @@ #include "../utils/common.h" -// Assume at most 1000 permutations +// Assume there are at most 1000 permutations #define MAX_SIZE 1000 -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (stateSize == choicesSize) { res[*resSize] = (int *)malloc(choicesSize * sizeof(int)); for (int i = 0; i < choicesSize; i++) { @@ -20,17 +20,17 @@ void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *s (*resSize)++; return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choicesSize; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state[stateSize] = choice; - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; } } @@ -54,7 +54,7 @@ int **permutationsI(int *nums, int numsSize, int *returnSize) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {1, 2, 3}; int numsSize = sizeof(nums) / sizeof(nums[0]); diff --git a/en/codes/c/chapter_backtracking/permutations_ii.c b/en/codes/c/chapter_backtracking/permutations_ii.c index 34073f779..7b0fb9dd0 100644 --- a/en/codes/c/chapter_backtracking/permutations_ii.c +++ b/en/codes/c/chapter_backtracking/permutations_ii.c @@ -6,12 +6,12 @@ #include "../utils/common.h" -// Assume at most 1000 permutations, max element value 1000 +// Assume at most 1000 permutations and a maximum element value of 1000 #define MAX_SIZE 1000 -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (stateSize == choicesSize) { res[*resSize] = (int *)malloc(choicesSize * sizeof(int)); for (int i = 0; i < choicesSize; i++) { @@ -20,19 +20,19 @@ void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *s (*resSize)++; return; } - // Traverse all choices + // Iterate over all choices bool duplicated[MAX_SIZE] = {false}; for (int i = 0; i < choicesSize; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && !duplicated[choice]) { - // Attempt: make choice, update state - duplicated[choice] = true; // Record the selected element value + // Try: make a choice and update the state + duplicated[choice] = true; // Record the chosen value selected[i] = true; state[stateSize] = choice; - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; } } @@ -56,7 +56,7 @@ int **permutationsII(int *nums, int numsSize, int *returnSize) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {1, 1, 2}; int numsSize = sizeof(nums) / sizeof(nums[0]); diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_i_compact.c b/en/codes/c/chapter_backtracking/preorder_traversal_i_compact.c index aa969fabd..fc9ef6252 100644 --- a/en/codes/c/chapter_backtracking/preorder_traversal_i_compact.c +++ b/en/codes/c/chapter_backtracking/preorder_traversal_i_compact.c @@ -6,36 +6,36 @@ #include "../utils/common.h" -// Assume result length not exceeding 100 +// Assume the result contains at most 100 entries #define MAX_SIZE 100 TreeNode *res[MAX_SIZE]; int resSize = 0; -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ void preOrder(TreeNode *root) { if (root == NULL) { return; } if (root->val == 7) { - // Record solution + // Record the solution res[resSize++] = root; } preOrder(root->left); preOrder(root->right); } -/* Driver Code */ +/* Driver code */ int main() { int arr[] = {1, 7, 3, 4, 5, 6, 7}; TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); - printf("\nInitialize binary tree\n"); + printf("\nInitialize the binary tree\n"); printTree(root); // Preorder traversal preOrder(root); - printf("\nOutput all nodes with value 7\n"); + printf("\nPrint all nodes with value 7\n"); int *vals = malloc(resSize * sizeof(int)); for (int i = 0; i < resSize; i++) { vals[i] = res[i]->val; diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c b/en/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c index f85b6f45a..44b2565d1 100644 --- a/en/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c +++ b/en/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -// Assume path and result length not exceeding 100 +// Assume each path and the result list contain at most 100 entries #define MAX_SIZE 100 #define MAX_RES_SIZE 100 @@ -14,15 +14,15 @@ TreeNode *path[MAX_SIZE]; TreeNode *res[MAX_RES_SIZE][MAX_SIZE]; int pathSize = 0, resSize = 0; -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ void preOrder(TreeNode *root) { if (root == NULL) { return; } - // Attempt + // Try path[pathSize++] = root; if (root->val == 7) { - // Record solution + // Record the solution for (int i = 0; i < pathSize; ++i) { res[resSize][i] = path[i]; } @@ -34,17 +34,17 @@ void preOrder(TreeNode *root) { pathSize--; } -/* Driver Code */ +/* Driver code */ int main() { int arr[] = {1, 7, 3, 4, 5, 6, 7}; TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); - printf("\nInitialize binary tree\n"); + printf("\nInitialize the binary tree\n"); printTree(root); // Preorder traversal preOrder(root); - printf("\nOutput all paths from root to node 7\n"); + printf("\nPrint all paths from the root to nodes with value 7\n"); for (int i = 0; i < resSize; ++i) { int *vals = malloc(MAX_SIZE * sizeof(int)); int size = 0; diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c b/en/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c index 76e82f1ad..7ee7d1218 100644 --- a/en/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c +++ b/en/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -// Assume path and result length not exceeding 100 +// Assume each path and the result list contain at most 100 entries #define MAX_SIZE 100 #define MAX_RES_SIZE 100 @@ -14,16 +14,16 @@ TreeNode *path[MAX_SIZE]; TreeNode *res[MAX_RES_SIZE][MAX_SIZE]; int pathSize = 0, resSize = 0; -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ void preOrder(TreeNode *root) { - // Pruning + // Prune if (root == NULL || root->val == 3) { return; } - // Attempt + // Try path[pathSize++] = root; if (root->val == 7) { - // Record solution + // Record the solution for (int i = 0; i < pathSize; i++) { res[resSize][i] = path[i]; } @@ -35,17 +35,17 @@ void preOrder(TreeNode *root) { pathSize--; } -/* Driver Code */ +/* Driver code */ int main() { int arr[] = {1, 7, 3, 4, 5, 6, 7}; TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); - printf("\nInitialize binary tree\n"); + printf("\nInitialize the binary tree\n"); printTree(root); // Preorder traversal preOrder(root); - printf("\nOutput all paths from root to node 7, excluding nodes with value 3\n"); + printf("\nPrint all paths from the root to nodes with value 7 that contain no node with value 3\n"); for (int i = 0; i < resSize; ++i) { int *vals = malloc(MAX_SIZE * sizeof(int)); int size = 0; diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_iii_template.c b/en/codes/c/chapter_backtracking/preorder_traversal_iii_template.c index 40dfaed85..cb69279db 100644 --- a/en/codes/c/chapter_backtracking/preorder_traversal_iii_template.c +++ b/en/codes/c/chapter_backtracking/preorder_traversal_iii_template.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -// Assume path and result length not exceeding 100 +// Assume each path and the result list contain at most 100 entries #define MAX_SIZE 100 #define MAX_RES_SIZE 100 @@ -14,12 +14,12 @@ TreeNode *path[MAX_SIZE]; TreeNode *res[MAX_RES_SIZE][MAX_SIZE]; int pathSize = 0, resSize = 0; -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ bool isSolution(void) { return pathSize > 0 && path[pathSize - 1]->val == 7; } -/* Record solution */ +/* Record the solution */ void recordSolution(void) { for (int i = 0; i < pathSize; i++) { res[resSize][i] = path[i]; @@ -27,56 +27,56 @@ void recordSolution(void) { resSize++; } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ bool isValid(TreeNode *choice) { return choice != NULL && choice->val != 3; } -/* Update state */ +/* Update the state */ void makeChoice(TreeNode *choice) { path[pathSize++] = choice; } -/* Restore state */ +/* Restore the state */ void undoChoice(void) { pathSize--; } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ void backtrack(TreeNode *choices[2]) { - // Check if it is a solution + // Check whether this is a solution if (isSolution()) { - // Record solution + // Record the solution recordSolution(); } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < 2; i++) { TreeNode *choice = choices[i]; - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(choice); - // Proceed to the next round of selection + // Proceed to the next choice TreeNode *nextChoices[2] = {choice->left, choice->right}; backtrack(nextChoices); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(); } } } -/* Driver Code */ +/* Driver code */ int main() { int arr[] = {1, 7, 3, 4, 5, 6, 7}; TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); - printf("\nInitialize binary tree\n"); + printf("\nInitialize the binary tree\n"); printTree(root); // Backtracking algorithm TreeNode *choices[2] = {root, NULL}; backtrack(choices); - printf("\nOutput all paths from root to node 7, excluding nodes with value 3\n"); + printf("\nPrint all paths from the root to nodes with value 7 that contain no node with value 3\n"); for (int i = 0; i < resSize; ++i) { int *vals = malloc(MAX_SIZE * sizeof(int)); int size = 0; diff --git a/en/codes/c/chapter_backtracking/subset_sum_i.c b/en/codes/c/chapter_backtracking/subset_sum_i.c index 1e34efb5c..51f8392cc 100644 --- a/en/codes/c/chapter_backtracking/subset_sum_i.c +++ b/en/codes/c/chapter_backtracking/subset_sum_i.c @@ -13,14 +13,14 @@ int state[MAX_SIZE]; int stateSize = 0; -// Result list (subset list) +// Result list (list of subsets) int res[MAX_RES_SIZE][MAX_SIZE]; int resColSizes[MAX_RES_SIZE]; int resSize = 0; -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ void backtrack(int target, int *choices, int choicesSize, int start) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is 0 if (target == 0) { for (int i = 0; i < stateSize; ++i) { res[resSize][i] = state[i]; @@ -28,20 +28,20 @@ void backtrack(int target, int *choices, int choicesSize, int start) { resColSizes[resSize++] = stateSize; return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (int i = start; i < choicesSize; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if the choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if (target - choices[i] < 0) { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state[stateSize] = choices[i]; stateSize++; - // Proceed to the next round of selection + // Proceed to the next choice backtrack(target - choices[i], choices, choicesSize, i); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state stateSize--; } } @@ -54,11 +54,11 @@ int cmp(const void *a, const void *b) { /* Solve subset sum I */ void subsetSumI(int *nums, int numsSize, int target) { qsort(nums, numsSize, sizeof(int), cmp); // Sort nums - int start = 0; // Start point for traversal + int start = 0; // Starting index for iteration backtrack(target, nums, numsSize, start); } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {3, 4, 5}; int numsSize = sizeof(nums) / sizeof(nums[0]); @@ -69,7 +69,7 @@ int main() { printf("Input array nums = "); printArray(nums, numsSize); printf("target = %d\n", target); - printf("All subsets with sum equal to %d res = \n", target); + printf("All subsets whose sum equals %d, res = \n", target); for (int i = 0; i < resSize; ++i) { printArray(res[i], resColSizes[i]); } diff --git a/en/codes/c/chapter_backtracking/subset_sum_i_naive.c b/en/codes/c/chapter_backtracking/subset_sum_i_naive.c index 7621bf7d6..846ce3140 100644 --- a/en/codes/c/chapter_backtracking/subset_sum_i_naive.c +++ b/en/codes/c/chapter_backtracking/subset_sum_i_naive.c @@ -13,14 +13,14 @@ int state[MAX_SIZE]; int stateSize = 0; -// Result list (subset list) +// Result list (list of subsets) int res[MAX_RES_SIZE][MAX_SIZE]; int resColSizes[MAX_RES_SIZE]; int resSize = 0; -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ void backtrack(int target, int total, int *choices, int choicesSize) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total == target) { for (int i = 0; i < stateSize; i++) { res[resSize][i] = state[i]; @@ -28,28 +28,28 @@ void backtrack(int target, int total, int *choices, int choicesSize) { resColSizes[resSize++] = stateSize; return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choicesSize; i++) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state[stateSize++] = choices[i]; - // Proceed to the next round of selection + // Proceed to the next choice backtrack(target, total + choices[i], choices, choicesSize); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state stateSize--; } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ void subsetSumINaive(int *nums, int numsSize, int target) { - resSize = 0; // Initialize solution count to 0 + resSize = 0; // Initialize the solution count to 0 backtrack(target, 0, nums, numsSize); } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {3, 4, 5}; int numsSize = sizeof(nums) / sizeof(nums[0]); @@ -60,7 +60,7 @@ int main() { printf("Input array nums = "); printArray(nums, numsSize); printf("target = %d\n", target); - printf("All subsets with sum equal to %d res = \n", target); + printf("All subsets whose sum equals %d, res = \n", target); for (int i = 0; i < resSize; i++) { printArray(res[i], resColSizes[i]); } diff --git a/en/codes/c/chapter_backtracking/subset_sum_ii.c b/en/codes/c/chapter_backtracking/subset_sum_ii.c index 5b2399d65..4e3d7e144 100644 --- a/en/codes/c/chapter_backtracking/subset_sum_ii.c +++ b/en/codes/c/chapter_backtracking/subset_sum_ii.c @@ -13,14 +13,14 @@ int state[MAX_SIZE]; int stateSize = 0; -// Result list (subset list) +// Result list (list of subsets) int res[MAX_RES_SIZE][MAX_SIZE]; int resColSizes[MAX_RES_SIZE]; int resSize = 0; -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ void backtrack(int target, int *choices, int choicesSize, int start) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is 0 if (target == 0) { for (int i = 0; i < stateSize; i++) { res[resSize][i] = state[i]; @@ -28,24 +28,24 @@ void backtrack(int target, int *choices, int choicesSize, int start) { resColSizes[resSize++] = stateSize; return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: iterate from start to avoid choosing the same element again for (int i = start; i < choicesSize; i++) { - // Pruning 1: Skip if subset sum exceeds target + // Pruning 1: skip this choice if it exceeds the remaining target if (target - choices[i] < 0) { continue; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if (i > start && choices[i] == choices[i - 1]) { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state[stateSize] = choices[i]; stateSize++; - // Proceed to the next round of selection + // Proceed to the next choice backtrack(target - choices[i], choices, choicesSize, i + 1); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state stateSize--; } } @@ -63,7 +63,7 @@ void subsetSumII(int *nums, int numsSize, int target) { backtrack(target, nums, numsSize, 0); } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {4, 4, 5}; int numsSize = sizeof(nums) / sizeof(nums[0]); @@ -74,7 +74,7 @@ int main() { printf("Input array nums = "); printArray(nums, numsSize); printf("target = %d\n", target); - printf("All subsets with sum equal to %d res = \n", target); + printf("All subsets whose sum equals %d, res = \n", target); for (int i = 0; i < resSize; ++i) { printArray(res[i], resColSizes[i]); } diff --git a/en/codes/c/chapter_computational_complexity/complexity_exercises.c b/en/codes/c/chapter_computational_complexity/complexity_exercises.c index fbdb69329..367bc3b7e 100644 --- a/en/codes/c/chapter_computational_complexity/complexity_exercises.c +++ b/en/codes/c/chapter_computational_complexity/complexity_exercises.c @@ -6,7 +6,7 @@ #include -/* Iterative summation */ +/* Sum using iteration */ int sumIter(int n) { int res = 0; for (int i = 1; i <= n; i++) { @@ -15,7 +15,7 @@ int sumIter(int n) { return res; } -/* Recursive summation */ +/* Sum using recursion */ int sumRecur(int n) { if (n == 1) { return 1; @@ -23,7 +23,7 @@ int sumRecur(int n) { return n + sumRecur(n - 1); } -/* Linear loop */ +/* Linear-time loop */ int linearLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -32,7 +32,7 @@ int linearLoop(int n) { return res; } -/* Quadratic loop */ +/* Quadratic-time loop */ int quadraticLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -43,7 +43,7 @@ int quadraticLoop(int n) { return res; } -/* Logarithmic loop */ +/* Logarithmic-time loop */ int logarithmicLoop(int n) { while (n > 1) { n /= 2; diff --git a/en/codes/c/chapter_computational_complexity/iteration.c b/en/codes/c/chapter_computational_complexity/iteration.c index 71f099a3b..74ecedcc4 100644 --- a/en/codes/c/chapter_computational_complexity/iteration.c +++ b/en/codes/c/chapter_computational_complexity/iteration.c @@ -9,7 +9,7 @@ /* for loop */ int forLoop(int n) { int res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (int i = 1; i <= n; i++) { res += i; } @@ -19,11 +19,11 @@ int forLoop(int n) { /* while loop */ int whileLoop(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 2, ..., n-1, n + int i = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i; - i++; // Update condition variable + i++; // Update the loop control variable } return res; } @@ -31,25 +31,25 @@ int whileLoop(int n) { /* while loop (two updates) */ int whileLoopII(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 4, 10, ... + int i = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i; - // Update condition variable + // Update the loop control variable i++; i *= 2; } return res; } -/* Nested for loop */ +/* Nested for loops */ char *nestedForLoop(int n) { - // n * n is the number of points, "(i, j), " string max length is 6+10*2, plus extra space for null character \0 + // There are n * n points; each "(i, j), " takes at most 6+10*2 characters, plus space for the final null character \0 int size = n * n * 26 + 1; char *res = malloc(size * sizeof(char)); - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (int i = 1; i <= n; i++) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (int j = 1; j <= n; j++) { char tmp[26]; snprintf(tmp, sizeof(tmp), "(%d, %d), ", i, j); @@ -59,22 +59,22 @@ char *nestedForLoop(int n) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int n = 5; int res; res = forLoop(n); - printf("\nFor loop sum result res = %d\n", res); + printf("\nSum from the for loop, res = %d\n", res); res = whileLoop(n); - printf("\nWhile loop sum result res = %d\n", res); + printf("\nSum from the while loop, res = %d\n", res); res = whileLoopII(n); - printf("\nWhile loop (two updates) sum result res = %d\n", res); + printf("\nSum from the while loop (two updates), res = %d\n", res); char *resStr = nestedForLoop(n); - printf("\nNested for loop traversal result %s\r\n", resStr); + printf("\nTraversal result from nested for loops: %s\r\n", resStr); free(resStr); return 0; diff --git a/en/codes/c/chapter_computational_complexity/recursion.c b/en/codes/c/chapter_computational_complexity/recursion.c index e11460559..002cf26cb 100644 --- a/en/codes/c/chapter_computational_complexity/recursion.c +++ b/en/codes/c/chapter_computational_complexity/recursion.c @@ -8,28 +8,28 @@ /* Recursion */ int recur(int n) { - // Termination condition + // Base case if (n == 1) return 1; - // Recurse: recursive call + // Recurse: make a recursive call int res = recur(n - 1); - // Return: return result + // Unwind: return the result return n + res; } /* Simulate recursion using iteration */ int forLoopRecur(int n) { - int stack[1000]; // Use a large array to simulate stack - int top = -1; // Stack top index + int stack[1000]; // Use a large array to simulate a stack + int top = -1; // Top-of-stack index int res = 0; - // Recurse: recursive call + // Recurse: make a recursive call for (int i = n; i > 0; i--) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack[1 + top++] = i; } - // Return: return result + // Unwind: return the result while (top >= 0) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack[top--]; } // res = 1+2+3+...+n @@ -38,40 +38,40 @@ int forLoopRecur(int n) { /* Tail recursion */ int tailRecur(int n, int res) { - // Termination condition + // Base case if (n == 0) return res; - // Tail recursive call + // Tail-recursive call return tailRecur(n - 1, res + n); } /* Fibonacci sequence: recursion */ int fib(int n) { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n == 1 || n == 2) return n - 1; - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) int res = fib(n - 1) + fib(n - 2); - // Return result f(n) + // Return the result f(n) return res; } -/* Driver Code */ +/* Driver code */ int main() { int n = 5; int res; res = recur(n); - printf("\nRecursion sum result res = %d\n", res); + printf("\nSum from the recursive function, res = %d\n", res); res = forLoopRecur(n); - printf("\nUsing iteration to simulate recursion sum result res = %d\n", res); + printf("\nSum from simulating recursion with iteration, res = %d\n", res); res = tailRecur(n, 0); - printf("\nTail recursion sum result res = %d\n", res); + printf("\nSum from the tail-recursive function, res = %d\n", res); res = fib(n); - printf("\nThe %dth Fibonacci number is %d\n", n, res); + printf("\nFibonacci term %d is %d\n", n, res); return 0; } diff --git a/en/codes/c/chapter_computational_complexity/space_complexity.c b/en/codes/c/chapter_computational_complexity/space_complexity.c index 35efeca76..673acb84b 100644 --- a/en/codes/c/chapter_computational_complexity/space_complexity.c +++ b/en/codes/c/chapter_computational_complexity/space_complexity.c @@ -12,9 +12,9 @@ int func() { return 0; } -/* Constant order */ +/* Constant complexity */ void constant(int n) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space const int a = 0; int b = 0; int nums[1000]; @@ -24,7 +24,7 @@ void constant(int n) { for (int i = 0; i < n; i++) { int c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for (int i = 0; i < n; i++) { func(); } @@ -37,9 +37,9 @@ typedef struct { UT_hash_handle hh; // Implemented using uthash.h } HashTable; -/* Linear order */ +/* Linear complexity */ void linear(int n) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space int *nums = malloc(sizeof(int) * n); free(nums); @@ -48,13 +48,13 @@ void linear(int n) { for (int i = 0; i < n; i++) { nodes[i] = newListNode(i); } - // Memory release + // Free memory for (int i = 0; i < n; i++) { free(nodes[i]); } free(nodes); - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space HashTable *h = NULL; for (int i = 0; i < n; i++) { HashTable *tmp = malloc(sizeof(HashTable)); @@ -63,7 +63,7 @@ void linear(int n) { HASH_ADD_INT(h, key, tmp); } - // Memory release + // Free memory HashTable *curr, *tmp; HASH_ITER(hh, h, curr, tmp) { HASH_DEL(h, curr); @@ -71,17 +71,17 @@ void linear(int n) { } } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ void linearRecur(int n) { - printf("Recursion n = %d\r\n", n); + printf("Recursive call n = %d\r\n", n); if (n == 1) return; linearRecur(n - 1); } -/* Exponential order */ +/* Quadratic complexity */ void quadratic(int n) { - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space int **numMatrix = malloc(sizeof(int *) * n); for (int i = 0; i < n; i++) { int *tmp = malloc(sizeof(int) * n); @@ -91,25 +91,25 @@ void quadratic(int n) { numMatrix[i] = tmp; } - // Memory release + // Free memory for (int i = 0; i < n; i++) { free(numMatrix[i]); } free(numMatrix); } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ int quadraticRecur(int n) { if (n <= 0) return 0; int *nums = malloc(sizeof(int) * n); - printf("In recursion n = %d, nums length = %d\r\n", n, n); + printf("In recursive call n = %d, nums has length = %d\r\n", n, n); int res = quadraticRecur(n - 1); free(nums); return res; } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ TreeNode *buildTree(int n) { if (n == 0) return NULL; @@ -119,18 +119,18 @@ TreeNode *buildTree(int n) { return root; } -/* Driver Code */ +/* Driver code */ int main() { int n = 5; - // Constant order + // Constant complexity constant(n); - // Linear order + // Linear complexity linear(n); linearRecur(n); - // Exponential order + // Quadratic complexity quadratic(n); quadraticRecur(n); - // Exponential order + // Exponential complexity TreeNode *root = buildTree(n); printTree(root); diff --git a/en/codes/c/chapter_computational_complexity/time_complexity.c b/en/codes/c/chapter_computational_complexity/time_complexity.c index b9842a68a..abbb7ce58 100644 --- a/en/codes/c/chapter_computational_complexity/time_complexity.c +++ b/en/codes/c/chapter_computational_complexity/time_complexity.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -/* Constant order */ +/* Constant complexity */ int constant(int n) { int count = 0; int size = 100000; @@ -16,7 +16,7 @@ int constant(int n) { return count; } -/* Linear order */ +/* Linear complexity */ int linear(int n) { int count = 0; for (int i = 0; i < n; i++) { @@ -25,20 +25,20 @@ int linear(int n) { return count; } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ int arrayTraversal(int *nums, int n) { int count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for (int i = 0; i < n; i++) { count++; } return count; } -/* Exponential order */ +/* Quadratic complexity */ int quadratic(int n) { int count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -47,30 +47,30 @@ int quadratic(int n) { return count; } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ int bubbleSort(int *nums, int n) { int count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = n - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } return count; } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ int exponential(int n) { int count = 0; int bas = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (int i = 0; i < n; i++) { for (int j = 0; j < bas; j++) { count++; @@ -81,14 +81,14 @@ int exponential(int n) { return count; } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation) */ int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation) */ int logarithmic(int n) { int count = 0; while (n > 1) { @@ -98,14 +98,14 @@ int logarithmic(int n) { return count; } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation) */ int logRecur(int n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } -/* Linearithmic order */ +/* Linearithmic complexity */ int linearLogRecur(int n) { if (n <= 1) return 1; @@ -116,7 +116,7 @@ int linearLogRecur(int n) { return count; } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ int factorialRecur(int n) { if (n == 0) return 1; @@ -127,47 +127,47 @@ int factorialRecur(int n) { return count; } -/* Driver Code */ +/* Driver code */ int main(int argc, char *argv[]) { - // You can modify n to run and observe the trend of the number of operations for various complexities + // Try different values of n to see how the operation count grows for each complexity int n = 8; - printf("Input data size n = %d\n", n); + printf("Input size n = %d\n", n); int count = constant(n); - printf("Constant-time operations count = %d\n", count); + printf("Operation count for constant complexity = %d\n", count); count = linear(n); - printf("Linear-time operations count = %d\n", count); - // Allocate heap memory (create 1D variable-length array: n elements of type int) + printf("Operation count for linear complexity = %d\n", count); + // Allocate heap memory for a one-dimensional array of n int elements int *nums = (int *)malloc(n * sizeof(int)); count = arrayTraversal(nums, n); - printf("Linear-time (array traversal) operations count = %d\n", count); + printf("Operation count for linear complexity (array traversal) = %d\n", count); count = quadratic(n); - printf("Quadratic-time operations count = %d\n", count); + printf("Operation count for quadratic complexity = %d\n", count); for (int i = 0; i < n; i++) { nums[i] = n - i; // [n,n-1,...,2,1] } count = bubbleSort(nums, n); - printf("Quadratic-time (bubble sort) operations count = %d\n", count); + printf("Operation count for quadratic complexity (bubble sort) = %d\n", count); count = exponential(n); - printf("Exponential-time (iterative) operations count = %d\n", count); + printf("Operation count for exponential complexity (iteration) = %d\n", count); count = expRecur(n); - printf("Exponential-time (recursive) operations count = %d\n", count); + printf("Operation count for exponential complexity (recursion) = %d\n", count); count = logarithmic(n); - printf("Logarithmic-time (iterative) operations count = %d\n", count); + printf("Operation count for logarithmic complexity (iteration) = %d\n", count); count = logRecur(n); - printf("Logarithmic-time (recursive) operations count = %d\n", count); + printf("Operation count for logarithmic complexity (recursion) = %d\n", count); count = linearLogRecur(n); - printf("Linearithmic-time (recursive) operations count = %d\n", count); + printf("Operation count for linearithmic complexity (recursion) = %d\n", count); count = factorialRecur(n); - printf("Factorial-time (recursive) operations count = %d\n", count); + printf("Operation count for factorial complexity (recursion) = %d\n", count); - // Free heap memory + // Free the heap memory if (nums != NULL) { free(nums); nums = NULL; diff --git a/en/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/en/codes/c/chapter_computational_complexity/worst_best_time_complexity.c index 068c59365..0e7111d48 100644 --- a/en/codes/c/chapter_computational_complexity/worst_best_time_complexity.c +++ b/en/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -6,15 +6,15 @@ #include "../utils/common.h" -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ int *randomNumbers(int n) { - // Allocate heap memory (create 1D variable-length array: n elements of type int) + // Allocate heap memory for a one-dimensional array of n int elements int *nums = (int *)malloc(n * sizeof(int)); - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (int i = 0; i < n; i++) { nums[i] = i + 1; } - // Randomly shuffle array elements + // Randomly shuffle the array elements for (int i = n - 1; i > 0; i--) { int j = rand() % (i + 1); int temp = nums[i]; @@ -24,20 +24,20 @@ int *randomNumbers(int n) { return nums; } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ int findOne(int *nums, int n) { for (int i = 0; i < n; i++) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] == 1) return i; } return -1; } -/* Driver Code */ +/* Driver code */ int main(int argc, char *argv[]) { - // Initialize random seed + // Initialize the random seed srand((unsigned int)time(NULL)); for (int i = 0; i < 10; i++) { int n = 100; @@ -45,8 +45,8 @@ int main(int argc, char *argv[]) { int index = findOne(nums, n); printf("\nArray [ 1, 2, ..., n ] after shuffling = "); printArray(nums, n); - printf("Index of number 1 is %d\n", index); - // Free heap memory + printf("Index of the number 1: %d\n", index); + // Free the heap memory if (nums != NULL) { free(nums); nums = NULL; diff --git a/en/codes/c/chapter_divide_and_conquer/binary_search_recur.c b/en/codes/c/chapter_divide_and_conquer/binary_search_recur.c index 77ad293ce..b825c550f 100644 --- a/en/codes/c/chapter_divide_and_conquer/binary_search_recur.c +++ b/en/codes/c/chapter_divide_and_conquer/binary_search_recur.c @@ -8,20 +8,20 @@ /* Binary search: problem f(i, j) */ int dfs(int nums[], int target, int i, int j) { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1; } // Calculate the midpoint index m int m = (i + j) / 2; if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -29,17 +29,17 @@ int dfs(int nums[], int target, int i, int j) { /* Binary search */ int binarySearch(int nums[], int target, int numsSize) { int n = numsSize; - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1); } -/* Driver Code */ +/* Driver code */ int main() { int target = 6; int nums[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; int numsSize = sizeof(nums) / sizeof(nums[0]); - // Binary search (closed interval on both sides) + // Binary search (closed interval) int index = binarySearch(nums, target, numsSize); printf("Index of target element 6 = %d\n", index); diff --git a/en/codes/c/chapter_divide_and_conquer/build_tree.c b/en/codes/c/chapter_divide_and_conquer/build_tree.c index d3dfe6da3..e608c536d 100644 --- a/en/codes/c/chapter_divide_and_conquer/build_tree.c +++ b/en/codes/c/chapter_divide_and_conquer/build_tree.c @@ -6,12 +6,12 @@ #include "../utils/common.h" -// Assume all elements less than 1000 +// Assume all elements are less than 1000 #define MAX_SIZE 1000 -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) return NULL; // Initialize the root node @@ -19,7 +19,7 @@ TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) { root->val = preorder[i]; root->left = NULL; root->right = NULL; - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees int m = inorderMap[preorder[i]]; // Subproblem: build the left subtree root->left = dfs(preorder, inorderMap, i + 1, l, m - 1, size); @@ -29,9 +29,9 @@ TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) { return root; } -/* Build binary tree */ +/* Build a binary tree */ TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a lookup array mapping inorder values to their indices int *inorderMap = (int *)malloc(sizeof(int) * MAX_SIZE); for (int i = 0; i < inorderSize; i++) { inorderMap[inorder[i]] = i; @@ -41,7 +41,7 @@ TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSi return root; } -/* Driver Code */ +/* Driver code */ int main() { int preorder[] = {3, 9, 2, 1, 7}; int inorder[] = {9, 3, 1, 2, 7}; @@ -53,7 +53,7 @@ int main() { printArray(inorder, inorderSize); TreeNode *root = buildTree(preorder, preorderSize, inorder, inorderSize); - printf("The constructed binary tree is:\n"); + printf("Constructed binary tree:\n"); printTree(root); freeMemoryTree(root); diff --git a/en/codes/c/chapter_divide_and_conquer/fast_power.c b/en/codes/c/chapter_divide_and_conquer/fast_power.c index 4ed78e867..cf56aceff 100644 --- a/en/codes/c/chapter_divide_and_conquer/fast_power.c +++ b/en/codes/c/chapter_divide_and_conquer/fast_power.c @@ -6,7 +6,7 @@ #include -/* Exponentiation by squaring */ +/* Fast exponentiation */ int fastPow(int x, int n) { if (n == 0) { return 1; diff --git a/en/codes/c/chapter_divide_and_conquer/hanota.c b/en/codes/c/chapter_divide_and_conquer/hanota.c index 156e2ae3a..4c97c8b3f 100644 --- a/en/codes/c/chapter_divide_and_conquer/hanota.c +++ b/en/codes/c/chapter_divide_and_conquer/hanota.c @@ -6,12 +6,12 @@ #include "../utils/common.h" -// Assume at most 1000 permutations +// Assume at most 1000 disks #define MAX_SIZE 1000 -/* Move a disk */ +/* Move one disk */ void move(int *src, int *srcSize, int *tar, int *tarSize) { - // Take out a disk from the top of src + // Take a disk from the top of src int pan = src[*srcSize - 1]; src[*srcSize - 1] = 0; (*srcSize)--; @@ -22,14 +22,14 @@ void move(int *src, int *srcSize, int *tar, int *tarSize) { /* Solve the Tower of Hanoi problem f(i) */ void dfs(int i, int *src, int *srcSize, int *buf, int *bufSize, int *tar, int *tarSize) { - // If there is only one disk left in src, move it directly to tar + // If only one disk remains to be moved from src, move it directly to tar if (i == 1) { move(src, srcSize, tar, tarSize); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, srcSize, tar, tarSize, buf, bufSize); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, srcSize, tar, tarSize); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, bufSize, src, srcSize, tar, tarSize); @@ -41,9 +41,9 @@ void solveHanota(int *A, int *ASize, int *B, int *BSize, int *C, int *CSize) { dfs(*ASize, A, ASize, B, BSize, C, CSize); } -/* Driver Code */ +/* Driver code */ int main() { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg int a[] = {5, 4, 3, 2, 1}; int b[MAX_SIZE] = {0}; int c[MAX_SIZE] = {0}; @@ -62,7 +62,7 @@ int main() { solveHanota(a, &ASize, b, &BSize, c, &CSize); - printf("\nAfter disk movement:"); + printf("\nAfter moving the disks:"); printf("A = "); printArray(a, ASize); printf("B = "); diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c index cc229ed90..7b1251638 100644 --- a/en/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c @@ -8,27 +8,27 @@ /* Backtracking */ void backtrack(int *choices, int state, int n, int *res, int len) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state == n) res[0]++; - // Traverse all choices + // Iterate over all choices for (int i = 0; i < len; i++) { int choice = choices[i]; - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue; - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res, len); // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ int climbingStairsBacktrack(int n) { - int choices[2] = {1, 2}; // Can choose to climb up 1 or 2 stairs - int state = 0; // Start climbing from the 0-th stair + int choices[2] = {1, 2}; // Choose to climb 1 or 2 steps + int state = 0; // Start climbing from step 0 int *res = (int *)malloc(sizeof(int)); - *res = 0; // Use res[0] to record the solution count + *res = 0; // Use res[0] to record the number of ways int len = sizeof(choices) / sizeof(int); backtrack(choices, state, n, res, len); int result = *res; @@ -36,12 +36,12 @@ int climbingStairsBacktrack(int n) { return result; } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsBacktrack(n); - printf("Climbing %d stairs has %d solutions\n", n, res); + printf("Number of ways to climb %d stairs: %d\n", n, res); return 0; } \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c index e8037064f..5190f4f24 100644 --- a/en/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c @@ -6,22 +6,22 @@ #include "../utils/common.h" -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ int climbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int **dp = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { dp[i] = calloc(3, sizeof(int)); } - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -35,12 +35,12 @@ int climbingStairsConstraintDP(int n) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsConstraintDP(n); - printf("Climbing %d stairs has %d solutions\n", n, res); + printf("Number of ways to climb %d stairs: %d\n", n, res); return 0; } diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c index 9a30935b7..d87267471 100644 --- a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c @@ -8,7 +8,7 @@ /* Search */ int dfs(int i) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; // dp[i] = dp[i-1] + dp[i-2] @@ -16,17 +16,17 @@ int dfs(int i) { return count; } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ int climbingStairsDFS(int n) { return dfs(n); } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsDFS(n); - printf("Climbing %d stairs has %d solutions\n", n, res); + printf("Number of ways to climb %d stairs: %d\n", n, res); return 0; } \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c index 30fe9a241..f1708ccbc 100644 --- a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c @@ -6,12 +6,12 @@ #include "../utils/common.h" -/* Memoization search */ +/* Memoized search */ int dfs(int i, int *mem) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] @@ -21,9 +21,9 @@ int dfs(int i, int *mem) { return count; } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ int climbingStairsDFSMem(int n) { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded int *mem = (int *)malloc((n + 1) * sizeof(int)); for (int i = 0; i <= n; i++) { mem[i] = -1; @@ -33,12 +33,12 @@ int climbingStairsDFSMem(int n) { return result; } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsDFSMem(n); - printf("Climbing %d stairs has %d solutions\n", n, res); + printf("Number of ways to climb %d stairs: %d\n", n, res); return 0; } \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c index cfd2ef790..9f959fba3 100644 --- a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c @@ -6,16 +6,16 @@ #include "../utils/common.h" -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ int climbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int *dp = (int *)malloc((n + 1) * sizeof(int)); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } @@ -24,7 +24,7 @@ int climbingStairsDP(int n) { return result; } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ int climbingStairsDPComp(int n) { if (n == 1 || n == 2) return n; @@ -37,15 +37,15 @@ int climbingStairsDPComp(int n) { return b; } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsDP(n); - printf("Climbing %d stairs has %d solutions\n", n, res); + printf("Number of ways to climb %d stairs: %d\n", n, res); res = climbingStairsDPComp(n); - printf("Climbing %d stairs has %d solutions\n", n, res); + printf("Number of ways to climb %d stairs: %d\n", n, res); return 0; } \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/coin_change.c b/en/codes/c/chapter_dynamic_programming/coin_change.c index 1c9e562b2..6c29d7daa 100644 --- a/en/codes/c/chapter_dynamic_programming/coin_change.c +++ b/en/codes/c/chapter_dynamic_programming/coin_change.c @@ -6,32 +6,32 @@ #include "../utils/common.h" -/* Find minimum value */ +/* Find the minimum */ int myMin(int a, int b) { return a < b ? a : b; } -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ int coinChangeDP(int coins[], int amt, int coinsSize) { int n = coinsSize; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table int **dp = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { dp[i] = calloc(amt + 1, sizeof(int)); } - // State transition: first row and first column + // State transitions: first row and first column for (int a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = myMin(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -45,25 +45,25 @@ int coinChangeDP(int coins[], int amt, int coinsSize) { return res; } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ int coinChangeDPComp(int coins[], int amt, int coinsSize) { int n = coinsSize; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table int *dp = malloc((amt + 1) * sizeof(int)); for (int j = 1; j <= amt; j++) { dp[j] = MAX; } dp[0] = 0; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = myMin(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -82,11 +82,11 @@ int main() { // Dynamic programming int res = coinChangeDP(coins, amt, coinsSize); - printf("Minimum number of coins needed to make target amount is %d\n", res); + printf("Minimum number of coins needed to make the target amount: %d\n", res); // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt, coinsSize); - printf("Minimum number of coins needed to make target amount is %d\n", res); + printf("Minimum number of coins needed to make the target amount: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_dynamic_programming/coin_change_ii.c b/en/codes/c/chapter_dynamic_programming/coin_change_ii.c index ca37fce59..ecb9d265b 100644 --- a/en/codes/c/chapter_dynamic_programming/coin_change_ii.c +++ b/en/codes/c/chapter_dynamic_programming/coin_change_ii.c @@ -6,26 +6,26 @@ #include "../utils/common.h" -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ int coinChangeIIDP(int coins[], int amt, int coinsSize) { int n = coinsSize; - // Initialize dp table + // Initialize the dp table int **dp = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { dp[i] = calloc(amt + 1, sizeof(int)); } - // Initialize first column + // Initialize the first column for (int i = 0; i <= n; i++) { dp[i][0] = 1; } - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -39,20 +39,20 @@ int coinChangeIIDP(int coins[], int amt, int coinsSize) { return res; } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ int coinChangeIIDPComp(int coins[], int amt, int coinsSize) { int n = coinsSize; - // Initialize dp table + // Initialize the dp table int *dp = calloc(amt + 1, sizeof(int)); dp[0] = 1; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -71,11 +71,11 @@ int main() { // Dynamic programming int res = coinChangeIIDP(coins, amt, coinsSize); - printf("Number of coin combinations to make target amount is %d\n", res); + printf("Number of coin combinations that make the target amount: %d\n", res); // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt, coinsSize); - printf("Number of coin combinations to make target amount is %d\n", res); + printf("Number of coin combinations that make the target amount: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_dynamic_programming/edit_distance.c b/en/codes/c/chapter_dynamic_programming/edit_distance.c index 9188c3d9b..f39d823e5 100644 --- a/en/codes/c/chapter_dynamic_programming/edit_distance.c +++ b/en/codes/c/chapter_dynamic_programming/edit_distance.c @@ -6,80 +6,80 @@ #include "../utils/common.h" -/* Find minimum value */ +/* Find the minimum */ int myMin(int a, int b) { return a < b ? a : b; } -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ int editDistanceDFS(char *s, char *t, int i, int j) { - // If both s and t are empty, return 0 + // If both prefixes s[0:i] and t[0:j] are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the prefix s[0:i] is empty, return j, the length of t[0:j] if (i == 0) return j; - // If t is empty, return length of s + // If the prefix t[0:j] is empty, return i, the length of s[0:i] if (j == 0) return i; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFS(s, t, i, j - 1); int del = editDistanceDFS(s, t, i - 1, j); int replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits return myMin(myMin(insert, del), replace) + 1; } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ int editDistanceDFSMem(char *s, char *t, int memCols, int **mem, int i, int j) { - // If both s and t are empty, return 0 + // If both prefixes s[0:i] and t[0:j] are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the prefix s[0:i] is empty, return j, the length of t[0:j] if (i == 0) return j; - // If t is empty, return length of s + // If the prefix t[0:j] is empty, return i, the length of s[0:i] if (j == 0) return i; - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) return mem[i][j]; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFSMem(s, t, memCols, mem, i, j - 1); int del = editDistanceDFSMem(s, t, memCols, mem, i - 1, j); int replace = editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = myMin(myMin(insert, del), replace) + 1; return mem[i][j]; } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ int editDistanceDP(char *s, char *t, int n, int m) { int **dp = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { dp[i] = calloc(m + 1, sizeof(int)); } - // State transition: first row and first column + // State transitions: first row and first column for (int i = 1; i <= n; i++) { dp[i][0] = i; } for (int j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = myMin(myMin(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } } @@ -92,29 +92,29 @@ int editDistanceDP(char *s, char *t, int n, int m) { return res; } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ int editDistanceDPComp(char *s, char *t, int n, int m) { int *dp = calloc(m + 1, sizeof(int)); - // State transition: first row + // State transitions: first row for (int j = 1; j <= m; j++) { dp[j] = j; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i <= n; i++) { - // State transition: first column + // State transitions: first column int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = myMin(myMin(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } int res = dp[m]; @@ -123,7 +123,7 @@ int editDistanceDPComp(char *s, char *t, int n, int m) { return res; } -/* Driver Code */ +/* Driver code */ int main() { char *s = "bag"; char *t = "pack"; @@ -131,16 +131,16 @@ int main() { // Brute-force search int res = editDistanceDFS(s, t, n, m); - printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + printf("Changing %s to %s requires at least %d edits\n", s, t, res); - // Memoization search + // Memoized search int **mem = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { mem[i] = malloc((m + 1) * sizeof(int)); memset(mem[i], -1, (m + 1) * sizeof(int)); } res = editDistanceDFSMem(s, t, m + 1, mem, n, m); - printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + printf("Changing %s to %s requires at least %d edits\n", s, t, res); // Free memory for (int i = 0; i <= n; i++) { free(mem[i]); @@ -149,11 +149,11 @@ int main() { // Dynamic programming res = editDistanceDP(s, t, n, m); - printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + printf("Changing %s to %s requires at least %d edits\n", s, t, res); // Space-optimized dynamic programming res = editDistanceDPComp(s, t, n, m); - printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + printf("Changing %s to %s requires at least %d edits\n", s, t, res); return 0; } diff --git a/en/codes/c/chapter_dynamic_programming/knapsack.c b/en/codes/c/chapter_dynamic_programming/knapsack.c index 2fa42ab25..698fe4f95 100644 --- a/en/codes/c/chapter_dynamic_programming/knapsack.c +++ b/en/codes/c/chapter_dynamic_programming/knapsack.c @@ -6,66 +6,66 @@ #include "../utils/common.h" -/* Find maximum value */ +/* Find the maximum */ int myMax(int a, int b) { return a > b ? a : b; } -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ int knapsackDFS(int wgt[], int val[], int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFS(wgt, val, i - 1, c); int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices return myMax(no, yes); } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ int knapsackDFSMem(int wgt[], int val[], int memCols, int **mem, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] != -1) { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, memCols, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c); int yes = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = myMax(no, yes); return mem[i][c]; } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ int knapsackDP(int wgt[], int val[], int cap, int wgtSize) { int n = wgtSize; - // Initialize dp table + // Initialize the dp table int **dp = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { dp[i] = calloc(cap + 1, sizeof(int)); } - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = myMax(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); } } @@ -78,17 +78,17 @@ int knapsackDP(int wgt[], int val[], int cap, int wgtSize) { return res; } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) { int n = wgtSize; - // Initialize dp table + // Initialize the dp table int *dp = calloc(cap + 1, sizeof(int)); - // State transition + // State transitions for (int i = 1; i <= n; i++) { - // Traverse in reverse order + // Traverse in decreasing order for (int c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -99,7 +99,7 @@ int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int wgt[] = {10, 20, 30, 40, 50}; int val[] = {50, 120, 150, 210, 240}; @@ -109,16 +109,16 @@ int main() { // Brute-force search int res = knapsackDFS(wgt, val, n, cap); - printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + printf("Maximum item value within the knapsack capacity: %d\n", res); - // Memoization search + // Memoized search int **mem = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { mem[i] = malloc((cap + 1) * sizeof(int)); memset(mem[i], -1, (cap + 1) * sizeof(int)); } res = knapsackDFSMem(wgt, val, cap + 1, mem, n, cap); - printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + printf("Maximum item value within the knapsack capacity: %d\n", res); // Free memory for (int i = 0; i <= n; i++) { free(mem[i]); @@ -127,11 +127,11 @@ int main() { // Dynamic programming res = knapsackDP(wgt, val, cap, wgtSize); - printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + printf("Maximum item value within the knapsack capacity: %d\n", res); // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap, wgtSize); - printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + printf("Maximum item value within the knapsack capacity: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c b/en/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c index 02bc2f18b..1579fa1bc 100644 --- a/en/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c +++ b/en/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c @@ -6,22 +6,22 @@ #include "../utils/common.h" -/* Find minimum value */ +/* Find the minimum */ int myMin(int a, int b) { return a < b ? a : b; } -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ int minCostClimbingStairsDP(int cost[], int costSize) { int n = costSize - 1; if (n == 1 || n == 2) return cost[n]; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int *dp = calloc(n + 1, sizeof(int)); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = myMin(dp[i - 1], dp[i - 2]) + cost[i]; } @@ -31,7 +31,7 @@ int minCostClimbingStairsDP(int cost[], int costSize) { return res; } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ int minCostClimbingStairsDPComp(int cost[], int costSize) { int n = costSize - 1; if (n == 1 || n == 2) @@ -45,18 +45,18 @@ int minCostClimbingStairsDPComp(int cost[], int costSize) { return b; } -/* Driver Code */ +/* Driver code */ int main() { int cost[] = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1}; int costSize = sizeof(cost) / sizeof(cost[0]); - printf("Input stair cost list is:"); + printf("Input list of stair costs:"); printArray(cost, costSize); int res = minCostClimbingStairsDP(cost, costSize); - printf("Minimum cost to climb stairs is %d\n", res); + printf("Minimum cost to climb the stairs: %d\n", res); res = minCostClimbingStairsDPComp(cost, costSize); - printf("Minimum cost to climb stairs is %d\n", res); + printf("Minimum cost to climb the stairs: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_dynamic_programming/min_path_sum.c b/en/codes/c/chapter_dynamic_programming/min_path_sum.c index 36ac13b2b..acd48a1e9 100644 --- a/en/codes/c/chapter_dynamic_programming/min_path_sum.c +++ b/en/codes/c/chapter_dynamic_programming/min_path_sum.c @@ -6,70 +6,70 @@ #include "../utils/common.h" -// Assume max matrix rows and columns is 100 +// Assume the matrix has at most 100 rows and 100 columns #define MAX_SIZE 100 -/* Find minimum value */ +/* Find the minimum */ int myMin(int a, int b) { return a < b ? a : b; } -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ int minPathSumDFS(int grid[MAX_SIZE][MAX_SIZE], int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return INT_MAX; } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) int up = minPathSumDFS(grid, i - 1, j); int left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX; } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ int minPathSumDFSMem(int grid[MAX_SIZE][MAX_SIZE], int mem[MAX_SIZE][MAX_SIZE], int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return INT_MAX; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above int up = minPathSumDFSMem(grid, mem, i - 1, j); int left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX; return mem[i][j]; } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { - // Initialize dp table + // Initialize the dp table int **dp = malloc(n * sizeof(int *)); for (int i = 0; i < n; i++) { dp[i] = calloc(m, sizeof(int)); } dp[0][0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (int j = 1; j < m; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for (int i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = myMin(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -83,20 +83,20 @@ int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { return res; } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { - // Initialize dp table + // Initialize the dp table int *dp = calloc(m, sizeof(int)); - // State transition: first row + // State transitions: first row dp[0] = grid[0][0]; for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i < n; i++) { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j < m; j++) { dp[j] = myMin(dp[j - 1], dp[j]) + grid[i][j]; } @@ -107,28 +107,28 @@ int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int grid[MAX_SIZE][MAX_SIZE] = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}}; - int n = 4, m = 4; // Matrix capacity is MAX_SIZE * MAX_SIZE, valid rows and columns are n * m + int n = 4, m = 4; // The matrix capacity is MAX_SIZE * MAX_SIZE; the active region has n rows and m columns // Brute-force search int res = minPathSumDFS(grid, n - 1, m - 1); - printf("Minimum path sum from top-left to bottom-right is %d\n", res); + printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res); - // Memoization search + // Memoized search int mem[MAX_SIZE][MAX_SIZE]; memset(mem, -1, sizeof(mem)); res = minPathSumDFSMem(grid, mem, n - 1, m - 1); - printf("Minimum path sum from top-left to bottom-right is %d\n", res); + printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res); // Dynamic programming res = minPathSumDP(grid, n, m); - printf("Minimum path sum from top-left to bottom-right is %d\n", res); + printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res); // Space-optimized dynamic programming res = minPathSumDPComp(grid, n, m); - printf("Minimum path sum from top-left to bottom-right is %d\n", res); + printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c b/en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c index 666317199..e01bb6828 100644 --- a/en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c +++ b/en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c @@ -6,27 +6,27 @@ #include "../utils/common.h" -/* Find maximum value */ +/* Find the maximum */ int myMax(int a, int b) { return a > b ? a : b; } -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) { int n = wgtSize; - // Initialize dp table + // Initialize the dp table int **dp = malloc((n + 1) * sizeof(int *)); for (int i = 0; i <= n; i++) { dp[i] = calloc(cap + 1, sizeof(int)); } - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = myMax(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); } } @@ -39,19 +39,19 @@ int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) { return res; } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ int unboundedKnapsackDPComp(int wgt[], int val[], int cap, int wgtSize) { int n = wgtSize; - // Initialize dp table + // Initialize the dp table int *dp = calloc(cap + 1, sizeof(int)); - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -71,11 +71,11 @@ int main() { // Dynamic programming int res = unboundedKnapsackDP(wgt, val, cap, wgtSize); - printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + printf("Maximum item value within the knapsack capacity: %d\n", res); // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, cap, wgtSize); - printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + printf("Maximum item value within the knapsack capacity: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_graph/graph_adjacency_list.c b/en/codes/c/chapter_graph/graph_adjacency_list.c index fe6f1f6b6..cb8534099 100644 --- a/en/codes/c/chapter_graph/graph_adjacency_list.c +++ b/en/codes/c/chapter_graph/graph_adjacency_list.c @@ -6,19 +6,19 @@ #include "../utils/common.h" -// Assume max node count is 100 +// Assume at most 100 vertices #define MAX_SIZE 100 -/* Node structure */ +/* Adjacency-list node structure */ typedef struct AdjListNode { Vertex *vertex; // Vertex - struct AdjListNode *next; // Successor node + struct AdjListNode *next; // Next node } AdjListNode; -/* Undirected graph class based on adjacency list */ +/* Undirected graph structure implemented with an adjacency list */ typedef struct { - AdjListNode *heads[MAX_SIZE]; // Node array - int size; // Node count + AdjListNode *heads[MAX_SIZE]; // Array of list heads + int size; // Number of vertices } GraphAdjList; /* Constructor */ @@ -51,7 +51,7 @@ void delGraphAdjList(GraphAdjList *graph) { free(graph); } -/* Find node corresponding to vertex */ +/* Find the list node corresponding to a vertex */ AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) { for (int i = 0; i < graph->size; i++) { if (graph->heads[i]->vertex == vet) { @@ -61,16 +61,16 @@ AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) { return NULL; } -/* Add edge helper function */ +/* Helper for adding an edge */ void addEdgeHelper(AdjListNode *head, Vertex *vet) { AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode)); node->vertex = vet; - // Head insertion + // Insert at the front of the neighbor list node->next = head->next; head->next = node; } -/* Add edge */ +/* Add an edge */ void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { AdjListNode *head1 = findNode(graph, vet1); AdjListNode *head2 = findNode(graph, vet2); @@ -80,24 +80,24 @@ void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { addEdgeHelper(head2, vet1); } -/* Remove edge helper function */ +/* Helper for removing an edge */ void removeEdgeHelper(AdjListNode *head, Vertex *vet) { AdjListNode *pre = head; AdjListNode *cur = head->next; - // Search for node corresponding to vet in list + // Search the linked list for the node corresponding to vet while (cur != NULL && cur->vertex != vet) { pre = cur; cur = cur->next; } if (cur == NULL) return; - // Remove node corresponding to vet from list + // Remove the node corresponding to vet from the linked list pre->next = cur->next; // Free memory free(cur); } -/* Remove edge */ +/* Remove an edge */ void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { AdjListNode *head1 = findNode(graph, vet1); AdjListNode *head2 = findNode(graph, vet2); @@ -107,28 +107,28 @@ void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { removeEdgeHelper(head2, head1->vertex); } -/* Add vertex */ +/* Add a vertex */ void addVertex(GraphAdjList *graph, Vertex *vet) { assert(graph != NULL && graph->size < MAX_SIZE); AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode)); head->vertex = vet; head->next = NULL; - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list graph->heads[graph->size++] = head; } -/* Remove vertex */ +/* Remove a vertex */ void removeVertex(GraphAdjList *graph, Vertex *vet) { AdjListNode *node = findNode(graph, vet); assert(node != NULL); - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list AdjListNode *cur = node, *pre = NULL; while (cur) { pre = cur; cur = cur->next; free(pre); } - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for (int i = 0; i < graph->size; i++) { cur = graph->heads[i]; pre = NULL; @@ -142,7 +142,7 @@ void removeVertex(GraphAdjList *graph, Vertex *vet) { } } } - // Move vertices after this vertex forward to fill gap + // Shift subsequent vertices forward to fill the gap int i; for (i = 0; i < graph->size; i++) { if (graph->heads[i] == node) @@ -155,7 +155,7 @@ void removeVertex(GraphAdjList *graph, Vertex *vet) { free(vet); } -/* Print adjacency list */ +/* Print the adjacency list */ void printGraph(const GraphAdjList *graph) { printf("Adjacency list =\n"); for (int i = 0; i < graph->size; ++i) { diff --git a/en/codes/c/chapter_graph/graph_adjacency_list_test.c b/en/codes/c/chapter_graph/graph_adjacency_list_test.c index 837d08f44..f8a4f6ec4 100644 --- a/en/codes/c/chapter_graph/graph_adjacency_list_test.c +++ b/en/codes/c/chapter_graph/graph_adjacency_list_test.c @@ -6,7 +6,7 @@ #include "graph_adjacency_list.c" -/* Driver Code */ +/* Driver code */ int main() { int vals[] = {1, 3, 2, 5, 4}; int size = sizeof(vals) / sizeof(vals[0]); @@ -21,31 +21,31 @@ int main() { for (int i = 0; i < egdeSize; i++) { addEdge(graph, edges[i][0], edges[i][1]); } - printf("\nAfter initialization, graph is\n"); + printf("\nGraph after initialization:\n"); printGraph(graph); - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] addEdge(graph, v[0], v[2]); - printf("\nAfter adding edge 1-2, graph is\n"); + printf("\nGraph after adding edge 1-2:\n"); printGraph(graph); - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] removeEdge(graph, v[0], v[1]); - printf("\nAfter deleting edge 1-3, graph is\n"); + printf("\nGraph after removing edge 1-3:\n"); printGraph(graph); - /* Add vertex */ + /* Add a vertex */ Vertex *v5 = newVertex(6); addVertex(graph, v5); - printf("\nAfter adding vertex 6, graph is\n"); + printf("\nGraph after adding vertex 6:\n"); printGraph(graph); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] removeVertex(graph, v[1]); - printf("\nAfter deleting vertex 3, graph is:\n"); + printf("\nGraph after removing vertex 3:\n"); printGraph(graph); // Free memory diff --git a/en/codes/c/chapter_graph/graph_adjacency_matrix.c b/en/codes/c/chapter_graph/graph_adjacency_matrix.c index a4341a22d..79a7038e2 100644 --- a/en/codes/c/chapter_graph/graph_adjacency_matrix.c +++ b/en/codes/c/chapter_graph/graph_adjacency_matrix.c @@ -6,10 +6,10 @@ #include "../utils/common.h" -// Assume max vertex count is 100 +// Assume at most 100 vertices #define MAX_SIZE 100 -/* Undirected graph structure based on adjacency matrix */ +/* Undirected graph structure implemented with an adjacency matrix */ typedef struct { int vertices[MAX_SIZE]; int adjMat[MAX_SIZE][MAX_SIZE]; @@ -33,13 +33,13 @@ void delGraphAdjMat(GraphAdjMat *graph) { free(graph); } -/* Add vertex */ +/* Add a vertex */ void addVertex(GraphAdjMat *graph, int val) { if (graph->size == MAX_SIZE) { - fprintf(stderr, "Graph vertex count has reached maximum\n"); + fprintf(stderr, "The graph has reached its maximum number of vertices\n"); return; } - // Add nth vertex and zero nth row and column + // Add the vertex at index n and set row n and column n to zero int n = graph->size; graph->vertices[n] = val; for (int i = 0; i <= n; i++) { @@ -48,23 +48,23 @@ void addVertex(GraphAdjMat *graph, int val) { graph->size++; } -/* Remove vertex */ +/* Remove a vertex */ void removeVertex(GraphAdjMat *graph, int index) { if (index < 0 || index >= graph->size) { fprintf(stderr, "Vertex index out of bounds\n"); return; } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list for (int i = index; i < graph->size - 1; i++) { graph->vertices[i] = graph->vertices[i + 1]; } - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix for (int i = index; i < graph->size - 1; i++) { for (int j = 0; j < graph->size; j++) { graph->adjMat[i][j] = graph->adjMat[i + 1][j]; } } - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for (int i = 0; i < graph->size; i++) { for (int j = index; j < graph->size - 1; j++) { graph->adjMat[i][j] = graph->adjMat[i][j + 1]; @@ -73,29 +73,29 @@ void removeVertex(GraphAdjMat *graph, int index) { graph->size--; } -/* Add edge */ -// Parameters i, j correspond to the vertices element indices +/* Add an edge */ +// Parameters i and j are element indices in vertices void addEdge(GraphAdjMat *graph, int i, int j) { if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { - fprintf(stderr, "Edge index out of bounds or equal\n"); + fprintf(stderr, "Edge endpoint indices are out of bounds or equal\n"); return; } graph->adjMat[i][j] = 1; graph->adjMat[j][i] = 1; } -/* Remove edge */ -// Parameters i, j correspond to the vertices element indices +/* Remove an edge */ +// Parameters i and j are element indices in vertices void removeEdge(GraphAdjMat *graph, int i, int j) { if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { - fprintf(stderr, "Edge index out of bounds or equal\n"); + fprintf(stderr, "Edge endpoint indices are out of bounds or equal\n"); return; } graph->adjMat[i][j] = 0; graph->adjMat[j][i] = 0; } -/* Print adjacency matrix */ +/* Print the adjacency matrix */ void printGraphAdjMat(GraphAdjMat *graph) { printf("Vertex list = "); printArray(graph->vertices, graph->size); @@ -105,9 +105,9 @@ void printGraphAdjMat(GraphAdjMat *graph) { } } -/* Driver Code */ +/* Driver code */ int main() { - // Add edge + // Initialize the undirected graph GraphAdjMat *graph = newGraphAdjMat(); int vertices[] = {1, 3, 2, 5, 4}; for (int i = 0; i < 5; i++) { @@ -117,30 +117,30 @@ int main() { for (int i = 0; i < 6; i++) { addEdge(graph, edges[i][0], edges[i][1]); } - printf("\nAfter initialization, graph is\n"); + printf("\nGraph after initialization:\n"); printGraphAdjMat(graph); - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively addEdge(graph, 0, 2); - printf("\nAfter adding edge 1-2, graph is\n"); + printf("\nGraph after adding edge 1-2:\n"); printGraphAdjMat(graph); - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively removeEdge(graph, 0, 1); - printf("\nAfter deleting edge 1-3, graph is\n"); + printf("\nGraph after removing edge 1-3:\n"); printGraphAdjMat(graph); - /* Add vertex */ + /* Add a vertex */ addVertex(graph, 6); - printf("\nAfter adding vertex 6, graph is\n"); + printf("\nGraph after adding vertex 6:\n"); printGraphAdjMat(graph); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 removeVertex(graph, 1); - printf("\nAfter deleting vertex 3, graph is\n"); + printf("\nGraph after removing vertex 3:\n"); printGraphAdjMat(graph); // Free memory diff --git a/en/codes/c/chapter_graph/graph_bfs.c b/en/codes/c/chapter_graph/graph_bfs.c index ec3c9f828..4cf44f79b 100644 --- a/en/codes/c/chapter_graph/graph_bfs.c +++ b/en/codes/c/chapter_graph/graph_bfs.c @@ -6,10 +6,10 @@ #include "graph_adjacency_list.c" -// Assume max node count is 100 +// Assume at most 100 vertices #define MAX_SIZE 100 -/* Node queue structure */ +/* Vertex queue structure */ typedef struct { Vertex *vertices[MAX_SIZE]; int front, rear, size; @@ -22,7 +22,7 @@ Queue *newQueue() { return q; } -/* Check if the queue is empty */ +/* Check whether the queue is empty */ int isEmpty(Queue *q) { return q->size == 0; } @@ -42,9 +42,9 @@ Vertex *dequeue(Queue *q) { return vet; } -/* Check if vertex has been visited */ +/* Check whether a vertex has been visited */ int isVisited(Vertex **visited, int size, Vertex *vet) { - // Traverse to find node using O(n) time + // Search for the vertex with a linear scan in O(n) time for (int i = 0; i < size; i++) { if (visited[i] == vet) return 1; @@ -53,22 +53,22 @@ int isVisited(Vertex **visited, int size, Vertex *vet) { } /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) { - // Queue used to implement BFS + // Use a queue to implement BFS Queue *queue = newQueue(); enqueue(queue, startVet); visited[(*visitedSize)++] = startVet; - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, continue until all reachable vertices have been visited while (!isEmpty(queue)) { Vertex *vet = dequeue(queue); // Dequeue the front vertex - res[(*resSize)++] = vet; // Record visited vertex - // Traverse all adjacent vertices of this vertex + res[(*resSize)++] = vet; // Record the visited vertex + // Traverse all neighbors of this vertex AdjListNode *node = findNode(graph, vet); while (node != NULL) { - // Skip vertices that have been visited + // Skip vertices that have already been visited if (!isVisited(visited, *visitedSize, node->vertex)) { - enqueue(queue, node->vertex); // Only enqueue unvisited vertices + enqueue(queue, node->vertex); // Enqueue only unvisited vertices visited[(*visitedSize)++] = node->vertex; // Mark this vertex as visited } node = node->next; @@ -78,9 +78,9 @@ void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, free(queue); } -/* Driver Code */ +/* Driver code */ int main() { - // Add edge + // Initialize the undirected graph int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int size = sizeof(vals) / sizeof(vals[0]); Vertex **v = valsToVets(vals, size); @@ -95,18 +95,18 @@ int main() { for (int i = 0; i < egdeSize; i++) { addEdge(graph, edges[i][0], edges[i][1]); } - printf("\nAfter initialization, graph is\n"); + printf("\nGraph after initialization:\n"); printGraph(graph); // Breadth-first traversal // Vertex traversal sequence Vertex *res[MAX_SIZE]; int resSize = 0; - // Used to record visited vertices + // Track vertices that have already been visited Vertex *visited[MAX_SIZE]; int visitedSize = 0; graphBFS(graph, v[0], res, &resSize, visited, &visitedSize); - printf("\nBreadth-first traversal (BFS) vertex sequence is\n"); + printf("\nVertex sequence from breadth-first traversal (BFS):\n"); printArray(vetsToVals(res, resSize), resSize); // Free memory diff --git a/en/codes/c/chapter_graph/graph_dfs.c b/en/codes/c/chapter_graph/graph_dfs.c index a88bd36b9..13098bf49 100644 --- a/en/codes/c/chapter_graph/graph_dfs.c +++ b/en/codes/c/chapter_graph/graph_dfs.c @@ -6,12 +6,12 @@ #include "graph_adjacency_list.c" -// Assume max node count is 100 +// Assume at most 100 vertices #define MAX_SIZE 100 -/* Check if vertex has been visited */ +/* Check whether a vertex has been visited */ int isVisited(Vertex **res, int size, Vertex *vet) { - // Traverse to find node using O(n) time + // Search for the vertex with a linear scan in O(n) time for (int i = 0; i < size; i++) { if (res[i] == vet) { return 1; @@ -22,14 +22,14 @@ int isVisited(Vertex **res, int size, Vertex *vet) { /* Depth-first traversal helper function */ void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) { - // Record visited vertex + // Record the visited vertex res[(*resSize)++] = vet; - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex AdjListNode *node = findNode(graph, vet); while (node != NULL) { - // Skip vertices that have been visited + // Skip vertices that have already been visited if (!isVisited(res, *resSize, node->vertex)) { - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex dfs(graph, res, resSize, node->vertex); } node = node->next; @@ -37,14 +37,14 @@ void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) { } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) { dfs(graph, res, resSize, startVet); } -/* Driver Code */ +/* Driver code */ int main() { - // Add edge + // Initialize the undirected graph int vals[] = {0, 1, 2, 3, 4, 5, 6}; int size = sizeof(vals) / sizeof(vals[0]); Vertex **v = valsToVets(vals, size); @@ -58,14 +58,14 @@ int main() { for (int i = 0; i < egdeSize; i++) { addEdge(graph, edges[i][0], edges[i][1]); } - printf("\nAfter initialization, graph is\n"); + printf("\nGraph after initialization:\n"); printGraph(graph); // Depth-first traversal Vertex *res[MAX_SIZE]; int resSize = 0; graphDFS(graph, v[0], res, &resSize); - printf("\nDepth-first traversal (DFS) vertex sequence is\n"); + printf("\nVertex sequence from depth-first traversal (DFS):\n"); printArray(vetsToVals(res, resSize), resSize); // Free memory diff --git a/en/codes/c/chapter_greedy/coin_change_greedy.c b/en/codes/c/chapter_greedy/coin_change_greedy.c index b8f8908ae..21f064f84 100644 --- a/en/codes/c/chapter_greedy/coin_change_greedy.c +++ b/en/codes/c/chapter_greedy/coin_change_greedy.c @@ -6,14 +6,14 @@ #include "../utils/common.h" -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ int coinChangeGreedy(int *coins, int size, int amt) { - // Assume coins list is sorted + // Assume the coins list is sorted int i = size - 1; int count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (amt > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -21,40 +21,40 @@ int coinChangeGreedy(int *coins, int size, int amt) { amt -= coins[i]; count++; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt == 0 ? count : -1; } -/* Driver Code */ +/* Driver code */ int main() { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution int coins1[6] = {1, 5, 10, 20, 50, 100}; int amt = 186; int res = coinChangeGreedy(coins1, 6, amt); printf("\ncoins = "); printArray(coins1, 6); printf("amt = %d\n", amt); - printf("Minimum number of coins needed to make %d is %d\n", amt, res); + printf("Greedy coin count for amount %d: %d\n", amt, res); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution int coins2[3] = {1, 20, 50}; amt = 60; res = coinChangeGreedy(coins2, 3, amt); printf("\ncoins = "); printArray(coins2, 3); printf("amt = %d\n", amt); - printf("Minimum number of coins needed to make %d is %d\n", amt, res); - printf("Actually minimum needed is 3, i.e., 20 + 20 + 20\n"); + printf("Greedy coin count for amount %d: %d\n", amt, res); + printf("The actual minimum is 3 coins: 20 + 20 + 20\n"); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution int coins3[3] = {1, 49, 50}; amt = 98; res = coinChangeGreedy(coins3, 3, amt); printf("\ncoins = "); printArray(coins3, 3); printf("amt = %d\n", amt); - printf("Minimum number of coins needed to make %d is %d\n", amt, res); - printf("Actually minimum needed is 2, i.e., 49 + 49\n"); + printf("Greedy coin count for amount %d: %d\n", amt, res); + printf("The actual minimum is 2 coins: 49 + 49\n"); return 0; } diff --git a/en/codes/c/chapter_greedy/fractional_knapsack.c b/en/codes/c/chapter_greedy/fractional_knapsack.c index 22878a163..fc5088fbd 100644 --- a/en/codes/c/chapter_greedy/fractional_knapsack.c +++ b/en/codes/c/chapter_greedy/fractional_knapsack.c @@ -12,31 +12,31 @@ typedef struct { int v; // Item value } Item; -/* Sort by value density */ +/* Sort by value per unit weight */ int sortByValueDensity(const void *a, const void *b) { Item *t1 = (Item *)a; Item *t2 = (Item *)b; return (float)(t1->v) / t1->w < (float)(t2->v) / t2->w; } -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value Item *items = malloc(sizeof(Item) * itemCount); for (int i = 0; i < itemCount; i++) { items[i] = (Item){.w = wgt[i], .v = val[i]}; } - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity); - // Loop for greedy selection + // Repeatedly make greedy choices float res = 0.0; for (int i = 0; i < itemCount; i++) { if (items[i].w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += items[i].v; cap -= items[i].w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += (float)cap / items[i].w * items[i].v; cap = 0; break; @@ -46,7 +46,7 @@ float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) { return res; } -/* Driver Code */ +/* Driver code */ int main(void) { int wgt[] = {10, 20, 30, 40, 50}; int val[] = {50, 120, 150, 210, 240}; @@ -54,7 +54,7 @@ int main(void) { // Greedy algorithm float res = fractionalKnapsack(wgt, val, sizeof(wgt) / sizeof(int), capacity); - printf("Maximum item value not exceeding knapsack capacity is %0.2f\n", res); + printf("Maximum item value within the knapsack capacity: %0.2f\n", res); return 0; } diff --git a/en/codes/c/chapter_greedy/max_capacity.c b/en/codes/c/chapter_greedy/max_capacity.c index a11df42ef..401983249 100644 --- a/en/codes/c/chapter_greedy/max_capacity.c +++ b/en/codes/c/chapter_greedy/max_capacity.c @@ -6,28 +6,28 @@ #include "../utils/common.h" -/* Find minimum value */ +/* Find the minimum */ int myMin(int a, int b) { return a < b ? a : b; } -/* Find maximum value */ +/* Find the maximum */ int myMax(int a, int b) { return a > b ? a : b; } -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ int maxCapacity(int ht[], int htLength) { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array int i = 0; int j = htLength - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 int res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity int capacity = myMin(ht[i], ht[j]) * (j - i); res = myMax(res, capacity); - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i++; } else { @@ -37,13 +37,13 @@ int maxCapacity(int ht[], int htLength) { return res; } -/* Driver Code */ +/* Driver code */ int main(void) { int ht[] = {3, 8, 5, 2, 7, 7, 3, 4}; // Greedy algorithm int res = maxCapacity(ht, sizeof(ht) / sizeof(int)); - printf("Maximum capacity is %d\n", res); + printf("Maximum capacity: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_greedy/max_product_cutting.c b/en/codes/c/chapter_greedy/max_product_cutting.c index 7f34abd47..3c522afb9 100644 --- a/en/codes/c/chapter_greedy/max_product_cutting.c +++ b/en/codes/c/chapter_greedy/max_product_cutting.c @@ -6,33 +6,33 @@ #include "../utils/common.h" -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ int maxProductCutting(int n) { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder int a = n / 3; int b = n % 3; if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return pow(3, a - 1) * 2 * 2; } if (b == 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return pow(3, a) * 2; } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return pow(3, a); } -/* Driver Code */ +/* Driver code */ int main(void) { int n = 58; // Greedy algorithm int res = maxProductCutting(n); - printf("Maximum cutting product is %d\n", res); + printf("Maximum product after splitting: %d\n", res); return 0; } diff --git a/en/codes/c/chapter_hashing/array_hash_map.c b/en/codes/c/chapter_hashing/array_hash_map.c index 94e4abff2..4f570b4b4 100644 --- a/en/codes/c/chapter_hashing/array_hash_map.c +++ b/en/codes/c/chapter_hashing/array_hash_map.c @@ -9,7 +9,7 @@ /* Default hash table size */ #define MAX_SIZE 100 -/* Key-value pair int->string */ +/* Key-value pair: int -> string */ typedef struct { int key; char *val; @@ -21,7 +21,7 @@ typedef struct { int len; } MapSet; -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ typedef struct { Pair *buckets[MAX_SIZE]; } ArrayHashMap; @@ -52,7 +52,7 @@ int hashFunc(int key) { return index; } -/* Query operation */ +/* Lookup operation */ const char *get(const ArrayHashMap *hmap, const int key) { int index = hashFunc(key); const Pair *Pair = hmap->buckets[index]; @@ -61,7 +61,7 @@ const char *get(const ArrayHashMap *hmap, const int key) { return Pair->val; } -/* Add operation */ +/* Insert operation */ void put(ArrayHashMap *hmap, const int key, const char *val) { Pair *Pair = malloc(sizeof(Pair)); Pair->key = key; @@ -85,7 +85,7 @@ void pairSet(ArrayHashMap *hmap, MapSet *set) { Pair *entries; int i = 0, index = 0; int total = 0; - /* Count valid key-value pairs */ + /* Count stored key-value pairs */ for (i = 0; i < MAX_SIZE; i++) { if (hmap->buckets[i] != NULL) { total++; @@ -109,7 +109,7 @@ void keySet(ArrayHashMap *hmap, MapSet *set) { int *keys; int i = 0, index = 0; int total = 0; - /* Count valid key-value pairs */ + /* Count stored key-value pairs */ for (i = 0; i < MAX_SIZE; i++) { if (hmap->buckets[i] != NULL) { total++; @@ -131,7 +131,7 @@ void valueSet(ArrayHashMap *hmap, MapSet *set) { char **vals; int i = 0, index = 0; int total = 0; - /* Count valid key-value pairs */ + /* Count stored key-value pairs */ for (i = 0; i < MAX_SIZE; i++) { if (hmap->buckets[i] != NULL) { total++; @@ -148,7 +148,7 @@ void valueSet(ArrayHashMap *hmap, MapSet *set) { set->len = total; } -/* Print hash table */ +/* Print the hash table */ void print(ArrayHashMap *hmap) { int i; MapSet set; @@ -160,43 +160,43 @@ void print(ArrayHashMap *hmap) { free(set.set); } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize hash table */ + /* Initialize the hash table */ ArrayHashMap *hmap = newArrayHashMap(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - put(hmap, 12836, "Xiao Ha"); - put(hmap, 15937, "Xiao Luo"); - put(hmap, 16750, "Xiao Suan"); - put(hmap, 13276, "Xiao Fa"); - put(hmap, 10583, "Xiao Ya"); - printf("\nAfter addition, hash table is\nKey -> Value\n"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + put(hmap, 12836, "小哈"); + put(hmap, 15937, "小啰"); + put(hmap, 16750, "小算"); + put(hmap, 13276, "小法"); + put(hmap, 10583, "小鸭"); + printf("\nHash table after insertion:\nKey -> Value\n"); print(hmap); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value const char *name = get(hmap, 15937); - printf("\nInput student ID 15937, found name %s\n", name); + printf("\nLook up student ID 15937; name found: %s\n", name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table removeItem(hmap, 10583); - printf("\nAfter deleting 10583, hash table is\nKey -> Value\n"); + printf("\nHash table after removing 10583:\nKey -> Value\n"); print(hmap); - /* Traverse hash table */ + /* Traverse the hash table */ int i; - printf("\nTraverse key-value pairs Key->Value\n"); + printf("\nIterate over key-value pairs, Key->Value\n"); print(hmap); MapSet set; keySet(hmap, &set); int *keys = (int *)set.set; - printf("\nTraverse keys only Key\n"); + printf("\nIterate over keys only, Key\n"); for (i = 0; i < set.len; i++) { printf("%d\n", keys[i]); } @@ -204,7 +204,7 @@ int main() { valueSet(hmap, &set); char **vals = (char **)set.set; - printf("\nTraverse values only Value\n"); + printf("\nIterate over values only, Value\n"); for (i = 0; i < set.len; i++) { printf("%s\n", vals[i]); } diff --git a/en/codes/c/chapter_hashing/hash_map_chaining.c b/en/codes/c/chapter_hashing/hash_map_chaining.c index c2fd415d5..9c3f9e174 100644 --- a/en/codes/c/chapter_hashing/hash_map_chaining.c +++ b/en/codes/c/chapter_hashing/hash_map_chaining.c @@ -8,7 +8,7 @@ #include #include -// Assume max val length is 100 +// Assume val has a maximum length of 100 #define MAX_SIZE 100 /* Key-value pair */ @@ -27,8 +27,8 @@ typedef struct Node { typedef struct { int size; // Number of key-value pairs int capacity; // Hash table capacity - double loadThres; // Load factor threshold for triggering expansion - int extendRatio; // Expansion multiplier + double loadThres; // Load factor threshold that triggers capacity growth + int extendRatio; // Capacity growth factor Node **buckets; // Bucket array } HashMapChaining; @@ -71,10 +71,10 @@ double loadFactor(HashMapChaining *hashMap) { return (double)hashMap->size / (double)hashMap->capacity; } -/* Query operation */ +/* Lookup operation */ char *get(HashMapChaining *hashMap, int key) { int index = hashFunc(hashMap, key); - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val Node *cur = hashMap->buckets[index]; while (cur) { if (cur->pair->key == key) { @@ -82,25 +82,25 @@ char *get(HashMapChaining *hashMap, int key) { } cur = cur->next; } - return ""; // Return empty string if key not found + return ""; // Return an empty string if key is not found } -/* Add operation */ +/* Insert operation */ void put(HashMapChaining *hashMap, int key, const char *val); -/* Expand hash table */ +/* Grow the hash table capacity */ void extend(HashMapChaining *hashMap) { - // Temporarily store the original hash table + // Keep a reference to the original hash table int oldCapacity = hashMap->capacity; Node **oldBuckets = hashMap->buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity hashMap->capacity *= hashMap->extendRatio; hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *)); for (int i = 0; i < hashMap->capacity; i++) { hashMap->buckets[i] = NULL; } hashMap->size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (int i = 0; i < oldCapacity; i++) { Node *cur = oldBuckets[i]; while (cur) { @@ -116,23 +116,23 @@ void extend(HashMapChaining *hashMap) { free(oldBuckets); } -/* Add operation */ +/* Insert operation */ void put(HashMapChaining *hashMap, int key, const char *val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor(hashMap) > hashMap->loadThres) { extend(hashMap); } int index = hashFunc(hashMap, key); - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return Node *cur = hashMap->buckets[index]; while (cur) { if (cur->pair->key == key) { - strcpy(cur->pair->val, val); // If specified key is found, update corresponding val and return + strcpy(cur->pair->val, val); // If the specified key is found, update its val and return return; } cur = cur->next; } - // If key not found, add key-value pair to list head + // If key is absent, add the key-value pair at the head of the linked list Pair *newPair = (Pair *)malloc(sizeof(Pair)); newPair->key = key; strcpy(newPair->val, val); @@ -150,7 +150,7 @@ void removeItem(HashMapChaining *hashMap, int key) { Node *pre = NULL; while (cur) { if (cur->pair->key == key) { - // Remove key-value pair from it + // Remove the key-value pair from the list if (pre) { pre->next = cur->next; } else { @@ -167,7 +167,7 @@ void removeItem(HashMapChaining *hashMap, int key) { } } -/* Print hash table */ +/* Print the hash table */ void print(HashMapChaining *hashMap) { for (int i = 0; i < hashMap->capacity; i++) { Node *cur = hashMap->buckets[i]; @@ -180,33 +180,33 @@ void print(HashMapChaining *hashMap) { } } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize hash table */ + /* Initialize the hash table */ HashMapChaining *hashMap = newHashMapChaining(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - put(hashMap, 12836, "Xiao Ha"); - put(hashMap, 15937, "Xiao Luo"); - put(hashMap, 16750, "Xiao Suan"); - put(hashMap, 13276, "Xiao Fa"); - put(hashMap, 10583, "Xiao Ya"); - printf("\nAfter addition, hash table is\nKey -> Value\n"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + put(hashMap, 12836, "小哈"); + put(hashMap, 15937, "小啰"); + put(hashMap, 16750, "小算"); + put(hashMap, 13276, "小法"); + put(hashMap, 10583, "小鸭"); + printf("\nHash table after insertion:\nKey -> Value\n"); print(hashMap); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value char *name = get(hashMap, 13276); - printf("\nInput student ID 13276, found name %s\n", name); + printf("\nLook up student ID 13276; name found: %s\n", name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table removeItem(hashMap, 12836); - printf("\nAfter deleting student ID 12836, hash table is\nKey -> Value\n"); + printf("\nHash table after removing student ID 12836:\nKey -> Value\n"); print(hashMap); - /* Free hash table space */ + /* Free the hash table's memory */ delHashMapChaining(hashMap); return 0; diff --git a/en/codes/c/chapter_hashing/hash_map_open_addressing.c b/en/codes/c/chapter_hashing/hash_map_open_addressing.c index d6ad81f1f..b6af8d61b 100644 --- a/en/codes/c/chapter_hashing/hash_map_open_addressing.c +++ b/en/codes/c/chapter_hashing/hash_map_open_addressing.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -/* Hash table with open addressing */ +/* Key-value pair */ typedef struct { int key; char *val; @@ -16,10 +16,10 @@ typedef struct { typedef struct { int size; // Number of key-value pairs int capacity; // Hash table capacity - double loadThres; // Load factor threshold for triggering expansion - int extendRatio; // Expansion multiplier + double loadThres; // Load factor threshold that triggers capacity growth + int extendRatio; // Capacity growth factor Pair **buckets; // Bucket array - Pair *TOMBSTONE; // Removal marker + Pair *TOMBSTONE; // Tombstone marker } HashMapOpenAddressing; // Function declaration @@ -64,54 +64,54 @@ double loadFactor(HashMapOpenAddressing *hashMap) { return (double)hashMap->size / (double)hashMap->capacity; } -/* Search for bucket index corresponding to key */ +/* Find the bucket index for key */ int findBucket(HashMapOpenAddressing *hashMap, int key) { int index = hashFunc(hashMap, key); int firstTombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (hashMap->buckets[index] != NULL) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (hashMap->buckets[index]->key == key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone != -1) { hashMap->buckets[firstTombstone] = hashMap->buckets[index]; hashMap->buckets[index] = hashMap->TOMBSTONE; - return firstTombstone; // Return the moved bucket index + return firstTombstone; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if (firstTombstone == -1 && hashMap->buckets[index] == hashMap->TOMBSTONE) { firstTombstone = index; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % hashMap->capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone == -1 ? index : firstTombstone; } -/* Query operation */ +/* Lookup operation */ char *get(HashMapOpenAddressing *hashMap, int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(hashMap, key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { return hashMap->buckets[index]->val; } - // Return empty string if key-value pair does not exist + // Return an empty string if the key-value pair does not exist return ""; } -/* Add operation */ +/* Insert operation */ void put(HashMapOpenAddressing *hashMap, int key, char *val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor(hashMap) > hashMap->loadThres) { extend(hashMap); } - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(hashMap, key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { free(hashMap->buckets[index]->val); hashMap->buckets[index]->val = (char *)malloc(sizeof(strlen(val) + 1)); @@ -119,7 +119,7 @@ void put(HashMapOpenAddressing *hashMap, int key, char *val) { hashMap->buckets[index]->val[strlen(val)] = '\0'; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it Pair *pair = (Pair *)malloc(sizeof(Pair)); pair->key = key; pair->val = (char *)malloc(sizeof(strlen(val) + 1)); @@ -132,9 +132,9 @@ void put(HashMapOpenAddressing *hashMap, int key, char *val) { /* Remove operation */ void removeItem(HashMapOpenAddressing *hashMap, int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(hashMap, key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { Pair *pair = hashMap->buckets[index]; free(pair->val); @@ -144,16 +144,16 @@ void removeItem(HashMapOpenAddressing *hashMap, int key) { } } -/* Expand hash table */ +/* Grow the hash table capacity */ void extend(HashMapOpenAddressing *hashMap) { - // Temporarily store the original hash table + // Keep a reference to the original hash table Pair **bucketsTmp = hashMap->buckets; int oldCapacity = hashMap->capacity; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity hashMap->capacity *= hashMap->extendRatio; hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *)); hashMap->size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (int i = 0; i < oldCapacity; i++) { Pair *pair = bucketsTmp[i]; if (pair != NULL && pair != hashMap->TOMBSTONE) { @@ -165,7 +165,7 @@ void extend(HashMapOpenAddressing *hashMap) { free(bucketsTmp); } -/* Print hash table */ +/* Print the hash table */ void print(HashMapOpenAddressing *hashMap) { for (int i = 0; i < hashMap->capacity; i++) { Pair *pair = hashMap->buckets[i]; @@ -179,33 +179,33 @@ void print(HashMapOpenAddressing *hashMap) { } } -/* Driver Code */ +/* Driver code */ int main() { - // Initialize hash table + // Initialize the hash table HashMapOpenAddressing *hashmap = newHashMapOpenAddressing(); - // Add operation - // Add key-value pair (key, val) to the hash table - put(hashmap, 12836, "Xiao Ha"); - put(hashmap, 15937, "Xiao Luo"); - put(hashmap, 16750, "Xiao Suan"); - put(hashmap, 13276, "Xiao Fa"); - put(hashmap, 10583, "Xiao Ya"); - printf("\nAfter addition, hash table is\nKey -> Value\n"); + // Insert operation + // Insert the key-value pair (key, val) into the hash table + put(hashmap, 12836, "小哈"); + put(hashmap, 15937, "小啰"); + put(hashmap, 16750, "小算"); + put(hashmap, 13276, "小法"); + put(hashmap, 10583, "小鸭"); + printf("\nHash table after insertion:\nKey -> Value\n"); print(hashmap); - // Query operation - // Input key into hash table to get value val + // Lookup operation + // Look up key in the hash table to obtain val char *name = get(hashmap, 13276); - printf("\nInput student ID 13276, found name %s\n", name); + printf("\nLook up student ID 13276; name found: %s\n", name); // Remove operation - // Remove key-value pair (key, val) from hash table + // Remove the key-value pair (key, val) from the hash table removeItem(hashmap, 16750); - printf("\nAfter deleting 16750, hash table is\nKey -> Value\n"); + printf("\nHash table after removing 16750:\nKey -> Value\n"); print(hashmap); - // Destroy hash table + // Destroy the hash table delHashMapOpenAddressing(hashmap); return 0; } diff --git a/en/codes/c/chapter_hashing/simple_hash.c b/en/codes/c/chapter_hashing/simple_hash.c index 41c1df04c..33b2b26e5 100644 --- a/en/codes/c/chapter_hashing/simple_hash.c +++ b/en/codes/c/chapter_hashing/simple_hash.c @@ -37,7 +37,7 @@ int xorHash(char *key) { return hash & MODULUS; } -/* Rotational hash */ +/* Rotating hash */ int rotHash(char *key) { long long hash = 0; const int MODULUS = 1000000007; @@ -48,21 +48,21 @@ int rotHash(char *key) { return (int)hash; } -/* Driver Code */ +/* Driver code */ int main() { - char *key = "Hello Algo"; + char *key = "Hello 算法"; int hash = addHash(key); - printf("Additive hash value is %d\n", hash); + printf("Additive hash value: %d\n", hash); hash = mulHash(key); - printf("Multiplicative hash value is %d\n", hash); + printf("Multiplicative hash value: %d\n", hash); hash = xorHash(key); - printf("XOR hash value is %d\n", hash); + printf("XOR hash value: %d\n", hash); hash = rotHash(key); - printf("Rotational hash value is %d\n", hash); + printf("Rotation hash value: %d\n", hash); return 0; } diff --git a/en/codes/c/chapter_heap/my_heap.c b/en/codes/c/chapter_heap/my_heap.c index 929772ee7..9baa7b76e 100644 --- a/en/codes/c/chapter_heap/my_heap.c +++ b/en/codes/c/chapter_heap/my_heap.c @@ -10,9 +10,9 @@ /* Max heap */ typedef struct { - // size represents the actual number of elements + // size is the actual number of elements int size; - // Use pre-allocated array to avoid expansion + // Use a preallocated array to avoid resizing int data[MAX_SIZE]; } MaxHeap; @@ -21,14 +21,14 @@ void siftDown(MaxHeap *maxHeap, int i); void siftUp(MaxHeap *maxHeap, int i); int parent(MaxHeap *maxHeap, int i); -/* Constructor, build heap from slice */ +/* Constructor: build a heap from the input array */ MaxHeap *newMaxHeap(int nums[], int size) { - // Push all elements to heap + // Place all elements in the heap MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap)); maxHeap->size = size; memcpy(maxHeap->data, nums, size * sizeof(int)); for (int i = parent(maxHeap, size - 1); i >= 0; i--) { - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node siftDown(maxHeap, i); } return maxHeap; @@ -40,19 +40,19 @@ void delMaxHeap(MaxHeap *maxHeap) { free(maxHeap); } -/* Get index of left child node */ +/* Get the left child index */ int left(MaxHeap *maxHeap, int i) { return 2 * i + 1; } -/* Get index of right child node */ +/* Get the right child index */ int right(MaxHeap *maxHeap, int i) { return 2 * i + 2; } -/* Get index of parent node */ +/* Get the parent index */ int parent(MaxHeap *maxHeap, int i) { - return (i - 1) / 2; // Round down + return (i - 1) / 2; // Integer division truncates toward zero } /* Swap elements */ @@ -62,59 +62,59 @@ void swap(MaxHeap *maxHeap, int i, int j) { maxHeap->data[j] = temp; } -/* Get heap size */ +/* Get the heap size */ int size(MaxHeap *maxHeap) { return maxHeap->size; } -/* Check if heap is empty */ +/* Check whether the heap is empty */ int isEmpty(MaxHeap *maxHeap) { return maxHeap->size == 0; } -/* Access top element */ +/* Access the top element of the heap */ int peek(MaxHeap *maxHeap) { return maxHeap->data[0]; } -/* Element enters heap */ +/* Push an element onto the heap */ void push(MaxHeap *maxHeap, int val) { - // By default, should not add this many nodes + // Normally, the number of nodes should remain below this limit if (maxHeap->size == MAX_SIZE) { - printf("heap is full!"); + printf("Heap is full!"); return; } - // Add node + // Add a node maxHeap->data[maxHeap->size] = val; maxHeap->size++; - // Heapify from bottom to top + // Sift up siftUp(maxHeap, maxHeap->size - 1); } -/* Element exits heap */ +/* Pop an element from the heap */ int pop(MaxHeap *maxHeap) { - // Handle empty case + // Handle an empty heap if (isEmpty(maxHeap)) { - printf("heap is empty!"); + printf("Heap is empty!"); return INT_MAX; } - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) swap(maxHeap, 0, size(maxHeap) - 1); - // Remove node + // Remove a node int val = maxHeap->data[maxHeap->size - 1]; maxHeap->size--; - // Return top element + // Sift down siftDown(maxHeap, 0); - // Return heap top element + // Return the top element of the heap return val; } -/* Starting from node i, heapify from top to bottom */ +/* Sift down starting from node i */ void siftDown(MaxHeap *maxHeap, int i) { while (true) { - // Find node with maximum value among nodes i, l, r, denoted as max + // Find the node with the largest value among i, l, and r, and store its index in max int l = left(maxHeap, i); int r = right(maxHeap, i); int max = i; @@ -124,29 +124,29 @@ void siftDown(MaxHeap *maxHeap, int i) { if (r < size(maxHeap) && maxHeap->data[r] > maxHeap->data[max]) { max = r; } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (max == i) { break; } - // Swap two nodes + // Swap the two nodes swap(maxHeap, i, max); - // Loop downwards heapification + // Continue sifting down i = max; } } -/* Starting from node i, heapify from bottom to top */ +/* Sift up starting from node i */ void siftUp(MaxHeap *maxHeap, int i) { while (true) { - // Get parent node of node i + // Get the parent of node i int p = parent(maxHeap, i); - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) { break; } - // Swap two nodes + // Swap the two nodes swap(maxHeap, i, p); - // Loop upward heapify + // Continue sifting up i = p; } } diff --git a/en/codes/c/chapter_heap/my_heap_test.c b/en/codes/c/chapter_heap/my_heap_test.c index 9e2fa8a7f..36a0b077e 100644 --- a/en/codes/c/chapter_heap/my_heap_test.c +++ b/en/codes/c/chapter_heap/my_heap_test.c @@ -6,33 +6,33 @@ #include "my_heap.c" -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize heap */ - // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + /* Initialize the heap */ + // Initialize a max heap int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; MaxHeap *maxHeap = newMaxHeap(nums, sizeof(nums) / sizeof(int)); - printf("After input array and building heap\n"); + printf("After building a heap from the input array:\n"); printHeap(maxHeap->data, maxHeap->size); - /* Check if heap is empty */ - printf("\nHeap top element is %d\n", peek(maxHeap)); + /* Get the top element of the heap */ + printf("\nTop element of the heap: %d\n", peek(maxHeap)); - /* Element enters heap */ + /* Push an element onto the heap */ push(maxHeap, 7); - printf("\nAfter element 7 pushes to heap\n"); + printf("\nAfter pushing element 7 onto the heap:\n"); printHeap(maxHeap->data, maxHeap->size); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ int top = pop(maxHeap); - printf("\nAfter heap top element %d exits heap\n", top); + printf("\nAfter popping top element %d from the heap:\n", top); printHeap(maxHeap->data, maxHeap->size); - /* Get heap size */ - printf("\nHeap element count is %d\n", size(maxHeap)); + /* Get the heap size */ + printf("\nNumber of elements in the heap: %d\n", size(maxHeap)); - /* Check if heap is empty */ - printf("\nIs heap empty %d\n", isEmpty(maxHeap)); + /* Check whether the heap is empty */ + printf("\nIs the heap empty: %d\n", isEmpty(maxHeap)); // Free memory delMaxHeap(maxHeap); diff --git a/en/codes/c/chapter_heap/top_k.c b/en/codes/c/chapter_heap/top_k.c index 7a82c6adb..25e60e3c5 100644 --- a/en/codes/c/chapter_heap/top_k.c +++ b/en/codes/c/chapter_heap/top_k.c @@ -6,27 +6,27 @@ #include "my_heap.c" -/* Element enters heap */ +/* Push an element onto the heap */ void pushMinHeap(MaxHeap *maxHeap, int val) { - // Negate element + // Negate the element push(maxHeap, -val); } -/* Element exits heap */ +/* Pop an element from the heap */ int popMinHeap(MaxHeap *maxHeap) { - // Negate element + // Negate the element return -pop(maxHeap); } -/* Access top element */ +/* Access the top element of the heap */ int peekMinHeap(MaxHeap *maxHeap) { - // Negate element + // Negate the element return -peek(maxHeap); } -/* Extract elements from heap */ +/* Get the elements in the heap */ int *getMinHeap(MaxHeap *maxHeap) { - // Negate all heap elements and store in res array + // Negate every element in the heap and store the results in array res int *res = (int *)malloc(maxHeap->size * sizeof(int)); for (int i = 0; i < maxHeap->size; i++) { res[i] = -maxHeap->data[i]; @@ -34,19 +34,19 @@ int *getMinHeap(MaxHeap *maxHeap) { return res; } -// Function to find k largest elements in array using heap +// Use a heap to find the k largest elements in an array int *topKHeap(int *nums, int sizeNums, int k) { - // Python's heapq module implements min heap by default - // Note: We negate all heap elements to simulate min heap using max heap + // Initialize a min heap + // Note: negate every element to simulate a min heap with a max heap int *empty = (int *)malloc(0); MaxHeap *maxHeap = newMaxHeap(empty, 0); - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for (int i = 0; i < k; i++) { pushMinHeap(maxHeap, nums[i]); } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for (int i = k; i < sizeNums; i++) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if (nums[i] > peekMinHeap(maxHeap)) { popMinHeap(maxHeap); pushMinHeap(maxHeap, nums[i]); @@ -58,14 +58,14 @@ int *topKHeap(int *nums, int sizeNums, int k) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {1, 7, 6, 3, 2}; int k = 3; int sizeNums = sizeof(nums) / sizeof(nums[0]); int *res = topKHeap(nums, sizeNums, k); - printf("The largest %d elements are: ", k); + printf("The %d largest elements are: ", k); printArray(res, k); free(res); diff --git a/en/codes/c/chapter_searching/binary_search.c b/en/codes/c/chapter_searching/binary_search.c index 84247319a..fc2d93c8f 100644 --- a/en/codes/c/chapter_searching/binary_search.c +++ b/en/codes/c/chapter_searching/binary_search.c @@ -6,52 +6,52 @@ #include "../utils/common.h" -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ int binarySearch(int *nums, int len, int target) { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements int i = 0, j = len - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j] + if (nums[m] < target) // In this case, target must be in [m+1, j] if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m-1] + else if (nums[m] > target) // In this case, target must be in [i, m-1] if it exists j = m - 1; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ int binarySearchLCRO(int *nums, int len, int target) { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last int i = 0, j = len; - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j) + if (nums[m] < target) // In this case, target must be in [m+1, j) if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m) + else if (nums[m] > target) // In this case, target must be in [i, m) if it exists j = m; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Driver Code */ +/* Driver code */ int main() { int target = 6; int nums[10] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ int index = binarySearch(nums, 10, target); printf("Index of target element 6 = %d\n", index); - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums, 10, target); printf("Index of target element 6 = %d\n", index); diff --git a/en/codes/c/chapter_searching/binary_search_edge.c b/en/codes/c/chapter_searching/binary_search_edge.c index 5d295bd55..13ea07c5b 100644 --- a/en/codes/c/chapter_searching/binary_search_edge.c +++ b/en/codes/c/chapter_searching/binary_search_edge.c @@ -6,61 +6,61 @@ #include "../utils/common.h" -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ int binarySearchInsertion(int *nums, int numSize, int target) { - int i = 0, j = numSize - 1; // Initialize closed interval [0, n-1] + int i = 0, j = numSize - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i return i; } -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ int binarySearchLeftEdge(int *nums, int numSize, int target) { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target int i = binarySearchInsertion(nums, numSize, target); - // Target not found, return -1 + // If target was not found, return -1 if (i == numSize || nums[i] != target) { return -1; } - // Found target, return index i + // Target was found; return index i return i; } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ int binarySearchRightEdge(int *nums, int numSize, int target) { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 int i = binarySearchInsertion(nums, numSize, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // j points to the rightmost target, while i points to the first element greater than target int j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if (j == -1 || nums[j] != target) { return -1; } - // Found target, return index j + // Target was found; return index j return j; } -/* Driver Code */ +/* Driver code */ int main() { - // Array with duplicate elements + // Array containing duplicate elements int nums[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; printf("\nArray nums = "); printArray(nums, sizeof(nums) / sizeof(nums[0])); - // Binary search left and right boundaries + // Find the left and right boundaries using binary search int targets[] = {6, 7}; for (int i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) { int index = binarySearchLeftEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]); - printf("Leftmost element %d index is %d\n", targets[i], index); + printf("Index of the leftmost occurrence of %d: %d\n", targets[i], index); index = binarySearchRightEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]); - printf("Rightmost element %d index is %d\n", targets[i], index); + printf("Index of the rightmost occurrence of %d: %d\n", targets[i], index); } return 0; diff --git a/en/codes/c/chapter_searching/binary_search_insertion.c b/en/codes/c/chapter_searching/binary_search_insertion.c index c7b6401fc..8ba7f3430 100644 --- a/en/codes/c/chapter_searching/binary_search_insertion.c +++ b/en/codes/c/chapter_searching/binary_search_insertion.c @@ -6,62 +6,62 @@ #include "../utils/common.h" -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ int binarySearchInsertionSimple(int *nums, int numSize, int target) { - int i = 0, j = numSize - 1; // Initialize closed interval [0, n-1] + int i = 0, j = numSize - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - return m; // Found target, return insertion point m + return m; // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i; } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ int binarySearchInsertion(int *nums, int numSize, int target) { - int i = 0, j = numSize - 1; // Initialize closed interval [0, n-1] + int i = 0, j = numSize - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i return i; } -/* Driver Code */ +/* Driver code */ int main() { - // Array without duplicate elements + // Array with no duplicate elements int nums1[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; printf("\nArray nums = "); printArray(nums1, sizeof(nums1) / sizeof(nums1[0])); - // Binary search for insertion point + // Find an insertion point using binary search int targets1[] = {6, 9}; for (int i = 0; i < sizeof(targets1) / sizeof(targets1[0]); i++) { int index = binarySearchInsertionSimple(nums1, sizeof(nums1) / sizeof(nums1[0]), targets1[i]); - printf("Insertion point index for element %d is %d\n", targets1[i], index); + printf("Insertion index for element %d: %d\n", targets1[i], index); } - // Array with duplicate elements + // Array containing duplicate elements int nums2[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; printf("\nArray nums = "); printArray(nums2, sizeof(nums2) / sizeof(nums2[0])); - // Binary search for insertion point + // Find an insertion point using binary search int targets2[] = {2, 6, 20}; for (int i = 0; i < sizeof(targets2) / sizeof(int); i++) { int index = binarySearchInsertion(nums2, sizeof(nums2) / sizeof(nums2[0]), targets2[i]); - printf("Insertion point index for element %d is %d\n", targets2[i], index); + printf("Insertion index for element %d: %d\n", targets2[i], index); } return 0; diff --git a/en/codes/c/chapter_searching/two_sum.c b/en/codes/c/chapter_searching/two_sum.c index 34c106ebf..c3fdc0eec 100644 --- a/en/codes/c/chapter_searching/two_sum.c +++ b/en/codes/c/chapter_searching/two_sum.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) { for (int i = 0; i < numsSize; ++i) { for (int j = i + 1; j < numsSize; ++j) { @@ -29,14 +29,14 @@ typedef struct { UT_hash_handle hh; // Implemented using uthash.h } HashTable; -/* Hash table lookup */ +/* Look up a key in the hash table */ HashTable *find(HashTable *h, int key) { HashTable *tmp; HASH_FIND_INT(h, &key, tmp); return tmp; } -/* Hash table element insertion */ +/* Insert an entry into the hash table */ void insert(HashTable **h, int key, int val) { HashTable *t = find(*h, key); if (t == NULL) { @@ -48,7 +48,7 @@ void insert(HashTable **h, int key, int val) { } } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) { HashTable *hashtable = NULL; for (int i = 0; i < numsSize; i++) { @@ -65,21 +65,21 @@ int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) { return NULL; } -/* Driver Code */ +/* Driver code */ int main() { - // ======= Test Case ======= + // ======= Test case ======= int nums[] = {2, 7, 11, 15}; int target = 13; - // ====== Driver Code ====== + // ====== Driver code ====== int returnSize; int *res = twoSumBruteForce(nums, sizeof(nums) / sizeof(int), target, &returnSize); // Method 1 - printf("Method 1 res = "); + printf("Method 1: res = "); printArray(res, returnSize); // Method 2 res = twoSumHashTable(nums, sizeof(nums) / sizeof(int), target, &returnSize); - printf("Method 2 res = "); + printf("Method 2: res = "); printArray(res, returnSize); return 0; diff --git a/en/codes/c/chapter_sorting/bubble_sort.c b/en/codes/c/chapter_sorting/bubble_sort.c index 198e3f33d..062d1a217 100644 --- a/en/codes/c/chapter_sorting/bubble_sort.c +++ b/en/codes/c/chapter_sorting/bubble_sort.c @@ -8,9 +8,9 @@ /* Bubble sort */ void bubbleSort(int nums[], int size) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = size - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { int temp = nums[j]; @@ -21,12 +21,12 @@ void bubbleSort(int nums[], int size) { } } -/* Bubble sort (flag optimization) */ +/* Bubble sort (flag optimization)*/ void bubbleSortWithFlag(int nums[], int size) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = size - 1; i > 0; i--) { bool flag = false; - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { int temp = nums[j]; @@ -40,7 +40,7 @@ void bubbleSortWithFlag(int nums[], int size) { } } -/* Driver Code */ +/* Driver code */ int main() { int nums[6] = {4, 1, 3, 1, 5, 2}; printf("After bubble sort: "); diff --git a/en/codes/c/chapter_sorting/bucket_sort.c b/en/codes/c/chapter_sorting/bucket_sort.c index 9d2b76d7d..9fc0ea63d 100644 --- a/en/codes/c/chapter_sorting/bucket_sort.c +++ b/en/codes/c/chapter_sorting/bucket_sort.c @@ -18,14 +18,14 @@ int compare(const void *a, const void *b) { /* Bucket sort */ void bucketSort(float nums[], int n) { int k = n / 2; // Initialize k = n/2 buckets - int *sizes = malloc(k * sizeof(int)); // Record each bucket's size + int *sizes = malloc(k * sizeof(int)); // Record the size of each bucket float **buckets = malloc(k * sizeof(float *)); // Array of dynamic arrays (buckets) - // Pre-allocate sufficient space for each bucket + // Preallocate enough space for each bucket for (int i = 0; i < k; ++i) { buckets[i] = (float *)malloc(n * sizeof(float)); sizes[i] = 0; } - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for (int i = 0; i < n; ++i) { int idx = (int)(nums[i] * k); buckets[idx][sizes[idx]++] = nums[i]; @@ -34,7 +34,7 @@ void bucketSort(float nums[], int n) { for (int i = 0; i < k; ++i) { qsort(buckets[i], sizes[i], sizeof(float), compare); } - // 3. Merge sorted buckets + // 3. Merge the sorted buckets int idx = 0; for (int i = 0; i < k; ++i) { for (int j = 0; j < sizes[i]; ++j) { @@ -45,12 +45,12 @@ void bucketSort(float nums[], int n) { } } -/* Driver Code */ +/* Driver code */ int main() { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) float nums[SIZE] = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f}; bucketSort(nums, SIZE); - printf("After bucket sort completes, nums = "); + printf("After bucket sort, nums = "); printArrayFloat(nums, SIZE); return 0; diff --git a/en/codes/c/chapter_sorting/counting_sort.c b/en/codes/c/chapter_sorting/counting_sort.c index 9a101b2b8..6c17d06f0 100644 --- a/en/codes/c/chapter_sorting/counting_sort.c +++ b/en/codes/c/chapter_sorting/counting_sort.c @@ -7,22 +7,22 @@ #include "../utils/common.h" /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects void countingSortNaive(int nums[], int size) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int i = 0; i < size; i++) { if (nums[i] > m) { m = nums[i]; } } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num int *counter = calloc(m + 1, sizeof(int)); for (int i = 0; i < size; i++) { counter[nums[i]]++; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements int i = 0; for (int num = 0; num < m + 1; num++) { for (int j = 0; j < counter[num]; j++, i++) { @@ -34,53 +34,53 @@ void countingSortNaive(int nums[], int size) { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Full, stable implementation; can be adapted to sort objects void countingSort(int nums[], int size) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int i = 0; i < size; i++) { if (nums[i] > m) { m = nums[i]; } } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num int *counter = calloc(m, sizeof(int)); for (int i = 0; i < size; i++) { counter[nums[i]]++; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for (int i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result int *res = malloc(sizeof(int) * size); for (int i = size - 1; i >= 0; i--) { int num = nums[i]; res[counter[num] - 1] = num; // Place num at the corresponding index - counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + counter[num]--; // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res memcpy(nums, res, size * sizeof(int)); // 5. Free memory free(res); free(counter); } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; int size = sizeof(nums) / sizeof(int); countingSortNaive(nums, size); - printf("After counting sort (cannot sort objects) completes, nums = "); + printf("After counting sort (without support for objects), nums = "); printArray(nums, size); int nums1[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; int size1 = sizeof(nums1) / sizeof(int); countingSort(nums1, size1); - printf("After counting sort completes, nums1 = "); + printf("After counting sort, nums1 = "); printArray(nums1, size1); return 0; diff --git a/en/codes/c/chapter_sorting/heap_sort.c b/en/codes/c/chapter_sorting/heap_sort.c index 41c4316e5..a9f774090 100644 --- a/en/codes/c/chapter_sorting/heap_sort.c +++ b/en/codes/c/chapter_sorting/heap_sort.c @@ -6,10 +6,10 @@ #include "../utils/common.h" -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ void siftDown(int nums[], int n, int i) { while (1) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; @@ -17,43 +17,43 @@ void siftDown(int nums[], int n, int i) { ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) { break; } - // Swap two nodes + // Swap the two nodes int temp = nums[i]; nums[i] = nums[ma]; nums[ma] = temp; - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ void heapSort(int nums[], int n) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (int i = n / 2 - 1; i >= 0; --i) { siftDown(nums, n, i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (int i = n - 1; i > 0; --i) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) int tmp = nums[0]; nums[0] = nums[i]; nums[i] = tmp; - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0); } } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {4, 1, 3, 1, 5, 2}; int n = sizeof(nums) / sizeof(nums[0]); heapSort(nums, n); - printf("After heap sort completes, nums = "); + printf("After heap sort, nums = "); printArray(nums, n); return 0; diff --git a/en/codes/c/chapter_sorting/insertion_sort.c b/en/codes/c/chapter_sorting/insertion_sort.c index d0953d1ab..260387b85 100644 --- a/en/codes/c/chapter_sorting/insertion_sort.c +++ b/en/codes/c/chapter_sorting/insertion_sort.c @@ -8,25 +8,25 @@ /* Insertion sort */ void insertionSort(int nums[], int size) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for (int i = 1; i < size; i++) { int base = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - // Move nums[j] to the right by one position + // Shift nums[j] one position to the right nums[j + 1] = nums[j]; j--; } - // Assign base to the correct position + // Place base at the correct position nums[j + 1] = base; } } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {4, 1, 3, 1, 5, 2}; insertionSort(nums, 6); - printf("After insertion sort completes, nums = "); + printf("After insertion sort, nums = "); for (int i = 0; i < 6; i++) { printf("%d ", nums[i]); } diff --git a/en/codes/c/chapter_sorting/merge_sort.c b/en/codes/c/chapter_sorting/merge_sort.c index 3e9ae4624..54a3d1620 100644 --- a/en/codes/c/chapter_sorting/merge_sort.c +++ b/en/codes/c/chapter_sorting/merge_sort.c @@ -6,15 +6,15 @@ #include "../utils/common.h" -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ void merge(int *nums, int left, int mid, int right) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result int tmpSize = right - left + 1; int *tmp = (int *)malloc(tmpSize * sizeof(int)); - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays int i = left, j = mid + 1, k = 0; - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { tmp[k++] = nums[i++]; @@ -29,7 +29,7 @@ void merge(int *nums, int left, int mid, int right) { while (j <= right) { tmp[k++] = nums[j++]; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (k = 0; k < tmpSize; ++k) { nums[left + k] = tmp[k]; } @@ -39,24 +39,24 @@ void merge(int *nums, int left, int mid, int right) { /* Merge sort */ void mergeSort(int *nums, int left, int right) { - // Termination condition + // Base case if (left >= right) - return; // Terminate recursion when subarray length is 1 - // Divide and conquer stage - int mid = left + (right - left) / 2; // Calculate midpoint - mergeSort(nums, left, mid); // Recursively process the left subarray - mergeSort(nums, mid + 1, right); // Recursively process the right subarray - // Merge stage + return; // Stop recursing when the subarray has at most one element + // Divide phase + int mid = left + (right - left) / 2; // Calculate the midpoint + mergeSort(nums, left, mid); // Recurse on the left subarray + mergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase merge(nums, left, mid, right); } -/* Driver Code */ +/* Driver code */ int main() { /* Merge sort */ int nums[] = {7, 3, 2, 6, 0, 1, 5, 4}; int size = sizeof(nums) / sizeof(int); mergeSort(nums, 0, size - 1); - printf("After merge sort completes, nums = "); + printf("After merge sort, nums = "); printArray(nums, size); return 0; diff --git a/en/codes/c/chapter_sorting/quick_sort.c b/en/codes/c/chapter_sorting/quick_sort.c index 005eb14df..b3b369c3a 100644 --- a/en/codes/c/chapter_sorting/quick_sort.c +++ b/en/codes/c/chapter_sorting/quick_sort.c @@ -6,122 +6,122 @@ #include "../utils/common.h" -/* Swap elements */ +/* Swap the elements */ void swap(int nums[], int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } -/* Sentinel partition */ +/* Two-pointer partitioning */ int partition(int nums[], int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) { - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot } while (i < j && nums[i] <= nums[left]) { - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot } // Swap these two elements swap(nums, i, j); } - // Swap the pivot to the boundary between the two subarrays + // Swap the pivot into the boundary position between the two subarrays swap(nums, i, left); - // Return the index of the pivot + // Return the pivot index return i; } /* Quick sort */ void quickSort(int nums[], int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) { return; } - // Sentinel partition + // Two-pointer partitioning int pivot = partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } -// Quick sort with median-of-three optimization below +// Quick sort with median-of-three pivot optimization follows /* Select the median of three candidate elements */ int medianThree(int nums[], int left, int mid, int right) { int l = nums[left], m = nums[mid], r = nums[right]; if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m is between l and r + return mid; // m lies between l and r if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l is between m and r + return left; // l lies between m and r return right; } -/* Sentinel partition (median of three) */ +/* Two-pointer partitioning (median of three) */ int partitionMedian(int nums[], int left, int right) { // Select the median of three candidate elements int med = medianThree(nums, left, (left + right) / 2, right); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the array swap(nums, left, med); // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot swap(nums, i, j); // Swap these two elements } - swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } -/* Quick sort (median-of-three) */ +/* Quick sort (median of three) */ void quickSortMedian(int nums[], int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = partitionMedian(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSortMedian(nums, left, pivot - 1); quickSortMedian(nums, pivot + 1, right); } -// Quick sort with recursion depth optimization below +// Quick sort with recursion depth optimization follows /* Quick sort (recursion depth optimization) */ void quickSortTailCall(int nums[], int left, int right) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while (left < right) { - // Sentinel partition operation + // Perform two-pointer partitioning int pivot = partition(nums, left, right); - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left < right - pivot) { // Recursively sort the left subarray quickSortTailCall(nums, left, pivot - 1); - // Remaining unsorted interval is [pivot + 1, right] + // The remaining unsorted range is [pivot + 1, right] left = pivot + 1; } else { // Recursively sort the right subarray quickSortTailCall(nums, pivot + 1, right); - // Remaining unsorted interval is [left, pivot - 1] + // The remaining unsorted range is [left, pivot - 1] right = pivot - 1; } } } -/* Driver Code */ +/* Driver code */ int main() { /* Quick sort */ int nums[] = {2, 4, 1, 0, 3, 5}; int size = sizeof(nums) / sizeof(int); quickSort(nums, 0, size - 1); - printf("After quick sort completes, nums = "); + printf("After quick sort, nums = "); printArray(nums, size); - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ int nums1[] = {2, 4, 1, 0, 3, 5}; quickSortMedian(nums1, 0, size - 1); printf("After quick sort (median pivot optimization), nums = "); diff --git a/en/codes/c/chapter_sorting/radix_sort.c b/en/codes/c/chapter_sorting/radix_sort.c index 1eb08c8ed..fb6c54e22 100644 --- a/en/codes/c/chapter_sorting/radix_sort.c +++ b/en/codes/c/chapter_sorting/radix_sort.c @@ -6,37 +6,37 @@ #include "../utils/common.h" -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ int digit(int num, int exp) { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (num / exp) % 10; } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ void countingSortDigit(int nums[], int size, int exp) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 int *counter = (int *)malloc((sizeof(int) * 10)); - memset(counter, 0, sizeof(int) * 10); // Initialize to 0 to support subsequent memory release - // Count the occurrence of digits 0~9 + memset(counter, 0, sizeof(int) * 10); // Initialize the counts to 0 + // Count the occurrences of each digit from 0 to 9 for (int i = 0; i < size; i++) { - // Get the k-th digit of nums[i], noted as d + // Get the k-th digit of nums[i] and store it in d int d = digit(nums[i], exp); - // Count the occurrence of digit d + // Count the occurrences of digit d counter[d]++; } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for (int i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts int *res = (int *)malloc(sizeof(int) * size); for (int i = size - 1; i >= 0; i--) { int d = digit(nums[i], exp); - int j = counter[d] - 1; // Get the index j for d in the array + int j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d]--; // Decrease the count of d by 1 + counter[d]--; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for (int i = 0; i < size; i++) { nums[i] = res[i]; } @@ -47,29 +47,29 @@ void countingSortDigit(int nums[], int size, int exp) { /* Radix sort */ void radixSort(int nums[], int size) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits int max = INT32_MIN; for (int i = 0; i < size; i++) { if (nums[i] > max) { max = nums[i]; } } - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for (int exp = 1; max >= exp; exp *= 10) - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, size, exp); } -/* Driver Code */ +/* Driver code */ int main() { // Radix sort int nums[] = {10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996}; int size = sizeof(nums) / sizeof(int); radixSort(nums, size); - printf("After radix sort completes, nums = "); + printf("After radix sort, nums = "); printArray(nums, size); } diff --git a/en/codes/c/chapter_sorting/selection_sort.c b/en/codes/c/chapter_sorting/selection_sort.c index 7a5301d49..a05e99bf0 100644 --- a/en/codes/c/chapter_sorting/selection_sort.c +++ b/en/codes/c/chapter_sorting/selection_sort.c @@ -8,29 +8,29 @@ /* Selection sort */ void selectionSort(int nums[], int n) { - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (int i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) k = j; // Record the index of the smallest element } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range int temp = nums[i]; nums[i] = nums[k]; nums[k] = temp; } } -/* Driver Code */ +/* Driver code */ int main() { int nums[] = {4, 1, 3, 1, 5, 2}; int n = sizeof(nums) / sizeof(nums[0]); selectionSort(nums, n); - printf("After selection sort completes, nums = "); + printf("After selection sort, nums = "); printArray(nums, n); return 0; diff --git a/en/codes/c/chapter_stack_and_queue/array_deque.c b/en/codes/c/chapter_stack_and_queue/array_deque.c index ab8bbe32f..6db5a6348 100644 --- a/en/codes/c/chapter_stack_and_queue/array_deque.c +++ b/en/codes/c/chapter_stack_and_queue/array_deque.c @@ -6,18 +6,18 @@ #include "../utils/common.h" -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ typedef struct { - int *nums; // Array for storing queue elements - int front; // Front pointer, points to the front of the queue element - int queSize; // Rear pointer, points to rear + 1 + int *nums; // Array that stores the queue elements + int front; // Front pointer, pointing to the first element + int queSize; // Current number of elements in the deque int queCapacity; // Queue capacity } ArrayDeque; /* Constructor */ ArrayDeque *newArrayDeque(int capacity) { ArrayDeque *deque = (ArrayDeque *)malloc(sizeof(ArrayDeque)); - // Initialize array + // Initialize the array deque->queCapacity = capacity; deque->nums = (int *)malloc(sizeof(int) * deque->queCapacity); deque->front = deque->queSize = 0; @@ -30,88 +30,88 @@ void delArrayDeque(ArrayDeque *deque) { free(deque); } -/* Get the capacity of the double-ended queue */ +/* Get the deque capacity */ int capacity(ArrayDeque *deque) { return deque->queCapacity; } -/* Get the length of the double-ended queue */ +/* Get the deque size */ int size(ArrayDeque *deque) { return deque->queSize; } -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ bool empty(ArrayDeque *deque) { return deque->queSize == 0; } -/* Calculate circular array index */ +/* Calculate an index in the circular array */ int dequeIndex(ArrayDeque *deque, int i) { - // Use modulo operation to wrap the array head and tail together - // When i exceeds array end, wrap to head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap to the beginning + // When i passes the beginning of the array, wrap around to the end return ((i + capacity(deque)) % capacity(deque)); } -/* Front of the queue enqueue */ +/* Enqueue at the front */ void pushFirst(ArrayDeque *deque, int num) { if (deque->queSize == capacity(deque)) { printf("Deque is full\r\n"); return; } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Use modulo to wrap front from array head to rear + // Move the front pointer one position to the left + // Use modulo to wrap front to the end when it passes the beginning deque->front = dequeIndex(deque, deque->front - 1); - // Add num to queue front + // Add num to the front deque->nums[deque->front] = num; deque->queSize++; } -/* Rear of the queue enqueue */ +/* Enqueue at the rear */ void pushLast(ArrayDeque *deque, int num) { if (deque->queSize == capacity(deque)) { printf("Deque is full\r\n"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear pointer, which points one position past the last element int rear = dequeIndex(deque, deque->front + deque->queSize); - // Front pointer moves one position backward + // Add num to the rear deque->nums[rear] = num; deque->queSize++; } -/* Return list for printing */ +/* Access the front element */ int peekFirst(ArrayDeque *deque) { - // Access error: Deque is empty + // Access error: the deque is empty assert(empty(deque) == 0); return deque->nums[deque->front]; } -/* Driver Code */ +/* Access the rear element */ int peekLast(ArrayDeque *deque) { - // Access error: Deque is empty + // Access error: the deque is empty assert(empty(deque) == 0); int last = dequeIndex(deque, deque->front + deque->queSize - 1); return deque->nums[last]; } -/* Rear of the queue dequeue */ +/* Dequeue from the front */ int popFirst(ArrayDeque *deque) { int num = peekFirst(deque); - // Move front pointer backward by one position + // Advance the front pointer by one position deque->front = dequeIndex(deque, deque->front + 1); deque->queSize--; return num; } -/* Access rear of the queue element */ +/* Dequeue from the rear */ int popLast(ArrayDeque *deque) { int num = peekLast(deque); deque->queSize--; return num; } -/* Return array for printing */ +/* Return an array for printing */ int *toArray(ArrayDeque *deque, int *queSize) { *queSize = deque->queSize; int *res = (int *)calloc(deque->queSize, sizeof(int)); @@ -123,47 +123,47 @@ int *toArray(ArrayDeque *deque, int *queSize) { return res; } -/* Driver Code */ +/* Driver code */ int main() { - /* Access front of the queue element */ + /* Initialize the queue */ int capacity = 10; int queSize; ArrayDeque *deque = newArrayDeque(capacity); pushLast(deque, 3); pushLast(deque, 2); pushLast(deque, 5); - printf("Double-ended queue deque = "); + printf("Deque deque = "); printArray(toArray(deque, &queSize), queSize); - /* Update element */ + /* Access an element */ int peekFirstNum = peekFirst(deque); printf("Front element peekFirst = %d\r\n", peekFirstNum); int peekLastNum = peekLast(deque); printf("Rear element peekLast = %d\r\n", peekLastNum); - /* Elements enqueue */ + /* Enqueue elements */ pushLast(deque, 4); - printf("After element 4 enqueues at rear, deque = "); + printf("After enqueuing element 4 at the rear, deque = "); printArray(toArray(deque, &queSize), queSize); pushFirst(deque, 1); - printf("After element 1 enqueues at front, deque = "); + printf("After enqueuing element 1 at the front, deque = "); printArray(toArray(deque, &queSize), queSize); - /* Element dequeue */ + /* Dequeue an element */ int popLastNum = popLast(deque); - printf("Dequeue from rear = %d, deque after rear dequeue = ", popLastNum); + printf("Element removed from the rear = %d; after removal, deque= ", popLastNum); printArray(toArray(deque, &queSize), queSize); int popFirstNum = popFirst(deque); - printf("Dequeue from front = %d, deque after front dequeue = ", popFirstNum); + printf("Element removed from the front = %d; after removal, deque= ", popFirstNum); printArray(toArray(deque, &queSize), queSize); - /* Get the length of the queue */ + /* Get the queue size */ int dequeSize = size(deque); printf("Deque size = %d\r\n", dequeSize); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = empty(deque); - printf("Is queue empty = %s\r\n", isEmpty ? "true" : "false"); + printf("Is the queue empty = %s\r\n", isEmpty ? "true" : "false"); // Free memory delArrayDeque(deque); diff --git a/en/codes/c/chapter_stack_and_queue/array_queue.c b/en/codes/c/chapter_stack_and_queue/array_queue.c index ee8a9a80b..6f78102d3 100644 --- a/en/codes/c/chapter_stack_and_queue/array_queue.c +++ b/en/codes/c/chapter_stack_and_queue/array_queue.c @@ -6,10 +6,10 @@ #include "../utils/common.h" -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ typedef struct { - int *nums; // Array for storing queue elements - int front; // Front pointer, points to the front of the queue element + int *nums; // Array that stores the queue elements + int front; // Front pointer, pointing to the first element int queSize; // Current number of elements in the queue int queCapacity; // Queue capacity } ArrayQueue; @@ -17,7 +17,7 @@ typedef struct { /* Constructor */ ArrayQueue *newArrayQueue(int capacity) { ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue)); - // Initialize array + // Initialize the array queue->queCapacity = capacity; queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity); queue->front = queue->queSize = 0; @@ -30,22 +30,22 @@ void delArrayQueue(ArrayQueue *queue) { free(queue); } -/* Get the capacity of the queue */ +/* Get the queue capacity */ int capacity(ArrayQueue *queue) { return queue->queCapacity; } -/* Get the length of the queue */ +/* Get the queue size */ int size(ArrayQueue *queue) { return queue->queSize; } -/* Check if the queue is empty */ +/* Check whether the queue is empty */ bool empty(ArrayQueue *queue) { return queue->queSize == 0; } -/* Return list for printing */ +/* Access the front element */ int peek(ArrayQueue *queue) { assert(size(queue) != 0); return queue->nums[queue->front]; @@ -57,10 +57,10 @@ void push(ArrayQueue *queue, int num) { printf("Queue is full\r\n"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear pointer, which points one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array int rear = (queue->front + queue->queSize) % queue->queCapacity; - // Front pointer moves one position backward + // Add num to the rear queue->nums[rear] = num; queue->queSize++; } @@ -68,13 +68,13 @@ void push(ArrayQueue *queue, int num) { /* Dequeue */ int pop(ArrayQueue *queue) { int num = peek(queue); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front pointer by one position, wrapping to the beginning if it passes the end queue->front = (queue->front + 1) % queue->queCapacity; queue->queSize--; return num; } -/* Return array for printing */ +/* Return an array for printing */ int *toArray(ArrayQueue *queue, int *queSize) { *queSize = queue->queSize; int *res = (int *)calloc(queue->queSize, sizeof(int)); @@ -86,14 +86,14 @@ int *toArray(ArrayQueue *queue, int *queSize) { return res; } -/* Driver Code */ +/* Driver code */ int main() { - /* Access front of the queue element */ + /* Initialize the queue */ int capacity = 10; int queSize; ArrayQueue *queue = newArrayQueue(capacity); - /* Elements enqueue */ + /* Enqueue elements */ push(queue, 1); push(queue, 3); push(queue, 2); @@ -102,28 +102,28 @@ int main() { printf("Queue queue = "); printArray(toArray(queue, &queSize), queSize); - /* Return list for printing */ + /* Access the front element */ int peekNum = peek(queue); printf("Front element peek = %d\r\n", peekNum); - /* Element dequeue */ + /* Dequeue an element */ peekNum = pop(queue); - printf("Dequeue element pop = %d, queue after dequeue = ", peekNum); + printf("Dequeued element pop = %d; after dequeuing, queue = ", peekNum); printArray(toArray(queue, &queSize), queSize); - /* Get the length of the queue */ + /* Get the queue size */ int queueSize = size(queue); printf("Queue size = %d\r\n", queueSize); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = empty(queue); - printf("Is queue empty = %s\r\n", isEmpty ? "true" : "false"); + printf("Is the queue empty = %s\r\n", isEmpty ? "true" : "false"); - /* Test circular array */ + /* Test the circular array */ for (int i = 0; i < 10; i++) { push(queue, i); pop(queue); - printf("After round %d enqueue + dequeue, queue = ", i); + printf("After enqueueing and dequeueing in round %d, queue = ", i); printArray(toArray(queue, &queSize), queSize); } @@ -131,4 +131,4 @@ int main() { delArrayQueue(queue); return 0; -} +} \ No newline at end of file diff --git a/en/codes/c/chapter_stack_and_queue/array_stack.c b/en/codes/c/chapter_stack_and_queue/array_stack.c index df574bc61..33e4301de 100644 --- a/en/codes/c/chapter_stack_and_queue/array_stack.c +++ b/en/codes/c/chapter_stack_and_queue/array_stack.c @@ -8,7 +8,7 @@ #define MAX_SIZE 5000 -/* Stack based on array implementation */ +/* Stack implemented with an array */ typedef struct { int *data; int size; @@ -17,7 +17,7 @@ typedef struct { /* Constructor */ ArrayStack *newArrayStack() { ArrayStack *stack = malloc(sizeof(ArrayStack)); - // Initialize with large capacity to avoid expansion + // Initialize with a large capacity to avoid resizing stack->data = malloc(sizeof(int) * MAX_SIZE); stack->size = 0; return stack; @@ -29,12 +29,12 @@ void delArrayStack(ArrayStack *stack) { free(stack); } -/* Get the length of the stack */ +/* Get the stack size */ int size(ArrayStack *stack) { return stack->size; } -/* Check if the stack is empty */ +/* Check whether the stack is empty */ bool isEmpty(ArrayStack *stack) { return stack->size == 0; } @@ -49,7 +49,7 @@ void push(ArrayStack *stack, int num) { stack->size++; } -/* Return list for printing */ +/* Access the top element */ int peek(ArrayStack *stack) { if (stack->size == 0) { printf("Stack is empty\n"); @@ -65,12 +65,12 @@ int pop(ArrayStack *stack) { return val; } -/* Driver Code */ +/* Driver code */ int main() { - /* Access top of the stack element */ + /* Initialize the stack */ ArrayStack *stack = newArrayStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ push(stack, 1); push(stack, 3); push(stack, 2); @@ -79,22 +79,22 @@ int main() { printf("Stack stack = "); printArray(stack->data, stack->size); - /* Return list for printing */ + /* Access the top element */ int val = peek(stack); printf("Top element top = %d\n", val); - /* Element pop from stack */ + /* Pop an element from the stack */ val = pop(stack); - printf("Pop element pop = %d, stack after pop = ", val); + printf("Popped element pop = %d; after popping, stack = ", val); printArray(stack->data, stack->size); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack->size; printf("Stack size = %d\n", size); - /* Check if empty */ + /* Check whether it is empty */ bool empty = isEmpty(stack); - printf("Is stack empty = %s\n", empty ? "true" : "false"); + printf("Is the stack empty = %s\n", empty ? "true" : "false"); // Free memory delArrayStack(stack); diff --git a/en/codes/c/chapter_stack_and_queue/linkedlist_deque.c b/en/codes/c/chapter_stack_and_queue/linkedlist_deque.c index 98e32b4d4..f8b7aff4f 100644 --- a/en/codes/c/chapter_stack_and_queue/linkedlist_deque.c +++ b/en/codes/c/chapter_stack_and_queue/linkedlist_deque.c @@ -9,8 +9,8 @@ /* Doubly linked list node */ typedef struct DoublyListNode { int val; // Node value - struct DoublyListNode *next; // Successor node - struct DoublyListNode *prev; // Predecessor node + struct DoublyListNode *next; // Next node + struct DoublyListNode *prev; // Previous node } DoublyListNode; /* Constructor */ @@ -27,10 +27,10 @@ void delDoublyListNode(DoublyListNode *node) { free(node); } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ typedef struct { - DoublyListNode *front, *rear; // Head node front, tail node rear - int queSize; // Length of the double-ended queue + DoublyListNode *front, *rear; // Head node front and tail node rear + int queSize; // Deque size } LinkedListDeque; /* Constructor */ @@ -50,16 +50,16 @@ void delLinkedListdeque(LinkedListDeque *deque) { deque->front = deque->front->next; free(tmp); } - // Free deque structure + // Free the deque structure free(deque); } -/* Get the length of the queue */ +/* Get the queue size */ int size(LinkedListDeque *deque) { return deque->queSize; } -/* Check if the queue is empty */ +/* Check whether the queue is empty */ bool empty(LinkedListDeque *deque) { return (size(deque) == 0); } @@ -67,44 +67,44 @@ bool empty(LinkedListDeque *deque) { /* Enqueue */ void push(LinkedListDeque *deque, int num, bool isFront) { DoublyListNode *node = newDoublyListNode(num); - // If list is empty, set both front and rear to node + // If the list is empty, make both front and rear point to node if (empty(deque)) { deque->front = deque->rear = node; } - // Front of the queue enqueue operation + // Enqueue at the front else if (isFront) { // Add node to the head of the linked list deque->front->prev = node; node->next = deque->front; - deque->front = node; // Update head node + deque->front = node; // Update the head node } - // Rear of the queue enqueue operation + // Enqueue at the rear else { // Add node to the tail of the linked list deque->rear->next = node; node->prev = deque->rear; deque->rear = node; } - deque->queSize++; // Update queue length + deque->queSize++; // Update the queue size } -/* Front of the queue enqueue */ +/* Enqueue at the front */ void pushFirst(LinkedListDeque *deque, int num) { push(deque, num, true); } -/* Rear of the queue enqueue */ +/* Enqueue at the rear */ void pushLast(LinkedListDeque *deque, int num) { push(deque, num, false); } -/* Return list for printing */ +/* Access the front element */ int peekFirst(LinkedListDeque *deque) { assert(size(deque) && deque->front); return deque->front->val; } -/* Driver Code */ +/* Access the rear element */ int peekLast(LinkedListDeque *deque) { assert(size(deque) && deque->rear); return deque->rear->val; @@ -115,49 +115,49 @@ int pop(LinkedListDeque *deque, bool isFront) { if (empty(deque)) return -1; int val; - // When the deque has one element, front and rear point to the same node + // When the queue size is 1, the head and tail point to the same node if (deque->queSize == 1) { val = deque->front->val; - delDoublyListNode(deque->front); // Free the shared node once + delDoublyListNode(deque->front); // Both point to the same node, so free it only once deque->front = NULL; deque->rear = NULL; } - // Temporarily store head node value + // Dequeue from the front else if (isFront) { - val = peekFirst(deque); // Delete head node + val = peekFirst(deque); // Save the head node value DoublyListNode *fNext = deque->front->next; fNext->prev = NULL; deque->front->next = NULL; delDoublyListNode(deque->front); - deque->front = fNext; // Update head node + deque->front = fNext; // Update the head node } - // Temporarily store tail node value + // Dequeue from the rear else { - val = peekLast(deque); // Delete tail node + val = peekLast(deque); // Save the tail node value DoublyListNode *rPrev = deque->rear->prev; rPrev->next = NULL; deque->rear->prev = NULL; delDoublyListNode(deque->rear); - deque->rear = rPrev; // Update tail node + deque->rear = rPrev; // Update the tail node } - deque->queSize--; // Update queue length + deque->queSize--; // Update the queue size return val; } -/* Rear of the queue dequeue */ +/* Dequeue from the front */ int popFirst(LinkedListDeque *deque) { return pop(deque, true); } -/* Access rear of the queue element */ +/* Dequeue from the rear */ int popLast(LinkedListDeque *deque) { return pop(deque, false); } -/* Print queue */ +/* Print the queue */ void printLinkedListDeque(LinkedListDeque *deque) { int *arr = malloc(sizeof(int) * deque->queSize); - // Copy data from list to array + // Copy the linked list data into an array int i; DoublyListNode *node; for (i = 0, node = deque->front; i < deque->queSize; i++) { @@ -168,45 +168,45 @@ void printLinkedListDeque(LinkedListDeque *deque) { free(arr); } -/* Driver Code */ +/* Driver code */ int main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ LinkedListDeque *deque = newLinkedListDeque(); pushLast(deque, 3); pushLast(deque, 2); pushLast(deque, 5); - printf("Double-ended queue deque = "); + printf("Deque deque = "); printLinkedListDeque(deque); - /* Update element */ + /* Access an element */ int peekFirstNum = peekFirst(deque); printf("Front element peekFirst = %d\r\n", peekFirstNum); int peekLastNum = peekLast(deque); - printf("Front element peekLast = %d\r\n", peekLastNum); + printf("Rear element peekLast = %d\r\n", peekLastNum); - /* Elements enqueue */ + /* Enqueue elements */ pushLast(deque, 4); - printf("After element 4 enqueues at back, deque ="); + printf("After enqueuing element 4 at the rear, deque ="); printLinkedListDeque(deque); pushFirst(deque, 1); - printf("After element 1 enqueues at front, deque ="); + printf("After enqueuing element 1 at the front, deque ="); printLinkedListDeque(deque); - /* Element dequeue */ + /* Dequeue an element */ int popLastNum = popLast(deque); - printf("Dequeue from rear popLast = %d, deque after rear dequeue = ", popLastNum); + printf("Element removed from the rear, popLast = %d; after removal, deque = ", popLastNum); printLinkedListDeque(deque); int popFirstNum = popFirst(deque); - printf("Dequeue from front popFirst = %d, deque after front dequeue = ", popFirstNum); + printf("Element removed from the front, popFirst = %d; after removal, deque = ", popFirstNum); printLinkedListDeque(deque); - /* Get the length of the queue */ + /* Get the queue size */ int dequeSize = size(deque); printf("Deque size = %d\r\n", dequeSize); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = empty(deque); - printf("Is deque empty = %s\r\n", isEmpty ? "true" : "false"); + printf("Is the deque empty = %s\r\n", isEmpty ? "true" : "false"); // Free memory delLinkedListdeque(deque); diff --git a/en/codes/c/chapter_stack_and_queue/linkedlist_queue.c b/en/codes/c/chapter_stack_and_queue/linkedlist_queue.c index 471a73cd7..004a8f155 100644 --- a/en/codes/c/chapter_stack_and_queue/linkedlist_queue.c +++ b/en/codes/c/chapter_stack_and_queue/linkedlist_queue.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ typedef struct { ListNode *front, *rear; int queSize; @@ -29,30 +29,30 @@ void delLinkedListQueue(LinkedListQueue *queue) { queue->front = queue->front->next; free(tmp); } - // Free queue structure + // Free the queue structure free(queue); } -/* Get the length of the queue */ +/* Get the queue size */ int size(LinkedListQueue *queue) { return queue->queSize; } -/* Check if the queue is empty */ +/* Check whether the queue is empty */ bool empty(LinkedListQueue *queue) { return (size(queue) == 0); } /* Enqueue */ void push(LinkedListQueue *queue, int num) { - // Add node at tail + // Add node at the tail ListNode *node = newListNode(num); - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (queue->front == NULL) { queue->front = node; queue->rear = node; } - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node else { queue->rear->next = node; queue->rear = node; @@ -60,7 +60,7 @@ void push(LinkedListQueue *queue, int num) { queue->queSize++; } -/* Return list for printing */ +/* Access the front element */ int peek(LinkedListQueue *queue) { assert(size(queue) && queue->front); return queue->front->val; @@ -76,10 +76,10 @@ int pop(LinkedListQueue *queue) { return num; } -/* Print queue */ +/* Print the queue */ void printLinkedListQueue(LinkedListQueue *queue) { int *arr = malloc(sizeof(int) * queue->queSize); - // Copy data from list to array + // Copy the linked list data into an array int i; ListNode *node; for (i = 0, node = queue->front; i < queue->queSize; i++) { @@ -90,12 +90,12 @@ void printLinkedListQueue(LinkedListQueue *queue) { free(arr); } -/* Driver Code */ +/* Driver code */ int main() { - /* Access front of the queue element */ + /* Initialize the queue */ LinkedListQueue *queue = newLinkedListQueue(); - /* Elements enqueue */ + /* Enqueue elements */ push(queue, 1); push(queue, 3); push(queue, 2); @@ -104,22 +104,22 @@ int main() { printf("Queue queue = "); printLinkedListQueue(queue); - /* Return list for printing */ + /* Access the front element */ int peekNum = peek(queue); printf("Front element peek = %d\r\n", peekNum); - /* Element dequeue */ + /* Dequeue an element */ peekNum = pop(queue); - printf("Dequeue element pop = %d, queue after dequeue = ", peekNum); + printf("Dequeued element pop = %d; after dequeuing, queue = ", peekNum); printLinkedListQueue(queue); - /* Get the length of the queue */ + /* Get the queue size */ int queueSize = size(queue); printf("Queue size = %d\r\n", queueSize); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = empty(queue); - printf("Is queue empty = %s\r\n", isEmpty ? "true" : "false"); + printf("Is the queue empty = %s\r\n", isEmpty ? "true" : "false"); // Free memory delLinkedListQueue(queue); diff --git a/en/codes/c/chapter_stack_and_queue/linkedlist_stack.c b/en/codes/c/chapter_stack_and_queue/linkedlist_stack.c index a300dfaa1..580379fef 100644 --- a/en/codes/c/chapter_stack_and_queue/linkedlist_stack.c +++ b/en/codes/c/chapter_stack_and_queue/linkedlist_stack.c @@ -6,10 +6,10 @@ #include "../utils/common.h" -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ typedef struct { - ListNode *top; // Use head node as stack top - int size; // Stack length + ListNode *top; // Use the head node as the stack top + int size; // Stack size } LinkedListStack; /* Constructor */ @@ -30,12 +30,12 @@ void delLinkedListStack(LinkedListStack *s) { free(s); } -/* Get the length of the stack */ +/* Get the stack size */ int size(LinkedListStack *s) { return s->size; } -/* Check if the stack is empty */ +/* Check whether the stack is empty */ bool isEmpty(LinkedListStack *s) { return size(s) == 0; } @@ -43,13 +43,13 @@ bool isEmpty(LinkedListStack *s) { /* Push */ void push(LinkedListStack *s, int num) { ListNode *node = (ListNode *)malloc(sizeof(ListNode)); - node->next = s->top; // Update new node's pointer field - node->val = num; // Update new node's data field - s->top = node; // Update stack top - s->size++; // Update stack size + node->next = s->top; // Set the new node's pointer field + node->val = num; // Set the new node's data field + s->top = node; // Update the stack top + s->size++; // Update the stack size } -/* Return list for printing */ +/* Access the top element */ int peek(LinkedListStack *s) { if (s->size == 0) { printf("Stack is empty\n"); @@ -69,12 +69,12 @@ int pop(LinkedListStack *s) { return val; } -/* Driver Code */ +/* Driver code */ int main() { - /* Access top of the stack element */ + /* Initialize the stack */ LinkedListStack *stack = newLinkedListStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ push(stack, 1); push(stack, 3); push(stack, 2); @@ -84,21 +84,21 @@ int main() { printf("Stack stack = "); printLinkedList(stack->top); - /* Return list for printing */ + /* Access the top element */ int val = peek(stack); printf("Top element top = %d\r\n", val); - /* Element pop from stack */ + /* Pop an element from the stack */ val = pop(stack); - printf("Pop element pop = %d, stack after pop = ", val); + printf("Popped element pop = %d; after popping, stack = ", val); printLinkedList(stack->top); - /* Get the length of the stack */ + /* Get the stack size */ printf("Stack size = %d\n", size(stack)); - /* Check if empty */ + /* Check whether it is empty */ bool empty = isEmpty(stack); - printf("Is stack empty = %s\n", empty ? "true" : "false"); + printf("Is the stack empty = %s\n", empty ? "true" : "false"); // Free memory delLinkedListStack(stack); diff --git a/en/codes/c/chapter_tree/array_binary_tree.c b/en/codes/c/chapter_tree/array_binary_tree.c index 1ecf063fd..921aeb83a 100644 --- a/en/codes/c/chapter_tree/array_binary_tree.c +++ b/en/codes/c/chapter_tree/array_binary_tree.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -/* Binary tree structure in array representation */ +/* Binary tree structure represented by an array */ typedef struct { int *tree; int size; @@ -27,30 +27,30 @@ void delArrayBinaryTree(ArrayBinaryTree *abt) { free(abt); } -/* List capacity */ +/* Array length */ int size(ArrayBinaryTree *abt) { return abt->size; } -/* Get value of node at index i */ +/* Get the value of the node at index i */ int val(ArrayBinaryTree *abt, int i) { - // Return INT_MAX if index out of bounds, representing empty position + // If the index is out of bounds, return INT_MAX to represent an empty position if (i < 0 || i >= size(abt)) return INT_MAX; return abt->tree[i]; } -/* Get index of left child node of node at index i */ +/* Get the left child index of the node at index i */ int left(int i) { return 2 * i + 1; } -/* Get index of right child node of node at index i */ +/* Get the right child index of the node at index i */ int right(int i) { return 2 * i + 2; } -/* Get index of parent node of node at index i */ +/* Get the parent index of the node at index i */ int parent(int i) { return (i - 1) / 2; } @@ -59,7 +59,7 @@ int parent(int i) { int *levelOrder(ArrayBinaryTree *abt, int *returnSize) { int *res = (int *)malloc(sizeof(int) * size(abt)); int index = 0; - // Traverse array directly + // Traverse the array directly for (int i = 0; i < size(abt); i++) { if (val(abt, i) != INT_MAX) res[index++] = val(abt, i); @@ -70,7 +70,7 @@ int *levelOrder(ArrayBinaryTree *abt, int *returnSize) { /* Depth-first traversal */ void dfs(ArrayBinaryTree *abt, int i, char *order, int *res, int *index) { - // If empty position, return + // Return if this position is empty if (val(abt, i) == INT_MAX) return; // Preorder traversal @@ -113,30 +113,30 @@ int *postOrder(ArrayBinaryTree *abt, int *returnSize) { return res; } -/* Driver Code */ +/* Driver code */ int main() { - // Initialize binary tree - // Use INT_MAX to represent NULL + // Initialize the binary tree + // Use INT_MAX to represent an empty position (NULL) int arr[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; int arrSize = sizeof(arr) / sizeof(arr[0]); TreeNode *root = arrayToTree(arr, arrSize); - printf("\nInitialize binary tree\n"); - printf("Array representation of binary tree:\n"); + printf("\nInitialize the binary tree\n"); + printf("Array representation of the binary tree:\n"); printArray(arr, arrSize); - printf("Linked list representation of binary tree:\n"); + printf("Linked representation of the binary tree:\n"); printTree(root); ArrayBinaryTree *abt = newArrayBinaryTree(arr, arrSize); - // Access node + // Access a node int i = 1; int l = left(i), r = right(i), p = parent(i); - printf("\nCurrent node index is %d, value is %d\n", i, val(abt, i)); - printf("Its left child index is %d, value is %d\n", l, l < arrSize ? val(abt, l) : INT_MAX); - printf("Its right child index is %d, value is %d\n", r, r < arrSize ? val(abt, r) : INT_MAX); - printf("Its parent node index is %d, value is %d\n", p, p < arrSize ? val(abt, p) : INT_MAX); + printf("\nCurrent node: index %d, value %d\n", i, val(abt, i)); + printf("Its left child: index %d, value %d\n", l, l < arrSize ? val(abt, l) : INT_MAX); + printf("Its right child: index %d, value %d\n", r, r < arrSize ? val(abt, r) : INT_MAX); + printf("Its parent: index %d, value %d\n", p, p < arrSize ? val(abt, p) : INT_MAX); - // Traverse tree + // Traverse the tree int returnSize; int *res; @@ -146,17 +146,17 @@ int main() { free(res); res = preOrder(abt, &returnSize); - printf("Pre-order traversal: "); + printf("Preorder traversal: "); printArray(res, returnSize); free(res); res = inOrder(abt, &returnSize); - printf("In-order traversal: "); + printf("Inorder traversal: "); printArray(res, returnSize); free(res); res = postOrder(abt, &returnSize); - printf("Post-order traversal: "); + printf("Postorder traversal: "); printArray(res, returnSize); free(res); diff --git a/en/codes/c/chapter_tree/avl_tree.c b/en/codes/c/chapter_tree/avl_tree.c index 1561fb26c..c60cb45b7 100644 --- a/en/codes/c/chapter_tree/avl_tree.c +++ b/en/codes/c/chapter_tree/avl_tree.c @@ -24,20 +24,20 @@ void delAVLTree(AVLTree *tree) { free(tree); } -/* Get node height */ +/* Get the node height */ int height(TreeNode *node) { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 if (node != NULL) { return node->height; } return -1; } -/* Update node height */ +/* Update the node height */ void updateHeight(TreeNode *node) { int lh = height(node->left); int rh = height(node->right); - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 if (lh > rh) { node->height = lh + 1; } else { @@ -45,9 +45,9 @@ void updateHeight(TreeNode *node) { } } -/* Get balance factor */ +/* Get the balance factor */ int balanceFactor(TreeNode *node) { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node == NULL) { return 0; } @@ -55,100 +55,100 @@ int balanceFactor(TreeNode *node) { return height(node->left) - height(node->right); } -/* Right rotation operation */ +/* Right rotation */ TreeNode *rightRotate(TreeNode *node) { TreeNode *child, *grandChild; child = node->left; grandChild = child->right; - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child->right = node; node->left = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } -/* Left rotation operation */ +/* Left rotation */ TreeNode *leftRotate(TreeNode *node) { TreeNode *child, *grandChild; child = node->right; grandChild = child->left; - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child->left = node; node->right = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } -/* Perform rotation operation to restore balance to this subtree */ +/* Perform rotations to restore balance in this subtree */ TreeNode *rotate(TreeNode *node) { - // Get balance factor of node + // Get the balance factor of node int bf = balanceFactor(node); - // Left-leaning tree + // Left-heavy tree if (bf > 1) { if (balanceFactor(node->left) >= 0) { - // Right rotation + // Rotate right return rightRotate(node); } else { - // First left rotation then right rotation + // Rotate left, then right node->left = leftRotate(node->left); return rightRotate(node); } } - // Right-leaning tree + // Right-heavy tree if (bf < -1) { if (balanceFactor(node->right) <= 0) { - // Left rotation + // Rotate left return leftRotate(node); } else { - // First right rotation then left rotation + // Rotate right, then left node->right = rightRotate(node->right); return leftRotate(node); } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node; } -/* Recursively insert node (helper function) */ +/* Recursively insert a node (helper function) */ TreeNode *insertHelper(TreeNode *node, int val) { if (node == NULL) { return newTreeNode(val); } - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (val < node->val) { node->left = insertHelper(node->left, val); } else if (val > node->val) { node->right = insertHelper(node->right, val); } else { - // Duplicate node not inserted, return directly + // Return without inserting a duplicate node return node; } - // Update node height + // Update the node height updateHeight(node); - /* 2. Perform rotation operation to restore balance to this subtree */ + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } -/* Insert node */ +/* Insert a node */ void insert(AVLTree *tree, int val) { tree->root = insertHelper(tree->root, val); } -/* Recursively remove node (helper function) */ +/* Recursively remove a node (helper function) */ TreeNode *removeHelper(TreeNode *node, int val) { TreeNode *child, *grandChild; if (node == NULL) { return NULL; } - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (val < node->val) { node->left = removeHelper(node->left, val); } else if (val > node->val) { @@ -159,15 +159,15 @@ TreeNode *removeHelper(TreeNode *node, int val) { if (node->right != NULL) { child = node->right; } - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child == NULL) { return NULL; } else { - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly node = child; } } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value TreeNode *temp = node->right; while (temp->left != NULL) { temp = temp->left; @@ -177,58 +177,58 @@ TreeNode *removeHelper(TreeNode *node, int val) { node->val = tempVal; } } - // Update node height + // Update the node height updateHeight(node); - /* 2. Perform rotation operation to restore balance to this subtree */ + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } -/* Remove node */ -// Cannot use remove keyword here due to stdio.h inclusion +/* Remove a node */ +// The name remove conflicts with the function declared in stdio.h void removeItem(AVLTree *tree, int val) { TreeNode *root = removeHelper(tree->root, val); } -/* Search node */ +/* Find a node */ TreeNode *search(AVLTree *tree, int val) { TreeNode *cur = tree->root; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != NULL) { if (cur->val < val) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur cur = cur->right; } else if (cur->val > val) { - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur cur = cur->left; } else { - // Found target node, exit loop + // The target node was found; exit the loop break; } } - // Found target node, exit loop + // Return the matching node, or NULL if no match was found return cur; } void testInsert(AVLTree *tree, int val) { insert(tree, val); - printf("\nAfter inserting node %d, AVL tree is \n", val); + printf("\nAVL tree after inserting node %d: \n", val); printTree(tree->root); } void testRemove(AVLTree *tree, int val) { removeItem(tree, val); - printf("\nAfter removing node %d, AVL tree is \n", val); + printf("\nAVL tree after removing node %d: \n", val); printTree(tree->root); } -/* Driver Code */ +/* Driver code */ int main() { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ AVLTree *tree = (AVLTree *)newAVLTree(); - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted testInsert(tree, 1); testInsert(tree, 2); testInsert(tree, 3); @@ -240,18 +240,18 @@ int main() { testInsert(tree, 10); testInsert(tree, 6); - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ testInsert(tree, 7); - /* Remove node */ - // Delete node with degree 1 - testRemove(tree, 8); // Delete node with degree 2 - testRemove(tree, 5); // Remove node with degree 1 - testRemove(tree, 4); // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + testRemove(tree, 8); // Remove a node with 0 children + testRemove(tree, 5); // Remove a node with 1 child + testRemove(tree, 4); // Remove a node with 2 children - /* Search node */ + /* Look up a node */ TreeNode *node = search(tree, 7); - printf("\nFound node object value = %d \n", node->val); + printf("\nValue of the node found = %d \n", node->val); // Free memory delAVLTree(tree); diff --git a/en/codes/c/chapter_tree/binary_search_tree.c b/en/codes/c/chapter_tree/binary_search_tree.c index b09d7bfa0..fd5efbb1b 100644 --- a/en/codes/c/chapter_tree/binary_search_tree.c +++ b/en/codes/c/chapter_tree/binary_search_tree.c @@ -13,7 +13,7 @@ typedef struct { /* Constructor */ BinarySearchTree *newBinarySearchTree() { - // Initialize empty tree + // Initialize an empty tree BinarySearchTree *bst = (BinarySearchTree *)malloc(sizeof(BinarySearchTree)); bst->root = NULL; return bst; @@ -25,55 +25,55 @@ void delBinarySearchTree(BinarySearchTree *bst) { free(bst); } -/* Get binary tree root node */ +/* Get the root of the binary tree */ TreeNode *getRoot(BinarySearchTree *bst) { return bst->root; } -/* Search node */ +/* Find a node */ TreeNode *search(BinarySearchTree *bst, int num) { TreeNode *cur = bst->root; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != NULL) { if (cur->val < num) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur cur = cur->right; } else if (cur->val > num) { - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur cur = cur->left; } else { - // Found target node, exit loop + // The target node was found; exit the loop break; } } - // Return target node + // Return the target node return cur; } -/* Insert node */ +/* Insert a node */ void insert(BinarySearchTree *bst, int num) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (bst->root == NULL) { bst->root = newTreeNode(num); return; } TreeNode *cur = bst->root, *pre = NULL; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != NULL) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur->val == num) { return; } pre = cur; if (cur->val < num) { - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur cur = cur->right; } else { - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur cur = cur->left; } } - // Insert node + // Insert a node TreeNode *node = newTreeNode(num); if (pre->val < num) { pre->right = node; @@ -82,36 +82,36 @@ void insert(BinarySearchTree *bst, int num) { } } -/* Remove node */ -// Cannot use remove keyword here due to stdio.h inclusion +/* Remove a node */ +// The name remove conflicts with the function declared in stdio.h void removeItem(BinarySearchTree *bst, int num) { - // If tree is empty, return directly + // Return immediately if the tree is empty if (bst->root == NULL) return; TreeNode *cur = bst->root, *pre = NULL; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != NULL) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur->val == num) break; pre = cur; if (cur->val < num) { - // Node to delete is in right subtree of root + // The node to remove is in cur's right subtree cur = cur->right; } else { - // Node to delete is in left subtree of root + // The node to remove is in cur's left subtree cur = cur->left; } } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if (cur == NULL) return; - // Check if node to delete has children + // Check whether the node to remove has children if (cur->left == NULL || cur->right == NULL) { - /* Number of child nodes = 0 or 1 */ - // When number of child nodes = 0 / 1, child = nullptr / that child node + /* Number of children = 0 or 1 */ + // With 0 or 1 child, child is NULL or that child, respectively TreeNode *child = cur->left != NULL ? cur->left : cur->right; - // Delete node cur + // Remove node cur if (pre->left == cur) { pre->left = child; } else { @@ -120,49 +120,49 @@ void removeItem(BinarySearchTree *bst, int num) { // Free memory free(cur); } else { - /* Number of child nodes = 2 */ - // Get next node of cur in inorder traversal + /* Number of children = 2 */ + // Get the inorder successor of cur TreeNode *tmp = cur->right; while (tmp->left != NULL) { tmp = tmp->left; } int tmpVal = tmp->val; - // Recursively delete node tmp + // Recursively remove node tmp removeItem(bst, tmp->val); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur->val = tmpVal; } } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ int nums[] = {8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15}; BinarySearchTree *bst = newBinarySearchTree(); for (int i = 0; i < sizeof(nums) / sizeof(int); i++) { insert(bst, nums[i]); } - printf("Initialized binary tree is\n"); + printf("Binary tree after initialization:\n"); printTree(getRoot(bst)); - /* Search node */ + /* Find a node */ TreeNode *node = search(bst, 7); - printf("Found node object value = %d\n", node->val); + printf("Value of the node found = %d\n", node->val); - /* Insert node */ + /* Insert a node */ insert(bst, 16); - printf("After inserting node 16, binary tree is\n"); + printf("Binary tree after inserting node 16:\n"); printTree(getRoot(bst)); - /* Remove node */ + /* Remove a node */ removeItem(bst, 1); - printf("After removing node 1, binary tree is\n"); + printf("Binary tree after removing node 1:\n"); printTree(getRoot(bst)); removeItem(bst, 2); - printf("After removing node 2, binary tree is\n"); + printf("Binary tree after removing node 2:\n"); printTree(getRoot(bst)); removeItem(bst, 4); - printf("After removing node 4, binary tree is\n"); + printf("Binary tree after removing node 4:\n"); printTree(getRoot(bst)); // Free memory diff --git a/en/codes/c/chapter_tree/binary_tree.c b/en/codes/c/chapter_tree/binary_tree.c index d0f006ebb..44a4e573c 100644 --- a/en/codes/c/chapter_tree/binary_tree.c +++ b/en/codes/c/chapter_tree/binary_tree.c @@ -6,36 +6,36 @@ #include "../utils/common.h" -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes TreeNode *n1 = newTreeNode(1); TreeNode *n2 = newTreeNode(2); TreeNode *n3 = newTreeNode(3); TreeNode *n4 = newTreeNode(4); TreeNode *n5 = newTreeNode(5); - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1->left = n2; n1->right = n3; n2->left = n4; n2->right = n5; - printf("Initialize binary tree\n"); + printf("Initialize the binary tree\n"); printTree(n1); - /* Insert node P between n1 -> n2 */ + /* Insert and remove nodes */ TreeNode *P = newTreeNode(0); - // Delete node + // Insert node P between n1 and n2 in n1 -> n2 n1->left = P; P->left = n2; - printf("After inserting node P\n"); + printf("After inserting node P:\n"); printTree(n1); // Remove node P n1->left = n2; // Free memory free(P); - printf("After removing node P\n"); + printf("After removing node P:\n"); printTree(n1); freeMemoryTree(n1); diff --git a/en/codes/c/chapter_tree/binary_tree_bfs.c b/en/codes/c/chapter_tree/binary_tree_bfs.c index c8e023a50..8a953ff8b 100644 --- a/en/codes/c/chapter_tree/binary_tree_bfs.c +++ b/en/codes/c/chapter_tree/binary_tree_bfs.c @@ -18,52 +18,52 @@ int *levelOrder(TreeNode *root, int *size) { /* Auxiliary queue */ queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE); - // Queue pointer + // Queue indices front = 0, rear = 0; - // Add root node + // Enqueue the root node queue[rear++] = root; - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence /* Auxiliary array */ arr = (int *)malloc(sizeof(int) * MAX_SIZE); - // Array pointer + // Array index index = 0; while (front < rear) { - // Dequeue + // Dequeue from the queue node = queue[front++]; - // Save node value + // Store the node value arr[index++] = node->val; if (node->left != NULL) { - // Left child node enqueue + // Enqueue the left child queue[rear++] = node->left; } if (node->right != NULL) { - // Right child node enqueue + // Enqueue the right child queue[rear++] = node->right; } } - // Update array length value + // Update the array length *size = index; arr = realloc(arr, sizeof(int) * (*size)); - // Free auxiliary array space + // Free the auxiliary queue array free(queue); return arr; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array int nums[] = {1, 2, 3, 4, 5, 6, 7}; int size = sizeof(nums) / sizeof(int); TreeNode *root = arrayToTree(nums, size); - printf("Initialize binary tree\n"); + printf("Initialize the binary tree\n"); printTree(root); /* Level-order traversal */ - // Need to pass array length + // Pass a pointer to receive the array length int *arr = levelOrder(root, &size); - printf("Level-order traversal node print sequence = "); + printf("Node sequence from level-order traversal = "); printArray(arr, size); // Free memory diff --git a/en/codes/c/chapter_tree/binary_tree_dfs.c b/en/codes/c/chapter_tree/binary_tree_dfs.c index 0d77179a3..4e4d62b5f 100644 --- a/en/codes/c/chapter_tree/binary_tree_dfs.c +++ b/en/codes/c/chapter_tree/binary_tree_dfs.c @@ -8,14 +8,14 @@ #define MAX_SIZE 100 -// Auxiliary array for storing traversal sequence +// Auxiliary array for storing the traversal sequence int arr[MAX_SIZE]; /* Preorder traversal */ void preOrder(TreeNode *root, int *size) { if (root == NULL) return; - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree arr[(*size)++] = root->val; preOrder(root->left, size); preOrder(root->right, size); @@ -25,7 +25,7 @@ void preOrder(TreeNode *root, int *size) { void inOrder(TreeNode *root, int *size) { if (root == NULL) return; - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(root->left, size); arr[(*size)++] = root->val; inOrder(root->right, size); @@ -35,39 +35,39 @@ void inOrder(TreeNode *root, int *size) { void postOrder(TreeNode *root, int *size) { if (root == NULL) return; - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(root->left, size); postOrder(root->right, size); arr[(*size)++] = root->val; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array int nums[] = {1, 2, 3, 4, 5, 6, 7}; int size = sizeof(nums) / sizeof(int); TreeNode *root = arrayToTree(nums, size); - printf("Initialize binary tree\n"); + printf("Initialize the binary tree\n"); printTree(root); /* Preorder traversal */ - // Initialize auxiliary array + // Initialize the auxiliary array's element count size = 0; preOrder(root, &size); - printf("Pre-order traversal node print sequence = "); + printf("Node sequence from preorder traversal = "); printArray(arr, size); /* Inorder traversal */ size = 0; inOrder(root, &size); - printf("In-order traversal node print sequence = "); + printf("Node sequence from inorder traversal = "); printArray(arr, size); /* Postorder traversal */ size = 0; postOrder(root, &size); - printf("Post-order traversal node print sequence = "); + printf("Node sequence from postorder traversal = "); printArray(arr, size); freeMemoryTree(root); diff --git a/en/codes/c/utils/common.h b/en/codes/c/utils/common.h index 8b9adeff7..182a51bbf 100644 --- a/en/codes/c/utils/common.h +++ b/en/codes/c/utils/common.h @@ -20,7 +20,7 @@ #include "tree_node.h" #include "vertex.h" -// hash table lib +// Hash table library #include "uthash.h" #include "vector.h" diff --git a/en/codes/c/utils/common_test.c b/en/codes/c/utils/common_test.c index a889b423b..6d4f04008 100644 --- a/en/codes/c/utils/common_test.c +++ b/en/codes/c/utils/common_test.c @@ -18,10 +18,10 @@ void testTreeNode() { int size = sizeof(nums) / sizeof(int); TreeNode *root = arrayToTree(nums, size); - // print tree + // Print the tree printTree(root); - // tree to arr + // Convert the tree to an array int *arr = treeToArray(root, &size); printArray(arr, size); } diff --git a/en/codes/c/utils/list_node.h b/en/codes/c/utils/list_node.h index c39483302..58040602c 100644 --- a/en/codes/c/utils/list_node.h +++ b/en/codes/c/utils/list_node.h @@ -14,10 +14,10 @@ extern "C" { /* Linked list node structure */ typedef struct ListNode { int val; // Node value - struct ListNode *next; // Reference to next node + struct ListNode *next; // Reference to the next node } ListNode; -/* Constructor, initialize a new node */ +/* Constructor: initialize a new node */ ListNode *newListNode(int val) { ListNode *node; node = (ListNode *)malloc(sizeof(ListNode)); @@ -26,7 +26,7 @@ ListNode *newListNode(int val) { return node; } -/* Deserialize array to linked list */ +/* Deserialize an array into a linked list */ ListNode *arrToLinkedList(const int *arr, size_t size) { if (size <= 0) { return NULL; @@ -41,7 +41,7 @@ ListNode *arrToLinkedList(const int *arr, size_t size) { return dummy->next; } -/* Free memory allocated to linked list */ +/* Free the memory allocated to the linked list */ void freeMemoryLinkedList(ListNode *cur) { // Free memory ListNode *pre; diff --git a/en/codes/c/utils/print_util.h b/en/codes/c/utils/print_util.h index 00289d686..d4737beda 100644 --- a/en/codes/c/utils/print_util.h +++ b/en/codes/c/utils/print_util.h @@ -18,7 +18,7 @@ extern "C" { #endif -/* Print array */ +/* Print an array */ void printArray(int arr[], int size) { if (arr == NULL || size == 0) { printf("[]"); @@ -31,7 +31,7 @@ void printArray(int arr[], int size) { printf("%d]\n", arr[size - 1]); } -/* Print array */ +/* Print an array */ void printArrayFloat(float arr[], int size) { if (arr == NULL || size == 0) { printf("[]"); @@ -44,7 +44,7 @@ void printArrayFloat(float arr[], int size) { printf("%.2f]\n", arr[size - 1]); } -/* Print linked list */ +/* Print a linked list */ void printLinkedList(ListNode *node) { if (node == NULL) { return; @@ -78,7 +78,7 @@ void showTrunks(Trunk *trunk) { } /** - * Print binary tree + * Print a binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -109,17 +109,17 @@ void printTreeHelper(TreeNode *node, Trunk *prev, bool isRight) { printTreeHelper(node->left, trunk, false); } -/* Print binary tree */ +/* Print a binary tree */ void printTree(TreeNode *root) { printTreeHelper(root, NULL, false); } -/* Print heap */ +/* Print a heap */ void printHeap(int arr[], int size) { TreeNode *root; - printf("Heap array representation:"); + printf("Array representation of the heap:"); printArray(arr, size); - printf("Heap tree representation:\n"); + printf("Tree representation of the heap:\n"); root = arrayToTree(arr, size); printTree(root); } diff --git a/en/codes/c/utils/tree_node.h b/en/codes/c/utils/tree_node.h index a95f1a97a..eab27ba1d 100644 --- a/en/codes/c/utils/tree_node.h +++ b/en/codes/c/utils/tree_node.h @@ -19,8 +19,8 @@ extern "C" { typedef struct TreeNode { int val; // Node value int height; // Node height - struct TreeNode *left; // Left child pointer - struct TreeNode *right; // Right child pointer + struct TreeNode *left; // Pointer to the left child + struct TreeNode *right; // Pointer to the right child } TreeNode; /* Constructor */ @@ -35,11 +35,11 @@ TreeNode *newTreeNode(int val) { return node; } -// For the serialization encoding rules, please refer to: -// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ -// Array representation of binary tree: +// For the serialization rules, see: +// https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ +// Array representation of the binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] -// Linked list representation of binary tree: +// Linked representation of the binary tree: // /——— 15 // /——— 7 // /——— 3 @@ -91,7 +91,7 @@ int *treeToArray(TreeNode *root, int *size) { return res; } -/* Free binary tree memory */ +/* Free the binary tree memory */ void freeMemoryTree(TreeNode *root) { if (root == NULL) return; diff --git a/en/codes/c/utils/vector.h b/en/codes/c/utils/vector.h index be9f522a1..4d9cffa1a 100644 --- a/en/codes/c/utils/vector.h +++ b/en/codes/c/utils/vector.h @@ -11,15 +11,15 @@ extern "C" { #endif -/* Define vector type */ +/* Define the vector type */ typedef struct vector { int size; // Current vector size int capacity; // Current vector capacity int depth; // Current vector depth - void **data; // Pointer array to data + void **data; // Array of pointers to the data } vector; -/* Construct vector */ +/* Construct a vector */ vector *newVector() { vector *v = malloc(sizeof(vector)); v->size = 0; @@ -29,7 +29,7 @@ vector *newVector() { return v; } -/* Construct vector with specified size and default value */ +/* Construct a vector with the specified size and default element value */ vector *_newVector(int size, void *elem, int elemSize) { vector *v = malloc(sizeof(vector)); v->size = size; @@ -44,7 +44,7 @@ vector *_newVector(int size, void *elem, int elemSize) { return v; } -/* Destruct vector */ +/* Destroy the vector */ void delVector(vector *v) { if (v) { if (v->depth == 0) { @@ -63,7 +63,7 @@ void delVector(vector *v) { } } -/* Add element (by copy) to vector tail */ +/* Append a copy of an element to the vector */ void vectorPushback(vector *v, void *elem, int elemSize) { if (v->size == v->capacity) { v->capacity *= 2; @@ -74,7 +74,7 @@ void vectorPushback(vector *v, void *elem, int elemSize) { v->data[v->size++] = tmp; } -/* Pop element from vector tail */ +/* Pop the last element from the vector */ void vectorPopback(vector *v) { if (v->size != 0) { free(v->data[v->size - 1]); @@ -82,7 +82,7 @@ void vectorPopback(vector *v) { } } -/* Clear vector */ +/* Clear the vector */ void vectorClear(vector *v) { delVector(v); v->size = 0; @@ -91,23 +91,23 @@ void vectorClear(vector *v) { v->data = malloc(v->capacity * sizeof(void *)); } -/* Get vector size */ +/* Get the vector size */ int vectorSize(vector *v) { return v->size; } -/* Get vector tail element */ +/* Get the last element of the vector */ void *vectorBack(vector *v) { int n = v->size; return n > 0 ? v->data[n - 1] : NULL; } -/* Get vector head element */ +/* Get the first element of the vector */ void *vectorFront(vector *v) { return v->size > 0 ? v->data[0] : NULL; } -/* Get vector element at index pos */ +/* Get the vector element at index pos */ void *vectorAt(vector *v, int pos) { if (pos < 0 || pos >= v->size) { printf("vectorAt: out of range\n"); @@ -116,7 +116,7 @@ void *vectorAt(vector *v, int pos) { return v->data[pos]; } -/* Set vector element at index pos */ +/* Set the vector element at index pos */ void vectorSet(vector *v, int pos, void *elem, int elemSize) { if (pos < 0 || pos >= v->size) { printf("vectorSet: out of range\n"); @@ -128,19 +128,19 @@ void vectorSet(vector *v, int pos, void *elem, int elemSize) { v->data[pos] = tmp; } -/* Expand vector capacity */ +/* Grow the vector capacity */ void vectorExpand(vector *v) { v->capacity *= 2; v->data = realloc(v->data, v->capacity * sizeof(void *)); } -/* Shrink vector capacity */ +/* Shrink the vector capacity */ void vectorShrink(vector *v) { v->capacity /= 2; v->data = realloc(v->data, v->capacity * sizeof(void *)); } -/* Insert element at vector index pos */ +/* Insert an element at vector index pos */ void vectorInsert(vector *v, int pos, void *elem, int elemSize) { if (v->size == v->capacity) { vectorExpand(v); @@ -154,7 +154,7 @@ void vectorInsert(vector *v, int pos, void *elem, int elemSize) { v->size++; } -/* Delete element at vector index pos */ +/* Remove the element at vector index pos */ void vectorErase(vector *v, int pos) { if (v->size != 0) { free(v->data[pos]); @@ -172,40 +172,40 @@ void vectorSwap(vector *v, int i, int j) { v->data[j] = tmp; } -/* Is vector empty */ +/* Check whether the vector is empty */ bool vectorEmpty(vector *v) { return v->size == 0; } -/* Is vector full */ +/* Check whether the vector is full */ bool vectorFull(vector *v) { return v->size == v->capacity; } -/* Are vectors equal */ +/* Check whether the vectors are equal */ bool vectorEqual(vector *v1, vector *v2) { if (v1->size != v2->size) { - printf("size not equal\n"); + printf("Sizes are not equal\n"); return false; } for (int i = 0; i < v1->size; i++) { void *a = v1->data[i]; void *b = v2->data[i]; if (memcmp(a, b, sizeof(a)) != 0) { - printf("data %d not equal\n", i); + printf("Data at index %d are not equal\n", i); return false; } } return true; } -/* Sort vector internally */ +/* Sort the elements within the vector */ void vectorSort(vector *v, int (*cmp)(const void *, const void *)) { qsort(v->data, v->size, sizeof(void *), cmp); } -/* Print function, need to pass a function to print variables */ -/* Currently only supports printing vector with depth 1 */ +/* Print the vector using a supplied element-printing function */ +/* Currently supports printing only vectors of depth 1 */ void printVector(vector *v, void (*printFunc)(vector *v, void *p)) { if (v) { if (v->depth == 0) { @@ -235,7 +235,7 @@ void printVector(vector *v, void (*printFunc)(vector *v, void *p)) { } } -/* Currently only supports printing vector with depth 2 */ +/* Currently supports printing only vectors of depth 2 */ void printVectorMatrix(vector *vv, void (*printFunc)(vector *v, void *p)) { printf("[\n"); for (int i = 0; i < vv->size; i++) { diff --git a/en/codes/c/utils/vertex.h b/en/codes/c/utils/vertex.h index 0cab3534a..dc64a6f49 100644 --- a/en/codes/c/utils/vertex.h +++ b/en/codes/c/utils/vertex.h @@ -16,7 +16,7 @@ typedef struct { int val; } Vertex; -/* Constructor, initialize a new node */ +/* Constructor: initialize a new vertex */ Vertex *newVertex(int val) { Vertex *vet; vet = (Vertex *)malloc(sizeof(Vertex)); @@ -24,7 +24,7 @@ Vertex *newVertex(int val) { return vet; } -/* Convert value array to vertex array */ +/* Convert an array of values to an array of vertices */ Vertex **valsToVets(int *vals, int size) { Vertex **vertices = (Vertex **)malloc(size * sizeof(Vertex *)); for (int i = 0; i < size; ++i) { @@ -33,7 +33,7 @@ Vertex **valsToVets(int *vals, int size) { return vertices; } -/* Convert vertex array to value array */ +/* Convert an array of vertices to an array of values */ int *vetsToVals(Vertex **vertices, int size) { int *vals = (int *)malloc(size * sizeof(int)); for (int i = 0; i < size; ++i) { diff --git a/en/codes/cpp/.gitignore b/en/codes/cpp/.gitignore index dc1ffacf4..a019b3be4 100644 --- a/en/codes/cpp/.gitignore +++ b/en/codes/cpp/.gitignore @@ -1,8 +1,8 @@ # Ignore all * -# Unignore all with extensions +# Unignore all files with extensions !*.* -# Unignore all dirs +# Unignore all directories !*/ *.dSYM/ diff --git a/en/codes/cpp/chapter_array_and_linkedlist/array.cpp b/en/codes/cpp/chapter_array_and_linkedlist/array.cpp index d89f28fc1..2148e6586 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/array.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/array.cpp @@ -6,18 +6,18 @@ #include "../utils/common.hpp" -/* Random access to element */ +/* Access an element at a random index */ int randomAccess(int *nums, int size) { - // Randomly select a number from interval [0, size) + // Choose a random number in [0, size) int randomIndex = rand() % size; - // Retrieve and return the random element + // Get and return the randomly selected element int randomNum = nums[randomIndex]; return randomNum; } -/* Extend array length */ +/* Extend the array length */ int *extend(int *nums, int size, int enlarge) { - // Initialize an array with extended length + // Initialize an array with the 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++) { @@ -25,38 +25,38 @@ int *extend(int *nums, int size, int enlarge) { } // Free memory delete[] nums; - // Return the extended new array + // Return the new, extended array return res; } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for (int i = size - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num; } -/* Remove the element at index index */ +/* Remove the element at index `index` */ void remove(int *nums, int size, int index) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (int i = index; i < size - 1; i++) { nums[i] = nums[i + 1]; } } -/* Traverse array */ +/* Traverse the array */ void traverse(int *nums, int size) { int count = 0; - // Traverse array by index + // Traverse the array by index for (int i = 0; i < size; i++) { count += nums[i]; } } -/* Find the specified element in the array */ +/* Find a specified element in the array */ int find(int *nums, int size, int target) { for (int i = 0; i < size; i++) { if (nums[i] == target) @@ -65,9 +65,9 @@ int find(int *nums, int size, int target) { return -1; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize array */ + /* Initialize the array */ int size = 5; int *arr = new int[size]; cout << "Array arr = "; @@ -77,33 +77,33 @@ int main() { cout << "Array nums = "; printArray(nums, size); - /* Insert element */ + /* Random access */ int randomNum = randomAccess(nums, size); - cout << "Get random element in nums " << randomNum << endl; + cout << "Randomly selected element from nums: " << randomNum << endl; - /* Traverse array */ + /* Extend the length */ int enlarge = 3; nums = extend(nums, size, enlarge); size += enlarge; - cout << "Extend array length to 8, resulting in nums = "; + cout << "Extend the array length to 8, giving nums = "; printArray(nums, size); - /* Insert element */ + /* Insert an element */ insert(nums, size, 6, 3); - cout << "Insert number 6 at index 3, resulting in nums = "; + cout << "Insert the number 6 at index 3, giving nums = "; printArray(nums, size); - /* Remove element */ + /* Remove an element */ remove(nums, size, 2); - cout << "Remove element at index 2, resulting in nums = "; + cout << "Remove the element at index 2, giving nums = "; printArray(nums, size); - /* Traverse array */ + /* Traverse the array */ traverse(nums, size); - /* Find element */ + /* Find an element */ int index = find(nums, size, 3); - cout << "Find element 3 in nums, get index = " << index << endl; + cout << "Find element 3 in nums, giving index = " << index << endl; // Free memory delete[] arr; diff --git a/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp index a054ba086..1e0f73a7d 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp @@ -25,7 +25,7 @@ void remove(ListNode *n0) { delete P; } -/* Access the node at index 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) @@ -47,40 +47,40 @@ int find(ListNode *head, int target) { return -1; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize linked list */ - // Initialize each node + /* Initialize the linked list */ + // Initialize the individual nodes ListNode *n0 = new ListNode(1); ListNode *n1 = new ListNode(3); ListNode *n2 = new ListNode(2); ListNode *n3 = new ListNode(5); ListNode *n4 = new ListNode(4); - // Build references between nodes + // Set up references between the nodes n0->next = n1; n1->next = n2; n2->next = n3; n3->next = n4; - cout << "Initialized linked list is" << endl; + cout << "The initialized linked list is" << endl; printLinkedList(n0); - /* Insert node */ + /* Insert a node */ insert(n0, new ListNode(0)); - cout << "Linked list after inserting node is" << endl; + cout << "The linked list after inserting a node is" << endl; printLinkedList(n0); - /* Remove node */ + /* Remove a node */ remove(n0); - cout << "Linked list after removing node is" << endl; + cout << "The linked list after removing a node is" << endl; printLinkedList(n0); - /* Access node */ + /* Access a node */ ListNode *node = access(n0, 3); - cout << "Value of node at index 3 in linked list = " << node->val << endl; + cout << "Value of the linked list node at index 3 = " << node->val << endl; - /* Search node */ + /* Find a node */ int index = find(n0, 2); - cout << "Index of node with value 2 in linked list = " << index << endl; + cout << "Index of the linked list node with value 2 = " << index << endl; // Free memory freeMemoryLinkedList(n0); diff --git a/en/codes/cpp/chapter_array_and_linkedlist/list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/list.cpp index 1a3a9cd80..8c9a536b2 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/list.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/list.cpp @@ -6,28 +6,28 @@ #include "../utils/common.hpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize list */ + /* Initialize the list */ vector nums = {1, 3, 2, 5, 4}; cout << "List nums = "; printVector(nums); - /* Update element */ + /* Access an element */ int num = nums[1]; - cout << "Access element at index 1, get num = " << num << endl; + cout << "Access the element at index 1, giving num = " << num << endl; - /* Add elements at the end */ + /* Update an element */ nums[1] = 0; - cout << "Update element at index 1 to 0, resulting in nums = "; + cout << "Update the element at index 1 to 0, giving nums = "; printVector(nums); - /* Remove element */ + /* Clear the list */ nums.clear(); - cout << "After clearing list, nums = "; + cout << "After clearing the list, nums = "; printVector(nums); - /* Direct traversal of list elements */ + /* Append an element at the end */ nums.push_back(1); nums.push_back(3); nums.push_back(2); @@ -36,22 +36,22 @@ int main() { cout << "After adding elements, nums = "; printVector(nums); - /* Sort list */ + /* Insert an element in the middle */ nums.insert(nums.begin() + 3, 6); - cout << "Insert number 6 at index 3, resulting in nums = "; + cout << "Insert the number 6 at index 3, giving nums = "; printVector(nums); - /* Remove element */ + /* Remove an element */ nums.erase(nums.begin() + 3); - cout << "Remove element at index 3, resulting in nums = "; + cout << "Remove the element at index 3, giving nums = "; printVector(nums); - /* Traverse list by index */ + /* Traverse the list by index */ int count = 0; for (int i = 0; i < nums.size(); i++) { count += nums[i]; } - /* Directly traverse list elements */ + /* Iterate directly over the list elements */ count = 0; for (int x : nums) { count += x; @@ -60,12 +60,12 @@ int main() { /* Concatenate two lists */ vector nums1 = {6, 8, 7, 10, 9}; nums.insert(nums.end(), nums1.begin(), nums1.end()); - cout << "Concatenate list nums1 to nums, resulting in nums = "; + cout << "After appending list nums1 to nums, nums = "; printVector(nums); - /* Sort list */ + /* Sort the list */ sort(nums.begin(), nums.end()); - cout << "After sorting list, nums = "; + cout << "After sorting the list, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp index d3390b679..4040d9241 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp @@ -9,10 +9,10 @@ /* List class */ class MyList { private: - int *arr; // Array (stores list elements) + int *arr; // Array (stores the list elements) int arrCapacity = 10; // List capacity int arrSize = 0; // List length (current number of elements) - int extendRatio = 2; // Multiple by which the list capacity is extended each time + int extendRatio = 2; // Factor by which the list capacity grows each time public: /* Constructor */ @@ -25,34 +25,34 @@ class MyList { delete[] arr; } - /* Get list length (current number of elements)*/ + /* Get the list length (current number of elements)*/ int size() { return arrSize; } - /* Get list capacity */ + /* Get the list capacity */ int capacity() { return arrCapacity; } - /* Update element */ + /* Access an element */ int get(int index) { - // If the index is out of bounds, throw an exception, as below + // Raise an exception if the index is out of bounds; the same applies below if (index < 0 || index >= size()) throw out_of_range("Index out of bounds"); return arr[index]; } - /* Add elements at the end */ + /* Update an element */ void set(int index, int num) { if (index < 0 || index >= size()) throw out_of_range("Index out of bounds"); arr[index] = num; } - /* Direct traversal of list elements */ + /* Append an element at the end */ void add(int num) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if (size() == capacity()) extendCapacity(); arr[size()] = num; @@ -60,14 +60,14 @@ class MyList { arrSize++; } - /* Sort list */ + /* Insert an element in the middle */ 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 extension mechanism + // Grow the capacity when adding an element would exceed it if (size() == capacity()) extendCapacity(); - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for (int j = size() - 1; j >= index; j--) { arr[j + 1] = arr[j]; } @@ -76,12 +76,12 @@ class MyList { arrSize++; } - /* Remove element */ + /* Remove an element */ int remove(int index) { if (index < 0 || index >= size()) throw out_of_range("Index out of bounds"); int num = arr[index]; - // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array + // Shift all elements after index `index` one position to the left for (int j = index; j < size() - 1; j++) { arr[j] = arr[j + 1]; } @@ -91,9 +91,9 @@ class MyList { return num; } - /* Driver Code */ + /* Grow the list capacity */ void extendCapacity() { - // Create a new array with length extendRatio times the original array + // Create a new array with extendRatio times the original array's length int newCapacity = capacity() * extendRatio; int *tmp = arr; arr = new int[newCapacity]; @@ -106,9 +106,9 @@ class MyList { arrCapacity = newCapacity; } - /* Convert list to Vector for printing */ + /* Convert the list to a vector for printing */ vector toVector() { - // Elements enqueue + // Convert only the list elements within the occupied range vector vec(size()); for (int i = 0; i < size(); i++) { vec[i] = arr[i]; @@ -117,11 +117,11 @@ class MyList { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize list */ + /* Initialize the list */ MyList *nums = new MyList(); - /* Direct traversal of list elements */ + /* Append an element at the end */ nums->add(1); nums->add(3); nums->add(2); @@ -130,39 +130,39 @@ int main() { cout << "List nums = "; vector vec = nums->toVector(); printVector(vec); - cout << "Capacity = " << nums->capacity() << ", length = " << nums->size() << endl; + cout << "Capacity = " << nums->capacity() << " , size = " << nums->size() << endl; - /* Sort list */ + /* Insert an element in the middle */ nums->insert(3, 6); - cout << "Insert number 6 at index 3, resulting in nums = "; + cout << "Insert the number 6 at index 3, giving nums = "; vec = nums->toVector(); printVector(vec); - /* Remove element */ + /* Remove an element */ nums->remove(3); - cout << "Remove element at index 3, resulting in nums = "; + cout << "Remove the element at index 3, giving nums = "; vec = nums->toVector(); printVector(vec); - /* Update element */ + /* Access an element */ int num = nums->get(1); - cout << "Access element at index 1, get num = " << num << endl; + cout << "Access the element at index 1, giving num = " << num << endl; - /* Add elements at the end */ + /* Update an element */ nums->set(1, 0); - cout << "Update element at index 1 to 0, resulting in nums = "; + cout << "Update the element at index 1 to 0, giving nums = "; vec = nums->toVector(); printVector(vec); - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for (int i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums->add(i); } - cout << "List nums after expansion = "; + cout << "List after capacity growth, nums = "; vec = nums->toVector(); printVector(vec); - cout << "Capacity = " << nums->capacity() << ", length = " << nums->size() << endl; + cout << "Capacity = " << nums->capacity() << " , size = " << nums->size() << endl; // Free memory delete nums; diff --git a/en/codes/cpp/chapter_backtracking/n_queens.cpp b/en/codes/cpp/chapter_backtracking/n_queens.cpp index 224330c47..761ec3518 100644 --- a/en/codes/cpp/chapter_backtracking/n_queens.cpp +++ b/en/codes/cpp/chapter_backtracking/n_queens.cpp @@ -6,40 +6,40 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ void backtrack(int row, int n, vector> &state, vector>> &res, vector &cols, vector &diags1, vector &diags2) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if (row == n) { res.push_back(state); return; } - // Traverse all columns + // Iterate over all columns for (int col = 0; col < n; col++) { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square int diag1 = row - col + n - 1; int diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = "Q"; cols[col] = diags1[diag1] = diags2[diag2] = true; - // Place the next row + // Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = "#"; cols[col] = diags1[diag1] = diags2[diag2] = false; } } } -/* Solve N queens */ +/* Solve the n-queens problem */ vector>> nQueens(int n) { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square vector> state(n, vector(n, "#")); - vector cols(n, false); // Record whether there is a queen in the column - vector diags1(2 * n - 1, false); // Record whether there is a queen on the main diagonal - vector diags2(2 * n - 1, false); // Record whether there is a queen on the anti-diagonal + vector cols(n, false); // Track whether a column contains a queen + vector diags1(2 * n - 1, false); // Track whether a main diagonal contains a queen + vector diags2(2 * n - 1, false); // Track whether an anti-diagonal contains a queen vector>> res; backtrack(0, n, state, res, cols, diags1, diags2); @@ -47,13 +47,13 @@ vector>> nQueens(int n) { return res; } -/* Driver Code */ +/* Driver code */ int main() { int n = 4; vector>> res = nQueens(n); - cout << "Input board size is " << n << endl; - cout << "Total queen placement solutions: " << res.size() << endl; + cout << "Board width and height: " << n << endl; + cout << "Queen placements: " << res.size() << " solutions" << endl; for (const vector> &state : res) { cout << "--------------------" << endl; for (const vector &row : state) { diff --git a/en/codes/cpp/chapter_backtracking/permutations_i.cpp b/en/codes/cpp/chapter_backtracking/permutations_i.cpp index 10813b7fd..5138c676a 100644 --- a/en/codes/cpp/chapter_backtracking/permutations_i.cpp +++ b/en/codes/cpp/chapter_backtracking/permutations_i.cpp @@ -6,24 +6,24 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.size() == choices.size()) { res.push_back(state); return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choices.size(); i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state.push_back(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop_back(); } @@ -39,7 +39,7 @@ vector> permutationsI(vector nums) { return res; } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {1, 2, 3}; diff --git a/en/codes/cpp/chapter_backtracking/permutations_ii.cpp b/en/codes/cpp/chapter_backtracking/permutations_ii.cpp index cb2ccd061..5c5682ce7 100644 --- a/en/codes/cpp/chapter_backtracking/permutations_ii.cpp +++ b/en/codes/cpp/chapter_backtracking/permutations_ii.cpp @@ -6,26 +6,26 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.size() == choices.size()) { res.push_back(state); return; } - // Traverse all choices + // Iterate over all choices unordered_set duplicated; for (int i = 0; i < choices.size(); i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && duplicated.find(choice) == duplicated.end()) { - // Attempt: make choice, update state - duplicated.emplace(choice); // Record the selected element value + // Try: make a choice and update the state + duplicated.emplace(choice); // Record the chosen value selected[i] = true; state.push_back(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop_back(); } @@ -41,7 +41,7 @@ vector> permutationsII(vector nums) { return res; } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {1, 1, 2}; diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp index 9d1134ede..d66e23fef 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp @@ -8,23 +8,23 @@ vector res; -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ void preOrder(TreeNode *root) { if (root == nullptr) { return; } if (root->val == 7) { - // Record solution + // Record the solution res.push_back(root); } preOrder(root->left); preOrder(root->right); } -/* Driver Code */ +/* Driver code */ int main() { TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); - cout << "\nInitialize binary tree" << endl; + cout << "\nInitialize the binary tree" << endl; printTree(root); // Preorder traversal diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp index 1c1bb0875..f1d6bbac5 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp @@ -9,15 +9,15 @@ vector path; vector> res; -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ void preOrder(TreeNode *root) { if (root == nullptr) { return; } - // Attempt + // Try path.push_back(root); if (root->val == 7) { - // Record solution + // Record the solution res.push_back(path); } preOrder(root->left); @@ -26,16 +26,16 @@ void preOrder(TreeNode *root) { path.pop_back(); } -/* Driver Code */ +/* Driver code */ int main() { TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); - cout << "\nInitialize binary tree" << endl; + cout << "\nInitialize the binary tree" << endl; printTree(root); // Preorder traversal preOrder(root); - cout << "\nOutput all paths from root node to node 7" << endl; + cout << "\nOutput all paths from the root to nodes with value 7" << endl; for (vector &path : res) { vector vals; for (TreeNode *node : path) { diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp index 4d8cc19fe..a3b7f3135 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp @@ -9,16 +9,16 @@ vector path; vector> res; -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ void preOrder(TreeNode *root) { - // Pruning + // Prune if (root == nullptr || root->val == 3) { return; } - // Attempt + // Try path.push_back(root); if (root->val == 7) { - // Record solution + // Record the solution res.push_back(path); } preOrder(root->left); @@ -27,16 +27,16 @@ void preOrder(TreeNode *root) { path.pop_back(); } -/* Driver Code */ +/* Driver code */ int main() { TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); - cout << "\nInitialize binary tree" << endl; + cout << "\nInitialize the binary tree" << endl; printTree(root); // Preorder traversal preOrder(root); - cout << "\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3" << endl; + cout << "\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3" << endl; for (vector &path : res) { vector vals; for (TreeNode *node : path) { diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp index 1441c02e0..35402009e 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp @@ -6,57 +6,57 @@ #include "../utils/common.hpp" -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ bool isSolution(vector &state) { return !state.empty() && state.back()->val == 7; } -/* Record solution */ +/* Record the solution */ void recordSolution(vector &state, vector> &res) { res.push_back(state); } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ bool isValid(vector &state, TreeNode *choice) { return choice != nullptr && choice->val != 3; } -/* Update state */ +/* Update the state */ void makeChoice(vector &state, TreeNode *choice) { state.push_back(choice); } -/* Restore state */ +/* Restore the state */ void undoChoice(vector &state, TreeNode *choice) { state.pop_back(); } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ void backtrack(vector &state, vector &choices, vector> &res) { - // Check if it is a solution + // Check whether this is a solution if (isSolution(state)) { - // Record solution + // Record the solution recordSolution(state, res); } - // Traverse all choices + // Iterate over all choices for (TreeNode *choice : choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state, choice); - // Proceed to the next round of selection + // Proceed to the next choice vector nextChoices{choice->left, choice->right}; backtrack(state, nextChoices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } } -/* Driver Code */ +/* Driver code */ int main() { TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); - cout << "\nInitialize binary tree" << endl; + cout << "\nInitialize the binary tree" << endl; printTree(root); // Backtracking algorithm @@ -65,7 +65,7 @@ int main() { vector> res; backtrack(state, choices, res); - cout << "\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3" << endl; + cout << "\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3" << endl; for (vector &path : res) { vector vals; for (TreeNode *node : path) { diff --git a/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp b/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp index d2c406730..7f9ad8af1 100644 --- a/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp +++ b/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp @@ -6,26 +6,26 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target reaches 0 if (target == 0) { res.push_back(state); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (int i = start; i < choices.size(); i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if (target - choices[i] < 0) { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push_back(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop_back(); } } @@ -34,13 +34,13 @@ void backtrack(vector &state, int target, vector &choices, int start, vector> subsetSumI(vector &nums, int target) { vector state; // State (subset) sort(nums.begin(), nums.end()); // Sort nums - int start = 0; // Start point for traversal - vector> res; // Result list (subset list) + int start = 0; // Starting index for iteration + vector> res; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {3, 4, 5}; int target = 9; @@ -50,7 +50,7 @@ int main() { cout << "Input array nums = "; printVector(nums); cout << "target = " << target << endl; - cout << "All subsets with sum equal to " << target << " are res = " << endl; + cout << "All subsets with sum " << target << " , res = " << endl; printVectorMatrix(res); return 0; diff --git a/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp b/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp index 273e11c62..beec42ff1 100644 --- a/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp +++ b/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp @@ -6,38 +6,38 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ void backtrack(vector &state, int target, int total, vector &choices, vector> &res) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total == target) { res.push_back(state); return; } - // Traverse all choices + // Iterate over all choices for (size_t i = 0; i < choices.size(); i++) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.push_back(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target, total + choices[i], choices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop_back(); } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ vector> subsetSumINaive(vector &nums, int target) { vector state; // State (subset) int total = 0; // Subset sum - vector> res; // Result list (subset list) + vector> res; // Result list (list of subsets) backtrack(state, target, total, nums, res); return res; } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {3, 4, 5}; int target = 9; @@ -47,7 +47,7 @@ int main() { cout << "Input array nums = "; printVector(nums); cout << "target = " << target << endl; - cout << "All subsets with sum equal to " << target << " are res = " << endl; + cout << "All subsets with sum " << target << " , res = " << endl; printVectorMatrix(res); return 0; diff --git a/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp b/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp index b452f1f97..9ca446f9e 100644 --- a/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp +++ b/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp @@ -6,31 +6,31 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target reaches 0 if (target == 0) { res.push_back(state); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: iterate from start to avoid choosing the same element again for (int i = start; i < choices.size(); i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if (target - choices[i] < 0) { break; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if (i > start && choices[i] == choices[i - 1]) { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push_back(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop_back(); } } @@ -39,13 +39,13 @@ void backtrack(vector &state, int target, vector &choices, int start, vector> subsetSumII(vector &nums, int target) { vector state; // State (subset) sort(nums.begin(), nums.end()); // Sort nums - int start = 0; // Start point for traversal - vector> res; // Result list (subset list) + int start = 0; // Starting index for iteration + vector> res; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {4, 4, 5}; int target = 9; @@ -55,7 +55,7 @@ int main() { cout << "Input array nums = "; printVector(nums); cout << "target = " << target << endl; - cout << "All subsets with sum equal to " << target << " are res = " << endl; + cout << "All subsets with sum " << target << " , res = " << endl; printVectorMatrix(res); return 0; diff --git a/en/codes/cpp/chapter_computational_complexity/complexity_exercises.cpp b/en/codes/cpp/chapter_computational_complexity/complexity_exercises.cpp index f05f0939e..c46e9daec 100644 --- a/en/codes/cpp/chapter_computational_complexity/complexity_exercises.cpp +++ b/en/codes/cpp/chapter_computational_complexity/complexity_exercises.cpp @@ -6,7 +6,7 @@ #include -/* Iterative summation */ +/* Sum using iteration */ int sumIter(int n) { int res = 0; for (int i = 1; i <= n; ++i) { @@ -15,7 +15,7 @@ int sumIter(int n) { return res; } -/* Recursive summation */ +/* Sum using recursion */ int sumRecur(int n) { if (n == 1) { return 1; @@ -23,7 +23,7 @@ int sumRecur(int n) { return n + sumRecur(n - 1); } -/* Linear loop */ +/* Linear-time loop */ int linearLoop(int n) { int res = 0; for (int i = 0; i < n; ++i) { @@ -32,7 +32,7 @@ int linearLoop(int n) { return res; } -/* Quadratic loop */ +/* Quadratic-time loop */ int quadraticLoop(int n) { int res = 0; for (int i = 0; i < n; ++i) { @@ -43,7 +43,7 @@ int quadraticLoop(int n) { return res; } -/* Logarithmic loop */ +/* Logarithmic-time loop */ int logarithmicLoop(int n) { while (n > 1) { n /= 2; diff --git a/en/codes/cpp/chapter_computational_complexity/iteration.cpp b/en/codes/cpp/chapter_computational_complexity/iteration.cpp index 09eb65f22..ccc31a406 100644 --- a/en/codes/cpp/chapter_computational_complexity/iteration.cpp +++ b/en/codes/cpp/chapter_computational_complexity/iteration.cpp @@ -9,7 +9,7 @@ /* for loop */ int forLoop(int n) { int res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (int i = 1; i <= n; ++i) { res += i; } @@ -19,11 +19,11 @@ int forLoop(int n) { /* while loop */ int whileLoop(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 2, ..., n-1, n + int i = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i; - i++; // Update condition variable + i++; // Update the loop control variable } return res; } @@ -31,23 +31,23 @@ int whileLoop(int n) { /* while loop (two updates) */ int whileLoopII(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 4, 10, ... + int i = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i; - // Update condition variable + // Update the loop control variable i++; i *= 2; } return res; } -/* Nested for loop */ +/* Nested for loops */ string nestedForLoop(int n) { ostringstream res; - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (int i = 1; i <= n; ++i) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (int j = 1; j <= n; ++j) { res << "(" << i << ", " << j << "), "; } @@ -55,22 +55,22 @@ string nestedForLoop(int n) { return res.str(); } -/* Driver Code */ +/* Driver code */ int main() { int n = 5; int res; res = forLoop(n); - cout << "\nfor loop sum result res = " << res << endl; + cout << "\nSum from the for loop, res = " << res << endl; res = whileLoop(n); - cout << "\nwhile loop sum result res = " << res << endl; + cout << "\nSum from the while loop, res = " << res << endl; res = whileLoopII(n); - cout << "\nwhile loop (two updates) sum result res = " << res << endl; + cout << "\nSum from the while loop with two updates, res = " << res << endl; string resStr = nestedForLoop(n); - cout << "\nDouble for loop traversal result " << resStr << endl; + cout << "\nTraversal result from the nested for loops: " << resStr << endl; return 0; } diff --git a/en/codes/cpp/chapter_computational_complexity/recursion.cpp b/en/codes/cpp/chapter_computational_complexity/recursion.cpp index bc6ecc067..3621eadd2 100644 --- a/en/codes/cpp/chapter_computational_complexity/recursion.cpp +++ b/en/codes/cpp/chapter_computational_complexity/recursion.cpp @@ -8,12 +8,12 @@ /* Recursion */ int recur(int n) { - // Termination condition + // Base case if (n == 1) return 1; - // Recurse: recursive call + // Recurse: make a recursive call int res = recur(n - 1); - // Return: return result + // Unwind: return the result return n + res; } @@ -22,14 +22,14 @@ int forLoopRecur(int n) { // Use an explicit stack to simulate the system call stack stack stack; int res = 0; - // Recurse: recursive call + // Recurse: make a recursive call for (int i = n; i > 0; i--) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.push(i); } - // Return: return result + // Unwind: return the result while (!stack.empty()) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.top(); stack.pop(); } @@ -39,40 +39,40 @@ int forLoopRecur(int n) { /* Tail recursion */ int tailRecur(int n, int res) { - // Termination condition + // Base case if (n == 0) return res; - // Tail recursive call + // Tail-recursive call return tailRecur(n - 1, res + n); } /* Fibonacci sequence: recursion */ int fib(int n) { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n == 1 || n == 2) return n - 1; - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) int res = fib(n - 1) + fib(n - 2); - // Return result f(n) + // Return the result f(n) return res; } -/* Driver Code */ +/* Driver code */ int main() { int n = 5; int res; res = recur(n); - cout << "\nRecursive function sum result res = " << res << endl; + cout << "\nSum from the recursive function, res = " << res << endl; res = forLoopRecur(n); - cout << "\nUsing iteration to simulate recursive sum result res = " << res << endl; + cout << "\nSum from simulating recursion with iteration, res = " << res << endl; res = tailRecur(n, 0); - cout << "\nTail recursive function sum result res = " << res << endl; + cout << "\nSum from the tail-recursive function, res = " << res << endl; res = fib(n); - cout << "\nThe " << n << "th term of the Fibonacci sequence is " << res << endl; + cout << "\nFibonacci term " << n << " is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp b/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp index 66e1d4eb9..a242299fd 100644 --- a/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp +++ b/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp @@ -12,9 +12,9 @@ int func() { return 0; } -/* Constant order */ +/* Constant complexity */ void constant(int n) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space const int a = 0; int b = 0; vector nums(10000); @@ -23,39 +23,39 @@ void constant(int n) { for (int i = 0; i < n; i++) { int c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for (int i = 0; i < n; i++) { func(); } } -/* Linear order */ +/* Linear complexity */ void linear(int n) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space vector nums(n); // A list of length n occupies O(n) space vector nodes; for (int i = 0; i < n; i++) { nodes.push_back(ListNode(i)); } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space unordered_map map; for (int i = 0; i < n; i++) { map[i] = to_string(i); } } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ void linearRecur(int n) { - cout << "Recursion n = " << n << endl; + cout << "Recursion: n = " << n << endl; if (n == 1) return; linearRecur(n - 1); } -/* Exponential order */ +/* Quadratic complexity */ void quadratic(int n) { - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space vector> numMatrix; for (int i = 0; i < n; i++) { vector tmp; @@ -66,16 +66,16 @@ void quadratic(int n) { } } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ int quadraticRecur(int n) { if (n <= 0) return 0; vector nums(n); - cout << "In recursion n = " << n << ", nums length = " << nums.size() << endl; + cout << "Recursion: n = " << n << " , length of nums = " << nums.size() << endl; return quadraticRecur(n - 1); } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ TreeNode *buildTree(int n) { if (n == 0) return nullptr; @@ -85,18 +85,18 @@ TreeNode *buildTree(int n) { return root; } -/* Driver Code */ +/* Driver code */ int main() { int n = 5; - // Constant order + // Constant complexity constant(n); - // Linear order + // Linear complexity linear(n); linearRecur(n); - // Exponential order + // Quadratic complexity quadratic(n); quadraticRecur(n); - // Exponential order + // Exponential complexity TreeNode *root = buildTree(n); printTree(root); diff --git a/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp b/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp index 23f18dca9..dbbd4b320 100644 --- a/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp +++ b/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Constant order */ +/* Constant complexity */ int constant(int n) { int count = 0; int size = 100000; @@ -15,7 +15,7 @@ int constant(int n) { return count; } -/* Linear order */ +/* Linear complexity */ int linear(int n) { int count = 0; for (int i = 0; i < n; i++) @@ -23,20 +23,20 @@ int linear(int n) { return count; } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ int arrayTraversal(vector &nums) { int count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for (int num : nums) { count++; } return count; } -/* Exponential order */ +/* Quadratic complexity */ int quadratic(int n) { int count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -45,29 +45,29 @@ int quadratic(int n) { return count; } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ int bubbleSort(vector &nums) { int count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.size() - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } return count; } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ int exponential(int n) { int count = 0, base = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (int i = 0; i < n; i++) { for (int j = 0; j < base; j++) { count++; @@ -78,14 +78,14 @@ int exponential(int n) { return count; } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation) */ int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation) */ int logarithmic(int n) { int count = 0; while (n > 1) { @@ -95,14 +95,14 @@ int logarithmic(int n) { return count; } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation) */ int logRecur(int n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } -/* Linearithmic order */ +/* Linearithmic complexity */ int linearLogRecur(int n) { if (n <= 1) return 1; @@ -113,56 +113,56 @@ int linearLogRecur(int n) { return count; } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - // Split from 1 into n + // Split one problem into n subproblems for (int i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; } -/* Driver Code */ +/* Driver code */ int main() { - // You can modify n to run and observe the trend of the number of operations for various complexities + // Try different values of n to see how the operation count grows for each complexity int n = 8; - cout << "Input data size n = " << n << endl; + cout << "Input size n = " << n << endl; int count = constant(n); - cout << "Constant order operation count = " << count << endl; + cout << "Operation count for constant complexity = " << count << endl; count = linear(n); - cout << "Linear order operation count = " << count << endl; + cout << "Operation count for linear complexity = " << count << endl; vector arr(n); count = arrayTraversal(arr); - cout << "Linear order (array traversal) operation count = " << count << endl; + cout << "Operation count for linear complexity (array traversal) = " << count << endl; count = quadratic(n); - cout << "Quadratic order operation count = " << count << endl; + cout << "Operation count for quadratic complexity = " << count << endl; vector nums(n); for (int i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = bubbleSort(nums); - cout << "Quadratic order (bubble sort) operation count = " << count << endl; + cout << "Operation count for quadratic complexity (bubble sort) = " << count << endl; count = exponential(n); - cout << "Exponential order (loop implementation) operation count = " << count << endl; + cout << "Operation count for exponential complexity (iterative implementation) = " << count << endl; count = expRecur(n); - cout << "Exponential order (recursive implementation) operation count = " << count << endl; + cout << "Operation count for exponential complexity (recursive implementation) = " << count << endl; count = logarithmic(n); - cout << "Logarithmic order (loop implementation) operation count = " << count << endl; + cout << "Operation count for logarithmic complexity (iterative implementation) = " << count << endl; count = logRecur(n); - cout << "Logarithmic order (recursive implementation) operation count = " << count << endl; + cout << "Operation count for logarithmic complexity (recursive implementation) = " << count << endl; count = linearLogRecur(n); - cout << "Linearithmic order (recursive implementation) operation count = " << count << endl; + cout << "Operation count for linearithmic complexity (recursive implementation) = " << count << endl; count = factorialRecur(n); - cout << "Factorial order (recursive implementation) operation count = " << count << endl; + cout << "Operation count for factorial complexity (recursive implementation) = " << count << endl; return 0; } diff --git a/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp b/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp index 8f30f2f7d..c515732df 100644 --- a/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp +++ b/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp @@ -6,32 +6,32 @@ #include "../utils/common.hpp" -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ vector randomNumbers(int n) { vector nums(n); - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (int i = 0; i < n; i++) { nums[i] = i + 1; } - // Use system time to generate random seed + // Use the system time to generate a random seed unsigned seed = chrono::system_clock::now().time_since_epoch().count(); - // Randomly shuffle array elements + // Randomly shuffle the array elements shuffle(nums.begin(), nums.end(), default_random_engine(seed)); return nums; } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ int findOne(vector &nums) { for (int i = 0; i < nums.size(); i++) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] == 1) return i; } return -1; } -/* Driver Code */ +/* Driver code */ int main() { for (int i = 0; i < 1000; i++) { int n = 100; @@ -39,7 +39,7 @@ int main() { int index = findOne(nums); cout << "\nArray [ 1, 2, ..., n ] after shuffling = "; printVector(nums); - cout << "Index of number 1 is " << index << endl; + cout << "The index of the number 1 is " << index << endl; } return 0; } diff --git a/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp b/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp index d8b3ba398..f781f9e8f 100644 --- a/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp +++ b/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp @@ -8,20 +8,20 @@ /* Binary search: problem f(i, j) */ int dfs(vector &nums, int target, int i, int j) { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1; } // Calculate the midpoint index m int m = (i + j) / 2; if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -29,16 +29,16 @@ int dfs(vector &nums, int target, int i, int j) { /* Binary search */ int binarySearch(vector &nums, int target) { int n = nums.size(); - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1); } -/* Driver Code */ +/* Driver code */ int main() { int target = 6; vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; - // Binary search (closed interval on both sides) + // Binary search (closed interval) int index = binarySearch(nums, target); cout << "Index of target element 6 = " << index << endl; diff --git a/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp b/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp index bb59dc459..c666cde09 100644 --- a/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp +++ b/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ TreeNode *dfs(vector &preorder, unordered_map &inorderMap, int i, int l, int r) { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) return NULL; // Initialize the root node TreeNode *root = new TreeNode(preorder[i]); - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees int m = inorderMap[preorder[i]]; // Subproblem: build the left subtree root->left = dfs(preorder, inorderMap, i + 1, l, m - 1); @@ -23,9 +23,9 @@ TreeNode *dfs(vector &preorder, unordered_map &inorderMap, int i, return root; } -/* Build binary tree */ +/* Build a binary tree */ TreeNode *buildTree(vector &preorder, vector &inorder) { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index unordered_map inorderMap; for (int i = 0; i < inorder.size(); i++) { inorderMap[inorder[i]] = i; @@ -34,7 +34,7 @@ TreeNode *buildTree(vector &preorder, vector &inorder) { return root; } -/* Driver Code */ +/* Driver code */ int main() { vector preorder = {3, 9, 2, 1, 7}; vector inorder = {9, 3, 1, 2, 7}; @@ -44,7 +44,7 @@ int main() { printVector(inorder); TreeNode *root = buildTree(preorder, inorder); - cout << "The constructed binary tree is:\n"; + cout << "Constructed binary tree:\n"; printTree(root); return 0; diff --git a/en/codes/cpp/chapter_divide_and_conquer/fast_power.cpp b/en/codes/cpp/chapter_divide_and_conquer/fast_power.cpp index 942c0dc6d..cbcc31a45 100644 --- a/en/codes/cpp/chapter_divide_and_conquer/fast_power.cpp +++ b/en/codes/cpp/chapter_divide_and_conquer/fast_power.cpp @@ -6,7 +6,7 @@ #include -/* Exponentiation by squaring */ +/* Fast exponentiation */ int fastPow(int x, int n) { if (n == 0) { return 1; diff --git a/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp b/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp index dd644025d..f995e81a6 100644 --- a/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp +++ b/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp @@ -6,9 +6,9 @@ #include "../utils/common.hpp" -/* Move a disk */ +/* Move one disk */ void move(vector &src, vector &tar) { - // Take out a disk from the top of src + // Take a disk from the top of src int pan = src.back(); src.pop_back(); // Place the disk on top of tar @@ -17,14 +17,14 @@ void move(vector &src, vector &tar) { /* Solve the Tower of Hanoi problem f(i) */ void dfs(int i, vector &src, vector &buf, vector &tar) { - // If there is only one disk left in src, move it directly to tar + // If only one disk remains to be moved from src, move it directly to tar if (i == 1) { move(src, tar); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); @@ -37,9 +37,9 @@ void solveHanota(vector &A, vector &B, vector &C) { dfs(n, A, B, C); } -/* Driver Code */ +/* Driver code */ int main() { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg vector A = {5, 4, 3, 2, 1}; vector B = {}; vector C = {}; @@ -54,7 +54,7 @@ int main() { solveHanota(A, B, C); - cout << "After disk movement:\n"; + cout << "After moving all disks:\n"; cout << "A ="; printVector(A); cout << "B ="; diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp index 168293121..9f31f9f25 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp @@ -9,35 +9,35 @@ /* Backtracking */ void backtrack(vector &choices, int state, int n, vector &res) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state == n) res[0]++; - // Traverse all choices + // Iterate over all choices for (auto &choice : choices) { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue; - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res); // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ int climbingStairsBacktrack(int n) { - vector choices = {1, 2}; // Can choose to climb up 1 or 2 stairs - int state = 0; // Start climbing from the 0-th stair - vector res = {0}; // Use res[0] to record the solution count + vector choices = {1, 2}; // Choose to climb 1 or 2 steps + int state = 0; // Start climbing from step 0 + vector res = {0}; // Use res[0] to record the number of ways backtrack(choices, state, n, res); return res[0]; } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsBacktrack(n); - cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; + cout << "Climbing " << n << " stairs: " << res << " ways" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp index 87b70fbbe..a7990caab 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp @@ -6,19 +6,19 @@ #include "../utils/common.hpp" -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ int climbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions vector> dp(n + 1, vector(3, 0)); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -26,12 +26,12 @@ int climbingStairsConstraintDP(int n) { return dp[n][1] + dp[n][2]; } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsConstraintDP(n); - cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; + cout << "Climbing " << n << " stairs: " << res << " ways" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp index dd326e4cd..69b88a9f3 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp @@ -8,7 +8,7 @@ /* Search */ int dfs(int i) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; // dp[i] = dp[i-1] + dp[i-2] @@ -16,17 +16,17 @@ int dfs(int i) { return count; } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ int climbingStairsDFS(int n) { return dfs(n); } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsDFS(n); - cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; + cout << "Climbing " << n << " stairs: " << res << " ways" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp index 8f59f5c84..30f73c456 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp @@ -6,12 +6,12 @@ #include "../utils/common.hpp" -/* Memoization search */ +/* Memoized search */ int dfs(int i, vector &mem) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] @@ -21,19 +21,19 @@ int dfs(int i, vector &mem) { return count; } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ int climbingStairsDFSMem(int n) { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded vector mem(n + 1, -1); return dfs(n, mem); } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsDFSMem(n); - cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; + cout << "Climbing " << n << " stairs: " << res << " ways" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp index 76ca8980d..45213a066 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp @@ -6,23 +6,23 @@ #include "../utils/common.hpp" -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ int climbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions vector dp(n + 1); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ int climbingStairsDPComp(int n) { if (n == 1 || n == 2) return n; @@ -35,15 +35,15 @@ int climbingStairsDPComp(int n) { return b; } -/* Driver Code */ +/* Driver code */ int main() { int n = 9; int res = climbingStairsDP(n); - cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; + cout << "Climbing " << n << " stairs: " << res << " ways" << endl; res = climbingStairsDPComp(n); - cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; + cout << "Climbing " << n << " stairs: " << res << " ways" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp b/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp index 8299c8295..c4f881478 100644 --- a/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp @@ -6,24 +6,24 @@ #include "../utils/common.hpp" -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ int coinChangeDP(vector &coins, int amt) { int n = coins.size(); int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table vector> dp(n + 1, vector(amt + 1, 0)); - // State transition: first row and first column + // State transitions: first row and first column for (int a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -31,21 +31,21 @@ int coinChangeDP(vector &coins, int amt) { return dp[n][amt] != MAX ? dp[n][amt] : -1; } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ int coinChangeDPComp(vector &coins, int amt) { int n = coins.size(); int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table vector dp(amt + 1, MAX); dp[0] = 0; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -60,11 +60,11 @@ int main() { // Dynamic programming int res = coinChangeDP(coins, amt); - cout << "Minimum number of coins needed to make target amount is " << res << endl; + cout << "Minimum number of coins needed to reach the target amount: " << res << endl; // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt); - cout << "Minimum number of coins needed to make target amount is " << res << endl; + cout << "Minimum number of coins needed to reach the target amount: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp b/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp index 0efc98159..0f943def7 100644 --- a/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp @@ -6,23 +6,23 @@ #include "../utils/common.hpp" -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ int coinChangeIIDP(vector &coins, int amt) { int n = coins.size(); - // Initialize dp table + // Initialize the dp table vector> dp(n + 1, vector(amt + 1, 0)); - // Initialize first column + // Initialize the first column for (int i = 0; i <= n; i++) { dp[i][0] = 1; } - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -30,20 +30,20 @@ int coinChangeIIDP(vector &coins, int amt) { return dp[n][amt]; } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ int coinChangeIIDPComp(vector &coins, int amt) { int n = coins.size(); - // Initialize dp table + // Initialize the dp table vector dp(amt + 1, 0); dp[0] = 1; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -58,11 +58,11 @@ int main() { // Dynamic programming int res = coinChangeIIDP(coins, amt); - cout << "Number of coin combinations to make target amount is " << res << endl; + cout << "Number of coin combinations that make the target amount: " << res << endl; // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt); - cout << "Number of coin combinations to make target amount is " << res << endl; + cout << "Number of coin combinations that make the target amount: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp b/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp index 7911d8dfd..20b353691 100644 --- a/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp @@ -6,73 +6,73 @@ #include "../utils/common.hpp" -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ int editDistanceDFS(string s, string t, int i, int j) { - // If both s and t are empty, return 0 + // If both prefixes s[0:i] and t[0:j] are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the prefix s[0:i] is empty, return j, the length of t[0:j] if (i == 0) return j; - // If t is empty, return length of s + // If the prefix t[0:j] is empty, return i, the length of s[0:i] if (j == 0) return i; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFS(s, t, i, j - 1); int del = editDistanceDFS(s, t, i - 1, j); int replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits return min(min(insert, del), replace) + 1; } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ int editDistanceDFSMem(string s, string t, vector> &mem, int i, int j) { - // If both s and t are empty, return 0 + // If both prefixes s[0:i] and t[0:j] are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the prefix s[0:i] is empty, return j, the length of t[0:j] if (i == 0) return j; - // If t is empty, return length of s + // If the prefix t[0:j] is empty, return i, the length of s[0:i] if (j == 0) return i; - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) return mem[i][j]; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFSMem(s, t, mem, i, j - 1); int del = editDistanceDFSMem(s, t, mem, i - 1, j); int replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = min(min(insert, del), replace) + 1; return mem[i][j]; } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ int editDistanceDP(string s, string t) { int n = s.length(), m = t.length(); vector> dp(n + 1, vector(m + 1, 0)); - // State transition: first row and first column + // State transitions: first row and first column for (int i = 1; i <= n; i++) { dp[i][0] = i; } for (int j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } } @@ -80,36 +80,36 @@ int editDistanceDP(string s, string t) { return dp[n][m]; } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ int editDistanceDPComp(string s, string t) { int n = s.length(), m = t.length(); vector dp(m + 1, 0); - // State transition: first row + // State transitions: first row for (int j = 1; j <= m; j++) { dp[j] = j; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i <= n; i++) { - // State transition: first column + // State transitions: first column int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } return dp[m]; } -/* Driver Code */ +/* Driver code */ int main() { string s = "bag"; string t = "pack"; @@ -117,20 +117,20 @@ int main() { // Brute-force search int res = editDistanceDFS(s, t, n, m); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; + cout << "Changing " << s << " to " << t << " requires at least " << res << " edits\n"; - // Memoization search + // Memoized search vector> mem(n + 1, vector(m + 1, -1)); res = editDistanceDFSMem(s, t, mem, n, m); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; + cout << "Changing " << s << " to " << t << " requires at least " << res << " edits\n"; // Dynamic programming res = editDistanceDP(s, t); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; + cout << "Changing " << s << " to " << t << " requires at least " << res << " edits\n"; // Space-optimized dynamic programming res = editDistanceDPComp(s, t); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; + cout << "Changing " << s << " to " << t << " requires at least " << res << " edits\n"; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp b/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp index 3531b0323..ded27316c 100644 --- a/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp @@ -4,58 +4,58 @@ using namespace std; -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ int knapsackDFS(vector &wgt, vector &val, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFS(wgt, val, i - 1, c); int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices return max(no, yes); } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ int knapsackDFSMem(vector &wgt, vector &val, vector> &mem, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] != -1) { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFSMem(wgt, val, mem, i - 1, c); int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = max(no, yes); return mem[i][c]; } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ int knapsackDP(vector &wgt, vector &val, int cap) { int n = wgt.size(); - // Initialize dp table + // Initialize the dp table vector> dp(n + 1, vector(cap + 1, 0)); - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); } } @@ -63,17 +63,17 @@ int knapsackDP(vector &wgt, vector &val, int cap) { return dp[n][cap]; } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ int knapsackDPComp(vector &wgt, vector &val, int cap) { int n = wgt.size(); - // Initialize dp table + // Initialize the dp table vector dp(cap + 1, 0); - // State transition + // State transitions for (int i = 1; i <= n; i++) { - // Traverse in reverse order + // Traverse in decreasing order for (int c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -81,7 +81,7 @@ int knapsackDPComp(vector &wgt, vector &val, int cap) { return dp[cap]; } -/* Driver Code */ +/* Driver code */ int main() { vector wgt = {10, 20, 30, 40, 50}; vector val = {50, 120, 150, 210, 240}; @@ -90,20 +90,20 @@ int main() { // Brute-force search int res = knapsackDFS(wgt, val, n, cap); - cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; + cout << "Maximum total item value within the knapsack capacity: " << res << endl; - // Memoization search + // Memoized search vector> mem(n + 1, vector(cap + 1, -1)); res = knapsackDFSMem(wgt, val, mem, n, cap); - cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; + cout << "Maximum total item value within the knapsack capacity: " << res << endl; // Dynamic programming res = knapsackDP(wgt, val, cap); - cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; + cout << "Maximum total item value within the knapsack capacity: " << res << endl; // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap); - cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; + cout << "Maximum total item value within the knapsack capacity: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp b/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp index dc9a83b30..edeb3ff77 100644 --- a/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp @@ -6,24 +6,24 @@ #include "../utils/common.hpp" -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ int minCostClimbingStairsDP(vector &cost) { int n = cost.size() - 1; if (n == 1 || n == 2) return cost[n]; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions vector dp(n + 1); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; } return dp[n]; } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ int minCostClimbingStairsDPComp(vector &cost) { int n = cost.size() - 1; if (n == 1 || n == 2) @@ -37,17 +37,17 @@ int minCostClimbingStairsDPComp(vector &cost) { return b; } -/* Driver Code */ +/* Driver code */ int main() { vector cost = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1}; - cout << "Input stair cost list is "; + cout << "Input stair cost list: "; printVector(cost); int res = minCostClimbingStairsDP(cost); - cout << "Minimum cost to climb stairs is " << res << endl; + cout << "Minimum cost to climb the stairs: " << res << endl; res = minCostClimbingStairsDPComp(cost); - cout << "Minimum cost to climb stairs is " << res << endl; + cout << "Minimum cost to climb the stairs: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp b/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp index b98bcccd9..ec2b6de47 100644 --- a/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp @@ -6,60 +6,60 @@ #include "../utils/common.hpp" -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ int minPathSumDFS(vector> &grid, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return INT_MAX; } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) int up = minPathSumDFS(grid, i - 1, j); int left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ int minPathSumDFSMem(vector> &grid, vector> &mem, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return INT_MAX; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above int up = minPathSumDFSMem(grid, mem, i - 1, j); int left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; return mem[i][j]; } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ int minPathSumDP(vector> &grid) { int n = grid.size(), m = grid[0].size(); - // Initialize dp table + // Initialize the dp table vector> dp(n, vector(m)); dp[0][0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (int j = 1; j < m; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for (int i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -68,21 +68,21 @@ int minPathSumDP(vector> &grid) { return dp[n - 1][m - 1]; } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ int minPathSumDPComp(vector> &grid) { int n = grid.size(), m = grid[0].size(); - // Initialize dp table + // Initialize the dp table vector dp(m); - // State transition: first row + // State transitions: first row dp[0] = grid[0][0]; for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i < n; i++) { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j < m; j++) { dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -90,27 +90,27 @@ int minPathSumDPComp(vector> &grid) { return dp[m - 1]; } -/* Driver Code */ +/* Driver code */ int main() { vector> grid = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}}; int n = grid.size(), m = grid[0].size(); // Brute-force search int res = minPathSumDFS(grid, n - 1, m - 1); - cout << "Minimum path sum from top-left to bottom-right is " << res << endl; + cout << "Minimum path sum from the top-left to the bottom-right corner: " << res << endl; - // Memoization search + // Memoized search vector> mem(n, vector(m, -1)); res = minPathSumDFSMem(grid, mem, n - 1, m - 1); - cout << "Minimum path sum from top-left to bottom-right is " << res << endl; + cout << "Minimum path sum from the top-left to the bottom-right corner: " << res << endl; // Dynamic programming res = minPathSumDP(grid); - cout << "Minimum path sum from top-left to bottom-right is " << res << endl; + cout << "Minimum path sum from the top-left to the bottom-right corner: " << res << endl; // Space-optimized dynamic programming res = minPathSumDPComp(grid); - cout << "Minimum path sum from top-left to bottom-right is " << res << endl; + cout << "Minimum path sum from the top-left to the bottom-right corner: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp b/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp index 5410185b8..ebc7f75a6 100644 --- a/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp @@ -6,19 +6,19 @@ #include "../utils/common.hpp" -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ int unboundedKnapsackDP(vector &wgt, vector &val, int cap) { int n = wgt.size(); - // Initialize dp table + // Initialize the dp table vector> dp(n + 1, vector(cap + 1, 0)); - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); } } @@ -26,19 +26,19 @@ int unboundedKnapsackDP(vector &wgt, vector &val, int cap) { return dp[n][cap]; } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ int unboundedKnapsackDPComp(vector &wgt, vector &val, int cap) { int n = wgt.size(); - // Initialize dp table + // Initialize the dp table vector dp(cap + 1, 0); - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -54,11 +54,11 @@ int main() { // Dynamic programming int res = unboundedKnapsackDP(wgt, val, cap); - cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; + cout << "Maximum total item value within the knapsack capacity: " << res << endl; // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, cap); - cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; + cout << "Maximum total item value within the knapsack capacity: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp index 810bdb146..7cf33f9b8 100644 --- a/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp +++ b/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp @@ -6,13 +6,13 @@ #include "../utils/common.hpp" -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ class GraphAdjList { public: - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors unordered_map> adjList; - /* Remove specified node from vector */ + /* Remove the specified vertex from the vector */ void remove(vector &vec, Vertex *vet) { for (int i = 0; i < vec.size(); i++) { if (vec[i] == vet) { @@ -37,45 +37,45 @@ class GraphAdjList { return adjList.size(); } - /* Add edge */ + /* Add an edge */ void addEdge(Vertex *vet1, Vertex *vet2) { if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) - throw invalid_argument("Vertex does not exist"); + throw invalid_argument("Invalid vertex"); // Add edge vet1 - vet2 adjList[vet1].push_back(vet2); adjList[vet2].push_back(vet1); } - /* Remove edge */ + /* Remove an edge */ void removeEdge(Vertex *vet1, Vertex *vet2) { if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) - throw invalid_argument("Vertex does not exist"); + throw invalid_argument("Invalid vertex"); // Remove edge vet1 - vet2 remove(adjList[vet1], vet2); remove(adjList[vet2], vet1); } - /* Add vertex */ + /* Add a vertex */ void addVertex(Vertex *vet) { if (adjList.count(vet)) return; - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list adjList[vet] = vector(); } - /* Remove vertex */ + /* Remove a vertex */ void removeVertex(Vertex *vet) { if (!adjList.count(vet)) - throw invalid_argument("Vertex does not exist"); - // Remove the linked list corresponding to vertex vet in the adjacency list + throw invalid_argument("Invalid vertex"); + // Remove the list corresponding to vertex vet from the adjacency list adjList.erase(vet); - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for (auto &adj : adjList) { remove(adj.second, vet); } } - /* Print adjacency list */ + /* Print the adjacency list */ void print() { cout << "Adjacency list =" << endl; for (auto &adj : adjList) { @@ -87,4 +87,4 @@ class GraphAdjList { } }; -// See graph_adjacency_list_test.cpp for test cases +// See graph_adjacency_list_test.cpp for the test example diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp index e95b724ba..392a2d532 100644 --- a/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp +++ b/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp @@ -6,38 +6,38 @@ #include "./graph_adjacency_list.cpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Add edge */ + /* Initialize the undirected graph */ vector v = valsToVets(vector{1, 3, 2, 5, 4}); vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}}; GraphAdjList graph(edges); - cout << "\nAfter initialization, graph is" << endl; + cout << "\nGraph after initialization:" << endl; graph.print(); - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.addEdge(v[0], v[2]); - cout << "\nAfter adding edge 1-2, graph is" << endl; + cout << "\nGraph after adding edge 1-2:" << endl; graph.print(); - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.removeEdge(v[0], v[1]); - cout << "\nAfter removing edge 1-3, graph is" << endl; + cout << "\nGraph after removing edge 1-3:" << endl; graph.print(); - /* Add vertex */ + /* Add a vertex */ Vertex *v5 = new Vertex(6); graph.addVertex(v5); - cout << "\nAfter adding vertex 6, graph is" << endl; + cout << "\nGraph after adding vertex 6:" << endl; graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.removeVertex(v[1]); - cout << "\nAfter removing vertex 3, graph is" << endl; + cout << "\nGraph after removing vertex 3:" << endl; graph.print(); // Free memory diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp index f21c60a42..f9ec31a0a 100644 --- a/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp +++ b/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp @@ -6,20 +6,20 @@ #include "../utils/common.hpp" -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat { - vector vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" - vector> adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + vector vertices; // Vertex list: elements are vertex values, and indices are vertex indices + vector> adjMat; // Adjacency matrix: row and column indices correspond to vertex indices public: /* Constructor */ GraphAdjMat(const vector &vertices, const vector> &edges) { - // Add vertex + // Add a vertex for (int val : vertices) { addVertex(val); } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for (const vector &edge : edges) { addEdge(edge[0], edge[1]); } @@ -30,10 +30,10 @@ class GraphAdjMat { return vertices.size(); } - /* Add vertex */ + /* Add a vertex */ void addVertex(int val) { int n = size(); - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list vertices.push_back(val); // Add a row to the adjacency matrix adjMat.emplace_back(vector(n, 0)); @@ -43,45 +43,45 @@ class GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ void removeVertex(int index) { if (index >= size()) { - throw out_of_range("Vertex does not exist"); + throw out_of_range("Invalid vertex"); } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list vertices.erase(vertices.begin() + index); - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix adjMat.erase(adjMat.begin() + index); - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for (vector &row : adjMat) { row.erase(row.begin() + index); } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices void addEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { - throw out_of_range("Vertex does not exist"); + throw out_of_range("Invalid vertex"); } - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal adjMat[i][j] = 1; adjMat[j][i] = 1; } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices void removeEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { - throw out_of_range("Vertex does not exist"); + throw out_of_range("Invalid vertex"); } adjMat[i][j] = 0; adjMat[j][i] = 0; } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ void print() { cout << "Vertex list = "; printVector(vertices); @@ -90,37 +90,37 @@ class GraphAdjMat { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices vector vertices = {1, 3, 2, 5, 4}; vector> edges = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}}; GraphAdjMat graph(vertices, edges); - cout << "\nAfter initialization, graph is" << endl; + cout << "\nGraph after initialization:" << endl; graph.print(); - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(0, 2); - cout << "\nAfter adding edge 1-2, graph is" << endl; + cout << "\nGraph after adding edge 1-2:" << endl; graph.print(); - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(0, 1); - cout << "\nAfter removing edge 1-3, graph is" << endl; + cout << "\nGraph after removing edge 1-3:" << endl; graph.print(); - /* Add vertex */ + /* Add a vertex */ graph.addVertex(6); - cout << "\nAfter adding vertex 6, graph is" << endl; + cout << "\nGraph after adding vertex 6:" << endl; graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(1); - cout << "\nAfter removing vertex 3, graph is" << endl; + cout << "\nGraph after removing vertex 3:" << endl; graph.print(); return 0; diff --git a/en/codes/cpp/chapter_graph/graph_bfs.cpp b/en/codes/cpp/chapter_graph/graph_bfs.cpp index 573500ea4..fd2bbdd44 100644 --- a/en/codes/cpp/chapter_graph/graph_bfs.cpp +++ b/en/codes/cpp/chapter_graph/graph_bfs.cpp @@ -8,46 +8,46 @@ #include "./graph_adjacency_list.cpp" /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex vector graphBFS(GraphAdjList &graph, Vertex *startVet) { // Vertex traversal sequence vector res; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices unordered_set visited = {startVet}; - // Queue used to implement BFS + // Use a queue to implement BFS queue que; que.push(startVet); - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, continue until all reachable vertices have been visited while (!que.empty()) { Vertex *vet = que.front(); que.pop(); // Dequeue the front vertex - res.push_back(vet); // Record visited vertex - // Traverse all adjacent vertices of this vertex + res.push_back(vet); // Record the visited vertex + // Traverse all neighbors of this vertex for (auto adjVet : graph.adjList[vet]) { if (visited.count(adjVet)) - continue; // Skip vertices that have been visited - que.push(adjVet); // Only enqueue unvisited vertices + continue; // Skip vertices that have already been visited + que.push(adjVet); // Enqueue only unvisited vertices visited.emplace(adjVet); // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res; } -/* Driver Code */ +/* Driver code */ int main() { - /* Add edge */ + /* Initialize the undirected graph */ vector v = valsToVets({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]}, {v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}}; GraphAdjList graph(edges); - cout << "\nAfter initialization, graph is\\n"; + cout << "\nGraph after initialization:\\n"; graph.print(); /* Breadth-first traversal */ vector res = graphBFS(graph, v[0]); - cout << "\nBreadth-first traversal (BFS) vertex sequence is" << endl; + cout << "\nVertex sequence from breadth-first traversal (BFS):" << endl; printVector(vetsToVals(res)); // Free memory diff --git a/en/codes/cpp/chapter_graph/graph_dfs.cpp b/en/codes/cpp/chapter_graph/graph_dfs.cpp index 050e8b0a8..d5fdb75d9 100644 --- a/en/codes/cpp/chapter_graph/graph_dfs.cpp +++ b/en/codes/cpp/chapter_graph/graph_dfs.cpp @@ -9,41 +9,41 @@ /* Depth-first traversal helper function */ void dfs(GraphAdjList &graph, unordered_set &visited, vector &res, Vertex *vet) { - res.push_back(vet); // Record visited vertex + res.push_back(vet); // Record the visited vertex visited.emplace(vet); // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for (Vertex *adjVet : graph.adjList[vet]) { if (visited.count(adjVet)) - continue; // Skip vertices that have been visited - // Recursively visit adjacent vertices + continue; // Skip vertices that have already been visited + // Recursively visit the neighboring vertex dfs(graph, visited, res, adjVet); } } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex vector graphDFS(GraphAdjList &graph, Vertex *startVet) { // Vertex traversal sequence vector res; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices unordered_set visited; dfs(graph, visited, res, startVet); return res; } -/* Driver Code */ +/* Driver code */ int main() { - /* Add edge */ + /* Initialize the undirected graph */ vector v = valsToVets(vector{0, 1, 2, 3, 4, 5, 6}); vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}}; GraphAdjList graph(edges); - cout << "\nAfter initialization, graph is" << endl; + cout << "\nGraph after initialization:" << endl; graph.print(); /* Depth-first traversal */ vector res = graphDFS(graph, v[0]); - cout << "\nDepth-first traversal (DFS) vertex sequence is" << endl; + cout << "\nVertex sequence from depth-first traversal (DFS):" << endl; printVector(vetsToVals(res)); // Free memory diff --git a/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp b/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp index 5e9a9328c..825ef6aea 100644 --- a/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp +++ b/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ int coinChangeGreedy(vector &coins, int amt) { - // Assume coins list is sorted + // Assume the coins list is sorted int i = coins.size() - 1; int count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (amt > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin that does not exceed the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -21,40 +21,40 @@ int coinChangeGreedy(vector &coins, int amt) { amt -= coins[i]; count++; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt == 0 ? count : -1; } -/* Driver Code */ +/* Driver code */ int main() { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution vector coins = {1, 5, 10, 20, 50, 100}; int amt = 186; int res = coinChangeGreedy(coins, amt); cout << "\ncoins = "; printVector(coins); cout << "amt = " << amt << endl; - cout << "Minimum number of coins needed to make " << amt << " is " << res << endl; + cout << "For amount " << amt << " , the greedy coin count is " << res << endl; - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = {1, 20, 50}; amt = 60; res = coinChangeGreedy(coins, amt); cout << "\ncoins = "; printVector(coins); cout << "amt = " << amt << endl; - cout << "Minimum number of coins needed to make " << amt << " is " << res << endl; - cout << "Actually the minimum number needed is 3, i.e., 20 + 20 + 20" << endl; + cout << "For amount " << amt << " , the greedy coin count is " << res << endl; + cout << "The actual minimum is 3 coins: 20 + 20 + 20" << endl; - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = {1, 49, 50}; amt = 98; res = coinChangeGreedy(coins, amt); cout << "\ncoins = "; printVector(coins); cout << "amt = " << amt << endl; - cout << "Minimum number of coins needed to make " << amt << " is " << res << endl; - cout << "Actually the minimum number needed is 2, i.e., 49 + 49" << endl; + cout << "For amount " << amt << " , the greedy coin count is " << res << endl; + cout << "The actual minimum is 2 coins: 49 + 49" << endl; return 0; } diff --git a/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp b/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp index fedb8bddd..95c107413 100644 --- a/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp +++ b/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp @@ -16,33 +16,33 @@ class Item { } }; -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ double fractionalKnapsack(vector &wgt, vector &val, int cap) { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value vector items; for (int i = 0; i < wgt.size(); i++) { items.push_back(Item(wgt[i], val[i])); } - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order sort(items.begin(), items.end(), [](Item &a, Item &b) { return (double)a.v / a.w > (double)b.v / b.w; }); - // Loop for greedy selection + // Repeatedly make greedy choices double res = 0; for (auto &item : items) { if (item.w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v; cap -= item.w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += (double)item.v / item.w * cap; - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break; } } return res; } -/* Driver Code */ +/* Driver code */ int main() { vector wgt = {10, 20, 30, 40, 50}; vector val = {50, 120, 150, 210, 240}; @@ -50,7 +50,7 @@ int main() { // Greedy algorithm double res = fractionalKnapsack(wgt, val, cap); - cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; + cout << "Maximum total item value within the knapsack capacity: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_greedy/max_capacity.cpp b/en/codes/cpp/chapter_greedy/max_capacity.cpp index 4f112c001..21f63c030 100644 --- a/en/codes/cpp/chapter_greedy/max_capacity.cpp +++ b/en/codes/cpp/chapter_greedy/max_capacity.cpp @@ -6,18 +6,18 @@ #include "../utils/common.hpp" -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ int maxCapacity(vector &ht) { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array int i = 0, j = ht.size() - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 int res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity int cap = min(ht[i], ht[j]) * (j - i); res = max(res, cap); - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i++; } else { @@ -27,13 +27,13 @@ int maxCapacity(vector &ht) { return res; } -/* Driver Code */ +/* Driver code */ int main() { vector ht = {3, 8, 5, 2, 7, 7, 3, 4}; // Greedy algorithm int res = maxCapacity(ht); - cout << "Maximum capacity is " << res << endl; + cout << "Maximum capacity: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_greedy/max_product_cutting.cpp b/en/codes/cpp/chapter_greedy/max_product_cutting.cpp index dfe816dbf..80213ac45 100644 --- a/en/codes/cpp/chapter_greedy/max_product_cutting.cpp +++ b/en/codes/cpp/chapter_greedy/max_product_cutting.cpp @@ -6,34 +6,34 @@ #include "../utils/common.hpp" -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ int maxProductCutting(int n) { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder int a = n / 3; int b = n % 3; if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return (int)pow(3, a - 1) * 2 * 2; } if (b == 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return (int)pow(3, a) * 2; } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return (int)pow(3, a); } -/* Driver Code */ +/* Driver code */ int main() { int n = 58; // Greedy algorithm int res = maxProductCutting(n); - cout << "Maximum cutting product is" << res << endl; + cout << "Maximum product after cutting: " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_hashing/array_hash_map.cpp b/en/codes/cpp/chapter_hashing/array_hash_map.cpp index 7d91ae332..fc1d30899 100644 --- a/en/codes/cpp/chapter_hashing/array_hash_map.cpp +++ b/en/codes/cpp/chapter_hashing/array_hash_map.cpp @@ -17,14 +17,14 @@ struct Pair { } }; -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { private: vector buckets; public: ArrayHashMap() { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets buckets = vector(100); } @@ -42,7 +42,7 @@ class ArrayHashMap { return index; } - /* Query operation */ + /* Lookup operation */ string get(int key) { int index = hashFunc(key); Pair *pair = buckets[index]; @@ -51,7 +51,7 @@ class ArrayHashMap { return pair->val; } - /* Add operation */ + /* Insert operation */ void put(int key, string val) { Pair *pair = new Pair(key, val); int index = hashFunc(key); @@ -61,7 +61,7 @@ class ArrayHashMap { /* Remove operation */ void remove(int key) { int index = hashFunc(key); - // Free memory and set to nullptr + // Free the memory and set the pointer to nullptr delete buckets[index]; buckets[index] = nullptr; } @@ -99,7 +99,7 @@ class ArrayHashMap { return valueSet; } - /* Print hash table */ + /* Print the hash table */ void print() { for (Pair *kv : pairSet()) { cout << kv->key << " -> " << kv->val << endl; @@ -107,4 +107,4 @@ class ArrayHashMap { } }; -// See array_hash_map_test.cpp for test cases +// See array_hash_map_test.cpp for the test example diff --git a/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp b/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp index 0afe1d515..00e5c10ca 100644 --- a/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp +++ b/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp @@ -6,44 +6,44 @@ #include "./array_hash_map.cpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize hash table */ + /* Initialize the hash table */ ArrayHashMap map = ArrayHashMap(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + cout << "\nHash table after insertion:\nKey -> Value" << endl; map.print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value string name = map.get(15937); - cout << "\nInput student ID 15937, query name " << name << endl; + cout << "\nLook up student ID 15937 to obtain the name " << name << endl; /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583); - cout << "\nAfter removing 10583, hash table is\nKey -> Value" << endl; + cout << "\nHash table after removing 10583:\nKey -> Value" << endl; map.print(); - /* Traverse hash table */ - cout << "\nTraverse key-value pairs Key->Value" << endl; + /* Traverse the hash table */ + cout << "\nTraverse key-value pairs: Key->Value" << endl; for (auto kv : map.pairSet()) { cout << kv->key << " -> " << kv->val << endl; } - cout << "\nTraverse keys only Key" << endl; + cout << "\nTraverse keys only: Key" << endl; for (auto key : map.keySet()) { cout << key << endl; } - cout << "\nTraverse values only Value" << endl; + cout << "\nTraverse values only: Value" << endl; for (auto val : map.valueSet()) { cout << val << endl; } diff --git a/en/codes/cpp/chapter_hashing/built_in_hash.cpp b/en/codes/cpp/chapter_hashing/built_in_hash.cpp index 0f09b1a44..08a605ecf 100644 --- a/en/codes/cpp/chapter_hashing/built_in_hash.cpp +++ b/en/codes/cpp/chapter_hashing/built_in_hash.cpp @@ -6,24 +6,24 @@ #include "../utils/common.hpp" -/* Driver Code */ +/* Driver code */ int main() { int num = 3; size_t hashNum = hash()(num); - cout << "Hash value of integer " << num << " is " << hashNum << "\n"; + cout << "Integer " << num << " has hash value " << hashNum << "\n"; bool bol = true; size_t hashBol = hash()(bol); - cout << "Hash value of boolean " << bol << " is " << hashBol << "\n"; + cout << "Boolean " << bol << " has hash value " << hashBol << "\n"; double dec = 3.14159; size_t hashDec = hash()(dec); - cout << "Hash value of decimal " << dec << " is " << hashDec << "\n"; + cout << "Floating-point number " << dec << " has hash value " << hashDec << "\n"; - string str = "Hello Algo"; + string str = "Hello 算法"; size_t hashStr = hash()(str); - cout << "Hash value of string " << str << " is " << hashStr << "\n"; + cout << "String " << str << " has hash value " << hashStr << "\n"; - // In C++, built-in std::hash() only provides hash calculation for basic data types - // Hash calculation for arrays and objects needs to be implemented manually + // C++ std::hash provides hashing for fundamental types and selected standard-library types + // For arrays and unsupported object types, supply your own hash implementation } diff --git a/en/codes/cpp/chapter_hashing/hash_map.cpp b/en/codes/cpp/chapter_hashing/hash_map.cpp index e3a8accea..9327f55a6 100644 --- a/en/codes/cpp/chapter_hashing/hash_map.cpp +++ b/en/codes/cpp/chapter_hashing/hash_map.cpp @@ -6,38 +6,38 @@ #include "../utils/common.hpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize hash table */ + /* Initialize the hash table */ unordered_map map; - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map[12836] = "Xiao Ha"; - map[15937] = "Xiao Luo"; - map[16750] = "Xiao Suan"; - map[13276] = "Xiao Fa"; - map[10583] = "Xiao Ya"; - cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map[12836] = "小哈"; + map[15937] = "小啰"; + map[16750] = "小算"; + map[13276] = "小法"; + map[10583] = "小鸭"; + cout << "\nHash table after insertion:\nKey -> Value" << endl; printHashMap(map); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value string name = map[15937]; - cout << "\nInput student ID 15937, query name " << name << endl; + cout << "\nLook up student ID 15937 to obtain the name " << name << endl; /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.erase(10583); - cout << "\nAfter removing 10583, hash table is\nKey -> Value" << endl; + cout << "\nHash table after removing 10583:\nKey -> Value" << endl; printHashMap(map); - /* Traverse hash table */ - cout << "\nTraverse key-value pairs Key->Value" << endl; + /* Traverse the hash table */ + cout << "\nTraverse key-value pairs: Key->Value" << endl; for (auto kv : map) { cout << kv.first << " -> " << kv.second << endl; } - cout << "\nTraverse Key->Value using iterator" << endl; + cout << "\nTraverse Key->Value pairs using an iterator" << endl; for (auto iter = map.begin(); iter != map.end(); iter++) { cout << iter->first << "->" << iter->second << endl; } diff --git a/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp b/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp index 19deb4290..3c3054977 100644 --- a/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp +++ b/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp @@ -11,8 +11,8 @@ class HashMapChaining { private: int size; // Number of key-value pairs int capacity; // Hash table capacity - double loadThres; // Load factor threshold for triggering expansion - int extendRatio; // Expansion multiplier + double loadThres; // Load factor threshold that triggers capacity growth + int extendRatio; // Capacity growth factor vector> buckets; // Bucket array public: @@ -41,34 +41,34 @@ class HashMapChaining { return (double)size / (double)capacity; } - /* Query operation */ + /* Lookup operation */ string get(int key) { int index = hashFunc(key); - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for (Pair *pair : buckets[index]) { if (pair->key == key) { return pair->val; } } - // Return empty string if key not found + // Return an empty string if key is not found return ""; } - /* Add operation */ + /* Insert operation */ void put(int key, string val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > loadThres) { extend(); } int index = hashFunc(key); - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for (Pair *pair : buckets[index]) { if (pair->key == key) { pair->val = val; return; } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end buckets[index].push_back(new Pair(key, val)); size++; } @@ -77,11 +77,11 @@ class HashMapChaining { void remove(int key) { int index = hashFunc(key); auto &bucket = buckets[index]; - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (int i = 0; i < bucket.size(); i++) { if (bucket[i]->key == key) { Pair *tmp = bucket[i]; - bucket.erase(bucket.begin() + i); // Remove key-value pair from it + bucket.erase(bucket.begin() + i); // Remove the key-value pair from the list delete tmp; // Free memory size--; return; @@ -89,16 +89,16 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ void extend() { - // Temporarily store the original hash table + // Keep a copy of the original bucket array vector> bucketsTmp = buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio; buckets.clear(); buckets.resize(capacity); size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (auto &bucket : bucketsTmp) { for (Pair *pair : bucket) { put(pair->key, pair->val); @@ -108,7 +108,7 @@ class HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ void print() { for (auto &bucket : buckets) { cout << "["; @@ -120,30 +120,30 @@ class HashMapChaining { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize hash table */ + /* Initialize the hash table */ HashMapChaining map = HashMapChaining(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + cout << "\nHash table after insertion:\nKey -> Value" << endl; map.print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value string name = map.get(13276); - cout << "\nInput student ID 13276, query name " << name << endl; + cout << "\nLook up student ID 13276 to obtain the name " << name << endl; /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(12836); - cout << "\nAfter removing 12836, hash table is\nKey -> Value" << endl; + cout << "\nHash table after removing 12836:\nKey -> Value" << endl; map.print(); return 0; diff --git a/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp b/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp index cd0fbfc47..0ed325ea4 100644 --- a/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp +++ b/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp @@ -11,10 +11,10 @@ class HashMapOpenAddressing { private: int size; // Number of key-value pairs int capacity = 4; // Hash table capacity - const double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion - const int extendRatio = 2; // Expansion multiplier + const double loadThres = 2.0 / 3.0; // Load factor threshold that triggers capacity growth + const int extendRatio = 2; // Capacity growth factor vector buckets; // Bucket array - Pair *TOMBSTONE = new Pair(-1, "-1"); // Removal marker + Pair *TOMBSTONE = new Pair(-1, "-1"); // Tombstone marker public: /* Constructor */ @@ -41,68 +41,68 @@ class HashMapOpenAddressing { return (double)size / capacity; } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ int findBucket(int key) { int index = hashFunc(key); int firstTombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (buckets[index] != nullptr) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (buckets[index]->key == key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; - return firstTombstone; // Return the moved bucket index + return firstTombstone; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone == -1 ? index : firstTombstone; } - /* Query operation */ + /* Lookup operation */ string get(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { return buckets[index]->val; } - // Return empty string if key-value pair does not exist + // Return an empty string if the key-value pair does not exist return ""; } - /* Add operation */ + /* Insert operation */ void put(int key, string val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > loadThres) { extend(); } - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { buckets[index]->val = val; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it buckets[index] = new Pair(key, val); size++; } /* Remove operation */ void remove(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { delete buckets[index]; buckets[index] = TOMBSTONE; @@ -110,15 +110,15 @@ class HashMapOpenAddressing { } } - /* Expand hash table */ + /* Grow the hash table capacity */ void extend() { - // Temporarily store the original hash table + // Keep a copy of the original bucket array vector bucketsTmp = buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio; buckets = vector(capacity, nullptr); size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (Pair *pair : bucketsTmp) { if (pair != nullptr && pair != TOMBSTONE) { put(pair->key, pair->val); @@ -127,7 +127,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ void print() { for (Pair *pair : buckets) { if (pair == nullptr) { @@ -141,30 +141,30 @@ class HashMapOpenAddressing { } }; -/* Driver Code */ +/* Driver code */ int main() { - // Initialize hash table + // Initialize the hash table HashMapOpenAddressing hashmap; - // Add operation - // Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Xiao Ha"); - hashmap.put(15937, "Xiao Luo"); - hashmap.put(16750, "Xiao Suan"); - hashmap.put(13276, "Xiao Fa"); - hashmap.put(10583, "Xiao Ya"); - cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; + // Insert operation + // Insert the key-value pair (key, val) into the hash table + hashmap.put(12836, "小哈"); + hashmap.put(15937, "小啰"); + hashmap.put(16750, "小算"); + hashmap.put(13276, "小法"); + hashmap.put(10583, "小鸭"); + cout << "\nHash table after insertion:\nKey -> Value" << endl; hashmap.print(); - // Query operation - // Input key into hash table to get value val + // Lookup operation + // Look up key in the hash table to obtain val string name = hashmap.get(13276); - cout << "\nInput student ID 13276, query name " << name << endl; + cout << "\nLook up student ID 13276 to obtain the name " << name << endl; // Remove operation - // Remove key-value pair (key, val) from hash table + // Remove the key-value pair (key, val) from the hash table hashmap.remove(16750); - cout << "\nAfter removing 16750, hash table is\nKey -> Value" << endl; + cout << "\nHash table after removing 16750:\nKey -> Value" << endl; hashmap.print(); return 0; diff --git a/en/codes/cpp/chapter_hashing/simple_hash.cpp b/en/codes/cpp/chapter_hashing/simple_hash.cpp index f52c70317..af7a259f5 100644 --- a/en/codes/cpp/chapter_hashing/simple_hash.cpp +++ b/en/codes/cpp/chapter_hashing/simple_hash.cpp @@ -36,7 +36,7 @@ int xorHash(string key) { return hash & MODULUS; } -/* Rotational hash */ +/* Rotating hash */ int rotHash(string key) { long long hash = 0; const int MODULUS = 1000000007; @@ -46,21 +46,21 @@ int rotHash(string key) { return (int)hash; } -/* Driver Code */ +/* Driver code */ int main() { - string key = "Hello Algo"; + string key = "Hello 算法"; int hash = addHash(key); - cout << "Additive hash value is " << hash << endl; + cout << "Additive hash value: " << hash << endl; hash = mulHash(key); - cout << "Multiplicative hash value is " << hash << endl; + cout << "Multiplicative hash value: " << hash << endl; hash = xorHash(key); - cout << "XOR hash value is " << hash << endl; + cout << "XOR hash value: " << hash << endl; hash = rotHash(key); - cout << "Rotational hash value is " << hash << endl; + cout << "Rotation hash value: " << hash << endl; return 0; } diff --git a/en/codes/cpp/chapter_heap/heap.cpp b/en/codes/cpp/chapter_heap/heap.cpp index c33029882..9789eb385 100644 --- a/en/codes/cpp/chapter_heap/heap.cpp +++ b/en/codes/cpp/chapter_heap/heap.cpp @@ -7,59 +7,59 @@ #include "../utils/common.hpp" void testPush(priority_queue &heap, int val) { - heap.push(val); // Element enters heap - cout << "\nAfter element " << val << " pushes to heap" << endl; + heap.push(val); // Push an element onto the heap + cout << "\nAfter pushing element " << val << " onto the heap:" << endl; printHeap(heap); } void testPop(priority_queue &heap) { int val = heap.top(); heap.pop(); - cout << "\nAfter heap top element " << val << " pops from heap" << endl; + cout << "\nAfter popping top element " << val << " from the heap:" << endl; printHeap(heap); } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize heap */ - // Python's heapq module implements min heap by default + /* Initialize the heap */ + // Initialize a min heap // priority_queue, greater> minHeap; - // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + // Initialize a max heap priority_queue, less> maxHeap; - cout << "\nThe following test cases are for max heap" << endl; + cout << "\nThe following example uses a max heap" << endl; - /* Element enters heap */ + /* Push an element onto the heap */ testPush(maxHeap, 1); testPush(maxHeap, 3); testPush(maxHeap, 2); testPush(maxHeap, 5); testPush(maxHeap, 4); - /* Check if heap is empty */ + /* Get the top element of the heap */ int peek = maxHeap.top(); - cout << "\nHeap top element is " << peek << endl; + cout << "\nTop element of the heap: " << peek << endl; - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); - /* Get heap size */ + /* Get the heap size */ int size = maxHeap.size(); - cout << "\nHeap size is " << size << endl; + cout << "\nNumber of elements in the heap: " << size << endl; - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty = maxHeap.empty(); - cout << "\nIs heap empty " << isEmpty << endl; + cout << "\nIs the heap empty: " << isEmpty << endl; - /* Input list and build heap */ - // Time complexity is O(n), not O(nlogn) + /* Build a heap from an input list */ + // The time complexity is O(n), not O(nlogn) vector input{1, 3, 2, 5, 4}; priority_queue, greater> minHeap(input.begin(), input.end()); - cout << "After input list and building min heap" << endl; + cout << "After building a min heap from the input list:" << endl; printHeap(minHeap); return 0; diff --git a/en/codes/cpp/chapter_heap/my_heap.cpp b/en/codes/cpp/chapter_heap/my_heap.cpp index 8a0649fb2..e269c57ff 100644 --- a/en/codes/cpp/chapter_heap/my_heap.cpp +++ b/en/codes/cpp/chapter_heap/my_heap.cpp @@ -9,147 +9,147 @@ /* Max heap */ class MaxHeap { private: - // Use dynamic array to avoid expansion issues + // Use a dynamic array so capacity growth is handled automatically vector maxHeap; - /* Get index of left child node */ + /* Get the left child index */ int left(int i) { return 2 * i + 1; } - /* Get index of right child node */ + /* Get the right child index */ int right(int i) { return 2 * i + 2; } - /* Get index of parent node */ + /* Get the parent index */ int parent(int i) { - return (i - 1) / 2; // Floor division + return (i - 1) / 2; // Integer division truncates toward zero } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ void siftUp(int i) { while (true) { - // Get parent node of node i + // Get the parent of node i int p = parent(i); - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || maxHeap[i] <= maxHeap[p]) break; - // Swap two nodes + // Swap the two nodes swap(maxHeap[i], maxHeap[p]); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ void siftDown(int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = left(i), r = right(i), ma = i; if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l; if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break; swap(maxHeap[i], maxHeap[ma]); - // Loop downwards heapification + // Continue sifting down i = ma; } } public: - /* Constructor, build heap based on input list */ + /* Constructor: build a heap from the input list */ MaxHeap(vector nums) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order maxHeap = nums; - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for (int i = parent(size() - 1); i >= 0; i--) { siftDown(i); } } - /* Get heap size */ + /* Get the heap size */ int size() { return maxHeap.size(); } - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty() { return size() == 0; } - /* Access top element */ + /* Access the top element of the heap */ int peek() { return maxHeap[0]; } - /* Element enters heap */ + /* Push an element onto the heap */ void push(int val) { - // Add node + // Add a node maxHeap.push_back(val); - // Heapify from bottom to top + // Sift up siftUp(size() - 1); } - /* Element exits heap */ + /* Pop an element from the heap */ void pop() { - // Handle empty case + // Handle an empty heap if (isEmpty()) { throw out_of_range("Heap is empty"); } - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) swap(maxHeap[0], maxHeap[size() - 1]); - // Remove node + // Remove a node maxHeap.pop_back(); - // Return top element + // Sift down siftDown(0); } - /* Driver Code*/ + /* Print the heap (binary tree)*/ void print() { - cout << "Heap array representation:"; + cout << "Array representation of the heap:"; printVector(maxHeap); - cout << "Heap tree representation:" << endl; + cout << "Tree representation of the heap:" << endl; TreeNode *root = vectorToTree(maxHeap); printTree(root); freeMemoryTree(root); } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ vector vec{9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; MaxHeap maxHeap(vec); - cout << "\nAfter inputting list and building heap" << endl; + cout << "\nAfter building a heap from the input list:" << endl; maxHeap.print(); - /* Check if heap is empty */ + /* Get the top element of the heap */ int peek = maxHeap.peek(); - cout << "\nHeap top element is " << peek << endl; + cout << "\nTop element of the heap: " << peek << endl; - /* Element enters heap */ + /* Push an element onto the heap */ int val = 7; maxHeap.push(val); - cout << "\nAfter element " << val << " pushes to heap" << endl; + cout << "\nAfter pushing element " << val << " onto the heap:" << endl; maxHeap.print(); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.peek(); maxHeap.pop(); - cout << "\nAfter heap top element " << peek << " pops from heap" << endl; + cout << "\nAfter popping top element " << peek << " from the heap:" << endl; maxHeap.print(); - /* Get heap size */ + /* Get the heap size */ int size = maxHeap.size(); - cout << "\nHeap size is " << size << endl; + cout << "\nNumber of elements in the heap: " << size << endl; - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty = maxHeap.isEmpty(); - cout << "\nIs heap empty " << isEmpty << endl; + cout << "\nIs the heap empty: " << isEmpty << endl; return 0; } diff --git a/en/codes/cpp/chapter_heap/top_k.cpp b/en/codes/cpp/chapter_heap/top_k.cpp index 1bb9a1d92..3cc352cf6 100644 --- a/en/codes/cpp/chapter_heap/top_k.cpp +++ b/en/codes/cpp/chapter_heap/top_k.cpp @@ -6,17 +6,17 @@ #include "../utils/common.hpp" -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ priority_queue, greater> topKHeap(vector &nums, int k) { - // Python's heapq module implements min heap by default + // Initialize a min heap priority_queue, greater> heap; - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for (int i = 0; i < k; i++) { heap.push(nums[i]); } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for (int i = k; i < nums.size(); i++) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if (nums[i] > heap.top()) { heap.pop(); heap.push(nums[i]); @@ -25,7 +25,7 @@ priority_queue, greater> topKHeap(vector &nums, int k return heap; } -// Driver Code +// Driver code int main() { vector nums = {1, 7, 6, 3, 2}; int k = 3; diff --git a/en/codes/cpp/chapter_searching/binary_search.cpp b/en/codes/cpp/chapter_searching/binary_search.cpp index e3de96213..4c3a66cfc 100644 --- a/en/codes/cpp/chapter_searching/binary_search.cpp +++ b/en/codes/cpp/chapter_searching/binary_search.cpp @@ -6,52 +6,52 @@ #include "../utils/common.hpp" -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ int binarySearch(vector &nums, int target) { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements int i = 0, j = nums.size() - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j] + if (nums[m] < target) // In this case, target must be in [m+1, j] if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m-1] + else if (nums[m] > target) // In this case, target must be in [i, m-1] if it exists j = m - 1; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ int binarySearchLCRO(vector &nums, int target) { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last int i = 0, j = nums.size(); - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j) + if (nums[m] < target) // In this case, target must be in [m+1, j) if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m) + else if (nums[m] > target) // In this case, target must be in [i, m) if it exists j = m; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Driver Code */ +/* Driver code */ int main() { int target = 6; vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ int index = binarySearch(nums, target); cout << "Index of target element 6 = " << index << endl; - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums, target); cout << "Index of target element 6 = " << index << endl; diff --git a/en/codes/cpp/chapter_searching/binary_search_edge.cpp b/en/codes/cpp/chapter_searching/binary_search_edge.cpp index 83dd3464d..90dff0379 100644 --- a/en/codes/cpp/chapter_searching/binary_search_edge.cpp +++ b/en/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -6,60 +6,60 @@ #include "../utils/common.hpp" -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ int binarySearchInsertion(const vector &nums, int target) { - int i = 0, j = nums.size() - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.size() - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue searching [i, m-1] for the insertion point before any equal elements } } // Return insertion point i return i; } -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ int binarySearchLeftEdge(vector &nums, int target) { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target int i = binarySearchInsertion(nums, target); - // Target not found, return -1 + // If target was not found, return -1 if (i == nums.size() || nums[i] != target) { return -1; } - // Found target, return index i + // Target was found; return index i return i; } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ int binarySearchRightEdge(vector &nums, int target) { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 int i = binarySearchInsertion(nums, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // j points to the rightmost target, while i points to the first element greater than target int j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if (j == -1 || nums[j] != target) { return -1; } - // Found target, return index j + // Target was found; return index j return j; } -/* Driver Code */ +/* Driver code */ int main() { - // Array with duplicate elements + // Array containing duplicate elements vector nums = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; cout << "\nArray nums = "; printVector(nums); - // Binary search left and right boundaries + // Find the left and right boundaries using binary search for (int target : {6, 7}) { int index = binarySearchLeftEdge(nums, target); - cout << "Index of leftmost element " << target << " is " << index << endl; + cout << "Leftmost occurrence of element " << target << " has index " << index << endl; index = binarySearchRightEdge(nums, target); - cout << "Index of rightmost element " << target << " is " << index << endl; + cout << "Rightmost occurrence of element " << target << " has index " << index << endl; } return 0; diff --git a/en/codes/cpp/chapter_searching/binary_search_insertion.cpp b/en/codes/cpp/chapter_searching/binary_search_insertion.cpp index 68ef51257..b3c2ef2d7 100644 --- a/en/codes/cpp/chapter_searching/binary_search_insertion.cpp +++ b/en/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -6,60 +6,60 @@ #include "../utils/common.hpp" -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ int binarySearchInsertionSimple(vector &nums, int target) { - int i = 0, j = nums.size() - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.size() - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - return m; // Found target, return insertion point m + return m; // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i; } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ int binarySearchInsertion(vector &nums, int target) { - int i = 0, j = nums.size() - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.size() - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue searching [i, m-1] for the insertion point before any equal elements } } // Return insertion point i return i; } -/* Driver Code */ +/* Driver code */ int main() { - // Array without duplicate elements + // Array with no duplicate elements vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; cout << "\nArray nums = "; printVector(nums); - // Binary search for insertion point + // Find an insertion point using binary search for (int target : {6, 9}) { int index = binarySearchInsertionSimple(nums, target); - cout << "Insertion point index for element " << target << " is " << index << endl; + cout << "Element " << target << " has insertion index " << index << endl; } - // Array with duplicate elements + // Array containing duplicate elements nums = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; cout << "\nArray nums = "; printVector(nums); - // Binary search for insertion point + // Find an insertion point using binary search for (int target : {2, 6, 20}) { int index = binarySearchInsertion(nums, target); - cout << "Insertion point index for element " << target << " is " << index << endl; + cout << "Element " << target << " has insertion index " << index << endl; } return 0; diff --git a/en/codes/cpp/chapter_searching/hashing_search.cpp b/en/codes/cpp/chapter_searching/hashing_search.cpp index eb1e6a451..e9398843d 100644 --- a/en/codes/cpp/chapter_searching/hashing_search.cpp +++ b/en/codes/cpp/chapter_searching/hashing_search.cpp @@ -6,48 +6,48 @@ #include "../utils/common.hpp" -/* Hash search (array) */ +/* Hash-based search (array) */ int hashingSearchArray(unordered_map map, int target) { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key if (map.find(target) == map.end()) return -1; return map[target]; } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ ListNode *hashingSearchLinkedList(unordered_map map, int target) { - // Hash table key: target node value, value: node object - // Return nullptr if key does not exist in hash table + // Hash table key: target node value; value: node object + // Return nullptr if this key is absent from the hash table if (map.find(target) == map.end()) return nullptr; return map[target]; } -/* Driver Code */ +/* Driver code */ int main() { int target = 3; - /* Hash search (array) */ + /* Hash-based search (array) */ vector nums = {1, 5, 3, 2, 4, 7, 5, 9, 10, 8}; - // Initialize hash table + // Initialize the hash table unordered_map map; for (int i = 0; i < nums.size(); i++) { - map[nums[i]] = i; // key: element, value: index + map[nums[i]] = i; // key: element; value: index } int index = hashingSearchArray(map, target); cout << "Index of target element 3 = " << index << endl; - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ ListNode *head = vecToLinkedList(nums); - // Initialize hash table + // Initialize the hash table unordered_map map1; while (head != nullptr) { - map1[head->val] = head; // key: node value, value: node + map1[head->val] = head; // key: node value; value: node head = head->next; } ListNode *node = hashingSearchLinkedList(map1, target); - cout << "Node object corresponding to target node value 3 is " << node << endl; + cout << "The node object corresponding to target value 3 is " << node << endl; return 0; } diff --git a/en/codes/cpp/chapter_searching/linear_search.cpp b/en/codes/cpp/chapter_searching/linear_search.cpp index 56cae04be..714b7bab7 100644 --- a/en/codes/cpp/chapter_searching/linear_search.cpp +++ b/en/codes/cpp/chapter_searching/linear_search.cpp @@ -8,13 +8,13 @@ /* Linear search (array) */ int linearSearchArray(vector &nums, int target) { - // Traverse array + // Traverse the array for (int i = 0; i < nums.size(); i++) { - // Found the target element, return its index + // The target element was found; return its index if (nums[i] == target) return i; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } @@ -22,28 +22,28 @@ int linearSearchArray(vector &nums, int target) { ListNode *linearSearchLinkedList(ListNode *head, int target) { // Traverse the linked list while (head != nullptr) { - // Found the target node, return it + // The target node was found; return it if (head->val == target) return head; head = head->next; } - // Target node not found, return nullptr + // The target node was not found; return nullptr return nullptr; } -/* Driver Code */ +/* Driver code */ int main() { int target = 3; - /* Perform linear search in array */ + /* Perform linear search on the array */ vector nums = {1, 5, 3, 2, 4, 7, 5, 9, 10, 8}; int index = linearSearchArray(nums, target); cout << "Index of target element 3 = " << index << endl; - /* Perform linear search in linked list */ + /* Perform linear search on the linked list */ ListNode *head = vecToLinkedList(nums); ListNode *node = linearSearchLinkedList(head, target); - cout << "Node object corresponding to target node value 3 is " << node << endl; + cout << "The node object corresponding to target value 3 is " << node << endl; return 0; } diff --git a/en/codes/cpp/chapter_searching/two_sum.cpp b/en/codes/cpp/chapter_searching/two_sum.cpp index 99b27f334..86a053462 100644 --- a/en/codes/cpp/chapter_searching/two_sum.cpp +++ b/en/codes/cpp/chapter_searching/two_sum.cpp @@ -6,10 +6,10 @@ #include "../utils/common.hpp" -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ vector twoSumBruteForce(vector &nums, int target) { int size = nums.size(); - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) @@ -19,12 +19,12 @@ vector twoSumBruteForce(vector &nums, int target) { return {}; } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ vector twoSumHashTable(vector &nums, int target) { int size = nums.size(); - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space unordered_map dic; - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (int i = 0; i < size; i++) { if (dic.find(target - nums[i]) != dic.end()) { return {dic[target - nums[i]], i}; @@ -34,20 +34,20 @@ vector twoSumHashTable(vector &nums, int target) { return {}; } -/* Driver Code */ +/* Driver code */ int main() { - // ======= Test Case ======= + // ======= Test case ======= vector nums = {2, 7, 11, 15}; int target = 13; - // ====== Driver Code ====== + // ====== Driver code ====== // Method 1 vector res = twoSumBruteForce(nums, target); - cout << "Method 1 res = "; + cout << "Method 1: res = "; printVector(res); // Method 2 res = twoSumHashTable(nums, target); - cout << "Method 2 res = "; + cout << "Method 2: res = "; printVector(res); return 0; diff --git a/en/codes/cpp/chapter_sorting/bubble_sort.cpp b/en/codes/cpp/chapter_sorting/bubble_sort.cpp index 6b27f9be3..697f4f148 100644 --- a/en/codes/cpp/chapter_sorting/bubble_sort.cpp +++ b/en/codes/cpp/chapter_sorting/bubble_sort.cpp @@ -8,13 +8,13 @@ /* Bubble sort */ void bubbleSort(vector &nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.size() - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] - // Using std::swap() function here + // Use the std::swap() function here swap(nums[j], nums[j + 1]); } } @@ -23,33 +23,33 @@ void bubbleSort(vector &nums) { /* Bubble sort (flag optimization)*/ void bubbleSortWithFlag(vector &nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.size() - 1; i > 0; i--) { - bool flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + bool flag = false; // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] - // Using std::swap() function here + // Use the std::swap() function here swap(nums[j], nums[j + 1]); - flag = true; // Record element swap + flag = true; // Record that a swap occurred } } if (!flag) - break; // No elements were swapped in this round of "bubbling", exit directly + break; // Exit immediately if this pass made no swaps } } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {4, 1, 3, 1, 5, 2}; bubbleSort(nums); - cout << "After bubble sort completes, nums = "; + cout << "After bubble sort, nums = "; printVector(nums); vector nums1 = {4, 1, 3, 1, 5, 2}; bubbleSortWithFlag(nums1); - cout << "After bubble sort completes, nums1 = "; + cout << "After bubble sort, nums1 = "; printVector(nums1); return 0; diff --git a/en/codes/cpp/chapter_sorting/bucket_sort.cpp b/en/codes/cpp/chapter_sorting/bucket_sort.cpp index aa31c98ef..4a2918e47 100644 --- a/en/codes/cpp/chapter_sorting/bucket_sort.cpp +++ b/en/codes/cpp/chapter_sorting/bucket_sort.cpp @@ -8,22 +8,22 @@ /* Bucket sort */ void bucketSort(vector &nums) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = n/2 buckets, expecting 2 elements per bucket int k = nums.size() / 2; vector> buckets(k); - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for (float num : nums) { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); use num * k to map each value to an index in [0, k-1] int i = num * k; - // Add num to bucket bucket_idx + // Add num to bucket i buckets[i].push_back(num); } // 2. Sort each bucket for (vector &bucket : buckets) { - // Use built-in sorting function, can also replace with other sorting algorithms + // Use the built-in sorting function, or replace it with another sorting algorithm sort(bucket.begin(), bucket.end()); } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results int i = 0; for (vector &bucket : buckets) { for (float num : bucket) { @@ -32,12 +32,12 @@ void bucketSort(vector &nums) { } } -/* Driver Code */ +/* Driver code */ int main() { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) vector nums = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f}; bucketSort(nums); - cout << "After bucket sort completes, nums = "; + cout << "After bucket sort, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/counting_sort.cpp b/en/codes/cpp/chapter_sorting/counting_sort.cpp index af964a3bd..49239f933 100644 --- a/en/codes/cpp/chapter_sorting/counting_sort.cpp +++ b/en/codes/cpp/chapter_sorting/counting_sort.cpp @@ -7,20 +7,20 @@ #include "../utils/common.hpp" /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects void countingSortNaive(vector &nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int num : nums) { m = max(m, num); } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num vector counter(m + 1, 0); for (int num : nums) { counter[num]++; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements int i = 0; for (int num = 0; num < m + 1; num++) { for (int j = 0; j < counter[num]; j++, i++) { @@ -30,47 +30,47 @@ void countingSortNaive(vector &nums) { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Stable implementation that can be adapted to sort objects void countingSort(vector &nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int num : nums) { m = max(m, num); } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num vector counter(m + 1, 0); for (int num : nums) { counter[num]++; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for (int i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result int n = nums.size(); vector res(n); for (int i = n - 1; i >= 0; i--) { int num = nums[i]; res[counter[num] - 1] = num; // Place num at the corresponding index - counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + counter[num]--; // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res nums = res; } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; countingSortNaive(nums); - cout << "After counting sort (cannot sort objects) completes, nums = "; + cout << "After counting sort (without support for objects), nums = "; printVector(nums); vector nums1 = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; countingSort(nums1); - cout << "After counting sort completes, nums1 = "; + cout << "After counting sort, nums1 = "; printVector(nums1); return 0; diff --git a/en/codes/cpp/chapter_sorting/heap_sort.cpp b/en/codes/cpp/chapter_sorting/heap_sort.cpp index 42eeca195..ac0e67b52 100644 --- a/en/codes/cpp/chapter_sorting/heap_sort.cpp +++ b/en/codes/cpp/chapter_sorting/heap_sort.cpp @@ -6,10 +6,10 @@ #include "../utils/common.hpp" -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ void siftDown(vector &nums, int n, int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; @@ -17,37 +17,37 @@ void siftDown(vector &nums, int n, int i) { ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) { break; } - // Swap two nodes + // Swap the two nodes swap(nums[i], nums[ma]); - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ void heapSort(vector &nums) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (int i = nums.size() / 2 - 1; i >= 0; --i) { siftDown(nums, nums.size(), i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (int i = nums.size() - 1; i > 0; --i) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) swap(nums[0], nums[i]); - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0); } } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {4, 1, 3, 1, 5, 2}; heapSort(nums); - cout << "After heap sort completes, nums = "; + cout << "After heap sort, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/insertion_sort.cpp b/en/codes/cpp/chapter_sorting/insertion_sort.cpp index 0041f3c6e..1cfc12b0e 100644 --- a/en/codes/cpp/chapter_sorting/insertion_sort.cpp +++ b/en/codes/cpp/chapter_sorting/insertion_sort.cpp @@ -8,23 +8,23 @@ /* Insertion sort */ void insertionSort(vector &nums) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for (int i = 1; i < nums.size(); i++) { int base = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + nums[j + 1] = nums[j]; // Shift nums[j] one position to the right j--; } - nums[j + 1] = base; // Assign base to the correct position + nums[j + 1] = base; // Place base at the correct position } } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {4, 1, 3, 1, 5, 2}; insertionSort(nums); - cout << "After insertion sort completes, nums = "; + cout << "After insertion sort, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/merge_sort.cpp b/en/codes/cpp/chapter_sorting/merge_sort.cpp index 86486f49c..2470eb8f3 100644 --- a/en/codes/cpp/chapter_sorting/merge_sort.cpp +++ b/en/codes/cpp/chapter_sorting/merge_sort.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ void merge(vector &nums, int left, int mid, int right) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result vector tmp(right - left + 1); - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays int i = left, j = mid + 1, k = 0; - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; @@ -27,7 +27,7 @@ void merge(vector &nums, int left, int mid, int right) { while (j <= right) { tmp[k++] = nums[j++]; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (k = 0; k < tmp.size(); k++) { nums[left + k] = tmp[k]; } @@ -35,23 +35,23 @@ void merge(vector &nums, int left, int mid, int right) { /* Merge sort */ void mergeSort(vector &nums, int left, int right) { - // Termination condition + // Base case if (left >= right) - return; // Terminate recursion when subarray length is 1 - // Divide and conquer stage - int mid = left + (right - left) / 2; // Calculate midpoint - mergeSort(nums, left, mid); // Recursively process the left subarray - mergeSort(nums, mid + 1, right); // Recursively process the right subarray - // Merge stage + return; // Stop recursing when the subarray has at most one element + // Divide phase + int mid = left + (right - left) / 2; // Calculate the midpoint + mergeSort(nums, left, mid); // Recurse on the left subarray + mergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase merge(nums, left, mid, right); } -/* Driver Code */ +/* Driver code */ int main() { /* Merge sort */ vector nums = {7, 3, 2, 6, 0, 1, 5, 4}; mergeSort(nums, 0, nums.size() - 1); - cout << "After merge sort completes, nums = "; + cout << "After merge sort, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/quick_sort.cpp b/en/codes/cpp/chapter_sorting/quick_sort.cpp index fea8f5b11..7df75ebd2 100644 --- a/en/codes/cpp/chapter_sorting/quick_sort.cpp +++ b/en/codes/cpp/chapter_sorting/quick_sort.cpp @@ -9,30 +9,30 @@ /* Quick sort class */ class QuickSort { private: - /* Sentinel partition */ + /* Two-pointer partitioning */ static int partition(vector &nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot swap(nums[i], nums[j]); // Swap these two elements } - swap(nums[i], nums[left]); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums[i], nums[left]); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } public: /* Quick sort */ static void quickSort(vector &nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } @@ -45,40 +45,40 @@ class QuickSortMedian { static int medianThree(vector &nums, int left, int mid, int right) { int l = nums[left], m = nums[mid], r = nums[right]; if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m is between l and r + return mid; // m lies between l and r if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l is between m and r + return left; // l lies between m and r return right; } - /* Sentinel partition (median of three) */ + /* Two-pointer partitioning (median of three) */ static int partition(vector &nums, int left, int right) { // Select the median of three candidate elements int med = medianThree(nums, left, (left + right) / 2, right); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray swap(nums[left], nums[med]); // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot swap(nums[i], nums[j]); // Swap these two elements } - swap(nums[i], nums[left]); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums[i], nums[left]); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } public: /* Quick sort */ static void quickSort(vector &nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } @@ -87,58 +87,58 @@ class QuickSortMedian { /* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { private: - /* Sentinel partition */ + /* Two-pointer partitioning */ static int partition(vector &nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot swap(nums[i], nums[j]); // Swap these two elements } - swap(nums[i], nums[left]); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums[i], nums[left]); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } public: /* Quick sort (recursion depth optimization) */ static void quickSort(vector &nums, int left, int right) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while (left < right) { - // Sentinel partition operation + // Perform two-pointer partitioning int pivot = partition(nums, left, right); - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left < right - pivot) { quickSort(nums, left, pivot - 1); // Recursively sort the left subarray - left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] } else { quickSort(nums, pivot + 1, right); // Recursively sort the right subarray - right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] } } } }; -/* Driver Code */ +/* Driver code */ int main() { /* Quick sort */ vector nums{2, 4, 1, 0, 3, 5}; QuickSort::quickSort(nums, 0, nums.size() - 1); - cout << "After quick sort completes, nums = "; + cout << "After quick sort, nums = "; printVector(nums); - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ vector nums1 = {2, 4, 1, 0, 3, 5}; QuickSortMedian::quickSort(nums1, 0, nums1.size() - 1); - cout << "After quick sort (median pivot optimization), nums = "; + cout << "After quick sort with median-of-three pivot selection, nums1 = "; printVector(nums1); /* Quick sort (recursion depth optimization) */ vector nums2 = {2, 4, 1, 0, 3, 5}; QuickSortTailCall::quickSort(nums2, 0, nums2.size() - 1); - cout << "After quick sort (recursion depth optimization), nums = "; + cout << "After quick sort with recursion-depth optimization, nums2 = "; printVector(nums2); return 0; diff --git a/en/codes/cpp/chapter_sorting/radix_sort.cpp b/en/codes/cpp/chapter_sorting/radix_sort.cpp index 2f68a4997..86b80794d 100644 --- a/en/codes/cpp/chapter_sorting/radix_sort.cpp +++ b/en/codes/cpp/chapter_sorting/radix_sort.cpp @@ -6,59 +6,59 @@ #include "../utils/common.hpp" -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ int digit(int num, int exp) { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (num / exp) % 10; } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ void countingSortDigit(vector &nums, int exp) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 vector counter(10, 0); int n = nums.size(); - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for (int i = 0; i < n; i++) { - int d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d - counter[d]++; // Count the occurrence of digit d + int d = digit(nums[i], exp); // Get the k-th digit of nums[i] and store it in d + counter[d]++; // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for (int i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts vector res(n, 0); for (int i = n - 1; i >= 0; i--) { int d = digit(nums[i], exp); - int j = counter[d] - 1; // Get the index j for d in the array + int j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d]--; // Decrease the count of d by 1 + counter[d]--; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for (int i = 0; i < n; i++) nums[i] = res[i]; } /* Radix sort */ void radixSort(vector &nums) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits int m = *max_element(nums.begin(), nums.end()); - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for (int exp = 1; exp <= m; exp *= 10) - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, exp); } -/* Driver Code */ +/* Driver code */ int main() { // Radix sort vector nums = {10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996}; radixSort(nums); - cout << "After radix sort completes, nums = "; + cout << "After radix sort, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/selection_sort.cpp b/en/codes/cpp/chapter_sorting/selection_sort.cpp index c58902457..f9d7f28e4 100644 --- a/en/codes/cpp/chapter_sorting/selection_sort.cpp +++ b/en/codes/cpp/chapter_sorting/selection_sort.cpp @@ -9,25 +9,25 @@ /* Selection sort */ void selectionSort(vector &nums) { int n = nums.size(); - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (int i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) k = j; // Record the index of the smallest element } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range swap(nums[i], nums[k]); } } -/* Driver Code */ +/* Driver code */ int main() { vector nums = {4, 1, 3, 1, 5, 2}; selectionSort(nums); - cout << "After selection sort completes, nums = "; + cout << "After selection sort, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp b/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp index 5af0c8440..919127539 100644 --- a/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp @@ -6,12 +6,12 @@ #include "../utils/common.hpp" -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ class ArrayDeque { private: - vector nums; // Array for storing double-ended queue elements - int front; // Front pointer, points to the front of the queue element - int queSize; // Double-ended queue length + vector nums; // Array for storing deque elements + int front; // Front index, identifying the first element + int queSize; // Deque size public: /* Constructor */ @@ -20,91 +20,91 @@ class ArrayDeque { front = queSize = 0; } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ int capacity() { return nums.size(); } - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size() { return queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty() { return queSize == 0; } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ int index(int i) { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end return (i + capacity()) % capacity(); } - /* Front of the queue enqueue */ + /* Enqueue at the front */ void pushFirst(int num) { if (queSize == capacity()) { - cout << "Double-ended queue is full" << endl; + cout << "Deque is full" << endl; return; } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front index one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array front = index(front - 1); - // Add num to front of queue + // Add num to the front nums[front] = num; queSize++; } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ void pushLast(int num) { if (queSize == capacity()) { - cout << "Double-ended queue is full" << endl; + cout << "Deque is full" << endl; return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear index, one position past the last element int rear = index(front + queSize); - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num; queSize++; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ int popFirst() { int num = peekFirst(); - // Move front pointer backward by one position + // Advance the front index by one position front = index(front + 1); queSize--; return num; } - /* Access rear of the queue element */ + /* Dequeue from the rear */ int popLast() { int num = peekLast(); queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ int peekFirst() { if (isEmpty()) throw out_of_range("Deque is empty"); return nums[front]; } - /* Driver Code */ + /* Access the rear element */ int peekLast() { if (isEmpty()) throw out_of_range("Deque is empty"); - // Initialize double-ended queue + // Calculate the index of the last element int last = index(front + queSize - 1); return nums[last]; } - /* Return array for printing */ + /* Return a vector for printing */ vector toVector() { - // Elements enqueue + // Copy only the elements in the occupied range vector res(queSize); for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[index(j)]; @@ -113,44 +113,44 @@ class ArrayDeque { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ ArrayDeque *deque = new ArrayDeque(10); deque->pushLast(3); deque->pushLast(2); deque->pushLast(5); - cout << "Double-ended queue deque = "; + cout << "Deque deque = "; printVector(deque->toVector()); - /* Update element */ + /* Access an element */ int peekFirst = deque->peekFirst(); cout << "Front element peekFirst = " << peekFirst << endl; int peekLast = deque->peekLast(); cout << "Rear element peekLast = " << peekLast << endl; - /* Elements enqueue */ + /* Enqueue elements */ deque->pushLast(4); - cout << "After element 4 enqueues at rear, deque = "; + cout << "After enqueuing element 4 at the rear, deque = "; printVector(deque->toVector()); deque->pushFirst(1); - cout << "After element 1 enqueues at front, deque = "; + cout << "After enqueuing element 1 at the front, deque = "; printVector(deque->toVector()); - /* Element dequeue */ + /* Dequeue an element */ int popLast = deque->popLast(); - cout << "Rear dequeue element = " << popLast << ", after rear dequeue, deque = "; + cout << "Element dequeued from the rear = " << popLast << ", after dequeuing from the rear, deque = "; printVector(deque->toVector()); int popFirst = deque->popFirst(); - cout << "Front dequeue element = " << popFirst << ", after front dequeue, deque = "; + cout << "Element dequeued from the front = " << popFirst << ", after dequeuing from the front, deque = "; printVector(deque->toVector()); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque->size(); - cout << "Double-ended queue length size = " << size << endl; + cout << "Deque size = " << size << endl; - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque->isEmpty(); - cout << "Double-ended queue is empty = " << boolalpha << isEmpty << endl; + cout << "Is the deque empty = " << boolalpha << isEmpty << endl; return 0; } diff --git a/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp b/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp index 50b5b6a1c..3cadb4f08 100644 --- a/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -6,17 +6,17 @@ #include "../utils/common.hpp" -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ class ArrayQueue { private: - int *nums; // Array for storing queue elements - int front; // Front pointer, points to the front of the queue element - int queSize; // Queue length + int *nums; // Array that stores the queue elements + int front; // Front index, identifying the first element + int queSize; // Queue size int queCapacity; // Queue capacity public: ArrayQueue(int capacity) { - // Initialize array + // Initialize the array nums = new int[capacity]; queCapacity = capacity; front = queSize = 0; @@ -26,17 +26,17 @@ class ArrayQueue { delete[] nums; } - /* Get the capacity of the queue */ + /* Get the queue capacity */ int capacity() { return queCapacity; } - /* Get the length of the queue */ + /* Get the queue size */ int size() { return queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty() { return size() == 0; } @@ -47,10 +47,10 @@ class ArrayQueue { cout << "Queue is full" << endl; return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear index, one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array int rear = (front + queSize) % queCapacity; - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num; queSize++; } @@ -58,22 +58,22 @@ class ArrayQueue { /* Dequeue */ int pop() { int num = peek(); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front index by one position, wrapping to the beginning if it passes the end front = (front + 1) % queCapacity; queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ int peek() { if (isEmpty()) throw out_of_range("Queue is empty"); return nums[front]; } - /* Convert array to Vector and return */ + /* Convert the array to a vector and return it */ vector toVector() { - // Elements enqueue + // Copy only the elements in the occupied range vector arr(queSize); for (int i = 0, j = front; i < queSize; i++, j++) { arr[i] = nums[j % queCapacity]; @@ -82,13 +82,13 @@ class ArrayQueue { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Access front of the queue element */ + /* Initialize the queue */ int capacity = 10; ArrayQueue *queue = new ArrayQueue(capacity); - /* Elements enqueue */ + /* Enqueue elements */ queue->push(1); queue->push(3); queue->push(2); @@ -97,28 +97,28 @@ int main() { cout << "Queue queue = "; printVector(queue->toVector()); - /* Return list for printing */ + /* Access the front element */ int peek = queue->peek(); cout << "Front element peek = " << peek << endl; - /* Element dequeue */ + /* Dequeue an element */ peek = queue->pop(); - cout << "Dequeue element pop = " << peek << ", after dequeue, queue = "; + cout << "Dequeued element pop = " << peek << "; after dequeueing, queue = "; printVector(queue->toVector()); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue->size(); - cout << "Queue length size = " << size << endl; + cout << "Queue size = " << size << endl; - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool empty = queue->isEmpty(); - cout << "Queue is empty = " << empty << endl; + cout << "Is the queue empty = " << empty << endl; - /* Test circular array */ + /* Test the circular array */ for (int i = 0; i < 10; i++) { queue->push(i); queue->pop(); - cout << "After round " << i << " enqueue + dequeue, queue = "; + cout << "Round " << i << " : after enqueue + dequeue, queue = "; printVector(queue->toVector()); } diff --git a/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp b/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp index 9429467d0..f3ae1bcfb 100644 --- a/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp @@ -6,18 +6,18 @@ #include "../utils/common.hpp" -/* Stack based on array implementation */ +/* Stack implemented with an array */ class ArrayStack { private: vector stack; public: - /* Get the length of the stack */ + /* Get the stack size */ int size() { return stack.size(); } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ bool isEmpty() { return stack.size() == 0; } @@ -34,25 +34,25 @@ class ArrayStack { return num; } - /* Return list for printing */ + /* Access the top element */ int top() { if (isEmpty()) throw out_of_range("Stack is empty"); return stack.back(); } - /* Return Vector */ + /* Return a vector */ vector toVector() { return stack; } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Access top of the stack element */ + /* Initialize the stack */ ArrayStack *stack = new ArrayStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack->push(1); stack->push(3); stack->push(2); @@ -61,22 +61,22 @@ int main() { cout << "Stack stack = "; printVector(stack->toVector()); - /* Return list for printing */ + /* Access the top element */ int top = stack->top(); - cout << "Stack top element top = " << top << endl; + cout << "Top element top = " << top << endl; - /* Element pop from stack */ + /* Pop an element from the stack */ top = stack->pop(); - cout << "Pop element pop = " << top << ", after pop, stack = "; + cout << "Popped element pop = " << top << "; after popping, stack = "; printVector(stack->toVector()); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack->size(); - cout << "Stack length size = " << size << endl; + cout << "Stack size = " << size << endl; - /* Check if empty */ + /* Check whether it is empty */ bool empty = stack->isEmpty(); - cout << "Stack is empty = " << empty << endl; + cout << "Is the stack empty = " << empty << endl; // Free memory delete stack; diff --git a/en/codes/cpp/chapter_stack_and_queue/deque.cpp b/en/codes/cpp/chapter_stack_and_queue/deque.cpp index c9f7ecc5d..d2201d84b 100644 --- a/en/codes/cpp/chapter_stack_and_queue/deque.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/deque.cpp @@ -6,41 +6,41 @@ #include "../utils/common.hpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ deque deque; - /* Elements enqueue */ + /* Enqueue elements */ deque.push_back(2); deque.push_back(5); deque.push_back(4); deque.push_front(3); deque.push_front(1); - cout << "Double-ended queue deque = "; + cout << "Deque deque = "; printDeque(deque); - /* Update element */ + /* Access an element */ int front = deque.front(); cout << "Front element front = " << front << endl; int back = deque.back(); - cout << "Back element back = " << back << endl; + cout << "Rear element back = " << back << endl; - /* Element dequeue */ + /* Dequeue an element */ deque.pop_front(); - cout << "Front dequeue element popFront = " << front << ", after front dequeue, deque = "; + cout << "Element removed from the front, popFront = " << front << ", after dequeuing from the front, deque = "; printDeque(deque); deque.pop_back(); - cout << "Rear dequeue element popLast = " << back << ", after rear dequeue, deque = "; + cout << "Element removed from the rear, popLast = " << back << ", after dequeuing from the rear, deque = "; printDeque(deque); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.size(); - cout << "Double-ended queue length size = " << size << endl; + cout << "Deque size = " << size << endl; - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool empty = deque.empty(); - cout << "Double-ended queue is empty = " << empty << endl; + cout << "Is the deque empty = " << empty << endl; return 0; } diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp index e0999fd7b..b5c8957b2 100644 --- a/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp @@ -9,17 +9,17 @@ /* Doubly linked list node */ struct DoublyListNode { int val; // Node value - DoublyListNode *next; // Successor node pointer - DoublyListNode *prev; // Predecessor node pointer + DoublyListNode *next; // Pointer to the next node + DoublyListNode *prev; // Pointer to the previous node DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) { } }; -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ class LinkedListDeque { private: - DoublyListNode *front, *rear; // Head node front, tail node rear - int queSize = 0; // Length of the double-ended queue + DoublyListNode *front, *rear; // Head node front and tail node rear + int queSize = 0; // Deque size public: /* Constructor */ @@ -28,7 +28,7 @@ class LinkedListDeque { /* Destructor */ ~LinkedListDeque() { - // Traverse linked list to delete nodes and free memory + // Traverse the linked list and delete each node to free its memory DoublyListNode *pre, *cur = front; while (cur != nullptr) { pre = cur; @@ -37,12 +37,12 @@ class LinkedListDeque { } } - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size() { return queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty() { return size() == 0; } @@ -53,28 +53,28 @@ class LinkedListDeque { // If the linked list is empty, make both front and rear point to node if (isEmpty()) front = rear = node; - // Front of the queue enqueue operation + // Enqueue at the front else if (isFront) { // Add node to the head of the linked list front->prev = node; node->next = front; - front = node; // Update head node - // Rear of the queue enqueue operation + front = node; // Update the head node + // Enqueue at the rear } else { // Add node to the tail of the linked list rear->next = node; node->prev = rear; - rear = node; // Update tail node + rear = node; // Update the tail node } - queSize++; // Update queue length + queSize++; // Update the deque size } - /* Front of the queue enqueue */ + /* Enqueue at the front */ void pushFirst(int num) { push(num, true); } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ void pushLast(int num) { push(num, false); } @@ -82,63 +82,63 @@ class LinkedListDeque { /* Dequeue operation */ int pop(bool isFront) { if (isEmpty()) - throw out_of_range("Queue is empty"); + throw out_of_range("Deque is empty"); int val; - // When the deque has one element, front and rear point to the same node + // When the deque size is 1, the head and tail point to the same node if (queSize == 1) { val = front->val; - delete front; // Free the shared node once + delete front; // Both point to the same node, so free it only once front = nullptr; rear = nullptr; } - // Temporarily store head node value + // Dequeue from the front else if (isFront) { - val = front->val; // Delete head node - // Delete head node + val = front->val; // Save the head node value + // Remove the head node DoublyListNode *fNext = front->next; fNext->prev = nullptr; front->next = nullptr; delete front; - front = fNext; // Update head node - // Temporarily store tail node value + front = fNext; // Update the head node + // Dequeue from the rear } else { - val = rear->val; // Delete tail node - // Update tail node + val = rear->val; // Save the tail node value + // Remove the tail node DoublyListNode *rPrev = rear->prev; rPrev->next = nullptr; rear->prev = nullptr; delete rear; - rear = rPrev; // Update tail node + rear = rPrev; // Update the tail node } - queSize--; // Update queue length + queSize--; // Update the deque size return val; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ int popFirst() { return pop(true); } - /* Access rear of the queue element */ + /* Dequeue from the rear */ int popLast() { return pop(false); } - /* Return list for printing */ + /* Access the front element */ int peekFirst() { if (isEmpty()) throw out_of_range("Deque is empty"); return front->val; } - /* Driver Code */ + /* Access the rear element */ int peekLast() { if (isEmpty()) throw out_of_range("Deque is empty"); return rear->val; } - /* Return array for printing */ + /* Return a vector for printing */ vector toVector() { DoublyListNode *node = front; vector res(size()); @@ -150,45 +150,45 @@ class LinkedListDeque { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ LinkedListDeque *deque = new LinkedListDeque(); deque->pushLast(3); deque->pushLast(2); deque->pushLast(5); - cout << "Double-ended queue deque = "; + cout << "Deque deque = "; printVector(deque->toVector()); - /* Update element */ + /* Access an element */ int peekFirst = deque->peekFirst(); cout << "Front element peekFirst = " << peekFirst << endl; int peekLast = deque->peekLast(); cout << "Rear element peekLast = " << peekLast << endl; - /* Elements enqueue */ + /* Enqueue elements */ deque->pushLast(4); - cout << "After element 4 enqueues at back, deque ="; + cout << "After enqueuing element 4 at the rear, deque ="; printVector(deque->toVector()); deque->pushFirst(1); - cout << "After element 1 enqueues at front, deque = "; + cout << "After enqueuing element 1 at the front, deque = "; printVector(deque->toVector()); - /* Element dequeue */ + /* Dequeue an element */ int popLast = deque->popLast(); - cout << "Rear dequeue element = " << popLast << ", after rear dequeue, deque = "; + cout << "Element dequeued from the rear = " << popLast << ", after dequeuing from the rear, deque = "; printVector(deque->toVector()); int popFirst = deque->popFirst(); - cout << "Front dequeue element = " << popFirst << ", after front dequeue, deque = "; + cout << "Element dequeued from the front = " << popFirst << ", after dequeuing from the front, deque = "; printVector(deque->toVector()); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque->size(); - cout << "Double-ended queue length size = " << size << endl; + cout << "Deque size = " << size << endl; - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque->isEmpty(); - cout << "Double-ended queue is empty = " << boolalpha << isEmpty << endl; + cout << "Is the deque empty = " << boolalpha << isEmpty << endl; // Free memory delete deque; diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp index 805861090..f448b319a 100644 --- a/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -6,10 +6,10 @@ #include "../utils/common.hpp" -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue { private: - ListNode *front, *rear; // Head node front, tail node rear + ListNode *front, *rear; // Head node front and tail node rear int queSize; public: @@ -20,16 +20,16 @@ class LinkedListQueue { } ~LinkedListQueue() { - // Traverse linked list to delete nodes and free memory + // Traverse the linked list and delete each node to free its memory freeMemoryLinkedList(front); } - /* Get the length of the queue */ + /* Get the queue size */ int size() { return queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty() { return queSize == 0; } @@ -38,12 +38,12 @@ class LinkedListQueue { void push(int num) { // Add num after the tail node ListNode *node = new ListNode(num); - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (front == nullptr) { front = node; rear = node; } - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node else { rear->next = node; rear = node; @@ -54,7 +54,7 @@ class LinkedListQueue { /* Dequeue */ int pop() { int num = peek(); - // Delete head node + // Remove the head node ListNode *tmp = front; front = front->next; // Free memory @@ -63,14 +63,14 @@ class LinkedListQueue { return num; } - /* Return list for printing */ + /* Access the front element */ int peek() { if (size() == 0) throw out_of_range("Queue is empty"); return front->val; } - /* Convert linked list to Vector and return */ + /* Convert the linked list to a vector and return it */ vector toVector() { ListNode *node = front; vector res(size()); @@ -82,12 +82,12 @@ class LinkedListQueue { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Access front of the queue element */ + /* Initialize the queue */ LinkedListQueue *queue = new LinkedListQueue(); - /* Elements enqueue */ + /* Enqueue elements */ queue->push(1); queue->push(3); queue->push(2); @@ -96,22 +96,22 @@ int main() { cout << "Queue queue = "; printVector(queue->toVector()); - /* Return list for printing */ + /* Access the front element */ int peek = queue->peek(); cout << "Front element peek = " << peek << endl; - /* Element dequeue */ + /* Dequeue an element */ peek = queue->pop(); - cout << "Dequeue element pop = " << peek << ", after dequeue, queue = "; + cout << "Dequeued element pop = " << peek << "; after dequeueing, queue = "; printVector(queue->toVector()); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue->size(); - cout << "Queue length size = " << size << endl; + cout << "Queue size = " << size << endl; - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool empty = queue->isEmpty(); - cout << "Queue is empty = " << empty << endl; + cout << "Is the queue empty = " << empty << endl; // Free memory delete queue; diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp index fbc791dc9..2a0629f35 100644 --- a/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp @@ -6,11 +6,11 @@ #include "../utils/common.hpp" -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ class LinkedListStack { private: - ListNode *stackTop; // Use head node as stack top - int stkSize; // Stack length + ListNode *stackTop; // Use the head node as the stack top + int stkSize; // Stack size public: LinkedListStack() { @@ -19,16 +19,16 @@ class LinkedListStack { } ~LinkedListStack() { - // Traverse linked list to delete nodes and free memory + // Traverse the linked list and delete each node to free its memory freeMemoryLinkedList(stackTop); } - /* Get the length of the stack */ + /* Get the stack size */ int size() { return stkSize; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ bool isEmpty() { return size() == 0; } @@ -52,14 +52,14 @@ class LinkedListStack { return num; } - /* Return list for printing */ + /* Access the top element */ int top() { if (isEmpty()) throw out_of_range("Stack is empty"); return stackTop->val; } - /* Convert List to Array and return */ + /* Convert the linked list to a vector and return it */ vector toVector() { ListNode *node = stackTop; vector res(size()); @@ -71,12 +71,12 @@ class LinkedListStack { } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Access top of the stack element */ + /* Initialize the stack */ LinkedListStack *stack = new LinkedListStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack->push(1); stack->push(3); stack->push(2); @@ -85,22 +85,22 @@ int main() { cout << "Stack stack = "; printVector(stack->toVector()); - /* Return list for printing */ + /* Access the top element */ int top = stack->top(); - cout << "Stack top element top = " << top << endl; + cout << "Top element top = " << top << endl; - /* Element pop from stack */ + /* Pop an element from the stack */ top = stack->pop(); - cout << "Pop element pop = " << top << ", after pop, stack = "; + cout << "Popped element pop = " << top << "; after popping, stack = "; printVector(stack->toVector()); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack->size(); - cout << "Stack length size = " << size << endl; + cout << "Stack size = " << size << endl; - /* Check if empty */ + /* Check whether it is empty */ bool empty = stack->isEmpty(); - cout << "Stack is empty = " << empty << endl; + cout << "Is the stack empty = " << empty << endl; // Free memory delete stack; diff --git a/en/codes/cpp/chapter_stack_and_queue/queue.cpp b/en/codes/cpp/chapter_stack_and_queue/queue.cpp index 7b541b653..294aa5a2c 100644 --- a/en/codes/cpp/chapter_stack_and_queue/queue.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/queue.cpp @@ -6,12 +6,12 @@ #include "../utils/common.hpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Access front of the queue element */ + /* Initialize the queue */ queue queue; - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -20,22 +20,22 @@ int main() { cout << "Queue queue = "; printQueue(queue); - /* Return list for printing */ + /* Access the front element */ int front = queue.front(); cout << "Front element front = " << front << endl; - /* Element dequeue */ + /* Dequeue an element */ queue.pop(); - cout << "Dequeue element front = " << front << ", after dequeue, queue = "; + cout << "Dequeued element front = " << front << "; after dequeueing, queue = "; printQueue(queue); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue.size(); - cout << "Queue length size = " << size << endl; + cout << "Queue size = " << size << endl; - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool empty = queue.empty(); - cout << "Queue is empty = " << empty << endl; + cout << "Is the queue empty = " << empty << endl; return 0; } diff --git a/en/codes/cpp/chapter_stack_and_queue/stack.cpp b/en/codes/cpp/chapter_stack_and_queue/stack.cpp index 1d20521b2..86889b3f4 100644 --- a/en/codes/cpp/chapter_stack_and_queue/stack.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/stack.cpp @@ -6,12 +6,12 @@ #include "../utils/common.hpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Access top of the stack element */ + /* Initialize the stack */ stack stack; - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -20,22 +20,22 @@ int main() { cout << "Stack stack = "; printStack(stack); - /* Return list for printing */ + /* Access the top element */ int top = stack.top(); - cout << "Stack top element top = " << top << endl; + cout << "Top element top = " << top << endl; - /* Element pop from stack */ + /* Pop an element from the stack */ stack.pop(); // No return value - cout << "Pop element pop = " << top << ", after pop, stack = "; + cout << "Popped element pop = " << top << "; after popping, stack = "; printStack(stack); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack.size(); - cout << "Stack length size = " << size << endl; + cout << "Stack size = " << size << endl; - /* Check if empty */ + /* Check whether it is empty */ bool empty = stack.empty(); - cout << "Stack is empty = " << empty << endl; + cout << "Is the stack empty = " << empty << endl; return 0; } diff --git a/en/codes/cpp/chapter_tree/array_binary_tree.cpp b/en/codes/cpp/chapter_tree/array_binary_tree.cpp index 727a94fcc..56b436a1a 100644 --- a/en/codes/cpp/chapter_tree/array_binary_tree.cpp +++ b/en/codes/cpp/chapter_tree/array_binary_tree.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ class ArrayBinaryTree { public: /* Constructor */ @@ -14,30 +14,30 @@ class ArrayBinaryTree { tree = arr; } - /* List capacity */ + /* Number of positions in the array */ int size() { return tree.size(); } - /* Get value of node at index i */ + /* Get the value of the node at index i */ int val(int i) { - // Return INT_MAX if index out of bounds, representing empty position + // If the index is out of bounds, return INT_MAX to represent an empty position if (i < 0 || i >= size()) return INT_MAX; return tree[i]; } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ int left(int i) { return 2 * i + 1; } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ int right(int i) { return 2 * i + 2; } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ int parent(int i) { return (i - 1) / 2; } @@ -45,7 +45,7 @@ class ArrayBinaryTree { /* Level-order traversal */ vector levelOrder() { vector res; - // Traverse array directly + // Traverse the array directly for (int i = 0; i < size(); i++) { if (val(i) != INT_MAX) res.push_back(val(i)); @@ -79,7 +79,7 @@ class ArrayBinaryTree { /* Depth-first traversal */ void dfs(int i, string order, vector &res) { - // If empty position, return + // Return if this position is empty if (val(i) == INT_MAX) return; // Preorder traversal @@ -96,41 +96,41 @@ class ArrayBinaryTree { } }; -/* Driver Code */ +/* Driver code */ int main() { - // Initialize binary tree - // Use INT_MAX to represent empty position nullptr + // Initialize the binary tree + // Use INT_MAX to represent an empty position (nullptr) vector arr = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; TreeNode *root = vectorToTree(arr); - cout << "\nInitialize binary tree\n"; - cout << "Array representation of binary tree:\n"; + cout << "\nInitialize the binary tree\n"; + cout << "Array representation of the binary tree:\n"; printVector(arr); - cout << "Linked list representation of binary tree:\n"; + cout << "Linked representation of the binary tree:\n"; printTree(root); - // Binary tree class represented by array + // Binary tree class using an array representation ArrayBinaryTree abt(arr); - // Access node + // Access a node int i = 1; int l = abt.left(i), r = abt.right(i), p = abt.parent(i); - cout << "\nCurrent node index is " << i << ", value is " << abt.val(i) << "\n"; - cout << "Its left child node index is " << l << ", value is " << (abt.val(l) != INT_MAX ? to_string(abt.val(l)) : "nullptr") << "\n"; - cout << "Its right child node index is " << r << ", value is " << (abt.val(r) != INT_MAX ? to_string(abt.val(r)) : "nullptr") << "\n"; - cout << "Its parent node index is " << p << ", value is " << (abt.val(p) != INT_MAX ? to_string(abt.val(p)) : "nullptr") << "\n"; + cout << "\nCurrent node index: " << i << ", value: " << abt.val(i) << "\n"; + cout << "Left child index: " << l << ", value: " << (abt.val(l) != INT_MAX ? to_string(abt.val(l)) : "nullptr") << "\n"; + cout << "Right child index: " << r << ", value: " << (abt.val(r) != INT_MAX ? to_string(abt.val(r)) : "nullptr") << "\n"; + cout << "Parent index: " << p << ", value: " << (abt.val(p) != INT_MAX ? to_string(abt.val(p)) : "nullptr") << "\n"; - // Traverse tree + // Traverse the tree vector res = abt.levelOrder(); cout << "\nLevel-order traversal: "; printVector(res); res = abt.preOrder(); - cout << "Pre-order traversal: "; + cout << "Preorder traversal: "; printVector(res); res = abt.inOrder(); - cout << "In-order traversal: "; + cout << "Inorder traversal: "; printVector(res); res = abt.postOrder(); - cout << "Post-order traversal: "; + cout << "Postorder traversal: "; printVector(res); return 0; diff --git a/en/codes/cpp/chapter_tree/avl_tree.cpp b/en/codes/cpp/chapter_tree/avl_tree.cpp index 5047bba3a..b56f23ad2 100644 --- a/en/codes/cpp/chapter_tree/avl_tree.cpp +++ b/en/codes/cpp/chapter_tree/avl_tree.cpp @@ -9,93 +9,93 @@ /* AVL tree */ class AVLTree { private: - /* Update node height */ + /* Update the node height */ void updateHeight(TreeNode *node) { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node->height = max(height(node->left), height(node->right)) + 1; } - /* Right rotation operation */ + /* Right rotation */ TreeNode *rightRotate(TreeNode *node) { TreeNode *child = node->left; TreeNode *grandChild = child->right; - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child->right = node; node->left = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Left rotation operation */ + /* Left rotation */ TreeNode *leftRotate(TreeNode *node) { TreeNode *child = node->right; TreeNode *grandChild = child->left; - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child->left = node; node->right = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ TreeNode *rotate(TreeNode *node) { - // Get balance factor of node + // Get the balance factor of node int _balanceFactor = balanceFactor(node); - // Left-leaning tree + // Left-heavy tree if (_balanceFactor > 1) { if (balanceFactor(node->left) >= 0) { - // Right rotation + // Rotate right return rightRotate(node); } else { - // First left rotation then right rotation + // Rotate left, then right node->left = leftRotate(node->left); return rightRotate(node); } } - // Right-leaning tree + // Right-heavy tree if (_balanceFactor < -1) { if (balanceFactor(node->right) <= 0) { - // Left rotation + // Rotate left return leftRotate(node); } else { - // First right rotation then left rotation + // Rotate right, then left node->right = rightRotate(node->right); return leftRotate(node); } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node; } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ TreeNode *insertHelper(TreeNode *node, int val) { if (node == nullptr) return new TreeNode(val); - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (val < node->val) node->left = insertHelper(node->left, val); else if (val > node->val) node->right = insertHelper(node->right, val); else - return node; // Duplicate node not inserted, return directly - updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + return node; // Return without inserting a duplicate node + updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ TreeNode *removeHelper(TreeNode *node, int val) { if (node == nullptr) return nullptr; - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (val < node->val) node->left = removeHelper(node->left, val); else if (val > node->val) @@ -103,18 +103,18 @@ class AVLTree { else { if (node->left == nullptr || node->right == nullptr) { TreeNode *child = node->left != nullptr ? node->left : node->right; - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child == nullptr) { delete node; return nullptr; } - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly else { delete node; node = child; } } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value TreeNode *temp = node->right; while (temp->left != nullptr) { temp = temp->left; @@ -124,57 +124,57 @@ class AVLTree { node->val = tempVal; } } - updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } public: TreeNode *root; // Root node - /* Get node height */ + /* Get the node height */ int height(TreeNode *node) { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 return node == nullptr ? -1 : node->height; } - /* Get balance factor */ + /* Get the balance factor */ int balanceFactor(TreeNode *node) { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node == nullptr) return 0; // Node balance factor = left subtree height - right subtree height return height(node->left) - height(node->right); } - /* Insert node */ + /* Insert a node */ void insert(int val) { root = insertHelper(root, val); } - /* Remove node */ + /* Remove a node */ void remove(int val) { root = removeHelper(root, val); } - /* Search node */ + /* Find a node */ TreeNode *search(int val) { TreeNode *cur = root; - // Loop search, exit after passing leaf node + // Continue searching while the current node pointer is not null while (cur != nullptr) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur->val < val) cur = cur->right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur->val > val) cur = cur->left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node, or nullptr if it was not found return cur; } @@ -190,23 +190,23 @@ class AVLTree { void testInsert(AVLTree &tree, int val) { tree.insert(val); - cout << "\nAfter inserting node " << val << ", AVL tree is" << endl; + cout << "\nAfter inserting node " << val << " , the AVL tree is" << endl; printTree(tree.root); } void testRemove(AVLTree &tree, int val) { tree.remove(val); - cout << "\nAfter removing node " << val << ", AVL tree is" << endl; + cout << "\nAfter removing node " << val << " , the AVL tree is" << endl; printTree(tree.root); } -/* Driver Code */ +/* Driver code */ int main() { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ AVLTree avlTree; - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted testInsert(avlTree, 1); testInsert(avlTree, 2); testInsert(avlTree, 3); @@ -218,16 +218,16 @@ int main() { testInsert(avlTree, 10); testInsert(avlTree, 6); - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ testInsert(avlTree, 7); - /* Remove node */ - // Delete node with degree 1 - testRemove(avlTree, 8); // Delete node with degree 2 - testRemove(avlTree, 5); // Remove node with degree 1 - testRemove(avlTree, 4); // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + testRemove(avlTree, 8); // Remove a node with 0 children + testRemove(avlTree, 5); // Remove a node with 1 child + testRemove(avlTree, 4); // Remove a node with 2 children - /* Search node */ + /* Look up a node */ TreeNode *node = avlTree.search(7); - cout << "\nFound node object is " << node << ", node value = " << node->val << endl; + cout << "\nFound node address: " << node << ", node value = " << node->val << endl; } diff --git a/en/codes/cpp/chapter_tree/binary_search_tree.cpp b/en/codes/cpp/chapter_tree/binary_search_tree.cpp index 0a2888b4a..b272b0527 100644 --- a/en/codes/cpp/chapter_tree/binary_search_tree.cpp +++ b/en/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -14,7 +14,7 @@ class BinarySearchTree { public: /* Constructor */ BinarySearchTree() { - // Initialize empty tree + // Initialize an empty tree root = nullptr; } @@ -23,52 +23,52 @@ class BinarySearchTree { freeMemoryTree(root); } - /* Get binary tree root node */ + /* Get the root of the binary tree */ TreeNode *getRoot() { return root; } - /* Search node */ + /* Find a node */ TreeNode *search(int num) { TreeNode *cur = root; - // Loop search, exit after passing leaf node + // Continue searching while the current node pointer is not null while (cur != nullptr) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur->val < num) cur = cur->right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur->val > num) cur = cur->left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node, or nullptr if it was not found return cur; } - /* Insert node */ + /* Insert a node */ void insert(int num) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (root == nullptr) { root = new TreeNode(num); return; } TreeNode *cur = root, *pre = nullptr; - // Loop search, exit after passing leaf node + // Continue searching while the current node pointer is not null while (cur != nullptr) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur->val == num) return; pre = cur; - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur if (cur->val < num) cur = cur->right; - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else cur = cur->left; } - // Insert node + // Insert a node TreeNode *node = new TreeNode(num); if (pre->val < num) pre->right = node; @@ -76,91 +76,91 @@ class BinarySearchTree { pre->left = node; } - /* Remove node */ + /* Remove a node */ void remove(int num) { - // If tree is empty, return directly + // Return immediately if the tree is empty if (root == nullptr) return; TreeNode *cur = root, *pre = nullptr; - // Loop search, exit after passing leaf node + // Continue searching while the current node pointer is not null while (cur != nullptr) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur->val == num) break; pre = cur; - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur if (cur->val < num) cur = cur->right; - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur else cur = cur->left; } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if (cur == nullptr) return; - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if (cur->left == nullptr || cur->right == nullptr) { - // When number of child nodes = 0 / 1, child = nullptr / that child node + // With 0 or 1 child, child is nullptr or that child, respectively TreeNode *child = cur->left != nullptr ? cur->left : cur->right; - // Delete node cur + // Remove node cur if (cur != root) { if (pre->left == cur) pre->left = child; else pre->right = child; } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root root = child; } // Free memory delete cur; } - // Number of child nodes = 2 + // Number of children = 2 else { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur TreeNode *tmp = cur->right; while (tmp->left != nullptr) { tmp = tmp->left; } int tmpVal = tmp->val; - // Recursively delete node tmp + // Recursively remove node tmp remove(tmp->val); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur->val = tmpVal; } } }; -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ BinarySearchTree *bst = new BinarySearchTree(); - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree vector nums = {8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15}; for (int num : nums) { bst->insert(num); } - cout << endl << "Initialized binary tree is\n" << endl; + cout << endl << "Binary tree after initialization:\n" << endl; printTree(bst->getRoot()); - /* Search node */ + /* Find a node */ TreeNode *node = bst->search(7); - cout << endl << "Found node object is " << node << ", node value = " << node->val << endl; + cout << endl << "Found node address: " << node << ", node value = " << node->val << endl; - /* Insert node */ + /* Insert a node */ bst->insert(16); - cout << endl << "After inserting node 16, binary tree is\n" << endl; + cout << endl << "Binary tree after inserting node 16:\n" << endl; printTree(bst->getRoot()); - /* Remove node */ + /* Remove a node */ bst->remove(1); - cout << endl << "After removing node 1, binary tree is\n" << endl; + cout << endl << "Binary tree after removing node 1:\n" << endl; printTree(bst->getRoot()); bst->remove(2); - cout << endl << "After removing node 2, binary tree is\n" << endl; + cout << endl << "Binary tree after removing node 2:\n" << endl; printTree(bst->getRoot()); bst->remove(4); - cout << endl << "After removing node 4, binary tree is\n" << endl; + cout << endl << "Binary tree after removing node 4:\n" << endl; printTree(bst->getRoot()); // Free memory diff --git a/en/codes/cpp/chapter_tree/binary_tree.cpp b/en/codes/cpp/chapter_tree/binary_tree.cpp index fe24118e1..0b71e897d 100644 --- a/en/codes/cpp/chapter_tree/binary_tree.cpp +++ b/en/codes/cpp/chapter_tree/binary_tree.cpp @@ -6,34 +6,34 @@ #include "../utils/common.hpp" -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes TreeNode *n1 = new TreeNode(1); TreeNode *n2 = new TreeNode(2); TreeNode *n3 = new TreeNode(3); TreeNode *n4 = new TreeNode(4); TreeNode *n5 = new TreeNode(5); - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1->left = n2; n1->right = n3; n2->left = n4; n2->right = n5; - cout << endl << "Initialize binary tree\n" << endl; + cout << endl << "Initialize the binary tree\n" << endl; printTree(n1); - /* Insert node P between n1 -> n2 */ + /* Insert and remove nodes */ TreeNode *P = new TreeNode(0); - // Delete node + // Insert node P between n1 and n2 in n1 -> n2 n1->left = P; P->left = n2; - cout << endl << "After inserting node P\n" << endl; + cout << endl << "After inserting node P:\n" << endl; printTree(n1); // Remove node P n1->left = n2; delete P; // Free memory - cout << endl << "After removing node P\n" << endl; + cout << endl << "After removing node P:\n" << endl; printTree(n1); // Free memory diff --git a/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp b/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp index 0b383a7cf..bf5922c55 100644 --- a/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp +++ b/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp @@ -8,34 +8,34 @@ /* Level-order traversal */ vector levelOrder(TreeNode *root) { - // Initialize queue, add root node + // Initialize a queue and enqueue the root queue queue; queue.push(root); - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence vector vec; while (!queue.empty()) { TreeNode *node = queue.front(); - queue.pop(); // Dequeue - vec.push_back(node->val); // Save node value + queue.pop(); // Dequeue from the queue + vec.push_back(node->val); // Store the node value if (node->left != nullptr) - queue.push(node->left); // Left child node enqueue + queue.push(node->left); // Enqueue the left child if (node->right != nullptr) - queue.push(node->right); // Right child node enqueue + queue.push(node->right); // Enqueue the right child } return vec; } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode *root = vectorToTree(vector{1, 2, 3, 4, 5, 6, 7}); - cout << endl << "Initialize binary tree\n" << endl; + cout << endl << "Initialize the binary tree\n" << endl; printTree(root); /* Level-order traversal */ vector vec = levelOrder(root); - cout << endl << "Level-order traversal node print sequence = "; + cout << endl << "Node sequence from level-order traversal = "; printVector(vec); return 0; diff --git a/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp b/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp index 68053275e..56c1a4130 100644 --- a/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp +++ b/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -// Initialize list for storing traversal sequence +// Initialize a list to store the traversal sequence vector vec; /* Preorder traversal */ void preOrder(TreeNode *root) { if (root == nullptr) return; - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree vec.push_back(root->val); preOrder(root->left); preOrder(root->right); @@ -23,7 +23,7 @@ void preOrder(TreeNode *root) { void inOrder(TreeNode *root) { if (root == nullptr) return; - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(root->left); vec.push_back(root->val); inOrder(root->right); @@ -33,36 +33,36 @@ void inOrder(TreeNode *root) { void postOrder(TreeNode *root) { if (root == nullptr) return; - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(root->left); postOrder(root->right); vec.push_back(root->val); } -/* Driver Code */ +/* Driver code */ int main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode *root = vectorToTree(vector{1, 2, 3, 4, 5, 6, 7}); - cout << endl << "Initialize binary tree\n" << endl; + cout << endl << "Initialize the binary tree\n" << endl; printTree(root); /* Preorder traversal */ vec.clear(); preOrder(root); - cout << endl << "Pre-order traversal node print sequence = "; + cout << endl << "Node sequence from preorder traversal = "; printVector(vec); /* Inorder traversal */ vec.clear(); inOrder(root); - cout << endl << "In-order traversal node print sequence = "; + cout << endl << "Node sequence from inorder traversal = "; printVector(vec); /* Postorder traversal */ vec.clear(); postOrder(root); - cout << endl << "Post-order traversal node print sequence = "; + cout << endl << "Node sequence from postorder traversal = "; printVector(vec); return 0; diff --git a/en/codes/cpp/utils/list_node.hpp b/en/codes/cpp/utils/list_node.hpp index 33eadf4aa..3890e724d 100644 --- a/en/codes/cpp/utils/list_node.hpp +++ b/en/codes/cpp/utils/list_node.hpp @@ -30,7 +30,7 @@ ListNode *vecToLinkedList(vector list) { return dum->next; } -/* Free memory allocated to linked list */ +/* Free the memory allocated to the linked list */ void freeMemoryLinkedList(ListNode *cur) { // Free memory ListNode *pre; diff --git a/en/codes/cpp/utils/print_utils.hpp b/en/codes/cpp/utils/print_utils.hpp index c29a99ad9..adc06a814 100644 --- a/en/codes/cpp/utils/print_utils.hpp +++ b/en/codes/cpp/utils/print_utils.hpp @@ -13,7 +13,7 @@ #include #include -/* Find an element in a vector */ +/* Find the last matching element's index in a vector */ template int vecFind(const vector &vec, T ele) { int j = INT_MAX; for (int i = 0; i < vec.size(); i++) { @@ -24,7 +24,7 @@ template int vecFind(const vector &vec, T ele) { return j; } -/* Concatenate a vector with a delim */ +/* Join vector elements with a delimiter */ template string strJoin(const string &delim, const T &vec) { ostringstream s; for (const auto &i : vec) { @@ -36,7 +36,7 @@ template string strJoin(const string &delim, const T &vec) { return s.str(); } -/* Repeat a string for n times */ +/* Repeat a string n times */ string strRepeat(string str, int n) { ostringstream os; for (int i = 0; i < n; i++) @@ -44,7 +44,7 @@ string strRepeat(string str, int n) { return os.str(); } -/* Print array */ +/* Print an array */ template void printArray(T *arr, int n) { cout << "["; for (int i = 0; i < n - 1; i++) { @@ -56,17 +56,17 @@ template void printArray(T *arr, int n) { cout << "]" << endl; } -/* Get the Vector String object */ +/* Get a string representation of a vector */ template string getVectorString(vector &list) { return "[" + strJoin(", ", list) + "]"; } -/* Print list */ +/* Print the list */ template void printVector(vector list) { cout << getVectorString(list) << '\n'; } -/* Print matrix */ +/* Print a matrix */ template void printVectorMatrix(vector> &matrix) { cout << "[" << '\n'; for (vector &list : matrix) @@ -74,7 +74,7 @@ template void printVectorMatrix(vector> &matrix) { cout << "]" << '\n'; } -/* Print linked list */ +/* Print a linked list */ void printLinkedList(ListNode *head) { vector list; while (head != nullptr) { @@ -104,7 +104,7 @@ void showTrunks(Trunk *p) { } /** - * 打印二叉树 + * Print a binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -139,12 +139,12 @@ void printTree(TreeNode *root, Trunk *prev, bool isRight) { printTree(root->left, &trunk, false); } -/* Print binary tree */ +/* Print a binary tree */ void printTree(TreeNode *root) { printTree(root, nullptr, false); } -/* Print stack */ +/* Print the stack */ template void printStack(stack stk) { // Reverse the input stack stack tmp; @@ -166,7 +166,7 @@ template void printStack(stack stk) { cout << "[" + s.str() + "]" << '\n'; } -/* Print queue */ +/* Print the queue */ template void printQueue(queue queue) { // Generate the string to print ostringstream s; @@ -182,7 +182,7 @@ template void printQueue(queue queue) { cout << "[" + s.str() + "]" << '\n'; } -/* Print deque */ +/* Print the deque */ template void printDeque(deque deque) { // Generate the string to print ostringstream s; @@ -198,8 +198,8 @@ template void printDeque(deque deque) { cout << "[" + s.str() + "]" << '\n'; } -/* Print hash table */ -// Define template parameters TKey and TValue to specify key-value pair types +/* Print the hash table */ +// Define template parameters TKey and TValue for the key and value types template void printHashMap(unordered_map map) { for (auto kv : map) { cout << kv.first << " -> " << kv.second << '\n'; @@ -216,12 +216,12 @@ template S &Container(priority_queue void printHeap(priority_queue &heap) { vector vec = Container(heap); - cout << "Heap array representation:"; + cout << "Array representation of the heap:"; printVector(vec); - cout << "Heap tree representation:" << endl; + cout << "Tree representation of the heap:" << endl; TreeNode *root = vectorToTree(vec); printTree(root); freeMemoryTree(root); diff --git a/en/codes/cpp/utils/tree_node.hpp b/en/codes/cpp/utils/tree_node.hpp index acd385dde..b790033d4 100644 --- a/en/codes/cpp/utils/tree_node.hpp +++ b/en/codes/cpp/utils/tree_node.hpp @@ -23,11 +23,11 @@ struct TreeNode { } }; -// For the serialization encoding rules, please refer to: -// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ -// Array representation of binary tree: +// For the serialization rules, see: +// https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ +// Array representation of the binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] -// Linked list representation of binary tree: +// Linked representation of the binary tree: // /——— 15 // /——— 7 // /——— 3 @@ -74,7 +74,7 @@ vector treeToVecor(TreeNode *root) { return res; } -/* Free binary tree memory */ +/* Free the binary tree memory */ void freeMemoryTree(TreeNode *root) { if (root == nullptr) return; diff --git a/en/codes/cpp/utils/vertex.hpp b/en/codes/cpp/utils/vertex.hpp index c8c21eafd..0664f30ee 100644 --- a/en/codes/cpp/utils/vertex.hpp +++ b/en/codes/cpp/utils/vertex.hpp @@ -17,7 +17,7 @@ struct Vertex { } }; -/* Input value list vals, return vertex list vets */ +/* Given the list of values vals, return the list of vertices vets */ vector valsToVets(vector vals) { vector vets; for (int val : vals) { @@ -26,7 +26,7 @@ vector valsToVets(vector vals) { return vets; } -/* Input vertex list vets, return value list vals */ +/* Given the list of vertices vets, return the list of values vals */ vector vetsToVals(vector vets) { vector vals; for (Vertex *vet : vets) { diff --git a/en/codes/csharp/chapter_array_and_linkedlist/array.cs b/en/codes/csharp/chapter_array_and_linkedlist/array.cs index a20a16f23..0269406ec 100644 --- a/en/codes/csharp/chapter_array_and_linkedlist/array.cs +++ b/en/codes/csharp/chapter_array_and_linkedlist/array.cs @@ -5,60 +5,60 @@ namespace hello_algo.chapter_array_and_linkedlist; public class array { - /* Random access to element */ + /* Access an element at a random index */ int RandomAccess(int[] nums) { Random random = new(); - // Randomly select a number in interval [0, nums.Length) + // Randomly select a number from [0, nums.Length) int randomIndex = random.Next(nums.Length); - // Retrieve and return the random element + // Get and return the randomly selected element int randomNum = nums[randomIndex]; return randomNum; } - /* Extend array length */ + /* Extend the array length */ int[] Extend(int[] nums, int enlarge) { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res; } - /* Insert element num at index index in the array */ + /* 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 + // Shift all elements at and after index `index` one position to the right for (int i = nums.Length - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num; } - /* Remove the element at index index */ + /* Remove the element at index `index` */ void Remove(int[] nums, int index) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (int i = index; i < nums.Length - 1; i++) { nums[i] = nums[i + 1]; } } - /* Traverse array */ + /* Traverse the array */ void Traverse(int[] nums) { int count = 0; - // Traverse array by index + // Traverse the array by index for (int i = 0; i < nums.Length; i++) { count += nums[i]; } - // Direct traversal of array elements + // Iterate directly over the array elements foreach (int num in nums) { count += num; } } - /* Find the specified element in the array */ + /* Find a specified element in the array */ int Find(int[] nums, int target) { for (int i = 0; i < nums.Length; i++) { if (nums[i] == target) @@ -67,7 +67,7 @@ public class array { return -1; } - /* Helper function, convert array to string */ + /* Helper function: convert an array to a string */ string ToString(int[] nums) { return string.Join(",", nums); } @@ -75,33 +75,33 @@ public class array { [Test] public void Test() { - // Initialize array + // Initialize the array int[] arr = new int[5]; Console.WriteLine("Array arr = " + ToString(arr)); int[] nums = [1, 3, 2, 5, 4]; Console.WriteLine("Array nums = " + ToString(nums)); - // Insert element + // Random access int randomNum = RandomAccess(nums); - Console.WriteLine("Get random element in nums " + randomNum); + Console.WriteLine("Randomly selected element from nums: " + randomNum); - // Traverse array + // Extend the length nums = Extend(nums, 3); - Console.WriteLine("Extend array length to 8, resulting in nums = " + ToString(nums)); + Console.WriteLine("Extend the array length to 8, giving nums = " + ToString(nums)); - // Insert element + // Insert an element Insert(nums, 6, 3); - Console.WriteLine("Insert number 6 at index 3, resulting in nums = " + ToString(nums)); + Console.WriteLine("Insert the number 6 at index 3, giving nums = " + ToString(nums)); - // Remove element + // Remove an element Remove(nums, 2); - Console.WriteLine("Remove element at index 2, resulting in nums = " + ToString(nums)); + Console.WriteLine("Remove the element at index 2, giving nums = " + ToString(nums)); - // Traverse array + // Traverse the array Traverse(nums); - // Find element + // Find an element int index = Find(nums, 3); - Console.WriteLine("Find element 3 in nums, get index = " + index); + Console.WriteLine("Find element 3 in nums, giving index = " + index); } } diff --git a/en/codes/csharp/chapter_array_and_linkedlist/linked_list.cs b/en/codes/csharp/chapter_array_and_linkedlist/linked_list.cs index 4ffa92dc9..7754527fe 100644 --- a/en/codes/csharp/chapter_array_and_linkedlist/linked_list.cs +++ b/en/codes/csharp/chapter_array_and_linkedlist/linked_list.cs @@ -22,7 +22,7 @@ public class linked_list { n0.next = n1; } - /* Access the node at index 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) @@ -47,34 +47,34 @@ public class linked_list { [Test] public void Test() { - // Initialize linked list - // Initialize each node + // Initialize the linked list + // Initialize the individual nodes ListNode n0 = new(1); ListNode n1 = new(3); ListNode n2 = new(2); ListNode n3 = new(5); ListNode n4 = new(4); - // Build references between nodes + // Set up references between the nodes n0.next = n1; n1.next = n2; n2.next = n3; n3.next = n4; - Console.WriteLine($"Initialized linked list is{n0}"); + Console.WriteLine($"Initialized linked list: {n0}"); - // Insert node + // Insert a node Insert(n0, new ListNode(0)); - Console.WriteLine($"Linked list after node insertion is{n0}"); + Console.WriteLine($"Linked list after inserting a node: {n0}"); - // Remove node + // Remove a node Remove(n0); - Console.WriteLine($"Linked list after node deletion is{n0}"); + Console.WriteLine($"Linked list after removing a node: {n0}"); - // Access node + // Access a node ListNode? node = Access(n0, 3); - Console.WriteLine($"Value of node at index 3 in linked list = {node?.val}"); + Console.WriteLine($"Value of the node at index 3 in the linked list = {node?.val}"); - // Search node + // Find a node int index = Find(n0, 2); - Console.WriteLine($"Index of node with value 2 in linked list = {index}"); + Console.WriteLine($"Index of the node with value 2 in the linked list = {index}"); } } diff --git a/en/codes/csharp/chapter_array_and_linkedlist/list.cs b/en/codes/csharp/chapter_array_and_linkedlist/list.cs index 2d441c3ed..c91097272 100644 --- a/en/codes/csharp/chapter_array_and_linkedlist/list.cs +++ b/en/codes/csharp/chapter_array_and_linkedlist/list.cs @@ -10,24 +10,24 @@ public class list { [Test] public void Test() { - /* Initialize list */ + /* Initialize the list */ int[] numbers = [1, 3, 2, 5, 4]; List nums = [.. numbers]; Console.WriteLine("List nums = " + string.Join(",", nums)); - /* Update element */ + /* Access an element */ int num = nums[1]; - Console.WriteLine("Access element at index 1, get num = " + num); + Console.WriteLine("Access the element at index 1, giving num = " + num); - /* Add elements at the end */ + /* Update an element */ nums[1] = 0; - Console.WriteLine("Update element at index 1 to 0, resulting in nums = " + string.Join(",", nums)); + Console.WriteLine("Update the element at index 1 to 0, giving nums = " + string.Join(",", nums)); - /* Remove element */ + /* Clear the list */ nums.Clear(); - Console.WriteLine("After clearing list, nums = " + string.Join(",", nums)); + Console.WriteLine("After clearing the list, nums = " + string.Join(",", nums)); - /* Direct traversal of list elements */ + /* Append at the end */ nums.Add(1); nums.Add(3); nums.Add(2); @@ -35,20 +35,20 @@ public class list { nums.Add(4); Console.WriteLine("After adding elements, nums = " + string.Join(",", nums)); - /* Sort list */ + /* Insert an element in the middle */ nums.Insert(3, 6); - Console.WriteLine("Insert number 6 at index 3, resulting in nums = " + string.Join(",", nums)); + Console.WriteLine("Insert the number 6 at index 3, giving nums = " + string.Join(",", nums)); - /* Remove element */ + /* Remove an element */ nums.RemoveAt(3); - Console.WriteLine("Remove element at index 3, resulting in nums = " + string.Join(",", nums)); + Console.WriteLine("Remove the element at index 3, giving nums = " + string.Join(",", nums)); - /* Traverse list by index */ + /* Traverse the list by index */ int count = 0; for (int i = 0; i < nums.Count; i++) { count += nums[i]; } - /* Directly traverse list elements */ + /* Iterate directly over the list elements */ count = 0; foreach (int x in nums) { count += x; @@ -57,10 +57,10 @@ public class list { /* Concatenate two lists */ List nums1 = [6, 8, 7, 10, 9]; nums.AddRange(nums1); - Console.WriteLine("Concatenate list nums1 to nums, resulting in nums = " + string.Join(",", nums)); + Console.WriteLine("After appending list nums1 to nums, nums = " + string.Join(",", nums)); - /* Sort list */ - nums.Sort(); // After sorting, list elements are arranged from smallest to largest - Console.WriteLine("After sorting list, nums = " + string.Join(",", nums)); + /* Sort the list */ + nums.Sort(); // After sorting, the list elements are in ascending order + Console.WriteLine("After sorting the list, nums = " + string.Join(",", nums)); } } diff --git a/en/codes/csharp/chapter_array_and_linkedlist/my_list.cs b/en/codes/csharp/chapter_array_and_linkedlist/my_list.cs index 2c9892361..7e6709d5e 100644 --- a/en/codes/csharp/chapter_array_and_linkedlist/my_list.cs +++ b/en/codes/csharp/chapter_array_and_linkedlist/my_list.cs @@ -8,44 +8,44 @@ namespace hello_algo.chapter_array_and_linkedlist; /* List class */ class MyList { - private int[] arr; // Array (stores list elements) + private int[] arr; // Array (stores the 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 + private readonly int extendRatio = 2; // Factor by which the list capacity grows each time /* Constructor */ public MyList() { arr = new int[arrCapacity]; } - /* Get list length (current number of elements) */ + /* Get the list length (current number of elements)*/ public int Size() { return arrSize; } - /* Get list capacity */ + /* Get the list capacity */ public int Capacity() { return arrCapacity; } - /* Update element */ + /* Access an element */ public int Get(int index) { - // If the index is out of bounds, throw an exception, as below + // Throw an exception if the index is out of bounds; the same applies below if (index < 0 || index >= arrSize) throw new IndexOutOfRangeException("Index out of bounds"); return arr[index]; } - /* Add elements at the end */ + /* Update an element */ 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 */ + /* Append at the end */ public void Add(int num) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if (arrSize == arrCapacity) ExtendCapacity(); arr[arrSize] = num; @@ -53,14 +53,14 @@ class MyList { arrSize++; } - /* Sort list */ + /* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if (arrSize == arrCapacity) ExtendCapacity(); - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for (int j = arrSize - 1; j >= index; j--) { arr[j + 1] = arr[j]; } @@ -69,12 +69,12 @@ class MyList { arrSize++; } - /* Remove element */ + /* Remove an 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 + // Shift all elements after index `index` one position to the left for (int j = index; j < arrSize - 1; j++) { arr[j] = arr[j + 1]; } @@ -84,17 +84,17 @@ class MyList { return num; } - /* Driver Code */ + /* Grow the list capacity */ public void ExtendCapacity() { - // Create new array of length arrCapacity * extendRatio and copy original array to new array + // Create an array of length arrCapacity * extendRatio and copy the original array into it Array.Resize(ref arr, arrCapacity * extendRatio); - // Add elements at the end + // Update the list capacity arrCapacity = arr.Length; } - /* Convert list to array */ + /* Convert the list to an array */ public int[] ToArray() { - // Elements enqueue + // Convert only the list elements within the occupied range int[] arr = new int[arrSize]; for (int i = 0; i < arrSize; i++) { arr[i] = Get(i); @@ -106,39 +106,39 @@ class MyList { public class my_list { [Test] public void Test() { - /* Initialize list */ + /* Initialize the list */ MyList nums = new(); - /* Direct traversal of list elements */ + /* Append at the end */ nums.Add(1); nums.Add(3); nums.Add(2); nums.Add(5); nums.Add(4); Console.WriteLine("List nums = " + string.Join(",", nums.ToArray()) + - ", capacity = " + nums.Capacity() + ", length = " + nums.Size()); + " , capacity = " + nums.Capacity() + " , size = " + nums.Size()); - /* Sort list */ + /* Insert an element in the middle */ nums.Insert(3, 6); - Console.WriteLine("Insert number 6 at index 3, resulting in nums = " + string.Join(",", nums.ToArray())); + Console.WriteLine("Insert the number 6 at index 3, giving nums = " + string.Join(",", nums.ToArray())); - /* Remove element */ + /* Remove an element */ nums.Remove(3); - Console.WriteLine("Remove element at index 3, resulting in nums = " + string.Join(",", nums.ToArray())); + Console.WriteLine("Remove the element at index 3, giving nums = " + string.Join(",", nums.ToArray())); - /* Update element */ + /* Access an element */ int num = nums.Get(1); - Console.WriteLine("Access element at index 1, get num = " + num); + Console.WriteLine("Access the element at index 1, giving num = " + num); - /* Add elements at the end */ + /* Update an element */ nums.Set(1, 0); - Console.WriteLine("Update element at index 1 to 0, resulting in nums = " + string.Join(",", nums.ToArray())); + Console.WriteLine("Update the element at index 1 to 0, giving nums = " + string.Join(",", nums.ToArray())); - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for (int i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.Add(i); } - Console.WriteLine("List nums after expansion = " + string.Join(",", nums.ToArray()) + - ", capacity = " + nums.Capacity() + ", length = " + nums.Size()); + Console.WriteLine("After expansion, list nums = " + string.Join(",", nums.ToArray()) + + " , capacity = " + nums.Capacity() + " , size = " + nums.Size()); } } diff --git a/en/codes/csharp/chapter_backtracking/n_queens.cs b/en/codes/csharp/chapter_backtracking/n_queens.cs index 762758177..f7de258c6 100644 --- a/en/codes/csharp/chapter_backtracking/n_queens.cs +++ b/en/codes/csharp/chapter_backtracking/n_queens.cs @@ -7,10 +7,10 @@ namespace hello_algo.chapter_backtracking; public class n_queens { - /* Backtracking algorithm: N queens */ + /* Backtracking: n-queens */ void Backtrack(int row, int n, List> state, List>> res, bool[] cols, bool[] diags1, bool[] diags2) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if (row == n) { List> copyState = []; foreach (List sRow in state) { @@ -19,28 +19,28 @@ public class n_queens { res.Add(copyState); return; } - // Traverse all columns + // Iterate over all columns for (int col = 0; col < n; col++) { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square int diag1 = row - col + n - 1; int diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = "Q"; cols[col] = diags1[diag1] = diags2[diag2] = true; - // Place the next row + // Place a queen in the next row Backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = "#"; cols[col] = diags1[diag1] = diags2[diag2] = false; } } } - /* Solve N queens */ + /* Solve the n-queens problem */ List>> NQueens(int n) { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square List> state = []; for (int i = 0; i < n; i++) { List row = []; @@ -49,9 +49,9 @@ public class n_queens { } state.Add(row); } - bool[] cols = new bool[n]; // Record whether there is a queen in the column - bool[] diags1 = new bool[2 * n - 1]; // Record whether there is a queen on the main diagonal - bool[] diags2 = new bool[2 * n - 1]; // Record whether there is a queen on the anti-diagonal + bool[] cols = new bool[n]; // Track whether a column contains a queen + bool[] diags1 = new bool[2 * n - 1]; // Track whether a main diagonal contains a queen + bool[] diags2 = new bool[2 * n - 1]; // Track whether an anti-diagonal contains a queen List>> res = []; Backtrack(0, n, state, res, cols, diags1, diags2); @@ -64,8 +64,8 @@ public class n_queens { int n = 4; List>> res = NQueens(n); - Console.WriteLine("Input board size is " + n); - Console.WriteLine("Total queen placement solutions: " + res.Count + " solutions"); + Console.WriteLine("Input board side length: " + n); + Console.WriteLine("There are " + res.Count + " queen placements"); foreach (List> state in res) { Console.WriteLine("--------------------"); foreach (List row in state) { diff --git a/en/codes/csharp/chapter_backtracking/permutations_i.cs b/en/codes/csharp/chapter_backtracking/permutations_i.cs index 99393f96e..d38fd83ed 100644 --- a/en/codes/csharp/chapter_backtracking/permutations_i.cs +++ b/en/codes/csharp/chapter_backtracking/permutations_i.cs @@ -7,24 +7,24 @@ namespace hello_algo.chapter_backtracking; public class permutations_i { - /* Backtracking algorithm: Permutations I */ + /* Backtracking: permutations I */ void Backtrack(List state, int[] choices, bool[] selected, List> res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.Count == choices.Length) { res.Add(new List(state)); return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choices.Length; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state.Add(choice); - // Proceed to the next round of selection + // Proceed to the next choice Backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.RemoveAt(state.Count - 1); } @@ -45,7 +45,7 @@ public class permutations_i { List> res = PermutationsI(nums); Console.WriteLine("Input array nums = " + string.Join(", ", nums)); - Console.WriteLine("All permutations res = "); + Console.WriteLine("All permutations: res = "); foreach (List permutation in res) { PrintUtil.PrintList(permutation); } diff --git a/en/codes/csharp/chapter_backtracking/permutations_ii.cs b/en/codes/csharp/chapter_backtracking/permutations_ii.cs index 3b955071e..a3f57fbb2 100644 --- a/en/codes/csharp/chapter_backtracking/permutations_ii.cs +++ b/en/codes/csharp/chapter_backtracking/permutations_ii.cs @@ -7,26 +7,26 @@ namespace hello_algo.chapter_backtracking; public class permutations_ii { - /* Backtracking algorithm: Permutations II */ + /* Backtracking: permutations II */ void Backtrack(List state, int[] choices, bool[] selected, List> res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.Count == choices.Length) { res.Add(new List(state)); return; } - // Traverse all choices + // Iterate over all choices HashSet duplicated = []; for (int i = 0; i < choices.Length; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && !duplicated.Contains(choice)) { - // Attempt: make choice, update state - duplicated.Add(choice); // Record the selected element value + // Try: make a choice and update the state + duplicated.Add(choice); // Record the chosen value selected[i] = true; state.Add(choice); - // Proceed to the next round of selection + // Proceed to the next choice Backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.RemoveAt(state.Count - 1); } @@ -47,7 +47,7 @@ public class permutations_ii { List> res = PermutationsII(nums); Console.WriteLine("Input array nums = " + string.Join(", ", nums)); - Console.WriteLine("All permutations res = "); + Console.WriteLine("All permutations: res = "); foreach (List permutation in res) { PrintUtil.PrintList(permutation); } diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_i_compact.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_i_compact.cs index 02f21d533..f76cd889e 100644 --- a/en/codes/csharp/chapter_backtracking/preorder_traversal_i_compact.cs +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_i_compact.cs @@ -9,13 +9,13 @@ namespace hello_algo.chapter_backtracking; public class preorder_traversal_i_compact { List res = []; - /* Preorder traversal: Example 1 */ + /* Preorder traversal: example 1 */ void PreOrder(TreeNode? root) { if (root == null) { return; } if (root.val == 7) { - // Record solution + // Record the solution res.Add(root); } PreOrder(root.left); @@ -25,7 +25,7 @@ public class preorder_traversal_i_compact { [Test] public void Test() { TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); - Console.WriteLine("\nInitialize binary tree"); + Console.WriteLine("\nInitialize the binary tree"); PrintUtil.PrintTree(root); // Preorder traversal diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_ii_compact.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_ii_compact.cs index 56b138210..92a5d6bcd 100644 --- a/en/codes/csharp/chapter_backtracking/preorder_traversal_ii_compact.cs +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_ii_compact.cs @@ -10,15 +10,15 @@ public class preorder_traversal_ii_compact { List path = []; List> res = []; - /* Preorder traversal: Example 2 */ + /* Preorder traversal: example 2 */ void PreOrder(TreeNode? root) { if (root == null) { return; } - // Attempt + // Try path.Add(root); if (root.val == 7) { - // Record solution + // Record the solution res.Add(new List(path)); } PreOrder(root.left); @@ -30,13 +30,13 @@ public class preorder_traversal_ii_compact { [Test] public void Test() { TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); - Console.WriteLine("\nInitialize binary tree"); + Console.WriteLine("\nInitialize the binary tree"); PrintUtil.PrintTree(root); // Preorder traversal PreOrder(root); - Console.WriteLine("\nOutput all paths from root node to node 7"); + Console.WriteLine("\nOutput all paths from the root to nodes with value 7"); foreach (List path in res) { PrintUtil.PrintList(path.Select(p => p.val).ToList()); } diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_compact.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_compact.cs index 6b0317348..1a6d407b3 100644 --- a/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_compact.cs +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_compact.cs @@ -10,16 +10,16 @@ public class preorder_traversal_iii_compact { List path = []; List> res = []; - /* Preorder traversal: Example 3 */ + /* Preorder traversal: example 3 */ void PreOrder(TreeNode? root) { - // Pruning + // Prune if (root == null || root.val == 3) { return; } - // Attempt + // Try path.Add(root); if (root.val == 7) { - // Record solution + // Record the solution res.Add(new List(path)); } PreOrder(root.left); @@ -31,13 +31,13 @@ public class preorder_traversal_iii_compact { [Test] public void Test() { TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); - Console.WriteLine("\nInitialize binary tree"); + Console.WriteLine("\nInitialize the binary tree"); PrintUtil.PrintTree(root); // Preorder traversal PreOrder(root); - Console.WriteLine("\nOutput all paths from root node to node 7, paths do not include nodes with value 3"); + Console.WriteLine("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3"); foreach (List path in res) { PrintUtil.PrintList(path.Select(p => p.val).ToList()); } diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_template.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_template.cs index c25e67775..992c5e959 100644 --- a/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_template.cs +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_template.cs @@ -7,47 +7,47 @@ namespace hello_algo.chapter_backtracking; public class preorder_traversal_iii_template { - /* Check if the current state is a solution */ + /* Check whether the current state is a solution */ bool IsSolution(List state) { return state.Count != 0 && state[^1].val == 7; } - /* Record solution */ + /* Record the solution */ void RecordSolution(List state, List> res) { res.Add(new List(state)); } - /* Check if the choice is valid under the current state */ + /* Check whether the choice is valid in the current state */ bool IsValid(List state, TreeNode choice) { return choice != null && choice.val != 3; } - /* Update state */ + /* Update the state */ void MakeChoice(List state, TreeNode choice) { state.Add(choice); } - /* Restore state */ + /* Restore the state */ void UndoChoice(List state, TreeNode choice) { state.RemoveAt(state.Count - 1); } - /* Backtracking algorithm: Example 3 */ + /* Backtracking: example 3 */ void Backtrack(List state, List choices, List> res) { - // Check if it is a solution + // Check whether this is a solution if (IsSolution(state)) { - // Record solution + // Record the solution RecordSolution(state, res); } - // Traverse all choices + // Iterate over all choices foreach (TreeNode choice in choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (IsValid(state, choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state MakeChoice(state, choice); - // Proceed to the next round of selection + // Proceed to the next choice Backtrack(state, [choice.left!, choice.right!], res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state UndoChoice(state, choice); } } @@ -56,7 +56,7 @@ public class preorder_traversal_iii_template { [Test] public void Test() { TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); - Console.WriteLine("\nInitialize binary tree"); + Console.WriteLine("\nInitialize the binary tree"); PrintUtil.PrintTree(root); // Backtracking algorithm @@ -64,7 +64,7 @@ public class preorder_traversal_iii_template { List choices = [root!]; Backtrack([], choices, res); - Console.WriteLine("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); + Console.WriteLine("\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3"); foreach (List path in res) { PrintUtil.PrintList(path.Select(p => p.val).ToList()); } diff --git a/en/codes/csharp/chapter_backtracking/subset_sum_i.cs b/en/codes/csharp/chapter_backtracking/subset_sum_i.cs index 86ff301df..161e0e6cb 100644 --- a/en/codes/csharp/chapter_backtracking/subset_sum_i.cs +++ b/en/codes/csharp/chapter_backtracking/subset_sum_i.cs @@ -7,26 +7,26 @@ namespace hello_algo.chapter_backtracking; public class subset_sum_i { - /* Backtracking algorithm: Subset sum I */ + /* Backtracking: subset sum I */ void Backtrack(List state, int target, int[] choices, int start, List> res) { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target (remaining target is 0) if (target == 0) { res.Add(new List(state)); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (int i = start; i < choices.Length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice would exceed the remaining target + // The array is sorted, so later elements are at least as large and would also exceed the remaining target if (target - choices[i] < 0) { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.Add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice Backtrack(state, target - choices[i], choices, i, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.RemoveAt(state.Count - 1); } } @@ -35,8 +35,8 @@ public class subset_sum_i { List> SubsetSumI(int[] nums, int target) { List state = []; // State (subset) Array.Sort(nums); // Sort nums - int start = 0; // Start point for traversal - List> res = []; // Result list (subset list) + int start = 0; // Starting index for iteration + List> res = []; // Result list (list of subsets) Backtrack(state, target, nums, start, res); return res; } @@ -47,7 +47,7 @@ public class subset_sum_i { int target = 9; List> res = SubsetSumI(nums, target); Console.WriteLine("Input array nums = " + string.Join(", ", nums) + ", target = " + target); - Console.WriteLine("All subsets with sum equal to " + target + " are res = "); + Console.WriteLine("All subsets with sum " + target + " : res = "); foreach (var subset in res) { PrintUtil.PrintList(subset); } diff --git a/en/codes/csharp/chapter_backtracking/subset_sum_i_naive.cs b/en/codes/csharp/chapter_backtracking/subset_sum_i_naive.cs index 6fc11a833..a691280e3 100644 --- a/en/codes/csharp/chapter_backtracking/subset_sum_i_naive.cs +++ b/en/codes/csharp/chapter_backtracking/subset_sum_i_naive.cs @@ -7,33 +7,33 @@ namespace hello_algo.chapter_backtracking; public class subset_sum_i_naive { - /* Backtracking algorithm: Subset sum I */ + /* Backtracking: subset sum I */ void Backtrack(List state, int target, int total, int[] choices, List> res) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total == target) { res.Add(new List(state)); return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choices.Length; i++) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.Add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice Backtrack(state, target, total + choices[i], choices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.RemoveAt(state.Count - 1); } } - /* Solve subset sum I (including duplicate subsets) */ + /* Solve subset sum I (with duplicate subsets) */ List> SubsetSumINaive(int[] nums, int target) { List state = []; // State (subset) int total = 0; // Subset sum - List> res = []; // Result list (subset list) + List> res = []; // Result list (list of subsets) Backtrack(state, target, total, nums, res); return res; } @@ -44,10 +44,10 @@ public class subset_sum_i_naive { int target = 9; List> res = SubsetSumINaive(nums, target); Console.WriteLine("Input array nums = " + string.Join(", ", nums) + ", target = " + target); - Console.WriteLine("All subsets with sum equal to " + target + " are res = "); + Console.WriteLine("All subsets with sum " + target + " : res = "); foreach (var subset in res) { PrintUtil.PrintList(subset); } - Console.WriteLine("Please note that this method outputs results containing duplicate sets"); + Console.WriteLine("Note that this method produces duplicate subsets"); } } diff --git a/en/codes/csharp/chapter_backtracking/subset_sum_ii.cs b/en/codes/csharp/chapter_backtracking/subset_sum_ii.cs index 05e4eeaf0..b023b5b01 100644 --- a/en/codes/csharp/chapter_backtracking/subset_sum_ii.cs +++ b/en/codes/csharp/chapter_backtracking/subset_sum_ii.cs @@ -7,31 +7,31 @@ namespace hello_algo.chapter_backtracking; public class subset_sum_ii { - /* Backtracking algorithm: Subset sum II */ + /* Backtracking: subset sum II */ void Backtrack(List state, int target, int[] choices, int start, List> res) { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target (remaining target is 0) if (target == 0) { res.Add(new List(state)); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: start after the previously chosen index to avoid choosing the same element again for (int i = start; i < choices.Length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice would exceed the remaining target + // The array is sorted, so later elements are at least as large and would also exceed the remaining target if (target - choices[i] < 0) { break; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if (i > start && choices[i] == choices[i - 1]) { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.Add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice Backtrack(state, target - choices[i], choices, i + 1, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.RemoveAt(state.Count - 1); } } @@ -40,8 +40,8 @@ public class subset_sum_ii { List> SubsetSumII(int[] nums, int target) { List state = []; // State (subset) Array.Sort(nums); // Sort nums - int start = 0; // Start point for traversal - List> res = []; // Result list (subset list) + int start = 0; // Starting index for iteration + List> res = []; // Result list (list of subsets) Backtrack(state, target, nums, start, res); return res; } @@ -52,7 +52,7 @@ public class subset_sum_ii { int target = 9; List> res = SubsetSumII(nums, target); Console.WriteLine("Input array nums = " + string.Join(", ", nums) + ", target = " + target); - Console.WriteLine("All subsets with sum equal to " + target + " are res = "); + Console.WriteLine("All subsets with sum " + target + " : res = "); foreach (var subset in res) { PrintUtil.PrintList(subset); } diff --git a/en/codes/csharp/chapter_computational_complexity/complexity_exercises.cs b/en/codes/csharp/chapter_computational_complexity/complexity_exercises.cs index 051088bd4..336035f3f 100644 --- a/en/codes/csharp/chapter_computational_complexity/complexity_exercises.cs +++ b/en/codes/csharp/chapter_computational_complexity/complexity_exercises.cs @@ -7,7 +7,7 @@ namespace hello_algo.chapter_computational_complexity; public class complexity_exercises { - /* Iterative summation */ + /* Sum using iteration */ int SumIter(int n) { int res = 0; for (int i = 1; i <= n; i++) { @@ -16,7 +16,7 @@ public class complexity_exercises { return res; } - /* Recursive summation */ + /* Sum using recursion */ int SumRecur(int n) { if (n == 1) { return 1; @@ -24,7 +24,7 @@ public class complexity_exercises { return n + SumRecur(n - 1); } - /* Linear loop */ + /* Linear-time loop */ int LinearLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -33,7 +33,7 @@ public class complexity_exercises { return res; } - /* Quadratic loop */ + /* Quadratic-time loop */ int QuadraticLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -44,7 +44,7 @@ public class complexity_exercises { return res; } - /* Logarithmic loop */ + /* Logarithmic-time loop */ int LogarithmicLoop(int n) { while (n > 1) { n /= 2; diff --git a/en/codes/csharp/chapter_computational_complexity/iteration.cs b/en/codes/csharp/chapter_computational_complexity/iteration.cs index 18e37e584..a7836e765 100644 --- a/en/codes/csharp/chapter_computational_complexity/iteration.cs +++ b/en/codes/csharp/chapter_computational_complexity/iteration.cs @@ -10,7 +10,7 @@ public class iteration { /* for loop */ int ForLoop(int n) { int res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (int i = 1; i <= n; i++) { res += i; } @@ -20,11 +20,11 @@ public class iteration { /* while loop */ int WhileLoop(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 2, ..., n-1, n + int i = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i; - i += 1; // Update condition variable + i += 1; // Update the loop control variable } return res; } @@ -32,23 +32,23 @@ public class iteration { /* while loop (two updates) */ int WhileLoopII(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 4, 10, ... + int i = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i; - // Update condition variable + // Update the loop control variable i += 1; i *= 2; } return res; } - /* Nested for loop */ + /* Nested for loops */ string NestedForLoop(int n) { StringBuilder res = new(); - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (int i = 1; i <= n; i++) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (int j = 1; j <= n; j++) { res.Append($"({i}, {j}), "); } @@ -56,22 +56,22 @@ public class iteration { return res.ToString(); } - /* Driver Code */ + /* Driver code */ [Test] public void Test() { int n = 5; int res; res = ForLoop(n); - Console.WriteLine("\nfor loop sum result res = " + res); + Console.WriteLine("\nSum from the for loop: res = " + res); res = WhileLoop(n); - Console.WriteLine("\nwhile loop sum result res = " + res); + Console.WriteLine("\nSum from the while loop: res = " + res); res = WhileLoopII(n); - Console.WriteLine("\nwhile loop (two updates) sum result res = " + res); + Console.WriteLine("\nSum from the while loop (two updates): res = " + res); string resStr = NestedForLoop(n); - Console.WriteLine("\nDouble for loop traversal result " + resStr); + Console.WriteLine("\nTraversal result from the nested for loops: " + resStr); } } diff --git a/en/codes/csharp/chapter_computational_complexity/recursion.cs b/en/codes/csharp/chapter_computational_complexity/recursion.cs index 80e4fed24..583061966 100644 --- a/en/codes/csharp/chapter_computational_complexity/recursion.cs +++ b/en/codes/csharp/chapter_computational_complexity/recursion.cs @@ -9,12 +9,12 @@ namespace hello_algo.chapter_computational_complexity; public class recursion { /* Recursion */ int Recur(int n) { - // Termination condition + // Base case if (n == 1) return 1; - // Recurse: recursive call + // Recurse: make a recursive call int res = Recur(n - 1); - // Return: return result + // Unwind: return the result return n + res; } @@ -23,14 +23,14 @@ public class recursion { // Use an explicit stack to simulate the system call stack Stack stack = new(); int res = 0; - // Recurse: recursive call + // Recurse: make a recursive call for (int i = n; i > 0; i--) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.Push(i); } - // Return: return result + // Unwind: return the result while (stack.Count > 0) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.Pop(); } // res = 1+2+3+...+n @@ -39,40 +39,40 @@ public class recursion { /* Tail recursion */ int TailRecur(int n, int res) { - // Termination condition + // Base case if (n == 0) return res; - // Tail recursive call + // Tail-recursive call return TailRecur(n - 1, res + n); } /* Fibonacci sequence: recursion */ int Fib(int n) { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n == 1 || n == 2) return n - 1; - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) int res = Fib(n - 1) + Fib(n - 2); - // Return result f(n) + // Return the result f(n) return res; } - /* Driver Code */ + /* Driver code */ [Test] public void Test() { int n = 5; int res; res = Recur(n); - Console.WriteLine("\nRecursive function sum result res = " + res); + Console.WriteLine("\nSum from recursion: res = " + res); res = ForLoopRecur(n); - Console.WriteLine("\nUsing iteration to simulate recursive sum result res = " + res); + Console.WriteLine("\nSum from simulating recursion with iteration: res = " + res); res = TailRecur(n, 0); - Console.WriteLine("\nTail recursive function sum result res = " + res); + Console.WriteLine("\nSum from tail recursion: res = " + res); res = Fib(n); - Console.WriteLine("\nThe " + n + "th term of the Fibonacci sequence is " + res); + Console.WriteLine("\nFibonacci term " + n + " is " + res); } } diff --git a/en/codes/csharp/chapter_computational_complexity/space_complexity.cs b/en/codes/csharp/chapter_computational_complexity/space_complexity.cs index c2da20ea7..01613fdfb 100644 --- a/en/codes/csharp/chapter_computational_complexity/space_complexity.cs +++ b/en/codes/csharp/chapter_computational_complexity/space_complexity.cs @@ -13,9 +13,9 @@ public class space_complexity { return 0; } - /* Constant order */ + /* Constant complexity */ void Constant(int n) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space int a = 0; int b = 0; int[] nums = new int[10000]; @@ -24,40 +24,40 @@ public class space_complexity { for (int i = 0; i < n; i++) { int c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for (int i = 0; i < n; i++) { Function(); } } - /* Linear order */ + /* Linear complexity */ void Linear(int n) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space int[] nums = new int[n]; // A list of length n occupies O(n) space List nodes = []; for (int i = 0; i < n; i++) { nodes.Add(new ListNode(i)); } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space Dictionary map = []; for (int i = 0; i < n; i++) { map.Add(i, i.ToString()); } } - /* Linear order (recursive implementation) */ + /* Linear complexity (recursive implementation) */ void LinearRecur(int n) { - Console.WriteLine("Recursion n = " + n); + Console.WriteLine("Recursive call: n = " + n); if (n == 1) return; LinearRecur(n - 1); } - /* Exponential order */ + /* Quadratic complexity */ void Quadratic(int n) { - // Matrix uses O(n^2) space + // The matrix uses O(n^2) space int[,] numMatrix = new int[n, n]; - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space List> numList = []; for (int i = 0; i < n; i++) { List tmp = []; @@ -68,15 +68,15 @@ public class space_complexity { } } - /* Quadratic order (recursive implementation) */ + /* Quadratic complexity (recursive implementation) */ int QuadraticRecur(int n) { if (n <= 0) return 0; int[] nums = new int[n]; - Console.WriteLine("Recursion n = " + n + ", nums length = " + nums.Length); + Console.WriteLine("Recursive call: n = " + n + " with nums length = " + nums.Length); return QuadraticRecur(n - 1); } - /* Driver Code */ + /* Exponential complexity (building a perfect binary tree) */ TreeNode? BuildTree(int n) { if (n == 0) return null; TreeNode root = new(0) { @@ -89,15 +89,15 @@ public class space_complexity { [Test] public void Test() { int n = 5; - // Constant order + // Constant complexity Constant(n); - // Linear order + // Linear complexity Linear(n); LinearRecur(n); - // Exponential order + // Quadratic complexity Quadratic(n); QuadraticRecur(n); - // Exponential order + // Exponential complexity TreeNode? root = BuildTree(n); PrintUtil.PrintTree(root); } diff --git a/en/codes/csharp/chapter_computational_complexity/time_complexity.cs b/en/codes/csharp/chapter_computational_complexity/time_complexity.cs index f26c5d051..82534a6f7 100644 --- a/en/codes/csharp/chapter_computational_complexity/time_complexity.cs +++ b/en/codes/csharp/chapter_computational_complexity/time_complexity.cs @@ -8,13 +8,13 @@ namespace hello_algo.chapter_computational_complexity; public class time_complexity { void Algorithm(int n) { - int a = 1; // +0 (technique 1) - a += n; // +0 (technique 1) - // +n (technique 2) + int a = 1; // +0 (rule 1) + a += n; // +0 (rule 1) + // +n (rule 2) for (int i = 0; i < 5 * n + 1; i++) { Console.WriteLine(0); } - // +n*n (technique 3) + // +n*n (rule 3) for (int i = 0; i < 2 * n; i++) { for (int j = 0; j < n + 1; j++) { Console.WriteLine(0); @@ -41,7 +41,7 @@ public class time_complexity { } } - /* Constant order */ + /* Constant complexity */ int Constant(int n) { int count = 0; int size = 100000; @@ -50,7 +50,7 @@ public class time_complexity { return count; } - /* Linear order */ + /* Linear complexity */ int Linear(int n) { int count = 0; for (int i = 0; i < n; i++) @@ -58,20 +58,20 @@ public class time_complexity { return count; } - /* Linear order (traversing array) */ + /* Linear complexity (array traversal) */ int ArrayTraversal(int[] nums) { int count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length foreach (int num in nums) { count++; } return count; } - /* Exponential order */ + /* Quadratic complexity */ int Quadratic(int n) { int count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -80,27 +80,27 @@ public class time_complexity { return count; } - /* Quadratic order (bubble sort) */ + /* Quadratic complexity (bubble sort) */ int BubbleSort(int[] nums) { int count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.Length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } return count; } - /* Exponential order (loop implementation) */ + /* Exponential complexity (iterative implementation) */ int Exponential(int n) { int count = 0, bas = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (int i = 0; i < n; i++) { for (int j = 0; j < bas; j++) { count++; @@ -111,13 +111,13 @@ public class time_complexity { return count; } - /* Exponential order (recursive implementation) */ + /* Exponential complexity (recursive implementation) */ int ExpRecur(int n) { if (n == 1) return 1; return ExpRecur(n - 1) + ExpRecur(n - 1) + 1; } - /* Logarithmic order (loop implementation) */ + /* Logarithmic complexity (iterative implementation) */ int Logarithmic(int n) { int count = 0; while (n > 1) { @@ -127,13 +127,13 @@ public class time_complexity { return count; } - /* Logarithmic order (recursive implementation) */ + /* Logarithmic complexity (recursive implementation) */ int LogRecur(int n) { if (n <= 1) return 0; return LogRecur(n / 2) + 1; } - /* Linearithmic order */ + /* Linearithmic complexity */ int LinearLogRecur(int n) { if (n <= 1) return 1; int count = LinearLogRecur(n / 2) + LinearLogRecur(n / 2); @@ -143,11 +143,11 @@ public class time_complexity { return count; } - /* Factorial order (recursive implementation) */ + /* Factorial complexity (recursive implementation) */ int FactorialRecur(int n) { if (n == 0) return 1; int count = 0; - // Split from 1 into n + // Split one problem into n subproblems for (int i = 0; i < n; i++) { count += FactorialRecur(n - 1); } @@ -156,40 +156,40 @@ public class time_complexity { [Test] public void Test() { - // You can modify n to run and observe the trend of the number of operations for various complexities + // Try different values of n to see how the operation count grows for each complexity int n = 8; - Console.WriteLine("Input data size n = " + n); + Console.WriteLine("Input size n = " + n); int count = Constant(n); - Console.WriteLine("Constant order operation count = " + count); + Console.WriteLine("Operation count for constant complexity = " + count); count = Linear(n); - Console.WriteLine("Linear order operation count = " + count); + Console.WriteLine("Operation count for linear complexity = " + count); count = ArrayTraversal(new int[n]); - Console.WriteLine("Linear order (array traversal) operation count = " + count); + Console.WriteLine("Operation count for linear complexity (array traversal) = " + count); count = Quadratic(n); - Console.WriteLine("Quadratic order operation count = " + count); + Console.WriteLine("Operation count for quadratic complexity = " + count); int[] nums = new int[n]; for (int i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = BubbleSort(nums); - Console.WriteLine("Quadratic order (bubble sort) operation count = " + count); + Console.WriteLine("Operation count for quadratic complexity (bubble sort) = " + count); count = Exponential(n); - Console.WriteLine("Exponential order (loop implementation) operation count = " + count); + Console.WriteLine("Operation count for exponential complexity (iterative implementation) = " + count); count = ExpRecur(n); - Console.WriteLine("Exponential order (recursive implementation) operation count = " + count); + Console.WriteLine("Operation count for exponential complexity (recursive implementation) = " + count); count = Logarithmic(n); - Console.WriteLine("Logarithmic order (loop implementation) operation count = " + count); + Console.WriteLine("Operation count for logarithmic complexity (iterative implementation) = " + count); count = LogRecur(n); - Console.WriteLine("Logarithmic order (recursive implementation) operation count = " + count); + Console.WriteLine("Operation count for logarithmic complexity (recursive implementation) = " + count); count = LinearLogRecur(n); - Console.WriteLine("Linearithmic order (recursive implementation) operation count = " + count); + Console.WriteLine("Operation count for linearithmic complexity (recursive implementation) = " + count); count = FactorialRecur(n); - Console.WriteLine("Factorial order (recursive implementation) operation count = " + count); + Console.WriteLine("Operation count for factorial complexity (recursive implementation) = " + count); } } diff --git a/en/codes/csharp/chapter_computational_complexity/worst_best_time_complexity.cs b/en/codes/csharp/chapter_computational_complexity/worst_best_time_complexity.cs index 0cc737b73..e82c36ea0 100644 --- a/en/codes/csharp/chapter_computational_complexity/worst_best_time_complexity.cs +++ b/en/codes/csharp/chapter_computational_complexity/worst_best_time_complexity.cs @@ -7,15 +7,15 @@ namespace hello_algo.chapter_computational_complexity; public class worst_best_time_complexity { - /* Generate an array with elements { 1, 2, ..., n }, order shuffled */ + /* Generate an array containing { 1, 2, ..., n } in shuffled order */ int[] RandomNumbers(int n) { int[] nums = new int[n]; - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (int i = 0; i < n; i++) { nums[i] = i + 1; } - // Randomly shuffle array elements + // Randomly shuffle the array elements for (int i = 0; i < nums.Length; i++) { int index = new Random().Next(i, nums.Length); (nums[i], nums[index]) = (nums[index], nums[i]); @@ -23,11 +23,11 @@ public class worst_best_time_complexity { return nums; } - /* Find the index of number 1 in array nums */ + /* Find the index of the number 1 in array nums */ int FindOne(int[] nums) { for (int i = 0; i < nums.Length; i++) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] == 1) return i; } @@ -35,7 +35,7 @@ public class worst_best_time_complexity { } - /* Driver Code */ + /* Driver code */ [Test] public void Test() { for (int i = 0; i < 10; i++) { @@ -43,7 +43,7 @@ public class worst_best_time_complexity { int[] nums = RandomNumbers(n); int index = FindOne(nums); Console.WriteLine("\nArray [ 1, 2, ..., n ] after shuffling = " + string.Join(",", nums)); - Console.WriteLine("Index of number 1 is " + index); + Console.WriteLine("The index of the number 1 is " + index); } } } diff --git a/en/codes/csharp/chapter_divide_and_conquer/binary_search_recur.cs b/en/codes/csharp/chapter_divide_and_conquer/binary_search_recur.cs index bf97c0886..00364f6b0 100644 --- a/en/codes/csharp/chapter_divide_and_conquer/binary_search_recur.cs +++ b/en/codes/csharp/chapter_divide_and_conquer/binary_search_recur.cs @@ -9,20 +9,20 @@ namespace hello_algo.chapter_divide_and_conquer; public class binary_search_recur { /* Binary search: problem f(i, j) */ int DFS(int[] nums, int target, int i, int j) { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1; } // Calculate the midpoint index m int m = (i + j) / 2; if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return DFS(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return DFS(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -30,7 +30,7 @@ public class binary_search_recur { /* Binary search */ int BinarySearch(int[] nums, int target) { int n = nums.Length; - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return DFS(nums, target, 0, n - 1); } @@ -39,7 +39,7 @@ public class binary_search_recur { int target = 6; int[] nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; - // Binary search (closed interval on both sides) + // Binary search (closed interval) int index = BinarySearch(nums, target); Console.WriteLine("Index of target element 6 = " + index); } diff --git a/en/codes/csharp/chapter_divide_and_conquer/build_tree.cs b/en/codes/csharp/chapter_divide_and_conquer/build_tree.cs index ee06ced60..29dd54c0b 100644 --- a/en/codes/csharp/chapter_divide_and_conquer/build_tree.cs +++ b/en/codes/csharp/chapter_divide_and_conquer/build_tree.cs @@ -7,14 +7,14 @@ namespace hello_algo.chapter_divide_and_conquer; public class build_tree { - /* Build binary tree: divide and conquer */ + /* Build a binary tree: divide and conquer */ TreeNode? DFS(int[] preorder, Dictionary inorderMap, int i, int l, int r) { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) return null; // Initialize the root node TreeNode root = new(preorder[i]); - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees int m = inorderMap[preorder[i]]; // Subproblem: build the left subtree root.left = DFS(preorder, inorderMap, i + 1, l, m - 1); @@ -24,9 +24,9 @@ public class build_tree { return root; } - /* Build binary tree */ + /* Build a binary tree */ TreeNode? BuildTree(int[] preorder, int[] inorder) { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index Dictionary inorderMap = []; for (int i = 0; i < inorder.Length; i++) { inorderMap.TryAdd(inorder[i], i); diff --git a/en/codes/csharp/chapter_divide_and_conquer/fast_power.cs b/en/codes/csharp/chapter_divide_and_conquer/fast_power.cs index 0f7361152..e6ce310bc 100644 --- a/en/codes/csharp/chapter_divide_and_conquer/fast_power.cs +++ b/en/codes/csharp/chapter_divide_and_conquer/fast_power.cs @@ -7,7 +7,7 @@ namespace hello_algo.chapter_divide_and_conquer; public class fast_power { - /* Exponentiation by squaring */ + /* Fast exponentiation */ int FastPow(int x, int n) { if (n == 0) { return 1; diff --git a/en/codes/csharp/chapter_divide_and_conquer/hanota.cs b/en/codes/csharp/chapter_divide_and_conquer/hanota.cs index a6a1cd29e..aaa0483ff 100644 --- a/en/codes/csharp/chapter_divide_and_conquer/hanota.cs +++ b/en/codes/csharp/chapter_divide_and_conquer/hanota.cs @@ -7,9 +7,9 @@ namespace hello_algo.chapter_divide_and_conquer; public class hanota { - /* Move a disk */ + /* Move one disk */ void Move(List src, List tar) { - // Take out a disk from the top of src + // Take a disk from the top of src int pan = src[^1]; src.RemoveAt(src.Count - 1); // Place the disk on top of tar @@ -18,14 +18,14 @@ public class hanota { /* Solve the Tower of Hanoi problem f(i) */ void DFS(int i, List src, List buf, List tar) { - // If there is only one disk left in src, move it directly to tar + // If only one disk in this subproblem remains on src, move it directly to tar if (i == 1) { Move(src, tar); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar DFS(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar Move(src, tar); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src DFS(i - 1, buf, src, tar); @@ -40,18 +40,18 @@ public class hanota { [Test] public void Test() { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg List A = [5, 4, 3, 2, 1]; List B = []; List C = []; - Console.WriteLine("In initial state:"); + Console.WriteLine("Initial state:"); Console.WriteLine("A = " + string.Join(", ", A)); Console.WriteLine("B = " + string.Join(", ", B)); Console.WriteLine("C = " + string.Join(", ", C)); SolveHanota(A, B, C); - Console.WriteLine("After disk movement is complete:"); + Console.WriteLine("After moving the disks:"); Console.WriteLine("A = " + string.Join(", ", A)); Console.WriteLine("B = " + string.Join(", ", B)); Console.WriteLine("C = " + string.Join(", ", C)); diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_backtrack.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_backtrack.cs index 8e2b4fbe7..c99b48df4 100644 --- a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_backtrack.cs +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_backtrack.cs @@ -9,25 +9,25 @@ namespace hello_algo.chapter_dynamic_programming; public class climbing_stairs_backtrack { /* Backtracking */ void Backtrack(List choices, int state, int n, List res) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state == n) res[0]++; - // Traverse all choices + // Iterate over all choices foreach (int choice in choices) { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue; - // Attempt: make choice, update state + // Try: make a choice and update the state Backtrack(choices, state + choice, n, res); // Backtrack } } - /* Climbing stairs: Backtracking */ + /* Climbing stairs: backtracking */ int ClimbingStairsBacktrack(int n) { - List choices = [1, 2]; // Can choose to climb up 1 or 2 stairs - int state = 0; // Start climbing from the 0-th stair - List res = [0]; // Use res[0] to record the solution count + List choices = [1, 2]; // Choose to climb 1 or 2 steps + int state = 0; // Start climbing from step 0 + List res = [0]; // Use res[0] to record the number of ways Backtrack(choices, state, n, res); return res[0]; } @@ -36,6 +36,6 @@ public class climbing_stairs_backtrack { public void Test() { int n = 9; int res = ClimbingStairsBacktrack(n); - Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + Console.WriteLine($"Number of ways to climb {n} stairs: {res}"); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cs index 18ad6a88b..59229a11a 100644 --- a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cs +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cs @@ -7,19 +7,19 @@ namespace hello_algo.chapter_dynamic_programming; public class climbing_stairs_constraint_dp { - /* Climbing stairs with constraint: Dynamic programming */ + /* Climbing stairs with constraints: dynamic programming */ int ClimbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int[,] dp = new int[n + 1, 3]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1, 1] = 1; dp[1, 2] = 0; dp[2, 1] = 0; dp[2, 2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i, 1] = dp[i - 1, 2]; dp[i, 2] = dp[i - 2, 1] + dp[i - 2, 2]; @@ -31,6 +31,6 @@ public class climbing_stairs_constraint_dp { public void Test() { int n = 9; int res = ClimbingStairsConstraintDP(n); - Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + Console.WriteLine($"Number of ways to climb {n} stairs: {res}"); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs.cs index 017bbca79..905c9c19e 100644 --- a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs.cs +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs.cs @@ -9,7 +9,7 @@ namespace hello_algo.chapter_dynamic_programming; public class climbing_stairs_dfs { /* Search */ int DFS(int i) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; // dp[i] = dp[i-1] + dp[i-2] @@ -17,7 +17,7 @@ public class climbing_stairs_dfs { return count; } - /* Climbing stairs: Search */ + /* Climbing stairs: search */ int ClimbingStairsDFS(int n) { return DFS(n); } @@ -26,6 +26,6 @@ public class climbing_stairs_dfs { public void Test() { int n = 9; int res = ClimbingStairsDFS(n); - Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + Console.WriteLine($"Number of ways to climb {n} stairs: {res}"); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cs index 06dc5c9c7..ea6721470 100644 --- a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cs +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cs @@ -7,12 +7,12 @@ namespace hello_algo.chapter_dynamic_programming; public class climbing_stairs_dfs_mem { - /* Memoization search */ + /* Memoized search */ int DFS(int i, int[] mem) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] @@ -22,9 +22,9 @@ public class climbing_stairs_dfs_mem { return count; } - /* Climbing stairs: Memoization search */ + /* Climbing stairs: memoized search */ int ClimbingStairsDFSMem(int n) { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded int[] mem = new int[n + 1]; Array.Fill(mem, -1); return DFS(n, mem); @@ -34,6 +34,6 @@ public class climbing_stairs_dfs_mem { public void Test() { int n = 9; int res = ClimbingStairsDFSMem(n); - Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + Console.WriteLine($"Number of ways to climb {n} stairs: {res}"); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dp.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dp.cs index 57489392a..c41ed4bcf 100644 --- a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dp.cs +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dp.cs @@ -7,23 +7,23 @@ namespace hello_algo.chapter_dynamic_programming; public class climbing_stairs_dp { - /* Climbing stairs: Dynamic programming */ + /* Climbing stairs: dynamic programming */ int ClimbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int[] dp = new int[n + 1]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } - /* Climbing stairs: Space-optimized dynamic programming */ + /* Climbing stairs: space-optimized dynamic programming */ int ClimbingStairsDPComp(int n) { if (n == 1 || n == 2) return n; @@ -41,9 +41,9 @@ public class climbing_stairs_dp { int n = 9; int res = ClimbingStairsDP(n); - Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + Console.WriteLine($"Number of ways to climb {n} stairs: {res}"); res = ClimbingStairsDPComp(n); - Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + Console.WriteLine($"Number of ways to climb {n} stairs: {res}"); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/coin_change.cs b/en/codes/csharp/chapter_dynamic_programming/coin_change.cs index 13570b358..a52684d0f 100644 --- a/en/codes/csharp/chapter_dynamic_programming/coin_change.cs +++ b/en/codes/csharp/chapter_dynamic_programming/coin_change.cs @@ -7,24 +7,24 @@ namespace hello_algo.chapter_dynamic_programming; public class coin_change { - /* Coin change: Dynamic programming */ + /* Coin change: dynamic programming */ int CoinChangeDP(int[] coins, int amt) { int n = coins.Length; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table int[,] dp = new int[n + 1, amt + 1]; - // State transition: first row and first column + // State transitions: first row and first column for (int a = 1; a <= amt; a++) { dp[0, a] = MAX; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i, a] = dp[i - 1, a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i, a] = Math.Min(dp[i - 1, a], dp[i, a - coins[i - 1]] + 1); } } @@ -32,22 +32,22 @@ public class coin_change { return dp[n, amt] != MAX ? dp[n, amt] : -1; } - /* Coin change: Space-optimized dynamic programming */ + /* Coin change: space-optimized dynamic programming */ int CoinChangeDPComp(int[] coins, int amt) { int n = coins.Length; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table int[] dp = new int[amt + 1]; Array.Fill(dp, MAX); dp[0] = 0; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = Math.Min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -62,10 +62,10 @@ public class coin_change { // Dynamic programming int res = CoinChangeDP(coins, amt); - Console.WriteLine("Minimum number of coins needed to make target amount is " + res); + Console.WriteLine("Minimum number of coins needed to make the target amount: " + res); // Space-optimized dynamic programming res = CoinChangeDPComp(coins, amt); - Console.WriteLine("Minimum number of coins needed to make target amount is " + res); + Console.WriteLine("Minimum number of coins needed to make the target amount: " + res); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/coin_change_ii.cs b/en/codes/csharp/chapter_dynamic_programming/coin_change_ii.cs index 1dc91adce..80e36b286 100644 --- a/en/codes/csharp/chapter_dynamic_programming/coin_change_ii.cs +++ b/en/codes/csharp/chapter_dynamic_programming/coin_change_ii.cs @@ -7,23 +7,23 @@ namespace hello_algo.chapter_dynamic_programming; public class coin_change_ii { - /* Coin change II: Dynamic programming */ + /* Coin change II: dynamic programming */ int CoinChangeIIDP(int[] coins, int amt) { int n = coins.Length; - // Initialize dp table + // Initialize the dp table int[,] dp = new int[n + 1, amt + 1]; - // Initialize first column + // Initialize the first column for (int i = 0; i <= n; i++) { dp[i, 0] = 1; } - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i, a] = dp[i - 1, a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i, a] = dp[i - 1, a] + dp[i, a - coins[i - 1]]; } } @@ -31,20 +31,20 @@ public class coin_change_ii { return dp[n, amt]; } - /* Coin change II: Space-optimized dynamic programming */ + /* Coin change II: space-optimized dynamic programming */ int CoinChangeIIDPComp(int[] coins, int amt) { int n = coins.Length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[amt + 1]; dp[0] = 1; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -59,10 +59,10 @@ public class coin_change_ii { // Dynamic programming int res = CoinChangeIIDP(coins, amt); - Console.WriteLine("Number of coin combinations to make target amount is " + res); + Console.WriteLine("Number of coin combinations that make the target amount: " + res); // Space-optimized dynamic programming res = CoinChangeIIDPComp(coins, amt); - Console.WriteLine("Number of coin combinations to make target amount is " + res); + Console.WriteLine("Number of coin combinations that make the target amount: " + res); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/edit_distance.cs b/en/codes/csharp/chapter_dynamic_programming/edit_distance.cs index 3a8a2a4b4..c40d08b2f 100644 --- a/en/codes/csharp/chapter_dynamic_programming/edit_distance.cs +++ b/en/codes/csharp/chapter_dynamic_programming/edit_distance.cs @@ -7,73 +7,73 @@ namespace hello_algo.chapter_dynamic_programming; public class edit_distance { - /* Edit distance: Brute-force search */ + /* Edit distance: brute-force search */ int EditDistanceDFS(string s, string t, int i, int j) { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if (i == 0) return j; - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if (j == 0) return i; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return EditDistanceDFS(s, t, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = EditDistanceDFS(s, t, i, j - 1); int delete = EditDistanceDFS(s, t, i - 1, j); int replace = EditDistanceDFS(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits return Math.Min(Math.Min(insert, delete), replace) + 1; } - /* Edit distance: Memoization search */ + /* Edit distance: memoized search */ int EditDistanceDFSMem(string s, string t, int[][] mem, int i, int j) { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if (i == 0) return j; - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if (j == 0) return i; - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) return mem[i][j]; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return EditDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = EditDistanceDFSMem(s, t, mem, i, j - 1); int delete = EditDistanceDFSMem(s, t, mem, i - 1, j); int replace = EditDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = Math.Min(Math.Min(insert, delete), replace) + 1; return mem[i][j]; } - /* Edit distance: Dynamic programming */ + /* Edit distance: dynamic programming */ int EditDistanceDP(string s, string t) { int n = s.Length, m = t.Length; int[,] dp = new int[n + 1, m + 1]; - // State transition: first row and first column + // State transitions: first row and first column for (int i = 1; i <= n; i++) { dp[i, 0] = i; } for (int j = 1; j <= m; j++) { dp[0, j] = j; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i, j] = dp[i - 1, j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i, j] = Math.Min(Math.Min(dp[i, j - 1], dp[i - 1, j]), dp[i - 1, j - 1]) + 1; } } @@ -81,30 +81,30 @@ public class edit_distance { return dp[n, m]; } - /* Edit distance: Space-optimized dynamic programming */ + /* Edit distance: space-optimized dynamic programming */ int EditDistanceDPComp(string s, string t) { int n = s.Length, m = t.Length; int[] dp = new int[m + 1]; - // State transition: first row + // State transitions: first row for (int j = 1; j <= m; j++) { dp[j] = j; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i <= n; i++) { - // State transition: first column + // State transitions: first column int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = Math.Min(Math.Min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } return dp[m]; @@ -118,9 +118,9 @@ public class edit_distance { // Brute-force search int res = EditDistanceDFS(s, t, n, m); - Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + Console.WriteLine("Changing " + s + " to " + t + " requires at least " + res + " edits"); - // Memoization search + // Memoized search int[][] mem = new int[n + 1][]; for (int i = 0; i <= n; i++) { mem[i] = new int[m + 1]; @@ -128,14 +128,14 @@ public class edit_distance { } res = EditDistanceDFSMem(s, t, mem, n, m); - Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + Console.WriteLine("Changing " + s + " to " + t + " requires at least " + res + " edits"); // Dynamic programming res = EditDistanceDP(s, t); - Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + Console.WriteLine("Changing " + s + " to " + t + " requires at least " + res + " edits"); // Space-optimized dynamic programming res = EditDistanceDPComp(s, t); - Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + Console.WriteLine("Changing " + s + " to " + t + " requires at least " + res + " edits"); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/knapsack.cs b/en/codes/csharp/chapter_dynamic_programming/knapsack.cs index d830861a5..c0d74eb41 100644 --- a/en/codes/csharp/chapter_dynamic_programming/knapsack.cs +++ b/en/codes/csharp/chapter_dynamic_programming/knapsack.cs @@ -7,58 +7,58 @@ namespace hello_algo.chapter_dynamic_programming; public class knapsack { - /* 0-1 knapsack: Brute-force search */ + /* 0-1 knapsack: brute-force search */ int KnapsackDFS(int[] weight, int[] val, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (weight[i - 1] > c) { return KnapsackDFS(weight, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = KnapsackDFS(weight, val, i - 1, c); int yes = KnapsackDFS(weight, val, i - 1, c - weight[i - 1]) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices return Math.Max(no, yes); } - /* 0-1 knapsack: Memoization search */ + /* 0-1 knapsack: memoized search */ int KnapsackDFSMem(int[] weight, int[] val, int[][] mem, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] != -1) { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (weight[i - 1] > c) { return KnapsackDFSMem(weight, val, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = KnapsackDFSMem(weight, val, mem, i - 1, c); int yes = KnapsackDFSMem(weight, val, mem, i - 1, c - weight[i - 1]) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = Math.Max(no, yes); return mem[i][c]; } - /* 0-1 knapsack: Dynamic programming */ + /* 0-1 knapsack: dynamic programming */ int KnapsackDP(int[] weight, int[] val, int cap) { int n = weight.Length; - // Initialize dp table + // Initialize the dp table int[,] dp = new int[n + 1, cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (weight[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i, c] = dp[i - 1, c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i, c] = Math.Max(dp[i - 1, c - weight[i - 1]] + val[i - 1], dp[i - 1, c]); } } @@ -66,20 +66,20 @@ public class knapsack { return dp[n, cap]; } - /* 0-1 knapsack: Space-optimized dynamic programming */ + /* 0-1 knapsack: space-optimized dynamic programming */ int KnapsackDPComp(int[] weight, int[] val, int cap) { int n = weight.Length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { - // Traverse in reverse order + // Traverse in decreasing order for (int c = cap; c > 0; c--) { if (weight[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.Max(dp[c], dp[c - weight[i - 1]] + val[i - 1]); } } @@ -96,23 +96,23 @@ public class knapsack { // Brute-force search int res = KnapsackDFS(weight, val, n, cap); - Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + Console.WriteLine("Maximum total item value within the knapsack capacity: " + res); - // Memoization search + // Memoized search int[][] mem = new int[n + 1][]; for (int i = 0; i <= n; i++) { mem[i] = new int[cap + 1]; Array.Fill(mem[i], -1); } res = KnapsackDFSMem(weight, val, mem, n, cap); - Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + Console.WriteLine("Maximum total item value within the knapsack capacity: " + res); // Dynamic programming res = KnapsackDP(weight, val, cap); - Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + Console.WriteLine("Maximum total item value within the knapsack capacity: " + res); // Space-optimized dynamic programming res = KnapsackDPComp(weight, val, cap); - Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + Console.WriteLine("Maximum total item value within the knapsack capacity: " + res); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cs b/en/codes/csharp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cs index f824d23db..27a7b642d 100644 --- a/en/codes/csharp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cs +++ b/en/codes/csharp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cs @@ -7,24 +7,24 @@ namespace hello_algo.chapter_dynamic_programming; public class min_cost_climbing_stairs_dp { - /* Minimum cost climbing stairs: Dynamic programming */ + /* Minimum cost to climb stairs: dynamic programming */ int MinCostClimbingStairsDP(int[] cost) { int n = cost.Length - 1; if (n == 1 || n == 2) return cost[n]; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int[] dp = new int[n + 1]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = Math.Min(dp[i - 1], dp[i - 2]) + cost[i]; } return dp[n]; } - /* Minimum cost climbing stairs: Space-optimized dynamic programming */ + /* Minimum cost to climb stairs: space-optimized dynamic programming */ int MinCostClimbingStairsDPComp(int[] cost) { int n = cost.Length - 1; if (n == 1 || n == 2) @@ -41,13 +41,13 @@ public class min_cost_climbing_stairs_dp { [Test] public void Test() { int[] cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; - Console.WriteLine("Input stair cost list is"); + Console.WriteLine("Input stair costs:"); PrintUtil.PrintList(cost); int res = MinCostClimbingStairsDP(cost); - Console.WriteLine($"Minimum cost to climb stairs is {res}"); + Console.WriteLine($"Minimum cost to finish climbing the stairs: {res}"); res = MinCostClimbingStairsDPComp(cost); - Console.WriteLine($"Minimum cost to climb stairs is {res}"); + Console.WriteLine($"Minimum cost to finish climbing the stairs: {res}"); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/min_path_sum.cs b/en/codes/csharp/chapter_dynamic_programming/min_path_sum.cs index 9740f092a..f4c3f602c 100644 --- a/en/codes/csharp/chapter_dynamic_programming/min_path_sum.cs +++ b/en/codes/csharp/chapter_dynamic_programming/min_path_sum.cs @@ -7,60 +7,60 @@ namespace hello_algo.chapter_dynamic_programming; public class min_path_sum { - /* Minimum path sum: Brute-force search */ + /* Minimum path sum: brute-force search */ int MinPathSumDFS(int[][] grid, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return int.MaxValue; } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) int up = MinPathSumDFS(grid, i - 1, j); int left = MinPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return Math.Min(left, up) + grid[i][j]; } - /* Minimum path sum: Memoization search */ + /* Minimum path sum: memoized search */ int MinPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return int.MaxValue; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above int up = MinPathSumDFSMem(grid, mem, i - 1, j); int left = MinPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = Math.Min(left, up) + grid[i][j]; return mem[i][j]; } - /* Minimum path sum: Dynamic programming */ + /* Minimum path sum: dynamic programming */ int MinPathSumDP(int[][] grid) { int n = grid.Length, m = grid[0].Length; - // Initialize dp table + // Initialize the dp table int[,] dp = new int[n, m]; dp[0, 0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (int j = 1; j < m; j++) { dp[0, j] = dp[0, j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for (int i = 1; i < n; i++) { dp[i, 0] = dp[i - 1, 0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i, j] = Math.Min(dp[i, j - 1], dp[i - 1, j]) + grid[i][j]; @@ -69,21 +69,21 @@ public class min_path_sum { return dp[n - 1, m - 1]; } - /* Minimum path sum: Space-optimized dynamic programming */ + /* Minimum path sum: space-optimized dynamic programming */ int MinPathSumDPComp(int[][] grid) { int n = grid.Length, m = grid[0].Length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[m]; dp[0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i < n; i++) { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j < m; j++) { dp[j] = Math.Min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -105,23 +105,23 @@ public class min_path_sum { // Brute-force search int res = MinPathSumDFS(grid, n - 1, m - 1); - Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + Console.WriteLine("Minimum path sum from the top-left to the bottom-right corner: " + res); - // Memoization search + // Memoized search int[][] mem = new int[n][]; for (int i = 0; i < n; i++) { mem[i] = new int[m]; Array.Fill(mem[i], -1); } res = MinPathSumDFSMem(grid, mem, n - 1, m - 1); - Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + Console.WriteLine("Minimum path sum from the top-left to the bottom-right corner: " + res); // Dynamic programming res = MinPathSumDP(grid); - Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + Console.WriteLine("Minimum path sum from the top-left to the bottom-right corner: " + res); // Space-optimized dynamic programming res = MinPathSumDPComp(grid); - Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + Console.WriteLine("Minimum path sum from the top-left to the bottom-right corner: " + res); } } diff --git a/en/codes/csharp/chapter_dynamic_programming/unbounded_knapsack.cs b/en/codes/csharp/chapter_dynamic_programming/unbounded_knapsack.cs index a16442f04..a3ab53372 100644 --- a/en/codes/csharp/chapter_dynamic_programming/unbounded_knapsack.cs +++ b/en/codes/csharp/chapter_dynamic_programming/unbounded_knapsack.cs @@ -7,19 +7,19 @@ namespace hello_algo.chapter_dynamic_programming; public class unbounded_knapsack { - /* Unbounded knapsack: Dynamic programming */ + /* Unbounded knapsack: dynamic programming */ int UnboundedKnapsackDP(int[] wgt, int[] val, int cap) { int n = wgt.Length; - // Initialize dp table + // Initialize the dp table int[,] dp = new int[n + 1, cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i, c] = dp[i - 1, c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i, c] = Math.Max(dp[i - 1, c], dp[i, c - wgt[i - 1]] + val[i - 1]); } } @@ -27,19 +27,19 @@ public class unbounded_knapsack { return dp[n, cap]; } - /* Unbounded knapsack: Space-optimized dynamic programming */ + /* Unbounded knapsack: space-optimized dynamic programming */ int UnboundedKnapsackDPComp(int[] wgt, int[] val, int cap) { int n = wgt.Length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.Max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -55,10 +55,10 @@ public class unbounded_knapsack { // Dynamic programming int res = UnboundedKnapsackDP(wgt, val, cap); - Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + Console.WriteLine("Maximum total item value within the knapsack capacity: " + res); // Space-optimized dynamic programming res = UnboundedKnapsackDPComp(wgt, val, cap); - Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + Console.WriteLine("Maximum total item value within the knapsack capacity: " + res); } } diff --git a/en/codes/csharp/chapter_graph/graph_adjacency_list.cs b/en/codes/csharp/chapter_graph/graph_adjacency_list.cs index 15349cd3c..822560f28 100644 --- a/en/codes/csharp/chapter_graph/graph_adjacency_list.cs +++ b/en/codes/csharp/chapter_graph/graph_adjacency_list.cs @@ -6,9 +6,9 @@ namespace hello_algo.chapter_graph; -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ public class GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors public Dictionary> adjList; /* Constructor */ @@ -27,7 +27,7 @@ public class GraphAdjList { return adjList.Count; } - /* Add edge */ + /* Add an edge */ public void AddEdge(Vertex vet1, Vertex vet2) { if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) throw new InvalidOperationException(); @@ -36,7 +36,7 @@ public class GraphAdjList { adjList[vet2].Add(vet1); } - /* Remove edge */ + /* Remove an edge */ public void RemoveEdge(Vertex vet1, Vertex vet2) { if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) throw new InvalidOperationException(); @@ -45,27 +45,27 @@ public class GraphAdjList { adjList[vet2].Remove(vet1); } - /* Add vertex */ + /* Add a vertex */ public void AddVertex(Vertex vet) { if (adjList.ContainsKey(vet)) return; - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list adjList.Add(vet, []); } - /* Remove vertex */ + /* Remove a vertex */ public void RemoveVertex(Vertex vet) { if (!adjList.ContainsKey(vet)) throw new InvalidOperationException(); - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list adjList.Remove(vet); - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet foreach (List list in adjList.Values) { list.Remove(vet); } } - /* Print adjacency list */ + /* Print the adjacency list */ public void Print() { Console.WriteLine("Adjacency list ="); foreach (KeyValuePair> pair in adjList) { @@ -80,7 +80,7 @@ public class GraphAdjList { public class graph_adjacency_list { [Test] public void Test() { - /* Add edge */ + /* Initialize the undirected graph */ Vertex[] v = Vertex.ValsToVets([1, 3, 2, 5, 4]); Vertex[][] edges = [ @@ -92,31 +92,31 @@ public class graph_adjacency_list { [v[3], v[4]] ]; GraphAdjList graph = new(edges); - Console.WriteLine("\nAfter initialization, graph is"); + Console.WriteLine("\nGraph after initialization:"); graph.Print(); - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.AddEdge(v[0], v[2]); - Console.WriteLine("\nAfter adding edge 1-2, graph is"); + Console.WriteLine("\nGraph after adding edge 1-2:"); graph.Print(); - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.RemoveEdge(v[0], v[1]); - Console.WriteLine("\nAfter removing edge 1-3, graph is"); + Console.WriteLine("\nGraph after removing edge 1-3:"); graph.Print(); - /* Add vertex */ + /* Add a vertex */ Vertex v5 = new(6); graph.AddVertex(v5); - Console.WriteLine("\nAfter adding vertex 6, graph is"); + Console.WriteLine("\nGraph after adding vertex 6:"); graph.Print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.RemoveVertex(v[1]); - Console.WriteLine("\nAfter removing vertex 3, graph is"); + Console.WriteLine("\nGraph after removing vertex 3:"); graph.Print(); } } diff --git a/en/codes/csharp/chapter_graph/graph_adjacency_matrix.cs b/en/codes/csharp/chapter_graph/graph_adjacency_matrix.cs index 029797e25..78bc69250 100644 --- a/en/codes/csharp/chapter_graph/graph_adjacency_matrix.cs +++ b/en/codes/csharp/chapter_graph/graph_adjacency_matrix.cs @@ -6,21 +6,21 @@ namespace hello_algo.chapter_graph; -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat { - List vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" - List> adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + List vertices; // Vertex list: elements are vertex values, and indices are vertex indices + List> adjMat; // Adjacency matrix: row and column indices correspond to vertex indices /* Constructor */ public GraphAdjMat(int[] vertices, int[][] edges) { this.vertices = []; this.adjMat = []; - // Add vertex + // Add a vertex foreach (int val in vertices) { AddVertex(val); } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices foreach (int[] e in edges) { AddEdge(e[0], e[1]); } @@ -31,10 +31,10 @@ class GraphAdjMat { return vertices.Count; } - /* Add vertex */ + /* Add a vertex */ public void AddVertex(int val) { int n = Size(); - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list vertices.Add(val); // Add a row to the adjacency matrix List newRow = new(n); @@ -48,42 +48,42 @@ class GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ public void RemoveVertex(int index) { if (index >= Size()) throw new IndexOutOfRangeException(); - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list vertices.RemoveAt(index); - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix adjMat.RemoveAt(index); - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix foreach (List row in adjMat) { row.RemoveAt(index); } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices public void AddEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= Size() || j >= Size() || i == j) throw new IndexOutOfRangeException(); - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal adjMat[i][j] = 1; adjMat[j][i] = 1; } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices public void RemoveEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= Size() || j >= Size() || i == j) throw new IndexOutOfRangeException(); adjMat[i][j] = 0; adjMat[j][i] = 0; } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ public void Print() { Console.Write("Vertex list = "); PrintUtil.PrintList(vertices); @@ -95,8 +95,8 @@ class GraphAdjMat { public class graph_adjacency_matrix { [Test] public void Test() { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices int[] vertices = [1, 3, 2, 5, 4]; int[][] edges = [ @@ -108,30 +108,30 @@ public class graph_adjacency_matrix { [3, 4] ]; GraphAdjMat graph = new(vertices, edges); - Console.WriteLine("\nAfter initialization, graph is"); + Console.WriteLine("\nGraph after initialization:"); graph.Print(); - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.AddEdge(0, 2); - Console.WriteLine("\nAfter adding edge 1-2, graph is"); + Console.WriteLine("\nGraph after adding edge 1-2:"); graph.Print(); - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.RemoveEdge(0, 1); - Console.WriteLine("\nAfter removing edge 1-3, graph is"); + Console.WriteLine("\nGraph after removing edge 1-3:"); graph.Print(); - /* Add vertex */ + /* Add a vertex */ graph.AddVertex(6); - Console.WriteLine("\nAfter adding vertex 6, graph is"); + Console.WriteLine("\nGraph after adding vertex 6:"); graph.Print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.RemoveVertex(1); - Console.WriteLine("\nAfter removing vertex 3, graph is"); + Console.WriteLine("\nGraph after removing vertex 3:"); graph.Print(); } } diff --git a/en/codes/csharp/chapter_graph/graph_bfs.cs b/en/codes/csharp/chapter_graph/graph_bfs.cs index 0ca8886c2..741381d3f 100644 --- a/en/codes/csharp/chapter_graph/graph_bfs.cs +++ b/en/codes/csharp/chapter_graph/graph_bfs.cs @@ -8,35 +8,35 @@ namespace hello_algo.chapter_graph; public class graph_bfs { /* Breadth-first traversal */ - // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + // Represent the graph with an adjacency list to access all neighbors of a given vertex List GraphBFS(GraphAdjList graph, Vertex startVet) { // Vertex traversal sequence List res = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices HashSet visited = [startVet]; - // Queue used to implement BFS + // Use a queue to implement BFS Queue que = new(); que.Enqueue(startVet); - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited while (que.Count > 0) { Vertex vet = que.Dequeue(); // Dequeue the front vertex - res.Add(vet); // Record visited vertex + res.Add(vet); // Record the visited vertex foreach (Vertex adjVet in graph.adjList[vet]) { if (visited.Contains(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - que.Enqueue(adjVet); // Only enqueue unvisited vertices + que.Enqueue(adjVet); // Enqueue only unvisited vertices visited.Add(adjVet); // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res; } [Test] public void Test() { - /* Add edge */ + /* Initialize the undirected graph */ Vertex[] v = Vertex.ValsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); Vertex[][] edges = [ @@ -47,12 +47,12 @@ public class graph_bfs { ]; GraphAdjList graph = new(edges); - Console.WriteLine("\nAfter initialization, graph is"); + Console.WriteLine("\nGraph after initialization:"); graph.Print(); /* Breadth-first traversal */ List res = GraphBFS(graph, v[0]); - Console.WriteLine("\nBreadth-first traversal (BFS) vertex sequence is"); + Console.WriteLine("\nVertex sequence from breadth-first traversal (BFS):"); Console.WriteLine(string.Join(" ", Vertex.VetsToVals(res))); } } diff --git a/en/codes/csharp/chapter_graph/graph_dfs.cs b/en/codes/csharp/chapter_graph/graph_dfs.cs index f1adbc3eb..091421fa1 100644 --- a/en/codes/csharp/chapter_graph/graph_dfs.cs +++ b/en/codes/csharp/chapter_graph/graph_dfs.cs @@ -9,24 +9,24 @@ namespace hello_algo.chapter_graph; public class graph_dfs { /* Depth-first traversal helper function */ void DFS(GraphAdjList graph, HashSet visited, List res, Vertex vet) { - res.Add(vet); // Record visited vertex + res.Add(vet); // Record the visited vertex visited.Add(vet); // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex foreach (Vertex adjVet in graph.adjList[vet]) { if (visited.Contains(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex DFS(graph, visited, res, adjVet); } } /* Depth-first traversal */ - // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + // Represent the graph with an adjacency list to access all neighbors of a given vertex List GraphDFS(GraphAdjList graph, Vertex startVet) { // Vertex traversal sequence List res = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices HashSet visited = []; DFS(graph, visited, res, startVet); return res; @@ -34,7 +34,7 @@ public class graph_dfs { [Test] public void Test() { - /* Add edge */ + /* Initialize the undirected graph */ Vertex[] v = Vertex.ValsToVets([0, 1, 2, 3, 4, 5, 6]); Vertex[][] edges = [ @@ -43,12 +43,12 @@ public class graph_dfs { ]; GraphAdjList graph = new(edges); - Console.WriteLine("\nAfter initialization, graph is"); + Console.WriteLine("\nGraph after initialization:"); graph.Print(); /* Depth-first traversal */ List res = GraphDFS(graph, v[0]); - Console.WriteLine("\nDepth-first traversal (DFS) vertex sequence is"); + Console.WriteLine("\nVertex sequence from depth-first traversal (DFS):"); Console.WriteLine(string.Join(" ", Vertex.VetsToVals(res))); } } diff --git a/en/codes/csharp/chapter_greedy/coin_change_greedy.cs b/en/codes/csharp/chapter_greedy/coin_change_greedy.cs index 0e7cdb5a0..30d1cbdff 100644 --- a/en/codes/csharp/chapter_greedy/coin_change_greedy.cs +++ b/en/codes/csharp/chapter_greedy/coin_change_greedy.cs @@ -7,14 +7,14 @@ namespace hello_algo.chapter_greedy; public class coin_change_greedy { - /* Coin change: Greedy algorithm */ + /* Coin change: greedy algorithm */ int CoinChangeGreedy(int[] coins, int amt) { - // Assume coins list is sorted + // Assume the coins list is sorted int i = coins.Length - 1; int count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (amt > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -22,33 +22,33 @@ public class coin_change_greedy { amt -= coins[i]; count++; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt == 0 ? count : -1; } [Test] public void Test() { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution int[] coins = [1, 5, 10, 20, 50, 100]; int amt = 186; int res = CoinChangeGreedy(coins, amt); Console.WriteLine("\ncoins = " + coins.PrintList() + ", amt = " + amt); - Console.WriteLine("To make " + amt + ", minimum number of coins needed is " + res); + Console.WriteLine("For amount " + amt + " the greedy coin count is " + res); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 20, 50]; amt = 60; res = CoinChangeGreedy(coins, amt); Console.WriteLine("\ncoins = " + coins.PrintList() + ", amt = " + amt); - Console.WriteLine("To make " + amt + ", minimum number of coins needed is " + res); - Console.WriteLine("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); + Console.WriteLine("For amount " + amt + " the greedy coin count is " + res); + Console.WriteLine("The actual minimum is 3 coins: 20 + 20 + 20"); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 49, 50]; amt = 98; res = CoinChangeGreedy(coins, amt); Console.WriteLine("\ncoins = " + coins.PrintList() + ", amt = " + amt); - Console.WriteLine("To make " + amt + ", minimum number of coins needed is " + res); - Console.WriteLine("Actually the minimum number needed is 2, i.e., 49 + 49"); + Console.WriteLine("For amount " + amt + " the greedy coin count is " + res); + Console.WriteLine("The actual minimum is 2 coins: 49 + 49"); } } \ No newline at end of file diff --git a/en/codes/csharp/chapter_greedy/fractional_knapsack.cs b/en/codes/csharp/chapter_greedy/fractional_knapsack.cs index 940baa57d..8db7ede92 100644 --- a/en/codes/csharp/chapter_greedy/fractional_knapsack.cs +++ b/en/codes/csharp/chapter_greedy/fractional_knapsack.cs @@ -13,26 +13,26 @@ class Item(int w, int v) { } public class fractional_knapsack { - /* Fractional knapsack: Greedy algorithm */ + /* Fractional knapsack: greedy algorithm */ double FractionalKnapsack(int[] wgt, int[] val, int cap) { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value Item[] items = new Item[wgt.Length]; for (int i = 0; i < wgt.Length; i++) { items[i] = new Item(wgt[i], val[i]); } - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order Array.Sort(items, (x, y) => (y.v / y.w).CompareTo(x.v / x.w)); - // Loop for greedy selection + // Repeatedly make greedy choices double res = 0; foreach (Item item in items) { if (item.w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v; cap -= item.w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += (double)item.v / item.w * cap; - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break; } } @@ -47,6 +47,6 @@ public class fractional_knapsack { // Greedy algorithm double res = FractionalKnapsack(wgt, val, cap); - Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + Console.WriteLine("Maximum total item value within the knapsack capacity: " + res); } } \ No newline at end of file diff --git a/en/codes/csharp/chapter_greedy/max_capacity.cs b/en/codes/csharp/chapter_greedy/max_capacity.cs index 4bb373cb9..47008477e 100644 --- a/en/codes/csharp/chapter_greedy/max_capacity.cs +++ b/en/codes/csharp/chapter_greedy/max_capacity.cs @@ -7,18 +7,18 @@ namespace hello_algo.chapter_greedy; public class max_capacity { - /* Max capacity: Greedy algorithm */ + /* Maximum container capacity: greedy algorithm */ int MaxCapacity(int[] ht) { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array int i = 0, j = ht.Length - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 int res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity int cap = Math.Min(ht[i], ht[j]) * (j - i); res = Math.Max(res, cap); - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i++; } else { @@ -34,6 +34,6 @@ public class max_capacity { // Greedy algorithm int res = MaxCapacity(ht); - Console.WriteLine("Maximum capacity is " + res); + Console.WriteLine("Maximum container capacity: " + res); } } \ No newline at end of file diff --git a/en/codes/csharp/chapter_greedy/max_product_cutting.cs b/en/codes/csharp/chapter_greedy/max_product_cutting.cs index 7626c9aa1..e1fcc3dee 100644 --- a/en/codes/csharp/chapter_greedy/max_product_cutting.cs +++ b/en/codes/csharp/chapter_greedy/max_product_cutting.cs @@ -7,24 +7,24 @@ namespace hello_algo.chapter_greedy; public class max_product_cutting { - /* Max product cutting: Greedy algorithm */ + /* Maximum product after splitting: greedy algorithm */ int MaxProductCutting(int n) { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder int a = n / 3; int b = n % 3; if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return (int)Math.Pow(3, a - 1) * 2 * 2; } if (b == 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return (int)Math.Pow(3, a) * 2; } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return (int)Math.Pow(3, a); } @@ -34,6 +34,6 @@ public class max_product_cutting { // Greedy algorithm int res = MaxProductCutting(n); - Console.WriteLine("Maximum cutting product is" + res); + Console.WriteLine("Maximum product after splitting: " + res); } } \ No newline at end of file diff --git a/en/codes/csharp/chapter_hashing/array_hash_map.cs b/en/codes/csharp/chapter_hashing/array_hash_map.cs index cfa6cd0ae..189997174 100644 --- a/en/codes/csharp/chapter_hashing/array_hash_map.cs +++ b/en/codes/csharp/chapter_hashing/array_hash_map.cs @@ -6,17 +6,17 @@ namespace hello_algo.chapter_hashing; -/* Key-value pair int->string */ +/* Key-value pair: int->string */ class Pair(int key, string val) { public int key = key; public string val = val; } -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { List buckets; public ArrayHashMap() { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets buckets = []; for (int i = 0; i < 100; i++) { buckets.Add(null); @@ -29,7 +29,7 @@ class ArrayHashMap { return index; } - /* Query operation */ + /* Lookup operation */ public string? Get(int key) { int index = HashFunc(key); Pair? pair = buckets[index]; @@ -37,7 +37,7 @@ class ArrayHashMap { return pair.val; } - /* Add operation */ + /* Insert operation */ public void Put(int key, string val) { Pair pair = new(key, val); int index = HashFunc(key); @@ -47,7 +47,7 @@ class ArrayHashMap { /* Remove operation */ public void Remove(int key) { int index = HashFunc(key); - // Set to null to represent deletion + // Set the bucket to null to mark it as removed buckets[index] = null; } @@ -81,7 +81,7 @@ class ArrayHashMap { return valueSet; } - /* Print hash table */ + /* Print the hash table */ public void Print() { foreach (Pair kv in PairSet()) { Console.WriteLine(kv.key + " -> " + kv.val); @@ -93,40 +93,40 @@ class ArrayHashMap { public class array_hash_map { [Test] public void Test() { - /* Initialize hash table */ + /* Initialize the hash table */ ArrayHashMap map = new(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.Put(12836, "Xiao Ha"); - map.Put(15937, "Xiao Luo"); - map.Put(16750, "Xiao Suan"); - map.Put(13276, "Xiao Fa"); - map.Put(10583, "Xiao Ya"); - Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.Put(12836, "小哈"); + map.Put(15937, "小啰"); + map.Put(16750, "小算"); + map.Put(13276, "小法"); + map.Put(10583, "小鸭"); + Console.WriteLine("\nHash table after insertion:\nKey -> Value"); map.Print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value string? name = map.Get(15937); - Console.WriteLine("\nInput student ID 15937, query name " + name); + Console.WriteLine("\nLook up student ID 15937 to obtain the name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.Remove(10583); - Console.WriteLine("\nAfter removing 10583, hash table is\nKey -> Value"); + Console.WriteLine("\nHash table after removing 10583:\nKey -> Value"); map.Print(); - /* Traverse hash table */ - Console.WriteLine("\nTraverse key-value pairs Key->Value"); + /* Traverse the hash table */ + Console.WriteLine("\nTraverse key-value pairs: Key->Value"); foreach (Pair kv in map.PairSet()) { Console.WriteLine(kv.key + " -> " + kv.val); } - Console.WriteLine("\nTraverse keys only Key"); + Console.WriteLine("\nTraverse keys only: Key"); foreach (int key in map.KeySet()) { Console.WriteLine(key); } - Console.WriteLine("\nTraverse values only Value"); + Console.WriteLine("\nTraverse values only: Value"); foreach (string val in map.ValueSet()) { Console.WriteLine(val); } diff --git a/en/codes/csharp/chapter_hashing/built_in_hash.cs b/en/codes/csharp/chapter_hashing/built_in_hash.cs index 3a866444e..cc173bb50 100644 --- a/en/codes/csharp/chapter_hashing/built_in_hash.cs +++ b/en/codes/csharp/chapter_hashing/built_in_hash.cs @@ -19,15 +19,15 @@ public class built_in_hash { double dec = 3.14159; int hashDec = dec.GetHashCode(); - Console.WriteLine("Decimal " + dec + " has hash value " + hashDec); + Console.WriteLine("Floating-point number " + dec + " has hash value " + hashDec); - string str = "Hello Algo"; + string str = "Hello 算法"; int hashStr = str.GetHashCode(); Console.WriteLine("String " + str + " has hash value " + hashStr); - object[] arr = [12836, "Xiao Ha"]; + object[] arr = [12836, "小哈"]; int hashTup = arr.GetHashCode(); - Console.WriteLine("Array [" + string.Join(", ", arr) + "] hash value is " + hashTup); + Console.WriteLine("Array [" + string.Join(", ", arr) + "] has hash value " + hashTup); ListNode obj = new(0); int hashObj = obj.GetHashCode(); diff --git a/en/codes/csharp/chapter_hashing/hash_map.cs b/en/codes/csharp/chapter_hashing/hash_map.cs index d7ad8fbfa..0f4b4030e 100644 --- a/en/codes/csharp/chapter_hashing/hash_map.cs +++ b/en/codes/csharp/chapter_hashing/hash_map.cs @@ -10,40 +10,40 @@ namespace hello_algo.chapter_hashing; public class hash_map { [Test] public void Test() { - /* Initialize hash table */ + /* Initialize the hash table */ Dictionary map = new() { - /* Add operation */ - // Add key-value pair (key, value) to the hash table - { 12836, "Xiao Ha" }, - { 15937, "Xiao Luo" }, - { 16750, "Xiao Suan" }, - { 13276, "Xiao Fa" }, - { 10583, "Xiao Ya" } + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + { 12836, "小哈" }, + { 15937, "小啰" }, + { 16750, "小算" }, + { 13276, "小法" }, + { 10583, "小鸭" } }; - Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + Console.WriteLine("\nHash table after insertion:\nKey -> Value"); PrintUtil.PrintHashMap(map); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value string name = map[15937]; - Console.WriteLine("\nInput student ID 15937, query name " + name); + Console.WriteLine("\nLook up student ID 15937 to obtain the name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.Remove(10583); - Console.WriteLine("\nAfter removing 10583, hash table is\nKey -> Value"); + Console.WriteLine("\nHash table after removing 10583:\nKey -> Value"); PrintUtil.PrintHashMap(map); - /* Traverse hash table */ - Console.WriteLine("\nTraverse key-value pairs Key->Value"); + /* Traverse the hash table */ + Console.WriteLine("\nTraverse key-value pairs: Key->Value"); foreach (var kv in map) { Console.WriteLine(kv.Key + " -> " + kv.Value); } - Console.WriteLine("\nTraverse keys only Key"); + Console.WriteLine("\nTraverse keys only: Key"); foreach (int key in map.Keys) { Console.WriteLine(key); } - Console.WriteLine("\nTraverse values only Value"); + Console.WriteLine("\nTraverse values only: Value"); foreach (string val in map.Values) { Console.WriteLine(val); } diff --git a/en/codes/csharp/chapter_hashing/hash_map_chaining.cs b/en/codes/csharp/chapter_hashing/hash_map_chaining.cs index d8600f6c1..e026b3599 100644 --- a/en/codes/csharp/chapter_hashing/hash_map_chaining.cs +++ b/en/codes/csharp/chapter_hashing/hash_map_chaining.cs @@ -10,8 +10,8 @@ namespace hello_algo.chapter_hashing; class HashMapChaining { int size; // Number of key-value pairs int capacity; // Hash table capacity - double loadThres; // Load factor threshold for triggering expansion - int extendRatio; // Expansion multiplier + double loadThres; // Load factor threshold that triggers capacity growth + int extendRatio; // Capacity growth factor List> buckets; // Bucket array /* Constructor */ @@ -36,34 +36,34 @@ class HashMapChaining { return (double)size / capacity; } - /* Query operation */ + /* Lookup operation */ public string? Get(int key) { int index = HashFunc(key); - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val foreach (Pair pair in buckets[index]) { if (pair.key == key) { return pair.val; } } - // If key is not found, return null + // Return null if key is not found return null; } - /* Add operation */ + /* Insert operation */ public void Put(int key, string val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (LoadFactor() > loadThres) { Extend(); } int index = HashFunc(key); - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return foreach (Pair pair in buckets[index]) { if (pair.key == key) { pair.val = val; return; } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end buckets[index].Add(new Pair(key, val)); size++; } @@ -71,7 +71,7 @@ class HashMapChaining { /* Remove operation */ public void Remove(int key) { int index = HashFunc(key); - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair foreach (Pair pair in buckets[index].ToList()) { if (pair.key == key) { buckets[index].Remove(pair); @@ -81,18 +81,18 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ void Extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table List> bucketsTmp = buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio; buckets = new List>(capacity); for (int i = 0; i < capacity; i++) { buckets.Add([]); } size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one foreach (List bucket in bucketsTmp) { foreach (Pair pair in bucket) { Put(pair.key, pair.val); @@ -100,7 +100,7 @@ class HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ public void Print() { foreach (List bucket in buckets) { List res = []; @@ -117,28 +117,28 @@ class HashMapChaining { public class hash_map_chaining { [Test] public void Test() { - /* Initialize hash table */ + /* Initialize the hash table */ HashMapChaining map = new(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.Put(12836, "Xiao Ha"); - map.Put(15937, "Xiao Luo"); - map.Put(16750, "Xiao Suan"); - map.Put(13276, "Xiao Fa"); - map.Put(10583, "Xiao Ya"); - Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.Put(12836, "小哈"); + map.Put(15937, "小啰"); + map.Put(16750, "小算"); + map.Put(13276, "小法"); + map.Put(10583, "小鸭"); + Console.WriteLine("\nHash table after insertion:\nKey -> Value"); map.Print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value string? name = map.Get(13276); - Console.WriteLine("\nInput student ID 13276, query name " + name); + Console.WriteLine("\nLook up student ID 13276 to obtain the name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.Remove(12836); - Console.WriteLine("\nAfter removing 12836, hash table is\nKey -> Value"); + Console.WriteLine("\nHash table after removing 12836:\nKey -> Value"); map.Print(); } } \ No newline at end of file diff --git a/en/codes/csharp/chapter_hashing/hash_map_open_addressing.cs b/en/codes/csharp/chapter_hashing/hash_map_open_addressing.cs index e3cdd1591..d2184a875 100644 --- a/en/codes/csharp/chapter_hashing/hash_map_open_addressing.cs +++ b/en/codes/csharp/chapter_hashing/hash_map_open_addressing.cs @@ -10,10 +10,10 @@ namespace hello_algo.chapter_hashing; class HashMapOpenAddressing { int size; // Number of key-value pairs int capacity = 4; // Hash table capacity - double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion - int extendRatio = 2; // Expansion multiplier + double loadThres = 2.0 / 3.0; // Load factor threshold that triggers capacity growth + int extendRatio = 2; // Capacity growth factor Pair[] buckets; // Bucket array - Pair TOMBSTONE = new(-1, "-1"); // Removal marker + Pair TOMBSTONE = new(-1, "-1"); // Tombstone marker /* Constructor */ public HashMapOpenAddressing() { @@ -31,83 +31,83 @@ class HashMapOpenAddressing { return (double)size / capacity; } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ int FindBucket(int key) { int index = HashFunc(key); int firstTombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (buckets[index] != null) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (buckets[index].key == key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; - return firstTombstone; // Return the moved bucket index + return firstTombstone; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone == -1 ? index : firstTombstone; } - /* Query operation */ + /* Lookup operation */ public string? Get(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = FindBucket(key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if (buckets[index] != null && buckets[index] != TOMBSTONE) { return buckets[index].val; } - // If key-value pair does not exist, return null + // Return null if the key-value pair is absent return null; } - /* Add operation */ + /* Insert operation */ public void Put(int key, string val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (LoadFactor() > loadThres) { Extend(); } - // Search for bucket index corresponding to key + // Find the bucket index for key int index = FindBucket(key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index].val = val; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it buckets[index] = new Pair(key, val); size++; } /* Remove operation */ public void Remove(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = FindBucket(key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index] = TOMBSTONE; size--; } } - /* Expand hash table */ + /* Grow the hash table capacity */ void Extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table Pair[] bucketsTmp = buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio; buckets = new Pair[capacity]; size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one foreach (Pair pair in bucketsTmp) { if (pair != null && pair != TOMBSTONE) { Put(pair.key, pair.val); @@ -115,7 +115,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ public void Print() { foreach (Pair pair in buckets) { if (pair == null) { @@ -132,28 +132,28 @@ class HashMapOpenAddressing { public class hash_map_open_addressing { [Test] public void Test() { - /* Initialize hash table */ + /* Initialize the hash table */ HashMapOpenAddressing map = new(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.Put(12836, "Xiao Ha"); - map.Put(15937, "Xiao Luo"); - map.Put(16750, "Xiao Suan"); - map.Put(13276, "Xiao Fa"); - map.Put(10583, "Xiao Ya"); - Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.Put(12836, "小哈"); + map.Put(15937, "小啰"); + map.Put(16750, "小算"); + map.Put(13276, "小法"); + map.Put(10583, "小鸭"); + Console.WriteLine("\nHash table after insertion:\nKey -> Value"); map.Print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value string? name = map.Get(13276); - Console.WriteLine("\nInput student ID 13276, query name " + name); + Console.WriteLine("\nLook up student ID 13276 to obtain the name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.Remove(16750); - Console.WriteLine("\nAfter removing 16750, hash table is\nKey -> Value"); + Console.WriteLine("\nHash table after removing 16750:\nKey -> Value"); map.Print(); } } diff --git a/en/codes/csharp/chapter_hashing/simple_hash.cs b/en/codes/csharp/chapter_hashing/simple_hash.cs index b18e2294d..e1447eb99 100644 --- a/en/codes/csharp/chapter_hashing/simple_hash.cs +++ b/en/codes/csharp/chapter_hashing/simple_hash.cs @@ -37,7 +37,7 @@ public class simple_hash { return hash & MODULUS; } - /* Rotational hash */ + /* Rotating hash */ int RotHash(string key) { long hash = 0; const int MODULUS = 1000000007; @@ -49,18 +49,18 @@ public class simple_hash { [Test] public void Test() { - string key = "Hello Algo"; + string key = "Hello 算法"; int hash = AddHash(key); - Console.WriteLine("Additive hash value is " + hash); + Console.WriteLine("Additive hash value: " + hash); hash = MulHash(key); - Console.WriteLine("Multiplicative hash value is " + hash); + Console.WriteLine("Multiplicative hash value: " + hash); hash = XorHash(key); - Console.WriteLine("XOR hash value is " + hash); + Console.WriteLine("XOR hash value: " + hash); hash = RotHash(key); - Console.WriteLine("Rotational hash value is " + hash); + Console.WriteLine("Rotating hash value: " + hash); } } diff --git a/en/codes/csharp/chapter_heap/heap.cs b/en/codes/csharp/chapter_heap/heap.cs index 00001f14f..8ad49bd2e 100644 --- a/en/codes/csharp/chapter_heap/heap.cs +++ b/en/codes/csharp/chapter_heap/heap.cs @@ -8,57 +8,57 @@ namespace hello_algo.chapter_heap; public class heap { void TestPush(PriorityQueue heap, int val) { - heap.Enqueue(val, val); // Element enters heap - Console.WriteLine($"\nAfter element {val} pushes to heap\n"); + heap.Enqueue(val, val); // Push an element onto the heap + Console.WriteLine($"\nAfter pushing {val} onto the heap:\n"); PrintUtil.PrintHeap(heap); } void TestPop(PriorityQueue heap) { - int val = heap.Dequeue(); // Time complexity is O(n), not O(nlogn) - Console.WriteLine($"\nAfter heap top element {val} pops from heap\n"); + int val = heap.Dequeue(); // Pop the top element from the heap + Console.WriteLine($"\nAfter popping the top element {val} from the heap:\n"); PrintUtil.PrintHeap(heap); } [Test] public void Test() { - /* Initialize heap */ - // Python's heapq module implements min heap by default + /* Initialize heaps */ + // Initialize a min heap PriorityQueue minHeap = new(); - // Initialize max heap (modify Comparer using lambda expression) + // Initialize a max heap (use a lambda expression to customize the Comparer) PriorityQueue maxHeap = new(Comparer.Create((x, y) => y.CompareTo(x))); - Console.WriteLine("Following test cases are for max heap"); + Console.WriteLine("The following examples use a max heap"); - /* Element enters heap */ + /* Push an element onto the heap */ TestPush(maxHeap, 1); TestPush(maxHeap, 3); TestPush(maxHeap, 2); TestPush(maxHeap, 5); TestPush(maxHeap, 4); - /* Check if heap is empty */ + /* Get the top element of the heap */ int peek = maxHeap.Peek(); - Console.WriteLine($"Heap top element is {peek}"); + Console.WriteLine($"Top element of the heap: {peek}"); - /* Time complexity is O(n), not O(nlogn) */ - // Dequeued elements form a descending sequence + /* Pop the top element from the heap */ + // The elements are popped in descending order TestPop(maxHeap); TestPop(maxHeap); TestPop(maxHeap); TestPop(maxHeap); TestPop(maxHeap); - /* Get heap size */ + /* Get the heap size */ int size = maxHeap.Count; - Console.WriteLine($"Heap size is {size}"); + Console.WriteLine($"Number of elements in the heap: {size}"); - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty = maxHeap.Count == 0; - Console.WriteLine($"Is heap empty {isEmpty}"); + Console.WriteLine($"Is the heap empty? {isEmpty}"); - /* Input list and build heap */ + /* Build a heap from an input list */ var list = new int[] { 1, 3, 2, 5, 4 }; minHeap = new PriorityQueue(list.Select(x => (x, x))); - Console.WriteLine("After input list and building min heap"); + Console.WriteLine("After building a min heap from the input list:"); PrintUtil.PrintHeap(minHeap); } } diff --git a/en/codes/csharp/chapter_heap/my_heap.cs b/en/codes/csharp/chapter_heap/my_heap.cs index bd397d813..35100e50e 100644 --- a/en/codes/csharp/chapter_heap/my_heap.cs +++ b/en/codes/csharp/chapter_heap/my_heap.cs @@ -8,108 +8,108 @@ namespace hello_algo.chapter_heap; /* Max heap */ class MaxHeap { - // Use list instead of array, no need to consider capacity expansion + // Use a resizable list instead of an array to avoid managing capacity growth List maxHeap; - /* Constructor, build empty heap */ + /* Constructor: create an empty heap */ public MaxHeap() { maxHeap = []; } - /* Constructor, build heap from input list */ + /* Constructor: build a heap from an input list */ public MaxHeap(IEnumerable nums) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order maxHeap = new List(nums); - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node var size = Parent(this.Size() - 1); for (int i = size; i >= 0; i--) { SiftDown(i); } } - /* Get index of left child node */ + /* Get the left child index */ int Left(int i) { return 2 * i + 1; } - /* Get index of right child node */ + /* Get the right child index */ int Right(int i) { return 2 * i + 2; } - /* Get index of parent node */ + /* Get the parent index */ int Parent(int i) { - return (i - 1) / 2; // Floor division + return (i - 1) / 2; // Integer division (truncates toward zero in C#) } - /* Access top element */ + /* Access the top element of the heap */ public int Peek() { return maxHeap[0]; } - /* Element enters heap */ + /* Push an element onto the heap */ public void Push(int val) { - // Add node + // Add a node maxHeap.Add(val); - // Heapify from bottom to top + // Sift up SiftUp(Size() - 1); } - /* Get heap size */ + /* Get the heap size */ public int Size() { return maxHeap.Count; } - /* Check if heap is empty */ + /* Check whether the heap is empty */ public bool IsEmpty() { return Size() == 0; } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ void SiftUp(int i) { while (true) { - // Get parent node of node i + // Get the parent of node i int p = Parent(i); - // If 'past root node' or 'node needs no repair', end heapify + // Stop sifting if the root has been passed or the node needs no adjustment if (p < 0 || maxHeap[i] <= maxHeap[p]) break; - // Swap two nodes + // Swap the two nodes Swap(i, p); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Element exits heap */ + /* Pop an element from the heap */ public int Pop() { - // Handle empty case + // Handle an empty heap if (IsEmpty()) throw new IndexOutOfRangeException(); - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) Swap(0, Size() - 1); - // Remove node + // Remove a node int val = maxHeap.Last(); maxHeap.RemoveAt(Size() - 1); - // Return top element + // Sift down SiftDown(0); - // Return heap top element + // Return the top element of the heap return val; } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ void SiftDown(int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = Left(i), r = Right(i), ma = i; if (l < Size() && maxHeap[l] > maxHeap[ma]) ma = l; if (r < Size() && maxHeap[r] > maxHeap[ma]) ma = r; - // If 'node i is largest' or 'past leaf node', end heapify + // Stop sifting if node i has the largest value or has no children if (ma == i) break; - // Swap two nodes + // Swap the two nodes Swap(i, ma); - // Loop downwards heapification + // Continue sifting down i = ma; } } @@ -119,7 +119,7 @@ class MaxHeap { (maxHeap[i], maxHeap[p]) = (maxHeap[p], maxHeap[i]); } - /* Driver Code */ + /* Print the heap (binary tree) */ public void Print() { var queue = new Queue(maxHeap); PrintUtil.PrintHeap(queue); @@ -129,32 +129,32 @@ class MaxHeap { public class my_heap { [Test] public void Test() { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ MaxHeap maxHeap = new([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); - Console.WriteLine("\nAfter inputting list and building heap"); + Console.WriteLine("\nAfter building a heap from the input list:"); maxHeap.Print(); - /* Check if heap is empty */ + /* Get the top element of the heap */ int peek = maxHeap.Peek(); - Console.WriteLine($"Heap top element is {peek}"); + Console.WriteLine($"Top element of the heap: {peek}"); - /* Element enters heap */ + /* Push an element onto the heap */ int val = 7; maxHeap.Push(val); - Console.WriteLine($"After element {val} pushes to heap"); + Console.WriteLine($"After pushing {val} onto the heap:"); maxHeap.Print(); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.Pop(); - Console.WriteLine($"After heap top element {peek} pops from heap"); + Console.WriteLine($"After popping the top element {peek} from the heap:"); maxHeap.Print(); - /* Get heap size */ + /* Get the heap size */ int size = maxHeap.Size(); - Console.WriteLine($"Heap size is {size}"); + Console.WriteLine($"Number of elements in the heap: {size}"); - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty = maxHeap.IsEmpty(); - Console.WriteLine($"Is heap empty {isEmpty}"); + Console.WriteLine($"Is the heap empty? {isEmpty}"); } } diff --git a/en/codes/csharp/chapter_heap/top_k.cs b/en/codes/csharp/chapter_heap/top_k.cs index 187af84bc..ebdc81c3b 100644 --- a/en/codes/csharp/chapter_heap/top_k.cs +++ b/en/codes/csharp/chapter_heap/top_k.cs @@ -7,17 +7,17 @@ namespace hello_algo.chapter_heap; public class top_k { - /* Find the largest k elements in array based on heap */ + /* Use a heap to find the k largest elements in an array */ PriorityQueue TopKHeap(int[] nums, int k) { - // Python's heapq module implements min heap by default + // Initialize a min heap PriorityQueue heap = new(); - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for (int i = 0; i < k; i++) { heap.Enqueue(nums[i], nums[i]); } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for (int i = k; i < nums.Length; i++) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if (nums[i] > heap.Peek()) { heap.Dequeue(); heap.Enqueue(nums[i], nums[i]); @@ -31,7 +31,7 @@ public class top_k { int[] nums = [1, 7, 6, 3, 2]; int k = 3; PriorityQueue res = TopKHeap(nums, k); - Console.WriteLine("The largest " + k + " elements are"); + Console.WriteLine("The largest " + k + " elements are:"); PrintUtil.PrintHeap(res); } } diff --git a/en/codes/csharp/chapter_searching/binary_search.cs b/en/codes/csharp/chapter_searching/binary_search.cs index 1f4973d2e..be8a0c6fc 100644 --- a/en/codes/csharp/chapter_searching/binary_search.cs +++ b/en/codes/csharp/chapter_searching/binary_search.cs @@ -7,39 +7,39 @@ namespace hello_algo.chapter_searching; public class binary_search { - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ int BinarySearch(int[] nums, int target) { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements int i = 0, j = nums.Length - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j] + if (nums[m] < target) // In this case, target must be in [m+1, j] if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m-1] + else if (nums[m] > target) // In this case, target must be in [i, m-1] if it exists j = m - 1; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ int BinarySearchLCRO(int[] nums, int target) { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last int i = 0, j = nums.Length; - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j) + if (nums[m] < target) // In this case, target must be in [m+1, j) if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m) + else if (nums[m] > target) // In this case, target must be in [i, m) if it exists j = m; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } @@ -48,11 +48,11 @@ public class binary_search { int target = 6; int[] nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ int index = BinarySearch(nums, target); Console.WriteLine("Index of target element 6 = " + index); - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ index = BinarySearchLCRO(nums, target); Console.WriteLine("Index of target element 6 = " + index); } diff --git a/en/codes/csharp/chapter_searching/binary_search_edge.cs b/en/codes/csharp/chapter_searching/binary_search_edge.cs index 70584c60f..5629f8aad 100644 --- a/en/codes/csharp/chapter_searching/binary_search_edge.cs +++ b/en/codes/csharp/chapter_searching/binary_search_edge.cs @@ -7,44 +7,44 @@ namespace hello_algo.chapter_searching; public class binary_search_edge { - /* Binary search for the leftmost target */ + /* Find the leftmost occurrence of target using binary search */ int BinarySearchLeftEdge(int[] nums, int target) { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target int i = binary_search_insertion.BinarySearchInsertion(nums, target); - // Target not found, return -1 + // If target was not found, return -1 if (i == nums.Length || nums[i] != target) { return -1; } - // Found target, return index i + // Target was found; return index i return i; } - /* Binary search for the rightmost target */ + /* Find the rightmost occurrence of target using binary search */ int BinarySearchRightEdge(int[] nums, int target) { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 int i = binary_search_insertion.BinarySearchInsertion(nums, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // j is the candidate index of the rightmost target; i is the insertion point after all elements <= target int j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if (j == -1 || nums[j] != target) { return -1; } - // Found target, return index j + // Target was found; return index j return j; } [Test] public void Test() { - // Array with duplicate elements + // Array containing duplicate elements int[] nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; Console.WriteLine("\nArray nums = " + nums.PrintList()); - // Binary search left and right boundaries + // Find the left and right boundaries using binary search foreach (int target in new int[] { 6, 7 }) { int index = BinarySearchLeftEdge(nums, target); - Console.WriteLine("Leftmost element " + target + " has index " + index); + Console.WriteLine("Leftmost occurrence of " + target + " has index " + index); index = BinarySearchRightEdge(nums, target); - Console.WriteLine("Rightmost element " + target + " has index " + index); + Console.WriteLine("Rightmost occurrence of " + target + " has index " + index); } } } diff --git a/en/codes/csharp/chapter_searching/binary_search_insertion.cs b/en/codes/csharp/chapter_searching/binary_search_insertion.cs index 3a06f2ec9..014fa46c0 100644 --- a/en/codes/csharp/chapter_searching/binary_search_insertion.cs +++ b/en/codes/csharp/chapter_searching/binary_search_insertion.cs @@ -7,34 +7,34 @@ namespace hello_algo.chapter_searching; public class binary_search_insertion { - /* Binary search for insertion point (no duplicate elements) */ + /* Find an insertion point using binary search (no duplicates) */ public static int BinarySearchInsertionSimple(int[] nums, int target) { - int i = 0, j = nums.Length - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.Length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - return m; // Found target, return insertion point m + return m; // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i; } - /* Binary search for insertion point (with duplicate elements) */ + /* Find an insertion point using binary search (with duplicates) */ public static int BinarySearchInsertion(int[] nums, int target) { - int i = 0, j = nums.Length - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.Length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i @@ -43,22 +43,22 @@ public class binary_search_insertion { [Test] public void Test() { - // Array without duplicate elements + // Array with no duplicate elements int[] nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; Console.WriteLine("\nArray nums = " + nums.PrintList()); - // Binary search for insertion point + // Find an insertion point using binary search foreach (int target in new int[] { 6, 9 }) { int index = BinarySearchInsertionSimple(nums, target); - Console.WriteLine("Element " + target + "'s insertion point index is " + index); + Console.WriteLine("Element " + target + " has insertion point index " + index); } - // Array with duplicate elements + // Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; Console.WriteLine("\nArray nums = " + nums.PrintList()); - // Binary search for insertion point + // Find an insertion point using binary search foreach (int target in new int[] { 2, 6, 20 }) { int index = BinarySearchInsertion(nums, target); - Console.WriteLine("Element " + target + "'s insertion point index is " + index); + Console.WriteLine("Element " + target + " has insertion point index " + index); } } } diff --git a/en/codes/csharp/chapter_searching/hashing_search.cs b/en/codes/csharp/chapter_searching/hashing_search.cs index 7f6bc8c76..4a6b353dd 100644 --- a/en/codes/csharp/chapter_searching/hashing_search.cs +++ b/en/codes/csharp/chapter_searching/hashing_search.cs @@ -7,18 +7,18 @@ namespace hello_algo.chapter_searching; public class hashing_search { - /* Hash search (array) */ + /* Hash-based search (array) */ int HashingSearchArray(Dictionary map, int target) { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key return map.GetValueOrDefault(target, -1); } - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ ListNode? HashingSearchLinkedList(Dictionary map, int target) { - // Hash table key: target node value, value: node object - // If key is not in hash table, return null + // Hash table key: target node value; value: node object + // Return null if the hash table does not contain this key return map.GetValueOrDefault(target); } @@ -26,25 +26,25 @@ public class hashing_search { public void Test() { int target = 3; - /* Hash search (array) */ + /* Hash-based search (array) */ int[] nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; - // Initialize hash table + // Initialize the hash table Dictionary map = []; for (int i = 0; i < nums.Length; i++) { - map[nums[i]] = i; // key: element, value: index + map[nums[i]] = i; // key: element; value: index } int index = HashingSearchArray(map, target); Console.WriteLine("Index of target element 3 = " + index); - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ ListNode? head = ListNode.ArrToLinkedList(nums); - // Initialize hash table + // Initialize the hash table Dictionary map1 = []; while (head != null) { - map1[head.val] = head; // key: node value, value: node + map1[head.val] = head; // key: node value; value: node head = head.next; } ListNode? node = HashingSearchLinkedList(map1, target); - Console.WriteLine("Node object corresponding to target node value 3 is " + node); + Console.WriteLine("The node object corresponding to target value 3 is " + node); } } diff --git a/en/codes/csharp/chapter_searching/linear_search.cs b/en/codes/csharp/chapter_searching/linear_search.cs index 046da13d4..192d882dd 100644 --- a/en/codes/csharp/chapter_searching/linear_search.cs +++ b/en/codes/csharp/chapter_searching/linear_search.cs @@ -9,13 +9,13 @@ namespace hello_algo.chapter_searching; public class linear_search { /* Linear search (array) */ int LinearSearchArray(int[] nums, int target) { - // Traverse array + // Traverse the array for (int i = 0; i < nums.Length; i++) { - // Found the target element, return its index + // The target element was found; return its index if (nums[i] == target) return i; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } @@ -23,12 +23,12 @@ public class linear_search { ListNode? LinearSearchLinkedList(ListNode? head, int target) { // Traverse the linked list while (head != null) { - // Found the target node, return it + // The target node was found; return it if (head.val == target) return head; head = head.next; } - // Target node not found, return null + // The target node was not found; return null return null; } @@ -36,14 +36,14 @@ public class linear_search { public void Test() { int target = 3; - /* Perform linear search in array */ + /* Perform linear search on the array */ int[] nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; int index = LinearSearchArray(nums, target); Console.WriteLine("Index of target element 3 = " + index); - /* Perform linear search in linked list */ + /* Perform linear search on the linked list */ ListNode? head = ListNode.ArrToLinkedList(nums); ListNode? node = LinearSearchLinkedList(head, target); - Console.WriteLine("Node object corresponding to target node value 3 is " + node); + Console.WriteLine("The node object corresponding to target value 3 is " + node); } } diff --git a/en/codes/csharp/chapter_searching/two_sum.cs b/en/codes/csharp/chapter_searching/two_sum.cs index 9f8e63a5a..96d50944f 100644 --- a/en/codes/csharp/chapter_searching/two_sum.cs +++ b/en/codes/csharp/chapter_searching/two_sum.cs @@ -7,10 +7,10 @@ namespace hello_algo.chapter_searching; public class two_sum { - /* Method 1: Brute force enumeration */ + /* Method 1: brute-force enumeration */ int[] TwoSumBruteForce(int[] nums, int target) { int size = nums.Length; - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) @@ -20,12 +20,12 @@ public class two_sum { return []; } - /* Method 2: Auxiliary hash table */ + /* Method 2: auxiliary hash table */ int[] TwoSumHashTable(int[] nums, int target) { int size = nums.Length; - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space Dictionary dic = []; - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (int i = 0; i < size; i++) { if (dic.ContainsKey(target - nums[i])) { return [dic[target - nums[i]], i]; @@ -37,16 +37,16 @@ public class two_sum { [Test] public void Test() { - // ======= Test Case ======= + // ======= Test case ======= int[] nums = [2, 7, 11, 15]; int target = 13; - // ====== Driver Code ====== + // ====== Driver code ====== // Method 1 int[] res = TwoSumBruteForce(nums, target); - Console.WriteLine("Method 1 res = " + string.Join(",", res)); + Console.WriteLine("Method 1: res = " + string.Join(",", res)); // Method 2 res = TwoSumHashTable(nums, target); - Console.WriteLine("Method 2 res = " + string.Join(",", res)); + Console.WriteLine("Method 2: res = " + string.Join(",", res)); } } diff --git a/en/codes/csharp/chapter_sorting/bubble_sort.cs b/en/codes/csharp/chapter_sorting/bubble_sort.cs index 97e2103d9..1a57d4f1a 100644 --- a/en/codes/csharp/chapter_sorting/bubble_sort.cs +++ b/en/codes/csharp/chapter_sorting/bubble_sort.cs @@ -9,9 +9,9 @@ namespace hello_algo.chapter_sorting; public class bubble_sort { /* Bubble sort */ void BubbleSort(int[] nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.Length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] @@ -21,20 +21,20 @@ public class bubble_sort { } } - /* Bubble sort (flag optimization) */ + /* Bubble sort (flag optimization)*/ void BubbleSortWithFlag(int[] nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.Length - 1; i > 0; i--) { - bool flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + bool flag = false; // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); - flag = true; // Record element swap + flag = true; // Record that a swap occurred } } - if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + if (!flag) break; // Exit immediately if this pass made no swaps } } @@ -46,6 +46,6 @@ public class bubble_sort { int[] nums1 = [4, 1, 3, 1, 5, 2]; BubbleSortWithFlag(nums1); - Console.WriteLine("After bubble sort completes, nums1 = " + string.Join(",", nums1)); + Console.WriteLine("After bubble sort, nums1 = " + string.Join(",", nums1)); } } diff --git a/en/codes/csharp/chapter_sorting/bucket_sort.cs b/en/codes/csharp/chapter_sorting/bucket_sort.cs index 8e75dc9b6..ae545a85f 100644 --- a/en/codes/csharp/chapter_sorting/bucket_sort.cs +++ b/en/codes/csharp/chapter_sorting/bucket_sort.cs @@ -9,25 +9,25 @@ namespace hello_algo.chapter_sorting; public class bucket_sort { /* Bucket sort */ void BucketSort(float[] nums) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = n/2 buckets, expecting 2 elements per bucket int k = nums.Length / 2; List> buckets = []; for (int i = 0; i < k; i++) { buckets.Add([]); } - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets foreach (float num in nums) { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); use num * k to map each value to an index in [0, k-1] int i = (int)(num * k); // Add num to bucket i buckets[i].Add(num); } // 2. Sort each bucket foreach (List bucket in buckets) { - // Use built-in sorting function, can also replace with other sorting algorithms + // Use the built-in sorting function, or replace it with another sorting algorithm bucket.Sort(); } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results int j = 0; foreach (List bucket in buckets) { foreach (float num in bucket) { @@ -38,9 +38,9 @@ public class bucket_sort { [Test] public void Test() { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) float[] nums = [0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f]; BucketSort(nums); - Console.WriteLine("After bucket sort completes, nums = " + string.Join(" ", nums)); + Console.WriteLine("After bucket sort, nums = " + string.Join(" ", nums)); } } diff --git a/en/codes/csharp/chapter_sorting/counting_sort.cs b/en/codes/csharp/chapter_sorting/counting_sort.cs index 3fc363517..d3f46eb2b 100644 --- a/en/codes/csharp/chapter_sorting/counting_sort.cs +++ b/en/codes/csharp/chapter_sorting/counting_sort.cs @@ -8,20 +8,20 @@ namespace hello_algo.chapter_sorting; public class counting_sort { /* Counting sort */ - // Simple implementation, cannot be used for sorting objects + // Simple implementation; cannot be used to sort objects void CountingSortNaive(int[] nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; foreach (int num in nums) { m = Math.Max(m, num); } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num int[] counter = new int[m + 1]; foreach (int num in nums) { counter[num]++; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements int i = 0; for (int num = 0; num < m + 1; num++) { for (int j = 0; j < counter[num]; j++, i++) { @@ -31,34 +31,34 @@ public class counting_sort { } /* Counting sort */ - // Complete implementation, can sort objects and is a stable sort + // Full implementation; stable and adaptable to sorting objects void CountingSort(int[] nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; foreach (int num in nums) { m = Math.Max(m, num); } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num int[] counter = new int[m + 1]; foreach (int num in nums) { counter[num]++; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for (int i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result int n = nums.Length; int[] res = new int[n]; for (int i = n - 1; i >= 0; i--) { int num = nums[i]; res[counter[num] - 1] = num; // Place num at the corresponding index - counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + counter[num]--; // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res for (int i = 0; i < n; i++) { nums[i] = res[i]; } @@ -68,10 +68,10 @@ public class counting_sort { public void Test() { int[] nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; CountingSortNaive(nums); - Console.WriteLine("After counting sort (cannot sort objects) completes, nums = " + string.Join(" ", nums)); + Console.WriteLine("After counting sort (does not support objects), nums = " + string.Join(" ", nums)); int[] nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; CountingSort(nums1); - Console.WriteLine("After counting sort completes, nums1 = " + string.Join(" ", nums)); + Console.WriteLine("After counting sort, displayed nums = " + string.Join(" ", nums)); } } diff --git a/en/codes/csharp/chapter_sorting/heap_sort.cs b/en/codes/csharp/chapter_sorting/heap_sort.cs index d0ea610ab..b023340dc 100644 --- a/en/codes/csharp/chapter_sorting/heap_sort.cs +++ b/en/codes/csharp/chapter_sorting/heap_sort.cs @@ -7,10 +7,10 @@ namespace hello_algo.chapter_sorting; public class heap_sort { - /* Heap length is n, start heapifying node i, from top to bottom */ + /* Sift down from node i in a heap of size n */ void SiftDown(int[] nums, int n, int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; @@ -18,27 +18,27 @@ public class heap_sort { ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break; - // Swap two nodes + // Swap the two nodes (nums[ma], nums[i]) = (nums[i], nums[ma]); - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ void HeapSort(int[] nums) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (int i = nums.Length / 2 - 1; i >= 0; i--) { SiftDown(nums, nums.Length, i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (int i = nums.Length - 1; i > 0; i--) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) (nums[i], nums[0]) = (nums[0], nums[i]); - // Start heapifying the root node, from top to bottom + // Sift down starting at the root SiftDown(nums, i, 0); } } @@ -47,6 +47,6 @@ public class heap_sort { public void Test() { int[] nums = [4, 1, 3, 1, 5, 2]; HeapSort(nums); - Console.WriteLine("After heap sort completes, nums = " + string.Join(" ", nums)); + Console.WriteLine("After heap sort, nums = " + string.Join(" ", nums)); } } diff --git a/en/codes/csharp/chapter_sorting/insertion_sort.cs b/en/codes/csharp/chapter_sorting/insertion_sort.cs index 1290ccd49..72367e464 100644 --- a/en/codes/csharp/chapter_sorting/insertion_sort.cs +++ b/en/codes/csharp/chapter_sorting/insertion_sort.cs @@ -9,15 +9,15 @@ namespace hello_algo.chapter_sorting; public class insertion_sort { /* Insertion sort */ void InsertionSort(int[] nums) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for (int i = 1; i < nums.Length; i++) { int bas = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert bas at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > bas) { - nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + nums[j + 1] = nums[j]; // Shift nums[j] one position to the right j--; } - nums[j + 1] = bas; // Assign base to the correct position + nums[j + 1] = bas; // Place bas at the correct position } } @@ -25,6 +25,6 @@ public class insertion_sort { public void Test() { int[] nums = [4, 1, 3, 1, 5, 2]; InsertionSort(nums); - Console.WriteLine("After insertion sort completes, nums = " + string.Join(",", nums)); + Console.WriteLine("After insertion sort, nums = " + string.Join(",", nums)); } } diff --git a/en/codes/csharp/chapter_sorting/merge_sort.cs b/en/codes/csharp/chapter_sorting/merge_sort.cs index 8eb181231..7c583526a 100644 --- a/en/codes/csharp/chapter_sorting/merge_sort.cs +++ b/en/codes/csharp/chapter_sorting/merge_sort.cs @@ -7,14 +7,14 @@ namespace hello_algo.chapter_sorting; public class merge_sort { - /* Merge left subarray and right subarray */ + /* Merge the left and right subarrays */ void Merge(int[] nums, int left, int mid, int right) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result int[] tmp = new int[right - left + 1]; - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays int i = left, j = mid + 1, k = 0; - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; @@ -28,7 +28,7 @@ public class merge_sort { while (j <= right) { tmp[k++] = nums[j++]; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (k = 0; k < tmp.Length; ++k) { nums[left + k] = tmp[k]; } @@ -36,13 +36,13 @@ public class merge_sort { /* Merge sort */ void MergeSort(int[] nums, int left, int right) { - // Termination condition - if (left >= right) return; // Terminate recursion when subarray length is 1 - // Divide and conquer stage - int mid = left + (right - left) / 2; // Calculate midpoint - MergeSort(nums, left, mid); // Recursively process the left subarray - MergeSort(nums, mid + 1, right); // Recursively process the right subarray - // Merge stage + // Base case + if (left >= right) return; // Stop recursing when the subarray has at most one element + // Divide phase + int mid = left + (right - left) / 2; // Calculate the midpoint + MergeSort(nums, left, mid); // Recurse on the left subarray + MergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase Merge(nums, left, mid, right); } @@ -51,6 +51,6 @@ public class merge_sort { /* Merge sort */ int[] nums = [7, 3, 2, 6, 0, 1, 5, 4]; MergeSort(nums, 0, nums.Length - 1); - Console.WriteLine("After merge sort completes, nums = " + string.Join(",", nums)); + Console.WriteLine("After merge sort, nums = " + string.Join(",", nums)); } } diff --git a/en/codes/csharp/chapter_sorting/quick_sort.cs b/en/codes/csharp/chapter_sorting/quick_sort.cs index 50c90cccd..869bc7bbc 100644 --- a/en/codes/csharp/chapter_sorting/quick_sort.cs +++ b/en/codes/csharp/chapter_sorting/quick_sort.cs @@ -7,34 +7,34 @@ namespace hello_algo.chapter_sorting; class quickSort { - /* Swap elements */ + /* Swap the elements */ static void Swap(int[] nums, int i, int j) { (nums[j], nums[i]) = (nums[i], nums[j]); } - /* Sentinel partition */ + /* Two-pointer partitioning */ static int Partition(int[] nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot Swap(nums, i, j); // Swap these two elements } - Swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + Swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ public static void QuickSort(int[] nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = Partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays QuickSort(nums, left, pivot - 1); QuickSort(nums, pivot + 1, right); } @@ -42,7 +42,7 @@ class quickSort { /* Quick sort class (median pivot optimization) */ class QuickSortMedian { - /* Swap elements */ + /* Swap the elements */ static void Swap(int[] nums, int i, int j) { (nums[j], nums[i]) = (nums[i], nums[j]); } @@ -51,39 +51,39 @@ class QuickSortMedian { static int MedianThree(int[] nums, int left, int mid, int right) { int l = nums[left], m = nums[mid], r = nums[right]; if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m is between l and r + return mid; // m lies between l and r if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l is between m and r + return left; // l lies between m and r return right; } - /* Sentinel partition (median of three) */ + /* Two-pointer partitioning (median of three) */ static int Partition(int[] nums, int left, int right) { // Select the median of three candidate elements int med = MedianThree(nums, left, (left + right) / 2, right); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray Swap(nums, left, med); // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot Swap(nums, i, j); // Swap these two elements } - Swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + Swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ public static void QuickSort(int[] nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = Partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays QuickSort(nums, left, pivot - 1); QuickSort(nums, pivot + 1, right); } @@ -91,39 +91,39 @@ class QuickSortMedian { /* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { - /* Swap elements */ + /* Swap the elements */ static void Swap(int[] nums, int i, int j) { (nums[j], nums[i]) = (nums[i], nums[j]); } - /* Sentinel partition */ + /* Two-pointer partitioning */ static int Partition(int[] nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot Swap(nums, i, j); // Swap these two elements } - Swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + Swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort (recursion depth optimization) */ public static void QuickSort(int[] nums, int left, int right) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while (left < right) { - // Sentinel partition operation + // Perform two-pointer partitioning int pivot = Partition(nums, left, right); - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left < right - pivot) { QuickSort(nums, left, pivot - 1); // Recursively sort the left subarray - left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] } else { QuickSort(nums, pivot + 1, right); // Recursively sort the right subarray - right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] } } } @@ -135,16 +135,16 @@ public class quick_sort { /* Quick sort */ int[] nums = [2, 4, 1, 0, 3, 5]; quickSort.QuickSort(nums, 0, nums.Length - 1); - Console.WriteLine("After quick sort completes, nums = " + string.Join(",", nums)); + Console.WriteLine("After quick sort, nums = " + string.Join(",", nums)); - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ int[] nums1 = [2, 4, 1, 0, 3, 5]; QuickSortMedian.QuickSort(nums1, 0, nums1.Length - 1); - Console.WriteLine("After quick sort (median pivot optimization) completes, nums1 = " + string.Join(",", nums1)); + Console.WriteLine("After quick sort (median pivot optimization), nums1 = " + string.Join(",", nums1)); /* Quick sort (recursion depth optimization) */ int[] nums2 = [2, 4, 1, 0, 3, 5]; QuickSortTailCall.QuickSort(nums2, 0, nums2.Length - 1); - Console.WriteLine("After quick sort (recursion depth optimization) completes, nums2 = " + string.Join(",", nums2)); + Console.WriteLine("After quick sort (recursion depth optimization), nums2 = " + string.Join(",", nums2)); } } diff --git a/en/codes/csharp/chapter_sorting/radix_sort.cs b/en/codes/csharp/chapter_sorting/radix_sort.cs index a3d51cd26..2e6168477 100644 --- a/en/codes/csharp/chapter_sorting/radix_sort.cs +++ b/en/codes/csharp/chapter_sorting/radix_sort.cs @@ -7,35 +7,35 @@ namespace hello_algo.chapter_sorting; public class radix_sort { - /* Get the k-th digit of element num, where exp = 10^(k-1) */ + /* Get the k-th digit of num, where exp = 10^(k-1) */ int Digit(int num, int exp) { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (num / exp) % 10; } - /* Counting sort (based on nums k-th digit) */ + /* Counting sort (sort nums by the k-th digit) */ void CountingSortDigit(int[] nums, int exp) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 int[] counter = new int[10]; int n = nums.Length; - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for (int i = 0; i < n; i++) { - int d = Digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d - counter[d]++; // Count the occurrence of digit d + int d = Digit(nums[i], exp); // Get the k-th digit of nums[i] and store it in d + counter[d]++; // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for (int i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts int[] res = new int[n]; for (int i = n - 1; i >= 0; i--) { int d = Digit(nums[i], exp); - int j = counter[d] - 1; // Get the index j for d in the array + int j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d]--; // Decrease the count of d by 1 + counter[d]--; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for (int i = 0; i < n; i++) { nums[i] = res[i]; } @@ -43,17 +43,17 @@ public class radix_sort { /* Radix sort */ void RadixSort(int[] nums) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits int m = int.MinValue; foreach (int num in nums) { if (num > m) m = num; } - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for (int exp = 1; exp <= m; exp *= 10) { - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) CountingSortDigit(nums, exp); } } @@ -64,6 +64,6 @@ public class radix_sort { int[] nums = [ 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996 ]; RadixSort(nums); - Console.WriteLine("After radix sort completes, nums = " + string.Join(" ", nums)); + Console.WriteLine("After radix sort, nums = " + string.Join(" ", nums)); } } diff --git a/en/codes/csharp/chapter_sorting/selection_sort.cs b/en/codes/csharp/chapter_sorting/selection_sort.cs index ec6b676e1..35cc3c36e 100644 --- a/en/codes/csharp/chapter_sorting/selection_sort.cs +++ b/en/codes/csharp/chapter_sorting/selection_sort.cs @@ -10,15 +10,15 @@ public class selection_sort { /* Selection sort */ void SelectionSort(int[] nums) { int n = nums.Length; - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (int i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) k = j; // Record the index of the smallest element } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range (nums[k], nums[i]) = (nums[i], nums[k]); } } @@ -27,6 +27,6 @@ public class selection_sort { public void Test() { int[] nums = [4, 1, 3, 1, 5, 2]; SelectionSort(nums); - Console.WriteLine("After selection sort completes, nums = " + string.Join(" ", nums)); + Console.WriteLine("After selection sort, nums = " + string.Join(" ", nums)); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/array_deque.cs b/en/codes/csharp/chapter_stack_and_queue/array_deque.cs index ffc391f7b..c5b0823cf 100644 --- a/en/codes/csharp/chapter_stack_and_queue/array_deque.cs +++ b/en/codes/csharp/chapter_stack_and_queue/array_deque.cs @@ -6,11 +6,11 @@ namespace hello_algo.chapter_stack_and_queue; -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ public class ArrayDeque { - int[] nums; // Array for storing double-ended queue elements - int front; // Front pointer, points to the front of the queue element - int queSize; // Double-ended queue length + int[] nums; // Array that stores the deque elements + int front; // Front pointer, pointing to the first element + int queSize; // Deque size /* Constructor */ public ArrayDeque(int capacity) { @@ -18,73 +18,73 @@ public class ArrayDeque { front = queSize = 0; } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ int Capacity() { return nums.Length; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ public int Size() { return queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ public bool IsEmpty() { return queSize == 0; } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ int Index(int i) { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end return (i + Capacity()) % Capacity(); } - /* Front of the queue enqueue */ + /* Enqueue at the front */ public void PushFirst(int num) { if (queSize == Capacity()) { - Console.WriteLine("Double-ended queue is full"); + Console.WriteLine("Deque is full"); return; } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front pointer one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array front = Index(front - 1); - // Add num to front of queue + // Add num to the front nums[front] = num; queSize++; } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ public void PushLast(int num) { if (queSize == Capacity()) { - Console.WriteLine("Double-ended queue is full"); + Console.WriteLine("Deque is full"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear pointer, which points one position past the last element int rear = Index(front + queSize); - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num; queSize++; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ public int PopFirst() { int num = PeekFirst(); - // Move front pointer backward by one position + // Advance the front pointer by one position front = Index(front + 1); queSize--; return num; } - /* Access rear of the queue element */ + /* Dequeue from the rear */ public int PopLast() { int num = PeekLast(); queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ public int PeekFirst() { if (IsEmpty()) { throw new InvalidOperationException(); @@ -92,19 +92,19 @@ public class ArrayDeque { return nums[front]; } - /* Driver Code */ + /* Access the rear element */ public int PeekLast() { if (IsEmpty()) { throw new InvalidOperationException(); } - // Initialize double-ended queue + // Calculate the index of the last element int last = Index(front + queSize - 1); return nums[last]; } - /* Return array for printing */ + /* Return an array for printing */ public int[] ToArray() { - // Elements enqueue + // Copy only the elements within the occupied range int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[Index(j)]; @@ -116,37 +116,37 @@ public class ArrayDeque { public class array_deque { [Test] public void Test() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ ArrayDeque deque = new(10); deque.PushLast(3); deque.PushLast(2); deque.PushLast(5); - Console.WriteLine("Double-ended queue deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("Deque deque = " + string.Join(" ", deque.ToArray())); - /* Update element */ + /* Access an element */ int peekFirst = deque.PeekFirst(); Console.WriteLine("Front element peekFirst = " + peekFirst); int peekLast = deque.PeekLast(); Console.WriteLine("Rear element peekLast = " + peekLast); - /* Elements enqueue */ + /* Enqueue elements */ deque.PushLast(4); - Console.WriteLine("After element 4 enqueues at rear, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("After enqueuing element 4 at the rear, deque = " + string.Join(" ", deque.ToArray())); deque.PushFirst(1); - Console.WriteLine("After element 1 enqueues at front, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("After enqueuing element 1 at the front, deque = " + string.Join(" ", deque.ToArray())); - /* Element dequeue */ + /* Dequeue an element */ int popLast = deque.PopLast(); - Console.WriteLine("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("Element dequeued from the rear = " + popLast + ", after dequeuing from the rear, deque = " + string.Join(" ", deque.ToArray())); int popFirst = deque.PopFirst(); - Console.WriteLine("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("Element dequeued from the front = " + popFirst + ", after dequeuing from the front, deque = " + string.Join(" ", deque.ToArray())); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.Size(); - Console.WriteLine("Double-ended queue length size = " + size); + Console.WriteLine("Deque size = " + size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque.IsEmpty(); - Console.WriteLine("Double-ended queue is empty = " + isEmpty); + Console.WriteLine("Is the deque empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/array_queue.cs b/en/codes/csharp/chapter_stack_and_queue/array_queue.cs index ee897a8c3..8bfc07143 100644 --- a/en/codes/csharp/chapter_stack_and_queue/array_queue.cs +++ b/en/codes/csharp/chapter_stack_and_queue/array_queue.cs @@ -6,28 +6,28 @@ namespace hello_algo.chapter_stack_and_queue; -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ class ArrayQueue { - int[] nums; // Array for storing queue elements - int front; // Front pointer, points to the front of the queue element - int queSize; // Queue length + int[] nums; // Array that stores the queue elements + int front; // Front pointer, pointing to the first element + int queSize; // Queue size public ArrayQueue(int capacity) { nums = new int[capacity]; front = queSize = 0; } - /* Get the capacity of the queue */ + /* Get the queue capacity */ int Capacity() { return nums.Length; } - /* Get the length of the queue */ + /* Get the queue size */ public int Size() { return queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ public bool IsEmpty() { return queSize == 0; } @@ -38,10 +38,10 @@ class ArrayQueue { Console.WriteLine("Queue is full"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear pointer, which points one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array int rear = (front + queSize) % Capacity(); - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num; queSize++; } @@ -49,22 +49,22 @@ class ArrayQueue { /* Dequeue */ public int Pop() { int num = Peek(); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front pointer by one position, wrapping to the beginning if it passes the end front = (front + 1) % Capacity(); queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ public int Peek() { if (IsEmpty()) throw new Exception(); return nums[front]; } - /* Return array */ + /* Return an array */ public int[] ToArray() { - // Elements enqueue + // Copy only the elements within the occupied range int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[j % this.Capacity()]; @@ -76,11 +76,11 @@ class ArrayQueue { public class array_queue { [Test] public void Test() { - /* Access front of the queue element */ + /* Initialize the queue */ int capacity = 10; ArrayQueue queue = new(capacity); - /* Elements enqueue */ + /* Enqueue elements */ queue.Push(1); queue.Push(3); queue.Push(2); @@ -88,27 +88,27 @@ public class array_queue { queue.Push(4); Console.WriteLine("Queue queue = " + string.Join(",", queue.ToArray())); - /* Return list for printing */ + /* Access the front element */ int peek = queue.Peek(); Console.WriteLine("Front element peek = " + peek); - /* Element dequeue */ + /* Dequeue an element */ int pop = queue.Pop(); - Console.WriteLine("Dequeue element pop = " + pop + ", after dequeue, queue = " + string.Join(",", queue.ToArray())); + Console.WriteLine("Dequeued element pop = " + pop + ", after dequeuing, queue = " + string.Join(",", queue.ToArray())); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue.Size(); - Console.WriteLine("Queue length size = " + size); + Console.WriteLine("Queue size = " + size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = queue.IsEmpty(); - Console.WriteLine("Queue is empty = " + isEmpty); + Console.WriteLine("Is the queue empty = " + isEmpty); - /* Test circular array */ + /* Test the circular array */ for (int i = 0; i < 10; i++) { queue.Push(i); queue.Pop(); - Console.WriteLine("Round " + i + " enqueue + dequeue, queue = " + string.Join(",", queue.ToArray())); + Console.WriteLine("Round " + i + " : after enqueue + dequeue, queue = " + string.Join(",", queue.ToArray())); } } } diff --git a/en/codes/csharp/chapter_stack_and_queue/array_stack.cs b/en/codes/csharp/chapter_stack_and_queue/array_stack.cs index 798b5c6e2..97b518aff 100644 --- a/en/codes/csharp/chapter_stack_and_queue/array_stack.cs +++ b/en/codes/csharp/chapter_stack_and_queue/array_stack.cs @@ -6,20 +6,20 @@ namespace hello_algo.chapter_stack_and_queue; -/* Stack based on array implementation */ +/* Stack implemented with an array */ class ArrayStack { List stack; public ArrayStack() { - // Initialize list (dynamic array) + // Initialize the list (dynamic array) stack = []; } - /* Get the length of the stack */ + /* Get the stack size */ public int Size() { return stack.Count; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ public bool IsEmpty() { return Size() == 0; } @@ -38,14 +38,14 @@ class ArrayStack { return val; } - /* Return list for printing */ + /* Access the top element */ public int Peek() { if (IsEmpty()) throw new Exception(); return stack[Size() - 1]; } - /* Convert List to Array and return */ + /* Convert the list to an array and return it */ public int[] ToArray() { return [.. stack]; } @@ -54,10 +54,10 @@ class ArrayStack { public class array_stack { [Test] public void Test() { - /* Access top of the stack element */ + /* Initialize the stack */ ArrayStack stack = new(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.Push(1); stack.Push(3); stack.Push(2); @@ -65,20 +65,20 @@ public class array_stack { stack.Push(4); Console.WriteLine("Stack stack = " + string.Join(",", stack.ToArray())); - /* Return list for printing */ + /* Access the top element */ int peek = stack.Peek(); - Console.WriteLine("Stack top element peek = " + peek); + Console.WriteLine("Top element peek = " + peek); - /* Element pop from stack */ + /* Pop an element from the stack */ int pop = stack.Pop(); - Console.WriteLine("Pop element pop = " + pop + ", after pop, stack = " + string.Join(",", stack.ToArray())); + Console.WriteLine("Popped element pop = " + pop + ", after popping, stack = " + string.Join(",", stack.ToArray())); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack.Size(); - Console.WriteLine("Stack length size = " + size); + Console.WriteLine("Stack size = " + size); - /* Check if empty */ + /* Check whether it is empty */ bool isEmpty = stack.IsEmpty(); - Console.WriteLine("Stack is empty = " + isEmpty); + Console.WriteLine("Is the stack empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/deque.cs b/en/codes/csharp/chapter_stack_and_queue/deque.cs index 86750659d..4f412b2f5 100644 --- a/en/codes/csharp/chapter_stack_and_queue/deque.cs +++ b/en/codes/csharp/chapter_stack_and_queue/deque.cs @@ -9,36 +9,36 @@ namespace hello_algo.chapter_stack_and_queue; public class deque { [Test] public void Test() { - /* Get the length of the double-ended queue */ - // In C#, use LinkedList as deque + /* Initialize the deque */ + // In C#, use LinkedList as a deque LinkedList deque = new(); - /* Elements enqueue */ - deque.AddLast(2); // Add to the rear of the queue + /* Enqueue elements */ + deque.AddLast(2); // Add to the rear deque.AddLast(5); deque.AddLast(4); - deque.AddFirst(3); // Add to the front of the queue + deque.AddFirst(3); // Add to the front deque.AddFirst(1); - Console.WriteLine("Double-ended queue deque = " + string.Join(",", deque)); + Console.WriteLine("Deque deque = " + string.Join(",", deque)); - /* Update element */ - int? peekFirst = deque.First?.Value; // Rear of the queue element + /* Access an element */ + int? peekFirst = deque.First?.Value; // Front element Console.WriteLine("Front element peekFirst = " + peekFirst); - int? peekLast = deque.Last?.Value; // Front of the queue element dequeues + int? peekLast = deque.Last?.Value; // Rear element Console.WriteLine("Rear element peekLast = " + peekLast); - /* Element dequeue */ - deque.RemoveFirst(); // Check if the double-ended queue is empty - Console.WriteLine("After front element dequeues, deque = " + string.Join(",", deque)); - deque.RemoveLast(); // Rear element dequeue - Console.WriteLine("After rear element dequeues, deque = " + string.Join(",", deque)); + /* Dequeue an element */ + deque.RemoveFirst(); // Dequeue the front element + Console.WriteLine("After dequeuing the front element, deque = " + string.Join(",", deque)); + deque.RemoveLast(); // Dequeue the rear element + Console.WriteLine("After dequeuing the rear element, deque = " + string.Join(",", deque)); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.Count; - Console.WriteLine("Double-ended queue length size = " + size); + Console.WriteLine("Deque size = " + size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque.Count == 0; - Console.WriteLine("Double-ended queue is empty = " + isEmpty); + Console.WriteLine("Is the deque empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/linkedlist_deque.cs b/en/codes/csharp/chapter_stack_and_queue/linkedlist_deque.cs index 50fa3554c..e919d5bb9 100644 --- a/en/codes/csharp/chapter_stack_and_queue/linkedlist_deque.cs +++ b/en/codes/csharp/chapter_stack_and_queue/linkedlist_deque.cs @@ -9,26 +9,26 @@ namespace hello_algo.chapter_stack_and_queue; /* Doubly linked list node */ public class ListNode(int val) { public int val = val; // Node value - public ListNode? next = null; // Successor node reference - public ListNode? prev = null; // Predecessor node reference + public ListNode? next = null; // Reference to the next node + public ListNode? prev = null; // Reference to the previous node } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ public class LinkedListDeque { - ListNode? front, rear; // Head node front, tail node rear - int queSize = 0; // Length of the double-ended queue + ListNode? front, rear; // Head node front and tail node rear + int queSize = 0; // Deque size public LinkedListDeque() { front = null; rear = null; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ public int Size() { return queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ public bool IsEmpty() { return Size() == 0; } @@ -41,30 +41,30 @@ public class LinkedListDeque { front = node; rear = node; } - // Front of the queue enqueue operation + // Enqueue at the front else if (isFront) { // Add node to the head of the linked list front!.prev = node; node.next = front; - front = node; // Update head node + front = node; // Update the head node } - // Rear of the queue enqueue operation + // Enqueue at the rear else { // Add node to the tail of the linked list rear!.next = node; node.prev = rear; - rear = node; // Update tail node + rear = node; // Update the tail node } - queSize++; // Update queue length + queSize++; // Update the queue size } - /* Front of the queue enqueue */ + /* Enqueue at the front */ public void PushFirst(int num) { Push(num, true); } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ public void PushLast(int num) { Push(num, false); } @@ -74,60 +74,60 @@ public class LinkedListDeque { if (IsEmpty()) throw new Exception(); int? val; - // When the deque has one element, front and rear point to the same node + // When the queue size is 1, the head and tail point to the same node if (queSize == 1) { val = front!.val; front = null; rear = null; } - // Temporarily store head node value + // Dequeue from the front else if (isFront) { - val = front!.val; // Delete head node - // Delete head node + val = front!.val; // Save the head node value + // Remove the head node ListNode? fNext = front?.next; fNext!.prev = null; front.next = null; - front = fNext; // Update head node + front = fNext; // Update the head node } - // Temporarily store tail node value + // Dequeue from the rear else { - val = rear!.val; // Delete tail node - // Update tail node + val = rear!.val; // Save the tail node value + // Remove the tail node ListNode? rPrev = rear?.prev; rPrev!.next = null; rear.prev = null; - rear = rPrev; // Update tail node + rear = rPrev; // Update the tail node } - queSize--; // Update queue length + queSize--; // Update the queue size return val; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ public int? PopFirst() { return Pop(true); } - /* Access rear of the queue element */ + /* Dequeue from the rear */ public int? PopLast() { return Pop(false); } - /* Return list for printing */ + /* Access the front element */ public int? PeekFirst() { if (IsEmpty()) throw new Exception(); return front?.val; } - /* Driver Code */ + /* Access the rear element */ public int? PeekLast() { if (IsEmpty()) throw new Exception(); return rear?.val; } - /* Return array for printing */ + /* Return an array for printing */ public int?[] ToArray() { ListNode? node = front; int?[] res = new int?[Size()]; @@ -143,37 +143,37 @@ public class LinkedListDeque { public class linkedlist_deque { [Test] public void Test() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ LinkedListDeque deque = new(); deque.PushLast(3); deque.PushLast(2); deque.PushLast(5); - Console.WriteLine("Double-ended queue deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("Deque deque = " + string.Join(" ", deque.ToArray())); - /* Update element */ + /* Access an element */ int? peekFirst = deque.PeekFirst(); Console.WriteLine("Front element peekFirst = " + peekFirst); int? peekLast = deque.PeekLast(); Console.WriteLine("Rear element peekLast = " + peekLast); - /* Elements enqueue */ + /* Enqueue elements */ deque.PushLast(4); - Console.WriteLine("After element 4 enqueues at rear, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("After enqueuing element 4 at the rear, deque = " + string.Join(" ", deque.ToArray())); deque.PushFirst(1); - Console.WriteLine("After element 1 enqueues at front, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("After enqueuing element 1 at the front, deque = " + string.Join(" ", deque.ToArray())); - /* Element dequeue */ + /* Dequeue an element */ int? popLast = deque.PopLast(); - Console.WriteLine("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("Element dequeued from the rear = " + popLast + ", after dequeuing from the rear, deque = " + string.Join(" ", deque.ToArray())); int? popFirst = deque.PopFirst(); - Console.WriteLine("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + string.Join(" ", deque.ToArray())); + Console.WriteLine("Element dequeued from the front = " + popFirst + ", after dequeuing from the front, deque = " + string.Join(" ", deque.ToArray())); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.Size(); - Console.WriteLine("Double-ended queue length size = " + size); + Console.WriteLine("Deque size = " + size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque.IsEmpty(); - Console.WriteLine("Double-ended queue is empty = " + isEmpty); + Console.WriteLine("Is the deque empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs b/en/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs index e58811ce8..2d799dc3f 100644 --- a/en/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs +++ b/en/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs @@ -6,9 +6,9 @@ namespace hello_algo.chapter_stack_and_queue; -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue { - ListNode? front, rear; // Head node front, tail node rear + ListNode? front, rear; // Head node front and tail node rear int queSize = 0; public LinkedListQueue() { @@ -16,12 +16,12 @@ class LinkedListQueue { rear = null; } - /* Get the length of the queue */ + /* Get the queue size */ public int Size() { return queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ public bool IsEmpty() { return Size() == 0; } @@ -30,11 +30,11 @@ class LinkedListQueue { public void Push(int num) { // Add num after the tail node ListNode node = new(num); - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (front == null) { front = node; rear = node; - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node } else if (rear != null) { rear.next = node; rear = node; @@ -45,20 +45,20 @@ class LinkedListQueue { /* Dequeue */ public int Pop() { int num = Peek(); - // Delete head node + // Remove the head node front = front?.next; queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ public int Peek() { if (IsEmpty()) throw new Exception(); return front!.val; } - /* Convert linked list to Array and return */ + /* Convert the linked list to an array and return it */ public int[] ToArray() { if (front == null) return []; @@ -76,10 +76,10 @@ class LinkedListQueue { public class linkedlist_queue { [Test] public void Test() { - /* Access front of the queue element */ + /* Initialize the queue */ LinkedListQueue queue = new(); - /* Elements enqueue */ + /* Enqueue elements */ queue.Push(1); queue.Push(3); queue.Push(2); @@ -87,20 +87,20 @@ public class linkedlist_queue { queue.Push(4); Console.WriteLine("Queue queue = " + string.Join(",", queue.ToArray())); - /* Return list for printing */ + /* Access the front element */ int peek = queue.Peek(); Console.WriteLine("Front element peek = " + peek); - /* Element dequeue */ + /* Dequeue an element */ int pop = queue.Pop(); - Console.WriteLine("Dequeue element pop = " + pop + ", after dequeue, queue = " + string.Join(",", queue.ToArray())); + Console.WriteLine("Dequeued element pop = " + pop + ", after dequeuing, queue = " + string.Join(",", queue.ToArray())); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue.Size(); - Console.WriteLine("Queue length size = " + size); + Console.WriteLine("Queue size = " + size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = queue.IsEmpty(); - Console.WriteLine("Queue is empty = " + isEmpty); + Console.WriteLine("Is the queue empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/linkedlist_stack.cs b/en/codes/csharp/chapter_stack_and_queue/linkedlist_stack.cs index cf0c558b1..9908a7696 100644 --- a/en/codes/csharp/chapter_stack_and_queue/linkedlist_stack.cs +++ b/en/codes/csharp/chapter_stack_and_queue/linkedlist_stack.cs @@ -6,21 +6,21 @@ namespace hello_algo.chapter_stack_and_queue; -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ class LinkedListStack { - ListNode? stackPeek; // Use head node as stack top - int stkSize = 0; // Stack length + ListNode? stackPeek; // Use the head node as the stack top + int stkSize = 0; // Stack size public LinkedListStack() { stackPeek = null; } - /* Get the length of the stack */ + /* Get the stack size */ public int Size() { return stkSize; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ public bool IsEmpty() { return Size() == 0; } @@ -42,14 +42,14 @@ class LinkedListStack { return num; } - /* Return list for printing */ + /* Access the top element */ public int Peek() { if (IsEmpty()) throw new Exception(); return stackPeek!.val; } - /* Convert List to Array and return */ + /* Convert the linked list to an array and return it */ public int[] ToArray() { if (stackPeek == null) return []; @@ -67,10 +67,10 @@ class LinkedListStack { public class linkedlist_stack { [Test] public void Test() { - /* Access top of the stack element */ + /* Initialize the stack */ LinkedListStack stack = new(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.Push(1); stack.Push(3); stack.Push(2); @@ -78,20 +78,20 @@ public class linkedlist_stack { stack.Push(4); Console.WriteLine("Stack stack = " + string.Join(",", stack.ToArray())); - /* Return list for printing */ + /* Access the top element */ int peek = stack.Peek(); - Console.WriteLine("Stack top element peek = " + peek); + Console.WriteLine("Top element peek = " + peek); - /* Element pop from stack */ + /* Pop an element from the stack */ int pop = stack.Pop(); - Console.WriteLine("Pop element pop = " + pop + ", after pop, stack = " + string.Join(",", stack.ToArray())); + Console.WriteLine("Popped element pop = " + pop + ", after popping, stack = " + string.Join(",", stack.ToArray())); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack.Size(); - Console.WriteLine("Stack length size = " + size); + Console.WriteLine("Stack size = " + size); - /* Check if empty */ + /* Check whether it is empty */ bool isEmpty = stack.IsEmpty(); - Console.WriteLine("Stack is empty = " + isEmpty); + Console.WriteLine("Is the stack empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/queue.cs b/en/codes/csharp/chapter_stack_and_queue/queue.cs index 846f58831..94337eca9 100644 --- a/en/codes/csharp/chapter_stack_and_queue/queue.cs +++ b/en/codes/csharp/chapter_stack_and_queue/queue.cs @@ -9,10 +9,10 @@ namespace hello_algo.chapter_stack_and_queue; public class queue { [Test] public void Test() { - /* Access front of the queue element */ + /* Initialize the queue */ Queue queue = new(); - /* Elements enqueue */ + /* Enqueue elements */ queue.Enqueue(1); queue.Enqueue(3); queue.Enqueue(2); @@ -20,20 +20,20 @@ public class queue { queue.Enqueue(4); Console.WriteLine("Queue queue = " + string.Join(",", queue)); - /* Return list for printing */ + /* Access the front element */ int peek = queue.Peek(); Console.WriteLine("Front element peek = " + peek); - /* Element dequeue */ + /* Dequeue an element */ int pop = queue.Dequeue(); - Console.WriteLine("Dequeue element pop = " + pop + ", after dequeue, queue = " + string.Join(",", queue)); + Console.WriteLine("Dequeued element pop = " + pop + ", after dequeuing, queue = " + string.Join(",", queue)); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue.Count; - Console.WriteLine("Queue length size = " + size); + Console.WriteLine("Queue size = " + size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = queue.Count == 0; - Console.WriteLine("Queue is empty = " + isEmpty); + Console.WriteLine("Is the queue empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_stack_and_queue/stack.cs b/en/codes/csharp/chapter_stack_and_queue/stack.cs index 4c3e2e886..0ff0a660e 100644 --- a/en/codes/csharp/chapter_stack_and_queue/stack.cs +++ b/en/codes/csharp/chapter_stack_and_queue/stack.cs @@ -9,32 +9,32 @@ namespace hello_algo.chapter_stack_and_queue; public class stack { [Test] public void Test() { - /* Access top of the stack element */ + /* Initialize the stack */ Stack stack = new(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.Push(1); stack.Push(3); stack.Push(2); stack.Push(5); stack.Push(4); - // Note: stack.ToArray() returns reversed sequence, index 0 is stack top + // Note that stack.ToArray() returns elements in reverse insertion order, with the stack top at index 0 Console.WriteLine("Stack stack = " + string.Join(",", stack)); - /* Return list for printing */ + /* Access the top element */ int peek = stack.Peek(); - Console.WriteLine("Stack top element peek = " + peek); + Console.WriteLine("Top element peek = " + peek); - /* Element pop from stack */ + /* Pop an element from the stack */ int pop = stack.Pop(); - Console.WriteLine("Pop element pop = " + pop + ", after pop, stack = " + string.Join(",", stack)); + Console.WriteLine("Popped element pop = " + pop + ", after popping, stack = " + string.Join(",", stack)); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack.Count; - Console.WriteLine("Stack length size = " + size); + Console.WriteLine("Stack size = " + size); - /* Check if empty */ + /* Check whether it is empty */ bool isEmpty = stack.Count == 0; - Console.WriteLine("Stack is empty = " + isEmpty); + Console.WriteLine("Is the stack empty = " + isEmpty); } } diff --git a/en/codes/csharp/chapter_tree/array_binary_tree.cs b/en/codes/csharp/chapter_tree/array_binary_tree.cs index 22b0e4635..346c6b601 100644 --- a/en/codes/csharp/chapter_tree/array_binary_tree.cs +++ b/en/codes/csharp/chapter_tree/array_binary_tree.cs @@ -6,34 +6,34 @@ namespace hello_algo.chapter_tree; -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ public class ArrayBinaryTree(List arr) { List tree = new(arr); - /* List capacity */ + /* List size */ public int Size() { return tree.Count; } - /* Get value of node at index i */ + /* Get the value of the node at index i */ public int? Val(int i) { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return null to represent an empty position if (i < 0 || i >= Size()) return null; return tree[i]; } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ public int Left(int i) { return 2 * i + 1; } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ public int Right(int i) { return 2 * i + 2; } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ public int Parent(int i) { return (i - 1) / 2; } @@ -41,7 +41,7 @@ public class ArrayBinaryTree(List arr) { /* Level-order traversal */ public List LevelOrder() { List res = []; - // Traverse array directly + // Traverse the array directly for (int i = 0; i < Size(); i++) { if (Val(i).HasValue) res.Add(Val(i)!.Value); @@ -51,7 +51,7 @@ public class ArrayBinaryTree(List arr) { /* Depth-first traversal */ void DFS(int i, string order, List res) { - // If empty position, return + // Return if this position is empty if (!Val(i).HasValue) return; // Preorder traversal @@ -92,38 +92,38 @@ public class ArrayBinaryTree(List arr) { public class array_binary_tree { [Test] public void Test() { - // Initialize binary tree - // Here we use a function to generate a binary tree directly from an array + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array List arr = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15]; TreeNode? root = TreeNode.ListToTree(arr); - Console.WriteLine("\nInitialize binary tree\n"); - Console.WriteLine("Array representation of binary tree:"); + Console.WriteLine("\nInitialize the binary tree\n"); + Console.WriteLine("Array representation of the binary tree:"); Console.WriteLine(arr.PrintList()); - Console.WriteLine("Linked list representation of binary tree:"); + Console.WriteLine("Linked representation of the binary tree:"); PrintUtil.PrintTree(root); - // Binary tree class represented by array + // Binary tree class using an array representation ArrayBinaryTree abt = new(arr); - // Access node + // Access a node int i = 1; int l = abt.Left(i); int r = abt.Right(i); int p = abt.Parent(i); - Console.WriteLine("\nCurrent node index is " + i + ", value is " + abt.Val(i)); - Console.WriteLine("Its left child node index is " + l + ", value is " + (abt.Val(l).HasValue ? abt.Val(l) : "null")); - Console.WriteLine("Its right child node index is " + r + ", value is " + (abt.Val(r).HasValue ? abt.Val(r) : "null")); - Console.WriteLine("Its parent node index is " + p + ", value is " + (abt.Val(p).HasValue ? abt.Val(p) : "null")); + Console.WriteLine("\nCurrent node index: " + i + " with value " + abt.Val(i)); + Console.WriteLine("Left child index: " + l + " with value " + (abt.Val(l).HasValue ? abt.Val(l) : "null")); + Console.WriteLine("Right child index: " + r + " with value " + (abt.Val(r).HasValue ? abt.Val(r) : "null")); + Console.WriteLine("Parent index: " + p + " with value " + (abt.Val(p).HasValue ? abt.Val(p) : "null")); - // Traverse tree + // Traverse the tree List res = abt.LevelOrder(); - Console.WriteLine("\nLevel-order traversal is:" + res.PrintList()); + Console.WriteLine("\nLevel-order traversal:" + res.PrintList()); res = abt.PreOrder(); - Console.WriteLine("Preorder traversal is:" + res.PrintList()); + Console.WriteLine("Preorder traversal:" + res.PrintList()); res = abt.InOrder(); - Console.WriteLine("Inorder traversal is:" + res.PrintList()); + Console.WriteLine("Inorder traversal:" + res.PrintList()); res = abt.PostOrder(); - Console.WriteLine("Postorder traversal is:" + res.PrintList()); + Console.WriteLine("Postorder traversal:" + res.PrintList()); } } \ No newline at end of file diff --git a/en/codes/csharp/chapter_tree/avl_tree.cs b/en/codes/csharp/chapter_tree/avl_tree.cs index c207b618c..8b4966c04 100644 --- a/en/codes/csharp/chapter_tree/avl_tree.cs +++ b/en/codes/csharp/chapter_tree/avl_tree.cs @@ -10,115 +10,115 @@ namespace hello_algo.chapter_tree; class AVLTree { public TreeNode? root; // Root node - /* Get node height */ + /* Get the node height */ int Height(TreeNode? node) { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 return node == null ? -1 : node.height; } - /* Update node height */ + /* Update the node height */ void UpdateHeight(TreeNode node) { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node.height = Math.Max(Height(node.left), Height(node.right)) + 1; } - /* Get balance factor */ + /* Get the balance factor */ public int BalanceFactor(TreeNode? node) { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node == null) return 0; // Node balance factor = left subtree height - right subtree height return Height(node.left) - Height(node.right); } - /* Right rotation operation */ + /* Right rotation */ TreeNode? RightRotate(TreeNode? node) { TreeNode? child = node?.left; TreeNode? grandChild = child?.right; - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.right = node; node.left = grandChild; - // Update node height + // Update the node height UpdateHeight(node); UpdateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Left rotation operation */ + /* Left rotation */ TreeNode? LeftRotate(TreeNode? node) { TreeNode? child = node?.right; TreeNode? grandChild = child?.left; - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.left = node; node.right = grandChild; - // Update node height + // Update the node height UpdateHeight(node); UpdateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ TreeNode? Rotate(TreeNode? node) { - // Get balance factor of node + // Get the balance factor of node int balanceFactorInt = BalanceFactor(node); - // Left-leaning tree + // Left-heavy tree if (balanceFactorInt > 1) { if (BalanceFactor(node?.left) >= 0) { - // Right rotation + // Rotate right return RightRotate(node); } else { - // First left rotation then right rotation + // Rotate left, then right node!.left = LeftRotate(node!.left); return RightRotate(node); } } - // Right-leaning tree + // Right-heavy tree if (balanceFactorInt < -1) { if (BalanceFactor(node?.right) <= 0) { - // Left rotation + // Rotate left return LeftRotate(node); } else { - // First right rotation then left rotation + // Rotate right, then left node!.right = RightRotate(node!.right); return LeftRotate(node); } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node; } - /* Insert node */ + /* Insert a node */ public void Insert(int val) { root = InsertHelper(root, val); } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ TreeNode? InsertHelper(TreeNode? node, int val) { if (node == null) return new TreeNode(val); - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (val < node.val) node.left = InsertHelper(node.left, val); else if (val > node.val) node.right = InsertHelper(node.right, val); else - return node; // Duplicate node not inserted, return directly - UpdateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + return node; // Return without inserting a duplicate node + UpdateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = Rotate(node); - // Return root node of subtree + // Return the subtree root return node; } - /* Remove node */ + /* Remove a node */ public void Remove(int val) { root = RemoveHelper(root, val); } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ TreeNode? RemoveHelper(TreeNode? node, int val) { if (node == null) return null; - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (val < node.val) node.left = RemoveHelper(node.left, val); else if (val > node.val) @@ -126,14 +126,14 @@ class AVLTree { else { if (node.left == null || node.right == null) { TreeNode? child = node.left ?? node.right; - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child == null) return null; - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly else node = child; } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value TreeNode? temp = node.right; while (temp.left != null) { temp = temp.left; @@ -142,29 +142,29 @@ class AVLTree { node.val = temp.val; } } - UpdateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + UpdateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = Rotate(node); - // Return root node of subtree + // Return the subtree root return node; } - /* Search node */ + /* Find a node */ public TreeNode? Search(int val) { TreeNode? cur = root; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < val) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > val) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } } @@ -172,23 +172,23 @@ class AVLTree { public class avl_tree { static void TestInsert(AVLTree tree, int val) { tree.Insert(val); - Console.WriteLine("\nInsert node " + val + ", AVL tree is"); + Console.WriteLine("\nAfter inserting node " + val + " the AVL tree is:"); PrintUtil.PrintTree(tree.root); } static void TestRemove(AVLTree tree, int val) { tree.Remove(val); - Console.WriteLine("\nRemove node " + val + ", AVL tree is"); + Console.WriteLine("\nAfter removing node " + val + " the AVL tree is:"); PrintUtil.PrintTree(tree.root); } [Test] public void Test() { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ AVLTree avlTree = new(); - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted TestInsert(avlTree, 1); TestInsert(avlTree, 2); TestInsert(avlTree, 3); @@ -200,17 +200,17 @@ public class avl_tree { TestInsert(avlTree, 10); TestInsert(avlTree, 6); - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ TestInsert(avlTree, 7); - /* Remove node */ - // Delete node with degree 1 - TestRemove(avlTree, 8); // Delete node with degree 2 - TestRemove(avlTree, 5); // Remove node with degree 1 - TestRemove(avlTree, 4); // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + TestRemove(avlTree, 8); // Remove a node with 0 children + TestRemove(avlTree, 5); // Remove a node with 1 child + TestRemove(avlTree, 4); // Remove a node with 2 children - /* Search node */ + /* Look up a node */ TreeNode? node = avlTree.Search(7); - Console.WriteLine("\nFound node object is " + node + ", node value = " + node?.val); + Console.WriteLine("\nFound node object: " + node + ", node value = " + node?.val); } } diff --git a/en/codes/csharp/chapter_tree/binary_search_tree.cs b/en/codes/csharp/chapter_tree/binary_search_tree.cs index 273f4f0cd..fad4d19f3 100644 --- a/en/codes/csharp/chapter_tree/binary_search_tree.cs +++ b/en/codes/csharp/chapter_tree/binary_search_tree.cs @@ -10,57 +10,57 @@ class BinarySearchTree { TreeNode? root; public BinarySearchTree() { - // Initialize empty tree + // Initialize an empty tree root = null; } - /* Get binary tree root node */ + /* Get the root of the binary tree */ public TreeNode? GetRoot() { return root; } - /* Search node */ + /* Find a node */ public TreeNode? Search(int num) { TreeNode? cur = root; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > num) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } - /* Insert node */ + /* Insert a node */ public void Insert(int num) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (root == null) { root = new TreeNode(num); return; } TreeNode? cur = root, pre = null; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur.val == num) return; pre = cur; - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else cur = cur.left; } - // Insert node + // Insert a node TreeNode node = new(num); if (pre != null) { if (pre.val < num) @@ -71,53 +71,53 @@ class BinarySearchTree { } - /* Remove node */ + /* Remove a node */ public void Remove(int num) { - // If tree is empty, return directly + // Return immediately if the tree is empty if (root == null) return; TreeNode? cur = root, pre = null; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur.val == num) break; pre = cur; - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur else cur = cur.left; } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if (cur == null) return; - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if (cur.left == null || cur.right == null) { - // When number of child nodes = 0 / 1, child = null / that child node + // With 0 / 1 children, child is null / the child node, respectively TreeNode? child = cur.left ?? cur.right; - // Delete node cur + // Remove node cur if (cur != root) { if (pre!.left == cur) pre.left = child; else pre.right = child; } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root root = child; } } - // Number of child nodes = 2 + // Number of children = 2 else { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur TreeNode? tmp = cur.right; while (tmp.left != null) { tmp = tmp.left; } - // Recursively delete node tmp + // Recursively remove node tmp Remove(tmp.val!.Value); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur.val = tmp.val; } } @@ -126,35 +126,35 @@ class BinarySearchTree { public class binary_search_tree { [Test] public void Test() { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ BinarySearchTree bst = new(); - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree int[] nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; foreach (int num in nums) { bst.Insert(num); } - Console.WriteLine("\nInitialized binary tree is\n"); + Console.WriteLine("\nThe initialized binary tree is:\n"); PrintUtil.PrintTree(bst.GetRoot()); - /* Search node */ + /* Find a node */ TreeNode? node = bst.Search(7); - Console.WriteLine("\nFound node object is " + node + ", node value = " + node?.val); + Console.WriteLine("\nFound node object: " + node + ", node value = " + node?.val); - /* Insert node */ + /* Insert a node */ bst.Insert(16); - Console.WriteLine("\nAfter inserting node 16, binary tree is\n"); + Console.WriteLine("\nBinary tree after inserting node 16:\n"); PrintUtil.PrintTree(bst.GetRoot()); - /* Remove node */ + /* Remove a node */ bst.Remove(1); - Console.WriteLine("\nAfter removing node 1, binary tree is\n"); + Console.WriteLine("\nBinary tree after removing node 1:\n"); PrintUtil.PrintTree(bst.GetRoot()); bst.Remove(2); - Console.WriteLine("\nAfter removing node 2, binary tree is\n"); + Console.WriteLine("\nBinary tree after removing node 2:\n"); PrintUtil.PrintTree(bst.GetRoot()); bst.Remove(4); - Console.WriteLine("\nAfter removing node 4, binary tree is\n"); + Console.WriteLine("\nBinary tree after removing node 4:\n"); PrintUtil.PrintTree(bst.GetRoot()); } } diff --git a/en/codes/csharp/chapter_tree/binary_tree.cs b/en/codes/csharp/chapter_tree/binary_tree.cs index e527d7537..cba38ae44 100644 --- a/en/codes/csharp/chapter_tree/binary_tree.cs +++ b/en/codes/csharp/chapter_tree/binary_tree.cs @@ -9,31 +9,31 @@ namespace hello_algo.chapter_tree; public class binary_tree { [Test] public void Test() { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes TreeNode n1 = new(1); TreeNode n2 = new(2); TreeNode n3 = new(3); TreeNode n4 = new(4); TreeNode n5 = new(5); - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1.left = n2; n1.right = n3; n2.left = n4; n2.right = n5; - Console.WriteLine("\nInitialize binary tree\n"); + Console.WriteLine("\nInitialize the binary tree\n"); PrintUtil.PrintTree(n1); - /* Insert node P between n1 -> n2 */ + /* Insert and remove nodes */ TreeNode P = new(0); - // Delete node + // Insert node P between n1 and n2 in n1 -> n2 n1.left = P; P.left = n2; - Console.WriteLine("\nAfter inserting node P\n"); + Console.WriteLine("\nAfter inserting node P:\n"); PrintUtil.PrintTree(n1); // Remove node P n1.left = n2; - Console.WriteLine("\nAfter removing node P\n"); + Console.WriteLine("\nAfter removing node P:\n"); PrintUtil.PrintTree(n1); } } diff --git a/en/codes/csharp/chapter_tree/binary_tree_bfs.cs b/en/codes/csharp/chapter_tree/binary_tree_bfs.cs index b8dacb0ed..5437ab2e4 100644 --- a/en/codes/csharp/chapter_tree/binary_tree_bfs.cs +++ b/en/codes/csharp/chapter_tree/binary_tree_bfs.cs @@ -10,31 +10,31 @@ public class binary_tree_bfs { /* Level-order traversal */ List LevelOrder(TreeNode root) { - // Initialize queue, add root node + // Initialize a queue and enqueue the root Queue queue = new(); queue.Enqueue(root); - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence List list = []; while (queue.Count != 0) { - TreeNode node = queue.Dequeue(); // Dequeue - list.Add(node.val!.Value); // Save node value + TreeNode node = queue.Dequeue(); // Dequeue from the queue + list.Add(node.val!.Value); // Store the node value if (node.left != null) - queue.Enqueue(node.left); // Left child node enqueue + queue.Enqueue(node.left); // Enqueue the left child if (node.right != null) - queue.Enqueue(node.right); // Right child node enqueue + queue.Enqueue(node.right); // Enqueue the right child } return list; } [Test] public void Test() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode? root = TreeNode.ListToTree([1, 2, 3, 4, 5, 6, 7]); - Console.WriteLine("\nInitialize binary tree\n"); + Console.WriteLine("\nInitialize the binary tree\n"); PrintUtil.PrintTree(root); List list = LevelOrder(root!); - Console.WriteLine("\nLevel-order traversal node print sequence = " + string.Join(",", list)); + Console.WriteLine("\nNode sequence from level-order traversal = " + string.Join(",", list)); } } diff --git a/en/codes/csharp/chapter_tree/binary_tree_dfs.cs b/en/codes/csharp/chapter_tree/binary_tree_dfs.cs index 1348d6243..1d9de762f 100644 --- a/en/codes/csharp/chapter_tree/binary_tree_dfs.cs +++ b/en/codes/csharp/chapter_tree/binary_tree_dfs.cs @@ -12,7 +12,7 @@ public class binary_tree_dfs { /* Preorder traversal */ void PreOrder(TreeNode? root) { if (root == null) return; - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree list.Add(root.val!.Value); PreOrder(root.left); PreOrder(root.right); @@ -21,7 +21,7 @@ public class binary_tree_dfs { /* Inorder traversal */ void InOrder(TreeNode? root) { if (root == null) return; - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree InOrder(root.left); list.Add(root.val!.Value); InOrder(root.right); @@ -30,7 +30,7 @@ public class binary_tree_dfs { /* Postorder traversal */ void PostOrder(TreeNode? root) { if (root == null) return; - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root PostOrder(root.left); PostOrder(root.right); list.Add(root.val!.Value); @@ -38,22 +38,22 @@ public class binary_tree_dfs { [Test] public void Test() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode? root = TreeNode.ListToTree([1, 2, 3, 4, 5, 6, 7]); - Console.WriteLine("\nInitialize binary tree\n"); + Console.WriteLine("\nInitialize the binary tree\n"); PrintUtil.PrintTree(root); list.Clear(); PreOrder(root); - Console.WriteLine("\nPreorder traversal node print sequence = " + string.Join(",", list)); + Console.WriteLine("\nNode sequence from preorder traversal = " + string.Join(",", list)); list.Clear(); InOrder(root); - Console.WriteLine("\nInorder traversal node print sequence = " + string.Join(",", list)); + Console.WriteLine("\nNode sequence from inorder traversal = " + string.Join(",", list)); list.Clear(); PostOrder(root); - Console.WriteLine("\nPostorder traversal node print sequence = " + string.Join(",", list)); + Console.WriteLine("\nNode sequence from postorder traversal = " + string.Join(",", list)); } } diff --git a/en/codes/csharp/csharp.sln b/en/codes/csharp/csharp.sln index 0c74ccc53..5df88ec83 100644 --- a/en/codes/csharp/csharp.sln +++ b/en/codes/csharp/csharp.sln @@ -1,25 +1,25 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hello-algo", "hello-algo.csproj", "{48B60439-EFDC-4C8F-AE8D-41979958C8AC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {1E773F8A-FF66-4974-820B-FCE9032D19AE} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hello-algo", "hello-algo.csproj", "{48B60439-EFDC-4C8F-AE8D-41979958C8AC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1E773F8A-FF66-4974-820B-FCE9032D19AE} + EndGlobalSection +EndGlobal diff --git a/en/codes/csharp/utils/ListNode.cs b/en/codes/csharp/utils/ListNode.cs index 400b30361..6921f8bed 100644 --- a/en/codes/csharp/utils/ListNode.cs +++ b/en/codes/csharp/utils/ListNode.cs @@ -9,7 +9,7 @@ public class ListNode(int x) { public int val = x; public ListNode? next; - /* Deserialize array to linked list */ + /* Deserialize an array into a linked list */ public static ListNode? ArrToLinkedList(int[] arr) { ListNode dum = new(0); ListNode head = dum; diff --git a/en/codes/csharp/utils/PrintUtil.cs b/en/codes/csharp/utils/PrintUtil.cs index cd780ed98..74a9dcc4d 100644 --- a/en/codes/csharp/utils/PrintUtil.cs +++ b/en/codes/csharp/utils/PrintUtil.cs @@ -12,7 +12,7 @@ public class Trunk(Trunk? prev, string str) { }; public static class PrintUtil { - /* Print list */ + /* Print a list */ public static void PrintList(IList list) { Console.WriteLine("[" + string.Join(", ", list) + "]"); } @@ -21,7 +21,7 @@ public static class PrintUtil { return $"[ {string.Join(", ", list.Select(x => x?.ToString() ?? "null"))} ]"; } - /* Print matrix (Array) */ + /* Print a matrix (array) */ public static void PrintMatrix(T[][] matrix) { Console.WriteLine("["); foreach (T[] row in matrix) { @@ -30,7 +30,7 @@ public static class PrintUtil { Console.WriteLine("]"); } - /* Print matrix (List) */ + /* Print a matrix (list) */ public static void PrintMatrix(List> matrix) { Console.WriteLine("["); foreach (List row in matrix) { @@ -39,7 +39,7 @@ public static class PrintUtil { Console.WriteLine("]"); } - /* Print linked list */ + /* Print a linked list */ public static void PrintLinkedList(ListNode? head) { List list = []; while (head != null) { @@ -50,7 +50,7 @@ public static class PrintUtil { } /** - * Print binary tree + * Print a binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -58,7 +58,7 @@ public static class PrintUtil { PrintTree(root, null, false); } - /* Print binary tree */ + /* Print a binary tree */ public static void PrintTree(TreeNode? root, Trunk? prev, bool isRight) { if (root == null) { return; @@ -99,32 +99,32 @@ public static class PrintUtil { Console.Write(p.str); } - /* Print hash table */ + /* Print the hash table */ public static void PrintHashMap(Dictionary map) where K : notnull { foreach (var kv in map.Keys) { Console.WriteLine(kv.ToString() + " -> " + map[kv]?.ToString()); } } - /* Print heap */ + /* Print a heap */ public static void PrintHeap(Queue queue) { - Console.Write("Heap array representation:"); + Console.Write("Array representation of the heap:"); List list = [.. queue]; Console.WriteLine(string.Join(',', list)); - Console.WriteLine("Heap tree representation:"); + Console.WriteLine("Tree representation of the heap:"); TreeNode? tree = TreeNode.ListToTree(list.Cast().ToList()); PrintTree(tree); } - /* Print priority queue */ + /* Print a priority queue */ public static void PrintHeap(PriorityQueue queue) { var newQueue = new PriorityQueue(queue.UnorderedItems, queue.Comparer); - Console.Write("Heap array representation:"); + Console.Write("Array representation of the heap:"); List list = []; while (newQueue.TryDequeue(out int element, out _)) { list.Add(element); } - Console.WriteLine("Heap tree representation:"); + Console.WriteLine("Tree representation of the heap:"); Console.WriteLine(string.Join(',', list.ToList())); TreeNode? tree = TreeNode.ListToTree(list.Cast().ToList()); PrintTree(tree); diff --git a/en/codes/csharp/utils/TreeNode.cs b/en/codes/csharp/utils/TreeNode.cs index f4a2af8ee..a03c80741 100644 --- a/en/codes/csharp/utils/TreeNode.cs +++ b/en/codes/csharp/utils/TreeNode.cs @@ -10,14 +10,14 @@ namespace hello_algo.utils; public class TreeNode(int? x) { public int? val = x; // Node value public int height; // Node height - public TreeNode? left; // Reference to left child node - public TreeNode? right; // Reference to right child node + public TreeNode? left; // Reference to the left child + public TreeNode? right; // Reference to the right child - // For the serialization encoding rules, please refer to: - // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ - // Array representation of binary tree: + // For the serialization rules, see: + // https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ + // Array representation of the binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] - // Linked list representation of binary tree: + // Linked representation of the binary tree: // /——— 15 // /——— 7 // /——— 3 diff --git a/en/codes/csharp/utils/Vertex.cs b/en/codes/csharp/utils/Vertex.cs index 926bff993..9ce0c1510 100644 --- a/en/codes/csharp/utils/Vertex.cs +++ b/en/codes/csharp/utils/Vertex.cs @@ -10,7 +10,7 @@ namespace hello_algo.utils; public class Vertex(int val) { public int val = val; - /* Input value list vals, return vertex list vets */ + /* Given the array of values vals, return the array of vertices vets */ public static Vertex[] ValsToVets(int[] vals) { Vertex[] vets = new Vertex[vals.Length]; for (int i = 0; i < vals.Length; i++) { @@ -19,7 +19,7 @@ public class Vertex(int val) { return vets; } - /* Input vertex list vets, return value list vals */ + /* Given the list of vertices vets, return the list of values vals */ public static List VetsToVals(List vets) { List vals = []; foreach (Vertex vet in vets) { diff --git a/en/codes/dart/chapter_array_and_linkedlist/array.dart b/en/codes/dart/chapter_array_and_linkedlist/array.dart index de502849c..c0178566d 100644 --- a/en/codes/dart/chapter_array_and_linkedlist/array.dart +++ b/en/codes/dart/chapter_array_and_linkedlist/array.dart @@ -8,63 +8,63 @@ import 'dart:math'; -/* Random access to element */ +/* Access an element at a random index */ int randomAccess(List nums) { - // Randomly select a number in the interval [0, nums.length) + // Choose a random index in the interval [0, nums.length) int randomIndex = Random().nextInt(nums.length); - // Retrieve and return the random element + // Get and return the randomly selected element int randomNum = nums[randomIndex]; return randomNum; } -/* Extend array length */ +/* Extend the array length */ List extend(List nums, int enlarge) { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res; } -/* Insert element _num at array index index */ +/* Insert element _num at index in the array */ void insert(List nums, int _num, int index) { - // Move all elements at and after index index backward by one position + // Shift all elements at and after index `index` one position to the right for (var i = nums.length - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign _num to element at index + // Assign _num to the element at index nums[index] = _num; } -/* Remove the element at index index */ +/* Remove the element at index `index` */ void remove(List nums, int index) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (var i = index; i < nums.length - 1; i++) { nums[i] = nums[i + 1]; } } -/* Traverse array elements */ +/* Traverse the array elements */ void traverse(List nums) { int count = 0; - // Traverse array by index + // Traverse the array by index for (var i = 0; i < nums.length; i++) { count += nums[i]; } - // Direct traversal of array elements + // Iterate directly over the array elements for (int _num in nums) { count += _num; } - // Traverse array using forEach method + // Traverse the array using forEach nums.forEach((_num) { count += _num; }); } -/* Find the specified element in the array */ +/* Find a 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; @@ -72,34 +72,34 @@ int find(List nums, int target) { return -1; } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize array */ + /* Initialize the array */ var arr = List.filled(5, 0); print('Array arr = $arr'); List nums = [1, 3, 2, 5, 4]; print('Array nums = $nums'); - /* Insert element */ + /* Random access */ int randomNum = randomAccess(nums); - print('Get random element $randomNum from nums'); + print('Random element from nums: $randomNum'); - /* Traverse array */ + /* Extend the length */ nums = extend(nums, 3); - print('Extend array length to 8, get nums = $nums'); + print('After extending the array length to 8, nums = $nums'); - /* Insert element */ + /* Insert an element */ insert(nums, 6, 3); - print("Insert number 6 at index 3, get nums = $nums"); + print("After inserting 6 at index 3, nums = $nums"); - /* Remove element */ + /* Remove an element */ remove(nums, 2); - print("Delete element at index 2, get nums = $nums"); + print("After removing the element at index 2, nums = $nums"); - /* Traverse array */ + /* Traverse the array */ traverse(nums); - /* Find element */ + /* Find an element */ int index = find(nums, 3); - print("Find element 3 in nums, index = $index"); + print("Search for 3 in nums: index = $index"); } diff --git a/en/codes/dart/chapter_array_and_linkedlist/linked_list.dart b/en/codes/dart/chapter_array_and_linkedlist/linked_list.dart index 8d4115342..07408b650 100644 --- a/en/codes/dart/chapter_array_and_linkedlist/linked_list.dart +++ b/en/codes/dart/chapter_array_and_linkedlist/linked_list.dart @@ -23,7 +23,7 @@ void remove(ListNode n0) { n0.next = n1; } -/* Access the node at index index in the linked list */ +/* 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; @@ -45,39 +45,39 @@ int find(ListNode? head, int target) { return -1; } -/* Driver Code */ +/* Driver code */ void main() { - // Initialize linked list - // Initialize each node + // Initialize the linked list + // Initialize the individual nodes ListNode n0 = ListNode(1); ListNode n1 = ListNode(3); ListNode n2 = ListNode(2); ListNode n3 = ListNode(5); ListNode n4 = ListNode(4); - // Build references between nodes + // Set up references between the nodes n0.next = n1; n1.next = n2; n2.next = n3; n3.next = n4; - print('Initialized linked list is'); + print('The initialized linked list is'); printLinkedList(n0); - /* Insert node */ + /* Insert a node */ insert(n0, ListNode(0)); - print('Linked list after inserting node is'); + print('The linked list after inserting a node is'); printLinkedList(n0); - /* Remove node */ + /* Remove a node */ remove(n0); - print('Linked list after removing node is'); + print('The linked list after removing a node is'); printLinkedList(n0); - /* Access node */ + /* Access a node */ ListNode? node = access(n0, 3); - print('Value of node at index 3 in linked list = ${node!.val}'); + print('Value of the linked list node at index 3 = ${node!.val}'); - /* Search node */ + /* Find a node */ int index = find(n0, 2); - print('Index of node with value 2 in linked list = $index'); + print('Index of the linked list node with value 2 = $index'); } diff --git a/en/codes/dart/chapter_array_and_linkedlist/list.dart b/en/codes/dart/chapter_array_and_linkedlist/list.dart index 4effdb311..f9a6c1798 100644 --- a/en/codes/dart/chapter_array_and_linkedlist/list.dart +++ b/en/codes/dart/chapter_array_and_linkedlist/list.dart @@ -6,25 +6,25 @@ // ignore_for_file: unused_local_variable -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize list */ + /* Initialize the list */ List nums = [1, 3, 2, 5, 4]; print('List nums = $nums'); - /* Update element */ + /* Access an element */ int _num = nums[1]; - print('Access element at index 1, get _num = $_num'); + print('Access the element at index 1: _num = $_num'); - /* Add elements at the end */ + /* Update an element */ nums[1] = 0; - print('Update element at index 1 to 0, get nums = $nums'); + print('After updating the element at index 1 to 0, nums = $nums'); - /* Remove element */ + /* Clear the list */ nums.clear(); - print('After clearing list, nums = $nums'); + print('After clearing the list, nums = $nums'); - /* Direct traversal of list elements */ + /* Append an element at the end */ nums.add(1); nums.add(3); nums.add(2); @@ -32,20 +32,20 @@ void main() { nums.add(4); print('After adding elements, nums = $nums'); - /* Sort list */ + /* Insert an element in the middle */ nums.insert(3, 6); - print('Insert number 6 at index 3, get nums = $nums'); + print('After inserting 6 at index 3, nums = $nums'); - /* Remove element */ + /* Remove an element */ nums.removeAt(3); - print('Delete element at index 3, get nums = $nums'); + print('After removing the element at index 3, nums = $nums'); - /* Traverse list by index */ + /* Traverse the list by index */ int count = 0; for (var i = 0; i < nums.length; i++) { count += nums[i]; } - /* Directly traverse list elements */ + /* Iterate directly over the list elements */ count = 0; for (var x in nums) { count += x; @@ -54,9 +54,9 @@ void main() { /* Concatenate two lists */ List nums1 = [6, 8, 7, 10, 9]; nums.addAll(nums1); - print('After concatenating list nums1 to nums, get nums = $nums'); + print('After appending list nums1 to nums, nums = $nums'); - /* Sort list */ + /* Sort the list */ nums.sort(); - print('After sorting list, nums = $nums'); + print('After sorting the list, nums = $nums'); } diff --git a/en/codes/dart/chapter_array_and_linkedlist/my_list.dart b/en/codes/dart/chapter_array_and_linkedlist/my_list.dart index 6569ea188..bed95767f 100644 --- a/en/codes/dart/chapter_array_and_linkedlist/my_list.dart +++ b/en/codes/dart/chapter_array_and_linkedlist/my_list.dart @@ -6,49 +6,49 @@ /* List class */ class MyList { - late List _arr; // Array (stores list elements) + late List _arr; // Array (stores the 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 + int _extendRatio = 2; // Factor by which the list capacity grows each time /* Constructor */ MyList() { _arr = List.filled(_capacity, 0); } - /* Get list length (current number of elements) */ + /* Get the list length (current number of elements)*/ int size() => _size; - /* Get list capacity */ + /* Get the list capacity */ int capacity() => _capacity; - /* Update element */ + /* Access an element */ int get(int index) { if (index >= _size) throw RangeError('Index out of bounds'); return _arr[index]; } - /* Add elements at the end */ + /* Update an element */ void set(int index, int _num) { if (index >= _size) throw RangeError('Index out of bounds'); _arr[index] = _num; } - /* Direct traversal of list elements */ + /* Append an element at the end */ void add(int _num) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if (_size == _capacity) extendCapacity(); _arr[_size] = _num; // Update the number of elements _size++; } - /* Sort list */ + /* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if (_size == _capacity) extendCapacity(); - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for (var j = _size - 1; j >= index; j--) { _arr[j + 1] = _arr[j]; } @@ -57,11 +57,11 @@ class MyList { _size++; } - /* Remove element */ + /* Remove an 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 + // Shift all elements after index one position to the left for (var j = index; j < _size - 1; j++) { _arr[j] = _arr[j + 1]; } @@ -71,19 +71,19 @@ class MyList { return _num; } - /* Driver Code */ + /* Grow the list capacity */ void extendCapacity() { - // Create new array with length _extendRatio times original array + // Create a new array with _extendRatio times the length of the original array final _newNums = List.filled(_capacity * _extendRatio, 0); - // Copy original array to new array + // Copy the original array into the new array List.copyRange(_newNums, 0, _arr); - // Update _arr reference + // Update the reference _arr _arr = _newNums; - // Add elements at the end + // Update the list capacity _capacity = _arr.length; } - /* Convert list to array */ + /* Convert the list to an array */ List toArray() { List arr = []; for (var i = 0; i < _size; i++) { @@ -93,11 +93,11 @@ class MyList { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize list */ + /* Initialize the list */ MyList nums = MyList(); - /* Direct traversal of list elements */ + /* Append an element at the end */ nums.add(1); nums.add(3); nums.add(2); @@ -106,27 +106,27 @@ void main() { print( 'List nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}'); - /* Sort list */ + /* Insert an element in the middle */ nums.insert(3, 6); - print('Insert number 6 at index 3, get nums = ${nums.toArray()}'); + print('Insert the number 6 at index 3, giving nums = ${nums.toArray()}'); - /* Remove element */ + /* Remove an element */ nums.remove(3); - print('Delete element at index 3, get nums = ${nums.toArray()}'); + print('Remove the element at index 3, giving nums = ${nums.toArray()}'); - /* Update element */ + /* Access an element */ int _num = nums.get(1); - print('Access element at index 1, get _num = $_num'); + print('Access the element at index 1: _num = $_num'); - /* Add elements at the end */ + /* Update an element */ nums.set(1, 0); - print('Update element at index 1 to 0, get nums = ${nums.toArray()}'); + print('Update the element at index 1 to 0, giving nums = ${nums.toArray()}'); - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for (var i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i); } print( - 'After expansion, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}'); + 'After capacity growth, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}'); } diff --git a/en/codes/dart/chapter_backtracking/n_queens.dart b/en/codes/dart/chapter_backtracking/n_queens.dart index 8dc95fddf..d26a513b0 100644 --- a/en/codes/dart/chapter_backtracking/n_queens.dart +++ b/en/codes/dart/chapter_backtracking/n_queens.dart @@ -4,7 +4,7 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ void backtrack( int row, int n, @@ -14,7 +14,7 @@ void backtrack( List diags1, List diags2, ) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if (row == n) { List> copyState = []; for (List sRow in state) { @@ -23,21 +23,21 @@ void backtrack( res.add(copyState); return; } - // Traverse all columns + // Iterate over all columns for (int col = 0; col < n; col++) { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square int diag1 = row - col + n - 1; int diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = "Q"; cols[col] = true; diags1[diag1] = true; diags2[diag2] = true; - // Place the next row + // Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = "#"; cols[col] = false; diags1[diag1] = false; @@ -46,13 +46,13 @@ void backtrack( } } -/* Solve N queens */ +/* Solve the n-queens problem */ List>> nQueens(int n) { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square List> state = List.generate(n, (index) => List.filled(n, "#")); - List cols = List.filled(n, false); // Record whether there is a queen in the column - List diags1 = List.filled(2 * n - 1, false); // Record whether there is a queen on the main diagonal - List diags2 = List.filled(2 * n - 1, false); // Record whether there is a queen on the anti-diagonal + List cols = List.filled(n, false); // Track whether a column contains a queen + List diags1 = List.filled(2 * n - 1, false); // Track whether a main diagonal contains a queen + List diags2 = List.filled(2 * n - 1, false); // Track whether an anti-diagonal contains a queen List>> res = []; backtrack(0, n, state, res, cols, diags1, diags2); @@ -60,12 +60,12 @@ List>> nQueens(int n) { return res; } -/* Driver Code */ +/* Driver code */ void main() { int n = 4; List>> res = nQueens(n); - print("Input board size is $n"); - print("Total queen placement solutions: ${res.length}"); + print("Input board width and height: $n"); + print("Number of queen arrangements: ${res.length}"); for (List> state in res) { print("--------------------"); for (List row in state) { diff --git a/en/codes/dart/chapter_backtracking/permutations_i.dart b/en/codes/dart/chapter_backtracking/permutations_i.dart index 978effb11..bcda24b7a 100644 --- a/en/codes/dart/chapter_backtracking/permutations_i.dart +++ b/en/codes/dart/chapter_backtracking/permutations_i.dart @@ -4,29 +4,29 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ void backtrack( List state, List choices, List selected, List> res, ) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.length == choices.length) { res.add(List.from(state)); return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choices.length; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state.add(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.removeLast(); } @@ -40,7 +40,7 @@ List> permutationsI(List nums) { return res; } -/* Driver Code */ +/* Driver code */ void main() { List nums = [1, 2, 3]; diff --git a/en/codes/dart/chapter_backtracking/permutations_ii.dart b/en/codes/dart/chapter_backtracking/permutations_ii.dart index ce02b080f..29ce5d1ef 100644 --- a/en/codes/dart/chapter_backtracking/permutations_ii.dart +++ b/en/codes/dart/chapter_backtracking/permutations_ii.dart @@ -4,31 +4,31 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ void backtrack( List state, List choices, List selected, List> res, ) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.length == choices.length) { res.add(List.from(state)); return; } - // Traverse all choices + // Iterate over all choices Set duplicated = {}; for (int i = 0; i < choices.length; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && !duplicated.contains(choice)) { - // Attempt: make choice, update state - duplicated.add(choice); // Record the selected element value + // Try: make a choice and update the state + duplicated.add(choice); // Record the chosen value selected[i] = true; state.add(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.removeLast(); } @@ -42,7 +42,7 @@ List> permutationsII(List nums) { return res; } -/* Driver Code */ +/* Driver code */ void main() { List nums = [1, 2, 2]; diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_i_compact.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_i_compact.dart index 34d91a057..158586d27 100644 --- a/en/codes/dart/chapter_backtracking/preorder_traversal_i_compact.dart +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_i_compact.dart @@ -7,23 +7,23 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ void preOrder(TreeNode? root, List res) { if (root == null) { return; } if (root.val == 7) { - // Record solution + // Record the solution res.add(root); } preOrder(root.left, res); preOrder(root.right, res); } -/* Driver Code */ +/* Driver code */ void main() { TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); - print("\nInitialize binary tree"); + print("\nInitialize the binary tree"); printTree(root); // Preorder traversal diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_ii_compact.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_ii_compact.dart index d2eaa43fb..27aa62638 100644 --- a/en/codes/dart/chapter_backtracking/preorder_traversal_ii_compact.dart +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_ii_compact.dart @@ -7,7 +7,7 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ void preOrder( TreeNode? root, List path, @@ -17,10 +17,10 @@ void preOrder( return; } - // Attempt + // Try path.add(root); if (root.val == 7) { - // Record solution + // Record the solution res.add(List.from(path)); } preOrder(root.left, path, res); @@ -29,10 +29,10 @@ void preOrder( path.removeLast(); } -/* Driver Code */ +/* Driver code */ void main() { TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); - print("\nInitialize binary tree"); + print("\nInitialize the binary tree"); printTree(root); // Preorder traversal @@ -40,7 +40,7 @@ void main() { List> res = []; preOrder(root, path, res); - print("\nOutput all paths from root node to node 7"); + print("\nOutput all paths from the root to nodes with value 7"); for (List vals in res) { print(List.generate(vals.length, (i) => vals[i].val)); } diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_iii_compact.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_compact.dart index ccce19cb0..7fef6ec28 100644 --- a/en/codes/dart/chapter_backtracking/preorder_traversal_iii_compact.dart +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_compact.dart @@ -7,7 +7,7 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ void preOrder( TreeNode? root, List path, @@ -17,10 +17,10 @@ void preOrder( return; } - // Attempt + // Try path.add(root); if (root.val == 7) { - // Record solution + // Record the solution res.add(List.from(path)); } preOrder(root.left, path, res); @@ -29,10 +29,10 @@ void preOrder( path.removeLast(); } -/* Driver Code */ +/* Driver code */ void main() { TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); - print("\nInitialize binary tree"); + print("\nInitialize the binary tree"); printTree(root); // Preorder traversal @@ -40,7 +40,7 @@ void main() { List> res = []; preOrder(root, path, res); - print("\nOutput all paths from root node to node 7"); + print("\nOutput all paths from the root to nodes with value 7 that avoid nodes with value 3"); for (List vals in res) { print(List.generate(vals.length, (i) => vals[i].val)); } diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_iii_template.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_template.dart index 85b68e1f1..89d2fb986 100644 --- a/en/codes/dart/chapter_backtracking/preorder_traversal_iii_template.dart +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_template.dart @@ -7,66 +7,66 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ bool isSolution(List state) { return state.isNotEmpty && state.last.val == 7; } -/* Record solution */ +/* Record the solution */ void recordSolution(List state, List> res) { res.add(List.from(state)); } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ bool isValid(List state, TreeNode? choice) { return choice != null && choice.val != 3; } -/* Update state */ +/* Update the state */ void makeChoice(List state, TreeNode? choice) { state.add(choice!); } -/* Restore state */ +/* Restore the state */ void undoChoice(List state, TreeNode? choice) { state.removeLast(); } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ void backtrack( List state, List choices, List> res, ) { - // Check if it is a solution + // Check whether this is a solution if (isSolution(state)) { - // Record solution + // Record the solution recordSolution(state, res); } - // Traverse all choices + // Iterate over all choices for (TreeNode? choice in choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state, choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, [choice!.left, choice.right], res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } } -/* Driver Code */ +/* Driver code */ void main() { TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); - print("\nInitialize binary tree"); + print("\nInitialize the binary tree"); printTree(root); // Backtracking algorithm List> res = []; backtrack([], [root!], res); - print("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); + print("\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3"); for (List path in res) { print(List.from(path.map((e) => e.val))); } diff --git a/en/codes/dart/chapter_backtracking/subset_sum_i.dart b/en/codes/dart/chapter_backtracking/subset_sum_i.dart index 30b42e186..5a0caabd7 100644 --- a/en/codes/dart/chapter_backtracking/subset_sum_i.dart +++ b/en/codes/dart/chapter_backtracking/subset_sum_i.dart @@ -4,7 +4,7 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ void backtrack( List state, int target, @@ -12,24 +12,24 @@ void backtrack( int start, List> res, ) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum reaches the original target (the remaining target is 0) if (target == 0) { res.add(List.from(state)); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (int i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if (target - choices[i] < 0) { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeLast(); } } @@ -38,13 +38,13 @@ void backtrack( List> subsetSumI(List nums, int target) { List state = []; // State (subset) nums.sort(); // Sort nums - int start = 0; // Start point for traversal - List> res = []; // Result list (subset list) + int start = 0; // Starting index for iteration + List> res = []; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ void main() { List nums = [3, 4, 5]; int target = 9; @@ -52,5 +52,5 @@ void main() { List> res = subsetSumI(nums, target); print("Input array nums = $nums, target = $target"); - print("All subsets with sum equal to $target res = $res"); + print("All subsets with sum $target: res = $res"); } diff --git a/en/codes/dart/chapter_backtracking/subset_sum_i_naive.dart b/en/codes/dart/chapter_backtracking/subset_sum_i_naive.dart index 52845d172..c377f9b63 100644 --- a/en/codes/dart/chapter_backtracking/subset_sum_i_naive.dart +++ b/en/codes/dart/chapter_backtracking/subset_sum_i_naive.dart @@ -4,7 +4,7 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ void backtrack( List state, int target, @@ -12,36 +12,36 @@ void backtrack( List choices, List> res, ) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total == target) { res.add(List.from(state)); return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choices.length; i++) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target, total + choices[i], choices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeLast(); } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ List> subsetSumINaive(List nums, int target) { List state = []; // State (subset) - int total = 0; // Sum of elements - List> res = []; // Result list (subset list) + int total = 0; // Sum of the elements + List> res = []; // Result list (list of subsets) backtrack(state, target, total, nums, res); return res; } -/* Driver Code */ +/* Driver code */ void main() { List nums = [3, 4, 5]; int target = 9; @@ -49,6 +49,6 @@ void main() { List> res = subsetSumINaive(nums, target); print("Input array nums = $nums, target = $target"); - print("All subsets with sum equal to $target res = $res"); - print("Please note that this method outputs results containing duplicate sets"); + print("All subsets with sum $target: res = $res"); + print("Note that this method produces duplicate subsets"); } diff --git a/en/codes/dart/chapter_backtracking/subset_sum_ii.dart b/en/codes/dart/chapter_backtracking/subset_sum_ii.dart index 7c8dbe1a0..42de377d2 100644 --- a/en/codes/dart/chapter_backtracking/subset_sum_ii.dart +++ b/en/codes/dart/chapter_backtracking/subset_sum_ii.dart @@ -4,7 +4,7 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ void backtrack( List state, int target, @@ -12,29 +12,29 @@ void backtrack( int start, List> res, ) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum reaches the original target (the remaining target is 0) if (target == 0) { res.add(List.from(state)); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: start after the previously chosen index to avoid choosing the same element again for (int i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if (target - choices[i] < 0) { break; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if (i > start && choices[i] == choices[i - 1]) { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeLast(); } } @@ -43,13 +43,13 @@ void backtrack( List> subsetSumII(List nums, int target) { List state = []; // State (subset) nums.sort(); // Sort nums - int start = 0; // Start point for traversal - List> res = []; // Result list (subset list) + int start = 0; // Starting index for iteration + List> res = []; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ void main() { List nums = [4, 4, 5]; int target = 9; @@ -57,5 +57,5 @@ void main() { List> res = subsetSumII(nums, target); print("Input array nums = $nums, target = $target"); - print("All subsets with sum equal to $target res = $res"); + print("All subsets with sum $target: res = $res"); } diff --git a/en/codes/dart/chapter_computational_complexity/complexity_exercises.dart b/en/codes/dart/chapter_computational_complexity/complexity_exercises.dart index f577c19cc..556dc96eb 100644 --- a/en/codes/dart/chapter_computational_complexity/complexity_exercises.dart +++ b/en/codes/dart/chapter_computational_complexity/complexity_exercises.dart @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Iterative summation */ +/* Sum using iteration */ int sumIter(int n) { int res = 0; for (int i = 1; i <= n; i++) { @@ -13,7 +13,7 @@ int sumIter(int n) { return res; } -/* Recursive summation */ +/* Sum using recursion */ int sumRecur(int n) { if (n == 1) { return 1; @@ -21,7 +21,7 @@ int sumRecur(int n) { return n + sumRecur(n - 1); } -/* Linear loop */ +/* Linear-time loop */ int linearLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -30,7 +30,7 @@ int linearLoop(int n) { return res; } -/* Quadratic loop */ +/* Quadratic-time loop */ int quadraticLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -41,7 +41,7 @@ int quadraticLoop(int n) { return res; } -/* Logarithmic loop */ +/* Logarithmic-time loop */ int logarithmicLoop(int n) { while (n > 1) { n ~/= 2; @@ -58,6 +58,6 @@ void main() { quadraticLoop(4) != 20 || logarithmicLoop(4) != 1 || logarithmicLoop(5) != 1) { - throw StateError('complexity exercise check failed'); + throw StateError('Complexity exercise check failed'); } } diff --git a/en/codes/dart/chapter_computational_complexity/iteration.dart b/en/codes/dart/chapter_computational_complexity/iteration.dart index e05d5ed79..ae16fca05 100644 --- a/en/codes/dart/chapter_computational_complexity/iteration.dart +++ b/en/codes/dart/chapter_computational_complexity/iteration.dart @@ -7,7 +7,7 @@ /* for loop */ int forLoop(int n) { int res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (int i = 1; i <= n; i++) { res += i; } @@ -17,11 +17,11 @@ int forLoop(int n) { /* while loop */ int whileLoop(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 2, ..., n-1, n + int i = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i; - i++; // Update condition variable + i++; // Update the loop control variable } return res; } @@ -29,23 +29,23 @@ int whileLoop(int n) { /* while loop (two updates) */ int whileLoopII(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 4, 10, ... + int i = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i; - // Update condition variable + // Update the loop control variable i++; i *= 2; } return res; } -/* Nested for loop */ +/* Nested for loops */ String nestedForLoop(int n) { String res = ""; - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (int i = 1; i <= n; i++) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (int j = 1; j <= n; j++) { res += "($i, $j), "; } @@ -53,20 +53,20 @@ String nestedForLoop(int n) { return res; } -/* Driver Code */ +/* Driver code */ void main() { int n = 5; int res; res = forLoop(n); - print("\nFor loop sum result res = $res"); + print("\nSum from the for loop: res = $res"); res = whileLoop(n); - print("\nWhile loop sum result res = $res"); + print("\nSum from the while loop: res = $res"); res = whileLoopII(n); - print("\nWhile loop (two updates) sum result res = $res"); + print("\nSum from the while loop (two updates): res = $res"); String resStr = nestedForLoop(n); - print("\nNested for loop result $resStr"); + print("\nResult of the nested for loops: $resStr"); } diff --git a/en/codes/dart/chapter_computational_complexity/recursion.dart b/en/codes/dart/chapter_computational_complexity/recursion.dart index 10116a087..7c2cc651d 100644 --- a/en/codes/dart/chapter_computational_complexity/recursion.dart +++ b/en/codes/dart/chapter_computational_complexity/recursion.dart @@ -6,11 +6,11 @@ /* Recursion */ int recur(int n) { - // Termination condition + // Base case if (n == 1) return 1; - // Recurse: recursive call + // Recurse: make a recursive call int res = recur(n - 1); - // Return: return result + // Unwind: return the result return n + res; } @@ -19,14 +19,14 @@ int forLoopRecur(int n) { // Use an explicit stack to simulate the system call stack List stack = []; int res = 0; - // Recurse: recursive call + // Recurse: make a recursive call for (int i = n; i > 0; i--) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.add(i); } - // Return: return result + // Unwind: return the result while (!stack.isEmpty) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.removeLast(); } // res = 1+2+3+...+n @@ -35,36 +35,36 @@ int forLoopRecur(int n) { /* Tail recursion */ int tailRecur(int n, int res) { - // Termination condition + // Base case if (n == 0) return res; - // Tail recursive call + // Tail-recursive call return tailRecur(n - 1, res + n); } /* Fibonacci sequence: recursion */ int fib(int n) { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n == 1 || n == 2) return n - 1; - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) int res = fib(n - 1) + fib(n - 2); - // Return result f(n) + // Return the result f(n) return res; } -/* Driver Code */ +/* Driver code */ void main() { int n = 5; int res; res = recur(n); - print("\nRecursion sum result res = $res"); + print("\nSum from the recursive function: res = $res"); res = tailRecur(n, 0); - print("\nTail recursion sum result res = $res"); + print("\nSum from the tail-recursive function: res = $res"); res = forLoopRecur(n); - print("\nUsing iteration to simulate recursion sum result res = $res"); + print("\nSum from simulating recursion with iteration: res = $res"); res = fib(n); - print("\nThe ${n}th Fibonacci number is $res"); + print("\nFibonacci term $n is $res"); } diff --git a/en/codes/dart/chapter_computational_complexity/space_complexity.dart b/en/codes/dart/chapter_computational_complexity/space_complexity.dart index 4c6507457..5251df3dc 100644 --- a/en/codes/dart/chapter_computational_complexity/space_complexity.dart +++ b/en/codes/dart/chapter_computational_complexity/space_complexity.dart @@ -17,9 +17,9 @@ int function() { return 0; } -/* Constant order */ +/* Constant complexity */ void constant(int n) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space final int a = 0; int b = 0; List nums = List.filled(10000, 0); @@ -28,40 +28,40 @@ void constant(int n) { for (var i = 0; i < n; i++) { int c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for (var i = 0; i < n; i++) { function(); } } -/* Linear order */ +/* Linear complexity */ void linear(int n) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space List nums = List.filled(n, 0); // A list of length n occupies O(n) space List nodes = []; for (var i = 0; i < n; i++) { nodes.add(ListNode(i)); } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space Map map = HashMap(); for (var i = 0; i < n; i++) { map.putIfAbsent(i, () => i.toString()); } } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ void linearRecur(int n) { - print('Recursion n = $n'); + print('Recursive call: n = $n'); if (n == 1) return; linearRecur(n - 1); } -/* Exponential order */ +/* Quadratic complexity */ void quadratic(int n) { - // Matrix uses O(n^2) space + // The matrix uses O(n^2) space List> numMatrix = List.generate(n, (_) => List.filled(n, 0)); - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space List> numList = []; for (var i = 0; i < n; i++) { List tmp = []; @@ -72,15 +72,15 @@ void quadratic(int n) { } } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ int quadraticRecur(int n) { if (n <= 0) return 0; List nums = List.filled(n, 0); - print('In recursion n = $n, nums length = ${nums.length}'); + print('In the recursive call with n = $n, length of nums = ${nums.length}'); return quadraticRecur(n - 1); } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ TreeNode? buildTree(int n) { if (n == 0) return null; TreeNode root = TreeNode(0); @@ -89,18 +89,18 @@ TreeNode? buildTree(int n) { return root; } -/* Driver Code */ +/* Driver code */ void main() { int n = 5; - // Constant order + // Constant complexity constant(n); - // Linear order + // Linear complexity linear(n); linearRecur(n); - // Exponential order + // Quadratic complexity quadratic(n); quadraticRecur(n); - // Exponential order + // Exponential complexity TreeNode? root = buildTree(n); printTree(root); } diff --git a/en/codes/dart/chapter_computational_complexity/time_complexity.dart b/en/codes/dart/chapter_computational_complexity/time_complexity.dart index f2338055b..7b81bb284 100644 --- a/en/codes/dart/chapter_computational_complexity/time_complexity.dart +++ b/en/codes/dart/chapter_computational_complexity/time_complexity.dart @@ -6,7 +6,7 @@ // ignore_for_file: unused_local_variable -/* Constant order */ +/* Constant complexity */ int constant(int n) { int count = 0; int size = 100000; @@ -16,7 +16,7 @@ int constant(int n) { return count; } -/* Linear order */ +/* Linear complexity */ int linear(int n) { int count = 0; for (var i = 0; i < n; i++) { @@ -25,20 +25,20 @@ int linear(int n) { return count; } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ int arrayTraversal(List nums) { int count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for (var _num in nums) { count++; } return count; } -/* Exponential order */ +/* Quadratic complexity */ int quadratic(int n) { int count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -47,29 +47,29 @@ int quadratic(int n) { return count; } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ int bubbleSort(List nums) { int count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (var i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (var j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } return count; } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ int exponential(int n) { int count = 0, base = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (var i = 0; i < n; i++) { for (var j = 0; j < base; j++) { count++; @@ -80,13 +80,13 @@ int exponential(int n) { return count; } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation) */ int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation) */ int logarithmic(int n) { int count = 0; while (n > 1) { @@ -96,13 +96,13 @@ int logarithmic(int n) { return count; } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation) */ int logRecur(int n) { if (n <= 1) return 0; return logRecur(n ~/ 2) + 1; } -/* Linearithmic order */ +/* Linearithmic complexity */ int linearLogRecur(int n) { if (n <= 1) return 1; int count = linearLogRecur(n ~/ 2) + linearLogRecur(n ~/ 2); @@ -112,54 +112,54 @@ int linearLogRecur(int n) { return count; } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - // Split from 1 into n + // Split one problem into n subproblems for (var i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; } -/* Driver Code */ +/* Driver code */ void main() { - // You can modify n to run and observe the trend of the number of operations for various complexities + // Try different values of n to see how the operation count grows for each complexity int n = 8; - print('Input data size n = $n'); + print('Input size n = $n'); int count = constant(n); - print('Constant-time operations count = $count'); + print('Operation count for constant complexity = $count'); count = linear(n); - print('Linear-time operations count = $count'); + print('Operation count for linear complexity = $count'); count = arrayTraversal(List.filled(n, 0)); - print('Linear-time (array traversal) operations count = $count'); + print('Operation count for linear complexity (array traversal) = $count'); count = quadratic(n); - print('Quadratic-time operations count = $count'); + print('Operation count for quadratic complexity = $count'); final nums = List.filled(n, 0); for (int i = 0; i < n; i++) { nums[i] = n - i; // [n,n-1,...,2,1] } count = bubbleSort(nums); - print('Quadratic-time (bubble sort) operations count = $count'); + print('Operation count for quadratic complexity (bubble sort) = $count'); count = exponential(n); - print('Exponential-time (iterative) operations count = $count'); + print('Operation count for exponential complexity (iterative) = $count'); count = expRecur(n); - print('Exponential-time (recursive) operations count = $count'); + print('Operation count for exponential complexity (recursive) = $count'); count = logarithmic(n); - print('Logarithmic-time (iterative) operations count = $count'); + print('Operation count for logarithmic complexity (iterative) = $count'); count = logRecur(n); - print('Logarithmic-time (recursive) operations count = $count'); + print('Operation count for logarithmic complexity (recursive) = $count'); count = linearLogRecur(n); - print('Linearithmic-time (recursive) operations count = $count'); + print('Operation count for linearithmic complexity (recursive) = $count'); count = factorialRecur(n); - print('Factorial-time (recursive) operations count = $count'); + print('Operation count for factorial complexity (recursive) = $count'); } diff --git a/en/codes/dart/chapter_computational_complexity/worst_best_time_complexity.dart b/en/codes/dart/chapter_computational_complexity/worst_best_time_complexity.dart index 63d980844..6f22310d4 100644 --- a/en/codes/dart/chapter_computational_complexity/worst_best_time_complexity.dart +++ b/en/codes/dart/chapter_computational_complexity/worst_best_time_complexity.dart @@ -4,37 +4,37 @@ * Author: Jefferson (JeffersonHuang77@gmail.com) */ -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ List randomNumbers(int n) { final nums = List.filled(n, 0); - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (var i = 0; i < n; i++) { nums[i] = i + 1; } - // Randomly shuffle array elements + // Randomly shuffle the array elements nums.shuffle(); return nums; } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ int findOne(List nums) { for (var i = 0; i < nums.length; i++) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] == 1) return i; } return -1; } -/* Driver Code */ +/* Driver code */ void main() { for (var i = 0; i < 10; i++) { int n = 100; final nums = randomNumbers(n); int index = findOne(nums); print('\nArray [ 1, 2, ..., n ] after shuffling = $nums'); - print('Index of number 1 is + $index'); + print('Index of 1 = + $index'); } } diff --git a/en/codes/dart/chapter_divide_and_conquer/binary_search_recur.dart b/en/codes/dart/chapter_divide_and_conquer/binary_search_recur.dart index d33f1b751..aa3558e17 100644 --- a/en/codes/dart/chapter_divide_and_conquer/binary_search_recur.dart +++ b/en/codes/dart/chapter_divide_and_conquer/binary_search_recur.dart @@ -6,20 +6,20 @@ /* Binary search: problem f(i, j) */ int dfs(List nums, int target, int i, int j) { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1; } // Calculate the midpoint index m int m = (i + j) ~/ 2; if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -27,16 +27,16 @@ int dfs(List nums, int target, int i, int j) { /* Binary search */ int binarySearch(List nums, int target) { int n = nums.length; - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1); } -/* Driver Code */ +/* Driver code */ void main() { int target = 6; List nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; - // Binary search (closed interval on both sides) + // Binary search (closed interval) int index = binarySearch(nums, target); print("Index of target element 6 = $index"); } diff --git a/en/codes/dart/chapter_divide_and_conquer/build_tree.dart b/en/codes/dart/chapter_divide_and_conquer/build_tree.dart index a948bf0e7..039396c12 100644 --- a/en/codes/dart/chapter_divide_and_conquer/build_tree.dart +++ b/en/codes/dart/chapter_divide_and_conquer/build_tree.dart @@ -7,7 +7,7 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ TreeNode? dfs( List preorder, Map inorderMap, @@ -15,13 +15,13 @@ TreeNode? dfs( int l, int r, ) { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) { return null; } // Initialize the root node TreeNode? root = TreeNode(preorder[i]); - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees int m = inorderMap[preorder[i]]!; // Subproblem: build the left subtree root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); @@ -31,9 +31,9 @@ TreeNode? dfs( return root; } -/* Build binary tree */ +/* Build a binary tree */ TreeNode? buildTree(List preorder, List inorder) { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index Map inorderMap = {}; for (int i = 0; i < inorder.length; i++) { inorderMap[inorder[i]] = i; @@ -42,12 +42,12 @@ TreeNode? buildTree(List preorder, List inorder) { return root; } -/* Driver Code */ +/* Driver code */ void main() { List preorder = [3, 9, 2, 1, 7]; List inorder = [9, 3, 1, 2, 7]; - print("Pre-order traversal = $preorder"); - print("In-order traversal = $inorder"); + print("Preorder traversal = $preorder"); + print("Inorder traversal = $inorder"); TreeNode? root = buildTree(preorder, inorder); print("The constructed binary tree is:"); diff --git a/en/codes/dart/chapter_divide_and_conquer/fast_power.dart b/en/codes/dart/chapter_divide_and_conquer/fast_power.dart index 8610ab675..d6336abc7 100644 --- a/en/codes/dart/chapter_divide_and_conquer/fast_power.dart +++ b/en/codes/dart/chapter_divide_and_conquer/fast_power.dart @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Exponentiation by squaring */ +/* Fast exponentiation */ int fastPow(int x, int n) { if (n == 0) { return 1; @@ -18,6 +18,6 @@ int fastPow(int x, int n) { void main() { if (fastPow(7, 0) != 1 || fastPow(3, 5) != 243 || fastPow(2, 6) != 64) { - throw StateError('fast power check failed'); + throw StateError('Fast power check failed'); } } diff --git a/en/codes/dart/chapter_divide_and_conquer/hanota.dart b/en/codes/dart/chapter_divide_and_conquer/hanota.dart index bf4d4e3a8..ab0cb66c6 100644 --- a/en/codes/dart/chapter_divide_and_conquer/hanota.dart +++ b/en/codes/dart/chapter_divide_and_conquer/hanota.dart @@ -4,9 +4,9 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Move a disk */ +/* Move one disk */ void move(List src, List tar) { - // Take out a disk from the top of src + // Take a disk from the top of src int pan = src.removeLast(); // Place the disk on top of tar tar.add(pan); @@ -14,14 +14,14 @@ void move(List src, List tar) { /* Solve the Tower of Hanoi problem f(i) */ void dfs(int i, List src, List buf, List tar) { - // If there is only one disk left in src, move it directly to tar + // If only one disk remains to be moved from src, move it directly to tar if (i == 1) { move(src, tar); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); @@ -34,20 +34,20 @@ void solveHanota(List A, List B, List C) { dfs(n, A, B, C); } -/* Driver Code */ +/* Driver code */ void main() { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg List A = [5, 4, 3, 2, 1]; List B = []; List C = []; - print("In initial state:"); + print("Initial state:"); print("A = $A"); print("B = $B"); print("C = $C"); solveHanota(A, B, C); - print("After disk movement is complete:"); + print("After moving the disks:"); print("A = $A"); print("B = $B"); print("C = $C"); diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_backtrack.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_backtrack.dart index 38d02b666..e9b3c5eda 100644 --- a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_backtrack.dart +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_backtrack.dart @@ -6,34 +6,34 @@ /* Backtracking */ void backtrack(List choices, int state, int n, List res) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state == n) { res[0]++; } - // Traverse all choices + // Iterate over all choices for (int choice in choices) { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue; - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res); // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ int climbingStairsBacktrack(int n) { - List choices = [1, 2]; // Can choose to climb up 1 or 2 stairs - int state = 0; // Start climbing from the 0-th stair + List choices = [1, 2]; // Choose to climb 1 or 2 steps + int state = 0; // Start climbing from step 0 List res = []; - res.add(0); // Use res[0] to record the solution count + res.add(0); // Use res[0] to record the number of ways backtrack(choices, state, n, res); return res[0]; } -/* Driver Code */ +/* Driver code */ void main() { int n = 9; int res = climbingStairsBacktrack(n); - print("Climbing $n stairs has $res solutions"); + print("Number of ways to climb $n stairs: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_constraint_dp.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_constraint_dp.dart index e89c358b7..f5e646b5f 100644 --- a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_constraint_dp.dart +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_constraint_dp.dart @@ -4,19 +4,19 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ int climbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions List> dp = List.generate(n + 1, (index) => List.filled(3, 0)); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -24,10 +24,10 @@ int climbingStairsConstraintDP(int n) { return dp[n][1] + dp[n][2]; } -/* Driver Code */ +/* Driver code */ void main() { int n = 9; int res = climbingStairsConstraintDP(n); - print("Climbing $n stairs has $res solutions"); + print("Number of ways to climb $n stairs: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs.dart index 8a913dfa4..8068771e3 100644 --- a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs.dart +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs.dart @@ -6,22 +6,22 @@ /* Search */ int dfs(int i) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; // dp[i] = dp[i-1] + dp[i-2] int count = dfs(i - 1) + dfs(i - 2); return count; } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ int climbingStairsDFS(int n) { return dfs(n); } -/* Driver Code */ +/* Driver code */ void main() { int n = 9; int res = climbingStairsDFS(n); - print("Climbing $n stairs has $res solutions"); + print("Number of ways to climb $n stairs: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs_mem.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs_mem.dart index 9deb07499..6a55a94df 100644 --- a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs_mem.dart +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs_mem.dart @@ -4,11 +4,11 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Memoization search */ +/* Memoized search */ int dfs(int i, List mem) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] int count = dfs(i - 1, mem) + dfs(i - 2, mem); @@ -17,17 +17,17 @@ int dfs(int i, List mem) { return count; } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ int climbingStairsDFSMem(int n) { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded List mem = List.filled(n + 1, -1); return dfs(n, mem); } -/* Driver Code */ +/* Driver code */ void main() { int n = 9; int res = climbingStairsDFSMem(n); - print("Climbing $n stairs has $res solutions"); + print("Number of ways to climb $n stairs: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dp.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dp.dart index 43be90a58..68e345bfb 100644 --- a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dp.dart +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dp.dart @@ -4,22 +4,22 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ int climbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions List dp = List.filled(n + 1, 0); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ int climbingStairsDPComp(int n) { if (n == 1 || n == 2) return n; int a = 1, b = 2; @@ -31,13 +31,13 @@ int climbingStairsDPComp(int n) { return b; } -/* Driver Code */ +/* Driver code */ void main() { int n = 9; int res = climbingStairsDP(n); - print("Climbing $n stairs has $res solutions"); + print("Number of ways to climb $n stairs: $res"); res = climbingStairsDPComp(n); - print("Climbing $n stairs has $res solutions"); + print("Number of ways to climb $n stairs: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/coin_change.dart b/en/codes/dart/chapter_dynamic_programming/coin_change.dart index 463161229..65021a7db 100644 --- a/en/codes/dart/chapter_dynamic_programming/coin_change.dart +++ b/en/codes/dart/chapter_dynamic_programming/coin_change.dart @@ -6,24 +6,24 @@ import 'dart:math'; -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ int coinChangeDP(List coins, int amt) { int n = coins.length; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table List> dp = List.generate(n + 1, (index) => List.filled(amt + 1, 0)); - // State transition: first row and first column + // State transitions: first row and first column for (int a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -31,21 +31,21 @@ int coinChangeDP(List coins, int amt) { return dp[n][amt] != MAX ? dp[n][amt] : -1; } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ int coinChangeDPComp(List coins, int amt) { int n = coins.length; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table List dp = List.filled(amt + 1, MAX); dp[0] = 0; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current amount a, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -53,16 +53,16 @@ int coinChangeDPComp(List coins, int amt) { return dp[amt] != MAX ? dp[amt] : -1; } -/* Driver Code */ +/* Driver code */ void main() { List coins = [1, 2, 5]; int amt = 4; // Dynamic programming int res = coinChangeDP(coins, amt); - print("Minimum coins needed to make target amount is $res"); + print("Minimum number of coins needed to reach the target amount: $res"); // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt); - print("Minimum coins needed to make target amount is $res"); + print("Minimum number of coins needed to reach the target amount: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/coin_change_ii.dart b/en/codes/dart/chapter_dynamic_programming/coin_change_ii.dart index 5c30e80fb..ee3ef464f 100644 --- a/en/codes/dart/chapter_dynamic_programming/coin_change_ii.dart +++ b/en/codes/dart/chapter_dynamic_programming/coin_change_ii.dart @@ -4,23 +4,23 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ int coinChangeIIDP(List coins, int amt) { int n = coins.length; - // Initialize dp table + // Initialize the dp table List> dp = List.generate(n + 1, (index) => List.filled(amt + 1, 0)); - // Initialize first column + // Initialize the first column for (int i = 0; i <= n; i++) { dp[i][0] = 1; } - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -28,20 +28,20 @@ int coinChangeIIDP(List coins, int amt) { return dp[n][amt]; } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ int coinChangeIIDPComp(List coins, int amt) { int n = coins.length; - // Initialize dp table + // Initialize the dp table List dp = List.filled(amt + 1, 0); dp[0] = 1; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current amount a, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -49,16 +49,16 @@ int coinChangeIIDPComp(List coins, int amt) { return dp[amt]; } -/* Driver Code */ +/* Driver code */ void main() { List coins = [1, 2, 5]; int amt = 5; // Dynamic programming int res = coinChangeIIDP(coins, amt); - print("Number of coin combinations to make target amount is $res"); + print("Number of coin combinations that make the target amount: $res"); // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt); - print("Number of coin combinations to make target amount is $res"); + print("Number of coin combinations that make the target amount: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/edit_distance.dart b/en/codes/dart/chapter_dynamic_programming/edit_distance.dart index f48093e3a..43d2d632e 100644 --- a/en/codes/dart/chapter_dynamic_programming/edit_distance.dart +++ b/en/codes/dart/chapter_dynamic_programming/edit_distance.dart @@ -6,64 +6,64 @@ import 'dart:math'; -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ int editDistanceDFS(String s, String t, int i, int j) { - // If both s and t are empty, return 0 + // If both current prefixes of s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return j, the prefix length of t if (i == 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return i, the prefix length of s if (j == 0) return i; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFS(s, t, i, j - 1); int delete = editDistanceDFS(s, t, i - 1, j); int replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits return min(min(insert, delete), replace) + 1; } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ int editDistanceDFSMem(String s, String t, List> mem, int i, int j) { - // If both s and t are empty, return 0 + // If both current prefixes of s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return j, the prefix length of t if (i == 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return i, the prefix length of s if (j == 0) return i; - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) return mem[i][j]; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFSMem(s, t, mem, i, j - 1); int delete = editDistanceDFSMem(s, t, mem, i - 1, j); int replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = min(min(insert, delete), replace) + 1; return mem[i][j]; } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ int editDistanceDP(String s, String t) { int n = s.length, m = t.length; List> dp = List.generate(n + 1, (_) => List.filled(m + 1, 0)); - // State transition: first row and first column + // State transitions: first row and first column for (int i = 1; i <= n; i++) { dp[i][0] = i; } for (int j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } } @@ -71,36 +71,36 @@ int editDistanceDP(String s, String t) { return dp[n][m]; } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ int editDistanceDPComp(String s, String t) { int n = s.length, m = t.length; List dp = List.filled(m + 1, 0); - // State transition: first row + // State transitions: first row for (int j = 1; j <= m; j++) { dp[j] = j; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i <= n; i++) { - // State transition: first column + // State transitions: first column int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } return dp[m]; } -/* Driver Code */ +/* Driver code */ void main() { String s = "bag"; String t = "pack"; @@ -108,18 +108,18 @@ void main() { // Brute-force search int res = editDistanceDFS(s, t, n, m); - print("Changing " + s + " to " + t + " requires minimum $res edits"); + print("Changing " + s + " to " + t + " requires at least $res edits"); - // Memoization search + // Memoized search List> mem = List.generate(n + 1, (_) => List.filled(m + 1, -1)); res = editDistanceDFSMem(s, t, mem, n, m); - print("Changing " + s + " to " + t + " requires minimum $res edits"); + print("Changing " + s + " to " + t + " requires at least $res edits"); // Dynamic programming res = editDistanceDP(s, t); - print("Changing " + s + " to " + t + " requires minimum $res edits"); + print("Changing " + s + " to " + t + " requires at least $res edits"); // Space-optimized dynamic programming res = editDistanceDPComp(s, t); - print("Changing " + s + " to " + t + " requires minimum $res edits"); + print("Changing " + s + " to " + t + " requires at least $res edits"); } diff --git a/en/codes/dart/chapter_dynamic_programming/knapsack.dart b/en/codes/dart/chapter_dynamic_programming/knapsack.dart index b13744b84..f4815adbf 100644 --- a/en/codes/dart/chapter_dynamic_programming/knapsack.dart +++ b/en/codes/dart/chapter_dynamic_programming/knapsack.dart @@ -6,24 +6,24 @@ import 'dart:math'; -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ int knapsackDFS(List wgt, List val, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFS(wgt, val, i - 1, c); int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices return max(no, yes); } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ int knapsackDFSMem( List wgt, List val, @@ -31,39 +31,39 @@ int knapsackDFSMem( int i, int c, ) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] != -1) { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFSMem(wgt, val, mem, i - 1, c); int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = max(no, yes); return mem[i][c]; } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ int knapsackDP(List wgt, List val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table List> dp = List.generate(n + 1, (index) => List.filled(cap + 1, 0)); - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); } } @@ -71,17 +71,17 @@ int knapsackDP(List wgt, List val, int cap) { return dp[n][cap]; } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ int knapsackDPComp(List wgt, List val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table List dp = List.filled(cap + 1, 0); - // State transition + // State transitions for (int i = 1; i <= n; i++) { - // Traverse in reverse order + // Traverse in decreasing order for (int c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -89,7 +89,7 @@ int knapsackDPComp(List wgt, List val, int cap) { return dp[cap]; } -/* Driver Code */ +/* Driver code */ void main() { List wgt = [10, 20, 30, 40, 50]; List val = [50, 120, 150, 210, 240]; @@ -98,19 +98,19 @@ void main() { // Brute-force search int res = knapsackDFS(wgt, val, n, cap); - print("Maximum item value not exceeding knapsack capacity is $res"); + print("Maximum total item value within the knapsack capacity: $res"); - // Memoization search + // Memoized search List> mem = List.generate(n + 1, (index) => List.filled(cap + 1, -1)); res = knapsackDFSMem(wgt, val, mem, n, cap); - print("Maximum item value not exceeding knapsack capacity is $res"); + print("Maximum total item value within the knapsack capacity: $res"); // Dynamic programming res = knapsackDP(wgt, val, cap); - print("Maximum item value not exceeding knapsack capacity is $res"); + print("Maximum total item value within the knapsack capacity: $res"); // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap); - print("Maximum item value not exceeding knapsack capacity is $res"); + print("Maximum total item value within the knapsack capacity: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/min_cost_climbing_stairs_dp.dart b/en/codes/dart/chapter_dynamic_programming/min_cost_climbing_stairs_dp.dart index e87be967c..675402a4a 100644 --- a/en/codes/dart/chapter_dynamic_programming/min_cost_climbing_stairs_dp.dart +++ b/en/codes/dart/chapter_dynamic_programming/min_cost_climbing_stairs_dp.dart @@ -6,23 +6,23 @@ import 'dart:math'; -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ int minCostClimbingStairsDP(List cost) { int n = cost.length - 1; if (n == 1 || n == 2) return cost[n]; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions List dp = List.filled(n + 1, 0); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; } return dp[n]; } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ int minCostClimbingStairsDPComp(List cost) { int n = cost.length - 1; if (n == 1 || n == 2) return cost[n]; @@ -35,14 +35,14 @@ int minCostClimbingStairsDPComp(List cost) { return b; } -/* Driver Code */ +/* Driver code */ void main() { List cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; - print("Input stair cost list is $cost"); + print("Input stair cost list: $cost"); int res = minCostClimbingStairsDP(cost); - print("Minimum cost to climb stairs is $res"); + print("Minimum cost to climb the stairs: $res"); res = minCostClimbingStairsDPComp(cost); - print("Minimum cost to climb stairs is $res"); + print("Minimum cost to climb the stairs: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/min_path_sum.dart b/en/codes/dart/chapter_dynamic_programming/min_path_sum.dart index 1ab3b46fc..cacac56c6 100644 --- a/en/codes/dart/chapter_dynamic_programming/min_path_sum.dart +++ b/en/codes/dart/chapter_dynamic_programming/min_path_sum.dart @@ -6,62 +6,62 @@ import 'dart:math'; -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ int minPathSumDFS(List> grid, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a large cost representing +∞ if (i < 0 || j < 0) { - // In Dart, int type is fixed-range integer, no value representing "infinity" + // Dart int has no infinity value; use 2^31 as a sentinel for this example return BigInt.from(2).pow(31).toInt(); } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) int up = minPathSumDFS(grid, i - 1, j); int left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return min(left, up) + grid[i][j]; } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ int minPathSumDFSMem(List> grid, List> mem, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a large cost representing +∞ if (i < 0 || j < 0) { - // In Dart, int type is fixed-range integer, no value representing "infinity" + // Dart int has no infinity value; use 2^31 as a sentinel for this example return BigInt.from(2).pow(31).toInt(); } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above int up = minPathSumDFSMem(grid, mem, i - 1, j); int left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = min(left, up) + grid[i][j]; return mem[i][j]; } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ int minPathSumDP(List> grid) { int n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table List> dp = List.generate(n, (i) => List.filled(m, 0)); dp[0][0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (int j = 1; j < m; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for (int i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -70,20 +70,20 @@ int minPathSumDP(List> grid) { return dp[n - 1][m - 1]; } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ int minPathSumDPComp(List> grid) { int n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table List dp = List.filled(m, 0); dp[0] = grid[0][0]; for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i < n; i++) { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j < m; j++) { dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -91,7 +91,7 @@ int minPathSumDPComp(List> grid) { return dp[m - 1]; } -/* Driver Code */ +/* Driver code */ void main() { List> grid = [ [1, 3, 1, 5], @@ -103,18 +103,18 @@ void main() { // Brute-force search int res = minPathSumDFS(grid, n - 1, m - 1); - print("Minimum path sum from top-left to bottom-right is $res"); + print("Minimum path sum from the top-left to the bottom-right corner: $res"); -// Memoization search +// Memoized search List> mem = List.generate(n, (i) => List.filled(m, -1)); res = minPathSumDFSMem(grid, mem, n - 1, m - 1); - print("Minimum path sum from top-left to bottom-right is $res"); + print("Minimum path sum from the top-left to the bottom-right corner: $res"); // Dynamic programming res = minPathSumDP(grid); - print("Minimum path sum from top-left to bottom-right is $res"); + print("Minimum path sum from the top-left to the bottom-right corner: $res"); // Space-optimized dynamic programming res = minPathSumDPComp(grid); - print("Minimum path sum from top-left to bottom-right is $res"); + print("Minimum path sum from the top-left to the bottom-right corner: $res"); } diff --git a/en/codes/dart/chapter_dynamic_programming/unbounded_knapsack.dart b/en/codes/dart/chapter_dynamic_programming/unbounded_knapsack.dart index a6411a2ce..fc5507f1b 100644 --- a/en/codes/dart/chapter_dynamic_programming/unbounded_knapsack.dart +++ b/en/codes/dart/chapter_dynamic_programming/unbounded_knapsack.dart @@ -6,19 +6,19 @@ import 'dart:math'; -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ int unboundedKnapsackDP(List wgt, List val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table List> dp = List.generate(n + 1, (index) => List.filled(cap + 1, 0)); - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); } } @@ -26,19 +26,19 @@ int unboundedKnapsackDP(List wgt, List val, int cap) { return dp[n][cap]; } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ int unboundedKnapsackDPComp(List wgt, List val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table List dp = List.filled(cap + 1, 0); - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -46,7 +46,7 @@ int unboundedKnapsackDPComp(List wgt, List val, int cap) { return dp[cap]; } -/* Driver Code */ +/* Driver code */ void main() { List wgt = [1, 2, 3]; List val = [5, 11, 15]; @@ -54,9 +54,9 @@ void main() { // Dynamic programming int res = unboundedKnapsackDP(wgt, val, cap); - print("Maximum item value not exceeding knapsack capacity is $res"); + print("Maximum total item value within the knapsack capacity: $res"); // Space-optimized dynamic programming int resComp = unboundedKnapsackDPComp(wgt, val, cap); - print("Maximum item value not exceeding knapsack capacity is $resComp"); + print("Maximum total item value within the knapsack capacity: $resComp"); } diff --git a/en/codes/dart/chapter_graph/graph_adjacency_list.dart b/en/codes/dart/chapter_graph/graph_adjacency_list.dart index 5240af2ac..a9139bf7a 100644 --- a/en/codes/dart/chapter_graph/graph_adjacency_list.dart +++ b/en/codes/dart/chapter_graph/graph_adjacency_list.dart @@ -6,9 +6,9 @@ import '../utils/vertex.dart'; -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ class GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors Map> adjList = {}; /* Constructor */ @@ -25,7 +25,7 @@ class GraphAdjList { return adjList.length; } - /* Add edge */ + /* Add an edge */ void addEdge(Vertex vet1, Vertex vet2) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || @@ -37,7 +37,7 @@ class GraphAdjList { adjList[vet2]!.add(vet1); } - /* Remove edge */ + /* Remove an edge */ void removeEdge(Vertex vet1, Vertex vet2) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || @@ -49,27 +49,27 @@ class GraphAdjList { adjList[vet2]!.remove(vet1); } - /* Add vertex */ + /* Add a vertex */ void addVertex(Vertex vet) { if (adjList.containsKey(vet)) return; - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list adjList[vet] = []; } - /* Remove vertex */ + /* Remove a vertex */ void removeVertex(Vertex vet) { if (!adjList.containsKey(vet)) { throw ArgumentError; } - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list adjList.remove(vet); - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet adjList.forEach((key, value) { value.remove(vet); }); } - /* Print adjacency list */ + /* Print the adjacency list */ void printAdjList() { print("Adjacency list ="); adjList.forEach((key, value) { @@ -82,9 +82,9 @@ class GraphAdjList { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Add edge */ + /* Initialize the undirected graph */ List v = Vertex.valsToVets([1, 3, 2, 5, 4]); List> edges = [ [v[0], v[1]], @@ -95,30 +95,30 @@ void main() { [v[3], v[4]], ]; GraphAdjList graph = GraphAdjList(edges); - print("\nAfter initialization, graph is"); + print("\nGraph after initialization:"); graph.printAdjList(); - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.addEdge(v[0], v[2]); - print("\nAfter adding edge 1-2, graph is"); + print("\nGraph after adding edge 1-2:"); graph.printAdjList(); - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.removeEdge(v[0], v[1]); - print("\nAfter removing edge 1-3, graph is"); + print("\nGraph after removing edge 1-3:"); graph.printAdjList(); - /* Add vertex */ + /* Add a vertex */ Vertex v5 = Vertex(6); graph.addVertex(v5); - print("\nAfter adding vertex 6, graph is"); + print("\nGraph after adding vertex 6:"); graph.printAdjList(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.removeVertex(v[1]); - print("\nAfter removing vertex 3, graph is"); + print("\nGraph after removing vertex 3:"); graph.printAdjList(); } diff --git a/en/codes/dart/chapter_graph/graph_adjacency_matrix.dart b/en/codes/dart/chapter_graph/graph_adjacency_matrix.dart index 7bfd10eaa..8a6cdb0a0 100644 --- a/en/codes/dart/chapter_graph/graph_adjacency_matrix.dart +++ b/en/codes/dart/chapter_graph/graph_adjacency_matrix.dart @@ -6,21 +6,21 @@ import '../utils/print_util.dart'; -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat { - List vertices = []; // Vertex elements, elements represent "vertex values", indices represent "vertex indices" - List> adjMat = []; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + List vertices = []; // Vertex entries: each element is a vertex value, and its index is the vertex index + List> adjMat = []; //Adjacency matrix: row and column indices correspond to vertex indices /* Constructor */ GraphAdjMat(List vertices, List> edges) { this.vertices = []; this.adjMat = []; - // Add vertex + // Add a vertex for (int val in vertices) { addVertex(val); } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for (List e in edges) { addEdge(e[0], e[1]); } @@ -31,10 +31,10 @@ class GraphAdjMat { return vertices.length; } - /* Add vertex */ + /* Add a vertex */ void addVertex(int val) { int n = size(); - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list vertices.add(val); // Add a row to the adjacency matrix List newRow = List.filled(n, 0, growable: true); @@ -45,37 +45,37 @@ class GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ void removeVertex(int index) { if (index >= size()) { throw IndexError; } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list vertices.removeAt(index); - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix adjMat.removeAt(index); - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for (List row in adjMat) { row.removeAt(index); } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices void addEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { throw IndexError; } - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal adjMat[i][j] = 1; adjMat[j][i] = 1; } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices void removeEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { throw IndexError; } @@ -83,7 +83,7 @@ class GraphAdjMat { adjMat[j][i] = 0; } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ void printAdjMat() { print("Vertex list = $vertices"); print("Adjacency matrix = "); @@ -91,10 +91,10 @@ class GraphAdjMat { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices List vertices = [1, 3, 2, 5, 4]; List> edges = [ [0, 1], @@ -105,29 +105,29 @@ void main() { [3, 4], ]; GraphAdjMat graph = GraphAdjMat(vertices, edges); - print("\nAfter initialization, graph is"); + print("\nGraph after initialization:"); graph.printAdjMat(); - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(0, 2); - print("\nAfter adding edge 1-2, graph is"); + print("\nGraph after adding edge 1-2:"); graph.printAdjMat(); - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(0, 1); - print("\nAfter removing edge 1-3, graph is"); + print("\nGraph after removing edge 1-3:"); graph.printAdjMat(); - /* Add vertex */ + /* Add a vertex */ graph.addVertex(6); - print("\nAfter adding vertex 6, graph is"); + print("\nGraph after adding vertex 6:"); graph.printAdjMat(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(1); - print("\nAfter removing vertex 3, graph is"); + print("\nGraph after removing vertex 3:"); graph.printAdjMat(); } diff --git a/en/codes/dart/chapter_graph/graph_bfs.dart b/en/codes/dart/chapter_graph/graph_bfs.dart index 797f4a27d..95b35b473 100644 --- a/en/codes/dart/chapter_graph/graph_bfs.dart +++ b/en/codes/dart/chapter_graph/graph_bfs.dart @@ -11,35 +11,35 @@ import 'graph_adjacency_list.dart'; /* Breadth-first traversal */ List graphBFS(GraphAdjList graph, Vertex startVet) { - // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + // Represent the graph with an adjacency list to access all neighbors of a given vertex // Vertex traversal sequence List res = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices Set visited = {}; visited.add(startVet); - // Queue used to implement BFS + // Use a queue to implement BFS Queue que = Queue(); que.add(startVet); - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited while (que.isNotEmpty) { Vertex vet = que.removeFirst(); // Dequeue the front vertex - res.add(vet); // Record visited vertex - // Traverse all adjacent vertices of this vertex + res.add(vet); // Record the visited vertex + // Traverse all neighbors of this vertex for (Vertex adjVet in graph.adjList[vet]!) { if (visited.contains(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - que.add(adjVet); // Only enqueue unvisited vertices + que.add(adjVet); // Enqueue only unvisited vertices visited.add(adjVet); // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res; } -/* Dirver Code */ +/* Driver code */ void main() { - /* Add edge */ + /* Initialize the undirected graph */ List v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); List> edges = [ [v[0], v[1]], @@ -56,11 +56,11 @@ void main() { [v[7], v[8]], ]; GraphAdjList graph = GraphAdjList(edges); - print("\nAfter initialization, graph is"); + print("\nGraph after initialization:"); graph.printAdjList(); /* Breadth-first traversal */ List res = graphBFS(graph, v[0]); - print("\nBreadth-first traversal (BFS) vertex sequence is"); + print("\nVertex sequence from breadth-first traversal (BFS):"); print(Vertex.vetsToVals(res)); } diff --git a/en/codes/dart/chapter_graph/graph_dfs.dart b/en/codes/dart/chapter_graph/graph_dfs.dart index 3671a9c67..806fabb1d 100644 --- a/en/codes/dart/chapter_graph/graph_dfs.dart +++ b/en/codes/dart/chapter_graph/graph_dfs.dart @@ -14,14 +14,14 @@ void dfs( List res, Vertex vet, ) { - res.add(vet); // Record visited vertex + res.add(vet); // Record the visited vertex visited.add(vet); // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for (Vertex adjVet in graph.adjList[vet]!) { if (visited.contains(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex dfs(graph, visited, res, adjVet); } } @@ -30,15 +30,15 @@ void dfs( List graphDFS(GraphAdjList graph, Vertex startVet) { // Vertex traversal sequence List res = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices Set visited = {}; dfs(graph, visited, res, startVet); return res; } -/* Driver Code */ +/* Driver code */ void main() { - /* Add edge */ + /* Initialize the undirected graph */ List v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6]); List> edges = [ [v[0], v[1]], @@ -49,11 +49,11 @@ void main() { [v[5], v[6]], ]; GraphAdjList graph = GraphAdjList(edges); - print("\nAfter initialization, graph is"); + print("\nGraph after initialization:"); graph.printAdjList(); /* Depth-first traversal */ List res = graphDFS(graph, v[0]); - print("\nDepth-first traversal (DFS) vertex sequence is"); + print("\nVertex sequence from depth-first traversal (DFS):"); print(Vertex.vetsToVals(res)); } diff --git a/en/codes/dart/chapter_greedy/coin_change_greedy.dart b/en/codes/dart/chapter_greedy/coin_change_greedy.dart index 3e8e34eac..051aef0b9 100644 --- a/en/codes/dart/chapter_greedy/coin_change_greedy.dart +++ b/en/codes/dart/chapter_greedy/coin_change_greedy.dart @@ -4,14 +4,14 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ int coinChangeGreedy(List coins, int amt) { - // Assume coins list is sorted + // Assume the coins list is sorted in ascending order int i = coins.length - 1; int count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (amt > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -19,32 +19,32 @@ int coinChangeGreedy(List coins, int amt) { amt -= coins[i]; count++; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt == 0 ? count : -1; } -/* Driver Code */ +/* Driver code */ void main() { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution List coins = [1, 5, 10, 20, 50, 100]; int amt = 186; int res = coinChangeGreedy(coins, amt); print("\ncoins = $coins, amt = $amt"); - print("Minimum coins needed to make $amt is $res"); + print("Number of coins chosen by the greedy algorithm to make $amt: $res"); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 20, 50]; amt = 60; res = coinChangeGreedy(coins, amt); print("\ncoins = $coins, amt = $amt"); - print("Minimum coins needed to make $amt is $res"); - print("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); + print("Number of coins chosen by the greedy algorithm to make $amt: $res"); + print("The actual minimum is 3 coins: 20 + 20 + 20"); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 49, 50]; amt = 98; res = coinChangeGreedy(coins, amt); print("\ncoins = $coins, amt = $amt"); - print("Minimum coins needed to make $amt is $res"); - print("Actually the minimum number needed is 2, i.e., 49 + 49"); + print("Number of coins chosen by the greedy algorithm to make $amt: $res"); + print("The actual minimum is 2 coins: 49 + 49"); } diff --git a/en/codes/dart/chapter_greedy/fractional_knapsack.dart b/en/codes/dart/chapter_greedy/fractional_knapsack.dart index 4fa3c92ee..c82750376 100644 --- a/en/codes/dart/chapter_greedy/fractional_knapsack.dart +++ b/en/codes/dart/chapter_greedy/fractional_knapsack.dart @@ -12,30 +12,30 @@ class Item { Item(this.w, this.v); } -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ double fractionalKnapsack(List wgt, List val, int cap) { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value List items = List.generate(wgt.length, (i) => Item(wgt[i], val[i])); - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order items.sort((a, b) => (b.v / b.w).compareTo(a.v / a.w)); - // Loop for greedy selection + // Repeatedly make greedy choices double res = 0; for (Item item in items) { if (item.w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v; cap -= item.w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += item.v / item.w * cap; - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break; } } return res; } -/* Driver Code */ +/* Driver code */ void main() { List wgt = [10, 20, 30, 40, 50]; List val = [50, 120, 150, 210, 240]; @@ -43,5 +43,5 @@ void main() { // Greedy algorithm double res = fractionalKnapsack(wgt, val, cap); - print("Maximum item value not exceeding knapsack capacity is $res"); + print("Maximum total item value within the knapsack capacity: $res"); } diff --git a/en/codes/dart/chapter_greedy/max_capacity.dart b/en/codes/dart/chapter_greedy/max_capacity.dart index 48cb47303..fa6b913f9 100644 --- a/en/codes/dart/chapter_greedy/max_capacity.dart +++ b/en/codes/dart/chapter_greedy/max_capacity.dart @@ -6,18 +6,18 @@ import 'dart:math'; -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ int maxCapacity(List ht) { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array int i = 0, j = ht.length - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 int res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity int cap = min(ht[i], ht[j]) * (j - i); res = max(res, cap); - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i++; } else { @@ -27,11 +27,11 @@ int maxCapacity(List ht) { return res; } -/* Driver Code */ +/* Driver code */ void main() { List ht = [3, 8, 5, 2, 7, 7, 3, 4]; // Greedy algorithm int res = maxCapacity(ht); - print("Maximum capacity is $res"); + print("Maximum capacity: $res"); } diff --git a/en/codes/dart/chapter_greedy/max_product_cutting.dart b/en/codes/dart/chapter_greedy/max_product_cutting.dart index 53d21996c..fd3322c53 100644 --- a/en/codes/dart/chapter_greedy/max_product_cutting.dart +++ b/en/codes/dart/chapter_greedy/max_product_cutting.dart @@ -6,32 +6,32 @@ import 'dart:math'; -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ int maxProductCutting(int n) { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder int a = n ~/ 3; int b = n % 3; if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return (pow(3, a - 1) * 2 * 2).toInt(); } if (b == 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return (pow(3, a) * 2).toInt(); } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return pow(3, a).toInt(); } -/* Driver Code */ +/* Driver code */ void main() { int n = 58; // Greedy algorithm int res = maxProductCutting(n); - print("Maximum cutting product is $res"); + print("Maximum product after splitting: $res"); } diff --git a/en/codes/dart/chapter_hashing/array_hash_map.dart b/en/codes/dart/chapter_hashing/array_hash_map.dart index cc11982f7..f2e6b2dc3 100644 --- a/en/codes/dart/chapter_hashing/array_hash_map.dart +++ b/en/codes/dart/chapter_hashing/array_hash_map.dart @@ -11,12 +11,12 @@ class Pair { Pair(this.key, this.val); } -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { late List _buckets; ArrayHashMap() { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets _buckets = List.filled(100, null); } @@ -26,7 +26,7 @@ class ArrayHashMap { return index; } - /* Query operation */ + /* Lookup operation */ String? get(int key) { final int index = _hashFunc(key); final Pair? pair = _buckets[index]; @@ -36,7 +36,7 @@ class ArrayHashMap { return pair.val; } - /* Add operation */ + /* Insert operation */ void put(int key, String val) { final Pair pair = Pair(key, val); final int index = _hashFunc(key); @@ -82,7 +82,7 @@ class ArrayHashMap { return valueSet; } - /* Print hash table */ + /* Print the hash table */ void printHashMap() { for (final Pair kv in pairSet()) { print("${kv.key} -> ${kv.val}"); @@ -90,37 +90,37 @@ class ArrayHashMap { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize hash table */ + /* Initialize the hash table */ final ArrayHashMap map = ArrayHashMap(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - print("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + print("\nHash table after insertion:\nKey -> Value"); map.printHashMap(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value String? name = map.get(15937); - print("\nInput student ID 15937, found name $name"); + print("\nLook up student ID 15937: name = $name"); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583); - print("\nAfter removing 10583, hash table is\nKey -> Value"); + print("\nHash table after removing 10583:\nKey -> Value"); map.printHashMap(); - /* Traverse hash table */ - print("\nTraverse key-value pairs Key->Value"); + /* Traverse the hash table */ + print("\nTraverse key-value pairs: Key->Value"); map.pairSet().forEach((kv) => print("${kv.key} -> ${kv.val}")); - print("\nTraverse keys only Key"); + print("\nTraverse keys only: Key"); map.keySet().forEach((key) => print("$key")); - print("\nTraverse values only Value"); + print("\nTraverse values only: Value"); map.values().forEach((val) => print("$val")); } diff --git a/en/codes/dart/chapter_hashing/built_in_hash.dart b/en/codes/dart/chapter_hashing/built_in_hash.dart index 7f216cead..42fc209f0 100644 --- a/en/codes/dart/chapter_hashing/built_in_hash.dart +++ b/en/codes/dart/chapter_hashing/built_in_hash.dart @@ -6,29 +6,29 @@ import '../chapter_stack_and_queue/linkedlist_deque.dart'; -/* Driver Code */ +/* Driver code */ void main() { int _num = 3; int hashNum = _num.hashCode; - print("Hash value of integer $_num is $hashNum"); + print("Hash value of integer $_num: $hashNum"); bool bol = true; int hashBol = bol.hashCode; - print("Hash value of boolean $bol is $hashBol"); + print("Hash value of boolean $bol: $hashBol"); double dec = 3.14159; int hashDec = dec.hashCode; - print("Hash value of decimal $dec is $hashDec"); + print("Hash value of decimal $dec: $hashDec"); - String str = "Hello Algo"; + String str = "Hello 算法"; int hashStr = str.hashCode; - print("Hash value of string $str is $hashStr"); + print("Hash value of string $str: $hashStr"); - List arr = [12836, "Xiao Ha"]; + List arr = [12836, "小哈"]; int hashArr = arr.hashCode; - print("Hash value of array $arr is $hashArr"); + print("Hash value of array $arr: $hashArr"); ListNode obj = new ListNode(0); int hashObj = obj.hashCode; - print("Hash value of node object $obj is $hashObj"); + print("Hash value of node object $obj: $hashObj"); } diff --git a/en/codes/dart/chapter_hashing/hash_map.dart b/en/codes/dart/chapter_hashing/hash_map.dart index 1841415c3..93dd19d74 100644 --- a/en/codes/dart/chapter_hashing/hash_map.dart +++ b/en/codes/dart/chapter_hashing/hash_map.dart @@ -4,38 +4,38 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize hash table */ + /* Initialize the hash table */ final Map map = {}; - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map[12836] = "Xiao Ha"; - map[15937] = "Xiao Luo"; - map[16750] = "Xiao Suan"; - map[13276] = "Xiao Fa"; - map[10583] = "Xiao Ya"; - print("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map[12836] = "小哈"; + map[15937] = "小啰"; + map[16750] = "小算"; + map[13276] = "小法"; + map[10583] = "小鸭"; + print("\nHash table after insertion:\nKey -> Value"); map.forEach((key, value) => print("$key -> $value")); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value final String? name = map[15937]; - print("\nInput student ID 15937, found name $name"); + print("\nLook up student ID 15937: name = $name"); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583); - print("\nAfter removing 10583, hash table is\nKey -> Value"); + print("\nHash table after removing 10583:\nKey -> Value"); map.forEach((key, value) => print("$key -> $value")); - /* Traverse hash table */ - print("\nTraverse key-value pairs Key->Value"); + /* Traverse the hash table */ + print("\nTraverse key-value pairs: Key->Value"); map.forEach((key, value) => print("$key -> $value")); - print("\nTraverse keys only Key"); + print("\nTraverse keys only: Key"); map.keys.forEach((key) => print(key)); - print("\nTraverse values only Value"); + print("\nTraverse values only: Value"); map.forEach((key, value) => print("$value")); map.values.forEach((value) => print(value)); } diff --git a/en/codes/dart/chapter_hashing/hash_map_chaining.dart b/en/codes/dart/chapter_hashing/hash_map_chaining.dart index b35841d20..53ef88fa6 100644 --- a/en/codes/dart/chapter_hashing/hash_map_chaining.dart +++ b/en/codes/dart/chapter_hashing/hash_map_chaining.dart @@ -10,8 +10,8 @@ import 'array_hash_map.dart'; class HashMapChaining { late int size; // Number of key-value pairs late int capacity; // Hash table capacity - late double loadThres; // Load factor threshold for triggering expansion - late int extendRatio; // Expansion multiplier + late double loadThres; // Load factor threshold that triggers capacity growth + late int extendRatio; // Capacity growth factor late List> buckets; // Bucket array /* Constructor */ @@ -33,36 +33,36 @@ class HashMapChaining { return size / capacity; } - /* Query operation */ + /* Lookup operation */ String? get(int key) { int index = hashFunc(key); List bucket = buckets[index]; - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for (Pair pair in bucket) { if (pair.key == key) { return pair.val; } } - // If key is not found, return null + // Return null if key was not found return null; } - /* Add operation */ + /* Insert operation */ void put(int key, String val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > loadThres) { extend(); } int index = hashFunc(key); List bucket = buckets[index]; - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for (Pair pair in bucket) { if (pair.key == key) { pair.val = val; return; } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end Pair pair = Pair(key, val); bucket.add(pair); size++; @@ -72,7 +72,7 @@ class HashMapChaining { void remove(int key) { int index = hashFunc(key); List bucket = buckets[index]; - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (Pair pair in bucket) { if (pair.key == key) { bucket.remove(pair); @@ -82,15 +82,15 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ void extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table List> bucketsTmp = buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio; buckets = List.generate(capacity, (_) => []); size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (List bucket in bucketsTmp) { for (Pair pair in bucket) { put(pair.key, pair.val); @@ -98,7 +98,7 @@ class HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ void printHashMap() { for (List bucket in buckets) { List res = []; @@ -110,29 +110,29 @@ class HashMapChaining { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize hash table */ + /* Initialize the hash table */ HashMapChaining map = HashMapChaining(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - print("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + print("\nHash table after insertion:\nKey -> Value"); map.printHashMap(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value String? name = map.get(13276); - print("\nInput student ID 13276, found name ${name}"); + print("\nLook up student ID 13276: name = ${name}"); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(12836); - print("\nAfter removing 12836, hash table is\nKey -> Value"); + print("\nHash table after removing 12836:\nKey -> Value"); map.printHashMap(); } diff --git a/en/codes/dart/chapter_hashing/hash_map_open_addressing.dart b/en/codes/dart/chapter_hashing/hash_map_open_addressing.dart index 0c52edac5..d66ab50d7 100644 --- a/en/codes/dart/chapter_hashing/hash_map_open_addressing.dart +++ b/en/codes/dart/chapter_hashing/hash_map_open_addressing.dart @@ -10,10 +10,10 @@ import 'array_hash_map.dart'; class HashMapOpenAddressing { late int _size; // Number of key-value pairs int _capacity = 4; // Hash table capacity - double _loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion - int _extendRatio = 2; // Expansion multiplier + double _loadThres = 2.0 / 3.0; // Load factor threshold that triggers capacity growth + int _extendRatio = 2; // Capacity growth factor late List _buckets; // Bucket array - Pair _TOMBSTONE = Pair(-1, "-1"); // Removal marker + Pair _TOMBSTONE = Pair(-1, "-1"); // Tombstone marker /* Constructor */ HashMapOpenAddressing() { @@ -31,83 +31,83 @@ class HashMapOpenAddressing { return _size / _capacity; } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ int findBucket(int key) { int index = hashFunc(key); int firstTombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (_buckets[index] != null) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (_buckets[index]!.key == key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone != -1) { _buckets[firstTombstone] = _buckets[index]; _buckets[index] = _TOMBSTONE; - return firstTombstone; // Return the moved bucket index + return firstTombstone; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if (firstTombstone == -1 && _buckets[index] == _TOMBSTONE) { firstTombstone = index; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % _capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone == -1 ? index : firstTombstone; } - /* Query operation */ + /* Lookup operation */ String? get(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { return _buckets[index]!.val; } - // If key-value pair does not exist, return null + // Return null if the key-value pair does not exist return null; } - /* Add operation */ + /* Insert operation */ void put(int key, String val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > _loadThres) { extend(); } - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { _buckets[index]!.val = val; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it _buckets[index] = new Pair(key, val); _size++; } /* Remove operation */ void remove(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { _buckets[index] = _TOMBSTONE; _size--; } } - /* Expand hash table */ + /* Grow the hash table capacity */ void extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table List bucketsTmp = _buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity _capacity *= _extendRatio; _buckets = List.generate(_capacity, (index) => null); _size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (Pair? pair in bucketsTmp) { if (pair != null && pair != _TOMBSTONE) { put(pair.key, pair.val); @@ -115,7 +115,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ void printHashMap() { for (Pair? pair in _buckets) { if (pair == null) { @@ -129,29 +129,29 @@ class HashMapOpenAddressing { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize hash table */ + /* Initialize the hash table */ HashMapOpenAddressing map = HashMapOpenAddressing(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - print("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + print("\nHash table after insertion:\nKey -> Value"); map.printHashMap(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value String? name = map.get(13276); - print("\nInput student ID 13276, found name $name"); + print("\nLook up student ID 13276: name = $name"); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(16750); - print("\nAfter removing 16750, hash table is\nKey -> Value"); + print("\nHash table after removing 16750:\nKey -> Value"); map.printHashMap(); } diff --git a/en/codes/dart/chapter_hashing/simple_hash.dart b/en/codes/dart/chapter_hashing/simple_hash.dart index a92933a40..ef9621aad 100644 --- a/en/codes/dart/chapter_hashing/simple_hash.dart +++ b/en/codes/dart/chapter_hashing/simple_hash.dart @@ -34,7 +34,7 @@ int xorHash(String key) { return hash & MODULUS; } -/* Rotational hash */ +/* Rotating hash */ int rotHash(String key) { int hash = 0; final int MODULUS = 1000000007; @@ -44,19 +44,19 @@ int rotHash(String key) { return hash; } -/* Dirver Code */ +/* Driver code */ void main() { - String key = "Hello Algo"; + String key = "Hello 算法"; int hash = addHash(key); - print("Additive hash value is $hash"); + print("Additive hash value: $hash"); hash = mulHash(key); - print("Multiplicative hash value is $hash"); + print("Multiplicative hash value: $hash"); hash = xorHash(key); - print("XOR hash value is $hash"); + print("XOR hash value: $hash"); hash = rotHash(key); - print("Rotational hash value is $hash"); + print("Rotating hash value: $hash"); } diff --git a/en/codes/dart/chapter_heap/my_heap.dart b/en/codes/dart/chapter_heap/my_heap.dart index d986930ff..0d1027a0c 100644 --- a/en/codes/dart/chapter_heap/my_heap.dart +++ b/en/codes/dart/chapter_heap/my_heap.dart @@ -10,29 +10,29 @@ import '../utils/print_util.dart'; class MaxHeap { late List _maxHeap; - /* Constructor, build heap based on input list */ + /* Constructor: build a heap from the input list */ MaxHeap(List nums) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order _maxHeap = nums; - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for (int i = _parent(size() - 1); i >= 0; i--) { siftDown(i); } } - /* Get index of left child node */ + /* Get the left child index */ int _left(int i) { return 2 * i + 1; } - /* Get index of right child node */ + /* Get the right child index */ int _right(int i) { return 2 * i + 2; } - /* Get index of parent node */ + /* Get the parent index */ int _parent(int i) { - return (i - 1) ~/ 2; // Floor division + return (i - 1) ~/ 2; // Integer division } /* Swap elements */ @@ -42,110 +42,110 @@ class MaxHeap { _maxHeap[j] = tmp; } - /* Get heap size */ + /* Get the heap size */ int size() { return _maxHeap.length; } - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty() { return size() == 0; } - /* Access top element */ + /* Access the top element of the heap */ int peek() { return _maxHeap[0]; } - /* Element enters heap */ + /* Push an element onto the heap */ void push(int val) { - // Add node + // Add a node _maxHeap.add(val); - // Heapify from bottom to top + // Sift up siftUp(size() - 1); } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ void siftUp(int i) { while (true) { - // Get parent node of node i + // Get the parent of node i int p = _parent(i); - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || _maxHeap[i] <= _maxHeap[p]) { break; } - // Swap two nodes + // Swap the two nodes _swap(i, p); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Element exits heap */ + /* Pop an element from the heap */ int pop() { - // Handle empty case + // Handle an empty heap if (isEmpty()) throw Exception('Heap is empty'); - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) _swap(0, size() - 1); - // Remove node + // Remove a node int val = _maxHeap.removeLast(); - // Return top element + // Sift down siftDown(0); - // Return heap top element + // Return the top element of the heap return val; } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ void siftDown(int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = _left(i); int r = _right(i); int ma = i; if (l < size() && _maxHeap[l] > _maxHeap[ma]) ma = l; if (r < size() && _maxHeap[r] > _maxHeap[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break; - // Swap two nodes + // Swap the two nodes _swap(i, ma); - // Loop downwards heapification + // Continue sifting down i = ma; } } - /* Driver Code */ + /* Print the heap (binary tree) */ void print() { printHeap(_maxHeap); } } -/* Driver Code */ +/* Driver code */ void main() { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ MaxHeap maxHeap = MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); - print("\nAfter inputting list and building heap"); + print("\nAfter building a heap from the input list:"); maxHeap.print(); - /* Check if heap is empty */ + /* Get the top element of the heap */ int peek = maxHeap.peek(); - print("\nHeap top element is $peek"); + print("\nHeap root: $peek"); - /* Element enters heap */ + /* Push an element onto the heap */ int val = 7; maxHeap.push(val); - print("\nAfter element $val pushes to heap"); + print("\nAfter pushing $val onto the heap"); maxHeap.print(); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.pop(); - print("\nAfter heap top element $peek pops from heap"); + print("\nAfter popping root $peek from the heap"); maxHeap.print(); - /* Get heap size */ + /* Get the heap size */ int size = maxHeap.size(); - print("\nHeap size is $size"); + print("\nNumber of heap elements: $size"); - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty = maxHeap.isEmpty(); - print("\nIs heap empty $isEmpty"); + print("\nIs the heap empty: $isEmpty"); } diff --git a/en/codes/dart/chapter_heap/top_k.dart b/en/codes/dart/chapter_heap/top_k.dart index 159896357..4020229f3 100644 --- a/en/codes/dart/chapter_heap/top_k.dart +++ b/en/codes/dart/chapter_heap/top_k.dart @@ -6,13 +6,13 @@ import '../utils/print_util.dart'; -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ MinHeap topKHeap(List nums, int k) { - // Initialize min heap, push first k elements of array to heap + // Initialize a min heap with the first k elements of the array MinHeap heap = MinHeap(nums.sublist(0, k)); - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for (int i = k; i < nums.length; i++) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if (nums[i] > heap.peek()) { heap.pop(); heap.push(nums[i]); @@ -21,7 +21,7 @@ MinHeap topKHeap(List nums, int k) { return heap; } -/* Driver Code */ +/* Driver code */ void main() { List nums = [1, 7, 6, 3, 2]; int k = 3; @@ -35,34 +35,34 @@ void main() { class MinHeap { late List _minHeap; - /* Constructor, build heap based on input list */ + /* Constructor: build a heap from the input list */ MinHeap(List nums) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order _minHeap = nums; - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for (int i = _parent(size() - 1); i >= 0; i--) { siftDown(i); } } - /* Return elements in heap */ + /* Return the elements in the heap */ List getHeap() { return _minHeap; } - /* Get index of left child node */ + /* Get the left child index */ int _left(int i) { return 2 * i + 1; } - /* Get index of right child node */ + /* Get the right child index */ int _right(int i) { return 2 * i + 2; } - /* Get index of parent node */ + /* Get the parent index */ int _parent(int i) { - return (i - 1) ~/ 2; // Floor division + return (i - 1) ~/ 2; // Integer division } /* Swap elements */ @@ -72,78 +72,78 @@ class MinHeap { _minHeap[j] = tmp; } - /* Get heap size */ + /* Get the heap size */ int size() { return _minHeap.length; } - /* Check if heap is empty */ + /* Check whether the heap is empty */ bool isEmpty() { return size() == 0; } - /* Access top element */ + /* Access the top element of the heap */ int peek() { return _minHeap[0]; } - /* Element enters heap */ + /* Push an element onto the heap */ void push(int val) { - // Add node + // Add a node _minHeap.add(val); - // Heapify from bottom to top + // Sift up siftUp(size() - 1); } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ void siftUp(int i) { while (true) { - // Get parent node of node i + // Get the parent of node i int p = _parent(i); - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || _minHeap[i] >= _minHeap[p]) { break; } - // Swap two nodes + // Swap the two nodes _swap(i, p); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Element exits heap */ + /* Pop an element from the heap */ int pop() { - // Handle empty case + // Handle an empty heap if (isEmpty()) throw Exception('Heap is empty'); - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) _swap(0, size() - 1); - // Remove node + // Remove a node int val = _minHeap.removeLast(); - // Return top element + // Sift down siftDown(0); - // Return heap top element + // Return the top element of the heap return val; } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ void siftDown(int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the smallest value among i, l, and r, and store its index in mi int l = _left(i); int r = _right(i); int mi = i; if (l < size() && _minHeap[l] < _minHeap[mi]) mi = l; if (r < size() && _minHeap[r] < _minHeap[mi]) mi = r; - // Swap two nodes + // Stop if node i is smallest or both child indices l and r are out of bounds if (mi == i) break; - // Swap two nodes + // Swap the two nodes _swap(i, mi); - // Loop downwards heapification + // Continue sifting down i = mi; } } - /* Driver Code */ + /* Print the heap (binary tree) */ void print() { printHeap(_minHeap); } diff --git a/en/codes/dart/chapter_searching/binary_search.dart b/en/codes/dart/chapter_searching/binary_search.dart index dabdb13fe..584b32b40 100644 --- a/en/codes/dart/chapter_searching/binary_search.dart +++ b/en/codes/dart/chapter_searching/binary_search.dart @@ -4,51 +4,51 @@ * Author: Jefferson (JeffersonHuang77@gmail.com) */ -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ int binarySearch(List nums, int target) { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements int i = 0, j = nums.length - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { int m = i + (j - i) ~/ 2; // Calculate the midpoint index m if (nums[m] < target) { - // This means target is in the interval [m+1, j] + // In this case, target must be in [m+1, j] if it exists i = m + 1; } else if (nums[m] > target) { - // This means target is in the interval [i, m-1] + // In this case, target must be in [i, m-1] if it exists j = m - 1; } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ int binarySearchLCRO(List nums, int target) { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last int i = 0, j = nums.length; - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { int m = i + (j - i) ~/ 2; // Calculate the midpoint index m if (nums[m] < target) { - // This means target is in the interval [m+1, j) + // In this case, target must be in [m+1, j) if it exists i = m + 1; } else if (nums[m] > target) { - // This means target is in the interval [i, m) + // In this case, target must be in [i, m) if it exists j = m; } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Driver Code*/ +/* Driver code*/ void main() { int target = 6; final nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; @@ -57,7 +57,7 @@ void main() { int index = binarySearch(nums, target); print('Index of target element 6 = $index'); - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums, target); print('Index of target element 6 = $index'); } diff --git a/en/codes/dart/chapter_searching/binary_search_edge.dart b/en/codes/dart/chapter_searching/binary_search_edge.dart index d1b47acfc..478c16249 100644 --- a/en/codes/dart/chapter_searching/binary_search_edge.dart +++ b/en/codes/dart/chapter_searching/binary_search_edge.dart @@ -6,43 +6,43 @@ import 'binary_search_insertion.dart'; -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ int binarySearchLeftEdge(List nums, int target) { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target int i = binarySearchInsertion(nums, target); - // Target not found, return -1 + // If target was not found, return -1 if (i == nums.length || nums[i] != target) { return -1; } - // Found target, return index i + // Target was found; return index i return i; } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ int binarySearchRightEdge(List nums, int target) { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 int i = binarySearchInsertion(nums, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // i is the first index greater than target (or the array length); j is the candidate rightmost target index int j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if (j == -1 || nums[j] != target) { return -1; } - // Found target, return index j + // Target was found; return index j return j; } -/* Driver Code */ +/* Driver code */ void main() { - // Array with duplicate elements + // Array containing duplicate elements List nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; print("\nArray nums = $nums"); - // Binary search left and right boundaries + // Find the left and right boundaries using binary search for (int target in [6, 7]) { int index = binarySearchLeftEdge(nums, target); - print("Leftmost element $target index is $index"); + print("Index of the leftmost occurrence of $target: $index"); index = binarySearchRightEdge(nums, target); - print("Rightmost element $target index is $index"); + print("Index of the rightmost occurrence of $target: $index"); } } diff --git a/en/codes/dart/chapter_searching/binary_search_insertion.dart b/en/codes/dart/chapter_searching/binary_search_insertion.dart index 13f9fdb5e..6aec4c776 100644 --- a/en/codes/dart/chapter_searching/binary_search_insertion.dart +++ b/en/codes/dart/chapter_searching/binary_search_insertion.dart @@ -4,57 +4,57 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ int binarySearchInsertionSimple(List nums, int target) { - int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) ~/ 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - return m; // Found target, return insertion point m + return m; // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i; } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ int binarySearchInsertion(List nums, int target) { - int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) ~/ 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i return i; } -/* Driver Code */ +/* Driver code */ void main() { - // Array without duplicate elements + // Array with no duplicate elements List nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; print("\nArray nums = $nums"); - // Binary search for insertion point + // Find an insertion point using binary search for (int target in [6, 9]) { int index = binarySearchInsertionSimple(nums, target); - print("Insertion point index for element $target is $index"); + print("Insertion index for $target: $index"); } - // Array with duplicate elements + // Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; print("\nArray nums = $nums"); - // Binary search for insertion point + // Find an insertion point using binary search for (int target in [2, 6, 20]) { int index = binarySearchInsertion(nums, target); - print("Insertion point index for element $target is $index"); + print("Insertion index for $target: $index"); } } diff --git a/en/codes/dart/chapter_searching/hashing_search.dart b/en/codes/dart/chapter_searching/hashing_search.dart index 0f1af23e3..9b32a4c23 100644 --- a/en/codes/dart/chapter_searching/hashing_search.dart +++ b/en/codes/dart/chapter_searching/hashing_search.dart @@ -7,48 +7,48 @@ import 'dart:collection'; import '../utils/list_node.dart'; -/* Hash search (array) */ +/* Hash-based search (array) */ int hashingSearchArray(Map map, int target) { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key if (!map.containsKey(target)) { return -1; } return map[target]!; } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ ListNode? hashingSearchLinkedList(Map map, int target) { - // Hash table key: target node value, value: node object - // If key is not in hash table, return null + // Hash table key: target node value; value: node object + // Return null if this key is absent from the hash table if (!map.containsKey(target)) { return null; } return map[target]!; } -/* Driver Code */ +/* Driver code */ void main() { int target = 3; - /* Hash search (array) */ + /* Hash-based search (array) */ List nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; - // Initialize hash table + // Initialize the hash table Map map = HashMap(); for (int i = 0; i < nums.length; i++) { - map.putIfAbsent(nums[i], () => i); // key: element, value: index + map.putIfAbsent(nums[i], () => i); // key: element; value: index } int index = hashingSearchArray(map, target); print('Index of target element 3 = $index'); - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ ListNode? head = listToLinkedList(nums); - // Initialize hash table + // Initialize the hash table Map map1 = HashMap(); while (head != null) { - map1.putIfAbsent(head.val, () => head!); // key: node value, value: node + map1.putIfAbsent(head.val, () => head!); // key: node value; value: node head = head.next; } ListNode? node = hashingSearchLinkedList(map1, target); - print('Node object corresponding to target node value 3 is $node'); + print('Node object with target value 3: $node'); } diff --git a/en/codes/dart/chapter_searching/linear_search.dart b/en/codes/dart/chapter_searching/linear_search.dart index 22cf5c808..4799bfd6c 100644 --- a/en/codes/dart/chapter_searching/linear_search.dart +++ b/en/codes/dart/chapter_searching/linear_search.dart @@ -8,14 +8,14 @@ import '../utils/list_node.dart'; /* Linear search (array) */ int linearSearchArray(List nums, int target) { - // Traverse array + // Traverse the array for (int i = 0; i < nums.length; i++) { - // Found the target element, return its index + // The target element was found; return its index if (nums[i] == target) { return i; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } @@ -23,25 +23,25 @@ int linearSearchArray(List nums, int target) { ListNode? linearSearchList(ListNode? head, int target) { // Traverse the linked list while (head != null) { - // Found the target node, return it + // The target node was found; return it if (head.val == target) return head; head = head.next; } - // Target element not found, return null + // The target element was not found; return null return null; } -/* Driver Code */ +/* Driver code */ void main() { int target = 3; - /* Perform linear search in array */ + /* Perform linear search on the array */ List nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; int index = linearSearchArray(nums, target); print('Index of target element 3 = $index'); - /* Perform linear search in linked list */ + /* Perform linear search on the linked list */ ListNode? head = listToLinkedList(nums); ListNode? node = linearSearchList(head, target); - print('Node object corresponding to target node value 3 is $node'); + print('Node object with target value 3: $node'); } diff --git a/en/codes/dart/chapter_searching/two_sum.dart b/en/codes/dart/chapter_searching/two_sum.dart index 88e102dd1..988050d10 100644 --- a/en/codes/dart/chapter_searching/two_sum.dart +++ b/en/codes/dart/chapter_searching/two_sum.dart @@ -6,10 +6,10 @@ import 'dart:collection'; -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ List twoSumBruteForce(List nums, int target) { int size = nums.length; - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for (var i = 0; i < size - 1; i++) { for (var j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) return [i, j]; @@ -18,12 +18,12 @@ List twoSumBruteForce(List nums, int target) { return [0]; } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ List twoSumHashTable(List nums, int target) { int size = nums.length; - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space Map dic = HashMap(); - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (var i = 0; i < size; i++) { if (dic.containsKey(target - nums[i])) { return [dic[target - nums[i]]!, i]; @@ -33,17 +33,17 @@ List twoSumHashTable(List nums, int target) { return [0]; } -/* Driver Code */ +/* Driver code */ void main() { - // ======= Test Case ======= + // ======= Test case ======= List nums = [2, 7, 11, 15]; int target = 13; - // ====== Driver Code ====== + // ====== Driver code ====== // Method 1 List res = twoSumBruteForce(nums, target); - print('Method 1 res = $res'); + print('Method 1: res = $res'); // Method 2 res = twoSumHashTable(nums, target); - print('Method 2 res = $res'); + print('Method 2: res = $res'); } diff --git a/en/codes/dart/chapter_sorting/bubble_sort.dart b/en/codes/dart/chapter_sorting/bubble_sort.dart index 31534a36e..6dd27093b 100644 --- a/en/codes/dart/chapter_sorting/bubble_sort.dart +++ b/en/codes/dart/chapter_sorting/bubble_sort.dart @@ -6,9 +6,9 @@ /* Bubble sort */ void bubbleSort(List nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] @@ -20,26 +20,26 @@ void bubbleSort(List nums) { } } -/* Bubble sort (flag optimization) */ +/* Bubble sort (flag optimization)*/ void bubbleSortWithFlag(List nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - bool flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + bool flag = false; // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - flag = true; // Record element swap + flag = true; // Record that a swap occurred } } - if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + if (!flag) break; // Exit immediately if this pass made no swaps } } -/* Driver Code */ +/* Driver code */ void main() { List nums = [4, 1, 3, 1, 5, 2]; bubbleSort(nums); diff --git a/en/codes/dart/chapter_sorting/bucket_sort.dart b/en/codes/dart/chapter_sorting/bucket_sort.dart index 3e7b4d991..03e37e934 100644 --- a/en/codes/dart/chapter_sorting/bucket_sort.dart +++ b/en/codes/dart/chapter_sorting/bucket_sort.dart @@ -6,22 +6,22 @@ /* Bucket sort */ void bucketSort(List nums) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = floor(n/2) buckets, aiming for about 2 elements per bucket int k = nums.length ~/ 2; List> buckets = List.generate(k, (index) => []); - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for (double _num in nums) { - // Input data range is [0, 1), use _num * k to map to index range [0, k-1] + // The input range is [0, 1); use _num * k to map each value to an index in [0, k-1] int i = (_num * k).toInt(); - // Add _num to bucket bucket_idx + // Add _num to bucket i buckets[i].add(_num); } // 2. Sort each bucket for (List bucket in buckets) { bucket.sort(); } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results int i = 0; for (List bucket in buckets) { for (double _num in bucket) { @@ -30,9 +30,9 @@ void bucketSort(List nums) { } } -/* Driver Code*/ +/* Driver code*/ void main() { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) final nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; bucketSort(nums); print('After bucket sort, nums = $nums'); diff --git a/en/codes/dart/chapter_sorting/counting_sort.dart b/en/codes/dart/chapter_sorting/counting_sort.dart index 0e8923247..960180cf0 100644 --- a/en/codes/dart/chapter_sorting/counting_sort.dart +++ b/en/codes/dart/chapter_sorting/counting_sort.dart @@ -6,20 +6,20 @@ import 'dart:math'; /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects void countingSortNaive(List nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int _num in nums) { m = max(m, _num); } - // 2. Count the occurrence of each number - // counter[_num] represents occurrence count of _num + // 2. Count the occurrences of each number + // counter[_num] is the number of occurrences of _num List counter = List.filled(m + 1, 0); for (int _num in nums) { counter[_num]++; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements int i = 0; for (int _num = 0; _num < m + 1; _num++) { for (int j = 0; j < counter[_num]; j++, i++) { @@ -29,38 +29,38 @@ void countingSortNaive(List nums) { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Full stable implementation; this approach can be adapted to sort objects void countingSort(List nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int _num in nums) { m = max(m, _num); } - // 2. Count the occurrence of each number - // counter[_num] represents occurrence count of _num + // 2. Count the occurrences of each number + // counter[_num] is the number of occurrences of _num List counter = List.filled(m + 1, 0); for (int _num in nums) { counter[_num]++; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // That is, counter[_num]-1 is the last occurrence index of _num in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[_num]-1 is the index of the last occurrence of _num in res for (int i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result int n = nums.length; List res = List.filled(n, 0); for (int i = n - 1; i >= 0; i--) { int _num = nums[i]; - res[counter[_num] - 1] = _num; // Place _num at corresponding index - counter[_num]--; // Decrement prefix sum by 1 to get next placement index for _num + res[counter[_num] - 1] = _num; // Place _num at the corresponding index + counter[_num]--; // Decrement the prefix sum by 1 to determine where to place the next occurrence of _num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res nums.setAll(0, res); } -/* Driver Code*/ +/* Driver code*/ void main() { final nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; countingSortNaive(nums); diff --git a/en/codes/dart/chapter_sorting/heap_sort.dart b/en/codes/dart/chapter_sorting/heap_sort.dart index 62eb5da8d..6c46b0e49 100644 --- a/en/codes/dart/chapter_sorting/heap_sort.dart +++ b/en/codes/dart/chapter_sorting/heap_sort.dart @@ -4,44 +4,44 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ void siftDown(List nums, int n, int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; if (l < n && nums[l] > nums[ma]) ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break; - // Swap two nodes + // Swap the two nodes int temp = nums[i]; nums[i] = nums[ma]; nums[ma] = temp; - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ void heapSort(List nums) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (int i = nums.length ~/ 2 - 1; i >= 0; i--) { siftDown(nums, nums.length, i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (int i = nums.length - 1; i > 0; i--) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) int tmp = nums[0]; nums[0] = nums[i]; nums[i] = tmp; - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0); } } -/* Driver Code */ +/* Driver code */ void main() { List nums = [4, 1, 3, 1, 5, 2]; heapSort(nums); diff --git a/en/codes/dart/chapter_sorting/insertion_sort.dart b/en/codes/dart/chapter_sorting/insertion_sort.dart index 8fcccb994..033d5cbd6 100644 --- a/en/codes/dart/chapter_sorting/insertion_sort.dart +++ b/en/codes/dart/chapter_sorting/insertion_sort.dart @@ -6,19 +6,19 @@ /* Insertion sort */ void insertionSort(List nums) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for (int i = 1; i < nums.length; i++) { int base = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + nums[j + 1] = nums[j]; // Shift nums[j] one position to the right j--; } - nums[j + 1] = base; // Assign base to the correct position + nums[j + 1] = base; // Place base at the correct position } } -/* Driver Code */ +/* Driver code */ void main() { List nums = [4, 1, 3, 1, 5, 2]; insertionSort(nums); diff --git a/en/codes/dart/chapter_sorting/merge_sort.dart b/en/codes/dart/chapter_sorting/merge_sort.dart index 31a72533b..a3bb698a3 100644 --- a/en/codes/dart/chapter_sorting/merge_sort.dart +++ b/en/codes/dart/chapter_sorting/merge_sort.dart @@ -4,14 +4,14 @@ * Author: what-is-me (whatisme@outlook.jp) */ -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ void merge(List nums, int left, int mid, int right) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result List tmp = List.filled(right - left + 1, 0); - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays int i = left, j = mid + 1, k = 0; - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; @@ -25,7 +25,7 @@ void merge(List nums, int left, int mid, int right) { while (j <= right) { tmp[k++] = nums[j++]; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (k = 0; k < tmp.length; k++) { nums[left + k] = tmp[k]; } @@ -33,17 +33,17 @@ void merge(List nums, int left, int mid, int right) { /* Merge sort */ void mergeSort(List nums, int left, int right) { - // Termination condition - if (left >= right) return; // Terminate recursion when subarray length is 1 - // Divide and conquer stage - int mid = left + (right - left) ~/ 2; // Calculate midpoint - mergeSort(nums, left, mid); // Recursively process the left subarray - mergeSort(nums, mid + 1, right); // Recursively process the right subarray - // Merge stage + // Base case + if (left >= right) return; // Stop recursing when the subarray has at most one element + // Divide phase + int mid = left + (right - left) ~/ 2; // Calculate the midpoint + mergeSort(nums, left, mid); // Recurse on the left subarray + mergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase merge(nums, left, mid, right); } -/* Driver Code */ +/* Driver code */ void main() { /* Merge sort */ List nums = [7, 3, 2, 6, 0, 1, 5, 4]; diff --git a/en/codes/dart/chapter_sorting/quick_sort.dart b/en/codes/dart/chapter_sorting/quick_sort.dart index 42bbabab8..94b9b1c2e 100644 --- a/en/codes/dart/chapter_sorting/quick_sort.dart +++ b/en/codes/dart/chapter_sorting/quick_sort.dart @@ -6,33 +6,33 @@ /* Quick sort class */ class QuickSort { - /* Swap elements */ + /* Swap the elements */ static void _swap(List nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ static int _partition(List nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { - while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot - while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + while (i < j && nums[j] >= nums[left]) j--; // Scan from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Scan from left to right for the first element larger than the pivot _swap(nums, i, j); // Swap these two elements } - _swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + _swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ static void quickSort(List nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = _partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } @@ -40,7 +40,7 @@ class QuickSort { /* Quick sort class (median pivot optimization) */ class QuickSortMedian { - /* Swap elements */ + /* Swap the elements */ static void _swap(List nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; @@ -51,36 +51,36 @@ class QuickSortMedian { static int _medianThree(List nums, int left, int mid, int right) { int l = nums[left], m = nums[mid], r = nums[right]; if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m is between l and r + return mid; // m lies between l and r if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l is between m and r + return left; // l lies between m and r return right; } - /* Sentinel partition (median of three) */ + /* Two-pointer partitioning (median of three) */ static int _partition(List nums, int left, int right) { // Select the median of three candidate elements int med = _medianThree(nums, left, (left + right) ~/ 2, right); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray _swap(nums, left, med); // Use nums[left] as the pivot int i = left, j = right; while (i < j) { - while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot - while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + while (i < j && nums[j] >= nums[left]) j--; // Scan from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Scan from left to right for the first element larger than the pivot _swap(nums, i, j); // Swap these two elements } - _swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + _swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ static void quickSort(List nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = _partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } @@ -88,52 +88,52 @@ class QuickSortMedian { /* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { - /* Swap elements */ + /* Swap the elements */ static void _swap(List nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ static int _partition(List nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { - while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot - while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + while (i < j && nums[j] >= nums[left]) j--; // Scan from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Scan from left to right for the first element larger than the pivot _swap(nums, i, j); // Swap these two elements } - _swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + _swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort (recursion depth optimization) */ static void quickSort(List nums, int left, int right) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while (left < right) { - // Sentinel partition operation + // Perform two-pointer partitioning int pivot = _partition(nums, left, right); - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left < right - pivot) { quickSort(nums, left, pivot - 1); // Recursively sort the left subarray - left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] } else { quickSort(nums, pivot + 1, right); // Recursively sort the right subarray - right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] } } } } -/* Driver Code */ +/* Driver code */ void main() { /* Quick sort */ List nums = [2, 4, 1, 0, 3, 5]; QuickSort.quickSort(nums, 0, nums.length - 1); print("After quick sort, nums = $nums"); - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ List nums1 = [2, 4, 1, 0, 3, 5]; QuickSortMedian.quickSort(nums1, 0, nums1.length - 1); print("After quick sort (median pivot optimization), nums1 = $nums1"); diff --git a/en/codes/dart/chapter_sorting/radix_sort.dart b/en/codes/dart/chapter_sorting/radix_sort.dart index 9274ab1df..0805168dc 100644 --- a/en/codes/dart/chapter_sorting/radix_sort.dart +++ b/en/codes/dart/chapter_sorting/radix_sort.dart @@ -4,54 +4,54 @@ * Author: what-is-me (whatisme@outlook.jp) */ -/* Get k-th digit of element _num, where exp = 10^(k-1) */ +/* Get the k-th digit of _num, where exp = 10^(k-1) */ int digit(int _num, int exp) { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (_num ~/ exp) % 10; } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ void countingSortDigit(List nums, int exp) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 List counter = List.filled(10, 0); int n = nums.length; - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for (int i = 0; i < n; i++) { - int d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d - counter[d]++; // Count the occurrence of digit d + int d = digit(nums[i], exp); // Get the k-th digit of nums[i] and store it in d + counter[d]++; // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for (int i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts List res = List.filled(n, 0); for (int i = n - 1; i >= 0; i--) { int d = digit(nums[i], exp); - int j = counter[d] - 1; // Get the index j for d in the array + int j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d]--; // Decrease the count of d by 1 + counter[d]--; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for (int i = 0; i < n; i++) nums[i] = res[i]; } /* Radix sort */ void radixSort(List nums) { - // Get the maximum element of the array, used to determine the maximum number of digits - // In Dart, int length is 64 bits + // Get the largest array element to determine the maximum number of digits + // On native platforms, Dart int values are 64-bit integers int m = -1 << 63; for (int _num in nums) if (_num > m) m = _num; - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for (int exp = 1; exp <= m; exp *= 10) - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, exp); } -/* Driver Code */ +/* Driver code */ void main() { // Radix sort List nums = [ diff --git a/en/codes/dart/chapter_sorting/selection_sort.dart b/en/codes/dart/chapter_sorting/selection_sort.dart index 5a15c9a35..2123ddca8 100644 --- a/en/codes/dart/chapter_sorting/selection_sort.dart +++ b/en/codes/dart/chapter_sorting/selection_sort.dart @@ -7,21 +7,21 @@ /* Selection sort */ void selectionSort(List nums) { int n = nums.length; - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (int i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) k = j; // Record the index of the smallest element } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range int temp = nums[i]; nums[i] = nums[k]; nums[k] = temp; } } -/* Driver Code */ +/* Driver code */ void main() { List nums = [4, 1, 3, 1, 5, 2]; selectionSort(nums); diff --git a/en/codes/dart/chapter_stack_and_queue/array_deque.dart b/en/codes/dart/chapter_stack_and_queue/array_deque.dart index 2a41e6ac8..9798b7617 100644 --- a/en/codes/dart/chapter_stack_and_queue/array_deque.dart +++ b/en/codes/dart/chapter_stack_and_queue/array_deque.dart @@ -4,11 +4,11 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ class ArrayDeque { - late List _nums; // Array for storing double-ended queue elements - late int _front; // Front pointer, points to the front of the queue element - late int _queSize; // Double-ended queue length + late List _nums; // Array for storing deque elements + late int _front; // Front pointer, pointing to the first element + late int _queSize; // Deque size /* Constructor */ ArrayDeque(int capacity) { @@ -16,71 +16,71 @@ class ArrayDeque { this._front = this._queSize = 0; } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ int capacity() { return _nums.length; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size() { return _queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty() { return _queSize == 0; } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ int index(int i) { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end return (i + capacity()) % capacity(); } - /* Front of the queue enqueue */ + /* Enqueue at the front */ void pushFirst(int _num) { if (_queSize == capacity()) { - throw Exception("Double-ended queue is full"); + throw Exception("Deque is full"); } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Use modulo operation to wrap _front from array head back to tail + // Move the front pointer one position to the left + // Use modulo to wrap _front to the end when it passes the beginning of the array _front = index(_front - 1); - // Add _num to queue front + // Add _num to the front _nums[_front] = _num; _queSize++; } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ void pushLast(int _num) { if (_queSize == capacity()) { - throw Exception("Double-ended queue is full"); + throw Exception("Deque is full"); } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear pointer, which points one position past the last element int rear = index(_front + _queSize); - // Add _num to queue rear + // Add _num to the rear _nums[rear] = _num; _queSize++; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ int popFirst() { int _num = peekFirst(); - // Move front pointer right by one + // Move the front pointer one position to the right _front = index(_front + 1); _queSize--; return _num; } - /* Access rear of the queue element */ + /* Dequeue from the rear */ int popLast() { int _num = peekLast(); _queSize--; return _num; } - /* Return list for printing */ + /* Access the front element */ int peekFirst() { if (isEmpty()) { throw Exception("Deque is empty"); @@ -88,19 +88,19 @@ class ArrayDeque { return _nums[_front]; } - /* Driver Code */ + /* Access the rear element */ int peekLast() { if (isEmpty()) { throw Exception("Deque is empty"); } - // Initialize double-ended queue + // Calculate the index of the last element int last = index(_front + _queSize - 1); return _nums[last]; } - /* Return array for printing */ + /* Return an array for printing */ List toArray() { - // Elements enqueue + // Convert only the list elements within the occupied range List res = List.filled(_queSize, 0); for (int i = 0, j = _front; i < _queSize; i++, j++) { res[i] = _nums[index(j)]; @@ -109,38 +109,38 @@ class ArrayDeque { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ final ArrayDeque deque = ArrayDeque(10); deque.pushLast(3); deque.pushLast(2); deque.pushLast(5); print("Deque deque = ${deque.toArray()}"); - /* Update element */ + /* Access an element */ final int peekFirst = deque.peekFirst(); print("Front element peekFirst = $peekFirst"); final int peekLast = deque.peekLast(); print("Rear element peekLast = $peekLast"); - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(4); - print("After element 4 enqueues at rear, deque = ${deque.toArray()}"); + print("After enqueuing 4 at the rear, deque = ${deque.toArray()}"); deque.pushFirst(1); - print("After element 1 enqueues at front, deque = ${deque.toArray()}"); + print("After enqueuing 1 at the front, deque = ${deque.toArray()}"); - /* Element dequeue */ + /* Dequeue an element */ final int popLast = deque.popLast(); - print("Dequeue rear element = $popLast, after rear dequeue deque = ${deque.toArray()}"); + print("Element dequeued from the rear = $popLast; after dequeuing from the rear, deque = ${deque.toArray()}"); final int popFirst = deque.popFirst(); - print("Dequeue front element = $popFirst, after front dequeue deque = ${deque.toArray()}"); + print("Element dequeued from the front = $popFirst; after dequeuing from the front, deque = ${deque.toArray()}"); - /* Get the length of the double-ended queue */ + /* Get the deque size */ final int size = deque.size(); - print("Deque length size = $size"); + print("Deque size = $size"); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ final bool isEmpty = deque.isEmpty(); - print("Is deque empty = $isEmpty"); + print("Is the deque empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_stack_and_queue/array_queue.dart b/en/codes/dart/chapter_stack_and_queue/array_queue.dart index 45e3553f6..f51cbc085 100644 --- a/en/codes/dart/chapter_stack_and_queue/array_queue.dart +++ b/en/codes/dart/chapter_stack_and_queue/array_queue.dart @@ -4,28 +4,28 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ class ArrayQueue { - late List _nums; // Array for storing queue elements - late int _front; // Front pointer, points to the front of the queue element - late int _queSize; // Queue length + late List _nums; // Array that stores the queue elements + late int _front; // Front pointer, pointing to the first element + late int _queSize; // Queue size ArrayQueue(int capacity) { _nums = List.filled(capacity, 0); _front = _queSize = 0; } - /* Get the capacity of the queue */ + /* Get the queue capacity */ int capaCity() { return _nums.length; } - /* Get the length of the queue */ + /* Get the queue size */ int size() { return _queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty() { return _queSize == 0; } @@ -35,10 +35,10 @@ class ArrayQueue { if (_queSize == capaCity()) { throw Exception("Queue is full"); } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear pointer, which points one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array int rear = (_front + _queSize) % capaCity(); - // Add _num to queue rear + // Add _num to the rear _nums[rear] = _num; _queSize++; } @@ -46,13 +46,13 @@ class ArrayQueue { /* Dequeue */ int pop() { int _num = peek(); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front pointer by one position, wrapping to the beginning if it passes the end _front = (_front + 1) % capaCity(); _queSize--; return _num; } - /* Return list for printing */ + /* Access the front element */ int peek() { if (isEmpty()) { throw Exception("Queue is empty"); @@ -60,9 +60,9 @@ class ArrayQueue { return _nums[_front]; } - /* Return Array */ + /* Return an array */ List toArray() { - // Elements enqueue + // Convert only the list elements within the occupied range final List res = List.filled(_queSize, 0); for (int i = 0, j = _front; i < _queSize; i++, j++) { res[i] = _nums[j % capaCity()]; @@ -71,13 +71,13 @@ class ArrayQueue { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Access front of the queue element */ + /* Initialize the queue */ final int capacity = 10; final ArrayQueue queue = ArrayQueue(capacity); - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -85,26 +85,26 @@ void main() { queue.push(4); print("Queue queue = ${queue.toArray()}"); - /* Return list for printing */ + /* Access the front element */ final int peek = queue.peek(); print("Front element peek = $peek"); - /* Element dequeue */ + /* Dequeue an element */ final int pop = queue.pop(); - print("Dequeue element pop = $pop, after dequeue queue = ${queue.toArray()}"); + print("Dequeued element pop = $pop; after dequeuing, queue = ${queue.toArray()}"); - /* Get queue length */ + /* Get the queue size */ final int size = queue.size(); - print("Queue length size = $size"); + print("Queue size = $size"); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ final bool isEmpty = queue.isEmpty(); - print("Is queue empty = $isEmpty"); + print("Is the queue empty = $isEmpty"); - /* Test circular array */ + /* Test the circular array */ for (int i = 0; i < 10; i++) { queue.push(i); queue.pop(); - print("After round $i enqueue + dequeue, queue = ${queue.toArray()}"); + print("Round $i: after enqueue + dequeue, queue = ${queue.toArray()}"); } } diff --git a/en/codes/dart/chapter_stack_and_queue/array_stack.dart b/en/codes/dart/chapter_stack_and_queue/array_stack.dart index 6b5922417..06f7c1602 100644 --- a/en/codes/dart/chapter_stack_and_queue/array_stack.dart +++ b/en/codes/dart/chapter_stack_and_queue/array_stack.dart @@ -4,19 +4,19 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* Stack based on array implementation */ +/* Stack implemented with an array */ class ArrayStack { late List _stack; ArrayStack() { _stack = []; } - /* Get the length of the stack */ + /* Get the stack size */ int size() { return _stack.length; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ bool isEmpty() { return _stack.isEmpty; } @@ -34,7 +34,7 @@ class ArrayStack { return _stack.removeLast(); } - /* Return list for printing */ + /* Access the top element */ int peek() { if (isEmpty()) { throw Exception("Stack is empty"); @@ -42,16 +42,16 @@ class ArrayStack { return _stack.last; } - /* Convert stack to Array and return */ + /* Return the underlying stack array */ List toArray() => _stack; } -/* Driver Code */ +/* Driver code */ void main() { - /* Access top of the stack element */ + /* Initialize the stack */ final ArrayStack stack = ArrayStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -59,19 +59,19 @@ void main() { stack.push(4); print("Stack stack = ${stack.toArray()}"); - /* Return list for printing */ + /* Access the top element */ final int peek = stack.peek(); print("Top element peek = $peek"); - /* Element pop from stack */ + /* Pop an element from the stack */ final int pop = stack.pop(); - print("Pop element pop = $pop, after pop stack = ${stack.toArray()}"); + print("Popped element pop = $pop; after popping, stack = ${stack.toArray()}"); - /* Get the length of the stack */ + /* Get the stack size */ final int size = stack.size(); - print("Stack length size = $size"); + print("Stack size = $size"); - /* Check if empty */ + /* Check whether it is empty */ final bool isEmpty = stack.isEmpty(); - print("Is stack empty = $isEmpty"); + print("Is the stack empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_stack_and_queue/deque.dart b/en/codes/dart/chapter_stack_and_queue/deque.dart index c38e3c96e..3dce11337 100644 --- a/en/codes/dart/chapter_stack_and_queue/deque.dart +++ b/en/codes/dart/chapter_stack_and_queue/deque.dart @@ -7,36 +7,36 @@ import 'dart:collection'; void main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ final Queue deque = Queue(); deque.addFirst(3); deque.addLast(2); deque.addLast(5); print("Deque deque = $deque"); - /* Update element */ + /* Access an element */ final int peekFirst = deque.first; print("Front element peekFirst = $peekFirst"); final int peekLast = deque.last; print("Rear element peekLast = $peekLast"); - /* Elements enqueue */ + /* Enqueue elements */ deque.addLast(4); - print("After element 4 enqueues at rear, deque = $deque"); + print("After enqueuing 4 at the rear, deque = $deque"); deque.addFirst(1); - print("After element 1 enqueues at front, deque = $deque"); + print("After enqueuing 1 at the front, deque = $deque"); - /* Element dequeue */ + /* Dequeue an element */ final int popLast = deque.removeLast(); - print("Dequeue rear element = $popLast, after rear dequeue deque = $deque"); + print("Element dequeued from the rear = $popLast; after dequeuing from the rear, deque = $deque"); final int popFirst = deque.removeFirst(); - print("Dequeue front element = $popFirst, after front dequeue deque = $deque"); + print("Element dequeued from the front = $popFirst; after dequeuing from the front, deque = $deque"); - /* Get the length of the double-ended queue */ + /* Get the deque size */ final int size = deque.length; - print("Deque length size = $size"); + print("Deque size = $size"); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ final bool isEmpty = deque.isEmpty; - print("Is deque empty = $isEmpty"); + print("Is the deque empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_stack_and_queue/linkedlist_deque.dart b/en/codes/dart/chapter_stack_and_queue/linkedlist_deque.dart index 2f20f1de2..9e1a372e7 100644 --- a/en/codes/dart/chapter_stack_and_queue/linkedlist_deque.dart +++ b/en/codes/dart/chapter_stack_and_queue/linkedlist_deque.dart @@ -7,29 +7,29 @@ /* Doubly linked list node */ class ListNode { int val; // Node value - ListNode? next; // Successor node reference - ListNode? prev; // Predecessor node reference + ListNode? next; // Reference to the next node + ListNode? prev; // Reference to the previous node ListNode(this.val, {this.next, this.prev}); } -/* Deque implemented based on doubly linked list */ +/* Deque implemented with a doubly linked list */ class LinkedListDeque { late ListNode? _front; // Head node _front late ListNode? _rear; // Tail node _rear - int _queSize = 0; // Length of the double-ended queue + int _queSize = 0; // Deque size LinkedListDeque() { this._front = null; this._rear = null; } - /* Get deque length */ + /* Get the deque size */ int size() { return this._queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty() { return size() == 0; } @@ -38,89 +38,89 @@ class LinkedListDeque { void push(int _num, bool isFront) { final ListNode node = ListNode(_num); if (isEmpty()) { - // If list is empty, let both _front and _rear point to node + // If the linked list is empty, make both _front and _rear point to node _front = _rear = node; } else if (isFront) { - // Front of the queue enqueue operation + // Enqueue at the front // Add node to the head of the linked list _front!.prev = node; node.next = _front; - _front = node; // Update head node + _front = node; // Update the head node } else { - // Rear of the queue enqueue operation + // Enqueue at the rear // Add node to the tail of the linked list _rear!.next = node; node.prev = _rear; - _rear = node; // Update tail node + _rear = node; // Update the tail node } - _queSize++; // Update queue length + _queSize++; // Update the queue size } - /* Front of the queue enqueue */ + /* Enqueue at the front */ void pushFirst(int _num) { push(_num, true); } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ void pushLast(int _num) { push(_num, false); } /* Dequeue operation */ int? pop(bool isFront) { - // If queue is empty, return null directly + // If the queue is empty, return null immediately if (isEmpty()) { return null; } final int val; - // When the deque has one element, front and rear point to the same node + // When the queue size is 1, the head and tail point to the same node if (_queSize == 1) { val = _front!.val; _front = null; _rear = null; } - // Temporarily store head node value + // Dequeue from the front else if (isFront) { - val = _front!.val; // Delete head node - // Delete head node + val = _front!.val; // Save the head node value + // Remove the head node ListNode? fNext = _front!.next; fNext!.prev = null; _front!.next = null; - _front = fNext; // Update head node - // Temporarily store tail node value + _front = fNext; // Update the head node + // Dequeue from the rear } else { - val = _rear!.val; // Delete tail node - // Update tail node + val = _rear!.val; // Save the tail node value + // Remove the tail node ListNode? rPrev = _rear!.prev; rPrev!.next = null; _rear!.prev = null; - _rear = rPrev; // Update tail node + _rear = rPrev; // Update the tail node } - _queSize--; // Update queue length + _queSize--; // Update the queue size return val; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ int? popFirst() { return pop(true); } - /* Access rear of the queue element */ + /* Dequeue from the rear */ int? popLast() { return pop(false); } - /* Return list for printing */ + /* Access the front element */ int? peekFirst() { return _front?.val; } - /* Driver Code */ + /* Access the rear element */ int? peekLast() { return _rear?.val; } - /* Return array for printing */ + /* Return an array for printing */ List toArray() { ListNode? node = _front; final List res = []; @@ -132,38 +132,38 @@ class LinkedListDeque { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ final LinkedListDeque deque = LinkedListDeque(); deque.pushLast(3); deque.pushLast(2); deque.pushLast(5); print("Deque deque = ${deque.toArray()}"); - /* Update element */ + /* Access an element */ int? peekFirst = deque.peekFirst(); print("Front element peekFirst = $peekFirst"); int? peekLast = deque.peekLast(); print("Rear element peekLast = $peekLast"); - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(4); - print("After element 4 enqueues at rear, deque = ${deque.toArray()}"); + print("After enqueuing 4 at the rear, deque = ${deque.toArray()}"); deque.pushFirst(1); - print("After element 1 enqueues at front, deque = ${deque.toArray()}"); + print("After enqueuing 1 at the front, deque = ${deque.toArray()}"); - /* Element dequeue */ + /* Dequeue an element */ int? popLast = deque.popLast(); - print("Dequeue rear element = $popLast, after rear dequeue deque = ${deque.toArray()}"); + print("Element dequeued from the rear = $popLast; after dequeuing from the rear, deque = ${deque.toArray()}"); int? popFirst = deque.popFirst(); - print("Dequeue front element = $popFirst, after front dequeue deque = ${deque.toArray()}"); + print("Element dequeued from the front = $popFirst; after dequeuing from the front, deque = ${deque.toArray()}"); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.size(); - print("Deque length size = $size"); + print("Deque size = $size"); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque.isEmpty(); - print("Is deque empty = $isEmpty"); + print("Is the deque empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart b/en/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart index bc289d734..f6931b014 100644 --- a/en/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart +++ b/en/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart @@ -6,37 +6,37 @@ import '../utils/list_node.dart'; -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue { ListNode? _front; // Head node _front ListNode? _rear; // Tail node _rear - int _queSize = 0; // Queue length + int _queSize = 0; // Queue size LinkedListQueue() { _front = null; _rear = null; } - /* Get the length of the queue */ + /* Get the queue size */ int size() { return _queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ bool isEmpty() { return _queSize == 0; } /* Enqueue */ void push(int _num) { - // Add _num after tail node + // Add _num after the tail node final node = ListNode(_num); - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (_front == null) { _front = node; _rear = node; } else { - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node _rear!.next = node; _rear = node; } @@ -46,13 +46,13 @@ class LinkedListQueue { /* Dequeue */ int pop() { final int _num = peek(); - // Delete head node + // Remove the head node _front = _front!.next; _queSize--; return _num; } - /* Return list for printing */ + /* Access the front element */ int peek() { if (_queSize == 0) { throw Exception('Queue is empty'); @@ -60,7 +60,7 @@ class LinkedListQueue { return _front!.val; } - /* Convert linked list to Array and return */ + /* Convert the linked list to an array and return it */ List toArray() { ListNode? node = _front; final List queue = []; @@ -72,12 +72,12 @@ class LinkedListQueue { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Access front of the queue element */ + /* Initialize the queue */ final queue = LinkedListQueue(); - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -85,19 +85,19 @@ void main() { queue.push(4); print("Queue queue = ${queue.toArray()}"); - /* Return list for printing */ + /* Access the front element */ final int peek = queue.peek(); print("Front element peek = $peek"); - /* Element dequeue */ + /* Dequeue an element */ final int pop = queue.pop(); - print("Dequeue element pop = $pop, after dequeue queue = ${queue.toArray()}"); + print("Dequeued element pop = $pop; after dequeuing, queue = ${queue.toArray()}"); - /* Get the length of the queue */ + /* Get the queue size */ final int size = queue.size(); - print("Queue length size = $size"); + print("Queue size = $size"); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ final bool isEmpty = queue.isEmpty(); - print("Is queue empty = $isEmpty"); + print("Is the queue empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_stack_and_queue/linkedlist_stack.dart b/en/codes/dart/chapter_stack_and_queue/linkedlist_stack.dart index b4c022849..d8f46a03c 100644 --- a/en/codes/dart/chapter_stack_and_queue/linkedlist_stack.dart +++ b/en/codes/dart/chapter_stack_and_queue/linkedlist_stack.dart @@ -6,21 +6,21 @@ import '../utils/list_node.dart'; -/* Stack implemented based on linked list class */ +/* Stack implemented with a linked list */ class LinkedListStack { - ListNode? _stackPeek; // Use head node as stack top - int _stkSize = 0; // Stack length + ListNode? _stackPeek; // Use the head node as the stack top + int _stkSize = 0; // Stack size LinkedListStack() { _stackPeek = null; } - /* Get the length of the stack */ + /* Get the stack size */ int size() { return _stkSize; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ bool isEmpty() { return _stkSize == 0; } @@ -41,7 +41,7 @@ class LinkedListStack { return _num; } - /* Return list for printing */ + /* Access the top element */ int peek() { if (_stackPeek == null) { throw Exception("Stack is empty"); @@ -49,7 +49,7 @@ class LinkedListStack { return _stackPeek!.val; } - /* Convert linked list to List and return */ + /* Convert the linked list to a List and return it */ List toList() { ListNode? node = _stackPeek; List list = []; @@ -62,12 +62,12 @@ class LinkedListStack { } } -/* Driver Code */ +/* Driver code */ void main() { - /* Access top of the stack element */ + /* Initialize the stack */ final LinkedListStack stack = LinkedListStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -75,19 +75,19 @@ void main() { stack.push(4); print("Stack stack = ${stack.toList()}"); - /* Return list for printing */ + /* Access the top element */ final int peek = stack.peek(); print("Top element peek = $peek"); - /* Element pop from stack */ + /* Pop an element from the stack */ final int pop = stack.pop(); - print("Pop element pop = $pop, after pop stack = ${stack.toList()}"); + print("Popped element pop = $pop; after popping, stack = ${stack.toList()}"); - /* Get the length of the stack */ + /* Get the stack size */ final int size = stack.size(); - print("Stack length size = $size"); + print("Stack size = $size"); - /* Check if empty */ + /* Check whether it is empty */ final bool isEmpty = stack.isEmpty(); - print("Is stack empty = $isEmpty"); + print("Is the stack empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_stack_and_queue/queue.dart b/en/codes/dart/chapter_stack_and_queue/queue.dart index ae70db2b0..e5165d1ea 100644 --- a/en/codes/dart/chapter_stack_and_queue/queue.dart +++ b/en/codes/dart/chapter_stack_and_queue/queue.dart @@ -7,11 +7,11 @@ import 'dart:collection'; void main() { - /* Access front of the queue element */ - // In Dart, generally use Queue class as queue + /* Initialize the queue */ + // In Dart, the deque class Queue is commonly used as a queue final Queue queue = Queue(); - /* Elements enqueue */ + /* Enqueue elements */ queue.add(1); queue.add(3); queue.add(2); @@ -19,19 +19,19 @@ void main() { queue.add(4); print("Queue queue = $queue"); - /* Return list for printing */ + /* Access the front element */ final int peek = queue.first; print("Front element peek = $peek"); - /* Element dequeue */ + /* Dequeue an element */ final int pop = queue.removeFirst(); - print("Dequeue element pop = $pop, after dequeue queue = $queue"); + print("Dequeued element pop = $pop; after dequeuing, queue = $queue"); - /* Get queue length */ + /* Get the queue size */ final int size = queue.length; - print("Queue length size = $size"); + print("Queue size = $size"); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ final bool isEmpty = queue.isEmpty; - print("Is queue empty = $isEmpty"); + print("Is the queue empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_stack_and_queue/stack.dart b/en/codes/dart/chapter_stack_and_queue/stack.dart index e36c19bf7..bd5a45868 100644 --- a/en/codes/dart/chapter_stack_and_queue/stack.dart +++ b/en/codes/dart/chapter_stack_and_queue/stack.dart @@ -5,11 +5,11 @@ */ void main() { - /* Access top of the stack element */ - // Dart has no built-in stack class, can use List as stack + /* Initialize the stack */ + // Dart has no built-in stack class; a List can be used as a stack final List stack = []; - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.add(1); stack.add(3); stack.add(2); @@ -17,19 +17,19 @@ void main() { stack.add(4); print("Stack stack = $stack"); - /* Return list for printing */ + /* Access the top element */ final int peek = stack.last; print("Top element peek = $peek"); - /* Element pop from stack */ + /* Pop an element from the stack */ final int pop = stack.removeLast(); - print("Pop element pop = $pop, after pop stack = $stack"); + print("Popped element pop = $pop; after popping, stack = $stack"); - /* Get the length of the stack */ + /* Get the stack size */ final int size = stack.length; - print("Stack length size = $size"); + print("Stack size = $size"); - /* Check if empty */ + /* Check whether it is empty */ final bool isEmpty = stack.isEmpty; - print("Is stack empty = $isEmpty"); + print("Is the stack empty = $isEmpty"); } diff --git a/en/codes/dart/chapter_tree/array_binary_tree.dart b/en/codes/dart/chapter_tree/array_binary_tree.dart index 4347ec14c..a22097eba 100644 --- a/en/codes/dart/chapter_tree/array_binary_tree.dart +++ b/en/codes/dart/chapter_tree/array_binary_tree.dart @@ -7,38 +7,38 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ class ArrayBinaryTree { late List _tree; /* Constructor */ ArrayBinaryTree(this._tree); - /* List capacity */ + /* Array length, including empty positions */ int size() { return _tree.length; } - /* Get value of node at index i */ + /* Get the value of the node at index i */ int? val(int i) { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return null to indicate an empty position if (i < 0 || i >= size()) { return null; } return _tree[i]; } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ int? left(int i) { return 2 * i + 1; } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ int? right(int i) { return 2 * i + 2; } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ int? parent(int i) { return (i - 1) ~/ 2; } @@ -56,7 +56,7 @@ class ArrayBinaryTree { /* Depth-first traversal */ void dfs(int i, String order, List res) { - // If empty position, return + // Return if this position is empty if (val(i) == null) { return; } @@ -98,10 +98,10 @@ class ArrayBinaryTree { } } -/* Driver Code */ +/* Driver code */ void main() { - // Initialize binary tree - // Here we use a function to generate a binary tree directly from an array + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array List arr = [ 1, 2, @@ -121,32 +121,32 @@ void main() { ]; TreeNode? root = listToTree(arr); - print("\nInitialize binary tree\n"); - print("Array representation of binary tree:"); + print("\nInitialize the binary tree\n"); + print("Array representation of the binary tree:"); print(arr); - print("Linked list representation of binary tree:"); + print("Linked representation of the binary tree:"); printTree(root); - // Binary tree class represented by array + // Binary tree class using an array representation ArrayBinaryTree abt = ArrayBinaryTree(arr); - // Access node + // Access a node int i = 1; int? l = abt.left(i); int? r = abt.right(i); int? p = abt.parent(i); - print("\nCurrent node index is $i, value is ${abt.val(i)}"); - print("Its left child index is $l, value is ${(l == null ? "null" : abt.val(l))}"); - print("Its right child index is $r, value is ${(r == null ? "null" : abt.val(r))}"); - print("Its parent node index is $p, value is ${(p == null ? "null" : abt.val(p))}"); + print("\nCurrent node: index = $i, value = ${abt.val(i)}"); + print("Left child: index = $l, value = ${(l == null ? "null" : abt.val(l))}"); + print("Right child: index = $r, value = ${(r == null ? "null" : abt.val(r))}"); + print("Parent: index = $p, value = ${(p == null ? "null" : abt.val(p))}"); - // Traverse tree + // Traverse the tree List res = abt.levelOrder(); - print("\nLevel-order traversal is: $res"); + print("\nLevel-order traversal: $res"); res = abt.preOrder(); - print("Pre-order traversal is $res"); + print("Preorder traversal: $res"); res = abt.inOrder(); - print("In-order traversal is $res"); + print("Inorder traversal: $res"); res = abt.postOrder(); - print("Post-order traversal is $res"); + print("Postorder traversal: $res"); } diff --git a/en/codes/dart/chapter_tree/avl_tree.dart b/en/codes/dart/chapter_tree/avl_tree.dart index f6be5e173..6fa62f725 100644 --- a/en/codes/dart/chapter_tree/avl_tree.dart +++ b/en/codes/dart/chapter_tree/avl_tree.dart @@ -16,115 +16,115 @@ class AVLTree { root = null; } - /* Get node height */ + /* Get the node height */ int height(TreeNode? node) { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 return node == null ? -1 : node.height; } - /* Update node height */ + /* Update the node height */ void updateHeight(TreeNode? node) { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node!.height = max(height(node.left), height(node.right)) + 1; } - /* Get balance factor */ + /* Get the balance factor */ int balanceFactor(TreeNode? node) { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node == null) return 0; // Node balance factor = left subtree height - right subtree height return height(node.left) - height(node.right); } - /* Right rotation operation */ + /* Right rotation */ TreeNode? rightRotate(TreeNode? node) { TreeNode? child = node!.left; TreeNode? grandChild = child!.right; - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.right = node; node.left = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Left rotation operation */ + /* Left rotation */ TreeNode? leftRotate(TreeNode? node) { TreeNode? child = node!.right; TreeNode? grandChild = child!.left; - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.left = node; node.right = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ TreeNode? rotate(TreeNode? node) { - // Get balance factor of node + // Get the balance factor of node int factor = balanceFactor(node); - // Left-leaning tree + // Left-heavy tree if (factor > 1) { if (balanceFactor(node!.left) >= 0) { - // Right rotation + // Rotate right return rightRotate(node); } else { - // First left rotation then right rotation + // Rotate left, then right node.left = leftRotate(node.left); return rightRotate(node); } } - // Right-leaning tree + // Right-heavy tree if (factor < -1) { if (balanceFactor(node!.right) <= 0) { - // Left rotation + // Rotate left return leftRotate(node); } else { - // First right rotation then left rotation + // Rotate right, then left node.right = rightRotate(node.right); return leftRotate(node); } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node; } - /* Insert node */ + /* Insert a node */ void insert(int val) { root = insertHelper(root, val); } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ TreeNode? insertHelper(TreeNode? node, int val) { if (node == null) return TreeNode(val); - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (val < node.val) node.left = insertHelper(node.left, val); else if (val > node.val) node.right = insertHelper(node.right, val); else - return node; // Duplicate node not inserted, return directly - updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + return node; // Return without inserting a duplicate node + updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } - /* Remove node */ + /* Remove a node */ void remove(int val) { root = removeHelper(root, val); } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ TreeNode? removeHelper(TreeNode? node, int val) { if (node == null) return null; - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (val < node.val) node.left = removeHelper(node.left, val); else if (val > node.val) @@ -132,14 +132,14 @@ class AVLTree { else { if (node.left == null || node.right == null) { TreeNode? child = node.left ?? node.right; - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child == null) return null; - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly else node = child; } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value TreeNode? temp = node.right; while (temp!.left != null) { temp = temp.left; @@ -148,25 +148,25 @@ class AVLTree { node.val = temp.val; } } - updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } - /* Search node */ + /* Find a node */ TreeNode? search(int val) { TreeNode? cur = root; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Target node is in cur's right subtree + // The target node is in the left subtree of cur if (val < cur.val) cur = cur.left; - // Target node is in cur's left subtree + // The target node is in the right subtree of cur else if (val > cur.val) cur = cur.right; - // Target node equals current node + // The target value equals the current node value else break; } @@ -176,22 +176,22 @@ class AVLTree { void testInsert(AVLTree tree, int val) { tree.insert(val); - print("\nAfter inserting node $val, AVL tree is"); + print("\nAVL tree after inserting node $val:"); printTree(tree.root); } void testRemove(AVLTree tree, int val) { tree.remove(val); - print("\nAfter deleting node $val, AVL tree is"); + print("\nAVL tree after removing node $val:"); printTree(tree.root); } -/* Driver Code */ +/* Driver code */ void main() { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ AVLTree avlTree = AVLTree(); - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted testInsert(avlTree, 1); testInsert(avlTree, 2); testInsert(avlTree, 3); @@ -203,16 +203,16 @@ void main() { testInsert(avlTree, 10); testInsert(avlTree, 6); - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ testInsert(avlTree, 7); - /* Remove node */ - // Delete node with degree 1 - testRemove(avlTree, 8); // Delete node with degree 2 - testRemove(avlTree, 5); // Remove node with degree 1 - testRemove(avlTree, 4); // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + testRemove(avlTree, 8); // Remove a node with 0 children + testRemove(avlTree, 5); // Remove a node with 1 child + testRemove(avlTree, 4); // Remove a node with 2 children - /* Search node */ + /* Look up a node */ TreeNode? node = avlTree.search(7); - print("\nFound node object is $node, node value = ${node!.val}"); + print("\nNode found: $node, node value = ${node!.val}"); } diff --git a/en/codes/dart/chapter_tree/binary_search_tree.dart b/en/codes/dart/chapter_tree/binary_search_tree.dart index b37632f5f..9584159ac 100644 --- a/en/codes/dart/chapter_tree/binary_search_tree.dart +++ b/en/codes/dart/chapter_tree/binary_search_tree.dart @@ -13,56 +13,56 @@ class BinarySearchTree { /* Constructor */ BinarySearchTree() { - // Initialize empty tree + // Initialize an empty tree _root = null; } - /* Get root node of binary tree */ + /* Get the root node of the binary tree */ TreeNode? getRoot() { return _root; } - /* Search node */ + /* Find a node */ TreeNode? search(int _num) { TreeNode? cur = _root; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < _num) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > _num) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } - /* Insert node */ + /* Insert a node */ void insert(int _num) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (_root == null) { _root = TreeNode(_num); return; } TreeNode? cur = _root; TreeNode? pre = null; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur.val == _num) return; pre = cur; - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur if (cur.val < _num) cur = cur.right; - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else cur = cur.left; } - // Insert node + // Insert a node TreeNode? node = TreeNode(_num); if (pre!.val < _num) pre.right = node; @@ -70,84 +70,84 @@ class BinarySearchTree { pre.left = node; } - /* Remove node */ + /* Remove a node */ void remove(int _num) { - // If tree is empty, return directly + // Return immediately if the tree is empty if (_root == null) return; TreeNode? cur = _root; TreeNode? pre = null; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur != null) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur.val == _num) break; pre = cur; - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur if (cur.val < _num) cur = cur.right; - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur else cur = cur.left; } - // If no node to delete, return directly + // Return if the node to remove does not exist if (cur == null) return; - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if (cur.left == null || cur.right == null) { - // When number of child nodes = 0 / 1, child = null / that child node + // With 0 / 1 children, child is null / the child node, respectively TreeNode? child = cur.left ?? cur.right; - // Delete node cur + // Remove node cur if (cur != _root) { if (pre!.left == cur) pre.left = child; else pre.right = child; } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root _root = child; } } else { - // Number of child nodes = 2 - // Get next node of cur in inorder traversal + // Number of children = 2 + // Get the inorder successor of cur TreeNode? tmp = cur.right; while (tmp!.left != null) { tmp = tmp.left; } - // Recursively delete node tmp + // Recursively remove node tmp remove(tmp.val); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur.val = tmp.val; } } } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ BinarySearchTree bst = BinarySearchTree(); - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree List nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; for (int _num in nums) { bst.insert(_num); } - print("\nInitialized binary tree is\n"); + print("\nThe initialized binary tree is:\n"); printTree(bst.getRoot()); - /* Search node */ + /* Find a node */ TreeNode? node = bst.search(7); - print("\nFound node object is $node, node value = ${node?.val}"); + print("\nNode found: $node, node value = ${node?.val}"); - /* Insert node */ + /* Insert a node */ bst.insert(16); - print("\nAfter inserting node 16, binary tree is\n"); + print("\nBinary tree after inserting node 16:\n"); printTree(bst.getRoot()); - /* Remove node */ + /* Remove a node */ bst.remove(1); - print("\nAfter removing node 1, binary tree is\n"); + print("\nBinary tree after removing node 1:\n"); printTree(bst.getRoot()); bst.remove(2); - print("\nAfter removing node 2, binary tree is\n"); + print("\nBinary tree after removing node 2:\n"); printTree(bst.getRoot()); bst.remove(4); - print("\nAfter removing node 4, binary tree is\n"); + print("\nBinary tree after removing node 4:\n"); printTree(bst.getRoot()); } diff --git a/en/codes/dart/chapter_tree/binary_tree.dart b/en/codes/dart/chapter_tree/binary_tree.dart index 8555ac3b7..6717fdabe 100644 --- a/en/codes/dart/chapter_tree/binary_tree.dart +++ b/en/codes/dart/chapter_tree/binary_tree.dart @@ -8,30 +8,30 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; void main() { - /* Initialize binary tree */ - // Initialize node + /* Initialize the binary tree */ + // Initialize the nodes TreeNode n1 = TreeNode(1); TreeNode n2 = TreeNode(2); TreeNode n3 = TreeNode(3); TreeNode n4 = TreeNode(4); TreeNode n5 = TreeNode(5); - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1.left = n2; n1.right = n3; n2.left = n4; n2.right = n5; - print("\nInitialize binary tree\n"); + print("\nInitialize the binary tree\n"); printTree(n1); - /* Insert node P between n1 -> n2 */ + /* Insert and remove nodes */ TreeNode p = TreeNode(0); - // Insert node p between n1 -> n2 + // Insert node p between n1 and n2 n1.left = p; p.left = n2; - print("\nAfter inserting node P\n"); + print("\nAfter inserting node p:\n"); printTree(n1); - // Remove node P + // Remove node p n1.left = n2; - print("\nAfter removing node P\n"); + print("\nAfter removing node p:\n"); printTree(n1); } diff --git a/en/codes/dart/chapter_tree/binary_tree_bfs.dart b/en/codes/dart/chapter_tree/binary_tree_bfs.dart index e4e2a2a19..bffe5403a 100644 --- a/en/codes/dart/chapter_tree/binary_tree_bfs.dart +++ b/en/codes/dart/chapter_tree/binary_tree_bfs.dart @@ -10,29 +10,29 @@ import '../utils/tree_node.dart'; /* Level-order traversal */ List levelOrder(TreeNode? root) { - // Initialize queue, add root node + // Initialize a queue and enqueue the root Queue queue = Queue(); queue.add(root); - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence List res = []; while (queue.isNotEmpty) { - TreeNode? node = queue.removeFirst(); // Dequeue - res.add(node!.val); // Save node value - if (node.left != null) queue.add(node.left); // Left child node enqueue - if (node.right != null) queue.add(node.right); // Right child node enqueue + TreeNode? node = queue.removeFirst(); // Dequeue from the queue + res.add(node!.val); // Store the node value + if (node.left != null) queue.add(node.left); // Enqueue the left child + if (node.right != null) queue.add(node.right); // Enqueue the right child } return res; } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode? root = listToTree([1, 2, 3, 4, 5, 6, 7]); - print("\nInitialize binary tree\n"); + print("\nInitialize the binary tree\n"); printTree(root); // Level-order traversal List res = levelOrder(root); - print("\nLevel-order traversal node print sequence = $res"); + print("\nNode sequence from level-order traversal = $res"); } diff --git a/en/codes/dart/chapter_tree/binary_tree_dfs.dart b/en/codes/dart/chapter_tree/binary_tree_dfs.dart index 73d0253bc..cfec0347a 100644 --- a/en/codes/dart/chapter_tree/binary_tree_dfs.dart +++ b/en/codes/dart/chapter_tree/binary_tree_dfs.dart @@ -7,13 +7,13 @@ import '../utils/print_util.dart'; import '../utils/tree_node.dart'; -// Initialize list for storing traversal sequence +// Initialize a list to store the traversal sequence List list = []; /* Preorder traversal */ void preOrder(TreeNode? node) { if (node == null) return; - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree list.add(node.val); preOrder(node.left); preOrder(node.right); @@ -22,7 +22,7 @@ void preOrder(TreeNode? node) { /* Inorder traversal */ void inOrder(TreeNode? node) { if (node == null) return; - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(node.left); list.add(node.val); inOrder(node.right); @@ -31,32 +31,32 @@ void inOrder(TreeNode? node) { /* Postorder traversal */ void postOrder(TreeNode? node) { if (node == null) return; - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(node.left); postOrder(node.right); list.add(node.val); } -/* Driver Code */ +/* Driver code */ void main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode? root = listToTree([1, 2, 3, 4, 5, 6, 7]); - print("\nInitialize binary tree\n"); + print("\nInitialize the binary tree\n"); printTree(root); /* Preorder traversal */ list.clear(); preOrder(root); - print("\nPre-order traversal node print sequence = $list"); + print("\nNode sequence from preorder traversal = $list"); /* Inorder traversal */ list.clear(); inOrder(root); - print("\nIn-order traversal node print sequence = $list"); + print("\nNode sequence from inorder traversal = $list"); /* Postorder traversal */ list.clear(); postOrder(root); - print("\nPost-order traversal node print sequence = $list"); + print("\nNode sequence from postorder traversal = $list"); } diff --git a/en/codes/dart/utils/print_util.dart b/en/codes/dart/utils/print_util.dart index a976f93c5..6e64bebc1 100644 --- a/en/codes/dart/utils/print_util.dart +++ b/en/codes/dart/utils/print_util.dart @@ -16,7 +16,7 @@ class Trunk { Trunk(this.prev, this.str); } -/* Print matrix (Array) */ +/* Print a matrix (array) */ void printMatrix(List> matrix) { print("["); for (List row in matrix) { @@ -25,7 +25,7 @@ void printMatrix(List> matrix) { print("]"); } -/* Print linked list */ +/* Print a linked list */ void printLinkedList(ListNode? head) { List list = []; @@ -38,7 +38,7 @@ void printLinkedList(ListNode? head) { } /** - * Print binary tree + * Print the binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -81,10 +81,10 @@ void showTrunks(Trunk? p) { stdout.write(p.str); } -/* Print heap */ +/* Print a heap */ void printHeap(List heap) { - print("Array representation of heap: $heap"); - print("Heap tree representation:"); + print("Array representation of the heap: $heap"); + print("Tree representation of the heap:"); TreeNode? root = listToTree(heap); printTree(root); } diff --git a/en/codes/dart/utils/tree_node.dart b/en/codes/dart/utils/tree_node.dart index 43ddcee21..cb10ed8b0 100644 --- a/en/codes/dart/utils/tree_node.dart +++ b/en/codes/dart/utils/tree_node.dart @@ -8,8 +8,8 @@ class TreeNode { int val; // Node value int height; // Node height - TreeNode? left; // Reference to left child node - TreeNode? right; // Reference to right child node + TreeNode? left; // Reference to the left child + TreeNode? right; // Reference to the right child /* Constructor */ TreeNode(this.val, [this.height = 0, this.left, this.right]); diff --git a/en/codes/dart/utils/vertex.dart b/en/codes/dart/utils/vertex.dart index c685040b4..5309c5da6 100644 --- a/en/codes/dart/utils/vertex.dart +++ b/en/codes/dart/utils/vertex.dart @@ -9,7 +9,7 @@ class Vertex { int val; Vertex(this.val); - /* Input value list vals, return vertex list vets */ + /* Given the list of values vals, return the list of vertices vets */ static List valsToVets(List vals) { List vets = []; for (int i in vals) { @@ -18,7 +18,7 @@ class Vertex { return vets; } - /* Input vertex list vets, return value list vals */ + /* Given the list of vertices vets, return the list of values vals */ static List vetsToVals(List vets) { List vals = []; for (Vertex vet in vets) { diff --git a/en/codes/docker-compose.yml b/en/codes/docker-compose.yml new file mode 100644 index 000000000..8268eec4d --- /dev/null +++ b/en/codes/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.8' +services: + hello-algo-code: + build: + context: . + args: + # Choose which language environments to install + # Separate language names with spaces + LANGS: "python cpp java csharp" + image: hello-algo-code + container_name: hello-algo-code + stdin_open: true + tty: true diff --git a/en/codes/go/chapter_array_and_linkedlist/array.go b/en/codes/go/chapter_array_and_linkedlist/array.go index b2d58f7bd..0a46b9419 100644 --- a/en/codes/go/chapter_array_and_linkedlist/array.go +++ b/en/codes/go/chapter_array_and_linkedlist/array.go @@ -8,65 +8,65 @@ import ( "math/rand" ) -/* Random access to element */ +/* Access an element at a random index */ func randomAccess(nums []int) (randomNum int) { - // Randomly select a number in the interval [0, nums.length) + // Choose a random number in the interval [0, len(nums)) randomIndex := rand.Intn(len(nums)) - // Retrieve and return the random element + // Get and return the randomly selected element randomNum = nums[randomIndex] return } -/* Extend array length */ +/* Extend the array length */ func extend(nums []int, enlarge int) []int { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for i := len(nums) - 1; i > index; i-- { nums[i] = nums[i-1] } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num } -/* Remove the element at index index */ +/* Remove the element at index `index` */ func remove(nums []int, index int) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for i := index; i < len(nums)-1; i++ { nums[i] = nums[i+1] } } -/* Traverse array */ +/* Traverse the array */ func traverse(nums []int) { count := 0 - // Traverse array by index + // Traverse the array by index for i := 0; i < len(nums); i++ { count += nums[i] } count = 0 - // Direct traversal of array elements + // Iterate directly over the array elements for _, num := range nums { count += num } - // Traverse simultaneously data index and elements + // Iterate over both the indices and the elements for i, num := range nums { count += nums[i] count += num } } -/* Find the specified element in the array */ +/* Find a specified element in the array */ func find(nums []int, target int) (index int) { index = -1 for i := 0; i < len(nums); i++ { diff --git a/en/codes/go/chapter_array_and_linkedlist/array_test.go b/en/codes/go/chapter_array_and_linkedlist/array_test.go index fe696379b..27cce36c4 100644 --- a/en/codes/go/chapter_array_and_linkedlist/array_test.go +++ b/en/codes/go/chapter_array_and_linkedlist/array_test.go @@ -5,8 +5,8 @@ package chapter_array_and_linkedlist /** -We treat Go Slice as Array here. This reduces -the learning cost and allows us to focus on data structures and algorithms. +We treat Go slices as arrays in these examples. This simplifies the explanation +and lets us focus on data structures and algorithms. */ import ( @@ -14,37 +14,37 @@ import ( "testing" ) -/* Driver Code */ +/* Driver code */ func TestArray(t *testing.T) { - /* Initialize array */ + /* Initialize the array */ var arr [5]int fmt.Println("Array arr =", arr) - // In Go, specifying length ([5]int) creates an array, not specifying length ([]int) creates a slice - // Since Go 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() function, slices are treated as arrays below + // In Go, a type with a specified length ([5]int) is an array; without a length ([]int), it is a slice + // Go array lengths are determined at compile time, so the length must be a constant + // To make the extend() function easier to implement, we treat slices as arrays below nums := []int{1, 3, 2, 5, 4} fmt.Println("Array nums =", nums) - /* Insert element */ + /* Random access */ randomNum := randomAccess(nums) - fmt.Println("Get random element in nums", randomNum) + fmt.Println("Randomly selected element from nums:", randomNum) - /* Traverse array */ + /* Extend the length */ nums = extend(nums, 3) - fmt.Println("Extend array length to 8, get nums =", nums) + fmt.Println("Extend the array length to 8, giving nums =", nums) - /* Insert element */ + /* Insert an element */ insert(nums, 6, 3) - fmt.Println("Insert number 6 at index 3, get nums =", nums) + fmt.Println("Insert the number 6 at index 3, giving nums =", nums) - /* Remove element */ + /* Remove an element */ remove(nums, 2) - fmt.Println("Remove element at index 2, get nums =", nums) + fmt.Println("Remove the element at index 2, giving nums =", nums) - /* Traverse array */ + /* Traverse the array */ traverse(nums) - /* Find element */ + /* Find an element */ index := find(nums, 3) - fmt.Println("Find element 3 in nums, get index =", index) + fmt.Println("Find element 3 in nums, giving index =", index) } diff --git a/en/codes/go/chapter_array_and_linkedlist/linked_list.go b/en/codes/go/chapter_array_and_linkedlist/linked_list.go index 00b4d36e3..228c9dbc4 100644 --- a/en/codes/go/chapter_array_and_linkedlist/linked_list.go +++ b/en/codes/go/chapter_array_and_linkedlist/linked_list.go @@ -26,7 +26,7 @@ func removeItem(n0 *ListNode) { n0.Next = n1 } -/* Access the node at index index in the linked list */ +/* 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 { diff --git a/en/codes/go/chapter_array_and_linkedlist/linked_list_test.go b/en/codes/go/chapter_array_and_linkedlist/linked_list_test.go index d8a088214..4f2a2d3a2 100644 --- a/en/codes/go/chapter_array_and_linkedlist/linked_list_test.go +++ b/en/codes/go/chapter_array_and_linkedlist/linked_list_test.go @@ -12,37 +12,37 @@ import ( ) func TestLinkedList(t *testing.T) { - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the individual nodes n0 := NewListNode(1) n1 := NewListNode(3) n2 := NewListNode(2) n3 := NewListNode(5) n4 := NewListNode(4) - // Build references between nodes + // Set up references between the nodes n0.Next = n1 n1.Next = n2 n2.Next = n3 n3.Next = n4 - fmt.Println("Initialized linked list is") + fmt.Println("The initialized linked list is") PrintLinkedList(n0) - /* Insert node */ + /* Insert a node */ insertNode(n0, NewListNode(0)) - fmt.Println("Linked list after inserting node is") + fmt.Println("The linked list after inserting a node is") PrintLinkedList(n0) - /* Remove node */ + /* Remove a node */ removeItem(n0) - fmt.Println("Linked list after removing node is") + fmt.Println("The linked list after removing a node is") PrintLinkedList(n0) - /* Access node */ + /* Access a node */ node := access(n0, 3) - fmt.Println("Value of node at index 3 in linked list =", node) + fmt.Println("Node at index 3 in the linked list =", node) - /* Search node */ + /* Find a node */ index := findNode(n0, 2) - fmt.Println("Index of node with value 2 in linked list =", index) + fmt.Println("Index of the node with value 2 in the linked list =", index) } diff --git a/en/codes/go/chapter_array_and_linkedlist/list_test.go b/en/codes/go/chapter_array_and_linkedlist/list_test.go index 69824e2c6..829897f4c 100644 --- a/en/codes/go/chapter_array_and_linkedlist/list_test.go +++ b/en/codes/go/chapter_array_and_linkedlist/list_test.go @@ -10,25 +10,25 @@ import ( "testing" ) -/* Driver Code */ +/* Driver code */ func TestList(t *testing.T) { - /* Initialize list */ + /* Initialize the list */ nums := []int{1, 3, 2, 5, 4} fmt.Println("List nums =", nums) - /* Update element */ - num := nums[1] // Access element at index 1 - fmt.Println("Access element at index 1, get num =", num) + /* Access an element */ + num := nums[1] // Access the element at index 1 + fmt.Println("Access the element at index 1, giving num =", num) - /* Add elements at the end */ - nums[1] = 0 // Update element at index 1 to 0 - fmt.Println("Update element at index 1 to 0, get nums =", nums) + /* Update an element */ + nums[1] = 0 // Update the element at index 1 to 0 + fmt.Println("Update the element at index 1 to 0, giving nums =", nums) - /* Remove element */ + /* Clear the list */ nums = nil - fmt.Println("After clearing list, nums =", nums) + fmt.Println("After clearing the list, nums =", nums) - /* Direct traversal of list elements */ + /* Append at the end */ nums = append(nums, 1) nums = append(nums, 3) nums = append(nums, 2) @@ -36,20 +36,20 @@ func TestList(t *testing.T) { nums = append(nums, 4) fmt.Println("After adding elements, nums =", nums) - /* Sort list */ - nums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert number 6 at index 3 - fmt.Println("Insert number 6 at index 3, get nums =", nums) + /* Insert an element in the middle */ + nums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert the number 6 at index 3 + fmt.Println("Insert the number 6 at index 3, giving nums =", nums) - /* Remove element */ - nums = append(nums[:3], nums[4:]...) // Remove element at index 3 - fmt.Println("Remove element at index 3, get nums =", nums) + /* Remove an element */ + nums = append(nums[:3], nums[4:]...) // Remove the element at index 3 + fmt.Println("Remove the element at index 3, giving nums =", nums) - /* Traverse list by index */ + /* Traverse the list by index */ count := 0 for i := 0; i < len(nums); i++ { count += nums[i] } - /* Directly traverse list elements */ + /* Iterate directly over the list elements */ count = 0 for _, x := range nums { count += x @@ -57,10 +57,10 @@ func TestList(t *testing.T) { /* Concatenate two lists */ nums1 := []int{6, 8, 7, 10, 9} - nums = append(nums, nums1...) // Concatenate list nums1 to nums - fmt.Println("Concatenate list nums1 to nums, get nums =", nums) + nums = append(nums, nums1...) // Append list nums1 to nums + fmt.Println("After appending list nums1 to nums, nums =", nums) - /* Sort list */ - sort.Ints(nums) // After sorting, list elements are arranged from smallest to largest - fmt.Println("After sorting list, nums =", nums) + /* Sort the list */ + sort.Ints(nums) // After sorting, the list elements are in ascending order + fmt.Println("After sorting the list, nums =", nums) } diff --git a/en/codes/go/chapter_array_and_linkedlist/my_list.go b/en/codes/go/chapter_array_and_linkedlist/my_list.go index b61a2bee5..f854473b3 100644 --- a/en/codes/go/chapter_array_and_linkedlist/my_list.go +++ b/en/codes/go/chapter_array_and_linkedlist/my_list.go @@ -4,7 +4,7 @@ package chapter_array_and_linkedlist -/* List class */ +/* List type */ type myList struct { arrCapacity int arr []int @@ -16,32 +16,32 @@ type myList struct { func newMyList() *myList { return &myList{ arrCapacity: 10, // List capacity - arr: make([]int, 10), // Array (stores list elements) + arr: make([]int, 10), // Array (stores the list elements) arrSize: 0, // List length (current number of elements) - extendRatio: 2, // Multiple by which the list capacity is extended each time + extendRatio: 2, // Factor by which the list capacity grows each time } } -/* Get list length (current number of elements) */ +/* Get the list length (current number of elements) */ func (l *myList) size() int { return l.arrSize } -/* Get list capacity */ +/* Get the list capacity */ func (l *myList) capacity() int { return l.arrCapacity } -/* Update element */ +/* Access an element */ func (l *myList) get(index int) int { - // If the index is out of bounds, throw an exception, as below + // Panic if the index is out of bounds; the same applies below if index < 0 || index >= l.arrSize { panic("Index out of bounds") } return l.arr[index] } -/* Add elements at the end */ +/* Update an element */ func (l *myList) set(num, index int) { if index < 0 || index >= l.arrSize { panic("Index out of bounds") @@ -49,9 +49,9 @@ func (l *myList) set(num, index int) { l.arr[index] = num } -/* Direct traversal of list elements */ +/* Append at the end */ func (l *myList) add(num int) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if l.arrSize == l.arrCapacity { l.extendCapacity() } @@ -60,16 +60,16 @@ func (l *myList) add(num int) { l.arrSize++ } -/* Sort list */ +/* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if l.arrSize == l.arrCapacity { l.extendCapacity() } - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for j := l.arrSize - 1; j >= index; j-- { l.arr[j+1] = l.arr[j] } @@ -78,13 +78,13 @@ func (l *myList) insert(num, index int) { l.arrSize++ } -/* Remove element */ +/* Remove an 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 + // Shift all elements after index `index` one position to the left for j := index; j < l.arrSize-1; j++ { l.arr[j] = l.arr[j+1] } @@ -94,16 +94,16 @@ func (l *myList) remove(index int) int { return num } -/* Driver Code */ +/* Grow the list capacity */ func (l *myList) extendCapacity() { - // Create a new array with length extendRatio times the original array and copy the original array to the new array + // Extend the backing slice to extendRatio times its original length, preserving the existing elements l.arr = append(l.arr, make([]int, l.arrCapacity*(l.extendRatio-1))...) - // Add elements at the end + // Update the list capacity l.arrCapacity = len(l.arr) } -/* Return list with valid length */ +/* Return the portion of the list that contains elements */ func (l *myList) toArray() []int { - // Elements enqueue + // Return a slice containing only the occupied elements return l.arr[:l.arrSize] } diff --git a/en/codes/go/chapter_array_and_linkedlist/my_list_test.go b/en/codes/go/chapter_array_and_linkedlist/my_list_test.go index a7f31fc9d..61d77c4cf 100644 --- a/en/codes/go/chapter_array_and_linkedlist/my_list_test.go +++ b/en/codes/go/chapter_array_and_linkedlist/my_list_test.go @@ -9,11 +9,11 @@ import ( "testing" ) -/* Driver Code */ +/* Driver code */ func TestMyList(t *testing.T) { - /* Initialize list */ + /* Initialize the list */ nums := newMyList() - /* Direct traversal of list elements */ + /* Append at the end */ nums.add(1) nums.add(3) nums.add(2) @@ -21,26 +21,26 @@ func TestMyList(t *testing.T) { nums.add(4) fmt.Printf("List nums = %v, capacity = %v, length = %v\n", nums.toArray(), nums.capacity(), nums.size()) - /* Sort list */ + /* Insert an element in the middle */ nums.insert(6, 3) - fmt.Printf("Insert number 6 at index 3, get nums = %v\n", nums.toArray()) + fmt.Printf("Insert the number 6 at index 3, giving nums = %v\n", nums.toArray()) - /* Remove element */ + /* Remove an element */ nums.remove(3) - fmt.Printf("Remove element at index 3, get nums = %v\n", nums.toArray()) + fmt.Printf("Remove the element at index 3, giving nums = %v\n", nums.toArray()) - /* Update element */ + /* Access an element */ num := nums.get(1) - fmt.Printf("Access element at index 1, get num = %v\n", num) + fmt.Printf("Access the element at index 1, giving num = %v\n", num) - /* Add elements at the end */ + /* Update an element */ nums.set(0, 1) - fmt.Printf("Update element at index 1 to 0, get nums = %v\n", nums.toArray()) + fmt.Printf("Update the element at index 1 to 0, giving nums = %v\n", nums.toArray()) - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for i := 0; i < 10; i++ { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i) } - fmt.Printf("After expansion, list nums = %v, capacity = %v, length = %v\n", nums.toArray(), nums.capacity(), nums.size()) + fmt.Printf("After capacity growth: list nums = %v, capacity = %v, length = %v\n", nums.toArray(), nums.capacity(), nums.size()) } diff --git a/en/codes/go/chapter_backtracking/n_queens.go b/en/codes/go/chapter_backtracking/n_queens.go index 7cab2ce36..1f243468f 100644 --- a/en/codes/go/chapter_backtracking/n_queens.go +++ b/en/codes/go/chapter_backtracking/n_queens.go @@ -4,9 +4,9 @@ package chapter_backtracking -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if row == n { newState := make([][]string, len(*state)) for i, _ := range newState { @@ -17,28 +17,28 @@ func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, d *res = append(*res, newState) return } - // Traverse all columns + // Iterate over all columns for col := 0; col < n; col++ { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square diag1 := row - col + n - 1 diag2 := row + col - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if !(*cols)[col] && !(*diags1)[diag1] && !(*diags2)[diag2] { - // Attempt: place the queen in this cell + // Try: place a queen on this square (*state)[row][col] = "Q" (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true - // Place the next row + // Place a queen in the next row backtrack(row+1, n, state, res, cols, diags1, diags2) - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty (*state)[row][col] = "#" (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false } } } -/* Solve N queens */ +/* Solve the n-queens problem */ func nQueens(n int) [][][]string { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square state := make([][]string, n) for i := 0; i < n; i++ { row := make([]string, n) @@ -47,7 +47,7 @@ func nQueens(n int) [][][]string { } state[i] = row } - // Record whether there is a queen in the column + // Track whether a column contains a queen cols := make([]bool, n) diags1 := make([]bool, 2*n-1) diags2 := make([]bool, 2*n-1) diff --git a/en/codes/go/chapter_backtracking/n_queens_test.go b/en/codes/go/chapter_backtracking/n_queens_test.go index 903c2bd8e..7771d37d9 100644 --- a/en/codes/go/chapter_backtracking/n_queens_test.go +++ b/en/codes/go/chapter_backtracking/n_queens_test.go @@ -13,8 +13,8 @@ func TestNQueens(t *testing.T) { n := 4 res := nQueens(n) - fmt.Println("Input board size is ", n) - fmt.Println("Total queen placement solutions: ", len(res), " solutions") + fmt.Println("Input board side length: ", n) + fmt.Println("There are ", len(res), " queen placements") for _, state := range res { fmt.Println("--------------------") for _, row := range state { diff --git a/en/codes/go/chapter_backtracking/permutation_test.go b/en/codes/go/chapter_backtracking/permutation_test.go index 0d7d660d0..4b46e9278 100644 --- a/en/codes/go/chapter_backtracking/permutation_test.go +++ b/en/codes/go/chapter_backtracking/permutation_test.go @@ -18,7 +18,7 @@ func TestPermutationI(t *testing.T) { PrintSlice(nums) res := permutationsI(nums) - fmt.Printf("All permutations res = ") + fmt.Printf("All permutations: res = ") fmt.Println(res) } @@ -28,6 +28,6 @@ func TestPermutationII(t *testing.T) { PrintSlice(nums) res := permutationsII(nums) - fmt.Printf("All permutations res = ") + fmt.Printf("All permutations: res = ") fmt.Println(res) } diff --git a/en/codes/go/chapter_backtracking/permutations_i.go b/en/codes/go/chapter_backtracking/permutations_i.go index 3983036d8..02904f76c 100644 --- a/en/codes/go/chapter_backtracking/permutations_i.go +++ b/en/codes/go/chapter_backtracking/permutations_i.go @@ -4,24 +4,24 @@ package chapter_backtracking -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ func backtrackI(state *[]int, choices *[]int, selected *[]bool, res *[][]int) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if len(*state) == len(*choices) { newState := append([]int{}, *state...) *res = append(*res, newState) } - // Traverse all choices + // Iterate over all choices for i := 0; i < len(*choices); i++ { choice := (*choices)[i] - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if !(*selected)[i] { - // Attempt: make choice, update state + // Try: make a choice and update the state (*selected)[i] = true *state = append(*state, choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrackI(state, choices, selected, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state (*selected)[i] = false *state = (*state)[:len(*state)-1] } diff --git a/en/codes/go/chapter_backtracking/permutations_ii.go b/en/codes/go/chapter_backtracking/permutations_ii.go index d8fd5767d..6bf58d465 100644 --- a/en/codes/go/chapter_backtracking/permutations_ii.go +++ b/en/codes/go/chapter_backtracking/permutations_ii.go @@ -4,27 +4,27 @@ package chapter_backtracking -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ func backtrackII(state *[]int, choices *[]int, selected *[]bool, res *[][]int) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if len(*state) == len(*choices) { newState := append([]int{}, *state...) *res = append(*res, newState) } - // Traverse all choices + // Iterate over all choices duplicated := make(map[int]struct{}, 0) for i := 0; i < len(*choices); i++ { choice := (*choices)[i] - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if _, ok := duplicated[choice]; !ok && !(*selected)[i] { - // Attempt: make choice, update state - // Record the selected element value + // Try: make a choice and update the state + // Record the chosen value duplicated[choice] = struct{}{} (*selected)[i] = true *state = append(*state, choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrackII(state, choices, selected, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state (*selected)[i] = false *state = (*state)[:len(*state)-1] } diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_i_compact.go b/en/codes/go/chapter_backtracking/preorder_traversal_i_compact.go index 930bc67a2..aa754ce4c 100644 --- a/en/codes/go/chapter_backtracking/preorder_traversal_i_compact.go +++ b/en/codes/go/chapter_backtracking/preorder_traversal_i_compact.go @@ -8,13 +8,13 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ func preOrderI(root *TreeNode, res *[]*TreeNode) { if root == nil { return } if (root.Val).(int) == 7 { - // Record solution + // Record the solution *res = append(*res, root) } preOrderI(root.Left, res) diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go b/en/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go index c54bcf3dd..d0dcc066d 100644 --- a/en/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go +++ b/en/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go @@ -8,15 +8,15 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { if root == nil { return } - // Attempt + // Try *path = append(*path, root) if root.Val.(int) == 7 { - // Record solution + // Record the solution *res = append(*res, append([]*TreeNode{}, *path...)) } preOrderII(root.Left, res, path) diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go b/en/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go index e13942ce5..82b6d7865 100644 --- a/en/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go +++ b/en/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go @@ -8,16 +8,16 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { - // Pruning + // Prune if root == nil || root.Val == 3 { return } - // Attempt + // Try *path = append(*path, root) if root.Val.(int) == 7 { - // Record solution + // Record the solution *res = append(*res, append([]*TreeNode{}, *path...)) } preOrderIII(root.Left, res, path) diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_iii_template.go b/en/codes/go/chapter_backtracking/preorder_traversal_iii_template.go index 38efa8046..5e80ba9dd 100644 --- a/en/codes/go/chapter_backtracking/preorder_traversal_iii_template.go +++ b/en/codes/go/chapter_backtracking/preorder_traversal_iii_template.go @@ -8,49 +8,49 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ func isSolution(state *[]*TreeNode) bool { return len(*state) != 0 && (*state)[len(*state)-1].Val == 7 } -/* Record solution */ +/* Record the solution */ func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) { *res = append(*res, append([]*TreeNode{}, *state...)) } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ func isValid(state *[]*TreeNode, choice *TreeNode) bool { return choice != nil && choice.Val != 3 } -/* Update state */ +/* Update the state */ func makeChoice(state *[]*TreeNode, choice *TreeNode) { *state = append(*state, choice) } -/* Restore state */ +/* Restore the state */ func undoChoice(state *[]*TreeNode, choice *TreeNode) { *state = (*state)[:len(*state)-1] } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ func backtrackIII(state *[]*TreeNode, choices *[]*TreeNode, res *[][]*TreeNode) { - // Check if it is a solution + // Check whether this is a solution if isSolution(state) { - // Record solution + // Record the solution recordSolution(state, res) } - // Traverse all choices + // Iterate over all choices for _, choice := range *choices { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if isValid(state, choice) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state, choice) - // Proceed to the next round of selection + // Proceed to the next choice temp := make([]*TreeNode, 0) temp = append(temp, choice.Left, choice.Right) backtrackIII(state, &temp, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice) } } diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_test.go b/en/codes/go/chapter_backtracking/preorder_traversal_test.go index c95d05bf3..411db924d 100644 --- a/en/codes/go/chapter_backtracking/preorder_traversal_test.go +++ b/en/codes/go/chapter_backtracking/preorder_traversal_test.go @@ -12,9 +12,9 @@ import ( ) func TestPreorderTraversalICompact(t *testing.T) { - /* Initialize binary tree */ + /* Initialize the binary tree */ root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) - fmt.Println("\nInitialize binary tree") + fmt.Println("\nInitialize the binary tree") PrintTree(root) // Preorder traversal @@ -29,9 +29,9 @@ func TestPreorderTraversalICompact(t *testing.T) { } func TestPreorderTraversalIICompact(t *testing.T) { - /* Initialize binary tree */ + /* Initialize the binary tree */ root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) - fmt.Println("\nInitialize binary tree") + fmt.Println("\nInitialize the binary tree") PrintTree(root) // Preorder traversal @@ -39,7 +39,7 @@ func TestPreorderTraversalIICompact(t *testing.T) { res := make([][]*TreeNode, 0) preOrderII(root, &res, &path) - fmt.Println("\nOutput all paths from root node to node 7") + fmt.Println("\nOutput all paths from the root to nodes with value 7") for _, path := range res { for _, node := range path { fmt.Printf("%v ", node.Val) @@ -49,9 +49,9 @@ func TestPreorderTraversalIICompact(t *testing.T) { } func TestPreorderTraversalIIICompact(t *testing.T) { - /* Initialize binary tree */ + /* Initialize the binary tree */ root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) - fmt.Println("\nInitialize binary tree") + fmt.Println("\nInitialize the binary tree") PrintTree(root) // Preorder traversal @@ -59,7 +59,7 @@ func TestPreorderTraversalIIICompact(t *testing.T) { res := make([][]*TreeNode, 0) preOrderIII(root, &res, &path) - fmt.Println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + fmt.Println("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3") for _, path := range res { for _, node := range path { fmt.Printf("%v ", node.Val) @@ -69,9 +69,9 @@ func TestPreorderTraversalIIICompact(t *testing.T) { } func TestPreorderTraversalIIITemplate(t *testing.T) { - /* Initialize binary tree */ + /* Initialize the binary tree */ root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) - fmt.Println("\nInitialize binary tree") + fmt.Println("\nInitialize the binary tree") PrintTree(root) // Backtracking algorithm @@ -81,7 +81,7 @@ func TestPreorderTraversalIIITemplate(t *testing.T) { choices = append(choices, root) backtrackIII(&state, &choices, &res) - fmt.Println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + fmt.Println("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3") for _, path := range res { for _, node := range path { fmt.Printf("%v ", node.Val) diff --git a/en/codes/go/chapter_backtracking/subset_sum_i.go b/en/codes/go/chapter_backtracking/subset_sum_i.go index 6a293bdf8..80f3f2522 100644 --- a/en/codes/go/chapter_backtracking/subset_sum_i.go +++ b/en/codes/go/chapter_backtracking/subset_sum_i.go @@ -6,27 +6,27 @@ package chapter_backtracking import "sort" -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ func backtrackSubsetSumI(start, target int, state, choices *[]int, res *[][]int) { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target sum (remaining target is zero) if target == 0 { newState := append([]int{}, *state...) *res = append(*res, newState) return } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for i := start; i < len(*choices); i++ { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if target-(*choices)[i] < 0 { break } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start *state = append(*state, (*choices)[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrackSubsetSumI(i, target-(*choices)[i], state, choices, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state *state = (*state)[:len(*state)-1] } } @@ -35,8 +35,8 @@ func backtrackSubsetSumI(start, target int, state, choices *[]int, res *[][]int) func subsetSumI(nums []int, target int) [][]int { state := make([]int, 0) // State (subset) sort.Ints(nums) // Sort nums - start := 0 // Start point for traversal - res := make([][]int, 0) // Result list (subset list) + start := 0 // Starting index for iteration + res := make([][]int, 0) // Result list (list of subsets) backtrackSubsetSumI(start, target, &state, &nums, &res) return res } diff --git a/en/codes/go/chapter_backtracking/subset_sum_i_naive.go b/en/codes/go/chapter_backtracking/subset_sum_i_naive.go index ff207f7c7..09d40b909 100644 --- a/en/codes/go/chapter_backtracking/subset_sum_i_naive.go +++ b/en/codes/go/chapter_backtracking/subset_sum_i_naive.go @@ -4,34 +4,34 @@ package chapter_backtracking -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ func backtrackSubsetSumINaive(total, target int, state, choices *[]int, res *[][]int) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if target == total { newState := append([]int{}, *state...) *res = append(*res, newState) return } - // Traverse all choices + // Iterate over all choices for i := 0; i < len(*choices); i++ { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if total+(*choices)[i] > target { continue } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total *state = append(*state, (*choices)[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrackSubsetSumINaive(total+(*choices)[i], target, state, choices, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state *state = (*state)[:len(*state)-1] } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ func subsetSumINaive(nums []int, target int) [][]int { state := make([]int, 0) // State (subset) total := 0 // Subset sum - res := make([][]int, 0) // Result list (subset list) + res := make([][]int, 0) // Result list (list of subsets) backtrackSubsetSumINaive(total, target, &state, &nums, &res) return res } diff --git a/en/codes/go/chapter_backtracking/subset_sum_ii.go b/en/codes/go/chapter_backtracking/subset_sum_ii.go index ec172251b..4a1a8e2d0 100644 --- a/en/codes/go/chapter_backtracking/subset_sum_ii.go +++ b/en/codes/go/chapter_backtracking/subset_sum_ii.go @@ -6,32 +6,32 @@ package chapter_backtracking import "sort" -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ func backtrackSubsetSumII(start, target int, state, choices *[]int, res *[][]int) { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target sum (remaining target is zero) if target == 0 { newState := append([]int{}, *state...) *res = append(*res, newState) return } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: start after the previously chosen index to avoid choosing the same element again for i := start; i < len(*choices); i++ { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if target-(*choices)[i] < 0 { break } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if i > start && (*choices)[i] == (*choices)[i-1] { continue } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start *state = append(*state, (*choices)[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrackSubsetSumII(i+1, target-(*choices)[i], state, choices, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state *state = (*state)[:len(*state)-1] } } @@ -40,8 +40,8 @@ func backtrackSubsetSumII(start, target int, state, choices *[]int, res *[][]int func subsetSumII(nums []int, target int) [][]int { state := make([]int, 0) // State (subset) sort.Ints(nums) // Sort nums - start := 0 // Start point for traversal - res := make([][]int, 0) // Result list (subset list) + start := 0 // Starting index for iteration + res := make([][]int, 0) // Result list (list of subsets) backtrackSubsetSumII(start, target, &state, &nums, &res) return res } diff --git a/en/codes/go/chapter_backtracking/subset_sum_test.go b/en/codes/go/chapter_backtracking/subset_sum_test.go index 0f8d58031..32f0e69b8 100644 --- a/en/codes/go/chapter_backtracking/subset_sum_test.go +++ b/en/codes/go/chapter_backtracking/subset_sum_test.go @@ -20,11 +20,11 @@ func TestSubsetSumINaive(t *testing.T) { fmt.Printf("target = " + strconv.Itoa(target) + ", input array nums = ") PrintSlice(nums) - fmt.Println("All subsets with sum equal to " + strconv.Itoa(target) + " are res = ") + fmt.Println("All subsets with sum " + strconv.Itoa(target) + " are: res = ") for i := range res { PrintSlice(res[i]) } - fmt.Println("Please note that this method outputs results containing duplicate sets") + fmt.Println("Note that this method produces duplicate subsets") } func TestSubsetSumI(t *testing.T) { @@ -35,7 +35,7 @@ func TestSubsetSumI(t *testing.T) { fmt.Printf("target = " + strconv.Itoa(target) + ", input array nums = ") PrintSlice(nums) - fmt.Println("All subsets with sum equal to " + strconv.Itoa(target) + " are res = ") + fmt.Println("All subsets with sum " + strconv.Itoa(target) + " are: res = ") for i := range res { PrintSlice(res[i]) } @@ -49,7 +49,7 @@ func TestSubsetSumII(t *testing.T) { fmt.Printf("target = " + strconv.Itoa(target) + ", input array nums = ") PrintSlice(nums) - fmt.Println("All subsets with sum equal to " + strconv.Itoa(target) + " are res = ") + fmt.Println("All subsets with sum " + strconv.Itoa(target) + " are: res = ") for i := range res { PrintSlice(res[i]) } diff --git a/en/codes/go/chapter_computational_complexity/complexity_exercises.go b/en/codes/go/chapter_computational_complexity/complexity_exercises.go index d96a2563f..8516b2fa2 100644 --- a/en/codes/go/chapter_computational_complexity/complexity_exercises.go +++ b/en/codes/go/chapter_computational_complexity/complexity_exercises.go @@ -4,7 +4,7 @@ package chapter_computational_complexity -/* Iterative summation */ +/* Sum using iteration */ func sumIter(n int) int { res := 0 for i := 1; i <= n; i++ { @@ -13,7 +13,7 @@ func sumIter(n int) int { return res } -/* Recursive summation */ +/* Sum using recursion */ func sumRecur(n int) int { if n == 1 { return 1 @@ -21,7 +21,7 @@ func sumRecur(n int) int { return n + sumRecur(n-1) } -/* Linear loop */ +/* Linear-time loop */ func linearLoop(n int) int { res := 0 for i := 0; i < n; i++ { @@ -30,7 +30,7 @@ func linearLoop(n int) int { return res } -/* Quadratic loop */ +/* Quadratic-time loop */ func quadraticLoop(n int) int { res := 0 for i := 0; i < n; i++ { @@ -41,7 +41,7 @@ func quadraticLoop(n int) int { return res } -/* Logarithmic loop */ +/* Logarithmic-time loop */ func logarithmicLoop(n int) int { for n > 1 { n /= 2 diff --git a/en/codes/go/chapter_computational_complexity/iteration.go b/en/codes/go/chapter_computational_complexity/iteration.go index 2335932c0..880dd2b6a 100644 --- a/en/codes/go/chapter_computational_complexity/iteration.go +++ b/en/codes/go/chapter_computational_complexity/iteration.go @@ -9,7 +9,7 @@ import "fmt" /* for loop */ func forLoop(n int) int { res := 0 - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for i := 1; i <= n; i++ { res += i } @@ -19,12 +19,12 @@ func forLoop(n int) int { /* while loop */ func whileLoop(n int) int { res := 0 - // Initialize condition variable + // Initialize the loop control variable i := 1 - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for i <= n { res += i - // Update condition variable + // Update the loop control variable i++ } return res @@ -33,25 +33,25 @@ func whileLoop(n int) int { /* while loop (two updates) */ func whileLoopII(n int) int { res := 0 - // Initialize condition variable + // Initialize the loop control variable i := 1 - // Sum 1, 4, 10, ... + // Sum 1, 4, 10, ... in a loop for i <= n { res += i - // Update condition variable + // Update the loop control variable i++ i *= 2 } return res } -/* Nested for loop */ +/* Nested for loops */ func nestedForLoop(n int) string { res := "" - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for i := 1; i <= n; i++ { for j := 1; j <= n; j++ { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n res += fmt.Sprintf("(%d, %d), ", i, j) } } diff --git a/en/codes/go/chapter_computational_complexity/iteration_test.go b/en/codes/go/chapter_computational_complexity/iteration_test.go index fa01e9a99..f901a7ea4 100644 --- a/en/codes/go/chapter_computational_complexity/iteration_test.go +++ b/en/codes/go/chapter_computational_complexity/iteration_test.go @@ -9,18 +9,18 @@ import ( "testing" ) -/* Driver Code */ +/* Driver code */ func TestIteration(t *testing.T) { n := 5 res := forLoop(n) - fmt.Println("\nfor loop sum result res = ", res) + fmt.Println("\nSum from the for loop: res = ", res) res = whileLoop(n) - fmt.Println("\nwhile loop sum result res = ", res) + fmt.Println("\nSum from the while loop: res = ", res) res = whileLoopII(n) - fmt.Println("\nwhile loop (two updates) sum result res = ", res) + fmt.Println("\nSum from the while loop (two updates): res = ", res) resStr := nestedForLoop(n) - fmt.Println("\nDouble for loop traversal result ", resStr) + fmt.Println("\nTraversal result from the nested for loops: ", resStr) } diff --git a/en/codes/go/chapter_computational_complexity/recursion.go b/en/codes/go/chapter_computational_complexity/recursion.go index 776ca2535..16e1d7535 100644 --- a/en/codes/go/chapter_computational_complexity/recursion.go +++ b/en/codes/go/chapter_computational_complexity/recursion.go @@ -8,13 +8,13 @@ import "container/list" /* Recursion */ func recur(n int) int { - // Termination condition + // Base case if n == 1 { return 1 } - // Recurse: recursive call + // Recurse: make a recursive call res := recur(n - 1) - // Return: return result + // Unwind: return the result return n + res } @@ -23,14 +23,14 @@ func forLoopRecur(n int) int { // Use an explicit stack to simulate the system call stack stack := list.New() res := 0 - // Recurse: recursive call + // Descending: simulate recursive calls for i := n; i > 0; i-- { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.PushBack(i) } - // Return: return result + // Unwinding: simulate returning results for stack.Len() != 0 { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.Back().Value.(int) stack.Remove(stack.Back()) } @@ -40,22 +40,22 @@ func forLoopRecur(n int) int { /* Tail recursion */ func tailRecur(n int, res int) int { - // Termination condition + // Base case if n == 0 { return res } - // Tail recursive call + // Tail-recursive call return tailRecur(n-1, res+n) } /* Fibonacci sequence: recursion */ func fib(n int) int { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if n == 1 || n == 2 { return n - 1 } - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) res := fib(n-1) + fib(n-2) - // Return result f(n) + // Return the result f(n) return res } diff --git a/en/codes/go/chapter_computational_complexity/recursion_test.go b/en/codes/go/chapter_computational_complexity/recursion_test.go index 2322c5bc5..5122daaae 100644 --- a/en/codes/go/chapter_computational_complexity/recursion_test.go +++ b/en/codes/go/chapter_computational_complexity/recursion_test.go @@ -9,18 +9,18 @@ import ( "testing" ) -/* Driver Code */ +/* Driver code */ func TestRecursion(t *testing.T) { n := 5 res := recur(n) - fmt.Println("\nRecursive function sum result res = ", res) + fmt.Println("\nSum from recursion: res = ", res) res = forLoopRecur(n) - fmt.Println("\nUsing iteration to simulate recursive sum result res = ", res) + fmt.Println("\nSum from simulating recursion with iteration: res = ", res) res = tailRecur(n, 0) - fmt.Println("\nTail recursive function sum result res = ", res) + fmt.Println("\nSum from tail recursion: res = ", res) res = fib(n) - fmt.Println("\nThe ", n, "th term of Fibonacci sequence is", res) + fmt.Println("\nFibonacci term", n, "is", res) } diff --git a/en/codes/go/chapter_computational_complexity/space_complexity.go b/en/codes/go/chapter_computational_complexity/space_complexity.go index 98551c19d..f3ce5dead 100644 --- a/en/codes/go/chapter_computational_complexity/space_complexity.go +++ b/en/codes/go/chapter_computational_complexity/space_complexity.go @@ -17,7 +17,7 @@ type node struct { next *node } -/* Create node struct */ +/* Create a node struct */ func newNode(val int) *node { return &node{val: val} } @@ -28,9 +28,9 @@ func function() int { return 0 } -/* Constant order */ +/* Constant complexity */ func spaceConstant(n int) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space const a = 0 b := 0 nums := make([]int, 10000) @@ -40,7 +40,7 @@ func spaceConstant(n int) { for i := 0; i < n; i++ { c = 0 } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for i := 0; i < n; i++ { function() } @@ -50,51 +50,51 @@ func spaceConstant(n int) { node.val = 0 } -/* Linear order */ +/* Linear complexity */ func spaceLinear(n int) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space _ = make([]int, n) // A list of length n occupies O(n) space var nodes []*node for i := 0; i < n; i++ { nodes = append(nodes, newNode(i)) } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space m := make(map[int]string, n) for i := 0; i < n; i++ { m[i] = strconv.Itoa(i) } } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ func spaceLinearRecur(n int) { - fmt.Println("Recursion n =", n) + fmt.Println("Recursive call: n =", n) if n == 1 { return } spaceLinearRecur(n - 1) } -/* Exponential order */ +/* Quadratic complexity */ func spaceQuadratic(n int) { - // Matrix uses O(n^2) space + // The matrix uses O(n^2) space numMatrix := make([][]int, n) for i := 0; i < n; i++ { numMatrix[i] = make([]int, n) } } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ func spaceQuadraticRecur(n int) int { if n <= 0 { return 0 } nums := make([]int, n) - fmt.Printf("In recursion n = %d, nums length = %d \n", n, len(nums)) + fmt.Printf("Recursive call n = %d: length of nums = %d \n", n, len(nums)) return spaceQuadraticRecur(n - 1) } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ func buildTree(n int) *TreeNode { if n == 0 { return nil diff --git a/en/codes/go/chapter_computational_complexity/space_complexity_test.go b/en/codes/go/chapter_computational_complexity/space_complexity_test.go index b0a3f50a2..011cf009c 100644 --- a/en/codes/go/chapter_computational_complexity/space_complexity_test.go +++ b/en/codes/go/chapter_computational_complexity/space_complexity_test.go @@ -12,15 +12,15 @@ import ( func TestSpaceComplexity(t *testing.T) { n := 5 - // Constant order + // Constant complexity spaceConstant(n) - // Linear order + // Linear complexity spaceLinear(n) spaceLinearRecur(n) - // Exponential order + // Quadratic complexity spaceQuadratic(n) spaceQuadraticRecur(n) - // Exponential order + // Exponential complexity root := buildTree(n) PrintTree(root) } diff --git a/en/codes/go/chapter_computational_complexity/time_complexity.go b/en/codes/go/chapter_computational_complexity/time_complexity.go index 70211935c..220738c5d 100644 --- a/en/codes/go/chapter_computational_complexity/time_complexity.go +++ b/en/codes/go/chapter_computational_complexity/time_complexity.go @@ -4,7 +4,7 @@ package chapter_computational_complexity -/* Constant order */ +/* Constant complexity */ func constant(n int) int { count := 0 size := 100000 @@ -14,7 +14,7 @@ func constant(n int) int { return count } -/* Linear order */ +/* Linear complexity */ func linear(n int) int { count := 0 for i := 0; i < n; i++ { @@ -23,20 +23,20 @@ func linear(n int) int { return count } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ func arrayTraversal(nums []int) int { count := 0 - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for range nums { count++ } return count } -/* Exponential order */ +/* Quadratic complexity */ func quadratic(n int) int { count := 0 - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for i := 0; i < n; i++ { for j := 0; j < n; j++ { count++ @@ -45,29 +45,29 @@ func quadratic(n int) int { return count } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ func bubbleSort(nums []int) int { count := 0 // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i := len(nums) - 1; i > 0; i-- { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j := 0; j < i; j++ { if nums[j] > nums[j+1] { // Swap nums[j] and nums[j + 1] tmp := nums[j] nums[j] = nums[j+1] nums[j+1] = tmp - count += 3 // Element swap includes 3 unit operations + count += 3 // Swapping two elements takes 3 elementary operations } } } return count } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation)*/ func exponential(n int) int { count, base := 0, 1 - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for i := 0; i < n; i++ { for j := 0; j < base; j++ { count++ @@ -78,7 +78,7 @@ func exponential(n int) int { return count } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation)*/ func expRecur(n int) int { if n == 1 { return 1 @@ -86,7 +86,7 @@ func expRecur(n int) int { return expRecur(n-1) + expRecur(n-1) + 1 } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation)*/ func logarithmic(n int) int { count := 0 for n > 1 { @@ -96,7 +96,7 @@ func logarithmic(n int) int { return count } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation)*/ func logRecur(n int) int { if n <= 1 { return 0 @@ -104,7 +104,7 @@ func logRecur(n int) int { return logRecur(n/2) + 1 } -/* Linearithmic order */ +/* Linearithmic complexity */ func linearLogRecur(n int) int { if n <= 1 { return 1 @@ -116,13 +116,13 @@ func linearLogRecur(n int) int { return count } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ func factorialRecur(n int) int { if n == 0 { return 1 } count := 0 - // Split from 1 into n + // Split one problem into n subproblems for i := 0; i < n; i++ { count += factorialRecur(n - 1) } diff --git a/en/codes/go/chapter_computational_complexity/time_complexity_test.go b/en/codes/go/chapter_computational_complexity/time_complexity_test.go index 0f74bfe0c..fabfeb836 100644 --- a/en/codes/go/chapter_computational_complexity/time_complexity_test.go +++ b/en/codes/go/chapter_computational_complexity/time_complexity_test.go @@ -11,38 +11,38 @@ import ( func TestTimeComplexity(t *testing.T) { n := 8 - fmt.Println("Input data size n =", n) + fmt.Println("Input size n =", n) count := constant(n) - fmt.Println("Number of constant-order operations =", count) + fmt.Println("Operation count for constant complexity =", count) count = linear(n) - fmt.Println("Number of linear-order operations =", count) + fmt.Println("Operation count for linear complexity =", count) count = arrayTraversal(make([]int, n)) - fmt.Println("Number of linear-order (array traversal) operations =", count) + fmt.Println("Operation count for linear complexity (array traversal) =", count) count = quadratic(n) - fmt.Println("Number of quadratic-order operations =", count) + fmt.Println("Operation count for quadratic complexity =", count) nums := make([]int, n) for i := 0; i < n; i++ { nums[i] = n - i } count = bubbleSort(nums) - fmt.Println("Number of quadratic-order (bubble sort) operations =", count) + fmt.Println("Operation count for quadratic complexity (bubble sort) =", count) count = exponential(n) - fmt.Println("Number of exponential-order (loop implementation) operations =", count) + fmt.Println("Operation count for exponential complexity (iterative implementation) =", count) count = expRecur(n) - fmt.Println("Number of exponential-order (recursive implementation) operations =", count) + fmt.Println("Operation count for exponential complexity (recursive implementation) =", count) count = logarithmic(n) - fmt.Println("Number of logarithmic-order (loop implementation) operations =", count) + fmt.Println("Operation count for logarithmic complexity (iterative implementation) =", count) count = logRecur(n) - fmt.Println("Number of logarithmic-order (recursive implementation) operations =", count) + fmt.Println("Operation count for logarithmic complexity (recursive implementation) =", count) count = linearLogRecur(n) - fmt.Println("Number of linearithmic-order (recursive implementation) operations =", count) + fmt.Println("Operation count for linearithmic complexity (recursive implementation) =", count) count = factorialRecur(n) - fmt.Println("Number of factorial-order (recursive implementation) operations =", count) + fmt.Println("Operation count for factorial complexity (recursive implementation) =", count) } diff --git a/en/codes/go/chapter_computational_complexity/worst_best_time_complexity.go b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity.go index e3c4c1c30..a151a86a4 100644 --- a/en/codes/go/chapter_computational_complexity/worst_best_time_complexity.go +++ b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity.go @@ -8,25 +8,25 @@ import ( "math/rand" ) -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ func randomNumbers(n int) []int { nums := make([]int, n) - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for i := 0; i < n; i++ { nums[i] = i + 1 } - // Randomly shuffle array elements + // Randomly shuffle the array elements rand.Shuffle(len(nums), func(i, j int) { nums[i], nums[j] = nums[j], nums[i] }) return nums } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ func findOne(nums []int) int { for i := 0; i < len(nums); i++ { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if nums[i] == 1 { return i } diff --git a/en/codes/go/chapter_computational_complexity/worst_best_time_complexity_test.go b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity_test.go index a45d445bc..5a98421bf 100644 --- a/en/codes/go/chapter_computational_complexity/worst_best_time_complexity_test.go +++ b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity_test.go @@ -14,7 +14,7 @@ func TestWorstBestTimeComplexity(t *testing.T) { n := 100 nums := randomNumbers(n) index := findOne(nums) - fmt.Println("\nAfter shuffling array [ 1, 2, ..., n ] =", nums) - fmt.Println("Index of number 1 is", index) + fmt.Println("\nArray [ 1, 2, ..., n ] after shuffling =", nums) + fmt.Println("The index of the number 1 is", index) } } diff --git a/en/codes/go/chapter_divide_and_conquer/binary_search_recur.go b/en/codes/go/chapter_divide_and_conquer/binary_search_recur.go index 74d14a373..0268763b3 100644 --- a/en/codes/go/chapter_divide_and_conquer/binary_search_recur.go +++ b/en/codes/go/chapter_divide_and_conquer/binary_search_recur.go @@ -6,23 +6,23 @@ package chapter_divide_and_conquer /* Binary search: problem f(i, j) */ func dfs(nums []int, target, i, j int) int { - // If interval is empty, indicating no target element, return -1 + // If the interval is empty, the target is absent; return -1 if i > j { return -1 } - // Calculate midpoint index + // Calculate the midpoint index m := i + ((j - i) >> 1) - // Compare midpoint with target element + //Compare the midpoint element with the target if nums[m] < target { - // If smaller, recurse on right half of array - // Recursion subproblem f(m+1, j) + // If it is smaller, recurse on the right half + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m+1, j) } else if nums[m] > target { - // If larger, recurse on left half of array - // Recursion subproblem f(i, m-1) + // If it is larger, recurse on the left half + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m-1) } else { - // Found the target element, return its index + // The target element was found; return its index return m } } diff --git a/en/codes/go/chapter_divide_and_conquer/binary_search_recur_test.go b/en/codes/go/chapter_divide_and_conquer/binary_search_recur_test.go index 10d4b9876..6cc8ee6d2 100644 --- a/en/codes/go/chapter_divide_and_conquer/binary_search_recur_test.go +++ b/en/codes/go/chapter_divide_and_conquer/binary_search_recur_test.go @@ -16,5 +16,5 @@ func TestBinarySearch(t *testing.T) { targetIndex := binarySearch(nums, target) fmt.Println("Index of target element 6 = ", targetIndex) noTargetIndex := binarySearch(nums, noTarget) - fmt.Println("Index of non-existent target element = ", noTargetIndex) + fmt.Println("Index returned for the absent target = ", noTargetIndex) } diff --git a/en/codes/go/chapter_divide_and_conquer/build_tree.go b/en/codes/go/chapter_divide_and_conquer/build_tree.go index 8939988aa..6ed1b0267 100644 --- a/en/codes/go/chapter_divide_and_conquer/build_tree.go +++ b/en/codes/go/chapter_divide_and_conquer/build_tree.go @@ -6,15 +6,15 @@ package chapter_divide_and_conquer import . "github.com/krahets/hello-algo/pkg" -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ func dfsBuildTree(preorder []int, inorderMap map[int]int, i, l, r int) *TreeNode { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if r-l < 0 { return nil } // Initialize the root node root := NewTreeNode(preorder[i]) - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees m := inorderMap[preorder[i]] // Subproblem: build the left subtree root.Left = dfsBuildTree(preorder, inorderMap, i+1, l, m-1) @@ -24,9 +24,9 @@ func dfsBuildTree(preorder []int, inorderMap map[int]int, i, l, r int) *TreeNode return root } -/* Build binary tree */ +/* Build a binary tree */ func buildTree(preorder, inorder []int) *TreeNode { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index inorderMap := make(map[int]int, len(inorder)) for i := 0; i < len(inorder); i++ { inorderMap[inorder[i]] = i diff --git a/en/codes/go/chapter_divide_and_conquer/fast_power.go b/en/codes/go/chapter_divide_and_conquer/fast_power.go index c5fc9ce85..d51f22ff9 100644 --- a/en/codes/go/chapter_divide_and_conquer/fast_power.go +++ b/en/codes/go/chapter_divide_and_conquer/fast_power.go @@ -4,7 +4,7 @@ package chapter_divide_and_conquer -/* Exponentiation by squaring */ +/* Fast exponentiation */ func fastPow(x, n int) int { if n == 0 { return 1 diff --git a/en/codes/go/chapter_divide_and_conquer/hanota.go b/en/codes/go/chapter_divide_and_conquer/hanota.go index 759aecd3a..b5b196d86 100644 --- a/en/codes/go/chapter_divide_and_conquer/hanota.go +++ b/en/codes/go/chapter_divide_and_conquer/hanota.go @@ -6,26 +6,26 @@ package chapter_divide_and_conquer import "container/list" -/* Move a disk */ +/* Move one disk */ func move(src, tar *list.List) { - // Take out a disk from the top of src + // Take a disk from the top of src pan := src.Back() // Place the disk on top of tar tar.PushBack(pan.Value) - // Remove top disk from src + // Remove the disk from the top of src src.Remove(pan) } /* Solve the Tower of Hanoi problem f(i) */ func dfsHanota(i int, src, buf, tar *list.List) { - // If there is only one disk left in src, move it directly to tar + // If only one disk in this subproblem remains on src, move it directly to tar if i == 1 { move(src, tar) return } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfsHanota(i-1, src, tar, buf) - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar) // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfsHanota(i-1, buf, src, tar) diff --git a/en/codes/go/chapter_divide_and_conquer/hanota_test.go b/en/codes/go/chapter_divide_and_conquer/hanota_test.go index db68512de..25bedfb7a 100644 --- a/en/codes/go/chapter_divide_and_conquer/hanota_test.go +++ b/en/codes/go/chapter_divide_and_conquer/hanota_test.go @@ -13,14 +13,14 @@ import ( ) func TestHanota(t *testing.T) { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg A := list.New() for i := 5; i > 0; i-- { A.PushBack(i) } B := list.New() C := list.New() - fmt.Println("In initial state:") + fmt.Println("Initial state:") fmt.Print("A = ") PrintList(A) fmt.Print("B = ") @@ -30,7 +30,7 @@ func TestHanota(t *testing.T) { solveHanota(A, B, C) - fmt.Println("After disk movement is complete:") + fmt.Println("After moving the disks:") fmt.Print("A = ") PrintList(A) fmt.Print("B = ") diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_backtrack.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_backtrack.go index 998b968ca..fd1afdb53 100644 --- a/en/codes/go/chapter_dynamic_programming/climbing_stairs_backtrack.go +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_backtrack.go @@ -6,30 +6,30 @@ package chapter_dynamic_programming /* Backtracking */ func backtrack(choices []int, state, n int, res []int) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if state == n { res[0] = res[0] + 1 } - // Traverse all choices + // Iterate over all choices for _, choice := range choices { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if state+choice > n { continue } - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state+choice, n, res) // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ func climbingStairsBacktrack(n int) int { - // Can choose to climb up 1 or 2 stairs + // Choose to climb 1 or 2 steps choices := []int{1, 2} - // Start climbing from the 0-th stair + // Start climbing from step 0 state := 0 res := make([]int, 1) - // Use res[0] to record the solution count + // Use res[0] to record the number of ways res[0] = 0 backtrack(choices, state, n, res) return res[0] diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_constraint_dp.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_constraint_dp.go index e9d006962..9d8b97aa4 100644 --- a/en/codes/go/chapter_dynamic_programming/climbing_stairs_constraint_dp.go +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_constraint_dp.go @@ -4,19 +4,19 @@ package chapter_dynamic_programming -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ func climbingStairsConstraintDP(n int) int { if n == 1 || n == 2 { return 1 } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions dp := make([][3]int, n+1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1 dp[1][2] = 0 dp[2][1] = 0 dp[2][2] = 1 - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i := 3; i <= n; i++ { dp[i][1] = dp[i-1][2] dp[i][2] = dp[i-2][1] + dp[i-2][2] diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs.go index 31b5c3722..cfb700fb5 100644 --- a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs.go +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs.go @@ -6,7 +6,7 @@ package chapter_dynamic_programming /* Search */ func dfs(i int) int { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if i == 1 || i == 2 { return i } @@ -15,7 +15,7 @@ func dfs(i int) int { return count } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ func climbingStairsDFS(n int) int { return dfs(n) } diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs_mem.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs_mem.go index 8036389ac..b0cbf2bdf 100644 --- a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs_mem.go +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs_mem.go @@ -4,13 +4,13 @@ package chapter_dynamic_programming -/* Memoization search */ +/* Memoized search */ func dfsMem(i int, mem []int) int { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if i == 1 || i == 2 { return i } - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if mem[i] != -1 { return mem[i] } @@ -21,9 +21,9 @@ func dfsMem(i int, mem []int) int { return count } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ func climbingStairsDFSMem(n int) int { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded mem := make([]int, n+1) for i := range mem { mem[i] = -1 diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dp.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dp.go index 83d3f07df..216faca9a 100644 --- a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dp.go +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dp.go @@ -4,30 +4,30 @@ package chapter_dynamic_programming -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ func climbingStairsDP(n int) int { if n == 1 || n == 2 { return n } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions dp := make([]int, n+1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1 dp[2] = 2 - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i := 3; i <= n; i++ { dp[i] = dp[i-1] + dp[i-2] } return dp[n] } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ func climbingStairsDPComp(n int) int { if n == 1 || n == 2 { return n } a, b := 1, 2 - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i := 3; i <= n; i++ { a, b = b, a+b } diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_test.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_test.go index dd42e83ae..713776495 100644 --- a/en/codes/go/chapter_dynamic_programming/climbing_stairs_test.go +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_test.go @@ -12,46 +12,46 @@ import ( func TestClimbingStairsBacktrack(t *testing.T) { n := 9 res := climbingStairsBacktrack(n) - fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) + fmt.Printf("Number of ways to climb %d steps: %d\n", n, res) } func TestClimbingStairsDFS(t *testing.T) { n := 9 res := climbingStairsDFS(n) - fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) + fmt.Printf("Number of ways to climb %d steps: %d\n", n, res) } func TestClimbingStairsDFSMem(t *testing.T) { n := 9 res := climbingStairsDFSMem(n) - fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) + fmt.Printf("Number of ways to climb %d steps: %d\n", n, res) } func TestClimbingStairsDP(t *testing.T) { n := 9 res := climbingStairsDP(n) - fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) + fmt.Printf("Number of ways to climb %d steps: %d\n", n, res) } func TestClimbingStairsDPComp(t *testing.T) { n := 9 res := climbingStairsDPComp(n) - fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) + fmt.Printf("Number of ways to climb %d steps: %d\n", n, res) } func TestClimbingStairsConstraintDP(t *testing.T) { n := 9 res := climbingStairsConstraintDP(n) - fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) + fmt.Printf("Number of ways to climb %d steps: %d\n", n, res) } func TestMinCostClimbingStairsDPComp(t *testing.T) { cost := []int{0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1} - fmt.Printf("Input stair cost list is %v\n", cost) + fmt.Printf("Input stair costs: %v\n", cost) res := minCostClimbingStairsDP(cost) - fmt.Printf("Minimum cost to climb stairs is %d\n", res) + fmt.Printf("Minimum cost to reach the top: %d\n", res) res = minCostClimbingStairsDPComp(cost) - fmt.Printf("Minimum cost to climb stairs is %d\n", res) + fmt.Printf("Minimum cost to reach the top: %d\n", res) } diff --git a/en/codes/go/chapter_dynamic_programming/coin_change.go b/en/codes/go/chapter_dynamic_programming/coin_change.go index 73d2c42ca..e15a9fe02 100644 --- a/en/codes/go/chapter_dynamic_programming/coin_change.go +++ b/en/codes/go/chapter_dynamic_programming/coin_change.go @@ -6,27 +6,27 @@ package chapter_dynamic_programming import "math" -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ func coinChangeDP(coins []int, amt int) int { n := len(coins) max := amt + 1 - // Initialize dp table + // Initialize the dp table dp := make([][]int, n+1) for i := 0; i <= n; i++ { dp[i] = make([]int, amt+1) } - // State transition: first row and first column + // State transitions: first row and first column for a := 1; a <= amt; a++ { dp[0][a] = max } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i := 1; i <= n; i++ { for a := 1; a <= amt; a++ { if coins[i-1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[i][a] = dp[i-1][a] } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = int(math.Min(float64(dp[i-1][a]), float64(dp[i][a-coins[i-1]]+1))) } } @@ -37,24 +37,24 @@ func coinChangeDP(coins []int, amt int) int { return -1 } -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ func coinChangeDPComp(coins []int, amt int) int { n := len(coins) max := amt + 1 - // Initialize dp table + // Initialize the dp table dp := make([]int, amt+1) for i := 1; i <= amt; i++ { dp[i] = max } - // State transition + // State transitions for i := 1; i <= n; i++ { - // Traverse in forward order + // Traverse in increasing order for a := 1; a <= amt; a++ { if coins[i-1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[a] = dp[a] } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = int(math.Min(float64(dp[a]), float64(dp[a-coins[i-1]]+1))) } } diff --git a/en/codes/go/chapter_dynamic_programming/coin_change_ii.go b/en/codes/go/chapter_dynamic_programming/coin_change_ii.go index 89c4a246a..17881b68d 100644 --- a/en/codes/go/chapter_dynamic_programming/coin_change_ii.go +++ b/en/codes/go/chapter_dynamic_programming/coin_change_ii.go @@ -4,26 +4,26 @@ package chapter_dynamic_programming -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ func coinChangeIIDP(coins []int, amt int) int { n := len(coins) - // Initialize dp table + // Initialize the dp table dp := make([][]int, n+1) for i := 0; i <= n; i++ { dp[i] = make([]int, amt+1) } - // Initialize first column + // Initialize the first column for i := 0; i <= n; i++ { dp[i][0] = 1 } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i := 1; i <= n; i++ { for a := 1; a <= amt; a++ { if coins[i-1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[i][a] = dp[i-1][a] } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i-1][a] + dp[i][a-coins[i-1]] } } @@ -31,21 +31,21 @@ func coinChangeIIDP(coins []int, amt int) int { return dp[n][amt] } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ func coinChangeIIDPComp(coins []int, amt int) int { n := len(coins) - // Initialize dp table + // Initialize the dp table dp := make([]int, amt+1) dp[0] = 1 - // State transition + // State transitions for i := 1; i <= n; i++ { - // Traverse in forward order + // Traverse in increasing order for a := 1; a <= amt; a++ { if coins[i-1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[a] = dp[a] } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a-coins[i-1]] } } diff --git a/en/codes/go/chapter_dynamic_programming/coin_change_test.go b/en/codes/go/chapter_dynamic_programming/coin_change_test.go index 1f9de913a..294551555 100644 --- a/en/codes/go/chapter_dynamic_programming/coin_change_test.go +++ b/en/codes/go/chapter_dynamic_programming/coin_change_test.go @@ -15,9 +15,9 @@ func TestCoinChange(t *testing.T) { // Dynamic programming res := coinChangeDP(coins, amt) - fmt.Printf("Minimum number of coins needed to make target amount is %d\n", res) + fmt.Printf("Minimum number of coins needed to make the target amount: %d\n", res) // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt) - fmt.Printf("Minimum number of coins needed to make target amount is %d\n", res) + fmt.Printf("Minimum number of coins needed to make the target amount: %d\n", res) } diff --git a/en/codes/go/chapter_dynamic_programming/edit_distance.go b/en/codes/go/chapter_dynamic_programming/edit_distance.go index b4b93434a..c3c620b11 100644 --- a/en/codes/go/chapter_dynamic_programming/edit_distance.go +++ b/en/codes/go/chapter_dynamic_programming/edit_distance.go @@ -4,64 +4,64 @@ package chapter_dynamic_programming -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ func editDistanceDFS(s string, t string, i int, j int) int { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if i == 0 && j == 0 { return 0 } - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if i == 0 { return j } - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if j == 0 { return i } - // If two characters are equal, skip both characters + // If the two bytes are equal, skip both (each byte is one character for ASCII input) if s[i-1] == t[j-1] { return editDistanceDFS(s, t, i-1, j-1) } - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 insert := editDistanceDFS(s, t, i, j-1) deleted := editDistanceDFS(s, t, i-1, j) replace := editDistanceDFS(s, t, i-1, j-1) - // Return minimum edit steps + // Return the minimum number of edits return MinInt(MinInt(insert, deleted), replace) + 1 } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ func editDistanceDFSMem(s string, t string, mem [][]int, i int, j int) int { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if i == 0 && j == 0 { return 0 } - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if i == 0 { return j } - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if j == 0 { return i } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][j] != -1 { return mem[i][j] } - // If two characters are equal, skip both characters + // If the two bytes are equal, skip both (each byte is one character for ASCII input) if s[i-1] == t[j-1] { return editDistanceDFSMem(s, t, mem, i-1, j-1) } - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 insert := editDistanceDFSMem(s, t, mem, i, j-1) deleted := editDistanceDFSMem(s, t, mem, i-1, j) replace := editDistanceDFSMem(s, t, mem, i-1, j-1) - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = MinInt(MinInt(insert, deleted), replace) + 1 return mem[i][j] } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ func editDistanceDP(s string, t string) int { n := len(s) m := len(t) @@ -69,21 +69,21 @@ func editDistanceDP(s string, t string) int { for i := 0; i <= n; i++ { dp[i] = make([]int, m+1) } - // State transition: first row and first column + // State transitions: first row and first column for i := 1; i <= n; i++ { dp[i][0] = i } for j := 1; j <= m; j++ { dp[0][j] = j } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i := 1; i <= n; i++ { for j := 1; j <= m; j++ { if s[i-1] == t[j-1] { - // If two characters are equal, skip both characters + // If the two bytes are equal, skip both (each byte is one character for ASCII input) dp[i][j] = dp[i-1][j-1] } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = MinInt(MinInt(dp[i][j-1], dp[i-1][j]), dp[i-1][j-1]) + 1 } } @@ -91,31 +91,31 @@ func editDistanceDP(s string, t string) int { return dp[n][m] } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ func editDistanceDPComp(s string, t string) int { n := len(s) m := len(t) dp := make([]int, m+1) - // State transition: first row + // State transitions: first row for j := 1; j <= m; j++ { dp[j] = j } - // State transition: rest of the rows + // State transitions: remaining rows for i := 1; i <= n; i++ { - // State transition: first column + // State transitions: first column leftUp := dp[0] // Temporarily store dp[i-1, j-1] dp[0] = i - // State transition: rest of the columns + // State transitions: remaining columns for j := 1; j <= m; j++ { temp := dp[j] if s[i-1] == t[j-1] { - // If two characters are equal, skip both characters + // If the two bytes are equal, skip both (each byte is one character for ASCII input) dp[j] = leftUp } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = MinInt(MinInt(dp[j-1], dp[j]), leftUp) + 1 } - leftUp = temp // Update for next round's dp[i-1, j-1] + leftUp = temp // Update to dp[i-1, j-1] for the next iteration } } return dp[m] diff --git a/en/codes/go/chapter_dynamic_programming/edit_distance_test.go b/en/codes/go/chapter_dynamic_programming/edit_distance_test.go index bf403cb37..e9784773f 100644 --- a/en/codes/go/chapter_dynamic_programming/edit_distance_test.go +++ b/en/codes/go/chapter_dynamic_programming/edit_distance_test.go @@ -17,9 +17,9 @@ func TestEditDistanceDFS(test *testing.T) { // Brute-force search res := editDistanceDFS(s, t, n, m) - fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) + fmt.Printf("Changing %s to %s requires at least %d edits\n", s, t, res) - // Memoization search + // Memoized search mem := make([][]int, n+1) for i := 0; i <= n; i++ { mem[i] = make([]int, m+1) @@ -28,13 +28,13 @@ func TestEditDistanceDFS(test *testing.T) { } } res = editDistanceDFSMem(s, t, mem, n, m) - fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) + fmt.Printf("Changing %s to %s requires at least %d edits\n", s, t, res) // Dynamic programming res = editDistanceDP(s, t) - fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) + fmt.Printf("Changing %s to %s requires at least %d edits\n", s, t, res) // Space-optimized dynamic programming res = editDistanceDPComp(s, t) - fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) + fmt.Printf("Changing %s to %s requires at least %d edits\n", s, t, res) } diff --git a/en/codes/go/chapter_dynamic_programming/knapsack.go b/en/codes/go/chapter_dynamic_programming/knapsack.go index c49337c36..abd985418 100644 --- a/en/codes/go/chapter_dynamic_programming/knapsack.go +++ b/en/codes/go/chapter_dynamic_programming/knapsack.go @@ -6,61 +6,61 @@ package chapter_dynamic_programming import "math" -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ func knapsackDFS(wgt, val []int, i, c int) int { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if i == 0 || c == 0 { return 0 } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if wgt[i-1] > c { return knapsackDFS(wgt, val, i-1, c) } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i no := knapsackDFS(wgt, val, i-1, c) yes := knapsackDFS(wgt, val, i-1, c-wgt[i-1]) + val[i-1] - // Return the larger value of the two options + // Return the larger value from the two choices return int(math.Max(float64(no), float64(yes))) } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ func knapsackDFSMem(wgt, val []int, mem [][]int, i, c int) int { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if i == 0 || c == 0 { return 0 } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][c] != -1 { return mem[i][c] } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if wgt[i-1] > c { return knapsackDFSMem(wgt, val, mem, i-1, c) } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i no := knapsackDFSMem(wgt, val, mem, i-1, c) yes := knapsackDFSMem(wgt, val, mem, i-1, c-wgt[i-1]) + val[i-1] - // Return the larger value of the two options + // Return the larger value from the two choices mem[i][c] = int(math.Max(float64(no), float64(yes))) return mem[i][c] } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ func knapsackDP(wgt, val []int, cap int) int { n := len(wgt) - // Initialize dp table + // Initialize the dp table dp := make([][]int, n+1) for i := 0; i <= n; i++ { dp[i] = make([]int, cap+1) } - // State transition + // State transitions for i := 1; i <= n; i++ { for c := 1; c <= cap; c++ { if wgt[i-1] > c { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i-1][c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = int(math.Max(float64(dp[i-1][c]), float64(dp[i-1][c-wgt[i-1]]+val[i-1]))) } } @@ -68,17 +68,17 @@ func knapsackDP(wgt, val []int, cap int) int { return dp[n][cap] } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ func knapsackDPComp(wgt, val []int, cap int) int { n := len(wgt) - // Initialize dp table + // Initialize the dp table dp := make([]int, cap+1) - // State transition + // State transitions for i := 1; i <= n; i++ { - // Traverse in reverse order + // Traverse in decreasing order for c := cap; c >= 1; c-- { if wgt[i-1] <= c { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = int(math.Max(float64(dp[c]), float64(dp[c-wgt[i-1]]+val[i-1]))) } } diff --git a/en/codes/go/chapter_dynamic_programming/knapsack_test.go b/en/codes/go/chapter_dynamic_programming/knapsack_test.go index dcff496e8..df5f38498 100644 --- a/en/codes/go/chapter_dynamic_programming/knapsack_test.go +++ b/en/codes/go/chapter_dynamic_programming/knapsack_test.go @@ -17,9 +17,9 @@ func TestKnapsack(t *testing.T) { // Brute-force search res := knapsackDFS(wgt, val, n, c) - fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + fmt.Printf("Maximum total item value within the knapsack capacity: %d\n", res) - // Memoization search + // Memoized search mem := make([][]int, n+1) for i := 0; i <= n; i++ { mem[i] = make([]int, c+1) @@ -28,15 +28,15 @@ func TestKnapsack(t *testing.T) { } } res = knapsackDFSMem(wgt, val, mem, n, c) - fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + fmt.Printf("Maximum total item value within the knapsack capacity: %d\n", res) // Dynamic programming res = knapsackDP(wgt, val, c) - fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + fmt.Printf("Maximum total item value within the knapsack capacity: %d\n", res) // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, c) - fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + fmt.Printf("Maximum total item value within the knapsack capacity: %d\n", res) } func TestUnboundedKnapsack(t *testing.T) { @@ -46,9 +46,9 @@ func TestUnboundedKnapsack(t *testing.T) { // Dynamic programming res := unboundedKnapsackDP(wgt, val, c) - fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + fmt.Printf("Maximum total item value within the knapsack capacity: %d\n", res) // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, c) - fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + fmt.Printf("Maximum total item value within the knapsack capacity: %d\n", res) } diff --git a/en/codes/go/chapter_dynamic_programming/min_cost_climbing_stairs_dp.go b/en/codes/go/chapter_dynamic_programming/min_cost_climbing_stairs_dp.go index b35e58796..b7f285c78 100644 --- a/en/codes/go/chapter_dynamic_programming/min_cost_climbing_stairs_dp.go +++ b/en/codes/go/chapter_dynamic_programming/min_cost_climbing_stairs_dp.go @@ -4,7 +4,7 @@ package chapter_dynamic_programming -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ func minCostClimbingStairsDP(cost []int) int { n := len(cost) - 1 if n == 1 || n == 2 { @@ -16,19 +16,19 @@ func minCostClimbingStairsDP(cost []int) int { } return b } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions dp := make([]int, n+1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1] dp[2] = cost[2] - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i := 3; i <= n; i++ { dp[i] = min(dp[i-1], dp[i-2]) + cost[i] } return dp[n] } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ func minCostClimbingStairsDPComp(cost []int) int { n := len(cost) - 1 if n == 1 || n == 2 { @@ -40,9 +40,9 @@ func minCostClimbingStairsDPComp(cost []int) int { } return b } - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems a, b := cost[1], cost[2] - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i := 3; i <= n; i++ { tmp := b b = min(a, tmp) + cost[i] diff --git a/en/codes/go/chapter_dynamic_programming/min_path_sum.go b/en/codes/go/chapter_dynamic_programming/min_path_sum.go index a09008eef..75b559787 100644 --- a/en/codes/go/chapter_dynamic_programming/min_path_sum.go +++ b/en/codes/go/chapter_dynamic_programming/min_path_sum.go @@ -6,63 +6,63 @@ package chapter_dynamic_programming import "math" -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ func minPathSumDFS(grid [][]int, i, j int) int { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if i == 0 && j == 0 { return grid[0][0] } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return math.MaxInt to represent an infinite cost if i < 0 || j < 0 { return math.MaxInt } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) up := minPathSumDFS(grid, i-1, j) left := minPathSumDFS(grid, i, j-1) - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return int(math.Min(float64(left), float64(up))) + grid[i][j] } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ func minPathSumDFSMem(grid, mem [][]int, i, j int) int { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if i == 0 && j == 0 { return grid[0][0] } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return math.MaxInt to represent an infinite cost if i < 0 || j < 0 { return math.MaxInt } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][j] != -1 { return mem[i][j] } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above up := minPathSumDFSMem(grid, mem, i-1, j) left := minPathSumDFSMem(grid, mem, i, j-1) - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = int(math.Min(float64(left), float64(up))) + grid[i][j] return mem[i][j] } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ func minPathSumDP(grid [][]int) int { n, m := len(grid), len(grid[0]) - // Initialize dp table + // Initialize the dp table dp := make([][]int, n) for i := 0; i < n; i++ { dp[i] = make([]int, m) } dp[0][0] = grid[0][0] - // State transition: first row + // State transitions: first row for j := 1; j < m; j++ { dp[0][j] = dp[0][j-1] + grid[0][j] } - // State transition: first column + // State transitions: first column for i := 1; i < n; i++ { dp[i][0] = dp[i-1][0] + grid[i][0] } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i := 1; i < n; i++ { for j := 1; j < m; j++ { dp[i][j] = int(math.Min(float64(dp[i][j-1]), float64(dp[i-1][j]))) + grid[i][j] @@ -71,21 +71,21 @@ func minPathSumDP(grid [][]int) int { return dp[n-1][m-1] } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ func minPathSumDPComp(grid [][]int) int { n, m := len(grid), len(grid[0]) - // Initialize dp table + // Initialize the dp table dp := make([]int, m) - // State transition: first row + // State transitions: first row dp[0] = grid[0][0] for j := 1; j < m; j++ { dp[j] = dp[j-1] + grid[0][j] } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i := 1; i < n; i++ { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0] - // State transition: rest of the columns + // State transitions: remaining columns for j := 1; j < m; j++ { dp[j] = int(math.Min(float64(dp[j-1]), float64(dp[j]))) + grid[i][j] } diff --git a/en/codes/go/chapter_dynamic_programming/min_path_sum_test.go b/en/codes/go/chapter_dynamic_programming/min_path_sum_test.go index 2c9e4c0a0..ba394bf9f 100644 --- a/en/codes/go/chapter_dynamic_programming/min_path_sum_test.go +++ b/en/codes/go/chapter_dynamic_programming/min_path_sum_test.go @@ -20,9 +20,9 @@ func TestMinPathSum(t *testing.T) { // Brute-force search res := minPathSumDFS(grid, n-1, m-1) - fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) + fmt.Printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res) - // Memoization search + // Memoized search mem := make([][]int, n) for i := 0; i < n; i++ { mem[i] = make([]int, m) @@ -31,13 +31,13 @@ func TestMinPathSum(t *testing.T) { } } res = minPathSumDFSMem(grid, mem, n-1, m-1) - fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) + fmt.Printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res) // Dynamic programming res = minPathSumDP(grid) - fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) + fmt.Printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res) // Space-optimized dynamic programming res = minPathSumDPComp(grid) - fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) + fmt.Printf("Minimum path sum from the top-left to the bottom-right corner: %d\n", res) } diff --git a/en/codes/go/chapter_dynamic_programming/unbounded_knapsack.go b/en/codes/go/chapter_dynamic_programming/unbounded_knapsack.go index a717eec7f..8fcf36ecf 100644 --- a/en/codes/go/chapter_dynamic_programming/unbounded_knapsack.go +++ b/en/codes/go/chapter_dynamic_programming/unbounded_knapsack.go @@ -6,22 +6,22 @@ package chapter_dynamic_programming import "math" -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ func unboundedKnapsackDP(wgt, val []int, cap int) int { n := len(wgt) - // Initialize dp table + // Initialize the dp table dp := make([][]int, n+1) for i := 0; i <= n; i++ { dp[i] = make([]int, cap+1) } - // State transition + // State transitions for i := 1; i <= n; i++ { for c := 1; c <= cap; c++ { if wgt[i-1] > c { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i-1][c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = int(math.Max(float64(dp[i-1][c]), float64(dp[i][c-wgt[i-1]]+val[i-1]))) } } @@ -29,19 +29,19 @@ func unboundedKnapsackDP(wgt, val []int, cap int) int { return dp[n][cap] } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ func unboundedKnapsackDPComp(wgt, val []int, cap int) int { n := len(wgt) - // Initialize dp table + // Initialize the dp table dp := make([]int, cap+1) - // State transition + // State transitions for i := 1; i <= n; i++ { for c := 1; c <= cap; c++ { if wgt[i-1] > c { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = int(math.Max(float64(dp[c]), float64(dp[c-wgt[i-1]]+val[i-1]))) } } diff --git a/en/codes/go/chapter_graph/graph_adjacency_list.go b/en/codes/go/chapter_graph/graph_adjacency_list.go index e1339424b..830ab3989 100644 --- a/en/codes/go/chapter_graph/graph_adjacency_list.go +++ b/en/codes/go/chapter_graph/graph_adjacency_list.go @@ -12,9 +12,9 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -/* Undirected graph class based on adjacency list */ +/* Undirected graph type implemented with an adjacency list */ type graphAdjList struct { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors adjList map[Vertex][]Vertex } @@ -37,19 +37,19 @@ func (g *graphAdjList) size() int { return len(g.adjList) } -/* Add edge */ +/* Add an edge */ func (g *graphAdjList) addEdge(vet1 Vertex, vet2 Vertex) { _, ok1 := g.adjList[vet1] _, ok2 := g.adjList[vet2] if !ok1 || !ok2 || vet1 == vet2 { panic("error") } - // Add edge vet1 - vet2, add anonymous struct{}, + // Add edge vet1 - vet2 by appending each vertex to the other vertex's neighbor list g.adjList[vet1] = append(g.adjList[vet1], vet2) g.adjList[vet2] = append(g.adjList[vet2], vet1) } -/* Remove edge */ +/* Remove an edge */ func (g *graphAdjList) removeEdge(vet1 Vertex, vet2 Vertex) { _, ok1 := g.adjList[vet1] _, ok2 := g.adjList[vet2] @@ -61,31 +61,31 @@ func (g *graphAdjList) removeEdge(vet1 Vertex, vet2 Vertex) { g.adjList[vet2] = DeleteSliceElms(g.adjList[vet2], vet1) } -/* Add vertex */ +/* Add a vertex */ func (g *graphAdjList) addVertex(vet Vertex) { _, ok := g.adjList[vet] if ok { return } - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list g.adjList[vet] = make([]Vertex, 0) } -/* Remove vertex */ +/* Remove a vertex */ func (g *graphAdjList) removeVertex(vet Vertex) { _, ok := g.adjList[vet] if !ok { panic("error") } - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list delete(g.adjList, vet) - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for v, list := range g.adjList { g.adjList[v] = DeleteSliceElms(list, vet) } } -/* Print adjacency list */ +/* Print the adjacency list */ func (g *graphAdjList) print() { var builder strings.Builder fmt.Printf("Adjacency list = \n") diff --git a/en/codes/go/chapter_graph/graph_adjacency_list_test.go b/en/codes/go/chapter_graph/graph_adjacency_list_test.go index 2efef7efb..bae86d96d 100644 --- a/en/codes/go/chapter_graph/graph_adjacency_list_test.go +++ b/en/codes/go/chapter_graph/graph_adjacency_list_test.go @@ -12,34 +12,34 @@ import ( ) func TestGraphAdjList(t *testing.T) { - /* Add edge */ + /* Initialize the undirected graph */ v := ValsToVets([]int{1, 3, 2, 5, 4}) edges := [][]Vertex{{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}} graph := newGraphAdjList(edges) - fmt.Println("After initialization, graph is:") + fmt.Println("Graph after initialization:") graph.print() - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.addEdge(v[0], v[2]) - fmt.Println("\nAfter adding edge 1-2, graph is") + fmt.Println("\nGraph after adding edge 1-2:") graph.print() - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.removeEdge(v[0], v[1]) - fmt.Println("\nAfter removing edge 1-3, graph is") + fmt.Println("\nGraph after removing edge 1-3:") graph.print() - /* Add vertex */ + /* Add a vertex */ v5 := NewVertex(6) graph.addVertex(v5) - fmt.Println("\nAfter adding vertex 6, graph is") + fmt.Println("\nGraph after adding vertex 6:") graph.print() - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.removeVertex(v[1]) - fmt.Println("\nAfter removing vertex 3, graph is") + fmt.Println("\nGraph after removing vertex 3:") graph.print() } diff --git a/en/codes/go/chapter_graph/graph_adjacency_matrix.go b/en/codes/go/chapter_graph/graph_adjacency_matrix.go index 219bd7b70..78caf0c7d 100644 --- a/en/codes/go/chapter_graph/graph_adjacency_matrix.go +++ b/en/codes/go/chapter_graph/graph_adjacency_matrix.go @@ -6,29 +6,29 @@ package chapter_graph import "fmt" -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph type implemented with an adjacency matrix */ type graphAdjMat struct { - // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + // Vertex list: elements are vertex values, and indices are vertex indices vertices []int - // Adjacency matrix, where the row and column indices correspond to the "vertex index" + // Adjacency matrix: row and column indices correspond to vertex indices adjMat [][]int } /* Constructor */ func newGraphAdjMat(vertices []int, edges [][]int) *graphAdjMat { - // Add vertex + // Allocate the adjacency matrix for all vertices n := len(vertices) adjMat := make([][]int, n) for i := range adjMat { adjMat[i] = make([]int, n) } - // Initialize graph + // Initialize the graph g := &graphAdjMat{ vertices: vertices, adjMat: adjMat, } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add all edges + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for i := range edges { g.addEdge(edges[i][0], edges[i][1]) } @@ -40,10 +40,10 @@ func (g *graphAdjMat) size() int { return len(g.vertices) } -/* Add vertex */ +/* Add a vertex */ func (g *graphAdjMat) addVertex(val int) { n := g.size() - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list g.vertices = append(g.vertices, val) // Add a row to the adjacency matrix newRow := make([]int, n) @@ -54,45 +54,45 @@ func (g *graphAdjMat) addVertex(val int) { } } -/* Remove vertex */ +/* Remove a vertex */ func (g *graphAdjMat) removeVertex(index int) { if index >= g.size() { return } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list g.vertices = append(g.vertices[:index], g.vertices[index+1:]...) - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix g.adjMat = append(g.adjMat[:index], g.adjMat[index+1:]...) - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for i := range g.adjMat { g.adjMat[i] = append(g.adjMat[i][:index], g.adjMat[i][index+1:]...) } } -/* Add edge */ -// Parameters i, j correspond to the vertices element indices +/* Add an edge */ +// Parameters i and j are element indices in vertices func (g *graphAdjMat) addEdge(i, j int) { - // Handle index out of bounds and equality + // Check for out-of-bounds or equal indices (the constructed error is not returned) if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { - fmt.Errorf("%s", "Index Out Of Bounds Exception") + fmt.Errorf("%s", "Index out of bounds") } - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal g.adjMat[i][j] = 1 g.adjMat[j][i] = 1 } -/* Remove edge */ -// Parameters i, j correspond to the vertices element indices +/* Remove an edge */ +// Parameters i and j are element indices in vertices func (g *graphAdjMat) removeEdge(i, j int) { - // Handle index out of bounds and equality + // Check for out-of-bounds or equal indices (the constructed error is not returned) if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { - fmt.Errorf("%s", "Index Out Of Bounds Exception") + fmt.Errorf("%s", "Index out of bounds") } g.adjMat[i][j] = 0 g.adjMat[j][i] = 0 } -/* Print adjacency matrix */ +/* Print the adjacency matrix */ func (g *graphAdjMat) print() { fmt.Printf("\tVertex list = %v\n", g.vertices) fmt.Printf("\tAdjacency matrix = \n") diff --git a/en/codes/go/chapter_graph/graph_adjacency_matrix_test.go b/en/codes/go/chapter_graph/graph_adjacency_matrix_test.go index b9ce439f1..1f58280c8 100644 --- a/en/codes/go/chapter_graph/graph_adjacency_matrix_test.go +++ b/en/codes/go/chapter_graph/graph_adjacency_matrix_test.go @@ -10,34 +10,34 @@ import ( ) func TestGraphAdjMat(t *testing.T) { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices vertices := []int{1, 3, 2, 5, 4} edges := [][]int{{0, 1}, {1, 2}, {2, 3}, {0, 3}, {2, 4}, {3, 4}} graph := newGraphAdjMat(vertices, edges) - fmt.Println("After initialization, graph is:") + fmt.Println("Graph after initialization:") graph.print() - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(0, 2) - fmt.Println("After adding edge 1-2, graph is") + fmt.Println("Graph after adding edge 1-2:") graph.print() - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(0, 1) - fmt.Println("After removing edge 1-3, graph is") + fmt.Println("Graph after removing edge 1-3:") graph.print() - /* Add vertex */ + /* Add a vertex */ graph.addVertex(6) - fmt.Println("After adding vertex 6, graph is") + fmt.Println("Graph after adding vertex 6:") graph.print() - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(1) - fmt.Println("After removing vertex 3, graph is") + fmt.Println("Graph after removing vertex 3:") graph.print() } diff --git a/en/codes/go/chapter_graph/graph_bfs.go b/en/codes/go/chapter_graph/graph_bfs.go index 41d4b771c..5d6befd8a 100644 --- a/en/codes/go/chapter_graph/graph_bfs.go +++ b/en/codes/go/chapter_graph/graph_bfs.go @@ -9,33 +9,33 @@ import ( ) /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex func graphBFS(g *graphAdjList, startVet Vertex) []Vertex { // Vertex traversal sequence res := make([]Vertex, 0) - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices visited := make(map[Vertex]struct{}) visited[startVet] = struct{}{} - // Queue used to implement BFS, using slice to simulate queue + // Use a queue for BFS, represented here by a slice queue := make([]Vertex, 0) queue = append(queue, startVet) - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited for len(queue) > 0 { // Dequeue the front vertex vet := queue[0] queue = queue[1:] - // Record visited vertex + // Record the visited vertex res = append(res, vet) - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for _, adjVet := range g.adjList[vet] { _, isExist := visited[adjVet] - // Only enqueue unvisited vertices + // Enqueue only unvisited vertices if !isExist { queue = append(queue, adjVet) visited[adjVet] = struct{}{} } } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res } diff --git a/en/codes/go/chapter_graph/graph_bfs_test.go b/en/codes/go/chapter_graph/graph_bfs_test.go index bdcfc7fe5..f77058f4b 100644 --- a/en/codes/go/chapter_graph/graph_bfs_test.go +++ b/en/codes/go/chapter_graph/graph_bfs_test.go @@ -12,18 +12,18 @@ import ( ) func TestGraphBFS(t *testing.T) { - /* Add edge */ + /* Initialize the undirected graph */ vets := ValsToVets([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) edges := [][]Vertex{ {vets[0], vets[1]}, {vets[0], vets[3]}, {vets[1], vets[2]}, {vets[1], vets[4]}, {vets[2], vets[5]}, {vets[3], vets[4]}, {vets[3], vets[6]}, {vets[4], vets[5]}, {vets[4], vets[7]}, {vets[5], vets[8]}, {vets[6], vets[7]}, {vets[7], vets[8]}} graph := newGraphAdjList(edges) - fmt.Println("After initialization, graph is:") + fmt.Println("Graph after initialization:") graph.print() /* Breadth-first traversal */ res := graphBFS(graph, vets[0]) - fmt.Println("Breadth-first traversal (BFS) vertex sequence is:") + fmt.Println("Vertex sequence from breadth-first traversal (BFS):") PrintSlice(VetsToVals(res)) } diff --git a/en/codes/go/chapter_graph/graph_dfs.go b/en/codes/go/chapter_graph/graph_dfs.go index 60658f26d..02e8f0788 100644 --- a/en/codes/go/chapter_graph/graph_dfs.go +++ b/en/codes/go/chapter_graph/graph_dfs.go @@ -10,13 +10,13 @@ import ( /* Depth-first traversal helper function */ func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex) { - // append operation returns a new reference, must reassign original reference to new slice's reference + // append returns an updated slice, which must be assigned back through the original slice pointer *res = append(*res, vet) visited[vet] = struct{}{} - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for _, adjVet := range g.adjList[vet] { _, isExist := visited[adjVet] - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex if !isExist { dfs(g, visited, res, adjVet) } @@ -24,13 +24,13 @@ func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex func graphDFS(g *graphAdjList, startVet Vertex) []Vertex { // Vertex traversal sequence res := make([]Vertex, 0) - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices visited := make(map[Vertex]struct{}) dfs(g, visited, &res, startVet) - // Return vertex traversal sequence + // Return the vertex traversal sequence return res } diff --git a/en/codes/go/chapter_graph/graph_dfs_test.go b/en/codes/go/chapter_graph/graph_dfs_test.go index 694c24c89..e2e2b129e 100644 --- a/en/codes/go/chapter_graph/graph_dfs_test.go +++ b/en/codes/go/chapter_graph/graph_dfs_test.go @@ -12,17 +12,17 @@ import ( ) func TestGraphDFS(t *testing.T) { - /* Add edge */ + /* Initialize the undirected graph */ vets := ValsToVets([]int{0, 1, 2, 3, 4, 5, 6}) edges := [][]Vertex{ {vets[0], vets[1]}, {vets[0], vets[3]}, {vets[1], vets[2]}, {vets[2], vets[5]}, {vets[4], vets[5]}, {vets[5], vets[6]}} graph := newGraphAdjList(edges) - fmt.Println("After initialization, graph is:") + fmt.Println("Graph after initialization:") graph.print() /* Depth-first traversal */ res := graphDFS(graph, vets[0]) - fmt.Println("Depth-first traversal (DFS) vertex sequence is:") + fmt.Println("Vertex sequence from depth-first traversal (DFS):") PrintSlice(VetsToVals(res)) } diff --git a/en/codes/go/chapter_greedy/coin_change_greedy.go b/en/codes/go/chapter_greedy/coin_change_greedy.go index 635c08e1a..8492c46db 100644 --- a/en/codes/go/chapter_greedy/coin_change_greedy.go +++ b/en/codes/go/chapter_greedy/coin_change_greedy.go @@ -4,14 +4,14 @@ package chapter_greedy -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ func coinChangeGreedy(coins []int, amt int) int { - // Assume coins list is sorted + // Assume the coins list is sorted in ascending order i := len(coins) - 1 count := 0 - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains for amt > 0 { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount for i > 0 && coins[i] > amt { i-- } @@ -19,7 +19,7 @@ func coinChangeGreedy(coins []int, amt int) int { amt -= coins[i] count++ } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found if amt != 0 { return -1 } diff --git a/en/codes/go/chapter_greedy/coin_change_greedy_test.go b/en/codes/go/chapter_greedy/coin_change_greedy_test.go index be4f5272f..3b1e93f5c 100644 --- a/en/codes/go/chapter_greedy/coin_change_greedy_test.go +++ b/en/codes/go/chapter_greedy/coin_change_greedy_test.go @@ -10,26 +10,26 @@ import ( ) func TestCoinChangeGreedy(t *testing.T) { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution coins := []int{1, 5, 10, 20, 50, 100} amt := 186 res := coinChangeGreedy(coins, amt) fmt.Printf("coins = %v, amt = %d\n", coins, amt) - fmt.Printf("Minimum number of coins needed to make %d is %d\n", amt, res) + fmt.Printf("For amount %d, the greedy coin count is %d\n", amt, res) - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = []int{1, 20, 50} amt = 60 res = coinChangeGreedy(coins, amt) fmt.Printf("coins = %v, amt = %d\n", coins, amt) - fmt.Printf("Minimum number of coins needed to make %d is %d\n", amt, res) - fmt.Println("Actually the minimum number needed is 3, i.e., 20 + 20 + 20") + fmt.Printf("For amount %d, the greedy coin count is %d\n", amt, res) + fmt.Println("The actual minimum is 3 coins: 20 + 20 + 20") - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = []int{1, 49, 50} amt = 98 res = coinChangeGreedy(coins, amt) fmt.Printf("coins = %v, amt = %d\n", coins, amt) - fmt.Printf("Minimum number of coins needed to make %d is %d\n", amt, res) - fmt.Println("Actually the minimum number needed is 2, i.e., 49 + 49") + fmt.Printf("For amount %d, the greedy coin count is %d\n", amt, res) + fmt.Println("The actual minimum is 2 coins: 49 + 49") } diff --git a/en/codes/go/chapter_greedy/fractional_knapsack.go b/en/codes/go/chapter_greedy/fractional_knapsack.go index f54efaf9a..53ddc44c4 100644 --- a/en/codes/go/chapter_greedy/fractional_knapsack.go +++ b/en/codes/go/chapter_greedy/fractional_knapsack.go @@ -12,28 +12,28 @@ type Item struct { v int // Item value } -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ func fractionalKnapsack(wgt []int, val []int, cap int) float64 { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value items := make([]Item, len(wgt)) for i := 0; i < len(wgt); i++ { items[i] = Item{wgt[i], val[i]} } - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order sort.Slice(items, func(i, j int) bool { return float64(items[i].v)/float64(items[i].w) > float64(items[j].v)/float64(items[j].w) }) - // Loop for greedy selection + // Repeatedly make greedy choices res := 0.0 for _, item := range items { if item.w <= cap { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += float64(item.v) cap -= item.w } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += float64(item.v) / float64(item.w) * float64(cap) - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break } } diff --git a/en/codes/go/chapter_greedy/fractional_knapsack_test.go b/en/codes/go/chapter_greedy/fractional_knapsack_test.go index 3b2d80747..6f0d5c155 100644 --- a/en/codes/go/chapter_greedy/fractional_knapsack_test.go +++ b/en/codes/go/chapter_greedy/fractional_knapsack_test.go @@ -16,5 +16,5 @@ func TestFractionalKnapsack(t *testing.T) { // Greedy algorithm res := fractionalKnapsack(wgt, val, capacity) - fmt.Println("Maximum item value not exceeding knapsack capacity is", res) + fmt.Println("Maximum total item value within the knapsack capacity:", res) } diff --git a/en/codes/go/chapter_greedy/max_capacity.go b/en/codes/go/chapter_greedy/max_capacity.go index 0997c0c61..d7ce1c20f 100644 --- a/en/codes/go/chapter_greedy/max_capacity.go +++ b/en/codes/go/chapter_greedy/max_capacity.go @@ -6,18 +6,18 @@ package chapter_greedy import "math" -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ func maxCapacity(ht []int) int { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array i, j := 0, len(ht)-1 - // Initial max capacity is 0 + // The initial maximum capacity is 0 res := 0 - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet for i < j { - // Update max capacity + // Update the maximum capacity capacity := int(math.Min(float64(ht[i]), float64(ht[j]))) * (j - i) res = int(math.Max(float64(res), float64(capacity))) - // Move the shorter board inward + // Move the shorter side inward if ht[i] < ht[j] { i++ } else { diff --git a/en/codes/go/chapter_greedy/max_capacity_test.go b/en/codes/go/chapter_greedy/max_capacity_test.go index f1e2dfe51..239a37358 100644 --- a/en/codes/go/chapter_greedy/max_capacity_test.go +++ b/en/codes/go/chapter_greedy/max_capacity_test.go @@ -14,5 +14,5 @@ func TestMaxCapacity(t *testing.T) { // Greedy algorithm res := maxCapacity(ht) - fmt.Println("Maximum capacity is", res) + fmt.Println("Maximum container capacity:", res) } diff --git a/en/codes/go/chapter_greedy/max_product_cutting.go b/en/codes/go/chapter_greedy/max_product_cutting.go index 9e2893690..14c215abb 100644 --- a/en/codes/go/chapter_greedy/max_product_cutting.go +++ b/en/codes/go/chapter_greedy/max_product_cutting.go @@ -6,23 +6,23 @@ package chapter_greedy import "math" -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ func maxProductCutting(n int) int { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if n <= 3 { return 1 * (n - 1) } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder a := n / 3 b := n % 3 if b == 1 { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return int(math.Pow(3, float64(a-1))) * 2 * 2 } if b == 2 { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return int(math.Pow(3, float64(a))) * 2 } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return int(math.Pow(3, float64(a))) } diff --git a/en/codes/go/chapter_greedy/max_product_cutting_test.go b/en/codes/go/chapter_greedy/max_product_cutting_test.go index fef8f1ee3..29eba6872 100644 --- a/en/codes/go/chapter_greedy/max_product_cutting_test.go +++ b/en/codes/go/chapter_greedy/max_product_cutting_test.go @@ -13,5 +13,5 @@ func TestMaxProductCutting(t *testing.T) { n := 58 // Greedy algorithm res := maxProductCutting(n) - fmt.Println("Maximum cutting product is", res) + fmt.Println("Maximum product after splitting:", res) } diff --git a/en/codes/go/chapter_hashing/array_hash_map.go b/en/codes/go/chapter_hashing/array_hash_map.go index 703d302e3..dd59312d5 100644 --- a/en/codes/go/chapter_hashing/array_hash_map.go +++ b/en/codes/go/chapter_hashing/array_hash_map.go @@ -12,14 +12,14 @@ type pair struct { val string } -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ type arrayHashMap struct { buckets []*pair } -/* Initialize hash table */ +/* Initialize the hash table */ func newArrayHashMap() *arrayHashMap { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets buckets := make([]*pair, 100) return &arrayHashMap{buckets: buckets} } @@ -30,7 +30,7 @@ func (a *arrayHashMap) hashFunc(key int) int { return index } -/* Query operation */ +/* Lookup operation */ func (a *arrayHashMap) get(key int) string { index := a.hashFunc(key) pair := a.buckets[index] @@ -40,7 +40,7 @@ func (a *arrayHashMap) get(key int) string { return pair.val } -/* Add operation */ +/* Insert operation */ func (a *arrayHashMap) put(key int, val string) { pair := &pair{key: key, val: val} index := a.hashFunc(key) @@ -50,11 +50,11 @@ func (a *arrayHashMap) put(key int, val string) { /* Remove operation */ func (a *arrayHashMap) remove(key int) { index := a.hashFunc(key) - // Set to nil to delete + // Set to nil to mark the entry as removed a.buckets[index] = nil } -/* Get all key pairs */ +/* Get all key-value pairs */ func (a *arrayHashMap) pairSet() []*pair { var pairs []*pair for _, pair := range a.buckets { @@ -87,7 +87,7 @@ func (a *arrayHashMap) valueSet() []string { return values } -/* Print hash table */ +/* Print the hash table */ func (a *arrayHashMap) print() { for _, pair := range a.buckets { if pair != nil { diff --git a/en/codes/go/chapter_hashing/array_hash_map_test.go b/en/codes/go/chapter_hashing/array_hash_map_test.go index aec79c22a..deee4c148 100644 --- a/en/codes/go/chapter_hashing/array_hash_map_test.go +++ b/en/codes/go/chapter_hashing/array_hash_map_test.go @@ -10,42 +10,42 @@ import ( ) func TestArrayHashMap(t *testing.T) { - /* Initialize hash table */ + /* Initialize the hash table */ hmap := newArrayHashMap() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - hmap.put(12836, "Xiao Ha") - hmap.put(15937, "Xiao Luo") - hmap.put(16750, "Xiao Suan") - hmap.put(13276, "Xiao Fa") - hmap.put(10583, "Xiao Ya") - fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + fmt.Println("\nHash table after insertion:\nKey -> Value") hmap.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value name := hmap.get(15937) - fmt.Println("\nInput student ID 15937, query name " + name) + fmt.Println("\nLook up student ID 15937 to obtain the name " + name) /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table hmap.remove(10583) - fmt.Println("\nAfter removing 10583, hash table is\nKey -> Value") + fmt.Println("\nHash table after removing 10583:\nKey -> Value") hmap.print() - /* Traverse hash table */ - fmt.Println("\nTraverse key-value pairs Key->Value") + /* Traverse the hash table */ + fmt.Println("\nTraverse key-value pairs: Key->Value") for _, kv := range hmap.pairSet() { fmt.Println(kv.key, " -> ", kv.val) } - fmt.Println("\nTraverse keys only Key") + fmt.Println("\nTraverse keys only: Key") for _, key := range hmap.keySet() { fmt.Println(key) } - fmt.Println("\nTraverse values only Value") + fmt.Println("\nTraverse values only: Value") for _, val := range hmap.valueSet() { fmt.Println(val) } diff --git a/en/codes/go/chapter_hashing/hash_collision_test.go b/en/codes/go/chapter_hashing/hash_collision_test.go index f2dbfe197..faff68c67 100644 --- a/en/codes/go/chapter_hashing/hash_collision_test.go +++ b/en/codes/go/chapter_hashing/hash_collision_test.go @@ -10,53 +10,53 @@ import ( ) func TestHashMapChaining(t *testing.T) { - /* Initialize hash table */ + /* Initialize the hash table */ hmap := newHashMapChaining() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - hmap.put(12836, "Xiao Ha") - hmap.put(15937, "Xiao Luo") - hmap.put(16750, "Xiao Suan") - hmap.put(13276, "Xiao Fa") - hmap.put(10583, "Xiao Ya") - fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + fmt.Println("\nHash table after insertion:\nKey -> Value") hmap.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value name := hmap.get(15937) - fmt.Println("\nInput student ID 15937, found name", name) + fmt.Println("\nLook up student ID 15937 to obtain the name", name) /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table hmap.remove(12836) - fmt.Println("\nAfter removing 12836, hash table is\nKey -> Value") + fmt.Println("\nHash table after removing 12836:\nKey -> Value") hmap.print() } func TestHashMapOpenAddressing(t *testing.T) { - /* Initialize hash table */ + /* Initialize the hash table */ hmap := newHashMapOpenAddressing() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - hmap.put(12836, "Xiao Ha") - hmap.put(15937, "Xiao Luo") - hmap.put(16750, "Xiao Suan") - hmap.put(13276, "Xiao Fa") - hmap.put(10583, "Xiao Ya") - fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + fmt.Println("\nHash table after insertion:\nKey -> Value") hmap.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value name := hmap.get(13276) - fmt.Println("\nInput student ID 13276, query name ", name) + fmt.Println("\nLook up student ID 13276 to obtain the name ", name) /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table hmap.remove(16750) - fmt.Println("\nAfter removing 16750, hash table is\nKey -> Value") + fmt.Println("\nHash table after removing 16750:\nKey -> Value") hmap.print() } diff --git a/en/codes/go/chapter_hashing/hash_map_chaining.go b/en/codes/go/chapter_hashing/hash_map_chaining.go index 4e4dfbac5..77ffe77d9 100644 --- a/en/codes/go/chapter_hashing/hash_map_chaining.go +++ b/en/codes/go/chapter_hashing/hash_map_chaining.go @@ -14,8 +14,8 @@ import ( type hashMapChaining struct { size int // Number of key-value pairs capacity int // Hash table capacity - loadThres float64 // Load factor threshold for triggering expansion - extendRatio int // Expansion multiplier + loadThres float64 // Load factor threshold that triggers capacity growth + extendRatio int // Capacity growth factor buckets [][]pair // Bucket array } @@ -44,35 +44,35 @@ func (m *hashMapChaining) loadFactor() float64 { return float64(m.size) / float64(m.capacity) } -/* Query operation */ +/* Lookup operation */ func (m *hashMapChaining) get(key int) string { idx := m.hashFunc(key) bucket := m.buckets[idx] - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for _, p := range bucket { if p.key == key { return p.val } } - // Return empty string if key not found + // If key is not found, return an empty string return "" } -/* Add operation */ +/* Insert operation */ func (m *hashMapChaining) put(key int, val string) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if m.loadFactor() > m.loadThres { m.extend() } idx := m.hashFunc(key) - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for i := range m.buckets[idx] { if m.buckets[idx][i].key == key { m.buckets[idx][i].val = val return } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end p := pair{ key: key, val: val, @@ -84,10 +84,10 @@ func (m *hashMapChaining) put(key int, val string) { /* Remove operation */ func (m *hashMapChaining) remove(key int) { idx := m.hashFunc(key) - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for i, p := range m.buckets[idx] { if p.key == key { - // Slice deletion + // Remove the element from the slice m.buckets[idx] = append(m.buckets[idx][:i], m.buckets[idx][i+1:]...) m.size -= 1 break @@ -95,22 +95,22 @@ func (m *hashMapChaining) remove(key int) { } } -/* Expand hash table */ +/* Grow the hash table capacity */ func (m *hashMapChaining) extend() { - // Temporarily store the original hash table + // Save a copy of the original hash table tmpBuckets := make([][]pair, len(m.buckets)) for i := 0; i < len(m.buckets); i++ { tmpBuckets[i] = make([]pair, len(m.buckets[i])) copy(tmpBuckets[i], m.buckets[i]) } - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity m.capacity *= m.extendRatio m.buckets = make([][]pair, m.capacity) for i := 0; i < m.capacity; i++ { m.buckets[i] = make([]pair, 0) } m.size = 0 - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for _, bucket := range tmpBuckets { for _, p := range bucket { m.put(p.key, p.val) @@ -118,7 +118,7 @@ func (m *hashMapChaining) extend() { } } -/* Print hash table */ +/* Print the hash table */ func (m *hashMapChaining) print() { var builder strings.Builder diff --git a/en/codes/go/chapter_hashing/hash_map_open_addressing.go b/en/codes/go/chapter_hashing/hash_map_open_addressing.go index d2523a157..249ba0eb9 100644 --- a/en/codes/go/chapter_hashing/hash_map_open_addressing.go +++ b/en/codes/go/chapter_hashing/hash_map_open_addressing.go @@ -12,10 +12,10 @@ import ( type hashMapOpenAddressing struct { size int // Number of key-value pairs capacity int // Hash table capacity - loadThres float64 // Load factor threshold for triggering expansion - extendRatio int // Expansion multiplier + loadThres float64 // Load factor threshold that triggers capacity growth + extendRatio int // Capacity growth factor buckets []*pair // Bucket array - TOMBSTONE *pair // Removal marker + TOMBSTONE *pair // Tombstone marker } /* Constructor */ @@ -32,79 +32,79 @@ func newHashMapOpenAddressing() *hashMapOpenAddressing { /* Hash function */ func (h *hashMapOpenAddressing) hashFunc(key int) int { - return key % h.capacity // Calculate hash value based on key + return key % h.capacity // Calculate the hash value from the key } /* Load factor */ func (h *hashMapOpenAddressing) loadFactor() float64 { - return float64(h.size) / float64(h.capacity) // Calculate current load factor + return float64(h.size) / float64(h.capacity) // Calculate the current load factor } -/* Search for bucket index corresponding to key */ +/* Find the bucket index for key */ func (h *hashMapOpenAddressing) findBucket(key int) int { - index := h.hashFunc(key) // Get initial index - firstTombstone := -1 // Record position of first TOMBSTONE encountered + index := h.hashFunc(key) // Get the initial index + firstTombstone := -1 // Record the position of the first TOMBSTONE encountered for h.buckets[index] != nil { if h.buckets[index].key == key { if firstTombstone != -1 { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index h.buckets[firstTombstone] = h.buckets[index] h.buckets[index] = h.TOMBSTONE - return firstTombstone // Return the moved bucket index + return firstTombstone // Return the bucket index after the move } - return index // Return found index + return index // Return the index found } if firstTombstone == -1 && h.buckets[index] == h.TOMBSTONE { - firstTombstone = index // Record position of first deletion marker encountered + firstTombstone = index // Record the position of the first tombstone encountered } - index = (index + 1) % h.capacity // Linear probing, wrap around to head if past tail + index = (index + 1) % h.capacity // Probe linearly, wrapping to the beginning after the end } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion if firstTombstone != -1 { return firstTombstone } return index } -/* Query operation */ +/* Lookup operation */ func (h *hashMapOpenAddressing) get(key int) string { - index := h.findBucket(key) // Search for bucket index corresponding to key + index := h.findBucket(key) // Find the bucket index for key if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE { - return h.buckets[index].val // If key-value pair is found, return corresponding val + return h.buckets[index].val // If the key-value pair is found, return its val } - return "" // Return "" if key-value pair does not exist + return "" // If the key-value pair is absent, return "" } -/* Add operation */ +/* Insert operation */ func (h *hashMapOpenAddressing) put(key int, val string) { if h.loadFactor() > h.loadThres { - h.extend() // When load factor exceeds threshold, perform expansion + h.extend() // Grow the capacity when the load factor exceeds the threshold } - index := h.findBucket(key) // Search for bucket index corresponding to key + index := h.findBucket(key) // Find the bucket index for key if h.buckets[index] == nil || h.buckets[index] == h.TOMBSTONE { - h.buckets[index] = &pair{key, val} // If key-value pair does not exist, add the key-value pair + h.buckets[index] = &pair{key, val} // If the key-value pair is absent, insert it h.size++ } else { - h.buckets[index].val = val // If key-value pair found, overwrite val + h.buckets[index].val = val // If the key-value pair is found, overwrite val } } /* Remove operation */ func (h *hashMapOpenAddressing) remove(key int) { - index := h.findBucket(key) // Search for bucket index corresponding to key + index := h.findBucket(key) // Find the bucket index for key if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE { - h.buckets[index] = h.TOMBSTONE // If key-value pair is found, overwrite it with removal marker + h.buckets[index] = h.TOMBSTONE // If the key-value pair is found, replace it with a tombstone h.size-- } } -/* Expand hash table */ +/* Grow the hash table capacity */ func (h *hashMapOpenAddressing) extend() { - oldBuckets := h.buckets // Temporarily store the original hash table - h.capacity *= h.extendRatio // Update capacity - h.buckets = make([]*pair, h.capacity) // Initialize expanded new hash table - h.size = 0 // Reset size - // Move key-value pairs from original hash table to new hash table + oldBuckets := h.buckets // Keep a reference to the original hash table + h.capacity *= h.extendRatio // Update the capacity + h.buckets = make([]*pair, h.capacity) // Initialize a new hash table with the expanded capacity + h.size = 0 // Reset the size + // Move the key-value pairs from the original hash table to the new one for _, pair := range oldBuckets { if pair != nil && pair != h.TOMBSTONE { h.put(pair.key, pair.val) @@ -112,7 +112,7 @@ func (h *hashMapOpenAddressing) extend() { } } -/* Print hash table */ +/* Print the hash table */ func (h *hashMapOpenAddressing) print() { for _, pair := range h.buckets { if pair == nil { diff --git a/en/codes/go/chapter_hashing/hash_map_test.go b/en/codes/go/chapter_hashing/hash_map_test.go index ae88ba5a5..b2b81929c 100644 --- a/en/codes/go/chapter_hashing/hash_map_test.go +++ b/en/codes/go/chapter_hashing/hash_map_test.go @@ -13,43 +13,43 @@ import ( ) func TestHashMap(t *testing.T) { - /* Initialize hash table */ + /* Initialize the hash table */ hmap := make(map[int]string) - /* Add operation */ - // Add key-value pair (key, value) to the hash table - hmap[12836] = "Xiao Ha" - hmap[15937] = "Xiao Luo" - hmap[16750] = "Xiao Suan" - hmap[13276] = "Xiao Fa" - hmap[10583] = "Xiao Ya" - fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" + fmt.Println("\nHash table after insertion:\nKey -> Value") PrintMap(hmap) - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value name := hmap[15937] - fmt.Println("\nInput student ID 15937, query name ", name) + fmt.Println("\nLook up student ID 15937 to obtain the name ", name) /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table delete(hmap, 10583) - fmt.Println("\nAfter removing 10583, hash table is\nKey -> Value") + fmt.Println("\nHash table after removing 10583:\nKey -> Value") PrintMap(hmap) - /* Traverse hash table */ - // Traverse key-value pairs - fmt.Println("\nTraverse key-value pairs Key->Value") + /* Traverse the hash table */ + // Traverse key-value pairs: key->value + fmt.Println("\nTraverse key-value pairs: Key->Value") for key, value := range hmap { fmt.Println(key, "->", value) } - // Traverse keys only - fmt.Println("\nTraverse keys only Key") + // Traverse keys only: key + fmt.Println("\nTraverse keys only: Key") for key := range hmap { fmt.Println(key) } - // Traverse values only - fmt.Println("\nTraverse values only Value") + // Traverse values only: value + fmt.Println("\nTraverse values only: Value") for _, value := range hmap { fmt.Println(value) } @@ -58,17 +58,17 @@ func TestHashMap(t *testing.T) { func TestSimpleHash(t *testing.T) { var hash int - key := "Hello Algo" + key := "Hello 算法" hash = addHash(key) - fmt.Println("Additive hash value is " + strconv.Itoa(hash)) + fmt.Println("Additive hash value: " + strconv.Itoa(hash)) hash = mulHash(key) - fmt.Println("Multiplicative hash value is " + strconv.Itoa(hash)) + fmt.Println("Multiplicative hash value: " + strconv.Itoa(hash)) hash = xorHash(key) - fmt.Println("XOR hash value is " + strconv.Itoa(hash)) + fmt.Println("XOR hash value: " + strconv.Itoa(hash)) hash = rotHash(key) - fmt.Println("Rotational hash value is " + strconv.Itoa(hash)) + fmt.Println("Rotating hash value: " + strconv.Itoa(hash)) } diff --git a/en/codes/go/chapter_hashing/simple_hash.go b/en/codes/go/chapter_hashing/simple_hash.go index 23ec12db0..84f1c289b 100644 --- a/en/codes/go/chapter_hashing/simple_hash.go +++ b/en/codes/go/chapter_hashing/simple_hash.go @@ -42,7 +42,7 @@ func xorHash(key string) int { return hash & modulus } -/* Rotational hash */ +/* Rotating hash */ func rotHash(key string) int { var hash int64 var modulus int64 diff --git a/en/codes/go/chapter_heap/heap.go b/en/codes/go/chapter_heap/heap.go index f614fad6b..9d99bc57c 100644 --- a/en/codes/go/chapter_heap/heap.go +++ b/en/codes/go/chapter_heap/heap.go @@ -4,42 +4,42 @@ package chapter_heap -// In Go, integer max heap can be built by implementing heap.Interface -// Implementing heap.Interface requires also implementing sort.Interface +// In Go, implement heap.Interface to build an integer max heap +// Implementing heap.Interface also requires implementing sort.Interface type intHeap []any -// Push function of heap.Interface, implements pushing element to heap +// Push implements heap.Interface by appending an element for the heap package func (h *intHeap) Push(x any) { - // Push and Pop use pointer receiver as parameter - // Because they not only adjust the slice content, but also modify the slice length. + // Push and Pop use pointer receivers + // because they modify the slice length as well as its contents. *h = append(*h, x.(int)) } -// Pop function of heap.Interface, implements popping heap top element +// Pop implements heap.Interface by removing the last element, where heap.Pop has moved the top element func (h *intHeap) Pop() any { - // Element to be popped is stored at the end + // The element to remove is stored at the end last := (*h)[len(*h)-1] *h = (*h)[:len(*h)-1] return last } -// Len function of sort.Interface +// Len implements the sort.Interface length method func (h *intHeap) Len() int { return len(*h) } -// Less function of sort.Interface +// Less implements the sort.Interface comparison method func (h *intHeap) Less(i, j int) bool { - // If implementing min heap, need to change to less than sign + // For a min heap, change the comparison to less than return (*h)[i].(int) > (*h)[j].(int) } -// Swap function of sort.Interface +// Swap implements the sort.Interface swap method func (h *intHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } -// Top gets heap top element +// Top returns the top element of the heap func (h *intHeap) Top() any { return (*h)[0] } diff --git a/en/codes/go/chapter_heap/heap_test.go b/en/codes/go/chapter_heap/heap_test.go index 5c49aa335..07e98634b 100644 --- a/en/codes/go/chapter_heap/heap_test.go +++ b/en/codes/go/chapter_heap/heap_test.go @@ -14,88 +14,88 @@ import ( ) func testPush(h *intHeap, val int) { - // Call heap.Interface function to add element + // Call heap.Push, which uses heap.Interface to add an element heap.Push(h, val) - fmt.Printf("\nAfter element %d pushes to heap \n", val) + fmt.Printf("\nAfter pushing %d onto the heap: \n", val) PrintHeap(*h) } func testPop(h *intHeap) { - // Call heap.Interface function to remove element + // Call heap.Pop, which uses heap.Interface to remove the top element val := heap.Pop(h) - fmt.Printf("\nAfter heap top element %d pops from heap \n", val) + fmt.Printf("\nAfter popping the top element %d from the heap: \n", val) PrintHeap(*h) } func TestHeap(t *testing.T) { - /* Initialize heap */ - // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + /* Initialize the heap */ + // Initialize a max heap maxHeap := &intHeap{} heap.Init(maxHeap) - /* Element enters heap */ + /* Push an element onto the heap */ testPush(maxHeap, 1) testPush(maxHeap, 3) testPush(maxHeap, 2) testPush(maxHeap, 5) testPush(maxHeap, 4) - /* Check if heap is empty */ + /* Get the top element of the heap */ top := maxHeap.Top() - fmt.Printf("Heap top element is %d\n", top) + fmt.Printf("Top element of the heap: %d\n", top) - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ testPop(maxHeap) testPop(maxHeap) testPop(maxHeap) testPop(maxHeap) testPop(maxHeap) - /* Get heap size */ + /* Get the heap size */ size := len(*maxHeap) - fmt.Printf("Heap size is %d\n", size) + fmt.Printf("Number of elements in the heap: %d\n", size) - /* Check if heap is empty */ + /* Check whether the heap is empty */ isEmpty := len(*maxHeap) == 0 - fmt.Printf("Is heap empty %t\n", isEmpty) + fmt.Printf("Is the heap empty? %t\n", isEmpty) } func TestMyHeap(t *testing.T) { - /* Initialize heap */ - // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + /* Initialize the heap */ + // Initialize a max heap maxHeap := newMaxHeap([]any{9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}) - fmt.Printf("After input array and building heap\n") + fmt.Printf("After building a heap from the input array:\n") maxHeap.print() - /* Check if heap is empty */ + /* Get the top element of the heap */ peek := maxHeap.peek() - fmt.Printf("\nHeap top element is %d\n", peek) + fmt.Printf("\nTop element: %d\n", peek) - /* Element enters heap */ + /* Push an element onto the heap */ val := 7 maxHeap.push(val) - fmt.Printf("\nAfter element %d enters heap\n", val) + fmt.Printf("\nAfter pushing %d onto the heap:\n", val) maxHeap.print() - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.pop() - fmt.Printf("\nAfter heap top element %d exits heap\n", peek) + fmt.Printf("\nAfter popping the top element %d:\n", peek) maxHeap.print() - /* Get heap size */ + /* Get the heap size */ size := maxHeap.size() - fmt.Printf("\nHeap element count is %d\n", size) + fmt.Printf("\nNumber of elements in the heap: %d\n", size) - /* Check if heap is empty */ + /* Check whether the heap is empty */ isEmpty := maxHeap.isEmpty() - fmt.Printf("\nIs heap empty %t\n", isEmpty) + fmt.Printf("\nIs the heap empty? %t\n", isEmpty) } func TestTopKHeap(t *testing.T) { - /* Initialize heap */ - // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + /* Initialize the heap */ + // Prepare the input for heap-based top-k selection nums := []int{1, 7, 6, 3, 2} k := 3 res := topKHeap(nums, k) - fmt.Printf("The largest " + strconv.Itoa(k) + " elements are") + fmt.Printf("The largest " + strconv.Itoa(k) + " elements are:") PrintHeap(*res) } diff --git a/en/codes/go/chapter_heap/my_heap.go b/en/codes/go/chapter_heap/my_heap.go index 4c7a8cc52..bbefc296b 100644 --- a/en/codes/go/chapter_heap/my_heap.go +++ b/en/codes/go/chapter_heap/my_heap.go @@ -11,41 +11,41 @@ import ( ) type maxHeap struct { - // Use slice instead of array to avoid expansion issues + // Use a slice instead of an array so append can handle capacity growth data []any } -/* Constructor, build empty heap */ +/* Constructor: create an empty heap */ func newHeap() *maxHeap { return &maxHeap{ data: make([]any, 0), } } -/* Constructor, build heap from slice */ +/* Constructor: build a heap from a slice */ func newMaxHeap(nums []any) *maxHeap { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order h := &maxHeap{data: nums} for i := h.parent(len(h.data) - 1); i >= 0; i-- { - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node h.siftDown(i) } return h } -/* Get index of left child node */ +/* Get the left child index */ func (h *maxHeap) left(i int) int { return 2*i + 1 } -/* Get index of right child node */ +/* Get the right child index */ func (h *maxHeap) right(i int) int { return 2*i + 2 } -/* Get index of parent node */ +/* Get the parent index */ func (h *maxHeap) parent(i int) int { - // Floor division + // Integer division (truncates toward zero in Go) return (i - 1) / 2 } @@ -54,68 +54,68 @@ func (h *maxHeap) swap(i, j int) { h.data[i], h.data[j] = h.data[j], h.data[i] } -/* Get heap size */ +/* Get the heap size */ func (h *maxHeap) size() int { return len(h.data) } -/* Check if heap is empty */ +/* Check whether the heap is empty */ func (h *maxHeap) isEmpty() bool { return len(h.data) == 0 } -/* Access top element */ +/* Access the top element of the heap */ func (h *maxHeap) peek() any { return h.data[0] } -/* Element enters heap */ +/* Push an element onto the heap */ func (h *maxHeap) push(val any) { - // Add node + // Add a node h.data = append(h.data, val) - // Heapify from bottom to top + // Sift up h.siftUp(len(h.data) - 1) } -/* Starting from node i, heapify from bottom to top */ +/* Sift up starting from node i */ func (h *maxHeap) siftUp(i int) { for true { - // Get parent node of node i + // Get the parent of node i p := h.parent(i) - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if p < 0 || h.data[i].(int) <= h.data[p].(int) { break } - // Swap two nodes + // Swap the two nodes h.swap(i, p) - // Loop upward heapify + // Continue sifting up i = p } } -/* Element exits heap */ +/* Pop an element from the heap */ func (h *maxHeap) pop() any { - // Handle empty case + // Handle an empty heap if h.isEmpty() { fmt.Println("error") return nil } - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) h.swap(0, h.size()-1) - // Remove node + // Remove a node val := h.data[len(h.data)-1] h.data = h.data[:len(h.data)-1] - // Return top element + // Sift down h.siftDown(0) - // Return heap top element + // Return the top element of the heap return val } -/* Starting from node i, heapify from top to bottom */ +/* Sift down starting from node i */ func (h *maxHeap) siftDown(i int) { for true { - // Find node with maximum value among nodes i, l, r, denoted as max + // Find the node with the largest value among i, l, and r, and store its index in max l, r, max := h.left(i), h.right(i), i if l < h.size() && h.data[l].(int) > h.data[max].(int) { max = l @@ -123,18 +123,18 @@ func (h *maxHeap) siftDown(i int) { if r < h.size() && h.data[r].(int) > h.data[max].(int) { max = r } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if max == i { break } - // Swap two nodes + // Swap the two nodes h.swap(i, max) - // Loop downwards heapification + // Continue sifting down i = max } } -/* Driver Code */ +/* Print the heap (binary tree) */ func (h *maxHeap) print() { PrintHeap(h.data) } diff --git a/en/codes/go/chapter_heap/top_k.go b/en/codes/go/chapter_heap/top_k.go index 75adf5a93..4170891ed 100644 --- a/en/codes/go/chapter_heap/top_k.go +++ b/en/codes/go/chapter_heap/top_k.go @@ -12,36 +12,36 @@ func (h *minHeap) Len() int { return len(*h) } func (h *minHeap) Less(i, j int) bool { return (*h)[i].(int) < (*h)[j].(int) } func (h *minHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } -// Push method of heap.Interface, implements pushing element to heap +// Push implements heap.Interface by appending an element for the heap package func (h *minHeap) Push(x any) { *h = append(*h, x.(int)) } -// Pop method of heap.Interface, implements popping heap top element +// Pop implements heap.Interface by removing the last element, where heap.Pop has moved the top element func (h *minHeap) Pop() any { - // Element to be popped is stored at the end + // The element to remove is stored at the end last := (*h)[len(*h)-1] *h = (*h)[:len(*h)-1] return last } -// Top gets heap top element +// Top returns the top element of the heap func (h *minHeap) Top() any { return (*h)[0] } -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ func topKHeap(nums []int, k int) *minHeap { - // Python's heapq module implements min heap by default + // Initialize a min heap h := &minHeap{} heap.Init(h) - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for i := 0; i < k; i++ { heap.Push(h, nums[i]) } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for i := k; i < len(nums); i++ { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if nums[i] > h.Top().(int) { heap.Pop(h) heap.Push(h, nums[i]) diff --git a/en/codes/go/chapter_searching/binary_search.go b/en/codes/go/chapter_searching/binary_search.go index 16217c985..26a75da71 100644 --- a/en/codes/go/chapter_searching/binary_search.go +++ b/en/codes/go/chapter_searching/binary_search.go @@ -4,40 +4,40 @@ package chapter_searching -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ func binarySearch(nums []int, target int) int { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements i, j := 0, len(nums)-1 - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) for i <= j { m := i + (j-i)/2 // Calculate the midpoint index m - if nums[m] < target { // This means target is in the interval [m+1, j] + if nums[m] < target { // In this case, target must be in [m+1, j] if it exists i = m + 1 - } else if nums[m] > target { // This means target is in the interval [i, m-1] + } else if nums[m] > target { // In this case, target must be in [i, m-1] if it exists j = m - 1 - } else { // Found the target element, return its index + } else { // The target element was found; return its index return m } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ func binarySearchLCRO(nums []int, target int) int { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last i, j := 0, len(nums) - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) for i < j { m := i + (j-i)/2 // Calculate the midpoint index m - if nums[m] < target { // This means target is in the interval [m+1, j) + if nums[m] < target { // In this case, target must be in [m+1, j) if it exists i = m + 1 - } else if nums[m] > target { // This means target is in the interval [i, m) + } else if nums[m] > target { // In this case, target must be in [i, m) if it exists j = m - } else { // Found the target element, return its index + } else { // The target element was found; return its index return m } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } diff --git a/en/codes/go/chapter_searching/binary_search_edge.go b/en/codes/go/chapter_searching/binary_search_edge.go index 5725fbf43..232238799 100644 --- a/en/codes/go/chapter_searching/binary_search_edge.go +++ b/en/codes/go/chapter_searching/binary_search_edge.go @@ -4,28 +4,28 @@ package chapter_searching -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ func binarySearchLeftEdge(nums []int, target int) int { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target i := binarySearchInsertion(nums, target) - // Target not found, return -1 + // If target was not found, return -1 if i == len(nums) || nums[i] != target { return -1 } - // Found target, return index i + // Target was found; return index i return i } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ func binarySearchRightEdge(nums []int, target int) int { - // Convert to finding the leftmost target + 1 + // For integer values, find the leftmost insertion point of target + 1 (assuming no overflow) i := binarySearchInsertion(nums, target+1) - // j points to the rightmost target, i points to the first element greater than target + // i is the index of the first element greater than target (or len(nums)); j is the candidate index of the rightmost target j := i - 1 - // Target not found, return -1 + // If target was not found, return -1 if j == -1 || nums[j] != target { return -1 } - // Found target, return index j + // Target was found; return index j return j } diff --git a/en/codes/go/chapter_searching/binary_search_insertion.go b/en/codes/go/chapter_searching/binary_search_insertion.go index 22e139079..71d1c2273 100644 --- a/en/codes/go/chapter_searching/binary_search_insertion.go +++ b/en/codes/go/chapter_searching/binary_search_insertion.go @@ -4,43 +4,43 @@ package chapter_searching -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ func binarySearchInsertionSimple(nums []int, target int) int { - // Initialize closed interval [0, n-1] + // Initialize the closed interval [0, n-1] i, j := 0, len(nums)-1 for i <= j { // Calculate the midpoint index m m := i + (j-i)/2 if nums[m] < target { - // target is in the interval [m+1, j] + // Target must be in [m+1, j] if it exists i = m + 1 } else if nums[m] > target { - // target is in the interval [i, m-1] + // Target must be in [i, m-1] if it exists j = m - 1 } else { - // Found target, return insertion point m + // Target was found; return insertion point m return m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ func binarySearchInsertion(nums []int, target int) int { - // Initialize closed interval [0, n-1] + // Initialize the closed interval [0, n-1] i, j := 0, len(nums)-1 for i <= j { // Calculate the midpoint index m m := i + (j-i)/2 if nums[m] < target { - // target is in the interval [m+1, j] + // Target must be in [m+1, j] if it exists i = m + 1 } else if nums[m] > target { - // target is in the interval [i, m-1] + // Target must be in [i, m-1] if it exists j = m - 1 } else { - // The rightmost element less than target is in the interval [i, m-1] + // Continue in [i, m-1] to find the rightmost element smaller than target j = m - 1 } } diff --git a/en/codes/go/chapter_searching/binary_search_test.go b/en/codes/go/chapter_searching/binary_search_test.go index 59f674b8c..c5d8fa445 100644 --- a/en/codes/go/chapter_searching/binary_search_test.go +++ b/en/codes/go/chapter_searching/binary_search_test.go @@ -15,47 +15,47 @@ func TestBinarySearch(t *testing.T) { nums = []int{1, 3, 6, 8, 12, 15, 23, 26, 31, 35} expected = 2 ) - // Perform binary search in array + // Perform binary search on the array actual := binarySearch(nums, target) fmt.Println("Index of target element 6 =", actual) if actual != expected { - t.Errorf("Index of target element 6 = %d, should be %d", actual, expected) + t.Errorf("Index of target element 6 = %d; expected %d", actual, expected) } } func TestBinarySearchEdge(t *testing.T) { - // Array with duplicate elements + // Example array for a search that allows duplicates nums := []int{1, 3, 6, 8, 12, 15, 23, 26, 31, 35} fmt.Println("\nArray nums = ", nums) - // Binary search left and right boundaries + // Find the left and right boundaries using binary search for _, target := range []int{6, 7} { index := binarySearchLeftEdge(nums, target) - fmt.Println("Leftmost element", target, " index is", index) + fmt.Println("Leftmost occurrence of", target, "has index", index) index = binarySearchRightEdge(nums, target) - fmt.Println("Rightmost element", target, " index is", index) + fmt.Println("Rightmost occurrence of", target, "has index", index) } } func TestBinarySearchInsertion(t *testing.T) { - // Array without duplicate elements + // Array with no duplicate elements nums := []int{1, 3, 6, 8, 12, 15, 23, 26, 31, 35} fmt.Println("Array nums =", nums) - // Binary search for insertion point + // Find an insertion point using binary search for _, target := range []int{6, 9} { index := binarySearchInsertionSimple(nums, target) - fmt.Println("Element", target, " insertion point index is", index) + fmt.Println("Element", target, "has insertion point index", index) } - // Array with duplicate elements + // Example array for a search that allows duplicates nums = []int{1, 3, 6, 6, 6, 6, 6, 10, 12, 15} fmt.Println("\nArray nums =", nums) - // Binary search for insertion point + // Find an insertion point using binary search for _, target := range []int{2, 6, 20} { index := binarySearchInsertion(nums, target) - fmt.Println("Element", target, " insertion point index is", index) + fmt.Println("Element", target, "has insertion point index", index) } } diff --git a/en/codes/go/chapter_searching/hashing_search.go b/en/codes/go/chapter_searching/hashing_search.go index e98119afa..b6831c454 100644 --- a/en/codes/go/chapter_searching/hashing_search.go +++ b/en/codes/go/chapter_searching/hashing_search.go @@ -6,10 +6,10 @@ package chapter_searching import . "github.com/krahets/hello-algo/pkg" -/* Hash search (array) */ +/* Hash-based search (array) */ func hashingSearchArray(m map[int]int, target int) int { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key if index, ok := m[target]; ok { return index } else { @@ -17,10 +17,10 @@ func hashingSearchArray(m map[int]int, target int) int { } } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ func hashingSearchLinkedList(m map[int]*ListNode, target int) *ListNode { - // Hash table key: target node value, value: node object - // Return nil if key does not exist in hash table + // Hash table key: target node value; value: node object + // Return nil if the hash table does not contain this key if node, ok := m[target]; ok { return node } else { diff --git a/en/codes/go/chapter_searching/hashing_search_test.go b/en/codes/go/chapter_searching/hashing_search_test.go index 54f816208..27d3ba1cb 100644 --- a/en/codes/go/chapter_searching/hashing_search_test.go +++ b/en/codes/go/chapter_searching/hashing_search_test.go @@ -13,9 +13,9 @@ import ( func TestHashingSearch(t *testing.T) { target := 3 - /* Hash search (array) */ + /* Hash-based search (array) */ nums := []int{1, 5, 3, 2, 4, 7, 5, 9, 10, 8} - // Initialize hash table + // Initialize the hash table m := make(map[int]int) for i := 0; i < len(nums); i++ { m[nums[i]] = i @@ -23,14 +23,14 @@ func TestHashingSearch(t *testing.T) { index := hashingSearchArray(m, target) fmt.Println("Index of target element 3 = ", index) - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ head := ArrayToLinkedList(nums) - // Initialize hash table + // Initialize the hash table m1 := make(map[int]*ListNode) for head != nil { m1[head.Val] = head head = head.Next } node := hashingSearchLinkedList(m1, target) - fmt.Println("Node object corresponding to target node value 3 is ", node) + fmt.Println("The node object corresponding to target value 3 is ", node) } diff --git a/en/codes/go/chapter_searching/linear_search.go b/en/codes/go/chapter_searching/linear_search.go index 8949af1e2..2112e011d 100644 --- a/en/codes/go/chapter_searching/linear_search.go +++ b/en/codes/go/chapter_searching/linear_search.go @@ -10,14 +10,14 @@ import ( /* Linear search (array) */ func linearSearchArray(nums []int, target int) int { - // Traverse array + // Traverse the array for i := 0; i < len(nums); i++ { - // Found the target element, return its index + // The target element was found; return its index if nums[i] == target { return i } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } @@ -25,12 +25,12 @@ func linearSearchArray(nums []int, target int) int { func linearSearchLinkedList(node *ListNode, target int) *ListNode { // Traverse the linked list for node != nil { - // Found the target node, return it + // The target node was found; return it if node.Val == target { return node } node = node.Next } - // Target element not found, return nil + // The target element was not found; return nil return nil } diff --git a/en/codes/go/chapter_searching/linear_search_test.go b/en/codes/go/chapter_searching/linear_search_test.go index 27beb8e87..d5b9f04c8 100644 --- a/en/codes/go/chapter_searching/linear_search_test.go +++ b/en/codes/go/chapter_searching/linear_search_test.go @@ -15,12 +15,12 @@ func TestLinearSearch(t *testing.T) { target := 3 nums := []int{1, 5, 3, 2, 4, 7, 5, 9, 10, 8} - // Perform linear search in array + // Perform linear search on the array index := linearSearchArray(nums, target) fmt.Println("Index of target element 3 =", index) - // Perform linear search in linked list + // Perform linear search on the linked list head := ArrayToLinkedList(nums) node := linearSearchLinkedList(head, target) - fmt.Println("Node object with target value 3 is", node) + fmt.Println("The node object corresponding to target value 3 is", node) } diff --git a/en/codes/go/chapter_searching/two_sum.go b/en/codes/go/chapter_searching/two_sum.go index d73188a0c..45a926df0 100644 --- a/en/codes/go/chapter_searching/two_sum.go +++ b/en/codes/go/chapter_searching/two_sum.go @@ -4,10 +4,10 @@ package chapter_searching -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ func twoSumBruteForce(nums []int, target int) []int { size := len(nums) - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for i := 0; i < size-1; i++ { for j := i + 1; j < size; j++ { if nums[i]+nums[j] == target { @@ -18,11 +18,11 @@ func twoSumBruteForce(nums []int, target int) []int { return nil } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ func twoSumHashTable(nums []int, target int) []int { - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space hashTable := map[int]int{} - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for idx, val := range nums { if preIdx, ok := hashTable[target-val]; ok { return []int{preIdx, idx} diff --git a/en/codes/go/chapter_searching/two_sum_test.go b/en/codes/go/chapter_searching/two_sum_test.go index 1f2895bc0..5d124697d 100644 --- a/en/codes/go/chapter_searching/two_sum_test.go +++ b/en/codes/go/chapter_searching/two_sum_test.go @@ -10,15 +10,15 @@ import ( ) func TestTwoSum(t *testing.T) { - // ======= Test Case ======= + // ======= Test case ======= nums := []int{2, 7, 11, 15} target := 13 - // ====== Driver Code ====== - // Method 1: Brute-force approach + // ====== Driver code ====== + // Method 1: brute-force solution res := twoSumBruteForce(nums, target) - fmt.Println("Method 1 res =", res) - // Method 2: Hash table + fmt.Println("Method 1: res =", res) + // Method 2: hash table res = twoSumHashTable(nums, target) - fmt.Println("Method 2 res =", res) + fmt.Println("Method 2: res =", res) } diff --git a/en/codes/go/chapter_sorting/bubble_sort.go b/en/codes/go/chapter_sorting/bubble_sort.go index 91b62575d..739cd4c1b 100644 --- a/en/codes/go/chapter_sorting/bubble_sort.go +++ b/en/codes/go/chapter_sorting/bubble_sort.go @@ -6,9 +6,9 @@ package chapter_sorting /* Bubble sort */ func bubbleSort(nums []int) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i := len(nums) - 1; i > 0; i-- { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j := 0; j < i; j++ { if nums[j] > nums[j+1] { // Swap nums[j] and nums[j + 1] @@ -18,20 +18,20 @@ func bubbleSort(nums []int) { } } -/* Bubble sort (flag optimization) */ +/* Bubble sort (flag optimization)*/ func bubbleSortWithFlag(nums []int) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i := len(nums) - 1; i > 0; i-- { - flag := false // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + flag := false // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j := 0; j < i; j++ { if nums[j] > nums[j+1] { // Swap nums[j] and nums[j + 1] nums[j], nums[j+1] = nums[j+1], nums[j] - flag = true // Record element swap + flag = true // Record that a swap occurred } } - if flag == false { // No elements were swapped in this round of "bubbling", exit directly + if flag == false { // Exit immediately if this pass made no swaps break } } diff --git a/en/codes/go/chapter_sorting/bubble_sort_test.go b/en/codes/go/chapter_sorting/bubble_sort_test.go index 358e46b25..ad27d55b4 100644 --- a/en/codes/go/chapter_sorting/bubble_sort_test.go +++ b/en/codes/go/chapter_sorting/bubble_sort_test.go @@ -12,9 +12,9 @@ import ( func TestBubbleSort(t *testing.T) { nums := []int{4, 1, 3, 1, 5, 2} bubbleSort(nums) - fmt.Println("After bubble sort completes, nums = ", nums) + fmt.Println("After bubble sort, nums = ", nums) nums1 := []int{4, 1, 3, 1, 5, 2} bubbleSortWithFlag(nums1) - fmt.Println("After bubble sort completes, nums1 = ", nums1) + fmt.Println("After bubble sort, nums1 = ", nums1) } diff --git a/en/codes/go/chapter_sorting/bucket_sort.go b/en/codes/go/chapter_sorting/bucket_sort.go index 81dcdcbf5..3a579da80 100644 --- a/en/codes/go/chapter_sorting/bucket_sort.go +++ b/en/codes/go/chapter_sorting/bucket_sort.go @@ -8,25 +8,25 @@ import "sort" /* Bucket sort */ func bucketSort(nums []float64) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = floor(n/2) buckets, expecting about 2 elements per bucket for uniformly distributed input k := len(nums) / 2 buckets := make([][]float64, k) for i := 0; i < k; i++ { buckets[i] = make([]float64, 0) } - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for _, num := range nums { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); use num * k to map each value to an index in [0, k-1] i := int(num * float64(k)) // Add num to bucket i buckets[i] = append(buckets[i], num) } // 2. Sort each bucket for i := 0; i < k; i++ { - // Use built-in slice sorting function, can also be replaced with other sorting algorithms + // Use the standard slice-sorting function, or replace it with another sorting algorithm sort.Float64s(buckets[i]) } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results i := 0 for _, bucket := range buckets { for _, num := range bucket { diff --git a/en/codes/go/chapter_sorting/bucket_sort_test.go b/en/codes/go/chapter_sorting/bucket_sort_test.go index 95c791f95..15f03f2f7 100644 --- a/en/codes/go/chapter_sorting/bucket_sort_test.go +++ b/en/codes/go/chapter_sorting/bucket_sort_test.go @@ -10,8 +10,8 @@ import ( ) func TestBucketSort(t *testing.T) { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) nums := []float64{0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37} bucketSort(nums) - fmt.Println("After bucket sort completes, nums = ", nums) + fmt.Println("After bucket sort, nums = ", nums) } diff --git a/en/codes/go/chapter_sorting/counting_sort.go b/en/codes/go/chapter_sorting/counting_sort.go index 636d9539c..035a5ae54 100644 --- a/en/codes/go/chapter_sorting/counting_sort.go +++ b/en/codes/go/chapter_sorting/counting_sort.go @@ -7,22 +7,22 @@ package chapter_sorting type CountingSort struct{} /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects func countingSortNaive(nums []int) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m m := 0 for _, num := range nums { if num > m { m = num } } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num counter := make([]int, m+1) for _, num := range nums { counter[num]++ } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements for i, num := 0, 0; num < m+1; num++ { for j := 0; j < counter[num]; j++ { nums[i] = num @@ -32,37 +32,37 @@ func countingSortNaive(nums []int) { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Full implementation; stable and adaptable to sorting objects func countingSort(nums []int) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m m := 0 for _, num := range nums { if num > m { m = num } } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num counter := make([]int, m+1) for _, num := range nums { counter[num]++ } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for i := 0; i < m; i++ { counter[i+1] += counter[i] } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result n := len(nums) res := make([]int, n) for i := n - 1; i >= 0; i-- { num := nums[i] // Place num at the corresponding index res[counter[num]-1] = num - // Decrement the prefix sum by 1, getting the next index to place num + // Decrement the prefix sum by 1 to determine where to place the next occurrence of num counter[num]-- } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res copy(nums, res) } diff --git a/en/codes/go/chapter_sorting/counting_sort_test.go b/en/codes/go/chapter_sorting/counting_sort_test.go index f3b15ccf7..9f670f4e7 100644 --- a/en/codes/go/chapter_sorting/counting_sort_test.go +++ b/en/codes/go/chapter_sorting/counting_sort_test.go @@ -12,9 +12,9 @@ import ( func TestCountingSort(t *testing.T) { nums := []int{1, 0, 1, 2, 0, 4, 0, 2, 2, 4} countingSortNaive(nums) - fmt.Println("After counting sort (cannot sort objects) completes, nums = ", nums) + fmt.Println("After counting sort (does not support objects), nums = ", nums) nums1 := []int{1, 0, 1, 2, 0, 4, 0, 2, 2, 4} countingSort(nums1) - fmt.Println("After counting sort completes, nums1 = ", nums1) + fmt.Println("After counting sort, nums1 = ", nums1) } diff --git a/en/codes/go/chapter_sorting/heap_sort.go b/en/codes/go/chapter_sorting/heap_sort.go index 47b0c7d1b..0616e054b 100644 --- a/en/codes/go/chapter_sorting/heap_sort.go +++ b/en/codes/go/chapter_sorting/heap_sort.go @@ -4,10 +4,10 @@ package chapter_sorting -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ func siftDown(nums *[]int, n, i int) { for true { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma l := 2*i + 1 r := 2*i + 2 ma := i @@ -17,28 +17,28 @@ func siftDown(nums *[]int, n, i int) { if r < n && (*nums)[r] > (*nums)[ma] { ma = r } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if ma == i { break } - // Swap two nodes + // Swap the two nodes (*nums)[i], (*nums)[ma] = (*nums)[ma], (*nums)[i] - // Loop downwards heapification + // Continue sifting down i = ma } } /* Heap sort */ func heapSort(nums *[]int) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for i := len(*nums)/2 - 1; i >= 0; i-- { siftDown(nums, len(*nums), i) } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for i := len(*nums) - 1; i > 0; i-- { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) (*nums)[0], (*nums)[i] = (*nums)[i], (*nums)[0] - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0) } } diff --git a/en/codes/go/chapter_sorting/heap_sort_test.go b/en/codes/go/chapter_sorting/heap_sort_test.go index 123ebfa83..6f48c8cf0 100644 --- a/en/codes/go/chapter_sorting/heap_sort_test.go +++ b/en/codes/go/chapter_sorting/heap_sort_test.go @@ -12,5 +12,5 @@ import ( func TestHeapSort(t *testing.T) { nums := []int{4, 1, 3, 1, 5, 2} heapSort(&nums) - fmt.Println("After heap sort completes, nums = ", nums) + fmt.Println("After heap sort, nums = ", nums) } diff --git a/en/codes/go/chapter_sorting/insertion_sort.go b/en/codes/go/chapter_sorting/insertion_sort.go index 6d13bb6ff..e7133865b 100644 --- a/en/codes/go/chapter_sorting/insertion_sort.go +++ b/en/codes/go/chapter_sorting/insertion_sort.go @@ -6,15 +6,15 @@ package chapter_sorting /* Insertion sort */ func insertionSort(nums []int) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for i := 1; i < len(nums); i++ { base := nums[i] j := i - 1 - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] for j >= 0 && nums[j] > base { - nums[j+1] = nums[j] // Move nums[j] to the right by one position + nums[j+1] = nums[j] // Shift nums[j] one position to the right j-- } - nums[j+1] = base // Assign base to the correct position + nums[j+1] = base // Place base at the correct position } } diff --git a/en/codes/go/chapter_sorting/merge_sort.go b/en/codes/go/chapter_sorting/merge_sort.go index a2d9727ea..659952ec8 100644 --- a/en/codes/go/chapter_sorting/merge_sort.go +++ b/en/codes/go/chapter_sorting/merge_sort.go @@ -4,14 +4,14 @@ package chapter_sorting -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ func merge(nums []int, left, mid, right int) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result tmp := make([]int, right-left+1) - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays i, j, k := left, mid+1, 0 - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array for i <= mid && j <= right { if nums[i] <= nums[j] { tmp[k] = nums[i] @@ -33,7 +33,7 @@ func merge(nums []int, left, mid, right int) { j++ k++ } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for k := 0; k < len(tmp); k++ { nums[left+k] = tmp[k] } @@ -41,14 +41,14 @@ func merge(nums []int, left, mid, right int) { /* Merge sort */ func mergeSort(nums []int, left, right int) { - // Termination condition + // Base case if left >= right { return } - // Divide and conquer stage + // Divide phase mid := left + (right - left) / 2 mergeSort(nums, left, mid) mergeSort(nums, mid+1, right) - // Merge stage + // Merge phase merge(nums, left, mid, right) } diff --git a/en/codes/go/chapter_sorting/merge_sort_test.go b/en/codes/go/chapter_sorting/merge_sort_test.go index 9c4932b91..e4ba5eafe 100644 --- a/en/codes/go/chapter_sorting/merge_sort_test.go +++ b/en/codes/go/chapter_sorting/merge_sort_test.go @@ -12,5 +12,5 @@ import ( func TestMergeSort(t *testing.T) { nums := []int{7, 3, 2, 6, 0, 1, 5, 4} mergeSort(nums, 0, len(nums)-1) - fmt.Println("After merge sort completes, nums = ", nums) + fmt.Println("After merge sort, nums = ", nums) } diff --git a/en/codes/go/chapter_sorting/quick_sort.go b/en/codes/go/chapter_sorting/quick_sort.go index b0f0f83aa..613029cc3 100644 --- a/en/codes/go/chapter_sorting/quick_sort.go +++ b/en/codes/go/chapter_sorting/quick_sort.go @@ -7,40 +7,40 @@ package chapter_sorting // Quick sort type quickSort struct{} -// Quick sort (recursion depth optimization) +// Quick sort (median pivot optimization) type quickSortMedian struct{} // Quick sort (recursion depth optimization) type quickSortTailCall struct{} -/* Sentinel partition */ +/* Two-pointer partitioning */ func (q *quickSort) partition(nums []int, left, right int) int { // Use nums[left] as the pivot i, j := left, right for i < j { for i < j && nums[j] >= nums[left] { - j-- // Search from right to left for the first element smaller than the pivot + j-- // Scan from right to left for the first element smaller than the pivot } for i < j && nums[i] <= nums[left] { - i++ // Search from left to right for the first element greater than the pivot + i++ // Scan from left to right for the first element larger than the pivot } - // Swap elements + // Swap the elements nums[i], nums[j] = nums[j], nums[i] } - // Swap the pivot to the boundary between the two subarrays + // Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i // Return the index of the pivot + return i // Return the pivot index } /* Quick sort */ func (q *quickSort) quickSort(nums []int, left, right int) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if left >= right { return } - // Sentinel partition + // Two-pointer partitioning pivot := q.partition(nums, left, right) - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays q.quickSort(nums, left, pivot-1) q.quickSort(nums, pivot+1, right) } @@ -49,82 +49,82 @@ func (q *quickSort) quickSort(nums []int, left, right int) { func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int { l, m, r := nums[left], nums[mid], nums[right] if (l <= m && m <= r) || (r <= m && m <= l) { - return mid // m is between l and r + return mid // m lies between l and r } if (m <= l && l <= r) || (r <= l && l <= m) { - return left // l is between m and r + return left // l lies between m and r } return right } -/* Sentinel partition (median of three) */ +/* Two-pointer partitioning (median of three)*/ func (q *quickSortMedian) partition(nums []int, left, right int) int { - // Use nums[left] as the pivot + // Select the median of the left, middle, and right elements as the pivot med := q.medianThree(nums, left, (left+right)/2, right) - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray nums[left], nums[med] = nums[med], nums[left] // Use nums[left] as the pivot i, j := left, right for i < j { for i < j && nums[j] >= nums[left] { - j-- // Search from right to left for the first element smaller than the pivot + j-- //Scan from right to left for the first element smaller than the pivot } for i < j && nums[i] <= nums[left] { - i++ // Search from left to right for the first element greater than the pivot + i++ //Scan from left to right for the first element larger than the pivot } - // Swap elements + //Swap the elements nums[i], nums[j] = nums[j], nums[i] } - // Swap the pivot to the boundary between the two subarrays + //Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i // Return the index of the pivot + return i //Return the pivot index } /* Quick sort */ func (q *quickSortMedian) quickSort(nums []int, left, right int) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if left >= right { return } - // Sentinel partition + // Two-pointer partitioning pivot := q.partition(nums, left, right) - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays q.quickSort(nums, left, pivot-1) q.quickSort(nums, pivot+1, right) } -/* Sentinel partition */ +/* Two-pointer partitioning */ func (q *quickSortTailCall) partition(nums []int, left, right int) int { // Use nums[left] as the pivot i, j := left, right for i < j { for i < j && nums[j] >= nums[left] { - j-- // Search from right to left for the first element smaller than the pivot + j-- // Scan from right to left for the first element smaller than the pivot } for i < j && nums[i] <= nums[left] { - i++ // Search from left to right for the first element greater than the pivot + i++ // Scan from left to right for the first element larger than the pivot } - // Swap elements + // Swap the elements nums[i], nums[j] = nums[j], nums[i] } - // Swap the pivot to the boundary between the two subarrays + // Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i // Return the index of the pivot + return i // Return the pivot index } -/* Quick sort (recursion depth optimization) */ +/* Quick sort (recursion depth optimization)*/ func (q *quickSortTailCall) quickSort(nums []int, left, right int) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element for left < right { - // Sentinel partition operation + // Perform two-pointer partitioning pivot := q.partition(nums, left, right) - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if pivot-left < right-pivot { q.quickSort(nums, left, pivot-1) // Recursively sort the left subarray - left = pivot + 1 // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1 // The remaining unsorted range is [pivot + 1, right] } else { q.quickSort(nums, pivot+1, right) // Recursively sort the right subarray - right = pivot - 1 // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1 // The remaining unsorted range is [left, pivot - 1] } } } diff --git a/en/codes/go/chapter_sorting/quick_sort_test.go b/en/codes/go/chapter_sorting/quick_sort_test.go index 1e75185a2..cc533f7dc 100644 --- a/en/codes/go/chapter_sorting/quick_sort_test.go +++ b/en/codes/go/chapter_sorting/quick_sort_test.go @@ -14,10 +14,10 @@ func TestQuickSort(t *testing.T) { q := quickSort{} nums := []int{4, 1, 3, 1, 5, 2} q.quickSort(nums, 0, len(nums)-1) - fmt.Println("After quick sort completes, nums = ", nums) + fmt.Println("After quick sort, nums = ", nums) } -// Quick sort (recursion depth optimization) +// Quick sort (median pivot optimization) func TestQuickSortMedian(t *testing.T) { q := quickSortMedian{} nums := []int{4, 1, 3, 1, 5, 2} diff --git a/en/codes/go/chapter_sorting/radix_sort.go b/en/codes/go/chapter_sorting/radix_sort.go index a0cd41855..4623f5bfb 100644 --- a/en/codes/go/chapter_sorting/radix_sort.go +++ b/en/codes/go/chapter_sorting/radix_sort.go @@ -6,35 +6,35 @@ package chapter_sorting import "math" -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ func digit(num, exp int) int { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (num / exp) % 10 } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ func countingSortDigit(nums []int, exp int) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 counter := make([]int, 10) n := len(nums) - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for i := 0; i < n; i++ { - d := digit(nums[i], exp) // Get the k-th digit of nums[i], noted as d - counter[d]++ // Count the occurrence of digit d + d := digit(nums[i], exp) // Get the k-th digit of nums[i] and store it in d + counter[d]++ // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for i := 1; i < 10; i++ { counter[i] += counter[i-1] } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts res := make([]int, n) for i := n - 1; i >= 0; i-- { d := digit(nums[i], exp) - j := counter[d] - 1 // Get the index j for d in the array + j := counter[d] - 1 // Get the destination index j for digit d in the array res[j] = nums[i] // Place the current element at index j - counter[d]-- // Decrease the count of d by 1 + counter[d]-- // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for i := 0; i < n; i++ { nums[i] = res[i] } @@ -42,19 +42,19 @@ func countingSortDigit(nums []int, exp int) { /* Radix sort */ func radixSort(nums []int) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits max := math.MinInt for _, num := range nums { if num > max { max = num } } - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for exp := 1; max >= exp; exp *= 10 { - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, exp) } } diff --git a/en/codes/go/chapter_sorting/radix_sort_test.go b/en/codes/go/chapter_sorting/radix_sort_test.go index b3a550562..74617b4cc 100644 --- a/en/codes/go/chapter_sorting/radix_sort_test.go +++ b/en/codes/go/chapter_sorting/radix_sort_test.go @@ -14,5 +14,5 @@ func TestRadixSort(t *testing.T) { nums := []int{10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996} radixSort(nums) - fmt.Println("After radix sort completes, nums = ", nums) + fmt.Println("After radix sort, nums = ", nums) } diff --git a/en/codes/go/chapter_sorting/selection_sort.go b/en/codes/go/chapter_sorting/selection_sort.go index ecc83849e..32bb809e0 100644 --- a/en/codes/go/chapter_sorting/selection_sort.go +++ b/en/codes/go/chapter_sorting/selection_sort.go @@ -7,9 +7,9 @@ package chapter_sorting /* Selection sort */ func selectionSort(nums []int) { n := len(nums) - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for i := 0; i < n-1; i++ { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range k := i for j := i + 1; j < n; j++ { if nums[j] < nums[k] { @@ -17,7 +17,7 @@ func selectionSort(nums []int) { k = j } } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range nums[i], nums[k] = nums[k], nums[i] } diff --git a/en/codes/go/chapter_sorting/selection_sort_test.go b/en/codes/go/chapter_sorting/selection_sort_test.go index 88e0723b4..3876a7489 100644 --- a/en/codes/go/chapter_sorting/selection_sort_test.go +++ b/en/codes/go/chapter_sorting/selection_sort_test.go @@ -12,5 +12,5 @@ import ( func TestSelectionSort(t *testing.T) { nums := []int{4, 1, 3, 1, 5, 2} selectionSort(nums) - fmt.Println("After selection sort completes, nums = ", nums) + fmt.Println("After selection sort, nums = ", nums) } diff --git a/en/codes/go/chapter_stack_and_queue/array_deque.go b/en/codes/go/chapter_stack_and_queue/array_deque.go index 6a5685d0d..444d6ddd2 100644 --- a/en/codes/go/chapter_stack_and_queue/array_deque.go +++ b/en/codes/go/chapter_stack_and_queue/array_deque.go @@ -6,15 +6,15 @@ package chapter_stack_and_queue import "fmt" -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ type arrayDeque struct { - nums []int // Array for storing double-ended queue elements - front int // Front pointer, points to the front of the queue element - queSize int // Double-ended queue length - queCapacity int // Queue capacity (maximum number of elements) + nums []int // Array that stores the deque elements + front int // Front pointer, pointing to the first element + queSize int // Deque size + queCapacity int // Queue capacity (the maximum number of elements) } -/* Access front of the queue element */ +/* Initialize the queue */ func newArrayDeque(queCapacity int) *arrayDeque { return &arrayDeque{ nums: make([]int, queCapacity), @@ -24,64 +24,64 @@ func newArrayDeque(queCapacity int) *arrayDeque { } } -/* Get the length of the double-ended queue */ +/* Get the deque size */ func (q *arrayDeque) size() int { return q.queSize } -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ func (q *arrayDeque) isEmpty() bool { return q.queSize == 0 } -/* Calculate circular array index */ +/* Calculate an index in the circular array */ func (q *arrayDeque) index(i int) int { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end return (i + q.queCapacity) % q.queCapacity } -/* Front of the queue enqueue */ +/* Enqueue at the front */ func (q *arrayDeque) pushFirst(num int) { if q.queSize == q.queCapacity { - fmt.Println("Double-ended queue is full") + fmt.Println("Deque is full") return } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front pointer one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array q.front = q.index(q.front - 1) - // Add num to front of queue + // Add num to the front q.nums[q.front] = num q.queSize++ } -/* Rear of the queue enqueue */ +/* Enqueue at the rear */ func (q *arrayDeque) pushLast(num int) { if q.queSize == q.queCapacity { - fmt.Println("Double-ended queue is full") + fmt.Println("Deque is full") return } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear pointer, which points one position past the last element rear := q.index(q.front + q.queSize) - // Front pointer moves one position backward + // Add num to the rear q.nums[rear] = num q.queSize++ } -/* Rear of the queue dequeue */ +/* Dequeue from the front */ func (q *arrayDeque) popFirst() any { num := q.peekFirst() if num == nil { return nil } - // Move front pointer backward by one position + // Advance the front pointer by one position q.front = q.index(q.front + 1) q.queSize-- return num } -/* Access rear of the queue element */ +/* Dequeue from the rear */ func (q *arrayDeque) popLast() any { num := q.peekLast() if num == nil { @@ -91,7 +91,7 @@ func (q *arrayDeque) popLast() any { return num } -/* Return list for printing */ +/* Access the front element */ func (q *arrayDeque) peekFirst() any { if q.isEmpty() { return nil @@ -99,19 +99,19 @@ func (q *arrayDeque) peekFirst() any { return q.nums[q.front] } -/* Driver Code */ +/* Access the rear element */ func (q *arrayDeque) peekLast() any { if q.isEmpty() { return nil } - // Initialize double-ended queue + // Calculate the index of the last element last := q.index(q.front + q.queSize - 1) return q.nums[last] } -/* Get Slice for printing */ +/* Get a slice for printing */ func (q *arrayDeque) toSlice() []int { - // Elements enqueue + // Convert only the list elements within the occupied range res := make([]int, q.queSize) for i, j := 0, q.front; i < q.queSize; i++ { res[i] = q.nums[q.index(j)] diff --git a/en/codes/go/chapter_stack_and_queue/array_queue.go b/en/codes/go/chapter_stack_and_queue/array_queue.go index 735adc5d5..fea4e058b 100644 --- a/en/codes/go/chapter_stack_and_queue/array_queue.go +++ b/en/codes/go/chapter_stack_and_queue/array_queue.go @@ -4,15 +4,15 @@ package chapter_stack_and_queue -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ type arrayQueue struct { - nums []int // Array for storing queue elements - front int // Front pointer, points to the front of the queue element - queSize int // Queue length - queCapacity int // Queue capacity (maximum number of elements) + nums []int // Array that stores the queue elements + front int // Front pointer, pointing to the first element + queSize int // Queue size + queCapacity int // Queue capacity (the maximum number of elements) } -/* Access front of the queue element */ +/* Initialize the queue */ func newArrayQueue(queCapacity int) *arrayQueue { return &arrayQueue{ nums: make([]int, queCapacity), @@ -22,26 +22,26 @@ func newArrayQueue(queCapacity int) *arrayQueue { } } -/* Get the length of the queue */ +/* Get the queue size */ func (q *arrayQueue) size() int { return q.queSize } -/* Check if the queue is empty */ +/* Check whether the queue is empty */ func (q *arrayQueue) isEmpty() bool { return q.queSize == 0 } /* Enqueue */ func (q *arrayQueue) push(num int) { - // When rear == queCapacity, queue is full + // The queue is full when queSize == queCapacity if q.queSize == q.queCapacity { return } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear pointer, which points one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array rear := (q.front + q.queSize) % q.queCapacity - // Front pointer moves one position backward + // Add num to the rear q.nums[rear] = num q.queSize++ } @@ -53,13 +53,13 @@ func (q *arrayQueue) pop() any { return nil } - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front pointer by one position, wrapping to the beginning if it passes the end q.front = (q.front + 1) % q.queCapacity q.queSize-- return num } -/* Return list for printing */ +/* Access the front element */ func (q *arrayQueue) peek() any { if q.isEmpty() { return nil @@ -67,7 +67,7 @@ func (q *arrayQueue) peek() any { return q.nums[q.front] } -/* Get Slice for printing */ +/* Get a slice for printing */ func (q *arrayQueue) toSlice() []int { rear := (q.front + q.queSize) if rear >= q.queCapacity { diff --git a/en/codes/go/chapter_stack_and_queue/array_stack.go b/en/codes/go/chapter_stack_and_queue/array_stack.go index ca5add85c..6110c7825 100644 --- a/en/codes/go/chapter_stack_and_queue/array_stack.go +++ b/en/codes/go/chapter_stack_and_queue/array_stack.go @@ -4,32 +4,32 @@ package chapter_stack_and_queue -/* Stack based on array implementation */ +/* Stack implemented with an array */ type arrayStack struct { data []int // Data } -/* Access top of the stack element */ +/* Initialize the stack */ func newArrayStack() *arrayStack { return &arrayStack{ - // Set stack length to 0, capacity to 16 + // Set the stack length to 0 and its capacity to 16 data: make([]int, 0, 16), } } -/* Stack length */ +/* Stack size */ func (s *arrayStack) size() int { return len(s.data) } -/* Is stack empty */ +/* Check whether the stack is empty */ func (s *arrayStack) isEmpty() bool { return s.size() == 0 } /* Push */ func (s *arrayStack) push(v int) { - // Slice will automatically expand + // Appending grows the slice capacity automatically when needed s.data = append(s.data, v) } @@ -40,7 +40,7 @@ func (s *arrayStack) pop() any { return val } -/* Get stack top element */ +/* Get the top element */ func (s *arrayStack) peek() any { if s.isEmpty() { return nil @@ -49,7 +49,7 @@ func (s *arrayStack) peek() any { return val } -/* Get Slice for printing */ +/* Get a slice for printing */ func (s *arrayStack) toSlice() []int { return s.data } diff --git a/en/codes/go/chapter_stack_and_queue/deque_test.go b/en/codes/go/chapter_stack_and_queue/deque_test.go index 798eaf184..354f4f76f 100644 --- a/en/codes/go/chapter_stack_and_queue/deque_test.go +++ b/en/codes/go/chapter_stack_and_queue/deque_test.go @@ -13,90 +13,90 @@ import ( ) func TestDeque(t *testing.T) { - /* Get the length of the double-ended queue */ - // In Go, use list as deque + /* Initialize the deque */ + // In Go, list can be used as a deque deque := list.New() - /* Elements enqueue */ + /* Enqueue elements */ deque.PushBack(2) deque.PushBack(5) deque.PushBack(4) deque.PushFront(3) deque.PushFront(1) - fmt.Print("Double-ended queue deque = ") + fmt.Print("Deque deque = ") PrintList(deque) - /* Update element */ + /* Access an element */ front := deque.Front() fmt.Println("Front element front =", front.Value) rear := deque.Back() fmt.Println("Rear element rear =", rear.Value) - /* Element dequeue */ + /* Dequeue an element */ deque.Remove(front) - fmt.Print("Front dequeue element front = ", front.Value, ", after front dequeue, deque = ") + fmt.Print("Element dequeued from the front, front = ", front.Value, ", after dequeuing from the front, deque = ") PrintList(deque) deque.Remove(rear) - fmt.Print("Rear dequeue element rear = ", rear.Value, ", after rear dequeue, deque = ") + fmt.Print("Element dequeued from the rear, rear = ", rear.Value, ", after dequeuing from the rear, deque = ") PrintList(deque) - /* Get the length of the double-ended queue */ + /* Get the deque size */ size := deque.Len() - fmt.Println("Deque length size =", size) + fmt.Println("Deque size =", size) - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ isEmpty := deque.Len() == 0 - fmt.Println("Is deque empty =", isEmpty) + fmt.Println("Is the deque empty =", isEmpty) } func TestArrayDeque(t *testing.T) { - /* Get the length of the double-ended queue */ - // In Go, use list as deque + /* Initialize the deque */ + // Use the custom circular-array deque implementation deque := newArrayDeque(16) - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(3) deque.pushLast(2) deque.pushLast(5) - fmt.Print("Double-ended queue deque = ") + fmt.Print("Deque deque = ") PrintSlice(deque.toSlice()) - /* Update element */ + /* Access an element */ peekFirst := deque.peekFirst() fmt.Println("Front element peekFirst =", peekFirst) peekLast := deque.peekLast() fmt.Println("Rear element peekLast =", peekLast) - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(4) - fmt.Print("After element 4 enqueues at rear, deque = ") + fmt.Print("After enqueuing element 4 at the rear, deque = ") PrintSlice(deque.toSlice()) deque.pushFirst(1) - fmt.Print("After element 1 enqueues at front, deque = ") + fmt.Print("After enqueuing element 1 at the front, deque = ") PrintSlice(deque.toSlice()) - /* Element dequeue */ + /* Dequeue an element */ popFirst := deque.popFirst() - fmt.Print("Front dequeue element popFirst = ", popFirst, ", after front dequeue, deque = ") + fmt.Print("Element dequeued from the front, popFirst = ", popFirst, ", after dequeuing from the front, deque = ") PrintSlice(deque.toSlice()) popLast := deque.popLast() - fmt.Print("Back dequeue element popLast = ", popLast, ", after rear dequeue, deque = ") + fmt.Print("Element dequeued from the rear, popLast = ", popLast, ", after dequeuing from the rear, deque = ") PrintSlice(deque.toSlice()) - /* Get the length of the double-ended queue */ + /* Get the deque size */ size := deque.size() - fmt.Println("Deque length size =", size) + fmt.Println("Deque size =", size) - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ isEmpty := deque.isEmpty() - fmt.Println("Is deque empty =", isEmpty) + fmt.Println("Is the deque empty =", isEmpty) } func TestLinkedListDeque(t *testing.T) { - // Access front of the queue element + // Initialize the queue deque := newLinkedListDeque() - // Elements enqueue + // Enqueue elements deque.pushLast(2) deque.pushLast(5) deque.pushLast(4) @@ -105,33 +105,33 @@ func TestLinkedListDeque(t *testing.T) { fmt.Print("Deque deque = ") PrintList(deque.toList()) - // Return list for printing + // Access the front element front := deque.peekFirst() fmt.Println("Front element front =", front) rear := deque.peekLast() fmt.Println("Rear element rear =", rear) - // Element dequeue + // Dequeue an element popFirst := deque.popFirst() - fmt.Print("Front dequeue element popFirst = ", popFirst, ", after front dequeue, deque = ") + fmt.Print("Element dequeued from the front, popFirst = ", popFirst, ", after dequeuing from the front, deque = ") PrintList(deque.toList()) popLast := deque.popLast() - fmt.Print("Back dequeue element popLast = ", popLast, ", after rear dequeue, deque = ") + fmt.Print("Element dequeued from the rear, popLast = ", popLast, ", after dequeuing from the rear, deque = ") PrintList(deque.toList()) - // Get queue length + // Get the deque size size := deque.size() - fmt.Println("Queue length size =", size) + fmt.Println("Deque size =", size) - // Check if empty + // Check whether it is empty isEmpty := deque.isEmpty() - fmt.Println("Is queue empty =", isEmpty) + fmt.Println("Is the deque empty =", isEmpty) } -// BenchmarkLinkedListDeque 67.92 ns/op in Mac M1 Pro +// BenchmarkLinkedListDeque: 67.92 ns/op on a Mac M1 Pro func BenchmarkLinkedListDeque(b *testing.B) { deque := newLinkedListDeque() - // use b.N for looping + // Use b.N as the iteration count for i := 0; i < b.N; i++ { deque.pushLast(777) } diff --git a/en/codes/go/chapter_stack_and_queue/linkedlist_deque.go b/en/codes/go/chapter_stack_and_queue/linkedlist_deque.go index d93d04023..a20ca1ad9 100644 --- a/en/codes/go/chapter_stack_and_queue/linkedlist_deque.go +++ b/en/codes/go/chapter_stack_and_queue/linkedlist_deque.go @@ -8,30 +8,30 @@ import ( "container/list" ) -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ type linkedListDeque struct { - // Use built-in package list + // Use the standard library list package data *list.List } -/* Initialize deque */ +/* Initialize the deque */ func newLinkedListDeque() *linkedListDeque { return &linkedListDeque{ data: list.New(), } } -/* Front element enqueue */ +/* Enqueue an element at the front */ func (s *linkedListDeque) pushFirst(value any) { s.data.PushFront(value) } -/* Rear element enqueue */ +/* Enqueue an element at the rear */ func (s *linkedListDeque) pushLast(value any) { s.data.PushBack(value) } -/* Check if the double-ended queue is empty */ +/* Dequeue the front element */ func (s *linkedListDeque) popFirst() any { if s.isEmpty() { return nil @@ -41,7 +41,7 @@ func (s *linkedListDeque) popFirst() any { return e.Value } -/* Rear element dequeue */ +/* Dequeue the rear element */ func (s *linkedListDeque) popLast() any { if s.isEmpty() { return nil @@ -51,7 +51,7 @@ func (s *linkedListDeque) popLast() any { return e.Value } -/* Return list for printing */ +/* Access the front element */ func (s *linkedListDeque) peekFirst() any { if s.isEmpty() { return nil @@ -60,7 +60,7 @@ func (s *linkedListDeque) peekFirst() any { return e.Value } -/* Driver Code */ +/* Access the rear element */ func (s *linkedListDeque) peekLast() any { if s.isEmpty() { return nil @@ -69,17 +69,17 @@ func (s *linkedListDeque) peekLast() any { return e.Value } -/* Get the length of the queue */ +/* Get the queue size */ func (s *linkedListDeque) size() int { return s.data.Len() } -/* Check if the queue is empty */ +/* Check whether the queue is empty */ func (s *linkedListDeque) isEmpty() bool { return s.data.Len() == 0 } -/* Get List for printing */ +/* Get the list for printing */ func (s *linkedListDeque) toList() *list.List { return s.data } diff --git a/en/codes/go/chapter_stack_and_queue/linkedlist_queue.go b/en/codes/go/chapter_stack_and_queue/linkedlist_queue.go index 579e3c18e..5c30b5b93 100644 --- a/en/codes/go/chapter_stack_and_queue/linkedlist_queue.go +++ b/en/codes/go/chapter_stack_and_queue/linkedlist_queue.go @@ -8,13 +8,13 @@ import ( "container/list" ) -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ type linkedListQueue struct { - // Use built-in package list to implement queue + // Use the standard library list package to implement the queue data *list.List } -/* Access front of the queue element */ +/* Initialize the queue */ func newLinkedListQueue() *linkedListQueue { return &linkedListQueue{ data: list.New(), @@ -36,7 +36,7 @@ func (s *linkedListQueue) pop() any { return e.Value } -/* Return list for printing */ +/* Access the front element */ func (s *linkedListQueue) peek() any { if s.isEmpty() { return nil @@ -45,17 +45,17 @@ func (s *linkedListQueue) peek() any { return e.Value } -/* Get the length of the queue */ +/* Get the queue size */ func (s *linkedListQueue) size() int { return s.data.Len() } -/* Check if the queue is empty */ +/* Check whether the queue is empty */ func (s *linkedListQueue) isEmpty() bool { return s.data.Len() == 0 } -/* Get List for printing */ +/* Get the list for printing */ func (s *linkedListQueue) toList() *list.List { return s.data } diff --git a/en/codes/go/chapter_stack_and_queue/linkedlist_stack.go b/en/codes/go/chapter_stack_and_queue/linkedlist_stack.go index 36c48f468..c2b85e793 100644 --- a/en/codes/go/chapter_stack_and_queue/linkedlist_stack.go +++ b/en/codes/go/chapter_stack_and_queue/linkedlist_stack.go @@ -8,13 +8,13 @@ import ( "container/list" ) -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ type linkedListStack struct { - // Use built-in package list to implement stack + // Use the standard library list package to implement the stack data *list.List } -/* Access top of the stack element */ +/* Initialize the stack */ func newLinkedListStack() *linkedListStack { return &linkedListStack{ data: list.New(), @@ -36,7 +36,7 @@ func (s *linkedListStack) pop() any { return e.Value } -/* Return list for printing */ +/* Access the top element */ func (s *linkedListStack) peek() any { if s.isEmpty() { return nil @@ -45,17 +45,17 @@ func (s *linkedListStack) peek() any { return e.Value } -/* Get the length of the stack */ +/* Get the stack size */ func (s *linkedListStack) size() int { return s.data.Len() } -/* Check if the stack is empty */ +/* Check whether the stack is empty */ func (s *linkedListStack) isEmpty() bool { return s.data.Len() == 0 } -/* Get List for printing */ +/* Get the list for printing */ func (s *linkedListStack) toList() *list.List { return s.data } diff --git a/en/codes/go/chapter_stack_and_queue/queue_test.go b/en/codes/go/chapter_stack_and_queue/queue_test.go index 7e7a7c088..7a5fdf6d9 100644 --- a/en/codes/go/chapter_stack_and_queue/queue_test.go +++ b/en/codes/go/chapter_stack_and_queue/queue_test.go @@ -13,11 +13,11 @@ import ( ) func TestQueue(t *testing.T) { - /* Access front of the queue element */ - // In Go, use list as queue + /* Initialize the queue */ + // In Go, use list as a queue queue := list.New() - /* Elements enqueue */ + /* Enqueue elements */ queue.PushBack(1) queue.PushBack(3) queue.PushBack(2) @@ -26,35 +26,35 @@ func TestQueue(t *testing.T) { fmt.Print("Queue queue = ") PrintList(queue) - /* Return list for printing */ + /* Access the front element */ peek := queue.Front() fmt.Println("Front element peek =", peek.Value) - /* Element dequeue */ + /* Dequeue an element */ pop := queue.Front() queue.Remove(pop) - fmt.Print("Dequeue element pop = ", pop.Value, ", after dequeue, queue = ") + fmt.Print("Dequeued element pop = ", pop.Value, ", after dequeuing, queue = ") PrintList(queue) - /* Get the length of the queue */ + /* Get the queue size */ size := queue.Len() - fmt.Println("Queue length size =", size) + fmt.Println("Queue size =", size) - /* Check if the queue is empty */ + /* Check whether the queue is empty */ isEmpty := queue.Len() == 0 - fmt.Println("Is queue empty =", isEmpty) + fmt.Println("Is the queue empty =", isEmpty) } func TestArrayQueue(t *testing.T) { - // Initialize queue using queue's common interface + // Initialize the queue and use its standard queue operations capacity := 10 queue := newArrayQueue(capacity) if queue.pop() != nil { t.Errorf("want:%v,got:%v", nil, queue.pop()) } - // Elements enqueue + // Enqueue elements queue.push(1) queue.push(3) queue.push(2) @@ -63,37 +63,37 @@ func TestArrayQueue(t *testing.T) { fmt.Print("Queue queue = ") PrintSlice(queue.toSlice()) - // Return list for printing + // Access the front element peek := queue.peek() fmt.Println("Front element peek =", peek) - // Element dequeue + // Dequeue an element pop := queue.pop() - fmt.Print("Dequeue element pop = ", pop, ", after dequeue, queue = ") + fmt.Print("Dequeued element pop = ", pop, ", after dequeuing, queue = ") PrintSlice(queue.toSlice()) - // Get queue length + // Get the queue size size := queue.size() - fmt.Println("Queue length size =", size) + fmt.Println("Queue size =", size) - // Check if empty + // Check whether it is empty isEmpty := queue.isEmpty() - fmt.Println("Is queue empty =", isEmpty) + fmt.Println("Is the queue empty =", isEmpty) - /* Test circular array */ + /* Test the circular array */ for i := 0; i < 10; i++ { queue.push(i) queue.pop() - fmt.Print("Round ", i, " enqueue + dequeue, queue =") + fmt.Print("Round #", i, ": after enqueue + dequeue, queue =") PrintSlice(queue.toSlice()) } } func TestLinkedListQueue(t *testing.T) { - // Initialize queue + // Initialize the queue queue := newLinkedListQueue() - // Elements enqueue + // Enqueue elements queue.push(1) queue.push(3) queue.push(2) @@ -102,29 +102,29 @@ func TestLinkedListQueue(t *testing.T) { fmt.Print("Queue queue = ") PrintList(queue.toList()) - // Return list for printing + // Access the front element peek := queue.peek() fmt.Println("Front element peek =", peek) - // Element dequeue + // Dequeue an element pop := queue.pop() - fmt.Print("Dequeue element pop = ", pop, ", after dequeue, queue = ") + fmt.Print("Dequeued element pop = ", pop, ", after dequeuing, queue = ") PrintList(queue.toList()) - // Get queue length + // Get the queue size size := queue.size() - fmt.Println("Queue length size =", size) + fmt.Println("Queue size =", size) - // Check if empty + // Check whether it is empty isEmpty := queue.isEmpty() - fmt.Println("Is queue empty =", isEmpty) + fmt.Println("Is the queue empty =", isEmpty) } -// BenchmarkArrayQueue 8 ns/op in Mac M1 Pro +// BenchmarkArrayQueue: 8 ns/op on a Mac M1 Pro func BenchmarkArrayQueue(b *testing.B) { capacity := 1000 queue := newArrayQueue(capacity) - // use b.N for looping + // Use b.N as the iteration count for i := 0; i < b.N; i++ { queue.push(777) } @@ -133,10 +133,10 @@ func BenchmarkArrayQueue(b *testing.B) { } } -// BenchmarkLinkedQueue 62.66 ns/op in Mac M1 Pro +// BenchmarkLinkedQueue: 62.66 ns/op on a Mac M1 Pro func BenchmarkLinkedQueue(b *testing.B) { queue := newLinkedListQueue() - // use b.N for looping + // Use b.N as the iteration count for i := 0; i < b.N; i++ { queue.push(777) } diff --git a/en/codes/go/chapter_stack_and_queue/stack_test.go b/en/codes/go/chapter_stack_and_queue/stack_test.go index 0fa825152..36dc1856d 100644 --- a/en/codes/go/chapter_stack_and_queue/stack_test.go +++ b/en/codes/go/chapter_stack_and_queue/stack_test.go @@ -12,11 +12,11 @@ import ( ) func TestStack(t *testing.T) { - /* Access top of the stack element */ - // In Go, recommended to use Slice as stack + /* Initialize the stack */ + // In Go, a slice is the recommended way to implement a stack var stack []int - /* Elements push onto stack */ + /* Push elements onto the stack */ stack = append(stack, 1) stack = append(stack, 3) stack = append(stack, 2) @@ -25,30 +25,30 @@ func TestStack(t *testing.T) { fmt.Print("Stack stack = ") PrintSlice(stack) - /* Return list for printing */ + /* Access the top element */ peek := stack[len(stack)-1] - fmt.Println("Stack top element peek =", peek) + fmt.Println("Top element peek =", peek) - /* Element pop from stack */ + /* Pop an element from the stack */ pop := stack[len(stack)-1] stack = stack[:len(stack)-1] - fmt.Print("Pop element pop = ", pop, ", after pop, stack = ") + fmt.Print("Popped element pop = ", pop, ", after popping, stack = ") PrintSlice(stack) - /* Get the length of the stack */ + /* Get the stack size */ size := len(stack) - fmt.Println("Stack length size =", size) + fmt.Println("Stack size =", size) - /* Check if empty */ + /* Check whether it is empty */ isEmpty := len(stack) == 0 - fmt.Println("Is stack empty =", isEmpty) + fmt.Println("Is the stack empty =", isEmpty) } func TestArrayStack(t *testing.T) { - // Initialize stack using interface + // Initialize the stack for use through its stack operations stack := newArrayStack() - // Elements push onto stack + // Push elements onto the stack stack.push(1) stack.push(3) stack.push(2) @@ -57,28 +57,28 @@ func TestArrayStack(t *testing.T) { fmt.Print("Stack stack = ") PrintSlice(stack.toSlice()) - // Return list for printing + // Access the top element peek := stack.peek() - fmt.Println("Stack top element peek =", peek) + fmt.Println("Top element peek =", peek) - // Element pop from stack + // Pop an element from the stack pop := stack.pop() - fmt.Print("Pop element pop = ", pop, ", after pop, stack = ") + fmt.Print("Popped element pop = ", pop, ", after popping, stack = ") PrintSlice(stack.toSlice()) - // Get the length of the stack + // Get the stack size size := stack.size() - fmt.Println("Stack length size =", size) + fmt.Println("Stack size =", size) - // Check if empty + // Check whether it is empty isEmpty := stack.isEmpty() - fmt.Println("Is stack empty =", isEmpty) + fmt.Println("Is the stack empty =", isEmpty) } func TestLinkedListStack(t *testing.T) { - // Access top of the stack element + // Initialize the stack stack := newLinkedListStack() - // Elements push onto stack + // Push elements onto the stack stack.push(1) stack.push(3) stack.push(2) @@ -87,28 +87,28 @@ func TestLinkedListStack(t *testing.T) { fmt.Print("Stack stack = ") PrintList(stack.toList()) - // Return list for printing + // Access the top element peek := stack.peek() - fmt.Println("Stack top element peek =", peek) + fmt.Println("Top element peek =", peek) - // Element pop from stack + // Pop an element from the stack pop := stack.pop() - fmt.Print("Pop element pop = ", pop, ", after pop, stack = ") + fmt.Print("Popped element pop = ", pop, ", after popping, stack = ") PrintList(stack.toList()) - // Get the length of the stack + // Get the stack size size := stack.size() - fmt.Println("Stack length size =", size) + fmt.Println("Stack size =", size) - // Check if empty + // Check whether it is empty isEmpty := stack.isEmpty() - fmt.Println("Is stack empty =", isEmpty) + fmt.Println("Is the stack empty =", isEmpty) } -// BenchmarkArrayStack 8 ns/op in Mac M1 Pro +// BenchmarkArrayStack: 8 ns/op on a Mac M1 Pro func BenchmarkArrayStack(b *testing.B) { stack := newArrayStack() - // use b.N for looping + // Use b.N as the iteration count for i := 0; i < b.N; i++ { stack.push(777) } @@ -117,10 +117,10 @@ func BenchmarkArrayStack(b *testing.B) { } } -// BenchmarkLinkedListStack 65.02 ns/op in Mac M1 Pro +// BenchmarkLinkedListStack: 65.02 ns/op on a Mac M1 Pro func BenchmarkLinkedListStack(b *testing.B) { stack := newLinkedListStack() - // use b.N for looping + // Use b.N as the iteration count for i := 0; i < b.N; i++ { stack.push(777) } diff --git a/en/codes/go/chapter_tree/array_binary_tree.go b/en/codes/go/chapter_tree/array_binary_tree.go index a251d01f5..1515fae7c 100644 --- a/en/codes/go/chapter_tree/array_binary_tree.go +++ b/en/codes/go/chapter_tree/array_binary_tree.go @@ -4,7 +4,7 @@ package chapter_tree -/* Binary tree class represented by array */ +/* Binary tree type using an array representation */ type arrayBinaryTree struct { tree []any } @@ -16,31 +16,31 @@ func newArrayBinaryTree(arr []any) *arrayBinaryTree { } } -/* List capacity */ +/* Length of the backing slice */ func (abt *arrayBinaryTree) size() int { return len(abt.tree) } -/* Get value of node at index i */ +/* Get the value of the node at index i */ func (abt *arrayBinaryTree) val(i int) any { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return nil to represent an empty position if i < 0 || i >= abt.size() { return nil } return abt.tree[i] } -/* Get index of left child node of node at index i */ +/* Get the left child index of the node at index i */ func (abt *arrayBinaryTree) left(i int) int { return 2*i + 1 } -/* Get index of right child node of node at index i */ +/* Get the right child index of the node at index i */ func (abt *arrayBinaryTree) right(i int) int { return 2*i + 2 } -/* Get index of parent node of node at index i */ +/* Get the parent index of the node at index i */ func (abt *arrayBinaryTree) parent(i int) int { return (i - 1) / 2 } @@ -48,7 +48,7 @@ func (abt *arrayBinaryTree) parent(i int) int { /* Level-order traversal */ func (abt *arrayBinaryTree) levelOrder() []any { var res []any - // Traverse array directly + // Traverse the array directly for i := 0; i < abt.size(); i++ { if abt.val(i) != nil { res = append(res, abt.val(i)) @@ -59,7 +59,7 @@ func (abt *arrayBinaryTree) levelOrder() []any { /* Depth-first traversal */ func (abt *arrayBinaryTree) dfs(i int, order string, res *[]any) { - // If empty position, return + // Return if this position is empty if abt.val(i) == nil { return } diff --git a/en/codes/go/chapter_tree/array_binary_tree_test.go b/en/codes/go/chapter_tree/array_binary_tree_test.go index 1126e0939..fee5096f5 100644 --- a/en/codes/go/chapter_tree/array_binary_tree_test.go +++ b/en/codes/go/chapter_tree/array_binary_tree_test.go @@ -12,36 +12,36 @@ import ( ) func TestArrayBinaryTree(t *testing.T) { - // Initialize binary tree - // Here we use a function to generate a binary tree directly from an array + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array arr := []any{1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15} root := SliceToTree(arr) - fmt.Println("\nInitialize binary tree") - fmt.Println("Array representation of binary tree:") + fmt.Println("\nInitialize the binary tree") + fmt.Println("Array representation of the binary tree:") fmt.Println(arr) - fmt.Println("Linked list representation of binary tree:") + fmt.Println("Linked representation of the binary tree:") PrintTree(root) - // Binary tree class represented by array + // Binary tree type using an array representation abt := newArrayBinaryTree(arr) - // Access node + // Access a node i := 1 l := abt.left(i) r := abt.right(i) p := abt.parent(i) - fmt.Println("\nCurrent node index is", i, ", value is", abt.val(i)) - fmt.Println("Its left child node index is", l, ", value is", abt.val(l)) - fmt.Println("Its right child node index is", r, ", value is", abt.val(r)) - fmt.Println("Its parent node index is", p, ", value is", abt.val(p)) + fmt.Println("\nCurrent node index:", i, "with value", abt.val(i)) + fmt.Println("Left child index:", l, "with value", abt.val(l)) + fmt.Println("Right child index:", r, "with value", abt.val(r)) + fmt.Println("Parent index:", p, "with value", abt.val(p)) - // Traverse tree + // Traverse the tree res := abt.levelOrder() - fmt.Println("\nLevel-order traversal is:", res) + fmt.Println("\nLevel-order traversal:", res) res = abt.preOrder() - fmt.Println("Preorder traversal is:", res) + fmt.Println("Preorder traversal:", res) res = abt.inOrder() - fmt.Println("Inorder traversal is:", res) + fmt.Println("Inorder traversal:", res) res = abt.postOrder() - fmt.Println("Postorder traversal is:", res) + fmt.Println("Postorder traversal:", res) } diff --git a/en/codes/go/chapter_tree/avl_tree.go b/en/codes/go/chapter_tree/avl_tree.go index 3a938c86e..edbeea7c8 100644 --- a/en/codes/go/chapter_tree/avl_tree.go +++ b/en/codes/go/chapter_tree/avl_tree.go @@ -16,20 +16,20 @@ func newAVLTree() *aVLTree { return &aVLTree{root: nil} } -/* Get node height */ +/* Get the node height */ func (t *aVLTree) height(node *TreeNode) int { - // Empty node height is -1, leaf node height is 0 + // A nil node has height -1; a leaf has height 0 if node != nil { return node.Height } return -1 } -/* Update node height */ +/* Update the node height */ func (t *aVLTree) updateHeight(node *TreeNode) { lh := t.height(node.Left) rh := t.height(node.Right) - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 if lh > rh { node.Height = lh + 1 } else { @@ -37,9 +37,9 @@ func (t *aVLTree) updateHeight(node *TreeNode) { } } -/* Get balance factor */ +/* Get the balance factor */ func (t *aVLTree) balanceFactor(node *TreeNode) int { - // Empty node balance factor is 0 + // A nil node has balance factor 0 if node == nil { return 0 } @@ -47,103 +47,103 @@ func (t *aVLTree) balanceFactor(node *TreeNode) int { return t.height(node.Left) - t.height(node.Right) } -/* Right rotation operation */ +/* Right rotation */ func (t *aVLTree) rightRotate(node *TreeNode) *TreeNode { child := node.Left grandChild := child.Right - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.Right = node node.Left = grandChild - // Update node height + // Update the node height t.updateHeight(node) t.updateHeight(child) - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child } -/* Left rotation operation */ +/* Left rotation */ func (t *aVLTree) leftRotate(node *TreeNode) *TreeNode { child := node.Right grandChild := child.Left - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.Left = node node.Right = grandChild - // Update node height + // Update the node height t.updateHeight(node) t.updateHeight(child) - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child } -/* Perform rotation operation to restore balance to this subtree */ +/* Perform rotations to restore balance in this subtree */ func (t *aVLTree) rotate(node *TreeNode) *TreeNode { - // Get balance factor of node - // Go recommends short variables, here bf refers to t.balanceFactor + // Get the balance factor of node + // Go favors short local variable names; bf holds the result of t.balanceFactor(node) bf := t.balanceFactor(node) - // Left-leaning tree + // Left-heavy tree if bf > 1 { if t.balanceFactor(node.Left) >= 0 { - // Right rotation + // Rotate right return t.rightRotate(node) } else { - // First left rotation then right rotation + // Rotate left, then right node.Left = t.leftRotate(node.Left) return t.rightRotate(node) } } - // Right-leaning tree + // Right-heavy tree if bf < -1 { if t.balanceFactor(node.Right) <= 0 { - // Left rotation + // Rotate left return t.leftRotate(node) } else { - // First right rotation then left rotation + // Rotate right, then left node.Right = t.rightRotate(node.Right) return t.leftRotate(node) } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node } -/* Insert node */ +/* Insert a node */ func (t *aVLTree) insert(val int) { t.root = t.insertHelper(t.root, val) } -/* Recursively insert node (helper function) */ +/* Insert a node recursively (helper function) */ func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode { if node == nil { return NewTreeNode(val) } - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if val < node.Val.(int) { node.Left = t.insertHelper(node.Left, val) } else if val > node.Val.(int) { node.Right = t.insertHelper(node.Right, val) } else { - // Duplicate node not inserted, return directly + // Return without inserting a duplicate node return node } - // Update node height + // Update the node height t.updateHeight(node) - /* 2. Perform rotation operation to restore balance to this subtree */ + /* 2. Perform rotations to restore balance in this subtree */ node = t.rotate(node) - // Return root node of subtree + // Return the subtree root return node } -/* Remove node */ +/* Remove a node */ func (t *aVLTree) remove(val int) { t.root = t.removeHelper(t.root, val) } -/* Recursively remove node (helper function) */ +/* Remove a node recursively (helper function) */ func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode { if node == nil { return nil } - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if val < node.Val.(int) { node.Left = t.removeHelper(node.Left, val) } else if val > node.Val.(int) { @@ -155,14 +155,14 @@ func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode { child = node.Right } if child == nil { - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return return nil } else { - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly node = child } } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value temp := node.Right for temp.Left != nil { temp = temp.Left @@ -171,30 +171,30 @@ func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode { node.Val = temp.Val } } - // Update node height + // Update the node height t.updateHeight(node) - /* 2. Perform rotation operation to restore balance to this subtree */ + /* 2. Perform rotations to restore balance in this subtree */ node = t.rotate(node) - // Return root node of subtree + // Return the subtree root return node } -/* Search node */ +/* Find a node */ func (t *aVLTree) search(val int) *TreeNode { cur := t.root - // Loop search, exit after passing leaf node + // Continue searching while the current node is not nil for cur != nil { if cur.Val.(int) < val { - // Target node is in cur's right subtree + // If the target node exists, it lies in the right subtree of cur cur = cur.Right } else if cur.Val.(int) > val { - // Target node is in cur's left subtree + // If the target node exists, it lies in the left subtree of cur cur = cur.Left } else { - // Found target node, exit loop + // The target node was found; exit the loop break } } - // Return target node + // Return the target node, or nil if it was not found return cur } diff --git a/en/codes/go/chapter_tree/avl_tree_test.go b/en/codes/go/chapter_tree/avl_tree_test.go index 4ea32f43e..f412eb773 100644 --- a/en/codes/go/chapter_tree/avl_tree_test.go +++ b/en/codes/go/chapter_tree/avl_tree_test.go @@ -12,10 +12,10 @@ import ( ) func TestAVLTree(t *testing.T) { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ tree := newAVLTree() - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted testInsert(tree, 1) testInsert(tree, 2) testInsert(tree, 3) @@ -27,28 +27,28 @@ func TestAVLTree(t *testing.T) { testInsert(tree, 10) testInsert(tree, 6) - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ testInsert(tree, 7) - /* Remove node */ - // Delete node with degree 1 - testRemove(tree, 8) // Delete node with degree 2 - testRemove(tree, 5) // Remove node with degree 1 - testRemove(tree, 4) // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + testRemove(tree, 8) // Remove a node with 0 children + testRemove(tree, 5) // Remove a node with 1 child + testRemove(tree, 4) // Remove a node with 2 children - /* Search node */ + /* Look up a node */ node := tree.search(7) - fmt.Printf("\nFound node object is %#v, node value = %d \n", node, node.Val) + fmt.Printf("\nFound node: %#v; node value = %d \n", node, node.Val) } func testInsert(tree *aVLTree, val int) { tree.insert(val) - fmt.Printf("\nAfter inserting node %d, AVL tree is \n", val) + fmt.Printf("\nAVL tree after inserting node %d: \n", val) PrintTree(tree.root) } func testRemove(tree *aVLTree, val int) { tree.remove(val) - fmt.Printf("\nAfter removing node %d, AVL tree is \n", val) + fmt.Printf("\nAVL tree after removing node %d: \n", val) PrintTree(tree.root) } diff --git a/en/codes/go/chapter_tree/binary_search_tree.go b/en/codes/go/chapter_tree/binary_search_tree.go index 7ee4e7df7..9e2b8b393 100644 --- a/en/codes/go/chapter_tree/binary_search_tree.go +++ b/en/codes/go/chapter_tree/binary_search_tree.go @@ -14,47 +14,47 @@ type binarySearchTree struct { func newBinarySearchTree() *binarySearchTree { bst := &binarySearchTree{} - // Initialize empty tree + // Initialize an empty tree bst.root = nil return bst } -/* Get root node */ +/* Get the root node */ func (bst *binarySearchTree) getRoot() *TreeNode { return bst.root } -/* Search node */ +/* Find a node */ func (bst *binarySearchTree) search(num int) *TreeNode { node := bst.root - // Loop search, exit after passing leaf node + // Continue searching while the current node is not nil for node != nil { if node.Val.(int) < num { - // Target node is in cur's right subtree + // If the target node exists, it lies in the right subtree of node node = node.Right } else if node.Val.(int) > num { - // Target node is in cur's left subtree + // If the target node exists, it lies in the left subtree of node node = node.Left } else { - // Found target node, exit loop + // The target node was found; exit the loop break } } - // Return target node + // Return the target node, or nil if it was not found return node } -/* Insert node */ +/* Insert a node */ func (bst *binarySearchTree) insert(num int) { cur := bst.root - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if cur == nil { bst.root = NewTreeNode(num) return } - // Node position before the node to be inserted + // Track the parent of the node to insert var pre *TreeNode = nil - // Loop search, exit after passing leaf node + // Continue searching while the current node is not nil for cur != nil { if cur.Val == num { return @@ -66,7 +66,7 @@ func (bst *binarySearchTree) insert(num int) { cur = cur.Left } } - // Insert node + // Insert a node node := NewTreeNode(num) if pre.Val.(int) < num { pre.Right = node @@ -75,43 +75,43 @@ func (bst *binarySearchTree) insert(num int) { } } -/* Remove node */ +/* Remove a node */ func (bst *binarySearchTree) remove(num int) { cur := bst.root - // If tree is empty, return directly + // Return immediately if the tree is empty if cur == nil { return } - // Node position before the node to be removed + // Track the parent of the node to remove var pre *TreeNode = nil - // Loop search, exit after passing leaf node + // Continue searching while the current node is not nil for cur != nil { if cur.Val == num { break } pre = cur if cur.Val.(int) < num { - // Node to be removed is in right subtree + // The node to remove is in the right subtree cur = cur.Right } else { - // Node to be removed is in left subtree + // The node to remove is in the left subtree cur = cur.Left } } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if cur == nil { return } - // Number of child nodes is 0 or 1 + // The node has 0 or 1 child if cur.Left == nil || cur.Right == nil { var child *TreeNode = nil - // Get child node of node to be removed + // Get the child of the node to remove if cur.Left != nil { child = cur.Left } else { child = cur.Right } - // Delete node cur + // Remove node cur if cur != bst.root { if pre.Left == cur { pre.Left = child @@ -119,24 +119,24 @@ func (bst *binarySearchTree) remove(num int) { pre.Right = child } } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root bst.root = child } - // Number of child nodes is 2 + // The node has 2 children } else { - // Get next node of node cur to be removed in in-order traversal + // Get the inorder successor of cur, the node to remove tmp := cur.Right for tmp.Left != nil { tmp = tmp.Left } - // Recursively delete node tmp + // Recursively remove node tmp bst.remove(tmp.Val.(int)) - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur.Val = tmp.Val } } -/* Print binary search tree */ +/* Print the binary search tree */ func (bst *binarySearchTree) print() { PrintTree(bst.root) } diff --git a/en/codes/go/chapter_tree/binary_search_tree_test.go b/en/codes/go/chapter_tree/binary_search_tree_test.go index 47de52e6f..3ed8e3992 100644 --- a/en/codes/go/chapter_tree/binary_search_tree_test.go +++ b/en/codes/go/chapter_tree/binary_search_tree_test.go @@ -12,34 +12,34 @@ import ( func TestBinarySearchTree(t *testing.T) { bst := newBinarySearchTree() nums := []int{8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15} - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree for _, num := range nums { bst.insert(num) } - fmt.Println("\nInitialized binary tree is:") + fmt.Println("\nInitial binary tree:") bst.print() - // Get root node + // Get the root node node := bst.getRoot() - fmt.Println("\nRoot node of binary tree is:", node.Val) + fmt.Println("\nRoot of the binary tree:", node.Val) - // Search node + // Find a node node = bst.search(7) - fmt.Println("Found node object is", node, ", node value =", node.Val) + fmt.Println("Found node:", node, ", node value =", node.Val) - // Insert node + // Insert a node bst.insert(16) - fmt.Println("\nAfter inserting node 16, binary tree is:") + fmt.Println("\nBinary tree after inserting node 16:") bst.print() - // Remove node + // Remove a node bst.remove(1) - fmt.Println("\nAfter removing node 1, binary tree is:") + fmt.Println("\nBinary tree after removing node 1:") bst.print() bst.remove(2) - fmt.Println("\nAfter removing node 2, binary tree is:") + fmt.Println("\nBinary tree after removing node 2:") bst.print() bst.remove(4) - fmt.Println("\nAfter removing node 4, binary tree is:") + fmt.Println("\nBinary tree after removing node 4:") bst.print() } diff --git a/en/codes/go/chapter_tree/binary_tree_bfs.go b/en/codes/go/chapter_tree/binary_tree_bfs.go index e63bebf7b..3b457ea5f 100644 --- a/en/codes/go/chapter_tree/binary_tree_bfs.go +++ b/en/codes/go/chapter_tree/binary_tree_bfs.go @@ -12,22 +12,22 @@ import ( /* Level-order traversal */ func levelOrder(root *TreeNode) []any { - // Initialize queue, add root node + // Initialize a queue and enqueue the root queue := list.New() queue.PushBack(root) - // Initialize a slice to save traversal sequence + // Initialize a slice to store the traversal sequence nums := make([]any, 0) for queue.Len() > 0 { - // Dequeue + // Dequeue from the queue node := queue.Remove(queue.Front()).(*TreeNode) - // Save node value + // Store the node value nums = append(nums, node.Val) if node.Left != nil { - // Left child node enqueue + // Enqueue the left child queue.PushBack(node.Left) } if node.Right != nil { - // Right child node enqueue + // Enqueue the right child queue.PushBack(node.Right) } } diff --git a/en/codes/go/chapter_tree/binary_tree_bfs_test.go b/en/codes/go/chapter_tree/binary_tree_bfs_test.go index 4a5f88718..e05b9356c 100644 --- a/en/codes/go/chapter_tree/binary_tree_bfs_test.go +++ b/en/codes/go/chapter_tree/binary_tree_bfs_test.go @@ -12,13 +12,13 @@ import ( ) func TestLevelOrder(t *testing.T) { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array root := SliceToTree([]any{1, 2, 3, 4, 5, 6, 7}) - fmt.Println("\nInitialize binary tree: ") + fmt.Println("\nInitialize the binary tree: ") PrintTree(root) // Level-order traversal nums := levelOrder(root) - fmt.Println("\nLevel-order traversal node print sequence =", nums) + fmt.Println("\nNode sequence from level-order traversal =", nums) } diff --git a/en/codes/go/chapter_tree/binary_tree_dfs.go b/en/codes/go/chapter_tree/binary_tree_dfs.go index 7a5bb7700..4b23004e1 100644 --- a/en/codes/go/chapter_tree/binary_tree_dfs.go +++ b/en/codes/go/chapter_tree/binary_tree_dfs.go @@ -15,7 +15,7 @@ func preOrder(node *TreeNode) { if node == nil { return } - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree nums = append(nums, node.Val) preOrder(node.Left) preOrder(node.Right) @@ -26,7 +26,7 @@ func inOrder(node *TreeNode) { if node == nil { return } - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(node.Left) nums = append(nums, node.Val) inOrder(node.Right) @@ -37,7 +37,7 @@ func postOrder(node *TreeNode) { if node == nil { return } - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(node.Left) postOrder(node.Right) nums = append(nums, node.Val) diff --git a/en/codes/go/chapter_tree/binary_tree_dfs_test.go b/en/codes/go/chapter_tree/binary_tree_dfs_test.go index 6bee7960f..f64b24259 100644 --- a/en/codes/go/chapter_tree/binary_tree_dfs_test.go +++ b/en/codes/go/chapter_tree/binary_tree_dfs_test.go @@ -12,24 +12,24 @@ import ( ) func TestPreInPostOrderTraversal(t *testing.T) { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array root := SliceToTree([]any{1, 2, 3, 4, 5, 6, 7}) - fmt.Println("\nInitialize binary tree: ") + fmt.Println("\nInitialize the binary tree: ") PrintTree(root) // Preorder traversal nums = nil preOrder(root) - fmt.Println("\nPre-order traversal node print sequence =", nums) + fmt.Println("\nNode sequence from preorder traversal =", nums) // Inorder traversal nums = nil inOrder(root) - fmt.Println("\nIn-order traversal node print sequence =", nums) + fmt.Println("\nNode sequence from inorder traversal =", nums) // Postorder traversal nums = nil postOrder(root) - fmt.Println("\nPost-order traversal node print sequence =", nums) + fmt.Println("\nNode sequence from postorder traversal =", nums) } diff --git a/en/codes/go/chapter_tree/binary_tree_test.go b/en/codes/go/chapter_tree/binary_tree_test.go index 3ee31a9aa..17f0ca53a 100644 --- a/en/codes/go/chapter_tree/binary_tree_test.go +++ b/en/codes/go/chapter_tree/binary_tree_test.go @@ -12,30 +12,30 @@ import ( ) func TestBinaryTree(t *testing.T) { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes n1 := NewTreeNode(1) n2 := NewTreeNode(2) n3 := NewTreeNode(3) n4 := NewTreeNode(4) n5 := NewTreeNode(5) - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1.Left = n2 n1.Right = n3 n2.Left = n4 n2.Right = n5 - fmt.Println("Initialize binary tree") + fmt.Println("Initialize the binary tree") PrintTree(n1) - /* Insert node P between n1 -> n2 */ - // Insert node + /* Insert and remove nodes */ + // Insert a node p := NewTreeNode(0) n1.Left = p p.Left = n2 - fmt.Println("After inserting node P") + fmt.Println("After inserting node p") PrintTree(n1) - // Remove node + // Remove a node n1.Left = n2 - fmt.Println("After removing node P") + fmt.Println("After removing node p") PrintTree(n1) } diff --git a/en/codes/go/pkg/list_node.go b/en/codes/go/pkg/list_node.go index 2b923f752..687dd9144 100644 --- a/en/codes/go/pkg/list_node.go +++ b/en/codes/go/pkg/list_node.go @@ -4,13 +4,13 @@ package pkg -// ListNode linked list node +// ListNode represents a linked list node. type ListNode struct { Next *ListNode Val int } -// NewListNode linked list node constructor +// NewListNode constructs a linked list node. func NewListNode(v int) *ListNode { return &ListNode{ Next: nil, @@ -18,9 +18,9 @@ func NewListNode(v int) *ListNode { } } -// ArrayToLinkedList deserialize array to linked list +// ArrayToLinkedList deserializes an array into a linked list. func ArrayToLinkedList(arr []int) *ListNode { - // dummy header of linked list + // Dummy head of the linked list dummy := NewListNode(0) node := dummy for _, val := range arr { diff --git a/en/codes/go/pkg/print_utils.go b/en/codes/go/pkg/print_utils.go index 1a71a7358..2367f6ed7 100644 --- a/en/codes/go/pkg/print_utils.go +++ b/en/codes/go/pkg/print_utils.go @@ -11,20 +11,20 @@ import ( "strings" ) -// PrintSlice print slice +// PrintSlice prints a slice. func PrintSlice[T any](nums []T) { fmt.Printf("%v", nums) fmt.Println() } -// PrintList print list +// PrintList prints a list. func PrintList(list *list.List) { if list.Len() == 0 { fmt.Print("[]\n") return } e := list.Front() - // Force conversion to string will affect efficiency + // Formatting values as strings adds overhead fmt.Print("[") for e.Next() != nil { fmt.Print(e.Value, " ") @@ -33,23 +33,23 @@ func PrintList(list *list.List) { fmt.Print(e.Value, "]\n") } -// PrintMap print hash table +// PrintMap prints a hash map. func PrintMap[K comparable, V any](m map[K]V) { for key, value := range m { fmt.Println(key, "->", value) } } -// PrintHeap print heap +// PrintHeap prints a heap. func PrintHeap(h []any) { - fmt.Printf("Heap array representation:") + fmt.Printf("Array representation of the heap:") fmt.Printf("%v", h) - fmt.Printf("\nTree representation of heap:\n") + fmt.Printf("\nTree representation of the heap:\n") root := SliceToTree(h) PrintTree(root) } -// PrintLinkedList print linked list +// PrintLinkedList prints a linked list. func PrintLinkedList(node *ListNode) { if node == nil { return @@ -63,12 +63,12 @@ func PrintLinkedList(node *ListNode) { fmt.Println(builder.String()) } -// PrintTree print binary tree +// PrintTree prints a binary tree. func PrintTree(root *TreeNode) { printTreeHelper(root, nil, false) } -// printTreeHelper print binary tree +// printTreeHelper prints a binary tree. // This tree printer is borrowed from TECHIE DELIGHT // https://www.techiedelight.com/c-program-print-binary-tree/ func printTreeHelper(root *TreeNode, prev *trunk, isRight bool) { diff --git a/en/codes/go/pkg/tree_node.go b/en/codes/go/pkg/tree_node.go index fe2860499..70cb2270e 100644 --- a/en/codes/go/pkg/tree_node.go +++ b/en/codes/go/pkg/tree_node.go @@ -4,15 +4,15 @@ package pkg -// TreeNode binary tree node +// TreeNode represents a binary tree node. type TreeNode struct { Val any // Node value Height int // Node height - Left *TreeNode // Reference to left child node - Right *TreeNode // Reference to right child node + Left *TreeNode // Reference to the left child + Right *TreeNode // Reference to the right child } -// NewTreeNode binary tree node constructor +// NewTreeNode constructs a binary tree node. func NewTreeNode(v any) *TreeNode { return &TreeNode{ Val: v, @@ -22,11 +22,11 @@ func NewTreeNode(v any) *TreeNode { } } -// For the serialization encoding rules, please refer to: -// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ -// Array representation of binary tree: +// For the serialization rules, see: +// https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ +// Array representation of the binary tree: // [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] -// Linked list representation of binary tree: +// Linked representation of the binary tree: // // /——— 15 // /——— 7 @@ -41,7 +41,7 @@ func NewTreeNode(v any) *TreeNode { // \——— 4 // \——— 8 -// SliceToTreeDFS deserialize list to binary tree: recursion +// SliceToTreeDFS recursively deserializes a slice into a binary tree. func SliceToTreeDFS(arr []any, i int) *TreeNode { if i < 0 || i >= len(arr) || arr[i] == nil { return nil @@ -52,12 +52,12 @@ func SliceToTreeDFS(arr []any, i int) *TreeNode { return root } -// SliceToTree deserialize slice to binary tree +// SliceToTree deserializes a slice into a binary tree. func SliceToTree(arr []any) *TreeNode { return SliceToTreeDFS(arr, 0) } -// TreeToSliceDFS serialize binary tree to slice: recursion +// TreeToSliceDFS recursively serializes a binary tree into a slice. func TreeToSliceDFS(root *TreeNode, i int, res *[]any) { if root == nil { return @@ -70,7 +70,7 @@ func TreeToSliceDFS(root *TreeNode, i int, res *[]any) { TreeToSliceDFS(root.Right, 2*i+2, res) } -// TreeToSlice serialize binary tree to slice +// TreeToSlice serializes a binary tree into a slice. func TreeToSlice(root *TreeNode) []any { var res []any TreeToSliceDFS(root, 0, &res) diff --git a/en/codes/go/pkg/tree_node_test.go b/en/codes/go/pkg/tree_node_test.go index 043aab099..51c9b4947 100644 --- a/en/codes/go/pkg/tree_node_test.go +++ b/en/codes/go/pkg/tree_node_test.go @@ -13,9 +13,9 @@ func TestTreeNode(t *testing.T) { arr := []any{1, 2, 3, nil, 5, 6, nil} node := SliceToTree(arr) - // print tree + // Print the tree PrintTree(node) - // tree to arr + // Convert the tree to a slice fmt.Println(TreeToSlice(node)) } diff --git a/en/codes/go/pkg/vertex.go b/en/codes/go/pkg/vertex.go index 111c52344..8ff9aec4d 100644 --- a/en/codes/go/pkg/vertex.go +++ b/en/codes/go/pkg/vertex.go @@ -4,19 +4,19 @@ package pkg -// Vertex vertex class +// Vertex represents a graph vertex. type Vertex struct { Val int } -// NewVertex vertex constructor +// NewVertex constructs a vertex. func NewVertex(val int) Vertex { return Vertex{ Val: val, } } -// ValsToVets deserialize value list to vertex list +// ValsToVets deserializes a list of values into a list of vertices. func ValsToVets(vals []int) []Vertex { vets := make([]Vertex, len(vals)) for i := 0; i < len(vals); i++ { @@ -25,7 +25,7 @@ func ValsToVets(vals []int) []Vertex { return vets } -// VetsToVals serialize vertex list to value list +// VetsToVals serializes a list of vertices into a list of values. func VetsToVals(vets []Vertex) []int { vals := make([]int, len(vets)) for i := range vets { @@ -34,17 +34,17 @@ func VetsToVals(vets []Vertex) []int { return vals } -// DeleteSliceElms delete specified elements from slice +// DeleteSliceElms removes the specified elements from a slice. func DeleteSliceElms[T any](a []T, elms ...T) []T { if len(a) == 0 || len(elms) == 0 { return a } - // First convert elements to set + // First, collect the elements in a set m := make(map[any]struct{}) for _, v := range elms { m[v] = struct{}{} } - // Filter out specified elements + // Filter out the specified elements res := make([]T, 0, len(a)) for _, v := range a { if _, ok := m[v]; !ok { diff --git a/en/codes/java/chapter_array_and_linkedlist/array.java b/en/codes/java/chapter_array_and_linkedlist/array.java index 328eb4f45..6dd27e861 100644 --- a/en/codes/java/chapter_array_and_linkedlist/array.java +++ b/en/codes/java/chapter_array_and_linkedlist/array.java @@ -10,59 +10,59 @@ import java.util.*; import java.util.concurrent.ThreadLocalRandom; public class array { - /* Random access to element */ + /* Access an element at a random index */ static int randomAccess(int[] nums) { - // Randomly select a number in the interval [0, nums.length) + // Choose a random number in the interval [0, nums.length) int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length); - // Retrieve and return the random element + // Get and return the randomly selected element int randomNum = nums[randomIndex]; return randomNum; } - /* Extend array length */ + /* Extend the array length */ static int[] extend(int[] nums, int enlarge) { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res; } - /* Insert element num at index index in the array */ + /* Insert element num at index `index` in the array */ static void insert(int[] nums, int num, int index) { - // Move all elements at and after index index backward by one position + // Shift all elements at and after index `index` one position to the right for (int i = nums.length - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num; } - /* Remove the element at index index */ + /* Remove the element at index `index` */ static void remove(int[] nums, int index) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (int i = index; i < nums.length - 1; i++) { nums[i] = nums[i + 1]; } } - /* Traverse array */ + /* Traverse the array */ static void traverse(int[] nums) { int count = 0; - // Traverse array by index + // Traverse the array by index for (int i = 0; i < nums.length; i++) { count += nums[i]; } - // Direct traversal of array elements + // Iterate directly over the array elements for (int num : nums) { count += num; } } - /* Find the specified element in the array */ + /* Find a specified element in the array */ static int find(int[] nums, int target) { for (int i = 0; i < nums.length; i++) { if (nums[i] == target) @@ -71,35 +71,35 @@ public class array { return -1; } - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { - /* Initialize array */ + /* Initialize the array */ int[] arr = new int[5]; System.out.println("Array arr = " + Arrays.toString(arr)); int[] nums = { 1, 3, 2, 5, 4 }; System.out.println("Array nums = " + Arrays.toString(nums)); - /* Insert element */ + /* Random access */ int randomNum = randomAccess(nums); - System.out.println("Get random element in nums " + randomNum); + System.out.println("Randomly selected element from nums: " + randomNum); - /* Traverse array */ + /* Extend the length */ nums = extend(nums, 3); - System.out.println("Extend array length to 8, resulting in nums = " + Arrays.toString(nums)); + System.out.println("Extend the array length to 8, giving nums = " + Arrays.toString(nums)); - /* Insert element */ + /* Insert an element */ insert(nums, 6, 3); - System.out.println("Insert number 6 at index 3, resulting in nums = " + Arrays.toString(nums)); + System.out.println("Insert the number 6 at index 3, giving nums = " + Arrays.toString(nums)); - /* Remove element */ + /* Remove an element */ remove(nums, 2); - System.out.println("Remove element at index 2, resulting in nums = " + Arrays.toString(nums)); + System.out.println("Remove the element at index 2, giving nums = " + Arrays.toString(nums)); - /* Traverse array */ + /* Traverse the array */ traverse(nums); - /* Find element */ + /* Find an element */ int index = find(nums, 3); - System.out.println("Find element 3 in nums, get index = " + index); + System.out.println("Find element 3 in nums, giving index = " + index); } } diff --git a/en/codes/java/chapter_array_and_linkedlist/linked_list.java b/en/codes/java/chapter_array_and_linkedlist/linked_list.java index 8eb1683da..6a70084f8 100644 --- a/en/codes/java/chapter_array_and_linkedlist/linked_list.java +++ b/en/codes/java/chapter_array_and_linkedlist/linked_list.java @@ -26,7 +26,7 @@ public class linked_list { n0.next = n1; } - /* Access the node at index index in the linked list */ + /* Access the node at index `index` in the linked list */ static ListNode access(ListNode head, int index) { for (int i = 0; i < index; i++) { if (head == null) @@ -48,39 +48,39 @@ public class linked_list { return -1; } - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { - /* Initialize linked list */ - // Initialize each node + /* Initialize the linked list */ + // Initialize the individual nodes ListNode n0 = new ListNode(1); ListNode n1 = new ListNode(3); ListNode n2 = new ListNode(2); ListNode n3 = new ListNode(5); ListNode n4 = new ListNode(4); - // Build references between nodes + // Set up references between the nodes n0.next = n1; n1.next = n2; n2.next = n3; n3.next = n4; - System.out.println("Initialized linked list is"); + System.out.println("The initialized linked list is"); PrintUtil.printLinkedList(n0); - /* Insert node */ + /* Insert a node */ insert(n0, new ListNode(0)); - System.out.println("Linked list after inserting node is"); + System.out.println("The linked list after inserting a node is"); PrintUtil.printLinkedList(n0); - /* Remove node */ + /* Remove a node */ remove(n0); - System.out.println("Linked list after removing node is"); + System.out.println("The linked list after removing a node is"); PrintUtil.printLinkedList(n0); - /* Access node */ + /* Access a node */ ListNode node = access(n0, 3); - System.out.println("Value of node at index 3 in linked list = " + node.val); + System.out.println("Value of the node at index 3 in the linked list = " + node.val); - /* Search node */ + /* Find a node */ int index = find(n0, 2); - System.out.println("Index of node with value 2 in linked list = " + index); + System.out.println("Index of the node with value 2 in the linked list = " + index); } } diff --git a/en/codes/java/chapter_array_and_linkedlist/list.java b/en/codes/java/chapter_array_and_linkedlist/list.java index fb7d99776..5499f0d48 100644 --- a/en/codes/java/chapter_array_and_linkedlist/list.java +++ b/en/codes/java/chapter_array_and_linkedlist/list.java @@ -10,25 +10,25 @@ import java.util.*; public class list { public static void main(String[] args) { - /* Initialize list */ - // Note that the array element type is Integer[], the wrapper class of int[] + /* Initialize the list */ + // Use Integer[]: Integer is the wrapper type for int Integer[] numbers = new Integer[] { 1, 3, 2, 5, 4 }; List nums = new ArrayList<>(Arrays.asList(numbers)); System.out.println("List nums = " + nums); - /* Update element */ + /* Access an element */ int num = nums.get(1); - System.out.println("Access element at index 1, get num = " + num); + System.out.println("Access the element at index 1, giving num = " + num); - /* Add elements at the end */ + /* Update an element */ nums.set(1, 0); - System.out.println("Update element at index 1 to 0, resulting in nums = " + nums); + System.out.println("Update the element at index 1 to 0, giving nums = " + nums); - /* Remove element */ + /* Clear the list */ nums.clear(); - System.out.println("After clearing list, nums = " + nums); + System.out.println("After clearing the list, nums = " + nums); - /* Direct traversal of list elements */ + /* Append at the end */ nums.add(1); nums.add(3); nums.add(2); @@ -36,20 +36,20 @@ public class list { nums.add(4); System.out.println("After adding elements, nums = " + nums); - /* Sort list */ + /* Insert an element in the middle */ nums.add(3, 6); - System.out.println("Insert number 6 at index 3, resulting in nums = " + nums); + System.out.println("Insert the number 6 at index 3, giving nums = " + nums); - /* Remove element */ + /* Remove an element */ nums.remove(3); - System.out.println("Remove element at index 3, resulting in nums = " + nums); + System.out.println("Remove the element at index 3, giving nums = " + nums); - /* Traverse list by index */ + /* Traverse the list by index */ int count = 0; for (int i = 0; i < nums.size(); i++) { count += nums.get(i); } - /* Directly traverse list elements */ + /* Iterate directly over the list elements */ for (int x : nums) { count += x; } @@ -57,10 +57,10 @@ public class list { /* Concatenate two lists */ List nums1 = new ArrayList<>(Arrays.asList(new Integer[] { 6, 8, 7, 10, 9 })); nums.addAll(nums1); - System.out.println("Concatenate list nums1 to nums, resulting in nums = " + nums); + System.out.println("After appending list nums1 to nums, nums = " + nums); - /* Sort list */ + /* Sort the list */ Collections.sort(nums); - System.out.println("After sorting list, nums = " + nums); + System.out.println("After sorting the list, nums = " + nums); } } diff --git a/en/codes/java/chapter_array_and_linkedlist/my_list.java b/en/codes/java/chapter_array_and_linkedlist/my_list.java index c18087dff..9c147dc58 100644 --- a/en/codes/java/chapter_array_and_linkedlist/my_list.java +++ b/en/codes/java/chapter_array_and_linkedlist/my_list.java @@ -10,44 +10,44 @@ import java.util.*; /* List class */ class MyList { - private int[] arr; // Array (stores list elements) + private int[] arr; // Array (stores the list elements) private int capacity = 10; // List capacity private int size = 0; // List length (current number of elements) - private int extendRatio = 2; // Multiple by which the list capacity is extended each time + private int extendRatio = 2; // Factor by which the list capacity grows each time /* Constructor */ public MyList() { arr = new int[capacity]; } - /* Get list length (current number of elements) */ + /* Get the list length (current number of elements) */ public int size() { return size; } - /* Get list capacity */ + /* Get the list capacity */ public int capacity() { return capacity; } - /* Update element */ + /* Access an element */ public int get(int index) { - // If the index is out of bounds, throw an exception, as below + // Throw an exception if the index is out of bounds; the same applies below if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index out of bounds"); return arr[index]; } - /* Add elements at the end */ + /* Update an element */ public void set(int index, int num) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index out of bounds"); arr[index] = num; } - /* Direct traversal of list elements */ + /* Append at the end */ public void add(int num) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if (size == capacity()) extendCapacity(); arr[size] = num; @@ -55,14 +55,14 @@ class MyList { size++; } - /* Sort list */ + /* Insert an element in the middle */ 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 extension mechanism + // Grow the capacity when adding an element would exceed it if (size == capacity()) extendCapacity(); - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for (int j = size - 1; j >= index; j--) { arr[j + 1] = arr[j]; } @@ -71,12 +71,12 @@ class MyList { size++; } - /* Remove element */ + /* Remove an element */ public int remove(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index out of bounds"); int num = arr[index]; - // Move all elements after index forward by one position + // Shift all elements after index `index` one position to the left for (int j = index; j < size - 1; j++) { arr[j] = arr[j + 1]; } @@ -86,18 +86,18 @@ class MyList { return num; } - /* Driver Code */ + /* Grow the list capacity */ public void extendCapacity() { - // Create a new array with length extendRatio times the original array and copy the original array to the new array + // Create an array extendRatio times as long as the original and copy the original array into it arr = Arrays.copyOf(arr, capacity() * extendRatio); - // Add elements at the end + // Update the list capacity capacity = arr.length; } - /* Convert list to array */ + /* Convert the list to an array */ public int[] toArray() { int size = size(); - // Elements enqueue + // Convert only the list elements within the occupied range int[] arr = new int[size]; for (int i = 0; i < size; i++) { arr[i] = get(i); @@ -107,41 +107,41 @@ class MyList { } public class my_list { - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { - /* Initialize list */ + /* Initialize the list */ MyList nums = new MyList(); - /* Direct traversal of list elements */ + /* Append at the end */ nums.add(1); nums.add(3); nums.add(2); nums.add(5); nums.add(4); System.out.println("List nums = " + Arrays.toString(nums.toArray()) + - ", capacity = " + nums.capacity() + ", length = " + nums.size()); + " , capacity = " + nums.capacity() + " , size = " + nums.size()); - /* Sort list */ + /* Insert an element in the middle */ nums.insert(3, 6); - System.out.println("Insert number 6 at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); + System.out.println("Insert the number 6 at index 3, giving nums = " + Arrays.toString(nums.toArray())); - /* Remove element */ + /* Remove an element */ nums.remove(3); - System.out.println("Remove element at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); + System.out.println("Remove the element at index 3, giving nums = " + Arrays.toString(nums.toArray())); - /* Update element */ + /* Access an element */ int num = nums.get(1); - System.out.println("Access element at index 1, get num = " + num); + System.out.println("Access the element at index 1, giving num = " + num); - /* Add elements at the end */ + /* Update an element */ nums.set(1, 0); - System.out.println("Update element at index 1 to 0, resulting in nums = " + Arrays.toString(nums.toArray())); + System.out.println("Update the element at index 1 to 0, giving nums = " + Arrays.toString(nums.toArray())); - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for (int i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i); } - System.out.println("List nums after expansion = " + Arrays.toString(nums.toArray()) + - ", capacity = " + nums.capacity() + ", length = " + nums.size()); + System.out.println("After expansion, list nums = " + Arrays.toString(nums.toArray()) + + " , capacity = " + nums.capacity() + " , size = " + nums.size()); } } diff --git a/en/codes/java/chapter_backtracking/n_queens.java b/en/codes/java/chapter_backtracking/n_queens.java index d429d3250..f68ed2803 100644 --- a/en/codes/java/chapter_backtracking/n_queens.java +++ b/en/codes/java/chapter_backtracking/n_queens.java @@ -9,10 +9,10 @@ package chapter_backtracking; import java.util.*; public class n_queens { - /* Backtracking algorithm: N queens */ + /* Backtracking: n-queens */ public static void backtrack(int row, int n, List> state, List>> res, boolean[] cols, boolean[] diags1, boolean[] diags2) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if (row == n) { List> copyState = new ArrayList<>(); for (List sRow : state) { @@ -21,28 +21,28 @@ public class n_queens { res.add(copyState); return; } - // Traverse all columns + // Iterate over all columns for (int col = 0; col < n; col++) { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square int diag1 = row - col + n - 1; int diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in this cell + // Try: place a queen on this square state.get(row).set(col, "Q"); cols[col] = diags1[diag1] = diags2[diag2] = true; - // Place the next row + // Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state.get(row).set(col, "#"); cols[col] = diags1[diag1] = diags2[diag2] = false; } } } - /* Solve N queens */ + /* Solve the n-queens problem */ public static List>> nQueens(int n) { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square List> state = new ArrayList<>(); for (int i = 0; i < n; i++) { List row = new ArrayList<>(); @@ -51,9 +51,9 @@ public class n_queens { } state.add(row); } - boolean[] cols = new boolean[n]; // Record whether there is a queen in the column - boolean[] diags1 = new boolean[2 * n - 1]; // Record whether there is a queen on the main diagonal - boolean[] diags2 = new boolean[2 * n - 1]; // Record whether there is a queen on the anti-diagonal + boolean[] cols = new boolean[n]; // Track whether a column contains a queen + boolean[] diags1 = new boolean[2 * n - 1]; // Track whether a main diagonal contains a queen + boolean[] diags2 = new boolean[2 * n - 1]; // Track whether an anti-diagonal contains a queen List>> res = new ArrayList<>(); backtrack(0, n, state, res, cols, diags1, diags2); @@ -65,8 +65,8 @@ public class n_queens { int n = 4; List>> res = nQueens(n); - System.out.println("Input board size is " + n); - System.out.println("Total queen placement solutions: " + res.size() + ""); + System.out.println("Input board side length: " + n); + System.out.println("There are " + res.size() + " queen placements"); for (List> state : res) { System.out.println("--------------------"); for (List row : state) { diff --git a/en/codes/java/chapter_backtracking/permutations_i.java b/en/codes/java/chapter_backtracking/permutations_i.java index c2366c64a..0f7c7c4b4 100644 --- a/en/codes/java/chapter_backtracking/permutations_i.java +++ b/en/codes/java/chapter_backtracking/permutations_i.java @@ -9,24 +9,24 @@ package chapter_backtracking; import java.util.*; public class permutations_i { - /* Backtracking algorithm: Permutations I */ + /* Backtracking: permutations I */ public static void backtrack(List state, int[] choices, boolean[] selected, List> res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.size() == choices.length) { res.add(new ArrayList(state)); return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choices.length; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state.add(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.remove(state.size() - 1); } @@ -46,6 +46,6 @@ public class permutations_i { List> res = permutationsI(nums); System.out.println("Input array nums = " + Arrays.toString(nums)); - System.out.println("All permutations res = " + res); + System.out.println("All permutations: res = " + res); } } diff --git a/en/codes/java/chapter_backtracking/permutations_ii.java b/en/codes/java/chapter_backtracking/permutations_ii.java index a2ca4d431..93bc9761b 100644 --- a/en/codes/java/chapter_backtracking/permutations_ii.java +++ b/en/codes/java/chapter_backtracking/permutations_ii.java @@ -9,26 +9,26 @@ package chapter_backtracking; import java.util.*; public class permutations_ii { - /* Backtracking algorithm: Permutations II */ + /* Backtracking: permutations II */ static void backtrack(List state, int[] choices, boolean[] selected, List> res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.size() == choices.length) { res.add(new ArrayList(state)); return; } - // Traverse all choices + // Iterate over all choices Set duplicated = new HashSet(); for (int i = 0; i < choices.length; i++) { int choice = choices[i]; - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && !duplicated.contains(choice)) { - // Attempt: make choice, update state - duplicated.add(choice); // Record the selected element value + // Try: make a choice and update the state + duplicated.add(choice); // Record the chosen value selected[i] = true; state.add(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.remove(state.size() - 1); } @@ -48,6 +48,6 @@ public class permutations_ii { List> res = permutationsII(nums); System.out.println("Input array nums = " + Arrays.toString(nums)); - System.out.println("All permutations res = " + res); + System.out.println("All permutations: res = " + res); } } diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java index a042d7efe..dda84932e 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java @@ -12,13 +12,13 @@ import java.util.*; public class preorder_traversal_i_compact { static List res; - /* Preorder traversal: Example 1 */ + /* Preorder traversal: example 1 */ static void preOrder(TreeNode root) { if (root == null) { return; } if (root.val == 7) { - // Record solution + // Record the solution res.add(root); } preOrder(root.left); @@ -27,7 +27,7 @@ public class preorder_traversal_i_compact { public static void main(String[] args) { TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); - System.out.println("\nInitialize binary tree"); + System.out.println("\nInitialize the binary tree"); PrintUtil.printTree(root); // Preorder traversal diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java index 9d90b8857..67924a42d 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java @@ -13,15 +13,15 @@ public class preorder_traversal_ii_compact { static List path; static List> res; - /* Preorder traversal: Example 2 */ + /* Preorder traversal: example 2 */ static void preOrder(TreeNode root) { if (root == null) { return; } - // Attempt + // Try path.add(root); if (root.val == 7) { - // Record solution + // Record the solution res.add(new ArrayList<>(path)); } preOrder(root.left); @@ -32,7 +32,7 @@ public class preorder_traversal_ii_compact { public static void main(String[] args) { TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); - System.out.println("\nInitialize binary tree"); + System.out.println("\nInitialize the binary tree"); PrintUtil.printTree(root); // Preorder traversal @@ -40,7 +40,7 @@ public class preorder_traversal_ii_compact { res = new ArrayList<>(); preOrder(root); - System.out.println("\nOutput all paths from root node to node 7"); + System.out.println("\nOutput all paths from the root to nodes with value 7"); for (List path : res) { List vals = new ArrayList<>(); for (TreeNode node : path) { diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java index 0292f06fa..4f3db812e 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java @@ -13,16 +13,16 @@ public class preorder_traversal_iii_compact { static List path; static List> res; - /* Preorder traversal: Example 3 */ + /* Preorder traversal: example 3 */ static void preOrder(TreeNode root) { - // Pruning + // Prune if (root == null || root.val == 3) { return; } - // Attempt + // Try path.add(root); if (root.val == 7) { - // Record solution + // Record the solution res.add(new ArrayList<>(path)); } preOrder(root.left); @@ -33,7 +33,7 @@ public class preorder_traversal_iii_compact { public static void main(String[] args) { TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); - System.out.println("\nInitialize binary tree"); + System.out.println("\nInitialize the binary tree"); PrintUtil.printTree(root); // Preorder traversal @@ -41,7 +41,7 @@ public class preorder_traversal_iii_compact { res = new ArrayList<>(); preOrder(root); - System.out.println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3"); + System.out.println("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3"); for (List path : res) { List vals = new ArrayList<>(); for (TreeNode node : path) { diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java b/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java index f31847104..daad54db7 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java @@ -10,47 +10,47 @@ import utils.*; import java.util.*; public class preorder_traversal_iii_template { - /* Check if the current state is a solution */ + /* Check whether the current state is a solution */ static boolean isSolution(List state) { return !state.isEmpty() && state.get(state.size() - 1).val == 7; } - /* Record solution */ + /* Record the solution */ static void recordSolution(List state, List> res) { res.add(new ArrayList<>(state)); } - /* Check if the choice is valid under the current state */ + /* Check whether the choice is valid in the current state */ static boolean isValid(List state, TreeNode choice) { return choice != null && choice.val != 3; } - /* Update state */ + /* Update the state */ static void makeChoice(List state, TreeNode choice) { state.add(choice); } - /* Restore state */ + /* Restore the state */ static void undoChoice(List state, TreeNode choice) { state.remove(state.size() - 1); } - /* Backtracking algorithm: Example 3 */ + /* Backtracking: example 3 */ static void backtrack(List state, List choices, List> res) { - // Check if it is a solution + // Check whether this is a solution if (isSolution(state)) { - // Record solution + // Record the solution recordSolution(state, res); } - // Traverse all choices + // Iterate over all choices for (TreeNode choice : choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state, choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, Arrays.asList(choice.left, choice.right), res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } @@ -58,14 +58,14 @@ public class preorder_traversal_iii_template { public static void main(String[] args) { TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); - System.out.println("\nInitialize binary tree"); + System.out.println("\nInitialize the binary tree"); PrintUtil.printTree(root); // Backtracking algorithm List> res = new ArrayList<>(); backtrack(new ArrayList<>(), Arrays.asList(root), res); - System.out.println("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); + System.out.println("\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3"); for (List path : res) { List vals = new ArrayList<>(); for (TreeNode node : path) { diff --git a/en/codes/java/chapter_backtracking/subset_sum_i.java b/en/codes/java/chapter_backtracking/subset_sum_i.java index fa0f7ad0b..489822c3d 100644 --- a/en/codes/java/chapter_backtracking/subset_sum_i.java +++ b/en/codes/java/chapter_backtracking/subset_sum_i.java @@ -9,26 +9,26 @@ package chapter_backtracking; import java.util.*; public class subset_sum_i { - /* Backtracking algorithm: Subset sum I */ + /* Backtracking: subset sum I */ static void backtrack(List state, int target, int[] choices, int start, List> res) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is zero, so the subset sums to the original target if (target == 0) { res.add(new ArrayList<>(state)); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (int i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if the next element exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if (target - choices[i] < 0) { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.remove(state.size() - 1); } } @@ -37,8 +37,8 @@ public class subset_sum_i { static List> subsetSumI(int[] nums, int target) { List state = new ArrayList<>(); // State (subset) Arrays.sort(nums); // Sort nums - int start = 0; // Start point for traversal - List> res = new ArrayList<>(); // Result list (subset list) + int start = 0; // Starting index for iteration + List> res = new ArrayList<>(); // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } @@ -50,6 +50,6 @@ public class subset_sum_i { List> res = subsetSumI(nums, target); System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); - System.out.println("All subsets with sum equal to " + target + " are res = " + res); + System.out.println("All subsets with sum " + target + " : res = " + res); } } diff --git a/en/codes/java/chapter_backtracking/subset_sum_i_naive.java b/en/codes/java/chapter_backtracking/subset_sum_i_naive.java index 2160d299e..cba3cf90a 100644 --- a/en/codes/java/chapter_backtracking/subset_sum_i_naive.java +++ b/en/codes/java/chapter_backtracking/subset_sum_i_naive.java @@ -9,33 +9,33 @@ package chapter_backtracking; import java.util.*; public class subset_sum_i_naive { - /* Backtracking algorithm: Subset sum I */ + /* Backtracking: subset sum I */ static void backtrack(List state, int target, int total, int[] choices, List> res) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total == target) { res.add(new ArrayList<>(state)); return; } - // Traverse all choices + // Iterate over all choices for (int i = 0; i < choices.length; i++) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target, total + choices[i], choices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.remove(state.size() - 1); } } - /* Solve subset sum I (including duplicate subsets) */ + /* Solve subset sum I (with duplicate subsets) */ static List> subsetSumINaive(int[] nums, int target) { List state = new ArrayList<>(); // State (subset) int total = 0; // Subset sum - List> res = new ArrayList<>(); // Result list (subset list) + List> res = new ArrayList<>(); // Result list (list of subsets) backtrack(state, target, total, nums, res); return res; } @@ -47,7 +47,7 @@ public class subset_sum_i_naive { List> res = subsetSumINaive(nums, target); System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); - System.out.println("All subsets with sum equal to " + target + " are res = " + res); - System.out.println("Please note that this method outputs results containing duplicate sets"); + System.out.println("All subsets with sum " + target + " : res = " + res); + System.out.println("Note that this method produces duplicate subsets"); } } diff --git a/en/codes/java/chapter_backtracking/subset_sum_ii.java b/en/codes/java/chapter_backtracking/subset_sum_ii.java index e0383dd62..aef5d1d2b 100644 --- a/en/codes/java/chapter_backtracking/subset_sum_ii.java +++ b/en/codes/java/chapter_backtracking/subset_sum_ii.java @@ -9,31 +9,31 @@ package chapter_backtracking; import java.util.*; public class subset_sum_ii { - /* Backtracking algorithm: Subset sum II */ + /* Backtracking: subset sum II */ static void backtrack(List state, int target, int[] choices, int start, List> res) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is zero, so the subset sums to the original target if (target == 0) { res.add(new ArrayList<>(state)); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: iterate from start to avoid choosing the same element again for (int i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if the next element exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if (target - choices[i] < 0) { break; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if (i > start && choices[i] == choices[i - 1]) { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.add(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.remove(state.size() - 1); } } @@ -42,8 +42,8 @@ public class subset_sum_ii { static List> subsetSumII(int[] nums, int target) { List state = new ArrayList<>(); // State (subset) Arrays.sort(nums); // Sort nums - int start = 0; // Start point for traversal - List> res = new ArrayList<>(); // Result list (subset list) + int start = 0; // Starting index for iteration + List> res = new ArrayList<>(); // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } @@ -55,6 +55,6 @@ public class subset_sum_ii { List> res = subsetSumII(nums, target); System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); - System.out.println("All subsets with sum equal to " + target + " are res = " + res); + System.out.println("All subsets with sum " + target + " : res = " + res); } } diff --git a/en/codes/java/chapter_computational_complexity/complexity_exercises.java b/en/codes/java/chapter_computational_complexity/complexity_exercises.java index 9ccd21c39..f35305fc2 100644 --- a/en/codes/java/chapter_computational_complexity/complexity_exercises.java +++ b/en/codes/java/chapter_computational_complexity/complexity_exercises.java @@ -7,7 +7,7 @@ package chapter_computational_complexity; public class complexity_exercises { - /* Iterative summation */ + /* Sum using iteration */ static int sumIter(int n) { int res = 0; for (int i = 1; i <= n; i++) { @@ -16,7 +16,7 @@ public class complexity_exercises { return res; } - /* Recursive summation */ + /* Sum using recursion */ static int sumRecur(int n) { if (n == 1) { return 1; @@ -24,7 +24,7 @@ public class complexity_exercises { return n + sumRecur(n - 1); } - /* Linear loop */ + /* Linear-time loop */ static int linearLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -33,7 +33,7 @@ public class complexity_exercises { return res; } - /* Quadratic loop */ + /* Quadratic-time loop */ static int quadraticLoop(int n) { int res = 0; for (int i = 0; i < n; i++) { @@ -44,7 +44,7 @@ public class complexity_exercises { return res; } - /* Logarithmic loop */ + /* Logarithmic-time loop */ static int logarithmicLoop(int n) { while (n > 1) { n /= 2; diff --git a/en/codes/java/chapter_computational_complexity/iteration.java b/en/codes/java/chapter_computational_complexity/iteration.java index 0b4216548..fcdddc4d3 100644 --- a/en/codes/java/chapter_computational_complexity/iteration.java +++ b/en/codes/java/chapter_computational_complexity/iteration.java @@ -10,7 +10,7 @@ public class iteration { /* for loop */ static int forLoop(int n) { int res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (int i = 1; i <= n; i++) { res += i; } @@ -20,11 +20,11 @@ public class iteration { /* while loop */ static int whileLoop(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 2, ..., n-1, n + int i = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i; - i++; // Update condition variable + i++; // Update the loop control variable } return res; } @@ -32,23 +32,23 @@ public class iteration { /* while loop (two updates) */ static int whileLoopII(int n) { int res = 0; - int i = 1; // Initialize condition variable - // Sum 1, 4, 10, ... + int i = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i; - // Update condition variable + // Update the loop control variable i++; i *= 2; } return res; } - /* Nested for loop */ + /* Nested for loops */ static String nestedForLoop(int n) { StringBuilder res = new StringBuilder(); - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (int i = 1; i <= n; i++) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (int j = 1; j <= n; j++) { res.append("(" + i + ", " + j + "), "); } @@ -56,21 +56,21 @@ public class iteration { return res.toString(); } - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { int n = 5; int res; res = forLoop(n); - System.out.println("\nfor loop sum result res = " + res); + System.out.println("\nSum from the for loop: res = " + res); res = whileLoop(n); - System.out.println("\nwhile loop sum result res = " + res); + System.out.println("\nSum from the while loop: res = " + res); res = whileLoopII(n); - System.out.println("\nwhile loop (two updates) sum result res = " + res); + System.out.println("\nSum from the while loop (two updates): res = " + res); String resStr = nestedForLoop(n); - System.out.println("\nDouble for loop traversal result " + resStr); + System.out.println("\nTraversal result from the nested for loops: " + resStr); } } diff --git a/en/codes/java/chapter_computational_complexity/recursion.java b/en/codes/java/chapter_computational_complexity/recursion.java index b07c38a3a..faf5951bd 100644 --- a/en/codes/java/chapter_computational_complexity/recursion.java +++ b/en/codes/java/chapter_computational_complexity/recursion.java @@ -11,12 +11,12 @@ import java.util.Stack; public class recursion { /* Recursion */ static int recur(int n) { - // Termination condition + // Base case if (n == 1) return 1; - // Recurse: recursive call + // Recurse: make a recursive call int res = recur(n - 1); - // Return: return result + // Unwind: return the result return n + res; } @@ -25,14 +25,14 @@ public class recursion { // Use an explicit stack to simulate the system call stack Stack stack = new Stack<>(); int res = 0; - // Recurse: recursive call + // Recurse: make a recursive call for (int i = n; i > 0; i--) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.push(i); } - // Return: return result + // Unwind: return the result while (!stack.isEmpty()) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.pop(); } // res = 1+2+3+...+n @@ -41,39 +41,39 @@ public class recursion { /* Tail recursion */ static int tailRecur(int n, int res) { - // Termination condition + // Base case if (n == 0) return res; - // Tail recursive call + // Tail-recursive call return tailRecur(n - 1, res + n); } /* Fibonacci sequence: recursion */ static int fib(int n) { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n == 1 || n == 2) return n - 1; - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) int res = fib(n - 1) + fib(n - 2); - // Return result f(n) + // Return the result f(n) return res; } - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { int n = 5; int res; res = recur(n); - System.out.println("\nRecursive function sum result res = " + res); + System.out.println("\nSum from recursion: res = " + res); res = forLoopRecur(n); - System.out.println("\nUsing iteration to simulate recursive sum result res = " + res); + System.out.println("\nSum from simulating recursion with iteration: res = " + res); res = tailRecur(n, 0); - System.out.println("\nTail recursive function sum result res = " + res); + System.out.println("\nSum from tail recursion: res = " + res); res = fib(n); - System.out.println("\nThe " + n + "th term of the Fibonacci sequence is " + res); + System.out.println("\nFibonacci term " + n + " is " + res); } } diff --git a/en/codes/java/chapter_computational_complexity/space_complexity.java b/en/codes/java/chapter_computational_complexity/space_complexity.java index 94d2f99c6..1007db20d 100644 --- a/en/codes/java/chapter_computational_complexity/space_complexity.java +++ b/en/codes/java/chapter_computational_complexity/space_complexity.java @@ -16,9 +16,9 @@ public class space_complexity { return 0; } - /* Constant order */ + /* Constant complexity */ static void constant(int n) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space final int a = 0; int b = 0; int[] nums = new int[10000]; @@ -27,41 +27,41 @@ public class space_complexity { for (int i = 0; i < n; i++) { int c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for (int i = 0; i < n; i++) { function(); } } - /* Linear order */ + /* Linear complexity */ static void linear(int n) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space int[] nums = new int[n]; // A list of length n occupies O(n) space List nodes = new ArrayList<>(); for (int i = 0; i < n; i++) { nodes.add(new ListNode(i)); } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space Map map = new HashMap<>(); for (int i = 0; i < n; i++) { map.put(i, String.valueOf(i)); } } - /* Linear order (recursive implementation) */ + /* Linear complexity (recursive implementation) */ static void linearRecur(int n) { - System.out.println("Recursion n = " + n); + System.out.println("Recursive call: n = " + n); if (n == 1) return; linearRecur(n - 1); } - /* Exponential order */ + /* Quadratic complexity */ static void quadratic(int n) { - // Matrix uses O(n^2) space + // The matrix uses O(n^2) space int[][] numMatrix = new int[n][n]; - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space List> numList = new ArrayList<>(); for (int i = 0; i < n; i++) { List tmp = new ArrayList<>(); @@ -72,17 +72,17 @@ public class space_complexity { } } - /* Quadratic order (recursive implementation) */ + /* Quadratic complexity (recursive implementation) */ static int quadraticRecur(int n) { if (n <= 0) return 0; - // Array nums has length n, n-1, ..., 2, 1 + // The lengths of array nums are n, n-1, ..., 2, 1 int[] nums = new int[n]; - System.out.println("In recursion n = " + n + ", nums length = " + nums.length); + System.out.println("Recursive call: n = " + n + " with nums length = " + nums.length); return quadraticRecur(n - 1); } - /* Driver Code */ + /* Exponential complexity (building a perfect binary tree) */ static TreeNode buildTree(int n) { if (n == 0) return null; @@ -92,18 +92,18 @@ public class space_complexity { return root; } - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { int n = 5; - // Constant order + // Constant complexity constant(n); - // Linear order + // Linear complexity linear(n); linearRecur(n); - // Exponential order + // Quadratic complexity quadratic(n); quadraticRecur(n); - // Exponential order + // Exponential complexity TreeNode root = buildTree(n); PrintUtil.printTree(root); } diff --git a/en/codes/java/chapter_computational_complexity/time_complexity.java b/en/codes/java/chapter_computational_complexity/time_complexity.java index 06ac065cb..10269acb2 100644 --- a/en/codes/java/chapter_computational_complexity/time_complexity.java +++ b/en/codes/java/chapter_computational_complexity/time_complexity.java @@ -7,7 +7,7 @@ package chapter_computational_complexity; public class time_complexity { - /* Constant order */ + /* Constant complexity */ static int constant(int n) { int count = 0; int size = 100000; @@ -16,7 +16,7 @@ public class time_complexity { return count; } - /* Linear order */ + /* Linear complexity */ static int linear(int n) { int count = 0; for (int i = 0; i < n; i++) @@ -24,20 +24,20 @@ public class time_complexity { return count; } - /* Linear order (traversing array) */ + /* Linear complexity (array traversal) */ static int arrayTraversal(int[] nums) { int count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for (int num : nums) { count++; } return count; } - /* Exponential order */ + /* Quadratic complexity */ static int quadratic(int n) { int count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -46,29 +46,29 @@ public class time_complexity { return count; } - /* Quadratic order (bubble sort) */ + /* Quadratic complexity (bubble sort) */ static int bubbleSort(int[] nums) { int count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } return count; } - /* Exponential order (loop implementation) */ + /* Exponential complexity (iterative implementation) */ static int exponential(int n) { int count = 0, base = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (int i = 0; i < n; i++) { for (int j = 0; j < base; j++) { count++; @@ -79,14 +79,14 @@ public class time_complexity { return count; } - /* Exponential order (recursive implementation) */ + /* Exponential complexity (recursive implementation) */ static int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } - /* Logarithmic order (loop implementation) */ + /* Logarithmic complexity (iterative implementation) */ static int logarithmic(int n) { int count = 0; while (n > 1) { @@ -96,14 +96,14 @@ public class time_complexity { return count; } - /* Logarithmic order (recursive implementation) */ + /* Logarithmic complexity (recursive implementation) */ static int logRecur(int n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } - /* Linearithmic order */ + /* Linearithmic complexity */ static int linearLogRecur(int n) { if (n <= 1) return 1; @@ -114,54 +114,54 @@ public class time_complexity { return count; } - /* Factorial order (recursive implementation) */ + /* Factorial complexity (recursive implementation) */ static int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - // Split from 1 into n + // Split one problem into n subproblems for (int i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; } - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { - // You can modify n to run and observe the trend of the number of operations for various complexities + // Try different values of n to see how the operation count grows for each complexity int n = 8; - System.out.println("Input data size n = " + n); + System.out.println("Input size n = " + n); int count = constant(n); - System.out.println("Constant order operation count = " + count); + System.out.println("Operation count for constant complexity = " + count); count = linear(n); - System.out.println("Linear order operation count = " + count); + System.out.println("Operation count for linear complexity = " + count); count = arrayTraversal(new int[n]); - System.out.println("Linear order (array traversal) operation count = " + count); + System.out.println("Operation count for linear complexity (array traversal) = " + count); count = quadratic(n); - System.out.println("Quadratic order operation count = " + count); + System.out.println("Operation count for quadratic complexity = " + count); int[] nums = new int[n]; for (int i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = bubbleSort(nums); - System.out.println("Quadratic order (bubble sort) operation count = " + count); + System.out.println("Operation count for quadratic complexity (bubble sort) = " + count); count = exponential(n); - System.out.println("Exponential order (loop implementation) operation count = " + count); + System.out.println("Operation count for exponential complexity (iterative implementation) = " + count); count = expRecur(n); - System.out.println("Exponential order (recursive implementation) operation count = " + count); + System.out.println("Operation count for exponential complexity (recursive implementation) = " + count); count = logarithmic(n); - System.out.println("Logarithmic order (loop implementation) operation count = " + count); + System.out.println("Operation count for logarithmic complexity (iterative implementation) = " + count); count = logRecur(n); - System.out.println("Logarithmic order (recursive implementation) operation count = " + count); + System.out.println("Operation count for logarithmic complexity (recursive implementation) = " + count); count = linearLogRecur(n); - System.out.println("Linearithmic order (recursive implementation) operation count = " + count); + System.out.println("Operation count for linearithmic complexity (recursive implementation) = " + count); count = factorialRecur(n); - System.out.println("Factorial order (recursive implementation) operation count = " + count); + System.out.println("Operation count for factorial complexity (recursive implementation) = " + count); } } diff --git a/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java b/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java index d56cce41b..d5a0bfe53 100644 --- a/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java +++ b/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java @@ -9,14 +9,14 @@ package chapter_computational_complexity; import java.util.*; public class worst_best_time_complexity { - /* Generate an array with elements { 1, 2, ..., n }, order shuffled */ + /* Generate an array containing { 1, 2, ..., n } in shuffled order */ static int[] randomNumbers(int n) { Integer[] nums = new Integer[n]; - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (int i = 0; i < n; i++) { nums[i] = i + 1; } - // Randomly shuffle array elements + // Randomly shuffle the array elements Collections.shuffle(Arrays.asList(nums)); // Integer[] -> int[] int[] res = new int[n]; @@ -26,25 +26,25 @@ public class worst_best_time_complexity { return res; } - /* Find the index of number 1 in array nums */ + /* Find the index of the number 1 in array nums */ static int findOne(int[] nums) { for (int i = 0; i < nums.length; i++) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] == 1) return i; } return -1; } - /* Driver Code */ + /* Driver code */ public static void main(String[] args) { for (int i = 0; i < 10; i++) { int n = 100; int[] nums = randomNumbers(n); int index = findOne(nums); System.out.println("\nArray [ 1, 2, ..., n ] after shuffling = " + Arrays.toString(nums)); - System.out.println("Index of number 1 is " + index); + System.out.println("The index of the number 1 is " + index); } } } diff --git a/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java b/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java index caaa690d9..8aee85614 100644 --- a/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java +++ b/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java @@ -9,20 +9,20 @@ package chapter_divide_and_conquer; public class binary_search_recur { /* Binary search: problem f(i, j) */ static int dfs(int[] nums, int target, int i, int j) { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1; } // Calculate the midpoint index m int m = (i + j) / 2; if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -30,7 +30,7 @@ public class binary_search_recur { /* Binary search */ static int binarySearch(int[] nums, int target) { int n = nums.length; - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1); } @@ -38,7 +38,7 @@ public class binary_search_recur { int target = 6; int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; - // Binary search (closed interval on both sides) + // Binary search (closed interval) int index = binarySearch(nums, target); System.out.println("Index of target element 6 = " + index); } diff --git a/en/codes/java/chapter_divide_and_conquer/build_tree.java b/en/codes/java/chapter_divide_and_conquer/build_tree.java index aaca66ac4..cfa953be3 100644 --- a/en/codes/java/chapter_divide_and_conquer/build_tree.java +++ b/en/codes/java/chapter_divide_and_conquer/build_tree.java @@ -10,14 +10,14 @@ import utils.*; import java.util.*; public class build_tree { - /* Build binary tree: divide and conquer */ + /* Build a binary tree: divide and conquer */ static TreeNode dfs(int[] preorder, Map inorderMap, int i, int l, int r) { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) return null; // Initialize the root node TreeNode root = new TreeNode(preorder[i]); - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees int m = inorderMap.get(preorder[i]); // Subproblem: build the left subtree root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); @@ -27,9 +27,9 @@ public class build_tree { return root; } - /* Build binary tree */ + /* Build a binary tree */ static TreeNode buildTree(int[] preorder, int[] inorder) { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index Map inorderMap = new HashMap<>(); for (int i = 0; i < inorder.length; i++) { inorderMap.put(inorder[i], i); diff --git a/en/codes/java/chapter_divide_and_conquer/fast_power.java b/en/codes/java/chapter_divide_and_conquer/fast_power.java index e0533592d..c504ebd4a 100644 --- a/en/codes/java/chapter_divide_and_conquer/fast_power.java +++ b/en/codes/java/chapter_divide_and_conquer/fast_power.java @@ -7,7 +7,7 @@ package chapter_divide_and_conquer; public class fast_power { - /* Exponentiation by squaring */ + /* Fast exponentiation */ static int fastPow(int x, int n) { if (n == 0) { return 1; diff --git a/en/codes/java/chapter_divide_and_conquer/hanota.java b/en/codes/java/chapter_divide_and_conquer/hanota.java index 8671d41f8..08bf45439 100644 --- a/en/codes/java/chapter_divide_and_conquer/hanota.java +++ b/en/codes/java/chapter_divide_and_conquer/hanota.java @@ -9,9 +9,9 @@ package chapter_divide_and_conquer; import java.util.*; public class hanota { - /* Move a disk */ + /* Move one disk */ static void move(List src, List tar) { - // Take out a disk from the top of src + // Take a disk from the top of src Integer pan = src.remove(src.size() - 1); // Place the disk on top of tar tar.add(pan); @@ -19,14 +19,14 @@ public class hanota { /* Solve the Tower of Hanoi problem f(i) */ static void dfs(int i, List src, List buf, List tar) { - // If there is only one disk left in src, move it directly to tar + // If only one disk remains to be moved from src, move it directly to tar if (i == 1) { move(src, tar); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); @@ -40,18 +40,18 @@ public class hanota { } public static void main(String[] args) { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg List A = new ArrayList<>(Arrays.asList(5, 4, 3, 2, 1)); List B = new ArrayList<>(); List C = new ArrayList<>(); - System.out.println("In initial state:"); + System.out.println("Initial state:"); System.out.println("A = " + A); System.out.println("B = " + B); System.out.println("C = " + C); solveHanota(A, B, C); - System.out.println("After disk movement is complete:"); + System.out.println("After moving the disks:"); System.out.println("A = " + A); System.out.println("B = " + B); System.out.println("C = " + C); diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java index ea8e6e4b3..2d91c024c 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java @@ -11,26 +11,26 @@ import java.util.*; public class climbing_stairs_backtrack { /* Backtracking */ public static void backtrack(List choices, int state, int n, List res) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state == n) res.set(0, res.get(0) + 1); - // Traverse all choices + // Iterate over all choices for (Integer choice : choices) { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue; - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res); // Backtrack } } - /* Climbing stairs: Backtracking */ + /* Climbing stairs: backtracking */ public static int climbingStairsBacktrack(int n) { - List choices = Arrays.asList(1, 2); // Can choose to climb up 1 or 2 stairs - int state = 0; // Start climbing from the 0-th stair + List choices = Arrays.asList(1, 2); // Choose to climb 1 or 2 steps + int state = 0; // Start climbing from step 0 List res = new ArrayList<>(); - res.add(0); // Use res[0] to record the solution count + res.add(0); // Use res[0] to record the number of ways backtrack(choices, state, n, res); return res.get(0); } @@ -39,6 +39,6 @@ public class climbing_stairs_backtrack { int n = 9; int res = climbingStairsBacktrack(n); - System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); + System.out.println(String.format("Number of ways to climb %d stairs: %d", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java index e87aaa714..6822f07af 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java @@ -7,19 +7,19 @@ package chapter_dynamic_programming; public class climbing_stairs_constraint_dp { - /* Climbing stairs with constraint: Dynamic programming */ + /* Climbing stairs with constraints: dynamic programming */ static int climbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int[][] dp = new int[n + 1][3]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -31,6 +31,6 @@ public class climbing_stairs_constraint_dp { int n = 9; int res = climbingStairsConstraintDP(n); - System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); + System.out.println(String.format("Number of ways to climb %d stairs: %d", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java index 0085fa29d..e76eb8917 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java @@ -9,7 +9,7 @@ package chapter_dynamic_programming; public class climbing_stairs_dfs { /* Search */ public static int dfs(int i) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; // dp[i] = dp[i-1] + dp[i-2] @@ -17,7 +17,7 @@ public class climbing_stairs_dfs { return count; } - /* Climbing stairs: Search */ + /* Climbing stairs: search */ public static int climbingStairsDFS(int n) { return dfs(n); } @@ -26,6 +26,6 @@ public class climbing_stairs_dfs { int n = 9; int res = climbingStairsDFS(n); - System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); + System.out.println(String.format("Number of ways to climb %d stairs: %d", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java index 8819aa643..5b22bfad3 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java @@ -9,12 +9,12 @@ package chapter_dynamic_programming; import java.util.Arrays; public class climbing_stairs_dfs_mem { - /* Memoization search */ + /* Memoized search */ public static int dfs(int i, int[] mem) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i; - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] @@ -24,9 +24,9 @@ public class climbing_stairs_dfs_mem { return count; } - /* Climbing stairs: Memoization search */ + /* Climbing stairs: memoized search */ public static int climbingStairsDFSMem(int n) { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded int[] mem = new int[n + 1]; Arrays.fill(mem, -1); return dfs(n, mem); @@ -36,6 +36,6 @@ public class climbing_stairs_dfs_mem { int n = 9; int res = climbingStairsDFSMem(n); - System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); + System.out.println(String.format("Number of ways to climb %d stairs: %d", n, res)); } } \ No newline at end of file diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java index e43738187..bc73e854f 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java @@ -7,23 +7,23 @@ package chapter_dynamic_programming; public class climbing_stairs_dp { - /* Climbing stairs: Dynamic programming */ + /* Climbing stairs: dynamic programming */ public static int climbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int[] dp = new int[n + 1]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } - /* Climbing stairs: Space-optimized dynamic programming */ + /* Climbing stairs: space-optimized dynamic programming */ public static int climbingStairsDPComp(int n) { if (n == 1 || n == 2) return n; @@ -40,9 +40,9 @@ public class climbing_stairs_dp { int n = 9; int res = climbingStairsDP(n); - System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); + System.out.println(String.format("Number of ways to climb %d stairs: %d", n, res)); res = climbingStairsDPComp(n); - System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); + System.out.println(String.format("Number of ways to climb %d stairs: %d", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/coin_change.java b/en/codes/java/chapter_dynamic_programming/coin_change.java index d1abb4c4c..1c0c388ca 100644 --- a/en/codes/java/chapter_dynamic_programming/coin_change.java +++ b/en/codes/java/chapter_dynamic_programming/coin_change.java @@ -9,24 +9,24 @@ package chapter_dynamic_programming; import java.util.Arrays; public class coin_change { - /* Coin change: Dynamic programming */ + /* Coin change: dynamic programming */ static int coinChangeDP(int[] coins, int amt) { int n = coins.length; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table int[][] dp = new int[n + 1][amt + 1]; - // State transition: first row and first column + // State transitions: first row and first column for (int a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -34,22 +34,22 @@ public class coin_change { return dp[n][amt] != MAX ? dp[n][amt] : -1; } - /* Coin change: Space-optimized dynamic programming */ + /* Coin change: space-optimized dynamic programming */ static int coinChangeDPComp(int[] coins, int amt) { int n = coins.length; int MAX = amt + 1; - // Initialize dp table + // Initialize the dp table int[] dp = new int[amt + 1]; Arrays.fill(dp, MAX); dp[0] = 0; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -63,10 +63,10 @@ public class coin_change { // Dynamic programming int res = coinChangeDP(coins, amt); - System.out.println("Minimum number of coins needed to make target amount is " + res); + System.out.println("Minimum number of coins needed to make the target amount: " + res); // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt); - System.out.println("Minimum number of coins needed to make target amount is " + res); + System.out.println("Minimum number of coins needed to make the target amount: " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/coin_change_ii.java b/en/codes/java/chapter_dynamic_programming/coin_change_ii.java index 67b85cd9f..ab84dbf57 100644 --- a/en/codes/java/chapter_dynamic_programming/coin_change_ii.java +++ b/en/codes/java/chapter_dynamic_programming/coin_change_ii.java @@ -7,23 +7,23 @@ package chapter_dynamic_programming; public class coin_change_ii { - /* Coin change II: Dynamic programming */ + /* Coin change II: dynamic programming */ static int coinChangeIIDP(int[] coins, int amt) { int n = coins.length; - // Initialize dp table + // Initialize the dp table int[][] dp = new int[n + 1][amt + 1]; - // Initialize first column + // Initialize the first column for (int i = 0; i <= n; i++) { dp[i][0] = 1; } - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -31,20 +31,20 @@ public class coin_change_ii { return dp[n][amt]; } - /* Coin change II: Space-optimized dynamic programming */ + /* Coin change II: space-optimized dynamic programming */ static int coinChangeIIDPComp(int[] coins, int amt) { int n = coins.length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[amt + 1]; dp[0] = 1; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -58,10 +58,10 @@ public class coin_change_ii { // Dynamic programming int res = coinChangeIIDP(coins, amt); - System.out.println("Number of coin combinations to make target amount is " + res); + System.out.println("Number of coin combinations that make the target amount: " + res); // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt); - System.out.println("Number of coin combinations to make target amount is " + res); + System.out.println("Number of coin combinations that make the target amount: " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/edit_distance.java b/en/codes/java/chapter_dynamic_programming/edit_distance.java index cf883b466..b7fa61d29 100644 --- a/en/codes/java/chapter_dynamic_programming/edit_distance.java +++ b/en/codes/java/chapter_dynamic_programming/edit_distance.java @@ -9,73 +9,73 @@ package chapter_dynamic_programming; import java.util.Arrays; public class edit_distance { - /* Edit distance: Brute-force search */ + /* Edit distance: brute-force search */ static int editDistanceDFS(String s, String t, int i, int j) { - // If both s and t are empty, return 0 + // If the current prefixes of s and t are both empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return j, the length of the current prefix of t if (i == 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return i, the length of the current prefix of s if (j == 0) return i; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s.charAt(i - 1) == t.charAt(j - 1)) return editDistanceDFS(s, t, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFS(s, t, i, j - 1); int delete = editDistanceDFS(s, t, i - 1, j); int replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits return Math.min(Math.min(insert, delete), replace) + 1; } - /* Edit distance: Memoization search */ + /* Edit distance: memoized search */ static int editDistanceDFSMem(String s, String t, int[][] mem, int i, int j) { - // If both s and t are empty, return 0 + // If the current prefixes of s and t are both empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return j, the length of the current prefix of t if (i == 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return i, the length of the current prefix of s if (j == 0) return i; - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) return mem[i][j]; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s.charAt(i - 1) == t.charAt(j - 1)) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 int insert = editDistanceDFSMem(s, t, mem, i, j - 1); int delete = editDistanceDFSMem(s, t, mem, i - 1, j); int replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = Math.min(Math.min(insert, delete), replace) + 1; return mem[i][j]; } - /* Edit distance: Dynamic programming */ + /* Edit distance: dynamic programming */ static int editDistanceDP(String s, String t) { int n = s.length(), m = t.length(); int[][] dp = new int[n + 1][m + 1]; - // State transition: first row and first column + // State transitions: first row and first column for (int i = 1; i <= n; i++) { dp[i][0] = i; } for (int j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s.charAt(i - 1) == t.charAt(j - 1)) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } } @@ -83,30 +83,30 @@ public class edit_distance { return dp[n][m]; } - /* Edit distance: Space-optimized dynamic programming */ + /* Edit distance: space-optimized dynamic programming */ static int editDistanceDPComp(String s, String t) { int n = s.length(), m = t.length(); int[] dp = new int[m + 1]; - // State transition: first row + // State transitions: first row for (int j = 1; j <= m; j++) { dp[j] = j; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i <= n; i++) { - // State transition: first column + // State transitions: first column int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s.charAt(i - 1) == t.charAt(j - 1)) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } return dp[m]; @@ -119,21 +119,21 @@ public class edit_distance { // Brute-force search int res = editDistanceDFS(s, t, n, m); - System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + System.out.println("Changing " + s + " to " + t + " requires at least " + res + " edits"); - // Memoization search + // Memoized search int[][] mem = new int[n + 1][m + 1]; for (int[] row : mem) Arrays.fill(row, -1); res = editDistanceDFSMem(s, t, mem, n, m); - System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + System.out.println("Changing " + s + " to " + t + " requires at least " + res + " edits"); // Dynamic programming res = editDistanceDP(s, t); - System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + System.out.println("Changing " + s + " to " + t + " requires at least " + res + " edits"); // Space-optimized dynamic programming res = editDistanceDPComp(s, t); - System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + System.out.println("Changing " + s + " to " + t + " requires at least " + res + " edits"); } } diff --git a/en/codes/java/chapter_dynamic_programming/knapsack.java b/en/codes/java/chapter_dynamic_programming/knapsack.java index a1b2afcbe..36be3a85c 100644 --- a/en/codes/java/chapter_dynamic_programming/knapsack.java +++ b/en/codes/java/chapter_dynamic_programming/knapsack.java @@ -10,58 +10,58 @@ import java.util.Arrays; public class knapsack { - /* 0-1 knapsack: Brute-force search */ + /* 0-1 knapsack: brute-force search */ static int knapsackDFS(int[] wgt, int[] val, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFS(wgt, val, i - 1, c); int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices return Math.max(no, yes); } - /* 0-1 knapsack: Memoization search */ + /* 0-1 knapsack: memoized search */ static int knapsackDFSMem(int[] wgt, int[] val, int[][] mem, int i, int c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] != -1) { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i int no = knapsackDFSMem(wgt, val, mem, i - 1, c); int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = Math.max(no, yes); return mem[i][c]; } - /* 0-1 knapsack: Dynamic programming */ + /* 0-1 knapsack: dynamic programming */ static int knapsackDP(int[] wgt, int[] val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table int[][] dp = new int[n + 1][cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = Math.max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); } } @@ -69,17 +69,17 @@ public class knapsack { return dp[n][cap]; } - /* 0-1 knapsack: Space-optimized dynamic programming */ + /* 0-1 knapsack: space-optimized dynamic programming */ static int knapsackDPComp(int[] wgt, int[] val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { - // Traverse in reverse order + // Traverse in decreasing order for (int c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -95,22 +95,22 @@ public class knapsack { // Brute-force search int res = knapsackDFS(wgt, val, n, cap); - System.out.println("Maximum item value not exceeding knapsack capacity is " + res); + System.out.println("Maximum total item value within the knapsack capacity: " + res); - // Memoization search + // Memoized search int[][] mem = new int[n + 1][cap + 1]; for (int[] row : mem) { Arrays.fill(row, -1); } res = knapsackDFSMem(wgt, val, mem, n, cap); - System.out.println("Maximum item value not exceeding knapsack capacity is " + res); + System.out.println("Maximum total item value within the knapsack capacity: " + res); // Dynamic programming res = knapsackDP(wgt, val, cap); - System.out.println("Maximum item value not exceeding knapsack capacity is " + res); + System.out.println("Maximum total item value within the knapsack capacity: " + res); // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap); - System.out.println("Maximum item value not exceeding knapsack capacity is " + res); + System.out.println("Maximum total item value within the knapsack capacity: " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java b/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java index 9e24d7f15..33dd5c781 100644 --- a/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java +++ b/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java @@ -9,24 +9,24 @@ package chapter_dynamic_programming; import java.util.Arrays; public class min_cost_climbing_stairs_dp { - /* Minimum cost climbing stairs: Dynamic programming */ + /* Minimum cost to climb stairs: dynamic programming */ public static int minCostClimbingStairsDP(int[] cost) { int n = cost.length - 1; if (n == 1 || n == 2) return cost[n]; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions int[] dp = new int[n + 1]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (int i = 3; i <= n; i++) { dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; } return dp[n]; } - /* Minimum cost climbing stairs: Space-optimized dynamic programming */ + /* Minimum cost to climb stairs: space-optimized dynamic programming */ public static int minCostClimbingStairsDPComp(int[] cost) { int n = cost.length - 1; if (n == 1 || n == 2) @@ -42,12 +42,12 @@ public class min_cost_climbing_stairs_dp { public static void main(String[] args) { int[] cost = { 0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1 }; - System.out.println(String.format("Input staircase cost list is %s", Arrays.toString(cost))); + System.out.println(String.format("Input stair costs: %s", Arrays.toString(cost))); int res = minCostClimbingStairsDP(cost); - System.out.println(String.format("Minimum cost to climb staircase is %d", res)); + System.out.println(String.format("Minimum cost to climb the stairs: %d", res)); res = minCostClimbingStairsDPComp(cost); - System.out.println(String.format("Minimum cost to climb staircase is %d", res)); + System.out.println(String.format("Minimum cost to climb the stairs: %d", res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/min_path_sum.java b/en/codes/java/chapter_dynamic_programming/min_path_sum.java index 06e8d5d46..4fcb0b3dc 100644 --- a/en/codes/java/chapter_dynamic_programming/min_path_sum.java +++ b/en/codes/java/chapter_dynamic_programming/min_path_sum.java @@ -9,60 +9,60 @@ package chapter_dynamic_programming; import java.util.Arrays; public class min_path_sum { - /* Minimum path sum: Brute-force search */ + /* Minimum path sum: brute-force search */ static int minPathSumDFS(int[][] grid, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return Integer.MAX_VALUE; } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) int up = minPathSumDFS(grid, i - 1, j); int left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return Math.min(left, up) + grid[i][j]; } - /* Minimum path sum: Memoization search */ + /* Minimum path sum: memoized search */ static int minPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return Integer.MAX_VALUE; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above int up = minPathSumDFSMem(grid, mem, i - 1, j); int left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = Math.min(left, up) + grid[i][j]; return mem[i][j]; } - /* Minimum path sum: Dynamic programming */ + /* Minimum path sum: dynamic programming */ static int minPathSumDP(int[][] grid) { int n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table int[][] dp = new int[n][m]; dp[0][0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (int j = 1; j < m; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for (int i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -71,21 +71,21 @@ public class min_path_sum { return dp[n - 1][m - 1]; } - /* Minimum path sum: Space-optimized dynamic programming */ + /* Minimum path sum: space-optimized dynamic programming */ static int minPathSumDPComp(int[][] grid) { int n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[m]; - // State transition: first row + // State transitions: first row dp[0] = grid[0][0]; for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for (int i = 1; i < n; i++) { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for (int j = 1; j < m; j++) { dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -104,22 +104,22 @@ public class min_path_sum { // Brute-force search int res = minPathSumDFS(grid, n - 1, m - 1); - System.out.println("Minimum path sum from top-left to bottom-right is " + res); + System.out.println("Minimum path sum from the top-left to the bottom-right corner: " + res); - // Memoization search + // Memoized search int[][] mem = new int[n][m]; for (int[] row : mem) { Arrays.fill(row, -1); } res = minPathSumDFSMem(grid, mem, n - 1, m - 1); - System.out.println("Minimum path sum from top-left to bottom-right is " + res); + System.out.println("Minimum path sum from the top-left to the bottom-right corner: " + res); // Dynamic programming res = minPathSumDP(grid); - System.out.println("Minimum path sum from top-left to bottom-right is " + res); + System.out.println("Minimum path sum from the top-left to the bottom-right corner: " + res); // Space-optimized dynamic programming res = minPathSumDPComp(grid); - System.out.println("Minimum path sum from top-left to bottom-right is " + res); + System.out.println("Minimum path sum from the top-left to the bottom-right corner: " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java b/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java index 78bce1a8e..eb3558089 100644 --- a/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java +++ b/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java @@ -7,19 +7,19 @@ package chapter_dynamic_programming; public class unbounded_knapsack { - /* Unbounded knapsack: Dynamic programming */ + /* Unbounded knapsack: dynamic programming */ static int unboundedKnapsackDP(int[] wgt, int[] val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table int[][] dp = new int[n + 1][cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = Math.max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); } } @@ -27,19 +27,19 @@ public class unbounded_knapsack { return dp[n][cap]; } - /* Unbounded knapsack: Space-optimized dynamic programming */ + /* Unbounded knapsack: space-optimized dynamic programming */ static int unboundedKnapsackDPComp(int[] wgt, int[] val, int cap) { int n = wgt.length; - // Initialize dp table + // Initialize the dp table int[] dp = new int[cap + 1]; - // State transition + // State transitions for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -54,10 +54,10 @@ public class unbounded_knapsack { // Dynamic programming int res = unboundedKnapsackDP(wgt, val, cap); - System.out.println("Maximum item value not exceeding knapsack capacity is " + res); + System.out.println("Maximum total item value within the knapsack capacity: " + res); // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, cap); - System.out.println("Maximum item value not exceeding knapsack capacity is " + res); + System.out.println("Maximum total item value within the knapsack capacity: " + res); } } diff --git a/en/codes/java/chapter_graph/graph_adjacency_list.java b/en/codes/java/chapter_graph/graph_adjacency_list.java index c80b6bca0..1974330e4 100644 --- a/en/codes/java/chapter_graph/graph_adjacency_list.java +++ b/en/codes/java/chapter_graph/graph_adjacency_list.java @@ -9,9 +9,9 @@ package chapter_graph; import java.util.*; import utils.*; -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ class GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors Map> adjList; /* Constructor */ @@ -30,7 +30,7 @@ class GraphAdjList { return adjList.size(); } - /* Add edge */ + /* Add an edge */ public void addEdge(Vertex vet1, Vertex vet2) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) throw new IllegalArgumentException(); @@ -39,7 +39,7 @@ class GraphAdjList { adjList.get(vet2).add(vet1); } - /* Remove edge */ + /* Remove an edge */ public void removeEdge(Vertex vet1, Vertex vet2) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) throw new IllegalArgumentException(); @@ -48,27 +48,27 @@ class GraphAdjList { adjList.get(vet2).remove(vet1); } - /* Add vertex */ + /* Add a vertex */ public void addVertex(Vertex vet) { if (adjList.containsKey(vet)) return; - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list adjList.put(vet, new ArrayList<>()); } - /* Remove vertex */ + /* Remove a vertex */ public void removeVertex(Vertex vet) { if (!adjList.containsKey(vet)) throw new IllegalArgumentException(); - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list adjList.remove(vet); - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for (List list : adjList.values()) { list.remove(vet); } } - /* Print adjacency list */ + /* Print the adjacency list */ public void print() { System.out.println("Adjacency list ="); for (Map.Entry> pair : adjList.entrySet()) { @@ -82,36 +82,36 @@ class GraphAdjList { public class graph_adjacency_list { public static void main(String[] args) { - /* Add edge */ + /* Initialize the undirected graph */ Vertex[] v = Vertex.valsToVets(new int[] { 1, 3, 2, 5, 4 }); Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[2], v[3] }, { v[2], v[4] }, { v[3], v[4] } }; GraphAdjList graph = new GraphAdjList(edges); - System.out.println("\nAfter initialization, graph is"); + System.out.println("\nGraph after initialization:"); graph.print(); - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.addEdge(v[0], v[2]); - System.out.println("\nAfter adding edge 1-2, graph is"); + System.out.println("\nGraph after adding edge 1-2:"); graph.print(); - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.removeEdge(v[0], v[1]); - System.out.println("\nAfter removing edge 1-3, graph is"); + System.out.println("\nGraph after removing edge 1-3:"); graph.print(); - /* Add vertex */ + /* Add a vertex */ Vertex v5 = new Vertex(6); graph.addVertex(v5); - System.out.println("\nAfter adding vertex 6, graph is"); + System.out.println("\nGraph after adding vertex 6:"); graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.removeVertex(v[1]); - System.out.println("\nAfter removing vertex 3, graph is"); + System.out.println("\nGraph after removing vertex 3:"); graph.print(); } } diff --git a/en/codes/java/chapter_graph/graph_adjacency_matrix.java b/en/codes/java/chapter_graph/graph_adjacency_matrix.java index 99b26e6c7..76cbc4e36 100644 --- a/en/codes/java/chapter_graph/graph_adjacency_matrix.java +++ b/en/codes/java/chapter_graph/graph_adjacency_matrix.java @@ -9,21 +9,21 @@ package chapter_graph; import utils.*; import java.util.*; -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat { - List vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" - List> adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + List vertices; // Vertex list: elements are vertex values, and indices are vertex indices + List> adjMat; // Adjacency matrix: row and column indices correspond to vertex indices /* Constructor */ public GraphAdjMat(int[] vertices, int[][] edges) { this.vertices = new ArrayList<>(); this.adjMat = new ArrayList<>(); - // Add vertex + // Add a vertex for (int val : vertices) { addVertex(val); } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for (int[] e : edges) { addEdge(e[0], e[1]); } @@ -34,10 +34,10 @@ class GraphAdjMat { return vertices.size(); } - /* Add vertex */ + /* Add a vertex */ public void addVertex(int val) { int n = size(); - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list vertices.add(val); // Add a row to the adjacency matrix List newRow = new ArrayList<>(n); @@ -51,42 +51,42 @@ class GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ public void removeVertex(int index) { if (index >= size()) throw new IndexOutOfBoundsException(); - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list vertices.remove(index); - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix adjMat.remove(index); - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for (List row : adjMat) { row.remove(index); } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices public void addEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw new IndexOutOfBoundsException(); - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal adjMat.get(i).set(j, 1); adjMat.get(j).set(i, 1); } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices public void removeEdge(int i, int j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw new IndexOutOfBoundsException(); adjMat.get(i).set(j, 0); adjMat.get(j).set(i, 0); } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ public void print() { System.out.print("Vertex list = "); System.out.println(vertices); @@ -97,35 +97,35 @@ class GraphAdjMat { public class graph_adjacency_matrix { public static void main(String[] args) { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices int[] vertices = { 1, 3, 2, 5, 4 }; int[][] edges = { { 0, 1 }, { 0, 3 }, { 1, 2 }, { 2, 3 }, { 2, 4 }, { 3, 4 } }; GraphAdjMat graph = new GraphAdjMat(vertices, edges); - System.out.println("\nAfter initialization, graph is"); + System.out.println("\nGraph after initialization:"); graph.print(); - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(0, 2); - System.out.println("\nAfter adding edge 1-2, graph is"); + System.out.println("\nGraph after adding edge 1-2:"); graph.print(); - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(0, 1); - System.out.println("\nAfter removing edge 1-3, graph is"); + System.out.println("\nGraph after removing edge 1-3:"); graph.print(); - /* Add vertex */ + /* Add a vertex */ graph.addVertex(6); - System.out.println("\nAfter adding vertex 6, graph is"); + System.out.println("\nGraph after adding vertex 6:"); graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(1); - System.out.println("\nAfter removing vertex 3, graph is"); + System.out.println("\nGraph after removing vertex 3:"); graph.print(); } } diff --git a/en/codes/java/chapter_graph/graph_bfs.java b/en/codes/java/chapter_graph/graph_bfs.java index e9262f5df..fccac6173 100644 --- a/en/codes/java/chapter_graph/graph_bfs.java +++ b/en/codes/java/chapter_graph/graph_bfs.java @@ -11,45 +11,45 @@ import utils.*; public class graph_bfs { /* Breadth-first traversal */ - // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + // Represent the graph with an adjacency list to access all neighbors of a given vertex static List graphBFS(GraphAdjList graph, Vertex startVet) { // Vertex traversal sequence List res = new ArrayList<>(); - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices Set visited = new HashSet<>(); visited.add(startVet); - // Queue used to implement BFS + // Use a queue to implement BFS Queue que = new LinkedList<>(); que.offer(startVet); - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited while (!que.isEmpty()) { Vertex vet = que.poll(); // Dequeue the front vertex - res.add(vet); // Record visited vertex - // Traverse all adjacent vertices of this vertex + res.add(vet); // Record the visited vertex + // Traverse all neighbors of this vertex for (Vertex adjVet : graph.adjList.get(vet)) { if (visited.contains(adjVet)) - continue; // Skip vertices that have been visited - que.offer(adjVet); // Only enqueue unvisited vertices + continue; // Skip vertices that have already been visited + que.offer(adjVet); // Enqueue only unvisited vertices visited.add(adjVet); // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res; } public static void main(String[] args) { - /* Add edge */ + /* Initialize the undirected graph */ Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[1], v[4] }, { v[2], v[5] }, { v[3], v[4] }, { v[3], v[6] }, { v[4], v[5] }, { v[4], v[7] }, { v[5], v[8] }, { v[6], v[7] }, { v[7], v[8] } }; GraphAdjList graph = new GraphAdjList(edges); - System.out.println("\nAfter initialization, graph is"); + System.out.println("\nGraph after initialization:"); graph.print(); /* Breadth-first traversal */ List res = graphBFS(graph, v[0]); - System.out.println("\nBreadth-first traversal (BFS) vertex sequence is"); + System.out.println("\nVertex sequence from breadth-first traversal (BFS):"); System.out.println(Vertex.vetsToVals(res)); } } diff --git a/en/codes/java/chapter_graph/graph_dfs.java b/en/codes/java/chapter_graph/graph_dfs.java index fb1efcbf4..12775dc72 100644 --- a/en/codes/java/chapter_graph/graph_dfs.java +++ b/en/codes/java/chapter_graph/graph_dfs.java @@ -12,40 +12,40 @@ import utils.*; public class graph_dfs { /* Depth-first traversal helper function */ static void dfs(GraphAdjList graph, Set visited, List res, Vertex vet) { - res.add(vet); // Record visited vertex + res.add(vet); // Record the visited vertex visited.add(vet); // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for (Vertex adjVet : graph.adjList.get(vet)) { if (visited.contains(adjVet)) - continue; // Skip vertices that have been visited - // Recursively visit adjacent vertices + continue; // Skip vertices that have already been visited + // Recursively visit the neighboring vertex dfs(graph, visited, res, adjVet); } } /* Depth-first traversal */ - // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + // Represent the graph with an adjacency list to access all neighbors of a given vertex static List graphDFS(GraphAdjList graph, Vertex startVet) { // Vertex traversal sequence List res = new ArrayList<>(); - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices Set visited = new HashSet<>(); dfs(graph, visited, res, startVet); return res; } public static void main(String[] args) { - /* Add edge */ + /* Initialize the undirected graph */ Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6 }); Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[2], v[5] }, { v[4], v[5] }, { v[5], v[6] } }; GraphAdjList graph = new GraphAdjList(edges); - System.out.println("\nAfter initialization, graph is"); + System.out.println("\nGraph after initialization:"); graph.print(); /* Depth-first traversal */ List res = graphDFS(graph, v[0]); - System.out.println("\nDepth-first traversal (DFS) vertex sequence is"); + System.out.println("\nVertex sequence from depth-first traversal (DFS):"); System.out.println(Vertex.vetsToVals(res)); } } diff --git a/en/codes/java/chapter_greedy/coin_change_greedy.java b/en/codes/java/chapter_greedy/coin_change_greedy.java index 09201ba8f..092ad3afd 100644 --- a/en/codes/java/chapter_greedy/coin_change_greedy.java +++ b/en/codes/java/chapter_greedy/coin_change_greedy.java @@ -9,14 +9,14 @@ package chapter_greedy; import java.util.Arrays; public class coin_change_greedy { - /* Coin change: Greedy algorithm */ + /* Coin change: greedy algorithm */ static int coinChangeGreedy(int[] coins, int amt) { - // Assume coins list is sorted + // Assume the coins list is sorted int i = coins.length - 1; int count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (amt > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -24,32 +24,32 @@ public class coin_change_greedy { amt -= coins[i]; count++; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt == 0 ? count : -1; } public static void main(String[] args) { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution int[] coins = { 1, 5, 10, 20, 50, 100 }; int amt = 186; int res = coinChangeGreedy(coins, amt); System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); - System.out.println("Minimum number of coins needed to make " + amt + " is " + res); + System.out.println("For amount " + amt + " the greedy coin count is " + res); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = new int[] { 1, 20, 50 }; amt = 60; res = coinChangeGreedy(coins, amt); System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); - System.out.println("Minimum number of coins needed to make " + amt + " is " + res); - System.out.println("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); + System.out.println("For amount " + amt + " the greedy coin count is " + res); + System.out.println("The actual minimum is 3 coins: 20 + 20 + 20"); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = new int[] { 1, 49, 50 }; amt = 98; res = coinChangeGreedy(coins, amt); System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); - System.out.println("Minimum number of coins needed to make " + amt + " is " + res); - System.out.println("Actually the minimum number needed is 2, i.e., 49 + 49"); + System.out.println("For amount " + amt + " the greedy coin count is " + res); + System.out.println("The actual minimum is 2 coins: 49 + 49"); } } diff --git a/en/codes/java/chapter_greedy/fractional_knapsack.java b/en/codes/java/chapter_greedy/fractional_knapsack.java index fa0f59354..fa9b43ebf 100644 --- a/en/codes/java/chapter_greedy/fractional_knapsack.java +++ b/en/codes/java/chapter_greedy/fractional_knapsack.java @@ -21,26 +21,26 @@ class Item { } public class fractional_knapsack { - /* Fractional knapsack: Greedy algorithm */ + /* Fractional knapsack: greedy algorithm */ static double fractionalKnapsack(int[] wgt, int[] val, int cap) { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value Item[] items = new Item[wgt.length]; for (int i = 0; i < wgt.length; i++) { items[i] = new Item(wgt[i], val[i]); } - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order Arrays.sort(items, Comparator.comparingDouble(item -> -((double) item.v / item.w))); - // Loop for greedy selection + // Repeatedly make greedy choices double res = 0; for (Item item : items) { if (item.w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v; cap -= item.w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += (double) item.v / item.w * cap; - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break; } } @@ -54,6 +54,6 @@ public class fractional_knapsack { // Greedy algorithm double res = fractionalKnapsack(wgt, val, cap); - System.out.println("Maximum item value not exceeding knapsack capacity is " + res); + System.out.println("Maximum total item value within the knapsack capacity: " + res); } } diff --git a/en/codes/java/chapter_greedy/max_capacity.java b/en/codes/java/chapter_greedy/max_capacity.java index 684eb1cf6..d3c4a80c5 100644 --- a/en/codes/java/chapter_greedy/max_capacity.java +++ b/en/codes/java/chapter_greedy/max_capacity.java @@ -7,18 +7,18 @@ package chapter_greedy; public class max_capacity { - /* Max capacity: Greedy algorithm */ + /* Maximum container capacity: greedy algorithm */ static int maxCapacity(int[] ht) { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array int i = 0, j = ht.length - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 int res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity int cap = Math.min(ht[i], ht[j]) * (j - i); res = Math.max(res, cap); - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i++; } else { @@ -33,6 +33,6 @@ public class max_capacity { // Greedy algorithm int res = maxCapacity(ht); - System.out.println("Maximum capacity is " + res); + System.out.println("Maximum container capacity: " + res); } } diff --git a/en/codes/java/chapter_greedy/max_product_cutting.java b/en/codes/java/chapter_greedy/max_product_cutting.java index 91788951b..4f46b978e 100644 --- a/en/codes/java/chapter_greedy/max_product_cutting.java +++ b/en/codes/java/chapter_greedy/max_product_cutting.java @@ -9,24 +9,24 @@ package chapter_greedy; import java.lang.Math; public class max_product_cutting { - /* Max product cutting: Greedy algorithm */ + /* Maximum product after splitting: greedy algorithm */ public static int maxProductCutting(int n) { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder int a = n / 3; int b = n % 3; if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return (int) Math.pow(3, a - 1) * 2 * 2; } if (b == 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return (int) Math.pow(3, a) * 2; } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return (int) Math.pow(3, a); } @@ -35,6 +35,6 @@ public class max_product_cutting { // Greedy algorithm int res = maxProductCutting(n); - System.out.println("Maximum cutting product is " + res); + System.out.println("Maximum product after splitting: " + res); } } diff --git a/en/codes/java/chapter_hashing/array_hash_map.java b/en/codes/java/chapter_hashing/array_hash_map.java index 74388f984..b13e9909f 100644 --- a/en/codes/java/chapter_hashing/array_hash_map.java +++ b/en/codes/java/chapter_hashing/array_hash_map.java @@ -19,12 +19,12 @@ class Pair { } } -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { private List buckets; public ArrayHashMap() { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets buckets = new ArrayList<>(); for (int i = 0; i < 100; i++) { buckets.add(null); @@ -37,7 +37,7 @@ class ArrayHashMap { return index; } - /* Query operation */ + /* Lookup operation */ public String get(int key) { int index = hashFunc(key); Pair pair = buckets.get(index); @@ -46,7 +46,7 @@ class ArrayHashMap { return pair.val; } - /* Add operation */ + /* Insert operation */ public void put(int key, String val) { Pair pair = new Pair(key, val); int index = hashFunc(key); @@ -56,7 +56,7 @@ class ArrayHashMap { /* Remove operation */ public void remove(int key) { int index = hashFunc(key); - // Set to null to represent deletion + // Set the bucket to null to mark it as removed buckets.set(index, null); } @@ -90,7 +90,7 @@ class ArrayHashMap { return valueSet; } - /* Print hash table */ + /* Print the hash table */ public void print() { for (Pair kv : pairSet()) { System.out.println(kv.key + " -> " + kv.val); @@ -100,40 +100,40 @@ class ArrayHashMap { public class array_hash_map { public static void main(String[] args) { - /* Initialize hash table */ + /* Initialize the hash table */ ArrayHashMap map = new ArrayHashMap(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + System.out.println("\nHash table after insertion:\nKey -> Value"); map.print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value String name = map.get(15937); - System.out.println("\nInput student ID 15937, query name " + name); + System.out.println("\nLook up student ID 15937 to obtain the name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583); - System.out.println("\nAfter removing 10583, hash table is\nKey -> Value"); + System.out.println("\nHash table after removing 10583:\nKey -> Value"); map.print(); - /* Traverse hash table */ - System.out.println("\nTraverse key-value pairs Key->Value"); + /* Traverse the hash table */ + System.out.println("\nTraverse key-value pairs: Key->Value"); for (Pair kv : map.pairSet()) { System.out.println(kv.key + " -> " + kv.val); } - System.out.println("\nTraverse keys only Key"); + System.out.println("\nTraverse keys only: Key"); for (int key : map.keySet()) { System.out.println(key); } - System.out.println("\nTraverse values only Value"); + System.out.println("\nTraverse values only: Value"); for (String val : map.valueSet()) { System.out.println(val); } diff --git a/en/codes/java/chapter_hashing/built_in_hash.java b/en/codes/java/chapter_hashing/built_in_hash.java index 4249778b0..9ac1470bd 100644 --- a/en/codes/java/chapter_hashing/built_in_hash.java +++ b/en/codes/java/chapter_hashing/built_in_hash.java @@ -13,26 +13,26 @@ public class built_in_hash { public static void main(String[] args) { int num = 3; int hashNum = Integer.hashCode(num); - System.out.println("Hash value of integer " + num + " is " + hashNum); + System.out.println("Integer " + num + " has hash value " + hashNum); boolean bol = true; int hashBol = Boolean.hashCode(bol); - System.out.println("Hash value of boolean " + bol + " is " + hashBol); + System.out.println("Boolean " + bol + " has hash value " + hashBol); double dec = 3.14159; int hashDec = Double.hashCode(dec); - System.out.println("Hash value of decimal " + dec + " is " + hashDec); + System.out.println("Decimal " + dec + " has hash value " + hashDec); - String str = "Hello Algo"; + String str = "Hello 算法"; int hashStr = str.hashCode(); - System.out.println("Hash value of string " + str + " is " + hashStr); + System.out.println("String " + str + " has hash value " + hashStr); - Object[] arr = { 12836, "Xiao Ha" }; + Object[] arr = { 12836, "小哈" }; int hashTup = Arrays.hashCode(arr); - System.out.println("Hash value of array " + Arrays.toString(arr) + " is " + hashTup); + System.out.println("Array " + Arrays.toString(arr) + " has hash value " + hashTup); ListNode obj = new ListNode(0); int hashObj = obj.hashCode(); - System.out.println("Hash value of node object " + obj + " is " + hashObj); + System.out.println("Node object " + obj + " has hash value " + hashObj); } } diff --git a/en/codes/java/chapter_hashing/hash_map.java b/en/codes/java/chapter_hashing/hash_map.java index 9c368d634..6efa0798d 100644 --- a/en/codes/java/chapter_hashing/hash_map.java +++ b/en/codes/java/chapter_hashing/hash_map.java @@ -11,40 +11,40 @@ import utils.*; public class hash_map { public static void main(String[] args) { - /* Initialize hash table */ + /* Initialize the hash table */ Map map = new HashMap<>(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + System.out.println("\nHash table after insertion:\nKey -> Value"); PrintUtil.printHashMap(map); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value String name = map.get(15937); - System.out.println("\nInput student ID 15937, query name " + name); + System.out.println("\nLook up student ID 15937 to obtain the name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583); - System.out.println("\nAfter removing 10583, hash table is\nKey -> Value"); + System.out.println("\nHash table after removing 10583:\nKey -> Value"); PrintUtil.printHashMap(map); - /* Traverse hash table */ - System.out.println("\nTraverse key-value pairs Key->Value"); + /* Traverse the hash table */ + System.out.println("\nTraverse key-value pairs: Key->Value"); for (Map.Entry kv : map.entrySet()) { System.out.println(kv.getKey() + " -> " + kv.getValue()); } - System.out.println("\nTraverse keys only Key"); + System.out.println("\nTraverse keys only: Key"); for (int key : map.keySet()) { System.out.println(key); } - System.out.println("\nTraverse values only Value"); + System.out.println("\nTraverse values only: Value"); for (String val : map.values()) { System.out.println(val); } diff --git a/en/codes/java/chapter_hashing/hash_map_chaining.java b/en/codes/java/chapter_hashing/hash_map_chaining.java index 2db41716f..25813290d 100644 --- a/en/codes/java/chapter_hashing/hash_map_chaining.java +++ b/en/codes/java/chapter_hashing/hash_map_chaining.java @@ -13,8 +13,8 @@ import java.util.List; class HashMapChaining { int size; // Number of key-value pairs int capacity; // Hash table capacity - double loadThres; // Load factor threshold for triggering expansion - int extendRatio; // Expansion multiplier + double loadThres; // Load factor threshold that triggers capacity growth + int extendRatio; // Capacity growth factor List> buckets; // Bucket array /* Constructor */ @@ -39,36 +39,36 @@ class HashMapChaining { return (double) size / capacity; } - /* Query operation */ + /* Lookup operation */ String get(int key) { int index = hashFunc(key); List bucket = buckets.get(index); - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for (Pair pair : bucket) { if (pair.key == key) { return pair.val; } } - // If key is not found, return null + // Return null if key is not found return null; } - /* Add operation */ + /* Insert operation */ void put(int key, String val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > loadThres) { extend(); } int index = hashFunc(key); List bucket = buckets.get(index); - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for (Pair pair : bucket) { if (pair.key == key) { pair.val = val; return; } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end Pair pair = new Pair(key, val); bucket.add(pair); size++; @@ -78,7 +78,7 @@ class HashMapChaining { void remove(int key) { int index = hashFunc(key); List bucket = buckets.get(index); - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (Pair pair : bucket) { if (pair.key == key) { bucket.remove(pair); @@ -88,18 +88,18 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ void extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table List> bucketsTmp = buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio; buckets = new ArrayList<>(capacity); for (int i = 0; i < capacity; i++) { buckets.add(new ArrayList<>()); } size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (List bucket : bucketsTmp) { for (Pair pair : bucket) { put(pair.key, pair.val); @@ -107,7 +107,7 @@ class HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ void print() { for (List bucket : buckets) { List res = new ArrayList<>(); @@ -121,28 +121,28 @@ class HashMapChaining { public class hash_map_chaining { public static void main(String[] args) { - /* Initialize hash table */ + /* Initialize the hash table */ HashMapChaining map = new HashMapChaining(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + System.out.println("\nHash table after insertion:\nKey -> Value"); map.print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value String name = map.get(13276); - System.out.println("\nInput student ID 13276, query name " + name); + System.out.println("\nLook up student ID 13276 to obtain the name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(12836); - System.out.println("\nAfter removing 12836, hash table is\nKey -> Value"); + System.out.println("\nHash table after removing 12836:\nKey -> Value"); map.print(); } } diff --git a/en/codes/java/chapter_hashing/hash_map_open_addressing.java b/en/codes/java/chapter_hashing/hash_map_open_addressing.java index 6d157fed8..2372362be 100644 --- a/en/codes/java/chapter_hashing/hash_map_open_addressing.java +++ b/en/codes/java/chapter_hashing/hash_map_open_addressing.java @@ -10,10 +10,10 @@ package chapter_hashing; class HashMapOpenAddressing { private int size; // Number of key-value pairs private int capacity = 4; // Hash table capacity - private final double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion - private final int extendRatio = 2; // Expansion multiplier + private final double loadThres = 2.0 / 3.0; // Load factor threshold that triggers capacity growth + private final int extendRatio = 2; // Capacity growth factor private Pair[] buckets; // Bucket array - private final Pair TOMBSTONE = new Pair(-1, "-1"); // Removal marker + private final Pair TOMBSTONE = new Pair(-1, "-1"); // Tombstone marker /* Constructor */ public HashMapOpenAddressing() { @@ -31,83 +31,83 @@ class HashMapOpenAddressing { return (double) size / capacity; } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ private int findBucket(int key) { int index = hashFunc(key); int firstTombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (buckets[index] != null) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (buckets[index].key == key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; - return firstTombstone; // Return the moved bucket index + return firstTombstone; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone == -1 ? index : firstTombstone; } - /* Query operation */ + /* Lookup operation */ public String get(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if (buckets[index] != null && buckets[index] != TOMBSTONE) { return buckets[index].val; } - // If key-value pair does not exist, return null + // Return null if the key-value pair is absent return null; } - /* Add operation */ + /* Insert operation */ public void put(int key, String val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > loadThres) { extend(); } - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index].val = val; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it buckets[index] = new Pair(key, val); size++; } /* Remove operation */ public void remove(int key) { - // Search for bucket index corresponding to key + // Find the bucket index for key int index = findBucket(key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index] = TOMBSTONE; size--; } } - /* Expand hash table */ + /* Grow the hash table capacity */ private void extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table Pair[] bucketsTmp = buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio; buckets = new Pair[capacity]; size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (Pair pair : bucketsTmp) { if (pair != null && pair != TOMBSTONE) { put(pair.key, pair.val); @@ -115,7 +115,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ public void print() { for (Pair pair : buckets) { if (pair == null) { @@ -131,28 +131,28 @@ class HashMapOpenAddressing { public class hash_map_open_addressing { public static void main(String[] args) { - // Initialize hash table + // Initialize the hash table HashMapOpenAddressing hashmap = new HashMapOpenAddressing(); - // Add operation - // Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Xiao Ha"); - hashmap.put(15937, "Xiao Luo"); - hashmap.put(16750, "Xiao Suan"); - hashmap.put(13276, "Xiao Fa"); - hashmap.put(10583, "Xiao Ya"); - System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); + // Insert operation + // Insert the key-value pair (key, val) into the hash table + hashmap.put(12836, "小哈"); + hashmap.put(15937, "小啰"); + hashmap.put(16750, "小算"); + hashmap.put(13276, "小法"); + hashmap.put(10583, "小鸭"); + System.out.println("\nHash table after insertion:\nKey -> Value"); hashmap.print(); - // Query operation - // Input key into hash table to get value val + // Lookup operation + // Look up key in the hash table to obtain val String name = hashmap.get(13276); - System.out.println("\nInput student ID 13276, query name " + name); + System.out.println("\nLook up student ID 13276 to obtain the name " + name); // Remove operation - // Remove key-value pair (key, val) from hash table + // Remove the key-value pair (key, val) from the hash table hashmap.remove(16750); - System.out.println("\nAfter removing 16750, hash table is\nKey -> Value"); + System.out.println("\nHash table after removing 16750:\nKey -> Value"); hashmap.print(); } } diff --git a/en/codes/java/chapter_hashing/simple_hash.java b/en/codes/java/chapter_hashing/simple_hash.java index 1babeb451..ff228c0e2 100644 --- a/en/codes/java/chapter_hashing/simple_hash.java +++ b/en/codes/java/chapter_hashing/simple_hash.java @@ -37,7 +37,7 @@ public class simple_hash { return hash & MODULUS; } - /* Rotational hash */ + /* Rotating hash */ static int rotHash(String key) { long hash = 0; final int MODULUS = 1000000007; @@ -48,18 +48,18 @@ public class simple_hash { } public static void main(String[] args) { - String key = "Hello Algo"; + String key = "Hello 算法"; int hash = addHash(key); - System.out.println("Additive hash value is " + hash); + System.out.println("Additive hash value: " + hash); hash = mulHash(key); - System.out.println("Multiplicative hash value is " + hash); + System.out.println("Multiplicative hash value: " + hash); hash = xorHash(key); - System.out.println("XOR hash value is " + hash); + System.out.println("XOR hash value: " + hash); hash = rotHash(key); - System.out.println("Rotational hash value is " + hash); + System.out.println("Rotating hash value: " + hash); } } diff --git a/en/codes/java/chapter_heap/heap.java b/en/codes/java/chapter_heap/heap.java index 68cb08198..c7ae37863 100644 --- a/en/codes/java/chapter_heap/heap.java +++ b/en/codes/java/chapter_heap/heap.java @@ -11,56 +11,56 @@ import java.util.*; public class heap { public static void testPush(Queue heap, int val) { - heap.offer(val); // Element enters heap - System.out.format("\nAfter element %d enters heap\n", val); + heap.offer(val); // Push an element onto the heap + System.out.format("\nAfter pushing %d onto the heap:\n", val); PrintUtil.printHeap(heap); } public static void testPop(Queue heap) { - int val = heap.poll(); // Time complexity is O(n), not O(nlogn) - System.out.format("\nAfter heap top element %d exits heap\n", val); + int val = heap.poll(); // Pop the top element from the heap + System.out.format("\nAfter popping the top element %d:\n", val); PrintUtil.printHeap(heap); } public static void main(String[] args) { - /* Initialize heap */ - // Python's heapq module implements min heap by default + /* Initialize heaps */ + // Initialize a min heap Queue minHeap = new PriorityQueue<>(); - // Initialize max heap (modify Comparator using lambda expression) + // Initialize a max heap by supplying a lambda expression as the Comparator Queue maxHeap = new PriorityQueue<>((a, b) -> b - a); - System.out.println("\nThe following test cases are for max heap"); + System.out.println("\nThe following example uses a max heap"); - /* Element enters heap */ + /* Push an element onto the heap */ testPush(maxHeap, 1); testPush(maxHeap, 3); testPush(maxHeap, 2); testPush(maxHeap, 5); testPush(maxHeap, 4); - /* Check if heap is empty */ + /* Get the top element of the heap */ int peek = maxHeap.peek(); - System.out.format("\nHeap top element is %d\n", peek); + System.out.format("\nTop element: %d\n", peek); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); - /* Get heap size */ + /* Get the heap size */ int size = maxHeap.size(); - System.out.format("\nHeap element count is %d\n", size); + System.out.format("\nNumber of elements in the heap: %d\n", size); - /* Check if heap is empty */ + /* Check whether the heap is empty */ boolean isEmpty = maxHeap.isEmpty(); - System.out.format("\nHeap is empty %b\n", isEmpty); + System.out.format("\nIs the heap empty? %b\n", isEmpty); - /* Input list and build heap */ - // Time complexity is O(n), not O(nlogn) + /* Build a heap from an input list */ + // The time complexity is O(n), not O(nlogn) minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4)); - System.out.println("\nAfter inputting list and building min heap"); + System.out.println("\nAfter building a min heap from the input list:"); PrintUtil.printHeap(minHeap); } } diff --git a/en/codes/java/chapter_heap/my_heap.java b/en/codes/java/chapter_heap/my_heap.java index 0698e060d..41680f77e 100644 --- a/en/codes/java/chapter_heap/my_heap.java +++ b/en/codes/java/chapter_heap/my_heap.java @@ -11,32 +11,32 @@ import java.util.*; /* Max heap */ class MaxHeap { - // Use list instead of array, no need to consider capacity expansion + // Use a resizable list instead of an array to avoid managing capacity growth private List maxHeap; - /* Constructor, build heap based on input list */ + /* Constructor: build a heap from the input list */ public MaxHeap(List nums) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order maxHeap = new ArrayList<>(nums); - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for (int i = parent(size() - 1); i >= 0; i--) { siftDown(i); } } - /* Get index of left child node */ + /* Get the left child index */ private int left(int i) { return 2 * i + 1; } - /* Get index of right child node */ + /* Get the right child index */ private int right(int i) { return 2 * i + 2; } - /* Get index of parent node */ + /* Get the parent index */ private int parent(int i) { - return (i - 1) / 2; // Floor division + return (i - 1) / 2; // Integer division (truncates toward zero in Java) } /* Swap elements */ @@ -46,79 +46,79 @@ class MaxHeap { maxHeap.set(j, tmp); } - /* Get heap size */ + /* Get the heap size */ public int size() { return maxHeap.size(); } - /* Check if heap is empty */ + /* Check whether the heap is empty */ public boolean isEmpty() { return size() == 0; } - /* Access top element */ + /* Access the top element of the heap */ public int peek() { return maxHeap.get(0); } - /* Element enters heap */ + /* Push an element onto the heap */ public void push(int val) { - // Add node + // Add a node maxHeap.add(val); - // Heapify from bottom to top + // Sift up siftUp(size() - 1); } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ private void siftUp(int i) { while (true) { - // Get parent node of node i + // Get the parent of node i int p = parent(i); - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || maxHeap.get(i) <= maxHeap.get(p)) break; - // Swap two nodes + // Swap the two nodes swap(i, p); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Element exits heap */ + /* Pop an element from the heap */ public int pop() { - // Handle empty case + // Handle an empty heap if (isEmpty()) throw new IndexOutOfBoundsException(); - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) swap(0, size() - 1); - // Remove node + // Remove a node int val = maxHeap.remove(size() - 1); - // Return top element + // Sift down siftDown(0); - // Return heap top element + // Return the top element of the heap return val; } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ private void siftDown(int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = left(i), r = right(i), ma = i; if (l < size() && maxHeap.get(l) > maxHeap.get(ma)) ma = l; if (r < size() && maxHeap.get(r) > maxHeap.get(ma)) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break; - // Swap two nodes + // Swap the two nodes swap(i, ma); - // Loop downwards heapification + // Continue sifting down i = ma; } } - /* Driver Code */ + /* Print the heap (binary tree) */ public void print() { Queue queue = new PriorityQueue<>((a, b) -> { return b - a; }); queue.addAll(maxHeap); @@ -128,32 +128,32 @@ class MaxHeap { public class my_heap { public static void main(String[] args) { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ MaxHeap maxHeap = new MaxHeap(Arrays.asList(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2)); - System.out.println("\nAfter inputting list and building heap"); + System.out.println("\nAfter building a heap from the input list:"); maxHeap.print(); - /* Check if heap is empty */ + /* Get the top element of the heap */ int peek = maxHeap.peek(); - System.out.format("\nHeap top element is %d\n", peek); + System.out.format("\nTop element: %d\n", peek); - /* Element enters heap */ + /* Push an element onto the heap */ int val = 7; maxHeap.push(val); - System.out.format("\nAfter element %d enters heap\n", val); + System.out.format("\nAfter pushing %d onto the heap:\n", val); maxHeap.print(); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.pop(); - System.out.format("\nAfter heap top element %d exits heap\n", peek); + System.out.format("\nAfter popping the top element %d:\n", peek); maxHeap.print(); - /* Get heap size */ + /* Get the heap size */ int size = maxHeap.size(); - System.out.format("\nHeap element count is %d\n", size); + System.out.format("\nNumber of elements in the heap: %d\n", size); - /* Check if heap is empty */ + /* Check whether the heap is empty */ boolean isEmpty = maxHeap.isEmpty(); - System.out.format("\nHeap is empty %b\n", isEmpty); + System.out.format("\nIs the heap empty? %b\n", isEmpty); } } diff --git a/en/codes/java/chapter_heap/top_k.java b/en/codes/java/chapter_heap/top_k.java index c8a488111..5123e990e 100644 --- a/en/codes/java/chapter_heap/top_k.java +++ b/en/codes/java/chapter_heap/top_k.java @@ -10,17 +10,17 @@ import utils.*; import java.util.*; public class top_k { - /* Find the largest k elements in array based on heap */ + /* Use a heap to find the k largest elements in an array */ static Queue topKHeap(int[] nums, int k) { - // Python's heapq module implements min heap by default + // Initialize a min heap Queue heap = new PriorityQueue(); - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for (int i = 0; i < k; i++) { heap.offer(nums[i]); } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for (int i = k; i < nums.length; i++) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if (nums[i] > heap.peek()) { heap.poll(); heap.offer(nums[i]); @@ -34,7 +34,7 @@ public class top_k { int k = 3; Queue res = topKHeap(nums, k); - System.out.println("The largest " + k + " elements are"); + System.out.println("The largest " + k + " elements are:"); PrintUtil.printHeap(res); } } diff --git a/en/codes/java/chapter_searching/binary_search.java b/en/codes/java/chapter_searching/binary_search.java index 8ede4897b..8ac9ba3ed 100644 --- a/en/codes/java/chapter_searching/binary_search.java +++ b/en/codes/java/chapter_searching/binary_search.java @@ -7,39 +7,39 @@ package chapter_searching; public class binary_search { - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ static int binarySearch(int[] nums, int target) { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements int i = 0, j = nums.length - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j] + if (nums[m] < target) // In this case, target must be in [m+1, j] if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m-1] + else if (nums[m] > target) // In this case, target must be in [i, m-1] if it exists j = m - 1; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ static int binarySearchLCRO(int[] nums, int target) { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last int i = 0, j = nums.length; - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { int m = i + (j - i) / 2; // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j) + if (nums[m] < target) // In this case, target must be in [m+1, j) if it exists i = m + 1; - else if (nums[m] > target) // This means target is in the interval [i, m) + else if (nums[m] > target) // In this case, target must be in [i, m) if it exists j = m; - else // Found the target element, return its index + else // The target element was found; return its index return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } @@ -47,11 +47,11 @@ public class binary_search { int target = 6; int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ int index = binarySearch(nums, target); System.out.println("Index of target element 6 = " + index); - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums, target); System.out.println("Index of target element 6 = " + index); } diff --git a/en/codes/java/chapter_searching/binary_search_edge.java b/en/codes/java/chapter_searching/binary_search_edge.java index 8d6684dfa..69cdf8ba1 100644 --- a/en/codes/java/chapter_searching/binary_search_edge.java +++ b/en/codes/java/chapter_searching/binary_search_edge.java @@ -7,43 +7,43 @@ package chapter_searching; public class binary_search_edge { - /* Binary search for the leftmost target */ + /* Find the leftmost occurrence of target using binary search */ static int binarySearchLeftEdge(int[] nums, int target) { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target int i = binary_search_insertion.binarySearchInsertion(nums, target); - // Target not found, return -1 + // If target was not found, return -1 if (i == nums.length || nums[i] != target) { return -1; } - // Found target, return index i + // Target was found; return index i return i; } - /* Binary search for the rightmost target */ + /* Find the rightmost occurrence of target using binary search */ static int binarySearchRightEdge(int[] nums, int target) { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 int i = binary_search_insertion.binarySearchInsertion(nums, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // j points to the rightmost target, while i points to the first element greater than target int j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if (j == -1 || nums[j] != target) { return -1; } - // Found target, return index j + // Target was found; return index j return j; } public static void main(String[] args) { - // Array with duplicate elements + // Array containing duplicate elements int[] nums = { 1, 3, 6, 6, 6, 6, 6, 10, 12, 15 }; System.out.println("\nArray nums = " + java.util.Arrays.toString(nums)); - // Binary search left and right boundaries + // Find the left and right boundaries using binary search for (int target : new int[] { 6, 7 }) { int index = binarySearchLeftEdge(nums, target); - System.out.println("Index of leftmost element " + target + " is " + index); + System.out.println("Leftmost occurrence of " + target + " has index " + index); index = binarySearchRightEdge(nums, target); - System.out.println("Index of rightmost element " + target + " is " + index); + System.out.println("Rightmost occurrence of " + target + " has index " + index); } } } diff --git a/en/codes/java/chapter_searching/binary_search_insertion.java b/en/codes/java/chapter_searching/binary_search_insertion.java index 4b20deaf6..4a77a554a 100644 --- a/en/codes/java/chapter_searching/binary_search_insertion.java +++ b/en/codes/java/chapter_searching/binary_search_insertion.java @@ -7,34 +7,34 @@ package chapter_searching; class binary_search_insertion { - /* Binary search for insertion point (no duplicate elements) */ + /* Find an insertion point using binary search (no duplicates) */ static int binarySearchInsertionSimple(int[] nums, int target) { - int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - return m; // Found target, return insertion point m + return m; // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i; } - /* Binary search for insertion point (with duplicate elements) */ + /* Find an insertion point using binary search (with duplicates) */ static int binarySearchInsertion(int[] nums, int target) { - int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] + int i = 0, j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i @@ -42,22 +42,22 @@ class binary_search_insertion { } public static void main(String[] args) { - // Array without duplicate elements + // Array with no duplicate elements int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; System.out.println("\nArray nums = " + java.util.Arrays.toString(nums)); - // Binary search for insertion point + // Find an insertion point using binary search for (int target : new int[] { 6, 9 }) { int index = binarySearchInsertionSimple(nums, target); - System.out.println("Insertion point index for element " + target + " is " + index); + System.out.println("Element " + target + " has insertion point index " + index); } - // Array with duplicate elements + // Array containing duplicate elements nums = new int[] { 1, 3, 6, 6, 6, 6, 6, 10, 12, 15 }; System.out.println("\nArray nums = " + java.util.Arrays.toString(nums)); - // Binary search for insertion point + // Find an insertion point using binary search for (int target : new int[] { 2, 6, 20 }) { int index = binarySearchInsertion(nums, target); - System.out.println("Insertion point index for element " + target + " is " + index); + System.out.println("Element " + target + " has insertion point index " + index); } } } diff --git a/en/codes/java/chapter_searching/hashing_search.java b/en/codes/java/chapter_searching/hashing_search.java index ce920d6d2..eab7f0b0b 100644 --- a/en/codes/java/chapter_searching/hashing_search.java +++ b/en/codes/java/chapter_searching/hashing_search.java @@ -10,42 +10,42 @@ import utils.*; import java.util.*; public class hashing_search { - /* Hash search (array) */ + /* Hash-based search (array) */ static int hashingSearchArray(Map map, int target) { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key return map.getOrDefault(target, -1); } - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ static ListNode hashingSearchLinkedList(Map map, int target) { - // Hash table key: target node value, value: node object - // If key is not in hash table, return null + // Hash table key: target node value; value: node object + // Return null if the hash table does not contain this key return map.getOrDefault(target, null); } public static void main(String[] args) { int target = 3; - /* Hash search (array) */ + /* Hash-based search (array) */ int[] nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; - // Initialize hash table + // Initialize the hash table Map map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { - map.put(nums[i], i); // key: element, value: index + map.put(nums[i], i); // key: element; value: index } int index = hashingSearchArray(map, target); System.out.println("Index of target element 3 = " + index); - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ ListNode head = ListNode.arrToLinkedList(nums); - // Initialize hash table + // Initialize the hash table Map map1 = new HashMap<>(); while (head != null) { - map1.put(head.val, head); // key: node value, value: node + map1.put(head.val, head); // key: node value; value: node head = head.next; } ListNode node = hashingSearchLinkedList(map1, target); - System.out.println("Node object corresponding to target node value 3 is " + node); + System.out.println("The node object corresponding to target value 3 is " + node); } } diff --git a/en/codes/java/chapter_searching/linear_search.java b/en/codes/java/chapter_searching/linear_search.java index 4398e8209..55eebeaf2 100644 --- a/en/codes/java/chapter_searching/linear_search.java +++ b/en/codes/java/chapter_searching/linear_search.java @@ -11,13 +11,13 @@ import utils.*; public class linear_search { /* Linear search (array) */ static int linearSearchArray(int[] nums, int target) { - // Traverse array + // Traverse the array for (int i = 0; i < nums.length; i++) { - // Found the target element, return its index + // The target element was found; return its index if (nums[i] == target) return i; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } @@ -25,26 +25,26 @@ public class linear_search { static ListNode linearSearchLinkedList(ListNode head, int target) { // Traverse the linked list while (head != null) { - // Found the target node, return it + // The target node was found; return it if (head.val == target) return head; head = head.next; } - // Target node not found, return null + // The target node was not found; return null return null; } public static void main(String[] args) { int target = 3; - /* Perform linear search in array */ + /* Perform linear search on the array */ int[] nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; int index = linearSearchArray(nums, target); System.out.println("Index of target element 3 = " + index); - /* Perform linear search in linked list */ + /* Perform linear search on the linked list */ ListNode head = ListNode.arrToLinkedList(nums); ListNode node = linearSearchLinkedList(head, target); - System.out.println("Node object corresponding to target node value 3 is " + node); + System.out.println("The node object corresponding to target value 3 is " + node); } } diff --git a/en/codes/java/chapter_searching/two_sum.java b/en/codes/java/chapter_searching/two_sum.java index 34adfcb07..5d0c744a2 100644 --- a/en/codes/java/chapter_searching/two_sum.java +++ b/en/codes/java/chapter_searching/two_sum.java @@ -9,10 +9,10 @@ package chapter_searching; import java.util.*; public class two_sum { - /* Method 1: Brute force enumeration */ + /* Method 1: brute-force enumeration */ static int[] twoSumBruteForce(int[] nums, int target) { int size = nums.length; - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) @@ -22,12 +22,12 @@ public class two_sum { return new int[0]; } - /* Method 2: Auxiliary hash table */ + /* Method 2: auxiliary hash table */ static int[] twoSumHashTable(int[] nums, int target) { int size = nums.length; - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space Map dic = new HashMap<>(); - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (int i = 0; i < size; i++) { if (dic.containsKey(target - nums[i])) { return new int[] { dic.get(target - nums[i]), i }; @@ -38,16 +38,16 @@ public class two_sum { } public static void main(String[] args) { - // ======= Test Case ======= + // ======= Test case ======= int[] nums = { 2, 7, 11, 15 }; int target = 13; - // ====== Driver Code ====== + // ====== Driver code ====== // Method 1 int[] res = twoSumBruteForce(nums, target); - System.out.println("Method 1 res = " + Arrays.toString(res)); + System.out.println("Method 1: res = " + Arrays.toString(res)); // Method 2 res = twoSumHashTable(nums, target); - System.out.println("Method 2 res = " + Arrays.toString(res)); + System.out.println("Method 2: res = " + Arrays.toString(res)); } } diff --git a/en/codes/java/chapter_sorting/bubble_sort.java b/en/codes/java/chapter_sorting/bubble_sort.java index a6b2325d4..2a53685f0 100644 --- a/en/codes/java/chapter_sorting/bubble_sort.java +++ b/en/codes/java/chapter_sorting/bubble_sort.java @@ -11,9 +11,9 @@ import java.util.*; public class bubble_sort { /* Bubble sort */ static void bubbleSort(int[] nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] @@ -27,31 +27,31 @@ public class bubble_sort { /* Bubble sort (flag optimization) */ static void bubbleSortWithFlag(int[] nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - boolean flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + boolean flag = false; // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - flag = true; // Record element swap + flag = true; // Record that a swap occurred } } if (!flag) - break; // No elements were swapped in this round of "bubbling", exit directly + break; // Exit immediately if this pass made no swaps } } public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; bubbleSort(nums); - System.out.println("After bubble sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After bubble sort, nums = " + Arrays.toString(nums)); int[] nums1 = { 4, 1, 3, 1, 5, 2 }; bubbleSortWithFlag(nums1); - System.out.println("After bubble sort completes, nums1 = " + Arrays.toString(nums1)); + System.out.println("After bubble sort, nums1 = " + Arrays.toString(nums1)); } } diff --git a/en/codes/java/chapter_sorting/bucket_sort.java b/en/codes/java/chapter_sorting/bucket_sort.java index 9b76f24ea..691e4df01 100644 --- a/en/codes/java/chapter_sorting/bucket_sort.java +++ b/en/codes/java/chapter_sorting/bucket_sort.java @@ -11,25 +11,25 @@ import java.util.*; public class bucket_sort { /* Bucket sort */ static void bucketSort(float[] nums) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = n/2 buckets, expecting 2 elements per bucket int k = nums.length / 2; List> buckets = new ArrayList<>(); for (int i = 0; i < k; i++) { buckets.add(new ArrayList<>()); } - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for (float num : nums) { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); use num * k to map each value to an index in [0, k-1] int i = (int) (num * k); // Add num to bucket i buckets.get(i).add(num); } // 2. Sort each bucket for (List bucket : buckets) { - // Use built-in sorting function, can also replace with other sorting algorithms + // Use the built-in sorting function, or replace it with another sorting algorithm Collections.sort(bucket); } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results int i = 0; for (List bucket : buckets) { for (float num : bucket) { @@ -39,9 +39,9 @@ public class bucket_sort { } public static void main(String[] args) { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) float[] nums = { 0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f }; bucketSort(nums); - System.out.println("After bucket sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After bucket sort, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/counting_sort.java b/en/codes/java/chapter_sorting/counting_sort.java index b9555e4e3..277dfb23f 100644 --- a/en/codes/java/chapter_sorting/counting_sort.java +++ b/en/codes/java/chapter_sorting/counting_sort.java @@ -10,20 +10,20 @@ import java.util.*; public class counting_sort { /* Counting sort */ - // Simple implementation, cannot be used for sorting objects + // Simple implementation; cannot be used to sort objects static void countingSortNaive(int[] nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int num : nums) { m = Math.max(m, num); } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num int[] counter = new int[m + 1]; for (int num : nums) { counter[num]++; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements int i = 0; for (int num = 0; num < m + 1; num++) { for (int j = 0; j < counter[num]; j++, i++) { @@ -33,34 +33,34 @@ public class counting_sort { } /* Counting sort */ - // Complete implementation, can sort objects and is a stable sort + // Full implementation; stable and adaptable to sorting objects static void countingSort(int[] nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m int m = 0; for (int num : nums) { m = Math.max(m, num); } - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num int[] counter = new int[m + 1]; for (int num : nums) { counter[num]++; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for (int i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result int n = nums.length; int[] res = new int[n]; for (int i = n - 1; i >= 0; i--) { int num = nums[i]; res[counter[num] - 1] = num; // Place num at the corresponding index - counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + counter[num]--; // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res for (int i = 0; i < n; i++) { nums[i] = res[i]; } @@ -69,10 +69,10 @@ public class counting_sort { public static void main(String[] args) { int[] nums = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }; countingSortNaive(nums); - System.out.println("After counting sort (cannot sort objects) completes, nums = " + Arrays.toString(nums)); + System.out.println("After counting sort (does not support objects), nums = " + Arrays.toString(nums)); int[] nums1 = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }; countingSort(nums1); - System.out.println("After counting sort completes, nums1 = " + Arrays.toString(nums1)); + System.out.println("After counting sort, nums1 = " + Arrays.toString(nums1)); } } diff --git a/en/codes/java/chapter_sorting/heap_sort.java b/en/codes/java/chapter_sorting/heap_sort.java index 30d0569ce..43a18ea47 100644 --- a/en/codes/java/chapter_sorting/heap_sort.java +++ b/en/codes/java/chapter_sorting/heap_sort.java @@ -9,10 +9,10 @@ package chapter_sorting; import java.util.Arrays; public class heap_sort { - /* Heap length is n, start heapifying node i, from top to bottom */ + /* Sift down from node i in a heap of size n */ public static void siftDown(int[] nums, int n, int i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; @@ -20,31 +20,31 @@ public class heap_sort { ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break; - // Swap two nodes + // Swap the two nodes int temp = nums[i]; nums[i] = nums[ma]; nums[ma] = temp; - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ public static void heapSort(int[] nums) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (int i = nums.length / 2 - 1; i >= 0; i--) { siftDown(nums, nums.length, i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (int i = nums.length - 1; i > 0; i--) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) int tmp = nums[0]; nums[0] = nums[i]; nums[i] = tmp; - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0); } } @@ -52,6 +52,6 @@ public class heap_sort { public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; heapSort(nums); - System.out.println("After heap sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After heap sort, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/insertion_sort.java b/en/codes/java/chapter_sorting/insertion_sort.java index f66233bfc..24078e6e2 100644 --- a/en/codes/java/chapter_sorting/insertion_sort.java +++ b/en/codes/java/chapter_sorting/insertion_sort.java @@ -11,21 +11,21 @@ import java.util.*; public class insertion_sort { /* Insertion sort */ static void insertionSort(int[] nums) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for (int i = 1; i < nums.length; i++) { int base = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + nums[j + 1] = nums[j]; // Shift nums[j] one position to the right j--; } - nums[j + 1] = base; // Assign base to the correct position + nums[j + 1] = base; // Place base at the correct position } } public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; insertionSort(nums); - System.out.println("After insertion sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After insertion sort, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/merge_sort.java b/en/codes/java/chapter_sorting/merge_sort.java index f96f54a89..c055c3351 100644 --- a/en/codes/java/chapter_sorting/merge_sort.java +++ b/en/codes/java/chapter_sorting/merge_sort.java @@ -9,14 +9,14 @@ package chapter_sorting; import java.util.*; public class merge_sort { - /* Merge left subarray and right subarray */ + /* Merge the left and right subarrays */ static void merge(int[] nums, int left, int mid, int right) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result int[] tmp = new int[right - left + 1]; - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays int i = left, j = mid + 1, k = 0; - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; @@ -30,7 +30,7 @@ public class merge_sort { while (j <= right) { tmp[k++] = nums[j++]; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (k = 0; k < tmp.length; k++) { nums[left + k] = tmp[k]; } @@ -38,14 +38,14 @@ public class merge_sort { /* Merge sort */ static void mergeSort(int[] nums, int left, int right) { - // Termination condition + // Base case if (left >= right) - return; // Terminate recursion when subarray length is 1 - // Divide and conquer stage - int mid = left + (right - left) / 2; // Calculate midpoint - mergeSort(nums, left, mid); // Recursively process the left subarray - mergeSort(nums, mid + 1, right); // Recursively process the right subarray - // Merge stage + return; // Stop recursing when the subarray has at most one element + // Divide phase + int mid = left + (right - left) / 2; // Calculate the midpoint + mergeSort(nums, left, mid); // Recurse on the left subarray + mergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase merge(nums, left, mid, right); } @@ -53,6 +53,6 @@ public class merge_sort { /* Merge sort */ int[] nums = { 7, 3, 2, 6, 0, 1, 5, 4 }; mergeSort(nums, 0, nums.length - 1); - System.out.println("After merge sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After merge sort, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/quick_sort.java b/en/codes/java/chapter_sorting/quick_sort.java index d23c06f1d..79b3c7286 100644 --- a/en/codes/java/chapter_sorting/quick_sort.java +++ b/en/codes/java/chapter_sorting/quick_sort.java @@ -10,36 +10,36 @@ import java.util.*; /* Quick sort class */ class QuickSort { - /* Swap elements */ + /* Swap the elements */ static void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ static int partition(int[] nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot swap(nums, i, j); // Swap these two elements } - swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ public static void quickSort(int[] nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } @@ -47,7 +47,7 @@ class QuickSort { /* Quick sort class (median pivot optimization) */ class QuickSortMedian { - /* Swap elements */ + /* Swap the elements */ static void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; @@ -58,39 +58,39 @@ class QuickSortMedian { static int medianThree(int[] nums, int left, int mid, int right) { int l = nums[left], m = nums[mid], r = nums[right]; if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m is between l and r + return mid; // m lies between l and r if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l is between m and r + return left; // l lies between m and r return right; } - /* Sentinel partition (median of three) */ + /* Two-pointer partitioning (median of three) */ static int partition(int[] nums, int left, int right) { // Select the median of three candidate elements int med = medianThree(nums, left, (left + right) / 2, right); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray swap(nums, left, med); // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot swap(nums, i, j); // Swap these two elements } - swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ public static void quickSort(int[] nums, int left, int right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning int pivot = partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } @@ -98,41 +98,41 @@ class QuickSortMedian { /* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { - /* Swap elements */ + /* Swap the elements */ static void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ static int partition(int[] nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot swap(nums, i, j); // Swap these two elements } - swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort (recursion depth optimization) */ public static void quickSort(int[] nums, int left, int right) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while (left < right) { - // Sentinel partition operation + // Perform two-pointer partitioning int pivot = partition(nums, left, right); - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left < right - pivot) { quickSort(nums, left, pivot - 1); // Recursively sort the left subarray - left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] } else { quickSort(nums, pivot + 1, right); // Recursively sort the right subarray - right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] } } } @@ -143,16 +143,16 @@ public class quick_sort { /* Quick sort */ int[] nums = { 2, 4, 1, 0, 3, 5 }; QuickSort.quickSort(nums, 0, nums.length - 1); - System.out.println("After quick sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After quick sort, nums = " + Arrays.toString(nums)); - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ int[] nums1 = { 2, 4, 1, 0, 3, 5 }; QuickSortMedian.quickSort(nums1, 0, nums1.length - 1); - System.out.println("After quick sort (median pivot optimization) completes, nums1 = " + Arrays.toString(nums1)); + System.out.println("After quick sort (median pivot optimization), nums1 = " + Arrays.toString(nums1)); /* Quick sort (recursion depth optimization) */ int[] nums2 = { 2, 4, 1, 0, 3, 5 }; QuickSortTailCall.quickSort(nums2, 0, nums2.length - 1); - System.out.println("After quick sort (recursion depth optimization) completes, nums2 = " + Arrays.toString(nums2)); + System.out.println("After quick sort (recursion depth optimization), nums2 = " + Arrays.toString(nums2)); } } diff --git a/en/codes/java/chapter_sorting/radix_sort.java b/en/codes/java/chapter_sorting/radix_sort.java index f146b54cd..8658dd6b4 100644 --- a/en/codes/java/chapter_sorting/radix_sort.java +++ b/en/codes/java/chapter_sorting/radix_sort.java @@ -9,52 +9,52 @@ package chapter_sorting; import java.util.*; public class radix_sort { - /* Get the k-th digit of element num, where exp = 10^(k-1) */ + /* Get the k-th digit of num, where exp = 10^(k-1) */ static int digit(int num, int exp) { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (num / exp) % 10; } - /* Counting sort (based on nums k-th digit) */ + /* Counting sort (sort nums by the k-th digit) */ static void countingSortDigit(int[] nums, int exp) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 int[] counter = new int[10]; int n = nums.length; - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for (int i = 0; i < n; i++) { - int d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d - counter[d]++; // Count the occurrence of digit d + int d = digit(nums[i], exp); // Get the k-th digit of nums[i] and store it in d + counter[d]++; // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for (int i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts int[] res = new int[n]; for (int i = n - 1; i >= 0; i--) { int d = digit(nums[i], exp); - int j = counter[d] - 1; // Get the index j for d in the array + int j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d]--; // Decrease the count of d by 1 + counter[d]--; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for (int i = 0; i < n; i++) nums[i] = res[i]; } /* Radix sort */ static void radixSort(int[] nums) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits int m = Integer.MIN_VALUE; for (int num : nums) if (num > m) m = num; - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for (int exp = 1; exp <= m; exp *= 10) { - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, exp); } } @@ -64,6 +64,6 @@ public class radix_sort { int[] nums = { 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996 }; radixSort(nums); - System.out.println("After radix sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After radix sort, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/selection_sort.java b/en/codes/java/chapter_sorting/selection_sort.java index d126463e7..a382ce83f 100644 --- a/en/codes/java/chapter_sorting/selection_sort.java +++ b/en/codes/java/chapter_sorting/selection_sort.java @@ -12,15 +12,15 @@ public class selection_sort { /* Selection sort */ public static void selectionSort(int[] nums) { int n = nums.length; - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (int i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) k = j; // Record the index of the smallest element } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range int temp = nums[i]; nums[i] = nums[k]; nums[k] = temp; @@ -30,6 +30,6 @@ public class selection_sort { public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; selectionSort(nums); - System.out.println("After selection sort completes, nums = " + Arrays.toString(nums)); + System.out.println("After selection sort, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_stack_and_queue/array_deque.java b/en/codes/java/chapter_stack_and_queue/array_deque.java index 02ae0cd29..418b01b8b 100644 --- a/en/codes/java/chapter_stack_and_queue/array_deque.java +++ b/en/codes/java/chapter_stack_and_queue/array_deque.java @@ -8,11 +8,11 @@ package chapter_stack_and_queue; import java.util.*; -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ class ArrayDeque { - private int[] nums; // Array for storing double-ended queue elements - private int front; // Front pointer, points to the front of the queue element - private int queSize; // Double-ended queue length + private int[] nums; // Array that stores the deque elements + private int front; // Front pointer, pointing to the first element + private int queSize; // Deque size /* Constructor */ public ArrayDeque(int capacity) { @@ -20,91 +20,91 @@ class ArrayDeque { front = queSize = 0; } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ public int capacity() { return nums.length; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ public int size() { return queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ public boolean isEmpty() { return queSize == 0; } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ private int index(int i) { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end return (i + capacity()) % capacity(); } - /* Front of the queue enqueue */ + /* Enqueue at the front */ public void pushFirst(int num) { if (queSize == capacity()) { - System.out.println("Double-ended queue is full"); + System.out.println("Deque is full"); return; } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front pointer one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array front = index(front - 1); - // Add num to front of queue + // Add num to the front nums[front] = num; queSize++; } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ public void pushLast(int num) { if (queSize == capacity()) { - System.out.println("Double-ended queue is full"); + System.out.println("Deque is full"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear pointer, which points one position past the last element int rear = index(front + queSize); - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num; queSize++; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ public int popFirst() { int num = peekFirst(); - // Move front pointer backward by one position + // Advance the front pointer by one position front = index(front + 1); queSize--; return num; } - /* Access rear of the queue element */ + /* Dequeue from the rear */ public int popLast() { int num = peekLast(); queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ public int peekFirst() { if (isEmpty()) throw new IndexOutOfBoundsException(); return nums[front]; } - /* Driver Code */ + /* Access the rear element */ public int peekLast() { if (isEmpty()) throw new IndexOutOfBoundsException(); - // Initialize double-ended queue + // Calculate the index of the last element int last = index(front + queSize - 1); return nums[last]; } - /* Return array for printing */ + /* Return an array for printing */ public int[] toArray() { - // Elements enqueue + // Convert only the list elements within the occupied range int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[index(j)]; @@ -115,37 +115,37 @@ class ArrayDeque { public class array_deque { public static void main(String[] args) { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ ArrayDeque deque = new ArrayDeque(10); deque.pushLast(3); deque.pushLast(2); deque.pushLast(5); - System.out.println("Double-ended queue deque = " + Arrays.toString(deque.toArray())); + System.out.println("Deque deque = " + Arrays.toString(deque.toArray())); - /* Update element */ + /* Access an element */ int peekFirst = deque.peekFirst(); System.out.println("Front element peekFirst = " + peekFirst); int peekLast = deque.peekLast(); System.out.println("Rear element peekLast = " + peekLast); - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(4); - System.out.println("After element 4 enqueues at rear, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After enqueuing element 4 at the rear, deque = " + Arrays.toString(deque.toArray())); deque.pushFirst(1); - System.out.println("After element 1 enqueues at front, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After enqueuing element 1 at the front, deque = " + Arrays.toString(deque.toArray())); - /* Element dequeue */ + /* Dequeue an element */ int popLast = deque.popLast(); - System.out.println("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + Arrays.toString(deque.toArray())); + System.out.println("Element dequeued from the rear = " + popLast + ", after dequeuing from the rear, deque = " + Arrays.toString(deque.toArray())); int popFirst = deque.popFirst(); - System.out.println("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + Arrays.toString(deque.toArray())); + System.out.println("Element dequeued from the front = " + popFirst + ", after dequeuing from the front, deque = " + Arrays.toString(deque.toArray())); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.size(); - System.out.println("Double-ended queue length size = " + size); + System.out.println("Deque size = " + size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ boolean isEmpty = deque.isEmpty(); - System.out.println("Double-ended queue is empty = " + isEmpty); + System.out.println("Is the deque empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/array_queue.java b/en/codes/java/chapter_stack_and_queue/array_queue.java index 71f9c3072..662e957b7 100644 --- a/en/codes/java/chapter_stack_and_queue/array_queue.java +++ b/en/codes/java/chapter_stack_and_queue/array_queue.java @@ -8,28 +8,28 @@ package chapter_stack_and_queue; import java.util.*; -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ class ArrayQueue { - private int[] nums; // Array for storing queue elements - private int front; // Front pointer, points to the front of the queue element - private int queSize; // Queue length + private int[] nums; // Array that stores the queue elements + private int front; // Front pointer, pointing to the first element + private int queSize; // Queue size public ArrayQueue(int capacity) { nums = new int[capacity]; front = queSize = 0; } - /* Get the capacity of the queue */ + /* Get the queue capacity */ public int capacity() { return nums.length; } - /* Get the length of the queue */ + /* Get the queue size */ public int size() { return queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ public boolean isEmpty() { return queSize == 0; } @@ -40,10 +40,10 @@ class ArrayQueue { System.out.println("Queue is full"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear pointer, which points one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array int rear = (front + queSize) % capacity(); - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num; queSize++; } @@ -51,22 +51,22 @@ class ArrayQueue { /* Dequeue */ public int pop() { int num = peek(); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front pointer by one position, wrapping to the beginning if it passes the end front = (front + 1) % capacity(); queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return nums[front]; } - /* Return array */ + /* Return an array */ public int[] toArray() { - // Elements enqueue + // Convert only the list elements within the occupied range int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[j % capacity()]; @@ -77,11 +77,11 @@ class ArrayQueue { public class array_queue { public static void main(String[] args) { - /* Access front of the queue element */ + /* Initialize the queue */ int capacity = 10; ArrayQueue queue = new ArrayQueue(capacity); - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -89,27 +89,27 @@ public class array_queue { queue.push(4); System.out.println("Queue queue = " + Arrays.toString(queue.toArray())); - /* Return list for printing */ + /* Access the front element */ int peek = queue.peek(); System.out.println("Front element peek = " + peek); - /* Element dequeue */ + /* Dequeue an element */ int pop = queue.pop(); - System.out.println("Dequeue element pop = " + pop + ", after dequeue, queue = " + Arrays.toString(queue.toArray())); + System.out.println("Dequeued element pop = " + pop + ", after dequeuing, queue = " + Arrays.toString(queue.toArray())); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue.size(); - System.out.println("Queue length size = " + size); + System.out.println("Queue size = " + size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ boolean isEmpty = queue.isEmpty(); - System.out.println("Queue is empty = " + isEmpty); + System.out.println("Is the queue empty = " + isEmpty); - /* Test circular array */ + /* Test the circular array */ for (int i = 0; i < 10; i++) { queue.push(i); queue.pop(); - System.out.println("After round " + i + " enqueue + dequeue, queue = " + Arrays.toString(queue.toArray())); + System.out.println("Round " + i + " : after enqueue + dequeue, queue = " + Arrays.toString(queue.toArray())); } } } diff --git a/en/codes/java/chapter_stack_and_queue/array_stack.java b/en/codes/java/chapter_stack_and_queue/array_stack.java index 895029525..a258aeb32 100644 --- a/en/codes/java/chapter_stack_and_queue/array_stack.java +++ b/en/codes/java/chapter_stack_and_queue/array_stack.java @@ -8,21 +8,21 @@ package chapter_stack_and_queue; import java.util.*; -/* Stack based on array implementation */ +/* Stack implemented with an array */ class ArrayStack { private ArrayList stack; public ArrayStack() { - // Initialize list (dynamic array) + // Initialize the list (dynamic array) stack = new ArrayList<>(); } - /* Get the length of the stack */ + /* Get the stack size */ public int size() { return stack.size(); } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ public boolean isEmpty() { return size() == 0; } @@ -39,14 +39,14 @@ class ArrayStack { return stack.remove(size() - 1); } - /* Return list for printing */ + /* Access the top element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return stack.get(size() - 1); } - /* Convert List to Array and return */ + /* Convert the list to an array and return it */ public Object[] toArray() { return stack.toArray(); } @@ -54,10 +54,10 @@ class ArrayStack { public class array_stack { public static void main(String[] args) { - /* Access top of the stack element */ + /* Initialize the stack */ ArrayStack stack = new ArrayStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -65,20 +65,20 @@ public class array_stack { stack.push(4); System.out.println("Stack stack = " + Arrays.toString(stack.toArray())); - /* Return list for printing */ + /* Access the top element */ int peek = stack.peek(); - System.out.println("Stack top element peek = " + peek); + System.out.println("Top element peek = " + peek); - /* Element pop from stack */ + /* Pop an element from the stack */ int pop = stack.pop(); - System.out.println("Pop element pop = " + pop + ", after pop, stack = " + Arrays.toString(stack.toArray())); + System.out.println("Popped element pop = " + pop + ", after popping, stack = " + Arrays.toString(stack.toArray())); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack.size(); - System.out.println("Stack length size = " + size); + System.out.println("Stack size = " + size); - /* Check if empty */ + /* Check whether it is empty */ boolean isEmpty = stack.isEmpty(); - System.out.println("Stack is empty = " + isEmpty); + System.out.println("Is the stack empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/deque.java b/en/codes/java/chapter_stack_and_queue/deque.java index bda81fa3e..74a5924fc 100644 --- a/en/codes/java/chapter_stack_and_queue/deque.java +++ b/en/codes/java/chapter_stack_and_queue/deque.java @@ -10,37 +10,37 @@ import java.util.*; public class deque { public static void main(String[] args) { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ Deque deque = new LinkedList<>(); deque.offerLast(3); deque.offerLast(2); deque.offerLast(5); - System.out.println("Double-ended queue deque = " + deque); + System.out.println("Deque deque = " + deque); - /* Update element */ + /* Access an element */ int peekFirst = deque.peekFirst(); System.out.println("Front element peekFirst = " + peekFirst); int peekLast = deque.peekLast(); System.out.println("Rear element peekLast = " + peekLast); - /* Elements enqueue */ + /* Enqueue elements */ deque.offerLast(4); - System.out.println("After element 4 enqueues at rear, deque = " + deque); + System.out.println("After enqueuing element 4 at the rear, deque = " + deque); deque.offerFirst(1); - System.out.println("After element 1 enqueues at front, deque = " + deque); + System.out.println("After enqueuing element 1 at the front, deque = " + deque); - /* Element dequeue */ + /* Dequeue an element */ int popLast = deque.pollLast(); - System.out.println("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + deque); + System.out.println("Element dequeued from the rear = " + popLast + ", after dequeuing from the rear, deque = " + deque); int popFirst = deque.pollFirst(); - System.out.println("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + deque); + System.out.println("Element dequeued from the front = " + popFirst + ", after dequeuing from the front, deque = " + deque); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.size(); - System.out.println("Double-ended queue length size = " + size); + System.out.println("Deque size = " + size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ boolean isEmpty = deque.isEmpty(); - System.out.println("Double-ended queue is empty = " + isEmpty); + System.out.println("Is the deque empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java b/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java index a2eed7b25..81aee5115 100644 --- a/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java @@ -11,8 +11,8 @@ import java.util.*; /* Doubly linked list node */ class ListNode { int val; // Node value - ListNode next; // Successor node reference - ListNode prev; // Predecessor node reference + ListNode next; // Reference to the next node + ListNode prev; // Reference to the previous node ListNode(int val) { this.val = val; @@ -20,21 +20,21 @@ class ListNode { } } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ class LinkedListDeque { - private ListNode front, rear; // Head node front, tail node rear - private int queSize = 0; // Length of the double-ended queue + private ListNode front, rear; // Head node front and tail node rear + private int queSize = 0; // Deque size public LinkedListDeque() { front = rear = null; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ public int size() { return queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ public boolean isEmpty() { return size() == 0; } @@ -45,28 +45,28 @@ class LinkedListDeque { // If the linked list is empty, make both front and rear point to node if (isEmpty()) front = rear = node; - // Front of the queue enqueue operation + // Enqueue at the front else if (isFront) { // Add node to the head of the linked list front.prev = node; node.next = front; - front = node; // Update head node - // Rear of the queue enqueue operation + front = node; // Update the head node + // Enqueue at the rear } else { // Add node to the tail of the linked list rear.next = node; node.prev = rear; - rear = node; // Update tail node + rear = node; // Update the tail node } - queSize++; // Update queue length + queSize++; // Update the queue size } - /* Front of the queue enqueue */ + /* Enqueue at the front */ public void pushFirst(int num) { push(num, true); } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ public void pushLast(int num) { push(num, false); } @@ -76,58 +76,58 @@ class LinkedListDeque { if (isEmpty()) throw new IndexOutOfBoundsException(); int val; - // When the deque has one element, front and rear point to the same node + // When the queue size is 1, the head and tail point to the same node if (queSize == 1) { val = front.val; front = null; rear = null; } - // Temporarily store head node value + // Dequeue from the front else if (isFront) { - val = front.val; // Delete head node - // Delete head node + val = front.val; // Save the head node value + // Remove the head node ListNode fNext = front.next; fNext.prev = null; front.next = null; - front = fNext; // Update head node - // Temporarily store tail node value + front = fNext; // Update the head node + // Dequeue from the rear } else { - val = rear.val; // Delete tail node - // Update tail node + val = rear.val; // Save the tail node value + // Remove the tail node ListNode rPrev = rear.prev; rPrev.next = null; rear.prev = null; - rear = rPrev; // Update tail node + rear = rPrev; // Update the tail node } - queSize--; // Update queue length + queSize--; // Update the queue size return val; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ public int popFirst() { return pop(true); } - /* Access rear of the queue element */ + /* Dequeue from the rear */ public int popLast() { return pop(false); } - /* Return list for printing */ + /* Access the front element */ public int peekFirst() { if (isEmpty()) throw new IndexOutOfBoundsException(); return front.val; } - /* Driver Code */ + /* Access the rear element */ public int peekLast() { if (isEmpty()) throw new IndexOutOfBoundsException(); return rear.val; } - /* Return array for printing */ + /* Return an array for printing */ public int[] toArray() { ListNode node = front; int[] res = new int[size()]; @@ -141,37 +141,37 @@ class LinkedListDeque { public class linkedlist_deque { public static void main(String[] args) { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ LinkedListDeque deque = new LinkedListDeque(); deque.pushLast(3); deque.pushLast(2); deque.pushLast(5); - System.out.println("Double-ended queue deque = " + Arrays.toString(deque.toArray())); + System.out.println("Deque deque = " + Arrays.toString(deque.toArray())); - /* Update element */ + /* Access an element */ int peekFirst = deque.peekFirst(); System.out.println("Front element peekFirst = " + peekFirst); int peekLast = deque.peekLast(); System.out.println("Rear element peekLast = " + peekLast); - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(4); - System.out.println("After element 4 enqueues at rear, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After enqueuing element 4 at the rear, deque = " + Arrays.toString(deque.toArray())); deque.pushFirst(1); - System.out.println("After element 1 enqueues at front, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After enqueuing element 1 at the front, deque = " + Arrays.toString(deque.toArray())); - /* Element dequeue */ + /* Dequeue an element */ int popLast = deque.popLast(); - System.out.println("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + Arrays.toString(deque.toArray())); + System.out.println("Element dequeued from the rear = " + popLast + ", after dequeuing from the rear, deque = " + Arrays.toString(deque.toArray())); int popFirst = deque.popFirst(); - System.out.println("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + Arrays.toString(deque.toArray())); + System.out.println("Element dequeued from the front = " + popFirst + ", after dequeuing from the front, deque = " + Arrays.toString(deque.toArray())); - /* Get the length of the double-ended queue */ + /* Get the deque size */ int size = deque.size(); - System.out.println("Double-ended queue length size = " + size); + System.out.println("Deque size = " + size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ boolean isEmpty = deque.isEmpty(); - System.out.println("Double-ended queue is empty = " + isEmpty); + System.out.println("Is the deque empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java b/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java index e19c1ffc9..7999ea16d 100644 --- a/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java @@ -8,9 +8,9 @@ package chapter_stack_and_queue; import java.util.*; -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue { - private ListNode front, rear; // Head node front, tail node rear + private ListNode front, rear; // Head node front and tail node rear private int queSize = 0; public LinkedListQueue() { @@ -18,12 +18,12 @@ class LinkedListQueue { rear = null; } - /* Get the length of the queue */ + /* Get the queue size */ public int size() { return queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ public boolean isEmpty() { return size() == 0; } @@ -32,11 +32,11 @@ class LinkedListQueue { public void push(int num) { // Add num after the tail node ListNode node = new ListNode(num); - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (front == null) { front = node; rear = node; - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node } else { rear.next = node; rear = node; @@ -47,20 +47,20 @@ class LinkedListQueue { /* Dequeue */ public int pop() { int num = peek(); - // Delete head node + // Remove the head node front = front.next; queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return front.val; } - /* Convert linked list to Array and return */ + /* Convert the linked list to an array and return it */ public int[] toArray() { ListNode node = front; int[] res = new int[size()]; @@ -74,10 +74,10 @@ class LinkedListQueue { public class linkedlist_queue { public static void main(String[] args) { - /* Access front of the queue element */ + /* Initialize the queue */ LinkedListQueue queue = new LinkedListQueue(); - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -85,20 +85,20 @@ public class linkedlist_queue { queue.push(4); System.out.println("Queue queue = " + Arrays.toString(queue.toArray())); - /* Return list for printing */ + /* Access the front element */ int peek = queue.peek(); System.out.println("Front element peek = " + peek); - /* Element dequeue */ + /* Dequeue an element */ int pop = queue.pop(); - System.out.println("Dequeue element pop = " + pop + ", after dequeue, queue = " + Arrays.toString(queue.toArray())); + System.out.println("Dequeued element pop = " + pop + ", after dequeuing, queue = " + Arrays.toString(queue.toArray())); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue.size(); - System.out.println("Queue length size = " + size); + System.out.println("Queue size = " + size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ boolean isEmpty = queue.isEmpty(); - System.out.println("Queue is empty = " + isEmpty); + System.out.println("Is the queue empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java b/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java index 873a52692..e12a8fa74 100644 --- a/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java @@ -9,21 +9,21 @@ package chapter_stack_and_queue; import java.util.*; import utils.*; -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ class LinkedListStack { - private ListNode stackPeek; // Use head node as stack top - private int stkSize = 0; // Stack length + private ListNode stackPeek; // Use the head node as the stack top + private int stkSize = 0; // Stack size public LinkedListStack() { stackPeek = null; } - /* Get the length of the stack */ + /* Get the stack size */ public int size() { return stkSize; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ public boolean isEmpty() { return size() == 0; } @@ -44,14 +44,14 @@ class LinkedListStack { return num; } - /* Return list for printing */ + /* Access the top element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return stackPeek.val; } - /* Convert List to Array and return */ + /* Convert the list to an array and return it */ public int[] toArray() { ListNode node = stackPeek; int[] res = new int[size()]; @@ -65,10 +65,10 @@ class LinkedListStack { public class linkedlist_stack { public static void main(String[] args) { - /* Access top of the stack element */ + /* Initialize the stack */ LinkedListStack stack = new LinkedListStack(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -76,20 +76,20 @@ public class linkedlist_stack { stack.push(4); System.out.println("Stack stack = " + Arrays.toString(stack.toArray())); - /* Return list for printing */ + /* Access the top element */ int peek = stack.peek(); - System.out.println("Stack top element peek = " + peek); + System.out.println("Top element peek = " + peek); - /* Element pop from stack */ + /* Pop an element from the stack */ int pop = stack.pop(); - System.out.println("Pop element pop = " + pop + ", after pop, stack = " + Arrays.toString(stack.toArray())); + System.out.println("Popped element pop = " + pop + ", after popping, stack = " + Arrays.toString(stack.toArray())); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack.size(); - System.out.println("Stack length size = " + size); + System.out.println("Stack size = " + size); - /* Check if empty */ + /* Check whether it is empty */ boolean isEmpty = stack.isEmpty(); - System.out.println("Stack is empty = " + isEmpty); + System.out.println("Is the stack empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/queue.java b/en/codes/java/chapter_stack_and_queue/queue.java index 0db5d68db..c2b721512 100644 --- a/en/codes/java/chapter_stack_and_queue/queue.java +++ b/en/codes/java/chapter_stack_and_queue/queue.java @@ -10,10 +10,10 @@ import java.util.*; public class queue { public static void main(String[] args) { - /* Access front of the queue element */ + /* Initialize the queue */ Queue queue = new LinkedList<>(); - /* Elements enqueue */ + /* Enqueue elements */ queue.offer(1); queue.offer(3); queue.offer(2); @@ -21,20 +21,20 @@ public class queue { queue.offer(4); System.out.println("Queue queue = " + queue); - /* Return list for printing */ + /* Access the front element */ int peek = queue.peek(); System.out.println("Front element peek = " + peek); - /* Element dequeue */ + /* Dequeue an element */ int pop = queue.poll(); - System.out.println("Dequeue element pop = " + pop + ", after dequeue, queue = " + queue); + System.out.println("Dequeued element pop = " + pop + ", after dequeuing, queue = " + queue); - /* Get the length of the queue */ + /* Get the queue size */ int size = queue.size(); - System.out.println("Queue length size = " + size); + System.out.println("Queue size = " + size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ boolean isEmpty = queue.isEmpty(); - System.out.println("Queue is empty = " + isEmpty); + System.out.println("Is the queue empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/stack.java b/en/codes/java/chapter_stack_and_queue/stack.java index 4d7e952bb..b7b647e00 100644 --- a/en/codes/java/chapter_stack_and_queue/stack.java +++ b/en/codes/java/chapter_stack_and_queue/stack.java @@ -10,10 +10,10 @@ import java.util.*; public class stack { public static void main(String[] args) { - /* Access top of the stack element */ + /* Initialize the stack */ Stack stack = new Stack<>(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -21,20 +21,20 @@ public class stack { stack.push(4); System.out.println("Stack stack = " + stack); - /* Return list for printing */ + /* Access the top element */ int peek = stack.peek(); - System.out.println("Stack top element peek = " + peek); + System.out.println("Top element peek = " + peek); - /* Element pop from stack */ + /* Pop an element from the stack */ int pop = stack.pop(); - System.out.println("Pop element pop = " + pop + ", after pop, stack = " + stack); + System.out.println("Popped element pop = " + pop + ", after popping, stack = " + stack); - /* Get the length of the stack */ + /* Get the stack size */ int size = stack.size(); - System.out.println("Stack length size = " + size); + System.out.println("Stack size = " + size); - /* Check if empty */ + /* Check whether it is empty */ boolean isEmpty = stack.isEmpty(); - System.out.println("Stack is empty = " + isEmpty); + System.out.println("Is the stack empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_tree/array_binary_tree.java b/en/codes/java/chapter_tree/array_binary_tree.java index 4d3dc773c..b4e9b92a1 100644 --- a/en/codes/java/chapter_tree/array_binary_tree.java +++ b/en/codes/java/chapter_tree/array_binary_tree.java @@ -9,7 +9,7 @@ package chapter_tree; import utils.*; import java.util.*; -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ class ArrayBinaryTree { private List tree; @@ -18,30 +18,30 @@ class ArrayBinaryTree { tree = new ArrayList<>(arr); } - /* List capacity */ + /* List size */ public int size() { return tree.size(); } - /* Get value of node at index i */ + /* Get the value of the node at index i */ public Integer val(int i) { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return null to represent an empty position if (i < 0 || i >= size()) return null; return tree.get(i); } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ public Integer left(int i) { return 2 * i + 1; } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ public Integer right(int i) { return 2 * i + 2; } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ public Integer parent(int i) { return (i - 1) / 2; } @@ -49,7 +49,7 @@ class ArrayBinaryTree { /* Level-order traversal */ public List levelOrder() { List res = new ArrayList<>(); - // Traverse array directly + // Traverse the array directly for (int i = 0; i < size(); i++) { if (val(i) != null) res.add(val(i)); @@ -59,7 +59,7 @@ class ArrayBinaryTree { /* Depth-first traversal */ private void dfs(Integer i, String order, List res) { - // If empty position, return + // Return if this position is empty if (val(i) == null) return; // Preorder traversal @@ -99,38 +99,38 @@ class ArrayBinaryTree { public class array_binary_tree { public static void main(String[] args) { - // Initialize binary tree - // Here we use a function to generate a binary tree directly from an array + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array List arr = Arrays.asList(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15); TreeNode root = TreeNode.listToTree(arr); - System.out.println("\nInitialize binary tree\n"); - System.out.println("Array representation of binary tree:"); + System.out.println("\nInitialize the binary tree\n"); + System.out.println("Array representation of the binary tree:"); System.out.println(arr); - System.out.println("Linked list representation of binary tree:"); + System.out.println("Linked representation of the binary tree:"); PrintUtil.printTree(root); - // Binary tree class represented by array + // Binary tree class using an array representation ArrayBinaryTree abt = new ArrayBinaryTree(arr); - // Access node + // Access a node int i = 1; Integer l = abt.left(i); Integer r = abt.right(i); Integer p = abt.parent(i); - System.out.println("\nCurrent node index is " + i + ", value is " + abt.val(i)); - System.out.println("Its left child node index is " + l + ", value is " + (l == null ? "null" : abt.val(l))); - System.out.println("Its right child node index is " + r + ", value is " + (r == null ? "null" : abt.val(r))); - System.out.println("Its parent node index is " + p + ", value is " + (p == null ? "null" : abt.val(p))); + System.out.println("\nCurrent node index: " + i + " with value " + abt.val(i)); + System.out.println("Left child index: " + l + " with value " + (l == null ? "null" : abt.val(l))); + System.out.println("Right child index: " + r + " with value " + (r == null ? "null" : abt.val(r))); + System.out.println("Parent index: " + p + " with value " + (p == null ? "null" : abt.val(p))); - // Traverse tree + // Traverse the tree List res = abt.levelOrder(); - System.out.println("\nLevel-order traversal is:" + res); + System.out.println("\nLevel-order traversal:" + res); res = abt.preOrder(); - System.out.println("Preorder traversal is:" + res); + System.out.println("Preorder traversal:" + res); res = abt.inOrder(); - System.out.println("Inorder traversal is:" + res); + System.out.println("Inorder traversal:" + res); res = abt.postOrder(); - System.out.println("Postorder traversal is:" + res); + System.out.println("Postorder traversal:" + res); } } diff --git a/en/codes/java/chapter_tree/avl_tree.java b/en/codes/java/chapter_tree/avl_tree.java index 2740a518b..127716b76 100644 --- a/en/codes/java/chapter_tree/avl_tree.java +++ b/en/codes/java/chapter_tree/avl_tree.java @@ -12,118 +12,118 @@ import utils.*; class AVLTree { TreeNode root; // Root node - /* Get node height */ + /* Get the node height */ public int height(TreeNode node) { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 return node == null ? -1 : node.height; } - /* Update node height */ + /* Update the node height */ private void updateHeight(TreeNode node) { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node.height = Math.max(height(node.left), height(node.right)) + 1; } - /* Get balance factor */ + /* Get the balance factor */ public int balanceFactor(TreeNode node) { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node == null) return 0; // Node balance factor = left subtree height - right subtree height return height(node.left) - height(node.right); } - /* Right rotation operation */ + /* Right rotation */ private TreeNode rightRotate(TreeNode node) { TreeNode child = node.left; TreeNode grandChild = child.right; - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.right = node; node.left = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Left rotation operation */ + /* Left rotation */ private TreeNode leftRotate(TreeNode node) { TreeNode child = node.right; TreeNode grandChild = child.left; - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.left = node; node.right = grandChild; - // Update node height + // Update the node height updateHeight(node); updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ private TreeNode rotate(TreeNode node) { - // Get balance factor of node + // Get the balance factor of node int balanceFactor = balanceFactor(node); - // Left-leaning tree + // Left-heavy tree if (balanceFactor > 1) { if (balanceFactor(node.left) >= 0) { - // Right rotation + // Rotate right return rightRotate(node); } else { - // First left rotation then right rotation + // Rotate left, then right node.left = leftRotate(node.left); return rightRotate(node); } } - // Right-leaning tree + // Right-heavy tree if (balanceFactor < -1) { if (balanceFactor(node.right) <= 0) { - // Left rotation + // Rotate left return leftRotate(node); } else { - // First right rotation then left rotation + // Rotate right, then left node.right = rightRotate(node.right); return leftRotate(node); } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node; } - /* Insert node */ + /* Insert a node */ public void insert(int val) { root = insertHelper(root, val); } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ private TreeNode insertHelper(TreeNode node, int val) { if (node == null) return new TreeNode(val); - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (val < node.val) node.left = insertHelper(node.left, val); else if (val > node.val) node.right = insertHelper(node.right, val); else - return node; // Duplicate node not inserted, return directly - updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + return node; // Return without inserting a duplicate node + updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the subtree root return node; } - /* Remove node */ + /* Remove a node */ public void remove(int val) { root = removeHelper(root, val); } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ private TreeNode removeHelper(TreeNode node, int val) { if (node == null) return null; - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (val < node.val) node.left = removeHelper(node.left, val); else if (val > node.val) @@ -131,14 +131,14 @@ class AVLTree { else { if (node.left == null || node.right == null) { TreeNode child = node.left != null ? node.left : node.right; - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child == null) return null; - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly else node = child; } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value TreeNode temp = node.right; while (temp.left != null) { temp = temp.left; @@ -147,29 +147,29 @@ class AVLTree { node.val = temp.val; } } - updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node); - // Return root node of subtree + // Return the subtree root return node; } - /* Search node */ + /* Find a node */ public TreeNode search(int val) { TreeNode cur = root; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < val) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > val) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } } @@ -177,22 +177,22 @@ class AVLTree { public class avl_tree { static void testInsert(AVLTree tree, int val) { tree.insert(val); - System.out.println("\nAfter inserting node " + val + ", AVL tree is"); + System.out.println("\nAfter inserting node " + val + " the AVL tree is:"); PrintUtil.printTree(tree.root); } static void testRemove(AVLTree tree, int val) { tree.remove(val); - System.out.println("\nAfter removing node " + val + ", AVL tree is"); + System.out.println("\nAfter removing node " + val + " the AVL tree is:"); PrintUtil.printTree(tree.root); } public static void main(String[] args) { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ AVLTree avlTree = new AVLTree(); - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted testInsert(avlTree, 1); testInsert(avlTree, 2); testInsert(avlTree, 3); @@ -204,17 +204,17 @@ public class avl_tree { testInsert(avlTree, 10); testInsert(avlTree, 6); - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ testInsert(avlTree, 7); - /* Remove node */ - // Delete node with degree 1 - testRemove(avlTree, 8); // Delete node with degree 2 - testRemove(avlTree, 5); // Remove node with degree 1 - testRemove(avlTree, 4); // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + testRemove(avlTree, 8); // Remove a node with 0 children + testRemove(avlTree, 5); // Remove a node with 1 child + testRemove(avlTree, 4); // Remove a node with 2 children - /* Search node */ + /* Look up a node */ TreeNode node = avlTree.search(7); - System.out.println("\nFound node object is " + node + ", node value = " + node.val); + System.out.println("\nFound node object: " + node + ", node value = " + node.val); } } diff --git a/en/codes/java/chapter_tree/binary_search_tree.java b/en/codes/java/chapter_tree/binary_search_tree.java index 563e3d483..980787242 100644 --- a/en/codes/java/chapter_tree/binary_search_tree.java +++ b/en/codes/java/chapter_tree/binary_search_tree.java @@ -14,56 +14,56 @@ class BinarySearchTree { /* Constructor */ public BinarySearchTree() { - // Initialize empty tree + // Initialize an empty tree root = null; } - /* Get binary tree root node */ + /* Get the root of the binary tree */ public TreeNode getRoot() { return root; } - /* Search node */ + /* Find a node */ public TreeNode search(int num) { TreeNode cur = root; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > num) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } - /* Insert node */ + /* Insert a node */ public void insert(int num) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (root == null) { root = new TreeNode(num); return; } TreeNode cur = root, pre = null; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != null) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur.val == num) return; pre = cur; - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else cur = cur.left; } - // Insert node + // Insert a node TreeNode node = new TreeNode(num); if (pre.val < num) pre.right = node; @@ -71,53 +71,53 @@ class BinarySearchTree { pre.left = node; } - /* Remove node */ + /* Remove a node */ public void remove(int num) { - // If tree is empty, return directly + // Return immediately if the tree is empty if (root == null) return; TreeNode cur = root, pre = null; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur != null) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur.val == num) break; pre = cur; - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur else cur = cur.left; } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if (cur == null) return; - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if (cur.left == null || cur.right == null) { - // When number of child nodes = 0 / 1, child = null / that child node + // With 0 / 1 children, child is null / the child node, respectively TreeNode child = cur.left != null ? cur.left : cur.right; - // Delete node cur + // Remove node cur if (cur != root) { if (pre.left == cur) pre.left = child; else pre.right = child; } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root root = child; } } - // Number of child nodes = 2 + // Number of children = 2 else { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur TreeNode tmp = cur.right; while (tmp.left != null) { tmp = tmp.left; } - // Recursively delete node tmp + // Recursively remove node tmp remove(tmp.val); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur.val = tmp.val; } } @@ -125,34 +125,34 @@ class BinarySearchTree { public class binary_search_tree { public static void main(String[] args) { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ BinarySearchTree bst = new BinarySearchTree(); - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree int[] nums = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; for (int num : nums) { bst.insert(num); } - System.out.println("\nInitialized binary tree is\n"); + System.out.println("\nThe initialized binary tree is:\n"); PrintUtil.printTree(bst.getRoot()); - /* Search node */ + /* Find a node */ TreeNode node = bst.search(7); - System.out.println("\nFound node object is " + node + ", node value = " + node.val); + System.out.println("\nFound node object: " + node + ", node value = " + node.val); - /* Insert node */ + /* Insert a node */ bst.insert(16); - System.out.println("\nAfter inserting node 16, binary tree is\n"); + System.out.println("\nBinary tree after inserting node 16:\n"); PrintUtil.printTree(bst.getRoot()); - /* Remove node */ + /* Remove a node */ bst.remove(1); - System.out.println("\nAfter removing node 1, binary tree is\n"); + System.out.println("\nBinary tree after removing node 1:\n"); PrintUtil.printTree(bst.getRoot()); bst.remove(2); - System.out.println("\nAfter removing node 2, binary tree is\n"); + System.out.println("\nBinary tree after removing node 2:\n"); PrintUtil.printTree(bst.getRoot()); bst.remove(4); - System.out.println("\nAfter removing node 4, binary tree is\n"); + System.out.println("\nBinary tree after removing node 4:\n"); PrintUtil.printTree(bst.getRoot()); } } diff --git a/en/codes/java/chapter_tree/binary_tree.java b/en/codes/java/chapter_tree/binary_tree.java index d2edff09a..764bef187 100644 --- a/en/codes/java/chapter_tree/binary_tree.java +++ b/en/codes/java/chapter_tree/binary_tree.java @@ -10,31 +10,31 @@ import utils.*; public class binary_tree { public static void main(String[] args) { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes TreeNode n1 = new TreeNode(1); TreeNode n2 = new TreeNode(2); TreeNode n3 = new TreeNode(3); TreeNode n4 = new TreeNode(4); TreeNode n5 = new TreeNode(5); - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1.left = n2; n1.right = n3; n2.left = n4; n2.right = n5; - System.out.println("\nInitialize binary tree\n"); + System.out.println("\nInitialize the binary tree\n"); PrintUtil.printTree(n1); - /* Insert node P between n1 -> n2 */ + /* Insert and remove nodes */ TreeNode P = new TreeNode(0); - // Delete node + // Insert node P between n1 and n2 in n1 -> n2 n1.left = P; P.left = n2; - System.out.println("\nAfter inserting node P\n"); + System.out.println("\nAfter inserting node P:\n"); PrintUtil.printTree(n1); // Remove node P n1.left = n2; - System.out.println("\nAfter removing node P\n"); + System.out.println("\nAfter removing node P:\n"); PrintUtil.printTree(n1); } } diff --git a/en/codes/java/chapter_tree/binary_tree_bfs.java b/en/codes/java/chapter_tree/binary_tree_bfs.java index c1a2f575d..4b110db6e 100644 --- a/en/codes/java/chapter_tree/binary_tree_bfs.java +++ b/en/codes/java/chapter_tree/binary_tree_bfs.java @@ -12,31 +12,31 @@ import java.util.*; public class binary_tree_bfs { /* Level-order traversal */ static List levelOrder(TreeNode root) { - // Initialize queue, add root node + // Initialize a queue and enqueue the root Queue queue = new LinkedList<>(); queue.add(root); - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence List list = new ArrayList<>(); while (!queue.isEmpty()) { - TreeNode node = queue.poll(); // Dequeue - list.add(node.val); // Save node value + TreeNode node = queue.poll(); // Dequeue from the queue + list.add(node.val); // Store the node value if (node.left != null) - queue.offer(node.left); // Left child node enqueue + queue.offer(node.left); // Enqueue the left child if (node.right != null) - queue.offer(node.right); // Right child node enqueue + queue.offer(node.right); // Enqueue the right child } return list; } public static void main(String[] args) { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode root = TreeNode.listToTree(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); - System.out.println("\nInitialize binary tree\n"); + System.out.println("\nInitialize the binary tree\n"); PrintUtil.printTree(root); /* Level-order traversal */ List list = levelOrder(root); - System.out.println("\nLevel-order traversal node print sequence = " + list); + System.out.println("\nNode sequence from level-order traversal = " + list); } } diff --git a/en/codes/java/chapter_tree/binary_tree_dfs.java b/en/codes/java/chapter_tree/binary_tree_dfs.java index 485550f2f..1d24d62e5 100644 --- a/en/codes/java/chapter_tree/binary_tree_dfs.java +++ b/en/codes/java/chapter_tree/binary_tree_dfs.java @@ -10,14 +10,14 @@ import utils.*; import java.util.*; public class binary_tree_dfs { - // Initialize list for storing traversal sequence + // Initialize a list to store the traversal sequence static ArrayList list = new ArrayList<>(); /* Preorder traversal */ static void preOrder(TreeNode root) { if (root == null) return; - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree list.add(root.val); preOrder(root.left); preOrder(root.right); @@ -27,7 +27,7 @@ public class binary_tree_dfs { static void inOrder(TreeNode root) { if (root == null) return; - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(root.left); list.add(root.val); inOrder(root.right); @@ -37,32 +37,32 @@ public class binary_tree_dfs { static void postOrder(TreeNode root) { if (root == null) return; - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(root.left); postOrder(root.right); list.add(root.val); } public static void main(String[] args) { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array TreeNode root = TreeNode.listToTree(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); - System.out.println("\nInitialize binary tree\n"); + System.out.println("\nInitialize the binary tree\n"); PrintUtil.printTree(root); /* Preorder traversal */ list.clear(); preOrder(root); - System.out.println("\nPreorder traversal node print sequence = " + list); + System.out.println("\nNode sequence from preorder traversal = " + list); /* Inorder traversal */ list.clear(); inOrder(root); - System.out.println("\nInorder traversal node print sequence = " + list); + System.out.println("\nNode sequence from inorder traversal = " + list); /* Postorder traversal */ list.clear(); postOrder(root); - System.out.println("\nPostorder traversal node print sequence = " + list); + System.out.println("\nNode sequence from postorder traversal = " + list); } } diff --git a/en/codes/java/utils/ListNode.java b/en/codes/java/utils/ListNode.java index 2156d68a9..a9bf9b9e6 100644 --- a/en/codes/java/utils/ListNode.java +++ b/en/codes/java/utils/ListNode.java @@ -15,7 +15,7 @@ public class ListNode { val = x; } - /* Deserialize a list into a linked list */ + /* Deserialize an array into a linked list */ public static ListNode arrToLinkedList(int[] arr) { ListNode dum = new ListNode(0); ListNode head = dum; diff --git a/en/codes/java/utils/PrintUtil.java b/en/codes/java/utils/PrintUtil.java index 9f6a8208d..adb76eafc 100644 --- a/en/codes/java/utils/PrintUtil.java +++ b/en/codes/java/utils/PrintUtil.java @@ -19,7 +19,7 @@ class Trunk { }; public class PrintUtil { - /* Print matrix (Array) */ + /* Print a matrix (array) */ public static void printMatrix(T[][] matrix) { System.out.println("["); for (T[] row : matrix) { @@ -28,7 +28,7 @@ public class PrintUtil { System.out.println("]"); } - /* Print matrix (List) */ + /* Print a matrix (list) */ public static void printMatrix(List> matrix) { System.out.println("["); for (List row : matrix) { @@ -37,7 +37,7 @@ public class PrintUtil { System.out.println("]"); } - /* Print linked list */ + /* Print a linked list */ public static void printLinkedList(ListNode head) { List list = new ArrayList<>(); while (head != null) { @@ -47,13 +47,13 @@ public class PrintUtil { System.out.println(String.join(" -> ", list)); } - /* Print binary tree */ + /* Print a binary tree */ public static void printTree(TreeNode root) { printTree(root, null, false); } /** - * 打印二叉树 + * Print a binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -97,19 +97,19 @@ public class PrintUtil { System.out.print(p.str); } - /* Print hash table */ + /* Print the hash table */ public static void printHashMap(Map map) { for (Map.Entry kv : map.entrySet()) { System.out.println(kv.getKey() + " -> " + kv.getValue()); } } - /* Print heap (priority queue) */ + /* Print a heap (priority queue) */ public static void printHeap(Queue queue) { List list = new ArrayList<>(queue); - System.out.print("Heap array representation:"); + System.out.print("Array representation of the heap:"); System.out.println(list); - System.out.println("Heap tree representation:"); + System.out.println("Tree representation of the heap:"); TreeNode root = TreeNode.listToTree(list); printTree(root); } diff --git a/en/codes/java/utils/TreeNode.java b/en/codes/java/utils/TreeNode.java index b70544503..dc4d5fd64 100644 --- a/en/codes/java/utils/TreeNode.java +++ b/en/codes/java/utils/TreeNode.java @@ -12,19 +12,19 @@ import java.util.*; public class TreeNode { public int val; // Node value public int height; // Node height - public TreeNode left; // Reference to left child node - public TreeNode right; // Reference to right child node + public TreeNode left; // Reference to the left child + public TreeNode right; // Reference to the right child /* Constructor */ public TreeNode(int x) { val = x; } - // For the serialization encoding rules, please refer to: - // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ - // Array representation of binary tree: + // For the serialization rules, see: + // https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ + // Array representation of the binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] - // Linked list representation of binary tree: + // Linked representation of the binary tree: // /——— 15 // /——— 7 // /——— 3 diff --git a/en/codes/java/utils/Vertex.java b/en/codes/java/utils/Vertex.java index 91f369387..cf67ced40 100644 --- a/en/codes/java/utils/Vertex.java +++ b/en/codes/java/utils/Vertex.java @@ -16,7 +16,7 @@ public class Vertex { this.val = val; } - /* Input value list vals, return vertex list vets */ + /* Given the array of values vals, return the array of vertices vets */ public static Vertex[] valsToVets(int[] vals) { Vertex[] vets = new Vertex[vals.length]; for (int i = 0; i < vals.length; i++) { @@ -25,7 +25,7 @@ public class Vertex { return vets; } - /* Input vertex list vets, return value list vals */ + /* Given the list of vertices vets, return the list of values vals */ public static List vetsToVals(List vets) { List vals = new ArrayList<>(); for (Vertex vet : vets) { diff --git a/en/codes/javascript/chapter_array_and_linkedlist/array.js b/en/codes/javascript/chapter_array_and_linkedlist/array.js index 9fd628b9e..30be2261a 100644 --- a/en/codes/javascript/chapter_array_and_linkedlist/array.js +++ b/en/codes/javascript/chapter_array_and_linkedlist/array.js @@ -4,61 +4,61 @@ * Author: IsChristina (christinaxia77@foxmail.com) */ -/* Random access to element */ +/* Access an element at a random index */ function randomAccess(nums) { - // Randomly select a number in the interval [0, nums.length) + // Choose a random index in the interval [0, nums.length) const random_index = Math.floor(Math.random() * nums.length); - // Retrieve and return the random element + // Get and return the randomly selected element const random_num = nums[random_index]; return random_num; } -/* 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 +/* Extend the array length */ +// JavaScript Array is a dynamic array and can be extended directly +// For learning purposes, this function treats Array as a fixed-length array function extend(nums, enlarge) { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res; } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for (let i = nums.length - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num; } -/* Remove the element at index index */ +/* Remove the element at index `index` */ function remove(nums, index) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (let i = index; i < nums.length - 1; i++) { nums[i] = nums[i + 1]; } } -/* Traverse array */ +/* Traverse the array */ function traverse(nums) { let count = 0; - // Traverse array by index + // Traverse the array by index for (let i = 0; i < nums.length; i++) { count += nums[i]; } - // Direct traversal of array elements + // Iterate directly over the array elements for (const num of nums) { count += num; } } -/* Find the specified element in the array */ +/* Find a specified element in the array */ function find(nums, target) { for (let i = 0; i < nums.length; i++) { if (nums[i] === target) return i; @@ -66,32 +66,32 @@ function find(nums, target) { return -1; } -/* Driver Code */ -/* Initialize array */ +/* Driver code */ +/* Initialize the array */ const arr = new Array(5).fill(0); console.log('Array arr =', arr); let nums = [1, 3, 2, 5, 4]; console.log('Array nums =', nums); -/* Insert element */ +/* Random access */ let random_num = randomAccess(nums); -console.log('Get random element in nums', random_num); +console.log('Randomly selected element from nums:', random_num); -/* Traverse array */ +/* Extend the length */ nums = extend(nums, 3); -console.log('Extend array length to 8, get nums =', nums); +console.log('Extend the array length to 8, giving nums =', nums); -/* Insert element */ +/* Insert an element */ insert(nums, 6, 3); -console.log('Insert number 6 at index 3, get nums =', nums); +console.log('Insert the number 6 at index 3, giving nums =', nums); -/* Remove element */ +/* Remove an element */ remove(nums, 2); -console.log('Remove element at index 2, get nums =', nums); +console.log('Remove the element at index 2, giving nums =', nums); -/* Traverse array */ +/* Traverse the array */ traverse(nums); -/* Find element */ +/* Find an element */ let index = find(nums, 3); -console.log('Find element 3 in nums, get index =', index); +console.log('Find element 3 in nums, giving index =', index); diff --git a/en/codes/javascript/chapter_array_and_linkedlist/linked_list.js b/en/codes/javascript/chapter_array_and_linkedlist/linked_list.js index ed2756030..b4ef4621a 100644 --- a/en/codes/javascript/chapter_array_and_linkedlist/linked_list.js +++ b/en/codes/javascript/chapter_array_and_linkedlist/linked_list.js @@ -23,7 +23,7 @@ function remove(n0) { n0.next = n1; } -/* Access the node at index index in the linked list */ +/* Access the node at index `index` in the linked list */ function access(head, index) { for (let i = 0; i < index; i++) { if (!head) { @@ -47,36 +47,36 @@ function find(head, target) { return -1; } -/* Driver Code */ -/* Initialize linked list */ -// Initialize each node +/* Driver code */ +/* Initialize the linked list */ +// Initialize the individual nodes const n0 = new ListNode(1); const n1 = new ListNode(3); const n2 = new ListNode(2); const n3 = new ListNode(5); const n4 = new ListNode(4); -// Build references between nodes +// Set up references between the nodes n0.next = n1; n1.next = n2; n2.next = n3; n3.next = n4; -console.log('Initialized linked list is'); +console.log('The initialized linked list is'); printLinkedList(n0); -/* Insert node */ +/* Insert a node */ insert(n0, new ListNode(0)); -console.log('Linked list after inserting node is'); +console.log('The linked list after inserting a node is'); printLinkedList(n0); -/* Remove node */ +/* Remove a node */ remove(n0); -console.log('Linked list after removing node is'); +console.log('The linked list after removing a node is'); printLinkedList(n0); -/* Access node */ +/* Access a node */ const node = access(n0, 3); -console.log('Value of node at index 3 in linked list = ' + node.val); +console.log('Value of the node at index 3 in the linked list = ' + node.val); -/* Search node */ +/* Find a node */ const index = find(n0, 2); -console.log('Index of node with value 2 in linked list = ' + index); +console.log('Index of the node with value 2 in the linked list = ' + index); diff --git a/en/codes/javascript/chapter_array_and_linkedlist/list.js b/en/codes/javascript/chapter_array_and_linkedlist/list.js index f22517efd..bcadb2a3b 100644 --- a/en/codes/javascript/chapter_array_and_linkedlist/list.js +++ b/en/codes/javascript/chapter_array_and_linkedlist/list.js @@ -4,44 +4,44 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Initialize list */ +/* Initialize the list */ const nums = [1, 3, 2, 5, 4]; console.log(`List nums = ${nums}`); -/* Update element */ +/* Access an element */ const num = nums[1]; -console.log(`Access element at index 1, get num = ${num}`); +console.log(`Access the element at index 1, giving num = ${num}`); -/* Add elements at the end */ +/* Update an element */ nums[1] = 0; -console.log(`Update element at index 1 to 0, get nums = ${nums}`); +console.log(`Update the element at index 1 to 0, giving nums = ${nums}`); -/* Remove element */ +/* Clear the list */ nums.length = 0; -console.log(`After clearing list, nums = ${nums}`); +console.log(`After clearing the list, nums = ${nums}`); -/* Direct traversal of list elements */ +/* Append an element at the end */ nums.push(1); nums.push(3); nums.push(2); nums.push(5); nums.push(4); -console.log(`After adding elements, nums = ${nums}`); +console.log(`After appending elements, nums = ${nums}`); -/* Sort list */ +/* Insert an element in the middle */ nums.splice(3, 0, 6); -console.log(`Insert number 6 at index 3, get nums = ${nums}`); +console.log(`Insert the number 6 at index 3, giving nums = ${nums}`); -/* Remove element */ +/* Remove an element */ nums.splice(3, 1); -console.log(`Delete element at index 3, get nums = ${nums}`); +console.log(`Remove the element at index 3, giving nums = ${nums}`); -/* Traverse list by index */ +/* Traverse the list by index */ let count = 0; for (let i = 0; i < nums.length; i++) { count += nums[i]; } -/* Directly traverse list elements */ +/* Iterate directly over the list elements */ count = 0; for (const x of nums) { count += x; @@ -50,8 +50,8 @@ for (const x of nums) { /* Concatenate two lists */ const nums1 = [6, 8, 7, 10, 9]; nums.push(...nums1); -console.log(`After concatenating list nums1 to nums, get nums = ${nums}`); +console.log(`Append list nums1 to nums, giving nums = ${nums}`); -/* Sort list */ +/* Sort the list */ nums.sort((a, b) => a - b); -console.log(`After sorting list, nums = ${nums}`); +console.log(`After sorting the list, nums = ${nums}`); diff --git a/en/codes/javascript/chapter_array_and_linkedlist/my_list.js b/en/codes/javascript/chapter_array_and_linkedlist/my_list.js index 7b728bcb2..519d48188 100644 --- a/en/codes/javascript/chapter_array_and_linkedlist/my_list.js +++ b/en/codes/javascript/chapter_array_and_linkedlist/my_list.js @@ -6,58 +6,58 @@ /* List class */ class MyList { - #arr = new Array(); // Array (stores list elements) + #arr = new Array(); // Array (stores the 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 + #extendRatio = 2; // Factor by which the list capacity grows each time /* Constructor */ constructor() { this.#arr = new Array(this.#capacity); } - /* Get list length (current number of elements) */ + /* Get the list length (current number of elements)*/ size() { return this.#size; } - /* Get list capacity */ + /* Get the list capacity */ capacity() { return this.#capacity; } - /* Update element */ + /* Access an element */ get(index) { - // If the index is out of bounds, throw an exception, as below + // Throw an exception if the index is out of bounds; the same applies below if (index < 0 || index >= this.#size) throw new Error('Index out of bounds'); return this.#arr[index]; } - /* Add elements at the end */ + /* Update an element */ 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 */ + /* Append an element at the end */ add(num) { - // If length equals capacity, need to expand + // Grow the capacity if the length equals the capacity if (this.#size === this.#capacity) { this.extendCapacity(); } - // Add new element to end of list + // Append the new element to the end of the list this.#arr[this.#size] = num; this.#size++; } - /* Sort list */ + /* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if (this.#size === this.#capacity) { this.extendCapacity(); } - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for (let j = this.#size - 1; j >= index; j--) { this.#arr[j + 1] = this.#arr[j]; } @@ -66,11 +66,11 @@ class MyList { this.#size++; } - /* Remove element */ + /* Remove an 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 + // Shift all elements after index `index` one position to the left for (let j = index; j < this.#size - 1; j++) { this.#arr[j] = this.#arr[j + 1]; } @@ -80,20 +80,20 @@ class MyList { return num; } - /* Driver Code */ + /* Grow the list capacity */ extendCapacity() { - // Create a new array with length extendRatio times the original array and copy the original array to the new array + // Create an array extendRatio times the original length and copy the original array into it this.#arr = this.#arr.concat( new Array(this.capacity() * (this.#extendRatio - 1)) ); - // Add elements at the end + // Update the list capacity this.#capacity = this.#arr.length; } - /* Convert list to array */ + /* Convert the list to an array */ toArray() { let size = this.size(); - // Elements enqueue + // Convert only the list elements within the occupied range const arr = new Array(size); for (let i = 0; i < size; i++) { arr[i] = this.get(i); @@ -102,10 +102,10 @@ class MyList { } } -/* Driver Code */ -/* Initialize list */ +/* Driver code */ +/* Initialize the list */ const nums = new MyList(); -/* Direct traversal of list elements */ +/* Append an element at the end */ nums.add(1); nums.add(3); nums.add(2); @@ -115,27 +115,27 @@ console.log( `List nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` ); -/* Sort list */ +/* Insert an element in the middle */ nums.insert(3, 6); -console.log(`Insert number 6 at index 3, get nums = ${nums.toArray()}`); +console.log(`Insert the number 6 at index 3, giving nums = ${nums.toArray()}`); -/* Remove element */ +/* Remove an element */ nums.remove(3); -console.log(`Delete element at index 3, get nums = ${nums.toArray()}`); +console.log(`Remove the element at index 3, giving nums = ${nums.toArray()}`); -/* Update element */ +/* Access an element */ const num = nums.get(1); -console.log(`Access element at index 1, get num = ${num}`); +console.log(`Access the element at index 1, giving num = ${num}`); -/* Add elements at the end */ +/* Update an element */ nums.set(1, 0); -console.log(`Update element at index 1 to 0, get nums = ${nums.toArray()}`); +console.log(`Update the element at index 1 to 0, giving nums = ${nums.toArray()}`); -/* Test capacity expansion mechanism */ +/* Test capacity growth */ for (let i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i); } console.log( - `After expansion, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` + `After capacity growth, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` ); diff --git a/en/codes/javascript/chapter_backtracking/n_queens.js b/en/codes/javascript/chapter_backtracking/n_queens.js index 2e6997311..6bd797f27 100644 --- a/en/codes/javascript/chapter_backtracking/n_queens.js +++ b/en/codes/javascript/chapter_backtracking/n_queens.js @@ -4,51 +4,51 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ function backtrack(row, n, state, res, cols, diags1, diags2) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if (row === n) { res.push(state.map((row) => row.slice())); return; } - // Traverse all columns + // Iterate over all columns for (let col = 0; col < n; col++) { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square const diag1 = row - col + n - 1; const diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = 'Q'; cols[col] = diags1[diag1] = diags2[diag2] = true; - // Place the next row + // Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = '#'; cols[col] = diags1[diag1] = diags2[diag2] = false; } } } -/* Solve N queens */ +/* Solve the n-queens problem */ function nQueens(n) { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square const state = Array.from({ length: n }, () => Array(n).fill('#')); - const cols = Array(n).fill(false); // Record whether there is a queen in the column - const diags1 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the main diagonal - const diags2 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the anti-diagonal + const cols = Array(n).fill(false); // Track whether a column contains a queen + const diags1 = Array(2 * n - 1).fill(false); // Track whether a main diagonal contains a queen + const diags2 = Array(2 * n - 1).fill(false); // Track whether an anti-diagonal contains a queen const res = []; backtrack(0, n, state, res, cols, diags1, diags2); return res; } -// Driver Code +// Driver code const n = 4; const res = nQueens(n); -console.log(`Input board size is ${n}`); -console.log(`Total queen placement solutions: ${res.length}`); +console.log(`Input board side length: ${n}`); +console.log(`Number of queen arrangements: ${res.length}`); res.forEach((state) => { console.log('--------------------'); state.forEach((row) => console.log(row)); diff --git a/en/codes/javascript/chapter_backtracking/permutations_i.js b/en/codes/javascript/chapter_backtracking/permutations_i.js index 156772f37..df1cb3dfc 100644 --- a/en/codes/javascript/chapter_backtracking/permutations_i.js +++ b/en/codes/javascript/chapter_backtracking/permutations_i.js @@ -4,23 +4,23 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ function backtrack(state, choices, selected, res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.length === choices.length) { res.push([...state]); return; } - // Traverse all choices + // Iterate over all choices choices.forEach((choice, i) => { - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state.push(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop(); } @@ -34,7 +34,7 @@ function permutationsI(nums) { return res; } -// Driver Code +// Driver code const nums = [1, 2, 3]; const res = permutationsI(nums); diff --git a/en/codes/javascript/chapter_backtracking/permutations_ii.js b/en/codes/javascript/chapter_backtracking/permutations_ii.js index 7909ac3e0..d3cdc9dc7 100644 --- a/en/codes/javascript/chapter_backtracking/permutations_ii.js +++ b/en/codes/javascript/chapter_backtracking/permutations_ii.js @@ -4,25 +4,25 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ function backtrack(state, choices, selected, res) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.length === choices.length) { res.push([...state]); return; } - // Traverse all choices + // Iterate over all choices const duplicated = new Set(); choices.forEach((choice, i) => { - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && !duplicated.has(choice)) { - // Attempt: make choice, update state - duplicated.add(choice); // Record the selected element value + // Try: make a choice and update the state + duplicated.add(choice); // Record the chosen value selected[i] = true; state.push(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop(); } @@ -36,7 +36,7 @@ function permutationsII(nums) { return res; } -// Driver Code +// Driver code const nums = [1, 2, 2]; const res = permutationsII(nums); diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_i_compact.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_i_compact.js index 2e2ef4699..2fc2f0eb0 100644 --- a/en/codes/javascript/chapter_backtracking/preorder_traversal_i_compact.js +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_i_compact.js @@ -7,22 +7,22 @@ const { arrToTree } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ function preOrder(root, res) { if (root === null) { return; } if (root.val === 7) { - // Record solution + // Record the solution res.push(root); } preOrder(root.left, res); preOrder(root.right, res); } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Preorder traversal diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_ii_compact.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_ii_compact.js index 6898ae5dd..56d07e757 100644 --- a/en/codes/javascript/chapter_backtracking/preorder_traversal_ii_compact.js +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_ii_compact.js @@ -7,15 +7,15 @@ const { arrToTree } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ function preOrder(root, path, res) { if (root === null) { return; } - // Attempt + // Try path.push(root); if (root.val === 7) { - // Record solution + // Record the solution res.push([...path]); } preOrder(root.left, path, res); @@ -24,9 +24,9 @@ function preOrder(root, path, res) { path.pop(); } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Preorder traversal @@ -34,7 +34,7 @@ const path = []; const res = []; preOrder(root, path, res); -console.log('\nOutput all paths from root node to node 7'); +console.log('\nOutput all paths from the root to nodes with value 7'); res.forEach((path) => { console.log(path.map((node) => node.val)); }); diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_compact.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_compact.js index eb51e0ed8..f410ee6b4 100644 --- a/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_compact.js +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_compact.js @@ -7,16 +7,16 @@ const { arrToTree } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ function preOrder(root, path, res) { - // Pruning + // Prune if (root === null || root.val === 3) { return; } - // Attempt + // Try path.push(root); if (root.val === 7) { - // Record solution + // Record the solution res.push([...path]); } preOrder(root.left, path, res); @@ -25,9 +25,9 @@ function preOrder(root, path, res) { path.pop(); } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Preorder traversal @@ -35,7 +35,7 @@ const path = []; const res = []; preOrder(root, path, res); -console.log('\nOutput all paths from root node to node 7, paths do not include nodes with value 3'); +console.log('\nOutput all paths from the root to nodes with value 7 that contain no node with value 3'); res.forEach((path) => { console.log(path.map((node) => node.val)); }); diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_template.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_template.js index 594a2b475..c56770015 100644 --- a/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_template.js +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_template.js @@ -7,62 +7,62 @@ const { arrToTree } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ function isSolution(state) { return state && state[state.length - 1]?.val === 7; } -/* Record solution */ +/* Record the solution */ function recordSolution(state, res) { res.push([...state]); } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ function isValid(state, choice) { return choice !== null && choice.val !== 3; } -/* Update state */ +/* Update the state */ function makeChoice(state, choice) { state.push(choice); } -/* Restore state */ +/* Restore the state */ function undoChoice(state) { state.pop(); } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ function backtrack(state, choices, res) { - // Check if it is a solution + // Check whether this is a solution if (isSolution(state)) { - // Record solution + // Record the solution recordSolution(state, res); } - // Traverse all choices + // Iterate over all choices for (const choice of choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state, choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, [choice.left, choice.right], res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state); } } } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Backtracking algorithm const res = []; backtrack([], [root], res); -console.log('\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3'); +console.log('\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3'); res.forEach((path) => { console.log(path.map((node) => node.val)); }); diff --git a/en/codes/javascript/chapter_backtracking/subset_sum_i.js b/en/codes/javascript/chapter_backtracking/subset_sum_i.js index 6fde6ae12..58a04d519 100644 --- a/en/codes/javascript/chapter_backtracking/subset_sum_i.js +++ b/en/codes/javascript/chapter_backtracking/subset_sum_i.js @@ -4,26 +4,26 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ function backtrack(state, target, choices, start, res) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is zero, so the subset sum equals the original target if (target === 0) { res.push([...state]); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (let i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if the next choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and would also exceed the remaining target if (target - choices[i] < 0) { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } @@ -32,15 +32,15 @@ function backtrack(state, target, choices, start, res) { function subsetSumI(nums, target) { const state = []; // State (subset) nums.sort((a, b) => a - b); // Sort nums - const start = 0; // Start point for traversal - const res = []; // Result list (subset list) + const start = 0; // Starting index for iteration + const res = []; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ const nums = [3, 4, 5]; const target = 9; const res = subsetSumI(nums, target); console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); -console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); +console.log(`All subsets with sum ${target}: res = ${JSON.stringify(res)}`); diff --git a/en/codes/javascript/chapter_backtracking/subset_sum_i_naive.js b/en/codes/javascript/chapter_backtracking/subset_sum_i_naive.js index 2e02a3c16..704138e6e 100644 --- a/en/codes/javascript/chapter_backtracking/subset_sum_i_naive.js +++ b/en/codes/javascript/chapter_backtracking/subset_sum_i_naive.js @@ -4,41 +4,41 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ function backtrack(state, target, total, choices, res) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total === target) { res.push([...state]); return; } - // Traverse all choices + // Iterate over all choices for (let i = 0; i < choices.length; i++) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target, total + choices[i], choices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ function subsetSumINaive(nums, target) { const state = []; // State (subset) const total = 0; // Subset sum - const res = []; // Result list (subset list) + const res = []; // Result list (list of subsets) backtrack(state, target, total, nums, res); return res; } -/* Driver Code */ +/* Driver code */ const nums = [3, 4, 5]; const target = 9; const res = subsetSumINaive(nums, target); console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); -console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); -console.log('Please note that this method outputs results containing duplicate sets'); +console.log(`All subsets with sum ${target}: res = ${JSON.stringify(res)}`); +console.log('Note that this method produces duplicate subsets'); diff --git a/en/codes/javascript/chapter_backtracking/subset_sum_ii.js b/en/codes/javascript/chapter_backtracking/subset_sum_ii.js index ea5e3d33a..09c338abc 100644 --- a/en/codes/javascript/chapter_backtracking/subset_sum_ii.js +++ b/en/codes/javascript/chapter_backtracking/subset_sum_ii.js @@ -4,31 +4,31 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ function backtrack(state, target, choices, start, res) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is zero, so the subset sum equals the original target if (target === 0) { res.push([...state]); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: start after the previously chosen index to avoid choosing the same element again for (let i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if the next choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and would also exceed the remaining target if (target - choices[i] < 0) { break; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if (i > start && choices[i] === choices[i - 1]) { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } @@ -37,15 +37,15 @@ function backtrack(state, target, choices, start, res) { function subsetSumII(nums, target) { const state = []; // State (subset) nums.sort((a, b) => a - b); // Sort nums - const start = 0; // Start point for traversal - const res = []; // Result list (subset list) + const start = 0; // Starting index for iteration + const res = []; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ const nums = [4, 4, 5]; const target = 9; const res = subsetSumII(nums, target); console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); -console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); +console.log(`All subsets with sum ${target}: res = ${JSON.stringify(res)}`); diff --git a/en/codes/javascript/chapter_computational_complexity/complexity_exercises.js b/en/codes/javascript/chapter_computational_complexity/complexity_exercises.js index 7418687c2..69de7c4e4 100644 --- a/en/codes/javascript/chapter_computational_complexity/complexity_exercises.js +++ b/en/codes/javascript/chapter_computational_complexity/complexity_exercises.js @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Iterative summation */ +/* Sum using iteration */ function sumIter(n) { let res = 0; for (let i = 1; i <= n; i++) { @@ -13,7 +13,7 @@ function sumIter(n) { return res; } -/* Recursive summation */ +/* Sum using recursion */ function sumRecur(n) { if (n === 1) { return 1; @@ -21,7 +21,7 @@ function sumRecur(n) { return n + sumRecur(n - 1); } -/* Linear loop */ +/* Linear-time loop */ function linearLoop(n) { let res = 0; for (let i = 0; i < n; i++) { @@ -30,7 +30,7 @@ function linearLoop(n) { return res; } -/* Quadratic loop */ +/* Quadratic-time loop */ function quadraticLoop(n) { let res = 0; for (let i = 0; i < n; i++) { @@ -41,7 +41,7 @@ function quadraticLoop(n) { return res; } -/* Logarithmic loop */ +/* Logarithmic-time loop */ function logarithmicLoop(n) { while (n > 1) { n = Math.floor(n / 2); @@ -59,5 +59,5 @@ if ( logarithmicLoop(4) !== 1 || logarithmicLoop(5) !== 1 ) { - throw new Error('complexity exercise check failed'); + throw new Error('Complexity exercise check failed'); } diff --git a/en/codes/javascript/chapter_computational_complexity/iteration.js b/en/codes/javascript/chapter_computational_complexity/iteration.js index 67fcffc83..a35e53363 100644 --- a/en/codes/javascript/chapter_computational_complexity/iteration.js +++ b/en/codes/javascript/chapter_computational_complexity/iteration.js @@ -7,7 +7,7 @@ /* for loop */ function forLoop(n) { let res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (let i = 1; i <= n; i++) { res += i; } @@ -17,11 +17,11 @@ function forLoop(n) { /* while loop */ function whileLoop(n) { let res = 0; - let i = 1; // Initialize condition variable - // Sum 1, 2, ..., n-1, n + let i = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i; - i++; // Update condition variable + i++; // Update the loop control variable } return res; } @@ -29,23 +29,23 @@ function whileLoop(n) { /* while loop (two updates) */ function whileLoopII(n) { let res = 0; - let i = 1; // Initialize condition variable - // Sum 1, 4, 10, ... + let i = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i; - // Update condition variable + // Update the loop control variable i++; i *= 2; } return res; } -/* Nested for loop */ +/* Nested for loops */ function nestedForLoop(n) { let res = ''; - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (let i = 1; i <= n; i++) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (let j = 1; j <= n; j++) { res += `(${i}, ${j}), `; } @@ -53,18 +53,18 @@ function nestedForLoop(n) { return res; } -/* Driver Code */ +/* Driver code */ const n = 5; let res; res = forLoop(n); -console.log(`For loop sum result res = ${res}`); +console.log(`Sum from the for loop: res = ${res}`); res = whileLoop(n); -console.log(`While loop sum result res = ${res}`); +console.log(`Sum from the while loop: res = ${res}`); res = whileLoopII(n); -console.log(`While loop (two updates) sum result res = ${res}`); +console.log(`Sum from the while loop with two updates: res = ${res}`); const resStr = nestedForLoop(n); -console.log(`Nested for loop traversal result ${resStr}`); +console.log(`Traversal result from the nested for loops: ${resStr}`); diff --git a/en/codes/javascript/chapter_computational_complexity/recursion.js b/en/codes/javascript/chapter_computational_complexity/recursion.js index 6bc62355e..16ea0ae15 100644 --- a/en/codes/javascript/chapter_computational_complexity/recursion.js +++ b/en/codes/javascript/chapter_computational_complexity/recursion.js @@ -6,11 +6,11 @@ /* Recursion */ function recur(n) { - // Termination condition + // Base case if (n === 1) return 1; - // Recurse: recursive call + // Recurse: make a recursive call const res = recur(n - 1); - // Return: return result + // Unwind: return the result return n + res; } @@ -19,14 +19,14 @@ function forLoopRecur(n) { // Use an explicit stack to simulate the system call stack const stack = []; let res = 0; - // Recurse: recursive call + // Recurse: make a recursive call for (let i = n; i > 0; i--) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.push(i); } - // Return: return result + // Unwind: return the result while (stack.length) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.pop(); } // res = 1+2+3+...+n @@ -35,35 +35,35 @@ function forLoopRecur(n) { /* Tail recursion */ function tailRecur(n, res) { - // Termination condition + // Base case if (n === 0) return res; - // Tail recursive call + // Tail-recursive call return tailRecur(n - 1, res + n); } /* Fibonacci sequence: recursion */ function fib(n) { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n === 1 || n === 2) return n - 1; - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) const res = fib(n - 1) + fib(n - 2); - // Return result f(n) + // Return the result f(n) return res; } -/* Driver Code */ +/* Driver code */ const n = 5; let res; res = recur(n); -console.log(`Recursion sum result res = ${res}`); +console.log(`Sum from the recursive function: res = ${res}`); res = forLoopRecur(n); -console.log(`Using iteration to simulate recursion sum result res = ${res}`); +console.log(`Sum from simulating recursion with iteration: res = ${res}`); res = tailRecur(n, 0); -console.log(`Tail recursion sum result res = ${res}`); +console.log(`Sum from the tail-recursive function: res = ${res}`); res = fib(n); -console.log(`The ${n}th Fibonacci number is ${res}`); +console.log(`Fibonacci term ${n} is ${res}`); diff --git a/en/codes/javascript/chapter_computational_complexity/space_complexity.js b/en/codes/javascript/chapter_computational_complexity/space_complexity.js index 47b41e48d..87a53b9c6 100644 --- a/en/codes/javascript/chapter_computational_complexity/space_complexity.js +++ b/en/codes/javascript/chapter_computational_complexity/space_complexity.js @@ -14,9 +14,9 @@ function constFunc() { return 0; } -/* Constant order */ +/* Constant complexity */ function constant(n) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space const a = 0; const b = 0; const nums = new Array(10000); @@ -25,42 +25,42 @@ function constant(n) { for (let i = 0; i < n; i++) { const c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for (let i = 0; i < n; i++) { constFunc(); } } -/* Linear order */ +/* Linear complexity */ function linear(n) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space const nums = new Array(n); // A list of length n occupies O(n) space const nodes = []; for (let i = 0; i < n; i++) { nodes.push(new ListNode(i)); } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space const map = new Map(); for (let i = 0; i < n; i++) { map.set(i, i.toString()); } } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ function linearRecur(n) { - console.log(`Recursion n = ${n}`); + console.log(`Recursive call with n = ${n}`); if (n === 1) return; linearRecur(n - 1); } -/* Exponential order */ +/* Quadratic complexity */ function quadratic(n) { - // Matrix uses O(n^2) space + // The matrix uses O(n^2) space const numMatrix = Array(n) .fill(null) .map(() => Array(n).fill(null)); - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space const numList = []; for (let i = 0; i < n; i++) { const tmp = []; @@ -71,15 +71,15 @@ function quadratic(n) { } } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ function quadraticRecur(n) { if (n <= 0) return 0; const nums = new Array(n); - console.log(`In recursion n = ${n}, nums length = ${nums.length}`); + console.log(`In the recursive call with n = ${n}, nums has length ${nums.length}`); return quadraticRecur(n - 1); } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ function buildTree(n) { if (n === 0) return null; const root = new TreeNode(0); @@ -88,16 +88,16 @@ function buildTree(n) { return root; } -/* Driver Code */ +/* Driver code */ const n = 5; -// Constant order +// Constant complexity constant(n); -// Linear order +// Linear complexity linear(n); linearRecur(n); -// Exponential order +// Quadratic complexity quadratic(n); quadraticRecur(n); -// Exponential order +// Exponential complexity const root = buildTree(n); printTree(root); diff --git a/en/codes/javascript/chapter_computational_complexity/time_complexity.js b/en/codes/javascript/chapter_computational_complexity/time_complexity.js index 399c4699a..35e72576f 100644 --- a/en/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/en/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -4,7 +4,7 @@ * Author: RiverTwilight (contact@rene.wang) */ -/* Constant order */ +/* Constant complexity */ function constant(n) { let count = 0; const size = 100000; @@ -12,27 +12,27 @@ function constant(n) { return count; } -/* Linear order */ +/* Linear complexity */ function linear(n) { let count = 0; for (let i = 0; i < n; i++) count++; return count; } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ function arrayTraversal(nums) { let count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for (let i = 0; i < nums.length; i++) { count++; } return count; } -/* Exponential order */ +/* Quadratic complexity */ function quadratic(n) { let count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { count++; @@ -41,30 +41,30 @@ function quadratic(n) { return count; } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ function bubbleSort(nums) { let count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (let i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] let tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } return count; } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ function exponential(n) { let count = 0, base = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (let i = 0; i < n; i++) { for (let j = 0; j < base; j++) { count++; @@ -75,13 +75,13 @@ function exponential(n) { return count; } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation) */ function expRecur(n) { if (n === 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation) */ function logarithmic(n) { let count = 0; while (n > 1) { @@ -91,13 +91,13 @@ function logarithmic(n) { return count; } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation) */ function logRecur(n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } -/* Linearithmic order */ +/* Linearithmic complexity */ function linearLogRecur(n) { if (n <= 1) return 1; let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); @@ -107,49 +107,49 @@ function linearLogRecur(n) { return count; } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ function factorialRecur(n) { if (n === 0) return 1; let count = 0; - // Split from 1 into n + // Split one problem into n subproblems for (let i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; } -/* Driver Code */ -// You can modify n to run and observe the trend of the number of operations for various complexities +/* Driver code */ +// Try different values of n to see how the operation count grows for each complexity const n = 8; -console.log('Input data size n = ' + n); +console.log('Input size n = ' + n); let count = constant(n); -console.log('Constant order operation count = ' + count); +console.log('Operation count for constant complexity = ' + count); count = linear(n); -console.log('Linear order operation count = ' + count); +console.log('Operation count for linear complexity = ' + count); count = arrayTraversal(new Array(n)); -console.log('Linear order (array traversal) operation count = ' + count); +console.log('Operation count for linear complexity (array traversal) = ' + count); count = quadratic(n); -console.log('Quadratic order operation count = ' + count); +console.log('Operation count for quadratic complexity = ' + count); let nums = new Array(n); for (let i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = bubbleSort(nums); -console.log('Quadratic order (bubble sort) operation count = ' + count); +console.log('Operation count for quadratic complexity (bubble sort) = ' + count); count = exponential(n); -console.log('Exponential order (loop implementation) operation count = ' + count); +console.log('Operation count for exponential complexity (iterative implementation) = ' + count); count = expRecur(n); -console.log('Exponential order (recursive implementation) operation count = ' + count); +console.log('Operation count for exponential complexity (recursive implementation) = ' + count); count = logarithmic(n); -console.log('Logarithmic order (loop implementation) operation count = ' + count); +console.log('Operation count for logarithmic complexity (iterative implementation) = ' + count); count = logRecur(n); -console.log('Logarithmic order (recursive implementation) operation count = ' + count); +console.log('Operation count for logarithmic complexity (recursive implementation) = ' + count); count = linearLogRecur(n); -console.log('Linearithmic order (recursive implementation) operation count = ' + count); +console.log('Operation count for linearithmic complexity (recursive implementation) = ' + count); count = factorialRecur(n); -console.log('Factorial order (recursive implementation) operation count = ' + count); +console.log('Operation count for factorial complexity (recursive implementation) = ' + count); diff --git a/en/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js b/en/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js index 256faa5e8..361181490 100644 --- a/en/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js +++ b/en/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js @@ -4,14 +4,14 @@ * Author: RiverTwilight (contact@rene.wang) */ -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ function randomNumbers(n) { const nums = Array(n); - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } - // Randomly shuffle array elements + // Randomly shuffle the array elements for (let i = 0; i < n; i++) { const r = Math.floor(Math.random() * (i + 1)); const temp = nums[i]; @@ -21,11 +21,11 @@ function randomNumbers(n) { return nums; } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ function findOne(nums) { for (let i = 0; i < nums.length; i++) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] === 1) { return i; } @@ -33,11 +33,11 @@ function findOne(nums) { return -1; } -/* Driver Code */ +/* Driver code */ for (let i = 0; i < 10; i++) { const n = 100; const nums = randomNumbers(n); const index = findOne(nums); console.log('\nArray [ 1, 2, ..., n ] after shuffling = [' + nums.join(', ') + ']'); - console.log('Index of number 1 is ' + index); + console.log('The index of the number 1 is ' + index); } diff --git a/en/codes/javascript/chapter_divide_and_conquer/binary_search_recur.js b/en/codes/javascript/chapter_divide_and_conquer/binary_search_recur.js index 170ed9c70..2096ed166 100644 --- a/en/codes/javascript/chapter_divide_and_conquer/binary_search_recur.js +++ b/en/codes/javascript/chapter_divide_and_conquer/binary_search_recur.js @@ -6,20 +6,20 @@ /* Binary search: problem f(i, j) */ function dfs(nums, target, i, j) { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1; } // Calculate the midpoint index m const m = i + ((j - i) >> 1); if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -27,13 +27,13 @@ function dfs(nums, target, i, j) { /* Binary search */ function binarySearch(nums, target) { const n = nums.length; - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1); } -/* Driver Code */ +/* Driver code */ const target = 6; const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; -// Binary search (closed interval on both sides) +// Binary search (closed interval) const index = binarySearch(nums, target); -console.log(`Index of target element 6 is ${index}`); +console.log(`Index of target element 6 = ${index}`); diff --git a/en/codes/javascript/chapter_divide_and_conquer/build_tree.js b/en/codes/javascript/chapter_divide_and_conquer/build_tree.js index e7c26039a..f06f2755a 100644 --- a/en/codes/javascript/chapter_divide_and_conquer/build_tree.js +++ b/en/codes/javascript/chapter_divide_and_conquer/build_tree.js @@ -7,13 +7,13 @@ const { printTree } = require('../modules/PrintUtil'); const { TreeNode } = require('../modules/TreeNode'); -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ function dfs(preorder, inorderMap, i, l, r) { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) return null; // Initialize the root node const root = new TreeNode(preorder[i]); - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees const m = inorderMap.get(preorder[i]); // Subproblem: build the left subtree root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); @@ -23,9 +23,9 @@ function dfs(preorder, inorderMap, i, l, r) { return root; } -/* Build binary tree */ +/* Build a binary tree */ function buildTree(preorder, inorder) { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index let inorderMap = new Map(); for (let i = 0; i < inorder.length; i++) { inorderMap.set(inorder[i], i); @@ -34,7 +34,7 @@ function buildTree(preorder, inorder) { return root; } -/* Driver Code */ +/* Driver code */ const preorder = [3, 9, 2, 1, 7]; const inorder = [9, 3, 1, 2, 7]; console.log('Preorder traversal = ' + JSON.stringify(preorder)); diff --git a/en/codes/javascript/chapter_divide_and_conquer/fast_power.js b/en/codes/javascript/chapter_divide_and_conquer/fast_power.js index d13a0ee13..07d3f8cb7 100644 --- a/en/codes/javascript/chapter_divide_and_conquer/fast_power.js +++ b/en/codes/javascript/chapter_divide_and_conquer/fast_power.js @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Exponentiation by squaring */ +/* Fast exponentiation */ function fastPow(x, n) { if (n === 0) { return 1; @@ -17,5 +17,5 @@ function fastPow(x, n) { } if (fastPow(7, 0) !== 1 || fastPow(3, 5) !== 243 || fastPow(2, 6) !== 64) { - throw new Error('fast power check failed'); + throw new Error('Fast power check failed'); } diff --git a/en/codes/javascript/chapter_divide_and_conquer/hanota.js b/en/codes/javascript/chapter_divide_and_conquer/hanota.js index c943f7805..b4aaf1867 100644 --- a/en/codes/javascript/chapter_divide_and_conquer/hanota.js +++ b/en/codes/javascript/chapter_divide_and_conquer/hanota.js @@ -4,9 +4,9 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Move a disk */ +/* Move one disk */ function move(src, tar) { - // Take out a disk from the top of src + // Take a disk from the top of src const pan = src.pop(); // Place the disk on top of tar tar.push(pan); @@ -14,14 +14,14 @@ function move(src, tar) { /* Solve the Tower of Hanoi problem f(i) */ function dfs(i, src, buf, tar) { - // If there is only one disk left in src, move it directly to tar + // If only one disk remains to be moved from src, move it directly to tar if (i === 1) { move(src, tar); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); @@ -34,19 +34,19 @@ function solveHanota(A, B, C) { dfs(n, A, B, C); } -/* Driver Code */ -// The tail of the list is the top of the rod +/* Driver code */ +// The end of each list represents the top of the peg const A = [5, 4, 3, 2, 1]; const B = []; const C = []; -console.log('In initial state:'); +console.log('Initial state:'); console.log(`A = ${JSON.stringify(A)}`); console.log(`B = ${JSON.stringify(B)}`); console.log(`C = ${JSON.stringify(C)}`); solveHanota(A, B, C); -console.log('After disk movement is complete:'); +console.log('After moving the disks:'); console.log(`A = ${JSON.stringify(A)}`); console.log(`B = ${JSON.stringify(B)}`); console.log(`C = ${JSON.stringify(C)}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_backtrack.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_backtrack.js index d08f819f9..7eae85046 100644 --- a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_backtrack.js +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_backtrack.js @@ -6,29 +6,29 @@ /* Backtracking */ function backtrack(choices, state, n, res) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state === n) res.set(0, res.get(0) + 1); - // Traverse all choices + // Iterate over all choices for (const choice of choices) { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue; - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res); // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ function climbingStairsBacktrack(n) { - const choices = [1, 2]; // Can choose to climb up 1 or 2 stairs - const state = 0; // Start climbing from the 0-th stair + const choices = [1, 2]; // Choose to climb 1 or 2 steps + const state = 0; // Start climbing from step 0 const res = new Map(); - res.set(0, 0); // Use res[0] to record the solution count + res.set(0, 0); // Store the number of ways at key 0 in res backtrack(choices, state, n, res); return res.get(0); } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsBacktrack(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_constraint_dp.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_constraint_dp.js index 0f02bbbb3..2e2d2ed4a 100644 --- a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_constraint_dp.js +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_constraint_dp.js @@ -4,19 +4,19 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ function climbingStairsConstraintDP(n) { if (n === 1 || n === 2) { return 1; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions const dp = Array.from(new Array(n + 1), () => new Array(3)); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (let i = 3; i <= n; i++) { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -24,7 +24,7 @@ function climbingStairsConstraintDP(n) { return dp[n][1] + dp[n][2]; } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsConstraintDP(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs.js index c5e47e5c7..908c7353b 100644 --- a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs.js +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs.js @@ -6,19 +6,19 @@ /* Search */ function dfs(i) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i === 1 || i === 2) return i; // dp[i] = dp[i-1] + dp[i-2] const count = dfs(i - 1) + dfs(i - 2); return count; } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ function climbingStairsDFS(n) { return dfs(n); } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsDFS(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs_mem.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs_mem.js index b476ac6da..efb92d0f9 100644 --- a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs_mem.js +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs_mem.js @@ -4,11 +4,11 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Memoization search */ +/* Memoized search */ function dfs(i, mem) { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i === 1 || i === 2) return i; - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] const count = dfs(i - 1, mem) + dfs(i - 2, mem); @@ -17,14 +17,14 @@ function dfs(i, mem) { return count; } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ function climbingStairsDFSMem(n) { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded const mem = new Array(n + 1).fill(-1); return dfs(n, mem); } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsDFSMem(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dp.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dp.js index 13f361e60..94dab31ff 100644 --- a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dp.js +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dp.js @@ -4,22 +4,22 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ function climbingStairsDP(n) { if (n === 1 || n === 2) return n; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions const dp = new Array(n + 1).fill(-1); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (let i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ function climbingStairsDPComp(n) { if (n === 1 || n === 2) return n; let a = 1, @@ -32,9 +32,9 @@ function climbingStairsDPComp(n) { return b; } -/* Driver Code */ +/* Driver code */ const n = 9; let res = climbingStairsDP(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); res = climbingStairsDPComp(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/coin_change.js b/en/codes/javascript/chapter_dynamic_programming/coin_change.js index e5899a558..af31d5759 100644 --- a/en/codes/javascript/chapter_dynamic_programming/coin_change.js +++ b/en/codes/javascript/chapter_dynamic_programming/coin_change.js @@ -4,26 +4,26 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ function coinChangeDP(coins, amt) { const n = coins.length; const MAX = amt + 1; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n + 1 }, () => Array.from({ length: amt + 1 }, () => 0) ); - // State transition: first row and first column + // State transitions: first row and first column for (let a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -31,21 +31,21 @@ function coinChangeDP(coins, amt) { return dp[n][amt] !== MAX ? dp[n][amt] : -1; } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ function coinChangeDPComp(coins, amt) { const n = coins.length; const MAX = amt + 1; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: amt + 1 }, () => MAX); dp[0] = 0; - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -53,14 +53,14 @@ function coinChangeDPComp(coins, amt) { return dp[amt] !== MAX ? dp[amt] : -1; } -/* Driver Code */ +/* Driver code */ const coins = [1, 2, 5]; const amt = 4; // Dynamic programming let res = coinChangeDP(coins, amt); -console.log(`Minimum coins needed to make target amount is ${res}`); +console.log(`Minimum number of coins needed to make the target amount: ${res}`); // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt); -console.log(`Minimum coins needed to make target amount is ${res}`); +console.log(`Minimum number of coins needed to make the target amount: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/coin_change_ii.js b/en/codes/javascript/chapter_dynamic_programming/coin_change_ii.js index 2e4df5497..967233c10 100644 --- a/en/codes/javascript/chapter_dynamic_programming/coin_change_ii.js +++ b/en/codes/javascript/chapter_dynamic_programming/coin_change_ii.js @@ -4,25 +4,25 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ function coinChangeIIDP(coins, amt) { const n = coins.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n + 1 }, () => Array.from({ length: amt + 1 }, () => 0) ); - // Initialize first column + // Initialize the first column for (let i = 0; i <= n; i++) { dp[i][0] = 1; } - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -30,20 +30,20 @@ function coinChangeIIDP(coins, amt) { return dp[n][amt]; } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ function coinChangeIIDPComp(coins, amt) { const n = coins.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: amt + 1 }, () => 0); dp[0] = 1; - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -51,14 +51,14 @@ function coinChangeIIDPComp(coins, amt) { return dp[amt]; } -/* Driver Code */ +/* Driver code */ const coins = [1, 2, 5]; const amt = 5; // Dynamic programming let res = coinChangeIIDP(coins, amt); -console.log(`Number of coin combinations to make target amount is ${res}`); +console.log(`Number of coin combinations that make the target amount: ${res}`); // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt); -console.log(`Number of coin combinations to make target amount is ${res}`); +console.log(`Number of coin combinations that make the target amount: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/edit_distance.js b/en/codes/javascript/chapter_dynamic_programming/edit_distance.js index cd8259947..4e0c97e40 100644 --- a/en/codes/javascript/chapter_dynamic_programming/edit_distance.js +++ b/en/codes/javascript/chapter_dynamic_programming/edit_distance.js @@ -4,76 +4,76 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ function editDistanceDFS(s, t, i, j) { - // If both s and t are empty, return 0 + // If the current prefixes of both s and t are empty, return 0 if (i === 0 && j === 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return the current prefix length j of t if (i === 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return the current prefix length i of s if (j === 0) return i; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s.charAt(i - 1) === t.charAt(j - 1)) return editDistanceDFS(s, t, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 const insert = editDistanceDFS(s, t, i, j - 1); const del = editDistanceDFS(s, t, i - 1, j); const replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits return Math.min(insert, del, replace) + 1; } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ function editDistanceDFSMem(s, t, mem, i, j) { - // If both s and t are empty, return 0 + // If the current prefixes of both s and t are empty, return 0 if (i === 0 && j === 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return the current prefix length j of t if (i === 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return the current prefix length i of s if (j === 0) return i; - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] !== -1) return mem[i][j]; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s.charAt(i - 1) === t.charAt(j - 1)) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 const insert = editDistanceDFSMem(s, t, mem, i, j - 1); const del = editDistanceDFSMem(s, t, mem, i - 1, j); const replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = Math.min(insert, del, replace) + 1; return mem[i][j]; } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ function editDistanceDP(s, t) { const n = s.length, m = t.length; const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0)); - // State transition: first row and first column + // State transitions: first row and first column for (let i = 1; i <= n; i++) { dp[i][0] = i; } for (let j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (let i = 1; i <= n; i++) { for (let j = 1; j <= m; j++) { if (s.charAt(i - 1) === t.charAt(j - 1)) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1; } @@ -82,31 +82,31 @@ function editDistanceDP(s, t) { return dp[n][m]; } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ function editDistanceDPComp(s, t) { const n = s.length, m = t.length; const dp = new Array(m + 1).fill(0); - // State transition: first row + // State transitions: first row for (let j = 1; j <= m; j++) { dp[j] = j; } - // State transition: rest of the rows + // State transitions: remaining rows for (let i = 1; i <= n; i++) { - // State transition: first column + // State transitions: first column let leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: rest of the columns + // State transitions: remaining columns for (let j = 1; j <= m; j++) { const temp = dp[j]; if (s.charAt(i - 1) === t.charAt(j - 1)) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = Math.min(dp[j - 1], dp[j], leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } return dp[m]; @@ -119,17 +119,17 @@ const n = s.length, // Brute-force search let res = editDistanceDFS(s, t, n, m); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); -// Memoization search +// Memoized search const mem = Array.from(new Array(n + 1), () => new Array(m + 1).fill(-1)); res = editDistanceDFSMem(s, t, mem, n, m); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); // Dynamic programming res = editDistanceDP(s, t); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); // Space-optimized dynamic programming res = editDistanceDPComp(s, t); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/knapsack.js b/en/codes/javascript/chapter_dynamic_programming/knapsack.js index 6544a0349..7444653e3 100644 --- a/en/codes/javascript/chapter_dynamic_programming/knapsack.js +++ b/en/codes/javascript/chapter_dynamic_programming/knapsack.js @@ -4,61 +4,61 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ function knapsackDFS(wgt, val, i, c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i === 0 || c === 0) { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i const no = knapsackDFS(wgt, val, i - 1, c); const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices return Math.max(no, yes); } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ function knapsackDFSMem(wgt, val, mem, i, c) { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i === 0 || c === 0) { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] !== -1) { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i const no = knapsackDFSMem(wgt, val, mem, i - 1, c); const yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = Math.max(no, yes); return mem[i][c]; } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ function knapsackDP(wgt, val, cap) { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array(n + 1) .fill(0) .map(() => Array(cap + 1).fill(0)); - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = Math.max( dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1] @@ -69,17 +69,17 @@ function knapsackDP(wgt, val, cap) { return dp[n][cap]; } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ function knapsackDPComp(wgt, val, cap) { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array(cap + 1).fill(0); - // State transition + // State transitions for (let i = 1; i <= n; i++) { - // Traverse in reverse order + // Traverse in decreasing order for (let c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -87,7 +87,7 @@ function knapsackDPComp(wgt, val, cap) { return dp[cap]; } -/* Driver Code */ +/* Driver code */ const wgt = [10, 20, 30, 40, 50]; const val = [50, 120, 150, 210, 240]; const cap = 50; @@ -95,19 +95,19 @@ const n = wgt.length; // Brute-force search let res = knapsackDFS(wgt, val, n, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum item value within the knapsack capacity: ${res}`); -// Memoization search +// Memoized search const mem = Array.from({ length: n + 1 }, () => Array.from({ length: cap + 1 }, () => -1) ); res = knapsackDFSMem(wgt, val, mem, n, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum item value within the knapsack capacity: ${res}`); // Dynamic programming res = knapsackDP(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum item value within the knapsack capacity: ${res}`); // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum item value within the knapsack capacity: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.js b/en/codes/javascript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.js index 69d4bb379..8702dc22a 100644 --- a/en/codes/javascript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.js +++ b/en/codes/javascript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.js @@ -4,25 +4,25 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ function minCostClimbingStairsDP(cost) { const n = cost.length - 1; if (n === 1 || n === 2) { return cost[n]; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions const dp = new Array(n + 1); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (let i = 3; i <= n; i++) { dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; } return dp[n]; } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ function minCostClimbingStairsDPComp(cost) { const n = cost.length - 1; if (n === 1 || n === 2) { @@ -38,12 +38,12 @@ function minCostClimbingStairsDPComp(cost) { return b; } -/* Driver Code */ +/* Driver code */ const cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; -console.log('Input stair cost list is:', cost); +console.log('Input list of stair costs:', cost); let res = minCostClimbingStairsDP(cost); -console.log(`Minimum cost to climb stairs is: ${res}`); +console.log(`Minimum cost to climb the stairs: ${res}`); res = minCostClimbingStairsDPComp(cost); -console.log(`Minimum cost to climb stairs is: ${res}`); +console.log(`Minimum cost to climb the stairs: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/min_path_sum.js b/en/codes/javascript/chapter_dynamic_programming/min_path_sum.js index ef6e6a869..b8375642b 100644 --- a/en/codes/javascript/chapter_dynamic_programming/min_path_sum.js +++ b/en/codes/javascript/chapter_dynamic_programming/min_path_sum.js @@ -4,63 +4,63 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ function minPathSumDFS(grid, i, j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i === 0 && j === 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return Infinity; } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) const up = minPathSumDFS(grid, i - 1, j); const left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return Math.min(left, up) + grid[i][j]; } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ function minPathSumDFSMem(grid, mem, i, j) { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i === 0 && j === 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return Infinity; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] !== -1) { return mem[i][j]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above const up = minPathSumDFSMem(grid, mem, i - 1, j); const left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = Math.min(left, up) + grid[i][j]; return mem[i][j]; } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ function minPathSumDP(grid) { const n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n }, () => Array.from({ length: m }, () => 0) ); dp[0][0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (let j = 1; j < m; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for (let i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (let i = 1; i < n; i++) { for (let j = 1; j < m; j++) { dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -69,22 +69,22 @@ function minPathSumDP(grid) { return dp[n - 1][m - 1]; } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ function minPathSumDPComp(grid) { const n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table const dp = new Array(m); - // State transition: first row + // State transitions: first row dp[0] = grid[0][0]; for (let j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for (let i = 1; i < n; i++) { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for (let j = 1; j < m; j++) { dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -92,7 +92,7 @@ function minPathSumDPComp(grid) { return dp[m - 1]; } -/* Driver Code */ +/* Driver code */ const grid = [ [1, 3, 1, 5], [2, 2, 4, 2], @@ -103,19 +103,19 @@ const n = grid.length, m = grid[0].length; // Brute-force search let res = minPathSumDFS(grid, n - 1, m - 1); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); -// Memoization search +// Memoized search const mem = Array.from({ length: n }, () => Array.from({ length: m }, () => -1) ); res = minPathSumDFSMem(grid, mem, n - 1, m - 1); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); // Dynamic programming res = minPathSumDP(grid); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); // Space-optimized dynamic programming res = minPathSumDPComp(grid); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/unbounded_knapsack.js b/en/codes/javascript/chapter_dynamic_programming/unbounded_knapsack.js index e3c0b61c9..cfdecdf2e 100644 --- a/en/codes/javascript/chapter_dynamic_programming/unbounded_knapsack.js +++ b/en/codes/javascript/chapter_dynamic_programming/unbounded_knapsack.js @@ -4,21 +4,21 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ function unboundedKnapsackDP(wgt, val, cap) { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n + 1 }, () => Array.from({ length: cap + 1 }, () => 0) ); - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = Math.max( dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1] @@ -29,19 +29,19 @@ function unboundedKnapsackDP(wgt, val, cap) { return dp[n][cap]; } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ function unboundedKnapsackDPComp(wgt, val, cap) { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: cap + 1 }, () => 0); - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -49,15 +49,15 @@ function unboundedKnapsackDPComp(wgt, val, cap) { return dp[cap]; } -/* Driver Code */ +/* Driver code */ const wgt = [1, 2, 3]; const val = [5, 11, 15]; const cap = 4; // Dynamic programming let res = unboundedKnapsackDP(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum item value within the knapsack capacity: ${res}`); // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum item value within the knapsack capacity: ${res}`); diff --git a/en/codes/javascript/chapter_graph/graph_adjacency_list.js b/en/codes/javascript/chapter_graph/graph_adjacency_list.js index fac6f69c4..d11c16fca 100644 --- a/en/codes/javascript/chapter_graph/graph_adjacency_list.js +++ b/en/codes/javascript/chapter_graph/graph_adjacency_list.js @@ -6,9 +6,9 @@ const { Vertex } = require('../modules/Vertex'); -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ class GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors adjList; /* Constructor */ @@ -27,21 +27,21 @@ class GraphAdjList { return this.adjList.size; } - /* Add edge */ + /* Add an edge */ addEdge(vet1, vet2) { if ( !this.adjList.has(vet1) || !this.adjList.has(vet2) || vet1 === vet2 ) { - throw new Error('Illegal Argument Exception'); + throw new Error('Invalid argument'); } // Add edge vet1 - vet2 this.adjList.get(vet1).push(vet2); this.adjList.get(vet2).push(vet1); } - /* Remove edge */ + /* Remove an edge */ removeEdge(vet1, vet2) { if ( !this.adjList.has(vet1) || @@ -49,28 +49,28 @@ class GraphAdjList { vet1 === vet2 || this.adjList.get(vet1).indexOf(vet2) === -1 ) { - throw new Error('Illegal Argument Exception'); + throw new Error('Invalid argument'); } // Remove edge vet1 - vet2 this.adjList.get(vet1).splice(this.adjList.get(vet1).indexOf(vet2), 1); this.adjList.get(vet2).splice(this.adjList.get(vet2).indexOf(vet1), 1); } - /* Add vertex */ + /* Add a vertex */ addVertex(vet) { if (this.adjList.has(vet)) return; - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list this.adjList.set(vet, []); } - /* Remove vertex */ + /* Remove a vertex */ removeVertex(vet) { if (!this.adjList.has(vet)) { - throw new Error('Illegal Argument Exception'); + throw new Error('Invalid argument'); } - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list this.adjList.delete(vet); - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for (const set of this.adjList.values()) { const index = set.indexOf(vet); if (index > -1) { @@ -79,7 +79,7 @@ class GraphAdjList { } } - /* Print adjacency list */ + /* Print the adjacency list */ print() { console.log('Adjacency list ='); for (const [key, value] of this.adjList) { @@ -93,8 +93,8 @@ class GraphAdjList { } if (require.main === module) { - /* Driver Code */ - /* Add edge */ + /* Driver code */ + /* Initialize the undirected graph */ const v0 = new Vertex(1), v1 = new Vertex(3), v2 = new Vertex(2), @@ -109,31 +109,31 @@ if (require.main === module) { [v3, v4], ]; const graph = new GraphAdjList(edges); - console.log('\nAfter initialization, graph is'); + console.log('\nGraph after initialization:'); graph.print(); - /* Add edge */ - // Vertices 1, 2 are v0, v2 + /* Add an edge */ + // Vertices 1 and 2 are v0 and v2 graph.addEdge(v0, v2); - console.log('\nAfter adding edge 1-2, graph is'); + console.log('\nGraph after adding edge 1-2:'); graph.print(); - /* Remove edge */ - // Vertices 1, 3 are v0, v1 + /* Remove an edge */ + // Vertices 1 and 3 are v0 and v1 graph.removeEdge(v0, v1); - console.log('\nAfter removing edge 1-3, graph is'); + console.log('\nGraph after removing edge 1-3:'); graph.print(); - /* Add vertex */ + /* Add a vertex */ const v5 = new Vertex(6); graph.addVertex(v5); - console.log('\nAfter adding vertex 6, graph is'); + console.log('\nGraph after adding vertex 6:'); graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v1 graph.removeVertex(v1); - console.log('\nAfter removing vertex 3, graph is'); + console.log('\nGraph after removing vertex 3:'); graph.print(); } diff --git a/en/codes/javascript/chapter_graph/graph_adjacency_matrix.js b/en/codes/javascript/chapter_graph/graph_adjacency_matrix.js index 40565487b..dd793b0c8 100644 --- a/en/codes/javascript/chapter_graph/graph_adjacency_matrix.js +++ b/en/codes/javascript/chapter_graph/graph_adjacency_matrix.js @@ -4,21 +4,21 @@ * Author: Zhuo Qinyue (1403450829@qq.com) */ -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat { - vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" - adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + vertices; // Vertex list: elements are vertex values, and indices are vertex indices + adjMat; // Adjacency matrix: row and column indices correspond to vertex indices /* Constructor */ constructor(vertices, edges) { this.vertices = []; this.adjMat = []; - // Add vertex + // Add a vertex for (const val of vertices) { this.addVertex(val); } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for (const e of edges) { this.addEdge(e[0], e[1]); } @@ -29,10 +29,10 @@ class GraphAdjMat { return this.vertices.length; } - /* Add vertex */ + /* Add a vertex */ addVertex(val) { const n = this.size(); - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list this.vertices.push(val); // Add a row to the adjacency matrix const newRow = []; @@ -46,55 +46,55 @@ class GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ removeVertex(index) { if (index >= this.size()) { - throw new RangeError('Index Out Of Bounds Exception'); + throw new RangeError('Index out of bounds'); } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list this.vertices.splice(index, 1); - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix this.adjMat.splice(index, 1); - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for (const row of this.adjMat) { row.splice(index, 1); } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices addEdge(i, j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); + throw new RangeError('Index out of bounds'); } - // In undirected graph, adjacency matrix is symmetric about main diagonal, i.e., satisfies (i, j) === (j, i) + // In an undirected graph, the adjacency matrix is symmetric about its main diagonal: (i, j) === (j, i) this.adjMat[i][j] = 1; this.adjMat[j][i] = 1; } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices removeEdge(i, j) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); + throw new RangeError('Index out of bounds'); } this.adjMat[i][j] = 0; this.adjMat[j][i] = 0; } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ print() { console.log('Vertex list = ', this.vertices); console.log('Adjacency matrix =', this.adjMat); } } -/* Driver Code */ -/* Add edge */ -// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices +/* Driver code */ +/* Initialize the undirected graph */ +// Note that the entries in edges are vertex indices, corresponding to element indices in vertices const vertices = [1, 3, 2, 5, 4]; const edges = [ [0, 1], @@ -105,28 +105,28 @@ const edges = [ [3, 4], ]; const graph = new GraphAdjMat(vertices, edges); -console.log('\nAfter initialization, graph is'); +console.log('\nGraph after initialization:'); graph.print(); -/* Add edge */ -// Add vertex +/* Add an edge */ +// Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(0, 2); -console.log('\nAfter adding edge 1-2, graph is'); +console.log('\nGraph after adding edge 1-2:'); graph.print(); -/* Remove edge */ -// Vertices 1, 3 have indices 0, 1 respectively +/* Remove an edge */ +// Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(0, 1); -console.log('\nAfter removing edge 1-3, graph is'); +console.log('\nGraph after removing edge 1-3:'); graph.print(); -/* Add vertex */ +/* Add a vertex */ graph.addVertex(6); -console.log('\nAfter adding vertex 6, graph is'); +console.log('\nGraph after adding vertex 6:'); graph.print(); -/* Remove vertex */ +/* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(1); -console.log('\nAfter removing vertex 3, graph is'); +console.log('\nGraph after removing vertex 3:'); graph.print(); diff --git a/en/codes/javascript/chapter_graph/graph_bfs.js b/en/codes/javascript/chapter_graph/graph_bfs.js index 6330f571f..a66fe13fb 100644 --- a/en/codes/javascript/chapter_graph/graph_bfs.js +++ b/en/codes/javascript/chapter_graph/graph_bfs.js @@ -8,34 +8,34 @@ const { GraphAdjList } = require('./graph_adjacency_list'); const { Vertex } = require('../modules/Vertex'); /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex function graphBFS(graph, startVet) { // Vertex traversal sequence const res = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices const visited = new Set(); visited.add(startVet); - // Queue used to implement BFS + // Use a queue to implement BFS const que = [startVet]; - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited while (que.length) { const vet = que.shift(); // Dequeue the front vertex - res.push(vet); // Record visited vertex - // Traverse all adjacent vertices of this vertex + res.push(vet); // Record the visited vertex + // Traverse all neighbors of this vertex for (const adjVet of graph.adjList.get(vet) ?? []) { if (visited.has(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - que.push(adjVet); // Only enqueue unvisited vertices + que.push(adjVet); // Enqueue only unvisited vertices visited.add(adjVet); // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res; } -/* Driver Code */ -/* Add edge */ +/* Driver code */ +/* Initialize the undirected graph */ const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); const edges = [ [v[0], v[1]], @@ -52,10 +52,10 @@ const edges = [ [v[7], v[8]], ]; const graph = new GraphAdjList(edges); -console.log('\nAfter initialization, graph is'); +console.log('\nGraph after initialization:'); graph.print(); /* Breadth-first traversal */ const res = graphBFS(graph, v[0]); -console.log('\nBreadth-first traversal (BFS) vertex sequence is'); +console.log('\nVertex sequence from breadth-first traversal (BFS):'); console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/javascript/chapter_graph/graph_dfs.js b/en/codes/javascript/chapter_graph/graph_dfs.js index 70529e826..4748ffca6 100644 --- a/en/codes/javascript/chapter_graph/graph_dfs.js +++ b/en/codes/javascript/chapter_graph/graph_dfs.js @@ -8,33 +8,33 @@ const { Vertex } = require('../modules/Vertex'); const { GraphAdjList } = require('./graph_adjacency_list'); /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex function dfs(graph, visited, res, vet) { - res.push(vet); // Record visited vertex + res.push(vet); // Record the visited vertex visited.add(vet); // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for (const adjVet of graph.adjList.get(vet)) { if (visited.has(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex dfs(graph, visited, res, adjVet); } } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex function graphDFS(graph, startVet) { // Vertex traversal sequence const res = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices const visited = new Set(); dfs(graph, visited, res, startVet); return res; } -/* Driver Code */ -/* Add edge */ +/* Driver code */ +/* Initialize the undirected graph */ const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6]); const edges = [ [v[0], v[1]], @@ -45,10 +45,10 @@ const edges = [ [v[5], v[6]], ]; const graph = new GraphAdjList(edges); -console.log('\nAfter initialization, graph is'); +console.log('\nGraph after initialization:'); graph.print(); /* Depth-first traversal */ const res = graphDFS(graph, v[0]); -console.log('\nDepth-first traversal (DFS) vertex sequence is'); +console.log('\nVertex sequence from depth-first traversal (DFS):'); console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/javascript/chapter_greedy/coin_change_greedy.js b/en/codes/javascript/chapter_greedy/coin_change_greedy.js index 7a2e7d6b8..3bc2e6170 100644 --- a/en/codes/javascript/chapter_greedy/coin_change_greedy.js +++ b/en/codes/javascript/chapter_greedy/coin_change_greedy.js @@ -4,14 +4,14 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ function coinChangeGreedy(coins, amt) { - // Assume coins array is sorted + // Assume coins is sorted in ascending order let i = coins.length - 1; let count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (amt > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -19,30 +19,30 @@ function coinChangeGreedy(coins, amt) { amt -= coins[i]; count++; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt === 0 ? count : -1; } -/* Driver Code */ -// Greedy algorithm: Can guarantee finding the global optimal solution +/* Driver code */ +// Greedy algorithm: guaranteed to find a globally optimal solution let coins = [1, 5, 10, 20, 50, 100]; let amt = 186; let res = coinChangeGreedy(coins, amt); console.log(`\ncoins = ${coins}, amt = ${amt}`); -console.log(`Minimum coins needed to make ${amt} is ${res}`); +console.log(`Greedy coin count for amount ${amt}: ${res}`); -// Greedy algorithm: Cannot guarantee finding the global optimal solution +// Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 20, 50]; amt = 60; res = coinChangeGreedy(coins, amt); console.log(`\ncoins = ${coins}, amt = ${amt}`); -console.log(`Minimum coins needed to make ${amt} is ${res}`); -console.log('Actually the minimum number needed is 3, i.e., 20 + 20 + 20'); +console.log(`Greedy coin count for amount ${amt}: ${res}`); +console.log('The actual minimum is 3 coins: 20 + 20 + 20'); -// Greedy algorithm: Cannot guarantee finding the global optimal solution +// Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 49, 50]; amt = 98; res = coinChangeGreedy(coins, amt); console.log(`\ncoins = ${coins}, amt = ${amt}`); -console.log(`Minimum coins needed to make ${amt} is ${res}`); -console.log('Actually the minimum number needed is 2, i.e., 49 + 49'); +console.log(`Greedy coin count for amount ${amt}: ${res}`); +console.log('The actual minimum is 2 coins: 49 + 49'); diff --git a/en/codes/javascript/chapter_greedy/fractional_knapsack.js b/en/codes/javascript/chapter_greedy/fractional_knapsack.js index 2e794d2ff..039389a80 100644 --- a/en/codes/javascript/chapter_greedy/fractional_knapsack.js +++ b/en/codes/javascript/chapter_greedy/fractional_knapsack.js @@ -12,30 +12,30 @@ class Item { } } -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ function fractionalKnapsack(wgt, val, cap) { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value const items = wgt.map((w, i) => new Item(w, val[i])); - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order items.sort((a, b) => b.v / b.w - a.v / a.w); - // Loop for greedy selection + // Repeatedly make greedy choices let res = 0; for (const item of items) { if (item.w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v; cap -= item.w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += (item.v / item.w) * cap; - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break; } } return res; } -/* Driver Code */ +/* Driver code */ const wgt = [10, 20, 30, 40, 50]; const val = [50, 120, 150, 210, 240]; const cap = 50; @@ -43,4 +43,4 @@ const n = wgt.length; // Greedy algorithm const res = fractionalKnapsack(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum item value within the knapsack capacity: ${res}`); diff --git a/en/codes/javascript/chapter_greedy/max_capacity.js b/en/codes/javascript/chapter_greedy/max_capacity.js index 0982e2d65..1ad8ea27e 100644 --- a/en/codes/javascript/chapter_greedy/max_capacity.js +++ b/en/codes/javascript/chapter_greedy/max_capacity.js @@ -4,19 +4,19 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ function maxCapacity(ht) { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array let i = 0, j = ht.length - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 let res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity const cap = Math.min(ht[i], ht[j]) * (j - i); res = Math.max(res, cap); - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i += 1; } else { @@ -26,9 +26,9 @@ function maxCapacity(ht) { return res; } -/* Driver Code */ +/* Driver code */ const ht = [3, 8, 5, 2, 7, 7, 3, 4]; // Greedy algorithm const res = maxCapacity(ht); -console.log(`Maximum capacity is ${res}`); +console.log(`Maximum capacity: ${res}`); diff --git a/en/codes/javascript/chapter_greedy/max_product_cutting.js b/en/codes/javascript/chapter_greedy/max_product_cutting.js index c653d55b9..38e4eadf9 100644 --- a/en/codes/javascript/chapter_greedy/max_product_cutting.js +++ b/en/codes/javascript/chapter_greedy/max_product_cutting.js @@ -4,30 +4,30 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ function maxProductCutting(n) { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder let a = Math.floor(n / 3); let b = n % 3; if (b === 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return Math.pow(3, a - 1) * 2 * 2; } if (b === 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return Math.pow(3, a) * 2; } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return Math.pow(3, a); } -/* Driver Code */ +/* Driver code */ let n = 58; // Greedy algorithm let res = maxProductCutting(n); -console.log(`Maximum cutting product is ${res}`); +console.log(`Maximum product after splitting: ${res}`); diff --git a/en/codes/javascript/chapter_hashing/array_hash_map.js b/en/codes/javascript/chapter_hashing/array_hash_map.js index a39939b65..7e3d9df22 100644 --- a/en/codes/javascript/chapter_hashing/array_hash_map.js +++ b/en/codes/javascript/chapter_hashing/array_hash_map.js @@ -4,7 +4,7 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Key-value pair Number -> String */ +/* Key-value pair: Number -> String */ class Pair { constructor(key, val) { this.key = key; @@ -12,11 +12,11 @@ class Pair { } } -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { #buckets; constructor() { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets this.#buckets = new Array(100).fill(null); } @@ -25,7 +25,7 @@ class ArrayHashMap { return key % 100; } - /* Query operation */ + /* Lookup operation */ get(key) { let index = this.#hashFunc(key); let pair = this.#buckets[index]; @@ -33,7 +33,7 @@ class ArrayHashMap { return pair.val; } - /* Add operation */ + /* Insert operation */ set(key, val) { let index = this.#hashFunc(key); this.#buckets[index] = new Pair(key, val); @@ -42,7 +42,7 @@ class ArrayHashMap { /* Remove operation */ delete(key) { let index = this.#hashFunc(key); - // Set to null to represent deletion + // Set the bucket to null to mark it as deleted this.#buckets[index] = null; } @@ -79,7 +79,7 @@ class ArrayHashMap { return arr; } - /* Print hash table */ + /* Print the hash table */ print() { let pairSet = this.entries(); for (const pair of pairSet) { @@ -88,41 +88,41 @@ class ArrayHashMap { } } -/* Driver Code */ -/* Initialize hash table */ +/* Driver code */ +/* Initialize the hash table */ const map = new ArrayHashMap(); -/* Add operation */ -// Add key-value pair (key, value) to the hash table -map.set(12836, 'Xiao Ha'); -map.set(15937, 'Xiao Luo'); -map.set(16750, 'Xiao Suan'); -map.set(13276, 'Xiao Fa'); -map.set(10583, 'Xiao Ya'); -console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +/* Insert operation */ +// Insert the key-value pair (key, value) into the hash table +map.set(12836, '小哈'); +map.set(15937, '小啰'); +map.set(16750, '小算'); +map.set(13276, '小法'); +map.set(10583, '小鸭'); +console.info('\nHash table after insertion:\nKey -> Value'); map.print(); -/* Query operation */ -// Input key into hash table to get value +/* Lookup operation */ +// Look up key in the hash table to obtain value let name = map.get(15937); -console.info('\nInput student ID 15937, query name ' + name); +console.info('\nLook up student ID 15937 to obtain the name ' + name); /* Remove operation */ -// Remove key-value pair (key, value) from hash table +// Remove the key-value pair (key, value) from the hash table map.delete(10583); -console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +console.info('\nHash table after removing 10583:\nKey -> Value'); map.print(); -/* Traverse hash table */ -console.info('\nTraverse key-value pairs Key->Value'); +/* Traverse the hash table */ +console.info('\nTraverse key-value pairs: Key->Value'); for (const pair of map.entries()) { if (!pair) continue; console.info(pair.key + ' -> ' + pair.val); } -console.info('\nTraverse keys only Key'); +console.info('\nTraverse keys only: Key'); for (const key of map.keys()) { console.info(key); } -console.info('\nTraverse values only Value'); +console.info('\nTraverse values only: Value'); for (const val of map.values()) { console.info(val); } diff --git a/en/codes/javascript/chapter_hashing/hash_map.js b/en/codes/javascript/chapter_hashing/hash_map.js index 4933acb3d..9cb797f01 100644 --- a/en/codes/javascript/chapter_hashing/hash_map.js +++ b/en/codes/javascript/chapter_hashing/hash_map.js @@ -4,41 +4,41 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Driver Code */ -/* Initialize hash table */ +/* Driver code */ +/* Initialize the hash table */ const map = new Map(); -/* Add operation */ -// Add key-value pair (key, value) to the hash table -map.set(12836, 'Xiao Ha'); -map.set(15937, 'Xiao Luo'); -map.set(16750, 'Xiao Suan'); -map.set(13276, 'Xiao Fa'); -map.set(10583, 'Xiao Ya'); -console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +/* Insert operation */ +// Insert the key-value pair (key, value) into the hash table +map.set(12836, '小哈'); +map.set(15937, '小啰'); +map.set(16750, '小算'); +map.set(13276, '小法'); +map.set(10583, '小鸭'); +console.info('\nHash table after insertion:\nKey -> Value'); console.info(map); -/* Query operation */ -// Input key into hash table to get value +/* Lookup operation */ +// Look up key in the hash table to obtain value let name = map.get(15937); -console.info('\nInput student ID 15937, query name ' + name); +console.info('\nLook up student ID 15937 to obtain the name ' + name); /* Remove operation */ -// Remove key-value pair (key, value) from hash table +// Remove the key-value pair (key, value) from the hash table map.delete(10583); -console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +console.info('\nHash table after removing 10583:\nKey -> Value'); console.info(map); -/* Traverse hash table */ -console.info('\nTraverse key-value pairs Key->Value'); +/* Traverse the hash table */ +console.info('\nTraverse key-value pairs: Key->Value'); for (const [k, v] of map.entries()) { console.info(k + ' -> ' + v); } -console.info('\nTraverse keys only Key'); +console.info('\nTraverse keys only: Key'); for (const k of map.keys()) { console.info(k); } -console.info('\nTraverse values only Value'); +console.info('\nTraverse values only: Value'); for (const v of map.values()) { console.info(v); } diff --git a/en/codes/javascript/chapter_hashing/hash_map_chaining.js b/en/codes/javascript/chapter_hashing/hash_map_chaining.js index f4e092cbc..33b889a93 100644 --- a/en/codes/javascript/chapter_hashing/hash_map_chaining.js +++ b/en/codes/javascript/chapter_hashing/hash_map_chaining.js @@ -4,7 +4,7 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Key-value pair Number -> String */ +/* Key-value pair: Number -> String */ class Pair { constructor(key, val) { this.key = key; @@ -16,8 +16,8 @@ class Pair { class HashMapChaining { #size; // Number of key-value pairs #capacity; // Hash table capacity - #loadThres; // Load factor threshold for triggering expansion - #extendRatio; // Expansion multiplier + #loadThres; // Load factor threshold that triggers capacity growth + #extendRatio; // Capacity growth factor #buckets; // Bucket array /* Constructor */ @@ -39,36 +39,36 @@ class HashMapChaining { return this.#size / this.#capacity; } - /* Query operation */ + /* Lookup operation */ get(key) { const index = this.#hashFunc(key); const bucket = this.#buckets[index]; - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for (const pair of bucket) { if (pair.key === key) { return pair.val; } } - // If key is not found, return null + // Return null if key was not found return null; } - /* Add operation */ + /* Insert operation */ put(key, val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (this.#loadFactor() > this.#loadThres) { this.#extend(); } const index = this.#hashFunc(key); const bucket = this.#buckets[index]; - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for (const pair of bucket) { if (pair.key === key) { pair.val = val; return; } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end const pair = new Pair(key, val); bucket.push(pair); this.#size++; @@ -78,7 +78,7 @@ class HashMapChaining { remove(key) { const index = this.#hashFunc(key); let bucket = this.#buckets[index]; - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (let i = 0; i < bucket.length; i++) { if (bucket[i].key === key) { bucket.splice(i, 1); @@ -88,15 +88,15 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ #extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table const bucketsTmp = this.#buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity this.#capacity *= this.#extendRatio; this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); this.#size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (const bucket of bucketsTmp) { for (const pair of bucket) { this.put(pair.key, pair.val); @@ -104,7 +104,7 @@ class HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ print() { for (const bucket of this.#buckets) { let res = []; @@ -116,27 +116,27 @@ class HashMapChaining { } } -/* Driver Code */ -/* Initialize hash table */ +/* Driver code */ +/* Initialize the hash table */ const map = new HashMapChaining(); -/* Add operation */ -// Add key-value pair (key, value) to the hash table -map.put(12836, 'Xiao Ha'); -map.put(15937, 'Xiao Luo'); -map.put(16750, 'Xiao Suan'); -map.put(13276, 'Xiao Fa'); -map.put(10583, 'Xiao Ya'); -console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +/* Insert operation */ +// Insert the key-value pair (key, value) into the hash table +map.put(12836, '小哈'); +map.put(15937, '小啰'); +map.put(16750, '小算'); +map.put(13276, '小法'); +map.put(10583, '小鸭'); +console.log('\nHash table after insertion:\nKey -> Value'); map.print(); -/* Query operation */ -// Input key into hash table to get value +/* Lookup operation */ +// Look up key in the hash table to obtain value const name = map.get(13276); -console.log('\nInput student ID 13276, query name ' + name); +console.log('\nLook up student ID 13276 to obtain the name ' + name); /* Remove operation */ -// Remove key-value pair (key, value) from hash table +// Remove the key-value pair (key, value) from the hash table map.remove(12836); -console.log('\nAfter removing 12836, hash table is\nKey -> Value'); +console.log('\nHash table after removing 12836:\nKey -> Value'); map.print(); diff --git a/en/codes/javascript/chapter_hashing/hash_map_open_addressing.js b/en/codes/javascript/chapter_hashing/hash_map_open_addressing.js index 44cf433e5..d8a935c14 100644 --- a/en/codes/javascript/chapter_hashing/hash_map_open_addressing.js +++ b/en/codes/javascript/chapter_hashing/hash_map_open_addressing.js @@ -4,7 +4,7 @@ * Author: yuan0221 (yl1452491917@gmail.com), krahets (krahets@163.com) */ -/* Key-value pair Number -> String */ +/* Key-value pair: Number -> String */ class Pair { constructor(key, val) { this.key = key; @@ -16,19 +16,19 @@ class Pair { class HashMapOpenAddressing { #size; // Number of key-value pairs #capacity; // Hash table capacity - #loadThres; // Load factor threshold for triggering expansion - #extendRatio; // Expansion multiplier + #loadThres; // Load factor threshold that triggers capacity growth + #extendRatio; // Capacity growth factor #buckets; // Bucket array - #TOMBSTONE; // Removal marker + #TOMBSTONE; // Tombstone marker /* Constructor */ constructor() { this.#size = 0; // Number of key-value pairs this.#capacity = 4; // Hash table capacity - this.#loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion - this.#extendRatio = 2; // Expansion multiplier + this.#loadThres = 2.0 / 3.0; // Load factor threshold that triggers capacity growth + this.#extendRatio = 2; // Capacity growth factor this.#buckets = Array(this.#capacity).fill(null); // Bucket array - this.#TOMBSTONE = new Pair(-1, '-1'); // Removal marker + this.#TOMBSTONE = new Pair(-1, '-1'); // Tombstone marker } /* Hash function */ @@ -41,60 +41,60 @@ class HashMapOpenAddressing { return this.#size / this.#capacity; } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ #findBucket(key) { let index = this.#hashFunc(key); let firstTombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (this.#buckets[index] !== null) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (this.#buckets[index].key === key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone !== -1) { this.#buckets[firstTombstone] = this.#buckets[index]; this.#buckets[index] = this.#TOMBSTONE; - return firstTombstone; // Return the moved bucket index + return firstTombstone; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if ( firstTombstone === -1 && this.#buckets[index] === this.#TOMBSTONE ) { firstTombstone = index; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % this.#capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone === -1 ? index : firstTombstone; } - /* Query operation */ + /* Lookup operation */ get(key) { - // Search for bucket index corresponding to key + // Find the bucket index for key const index = this.#findBucket(key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if ( this.#buckets[index] !== null && this.#buckets[index] !== this.#TOMBSTONE ) { return this.#buckets[index].val; } - // If key-value pair does not exist, return null + // Return null if the key-value pair does not exist return null; } - /* Add operation */ + /* Insert operation */ put(key, val) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (this.#loadFactor() > this.#loadThres) { this.#extend(); } - // Search for bucket index corresponding to key + // Find the bucket index for key const index = this.#findBucket(key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if ( this.#buckets[index] !== null && this.#buckets[index] !== this.#TOMBSTONE @@ -102,16 +102,16 @@ class HashMapOpenAddressing { this.#buckets[index].val = val; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it this.#buckets[index] = new Pair(key, val); this.#size++; } /* Remove operation */ remove(key) { - // Search for bucket index corresponding to key + // Find the bucket index for key const index = this.#findBucket(key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if ( this.#buckets[index] !== null && this.#buckets[index] !== this.#TOMBSTONE @@ -121,15 +121,15 @@ class HashMapOpenAddressing { } } - /* Expand hash table */ + /* Grow the hash table capacity */ #extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table const bucketsTmp = this.#buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity this.#capacity *= this.#extendRatio; this.#buckets = Array(this.#capacity).fill(null); this.#size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (const pair of bucketsTmp) { if (pair !== null && pair !== this.#TOMBSTONE) { this.put(pair.key, pair.val); @@ -137,7 +137,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ print() { for (const pair of this.#buckets) { if (pair === null) { @@ -151,27 +151,27 @@ class HashMapOpenAddressing { } } -/* Driver Code */ -// Initialize hash table +/* Driver code */ +// Initialize the hash table const hashmap = new HashMapOpenAddressing(); -// Add operation -// Add key-value pair (key, val) to the hash table -hashmap.put(12836, 'Xiao Ha'); -hashmap.put(15937, 'Xiao Luo'); -hashmap.put(16750, 'Xiao Suan'); -hashmap.put(13276, 'Xiao Fa'); -hashmap.put(10583, 'Xiao Ya'); -console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +// Insert operation +// Insert the key-value pair (key, val) into the hash table +hashmap.put(12836, '小哈'); +hashmap.put(15937, '小啰'); +hashmap.put(16750, '小算'); +hashmap.put(13276, '小法'); +hashmap.put(10583, '小鸭'); +console.log('\nHash table after insertion:\nKey -> Value'); hashmap.print(); -// Query operation -// Input key into hash table to get value val +// Lookup operation +// Look up key in the hash table to obtain val const name = hashmap.get(13276); -console.log('\nInput student ID 13276, query name ' + name); +console.log('\nLook up student ID 13276 to obtain the name ' + name); // Remove operation -// Remove key-value pair (key, val) from hash table +// Remove the key-value pair (key, val) from the hash table hashmap.remove(16750); -console.log('\nAfter removing 16750, hash table is\nKey -> Value'); +console.log('\nHash table after removing 16750:\nKey -> Value'); hashmap.print(); diff --git a/en/codes/javascript/chapter_hashing/simple_hash.js b/en/codes/javascript/chapter_hashing/simple_hash.js index 9fc68b4fc..a45950943 100644 --- a/en/codes/javascript/chapter_hashing/simple_hash.js +++ b/en/codes/javascript/chapter_hashing/simple_hash.js @@ -34,7 +34,7 @@ function xorHash(key) { return hash % MODULUS; } -/* Rotational hash */ +/* Rotating hash */ function rotHash(key) { let hash = 0; const MODULUS = 1000000007; @@ -44,17 +44,17 @@ function rotHash(key) { return hash; } -/* Driver Code */ -const key = 'Hello Algo'; +/* Driver code */ +const key = 'Hello 算法'; let hash = addHash(key); -console.log('Additive hash value is ' + hash); +console.log('Additive hash value: ' + hash); hash = mulHash(key); -console.log('Multiplicative hash value is ' + hash); +console.log('Multiplicative hash value: ' + hash); hash = xorHash(key); -console.log('XOR hash value is ' + hash); +console.log('XOR hash value: ' + hash); hash = rotHash(key); -console.log('Rotational hash value is ' + hash); +console.log('Rotating hash value: ' + hash); diff --git a/en/codes/javascript/chapter_heap/my_heap.js b/en/codes/javascript/chapter_heap/my_heap.js index d5c6f0ea6..6727590e3 100644 --- a/en/codes/javascript/chapter_heap/my_heap.js +++ b/en/codes/javascript/chapter_heap/my_heap.js @@ -10,27 +10,27 @@ const { printHeap } = require('../modules/PrintUtil'); class MaxHeap { #maxHeap; - /* Constructor, build empty heap or build heap from input list */ + /* Constructor: create an empty heap or build a heap from the input list */ constructor(nums) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order this.#maxHeap = nums === undefined ? [] : [...nums]; - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for (let i = this.#parent(this.size() - 1); i >= 0; i--) { this.#siftDown(i); } } - /* Get index of left child node */ + /* Get the left child index */ #left(i) { return 2 * i + 1; } - /* Get index of right child node */ + /* Get the right child index */ #right(i) { return 2 * i + 2; } - /* Get index of parent node */ + /* Get the parent index */ #parent(i) { return Math.floor((i - 1) / 2); // Floor division } @@ -42,115 +42,115 @@ class MaxHeap { this.#maxHeap[j] = tmp; } - /* Get heap size */ + /* Get the heap size */ size() { return this.#maxHeap.length; } - /* Check if heap is empty */ + /* Check whether the heap is empty */ isEmpty() { return this.size() === 0; } - /* Access top element */ + /* Access the top element of the heap */ peek() { return this.#maxHeap[0]; } - /* Element enters heap */ + /* Push an element onto the heap */ push(val) { - // Add node + // Add a node this.#maxHeap.push(val); - // Heapify from bottom to top + // Sift up this.#siftUp(this.size() - 1); } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ #siftUp(i) { while (true) { - // Get parent node of node i + // Get the parent of node i const p = this.#parent(i); - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) break; - // Swap two nodes + // Swap the two nodes this.#swap(i, p); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Element exits heap */ + /* Pop an element from the heap */ pop() { - // Handle empty case + // Handle an empty heap if (this.isEmpty()) throw new Error('Heap is empty'); - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) this.#swap(0, this.size() - 1); - // Remove node + // Remove a node const val = this.#maxHeap.pop(); - // Return top element + // Sift down this.#siftDown(0); - // Return heap top element + // Return the top element of the heap return val; } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ #siftDown(i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma const l = this.#left(i), r = this.#right(i); let ma = i; if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[ma]) ma = l; if (r < this.size() && this.#maxHeap[r] > this.#maxHeap[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma === i) break; - // Swap two nodes + // Swap the two nodes this.#swap(i, ma); - // Loop downwards heapification + // Continue sifting down i = ma; } } - /* Driver Code */ + /* Print the heap (binary tree) */ print() { printHeap(this.#maxHeap); } - /* Extract elements from heap */ + /* Get the elements in the heap */ getMaxHeap() { return this.#maxHeap; } } -/* Driver Code */ +/* Driver code */ if (require.main === module) { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ const maxHeap = new MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); - console.log('\nAfter inputting list and building heap'); + console.log('\nAfter building a heap from the input list:'); maxHeap.print(); - /* Check if heap is empty */ + /* Get the top element of the heap */ let peek = maxHeap.peek(); - console.log(`\nHeap top element is ${peek}`); + console.log(`\nHeap top: ${peek}`); - /* Element enters heap */ + /* Push an element onto the heap */ let val = 7; maxHeap.push(val); - console.log(`\nAfter element ${val} pushes to heap`); + console.log(`\nAfter pushing ${val} into the heap:`); maxHeap.print(); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.pop(); - console.log(`\nAfter heap top element ${peek} pops from heap`); + console.log(`\nAfter popping the heap top ${peek}:`); maxHeap.print(); - /* Get heap size */ + /* Get the heap size */ let size = maxHeap.size(); - console.log(`\nHeap size is ${size}`); + console.log(`\nNumber of elements in the heap: ${size}`); - /* Check if heap is empty */ + /* Check whether the heap is empty */ let isEmpty = maxHeap.isEmpty(); - console.log(`\nIs heap empty ${isEmpty}`); + console.log(`\nIs the heap empty? ${isEmpty}`); } module.exports = { diff --git a/en/codes/javascript/chapter_heap/top_k.js b/en/codes/javascript/chapter_heap/top_k.js index f34e5c2ee..71495c1db 100644 --- a/en/codes/javascript/chapter_heap/top_k.js +++ b/en/codes/javascript/chapter_heap/top_k.js @@ -6,52 +6,52 @@ const { MaxHeap } = require('./my_heap'); -/* Element enters heap */ +/* Push an element onto the heap */ function pushMinHeap(maxHeap, val) { - // Negate element + // Negate the value maxHeap.push(-val); } -/* Element exits heap */ +/* Pop an element from the heap */ function popMinHeap(maxHeap) { - // Negate element + // Negate the value return -maxHeap.pop(); } -/* Access top element */ +/* Access the top element of the heap */ function peekMinHeap(maxHeap) { - // Negate element + // Negate the value return -maxHeap.peek(); } -/* Extract elements from heap */ +/* Get the elements in the heap */ function getMinHeap(maxHeap) { - // Negate element + // Negate the value return maxHeap.getMaxHeap().map((num) => -num); } -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ function topKHeap(nums, k) { - // Python's heapq module implements min heap by default - // Note: We negate all heap elements to simulate min heap using max heap + // Initialize a min heap + // Note: negate all values in the heap to simulate a min heap with a max heap const maxHeap = new MaxHeap([]); - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for (let i = 0; i < k; i++) { pushMinHeap(maxHeap, nums[i]); } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for (let i = k; i < nums.length; i++) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if (nums[i] > peekMinHeap(maxHeap)) { popMinHeap(maxHeap); pushMinHeap(maxHeap, nums[i]); } } - // Return elements in heap + // Return the elements in the heap return getMinHeap(maxHeap); } -/* Driver Code */ +/* Driver code */ const nums = [1, 7, 6, 3, 2]; const k = 3; const res = topKHeap(nums, k); diff --git a/en/codes/javascript/chapter_searching/binary_search.js b/en/codes/javascript/chapter_searching/binary_search.js index 285245407..f47d0f08d 100644 --- a/en/codes/javascript/chapter_searching/binary_search.js +++ b/en/codes/javascript/chapter_searching/binary_search.js @@ -4,57 +4,57 @@ * Author: JoseHung (szhong@link.cuhk.edu.hk) */ -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ function binarySearch(nums, target) { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements let i = 0, j = nums.length - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { - // Calculate midpoint index m, use parseInt() to round down + // Calculate midpoint index m, using parseInt() to round down const m = parseInt(i + (j - i) / 2); if (nums[m] < target) - // This means target is in the interval [m+1, j] + // In this case, target must be in [m+1, j] if it exists i = m + 1; else if (nums[m] > target) - // This means target is in the interval [i, m-1] + // In this case, target must be in [i, m-1] if it exists j = m - 1; - else return m; // Found the target element, return its index + else return m; // The target element was found; return its index } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ function binarySearchLCRO(nums, target) { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last let i = 0, j = nums.length; - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { - // Calculate midpoint index m, use parseInt() to round down + // Calculate midpoint index m, using parseInt() to round down const m = parseInt(i + (j - i) / 2); if (nums[m] < target) - // This means target is in the interval [m+1, j) + // In this case, target must be in [m+1, j) if it exists i = m + 1; else if (nums[m] > target) - // This means target is in the interval [i, m) + // In this case, target must be in [i, m) if it exists j = m; - // Found the target element, return its index + // The target element was found; return its index else return m; } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Driver Code */ +/* Driver code */ const target = 6; const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ let index = binarySearch(nums, target); console.log('Index of target element 6 = ' + index); -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums, target); console.log('Index of target element 6 = ' + index); diff --git a/en/codes/javascript/chapter_searching/binary_search_edge.js b/en/codes/javascript/chapter_searching/binary_search_edge.js index 2a9886c81..914b9a140 100644 --- a/en/codes/javascript/chapter_searching/binary_search_edge.js +++ b/en/codes/javascript/chapter_searching/binary_search_edge.js @@ -6,40 +6,40 @@ const { binarySearchInsertion } = require('./binary_search_insertion.js'); -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ function binarySearchLeftEdge(nums, target) { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target const i = binarySearchInsertion(nums, target); - // Target not found, return -1 + // If target was not found, return -1 if (i === nums.length || nums[i] !== target) { return -1; } - // Found target, return index i + // Target was found; return index i return i; } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ function binarySearchRightEdge(nums, target) { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 const i = binarySearchInsertion(nums, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // j points to the rightmost target, while i points to the first element greater than target const j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if (j === -1 || nums[j] !== target) { return -1; } - // Found target, return index j + // Target was found; return index j return j; } -/* Driver Code */ -// Array with duplicate elements +/* Driver code */ +// Array containing duplicate elements const nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; console.log('\nArray nums = ' + nums); -// Binary search left and right boundaries +// Find the left and right boundaries using binary search for (const target of [6, 7]) { let index = binarySearchLeftEdge(nums, target); - console.log('Leftmost element ' + target + ' has index ' + index); + console.log('Leftmost occurrence of ' + target + ' has index ' + index); index = binarySearchRightEdge(nums, target); - console.log('Rightmost element ' + target + ' has index ' + index); + console.log('Rightmost occurrence of ' + target + ' has index ' + index); } diff --git a/en/codes/javascript/chapter_searching/binary_search_insertion.js b/en/codes/javascript/chapter_searching/binary_search_insertion.js index 17a149ae2..c4be595f3 100644 --- a/en/codes/javascript/chapter_searching/binary_search_insertion.js +++ b/en/codes/javascript/chapter_searching/binary_search_insertion.js @@ -4,59 +4,59 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ function binarySearchInsertionSimple(nums, target) { let i = 0, - j = nums.length - 1; // Initialize closed interval [0, n-1] + j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, using Math.floor() to round down if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - return m; // Found target, return insertion point m + return m; // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i; } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ function binarySearchInsertion(nums, target) { let i = 0, - j = nums.length - 1; // Initialize closed interval [0, n-1] + j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, using Math.floor() to round down if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i return i; } -/* Driver Code */ -// Array without duplicate elements +/* Driver code */ +// Array with no duplicate elements let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; console.log('\nArray nums = ' + nums); -// Binary search for insertion point +// Find an insertion point using binary search for (const target of [6, 9]) { const index = binarySearchInsertionSimple(nums, target); - console.log('Element ' + target + "'s insertion point index is " + index); + console.log('Element ' + target + ' has insertion index ' + index); } -// Array with duplicate elements +// Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; console.log('\nArray nums = ' + nums); -// Binary search for insertion point +// Find an insertion point using binary search for (const target of [2, 6, 20]) { const index = binarySearchInsertion(nums, target); - console.log('Element ' + target + "'s insertion point index is " + index); + console.log('Element ' + target + ' has insertion index ' + index); } module.exports = { diff --git a/en/codes/javascript/chapter_searching/hashing_search.js b/en/codes/javascript/chapter_searching/hashing_search.js index 1d02aca98..f953d3a25 100644 --- a/en/codes/javascript/chapter_searching/hashing_search.js +++ b/en/codes/javascript/chapter_searching/hashing_search.js @@ -6,40 +6,40 @@ const { arrToLinkedList } = require('../modules/ListNode'); -/* Hash search (array) */ +/* Hash-based search (array) */ function hashingSearchArray(map, target) { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key return map.has(target) ? map.get(target) : -1; } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ function hashingSearchLinkedList(map, target) { - // Hash table key: target node value, value: node object - // If key is not in hash table, return null + // Hash table key: target node value; value: node object + // Return null if this key is absent from the hash table return map.has(target) ? map.get(target) : null; } -/* Driver Code */ +/* Driver code */ const target = 3; -/* Hash search (array) */ +/* Hash-based search (array) */ const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; -// Initialize hash table +// Initialize the hash table const map = new Map(); for (let i = 0; i < nums.length; i++) { - map.set(nums[i], i); // key: element, value: index + map.set(nums[i], i); // key: element; value: index } const index = hashingSearchArray(map, target); console.log('Index of target element 3 = ' + index); -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ let head = arrToLinkedList(nums); -// Initialize hash table +// Initialize the hash table const map1 = new Map(); while (head != null) { - map1.set(head.val, head); // key: node value, value: node + map1.set(head.val, head); // key: node value; value: node head = head.next; } const node = hashingSearchLinkedList(map1, target); -console.log('Node object with target value 3 is', node); +console.log('The node object corresponding to target value 3 is', node); diff --git a/en/codes/javascript/chapter_searching/linear_search.js b/en/codes/javascript/chapter_searching/linear_search.js index e8b07f560..9faf7f051 100644 --- a/en/codes/javascript/chapter_searching/linear_search.js +++ b/en/codes/javascript/chapter_searching/linear_search.js @@ -8,40 +8,40 @@ const { ListNode, arrToLinkedList } = require('../modules/ListNode'); /* Linear search (array) */ function linearSearchArray(nums, target) { - // Traverse array + // Traverse the array for (let i = 0; i < nums.length; i++) { - // Found the target element, return its index + // The target element was found; return its index if (nums[i] === target) { return i; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Linear search (linked list) */ +/* Linear search (linked list)*/ function linearSearchLinkedList(head, target) { // Traverse the linked list while (head) { - // Found the target node, return it + // The target node was found; return it if (head.val === target) { return head; } head = head.next; } - // Target node not found, return null + // Target node not found; return null return null; } -/* Driver Code */ +/* Driver code */ const target = 3; -/* Perform linear search in array */ +/* Perform linear search on the array */ const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; const index = linearSearchArray(nums, target); console.log('Index of target element 3 = ' + index); -/* Perform linear search in linked list */ +/* Perform linear search on the linked list */ const head = arrToLinkedList(nums); const node = linearSearchLinkedList(head, target); -console.log('Node object corresponding to target node value 3 is ', node); +console.log('The node object corresponding to target value 3 is ', node); diff --git a/en/codes/javascript/chapter_searching/two_sum.js b/en/codes/javascript/chapter_searching/two_sum.js index 5dc6fb40c..7c3f0da03 100644 --- a/en/codes/javascript/chapter_searching/two_sum.js +++ b/en/codes/javascript/chapter_searching/two_sum.js @@ -4,10 +4,10 @@ * Author: gyt95 (gytkwan@gmail.com) */ -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ function twoSumBruteForce(nums, target) { const n = nums.length; - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for (let i = 0; i < n; i++) { for (let j = i + 1; j < n; j++) { if (nums[i] + nums[j] === target) { @@ -18,11 +18,11 @@ function twoSumBruteForce(nums, target) { return []; } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ function twoSumHashTable(nums, target) { - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space let m = {}; - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (let i = 0; i < nums.length; i++) { if (m[target - nums[i]] !== undefined) { return [m[target - nums[i]], i]; @@ -33,14 +33,14 @@ function twoSumHashTable(nums, target) { return []; } -/* Driver Code */ +/* Driver code */ // Method 1 const nums = [2, 7, 11, 15], target = 13; let res = twoSumBruteForce(nums, target); -console.log('Method 1 res = ', res); +console.log('Method 1: res = ', res); // Method 2 res = twoSumHashTable(nums, target); -console.log('Method 2 res = ', res); +console.log('Method 2: res = ', res); diff --git a/en/codes/javascript/chapter_sorting/bubble_sort.js b/en/codes/javascript/chapter_sorting/bubble_sort.js index 71fa9a806..fab7b200f 100644 --- a/en/codes/javascript/chapter_sorting/bubble_sort.js +++ b/en/codes/javascript/chapter_sorting/bubble_sort.js @@ -6,9 +6,9 @@ /* Bubble sort */ function bubbleSort(nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (let i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] @@ -20,30 +20,30 @@ function bubbleSort(nums) { } } -/* Bubble sort (flag optimization) */ +/* Bubble sort (flag optimization)*/ function bubbleSortWithFlag(nums) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (let i = nums.length - 1; i > 0; i--) { - let flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + let flag = false; // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] let tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - flag = true; // Record element swap + flag = true; // Record that a swap occurred } } - if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + if (!flag) break; // Exit immediately if this pass made no swaps } } -/* Driver Code */ +/* Driver code */ const nums = [4, 1, 3, 1, 5, 2]; bubbleSort(nums); -console.log('After bubble sort, nums =', nums); +console.log('After bubble sort, array =', nums); const nums1 = [4, 1, 3, 1, 5, 2]; bubbleSortWithFlag(nums1); -console.log('After bubble sort, nums =', nums1); +console.log('After bubble sort, array =', nums1); diff --git a/en/codes/javascript/chapter_sorting/bucket_sort.js b/en/codes/javascript/chapter_sorting/bucket_sort.js index dbbd9d04d..6729d42c9 100644 --- a/en/codes/javascript/chapter_sorting/bucket_sort.js +++ b/en/codes/javascript/chapter_sorting/bucket_sort.js @@ -6,25 +6,25 @@ /* Bucket sort */ function bucketSort(nums) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize ceil(n/2) buckets, aiming for about 2 elements per bucket const k = nums.length / 2; const buckets = []; for (let i = 0; i < k; i++) { buckets.push([]); } - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for (const num of nums) { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); floor(num * k) maps values to indices from 0 to ceil(k)-1 const i = Math.floor(num * k); // Add num to bucket i buckets[i].push(num); } // 2. Sort each bucket for (const bucket of buckets) { - // Use built-in sorting function, can also replace with other sorting algorithms + // Use the built-in sorting function, or replace it with another sorting algorithm bucket.sort((a, b) => a - b); } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results let i = 0; for (const bucket of buckets) { for (const num of bucket) { @@ -33,7 +33,7 @@ function bucketSort(nums) { } } -/* Driver Code */ +/* Driver code */ const nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; bucketSort(nums); console.log('After bucket sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/counting_sort.js b/en/codes/javascript/chapter_sorting/counting_sort.js index 18c93641f..eb18ffe8f 100644 --- a/en/codes/javascript/chapter_sorting/counting_sort.js +++ b/en/codes/javascript/chapter_sorting/counting_sort.js @@ -5,17 +5,17 @@ */ /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects function countingSortNaive(nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m = Math.max(...nums); - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num const counter = new Array(m + 1).fill(0); for (const num of nums) { counter[num]++; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements let i = 0; for (let num = 0; num < m + 1; num++) { for (let j = 0; j < counter[num]; j++, i++) { @@ -25,37 +25,37 @@ function countingSortNaive(nums) { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Full implementation; stable and adaptable to sorting objects function countingSort(nums) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m = Math.max(...nums); - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num const counter = new Array(m + 1).fill(0); for (const num of nums) { counter[num]++; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for (let i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result const n = nums.length; const res = new Array(n); for (let i = n - 1; i >= 0; i--) { const num = nums[i]; res[counter[num] - 1] = num; // Place num at the corresponding index - counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + counter[num]--; // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res for (let i = 0; i < n; i++) { nums[i] = res[i]; } } -/* Driver Code */ +/* Driver code */ const nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; countingSortNaive(nums); console.log('After counting sort (cannot sort objects), nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/heap_sort.js b/en/codes/javascript/chapter_sorting/heap_sort.js index 3d9db9ab3..8fe28d178 100644 --- a/en/codes/javascript/chapter_sorting/heap_sort.js +++ b/en/codes/javascript/chapter_sorting/heap_sort.js @@ -4,10 +4,10 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ function siftDown(nums, n, i) { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma let l = 2 * i + 1; let r = 2 * i + 2; let ma = i; @@ -17,33 +17,33 @@ function siftDown(nums, n, i) { if (r < n && nums[r] > nums[ma]) { ma = r; } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma === i) { break; } - // Swap two nodes + // Swap the two nodes [nums[i], nums[ma]] = [nums[ma], nums[i]]; - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ function heapSort(nums) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) { siftDown(nums, nums.length, i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (let i = nums.length - 1; i > 0; i--) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) [nums[0], nums[i]] = [nums[i], nums[0]]; - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0); } } -/* Driver Code */ +/* Driver code */ const nums = [4, 1, 3, 1, 5, 2]; heapSort(nums); console.log('After heap sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/insertion_sort.js b/en/codes/javascript/chapter_sorting/insertion_sort.js index 40d3f494d..3ae1086b2 100644 --- a/en/codes/javascript/chapter_sorting/insertion_sort.js +++ b/en/codes/javascript/chapter_sorting/insertion_sort.js @@ -6,20 +6,20 @@ /* Insertion sort */ function insertionSort(nums) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for (let i = 1; i < nums.length; i++) { let base = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + nums[j + 1] = nums[j]; // Shift nums[j] one position to the right j--; } - nums[j + 1] = base; // Assign base to the correct position + nums[j + 1] = base; // Place base at the correct position } } -/* Driver Code */ +/* Driver code */ const nums = [4, 1, 3, 1, 5, 2]; insertionSort(nums); console.log('After insertion sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/merge_sort.js b/en/codes/javascript/chapter_sorting/merge_sort.js index b5df2ced3..2d5055a68 100644 --- a/en/codes/javascript/chapter_sorting/merge_sort.js +++ b/en/codes/javascript/chapter_sorting/merge_sort.js @@ -4,16 +4,16 @@ * Author: IsChristina (christinaxia77@foxmail.com) */ -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ function merge(nums, left, mid, right) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result const tmp = new Array(right - left + 1); - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays let i = left, j = mid + 1, k = 0; - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { tmp[k++] = nums[i++]; @@ -28,7 +28,7 @@ function merge(nums, left, mid, right) { while (j <= right) { tmp[k++] = nums[j++]; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (k = 0; k < tmp.length; k++) { nums[left + k] = tmp[k]; } @@ -36,17 +36,17 @@ function merge(nums, left, mid, right) { /* Merge sort */ function mergeSort(nums, left, right) { - // Termination condition - if (left >= right) return; // Terminate recursion when subarray length is 1 - // Divide and conquer stage - let mid = Math.floor(left + (right - left) / 2); // Calculate midpoint - mergeSort(nums, left, mid); // Recursively process the left subarray - mergeSort(nums, mid + 1, right); // Recursively process the right subarray - // Merge stage + // Base case + if (left >= right) return; // Stop recursing when the subarray has at most one element + // Divide phase + let mid = Math.floor(left + (right - left) / 2); // Calculate the midpoint + mergeSort(nums, left, mid); // Recurse on the left subarray + mergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase merge(nums, left, mid, right); } -/* Driver Code */ +/* Driver code */ const nums = [7, 3, 2, 6, 0, 1, 5, 4]; mergeSort(nums, 0, nums.length - 1); console.log('After merge sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/quick_sort.js b/en/codes/javascript/chapter_sorting/quick_sort.js index 9d8e3b106..1a73434e0 100644 --- a/en/codes/javascript/chapter_sorting/quick_sort.js +++ b/en/codes/javascript/chapter_sorting/quick_sort.js @@ -6,39 +6,39 @@ /* Quick sort class */ class QuickSort { - /* Swap elements */ + /* Swap the elements */ swap(nums, i, j) { let tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ partition(nums, left, right) { // Use nums[left] as the pivot let i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) { - j -= 1; // Search from right to left for the first element smaller than the pivot + j -= 1; // Scan from right to left for the first element smaller than the pivot } while (i < j && nums[i] <= nums[left]) { - i += 1; // Search from left to right for the first element greater than the pivot + i += 1; // Scan from left to right for the first element larger than the pivot } - // Swap elements + // Swap the elements this.swap(nums, i, j); // Swap these two elements } - this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + this.swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ quickSort(nums, left, right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning const pivot = this.partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays this.quickSort(nums, left, pivot - 1); this.quickSort(nums, pivot + 1, right); } @@ -46,7 +46,7 @@ class QuickSort { /* Quick sort class (median pivot optimization) */ class QuickSortMedian { - /* Swap elements */ + /* Swap the elements */ swap(nums, i, j) { let tmp = nums[i]; nums[i] = nums[j]; @@ -58,14 +58,14 @@ class QuickSortMedian { let l = nums[left], m = nums[mid], r = nums[right]; - // m is between l and r + // m lies between l and r if ((l <= m && m <= r) || (r <= m && m <= l)) return mid; - // l is between m and r + // l lies between m and r if ((m <= l && l <= r) || (r <= l && l <= m)) return left; return right; } - /* Sentinel partition (median of three) */ + /* Two-pointer partitioning (median of three) */ partition(nums, left, right) { // Select the median of three candidate elements let med = this.medianThree( @@ -74,27 +74,27 @@ class QuickSortMedian { Math.floor((left + right) / 2), right ); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray this.swap(nums, left, med); // Use nums[left] as the pivot let i = left, j = right; while (i < j) { - while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot - while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + while (i < j && nums[j] >= nums[left]) j--; // Scan from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Scan from left to right for the first element larger than the pivot this.swap(nums, i, j); // Swap these two elements } - this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + this.swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ quickSort(nums, left, right) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return; - // Sentinel partition + // Two-pointer partitioning const pivot = this.partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays this.quickSort(nums, left, pivot - 1); this.quickSort(nums, pivot + 1, right); } @@ -102,60 +102,60 @@ class QuickSortMedian { /* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { - /* Swap elements */ + /* Swap the elements */ swap(nums, i, j) { let tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ partition(nums, left, right) { // Use nums[left] as the pivot let i = left, j = right; while (i < j) { - while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot - while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + while (i < j && nums[j] >= nums[left]) j--; // Scan from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Scan from left to right for the first element larger than the pivot this.swap(nums, i, j); // Swap these two elements } - this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + this.swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort (recursion depth optimization) */ quickSort(nums, left, right) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while (left < right) { - // Sentinel partition operation + // Perform two-pointer partitioning let pivot = this.partition(nums, left, right); - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left < right - pivot) { this.quickSort(nums, left, pivot - 1); // Recursively sort the left subarray - left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] } else { this.quickSort(nums, pivot + 1, right); // Recursively sort the right subarray - right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] } } } } -/* Driver Code */ -/* Quick sort */ +/* Driver code */ +/* Quick sort */ const nums = [2, 4, 1, 0, 3, 5]; const quickSort = new QuickSort(); quickSort.quickSort(nums, 0, nums.length - 1); console.log('After quick sort, nums =', nums); -/* Quick sort (recursion depth optimization) */ +/* Quick sort (median pivot optimization) */ const nums1 = [2, 4, 1, 0, 3, 5]; const quickSortMedian = new QuickSortMedian(); quickSortMedian.quickSort(nums1, 0, nums1.length - 1); -console.log('After quick sort (median pivot optimization), nums =', nums1); +console.log('After quick sort (median pivot optimization), nums1 =', nums1); /* Quick sort (recursion depth optimization) */ const nums2 = [2, 4, 1, 0, 3, 5]; const quickSortTailCall = new QuickSortTailCall(); quickSortTailCall.quickSort(nums2, 0, nums2.length - 1); -console.log('After quick sort (recursion depth optimization), nums =', nums2); +console.log('After quick sort (recursion depth optimization), nums2 =', nums2); diff --git a/en/codes/javascript/chapter_sorting/radix_sort.js b/en/codes/javascript/chapter_sorting/radix_sort.js index 573228f7b..9f6355227 100644 --- a/en/codes/javascript/chapter_sorting/radix_sort.js +++ b/en/codes/javascript/chapter_sorting/radix_sort.js @@ -4,35 +4,35 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ function digit(num, exp) { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return Math.floor(num / exp) % 10; } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ function countingSortDigit(nums, exp) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 const counter = new Array(10).fill(0); const n = nums.length; - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for (let i = 0; i < n; i++) { - const d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d - counter[d]++; // Count the occurrence of digit d + const d = digit(nums[i], exp); // Get the k-th digit of nums[i] and store it in d + counter[d]++; // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for (let i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts const res = new Array(n).fill(0); for (let i = n - 1; i >= 0; i--) { const d = digit(nums[i], exp); - const j = counter[d] - 1; // Get the index j for d in the array + const j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d]--; // Decrease the count of d by 1 + counter[d]--; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for (let i = 0; i < n; i++) { nums[i] = res[i]; } @@ -40,19 +40,19 @@ function countingSortDigit(nums, exp) { /* Radix sort */ function radixSort(nums) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits let m = Math.max(... nums); - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for (let exp = 1; exp <= m; exp *= 10) { - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, exp); } } -/* Driver Code */ +/* Driver code */ const nums = [ 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996, diff --git a/en/codes/javascript/chapter_sorting/selection_sort.js b/en/codes/javascript/chapter_sorting/selection_sort.js index 162986711..7d6c43a6a 100644 --- a/en/codes/javascript/chapter_sorting/selection_sort.js +++ b/en/codes/javascript/chapter_sorting/selection_sort.js @@ -7,21 +7,21 @@ /* Selection sort */ function selectionSort(nums) { let n = nums.length; - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (let i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range let k = i; for (let j = i + 1; j < n; j++) { if (nums[j] < nums[k]) { k = j; // Record the index of the smallest element } } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range [nums[i], nums[k]] = [nums[k], nums[i]]; } } -/* Driver Code */ +/* Driver code */ const nums = [4, 1, 3, 1, 5, 2]; selectionSort(nums); console.log('After selection sort, nums =', nums); diff --git a/en/codes/javascript/chapter_stack_and_queue/array_deque.js b/en/codes/javascript/chapter_stack_and_queue/array_deque.js index c7545b978..e05d16341 100644 --- a/en/codes/javascript/chapter_stack_and_queue/array_deque.js +++ b/en/codes/javascript/chapter_stack_and_queue/array_deque.js @@ -4,11 +4,11 @@ * Author: Zhuo Qinyue (1403450829@qq.com) */ -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ class ArrayDeque { - #nums; // Array for storing double-ended queue elements - #front; // Front pointer, points to the front of the queue element - #queSize; // Double-ended queue length + #nums; // Array for storing deque elements + #front; // Front pointer, pointing to the first element + #queSize; // Deque size /* Constructor */ constructor(capacity) { @@ -17,89 +17,89 @@ class ArrayDeque { this.#queSize = 0; } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ capacity() { return this.#nums.length; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ size() { return this.#queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ isEmpty() { return this.#queSize === 0; } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ index(i) { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end return (i + this.capacity()) % this.capacity(); } - /* Front of the queue enqueue */ + /* Enqueue at the front */ pushFirst(num) { if (this.#queSize === this.capacity()) { - console.log('Double-ended queue is full'); + console.log('Deque is full'); return; } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front pointer one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array this.#front = this.index(this.#front - 1); - // Add num to front of queue + // Add num to the front this.#nums[this.#front] = num; this.#queSize++; } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ pushLast(num) { if (this.#queSize === this.capacity()) { - console.log('Double-ended queue is full'); + console.log('Deque is full'); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear pointer, which points one position past the last element const rear = this.index(this.#front + this.#queSize); - // Front pointer moves one position backward + // Add num to the rear this.#nums[rear] = num; this.#queSize++; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ popFirst() { const num = this.peekFirst(); - // Move front pointer backward by one position + // Advance the front pointer by one position this.#front = this.index(this.#front + 1); this.#queSize--; return num; } - /* Access rear of the queue element */ + /* Dequeue from the rear */ popLast() { const num = this.peekLast(); this.#queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ peekFirst() { - if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + if (this.isEmpty()) throw new Error('Deque is empty'); return this.#nums[this.#front]; } - /* Driver Code */ + /* Access the rear element */ peekLast() { - if (this.isEmpty()) throw new Error('The Deque Is Empty.'); - // Initialize double-ended queue + if (this.isEmpty()) throw new Error('Deque is empty'); + // Calculate the index of the last element const last = this.index(this.#front + this.#queSize - 1); return this.#nums[last]; } - /* Return array for printing */ + /* Return an array for printing */ toArray() { - // Elements enqueue + // Convert only the list elements within the occupied range const res = []; for (let i = 0, j = this.#front; i < this.#queSize; i++, j++) { res[i] = this.#nums[this.index(j)]; @@ -108,8 +108,8 @@ class ArrayDeque { } } -/* Driver Code */ -/* Get the length of the double-ended queue */ +/* Driver code */ +/* Initialize the deque */ const capacity = 5; const deque = new ArrayDeque(capacity); deque.pushLast(3); @@ -117,40 +117,40 @@ deque.pushLast(2); deque.pushLast(5); console.log('Deque deque = [' + deque.toArray() + ']'); -/* Update element */ +/* Access an element */ const peekFirst = deque.peekFirst(); console.log('Front element peekFirst = ' + peekFirst); const peekLast = deque.peekLast(); console.log('Rear element peekLast = ' + peekLast); -/* Elements enqueue */ +/* Enqueue elements */ deque.pushLast(4); -console.log('After element 4 enqueues at rear, deque = [' + deque.toArray() + ']'); +console.log('After enqueuing 4 at the rear, deque = [' + deque.toArray() + ']'); deque.pushFirst(1); -console.log('After element 1 enqueues at front, deque = [' + deque.toArray() + ']'); +console.log('After enqueuing 1 at the front, deque = [' + deque.toArray() + ']'); -/* Element dequeue */ +/* Dequeue an element */ const popLast = deque.popLast(); console.log( - 'Rear dequeue element = ' + + 'Element dequeued from the rear = ' + popLast + - ', after rear dequeue deque = [' + + '; after removing from the rear, deque = [' + deque.toArray() + ']' ); const popFirst = deque.popFirst(); console.log( - 'Front dequeue element = ' + + 'Element dequeued from the front = ' + popFirst + - ', after front dequeue deque = [' + + '; after removing from the front, deque = [' + deque.toArray() + ']' ); -/* Get the length of the double-ended queue */ +/* Get the deque size */ const size = deque.size(); -console.log('Double-ended queue length size = ' + size); +console.log('Deque size = ' + size); -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ const isEmpty = deque.isEmpty(); -console.log('Double-ended queue is empty = ' + isEmpty); +console.log('Is the deque empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/array_queue.js b/en/codes/javascript/chapter_stack_and_queue/array_queue.js index 6893d2904..ab158c4fb 100644 --- a/en/codes/javascript/chapter_stack_and_queue/array_queue.js +++ b/en/codes/javascript/chapter_stack_and_queue/array_queue.js @@ -4,27 +4,27 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ class ArrayQueue { - #nums; // Array for storing queue elements - #front = 0; // Front pointer, points to the front of the queue element - #queSize = 0; // Queue length + #nums; // Array that stores the queue elements + #front = 0; // Front pointer, pointing to the first element + #queSize = 0; // Queue size constructor(capacity) { this.#nums = new Array(capacity); } - /* Get the capacity of the queue */ + /* Get the queue capacity */ get capacity() { return this.#nums.length; } - /* Get the length of the queue */ + /* Get the queue size */ get size() { return this.#queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ isEmpty() { return this.#queSize === 0; } @@ -35,10 +35,10 @@ class ArrayQueue { console.log('Queue is full'); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear pointer, which points one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array const rear = (this.#front + this.size) % this.capacity; - // Front pointer moves one position backward + // Add num to the rear this.#nums[rear] = num; this.#queSize++; } @@ -46,21 +46,21 @@ class ArrayQueue { /* Dequeue */ pop() { const num = this.peek(); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front pointer by one position, wrapping to the beginning if it passes the end this.#front = (this.#front + 1) % this.capacity; this.#queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ peek() { if (this.isEmpty()) throw new Error('Queue is empty'); return this.#nums[this.#front]; } - /* Return Array */ + /* Return an Array */ toArray() { - // Elements enqueue + // Convert only the list elements within the occupied range const arr = new Array(this.size); for (let i = 0, j = this.#front; i < this.size; i++, j++) { arr[i] = this.#nums[j % this.capacity]; @@ -69,12 +69,12 @@ class ArrayQueue { } } -/* Driver Code */ -/* Access front of the queue element */ +/* Driver code */ +/* Initialize the queue */ const capacity = 10; const queue = new ArrayQueue(capacity); -/* Elements enqueue */ +/* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -82,25 +82,25 @@ queue.push(5); queue.push(4); console.log('Queue queue =', queue.toArray()); -/* Return list for printing */ +/* Access the front element */ const peek = queue.peek(); console.log('Front element peek = ' + peek); -/* Element dequeue */ +/* Dequeue an element */ const pop = queue.pop(); -console.log('Dequeue element pop = ' + pop + ', after dequeue queue =', queue.toArray()); +console.log('Dequeued element pop = ' + pop + '; after dequeuing, queue =', queue.toArray()); -/* Get the length of the queue */ +/* Get the queue size */ const size = queue.size; -console.log('Queue length size = ' + size); +console.log('Queue size = ' + size); -/* Check if the queue is empty */ +/* Check whether the queue is empty */ const isEmpty = queue.isEmpty(); -console.log('Queue is empty = ' + isEmpty); +console.log('Is the queue empty = ' + isEmpty); -/* Test circular array */ +/* Test the circular array */ for (let i = 0; i < 10; i++) { queue.push(i); queue.pop(); - console.log('Round ' + i + ' rounds of enqueue + dequeue, queue =', queue.toArray()); + console.log('Round ' + i + ' : after enqueue + dequeue, queue =', queue.toArray()); } diff --git a/en/codes/javascript/chapter_stack_and_queue/array_stack.js b/en/codes/javascript/chapter_stack_and_queue/array_stack.js index 4be71e2ec..a46a7a7fa 100644 --- a/en/codes/javascript/chapter_stack_and_queue/array_stack.js +++ b/en/codes/javascript/chapter_stack_and_queue/array_stack.js @@ -4,19 +4,19 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Stack based on array implementation */ +/* Stack implemented with an array */ class ArrayStack { #stack; constructor() { this.#stack = []; } - /* Get the length of the stack */ + /* Get the stack size */ get size() { return this.#stack.length; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ isEmpty() { return this.#stack.length === 0; } @@ -32,23 +32,23 @@ class ArrayStack { return this.#stack.pop(); } - /* Return list for printing */ + /* Access the top element */ top() { if (this.isEmpty()) throw new Error('Stack is empty'); return this.#stack[this.#stack.length - 1]; } - /* Return Array */ + /* Return an Array */ toArray() { return this.#stack; } } -/* Driver Code */ -/* Access top of the stack element */ +/* Driver code */ +/* Initialize the stack */ const stack = new ArrayStack(); -/* Elements push onto stack */ +/* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -57,19 +57,19 @@ stack.push(4); console.log('Stack stack = '); console.log(stack.toArray()); -/* Return list for printing */ +/* Access the top element */ const top = stack.top(); -console.log('Stack top element top = ' + top); +console.log('Top element top = ' + top); -/* Element pop from stack */ +/* Pop an element from the stack */ const pop = stack.pop(); -console.log('Pop element pop = ' + pop + ', after pop, stack = '); +console.log('Popped element pop = ' + pop + '; after popping, stack = '); console.log(stack.toArray()); -/* Get the length of the stack */ +/* Get the stack size */ const size = stack.size; -console.log('Stack length size = ' + size); +console.log('Stack size = ' + size); -/* Check if empty */ +/* Check whether it is empty */ const isEmpty = stack.isEmpty(); -console.log('Stack is empty = ' + isEmpty); +console.log('Is the stack empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/deque.js b/en/codes/javascript/chapter_stack_and_queue/deque.js index 6a92239c7..1609bd589 100644 --- a/en/codes/javascript/chapter_stack_and_queue/deque.js +++ b/en/codes/javascript/chapter_stack_and_queue/deque.js @@ -4,41 +4,41 @@ * Author: Zhuo Qinyue (1403450829@qq.com) */ -/* Driver Code */ -/* Get the length of the double-ended queue */ -// JavaScript has no built-in deque, can only use Array as deque +/* Driver code */ +/* Initialize the deque */ +// JavaScript has no built-in deque; use an Array as a deque const deque = []; -/* Elements enqueue */ +/* Enqueue elements */ deque.push(2); deque.push(5); deque.push(4); -// Note: due to array, unshift() method has O(n) time complexity +// Note: because this uses an array, unshift() has O(n) time complexity deque.unshift(3); deque.unshift(1); -console.log('Double-ended queue deque = ', deque); +console.log('Deque deque = ', deque); -/* Update element */ +/* Access an element */ const peekFirst = deque[0]; console.log('Front element peekFirst = ' + peekFirst); const peekLast = deque[deque.length - 1]; console.log('Rear element peekLast = ' + peekLast); -/* Element dequeue */ -// Note: due to array, shift() method has O(n) time complexity +/* Dequeue an element */ +// Note: because this uses an array, shift() has O(n) time complexity const popFront = deque.shift(); console.log( - 'Front dequeue element popFront = ' + popFront + ', after front dequeue, deque = ' + deque + 'Element removed from the front, popFront = ' + popFront + ', after dequeuing from the front, deque = ' + deque ); const popBack = deque.pop(); console.log( - 'Dequeue rear element popBack = ' + popBack + ', after rear dequeue, deque = ' + deque + 'Element removed from the rear, popBack = ' + popBack + ', after dequeuing from the rear, deque = ' + deque ); -/* Get the length of the double-ended queue */ +/* Get the deque size */ const size = deque.length; -console.log('Double-ended queue length size = ' + size); +console.log('Deque size = ' + size); -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ const isEmpty = size === 0; -console.log('Double-ended queue is empty = ' + isEmpty); +console.log('Is the deque empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/linkedlist_deque.js b/en/codes/javascript/chapter_stack_and_queue/linkedlist_deque.js index 03b51022d..e27864270 100644 --- a/en/codes/javascript/chapter_stack_and_queue/linkedlist_deque.js +++ b/en/codes/javascript/chapter_stack_and_queue/linkedlist_deque.js @@ -6,8 +6,8 @@ /* Doubly linked list node */ class ListNode { - prev; // Predecessor node reference (pointer) - next; // Successor node reference (pointer) + prev; // Reference (pointer) to the previous node + next; // Reference (pointer) to the next node val; // Node value constructor(val) { @@ -17,11 +17,11 @@ class ListNode { } } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ class LinkedListDeque { #front; // Head node front #rear; // Tail node rear - #queSize; // Length of the double-ended queue + #queSize; // Deque size constructor() { this.#front = null; @@ -29,7 +29,7 @@ class LinkedListDeque { this.#queSize = 0; } - /* Rear of the queue enqueue operation */ + /* Enqueue at the rear */ pushLast(val) { const node = new ListNode(val); // If the linked list is empty, make both front and rear point to node @@ -40,12 +40,12 @@ class LinkedListDeque { // Add node to the tail of the linked list this.#rear.next = node; node.prev = this.#rear; - this.#rear = node; // Update tail node + this.#rear = node; // Update the tail node } this.#queSize++; } - /* Front of the queue enqueue operation */ + /* Enqueue at the front */ pushFirst(val) { const node = new ListNode(val); // If the linked list is empty, make both front and rear point to node @@ -56,76 +56,76 @@ class LinkedListDeque { // Add node to the head of the linked list this.#front.prev = node; node.next = this.#front; - this.#front = node; // Update head node + this.#front = node; // Update the head node } this.#queSize++; } - /* Temporarily store tail node value */ + /* Dequeue from the rear */ popLast() { if (this.#queSize === 0) { return null; } - const value = this.#rear.val; // Store tail node value - // When the deque has one element, front and rear point to the same node + const value = this.#rear.val; // Store the tail node value + // When the queue size is 1, the head and tail point to the same node if (this.#queSize === 1) { this.#front = null; this.#rear = null; } - // Update tail node + // Remove the tail node else { let temp = this.#rear.prev; temp.next = null; this.#rear.prev = null; - this.#rear = temp; // Update tail node + this.#rear = temp; // Update the tail node } this.#queSize--; return value; } - /* Temporarily store head node value */ + /* Dequeue from the front */ popFirst() { if (this.#queSize === 0) { return null; } const value = this.#front.val; // Store the head node value - // When the deque has one element, front and rear point to the same node + // When the queue size is 1, the head and tail point to the same node if (this.#queSize === 1) { this.#front = null; this.#rear = null; } - // Delete head node + // Remove the head node else { let temp = this.#front.next; temp.prev = null; this.#front.next = null; - this.#front = temp; // Update head node + this.#front = temp; // Update the head node } this.#queSize--; return value; } - /* Driver Code */ + /* Access the rear element */ peekLast() { return this.#queSize === 0 ? null : this.#rear.val; } - /* Return list for printing */ + /* Access the front element */ peekFirst() { return this.#queSize === 0 ? null : this.#front.val; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ size() { return this.#queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ isEmpty() { return this.#queSize === 0; } - /* Print deque */ + /* Print the deque */ print() { const arr = []; let temp = this.#front; @@ -137,8 +137,8 @@ class LinkedListDeque { } } -/* Driver Code */ -/* Get the length of the double-ended queue */ +/* Driver code */ +/* Initialize the deque */ const linkedListDeque = new LinkedListDeque(); linkedListDeque.pushLast(3); linkedListDeque.pushLast(2); @@ -146,32 +146,32 @@ linkedListDeque.pushLast(5); console.log('Deque linkedListDeque = '); linkedListDeque.print(); -/* Update element */ +/* Access an element */ const peekFirst = linkedListDeque.peekFirst(); console.log('Front element peekFirst = ' + peekFirst); const peekLast = linkedListDeque.peekLast(); console.log('Rear element peekLast = ' + peekLast); -/* Elements enqueue */ +/* Enqueue elements */ linkedListDeque.pushLast(4); -console.log('After element 4 enqueues at rear, linkedListDeque = '); +console.log('After enqueuing 4 at the rear, linkedListDeque = '); linkedListDeque.print(); linkedListDeque.pushFirst(1); -console.log('After element 1 enqueues at front, linkedListDeque = '); +console.log('After enqueuing 1 at the front, linkedListDeque = '); linkedListDeque.print(); -/* Element dequeue */ +/* Dequeue an element */ const popLast = linkedListDeque.popLast(); -console.log('Rear dequeue element = ' + popLast + ', after rear dequeue linkedListDeque = '); +console.log('Element dequeued from the rear = ' + popLast + '; after removing from the rear, linkedListDeque = '); linkedListDeque.print(); const popFirst = linkedListDeque.popFirst(); -console.log('Front dequeue element = ' + popFirst + ', after front dequeue linkedListDeque = '); +console.log('Element dequeued from the front = ' + popFirst + '; after removing from the front, linkedListDeque = '); linkedListDeque.print(); -/* Get the length of the double-ended queue */ +/* Get the deque size */ const size = linkedListDeque.size(); -console.log('Double-ended queue length size = ' + size); +console.log('Deque size = ' + size); -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ const isEmpty = linkedListDeque.isEmpty(); -console.log('Double-ended queue is empty = ' + isEmpty); +console.log('Is the deque empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js b/en/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js index 31093bd99..e12b3590f 100644 --- a/en/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js +++ b/en/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js @@ -6,10 +6,10 @@ const { ListNode } = require('../modules/ListNode'); -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue { - #front; // Front node #front - #rear; // Rear node #rear + #front; // Head node #front + #rear; // Tail node #rear #queSize = 0; constructor() { @@ -17,12 +17,12 @@ class LinkedListQueue { this.#rear = null; } - /* Get the length of the queue */ + /* Get the queue size */ get size() { return this.#queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ isEmpty() { return this.size === 0; } @@ -31,11 +31,11 @@ class LinkedListQueue { push(num) { // Add num after the tail node const node = new ListNode(num); - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (!this.#front) { this.#front = node; this.#rear = node; - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node } else { this.#rear.next = node; this.#rear = node; @@ -46,19 +46,19 @@ class LinkedListQueue { /* Dequeue */ pop() { const num = this.peek(); - // Delete head node + // Remove the head node this.#front = this.#front.next; this.#queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ peek() { if (this.size === 0) throw new Error('Queue is empty'); return this.#front.val; } - /* Convert linked list to Array and return */ + /* Convert the linked list to an Array and return it */ toArray() { let node = this.#front; const res = new Array(this.size); @@ -70,11 +70,11 @@ class LinkedListQueue { } } -/* Driver Code */ -/* Access front of the queue element */ +/* Driver code */ +/* Initialize the queue */ const queue = new LinkedListQueue(); -/* Elements enqueue */ +/* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -82,18 +82,18 @@ queue.push(5); queue.push(4); console.log('Queue queue = ' + queue.toArray()); -/* Return list for printing */ +/* Access the front element */ const peek = queue.peek(); console.log('Front element peek = ' + peek); -/* Element dequeue */ +/* Dequeue an element */ const pop = queue.pop(); -console.log('Dequeue element pop = ' + pop + ', after dequeue, queue = ' + queue.toArray()); +console.log('Dequeued element pop = ' + pop + '; after dequeuing, queue = ' + queue.toArray()); -/* Get the length of the queue */ +/* Get the queue size */ const size = queue.size; -console.log('Queue length size = ' + size); +console.log('Queue size = ' + size); -/* Check if the queue is empty */ +/* Check whether the queue is empty */ const isEmpty = queue.isEmpty(); -console.log('Queue is empty = ' + isEmpty); +console.log('Is the queue empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/linkedlist_stack.js b/en/codes/javascript/chapter_stack_and_queue/linkedlist_stack.js index 7e7bb6a66..63205db5d 100644 --- a/en/codes/javascript/chapter_stack_and_queue/linkedlist_stack.js +++ b/en/codes/javascript/chapter_stack_and_queue/linkedlist_stack.js @@ -6,21 +6,21 @@ const { ListNode } = require('../modules/ListNode'); -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ class LinkedListStack { - #stackPeek; // Use head node as stack top - #stkSize = 0; // Stack length + #stackPeek; // Use the head node as the stack top + #stkSize = 0; // Stack size constructor() { this.#stackPeek = null; } - /* Get the length of the stack */ + /* Get the stack size */ get size() { return this.#stkSize; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ isEmpty() { return this.size === 0; } @@ -41,13 +41,13 @@ class LinkedListStack { return num; } - /* Return list for printing */ + /* Access the top element */ peek() { if (!this.#stackPeek) throw new Error('Stack is empty'); return this.#stackPeek.val; } - /* Convert linked list to Array and return */ + /* Convert the linked list to an Array and return it */ toArray() { let node = this.#stackPeek; const res = new Array(this.size); @@ -59,11 +59,11 @@ class LinkedListStack { } } -/* Driver Code */ -/* Access top of the stack element */ +/* Driver code */ +/* Initialize the stack */ const stack = new LinkedListStack(); -/* Elements push onto stack */ +/* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -71,18 +71,18 @@ stack.push(5); stack.push(4); console.log('Stack stack = ' + stack.toArray()); -/* Return list for printing */ +/* Access the top element */ const peek = stack.peek(); -console.log('Stack top element peek = ' + peek); +console.log('Top element peek = ' + peek); -/* Element pop from stack */ +/* Pop an element from the stack */ const pop = stack.pop(); -console.log('Pop element pop = ' + pop + ', after pop, stack = ' + stack.toArray()); +console.log('Popped element pop = ' + pop + '; after popping, stack = ' + stack.toArray()); -/* Get the length of the stack */ +/* Get the stack size */ const size = stack.size; -console.log('Stack length size = ' + size); +console.log('Stack size = ' + size); -/* Check if empty */ +/* Check whether it is empty */ const isEmpty = stack.isEmpty(); -console.log('Stack is empty = ' + isEmpty); +console.log('Is the stack empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/queue.js b/en/codes/javascript/chapter_stack_and_queue/queue.js index 15db49b28..46e28e2a4 100644 --- a/en/codes/javascript/chapter_stack_and_queue/queue.js +++ b/en/codes/javascript/chapter_stack_and_queue/queue.js @@ -4,12 +4,12 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Driver Code */ -/* Access front of the queue element */ -// JavaScript has no built-in queue, can use Array as queue +/* Driver code */ +/* Initialize the queue */ +// JavaScript has no built-in queue; an Array can be used as a queue const queue = []; -/* Elements enqueue */ +/* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -17,19 +17,19 @@ queue.push(5); queue.push(4); console.log('Queue queue =', queue); -/* Return list for printing */ +/* Access the front element */ const peek = queue[0]; console.log('Front element peek =', peek); -/* Element dequeue */ -// Underlying is array, so shift() method has O(n) time complexity +/* Dequeue an element */ +// The underlying structure is an array, so shift() has O(n) time complexity const pop = queue.shift(); -console.log('Dequeue element pop =', pop, ', after dequeue, queue = ', queue); +console.log('Dequeued element pop =', pop, '; after dequeuing, queue = ', queue); -/* Get the length of the queue */ +/* Get the queue size */ const size = queue.length; -console.log('Queue length size =', size); +console.log('Queue size =', size); -/* Check if the queue is empty */ +/* Check whether the queue is empty */ const isEmpty = queue.length === 0; -console.log('Queue is empty = ', isEmpty); +console.log('Is the queue empty = ', isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/stack.js b/en/codes/javascript/chapter_stack_and_queue/stack.js index 98a675eb8..ed4e63e7d 100644 --- a/en/codes/javascript/chapter_stack_and_queue/stack.js +++ b/en/codes/javascript/chapter_stack_and_queue/stack.js @@ -4,12 +4,12 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Driver Code */ -/* Access top of the stack element */ -// JavaScript has no built-in stack class, can use Array as stack +/* Driver code */ +/* Initialize the stack */ +// JavaScript has no built-in stack class; an Array can be used as a stack const stack = []; -/* Elements push onto stack */ +/* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -17,19 +17,19 @@ stack.push(5); stack.push(4); console.log('Stack stack =', stack); -/* Return list for printing */ +/* Access the top element */ const peek = stack[stack.length - 1]; -console.log('Stack top element peek =', peek); +console.log('Top element peek =', peek); -/* Element pop from stack */ +/* Pop an element from the stack */ const pop = stack.pop(); -console.log('Pop element pop =', pop); -console.log('After pop, stack =', stack); +console.log('Popped element pop =', pop); +console.log('After popping, stack =', stack); -/* Get the length of the stack */ +/* Get the stack size */ const size = stack.length; -console.log('Stack length size =', size); +console.log('Stack size =', size); -/* Check if empty */ +/* Check whether it is empty */ const isEmpty = stack.length === 0; -console.log('Is stack empty =', isEmpty); +console.log('Is the stack empty =', isEmpty); diff --git a/en/codes/javascript/chapter_tree/array_binary_tree.js b/en/codes/javascript/chapter_tree/array_binary_tree.js index 1d9643c0d..fbceb9113 100644 --- a/en/codes/javascript/chapter_tree/array_binary_tree.js +++ b/en/codes/javascript/chapter_tree/array_binary_tree.js @@ -7,7 +7,7 @@ const { arrToTree } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ class ArrayBinaryTree { #tree; @@ -16,29 +16,29 @@ class ArrayBinaryTree { this.#tree = arr; } - /* List capacity */ + /* Array length */ size() { return this.#tree.length; } - /* Get value of node at index i */ + /* Get the value of the node at index i */ val(i) { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return null to indicate an empty position if (i < 0 || i >= this.size()) return null; return this.#tree[i]; } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ left(i) { return 2 * i + 1; } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ right(i) { return 2 * i + 2; } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ parent(i) { return Math.floor((i - 1) / 2); // Floor division } @@ -46,7 +46,7 @@ class ArrayBinaryTree { /* Level-order traversal */ levelOrder() { let res = []; - // Traverse array directly + // Traverse the array directly for (let i = 0; i < this.size(); i++) { if (this.val(i) !== null) res.push(this.val(i)); } @@ -55,7 +55,7 @@ class ArrayBinaryTree { /* Depth-first traversal */ #dfs(i, order, res) { - // If empty position, return + // Return if this position is empty if (this.val(i) === null) return; // Preorder traversal if (order === 'pre') res.push(this.val(i)); @@ -89,9 +89,9 @@ class ArrayBinaryTree { } } -/* Driver Code */ -// Initialize binary tree -// Here we use a function to generate a binary tree directly from an array +/* Driver code */ +// Initialize the binary tree +// Use a helper function that builds a binary tree directly from an array const arr = Array.of( 1, 2, @@ -111,37 +111,37 @@ const arr = Array.of( ); const root = arrToTree(arr); -console.log('\nInitialize binary tree\n'); -console.log('Array representation of binary tree:'); +console.log('\nInitialize the binary tree\n'); +console.log('Array representation of the binary tree:'); console.log(arr); -console.log('Linked list representation of binary tree:'); +console.log('Linked representation of the binary tree:'); printTree(root); -// Binary tree class represented by array +// Binary tree class using an array representation const abt = new ArrayBinaryTree(arr); -// Access node +// Access a node const i = 1; const l = abt.left(i); const r = abt.right(i); const p = abt.parent(i); -console.log('\nCurrent node index is ' + i + ', value is ' + abt.val(i)); +console.log('\nCurrent node index: ' + i + ' with value ' + abt.val(i)); console.log( - 'Its left child node index is ' + l + ', value is ' + (l === null ? 'null' : abt.val(l)) + 'Left child index: ' + l + ' with value ' + (l === null ? 'null' : abt.val(l)) ); console.log( - 'Its right child node index is ' + r + ', value is ' + (r === null ? 'null' : abt.val(r)) + 'Right child index: ' + r + ' with value ' + (r === null ? 'null' : abt.val(r)) ); console.log( - 'Its parent node index is ' + p + ', value is ' + (p === null ? 'null' : abt.val(p)) + 'Parent index: ' + p + ' with value ' + (p === null ? 'null' : abt.val(p)) ); -// Traverse tree +// Traverse the tree let res = abt.levelOrder(); -console.log('\nLevel-order traversal is:' + res); +console.log('\nLevel-order traversal:' + res); res = abt.preOrder(); -console.log('Preorder traversal is:' + res); +console.log('Preorder traversal:' + res); res = abt.inOrder(); -console.log('Inorder traversal is:' + res); +console.log('Inorder traversal:' + res); res = abt.postOrder(); -console.log('Postorder traversal is:' + res); +console.log('Postorder traversal:' + res); diff --git a/en/codes/javascript/chapter_tree/avl_tree.js b/en/codes/javascript/chapter_tree/avl_tree.js index e2a276b25..806d1ace3 100644 --- a/en/codes/javascript/chapter_tree/avl_tree.js +++ b/en/codes/javascript/chapter_tree/avl_tree.js @@ -7,133 +7,133 @@ const { TreeNode } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -/* AVL tree */ +/* AVL tree*/ class AVLTree { /* Constructor */ constructor() { - this.root = null; // Root node + this.root = null; //Root node } - /* Get node height */ + /* Get the node height */ height(node) { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 return node === null ? -1 : node.height; } - /* Update node height */ + /* Update the node height */ #updateHeight(node) { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node.height = Math.max(this.height(node.left), this.height(node.right)) + 1; } - /* Get balance factor */ + /* Get the balance factor */ balanceFactor(node) { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node === null) return 0; // Node balance factor = left subtree height - right subtree height return this.height(node.left) - this.height(node.right); } - /* Right rotation operation */ + /* Right rotation */ #rightRotate(node) { const child = node.left; const grandChild = child.right; - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.right = node; node.left = grandChild; - // Update node height + // Update the node height this.#updateHeight(node); this.#updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Left rotation operation */ + /* Left rotation */ #leftRotate(node) { const child = node.right; const grandChild = child.left; - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.left = node; node.right = grandChild; - // Update node height + // Update the node height this.#updateHeight(node); this.#updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ #rotate(node) { - // Get balance factor of node + // Get the balance factor of node const balanceFactor = this.balanceFactor(node); - // Left-leaning tree + // Left-heavy tree if (balanceFactor > 1) { if (this.balanceFactor(node.left) >= 0) { - // Right rotation + // Rotate right return this.#rightRotate(node); } else { - // First left rotation then right rotation + // Rotate left, then right node.left = this.#leftRotate(node.left); return this.#rightRotate(node); } } - // Right-leaning tree + // Right-heavy tree if (balanceFactor < -1) { if (this.balanceFactor(node.right) <= 0) { - // Left rotation + // Rotate left return this.#leftRotate(node); } else { - // First right rotation then left rotation + // Rotate right, then left node.right = this.#rightRotate(node.right); return this.#leftRotate(node); } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node; } - /* Insert node */ + /* Insert a node */ insert(val) { this.root = this.#insertHelper(this.root, val); } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ #insertHelper(node, val) { if (node === null) return new TreeNode(val); - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (val < node.val) node.left = this.#insertHelper(node.left, val); else if (val > node.val) node.right = this.#insertHelper(node.right, val); - else return node; // Duplicate node not inserted, return directly - this.#updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + else return node; // Return without inserting a duplicate node + this.#updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = this.#rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } - /* Remove node */ + /* Remove a node */ remove(val) { this.root = this.#removeHelper(this.root, val); } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ #removeHelper(node, val) { if (node === null) return null; - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (val < node.val) node.left = this.#removeHelper(node.left, val); else if (val > node.val) node.right = this.#removeHelper(node.right, val); else { if (node.left === null || node.right === null) { const child = node.left !== null ? node.left : node.right; - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child === null) return null; - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly else node = child; } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value let temp = node.right; while (temp.left !== null) { temp = temp.left; @@ -142,47 +142,47 @@ class AVLTree { node.val = temp.val; } } - this.#updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + this.#updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = this.#rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } - /* Search node */ + /* Find a node */ search(val) { let cur = this.root; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur !== null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < val) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > val) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } } function testInsert(tree, val) { tree.insert(val); - console.log('\nInsert node ' + val + ', AVL tree is'); + console.log('\nAfter inserting node ' + val + ' the AVL tree is:'); printTree(tree.root); } function testRemove(tree, val) { tree.remove(val); - console.log('\nRemove node ' + val + ', AVL tree is'); + console.log('\nAfter removing node ' + val + ' the AVL tree is:'); printTree(tree.root); } -/* Driver Code */ -/* Please pay attention to how the AVL tree maintains balance after inserting nodes */ +/* Driver code */ +/* Initialize an empty AVL tree */ const avlTree = new AVLTree(); -/* Insert node */ -// Delete nodes +/* Insert a node */ +// Observe how the AVL tree maintains balance after a node is inserted testInsert(avlTree, 1); testInsert(avlTree, 2); testInsert(avlTree, 3); @@ -194,15 +194,15 @@ testInsert(avlTree, 9); testInsert(avlTree, 10); testInsert(avlTree, 6); -/* Please pay attention to how the AVL tree maintains balance after deleting nodes */ +/* Insert a duplicate node */ testInsert(avlTree, 7); -/* Remove node */ -// Delete node with degree 1 -testRemove(avlTree, 8); // Delete node with degree 2 -testRemove(avlTree, 5); // Remove node with degree 1 -testRemove(avlTree, 4); // Remove node with degree 2 +/* Remove a node */ +// Observe how the AVL tree maintains balance after a node is removed +testRemove(avlTree, 8); // Remove a node with 0 children +testRemove(avlTree, 5); // Remove a node with 1 child +testRemove(avlTree, 4); // Remove a node with 2 children -/* Search node */ +/* Look up a node */ const node = avlTree.search(7); -console.log('\nFound node object is', node, ', node value = ' + node.val); +console.log('\nFound node object:', node, ', node value = ' + node.val); diff --git a/en/codes/javascript/chapter_tree/binary_search_tree.js b/en/codes/javascript/chapter_tree/binary_search_tree.js index a262a6185..1dd81b625 100644 --- a/en/codes/javascript/chapter_tree/binary_search_tree.js +++ b/en/codes/javascript/chapter_tree/binary_search_tree.js @@ -11,129 +11,129 @@ const { printTree } = require('../modules/PrintUtil'); class BinarySearchTree { /* Constructor */ constructor() { - // Initialize empty tree + // Initialize an empty tree this.root = null; } - /* Get binary tree root node */ + /* Get the root of the binary tree */ getRoot() { return this.root; } - /* Search node */ + /* Find a node */ search(num) { let cur = this.root; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur !== null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > num) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } - /* Insert node */ + /* Insert a node */ insert(num) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (this.root === null) { this.root = new TreeNode(num); return; } let cur = this.root, pre = null; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur !== null) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur.val === num) return; pre = cur; - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else cur = cur.left; } - // Insert node + // Insert a node const node = new TreeNode(num); if (pre.val < num) pre.right = node; else pre.left = node; } - /* Remove node */ + /* Remove a node */ remove(num) { - // If tree is empty, return directly + // Return immediately if the tree is empty if (this.root === null) return; let cur = this.root, pre = null; - // Loop search, exit after passing leaf node + // Search in a loop until reaching a null reference beyond a leaf while (cur !== null) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur.val === num) break; pre = cur; - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur else cur = cur.left; } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if (cur === null) return; - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if (cur.left === null || cur.right === null) { - // When number of child nodes = 0 / 1, child = null / that child node + // With 0 / 1 children, child is null / the child node, respectively const child = cur.left !== null ? cur.left : cur.right; - // Delete node cur + // Remove node cur if (cur !== this.root) { if (pre.left === cur) pre.left = child; else pre.right = child; } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root this.root = child; } } - // Number of child nodes = 2 + // Number of children = 2 else { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur let tmp = cur.right; while (tmp.left !== null) { tmp = tmp.left; } - // Recursively delete node tmp + // Recursively remove node tmp this.remove(tmp.val); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur.val = tmp.val; } } } -/* Driver Code */ -/* Initialize binary search tree */ +/* Driver code */ +/* Initialize the binary search tree */ const bst = new BinarySearchTree(); -// Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree +// Different insertion orders produce different binary trees; this sequence produces a perfect binary tree const nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; for (const num of nums) { bst.insert(num); } -console.log('\nInitialized binary tree is\n'); +console.log('\nThe initialized binary tree is:\n'); printTree(bst.getRoot()); -/* Search node */ +/* Find a node */ const node = bst.search(7); -console.log('\nFound node object is ' + node + ', node value = ' + node.val); +console.log('\nFound node object: ' + node + ', node value = ' + node.val); -/* Insert node */ +/* Insert a node */ bst.insert(16); -console.log('\nAfter inserting node 16, binary tree is\n'); +console.log('\nBinary tree after inserting node 16:\n'); printTree(bst.getRoot()); -/* Remove node */ +/* Remove a node */ bst.remove(1); -console.log('\nAfter removing node 1, binary tree is\n'); +console.log('\nBinary tree after removing node 1:\n'); printTree(bst.getRoot()); bst.remove(2); -console.log('\nAfter removing node 2, binary tree is\n'); +console.log('\nBinary tree after removing node 2:\n'); printTree(bst.getRoot()); bst.remove(4); -console.log('\nAfter removing node 4, binary tree is\n'); +console.log('\nBinary tree after removing node 4:\n'); printTree(bst.getRoot()); diff --git a/en/codes/javascript/chapter_tree/binary_tree.js b/en/codes/javascript/chapter_tree/binary_tree.js index 136904084..fced8c5d9 100644 --- a/en/codes/javascript/chapter_tree/binary_tree.js +++ b/en/codes/javascript/chapter_tree/binary_tree.js @@ -7,29 +7,29 @@ const { TreeNode } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -/* Initialize binary tree */ -// Initialize nodes +/* Initialize the binary tree */ +// Initialize the nodes let n1 = new TreeNode(1), n2 = new TreeNode(2), n3 = new TreeNode(3), n4 = new TreeNode(4), n5 = new TreeNode(5); -// Build references (pointers) between nodes +// Set up references (pointers) between the nodes n1.left = n2; n1.right = n3; n2.left = n4; n2.right = n5; -console.log('\nInitialize binary tree\n'); +console.log('\nInitialize the binary tree\n'); printTree(n1); -/* Insert node P between n1 -> n2 */ +/* Insert and remove nodes */ const P = new TreeNode(0); -// Delete node +// Insert node P between n1 and n2 in n1 -> n2 n1.left = P; P.left = n2; -console.log('\nAfter inserting node P\n'); +console.log('\nAfter inserting node P:\n'); printTree(n1); // Remove node P n1.left = n2; -console.log('\nAfter removing node P\n'); +console.log('\nAfter removing node P:\n'); printTree(n1); diff --git a/en/codes/javascript/chapter_tree/binary_tree_bfs.js b/en/codes/javascript/chapter_tree/binary_tree_bfs.js index 9751558bb..8d14fec29 100644 --- a/en/codes/javascript/chapter_tree/binary_tree_bfs.js +++ b/en/codes/javascript/chapter_tree/binary_tree_bfs.js @@ -9,26 +9,26 @@ const { printTree } = require('../modules/PrintUtil'); /* Level-order traversal */ function levelOrder(root) { - // Initialize queue, add root node + // Initialize a queue and enqueue the root const queue = [root]; - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence const list = []; while (queue.length) { - let node = queue.shift(); // Dequeue - list.push(node.val); // Save node value - if (node.left) queue.push(node.left); // Left child node enqueue - if (node.right) queue.push(node.right); // Right child node enqueue + let node = queue.shift(); // Dequeue from the queue + list.push(node.val); // Store the node value + if (node.left) queue.push(node.left); // Enqueue the left child + if (node.right) queue.push(node.right); // Enqueue the right child } return list; } -/* Driver Code */ -/* Initialize binary tree */ -// Here we use a function to generate a binary tree directly from an array +/* Driver code */ +/* Initialize the binary tree */ +// Use a helper function that builds a binary tree directly from an array const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree\n'); +console.log('\nInitialize the binary tree\n'); printTree(root); /* Level-order traversal */ const list = levelOrder(root); -console.log('\nLevel-order traversal node print sequence = ' + list); +console.log('\nNode sequence from level-order traversal = ' + list); diff --git a/en/codes/javascript/chapter_tree/binary_tree_dfs.js b/en/codes/javascript/chapter_tree/binary_tree_dfs.js index df1e46a93..483638995 100644 --- a/en/codes/javascript/chapter_tree/binary_tree_dfs.js +++ b/en/codes/javascript/chapter_tree/binary_tree_dfs.js @@ -7,13 +7,13 @@ const { arrToTree } = require('../modules/TreeNode'); const { printTree } = require('../modules/PrintUtil'); -// Initialize list for storing traversal sequence +// Initialize a list to store the traversal sequence const list = []; /* Preorder traversal */ function preOrder(root) { if (root === null) return; - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree list.push(root.val); preOrder(root.left); preOrder(root.right); @@ -22,7 +22,7 @@ function preOrder(root) { /* Inorder traversal */ function inOrder(root) { if (root === null) return; - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(root.left); list.push(root.val); inOrder(root.right); @@ -31,30 +31,30 @@ function inOrder(root) { /* Postorder traversal */ function postOrder(root) { if (root === null) return; - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(root.left); postOrder(root.right); list.push(root.val); } -/* Driver Code */ -/* Initialize binary tree */ -// Here we use a function to generate a binary tree directly from an array +/* Driver code */ +/* Initialize the binary tree */ +// Use a helper function that builds a binary tree directly from an array const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree\n'); +console.log('\nInitialize the binary tree\n'); printTree(root); /* Preorder traversal */ list.length = 0; preOrder(root); -console.log('\nPreorder traversal node print sequence = ' + list); +console.log('\nNode sequence from preorder traversal = ' + list); /* Inorder traversal */ list.length = 0; inOrder(root); -console.log('\nInorder traversal node print sequence = ' + list); +console.log('\nNode sequence from inorder traversal = ' + list); /* Postorder traversal */ list.length = 0; postOrder(root); -console.log('\nPostorder traversal node print sequence = ' + list); +console.log('\nNode sequence from postorder traversal = ' + list); diff --git a/en/codes/javascript/modules/ListNode.js b/en/codes/javascript/modules/ListNode.js index 632ae8acf..b2dd1604a 100644 --- a/en/codes/javascript/modules/ListNode.js +++ b/en/codes/javascript/modules/ListNode.js @@ -7,7 +7,7 @@ /* Linked list node */ class ListNode { val; // Node value - next; // Reference (pointer) to next node + next; // Reference (pointer) to the next node constructor(val, next) { this.val = val === undefined ? 0 : val; this.next = next === undefined ? null : next; diff --git a/en/codes/javascript/modules/PrintUtil.js b/en/codes/javascript/modules/PrintUtil.js index f37c7ae4d..2dddac157 100644 --- a/en/codes/javascript/modules/PrintUtil.js +++ b/en/codes/javascript/modules/PrintUtil.js @@ -6,7 +6,7 @@ const { arrToTree } = require('./TreeNode'); -/* Print linked list */ +/* Print a linked list */ function printLinkedList(head) { let list = []; while (head !== null) { @@ -22,7 +22,7 @@ function Trunk(prev, str) { } /** - * Print binary tree + * Print the binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -30,7 +30,7 @@ function printTree(root) { printTree(root, null, false); } -/* Print binary tree */ +/* Print the binary tree */ function printTree(root, prev, isRight) { if (root === null) { return; @@ -71,11 +71,11 @@ function showTrunks(p) { process.stdout.write(p.str); } -/* Print heap */ +/* Print a heap */ function printHeap(arr) { - console.log('Heap array representation:'); + console.log('Array representation of the heap:'); console.log(arr); - console.log('Heap tree representation:'); + console.log('Tree representation of the heap:'); printTree(arrToTree(arr)); } diff --git a/en/codes/javascript/modules/TreeNode.js b/en/codes/javascript/modules/TreeNode.js index b1a35e86c..c299150a1 100644 --- a/en/codes/javascript/modules/TreeNode.js +++ b/en/codes/javascript/modules/TreeNode.js @@ -7,9 +7,9 @@ /* Binary tree node */ class TreeNode { val; // Node value - left; // Left child pointer - right; // Right child pointer - height; // Node height + left; // Pointer to the left child + right; // Pointer to the right child + height; //Node height constructor(val, left, right, height) { this.val = val === undefined ? 0 : val; this.left = left === undefined ? null : left; @@ -18,7 +18,7 @@ class TreeNode { } } -/* Deserialize array to binary tree */ +/* Deserialize an array into a binary tree */ function arrToTree(arr, i = 0) { if (i < 0 || i >= arr.length || arr[i] === null) { return null; diff --git a/en/codes/javascript/modules/Vertex.js b/en/codes/javascript/modules/Vertex.js index 59c285853..5efeb8ae9 100644 --- a/en/codes/javascript/modules/Vertex.js +++ b/en/codes/javascript/modules/Vertex.js @@ -11,7 +11,7 @@ class Vertex { this.val = val; } - /* Input value list vals, return vertex list vets */ + /* Given the list of values vals, return the list of vertices vets */ static valsToVets(vals) { const vets = []; for (let i = 0; i < vals.length; i++) { @@ -20,7 +20,7 @@ class Vertex { return vets; } - /* Input vertex list vets, return value list vals */ + /* Given the list of vertices vets, return the list of values vals */ static vetsToVals(vets) { const vals = []; for (const vet of vets) { diff --git a/en/codes/javascript/test_all.js b/en/codes/javascript/test_all.js index b43ef49f5..cadc57c46 100644 --- a/en/codes/javascript/test_all.js +++ b/en/codes/javascript/test_all.js @@ -47,7 +47,7 @@ for (const result of results) { } console.log(`Tested ${entries.length} files`); -console.log(`Found exception in ${errors.length} files`); +console.log(`Exceptions occurred in ${errors.length} files`); if (errors.length) { console.log(); diff --git a/en/codes/kotlin/chapter_array_and_linkedlist/array.kt b/en/codes/kotlin/chapter_array_and_linkedlist/array.kt index fdbcf7be4..d3cbd19f9 100644 --- a/en/codes/kotlin/chapter_array_and_linkedlist/array.kt +++ b/en/codes/kotlin/chapter_array_and_linkedlist/array.kt @@ -8,59 +8,59 @@ package chapter_array_and_linkedlist import java.util.concurrent.ThreadLocalRandom -/* Random access to element */ +/* Access an element at a random index */ fun randomAccess(nums: IntArray): Int { - // Randomly select a number in interval [0, nums.size) + // Randomly select a number in [0, nums.size) val randomIndex = ThreadLocalRandom.current().nextInt(0, nums.size) - // Retrieve and return the random element + // Get and return the randomly selected element val randomNum = nums[randomIndex] return randomNum } -/* Extend array length */ +/* Extend the array length */ fun extend(nums: IntArray, enlarge: Int): IntArray { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for (i in nums.size - 1 downTo index + 1) { nums[i] = nums[i - 1] } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num } -/* Remove the element at index index */ +/* Remove the element at index `index` */ fun remove(nums: IntArray, index: Int) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (i in index..= size) throw IndexOutOfBoundsException("Index out of bounds") return arr[index] } - /* Add elements at the end */ + /* Update an element */ 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 */ + /* Append at the end */ fun add(num: Int) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if (size == capacity()) extendCapacity() arr[size] = num @@ -53,14 +53,14 @@ class MyList { size++ } - /* Sort list */ + /* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if (size == capacity()) extendCapacity() - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for (j in size - 1 downTo index) arr[j + 1] = arr[j] arr[index] = num @@ -68,12 +68,12 @@ class MyList { size++ } - /* Remove element */ + /* Remove an 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 + // Shift all elements after index `index` one position to the left for (j in index..>() for (sRow in state) { @@ -25,21 +25,21 @@ fun backtrack( res.add(copyState) return } - // Traverse all columns + // Iterate over all columns for (col in 0..>?> { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square val state = mutableListOf>() for (i in 0..() @@ -59,9 +59,9 @@ fun nQueens(n: Int): MutableList>?> { } state.add(row) } - val cols = BooleanArray(n) // Record whether there is a queen in the column - val diags1 = BooleanArray(2 * n - 1) // Record whether there is a queen on the main diagonal - val diags2 = BooleanArray(2 * n - 1) // Record whether there is a queen on the anti-diagonal + val cols = BooleanArray(n) // Track whether a column contains a queen + val diags1 = BooleanArray(2 * n - 1) // Track whether a main diagonal contains a queen + val diags2 = BooleanArray(2 * n - 1) // Track whether an anti-diagonal contains a queen val res = mutableListOf>?>() backtrack(0, n, state, res, cols, diags1, diags2) @@ -69,13 +69,13 @@ fun nQueens(n: Int): MutableList>?> { return res } -/* Driver Code */ +/* Driver code */ fun main() { val n = 4 val res = nQueens(n) - println("Input board size is $n") - println("Total queen placement solutions: ${res.size}") + println("Input board width and height: $n") + println("Number of queen placements: ${res.size}") for (state in res) { println("--------------------") for (row in state!!) { diff --git a/en/codes/kotlin/chapter_backtracking/permutations_i.kt b/en/codes/kotlin/chapter_backtracking/permutations_i.kt index 263229f82..bfd011b53 100644 --- a/en/codes/kotlin/chapter_backtracking/permutations_i.kt +++ b/en/codes/kotlin/chapter_backtracking/permutations_i.kt @@ -6,29 +6,29 @@ package chapter_backtracking.permutations_i -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ fun backtrack( state: MutableList, choices: IntArray, selected: BooleanArray, res: MutableList?> ) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.size == choices.size) { res.add(state.toMutableList()) return } - // Traverse all choices + // Iterate over all choices for (i in choices.indices) { val choice = choices[i] - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true state.add(choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false state.removeAt(state.size - 1) } @@ -42,7 +42,7 @@ fun permutationsI(nums: IntArray): MutableList?> { return res } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(1, 2, 3) diff --git a/en/codes/kotlin/chapter_backtracking/permutations_ii.kt b/en/codes/kotlin/chapter_backtracking/permutations_ii.kt index 26c6e3a85..98312d5e0 100644 --- a/en/codes/kotlin/chapter_backtracking/permutations_ii.kt +++ b/en/codes/kotlin/chapter_backtracking/permutations_ii.kt @@ -6,31 +6,31 @@ package chapter_backtracking.permutations_ii -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ fun backtrack( state: MutableList, choices: IntArray, selected: BooleanArray, res: MutableList?> ) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.size == choices.size) { res.add(state.toMutableList()) return } - // Traverse all choices + // Iterate over all choices val duplicated = HashSet() for (i in choices.indices) { val choice = choices[i] - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && !duplicated.contains(choice)) { - // Attempt: make choice, update state - duplicated.add(choice) // Record the selected element value + // Try: make a choice and update the state + duplicated.add(choice) // Record the chosen value selected[i] = true state.add(choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false state.removeAt(state.size - 1) } @@ -44,7 +44,7 @@ fun permutationsII(nums: IntArray): MutableList?> { return res } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(1, 2, 2) val res = permutationsII(nums) diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_i_compact.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_i_compact.kt index cf3ff83dc..090d5b26b 100644 --- a/en/codes/kotlin/chapter_backtracking/preorder_traversal_i_compact.kt +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_i_compact.kt @@ -11,23 +11,23 @@ import utils.printTree var res: MutableList? = null -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ fun preOrder(root: TreeNode?) { if (root == null) { return } if (root._val == 7) { - // Record solution + // Record the solution res!!.add(root) } preOrder(root.left) preOrder(root.right) } -/* Driver Code */ +/* Driver code */ fun main() { val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) - println("\nInitialize binary tree") + println("\nInitialize the binary tree") printTree(root) // Preorder traversal diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_ii_compact.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_ii_compact.kt index ef1beb258..4f402e05f 100644 --- a/en/codes/kotlin/chapter_backtracking/preorder_traversal_ii_compact.kt +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_ii_compact.kt @@ -12,15 +12,15 @@ import utils.printTree var path: MutableList? = null var res: MutableList>? = null -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ fun preOrder(root: TreeNode?) { if (root == null) { return } - // Attempt + // Try path!!.add(root) if (root._val == 7) { - // Record solution + // Record the solution res!!.add(path!!.toMutableList()) } preOrder(root.left) @@ -29,10 +29,10 @@ fun preOrder(root: TreeNode?) { path!!.removeAt(path!!.size - 1) } -/* Driver Code */ +/* Driver code */ fun main() { val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) - println("\nInitialize binary tree") + println("\nInitialize the binary tree") printTree(root) // Preorder traversal @@ -40,7 +40,7 @@ fun main() { res = mutableListOf() preOrder(root) - println("\nOutput all paths from root node to node 7") + println("\nOutput all paths from the root to nodes with value 7") for (path in res!!) { val _vals = mutableListOf() for (node in path) { diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_compact.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_compact.kt index 8be3543e9..e55411cc0 100644 --- a/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_compact.kt +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_compact.kt @@ -12,16 +12,16 @@ import utils.printTree var path: MutableList? = null var res: MutableList>? = null -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ fun preOrder(root: TreeNode?) { - // Pruning + // Prune if (root == null || root._val == 3) { return } - // Attempt + // Try path!!.add(root) if (root._val == 7) { - // Record solution + // Record the solution res!!.add(path!!.toMutableList()) } preOrder(root.left) @@ -30,10 +30,10 @@ fun preOrder(root: TreeNode?) { path!!.removeAt(path!!.size - 1) } -/* Driver Code */ +/* Driver code */ fun main() { val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) - println("\nInitialize binary tree") + println("\nInitialize the binary tree") printTree(root) // Preorder traversal @@ -41,7 +41,7 @@ fun main() { res = mutableListOf() preOrder(root) - println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + println("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3") for (path in res!!) { val _vals = mutableListOf() for (node in path) { diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_template.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_template.kt index 70e241677..c0a11163f 100644 --- a/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_template.kt +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_template.kt @@ -9,67 +9,67 @@ package chapter_backtracking.preorder_traversal_iii_template import utils.TreeNode import utils.printTree -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ fun isSolution(state: MutableList): Boolean { return state.isNotEmpty() && state[state.size - 1]?._val == 7 } -/* Record solution */ +/* Record the solution */ fun recordSolution(state: MutableList?, res: MutableList?>) { res.add(state!!.toMutableList()) } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ fun isValid(state: MutableList?, choice: TreeNode?): Boolean { return choice != null && choice._val != 3 } -/* Update state */ +/* Update the state */ fun makeChoice(state: MutableList, choice: TreeNode?) { state.add(choice) } -/* Restore state */ +/* Restore the state */ fun undoChoice(state: MutableList, choice: TreeNode?) { state.removeLast() } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ fun backtrack( state: MutableList, choices: MutableList, res: MutableList?> ) { - // Check if it is a solution + // Check whether this is a solution if (isSolution(state)) { - // Record solution + // Record the solution recordSolution(state, res) } - // Traverse all choices + // Iterate over all choices for (choice in choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state, choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, mutableListOf(choice!!.left, choice.right), res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice) } } } -/* Driver Code */ +/* Driver code */ fun main() { val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) - println("\nInitialize binary tree") + println("\nInitialize the binary tree") printTree(root) // Backtracking algorithm val res = mutableListOf?>() backtrack(mutableListOf(), mutableListOf(root), res) - println("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3") + println("\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3") for (path in res) { val vals = mutableListOf() for (node in path!!) { diff --git a/en/codes/kotlin/chapter_backtracking/subset_sum_i.kt b/en/codes/kotlin/chapter_backtracking/subset_sum_i.kt index fcb7f2c94..3605f759b 100644 --- a/en/codes/kotlin/chapter_backtracking/subset_sum_i.kt +++ b/en/codes/kotlin/chapter_backtracking/subset_sum_i.kt @@ -6,7 +6,7 @@ package chapter_backtracking.subset_sum_i -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ fun backtrack( state: MutableList, target: Int, @@ -14,24 +14,24 @@ fun backtrack( start: Int, res: MutableList?> ) { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target (remaining target is 0) if (target == 0) { res.add(state.toMutableList()) return } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (i in start..?> { val state = mutableListOf() // State (subset) nums.sort() // Sort nums - val start = 0 // Start point for traversal - val res = mutableListOf?>() // Result list (subset list) + val start = 0 // Starting index for iteration + val res = mutableListOf?>() // Result list (list of subsets) backtrack(state, target, nums, start, res) return res } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(3, 4, 5) val target = 9 @@ -54,5 +54,5 @@ fun main() { val res = subsetSumI(nums, target) println("Input array nums = ${nums.contentToString()}, target = $target") - println("All subsets with sum equal to $target res = $res") + println("All subsets with sum $target: res = $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/subset_sum_i_naive.kt b/en/codes/kotlin/chapter_backtracking/subset_sum_i_naive.kt index c44a37a4b..c2d5f1ad5 100644 --- a/en/codes/kotlin/chapter_backtracking/subset_sum_i_naive.kt +++ b/en/codes/kotlin/chapter_backtracking/subset_sum_i_naive.kt @@ -6,7 +6,7 @@ package chapter_backtracking.subset_sum_i_naive -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ fun backtrack( state: MutableList, target: Int, @@ -14,42 +14,42 @@ fun backtrack( choices: IntArray, res: MutableList?> ) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total == target) { res.add(state.toMutableList()) return } - // Traverse all choices + // Iterate over all choices for (i in choices.indices) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.add(choices[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target, total + choices[i], choices, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeAt(state.size - 1) } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ fun subsetSumINaive(nums: IntArray, target: Int): MutableList?> { val state = mutableListOf() // State (subset) val total = 0 // Subset sum - val res = mutableListOf?>() // Result list (subset list) + val res = mutableListOf?>() // Result list (list of subsets) backtrack(state, target, total, nums, res) return res } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(3, 4, 5) val target = 9 val res = subsetSumINaive(nums, target) println("Input array nums = ${nums.contentToString()}, target = $target") - println("All subsets with sum equal to $target res = $res") - println("Please note that this method outputs results containing duplicate sets") + println("All subsets with sum $target: res = $res") + println("Note that this method produces duplicate subsets") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/subset_sum_ii.kt b/en/codes/kotlin/chapter_backtracking/subset_sum_ii.kt index 77bb910da..6f003ab88 100644 --- a/en/codes/kotlin/chapter_backtracking/subset_sum_ii.kt +++ b/en/codes/kotlin/chapter_backtracking/subset_sum_ii.kt @@ -6,7 +6,7 @@ package chapter_backtracking.subset_sum_ii -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ fun backtrack( state: MutableList, target: Int, @@ -14,29 +14,29 @@ fun backtrack( start: Int, res: MutableList?> ) { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target (remaining target is 0) if (target == 0) { res.add(state.toMutableList()) return } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: start after the previously chosen index to avoid choosing the same element again for (i in start.. start && choices[i] == choices[i - 1]) { continue } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.add(choices[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeAt(state.size - 1) } } @@ -45,18 +45,18 @@ fun backtrack( fun subsetSumII(nums: IntArray, target: Int): MutableList?> { val state = mutableListOf() // State (subset) nums.sort() // Sort nums - val start = 0 // Start point for traversal - val res = mutableListOf?>() // Result list (subset list) + val start = 0 // Starting index for iteration + val res = mutableListOf?>() // Result list (list of subsets) backtrack(state, target, nums, start, res) return res } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(4, 4, 5) val target = 9 val res = subsetSumII(nums, target) println("Input array nums = ${nums.contentToString()}, target = $target") - println("All subsets with sum equal to $target res = $res") + println("All subsets with sum $target: res = $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_computational_complexity/complexity_exercises.kt b/en/codes/kotlin/chapter_computational_complexity/complexity_exercises.kt index f23c48c8f..d3ac21b75 100644 --- a/en/codes/kotlin/chapter_computational_complexity/complexity_exercises.kt +++ b/en/codes/kotlin/chapter_computational_complexity/complexity_exercises.kt @@ -6,7 +6,7 @@ package chapter_computational_complexity.complexity_exercises -/* Iterative summation */ +/* Sum using iteration */ fun sumIter(n: Int): Int { var res = 0 for (i in 1..n) { @@ -15,7 +15,7 @@ fun sumIter(n: Int): Int { return res } -/* Recursive summation */ +/* Sum using recursion */ fun sumRecur(n: Int): Int { if (n == 1) { return 1 @@ -23,7 +23,7 @@ fun sumRecur(n: Int): Int { return n + sumRecur(n - 1) } -/* Linear loop */ +/* Linear-time loop */ fun linearLoop(n: Int): Int { var res = 0 for (i in 0 until n) { @@ -32,7 +32,7 @@ fun linearLoop(n: Int): Int { return res } -/* Quadratic loop */ +/* Quadratic-time loop */ fun quadraticLoop(n: Int): Int { var res = 0 for (i in 0 until n) { @@ -43,7 +43,7 @@ fun quadraticLoop(n: Int): Int { return res } -/* Logarithmic loop */ +/* Logarithmic-time loop */ fun logarithmicLoop(n: Int): Int { var value = n while (value > 1) { diff --git a/en/codes/kotlin/chapter_computational_complexity/iteration.kt b/en/codes/kotlin/chapter_computational_complexity/iteration.kt index b8a9143c4..fdafeed34 100644 --- a/en/codes/kotlin/chapter_computational_complexity/iteration.kt +++ b/en/codes/kotlin/chapter_computational_complexity/iteration.kt @@ -9,7 +9,7 @@ package chapter_computational_complexity.iteration /* for loop */ fun forLoop(n: Int): Int { var res = 0 - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (i in 1..n) { res += i } @@ -19,11 +19,11 @@ fun forLoop(n: Int): Int { /* while loop */ fun whileLoop(n: Int): Int { var res = 0 - var i = 1 // Initialize condition variable - // Sum 1, 2, ..., n-1, n + var i = 1 // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i - i++ // Update condition variable + i++ // Update the loop control variable } return res } @@ -31,23 +31,23 @@ fun whileLoop(n: Int): Int { /* while loop (two updates) */ fun whileLoopII(n: Int): Int { var res = 0 - var i = 1 // Initialize condition variable - // Sum 1, 4, 10, ... + var i = 1 // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i - // Update condition variable + // Update the loop control variable i++ i *= 2 } return res } -/* Nested for loop */ +/* Nested for loops */ fun nestedForLoop(n: Int): String { val res = StringBuilder() - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (i in 1..n) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (j in 1..n) { res.append(" ($i, $j), ") } @@ -55,20 +55,20 @@ fun nestedForLoop(n: Int): String { return res.toString() } -/* Driver Code */ +/* Driver code */ fun main() { val n = 5 var res: Int res = forLoop(n) - println("\nFor loop sum result res = $res") + println("\nSum from the for loop: res = $res") res = whileLoop(n) - println("\nWhile loop sum result res = $res") + println("\nSum from the while loop: res = $res") res = whileLoopII(n) - println("\nWhile loop (two updates) sum result res = $res") + println("\nSum from the while loop (two updates): res = $res") val resStr = nestedForLoop(n) - println("\nNested for loop traversal result $resStr") + println("\nTraversal result from the nested for loops: $resStr") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_computational_complexity/recursion.kt b/en/codes/kotlin/chapter_computational_complexity/recursion.kt index ac7ac707f..d752faaae 100644 --- a/en/codes/kotlin/chapter_computational_complexity/recursion.kt +++ b/en/codes/kotlin/chapter_computational_complexity/recursion.kt @@ -10,12 +10,12 @@ import java.util.* /* Recursion */ fun recur(n: Int): Int { - // Termination condition + // Base case if (n == 1) return 1 - // Descend: recursive call + // Descending: recursive calls val res = recur(n - 1) - // Return: return result + // Unwinding: return the result return n + res } @@ -24,14 +24,14 @@ fun forLoopRecur(n: Int): Int { // Use an explicit stack to simulate the system call stack val stack = Stack() var res = 0 - // Descend: recursive call + // Descending: recursive calls for (i in n downTo 0) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.push(i) } - // Return: return result + // Unwinding: return the result while (stack.isNotEmpty()) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.pop() } // res = 1+2+3+...+n @@ -40,39 +40,39 @@ fun forLoopRecur(n: Int): Int { /* Tail recursion */ tailrec fun tailRecur(n: Int, res: Int): Int { - // Add tailrec keyword to enable tail recursion optimization - // Termination condition + // Add the tailrec modifier to enable tail-call optimization + // Base case if (n == 0) return res - // Tail recursive call + // Tail-recursive call return tailRecur(n - 1, res + n) } /* Fibonacci sequence: recursion */ fun fib(n: Int): Int { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n == 1 || n == 2) return n - 1 - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) val res = fib(n - 1) + fib(n - 2) - // Return result f(n) + // Return the result f(n) return res } -/* Driver Code */ +/* Driver code */ fun main() { val n = 5 var res: Int res = recur(n) - println("\nRecursion sum result res = $res") + println("\nSum from the recursive function: res = $res") res = forLoopRecur(n) - println("\nUsing iteration to simulate recursion sum result res = $res") + println("\nSum from simulating recursion with iteration: res = $res") res = tailRecur(n, 0) - println("\nTail recursion sum result res = $res") + println("\nSum from the tail-recursive function: res = $res") res = fib(n) - println("\nThe ${n}th Fibonacci number is $res") + println("\nTerm $n of the Fibonacci sequence is $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_computational_complexity/space_complexity.kt b/en/codes/kotlin/chapter_computational_complexity/space_complexity.kt index 849dcb6a0..db3ac9d6a 100644 --- a/en/codes/kotlin/chapter_computational_complexity/space_complexity.kt +++ b/en/codes/kotlin/chapter_computational_complexity/space_complexity.kt @@ -16,9 +16,9 @@ fun function(): Int { return 0 } -/* Constant order */ +/* Constant complexity */ fun constant(n: Int) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space val a = 0 var b = 0 val nums = Array(10000) { 0 } @@ -27,41 +27,41 @@ fun constant(n: Int) { for (i in 0..() for (i in 0..() for (i in 0..?>(n) - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space val numList = mutableListOf>() for (i in 0..() @@ -72,17 +72,17 @@ fun quadratic(n: Int) { } } -/* Quadratic order (recursive implementation) */ +/* Quadratic allocation total (recursive example; tailrec removes recursive stack growth) */ tailrec fun quadraticRecur(n: Int): Int { if (n <= 0) return 0 - // Array nums has length n, n-1, ..., 2, 1 + // The lengths of array nums are n, n-1, ..., 2, 1 val nums = Array(n) { 0 } - println("In recursion n = $n, nums length = ${nums.size}") + println("In the recursive call with n = $n, nums has length ${nums.size}") return quadraticRecur(n - 1) } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ fun buildTree(n: Int): TreeNode? { if (n == 0) return null @@ -92,18 +92,18 @@ fun buildTree(n: Int): TreeNode? { return root } -/* Driver Code */ +/* Driver code */ fun main() { val n = 5 - // Constant order + // Constant complexity constant(n) - // Linear order + // Linear complexity linear(n) linearRecur(n) - // Exponential order + // Quadratic complexity quadratic(n) quadraticRecur(n) - // Exponential order + // Exponential complexity val root = buildTree(n) printTree(root) } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_computational_complexity/time_complexity.kt b/en/codes/kotlin/chapter_computational_complexity/time_complexity.kt index 8fa193134..fcfcc9af1 100644 --- a/en/codes/kotlin/chapter_computational_complexity/time_complexity.kt +++ b/en/codes/kotlin/chapter_computational_complexity/time_complexity.kt @@ -6,7 +6,7 @@ package chapter_computational_complexity.time_complexity -/* Constant order */ +/* Constant complexity */ fun constant(n: Int): Int { var count = 0 val size = 100000 @@ -15,7 +15,7 @@ fun constant(n: Int): Int { return count } -/* Linear order */ +/* Linear complexity */ fun linear(n: Int): Int { var count = 0 for (i in 0.. nums[j + 1]) { // Swap nums[j] and nums[j + 1] val temp = nums[j] nums[j] = nums[j + 1] nums[j + 1] = temp - count += 3 // Element swap includes 3 unit operations + count += 3 // Swapping two elements takes 3 elementary operations } } } return count } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ fun exponential(n: Int): Int { var count = 0 var base = 1 - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (i in 0.. { val nums = IntArray(n) - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (i in 0..(n) for (i in 0.. { return res } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ fun findOne(nums: Array): Int { for (i in nums.indices) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] == 1) return i } return -1 } -/* Driver Code */ +/* Driver code */ fun main() { for (i in 0..9) { val n = 100 val nums = randomNumbers(n) val index = findOne(nums) println("\nArray [ 1, 2, ..., n ] after shuffling = ${nums.contentToString()}") - println("Index of number 1 is $index") + println("Index of the number 1: $index") } } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_divide_and_conquer/binary_search_recur.kt b/en/codes/kotlin/chapter_divide_and_conquer/binary_search_recur.kt index 2e8ec02df..335bd06e5 100644 --- a/en/codes/kotlin/chapter_divide_and_conquer/binary_search_recur.kt +++ b/en/codes/kotlin/chapter_divide_and_conquer/binary_search_recur.kt @@ -13,20 +13,20 @@ fun dfs( i: Int, j: Int ): Int { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1 } // Calculate the midpoint index m val m = (i + j) / 2 return if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) dfs(nums, target, m + 1, j) } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) dfs(nums, target, i, m - 1) } else { - // Found the target element, return its index + // The target element was found; return its index m } } @@ -34,16 +34,16 @@ fun dfs( /* Binary search */ fun binarySearch(nums: IntArray, target: Int): Int { val n = nums.size - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1) } -/* Driver Code */ +/* Driver code */ fun main() { val target = 6 val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35) - // Binary search (closed interval on both sides) + // Binary search (closed interval) val index = binarySearch(nums, target) println("Index of target element 6 = $index") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_divide_and_conquer/build_tree.kt b/en/codes/kotlin/chapter_divide_and_conquer/build_tree.kt index b07142f18..263e8a1cd 100644 --- a/en/codes/kotlin/chapter_divide_and_conquer/build_tree.kt +++ b/en/codes/kotlin/chapter_divide_and_conquer/build_tree.kt @@ -9,7 +9,7 @@ package chapter_divide_and_conquer.build_tree import utils.TreeNode import utils.printTree -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ fun dfs( preorder: IntArray, inorderMap: Map, @@ -17,11 +17,11 @@ fun dfs( l: Int, r: Int ): TreeNode? { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) return null // Initialize the root node val root = TreeNode(preorder[i]) - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees val m = inorderMap[preorder[i]]!! // Subproblem: build the left subtree root.left = dfs(preorder, inorderMap, i + 1, l, m - 1) @@ -31,9 +31,9 @@ fun dfs( return root } -/* Build binary tree */ +/* Build a binary tree */ fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index val inorderMap = HashMap() for (i in inorder.indices) { inorderMap[inorder[i]] = i @@ -42,12 +42,12 @@ fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { return root } -/* Driver Code */ +/* Driver code */ fun main() { val preorder = intArrayOf(3, 9, 2, 1, 7) val inorder = intArrayOf(9, 3, 1, 2, 7) - println("Pre-order traversal = ${preorder.contentToString()}") - println("In-order traversal = ${inorder.contentToString()}") + println("Preorder traversal = ${preorder.contentToString()}") + println("Inorder traversal = ${inorder.contentToString()}") val root = buildTree(preorder, inorder) println("The constructed binary tree is:") diff --git a/en/codes/kotlin/chapter_divide_and_conquer/fast_power.kt b/en/codes/kotlin/chapter_divide_and_conquer/fast_power.kt index 65c2e38bf..57bd431fb 100644 --- a/en/codes/kotlin/chapter_divide_and_conquer/fast_power.kt +++ b/en/codes/kotlin/chapter_divide_and_conquer/fast_power.kt @@ -6,7 +6,7 @@ package chapter_divide_and_conquer.fast_power -/* Exponentiation by squaring */ +/* Fast exponentiation */ fun fastPow(x: Int, n: Int): Int { if (n == 0) { return 1 diff --git a/en/codes/kotlin/chapter_divide_and_conquer/hanota.kt b/en/codes/kotlin/chapter_divide_and_conquer/hanota.kt index 81ee8db2b..e1d72584f 100644 --- a/en/codes/kotlin/chapter_divide_and_conquer/hanota.kt +++ b/en/codes/kotlin/chapter_divide_and_conquer/hanota.kt @@ -6,9 +6,9 @@ package chapter_divide_and_conquer.hanota -/* Move a disk */ +/* Move one disk */ fun move(src: MutableList, tar: MutableList) { - // Take out a disk from the top of src + // Take a disk from the top of src val pan = src.removeAt(src.size - 1) // Place the disk on top of tar tar.add(pan) @@ -16,14 +16,14 @@ fun move(src: MutableList, tar: MutableList) { /* Solve the Tower of Hanoi problem f(i) */ fun dfs(i: Int, src: MutableList, buf: MutableList, tar: MutableList) { - // If there is only one disk left in src, move it directly to tar + // If only one disk in this subproblem remains on src, move it directly to tar if (i == 1) { move(src, tar) return } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf) - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar) // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar) @@ -36,20 +36,20 @@ fun solveHanota(A: MutableList, B: MutableList, C: MutableList) { dfs(n, A, B, C) } -/* Driver Code */ +/* Driver code */ fun main() { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg val A = mutableListOf(5, 4, 3, 2, 1) val B = mutableListOf() val C = mutableListOf() - println("In initial state:") + println("Initial state:") println("A = $A") println("B = $B") println("C = $C") solveHanota(A, B, C) - println("After disk movement is complete:") + println("After moving the disks:") println("A = $A") println("B = $B") println("C = $C") diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt index 8fee13f88..cd7559d18 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt @@ -13,33 +13,33 @@ fun backtrack( n: Int, res: MutableList ) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state == n) res[0] = res[0] + 1 - // Traverse all choices + // Iterate over all choices for (choice in choices) { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res) // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ fun climbingStairsBacktrack(n: Int): Int { - val choices = mutableListOf(1, 2) // Can choose to climb up 1 or 2 stairs - val state = 0 // Start climbing from the 0-th stair + val choices = mutableListOf(1, 2) // Choose to climb 1 or 2 steps + val state = 0 // Start climbing from step 0 val res = mutableListOf() - res.add(0) // Use res[0] to record the solution count + res.add(0) // Use res[0] to record the number of ways backtrack(choices, state, n, res) return res[0] } -/* Driver Code */ +/* Driver code */ fun main() { val n = 9 val res = climbingStairsBacktrack(n) - println("Climbing $n stairs has $res solutions") + println("There are $res ways to climb $n stairs") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt index a1e686e9d..0d82ac44a 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt @@ -6,19 +6,19 @@ package chapter_dynamic_programming -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ fun climbingStairsConstraintDP(n: Int): Int { if (n == 1 || n == 2) { return 1 } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions val dp = Array(n + 1) { IntArray(3) } - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1 dp[1][2] = 0 dp[2][1] = 0 dp[2][2] = 1 - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (i in 3..n) { dp[i][1] = dp[i - 1][2] dp[i][2] = dp[i - 2][1] + dp[i - 2][2] @@ -26,10 +26,10 @@ fun climbingStairsConstraintDP(n: Int): Int { return dp[n][1] + dp[n][2] } -/* Driver Code */ +/* Driver code */ fun main() { val n = 9 val res = climbingStairsConstraintDP(n) - println("Climbing $n stairs has $res solutions") + println("There are $res ways to climb $n stairs") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt index c3a50470b..b2a099218 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt @@ -8,22 +8,22 @@ package chapter_dynamic_programming /* Search */ fun dfs(i: Int): Int { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i // dp[i] = dp[i-1] + dp[i-2] val count = dfs(i - 1) + dfs(i - 2) return count } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ fun climbingStairsDFS(n: Int): Int { return dfs(n) } -/* Driver Code */ +/* Driver code */ fun main() { val n = 9 val res = climbingStairsDFS(n) - println("Climbing $n stairs has $res solutions") + println("There are $res ways to climb $n stairs") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs_mem.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs_mem.kt index 0ba68dbc2..83e292244 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs_mem.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs_mem.kt @@ -6,11 +6,11 @@ package chapter_dynamic_programming -/* Memoization search */ +/* Memoized search */ fun dfs(i: Int, mem: IntArray): Int { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i == 1 || i == 2) return i - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i] // dp[i] = dp[i-1] + dp[i-2] val count = dfs(i - 1, mem) + dfs(i - 2, mem) @@ -19,18 +19,18 @@ fun dfs(i: Int, mem: IntArray): Int { return count } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ fun climbingStairsDFSMem(n: Int): Int { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded val mem = IntArray(n + 1) mem.fill(-1) return dfs(n, mem) } -/* Driver Code */ +/* Driver code */ fun main() { val n = 9 val res = climbingStairsDFSMem(n) - println("Climbing $n stairs has $res solutions") + println("There are $res ways to climb $n stairs") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt index f6a4faf77..d44ef1a64 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt @@ -6,22 +6,22 @@ package chapter_dynamic_programming -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ fun climbingStairsDP(n: Int): Int { if (n == 1 || n == 2) return n - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions val dp = IntArray(n + 1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1 dp[2] = 2 - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (i in 3..n) { dp[i] = dp[i - 1] + dp[i - 2] } return dp[n] } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ fun climbingStairsDPComp(n: Int): Int { if (n == 1 || n == 2) return n var a = 1 @@ -34,13 +34,13 @@ fun climbingStairsDPComp(n: Int): Int { return b } -/* Driver Code */ +/* Driver code */ fun main() { val n = 9 var res = climbingStairsDP(n) - println("Climbing $n stairs has $res solutions") + println("There are $res ways to climb $n stairs") res = climbingStairsDPComp(n) - println("Climbing $n stairs has $res solutions") + println("There are $res ways to climb $n stairs") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/coin_change.kt b/en/codes/kotlin/chapter_dynamic_programming/coin_change.kt index 05e9e3180..79bb99158 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/coin_change.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/coin_change.kt @@ -8,24 +8,24 @@ package chapter_dynamic_programming import kotlin.math.min -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ fun coinChangeDP(coins: IntArray, amt: Int): Int { val n = coins.size val MAX = amt + 1 - // Initialize dp table + // Initialize the dp table val dp = Array(n + 1) { IntArray(amt + 1) } - // State transition: first row and first column + // State transitions: first row and first column for (a in 1..amt) { dp[0][a] = MAX } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (i in 1..n) { for (a in 1..amt) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin value exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a] } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) } } @@ -33,22 +33,22 @@ fun coinChangeDP(coins: IntArray, amt: Int): Int { return if (dp[n][amt] != MAX) dp[n][amt] else -1 } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ fun coinChangeDPComp(coins: IntArray, amt: Int): Int { val n = coins.size val MAX = amt + 1 - // Initialize dp table + // Initialize the dp table val dp = IntArray(amt + 1) dp.fill(MAX) dp[0] = 0 - // State transition + // State transitions for (i in 1..n) { for (a in 1..amt) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin value exceeds the current amount a, do not choose coin i dp[a] = dp[a] } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) } } @@ -56,16 +56,16 @@ fun coinChangeDPComp(coins: IntArray, amt: Int): Int { return if (dp[amt] != MAX) dp[amt] else -1 } -/* Driver Code */ +/* Driver code */ fun main() { val coins = intArrayOf(1, 2, 5) val amt = 4 // Dynamic programming var res = coinChangeDP(coins, amt) - println("Minimum coins needed to make target amount is $res") + println("Minimum number of coins needed to make the target amount: $res") // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt) - println("Minimum coins needed to make target amount is $res") + println("Minimum number of coins needed to make the target amount: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt b/en/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt index 65fccef40..4842bd767 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt @@ -6,23 +6,23 @@ package chapter_dynamic_programming -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ fun coinChangeIIDP(coins: IntArray, amt: Int): Int { val n = coins.size - // Initialize dp table + // Initialize the dp table val dp = Array(n + 1) { IntArray(amt + 1) } - // Initialize first column + // Initialize the first column for (i in 0..n) { dp[i][0] = 1 } - // State transition + // State transitions for (i in 1..n) { for (a in 1..amt) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin value exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a] } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] } } @@ -30,20 +30,20 @@ fun coinChangeIIDP(coins: IntArray, amt: Int): Int { return dp[n][amt] } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ fun coinChangeIIDPComp(coins: IntArray, amt: Int): Int { val n = coins.size - // Initialize dp table + // Initialize the dp table val dp = IntArray(amt + 1) dp[0] = 1 - // State transition + // State transitions for (i in 1..n) { for (a in 1..amt) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin value exceeds the current amount a, do not choose coin i dp[a] = dp[a] } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]] } } @@ -51,16 +51,16 @@ fun coinChangeIIDPComp(coins: IntArray, amt: Int): Int { return dp[amt] } -/* Driver Code */ +/* Driver code */ fun main() { val coins = intArrayOf(1, 2, 5) val amt = 5 // Dynamic programming var res = coinChangeIIDP(coins, amt) - println("Number of coin combinations to make target amount is $res") + println("Number of coin combinations that make the target amount: $res") // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt) - println("Number of coin combinations to make target amount is $res") + println("Number of coin combinations that make the target amount: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/edit_distance.kt b/en/codes/kotlin/chapter_dynamic_programming/edit_distance.kt index 8dc96fe6d..821dd67b5 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/edit_distance.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/edit_distance.kt @@ -8,30 +8,30 @@ package chapter_dynamic_programming import kotlin.math.min -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ fun editDistanceDFS( s: String, t: String, i: Int, j: Int ): Int { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if (i == 0 && j == 0) return 0 - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if (i == 0) return j - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if (j == 0) return i - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1) - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 val insert = editDistanceDFS(s, t, i, j - 1) val delete = editDistanceDFS(s, t, i - 1, j) val replace = editDistanceDFS(s, t, i - 1, j - 1) - // Return minimum edit steps + // Return the minimum number of edits return min(min(insert, delete), replace) + 1 } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ fun editDistanceDFSMem( s: String, t: String, @@ -39,45 +39,45 @@ fun editDistanceDFSMem( i: Int, j: Int ): Int { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if (i == 0 && j == 0) return 0 - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if (i == 0) return j - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if (j == 0) return i - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) return mem[i][j] - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1) - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 val insert = editDistanceDFSMem(s, t, mem, i, j - 1) val delete = editDistanceDFSMem(s, t, mem, i - 1, j) val replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1) - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = min(min(insert, delete), replace) + 1 return mem[i][j] } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ fun editDistanceDP(s: String, t: String): Int { val n = s.length val m = t.length val dp = Array(n + 1) { IntArray(m + 1) } - // State transition: first row and first column + // State transitions: first row and first column for (i in 1..n) { dp[i][0] = i } for (j in 1..m) { dp[0][j] = j } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (i in 1..n) { for (j in 1..m) { if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1] } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1 } } @@ -85,37 +85,37 @@ fun editDistanceDP(s: String, t: String): Int { return dp[n][m] } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ fun editDistanceDPComp(s: String, t: String): Int { val n = s.length val m = t.length val dp = IntArray(m + 1) - // State transition: first row + // State transitions: first row for (j in 1..m) { dp[j] = j } - // State transition: rest of the rows + // State transitions: remaining rows for (i in 1..n) { - // State transition: first column + // State transitions: first column var leftup = dp[0] // Temporarily store dp[i-1, j-1] dp[0] = i - // State transition: rest of the columns + // State transitions: remaining columns for (j in 1..m) { val temp = dp[j] if (s[i - 1] == t[j - 1]) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1 } - leftup = temp // Update for next round's dp[i-1, j-1] + leftup = temp // Update to dp[i-1, j-1] for the next iteration } } return dp[m] } -/* Driver Code */ +/* Driver code */ fun main() { val s = "bag" val t = "pack" @@ -124,20 +124,20 @@ fun main() { // Brute-force search var res = editDistanceDFS(s, t, n, m) - println("Changing $s to $t requires minimum $res edits") + println("Changing $s to $t requires at least $res edits") - // Memoization search + // Memoized search val mem = Array(n + 1) { IntArray(m + 1) } for (row in mem) row.fill(-1) res = editDistanceDFSMem(s, t, mem, n, m) - println("Changing $s to $t requires minimum $res edits") + println("Changing $s to $t requires at least $res edits") // Dynamic programming res = editDistanceDP(s, t) - println("Changing $s to $t requires minimum $res edits") + println("Changing $s to $t requires at least $res edits") // Space-optimized dynamic programming res = editDistanceDPComp(s, t) - println("Changing $s to $t requires minimum $res edits") + println("Changing $s to $t requires at least $res edits") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/knapsack.kt b/en/codes/kotlin/chapter_dynamic_programming/knapsack.kt index 455ddad0a..db4b3a380 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/knapsack.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/knapsack.kt @@ -8,29 +8,29 @@ package chapter_dynamic_programming import kotlin.math.max -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ fun knapsackDFS( wgt: IntArray, _val: IntArray, i: Int, c: Int ): Int { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0 } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFS(wgt, _val, i - 1, c) } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i val no = knapsackDFS(wgt, _val, i - 1, c) val yes = knapsackDFS(wgt, _val, i - 1, c - wgt[i - 1]) + _val[i - 1] - // Return the larger value of the two options + // Return the larger value from the two choices return max(no, yes) } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ fun knapsackDFSMem( wgt: IntArray, _val: IntArray, @@ -38,39 +38,39 @@ fun knapsackDFSMem( i: Int, c: Int ): Int { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i == 0 || c == 0) { return 0 } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] != -1) { return mem[i][c] } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, _val, mem, i - 1, c) } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i val no = knapsackDFSMem(wgt, _val, mem, i - 1, c) val yes = knapsackDFSMem(wgt, _val, mem, i - 1, c - wgt[i - 1]) + _val[i - 1] - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = max(no, yes) return mem[i][c] } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ fun knapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int { val n = wgt.size - // Initialize dp table + // Initialize the dp table val dp = Array(n + 1) { IntArray(cap + 1) } - // State transition + // State transitions for (i in 1..n) { for (c in 1..cap) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + _val[i - 1]) } } @@ -78,17 +78,17 @@ fun knapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int { return dp[n][cap] } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ fun knapsackDPComp(wgt: IntArray, _val: IntArray, cap: Int): Int { val n = wgt.size - // Initialize dp table + // Initialize the dp table val dp = IntArray(cap + 1) - // State transition + // State transitions for (i in 1..n) { - // Traverse in reverse order + // Traverse in decreasing order for (c in cap downTo 1) { if (wgt[i - 1] <= c) { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1]) } } @@ -96,7 +96,7 @@ fun knapsackDPComp(wgt: IntArray, _val: IntArray, cap: Int): Int { return dp[cap] } -/* Driver Code */ +/* Driver code */ fun main() { val wgt = intArrayOf(10, 20, 30, 40, 50) val _val = intArrayOf(50, 120, 150, 210, 240) @@ -105,21 +105,21 @@ fun main() { // Brute-force search var res = knapsackDFS(wgt, _val, n, cap) - println("Maximum item value not exceeding knapsack capacity is $res") + println("Maximum total item value within the knapsack capacity: $res") - // Memoization search + // Memoized search val mem = Array(n + 1) { IntArray(cap + 1) } for (row in mem) { row.fill(-1) } res = knapsackDFSMem(wgt, _val, mem, n, cap) - println("Maximum item value not exceeding knapsack capacity is $res") + println("Maximum total item value within the knapsack capacity: $res") // Dynamic programming res = knapsackDP(wgt, _val, cap) - println("Maximum item value not exceeding knapsack capacity is $res") + println("Maximum total item value within the knapsack capacity: $res") // Space-optimized dynamic programming res = knapsackDPComp(wgt, _val, cap) - println("Maximum item value not exceeding knapsack capacity is $res") + println("Maximum total item value within the knapsack capacity: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt b/en/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt index c4d70b927..2732f7e2d 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt @@ -8,23 +8,23 @@ package chapter_dynamic_programming import kotlin.math.min -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ fun minCostClimbingStairsDP(cost: IntArray): Int { val n = cost.size - 1 if (n == 1 || n == 2) return cost[n] - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions val dp = IntArray(n + 1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1] dp[2] = cost[2] - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (i in 3..n) { dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] } return dp[n] } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ fun minCostClimbingStairsDPComp(cost: IntArray): Int { val n = cost.size - 1 if (n == 1 || n == 2) return cost[n] @@ -38,14 +38,14 @@ fun minCostClimbingStairsDPComp(cost: IntArray): Int { return b } -/* Driver Code */ +/* Driver code */ fun main() { val cost = intArrayOf(0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1) - println("Input stair cost list is ${cost.contentToString()}") + println("Input stair costs: ${cost.contentToString()}") var res = minCostClimbingStairsDP(cost) - println("Minimum cost to climb stairs is $res") + println("Minimum cost to finish climbing the stairs: $res") res = minCostClimbingStairsDPComp(cost) - println("Minimum cost to climb stairs is $res") + println("Minimum cost to finish climbing the stairs: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt b/en/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt index 3d9375d1b..695516b35 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt @@ -8,66 +8,66 @@ package chapter_dynamic_programming import kotlin.math.min -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ fun minPathSumDFS(grid: Array, i: Int, j: Int): Int { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0] } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return Int.MAX_VALUE as an infinite-cost sentinel if (i < 0 || j < 0) { return Int.MAX_VALUE } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) val up = minPathSumDFS(grid, i - 1, j) val left = minPathSumDFS(grid, i, j - 1) - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return min(left, up) + grid[i][j] } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ fun minPathSumDFSMem( grid: Array, mem: Array, i: Int, j: Int ): Int { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i == 0 && j == 0) { return grid[0][0] } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return Int.MAX_VALUE as an infinite-cost sentinel if (i < 0 || j < 0) { return Int.MAX_VALUE } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) { return mem[i][j] } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above val up = minPathSumDFSMem(grid, mem, i - 1, j) val left = minPathSumDFSMem(grid, mem, i, j - 1) - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = min(left, up) + grid[i][j] return mem[i][j] } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ fun minPathSumDP(grid: Array): Int { val n = grid.size val m = grid[0].size - // Initialize dp table + // Initialize the dp table val dp = Array(n) { IntArray(m) } dp[0][0] = grid[0][0] - // State transition: first row + // State transitions: first row for (j in 1..): Int { return dp[n - 1][m - 1] } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ fun minPathSumDPComp(grid: Array): Int { val n = grid.size val m = grid[0].size - // Initialize dp table + // Initialize the dp table val dp = IntArray(m) - // State transition: first row + // State transitions: first row dp[0] = grid[0][0] for (j in 1..): Int { return dp[m - 1] } -/* Driver Code */ +/* Driver code */ fun main() { val grid = arrayOf( intArrayOf(1, 3, 1, 5), @@ -112,21 +112,21 @@ fun main() { // Brute-force search var res = minPathSumDFS(grid, n - 1, m - 1) - println("Minimum path sum from top-left to bottom-right is $res") + println("Minimum path sum from the top-left to the bottom-right corner: $res") - // Memoization search + // Memoized search val mem = Array(n) { IntArray(m) } for (row in mem) { row.fill(-1) } res = minPathSumDFSMem(grid, mem, n - 1, m - 1) - println("Minimum path sum from top-left to bottom-right is $res") + println("Minimum path sum from the top-left to the bottom-right corner: $res") // Dynamic programming res = minPathSumDP(grid) - println("Minimum path sum from top-left to bottom-right is $res") + println("Minimum path sum from the top-left to the bottom-right corner: $res") // Space-optimized dynamic programming res = minPathSumDPComp(grid) - println("Minimum path sum from top-left to bottom-right is $res") + println("Minimum path sum from the top-left to the bottom-right corner: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/unbounded_knapsack.kt b/en/codes/kotlin/chapter_dynamic_programming/unbounded_knapsack.kt index 55e2fb808..bb8985844 100644 --- a/en/codes/kotlin/chapter_dynamic_programming/unbounded_knapsack.kt +++ b/en/codes/kotlin/chapter_dynamic_programming/unbounded_knapsack.kt @@ -8,19 +8,19 @@ package chapter_dynamic_programming import kotlin.math.max -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ fun unboundedKnapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int { val n = wgt.size - // Initialize dp table + // Initialize the dp table val dp = Array(n + 1) { IntArray(cap + 1) } - // State transition + // State transitions for (i in 1..n) { for (c in 1..cap) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + _val[i - 1]) } } @@ -28,23 +28,23 @@ fun unboundedKnapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int { return dp[n][cap] } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ fun unboundedKnapsackDPComp( wgt: IntArray, _val: IntArray, cap: Int ): Int { val n = wgt.size - // Initialize dp table + // Initialize the dp table val dp = IntArray(cap + 1) - // State transition + // State transitions for (i in 1..n) { for (c in 1..cap) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1]) } } @@ -52,7 +52,7 @@ fun unboundedKnapsackDPComp( return dp[cap] } -/* Driver Code */ +/* Driver code */ fun main() { val wgt = intArrayOf(1, 2, 3) val _val = intArrayOf(5, 11, 15) @@ -60,9 +60,9 @@ fun main() { // Dynamic programming var res = unboundedKnapsackDP(wgt, _val, cap) - println("Maximum item value not exceeding knapsack capacity is $res") + println("Maximum total item value within the knapsack capacity: $res") // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, _val, cap) - println("Maximum item value not exceeding knapsack capacity is $res") + println("Maximum total item value within the knapsack capacity: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_adjacency_list.kt b/en/codes/kotlin/chapter_graph/graph_adjacency_list.kt index 2fb2eff76..aed464d82 100644 --- a/en/codes/kotlin/chapter_graph/graph_adjacency_list.kt +++ b/en/codes/kotlin/chapter_graph/graph_adjacency_list.kt @@ -8,9 +8,9 @@ package chapter_graph import utils.Vertex -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ class GraphAdjList(edges: Array>) { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors val adjList = HashMap>() /* Constructor */ @@ -28,7 +28,7 @@ class GraphAdjList(edges: Array>) { return adjList.size } - /* Add edge */ + /* Add an edge */ fun addEdge(vet1: Vertex, vet2: Vertex) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) throw IllegalArgumentException() @@ -37,7 +37,7 @@ class GraphAdjList(edges: Array>) { adjList[vet2]?.add(vet1) } - /* Remove edge */ + /* Remove an edge */ fun removeEdge(vet1: Vertex, vet2: Vertex) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) throw IllegalArgumentException() @@ -46,27 +46,27 @@ class GraphAdjList(edges: Array>) { adjList[vet2]?.remove(vet1) } - /* Add vertex */ + /* Add a vertex */ fun addVertex(vet: Vertex) { if (adjList.containsKey(vet)) return - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list adjList[vet] = mutableListOf() } - /* Remove vertex */ + /* Remove a vertex */ fun removeVertex(vet: Vertex) { if (!adjList.containsKey(vet)) throw IllegalArgumentException() - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list adjList.remove(vet) - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for (list in adjList.values) { list.remove(vet) } } - /* Print adjacency list */ + /* Print the adjacency list */ fun print() { println("Adjacency list =") for (pair in adjList.entries) { @@ -79,9 +79,9 @@ class GraphAdjList(edges: Array>) { } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Add edge */ + /* Initialize the undirected graph */ val v = Vertex.valsToVets(intArrayOf(1, 3, 2, 5, 4)) val edges = arrayOf( arrayOf(v[0], v[1]), @@ -92,30 +92,30 @@ fun main() { arrayOf(v[3], v[4]) ) val graph = GraphAdjList(edges) - println("\nAfter initialization, graph is") + println("\nGraph after initialization:") graph.print() - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.addEdge(v[0]!!, v[2]!!) - println("\nAfter adding edge 1-2, graph is") + println("\nGraph after adding edge 1-2:") graph.print() - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.removeEdge(v[0]!!, v[1]!!) - println("\nAfter removing edge 1-3, graph is") + println("\nGraph after removing edge 1-3:") graph.print() - /* Add vertex */ + /* Add a vertex */ val v5 = Vertex(6) graph.addVertex(v5) - println("\nAfter adding vertex 6, graph is") + println("\nGraph after adding vertex 6:") graph.print() - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.removeVertex(v[1]!!) - println("\nAfter removing vertex 3, graph is") + println("\nGraph after removing vertex 3:") graph.print() } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_adjacency_matrix.kt b/en/codes/kotlin/chapter_graph/graph_adjacency_matrix.kt index 4b5b7a920..f8e040ad4 100644 --- a/en/codes/kotlin/chapter_graph/graph_adjacency_matrix.kt +++ b/en/codes/kotlin/chapter_graph/graph_adjacency_matrix.kt @@ -8,19 +8,19 @@ package chapter_graph import utils.printMatrix -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat(vertices: IntArray, edges: Array) { - val vertices = mutableListOf() // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" - val adjMat = mutableListOf>() // Adjacency matrix, where the row and column indices correspond to the "vertex index" + val vertices = mutableListOf() // Vertex list: elements are vertex values, and indices are vertex indices + val adjMat = mutableListOf>() // Adjacency matrix: row and column indices correspond to vertex indices /* Constructor */ init { - // Add vertex + // Add a vertex for (vertex in vertices) { addVertex(vertex) } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for (edge in edges) { addEdge(edge[0], edge[1]) } @@ -31,10 +31,10 @@ class GraphAdjMat(vertices: IntArray, edges: Array) { return vertices.size } - /* Add vertex */ + /* Add a vertex */ fun addVertex(_val: Int) { val n = size() - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list vertices.add(_val) // Add a row to the adjacency matrix val newRow = mutableListOf() @@ -48,42 +48,42 @@ class GraphAdjMat(vertices: IntArray, edges: Array) { } } - /* Remove vertex */ + /* Remove a vertex */ fun removeVertex(index: Int) { if (index >= size()) throw IndexOutOfBoundsException() - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list vertices.removeAt(index) - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix adjMat.removeAt(index) - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for (row in adjMat) { row.removeAt(index) } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices fun addEdge(i: Int, j: Int) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw IndexOutOfBoundsException() - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal adjMat[i][j] = 1 adjMat[j][i] = 1 } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices fun removeEdge(i: Int, j: Int) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw IndexOutOfBoundsException() adjMat[i][j] = 0 adjMat[j][i] = 0 } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ fun print() { print("Vertex list = ") println(vertices) @@ -92,10 +92,10 @@ class GraphAdjMat(vertices: IntArray, edges: Array) { } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices val vertices = intArrayOf(1, 3, 2, 5, 4) val edges = arrayOf( intArrayOf(0, 1), @@ -106,29 +106,29 @@ fun main() { intArrayOf(3, 4) ) val graph = GraphAdjMat(vertices, edges) - println("\nAfter initialization, graph is") + println("\nGraph after initialization:") graph.print() - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(0, 2) - println("\nAfter adding edge 1-2, graph is") + println("\nGraph after adding edge 1-2:") graph.print() - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(0, 1) - println("\nAfter removing edge 1-3, graph is") + println("\nGraph after removing edge 1-3:") graph.print() - /* Add vertex */ + /* Add a vertex */ graph.addVertex(6) - println("\nAfter adding vertex 6, graph is") + println("\nGraph after adding vertex 6:") graph.print() - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(1) - println("\nAfter removing vertex 3, graph is") + println("\nGraph after removing vertex 3:") graph.print() } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_bfs.kt b/en/codes/kotlin/chapter_graph/graph_bfs.kt index 60086f04e..e0898ccac 100644 --- a/en/codes/kotlin/chapter_graph/graph_bfs.kt +++ b/en/codes/kotlin/chapter_graph/graph_bfs.kt @@ -10,35 +10,35 @@ import utils.Vertex import java.util.* /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList { // Vertex traversal sequence val res = mutableListOf() - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices val visited = HashSet() visited.add(startVet) - // Queue used to implement BFS + // Use a queue to implement BFS val que = LinkedList() que.offer(startVet) - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited while (!que.isEmpty()) { val vet = que.poll() // Dequeue the front vertex - res.add(vet) // Record visited vertex - // Traverse all adjacent vertices of this vertex + res.add(vet) // Record the visited vertex + // Traverse all neighbors of this vertex for (adjVet in graph.adjList[vet]!!) { if (visited.contains(adjVet)) - continue // Skip vertices that have been visited - que.offer(adjVet) // Only enqueue unvisited vertices + continue // Skip vertices that have already been visited + que.offer(adjVet) // Enqueue only unvisited vertices visited.add(adjVet) // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res } -/* Driver Code */ +/* Driver code */ fun main() { - /* Add edge */ + /* Initialize the undirected graph */ val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) val edges = arrayOf( arrayOf(v[0], v[1]), @@ -55,11 +55,11 @@ fun main() { arrayOf(v[7], v[8]) ) val graph = GraphAdjList(edges) - println("\nAfter initialization, graph is") + println("\nGraph after initialization:") graph.print() /* Breadth-first traversal */ val res = graphBFS(graph, v[0]!!) - println("\nBreadth-first traversal (BFS) vertex sequence is") + println("\nVertex sequence from breadth-first traversal (BFS):") println(Vertex.vetsToVals(res)) } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_dfs.kt b/en/codes/kotlin/chapter_graph/graph_dfs.kt index 64f2879b2..881d040fd 100644 --- a/en/codes/kotlin/chapter_graph/graph_dfs.kt +++ b/en/codes/kotlin/chapter_graph/graph_dfs.kt @@ -15,31 +15,31 @@ fun dfs( res: MutableList, vet: Vertex? ) { - res.add(vet) // Record visited vertex + res.add(vet) // Record the visited vertex visited.add(vet) // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for (adjVet in graph.adjList[vet]!!) { if (visited.contains(adjVet)) - continue // Skip vertices that have been visited - // Recursively visit adjacent vertices + continue // Skip vertices that have already been visited + // Recursively visit the neighboring vertex dfs(graph, visited, res, adjVet) } } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList { // Vertex traversal sequence val res = mutableListOf() - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices val visited = HashSet() dfs(graph, visited, res, startVet) return res } -/* Driver Code */ +/* Driver code */ fun main() { - /* Add edge */ + /* Initialize the undirected graph */ val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6)) val edges = arrayOf( arrayOf(v[0], v[1]), @@ -50,11 +50,11 @@ fun main() { arrayOf(v[5], v[6]) ) val graph = GraphAdjList(edges) - println("\nAfter initialization, graph is") + println("\nGraph after initialization:") graph.print() /* Depth-first traversal */ val res = graphDFS(graph, v[0]) - println("\nDepth-first traversal (DFS) vertex sequence is") + println("\nVertex sequence from depth-first traversal (DFS):") println(Vertex.vetsToVals(res)) } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/coin_change_greedy.kt b/en/codes/kotlin/chapter_greedy/coin_change_greedy.kt index 962337f56..9a9289082 100644 --- a/en/codes/kotlin/chapter_greedy/coin_change_greedy.kt +++ b/en/codes/kotlin/chapter_greedy/coin_change_greedy.kt @@ -6,15 +6,15 @@ package chapter_greedy -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ fun coinChangeGreedy(coins: IntArray, amt: Int): Int { - // Assume coins list is sorted + // Assume coins is sorted in ascending order var am = amt var i = coins.size - 1 var count = 0 - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (am > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while (i > 0 && coins[i] > am) { i-- } @@ -22,32 +22,32 @@ fun coinChangeGreedy(coins: IntArray, amt: Int): Int { am -= coins[i] count++ } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return if (am == 0) count else -1 } -/* Driver Code */ +/* Driver code */ fun main() { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution var coins = intArrayOf(1, 5, 10, 20, 50, 100) var amt = 186 var res = coinChangeGreedy(coins, amt) println("\ncoins = ${coins.contentToString()}, amt = $amt") - println("Minimum coins needed to make $amt is $res") + println("For amount $amt, the greedy coin count is $res") - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = intArrayOf(1, 20, 50) amt = 60 res = coinChangeGreedy(coins, amt) println("\ncoins = ${coins.contentToString()}, amt = $amt") - println("Minimum coins needed to make $amt is $res") - println("Actually the minimum number needed is 3, i.e., 20 + 20 + 20") + println("For amount $amt, the greedy coin count is $res") + println("The actual minimum is 3 coins: 20 + 20 + 20") - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = intArrayOf(1, 49, 50) amt = 98 res = coinChangeGreedy(coins, amt) println("\ncoins = ${coins.contentToString()}, amt = $amt") - println("Minimum coins needed to make $amt is $res") - println("Actually the minimum number needed is 2, i.e., 49 + 49") + println("For amount $amt, the greedy coin count is $res") + println("The actual minimum is 2 coins: 49 + 49") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/fractional_knapsack.kt b/en/codes/kotlin/chapter_greedy/fractional_knapsack.kt index d8f26cf09..f91b37070 100644 --- a/en/codes/kotlin/chapter_greedy/fractional_knapsack.kt +++ b/en/codes/kotlin/chapter_greedy/fractional_knapsack.kt @@ -8,38 +8,38 @@ package chapter_greedy /* Item */ class Item( - val w: Int, // Item + val w: Int, // Item weight val v: Int // Item value ) -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value var cap = c val items = arrayOfNulls(wgt.size) for (i in wgt.indices) { items[i] = Item(wgt[i], _val[i]) } - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) } - // Loop for greedy selection + // Repeatedly make greedy choices var res = 0.0 for (item in items) { if (item!!.w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v cap -= item.w } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += item.v.toDouble() / item.w * cap - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break } } return res } -/* Driver Code */ +/* Driver code */ fun main() { val wgt = intArrayOf(10, 20, 30, 40, 50) val _val = intArrayOf(50, 120, 150, 210, 240) @@ -47,5 +47,5 @@ fun main() { // Greedy algorithm val res = fractionalKnapsack(wgt, _val, cap) - println("Maximum item value not exceeding knapsack capacity is $res") + println("Maximum total item value within the knapsack capacity: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/max_capacity.kt b/en/codes/kotlin/chapter_greedy/max_capacity.kt index a8d6015bc..31c2ad6f8 100644 --- a/en/codes/kotlin/chapter_greedy/max_capacity.kt +++ b/en/codes/kotlin/chapter_greedy/max_capacity.kt @@ -9,19 +9,19 @@ package chapter_greedy import kotlin.math.max import kotlin.math.min -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ fun maxCapacity(ht: IntArray): Int { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array var i = 0 var j = ht.size - 1 - // Initial max capacity is 0 + // The initial maximum capacity is 0 var res = 0 - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity val cap = min(ht[i], ht[j]) * (j - i) res = max(res, cap) - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i++ } else { @@ -31,11 +31,11 @@ fun maxCapacity(ht: IntArray): Int { return res } -/* Driver Code */ +/* Driver code */ fun main() { val ht = intArrayOf(3, 8, 5, 2, 7, 7, 3, 4) // Greedy algorithm val res = maxCapacity(ht) - println("Maximum capacity is $res") + println("Maximum container capacity: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/max_product_cutting.kt b/en/codes/kotlin/chapter_greedy/max_product_cutting.kt index cd4b43678..9233f1c28 100644 --- a/en/codes/kotlin/chapter_greedy/max_product_cutting.kt +++ b/en/codes/kotlin/chapter_greedy/max_product_cutting.kt @@ -8,32 +8,32 @@ package chapter_greedy import kotlin.math.pow -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ fun maxProductCutting(n: Int): Int { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1) } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder val a = n / 3 val b = n % 3 if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return 3.0.pow((a - 1)).toInt() * 2 * 2 } if (b == 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return 3.0.pow(a).toInt() * 2 * 2 } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return 3.0.pow(a).toInt() } -/* Driver Code */ +/* Driver code */ fun main() { val n = 58 // Greedy algorithm val res = maxProductCutting(n) - println("Maximum cutting product is $res") + println("Maximum product after splitting: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/array_hash_map.kt b/en/codes/kotlin/chapter_hashing/array_hash_map.kt index 6ec179847..7e3d2a24a 100644 --- a/en/codes/kotlin/chapter_hashing/array_hash_map.kt +++ b/en/codes/kotlin/chapter_hashing/array_hash_map.kt @@ -12,9 +12,9 @@ class Pair( var _val: String ) -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets private val buckets = arrayOfNulls(100) /* Hash function */ @@ -23,14 +23,14 @@ class ArrayHashMap { return index } - /* Query operation */ + /* Lookup operation */ fun get(key: Int): String? { val index = hashFunc(key) val pair = buckets[index] ?: return null return pair._val } - /* Add operation */ + /* Insert operation */ fun put(key: Int, _val: String) { val pair = Pair(key, _val) val index = hashFunc(key) @@ -40,7 +40,7 @@ class ArrayHashMap { /* Remove operation */ fun remove(key: Int) { val index = hashFunc(key) - // Set to null to represent deletion + // Set the bucket to null to mark it as removed buckets[index] = null } @@ -74,7 +74,7 @@ class ArrayHashMap { return valueSet } - /* Print hash table */ + /* Print the hash table */ fun print() { for (kv in pairSet()) { val key = kv.key @@ -84,42 +84,42 @@ class ArrayHashMap { } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize hash table */ + /* Initialize the hash table */ val map = ArrayHashMap() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha") - map.put(15937, "Xiao Luo") - map.put(16750, "Xiao Suan") - map.put(13276, "Xiao Fa") - map.put(10583, "Xiao Ya") - println("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈") + map.put(15937, "小啰") + map.put(16750, "小算") + map.put(13276, "小法") + map.put(10583, "小鸭") + println("\nHash table after insertion:\nKey -> Value") map.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value val name = map.get(15937) - println("\nInput student ID 15937, found name $name") + println("\nLook up student ID 15937 to obtain the name $name") /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583) - println("\nAfter removing 10583, hash table is\nKey -> Value") + println("\nHash table after removing 10583:\nKey -> Value") map.print() - /* Traverse hash table */ - println("\nTraverse key-value pairs Key -> Value") + /* Traverse the hash table */ + println("\nTraverse key-value pairs: Key -> Value") for (kv in map.pairSet()) { println("${kv.key} -> ${kv._val}") } - println("\nTraverse keys only Key") + println("\nTraverse keys only: Key") for (key in map.keySet()) { println(key) } - println("\nTraverse values only Value") + println("\nTraverse values only: Value") for (_val in map.valueSet()) { println(_val) } diff --git a/en/codes/kotlin/chapter_hashing/built_in_hash.kt b/en/codes/kotlin/chapter_hashing/built_in_hash.kt index 2023f775b..53e691ae1 100644 --- a/en/codes/kotlin/chapter_hashing/built_in_hash.kt +++ b/en/codes/kotlin/chapter_hashing/built_in_hash.kt @@ -8,29 +8,29 @@ package chapter_hashing import utils.ListNode -/* Driver Code */ +/* Driver code */ fun main() { val num = 3 val hashNum = num.hashCode() - println("Hash value of integer $num is $hashNum") + println("Integer $num has hash value $hashNum") val bol = true val hashBol = bol.hashCode() - println("Hash value of boolean $bol is $hashBol") + println("Boolean $bol has hash value $hashBol") val dec = 3.14159 val hashDec = dec.hashCode() - println("Hash value of decimal $dec is $hashDec") + println("Floating-point number $dec has hash value $hashDec") - val str = "Hello Algo" + val str = "Hello 算法" val hashStr = str.hashCode() - println("Hash value of string $str is $hashStr") + println("String $str has hash value $hashStr") - val arr = arrayOf(12836, "Xiao Ha") + val arr = arrayOf(12836, "小哈") val hashTup = arr.contentHashCode() - println("Hash value of array ${arr.contentToString()} is $hashTup") + println("Array ${arr.contentToString()} has hash value $hashTup") val obj = ListNode(0) val hashObj = obj.hashCode() - println("Hash value of node object $obj is $hashObj") + println("Node object $obj has hash value $hashObj") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/hash_map.kt b/en/codes/kotlin/chapter_hashing/hash_map.kt index 95f20152d..bf2ead267 100644 --- a/en/codes/kotlin/chapter_hashing/hash_map.kt +++ b/en/codes/kotlin/chapter_hashing/hash_map.kt @@ -8,42 +8,42 @@ package chapter_hashing import utils.printHashMap -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize hash table */ + /* Initialize the hash table */ val map = HashMap() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map[12836] = "Xiao Ha" - map[15937] = "Xiao Luo" - map[16750] = "Xiao Suan" - map[13276] = "Xiao Fa" - map[10583] = "Xiao Ya" - println("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map[12836] = "小哈" + map[15937] = "小啰" + map[16750] = "小算" + map[13276] = "小法" + map[10583] = "小鸭" + println("\nHash table after insertion:\nKey -> Value") printHashMap(map) - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value val name = map[15937] - println("\nInput student ID 15937, found name $name") + println("\nLook up student ID 15937 to obtain the name $name") /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583) - println("\nAfter removing 10583, hash table is\nKey -> Value") + println("\nHash table after removing 10583:\nKey -> Value") printHashMap(map) - /* Traverse hash table */ - println("\nTraverse key-value pairs Key->Value") + /* Traverse the hash table */ + println("\nTraverse key-value pairs: Key->Value") for ((key, value) in map) { println("$key -> $value") } - println("\nTraverse keys only Key") + println("\nTraverse keys only: Key") for (key in map.keys) { println(key) } - println("\nTraverse values only Value") + println("\nTraverse values only: Value") for (_val in map.values) { println(_val) } diff --git a/en/codes/kotlin/chapter_hashing/hash_map_chaining.kt b/en/codes/kotlin/chapter_hashing/hash_map_chaining.kt index 0af4877f0..7ae6ba1fb 100644 --- a/en/codes/kotlin/chapter_hashing/hash_map_chaining.kt +++ b/en/codes/kotlin/chapter_hashing/hash_map_chaining.kt @@ -10,8 +10,8 @@ package chapter_hashing class HashMapChaining { var size: Int // Number of key-value pairs var capacity: Int // Hash table capacity - val loadThres: Double // Load factor threshold for triggering expansion - val extendRatio: Int // Expansion multiplier + val loadThres: Double // Load factor threshold that triggers capacity growth + val extendRatio: Int // Capacity growth factor var buckets: MutableList> // Bucket array /* Constructor */ @@ -36,34 +36,34 @@ class HashMapChaining { return (size / capacity).toDouble() } - /* Query operation */ + /* Lookup operation */ fun get(key: Int): String? { val index = hashFunc(key) val bucket = buckets[index] - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for (pair in bucket) { if (pair.key == key) return pair._val } - // If key is not found, return null + // Return null if key is not found return null } - /* Add operation */ + /* Insert operation */ fun put(key: Int, _val: String) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > loadThres) { extend() } val index = hashFunc(key) val bucket = buckets[index] - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for (pair in bucket) { if (pair.key == key) { pair._val = _val return } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end val pair = Pair(key, _val) bucket.add(pair) size++ @@ -73,7 +73,7 @@ class HashMapChaining { fun remove(key: Int) { val index = hashFunc(key) val bucket = buckets[index] - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (pair in bucket) { if (pair.key == key) { bucket.remove(pair) @@ -83,19 +83,19 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ fun extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table val bucketsTmp = buckets - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio - // mutablelist has no fixed size + // A mutable list has no fixed size buckets = mutableListOf() for (i in 0..() @@ -117,29 +117,29 @@ class HashMapChaining { } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize hash table */ + /* Initialize the hash table */ val map = HashMapChaining() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha") - map.put(15937, "Xiao Luo") - map.put(16750, "Xiao Suan") - map.put(13276, "Xiao Fa") - map.put(10583, "Xiao Ya") - println("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈") + map.put(15937, "小啰") + map.put(16750, "小算") + map.put(13276, "小法") + map.put(10583, "小鸭") + println("\nHash table after insertion:\nKey -> Value") map.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value val name = map.get(13276) - println("\nInput student ID 13276, found name $name") + println("\nLook up student ID 13276 to obtain the name $name") /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(12836) - println("\nAfter removing 12836, hash table is\nKey -> Value") + println("\nHash table after removing 12836:\nKey -> Value") map.print() } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/hash_map_open_addressing.kt b/en/codes/kotlin/chapter_hashing/hash_map_open_addressing.kt index c89ee9458..ef7b5028c 100644 --- a/en/codes/kotlin/chapter_hashing/hash_map_open_addressing.kt +++ b/en/codes/kotlin/chapter_hashing/hash_map_open_addressing.kt @@ -10,10 +10,10 @@ package chapter_hashing class HashMapOpenAddressing { private var size: Int // Number of key-value pairs private var capacity: Int // Hash table capacity - private val loadThres: Double // Load factor threshold for triggering expansion - private val extendRatio: Int // Expansion multiplier + private val loadThres: Double // Load factor threshold that triggers capacity growth + private val extendRatio: Int // Capacity growth factor private var buckets: Array // Bucket array - private val TOMBSTONE: Pair // Removal marker + private val TOMBSTONE: Pair // Tombstone marker /* Constructor */ init { @@ -35,83 +35,83 @@ class HashMapOpenAddressing { return (size / capacity).toDouble() } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ fun findBucket(key: Int): Int { var index = hashFunc(key) var firstTombstone = -1 - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (buckets[index] != null) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (buckets[index]?.key == key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index] buckets[index] = TOMBSTONE - return firstTombstone // Return the moved bucket index + return firstTombstone // Return the bucket index after the move } - return index // Return bucket index + return index // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % capacity } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return if (firstTombstone == -1) index else firstTombstone } - /* Query operation */ + /* Lookup operation */ fun get(key: Int): String? { - // Search for bucket index corresponding to key + // Find the bucket index for key val index = findBucket(key) - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if (buckets[index] != null && buckets[index] != TOMBSTONE) { return buckets[index]?._val } - // If key-value pair does not exist, return null + // Return null if the key-value pair is absent return null } - /* Add operation */ + /* Insert operation */ fun put(key: Int, _val: String) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (loadFactor() > loadThres) { extend() } - // Search for bucket index corresponding to key + // Find the bucket index for key val index = findBucket(key) - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index]!!._val = _val return } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it buckets[index] = Pair(key, _val) size++ } /* Remove operation */ fun remove(key: Int) { - // Search for bucket index corresponding to key + // Find the bucket index for key val index = findBucket(key) - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index] = TOMBSTONE size-- } } - /* Expand hash table */ + /* Grow the hash table capacity */ fun extend() { - // Temporarily store the original hash table + // Keep a reference to the original hash table val bucketsTmp = buckets - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio buckets = arrayOfNulls(capacity) size = 0 - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (pair in bucketsTmp) { if (pair != null && pair != TOMBSTONE) { put(pair.key, pair._val) @@ -119,7 +119,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ fun print() { for (pair in buckets) { if (pair == null) { @@ -133,29 +133,29 @@ class HashMapOpenAddressing { } } -/* Driver Code */ +/* Driver code */ fun main() { - // Initialize hash table + // Initialize the hash table val hashmap = HashMapOpenAddressing() - // Add operation - // Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Xiao Ha") - hashmap.put(15937, "Xiao Luo") - hashmap.put(16750, "Xiao Suan") - hashmap.put(13276, "Xiao Fa") - hashmap.put(10583, "Xiao Ya") - println("\nAfter adding is complete, hash table is\nKey -> Value") + // Insert operation + // Insert the key-value pair (key, val) into the hash table + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + println("\nHash table after insertion:\nKey -> Value") hashmap.print() - // Query operation - // Input key into hash table to get value val + // Lookup operation + // Look up key in the hash table to obtain val val name = hashmap.get(13276) - println("\nInput student ID 13276, found name $name") + println("\nLook up student ID 13276 to obtain the name $name") // Remove operation - // Remove key-value pair (key, val) from hash table + // Remove the key-value pair (key, val) from the hash table hashmap.remove(16750) - println("\nAfter removing 16750, hash table is\nKey -> Value") + println("\nHash table after removing 16750:\nKey -> Value") hashmap.print() } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/simple_hash.kt b/en/codes/kotlin/chapter_hashing/simple_hash.kt index d4ba2fb52..b68546d75 100644 --- a/en/codes/kotlin/chapter_hashing/simple_hash.kt +++ b/en/codes/kotlin/chapter_hashing/simple_hash.kt @@ -36,7 +36,7 @@ fun xorHash(key: String): Int { return hash and MODULUS } -/* Rotational hash */ +/* Rotating hash */ fun rotHash(key: String): Int { var hash = 0L val MODULUS = 1000000007 @@ -46,19 +46,19 @@ fun rotHash(key: String): Int { return hash.toInt() } -/* Driver Code */ +/* Driver code */ fun main() { - val key = "Hello Algo" + val key = "Hello 算法" var hash = addHash(key) - println("Additive hash value is $hash") + println("Additive hash value: $hash") hash = mulHash(key) - println("Multiplicative hash value is $hash") + println("Multiplicative hash value: $hash") hash = xorHash(key) - println("XOR hash value is $hash") + println("XOR hash value: $hash") hash = rotHash(key) - println("Rotational hash value is $hash") + println("Rotating hash value: $hash") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_heap/heap.kt b/en/codes/kotlin/chapter_heap/heap.kt index 962a44bb8..bed2b803d 100644 --- a/en/codes/kotlin/chapter_heap/heap.kt +++ b/en/codes/kotlin/chapter_heap/heap.kt @@ -10,57 +10,57 @@ import utils.printHeap import java.util.* fun testPush(heap: Queue, _val: Int) { - heap.offer(_val) // Element enters heap - print("\nAfter element $_val pushes to heap\n") + heap.offer(_val) // Push an element onto the heap + print("\nAfter pushing $_val onto the heap:\n") printHeap(heap) } fun testPop(heap: Queue) { - val _val = heap.poll() // Time complexity is O(n), not O(nlogn) - print("\nAfter heap top element $_val pops from heap\n") + val _val = heap.poll() // Pop the top element from the heap + print("\nAfter popping the top element $_val from the heap:\n") printHeap(heap) } -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize heap */ - // Python's heapq module implements min heap by default + /* Initialize heaps */ + // Initialize a min heap var minHeap = PriorityQueue() - // Initialize max heap (modify Comparator using lambda expression) + // Initialize a max heap by supplying a lambda expression as the Comparator val maxHeap = PriorityQueue { a: Int, b: Int -> b - a } - println("\nThe following test cases are for max heap") + println("\nThe following example uses a max heap") - /* Element enters heap */ + /* Push an element onto the heap */ testPush(maxHeap, 1) testPush(maxHeap, 3) testPush(maxHeap, 2) testPush(maxHeap, 5) testPush(maxHeap, 4) - /* Check if heap is empty */ + /* Get the top element of the heap */ val peek = maxHeap.peek() - print("\nHeap top element is $peek\n") + print("\nTop element of the heap: $peek\n") - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ testPop(maxHeap) testPop(maxHeap) testPop(maxHeap) testPop(maxHeap) testPop(maxHeap) - /* Get heap size */ + /* Get the heap size */ val size = maxHeap.size - print("\nHeap size is $size\n") + print("\nNumber of elements in the heap: $size\n") - /* Check if heap is empty */ + /* Check whether the heap is empty */ val isEmpty = maxHeap.isEmpty() - print("\nIs heap empty $isEmpty\n") + print("\nIs the heap empty? $isEmpty\n") - /* Input list and build heap */ - // Time complexity is O(n), not O(nlogn) + /* Build a heap from an input list */ + // The time complexity is O(n), not O(nlogn) minHeap = PriorityQueue(mutableListOf(1, 3, 2, 5, 4)) - println("\nAfter inputting list and building min heap") + println("\nAfter building a min heap from the input list:") printHeap(minHeap) } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_heap/my_heap.kt b/en/codes/kotlin/chapter_heap/my_heap.kt index 034aebff0..913f7ef22 100644 --- a/en/codes/kotlin/chapter_heap/my_heap.kt +++ b/en/codes/kotlin/chapter_heap/my_heap.kt @@ -11,32 +11,32 @@ import java.util.* /* Max heap */ class MaxHeap(nums: MutableList?) { - // Use list instead of array, no need to consider capacity expansion + // Use a resizable list instead of an array to avoid managing capacity growth private val maxHeap = mutableListOf() - /* Constructor, build heap based on input list */ + /* Constructor: build a heap from the input list */ init { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order maxHeap.addAll(nums!!) - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for (i in parent(size() - 1) downTo 0) { siftDown(i) } } - /* Get index of left child node */ + /* Get the left child index */ private fun left(i: Int): Int { return 2 * i + 1 } - /* Get index of right child node */ + /* Get the right child index */ private fun right(i: Int): Int { return 2 * i + 2 } - /* Get index of parent node */ + /* Get the parent index */ private fun parent(i: Int): Int { - return (i - 1) / 2 // Floor division + return (i - 1) / 2 // Integer division (truncates toward zero in Kotlin) } /* Swap elements */ @@ -46,81 +46,81 @@ class MaxHeap(nums: MutableList?) { maxHeap[j] = temp } - /* Get heap size */ + /* Get the heap size */ fun size(): Int { return maxHeap.size } - /* Check if heap is empty */ + /* Check whether the heap is empty */ fun isEmpty(): Boolean { - /* Check if heap is empty */ + /* Check whether the heap is empty */ return size() == 0 } - /* Access top element */ + /* Access the top element of the heap */ fun peek(): Int { return maxHeap[0] } - /* Element enters heap */ + /* Push an element onto the heap */ fun push(_val: Int) { - // Add node + // Add a node maxHeap.add(_val) - // Heapify from bottom to top + // Sift up siftUp(size() - 1) } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ private fun siftUp(it: Int) { - // Kotlin function parameters are immutable, so create temporary variable + // Kotlin function parameters are immutable, so create a temporary variable var i = it while (true) { - // Get parent node of node i + // Get the parent of node i val p = parent(i) - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || maxHeap[i] <= maxHeap[p]) break - // Swap two nodes + // Swap the two nodes swap(i, p) - // Loop upward heapify + // Continue sifting up i = p } } - /* Element exits heap */ + /* Pop an element from the heap */ fun pop(): Int { - // Handle empty case + // Handle an empty heap if (isEmpty()) throw IndexOutOfBoundsException() - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) swap(0, size() - 1) - // Remove node + // Remove a node val _val = maxHeap.removeAt(size() - 1) - // Return top element + // Sift down siftDown(0) - // Return heap top element + // Return the top element of the heap return _val } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ private fun siftDown(it: Int) { - // Kotlin function parameters are immutable, so create temporary variable + // Kotlin function parameters are immutable, so create a temporary variable var i = it while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma val l = left(i) val r = right(i) var ma = i if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break - // Swap two nodes + // Swap the two nodes swap(i, ma) - // Loop downwards heapification + // Continue sifting down i = ma } } - /* Driver Code */ + /* Print the heap (binary tree) */ fun print() { val queue = PriorityQueue { a: Int, b: Int -> b - a } queue.addAll(maxHeap) @@ -128,33 +128,33 @@ class MaxHeap(nums: MutableList?) { } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ val maxHeap = MaxHeap(mutableListOf(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2)) - println("\nAfter inputting list and building heap") + println("\nAfter building a heap from the input list:") maxHeap.print() - /* Check if heap is empty */ + /* Get the top element of the heap */ var peek = maxHeap.peek() - print("\nHeap top element is $peek\n") + print("\nTop element of the heap: $peek\n") - /* Element enters heap */ + /* Push an element onto the heap */ val _val = 7 maxHeap.push(_val) - print("\nAfter element $_val pushes to heap\n") + print("\nAfter pushing $_val onto the heap:\n") maxHeap.print() - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.pop() - print("\nAfter heap top element $peek pops from heap\n") + print("\nAfter popping the top element $peek from the heap:\n") maxHeap.print() - /* Get heap size */ + /* Get the heap size */ val size = maxHeap.size() - print("\nHeap size is $size\n") + print("\nNumber of elements in the heap: $size\n") - /* Check if heap is empty */ + /* Check whether the heap is empty */ val isEmpty = maxHeap.isEmpty() - print("\nIs heap empty $isEmpty\n") + print("\nIs the heap empty? $isEmpty\n") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_heap/top_k.kt b/en/codes/kotlin/chapter_heap/top_k.kt index 80109baaf..6f953f324 100644 --- a/en/codes/kotlin/chapter_heap/top_k.kt +++ b/en/codes/kotlin/chapter_heap/top_k.kt @@ -9,17 +9,17 @@ package chapter_heap import utils.printHeap import java.util.* -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ fun topKHeap(nums: IntArray, k: Int): Queue { - // Python's heapq module implements min heap by default + // Initialize a min heap val heap = PriorityQueue() - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for (i in 0.. heap.peek()) { heap.poll() heap.offer(nums[i]) @@ -28,11 +28,11 @@ fun topKHeap(nums: IntArray, k: Int): Queue { return heap } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(1, 7, 6, 3, 2) val k = 3 val res = topKHeap(nums, k) - println("The largest $k elements are") + println("The $k largest elements are:") printHeap(res) } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/binary_search.kt b/en/codes/kotlin/chapter_searching/binary_search.kt index 3052e3ad7..0af76de3d 100644 --- a/en/codes/kotlin/chapter_searching/binary_search.kt +++ b/en/codes/kotlin/chapter_searching/binary_search.kt @@ -6,54 +6,54 @@ package chapter_searching -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ fun binarySearch(nums: IntArray, target: Int): Int { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements var i = 0 var j = nums.size - 1 - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { val m = i + (j - i) / 2 // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j] + if (nums[m] < target) // In this case, target must be in [m+1, j] if it exists i = m + 1 - else if (nums[m] > target) // This means target is in the interval [i, m-1] + else if (nums[m] > target) // In this case, target must be in [i, m-1] if it exists j = m - 1 - else // Found the target element, return its index + else // The target element was found; return its index return m } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ fun binarySearchLCRO(nums: IntArray, target: Int): Int { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last var i = 0 var j = nums.size - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { val m = i + (j - i) / 2 // Calculate the midpoint index m - if (nums[m] < target) // This means target is in the interval [m+1, j) + if (nums[m] < target) // In this case, target must be in [m+1, j) if it exists i = m + 1 - else if (nums[m] > target) // This means target is in the interval [i, m) + else if (nums[m] > target) // In this case, target must be in [i, m) if it exists j = m - else // Found the target element, return its index + else // The target element was found; return its index return m } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } -/* Driver Code */ +/* Driver code */ fun main() { val target = 6 val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35) - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ var index = binarySearch(nums, target) println("Index of target element 6 = $index") - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums, target) println("Index of target element 6 = $index") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/binary_search_edge.kt b/en/codes/kotlin/chapter_searching/binary_search_edge.kt index 0b1b54a7f..430e62a3b 100644 --- a/en/codes/kotlin/chapter_searching/binary_search_edge.kt +++ b/en/codes/kotlin/chapter_searching/binary_search_edge.kt @@ -6,43 +6,43 @@ package chapter_searching -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ fun binarySearchLeftEdge(nums: IntArray, target: Int): Int { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target val i = binarySearchInsertion(nums, target) - // Target not found, return -1 + // If target was not found, return -1 if (i == nums.size || nums[i] != target) { return -1 } - // Found target, return index i + // Target was found; return index i return i } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ fun binarySearchRightEdge(nums: IntArray, target: Int): Int { - // Convert to finding the leftmost target + 1 + // For integer values, find the leftmost insertion point of target + 1 (assuming no overflow) val i = binarySearchInsertion(nums, target + 1) - // j points to the rightmost target, i points to the first element greater than target + // i is the index of the first element greater than target (or the array length); j is the candidate rightmost target index val j = i - 1 - // Target not found, return -1 + // If target was not found, return -1 if (j == -1 || nums[j] != target) { return -1 } - // Found target, return index j + // Target was found; return index j return j } -/* Driver Code */ +/* Driver code */ fun main() { - // Array with duplicate elements + // Array containing duplicate elements val nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15) println("\nArray nums = ${nums.contentToString()}") - // Binary search left and right boundaries + // Find the left and right boundaries using binary search for (target in intArrayOf(6, 7)) { var index = binarySearchLeftEdge(nums, target) - println("Leftmost element $target index is $index") + println("Index of the leftmost occurrence of $target: $index") index = binarySearchRightEdge(nums, target) - println("Rightmost element $target index is $index") + println("Index of the rightmost occurrence of $target: $index") } } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/binary_search_insertion.kt b/en/codes/kotlin/chapter_searching/binary_search_insertion.kt index 2b9cf71d7..848394902 100644 --- a/en/codes/kotlin/chapter_searching/binary_search_insertion.kt +++ b/en/codes/kotlin/chapter_searching/binary_search_insertion.kt @@ -6,60 +6,60 @@ package chapter_searching -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ fun binarySearchInsertionSimple(nums: IntArray, target: Int): Int { var i = 0 - var j = nums.size - 1 // Initialize closed interval [0, n-1] + var j = nums.size - 1 // Initialize the closed interval [0, n-1] while (i <= j) { val m = i + (j - i) / 2 // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1 // target is in the interval [m+1, j] + i = m + 1 // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1 // target is in the interval [i, m-1] + j = m - 1 // Target must be in [i, m-1] if it exists } else { - return m // Found target, return insertion point m + return m // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ fun binarySearchInsertion(nums: IntArray, target: Int): Int { var i = 0 - var j = nums.size - 1 // Initialize closed interval [0, n-1] + var j = nums.size - 1 // Initialize the closed interval [0, n-1] while (i <= j) { val m = i + (j - i) / 2 // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1 // target is in the interval [m+1, j] + i = m + 1 // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1 // target is in the interval [i, m-1] + j = m - 1 // Target must be in [i, m-1] if it exists } else { - j = m - 1 // The rightmost element less than target is in the interval [i, m-1] + j = m - 1 // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i return i } -/* Driver Code */ +/* Driver code */ fun main() { - // Array without duplicate elements + // Array with no duplicate elements var nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35) println("\nArray nums = ${nums.contentToString()}") - // Binary search for insertion point + // Find an insertion point using binary search for (target in intArrayOf(6, 9)) { val index = binarySearchInsertionSimple(nums, target) - println("Insertion point index for element $target is $index") + println("Insertion index for $target: $index") } - // Array with duplicate elements + // Array containing duplicate elements nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15) println("\nArray nums = ${nums.contentToString()}") - // Binary search for insertion point + // Find an insertion point using binary search for (target in intArrayOf(2, 6, 20)) { val index = binarySearchInsertion(nums, target) - println("Insertion point index for element $target is $index") + println("Insertion index for $target: $index") } } diff --git a/en/codes/kotlin/chapter_searching/hashing_search.kt b/en/codes/kotlin/chapter_searching/hashing_search.kt index 74c4abf65..ea3496126 100644 --- a/en/codes/kotlin/chapter_searching/hashing_search.kt +++ b/en/codes/kotlin/chapter_searching/hashing_search.kt @@ -8,42 +8,42 @@ package chapter_searching import utils.ListNode -/* Hash search (array) */ +/* Hash-based search (array) */ fun hashingSearchArray(map: Map, target: Int): Int { - // Hash table key: target element, _val: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key return map.getOrDefault(target, -1) } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ fun hashingSearchLinkedList(map: Map, target: Int): ListNode? { - // Hash table key: target node value, _val: node object - // If key is not in hash table, return null + // Hash table key: target node value; value: node object + // Return null if the hash table does not contain this key return map.getOrDefault(target, null) } -/* Driver Code */ +/* Driver code */ fun main() { val target = 3 - /* Hash search (array) */ + /* Hash-based search (array) */ val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8) - // Initialize hash table + // Initialize the hash table val map = HashMap() for (i in nums.indices) { - map[nums[i]] = i // key: element, _val: index + map[nums[i]] = i // Key: element; value: index } val index = hashingSearchArray(map, target) println("Index of target element 3 = $index") - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ var head = ListNode.arrToLinkedList(nums) - // Initialize hash table + // Initialize the hash table val map1 = HashMap() while (head != null) { - map1[head._val] = head // key: node value, _val: node + map1[head._val] = head // Key: node value; value: node head = head.next } val node = hashingSearchLinkedList(map1, target) - println("Node object corresponding to target node value 3 is $node") + println("Node object with target value 3: $node") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/linear_search.kt b/en/codes/kotlin/chapter_searching/linear_search.kt index b1c5e6587..733c16587 100644 --- a/en/codes/kotlin/chapter_searching/linear_search.kt +++ b/en/codes/kotlin/chapter_searching/linear_search.kt @@ -10,13 +10,13 @@ import utils.ListNode /* Linear search (array) */ fun linearSearchArray(nums: IntArray, target: Int): Int { - // Traverse array + // Traverse the array for (i in nums.indices) { - // Found the target element, return its index + // The target element was found; return its index if (nums[i] == target) return i } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } @@ -25,26 +25,26 @@ fun linearSearchLinkedList(h: ListNode?, target: Int): ListNode? { // Traverse the linked list var head = h while (head != null) { - // Found the target node, return it + // The target node was found; return it if (head._val == target) return head head = head.next } - // Target node not found, return null + // The target node was not found; return null return null } -/* Driver Code */ +/* Driver code */ fun main() { val target = 3 - /* Perform linear search in array */ + /* Perform linear search on the array */ val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8) val index = linearSearchArray(nums, target) println("Index of target element 3 = $index") - /* Perform linear search in linked list */ + /* Perform linear search on the linked list */ val head = ListNode.arrToLinkedList(nums) val node = linearSearchLinkedList(head, target) - println("Node object corresponding to target node value 3 is $node") + println("Node object with target value 3: $node") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/two_sum.kt b/en/codes/kotlin/chapter_searching/two_sum.kt index a93d6e033..ce5a38364 100644 --- a/en/codes/kotlin/chapter_searching/two_sum.kt +++ b/en/codes/kotlin/chapter_searching/two_sum.kt @@ -6,10 +6,10 @@ package chapter_searching -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ fun twoSumBruteForce(nums: IntArray, target: Int): IntArray { val size = nums.size - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for (i in 0..() - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (i in 0.. nums[j + 1]) { // Swap nums[j] and nums[j + 1] @@ -24,30 +24,30 @@ fun bubbleSort(nums: IntArray) { /* Bubble sort (flag optimization) */ fun bubbleSortWithFlag(nums: IntArray) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (i in nums.size - 1 downTo 1) { - var flag = false // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + var flag = false // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (j in 0.. nums[j + 1]) { // Swap nums[j] and nums[j + 1] val temp = nums[j] nums[j] = nums[j + 1] nums[j + 1] = temp - flag = true // Record element swap + flag = true // Record that a swap occurred } } - if (!flag) break // No elements were swapped in this round of "bubbling", exit directly + if (!flag) break // Exit immediately if this pass made no swaps } } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(4, 1, 3, 1, 5, 2) bubbleSort(nums) - println("After bubble sort, nums = ${nums.contentToString()}") + println("After bubble sort: nums = ${nums.contentToString()}") val nums1 = intArrayOf(4, 1, 3, 1, 5, 2) bubbleSortWithFlag(nums1) - println("After bubble sort, nums1 = ${nums1.contentToString()}") + println("After bubble sort: nums1 = ${nums1.contentToString()}") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/bucket_sort.kt b/en/codes/kotlin/chapter_sorting/bucket_sort.kt index d97871c67..f9d532984 100644 --- a/en/codes/kotlin/chapter_sorting/bucket_sort.kt +++ b/en/codes/kotlin/chapter_sorting/bucket_sort.kt @@ -8,25 +8,25 @@ package chapter_sorting /* Bucket sort */ fun bucketSort(nums: FloatArray) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = floor(n/2) buckets, expecting about 2 elements per bucket val k = nums.size / 2 val buckets = mutableListOf>() for (i in 0.. nums[ma]) ma = r - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma == i) break - // Swap two nodes + // Swap the two nodes val temp = nums[i] nums[i] = nums[ma] nums[ma] = temp - // Loop downwards heapification + // Continue sifting down i = ma } } /* Heap sort */ fun heapSort(nums: IntArray) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (i in nums.size / 2 - 1 downTo 0) { siftDown(nums, nums.size, i) } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (i in nums.size - 1 downTo 1) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) val temp = nums[0] nums[0] = nums[i] nums[i] = temp - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0) } } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(4, 1, 3, 1, 5, 2) heapSort(nums) - println("After heap sort, nums = ${nums.contentToString()}") + println("After heap sort: nums = ${nums.contentToString()}") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/insertion_sort.kt b/en/codes/kotlin/chapter_sorting/insertion_sort.kt index e84f2ba6f..d3de1b36b 100644 --- a/en/codes/kotlin/chapter_sorting/insertion_sort.kt +++ b/en/codes/kotlin/chapter_sorting/insertion_sort.kt @@ -8,22 +8,22 @@ package chapter_sorting /* Insertion sort */ fun insertionSort(nums: IntArray) { - // Outer loop: sorted elements are 1, 2, ..., n + //Outer loop: the sorted prefix grows to 1, 2, ..., n elements for (i in nums.indices) { val base = nums[i] var j = i - 1 - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j] // Move nums[j] to the right by one position + nums[j + 1] = nums[j] // Shift nums[j] one position to the right j-- } - nums[j + 1] = base // Assign base to the correct position + nums[j + 1] = base // Place base at the correct position } } -/* Driver Code */ +/* Driver code */ fun main() { val nums = intArrayOf(4, 1, 3, 1, 5, 2) insertionSort(nums) - println("After insertion sort, nums = ${nums.contentToString()}") + println("After insertion sort: nums = ${nums.contentToString()}") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/merge_sort.kt b/en/codes/kotlin/chapter_sorting/merge_sort.kt index 65cdd032d..eacd2471b 100644 --- a/en/codes/kotlin/chapter_sorting/merge_sort.kt +++ b/en/codes/kotlin/chapter_sorting/merge_sort.kt @@ -6,16 +6,16 @@ package chapter_sorting -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result val tmp = IntArray(right - left + 1) - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays var i = left var j = mid + 1 var k = 0 - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++] @@ -29,7 +29,7 @@ fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { while (j <= right) { tmp[k++] = nums[j++] } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (l in tmp.indices) { nums[left + l] = tmp[l] } @@ -37,20 +37,20 @@ fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { /* Merge sort */ fun mergeSort(nums: IntArray, left: Int, right: Int) { - // Termination condition - if (left >= right) return // Terminate recursion when subarray length is 1 - // Divide and conquer stage - val mid = left + (right - left) / 2 // Calculate midpoint - mergeSort(nums, left, mid) // Recursively process the left subarray - mergeSort(nums, mid + 1, right) // Recursively process the right subarray - // Merge stage + // Base case + if (left >= right) return // Stop recursing when the subarray has at most one element + // Divide phase + val mid = left + (right - left) / 2 // Calculate the midpoint + mergeSort(nums, left, mid) // Recurse on the left subarray + mergeSort(nums, mid + 1, right) // Recurse on the right subarray + // Merge phase merge(nums, left, mid, right) } -/* Driver Code */ +/* Driver code */ fun main() { /* Merge sort */ val nums = intArrayOf(7, 3, 2, 6, 0, 1, 5, 4) mergeSort(nums, 0, nums.size - 1) - println("After merge sort, nums = ${nums.contentToString()}") + println("After merge sort: nums = ${nums.contentToString()}") } diff --git a/en/codes/kotlin/chapter_sorting/quick_sort.kt b/en/codes/kotlin/chapter_sorting/quick_sort.kt index 257ab0aec..dcfb81b06 100644 --- a/en/codes/kotlin/chapter_sorting/quick_sort.kt +++ b/en/codes/kotlin/chapter_sorting/quick_sort.kt @@ -6,36 +6,36 @@ package chapter_sorting -/* Swap elements */ +/* Swap the elements */ fun swap(nums: IntArray, i: Int, j: Int) { val temp = nums[i] nums[i] = nums[j] nums[j] = temp } -/* Sentinel partition */ +/* Two-pointer partitioning */ fun partition(nums: IntArray, left: Int, right: Int): Int { // Use nums[left] as the pivot var i = left var j = right while (i < j) { while (i < j && nums[j] >= nums[left]) - j-- // Search from right to left for the first element smaller than the pivot + j-- // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++ // Search from left to right for the first element greater than the pivot + i++ // Scan from left to right for the first element larger than the pivot swap(nums, i, j) // Swap these two elements } - swap(nums, i, left) // Swap the pivot to the boundary between the two subarrays - return i // Return the index of the pivot + swap(nums, i, left) // Swap the pivot into the boundary position between the two subarrays + return i // Return the pivot index } /* Quick sort */ fun quickSort(nums: IntArray, left: Int, right: Int) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return - // Sentinel partition + // Two-pointer partitioning val pivot = partition(nums, left, right) - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1) quickSort(nums, pivot + 1, right) } @@ -46,76 +46,76 @@ fun medianThree(nums: IntArray, left: Int, mid: Int, right: Int): Int { val m = nums[mid] val r = nums[right] if ((m in l..r) || (m in r..l)) - return mid // m is between l and r + return mid // m lies between l and r if ((l in m..r) || (l in r..m)) - return left // l is between m and r + return left // l lies between m and r return right } -/* Sentinel partition (median of three) */ +/* Two-pointer partitioning (median of three) */ fun partitionMedian(nums: IntArray, left: Int, right: Int): Int { // Select the median of three candidate elements val med = medianThree(nums, left, (left + right) / 2, right) - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray swap(nums, left, med) // Use nums[left] as the pivot var i = left var j = right while (i < j) { while (i < j && nums[j] >= nums[left]) - j-- // Search from right to left for the first element smaller than the pivot + j-- // Scan from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++ // Search from left to right for the first element greater than the pivot + i++ // Scan from left to right for the first element larger than the pivot swap(nums, i, j) // Swap these two elements } - swap(nums, i, left) // Swap the pivot to the boundary between the two subarrays - return i // Return the index of the pivot + swap(nums, i, left) // Swap the pivot into the boundary position between the two subarrays + return i // Return the pivot index } /* Quick sort */ fun quickSortMedian(nums: IntArray, left: Int, right: Int) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if (left >= right) return - // Sentinel partition + // Two-pointer partitioning val pivot = partitionMedian(nums, left, right) - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums, left, pivot - 1) quickSort(nums, pivot + 1, right) } /* Quick sort (recursion depth optimization) */ fun quickSortTailCall(nums: IntArray, left: Int, right: Int) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element var l = left var r = right while (l < r) { - // Sentinel partition operation + // Perform two-pointer partitioning val pivot = partition(nums, l, r) - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - l < r - pivot) { quickSort(nums, l, pivot - 1) // Recursively sort the left subarray - l = pivot + 1 // Remaining unsorted interval is [pivot + 1, right] + l = pivot + 1 // The remaining unsorted range is [pivot + 1, r] } else { quickSort(nums, pivot + 1, r) // Recursively sort the right subarray - r = pivot - 1 // Remaining unsorted interval is [left, pivot - 1] + r = pivot - 1 // The remaining unsorted range is [l, pivot - 1] } } } -/* Driver Code */ +/* Driver code */ fun main() { /* Quick sort */ val nums = intArrayOf(2, 4, 1, 0, 3, 5) quickSort(nums, 0, nums.size - 1) - println("After quick sort, nums = ${nums.contentToString()}") + println("After quick sort: nums = ${nums.contentToString()}") - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ val nums1 = intArrayOf(2, 4, 1, 0, 3, 5) quickSortMedian(nums1, 0, nums1.size - 1) - println("After quick sort (median pivot optimization), nums1 = ${nums1.contentToString()}") + println("After quick sort (median pivot optimization): nums1 = ${nums1.contentToString()}") /* Quick sort (recursion depth optimization) */ val nums2 = intArrayOf(2, 4, 1, 0, 3, 5) quickSortTailCall(nums2, 0, nums2.size - 1) - println("After quick sort (recursion depth optimization), nums2 = ${nums2.contentToString()}") + println("After quick sort (recursion depth optimization): nums2 = ${nums2.contentToString()}") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/radix_sort.kt b/en/codes/kotlin/chapter_sorting/radix_sort.kt index 9003f9239..fdd28469a 100644 --- a/en/codes/kotlin/chapter_sorting/radix_sort.kt +++ b/en/codes/kotlin/chapter_sorting/radix_sort.kt @@ -6,57 +6,57 @@ package chapter_sorting -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ fun digit(num: Int, exp: Int): Int { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (num / exp) % 10 } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ fun countingSortDigit(nums: IntArray, exp: Int) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 val counter = IntArray(10) val n = nums.size - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for (i in 0.. m) m = num var exp = 1 - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant while (exp <= m) { - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, exp) exp *= 10 } } -/* Driver Code */ +/* Driver code */ fun main() { // Radix sort val nums = intArrayOf( @@ -64,5 +64,5 @@ fun main() { 88906420, 72429244, 30524779, 82060337, 63832996 ) radixSort(nums) - println("After radix sort, nums = ${nums.contentToString()}") + println("After radix sort: nums = ${nums.contentToString()}") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/selection_sort.kt b/en/codes/kotlin/chapter_sorting/selection_sort.kt index c0eab82ec..2c6927091 100644 --- a/en/codes/kotlin/chapter_sorting/selection_sort.kt +++ b/en/codes/kotlin/chapter_sorting/selection_sort.kt @@ -9,24 +9,24 @@ package chapter_sorting /* Selection sort */ fun selectionSort(nums: IntArray) { val n = nums.size - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (i in 0..() - /* Get the length of the stack */ + /* Get the stack size */ fun size(): Int { return stack.size } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ fun isEmpty(): Boolean { return size() == 0 } @@ -32,24 +32,24 @@ class ArrayStack { return stack.removeAt(size() - 1) } - /* Return list for printing */ + /* Access the top element */ fun peek(): Int { if (isEmpty()) throw IndexOutOfBoundsException() return stack[size() - 1] } - /* Convert List to Array and return */ + /* Convert the list to an array and return it */ fun toArray(): Array { return stack.toTypedArray() } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Access top of the stack element */ + /* Initialize the stack */ val stack = ArrayStack() - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1) stack.push(3) stack.push(2) @@ -57,19 +57,19 @@ fun main() { stack.push(4) println("Stack stack = ${stack.toArray().contentToString()}") - /* Return list for printing */ + /* Access the top element */ val peek = stack.peek() - println("Top element peek = $peek") + println("Top element: peek = $peek") - /* Element pop from stack */ + /* Pop an element from the stack */ val pop = stack.pop() - println("Pop element pop = $pop, after pop stack = ${stack.toArray().contentToString()}") + println("Popped element: pop = $pop; stack after removal = ${stack.toArray().contentToString()}") - /* Get the length of the stack */ + /* Get the stack size */ val size = stack.size() - println("Stack length size = $size") + println("Stack size = $size") - /* Check if empty */ + /* Check whether it is empty */ val isEmpty = stack.isEmpty() - println("Is stack empty = $isEmpty") + println("Is the stack empty? $isEmpty") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/deque.kt b/en/codes/kotlin/chapter_stack_and_queue/deque.kt index ccbde78bd..50bd90094 100644 --- a/en/codes/kotlin/chapter_stack_and_queue/deque.kt +++ b/en/codes/kotlin/chapter_stack_and_queue/deque.kt @@ -8,38 +8,38 @@ package chapter_stack_and_queue import java.util.* -/* Driver Code */ +/* Driver code */ fun main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ val deque = LinkedList() deque.offerLast(3) deque.offerLast(2) deque.offerLast(5) println("Deque deque = $deque") - /* Update element */ + /* Access an element */ val peekFirst = deque.peekFirst() - println("Front element peekFirst = $peekFirst") + println("Front element: peekFirst = $peekFirst") val peekLast = deque.peekLast() - println("Rear element peekLast = $peekLast") + println("Rear element: peekLast = $peekLast") - /* Elements enqueue */ + /* Enqueue elements */ deque.offerLast(4) - println("After element 4 enqueues at rear, deque = $deque") + println("After enqueuing 4 at the rear: deque = $deque") deque.offerFirst(1) - println("After element 1 enqueues at front, deque = $deque") + println("After enqueuing 1 at the front: deque = $deque") - /* Element dequeue */ + /* Dequeue an element */ val popLast = deque.pollLast() - println("Dequeue rear element = $popLast, after rear dequeue deque = $deque") + println("Element dequeued from the rear = $popLast; deque after removal = $deque") val popFirst = deque.pollFirst() - println("Dequeue front element = $popFirst, after front dequeue deque = $deque") + println("Element dequeued from the front = $popFirst; deque after removal = $deque") - /* Get the length of the double-ended queue */ + /* Get the deque size */ val size = deque.size - println("Deque length size = $size") + println("Deque size = $size") - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ val isEmpty = deque.isEmpty() - println("Is deque empty = $isEmpty") + println("Is the deque empty? $isEmpty") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_deque.kt b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_deque.kt index 2ffa825d8..e8c717e8d 100644 --- a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_deque.kt +++ b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_deque.kt @@ -8,23 +8,23 @@ package chapter_stack_and_queue /* Doubly linked list node */ class ListNode(var _val: Int) { - // Node value - var next: ListNode? = null // Successor node reference - var prev: ListNode? = null // Predecessor node reference + // The node value is stored in _val + var next: ListNode? = null // Reference to the next node + var prev: ListNode? = null // Reference to the previous node } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ class LinkedListDeque { private var front: ListNode? = null // Head node front private var rear: ListNode? = null // Tail node rear - private var queSize: Int = 0 // Length of the double-ended queue + private var queSize: Int = 0 // Deque size - /* Get the length of the double-ended queue */ + /* Get the deque size */ fun size(): Int { return queSize } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ fun isEmpty(): Boolean { return size() == 0 } @@ -36,28 +36,28 @@ class LinkedListDeque { if (isEmpty()) { rear = node front = rear - // Front of the queue enqueue operation + // Enqueue at the front } else if (isFront) { // Add node to the head of the linked list front?.prev = node node.next = front - front = node // Update head node - // Rear of the queue enqueue operation + front = node // Update the head node + // Enqueue at the rear } else { // Add node to the tail of the linked list rear?.next = node node.prev = rear - rear = node // Update tail node + rear = node // Update the tail node } - queSize++ // Update queue length + queSize++ // Update the queue size } - /* Front of the queue enqueue */ + /* Enqueue at the front */ fun pushFirst(num: Int) { push(num, true) } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ fun pushLast(num: Int) { push(num, false) } @@ -67,56 +67,56 @@ class LinkedListDeque { if (isEmpty()) throw IndexOutOfBoundsException() val _val: Int - // When the deque has one element, front and rear point to the same node + // When the queue size is 1, the head and tail point to the same node if (queSize == 1) { _val = front!!._val front = null rear = null } - // Temporarily store head node value + // Dequeue from the front else if (isFront) { - _val = front!!._val // Delete head node - // Delete head node + _val = front!!._val // Save the head node value + // Remove the head node val fNext = front!!.next fNext!!.prev = null front!!.next = null - front = fNext // Update head node - // Temporarily store tail node value + front = fNext // Update the head node + // Dequeue from the rear } else { - _val = rear!!._val // Delete tail node - // Update tail node + _val = rear!!._val // Save the tail node value + // Remove the tail node val rPrev = rear!!.prev rPrev!!.next = null rear!!.prev = null - rear = rPrev // Update tail node + rear = rPrev // Update the tail node } - queSize-- // Update queue length + queSize-- // Update the queue size return _val } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ fun popFirst(): Int { return pop(true) } - /* Access rear of the queue element */ + /* Dequeue from the rear */ fun popLast(): Int { return pop(false) } - /* Return list for printing */ + /* Access the front element */ fun peekFirst(): Int { if (isEmpty()) throw IndexOutOfBoundsException() return front!!._val } - /* Driver Code */ + /* Access the rear element */ fun peekLast(): Int { if (isEmpty()) throw IndexOutOfBoundsException() return rear!!._val } - /* Return array for printing */ + /* Return an array for printing */ fun toArray(): IntArray { var node = front val res = IntArray(size()) @@ -128,38 +128,38 @@ class LinkedListDeque { } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ val deque = LinkedListDeque() deque.pushLast(3) deque.pushLast(2) deque.pushLast(5) println("Deque deque = ${deque.toArray().contentToString()}") - /* Update element */ + /* Access an element */ val peekFirst = deque.peekFirst() - println("Front element peekFirst = $peekFirst") + println("Front element: peekFirst = $peekFirst") val peekLast = deque.peekLast() - println("Rear element peekLast = $peekLast") + println("Rear element: peekLast = $peekLast") - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(4) - println("After element 4 enqueues at rear, deque = ${deque.toArray().contentToString()}") + println("After enqueuing 4 at the rear: deque = ${deque.toArray().contentToString()}") deque.pushFirst(1) - println("After element 1 enqueues at front, deque = ${deque.toArray().contentToString()}") + println("After enqueuing 1 at the front: deque = ${deque.toArray().contentToString()}") - /* Element dequeue */ + /* Dequeue an element */ val popLast = deque.popLast() - println("Dequeue rear element = ${popLast}, after rear dequeue deque = ${deque.toArray().contentToString()}") + println("Element dequeued from the rear = ${popLast}; deque after removal = ${deque.toArray().contentToString()}") val popFirst = deque.popFirst() - println("Dequeue front element = ${popFirst}, after front dequeue deque = ${deque.toArray().contentToString()}") + println("Element dequeued from the front = ${popFirst}; deque after removal = ${deque.toArray().contentToString()}") - /* Get the length of the double-ended queue */ + /* Get the deque size */ val size = deque.size() - println("Deque length size = $size") + println("Deque size = $size") - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ val isEmpty = deque.isEmpty() - println("Is deque empty = $isEmpty") + println("Is the deque empty? $isEmpty") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_queue.kt b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_queue.kt index 13af0dbd3..c65ca2bb7 100644 --- a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_queue.kt +++ b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_queue.kt @@ -6,20 +6,20 @@ package chapter_stack_and_queue -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue( - // Head node front, tail node rear + // Head node front and tail node rear private var front: ListNode? = null, private var rear: ListNode? = null, private var queSize: Int = 0 ) { - /* Get the length of the queue */ + /* Get the queue size */ fun size(): Int { return queSize } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ fun isEmpty(): Boolean { return size() == 0 } @@ -28,11 +28,11 @@ class LinkedListQueue( fun push(num: Int) { // Add num after the tail node val node = ListNode(num) - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (front == null) { front = node rear = node - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node } else { rear?.next = node rear = node @@ -43,19 +43,19 @@ class LinkedListQueue( /* Dequeue */ fun pop(): Int { val num = peek() - // Delete head node + // Remove the head node front = front?.next queSize-- return num } - /* Return list for printing */ + /* Access the front element */ fun peek(): Int { if (isEmpty()) throw IndexOutOfBoundsException() return front!!._val } - /* Convert linked list to Array and return */ + /* Convert the linked list to an array and return it */ fun toArray(): IntArray { var node = front val res = IntArray(size()) @@ -67,12 +67,12 @@ class LinkedListQueue( } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Access front of the queue element */ + /* Initialize the queue */ val queue = LinkedListQueue() - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1) queue.push(3) queue.push(2) @@ -80,19 +80,19 @@ fun main() { queue.push(4) println("Queue queue = ${queue.toArray().contentToString()}") - /* Return list for printing */ + /* Access the front element */ val peek = queue.peek() - println("Front element peek = $peek") + println("Front element: peek = $peek") - /* Element dequeue */ + /* Dequeue an element */ val pop = queue.pop() - println("Dequeue element pop = $pop, after dequeue queue = ${queue.toArray().contentToString()}") + println("Dequeued element: pop = $pop; queue after removal = ${queue.toArray().contentToString()}") - /* Get the length of the queue */ + /* Get the queue size */ val size = queue.size() - println("Queue length size = $size") + println("Queue size = $size") - /* Check if the queue is empty */ + /* Check whether the queue is empty */ val isEmpty = queue.isEmpty() - println("Is queue empty = $isEmpty") + println("Is the queue empty? $isEmpty") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_stack.kt b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_stack.kt index 574a5c073..ea324aee8 100644 --- a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_stack.kt +++ b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_stack.kt @@ -6,18 +6,18 @@ package chapter_stack_and_queue -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ class LinkedListStack( - private var stackPeek: ListNode? = null, // Use head node as stack top - private var stkSize: Int = 0 // Stack length + private var stackPeek: ListNode? = null, // Use the head node as the stack top + private var stkSize: Int = 0 // Stack size ) { - /* Get the length of the stack */ + /* Get the stack size */ fun size(): Int { return stkSize } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ fun isEmpty(): Boolean { return size() == 0 } @@ -38,13 +38,13 @@ class LinkedListStack( return num } - /* Return list for printing */ + /* Access the top element */ fun peek(): Int? { if (isEmpty()) throw IndexOutOfBoundsException() return stackPeek?._val } - /* Convert List to Array and return */ + /* Convert the list to an array and return it */ fun toArray(): IntArray { var node = stackPeek val res = IntArray(size()) @@ -56,12 +56,12 @@ class LinkedListStack( } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Access top of the stack element */ + /* Initialize the stack */ val stack = LinkedListStack() - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1) stack.push(3) stack.push(2) @@ -69,19 +69,19 @@ fun main() { stack.push(4) println("Stack stack = ${stack.toArray().contentToString()}") - /* Return list for printing */ + /* Access the top element */ val peek = stack.peek()!! - println("Top element peek = $peek") + println("Top element: peek = $peek") - /* Element pop from stack */ + /* Pop an element from the stack */ val pop = stack.pop()!! - println("Pop element pop = $pop, after pop stack = ${stack.toArray().contentToString()}") + println("Popped element: pop = $pop; stack after removal = ${stack.toArray().contentToString()}") - /* Get the length of the stack */ + /* Get the stack size */ val size = stack.size() - println("Stack length size = $size") + println("Stack size = $size") - /* Check if empty */ + /* Check whether it is empty */ val isEmpty = stack.isEmpty() - println("Is stack empty = $isEmpty") + println("Is the stack empty? $isEmpty") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/queue.kt b/en/codes/kotlin/chapter_stack_and_queue/queue.kt index cbfc562b3..d4d7c112a 100644 --- a/en/codes/kotlin/chapter_stack_and_queue/queue.kt +++ b/en/codes/kotlin/chapter_stack_and_queue/queue.kt @@ -8,12 +8,12 @@ package chapter_stack_and_queue import java.util.* -/* Driver Code */ +/* Driver code */ fun main() { - /* Access front of the queue element */ + /* Initialize the queue */ val queue = LinkedList() - /* Elements enqueue */ + /* Enqueue elements */ queue.offer(1) queue.offer(3) queue.offer(2) @@ -21,19 +21,19 @@ fun main() { queue.offer(4) println("Queue queue = $queue") - /* Return list for printing */ + /* Access the front element */ val peek = queue.peek() - println("Front element peek = $peek") + println("Front element: peek = $peek") - /* Element dequeue */ + /* Dequeue an element */ val pop = queue.poll() - println("Dequeue element pop = $pop, after dequeue queue = $queue") + println("Dequeued element: pop = $pop; queue after removal = $queue") - /* Get the length of the queue */ + /* Get the queue size */ val size = queue.size - println("Queue length size = $size") + println("Queue size = $size") - /* Check if the queue is empty */ + /* Check whether the queue is empty */ val isEmpty = queue.isEmpty() - println("Is queue empty = $isEmpty") + println("Is the queue empty? $isEmpty") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/stack.kt b/en/codes/kotlin/chapter_stack_and_queue/stack.kt index 81e09dbad..3ddfe8c57 100644 --- a/en/codes/kotlin/chapter_stack_and_queue/stack.kt +++ b/en/codes/kotlin/chapter_stack_and_queue/stack.kt @@ -8,12 +8,12 @@ package chapter_stack_and_queue import java.util.* -/* Driver Code */ +/* Driver code */ fun main() { - /* Access top of the stack element */ + /* Initialize the stack */ val stack = Stack() - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1) stack.push(3) stack.push(2) @@ -21,19 +21,19 @@ fun main() { stack.push(4) println("Stack stack = $stack") - /* Return list for printing */ + /* Access the top element */ val peek = stack.peek() - println("Top element peek = $peek") + println("Top element: peek = $peek") - /* Element pop from stack */ + /* Pop an element from the stack */ val pop = stack.pop() - println("Pop element pop = $pop, after pop stack = $stack") + println("Popped element: pop = $pop; stack after removal = $stack") - /* Get the length of the stack */ + /* Get the stack size */ val size = stack.size - println("Stack length size = $size") + println("Stack size = $size") - /* Check if empty */ + /* Check whether it is empty */ val isEmpty = stack.isEmpty() - println("Is stack empty = $isEmpty") + println("Is the stack empty? $isEmpty") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/array_binary_tree.kt b/en/codes/kotlin/chapter_tree/array_binary_tree.kt index 51a195a96..c08a34678 100644 --- a/en/codes/kotlin/chapter_tree/array_binary_tree.kt +++ b/en/codes/kotlin/chapter_tree/array_binary_tree.kt @@ -9,31 +9,31 @@ package chapter_tree import utils.TreeNode import utils.printTree -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ class ArrayBinaryTree(private val tree: MutableList) { - /* List capacity */ + /* List size */ fun size(): Int { return tree.size } - /* Get value of node at index i */ + /* Get the value of the node at index i */ fun _val(i: Int): Int? { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return null to represent an empty position if (i < 0 || i >= size()) return null return tree[i] } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ fun left(i: Int): Int { return 2 * i + 1 } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ fun right(i: Int): Int { return 2 * i + 2 } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ fun parent(i: Int): Int { return (i - 1) / 2 } @@ -41,7 +41,7 @@ class ArrayBinaryTree(private val tree: MutableList) { /* Level-order traversal */ fun levelOrder(): MutableList { val res = mutableListOf() - // Traverse array directly + // Traverse the array directly for (i in 0..) { /* Depth-first traversal */ fun dfs(i: Int, order: String, res: MutableList) { - // If empty position, return + // Return if this position is empty if (_val(i) == null) return // Preorder traversal @@ -89,39 +89,39 @@ class ArrayBinaryTree(private val tree: MutableList) { } } -/* Driver Code */ +/* Driver code */ fun main() { - // Initialize binary tree - // Here we use a function to generate binary tree directly from list + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from a list val arr = mutableListOf(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15) val root = TreeNode.listToTree(arr) - println("\nInitialize binary tree\n") - println("Array representation of binary tree:") + println("\nInitialize the binary tree\n") + println("Array representation of the binary tree:") println(arr) - println("Linked list representation of binary tree:") + println("Linked representation of the binary tree:") printTree(root) - // Binary tree class represented by array + // Binary tree class using an array representation val abt = ArrayBinaryTree(arr) - // Access node + // Access a node val i = 1 val l = abt.left(i) val r = abt.right(i) val p = abt.parent(i) - println("Current node index is $i, value is ${abt._val(i)}") - println("Its left child index is $l, value is ${abt._val(l)}") - println("Its right child index is $r, value is ${abt._val(r)}") - println("Its parent node index is $p, value is ${abt._val(p)}") + println("Current node: index = $i, value = ${abt._val(i)}") + println("Its left child: index = $l, value = ${abt._val(l)}") + println("Its right child: index = $r, value = ${abt._val(r)}") + println("Its parent: index = $p, value = ${abt._val(p)}") - // Traverse tree + // Traverse the tree var res = abt.levelOrder() - println("\nLevel-order traversal is: $res") + println("\nLevel-order traversal: $res") res = abt.preOrder() - println("Pre-order traversal is: $res") + println("Preorder traversal: $res") res = abt.inOrder() - println("In-order traversal is: $res") + println("Inorder traversal: $res") res = abt.postOrder() - println("Post-order traversal is: $res") + println("Postorder traversal: $res") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/avl_tree.kt b/en/codes/kotlin/chapter_tree/avl_tree.kt index ec99a6958..dc8bbcee6 100644 --- a/en/codes/kotlin/chapter_tree/avl_tree.kt +++ b/en/codes/kotlin/chapter_tree/avl_tree.kt @@ -14,117 +14,117 @@ import kotlin.math.max class AVLTree { var root: TreeNode? = null // Root node - /* Get node height */ + /* Get the node height */ fun height(node: TreeNode?): Int { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 return node?.height ?: -1 } - /* Update node height */ + /* Update the node height */ private fun updateHeight(node: TreeNode?) { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node?.height = max(height(node?.left), height(node?.right)) + 1 } - /* Get balance factor */ + /* Get the balance factor */ fun balanceFactor(node: TreeNode?): Int { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node == null) return 0 // Node balance factor = left subtree height - right subtree height return height(node.left) - height(node.right) } - /* Right rotation operation */ + /* Right rotation */ private fun rightRotate(node: TreeNode?): TreeNode { val child = node!!.left val grandChild = child!!.right - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.right = node node.left = grandChild - // Update node height + // Update the node height updateHeight(node) updateHeight(child) - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child } - /* Left rotation operation */ + /* Left rotation */ private fun leftRotate(node: TreeNode?): TreeNode { val child = node!!.right val grandChild = child!!.left - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.left = node node.right = grandChild - // Update node height + // Update the node height updateHeight(node) updateHeight(child) - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ private fun rotate(node: TreeNode): TreeNode { - // Get balance factor of node + // Get the balance factor of node val balanceFactor = balanceFactor(node) - // Left-leaning tree + // Left-heavy tree if (balanceFactor > 1) { if (balanceFactor(node.left) >= 0) { - // Right rotation + // Rotate right return rightRotate(node) } else { - // First left rotation then right rotation + // Rotate left, then right node.left = leftRotate(node.left) return rightRotate(node) } } - // Right-leaning tree + // Right-heavy tree if (balanceFactor < -1) { if (balanceFactor(node.right) <= 0) { - // Left rotation + // Rotate left return leftRotate(node) } else { - // First right rotation then left rotation + // Rotate right, then left node.right = rightRotate(node.right) return leftRotate(node) } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node } - /* Insert node */ + /* Insert a node */ fun insert(_val: Int) { root = insertHelper(root, _val) } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ private fun insertHelper(n: TreeNode?, _val: Int): TreeNode { if (n == null) return TreeNode(_val) var node = n - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (_val < node._val) node.left = insertHelper(node.left, _val) else if (_val > node._val) node.right = insertHelper(node.right, _val) else - return node // Duplicate node not inserted, return directly - updateHeight(node) // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + return node // Return without inserting a duplicate node + updateHeight(node) // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node) - // Return root node of subtree + // Return the subtree root return node } - /* Remove node */ + /* Remove a node */ fun remove(_val: Int) { root = removeHelper(root, _val) } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ private fun removeHelper(n: TreeNode?, _val: Int): TreeNode? { var node = n ?: return null - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (_val < node._val) node.left = removeHelper(node.left, _val) else if (_val > node._val) @@ -135,14 +135,14 @@ class AVLTree { node.left else node.right - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child == null) return null - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly else node = child } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value var temp = node.right while (temp!!.left != null) { temp = temp.left @@ -151,52 +151,52 @@ class AVLTree { node._val = temp._val } } - updateHeight(node) // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + updateHeight(node) // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node) - // Return root node of subtree + // Return the subtree root return node } - /* Search node */ + /* Find a node */ fun search(_val: Int): TreeNode? { var cur = root - // Loop search, exit after passing leaf node + // Search while cur is not null; stop when the target is found while (cur != null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur cur = if (cur._val < _val) cur.right!! - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur._val > _val) cur.left - // Found target node, exit loop + // The target node was found; exit the loop else break } - // Return target node + // Return the target node return cur } } fun testInsert(tree: AVLTree, _val: Int) { tree.insert(_val) - println("\nAfter inserting node $_val, AVL tree is") + println("\nAVL tree after inserting node $_val:") printTree(tree.root) } fun testRemove(tree: AVLTree, _val: Int) { tree.remove(_val) - println("\nAfter deleting node $_val, AVL tree is") + println("\nAVL tree after removing node $_val:") printTree(tree.root) } -/* Driver Code */ +/* Driver code */ fun main() { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ val avlTree = AVLTree() - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted testInsert(avlTree, 1) testInsert(avlTree, 2) testInsert(avlTree, 3) @@ -208,16 +208,16 @@ fun main() { testInsert(avlTree, 10) testInsert(avlTree, 6) - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ testInsert(avlTree, 7) - /* Remove node */ - // Delete node with degree 1 - testRemove(avlTree, 8) // Delete node with degree 2 - testRemove(avlTree, 5) // Remove node with degree 1 - testRemove(avlTree, 4) // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + testRemove(avlTree, 8) // Remove a node with 0 children + testRemove(avlTree, 5) // Remove a node with 1 child + testRemove(avlTree, 4) // Remove a node with 2 children - /* Search node */ + /* Look up a node */ val node = avlTree.search(7) - println("\n Found node object is $node, node value = ${node?._val}") + println("\nFound node object: $node; node value = ${node?._val}") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_search_tree.kt b/en/codes/kotlin/chapter_tree/binary_search_tree.kt index d07341954..38f89648f 100644 --- a/en/codes/kotlin/chapter_tree/binary_search_tree.kt +++ b/en/codes/kotlin/chapter_tree/binary_search_tree.kt @@ -11,56 +11,56 @@ import utils.printTree /* Binary search tree */ class BinarySearchTree { - // Initialize empty tree + // Initialize an empty tree private var root: TreeNode? = null - /* Get binary tree root node */ + /* Get the root of the binary tree */ fun getRoot(): TreeNode? { return root } - /* Search node */ + /* Find a node */ fun search(num: Int): TreeNode? { var cur = root - // Loop search, exit after passing leaf node + // Continue searching while cur is not null while (cur != null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur cur = if (cur._val < num) cur.right - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur._val > num) cur.left - // Found target node, exit loop + // The target node was found; exit the loop else break } - // Return target node + // Return the target node return cur } - /* Insert node */ + /* Insert a node */ fun insert(num: Int) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (root == null) { root = TreeNode(num) return } var cur = root var pre: TreeNode? = null - // Loop search, exit after passing leaf node + // Continue searching while cur is not null while (cur != null) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur._val == num) return pre = cur - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur cur = if (cur._val < num) cur.right - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else cur.left } - // Insert node + // Insert a node val node = TreeNode(num) if (pre?._val!! < num) pre.right = node @@ -68,90 +68,90 @@ class BinarySearchTree { pre.left = node } - /* Remove node */ + /* Remove a node */ fun remove(num: Int) { - // If tree is empty, return directly + // Return immediately if the tree is empty if (root == null) return var cur = root var pre: TreeNode? = null - // Loop search, exit after passing leaf node + // Continue searching while cur is not null while (cur != null) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur._val == num) break pre = cur - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur cur = if (cur._val < num) cur.right - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur else cur.left } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if (cur == null) return - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if (cur.left == null || cur.right == null) { - // When number of child nodes = 0 / 1, child = null / that child node + // With 0 / 1 children, child is null / the child node, respectively val child = if (cur.left != null) cur.left else cur.right - // Delete node cur + // Remove node cur if (cur != root) { if (pre!!.left == cur) pre.left = child else pre.right = child } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root root = child } - // Number of child nodes = 2 + // Number of children = 2 } else { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur var tmp = cur.right while (tmp!!.left != null) { tmp = tmp.left } - // Recursively delete node tmp + // Recursively remove node tmp remove(tmp._val) - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur._val = tmp._val } } } -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ val bst = BinarySearchTree() - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree val nums = intArrayOf(8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15) for (num in nums) { bst.insert(num) } - println("\nInitialized binary tree is\n") + println("\nThe initialized binary tree is:\n") printTree(bst.getRoot()) - /* Search node */ + /* Find a node */ val node = bst.search(7) - println("Found node object is $node, node value = ${node?._val}") + println("Found node object: $node; node value = ${node?._val}") - /* Insert node */ + /* Insert a node */ bst.insert(16) - println("\nAfter inserting node 16, binary tree is\n") + println("\nBinary tree after inserting node 16:\n") printTree(bst.getRoot()) - /* Remove node */ + /* Remove a node */ bst.remove(1) - println("\nAfter removing node 1, binary tree is\n") + println("\nBinary tree after removing node 1:\n") printTree(bst.getRoot()) bst.remove(2) - println("\nAfter removing node 2, binary tree is\n") + println("\nBinary tree after removing node 2:\n") printTree(bst.getRoot()) bst.remove(4) - println("\nAfter removing node 4, binary tree is\n") + println("\nBinary tree after removing node 4:\n") printTree(bst.getRoot()) } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_tree.kt b/en/codes/kotlin/chapter_tree/binary_tree.kt index 53fe6b4dc..53fd8eda1 100644 --- a/en/codes/kotlin/chapter_tree/binary_tree.kt +++ b/en/codes/kotlin/chapter_tree/binary_tree.kt @@ -9,32 +9,32 @@ package chapter_tree import utils.TreeNode import utils.printTree -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes val n1 = TreeNode(1) val n2 = TreeNode(2) val n3 = TreeNode(3) val n4 = TreeNode(4) val n5 = TreeNode(5) - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1.left = n2 n1.right = n3 n2.left = n4 n2.right = n5 - println("\nInitialize binary tree\n") + println("\nInitialize the binary tree\n") printTree(n1) - /* Insert node P between n1 -> n2 */ + /* Insert and remove nodes */ val P = TreeNode(0) - // Delete node + // Insert node P between n1 and n2 in n1 -> n2 n1.left = P P.left = n2 - println("\nAfter inserting node P\n") + println("\nAfter inserting node P:\n") printTree(n1) // Remove node P n1.left = n2 - println("\nAfter removing node P\n") + println("\nAfter removing node P:\n") printTree(n1) } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_tree_bfs.kt b/en/codes/kotlin/chapter_tree/binary_tree_bfs.kt index 84656fdc9..59f07cd13 100644 --- a/en/codes/kotlin/chapter_tree/binary_tree_bfs.kt +++ b/en/codes/kotlin/chapter_tree/binary_tree_bfs.kt @@ -12,31 +12,31 @@ import java.util.* /* Level-order traversal */ fun levelOrder(root: TreeNode?): MutableList { - // Initialize queue, add root node + // Initialize a queue and enqueue the root val queue = LinkedList() queue.add(root) - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence val list = mutableListOf() while (queue.isNotEmpty()) { - val node = queue.poll() // Dequeue - list.add(node?._val!!) // Save node value + val node = queue.poll() // Dequeue from the queue + list.add(node?._val!!) // Store the node value if (node.left != null) - queue.offer(node.left) // Left child node enqueue + queue.offer(node.left) // Enqueue the left child if (node.right != null) - queue.offer(node.right) // Right child node enqueue + queue.offer(node.right) // Enqueue the right child } return list } -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize binary tree */ - // Here we use a function to generate binary tree directly from list + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from a list val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7)) - println("\nInitialize binary tree\n") + println("\nInitialize the binary tree\n") printTree(root) /* Level-order traversal */ val list = levelOrder(root) - println("\nLevel-order traversal node print sequence = $list") + println("\nNode sequence from level-order traversal = $list") } \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_tree_dfs.kt b/en/codes/kotlin/chapter_tree/binary_tree_dfs.kt index cad1f6239..2b8ccffdb 100644 --- a/en/codes/kotlin/chapter_tree/binary_tree_dfs.kt +++ b/en/codes/kotlin/chapter_tree/binary_tree_dfs.kt @@ -9,13 +9,13 @@ package chapter_tree import utils.TreeNode import utils.printTree -// Initialize list for storing traversal sequence +// Initialize a list to store the traversal sequence var list = mutableListOf() /* Preorder traversal */ fun preOrder(root: TreeNode?) { if (root == null) return - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree list.add(root._val) preOrder(root.left) preOrder(root.right) @@ -24,7 +24,7 @@ fun preOrder(root: TreeNode?) { /* Inorder traversal */ fun inOrder(root: TreeNode?) { if (root == null) return - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(root.left) list.add(root._val) inOrder(root.right) @@ -33,32 +33,32 @@ fun inOrder(root: TreeNode?) { /* Postorder traversal */ fun postOrder(root: TreeNode?) { if (root == null) return - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(root.left) postOrder(root.right) list.add(root._val) } -/* Driver Code */ +/* Driver code */ fun main() { - /* Initialize binary tree */ - // Here we use a function to generate binary tree directly from list + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from a list val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7)) - println("\nInitialize binary tree\n") + println("\nInitialize the binary tree\n") printTree(root) /* Preorder traversal */ list.clear() preOrder(root) - println("\nPre-order traversal node print sequence = $list") + println("\nNode sequence from preorder traversal = $list") /* Inorder traversal */ list.clear() inOrder(root) - println("\nIn-order traversal node print sequence = $list") + println("\nNode sequence from inorder traversal = $list") /* Postorder traversal */ list.clear() postOrder(root) - println("\nPost-order traversal node print sequence = $list") + println("\nNode sequence from postorder traversal = $list") } \ No newline at end of file diff --git a/en/codes/kotlin/utils/ListNode.kt b/en/codes/kotlin/utils/ListNode.kt index 218161c1a..1bbb76f0a 100644 --- a/en/codes/kotlin/utils/ListNode.kt +++ b/en/codes/kotlin/utils/ListNode.kt @@ -11,7 +11,7 @@ class ListNode(var _val: Int) { var next: ListNode? = null companion object { - /* Deserialize a list into a linked list */ + /* Deserialize an array into a linked list */ fun arrToLinkedList(arr: IntArray): ListNode? { val dum = ListNode(0) var head = dum diff --git a/en/codes/kotlin/utils/PrintUtil.kt b/en/codes/kotlin/utils/PrintUtil.kt index 04a138ab3..63cf51def 100644 --- a/en/codes/kotlin/utils/PrintUtil.kt +++ b/en/codes/kotlin/utils/PrintUtil.kt @@ -10,7 +10,7 @@ import java.util.* class Trunk(var prev: Trunk?, var str: String) -/* Print matrix (Array) */ +/* Print a matrix (array) */ fun printMatrix(matrix: Array>) { println("[") for (row in matrix) { @@ -19,7 +19,7 @@ fun printMatrix(matrix: Array>) { println("]") } -/* Print matrix (List) */ +/* Print a matrix (list) */ fun printMatrix(matrix: MutableList>) { println("[") for (row in matrix) { @@ -28,7 +28,7 @@ fun printMatrix(matrix: MutableList>) { println("]") } -/* Print linked list */ +/* Print a linked list */ fun printLinkedList(h: ListNode?) { var head = h val list = mutableListOf() @@ -39,13 +39,13 @@ fun printLinkedList(h: ListNode?) { println(list.joinToString(separator = " -> ")) } -/* Print binary tree */ +/* Print a binary tree */ fun printTree(root: TreeNode?) { printTree(root, null, false) } /** - * Print binary tree + * Print a binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -88,20 +88,20 @@ fun showTrunks(p: Trunk?) { print(p.str) } -/* Print hash table */ +/* Print the hash table */ fun printHashMap(map: Map) { for ((key, value) in map) { println("${key.toString()} -> $value") } } -/* Print heap */ +/* Print a heap */ fun printHeap(queue: Queue?) { val list = mutableListOf() queue?.let { list.addAll(it) } - print("Heap array representation:") + print("Array representation of the heap:") println(list) - println("Heap tree representation:") + println("Tree representation of the heap:") val root = TreeNode.listToTree(list) printTree(root) } \ No newline at end of file diff --git a/en/codes/kotlin/utils/TreeNode.kt b/en/codes/kotlin/utils/TreeNode.kt index ccbfebdf5..bb93e400b 100644 --- a/en/codes/kotlin/utils/TreeNode.kt +++ b/en/codes/kotlin/utils/TreeNode.kt @@ -12,14 +12,14 @@ class TreeNode( var _val: Int // Node value ) { var height: Int = 0 // Node height - var left: TreeNode? = null // Reference to left child node - var right: TreeNode? = null // Reference to right child node + var left: TreeNode? = null // Reference to the left child + var right: TreeNode? = null // Reference to the right child - // For the serialization encoding rules, please refer to: - // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ - // Array representation of binary tree: + // For the serialization rules, see: + // https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ + // Array representation of the binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] - // Linked list representation of binary tree: + // Linked representation of the binary tree: // /——— 15 // /——— 7 // /——— 3 diff --git a/en/codes/kotlin/utils/Vertex.kt b/en/codes/kotlin/utils/Vertex.kt index 2af403e38..960646b62 100644 --- a/en/codes/kotlin/utils/Vertex.kt +++ b/en/codes/kotlin/utils/Vertex.kt @@ -9,7 +9,7 @@ package utils /* Vertex class */ class Vertex(val _val: Int) { companion object { - /* Input value list vals, return vertex list vets */ + /* Given the array of values vals, return the array of vertices vets */ fun valsToVets(vals: IntArray): Array { val vets = arrayOfNulls(vals.size) for (i in vals.indices) { @@ -18,7 +18,7 @@ class Vertex(val _val: Int) { return vets } - /* Input vertex list vets, return value list vals */ + /* Given the list of vertices vets, return the list of values vals */ fun vetsToVals(vets: MutableList): MutableList { val vals = mutableListOf() for (vet in vets) { diff --git a/en/codes/python/chapter_array_and_linkedlist/array.py b/en/codes/python/chapter_array_and_linkedlist/array.py index 480bcc96f..5207f7b4b 100644 --- a/en/codes/python/chapter_array_and_linkedlist/array.py +++ b/en/codes/python/chapter_array_and_linkedlist/array.py @@ -8,69 +8,69 @@ import random def random_access(nums: list[int]) -> int: - """Random access to element""" - # Randomly select a number from the interval [0, len(nums)-1] + """Access an element at a random index""" + # Choose a random number in the range [0, len(nums)-1] random_index = random.randint(0, len(nums) - 1) - # Retrieve and return the random element + # Get and return the randomly selected element random_num = nums[random_index] return random_num -# Please note that Python's list is a dynamic array and can be extended directly -# For learning purposes, this function treats the list as an array with immutable length +# Note that a Python list is a dynamic array and can be extended directly +# For learning purposes, this function treats a list as a fixed-length array def extend(nums: list[int], enlarge: int) -> list[int]: - """Extend array length""" - # Initialize an array with extended length + """Extend the array length""" + # Initialize an array with the 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 extended new array + # Return the new, extended array return res def insert(nums: list[int], num: int, index: int): - """Insert element num at index index in the array""" - # Move all elements at and after index index backward by one position + """Insert element num at index `index` in the array""" + # Shift all elements at and after index `index` one position to the right for i in range(len(nums) - 1, index, -1): nums[i] = nums[i - 1] - # Assign num to the element at index index + # Assign num to the element at index `index` nums[index] = num def remove(nums: list[int], index: int): - """Remove the element at index index""" - # Move all elements after index index forward by one position + """Remove the element at index `index`""" + # Shift all elements after index `index` one position to the left for i in range(index, len(nums) - 1): nums[i] = nums[i + 1] def traverse(nums: list[int]): - """Traverse array""" + """Traverse the array""" count = 0 - # Traverse array by index + # Traverse the array by index for i in range(len(nums)): count += nums[i] - # Direct traversal of array elements + # Iterate directly over the array elements for num in nums: count += num - # Traverse simultaneously data index and elements + # Iterate over both the indices and the elements for i, num in enumerate(nums): count += nums[i] count += num def find(nums: list[int], target: int) -> int: - """Find the specified element in the array""" + """Find a specified element in the array""" for i in range(len(nums)): if nums[i] == target: return i return -1 -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize array + # Initialize the array arr = [0] * 5 print("Array arr =", arr) nums = [1, 3, 2, 5, 4] @@ -78,23 +78,23 @@ if __name__ == "__main__": # Random access random_num: int = random_access(nums) - print("Get random element from nums", random_num) + print("Randomly selected element from nums:", random_num) - # Length extension + # Extend the length nums: list[int] = extend(nums, 3) - print("Extend the array length to 8, get nums =", nums) + print("Extend the array length to 8, giving nums =", nums) - # Insert element + # Insert an element insert(nums, 6, 3) - print("Insert number 6 at index 3, get nums =", nums) + print("Insert the number 6 at index 3, giving nums =", nums) - # Remove element + # Remove an element remove(nums, 2) - print("Remove the element at index 2, get nums =", nums) + print("Remove the element at index 2, giving nums =", nums) - # Traverse array + # Traverse the array traverse(nums) - # Find element + # Find an element index: int = find(nums, 3) - print("Search for element 3 in nums, get index =", index) + print("Find element 3 in nums, giving index =", index) diff --git a/en/codes/python/chapter_array_and_linkedlist/linked_list.py b/en/codes/python/chapter_array_and_linkedlist/linked_list.py index 942536b70..4b3ff3bf0 100644 --- a/en/codes/python/chapter_array_and_linkedlist/linked_list.py +++ b/en/codes/python/chapter_array_and_linkedlist/linked_list.py @@ -29,7 +29,7 @@ def remove(n0: ListNode): def access(head: ListNode, index: int) -> ListNode | None: - """Access the node at index index in the linked list""" + """Access the node at index `index` in the linked list""" for _ in range(index): if not head: return None @@ -48,16 +48,16 @@ def find(head: ListNode, target: int) -> int: return -1 -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize linked list - # Initialize each node + # Initialize the linked list + # Initialize the individual nodes n0 = ListNode(1) n1 = ListNode(3) n2 = ListNode(2) n3 = ListNode(5) n4 = ListNode(4) - # Build references between nodes + # Set up references between the nodes n0.next = n1 n1.next = n2 n2.next = n3 @@ -65,21 +65,21 @@ if __name__ == "__main__": print("The initialized linked list is") print_linked_list(n0) - # Insert node + # Insert a node p = ListNode(0) insert(n0, p) print("The linked list after inserting a node is") print_linked_list(n0) - # Remove node + # Remove a node remove(n0) print("The linked list after removing a node is") print_linked_list(n0) - # Access node + # Access a node node: ListNode = access(n0, 3) - print("The value of the node at index 3 in the linked list = {}".format(node.val)) + print("Value of the node at index 3 in the linked list = {}".format(node.val)) - # Find node + # Find a node index: int = find(n0, 2) - print("The index of the node with value 2 in the linked list = {}".format(index)) + print("Index of the node with value 2 in the linked list = {}".format(index)) diff --git a/en/codes/python/chapter_array_and_linkedlist/list.py b/en/codes/python/chapter_array_and_linkedlist/list.py index 22851a22a..7ce0ef29a 100644 --- a/en/codes/python/chapter_array_and_linkedlist/list.py +++ b/en/codes/python/chapter_array_and_linkedlist/list.py @@ -4,53 +4,53 @@ Created Time: 2022-11-25 Author: krahets (krahets@163.com) """ -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize list + # Initialize the list nums: list[int] = [1, 3, 2, 5, 4] print("\nList nums =", nums) - # Access element + # Access an element x: int = nums[1] - print("\nAccess the element at index 1, get x =", x) + print("\nAccess the element at index 1, giving x =", x) - # Update element + # Update an element nums[1] = 0 - print("\nUpdate the element at index 1 to 0, get nums =", nums) + print("\nUpdate the element at index 1 to 0, giving nums =", nums) - # Clear list + # Clear the list nums.clear() - print("\nAfter clearing the list nums =", nums) + print("\nAfter clearing the list, nums =", nums) - # Add elements at the end + # Append an element at the end nums.append(1) nums.append(3) nums.append(2) nums.append(5) nums.append(4) - print("\nAfter adding elements nums =", nums) + print("\nAfter adding elements, nums =", nums) - # Insert element in the middle + # Insert an element in the middle nums.insert(3, 6) - print("\nInsert number 6 at index 3, get nums =", nums) + print("\nInsert the number 6 at index 3, giving nums =", nums) - # Remove element + # Remove an element nums.pop(3) - print("\nRemove the element at index 3, get nums =", nums) + print("\nRemove the element at index 3, giving nums =", nums) - # Traverse list by index + # Traverse the list by index count = 0 for i in range(len(nums)): count += nums[i] - # Direct traversal of list elements + # Iterate directly over the list elements for num in nums: count += num # Concatenate two lists nums1 = [6, 8, 7, 10, 9] nums += nums1 - print("\nConcatenate list nums1 to nums, get nums =", nums) + print("\nAppend list nums1 to nums, giving nums =", nums) - # Sort list + # Sort the list nums.sort() - print("\nAfter sorting the list nums =", nums) + print("\nAfter sorting the list, nums =", nums) diff --git a/en/codes/python/chapter_array_and_linkedlist/my_list.py b/en/codes/python/chapter_array_and_linkedlist/my_list.py index 1309019b4..84d8d0bdb 100644 --- a/en/codes/python/chapter_array_and_linkedlist/my_list.py +++ b/en/codes/python/chapter_array_and_linkedlist/my_list.py @@ -11,47 +11,47 @@ class MyList: def __init__(self): """Constructor""" self._capacity: int = 10 # List capacity - self._arr: list[int] = [0] * self._capacity # Array (stores list elements) + self._arr: list[int] = [0] * self._capacity # Array (stores the list elements) self._size: int = 0 # List length (current number of elements) - self._extend_ratio: int = 2 # Multiple by which the list capacity is extended each time + self._extend_ratio: int = 2 # Factor by which the list capacity grows each time def size(self) -> int: - """Get list length (current number of elements)""" + """Get the list length (current number of elements)""" return self._size def capacity(self) -> int: - """Get list capacity""" + """Get the list capacity""" return self._capacity def get(self, index: int) -> int: - """Access element""" - # If the index is out of bounds, throw an exception, as below + """Access an element""" + # Raise an exception if the index is out of bounds; the same applies below if index < 0 or index >= self._size: raise IndexError("Index out of bounds") return self._arr[index] def set(self, num: int, index: int): - """Update element""" + """Update an element""" if index < 0 or index >= self._size: raise IndexError("Index out of bounds") self._arr[index] = num def add(self, num: int): - """Add element at the end""" - # When the number of elements exceeds capacity, trigger the extension mechanism + """Append an element at the end""" + # Grow the capacity when adding an element would exceed it if self.size() == self.capacity(): self.extend_capacity() self._arr[self._size] = num self._size += 1 def insert(self, num: int, index: int): - """Insert element in the middle""" + """Insert an 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 extension mechanism + # Grow the capacity when adding an element would exceed it if self._size == self.capacity(): self.extend_capacity() - # Move all elements at and after index index backward by one position + # Shift all elements at and after index `index` one position to the right for j in range(self._size - 1, index - 1, -1): self._arr[j + 1] = self._arr[j] self._arr[index] = num @@ -59,11 +59,11 @@ class MyList: self._size += 1 def remove(self, index: int) -> int: - """Remove element""" + """Remove an element""" if index < 0 or index >= self._size: raise IndexError("Index out of bounds") num = self._arr[index] - # Move all elements after index index forward by one position + # Shift all elements after index `index` one position to the left for j in range(index, self._size - 1): self._arr[j] = self._arr[j + 1] # Update the number of elements @@ -72,22 +72,22 @@ class MyList: return num def extend_capacity(self): - """Extend list capacity""" - # Create a new array with length _extend_ratio times the original array, and copy the original array to the new array + """Grow the list capacity""" + # Create a new array with _extend_ratio times the original length and copy the original array into it self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1) - # Update list capacity + # Update the list capacity self._capacity = len(self._arr) def to_array(self) -> list[int]: - """Return list with valid length""" + """Return the portion of the list that contains elements""" return self._arr[: self._size] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize list + # Initialize the list nums = MyList() - # Add elements at the end + # Append an element at the end nums.add(1) nums.add(3) nums.add(2) @@ -95,24 +95,24 @@ if __name__ == "__main__": nums.add(4) print(f"List nums = {nums.to_array()}, capacity = {nums.capacity()}, length = {nums.size()}") - # Insert element in the middle + # Insert an element in the middle nums.insert(6, index=3) - print("Insert number 6 at index 3, get nums =", nums.to_array()) + print("Insert the number 6 at index 3, giving nums =", nums.to_array()) - # Remove element + # Remove an element nums.remove(3) - print("Remove the element at index 3, get nums =", nums.to_array()) + print("Remove the element at index 3, giving nums =", nums.to_array()) - # Access element + # Access an element num = nums.get(1) - print("Access the element at index 1, get num =", num) + print("Access the element at index 1, giving num =", num) - # Update element + # Update an element nums.set(0, 1) - print("Update the element at index 1 to 0, get nums =", nums.to_array()) + print("Update the element at index 1 to 0, giving nums =", nums.to_array()) - # Test extension mechanism + # Test capacity growth for i in range(10): - # At i = 5, the list length will exceed the list capacity, triggering the extension mechanism at this point + # When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i) - print(f"List after extension {nums.to_array()}, capacity = {nums.capacity()}, length = {nums.size()}") + print(f"List after growing its capacity: {nums.to_array()}, capacity = {nums.capacity()}, length = {nums.size()}") diff --git a/en/codes/python/chapter_backtracking/n_queens.py b/en/codes/python/chapter_backtracking/n_queens.py index 100861a37..8ef604c42 100644 --- a/en/codes/python/chapter_backtracking/n_queens.py +++ b/en/codes/python/chapter_backtracking/n_queens.py @@ -14,48 +14,48 @@ def backtrack( diags1: list[bool], diags2: list[bool], ): - """Backtracking algorithm: N queens""" - # When all rows are placed, record the solution + """Backtracking: n-queens""" + # Record a solution when a queen has been placed in every row if row == n: res.append([list(row) for row in state]) return - # Traverse all columns + # Iterate over all columns for col in range(n): - # Calculate the main diagonal and anti-diagonal corresponding to this cell + # Calculate the main-diagonal and anti-diagonal indices for this square diag1 = row - col + n - 1 diag2 = row + col - # Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + # Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if not cols[col] and not diags1[diag1] and not diags2[diag2]: - # Attempt: place the queen in this cell + # Try: place a queen on this square state[row][col] = "Q" cols[col] = diags1[diag1] = diags2[diag2] = True - # Place the next row + # Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2) - # Backtrack: restore this cell to an empty cell + # Backtrack: restore this square to empty state[row][col] = "#" cols[col] = diags1[diag1] = diags2[diag2] = False def n_queens(n: int) -> list[list[list[str]]]: - """Solve N queens""" - # Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + """Solve the n-queens problem""" + # Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square state = [["#" for _ in range(n)] for _ in range(n)] - cols = [False] * n # Record whether there is a queen in the column - diags1 = [False] * (2 * n - 1) # Record whether there is a queen on the main diagonal - diags2 = [False] * (2 * n - 1) # Record whether there is a queen on the anti-diagonal + cols = [False] * n # Track whether a column contains a queen + diags1 = [False] * (2 * n - 1) # Track whether a main diagonal contains a queen + diags2 = [False] * (2 * n - 1) # Track whether an anti-diagonal contains a queen res = [] backtrack(0, n, state, res, cols, diags1, diags2) return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 4 res = n_queens(n) - print(f"Input chessboard size is {n}") - print(f"There are {len(res)} queen placement solutions") + print(f"The input board has width and height {n}") + print(f"Number of ways to place the queens: {len(res)}") for state in res: print("--------------------") for row in state: diff --git a/en/codes/python/chapter_backtracking/permutations_i.py b/en/codes/python/chapter_backtracking/permutations_i.py index 75dc63f6a..393e45225 100644 --- a/en/codes/python/chapter_backtracking/permutations_i.py +++ b/en/codes/python/chapter_backtracking/permutations_i.py @@ -8,21 +8,21 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] ): - """Backtracking algorithm: Permutations I""" - # When the state length equals the number of elements, record the solution + """Backtracking: permutations I""" + # Record a solution when the state length equals the number of elements if len(state) == len(choices): res.append(list(state)) return - # Traverse all choices + # Iterate over all choices for i, choice in enumerate(choices): - # Pruning: do not allow repeated selection of elements + # Prune: do not choose an element more than once if not selected[i]: - # Attempt: make choice, update state + # Try: make a choice and update the state selected[i] = True state.append(choice) - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, choices, selected, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state selected[i] = False state.pop() @@ -34,11 +34,11 @@ def permutations_i(nums: list[int]) -> list[list[int]]: return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [1, 2, 3] res = permutations_i(nums) print(f"Input array nums = {nums}") - print(f"All permutations res = {res}") + print(f"All permutations: res = {res}") diff --git a/en/codes/python/chapter_backtracking/permutations_ii.py b/en/codes/python/chapter_backtracking/permutations_ii.py index bdaa6f4d3..b77f596aa 100644 --- a/en/codes/python/chapter_backtracking/permutations_ii.py +++ b/en/codes/python/chapter_backtracking/permutations_ii.py @@ -8,23 +8,23 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] ): - """Backtracking algorithm: Permutations II""" - # When the state length equals the number of elements, record the solution + """Backtracking: permutations II""" + # Record a solution when the state length equals the number of elements if len(state) == len(choices): res.append(list(state)) return - # Traverse all choices + # Iterate over all choices duplicated = set[int]() for i, choice in enumerate(choices): - # Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + # Prune: do not reuse an element or choose the same value again at this depth if not selected[i] and choice not in duplicated: - # Attempt: make choice, update state - duplicated.add(choice) # Record the selected element value + # Try: make a choice and update the state + duplicated.add(choice) # Record the chosen value selected[i] = True state.append(choice) - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, choices, selected, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state selected[i] = False state.pop() @@ -36,11 +36,11 @@ def permutations_ii(nums: list[int]) -> list[list[int]]: return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [1, 2, 2] res = permutations_ii(nums) print(f"Input array nums = {nums}") - print(f"All permutations res = {res}") + print(f"All permutations: res = {res}") diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py index c53aead9d..6cf8c9950 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py @@ -12,20 +12,20 @@ from modules import TreeNode, print_tree, list_to_tree def pre_order(root: TreeNode): - """Preorder traversal: Example 1""" + """Preorder traversal: example 1""" if root is None: return if root.val == 7: - # Record solution + # Record the solution res.append(root) pre_order(root.left) pre_order(root.right) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") print_tree(root) # Preorder traversal diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py index 7626190f8..b958a010b 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py @@ -12,13 +12,13 @@ from modules import TreeNode, print_tree, list_to_tree def pre_order(root: TreeNode): - """Preorder traversal: Example 2""" + """Preorder traversal: example 2""" if root is None: return - # Attempt + # Try path.append(root) if root.val == 7: - # Record solution + # Record the solution res.append(list(path)) pre_order(root.left) pre_order(root.right) @@ -26,10 +26,10 @@ def pre_order(root: TreeNode): path.pop() -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") print_tree(root) # Preorder traversal @@ -37,6 +37,6 @@ if __name__ == "__main__": res = list[list[TreeNode]]() pre_order(root) - print("\nOutput all paths from root node to node 7") + print("\nOutput all paths from the root to nodes with value 7") for path in res: print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py index 4e36db871..75b7addeb 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py @@ -12,14 +12,14 @@ from modules import TreeNode, print_tree, list_to_tree def pre_order(root: TreeNode): - """Preorder traversal: Example 3""" - # Pruning + """Preorder traversal: example 3""" + # Prune if root is None or root.val == 3: return - # Attempt + # Try path.append(root) if root.val == 7: - # Record solution + # Record the solution res.append(list(path)) pre_order(root.left) pre_order(root.right) @@ -27,10 +27,10 @@ def pre_order(root: TreeNode): path.pop() -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") print_tree(root) # Preorder traversal @@ -38,6 +38,6 @@ if __name__ == "__main__": res = list[list[TreeNode]]() pre_order(root) - print("\nOutput all paths from root node to node 7, excluding paths with nodes of value 3") + print("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3") for path in res: print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py b/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py index 6a9a7bb97..43af0bac3 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py @@ -12,60 +12,60 @@ from modules import TreeNode, print_tree, list_to_tree def is_solution(state: list[TreeNode]) -> bool: - """Check if the current state is a solution""" + """Check whether the current state is a solution""" return state and state[-1].val == 7 def record_solution(state: list[TreeNode], res: list[list[TreeNode]]): - """Record solution""" + """Record the solution""" res.append(list(state)) def is_valid(state: list[TreeNode], choice: TreeNode) -> bool: - """Check if the choice is valid under the current state""" + """Check whether the choice is valid in the current state""" return choice is not None and choice.val != 3 def make_choice(state: list[TreeNode], choice: TreeNode): - """Update state""" + """Update the state""" state.append(choice) def undo_choice(state: list[TreeNode], choice: TreeNode): - """Restore state""" + """Restore the state""" state.pop() def backtrack( state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]] ): - """Backtracking algorithm: Example 3""" - # Check if it is a solution + """Backtracking: example 3""" + # Check whether this is a solution if is_solution(state): - # Record solution + # Record the solution record_solution(state, res) - # Traverse all choices + # Iterate over all choices for choice in choices: - # Pruning: check if the choice is valid + # Prune: check whether the choice is valid if is_valid(state, choice): - # Attempt: make choice, update state + # Try: make a choice and update the state make_choice(state, choice) - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, [choice.left, choice.right], res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state undo_choice(state, choice) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") print_tree(root) # Backtracking algorithm res = [] backtrack(state=[], choices=[root], res=res) - print("\nOutput all paths from root node to node 7, excluding paths with nodes of value 3") + print("\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3") for path in res: print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/subset_sum_i.py b/en/codes/python/chapter_backtracking/subset_sum_i.py index fb5c08f6e..5358a4830 100644 --- a/en/codes/python/chapter_backtracking/subset_sum_i.py +++ b/en/codes/python/chapter_backtracking/subset_sum_i.py @@ -8,23 +8,23 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] ): - """Backtracking algorithm: Subset sum I""" - # When the subset sum equals target, record the solution + """Backtracking: subset sum I""" + # Record a solution when the remaining target is zero if target == 0: res.append(list(state)) return - # Traverse all choices - # Pruning 2: start traversing from start to avoid generating duplicate subsets + # Iterate over all choices + # Pruning 2: iterate from start to avoid generating duplicate subsets for i in range(start, len(choices)): - # Pruning 1: if the subset sum exceeds target, end the loop directly - # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + # Pruning 1: exit the loop if this choice exceeds the remaining target + # The array is sorted, so later elements are at least as large and also exceed the remaining target if target - choices[i] < 0: break - # Attempt: make choice, update target, start + # Try: make a choice and update target and start state.append(choices[i]) - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, target - choices[i], choices, i, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state state.pop() @@ -32,17 +32,17 @@ def subset_sum_i(nums: list[int], target: int) -> list[list[int]]: """Solve subset sum I""" state = [] # State (subset) nums.sort() # Sort nums - start = 0 # Start point for traversal - res = [] # Result list (subset list) + start = 0 # Starting index for iteration + res = [] # Result list (list of subsets) backtrack(state, target, nums, start, res) return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [3, 4, 5] target = 9 res = subset_sum_i(nums, target) print(f"Input array nums = {nums}, target = {target}") - print(f"All subsets with sum equal to {target} res = {res}") + print(f"All subsets with sum {target}: res = {res}") diff --git a/en/codes/python/chapter_backtracking/subset_sum_i_naive.py b/en/codes/python/chapter_backtracking/subset_sum_i_naive.py index 8b272cffc..d5ece4bb7 100644 --- a/en/codes/python/chapter_backtracking/subset_sum_i_naive.py +++ b/en/codes/python/chapter_backtracking/subset_sum_i_naive.py @@ -12,39 +12,39 @@ def backtrack( choices: list[int], res: list[list[int]], ): - """Backtracking algorithm: Subset sum I""" - # When the subset sum equals target, record the solution + """Backtracking: subset sum I""" + # Record a solution when the subset sum equals target if total == target: res.append(list(state)) return - # Traverse all choices + # Iterate over all choices for i in range(len(choices)): - # Pruning: if the subset sum exceeds target, skip this choice + # Prune: skip this choice if the subset sum would exceed target if total + choices[i] > target: continue - # Attempt: make choice, update element sum total + # Try: make a choice and update the sum total state.append(choices[i]) - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, target, total + choices[i], choices, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state state.pop() def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]: - """Solve subset sum I (including duplicate subsets)""" + """Solve subset sum I (with duplicate subsets)""" state = [] # State (subset) total = 0 # Subset sum - res = [] # Result list (subset list) + res = [] # Result list (list of subsets) backtrack(state, target, total, nums, res) return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [3, 4, 5] target = 9 res = subset_sum_i_naive(nums, target) print(f"Input array nums = {nums}, target = {target}") - print(f"All subsets with sum equal to {target} res = {res}") - print(f"Please note that the result output by this method contains duplicate sets") + print(f"All subsets with sum {target}: res = {res}") + print(f"Note that this method produces duplicate subsets") diff --git a/en/codes/python/chapter_backtracking/subset_sum_ii.py b/en/codes/python/chapter_backtracking/subset_sum_ii.py index 35f34b3b0..f2bada677 100644 --- a/en/codes/python/chapter_backtracking/subset_sum_ii.py +++ b/en/codes/python/chapter_backtracking/subset_sum_ii.py @@ -8,27 +8,27 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] ): - """Backtracking algorithm: Subset sum II""" - # When the subset sum equals target, record the solution + """Backtracking: subset sum II""" + # Record a solution when the remaining target is zero if target == 0: res.append(list(state)) return - # Traverse all choices - # Pruning 2: start traversing from start to avoid generating duplicate subsets - # Pruning 3: start traversing from start to avoid repeatedly selecting the same element + # Iterate over all choices + # Pruning 2: iterate from start to avoid generating duplicate subsets + # Pruning 3: start after the previously chosen index to avoid choosing the same element again for i in range(start, len(choices)): - # Pruning 1: if the subset sum exceeds target, end the loop directly - # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + # Pruning 1: exit the loop if this choice exceeds the remaining target + # The array is sorted, so later elements are at least as large and also exceed the remaining target if target - choices[i] < 0: break - # Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + # Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if i > start and choices[i] == choices[i - 1]: continue - # Attempt: make choice, update target, start + # Try: make a choice and update target and start state.append(choices[i]) - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state state.pop() @@ -36,17 +36,17 @@ def subset_sum_ii(nums: list[int], target: int) -> list[list[int]]: """Solve subset sum II""" state = [] # State (subset) nums.sort() # Sort nums - start = 0 # Start point for traversal - res = [] # Result list (subset list) + start = 0 # Starting index for iteration + res = [] # Result list (list of subsets) backtrack(state, target, nums, start, res) return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [4, 4, 5] target = 9 res = subset_sum_ii(nums, target) print(f"Input array nums = {nums}, target = {target}") - print(f"All subsets with sum equal to {target} res = {res}") + print(f"All subsets with sum {target}: res = {res}") diff --git a/en/codes/python/chapter_computational_complexity/complexity_exercises.py b/en/codes/python/chapter_computational_complexity/complexity_exercises.py index 56db8a540..c626c6214 100644 --- a/en/codes/python/chapter_computational_complexity/complexity_exercises.py +++ b/en/codes/python/chapter_computational_complexity/complexity_exercises.py @@ -6,7 +6,7 @@ Author: Hello Algo Team def sum_iter(n: int) -> int: - """Iterative summation""" + """Sum using iteration""" res = 0 for i in range(1, n + 1): res += i @@ -14,14 +14,14 @@ def sum_iter(n: int) -> int: def sum_recur(n: int) -> int: - """Recursive summation""" + """Sum using recursion""" if n == 1: return 1 return n + sum_recur(n - 1) def linear_loop(n: int) -> int: - """Linear loop""" + """Linear-time loop""" res = 0 for i in range(n): res += i @@ -29,7 +29,7 @@ def linear_loop(n: int) -> int: def quadratic_loop(n: int) -> int: - """Quadratic loop""" + """Quadratic-time loop""" res = 0 for i in range(n): for j in range(i, n): @@ -38,7 +38,7 @@ def quadratic_loop(n: int) -> int: def logarithmic_loop(n: int) -> int: - """Logarithmic loop""" + """Logarithmic-time loop""" while n > 1: n //= 2 return n diff --git a/en/codes/python/chapter_computational_complexity/iteration.py b/en/codes/python/chapter_computational_complexity/iteration.py index 61d778da2..2a8919335 100644 --- a/en/codes/python/chapter_computational_complexity/iteration.py +++ b/en/codes/python/chapter_computational_complexity/iteration.py @@ -8,7 +8,7 @@ Author: krahets (krahets@163.com) def for_loop(n: int) -> int: """for loop""" res = 0 - # Sum 1, 2, ..., n-1, n + # Sum 1, 2, ..., n-1, n in a loop for i in range(1, n + 1): res += i return res @@ -17,49 +17,49 @@ def for_loop(n: int) -> int: def while_loop(n: int) -> int: """while loop""" res = 0 - i = 1 # Initialize condition variable - # Sum 1, 2, ..., n-1, n + i = 1 # Initialize the loop control variable + # Sum 1, 2, ..., n-1, n in a loop while i <= n: res += i - i += 1 # Update condition variable + i += 1 # Update the loop control variable return res def while_loop_ii(n: int) -> int: """while loop (two updates)""" res = 0 - i = 1 # Initialize condition variable - # Sum 1, 4, 10, ... + i = 1 # Initialize the loop control variable + # Sum 1, 4, 10, ... in a loop while i <= n: res += i - # Update condition variable + # Update the loop control variable i += 1 i *= 2 return res def nested_for_loop(n: int) -> str: - """Nested for loop""" + """Nested for loops""" res = "" - # Loop i = 1, 2, ..., n-1, n + # Loop over i = 1, 2, ..., n-1, n for i in range(1, n + 1): - # Loop j = 1, 2, ..., n-1, n + # Loop over j = 1, 2, ..., n-1, n for j in range(1, n + 1): res += f"({i}, {j}), " return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 5 res = for_loop(n) - print(f"\nSum result of for loop res = {res}") + print(f"\nSum from the for loop: res = {res}") res = while_loop(n) - print(f"\nSum result of while loop res = {res}") + print(f"\nSum from the while loop: res = {res}") res = while_loop_ii(n) - print(f"\nSum result of while loop (two updates) res = {res}") + print(f"\nSum from the while loop (two updates): res = {res}") res = nested_for_loop(n) - print(f"\nTraversal result of nested for loop {res}") + print(f"\nTraversal from the nested for loops: {res}") diff --git a/en/codes/python/chapter_computational_complexity/recursion.py b/en/codes/python/chapter_computational_complexity/recursion.py index 954915c9d..6f722fb59 100644 --- a/en/codes/python/chapter_computational_complexity/recursion.py +++ b/en/codes/python/chapter_computational_complexity/recursion.py @@ -7,12 +7,12 @@ Author: krahets (krahets@163.com) def recur(n: int) -> int: """Recursion""" - # Termination condition + # Base case if n == 1: return 1 - # Recurse: recursive call + # Recurse: make a recursive call res = recur(n - 1) - # Return: return result + # Unwind: return the result return n + res @@ -21,13 +21,13 @@ def for_loop_recur(n: int) -> int: # Use an explicit stack to simulate the system call stack stack = [] res = 0 - # Recurse: recursive call + # Simulate the recursive-call phase for i in range(n, 0, -1): - # Simulate "recurse" with "push" + # Simulate recursive calls by pushing onto the stack stack.append(i) - # Return: return result + # Simulate unwinding and accumulating the result while stack: - # Simulate "return" with "pop" + # Simulate unwinding by popping from the stack res += stack.pop() # res = 1+2+3+...+n return res @@ -35,35 +35,35 @@ def for_loop_recur(n: int) -> int: def tail_recur(n, res): """Tail recursion""" - # Termination condition + # Base case if n == 0: return res - # Tail recursive call + # Tail-recursive call return tail_recur(n - 1, res + n) def fib(n: int) -> int: """Fibonacci sequence: recursion""" - # Termination condition f(1) = 0, f(2) = 1 + # Base cases: f(1) = 0, f(2) = 1 if n == 1 or n == 2: return n - 1 - # Recursive call f(n) = f(n-1) + f(n-2) + # Recursive calls: f(n) = f(n-1) + f(n-2) res = fib(n - 1) + fib(n - 2) - # Return result f(n) + # Return the result f(n) return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 5 res = recur(n) - print(f"\nSum result of recursive function res = {res}") + print(f"\nSum from the recursive function: res = {res}") res = for_loop_recur(n) - print(f"\nSum result of simulating recursion using iteration res = {res}") + print(f"\nSum from simulating recursion using iteration: res = {res}") res = tail_recur(n, 0) - print(f"\nSum result of tail recursive function res = {res}") + print(f"\nSum from the tail-recursive function: res = {res}") res = fib(n) - print(f"\nThe {n}th term of the Fibonacci sequence is {res}") + print(f"\nTerm {n} of the Fibonacci sequence is {res}") diff --git a/en/codes/python/chapter_computational_complexity/space_complexity.py b/en/codes/python/chapter_computational_complexity/space_complexity.py index cf20bc795..5f997b780 100644 --- a/en/codes/python/chapter_computational_complexity/space_complexity.py +++ b/en/codes/python/chapter_computational_complexity/space_complexity.py @@ -18,54 +18,54 @@ def function() -> int: def constant(n: int): - """Constant order""" - # Constants, variables, objects occupy O(1) space + """Constant complexity""" + # Constants, variables, and objects occupy O(1) space a = 0 nums = [0] * 10000 node = ListNode(0) # Variables in the loop occupy O(1) space for _ in range(n): c = 0 - # Functions in the loop occupy O(1) space + # Function calls in the loop occupy O(1) space for _ in range(n): function() def linear(n: int): - """Linear order""" + """Linear complexity""" # A list of length n occupies O(n) space nums = [0] * n - # A hash table of length n occupies O(n) space + # A hash table with n entries occupies O(n) space hmap = dict[int, str]() for i in range(n): hmap[i] = str(i) def linear_recur(n: int): - """Linear order (recursive implementation)""" - print("Recursion n =", n) + """Linear complexity (recursive implementation)""" + print("Recursion: n =", n) if n == 1: return linear_recur(n - 1) def quadratic(n: int): - """Quadratic order""" - # A 2D list occupies O(n^2) space + """Quadratic complexity""" + # The two-dimensional list occupies O(n^2) space num_matrix = [[0] * n for _ in range(n)] def quadratic_recur(n: int) -> int: - """Quadratic order (recursive implementation)""" + """Quadratic complexity (recursive implementation)""" if n <= 0: return 0 - # Array nums length is n, n-1, ..., 2, 1 + # The lengths of array nums are n, n-1, ..., 2, 1 nums = [0] * n return quadratic_recur(n - 1) def build_tree(n: int) -> TreeNode | None: - """Exponential order (build full binary tree)""" + """Exponential complexity (building a perfect binary tree)""" if n == 0: return None root = TreeNode(0) @@ -74,17 +74,17 @@ def build_tree(n: int) -> TreeNode | None: return root -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 5 - # Constant order + # Constant complexity constant(n) - # Linear order + # Linear complexity linear(n) linear_recur(n) - # Quadratic order + # Quadratic complexity quadratic(n) quadratic_recur(n) - # Exponential order + # Exponential complexity root = build_tree(n) print_tree(root) diff --git a/en/codes/python/chapter_computational_complexity/time_complexity.py b/en/codes/python/chapter_computational_complexity/time_complexity.py index d1ed54447..abf233e6e 100644 --- a/en/codes/python/chapter_computational_complexity/time_complexity.py +++ b/en/codes/python/chapter_computational_complexity/time_complexity.py @@ -6,7 +6,7 @@ Author: krahets (krahets@163.com) def constant(n: int) -> int: - """Constant order""" + """Constant complexity""" count = 0 size = 100000 for _ in range(size): @@ -15,7 +15,7 @@ def constant(n: int) -> int: def linear(n: int) -> int: - """Linear order""" + """Linear complexity""" count = 0 for _ in range(n): count += 1 @@ -23,18 +23,18 @@ def linear(n: int) -> int: def array_traversal(nums: list[int]) -> int: - """Linear order (traversing array)""" + """Linear complexity (array traversal)""" count = 0 - # Number of iterations is proportional to the array length + # The number of iterations is proportional to the array length for num in nums: count += 1 return count def quadratic(n: int) -> int: - """Quadratic order""" + """Quadratic complexity""" count = 0 - # Number of iterations is quadratically related to the data size n + # The number of iterations is proportional to the square of the input size n for i in range(n): for j in range(n): count += 1 @@ -42,26 +42,26 @@ def quadratic(n: int) -> int: def bubble_sort(nums: list[int]) -> int: - """Quadratic order (bubble sort)""" + """Quadratic complexity (bubble sort)""" count = 0 # Counter - # Outer loop: unsorted range is [0, i] + # Outer loop: the unsorted range is [0, i] for i in range(len(nums) - 1, 0, -1): - # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + # Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in range(i): if nums[j] > nums[j + 1]: # Swap nums[j] and nums[j + 1] tmp: int = nums[j] nums[j] = nums[j + 1] nums[j + 1] = tmp - count += 3 # Element swap includes 3 unit operations + count += 3 # Swapping two elements takes 3 elementary operations return count def exponential(n: int) -> int: - """Exponential order (loop implementation)""" + """Exponential complexity (iterative implementation)""" count = 0 base = 1 - # Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + # Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for _ in range(n): for _ in range(base): count += 1 @@ -71,14 +71,14 @@ def exponential(n: int) -> int: def exp_recur(n: int) -> int: - """Exponential order (recursive implementation)""" + """Exponential complexity (recursive implementation)""" if n == 1: return 1 return exp_recur(n - 1) + exp_recur(n - 1) + 1 def logarithmic(n: int) -> int: - """Logarithmic order (loop implementation)""" + """Logarithmic complexity (iterative implementation)""" count = 0 while n > 1: n = n / 2 @@ -87,67 +87,67 @@ def logarithmic(n: int) -> int: def log_recur(n: int) -> int: - """Logarithmic order (recursive implementation)""" + """Logarithmic complexity (recursive implementation)""" if n <= 1: return 0 return log_recur(n / 2) + 1 def linear_log_recur(n: int) -> int: - """Linearithmic order""" + """Linearithmic complexity""" if n <= 1: return 1 - # Divide into two, the scale of subproblems is reduced by half + # Split into two subproblems, each half the original size count = linear_log_recur(n // 2) + linear_log_recur(n // 2) - # Current subproblem contains n operations + # The current subproblem includes n operations for _ in range(n): count += 1 return count def factorial_recur(n: int) -> int: - """Factorial order (recursive implementation)""" + """Factorial complexity (recursive implementation)""" if n == 0: return 1 count = 0 - # Split from 1 into n + # Split one problem into n subproblems for _ in range(n): count += factorial_recur(n - 1) return count -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # You can modify n to run and observe the trend of the number of operations for various complexities + # Try different values of n to see how the operation count grows for each complexity n = 8 - print("Input data size n =", n) + print("Input size n =", n) count = constant(n) - print("Number of operations of constant order =", count) + print("Operation count for constant complexity =", count) count = linear(n) - print("Number of operations of linear order =", count) + print("Operation count for linear complexity =", count) count = array_traversal([0] * n) - print("Number of operations of linear order (traversing array) =", count) + print("Operation count for linear complexity (array traversal) =", count) count = quadratic(n) - print("Number of operations of quadratic order =", count) + print("Operation count for quadratic complexity =", count) nums = [i for i in range(n, 0, -1)] # [n, n-1, ..., 2, 1] count = bubble_sort(nums) - print("Number of operations of quadratic order (bubble sort) =", count) + print("Operation count for quadratic complexity (bubble sort) =", count) count = exponential(n) - print("Number of operations of exponential order (loop implementation) =", count) + print("Operation count for exponential complexity (iterative implementation) =", count) count = exp_recur(n) - print("Number of operations of exponential order (recursive implementation) =", count) + print("Operation count for exponential complexity (recursive implementation) =", count) count = logarithmic(n) - print("Number of operations of logarithmic order (loop implementation) =", count) + print("Operation count for logarithmic complexity (iterative implementation) =", count) count = log_recur(n) - print("Number of operations of logarithmic order (recursive implementation) =", count) + print("Operation count for logarithmic complexity (recursive implementation) =", count) count = linear_log_recur(n) - print("Number of operations of linearithmic order (recursive implementation) =", count) + print("Operation count for linearithmic complexity (recursive implementation) =", count) count = factorial_recur(n) - print("Number of operations of factorial order (recursive implementation) =", count) + print("Operation count for factorial complexity (recursive implementation) =", count) diff --git a/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py b/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py index 4a67d248d..2aa504f1c 100644 --- a/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py +++ b/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py @@ -8,29 +8,29 @@ import random def random_numbers(n: int) -> list[int]: - """Generate an array with elements: 1, 2, ..., n, shuffled in order""" - # Generate array nums =: 1, 2, 3, ..., n + """Generate an array containing 1, 2, ..., n in shuffled order""" + # Generate the array nums = 1, 2, 3, ..., n nums = [i for i in range(1, n + 1)] - # Randomly shuffle array elements + # Randomly shuffle the array elements random.shuffle(nums) return nums def find_one(nums: list[int]) -> int: - """Find the index of number 1 in array nums""" + """Find the index of the number 1 in array nums""" for i in range(len(nums)): - # When element 1 is at the head of the array, best time complexity O(1) is achieved - # When element 1 is at the tail of the array, worst time complexity O(n) is achieved + # When 1 is at the start of the array, the best-case time complexity is O(1) + # When 1 is at the end of the array, the worst-case time complexity is O(n) if nums[i] == 1: return i return -1 -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": for i in range(10): n = 100 nums: list[int] = random_numbers(n) index: int = find_one(nums) - print("\nArray [ 1, 2, ..., n ] after being shuffled =", nums) - print("The index of number 1 is", index) + print("\nArray [ 1, 2, ..., n ] after shuffling =", nums) + print("The index of the number 1 is", index) diff --git a/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py b/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py index d56139ab1..d738fe0fb 100644 --- a/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py +++ b/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py @@ -7,34 +7,34 @@ Author: krahets (krahets@163.com) def dfs(nums: list[int], target: int, i: int, j: int) -> int: """Binary search: problem f(i, j)""" - # If the interval is empty, it means there is no target element, return -1 + # If the interval is empty, the target is absent; return -1 if i > j: return -1 # Calculate the midpoint index m m = (i + j) // 2 if nums[m] < target: - # Recursion subproblem f(m+1, j) + # Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j) elif nums[m] > target: - # Recursion subproblem f(i, m-1) + # Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1) else: - # Found the target element, return its index + # The target element was found; return its index return m def binary_search(nums: list[int], target: int) -> int: """Binary search""" n = len(nums) - # Solve the problem f(0, n-1) + # Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": target = 6 nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] - # Binary search (closed interval on both sides) + # Binary search (closed interval) index = binary_search(nums, target) print("Index of target element 6 = ", index) diff --git a/en/codes/python/chapter_divide_and_conquer/build_tree.py b/en/codes/python/chapter_divide_and_conquer/build_tree.py index 9102d42de..b4c148898 100644 --- a/en/codes/python/chapter_divide_and_conquer/build_tree.py +++ b/en/codes/python/chapter_divide_and_conquer/build_tree.py @@ -18,13 +18,13 @@ def dfs( l: int, r: int, ) -> TreeNode | None: - """Build binary tree: divide and conquer""" - # Terminate when the subtree interval is empty + """Build a binary tree: divide and conquer""" + # Stop when the subtree interval is empty if r - l < 0: return None # Initialize the root node root = TreeNode(preorder[i]) - # Query m to divide the left and right subtrees + # Look up m to separate the left and right subtrees m = inorder_map[preorder[i]] # Subproblem: build the left subtree root.left = dfs(preorder, inorder_map, i + 1, l, m - 1) @@ -35,14 +35,14 @@ def dfs( def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None: - """Build binary tree""" - # Initialize hash map, storing the mapping from inorder elements to indices + """Build a binary tree""" + # Initialize a hash table mapping each element in inorder to its index inorder_map = {val: i for i, val in enumerate(inorder)} root = dfs(preorder, inorder_map, 0, 0, len(inorder) - 1) return root -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": preorder = [3, 9, 2, 1, 7] inorder = [9, 3, 1, 2, 7] @@ -50,5 +50,5 @@ if __name__ == "__main__": print(f"Inorder traversal = {inorder}") root = build_tree(preorder, inorder) - print("The built binary tree is:") + print("The constructed binary tree is:") print_tree(root) diff --git a/en/codes/python/chapter_divide_and_conquer/fast_power.py b/en/codes/python/chapter_divide_and_conquer/fast_power.py index eb768affa..bd259f174 100644 --- a/en/codes/python/chapter_divide_and_conquer/fast_power.py +++ b/en/codes/python/chapter_divide_and_conquer/fast_power.py @@ -6,7 +6,7 @@ Author: Hello Algo Team def fast_pow(x: int, n: int) -> int: - """Exponentiation by squaring""" + """Fast exponentiation""" if n == 0: return 1 half = fast_pow(x, n // 2) diff --git a/en/codes/python/chapter_divide_and_conquer/hanota.py b/en/codes/python/chapter_divide_and_conquer/hanota.py index 1e5c5fe41..d39d84249 100644 --- a/en/codes/python/chapter_divide_and_conquer/hanota.py +++ b/en/codes/python/chapter_divide_and_conquer/hanota.py @@ -6,8 +6,8 @@ Author: krahets (krahets@163.com) def move(src: list[int], tar: list[int]): - """Move a disk""" - # Take out a disk from the top of src + """Move one disk""" + # Take a disk from the top of src pan = src.pop() # Place the disk on top of tar tar.append(pan) @@ -15,13 +15,13 @@ def move(src: list[int], tar: list[int]): def dfs(i: int, src: list[int], buf: list[int], tar: list[int]): """Solve the Tower of Hanoi problem f(i)""" - # If there is only one disk left in src, move it directly to tar + # If this subproblem has only one disk to move, move it directly from src to tar if i == 1: move(src, tar) return # Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf) - # Subproblem f(1): move the remaining disk from src to tar + # Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar) # Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar) @@ -34,9 +34,9 @@ def solve_hanota(A: list[int], B: list[int], C: list[int]): dfs(n, A, B, C) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # The tail of the list is the top of the rod + # The end of each list represents the top of the peg A = [5, 4, 3, 2, 1] B = [] C = [] diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py index e3928681b..79dcb2caa 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py @@ -7,31 +7,31 @@ Author: krahets (krahets@163.com) def backtrack(choices: list[int], state: int, n: int, res: list[int]) -> int: """Backtracking""" - # When climbing to the n-th stair, add 1 to the solution count + # When step n is reached, increase the number of ways by 1 if state == n: res[0] += 1 - # Traverse all choices + # Iterate over all choices for choice in choices: - # Pruning: not allowed to go beyond the n-th stair + # Prune: do not go beyond step n if state + choice > n: continue - # Attempt: make a choice, update state + # Try: make a choice and update the state backtrack(choices, state + choice, n, res) # Backtrack def climbing_stairs_backtrack(n: int) -> int: - """Climbing stairs: Backtracking""" - choices = [1, 2] # Can choose to climb up 1 or 2 stairs - state = 0 # Start climbing from the 0-th stair - res = [0] # Use res[0] to record the solution count + """Climbing stairs: backtracking""" + choices = [1, 2] # Choose to climb 1 or 2 steps + state = 0 # Start climbing from step 0 + res = [0] # Use res[0] to record the number of ways backtrack(choices, state, n, res) return res[0] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 9 res = climbing_stairs_backtrack(n) - print(f"Climbing {n} stairs has {res} solutions") + print(f"Number of ways to climb {n} stairs: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py index 15ece1800..ac3c74b7f 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py @@ -6,24 +6,24 @@ Author: krahets (krahets@163.com) def climbing_stairs_constraint_dp(n: int) -> int: - """Climbing stairs with constraint: Dynamic programming""" + """Climbing stairs with constraints: dynamic programming""" if n == 1 or n == 2: return 1 - # Initialize dp table, used to store solutions to subproblems + # Initialize the dp table to store subproblem solutions dp = [[0] * 3 for _ in range(n + 1)] - # Initial state: preset the solution to the smallest subproblem + # Initial states: set the solutions to the smallest subproblems dp[1][1], dp[1][2] = 1, 0 dp[2][1], dp[2][2] = 0, 1 - # State transition: gradually solve larger subproblems from smaller ones + # State transitions: solve progressively larger subproblems using smaller ones for i in range(3, n + 1): dp[i][1] = dp[i - 1][2] dp[i][2] = dp[i - 2][1] + dp[i - 2][2] return dp[n][1] + dp[n][2] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 9 res = climbing_stairs_constraint_dp(n) - print(f"Climbing {n} stairs has {res} solutions") + print(f"Number of ways to climb {n} stairs: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py index f435763f3..3fea8bc1b 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py @@ -7,7 +7,7 @@ Author: krahets (krahets@163.com) def dfs(i: int) -> int: """Search""" - # Known dp[1] and dp[2], return them + # The values of dp[1] and dp[2] are known; return the corresponding value if i == 1 or i == 2: return i # dp[i] = dp[i-1] + dp[i-2] @@ -16,13 +16,13 @@ def dfs(i: int) -> int: def climbing_stairs_dfs(n: int) -> int: - """Climbing stairs: Search""" + """Climbing stairs: search""" return dfs(n) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 9 res = climbing_stairs_dfs(n) - print(f"Climbing {n} stairs has {res} solutions") + print(f"Number of ways to climb {n} stairs: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py index a753f5583..68676abaf 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py @@ -6,11 +6,11 @@ Author: krahets (krahets@163.com) def dfs(i: int, mem: list[int]) -> int: - """Memoization search""" - # Known dp[1] and dp[2], return them + """Memoized search""" + # The values of dp[1] and dp[2] are known; return the corresponding value if i == 1 or i == 2: return i - # If record dp[i] exists, return it directly + # If dp[i] has been recorded, return it directly if mem[i] != -1: return mem[i] # dp[i] = dp[i-1] + dp[i-2] @@ -21,15 +21,15 @@ def dfs(i: int, mem: list[int]) -> int: def climbing_stairs_dfs_mem(n: int) -> int: - """Climbing stairs: Memoization search""" - # mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + """Climbing stairs: memoized search""" + # mem[i] stores the number of ways to reach step i; -1 means no result has been recorded mem = [-1] * (n + 1) return dfs(n, mem) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 9 res = climbing_stairs_dfs_mem(n) - print(f"Climbing {n} stairs has {res} solutions") + print(f"Number of ways to climb {n} stairs: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py index 5cf0f0e9f..73ac18d0a 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py @@ -6,21 +6,21 @@ Author: krahets (krahets@163.com) def climbing_stairs_dp(n: int) -> int: - """Climbing stairs: Dynamic programming""" + """Climbing stairs: dynamic programming""" if n == 1 or n == 2: return n - # Initialize dp table, used to store solutions to subproblems + # Initialize the dp table to store subproblem solutions dp = [0] * (n + 1) - # Initial state: preset the solution to the smallest subproblem + # Initial states: set the solutions to the smallest subproblems dp[1], dp[2] = 1, 2 - # State transition: gradually solve larger subproblems from smaller ones + # State transitions: solve progressively larger subproblems using smaller ones for i in range(3, n + 1): dp[i] = dp[i - 1] + dp[i - 2] return dp[n] def climbing_stairs_dp_comp(n: int) -> int: - """Climbing stairs: Space-optimized dynamic programming""" + """Climbing stairs: space-optimized dynamic programming""" if n == 1 or n == 2: return n a, b = 1, 2 @@ -29,12 +29,12 @@ def climbing_stairs_dp_comp(n: int) -> int: return b -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 9 res = climbing_stairs_dp(n) - print(f"Climbing {n} stairs has {res} solutions") + print(f"Number of ways to climb {n} stairs: {res}") res = climbing_stairs_dp_comp(n) - print(f"Climbing {n} stairs has {res} solutions") + print(f"Number of ways to climb {n} stairs: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/coin_change.py b/en/codes/python/chapter_dynamic_programming/coin_change.py index b4f36f12f..c070138b0 100644 --- a/en/codes/python/chapter_dynamic_programming/coin_change.py +++ b/en/codes/python/chapter_dynamic_programming/coin_change.py @@ -6,55 +6,55 @@ Author: krahets (krahets@163.com) def coin_change_dp(coins: list[int], amt: int) -> int: - """Coin change: Dynamic programming""" + """Coin change: dynamic programming""" n = len(coins) MAX = amt + 1 - # Initialize dp table + # Initialize the dp table dp = [[0] * (amt + 1) for _ in range(n + 1)] - # State transition: first row and first column + # State transitions: first row and first column for a in range(1, amt + 1): dp[0][a] = MAX - # State transition: rest of the rows and columns + # State transitions: remaining rows and columns for i in range(1, n + 1): for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeds target amount, don't select coin i + # If the coin value exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a] else: - # The smaller value between not selecting and selecting coin i + # Take the smaller result of not choosing and choosing coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) return dp[n][amt] if dp[n][amt] != MAX else -1 def coin_change_dp_comp(coins: list[int], amt: int) -> int: - """Coin change: Space-optimized dynamic programming""" + """Coin change: space-optimized dynamic programming""" n = len(coins) MAX = amt + 1 - # Initialize dp table + # Initialize the dp table dp = [MAX] * (amt + 1) dp[0] = 0 - # State transition + # State transitions for i in range(1, n + 1): - # Traverse in forward order + # Traverse in increasing order for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeds target amount, don't select coin i + # If the coin value exceeds the current amount a, do not choose coin i dp[a] = dp[a] else: - # The smaller value between not selecting and selecting coin i + # Take the smaller result of not choosing and choosing coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) return dp[amt] if dp[amt] != MAX else -1 -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": coins = [1, 2, 5] amt = 4 # Dynamic programming res = coin_change_dp(coins, amt) - print(f"The minimum number of coins needed to make up the target amount is {res}") + print(f"Minimum number of coins needed to make the target amount: {res}") # Space-optimized dynamic programming res = coin_change_dp_comp(coins, amt) - print(f"The minimum number of coins needed to make up the target amount is {res}") + print(f"Minimum number of coins needed to make the target amount: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/coin_change_ii.py b/en/codes/python/chapter_dynamic_programming/coin_change_ii.py index 42db91a75..3f8369c71 100644 --- a/en/codes/python/chapter_dynamic_programming/coin_change_ii.py +++ b/en/codes/python/chapter_dynamic_programming/coin_change_ii.py @@ -6,53 +6,53 @@ Author: krahets (krahets@163.com) def coin_change_ii_dp(coins: list[int], amt: int) -> int: - """Coin change II: Dynamic programming""" + """Coin change II: dynamic programming""" n = len(coins) - # Initialize dp table + # Initialize the dp table dp = [[0] * (amt + 1) for _ in range(n + 1)] - # Initialize first column + # Initialize the first column for i in range(n + 1): dp[i][0] = 1 - # State transition + # State transitions for i in range(1, n + 1): for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeds target amount, don't select coin i + # If the coin value exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a] else: - # Sum of the two options: not selecting and selecting coin i + # Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] return dp[n][amt] def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int: - """Coin change II: Space-optimized dynamic programming""" + """Coin change II: space-optimized dynamic programming""" n = len(coins) - # Initialize dp table + # Initialize the dp table dp = [0] * (amt + 1) dp[0] = 1 - # State transition + # State transitions for i in range(1, n + 1): - # Traverse in forward order + # Traverse in increasing order for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeds target amount, don't select coin i + # If the coin value exceeds the current amount a, do not choose coin i dp[a] = dp[a] else: - # Sum of the two options: not selecting and selecting coin i + # Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]] return dp[amt] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": coins = [1, 2, 5] amt = 5 # Dynamic programming res = coin_change_ii_dp(coins, amt) - print(f"The number of coin combinations to make up the target amount is {res}") + print(f"Number of coin combinations that make the target amount: {res}") # Space-optimized dynamic programming res = coin_change_ii_dp_comp(coins, amt) - print(f"The number of coin combinations to make up the target amount is {res}") + print(f"Number of coin combinations that make the target amount: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/edit_distance.py b/en/codes/python/chapter_dynamic_programming/edit_distance.py index 59d8710c7..3cfd1ff8d 100644 --- a/en/codes/python/chapter_dynamic_programming/edit_distance.py +++ b/en/codes/python/chapter_dynamic_programming/edit_distance.py @@ -6,100 +6,100 @@ Author: krahets (krahets@163.com) def edit_distance_dfs(s: str, t: str, i: int, j: int) -> int: - """Edit distance: Brute-force search""" - # If both s and t are empty, return 0 + """Edit distance: brute-force search""" + # If both prefixes s[:i] and t[:j] are empty, return 0 if i == 0 and j == 0: return 0 - # If s is empty, return length of t + # If prefix s[:i] is empty, return the length j of prefix t[:j] if i == 0: return j - # If t is empty, return length of s + # If prefix t[:j] is empty, return the length i of prefix s[:i] if j == 0: return i - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them if s[i - 1] == t[j - 1]: return edit_distance_dfs(s, t, i - 1, j - 1) - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 insert = edit_distance_dfs(s, t, i, j - 1) delete = edit_distance_dfs(s, t, i - 1, j) replace = edit_distance_dfs(s, t, i - 1, j - 1) - # Return minimum edit steps + # Return the minimum number of edits return min(insert, delete, replace) + 1 def edit_distance_dfs_mem(s: str, t: str, mem: list[list[int]], i: int, j: int) -> int: - """Edit distance: Memoization search""" - # If both s and t are empty, return 0 + """Edit distance: memoized search""" + # If both prefixes s[:i] and t[:j] are empty, return 0 if i == 0 and j == 0: return 0 - # If s is empty, return length of t + # If prefix s[:i] is empty, return the length j of prefix t[:j] if i == 0: return j - # If t is empty, return length of s + # If prefix t[:j] is empty, return the length i of prefix s[:i] if j == 0: return i - # If there's a record, return it directly + # If a result has already been recorded, return it directly if mem[i][j] != -1: return mem[i][j] - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them if s[i - 1] == t[j - 1]: return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 insert = edit_distance_dfs_mem(s, t, mem, i, j - 1) delete = edit_distance_dfs_mem(s, t, mem, i - 1, j) replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) - # Record and return minimum edit steps + # Record and return the minimum number of edits mem[i][j] = min(insert, delete, replace) + 1 return mem[i][j] def edit_distance_dp(s: str, t: str) -> int: - """Edit distance: Dynamic programming""" + """Edit distance: dynamic programming""" n, m = len(s), len(t) dp = [[0] * (m + 1) for _ in range(n + 1)] - # State transition: first row and first column + # State transitions: first row and first column for i in range(1, n + 1): dp[i][0] = i for j in range(1, m + 1): dp[0][j] = j - # State transition: rest of the rows and columns + # State transitions: remaining rows and columns for i in range(1, n + 1): for j in range(1, m + 1): if s[i - 1] == t[j - 1]: - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1] else: - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 return dp[n][m] def edit_distance_dp_comp(s: str, t: str) -> int: - """Edit distance: Space-optimized dynamic programming""" + """Edit distance: space-optimized dynamic programming""" n, m = len(s), len(t) dp = [0] * (m + 1) - # State transition: first row + # State transitions: first row for j in range(1, m + 1): dp[j] = j - # State transition: rest of the rows + # State transitions: remaining rows for i in range(1, n + 1): - # State transition: first column + # State transitions: first column leftup = dp[0] # Temporarily store dp[i-1, j-1] dp[0] += 1 - # State transition: rest of the columns + # State transitions: remaining columns for j in range(1, m + 1): temp = dp[j] if s[i - 1] == t[j - 1]: - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them dp[j] = leftup else: - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = min(dp[j - 1], dp[j], leftup) + 1 - leftup = temp # Update for next round's dp[i-1, j-1] + leftup = temp # Update to dp[i-1, j-1] for the next iteration return dp[m] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": s = "bag" t = "pack" @@ -107,17 +107,17 @@ if __name__ == "__main__": # Brute-force search res = edit_distance_dfs(s, t, n, m) - print(f"Changing {s} to {t} requires a minimum of {res} edits") + print(f"Changing {s} to {t} requires at least {res} edits") - # Memoization search + # Memoized search mem = [[-1] * (m + 1) for _ in range(n + 1)] res = edit_distance_dfs_mem(s, t, mem, n, m) - print(f"Changing {s} to {t} requires a minimum of {res} edits") + print(f"Changing {s} to {t} requires at least {res} edits") # Dynamic programming res = edit_distance_dp(s, t) - print(f"Changing {s} to {t} requires a minimum of {res} edits") + print(f"Changing {s} to {t} requires at least {res} edits") # Space-optimized dynamic programming res = edit_distance_dp_comp(s, t) - print(f"Changing {s} to {t} requires a minimum of {res} edits") + print(f"Changing {s} to {t} requires at least {res} edits") diff --git a/en/codes/python/chapter_dynamic_programming/knapsack.py b/en/codes/python/chapter_dynamic_programming/knapsack.py index a40281c49..a219398f1 100644 --- a/en/codes/python/chapter_dynamic_programming/knapsack.py +++ b/en/codes/python/chapter_dynamic_programming/knapsack.py @@ -6,77 +6,77 @@ Author: krahets (krahets@163.com) def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int: - """0-1 knapsack: Brute-force search""" - # If all items have been selected or knapsack has no remaining capacity, return value 0 + """0-1 knapsack: brute-force search""" + # If all items have been considered or no capacity remains, return value 0 if i == 0 or c == 0: return 0 - # If exceeds knapsack capacity, can only choose not to put it in + # If the item exceeds the remaining capacity, it must be excluded if wgt[i - 1] > c: return knapsack_dfs(wgt, val, i - 1, c) - # Calculate the maximum value of not putting in and putting in item i + # Calculate the maximum values obtained by excluding and including item i no = knapsack_dfs(wgt, val, i - 1, c) yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] - # Return the larger value of the two options + # Return the larger value from the two choices return max(no, yes) def knapsack_dfs_mem( wgt: list[int], val: list[int], mem: list[list[int]], i: int, c: int ) -> int: - """0-1 knapsack: Memoization search""" - # If all items have been selected or knapsack has no remaining capacity, return value 0 + """0-1 knapsack: memoized search""" + # If all items have been considered or no capacity remains, return value 0 if i == 0 or c == 0: return 0 - # If there's a record, return it directly + # If a result has already been recorded, return it directly if mem[i][c] != -1: return mem[i][c] - # If exceeds knapsack capacity, can only choose not to put it in + # If the item exceeds the remaining capacity, it must be excluded if wgt[i - 1] > c: return knapsack_dfs_mem(wgt, val, mem, i - 1, c) - # Calculate the maximum value of not putting in and putting in item i + # Calculate the maximum values obtained by excluding and including item i no = knapsack_dfs_mem(wgt, val, mem, i - 1, c) yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1] - # Record and return the larger value of the two options + # Record and return the larger value from the two choices mem[i][c] = max(no, yes) return mem[i][c] def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: - """0-1 knapsack: Dynamic programming""" + """0-1 knapsack: dynamic programming""" n = len(wgt) - # Initialize dp table + # Initialize the dp table dp = [[0] * (cap + 1) for _ in range(n + 1)] - # State transition + # State transitions for i in range(1, n + 1): for c in range(1, cap + 1): if wgt[i - 1] > c: - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] else: - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]) return dp[n][cap] def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: - """0-1 knapsack: Space-optimized dynamic programming""" + """0-1 knapsack: space-optimized dynamic programming""" n = len(wgt) - # Initialize dp table + # Initialize the dp table dp = [0] * (cap + 1) - # State transition + # State transitions for i in range(1, n + 1): - # Traverse in reverse order + # Traverse in decreasing order for c in range(cap, 0, -1): if wgt[i - 1] > c: - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c] else: - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) return dp[cap] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": wgt = [10, 20, 30, 40, 50] val = [50, 120, 150, 210, 240] @@ -85,17 +85,17 @@ if __name__ == "__main__": # Brute-force search res = knapsack_dfs(wgt, val, n, cap) - print(f"The maximum item value not exceeding knapsack capacity is {res}") + print(f"Maximum total value within the knapsack capacity: {res}") - # Memoization search + # Memoized search mem = [[-1] * (cap + 1) for _ in range(n + 1)] res = knapsack_dfs_mem(wgt, val, mem, n, cap) - print(f"The maximum item value not exceeding knapsack capacity is {res}") + print(f"Maximum total value within the knapsack capacity: {res}") # Dynamic programming res = knapsack_dp(wgt, val, cap) - print(f"The maximum item value not exceeding knapsack capacity is {res}") + print(f"Maximum total value within the knapsack capacity: {res}") # Space-optimized dynamic programming res = knapsack_dp_comp(wgt, val, cap) - print(f"The maximum item value not exceeding knapsack capacity is {res}") + print(f"Maximum total value within the knapsack capacity: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py b/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py index 94abbfd4a..fe6719685 100644 --- a/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py +++ b/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py @@ -6,22 +6,22 @@ Author: krahets (krahets@163.com) def min_cost_climbing_stairs_dp(cost: list[int]) -> int: - """Minimum cost climbing stairs: Dynamic programming""" + """Minimum cost to climb stairs: dynamic programming""" n = len(cost) - 1 if n == 1 or n == 2: return cost[n] - # Initialize dp table, used to store solutions to subproblems + # Initialize the dp table to store subproblem solutions dp = [0] * (n + 1) - # Initial state: preset the solution to the smallest subproblem + # Initial states: set the solutions to the smallest subproblems dp[1], dp[2] = cost[1], cost[2] - # State transition: gradually solve larger subproblems from smaller ones + # State transitions: solve progressively larger subproblems using smaller ones for i in range(3, n + 1): dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] return dp[n] def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int: - """Minimum cost climbing stairs: Space-optimized dynamic programming""" + """Minimum cost to climb stairs: space-optimized dynamic programming""" n = len(cost) - 1 if n == 1 or n == 2: return cost[n] @@ -31,13 +31,13 @@ def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int: return b -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1] - print(f"Input stair cost list is {cost}") + print(f"Input list of stair costs: {cost}") res = min_cost_climbing_stairs_dp(cost) - print(f"The minimum cost to finish climbing the stairs is {res}") + print(f"Minimum cost to finish climbing the stairs: {res}") res = min_cost_climbing_stairs_dp_comp(cost) - print(f"The minimum cost to finish climbing the stairs is {res}") + print(f"Minimum cost to finish climbing the stairs: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/min_path_sum.py b/en/codes/python/chapter_dynamic_programming/min_path_sum.py index 48cdeb00e..376b9f857 100644 --- a/en/codes/python/chapter_dynamic_programming/min_path_sum.py +++ b/en/codes/python/chapter_dynamic_programming/min_path_sum.py @@ -8,54 +8,54 @@ from math import inf def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int: - """Minimum path sum: Brute-force search""" - # If it's the top-left cell, terminate the search + """Minimum path sum: brute-force search""" + # Stop searching at the top-left cell if i == 0 and j == 0: return grid[0][0] - # If row or column index is out of bounds, return +∞ cost + # If a row or column index is out of bounds, return a cost of +∞ if i < 0 or j < 0: return inf - # Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + # Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) up = min_path_sum_dfs(grid, i - 1, j) left = min_path_sum_dfs(grid, i, j - 1) - # Return the minimum path cost from top-left to (i, j) + # Return the minimum path cost from the top-left corner to (i, j) return min(left, up) + grid[i][j] def min_path_sum_dfs_mem( grid: list[list[int]], mem: list[list[int]], i: int, j: int ) -> int: - """Minimum path sum: Memoization search""" - # If it's the top-left cell, terminate the search + """Minimum path sum: memoized search""" + # Stop searching at the top-left cell if i == 0 and j == 0: return grid[0][0] - # If row or column index is out of bounds, return +∞ cost + # If a row or column index is out of bounds, return a cost of +∞ if i < 0 or j < 0: return inf - # If there's a record, return it directly + # If a result has already been recorded, return it directly if mem[i][j] != -1: return mem[i][j] - # Minimum path cost for left and upper cells + # Minimum path costs to the cells on the left and above up = min_path_sum_dfs_mem(grid, mem, i - 1, j) left = min_path_sum_dfs_mem(grid, mem, i, j - 1) - # Record and return the minimum path cost from top-left to (i, j) + # Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = min(left, up) + grid[i][j] return mem[i][j] def min_path_sum_dp(grid: list[list[int]]) -> int: - """Minimum path sum: Dynamic programming""" + """Minimum path sum: dynamic programming""" n, m = len(grid), len(grid[0]) - # Initialize dp table + # Initialize the dp table dp = [[0] * m for _ in range(n)] dp[0][0] = grid[0][0] - # State transition: first row + # State transitions: first row for j in range(1, m): dp[0][j] = dp[0][j - 1] + grid[0][j] - # State transition: first column + # State transitions: first column for i in range(1, n): dp[i][0] = dp[i - 1][0] + grid[i][0] - # State transition: rest of the rows and columns + # State transitions: remaining rows and columns for i in range(1, n): for j in range(1, m): dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] @@ -63,42 +63,42 @@ def min_path_sum_dp(grid: list[list[int]]) -> int: def min_path_sum_dp_comp(grid: list[list[int]]) -> int: - """Minimum path sum: Space-optimized dynamic programming""" + """Minimum path sum: space-optimized dynamic programming""" n, m = len(grid), len(grid[0]) - # Initialize dp table + # Initialize the dp table dp = [0] * m - # State transition: first row + # State transitions: first row dp[0] = grid[0][0] for j in range(1, m): dp[j] = dp[j - 1] + grid[0][j] - # State transition: rest of the rows + # State transitions: remaining rows for i in range(1, n): - # State transition: first column + # State transitions: first column dp[0] = dp[0] + grid[i][0] - # State transition: rest of the columns + # State transitions: remaining columns for j in range(1, m): dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] return dp[m - 1] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": grid = [[1, 3, 1, 5], [2, 2, 4, 2], [5, 3, 2, 1], [4, 3, 5, 2]] n, m = len(grid), len(grid[0]) # Brute-force search res = min_path_sum_dfs(grid, n - 1, m - 1) - print(f"The minimum path sum from top-left to bottom-right is {res}") + print(f"Minimum path sum from the top-left to the bottom-right corner: {res}") - # Memoization search + # Memoized search mem = [[-1] * m for _ in range(n)] res = min_path_sum_dfs_mem(grid, mem, n - 1, m - 1) - print(f"The minimum path sum from top-left to bottom-right is {res}") + print(f"Minimum path sum from the top-left to the bottom-right corner: {res}") # Dynamic programming res = min_path_sum_dp(grid) - print(f"The minimum path sum from top-left to bottom-right is {res}") + print(f"Minimum path sum from the top-left to the bottom-right corner: {res}") # Space-optimized dynamic programming res = min_path_sum_dp_comp(grid) - print(f"The minimum path sum from top-left to bottom-right is {res}") + print(f"Minimum path sum from the top-left to the bottom-right corner: {res}") diff --git a/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py b/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py index 72b568c15..7926b5da8 100644 --- a/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py +++ b/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py @@ -6,41 +6,41 @@ Author: krahets (krahets@163.com) def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: - """Unbounded knapsack: Dynamic programming""" + """Unbounded knapsack: dynamic programming""" n = len(wgt) - # Initialize dp table + # Initialize the dp table dp = [[0] * (cap + 1) for _ in range(n + 1)] - # State transition + # State transitions for i in range(1, n + 1): for c in range(1, cap + 1): if wgt[i - 1] > c: - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] else: - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]) return dp[n][cap] def unbounded_knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: - """Unbounded knapsack: Space-optimized dynamic programming""" + """Unbounded knapsack: space-optimized dynamic programming""" n = len(wgt) - # Initialize dp table + # Initialize the dp table dp = [0] * (cap + 1) - # State transition + # State transitions for i in range(1, n + 1): - # Traverse in forward order + # Traverse in increasing order for c in range(1, cap + 1): if wgt[i - 1] > c: - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c] else: - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) return dp[cap] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": wgt = [1, 2, 3] val = [5, 11, 15] @@ -48,8 +48,8 @@ if __name__ == "__main__": # Dynamic programming res = unbounded_knapsack_dp(wgt, val, cap) - print(f"The maximum item value not exceeding knapsack capacity is {res}") + print(f"Maximum total value within the knapsack capacity: {res}") # Space-optimized dynamic programming res = unbounded_knapsack_dp_comp(wgt, val, cap) - print(f"The maximum item value not exceeding knapsack capacity is {res}") + print(f"Maximum total value within the knapsack capacity: {res}") diff --git a/en/codes/python/chapter_graph/graph_adjacency_list.py b/en/codes/python/chapter_graph/graph_adjacency_list.py index adbd43827..1df48ca59 100644 --- a/en/codes/python/chapter_graph/graph_adjacency_list.py +++ b/en/codes/python/chapter_graph/graph_adjacency_list.py @@ -12,11 +12,11 @@ from modules import Vertex, vals_to_vets class GraphAdjList: - """Undirected graph class based on adjacency list""" + """Undirected graph class implemented with an adjacency list""" def __init__(self, edges: list[list[Vertex]]): """Constructor""" - # Adjacency list, key: vertex, value: all adjacent vertices of that vertex + # Adjacency list: key is a vertex, value is the list of all its neighbors self.adj_list = dict[Vertex, list[Vertex]]() # Add all vertices and edges for edge in edges: @@ -29,7 +29,7 @@ class GraphAdjList: return len(self.adj_list) def add_edge(self, vet1: Vertex, vet2: Vertex): - """Add edge""" + """Add an edge""" if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: raise ValueError() # Add edge vet1 - vet2 @@ -37,7 +37,7 @@ class GraphAdjList: self.adj_list[vet2].append(vet1) def remove_edge(self, vet1: Vertex, vet2: Vertex): - """Remove edge""" + """Remove an edge""" if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: raise ValueError() # Remove edge vet1 - vet2 @@ -45,34 +45,34 @@ class GraphAdjList: self.adj_list[vet2].remove(vet1) def add_vertex(self, vet: Vertex): - """Add vertex""" + """Add a vertex""" if vet in self.adj_list: return - # Add a new linked list in the adjacency list + # Add a new list to the adjacency list self.adj_list[vet] = [] def remove_vertex(self, vet: Vertex): - """Remove vertex""" + """Remove a vertex""" if vet not in self.adj_list: raise ValueError() - # Remove the linked list corresponding to vertex vet in the adjacency list + # Remove the list corresponding to vertex vet from the adjacency list self.adj_list.pop(vet) - # Traverse the linked lists of other vertices and remove all edges containing vet + # Traverse the lists of the other vertices and remove all edges incident to vet for vertex in self.adj_list: if vet in self.adj_list[vertex]: self.adj_list[vertex].remove(vet) def print(self): - """Print adjacency list""" + """Print the adjacency list""" print("Adjacency list =") for vertex in self.adj_list: tmp = [v.val for v in self.adj_list[vertex]] print(f"{vertex.val}: {tmp},") -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize undirected graph + # Initialize the undirected graph v = vals_to_vets([1, 3, 2, 5, 4]) edges = [ [v[0], v[1]], @@ -83,29 +83,29 @@ if __name__ == "__main__": [v[3], v[4]], ] graph = GraphAdjList(edges) - print("\nAfter initialization, the graph is") + print("\nGraph after initialization:") graph.print() - # Add edge - # Vertices 1, 2 are v[0], v[2] + # Add an edge + # Vertices 1 and 2 are v[0] and v[2] graph.add_edge(v[0], v[2]) - print("\nAfter adding edge 1-2, the graph is") + print("\nGraph after adding edge 1-2:") graph.print() - # Remove edge - # Vertices 1, 3 are v[0], v[1] + # Remove an edge + # Vertices 1 and 3 are v[0] and v[1] graph.remove_edge(v[0], v[1]) - print("\nAfter removing edge 1-3, the graph is") + print("\nGraph after removing edge 1-3:") graph.print() - # Add vertex + # Add a vertex v5 = Vertex(6) graph.add_vertex(v5) - print("\nAfter adding vertex 6, the graph is") + print("\nGraph after adding vertex 6:") graph.print() - # Remove vertex + # Remove a vertex # Vertex 3 is v[1] graph.remove_vertex(v[1]) - print("\nAfter removing vertex 3, the graph is") + print("\nGraph after removing vertex 3:") graph.print() diff --git a/en/codes/python/chapter_graph/graph_adjacency_matrix.py b/en/codes/python/chapter_graph/graph_adjacency_matrix.py index 4b86c6b1c..3b4c19fa5 100644 --- a/en/codes/python/chapter_graph/graph_adjacency_matrix.py +++ b/en/codes/python/chapter_graph/graph_adjacency_matrix.py @@ -12,19 +12,19 @@ from modules import Vertex, print_matrix class GraphAdjMat: - """Undirected graph class based on adjacency matrix""" + """Undirected graph class implemented with an adjacency matrix""" def __init__(self, vertices: list[int], edges: list[list[int]]): """Constructor""" - # Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + # Vertex list: elements are vertex values, and indices are vertex indices self.vertices: list[int] = [] - # Adjacency matrix, where the row and column indices correspond to the "vertex index" + # Adjacency matrix: row and column indices correspond to vertex indices self.adj_mat: list[list[int]] = [] - # Add vertices + # Add a vertex for val in vertices: self.add_vertex(val) - # Add edges - # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + # Add an edge + # Note that the entries in edges are vertex indices, corresponding to element indices in vertices for e in edges: self.add_edge(e[0], e[1]) @@ -33,9 +33,9 @@ class GraphAdjMat: return len(self.vertices) def add_vertex(self, val: int): - """Add vertex""" + """Add a vertex""" n = self.size() - # Add the value of the new vertex to the vertex list + # Append the new vertex value to the vertex list self.vertices.append(val) # Add a row to the adjacency matrix new_row = [0] * n @@ -45,72 +45,72 @@ class GraphAdjMat: row.append(0) def remove_vertex(self, index: int): - """Remove vertex""" + """Remove a vertex""" if index >= self.size(): raise IndexError() - # Remove the vertex at index from the vertex list + # Remove the vertex at index `index` from the vertex list self.vertices.pop(index) - # Remove the row at index from the adjacency matrix + # Remove row `index` from the adjacency matrix self.adj_mat.pop(index) - # Remove the column at index from the adjacency matrix + # Remove column `index` from the adjacency matrix for row in self.adj_mat: row.pop(index) def add_edge(self, i: int, j: int): - """Add edge""" - # Parameters i, j correspond to the vertices element indices - # Handle index out of bounds and equality + """Add an edge""" + # Parameters i and j are element indices in vertices + # Handle out-of-bounds or equal indices if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: raise IndexError() - # In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + # For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal self.adj_mat[i][j] = 1 self.adj_mat[j][i] = 1 def remove_edge(self, i: int, j: int): - """Remove edge""" - # Parameters i, j correspond to the vertices element indices - # Handle index out of bounds and equality + """Remove an edge""" + # Parameters i and j are element indices in vertices + # Handle out-of-bounds or equal indices if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: raise IndexError() self.adj_mat[i][j] = 0 self.adj_mat[j][i] = 0 def print(self): - """Print adjacency matrix""" + """Print the adjacency matrix""" print("Vertex list =", self.vertices) print("Adjacency matrix =") print_matrix(self.adj_mat) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize undirected graph - # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + # Initialize the undirected graph + # Note that the entries in edges are vertex indices, corresponding to element indices in vertices vertices = [1, 3, 2, 5, 4] edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]] graph = GraphAdjMat(vertices, edges) - print("\nAfter initialization, the graph is") + print("\nGraph after initialization:") graph.print() - # Add edge - # Vertices 1, 2 have indices 0, 2 respectively + # Add an edge + # Vertices 1 and 2 have indices 0 and 2, respectively graph.add_edge(0, 2) - print("\nAfter adding edge 1-2, the graph is") + print("\nGraph after adding edge 1-2:") graph.print() - # Remove edge - # Vertices 1, 3 have indices 0, 1 respectively + # Remove an edge + # Vertices 1 and 3 have indices 0 and 1, respectively graph.remove_edge(0, 1) - print("\nAfter removing edge 1-3, the graph is") + print("\nGraph after removing edge 1-3:") graph.print() - # Add vertex + # Add a vertex graph.add_vertex(6) - print("\nAfter adding vertex 6, the graph is") + print("\nGraph after adding vertex 6:") graph.print() - # Remove vertex + # Remove a vertex # Vertex 3 has index 1 graph.remove_vertex(1) - print("\nAfter removing vertex 3, the graph is") + print("\nGraph after removing vertex 3:") graph.print() diff --git a/en/codes/python/chapter_graph/graph_bfs.py b/en/codes/python/chapter_graph/graph_bfs.py index 59b6b66d1..e0bc38715 100644 --- a/en/codes/python/chapter_graph/graph_bfs.py +++ b/en/codes/python/chapter_graph/graph_bfs.py @@ -15,30 +15,30 @@ from graph_adjacency_list import GraphAdjList def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: """Breadth-first traversal""" - # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + # Represent the graph with an adjacency list to access all neighbors of a given vertex # Vertex traversal sequence res = [] - # Hash set for recording vertices that have been visited + # Hash set recording visited vertices visited = set[Vertex]([start_vet]) - # Queue used to implement BFS + # Use a queue to implement BFS que = deque[Vertex]([start_vet]) - # Starting from vertex vet, loop until all vertices are visited + # Starting from start_vet, loop until all reachable vertices have been visited while len(que) > 0: vet = que.popleft() # Dequeue the front vertex - res.append(vet) # Record visited vertex - # Traverse all adjacent vertices of this vertex + res.append(vet) # Record the visited vertex + # Traverse all neighbors of this vertex for adj_vet in graph.adj_list[vet]: if adj_vet in visited: - continue # Skip vertices that have been visited - que.append(adj_vet) # Only enqueue unvisited vertices + continue # Skip vertices that have already been visited + que.append(adj_vet) # Enqueue only unvisited vertices visited.add(adj_vet) # Mark this vertex as visited - # Return vertex traversal sequence + # Return the vertex traversal sequence return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize undirected graph + # Initialize the undirected graph v = vals_to_vets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) edges = [ [v[0], v[1]], @@ -55,10 +55,10 @@ if __name__ == "__main__": [v[7], v[8]], ] graph = GraphAdjList(edges) - print("\nAfter initialization, the graph is") + print("\nGraph after initialization:") graph.print() # Breadth-first traversal res = graph_bfs(graph, v[0]) - print("\nBreadth-first traversal (BFS) vertex sequence is") + print("\nVertex sequence from breadth-first traversal (BFS):") print(vets_to_vals(res)) diff --git a/en/codes/python/chapter_graph/graph_dfs.py b/en/codes/python/chapter_graph/graph_dfs.py index a4000b241..bf04ee275 100644 --- a/en/codes/python/chapter_graph/graph_dfs.py +++ b/en/codes/python/chapter_graph/graph_dfs.py @@ -14,30 +14,30 @@ from graph_adjacency_list import GraphAdjList def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex): """Depth-first traversal helper function""" - res.append(vet) # Record visited vertex + res.append(vet) # Record the visited vertex visited.add(vet) # Mark this vertex as visited - # Traverse all adjacent vertices of this vertex + # Traverse all neighbors of this vertex for adjVet in graph.adj_list[vet]: if adjVet in visited: - continue # Skip vertices that have been visited - # Recursively visit adjacent vertices + continue # Skip vertices that have already been visited + # Recursively visit the neighboring vertex dfs(graph, visited, res, adjVet) def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: """Depth-first traversal""" - # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + # Represent the graph with an adjacency list to access all neighbors of a given vertex # Vertex traversal sequence res = [] - # Hash set for recording vertices that have been visited + # Hash set recording visited vertices visited = set[Vertex]() dfs(graph, visited, res, start_vet) return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize undirected graph + # Initialize the undirected graph v = vals_to_vets([0, 1, 2, 3, 4, 5, 6]) edges = [ [v[0], v[1]], @@ -48,10 +48,10 @@ if __name__ == "__main__": [v[5], v[6]], ] graph = GraphAdjList(edges) - print("\nAfter initialization, the graph is") + print("\nGraph after initialization:") graph.print() # Depth-first traversal res = graph_dfs(graph, v[0]) - print("\nDepth-first traversal (DFS) vertex sequence is") + print("\nVertex sequence from depth-first traversal (DFS):") print(vets_to_vals(res)) diff --git a/en/codes/python/chapter_greedy/coin_change_greedy.py b/en/codes/python/chapter_greedy/coin_change_greedy.py index 0be2171d3..646502e16 100644 --- a/en/codes/python/chapter_greedy/coin_change_greedy.py +++ b/en/codes/python/chapter_greedy/coin_change_greedy.py @@ -6,43 +6,43 @@ Author: krahets (krahets@163.com) def coin_change_greedy(coins: list[int], amt: int) -> int: - """Coin change: Greedy algorithm""" - # Assume coins list is sorted + """Coin change: greedy algorithm""" + # Assume the coins list is sorted i = len(coins) - 1 count = 0 - # Loop to make greedy choices until no remaining amount + # Repeatedly make greedy choices until no amount remains while amt > 0: - # Find the coin that is less than and closest to the remaining amount + # Find the largest coin no greater than the remaining amount while i > 0 and coins[i] > amt: i -= 1 # Choose coins[i] amt -= coins[i] count += 1 - # If no feasible solution is found, return -1 + # Return -1 if no feasible solution was found return count if amt == 0 else -1 -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Greedy algorithm: Can guarantee finding the global optimal solution + # Greedy algorithm: guaranteed to find a globally optimal solution coins = [1, 5, 10, 20, 50, 100] amt = 186 res = coin_change_greedy(coins, amt) print(f"\ncoins = {coins}, amt = {amt}") - print(f"The minimum number of coins needed to make {amt} is {res}") + print(f"Number of coins chosen by the greedy algorithm for amount {amt}: {res}") - # Greedy algorithm: Cannot guarantee finding the global optimal solution + # Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 20, 50] amt = 60 res = coin_change_greedy(coins, amt) print(f"\ncoins = {coins}, amt = {amt}") - print(f"The minimum number of coins needed to make {amt} is {res}") - print(f"Actually the minimum number needed is 3, i.e., 20 + 20 + 20") + print(f"Number of coins chosen by the greedy algorithm for amount {amt}: {res}") + print(f"The actual minimum is 3 coins: 20 + 20 + 20") - # Greedy algorithm: Cannot guarantee finding the global optimal solution + # Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 49, 50] amt = 98 res = coin_change_greedy(coins, amt) print(f"\ncoins = {coins}, amt = {amt}") - print(f"The minimum number of coins needed to make {amt} is {res}") - print(f"Actually the minimum number needed is 2, i.e., 49 + 49") + print(f"Number of coins chosen by the greedy algorithm for amount {amt}: {res}") + print(f"The actual minimum is 2 coins: 49 + 49") diff --git a/en/codes/python/chapter_greedy/fractional_knapsack.py b/en/codes/python/chapter_greedy/fractional_knapsack.py index 2390181c7..e42f8ea79 100644 --- a/en/codes/python/chapter_greedy/fractional_knapsack.py +++ b/en/codes/python/chapter_greedy/fractional_knapsack.py @@ -14,27 +14,27 @@ class Item: def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int: - """Fractional knapsack: Greedy algorithm""" - # Create item list with two attributes: weight, value + """Fractional knapsack: greedy algorithm""" + # Create a list of items, each with two attributes: weight and value items = [Item(w, v) for w, v in zip(wgt, val)] - # Sort by unit value item.v / item.w from high to low + # Sort by value per unit weight, item.v / item.w, in descending order items.sort(key=lambda item: item.v / item.w, reverse=True) - # Loop for greedy selection + # Repeatedly make greedy choices res = 0 for item in items: if item.w <= cap: - # If remaining capacity is sufficient, put the entire current item into the knapsack + # If enough capacity remains, put the entire current item in the knapsack res += item.v cap -= item.w else: - # If remaining capacity is insufficient, put part of the current item into the knapsack + # If the remaining capacity is insufficient, put part of the current item in the knapsack res += (item.v / item.w) * cap - # No remaining capacity, so break out of the loop + # No capacity remains, so exit the loop break return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": wgt = [10, 20, 30, 40, 50] val = [50, 120, 150, 210, 240] @@ -43,4 +43,4 @@ if __name__ == "__main__": # Greedy algorithm res = fractional_knapsack(wgt, val, cap) - print(f"The maximum item value without exceeding knapsack capacity is {res}") + print(f"Maximum total value within the knapsack capacity: {res}") diff --git a/en/codes/python/chapter_greedy/max_capacity.py b/en/codes/python/chapter_greedy/max_capacity.py index b71576a2a..d9cdad354 100644 --- a/en/codes/python/chapter_greedy/max_capacity.py +++ b/en/codes/python/chapter_greedy/max_capacity.py @@ -6,17 +6,17 @@ Author: krahets (krahets@163.com) def max_capacity(ht: list[int]) -> int: - """Max capacity: Greedy algorithm""" - # Initialize i, j to be at both ends of the array + """Maximum container capacity: greedy algorithm""" + # Initialize i and j at opposite ends of the array i, j = 0, len(ht) - 1 - # Initial max capacity is 0 + # The initial maximum capacity is 0 res = 0 - # Loop for greedy selection until the two boards meet + # Repeatedly make greedy choices until the two sides meet while i < j: - # Update max capacity + # Update the maximum capacity cap = min(ht[i], ht[j]) * (j - i) res = max(res, cap) - # Move the shorter board inward + # Move the shorter side inward if ht[i] < ht[j]: i += 1 else: @@ -24,10 +24,10 @@ def max_capacity(ht: list[int]) -> int: return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": ht = [3, 8, 5, 2, 7, 7, 3, 4] # Greedy algorithm res = max_capacity(ht) - print(f"Max capacity is {res}") + print(f"Maximum capacity: {res}") diff --git a/en/codes/python/chapter_greedy/max_product_cutting.py b/en/codes/python/chapter_greedy/max_product_cutting.py index e47d3d2c9..f03edbfa8 100644 --- a/en/codes/python/chapter_greedy/max_product_cutting.py +++ b/en/codes/python/chapter_greedy/max_product_cutting.py @@ -8,26 +8,26 @@ import math def max_product_cutting(n: int) -> int: - """Max product cutting: Greedy algorithm""" - # When n <= 3, must cut out a 1 + """Maximum product after splitting: greedy algorithm""" + # When n <= 3, one part must be 1 if n <= 3: return 1 * (n - 1) - # Greedily cut out 3, a is the number of 3s, b is the remainder + # Greedily split off parts of size 3; a is the number of 3s and b is the remainder a, b = n // 3, n % 3 if b == 1: - # When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + # When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return int(math.pow(3, a - 1)) * 2 * 2 if b == 2: - # When the remainder is 2, do nothing + # When the remainder is 2, no adjustment is needed return int(math.pow(3, a)) * 2 - # When the remainder is 0, do nothing + # When the remainder is 0, no adjustment is needed return int(math.pow(3, a)) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": n = 58 # Greedy algorithm res = max_product_cutting(n) - print(f"Max product cutting is {res}") + print(f"Maximum product after splitting: {res}") diff --git a/en/codes/python/chapter_hashing/array_hash_map.py b/en/codes/python/chapter_hashing/array_hash_map.py index b727b6a70..236296f44 100644 --- a/en/codes/python/chapter_hashing/array_hash_map.py +++ b/en/codes/python/chapter_hashing/array_hash_map.py @@ -14,11 +14,11 @@ class Pair: class ArrayHashMap: - """Hash table based on array implementation""" + """Hash table implemented with an array""" def __init__(self): """Constructor""" - # Initialize array with 100 buckets + # Initialize an array containing 100 buckets self.buckets: list[Pair | None] = [None] * 100 def hash_func(self, key: int) -> int: @@ -27,7 +27,7 @@ class ArrayHashMap: return index def get(self, key: int) -> str | None: - """Query operation""" + """Lookup operation""" index: int = self.hash_func(key) pair: Pair = self.buckets[index] if pair is None: @@ -35,7 +35,7 @@ class ArrayHashMap: return pair.val def put(self, key: int, val: str): - """Add and update operation""" + """Insert and update operations""" pair = Pair(key, val) index: int = self.hash_func(key) self.buckets[index] = pair @@ -43,7 +43,7 @@ class ArrayHashMap: def remove(self, key: int): """Remove operation""" index: int = self.hash_func(key) - # Set to None to represent removal + # Set to None to mark removal self.buckets[index] = None def entry_set(self) -> list[Pair]: @@ -71,47 +71,47 @@ class ArrayHashMap: return result def print(self): - """Print hash table""" + """Print the hash table""" for pair in self.buckets: if pair is not None: print(pair.key, "->", pair.val) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize hash table + # Initialize the hash table hmap = ArrayHashMap() - # Add operation - # Add key-value pair (key, value) to the hash table - hmap.put(12836, "Xiao Ha") - hmap.put(15937, "Xiao Luo") - hmap.put(16750, "Xiao Suan") - hmap.put(13276, "Xiao Fa") - hmap.put(10583, "Xiao Ya") - print("\nAfter adding, the hash table is\nKey -> Value") + # Insert operation + # Insert the key-value pair (key, value) into the hash table + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + print("\nHash table after insertion:\nKey -> Value") hmap.print() - # Query operation - # Input key into the hash table to get value + # Lookup operation + # Look up key in the hash table to obtain value name = hmap.get(15937) - print("\nInput student ID 15937, found name " + name) + print("\nLook up student ID 15937 to obtain the name " + name) # Remove operation - # Remove key-value pair (key, value) from the hash table + # Remove the key-value pair (key, value) from the hash table hmap.remove(10583) - print("\nAfter removing 10583, the hash table is\nKey -> Value") + print("\nHash table after removing 10583:\nKey -> Value") hmap.print() - # Traverse hash table - print("\nTraverse key-value pairs Key->Value") + # Traverse the hash table + print("\nTraverse key-value pairs: Key->Value") for pair in hmap.entry_set(): print(pair.key, "->", pair.val) - print("\nTraverse keys only Key") + print("\nTraverse keys only: Key") for key in hmap.key_set(): print(key) - print("\nTraverse values only Value") + print("\nTraverse values only: Value") for val in hmap.value_set(): print(val) diff --git a/en/codes/python/chapter_hashing/built_in_hash.py b/en/codes/python/chapter_hashing/built_in_hash.py index a92137d13..b5534c861 100644 --- a/en/codes/python/chapter_hashing/built_in_hash.py +++ b/en/codes/python/chapter_hashing/built_in_hash.py @@ -10,28 +10,28 @@ from pathlib import Path sys.path.append(str(Path(__file__).parent.parent)) from modules import ListNode -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": num = 3 hash_num = hash(num) - print(f"Hash value of integer {num} is {hash_num}") + print(f"Hash value of integer {num}: {hash_num}") bol = True hash_bol = hash(bol) - print(f"Hash value of boolean {bol} is {hash_bol}") + print(f"Hash value of Boolean {bol}: {hash_bol}") dec = 3.14159 hash_dec = hash(dec) - print(f"Hash value of decimal {dec} is {hash_dec}") + print(f"Hash value of floating-point number {dec}: {hash_dec}") - str = "Hello algo" + str = "Hello 算法" hash_str = hash(str) - print(f"Hash value of string {str} is {hash_str}") + print(f"Hash value of string {str}: {hash_str}") - tup = (12836, "Xiao Ha") + tup = (12836, "小哈") hash_tup = hash(tup) - print(f"Hash value of tuple {tup} is {hash(hash_tup)}") + print(f"Hash of the hash value of tuple {tup}: {hash(hash_tup)}") obj = ListNode(0) hash_obj = hash(obj) - print(f"Hash value of node object {obj} is {hash_obj}") + print(f"Hash value of node object {obj}: {hash_obj}") diff --git a/en/codes/python/chapter_hashing/hash_map.py b/en/codes/python/chapter_hashing/hash_map.py index a250c658c..d35272f46 100644 --- a/en/codes/python/chapter_hashing/hash_map.py +++ b/en/codes/python/chapter_hashing/hash_map.py @@ -10,41 +10,41 @@ from pathlib import Path sys.path.append(str(Path(__file__).parent.parent)) from modules import print_dict -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize hash table + # Initialize the hash table hmap = dict[int, str]() - # Add operation - # Add key-value pair (key, value) to the hash table - hmap[12836] = "Xiao Ha" - hmap[15937] = "Xiao Luo" - hmap[16750] = "Xiao Suan" - hmap[13276] = "Xiao Fa" - hmap[10583] = "Xiao Ya" - print("\nAfter adding, the hash table is\nKey -> Value") + # Insert operation + # Insert the key-value pair (key, value) into the hash table + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" + print("\nHash table after insertion:\nKey -> Value") print_dict(hmap) - # Query operation - # Input key into the hash table to get value + # Lookup operation + # Look up key in the hash table to obtain value name: str = hmap[15937] - print("\nInput student ID 15937, found name " + name) + print("\nLook up student ID 15937 to obtain the name " + name) # Remove operation - # Remove key-value pair (key, value) from the hash table + # Remove the key-value pair (key, value) from the hash table hmap.pop(10583) - print("\nAfter removing 10583, the hash table is\nKey -> Value") + print("\nHash table after removing 10583:\nKey -> Value") print_dict(hmap) - # Traverse hash table - print("\nTraverse key-value pairs Key->Value") + # Traverse the hash table + print("\nTraverse key-value pairs: Key->Value") for key, value in hmap.items(): print(key, "->", value) - print("\nTraverse keys only Key") + print("\nTraverse keys only: Key") for key in hmap.keys(): print(key) - print("\nTraverse values only Value") + print("\nTraverse values only: Value") for val in hmap.values(): print(val) diff --git a/en/codes/python/chapter_hashing/hash_map_chaining.py b/en/codes/python/chapter_hashing/hash_map_chaining.py index d7cdd273c..b3da6de0b 100644 --- a/en/codes/python/chapter_hashing/hash_map_chaining.py +++ b/en/codes/python/chapter_hashing/hash_map_chaining.py @@ -18,8 +18,8 @@ class HashMapChaining: """Constructor""" self.size = 0 # Number of key-value pairs self.capacity = 4 # Hash table capacity - self.load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion - self.extend_ratio = 2 # Expansion multiplier + self.load_thres = 2.0 / 3.0 # Load factor threshold that triggers capacity growth + self.extend_ratio = 2 # Capacity growth factor self.buckets = [[] for _ in range(self.capacity)] # Bucket array def hash_func(self, key: int) -> int: @@ -31,29 +31,29 @@ class HashMapChaining: return self.size / self.capacity def get(self, key: int) -> str | None: - """Query operation""" + """Lookup operation""" index = self.hash_func(key) bucket = self.buckets[index] - # Traverse bucket, if key is found, return corresponding val + # Traverse the bucket; if key is found, return its corresponding val for pair in bucket: if pair.key == key: return pair.val - # If key is not found, return None + # Return None if key is not found return None def put(self, key: int, val: str): - """Add operation""" - # When load factor exceeds threshold, perform expansion + """Insert operation""" + # Grow the capacity when the load factor exceeds the threshold if self.load_factor() > self.load_thres: self.extend() index = self.hash_func(key) bucket = self.buckets[index] - # Traverse bucket, if specified key is encountered, update corresponding val and return + # Traverse the bucket; if the specified key is found, update its val and return for pair in bucket: if pair.key == key: pair.val = val return - # If key does not exist, append key-value pair to the end + # If key is absent, append the key-value pair at the end pair = Pair(key, val) bucket.append(pair) self.size += 1 @@ -62,7 +62,7 @@ class HashMapChaining: """Remove operation""" index = self.hash_func(key) bucket = self.buckets[index] - # Traverse bucket and remove key-value pair from it + # Traverse the bucket and remove the key-value pair for pair in bucket: if pair.key == key: bucket.remove(pair) @@ -70,20 +70,20 @@ class HashMapChaining: break def extend(self): - """Expand hash table""" - # Temporarily store the original hash table + """Grow the hash table capacity""" + # Keep a reference to the original hash table buckets = self.buckets - # Initialize expanded new hash table + # Initialize a new hash table with the expanded capacity self.capacity *= self.extend_ratio self.buckets = [[] for _ in range(self.capacity)] self.size = 0 - # Move key-value pairs from original hash table to new hash table + # Move the key-value pairs from the original hash table to the new one for bucket in buckets: for pair in bucket: self.put(pair.key, pair.val) def print(self): - """Print hash table""" + """Print the hash table""" for bucket in self.buckets: res = [] for pair in bucket: @@ -91,28 +91,28 @@ class HashMapChaining: print(res) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize hash table + # Initialize the hash table hashmap = HashMapChaining() - # Add operation - # Add key-value pair (key, value) to the hash table - hashmap.put(12836, "Xiao Ha") - hashmap.put(15937, "Xiao Luo") - hashmap.put(16750, "Xiao Suan") - hashmap.put(13276, "Xiao Fa") - hashmap.put(10583, "Xiao Ya") - print("\nAfter adding, the hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]") + # Insert operation + # Insert the key-value pair (key, value) into the hash table + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + print("\nHash table after insertion:\n[Key1 -> Value1, Key2 -> Value2, ...]") hashmap.print() - # Query operation - # Input key into the hash table to get value + # Lookup operation + # Look up key in the hash table to obtain value name = hashmap.get(13276) - print("\nInput student ID 13276, found name " + name) + print("\nLook up student ID 13276 to obtain the name " + name) # Remove operation - # Remove key-value pair (key, value) from the hash table + # Remove the key-value pair (key, value) from the hash table hashmap.remove(12836) - print("\nAfter removing 12836, the hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]") + print("\nHash table after removing 12836:\n[Key1 -> Value1, Key2 -> Value2, ...]") hashmap.print() diff --git a/en/codes/python/chapter_hashing/hash_map_open_addressing.py b/en/codes/python/chapter_hashing/hash_map_open_addressing.py index 7ab92a95c..50b8cf686 100644 --- a/en/codes/python/chapter_hashing/hash_map_open_addressing.py +++ b/en/codes/python/chapter_hashing/hash_map_open_addressing.py @@ -18,10 +18,10 @@ class HashMapOpenAddressing: """Constructor""" self.size = 0 # Number of key-value pairs self.capacity = 4 # Hash table capacity - self.load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion - self.extend_ratio = 2 # Expansion multiplier + self.load_thres = 2.0 / 3.0 # Load factor threshold that triggers capacity growth + self.extend_ratio = 2 # Capacity growth factor self.buckets: list[Pair | None] = [None] * self.capacity # Bucket array - self.TOMBSTONE = Pair(-1, "-1") # Removal marker + self.TOMBSTONE = Pair(-1, "-1") # Tombstone marker def hash_func(self, key: int) -> int: """Hash function""" @@ -32,76 +32,76 @@ class HashMapOpenAddressing: return self.size / self.capacity def find_bucket(self, key: int) -> int: - """Search for bucket index corresponding to key""" + """Find the bucket index for key""" index = self.hash_func(key) first_tombstone = -1 - # Linear probing, break when encountering an empty bucket + # Probe linearly until an empty bucket is reached while self.buckets[index] is not None: - # If key is encountered, return the corresponding bucket index + # If key is found, return its bucket index if self.buckets[index].key == key: - # If a removal marker was encountered before, move the key-value pair to that index + # If a tombstone was encountered earlier, move the key-value pair to that index if first_tombstone != -1: self.buckets[first_tombstone] = self.buckets[index] self.buckets[index] = self.TOMBSTONE - return first_tombstone # Return the moved bucket index - return index # Return bucket index - # Record the first removal marker encountered + return first_tombstone # Return the bucket index after the move + return index # Return the bucket index + # Record the first tombstone encountered if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE: first_tombstone = index - # Calculate bucket index, wrap around to the head if past the tail + # Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % self.capacity - # If key does not exist, return the index for insertion + # If key is absent, return the index for insertion return index if first_tombstone == -1 else first_tombstone def get(self, key: int) -> str: - """Query operation""" - # Search for bucket index corresponding to key + """Lookup operation""" + # Find the bucket index for key index = self.find_bucket(key) - # If key-value pair is found, return corresponding val + # If the key-value pair is found, return its val if self.buckets[index] not in [None, self.TOMBSTONE]: return self.buckets[index].val - # If key-value pair does not exist, return None + # Return None if the key-value pair is absent return None def put(self, key: int, val: str): - """Add operation""" - # When load factor exceeds threshold, perform expansion + """Insert operation""" + # Grow the capacity when the load factor exceeds the threshold if self.load_factor() > self.load_thres: self.extend() - # Search for bucket index corresponding to key + # Find the bucket index for key index = self.find_bucket(key) - # If key-value pair is found, overwrite val and return + # If the key-value pair is found, overwrite val and return if self.buckets[index] not in [None, self.TOMBSTONE]: self.buckets[index].val = val return - # If key-value pair does not exist, add the key-value pair + # If the key-value pair is absent, insert it self.buckets[index] = Pair(key, val) self.size += 1 def remove(self, key: int): """Remove operation""" - # Search for bucket index corresponding to key + # Find the bucket index for key index = self.find_bucket(key) - # If key-value pair is found, overwrite it with removal marker + # If the key-value pair is found, replace it with a tombstone if self.buckets[index] not in [None, self.TOMBSTONE]: self.buckets[index] = self.TOMBSTONE self.size -= 1 def extend(self): - """Expand hash table""" - # Temporarily store the original hash table + """Grow the hash table capacity""" + # Keep a reference to the original hash table buckets_tmp = self.buckets - # Initialize expanded new hash table + # Initialize a new hash table with the expanded capacity self.capacity *= self.extend_ratio self.buckets = [None] * self.capacity self.size = 0 - # Move key-value pairs from original hash table to new hash table + # Move the key-value pairs from the original hash table to the new one for pair in buckets_tmp: if pair not in [None, self.TOMBSTONE]: self.put(pair.key, pair.val) def print(self): - """Print hash table""" + """Print the hash table""" for pair in self.buckets: if pair is None: print("None") @@ -111,28 +111,28 @@ class HashMapOpenAddressing: print(pair.key, "->", pair.val) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize hash table + # Initialize the hash table hashmap = HashMapOpenAddressing() - # Add operation - # Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Xiao Ha") - hashmap.put(15937, "Xiao Luo") - hashmap.put(16750, "Xiao Suan") - hashmap.put(13276, "Xiao Fa") - hashmap.put(10583, "Xiao Ya") - print("\nAfter adding, the hash table is\nKey -> Value") + # Insert operation + # Insert the key-value pair (key, val) into the hash table + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + print("\nHash table after insertion:\nKey -> Value") hashmap.print() - # Query operation - # Input key into the hash table to get val + # Lookup operation + # Look up key in the hash table to obtain val name = hashmap.get(13276) - print("\nInput student ID 13276, found name " + name) + print("\nLook up student ID 13276 to obtain the name " + name) # Remove operation - # Remove key-value pair (key, val) from the hash table + # Remove the key-value pair (key, val) from the hash table hashmap.remove(16750) - print("\nAfter removing 16750, the hash table is\nKey -> Value") + print("\nHash table after removing 16750:\nKey -> Value") hashmap.print() diff --git a/en/codes/python/chapter_hashing/simple_hash.py b/en/codes/python/chapter_hashing/simple_hash.py index 3b3678cad..729955511 100644 --- a/en/codes/python/chapter_hashing/simple_hash.py +++ b/en/codes/python/chapter_hashing/simple_hash.py @@ -33,7 +33,7 @@ def xor_hash(key: str) -> int: def rot_hash(key: str) -> int: - """Rotational hash""" + """Rotating hash""" hash = 0 modulus = 1000000007 for c in key: @@ -41,18 +41,18 @@ def rot_hash(key: str) -> int: return hash % modulus -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - key = "Hello algo" + key = "Hello 算法" hash = add_hash(key) - print(f"Additive hash value is {hash}") + print(f"Additive hash value: {hash}") hash = mul_hash(key) - print(f"Multiplicative hash value is {hash}") + print(f"Multiplicative hash value: {hash}") hash = xor_hash(key) - print(f"XOR hash value is {hash}") + print(f"XOR hash value: {hash}") hash = rot_hash(key) - print(f"Rotational hash value is {hash}") + print(f"Rotating hash value: {hash}") diff --git a/en/codes/python/chapter_heap/heap.py b/en/codes/python/chapter_heap/heap.py index ac7c8b284..b08768cec 100644 --- a/en/codes/python/chapter_heap/heap.py +++ b/en/codes/python/chapter_heap/heap.py @@ -14,58 +14,58 @@ import heapq def test_push(heap: list, val: int, flag: int = 1): - heapq.heappush(heap, flag * val) # Element enters heap - print(f"\nAfter element {val} enters heap") + heapq.heappush(heap, flag * val) # Push an element onto the heap + print(f"\nAfter pushing element {val} onto the heap:") print_heap([flag * val for val in heap]) def test_pop(heap: list, flag: int = 1): - val = flag * heapq.heappop(heap) # Top element exits heap - print(f"\nAfter top element {val} exits heap") + val = flag * heapq.heappop(heap) # Pop the top element from the heap + print(f"\nAfter popping top element {val} from the heap:") print_heap([flag * val for val in heap]) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize min heap + # Initialize a min heap min_heap, flag = [], 1 - # Initialize max heap + # Initialize a max heap max_heap, flag = [], -1 - print("\nThe following test cases are for max heap") - # Python's heapq module implements min heap by default - # Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap - # In this example, flag = 1 corresponds to min heap, flag = -1 corresponds to max heap + print("\nThe following example uses a max heap") + # Python's heapq module implements a min heap by default + # Negate each element before pushing it onto the heap to reverse the ordering and implement a max heap + # In this example, flag = 1 represents a min heap and flag = -1 represents a max heap - # Elements enter heap + # Push an element onto the heap test_push(max_heap, 1, flag) test_push(max_heap, 3, flag) test_push(max_heap, 2, flag) test_push(max_heap, 5, flag) test_push(max_heap, 4, flag) - # Get top element + # Get the top element of the heap peek: int = flag * max_heap[0] - print(f"\nTop element is {peek}") + print(f"\nTop element of the heap: {peek}") - # Top element exits heap + # Pop the top element from the heap test_pop(max_heap, flag) test_pop(max_heap, flag) test_pop(max_heap, flag) test_pop(max_heap, flag) test_pop(max_heap, flag) - # Get heap size + # Get the heap size size: int = len(max_heap) - print(f"\nNumber of heap elements is {size}") + print(f"\nNumber of elements in the heap: {size}") - # Check if heap is empty + # Check whether the heap is empty is_empty: bool = not max_heap - print(f"\nIs heap empty {is_empty}") + print(f"\nIs the heap empty: {is_empty}") - # Input list and build heap - # Time complexity is O(n), not O(nlogn) + # Build a heap from an input list + # The time complexity is O(n), not O(nlogn) min_heap = [1, 3, 2, 5, 4] heapq.heapify(min_heap) - print("\nAfter inputting list and building min heap") + print("\nAfter building a min heap from the input list:") print_heap(min_heap) diff --git a/en/codes/python/chapter_heap/my_heap.py b/en/codes/python/chapter_heap/my_heap.py index a94f12129..0be3a0ae6 100644 --- a/en/codes/python/chapter_heap/my_heap.py +++ b/en/codes/python/chapter_heap/my_heap.py @@ -15,23 +15,23 @@ class MaxHeap: """Max heap""" def __init__(self, nums: list[int]): - """Constructor, build heap based on input list""" - # Add list elements to heap as is + """Constructor: build a heap from the input list""" + # Place the list elements in the heap without changing their order self.max_heap = nums - # Heapify all nodes except leaf nodes + # Sift down from every non-leaf node for i in range(self.parent(self.size() - 1), -1, -1): self.sift_down(i) def left(self, i: int) -> int: - """Get index of left child node""" + """Get the left child index""" return 2 * i + 1 def right(self, i: int) -> int: - """Get index of right child node""" + """Get the right child index""" return 2 * i + 2 def parent(self, i: int) -> int: - """Get index of parent node""" + """Get the parent index""" return (i - 1) // 2 # Floor division def swap(self, i: int, j: int): @@ -39,99 +39,99 @@ class MaxHeap: self.max_heap[i], self.max_heap[j] = self.max_heap[j], self.max_heap[i] def size(self) -> int: - """Get heap size""" + """Get the heap size""" return len(self.max_heap) def is_empty(self) -> bool: - """Check if heap is empty""" + """Check whether the heap is empty""" return self.size() == 0 def peek(self) -> int: - """Access top element""" + """Access the top element of the heap""" return self.max_heap[0] def push(self, val: int): - """Element enters heap""" - # Add node + """Push an element onto the heap""" + # Add a node self.max_heap.append(val) - # Heapify from bottom to top + # Sift up self.sift_up(self.size() - 1) def sift_up(self, i: int): - """Starting from node i, heapify from bottom to top""" + """Sift up starting from node i""" while True: - # Get parent node of node i + # Get the parent of node i p = self.parent(i) - # When "crossing root node" or "node needs no repair", end heapify + # Stop sifting when the root has been passed or the node needs no repair if p < 0 or self.max_heap[i] <= self.max_heap[p]: break - # Swap two nodes + # Swap the two nodes self.swap(i, p) - # Loop upward heapify + # Continue sifting up i = p def pop(self) -> int: - """Element exits heap""" - # Handle empty case + """Pop an element from the heap""" + # Handle an empty heap if self.is_empty(): raise IndexError("Heap is empty") - # Swap root node with rightmost leaf node (swap first element with last element) + # Swap the root with the rightmost leaf on the last level (swap the first and last elements) self.swap(0, self.size() - 1) - # Delete node + # Remove a node val = self.max_heap.pop() - # Heapify from top to bottom + # Sift down self.sift_down(0) - # Return top element + # Return the top element of the heap return val def sift_down(self, i: int): - """Starting from node i, heapify from top to bottom""" + """Sift down starting from node i""" while True: - # Find node with largest value among i, l, r, denoted as ma + # Find the node with the largest value among i, l, and r, and store its index in ma l, r, ma = self.left(i), self.right(i), i if l < self.size() and self.max_heap[l] > self.max_heap[ma]: ma = l if r < self.size() and self.max_heap[r] > self.max_heap[ma]: ma = r - # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + # Stop if node i is largest or both child indices l and r are out of bounds if ma == i: break - # Swap two nodes + # Swap the two nodes self.swap(i, ma) - # Loop downward heapify + # Continue sifting down i = ma def print(self): - """Print heap (binary tree)""" + """Print the heap (binary tree)""" print_heap(self.max_heap) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize max heap + # Initialize a max heap max_heap = MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) - print("\nAfter inputting list and building heap") + print("\nAfter building a heap from the input list:") max_heap.print() - # Get top element + # Get the top element of the heap peek = max_heap.peek() - print(f"\nTop element is {peek}") + print(f"\nTop element of the heap: {peek}") - # Element enters heap + # Push an element onto the heap val = 7 max_heap.push(val) - print(f"\nAfter element {val} enters heap") + print(f"\nAfter pushing element {val} onto the heap:") max_heap.print() - # Top element exits heap + # Pop the top element from the heap peek = max_heap.pop() - print(f"\nAfter top element {peek} exits heap") + print(f"\nAfter popping top element {peek} from the heap:") max_heap.print() - # Get heap size + # Get the heap size size = max_heap.size() - print(f"\nNumber of heap elements is {size}") + print(f"\nNumber of elements in the heap: {size}") - # Check if heap is empty + # Check whether the heap is empty is_empty = max_heap.is_empty() - print(f"\nIs heap empty {is_empty}") + print(f"\nIs the heap empty: {is_empty}") diff --git a/en/codes/python/chapter_heap/top_k.py b/en/codes/python/chapter_heap/top_k.py index b79c1e448..23aa16179 100644 --- a/en/codes/python/chapter_heap/top_k.py +++ b/en/codes/python/chapter_heap/top_k.py @@ -14,26 +14,26 @@ import heapq def top_k_heap(nums: list[int], k: int) -> list[int]: - """Find the largest k elements in array based on heap""" - # Initialize min heap + """Use a heap to find the k largest elements in an array""" + # Initialize a min heap heap = [] - # Enter the first k elements of array into heap + # Push the first k array elements onto the heap for i in range(k): heapq.heappush(heap, nums[i]) - # Starting from the (k+1)th element, maintain heap length as k + # Starting with element k+1, keep the heap size at k for i in range(k, len(nums)): - # If current element is greater than top element, top element exits heap, current element enters heap + # If the current element is larger than the heap top, pop the top element and push the current element if nums[i] > heap[0]: heapq.heappop(heap) heapq.heappush(heap, nums[i]) return heap -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [1, 7, 6, 3, 2] k = 3 res = top_k_heap(nums, k) - print(f"The largest {k} elements are") + print(f"The {k} largest elements are") print_heap(res) diff --git a/en/codes/python/chapter_searching/binary_search.py b/en/codes/python/chapter_searching/binary_search.py index 723689944..914371155 100644 --- a/en/codes/python/chapter_searching/binary_search.py +++ b/en/codes/python/chapter_searching/binary_search.py @@ -7,46 +7,46 @@ Author: timi (xisunyy@163.com) def binary_search(nums: list[int], target: int) -> int: """Binary search (closed interval)""" - # Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + # Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements i, j = 0, len(nums) - 1 - # Loop, exit when the search interval is empty (empty when i > j) + # Loop until the search interval is empty (when i > j) while i <= j: - # In theory, Python numbers can be infinitely large (depending on memory size), no need to consider large number overflow - m = (i + j) // 2 # Calculate midpoint index m + # Python integers can grow arbitrarily large, subject to available memory, so integer overflow is not a concern + m = (i + j) // 2 # Calculate the midpoint index m if nums[m] < target: - i = m + 1 # This means target is in the interval [m+1, j] + i = m + 1 # In this case, target must be in [m+1, j] if it exists elif nums[m] > target: - j = m - 1 # This means target is in the interval [i, m-1] + j = m - 1 # In this case, target must be in [i, m-1] if it exists else: - return m # Found the target element, return its index - return -1 # Target element not found, return -1 + return m # The target element was found; return its index + return -1 # The target element was not found; return -1 def binary_search_lcro(nums: list[int], target: int) -> int: - """Binary search (left-closed right-open interval)""" - # Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + """Binary search (left-closed, right-open interval)""" + # Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last i, j = 0, len(nums) - # Loop, exit when the search interval is empty (empty when i = j) + # Loop until the search interval is empty (when i = j) while i < j: - m = (i + j) // 2 # Calculate midpoint index m + m = (i + j) // 2 # Calculate the midpoint index m if nums[m] < target: - i = m + 1 # This means target is in the interval [m+1, j) + i = m + 1 # In this case, target must be in [m+1, j) if it exists elif nums[m] > target: - j = m # This means target is in the interval [i, m) + j = m # In this case, target must be in [i, m) if it exists else: - return m # Found the target element, return its index - return -1 # Target element not found, return -1 + return m # The target element was found; return its index + return -1 # The target element was not found; return -1 -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": target = 6 nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] # Binary search (closed interval) index = binary_search(nums, target) - print("Index of target element 6 =", index) + print("Index of target element 6 = ", index) - # Binary search (left-closed right-open interval) + # Binary search (left-closed, right-open interval) index = binary_search_lcro(nums, target) - print("Index of target element 6 =", index) + print("Index of target element 6 = ", index) diff --git a/en/codes/python/chapter_searching/binary_search_edge.py b/en/codes/python/chapter_searching/binary_search_edge.py index 01729c6ac..7d533e52a 100644 --- a/en/codes/python/chapter_searching/binary_search_edge.py +++ b/en/codes/python/chapter_searching/binary_search_edge.py @@ -12,38 +12,38 @@ from binary_search_insertion import binary_search_insertion def binary_search_left_edge(nums: list[int], target: int) -> int: - """Binary search for the leftmost target""" - # Equivalent to finding the insertion point of target + """Find the leftmost occurrence of target using binary search""" + # This is equivalent to finding the insertion point of target i = binary_search_insertion(nums, target) - # Target not found, return -1 + # If target was not found, return -1 if i == len(nums) or nums[i] != target: return -1 - # Found target, return index i + # Target was found; return index i return i def binary_search_right_edge(nums: list[int], target: int) -> int: - """Binary search for the rightmost target""" - # Convert to finding the leftmost target + 1 + """Find the rightmost occurrence of target using binary search""" + # Reduce the problem to finding the leftmost insertion point of target + 1 i = binary_search_insertion(nums, target + 1) - # j points to the rightmost target, i points to the first element greater than target + # j is the candidate rightmost target index; i is the index of the first element greater than target, or len(nums) j = i - 1 - # Target not found, return -1 + # If target was not found, return -1 if j == -1 or nums[j] != target: return -1 - # Found target, return index j + # Target was found; return index j return j -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Array with duplicate elements + # Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] print(f"\nArray nums = {nums}") - # Binary search for left boundary and right boundary + # Find the left and right boundaries using binary search for target in [6, 7]: index = binary_search_left_edge(nums, target) - print(f"Index of the leftmost element {target} is {index}") + print(f"Index of the leftmost occurrence of {target}: {index}") index = binary_search_right_edge(nums, target) - print(f"Index of the rightmost element {target} is {index}") + print(f"Index of the rightmost occurrence of {target}: {index}") diff --git a/en/codes/python/chapter_searching/binary_search_insertion.py b/en/codes/python/chapter_searching/binary_search_insertion.py index 95c909574..bb3079d74 100644 --- a/en/codes/python/chapter_searching/binary_search_insertion.py +++ b/en/codes/python/chapter_searching/binary_search_insertion.py @@ -6,49 +6,49 @@ Author: krahets (krahets@163.com) def binary_search_insertion_simple(nums: list[int], target: int) -> int: - """Binary search for insertion point (no duplicate elements)""" - i, j = 0, len(nums) - 1 # Initialize closed interval [0, n-1] + """Find an insertion point using binary search (no duplicates)""" + i, j = 0, len(nums) - 1 # Initialize the closed interval [0, n-1] while i <= j: - m = (i + j) // 2 # Calculate midpoint index m + m = (i + j) // 2 # Calculate the midpoint index m if nums[m] < target: - i = m + 1 # target is in the interval [m+1, j] + i = m + 1 # Target must be in [m+1, j] if it exists elif nums[m] > target: - j = m - 1 # target is in the interval [i, m-1] + j = m - 1 # Target must be in [i, m-1] if it exists else: - return m # Found target, return insertion point m - # Target not found, return insertion point i + return m # Target was found; return insertion point m + # Target was not found; return insertion point i return i def binary_search_insertion(nums: list[int], target: int) -> int: - """Binary search for insertion point (with duplicate elements)""" - i, j = 0, len(nums) - 1 # Initialize closed interval [0, n-1] + """Find an insertion point using binary search (with duplicates)""" + i, j = 0, len(nums) - 1 # Initialize the closed interval [0, n-1] while i <= j: - m = (i + j) // 2 # Calculate midpoint index m + m = (i + j) // 2 # Calculate the midpoint index m if nums[m] < target: - i = m + 1 # target is in the interval [m+1, j] + i = m + 1 # Target must be in [m+1, j] if it exists elif nums[m] > target: - j = m - 1 # target is in the interval [i, m-1] + j = m - 1 # Target must be in [i, m-1] if it exists else: - j = m - 1 # The rightmost element less than target is in the interval [i, m-1] + j = m - 1 # Continue in [i, m-1] to find the rightmost element smaller than target # Return insertion point i return i -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Array without duplicate elements + # Array with no duplicate elements nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] print(f"\nArray nums = {nums}") - # Binary search for insertion point + # Find an insertion point using binary search for target in [6, 9]: index = binary_search_insertion_simple(nums, target) - print(f"Index of insertion point for element {target} is {index}") + print(f"Insertion index for element {target}: {index}") - # Array with duplicate elements + # Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] print(f"\nArray nums = {nums}") - # Binary search for insertion point + # Find an insertion point using binary search for target in [2, 6, 20]: index = binary_search_insertion(nums, target) - print(f"Index of insertion point for element {target} is {index}") + print(f"Insertion index for element {target}: {index}") diff --git a/en/codes/python/chapter_searching/hashing_search.py b/en/codes/python/chapter_searching/hashing_search.py index 409f9230c..9af62728b 100644 --- a/en/codes/python/chapter_searching/hashing_search.py +++ b/en/codes/python/chapter_searching/hashing_search.py @@ -12,40 +12,40 @@ from modules import ListNode, list_to_linked_list def hashing_search_array(hmap: dict[int, int], target: int) -> int: - """Hash search (array)""" - # Hash table's key: target element, value: index - # If this key does not exist in the hash table, return -1 + """Hash-based search (array)""" + # Hash table key: target element; value: index + # Return -1 if the hash table does not contain this key return hmap.get(target, -1) def hashing_search_linkedlist( hmap: dict[int, ListNode], target: int ) -> ListNode | None: - """Hash search (linked list)""" - # Hash table's key: target element, value: node object - # If this key does not exist in the hash table, return None + """Hash-based search (linked list)""" + # Hash table key: target element; value: node object + # Return None if the hash table does not contain this key return hmap.get(target, None) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": target = 3 - # Hash search (array) + # Hash-based search (array) nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] - # Initialize hash table + # Initialize the hash table map0 = dict[int, int]() for i in range(len(nums)): - map0[nums[i]] = i # key: element, value: index + map0[nums[i]] = i # key: element; value: index index: int = hashing_search_array(map0, target) print("Index of target element 3 =", index) - # Hash search (linked list) + # Hash-based search (linked list) head: ListNode = list_to_linked_list(nums) - # Initialize hash table + # Initialize the hash table map1 = dict[int, ListNode]() while head: - map1[head.val] = head # key: node value, value: node + map1[head.val] = head # key: node value; value: node head = head.next node: ListNode = hashing_search_linkedlist(map1, target) - print("The corresponding node object for target node value 3 is", node) + print("The node object corresponding to target value 3 is", node) diff --git a/en/codes/python/chapter_searching/linear_search.py b/en/codes/python/chapter_searching/linear_search.py index 9c17d99d9..c86d47530 100644 --- a/en/codes/python/chapter_searching/linear_search.py +++ b/en/codes/python/chapter_searching/linear_search.py @@ -15,31 +15,31 @@ def linear_search_array(nums: list[int], target: int) -> int: """Linear search (array)""" # Traverse the array for i in range(len(nums)): - if nums[i] == target: # Found the target element, return its index + if nums[i] == target: # The target element was found; return its index return i - return -1 # Target element not found, return -1 + return -1 # The target element was not found; return -1 def linear_search_linkedlist(head: ListNode, target: int) -> ListNode | None: """Linear search (linked list)""" # Traverse the linked list while head: - if head.val == target: # Found the target node, return it + if head.val == target: # The target node was found; return it return head head = head.next - return None # Target node not found, return None + return None # The target node was not found; return None -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": target = 3 - # Perform linear search in array + # Perform linear search on the array nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] index: int = linear_search_array(nums, target) print("Index of target element 3 =", index) - # Perform linear search in linked list + # Perform linear search on the linked list head: ListNode = list_to_linked_list(nums) node: ListNode | None = linear_search_linkedlist(head, target) - print("The corresponding node object for target node value 3 is", node) + print("The node object corresponding to target value 3 is", node) diff --git a/en/codes/python/chapter_searching/two_sum.py b/en/codes/python/chapter_searching/two_sum.py index 83440629b..99b2532b0 100644 --- a/en/codes/python/chapter_searching/two_sum.py +++ b/en/codes/python/chapter_searching/two_sum.py @@ -6,8 +6,8 @@ Author: krahets (krahets@163.com) def two_sum_brute_force(nums: list[int], target: int) -> list[int]: - """Method 1: Brute force enumeration""" - # Two nested loops, time complexity is O(n^2) + """Method 1: brute-force enumeration""" + # Two nested loops give a time complexity of O(n^2) for i in range(len(nums) - 1): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: @@ -16,10 +16,10 @@ def two_sum_brute_force(nums: list[int], target: int) -> list[int]: def two_sum_hash_table(nums: list[int], target: int) -> list[int]: - """Method 2: Auxiliary hash table""" - # Auxiliary hash table, space complexity is O(n) + """Method 2: auxiliary hash table""" + # The auxiliary hash table uses O(n) space dic = {} - # Single loop, time complexity is O(n) + # A single loop with average O(1) hash operations gives an average time complexity of O(n) for i in range(len(nums)): if target - nums[i] in dic: return [dic[target - nums[i]], i] @@ -27,16 +27,16 @@ def two_sum_hash_table(nums: list[int], target: int) -> list[int]: return [] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # ======= Test Case ======= + # ======= Test case ======= nums = [2, 7, 11, 15] target = 13 - # ====== Driver Code ====== + # ====== Driver code ====== # Method 1 res: list[int] = two_sum_brute_force(nums, target) - print("Method 1 res =", res) + print("Method 1: res =", res) # Method 2 res: list[int] = two_sum_hash_table(nums, target) - print("Method 2 res =", res) + print("Method 2: res =", res) diff --git a/en/codes/python/chapter_sorting/bubble_sort.py b/en/codes/python/chapter_sorting/bubble_sort.py index 493bb4ccb..989121a94 100644 --- a/en/codes/python/chapter_sorting/bubble_sort.py +++ b/en/codes/python/chapter_sorting/bubble_sort.py @@ -8,9 +8,9 @@ Author: timi (xisunyy@163.com) def bubble_sort(nums: list[int]): """Bubble sort""" n = len(nums) - # Outer loop: unsorted interval is [0, i] + # Outer loop: the unsorted range is [0, i] for i in range(n - 1, 0, -1): - # Inner loop: swap the largest element in the unsorted interval [0, i] to the rightmost end of the interval + # Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in range(i): if nums[j] > nums[j + 1]: # Swap nums[j] and nums[j + 1] @@ -20,25 +20,25 @@ def bubble_sort(nums: list[int]): def bubble_sort_with_flag(nums: list[int]): """Bubble sort (flag optimization)""" n = len(nums) - # Outer loop: unsorted interval is [0, i] + # Outer loop: the unsorted range is [0, i] for i in range(n - 1, 0, -1): - flag = False # Initialize flag - # Inner loop: swap the largest element in the unsorted interval [0, i] to the rightmost end of the interval + flag = False # Initialize the flag + # Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in range(i): if nums[j] > nums[j + 1]: # Swap nums[j] and nums[j + 1] nums[j], nums[j + 1] = nums[j + 1], nums[j] - flag = True # Record element swap + flag = True # Record that a swap occurred if not flag: - break # No elements were swapped in this round of "bubbling", exit directly + break # Exit immediately if this pass made no swaps -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] bubble_sort(nums) - print("After bubble sort, nums =", nums) + print("After bubble sort, array =", nums) nums1 = [4, 1, 3, 1, 5, 2] bubble_sort_with_flag(nums1) - print("After bubble sort, nums =", nums1) + print("After bubble sort, array =", nums1) diff --git a/en/codes/python/chapter_sorting/bucket_sort.py b/en/codes/python/chapter_sorting/bucket_sort.py index cd556f9ad..f32911e7f 100644 --- a/en/codes/python/chapter_sorting/bucket_sort.py +++ b/en/codes/python/chapter_sorting/bucket_sort.py @@ -7,20 +7,20 @@ Author: krahets (krahets@163.com) def bucket_sort(nums: list[float]): """Bucket sort""" - # Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + # Initialize k = n/2 buckets, expecting 2 elements per bucket k = len(nums) // 2 buckets = [[] for _ in range(k)] - # 1. Distribute array elements into various buckets + # 1. Distribute the array elements among the buckets for num in nums: - # Input data range is [0, 1), use num * k to map to index range [0, k-1] + # The input range is [0, 1); use num * k to map each value to an index in [0, k-1] i = int(num * k) # Add num to bucket i buckets[i].append(num) # 2. Sort each bucket for bucket in buckets: - # Use built-in sorting function, can also replace with other sorting algorithms + # Use the built-in sorting function, or replace it with another sorting algorithm bucket.sort() - # 3. Traverse buckets to merge results + # 3. Traverse the buckets to combine the results i = 0 for bucket in buckets: for num in bucket: @@ -29,7 +29,7 @@ def bucket_sort(nums: list[float]): if __name__ == "__main__": - # Assume input data is floating point, interval [0, 1) + # Assume the input consists of floating-point numbers in [0, 1) nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37] bucket_sort(nums) print("After bucket sort, nums =", nums) diff --git a/en/codes/python/chapter_sorting/counting_sort.py b/en/codes/python/chapter_sorting/counting_sort.py index 41ed5e2e6..03abd8de8 100644 --- a/en/codes/python/chapter_sorting/counting_sort.py +++ b/en/codes/python/chapter_sorting/counting_sort.py @@ -7,17 +7,15 @@ Author: krahets (krahets@163.com) def counting_sort_naive(nums: list[int]): """Counting sort""" - # Simple implementation, cannot be used for sorting objects - # 1. Count the maximum element m in the array - m = 0 - for num in nums: - m = max(m, num) - # 2. Count the occurrence of each number - # counter[num] represents the occurrence of num + # Simple implementation; cannot be used to sort objects + # 1. Find the maximum array element m + m = max(nums) + # 2. Count the occurrences of each number + # counter[num] is the number of occurrences of num counter = [0] * (m + 1) for num in nums: counter[num] += 1 - # 3. Traverse counter, filling each element back into the original array nums + # 3. Traverse counter and fill the original array nums with the elements i = 0 for num in range(m + 1): for _ in range(counter[num]): @@ -27,37 +25,37 @@ def counting_sort_naive(nums: list[int]): def counting_sort(nums: list[int]): """Counting sort""" - # Complete implementation, can sort objects and is a stable sort - # 1. Count the maximum element m in the array + # Full implementation; stable and adaptable to sorting objects + # 1. Find the maximum array element m m = max(nums) - # 2. Count the occurrence of each number - # counter[num] represents the occurrence of num + # 2. Count the occurrences of each number + # counter[num] is the number of occurrences of num counter = [0] * (m + 1) for num in nums: counter[num] += 1 - # 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - # counter[num]-1 is the last index where num appears in res + # 3. Compute prefix sums of counter to convert occurrence counts into end positions + # That is, counter[num]-1 is the index of the last occurrence of num in res for i in range(m): counter[i + 1] += counter[i] - # 4. Traverse nums in reverse order, placing each element into the result array res - # Initialize the array res to record results + # 4. Traverse nums in reverse and place each element in the result array res + # Initialize array res to store the result n = len(nums) res = [0] * n for i in range(n - 1, -1, -1): num = nums[i] res[counter[num] - 1] = num # Place num at the corresponding index - counter[num] -= 1 # Decrement the prefix sum by 1, getting the next index to place num - # Use result array res to overwrite the original array nums + counter[num] -= 1 # Decrement the prefix sum by 1 to determine where to place the next occurrence of num + # Overwrite the original array nums with the result array res for i in range(n): nums[i] = res[i] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] counting_sort_naive(nums) - print(f"After counting sort (unable to sort objects), nums = {nums}") + print(f"After counting sort (without support for objects), nums = {nums}") nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] counting_sort(nums1) diff --git a/en/codes/python/chapter_sorting/heap_sort.py b/en/codes/python/chapter_sorting/heap_sort.py index d3756c549..aea477c80 100644 --- a/en/codes/python/chapter_sorting/heap_sort.py +++ b/en/codes/python/chapter_sorting/heap_sort.py @@ -6,9 +6,9 @@ Author: krahets (krahets@163.com) def sift_down(nums: list[int], n: int, i: int): - """Heap length is n, start heapifying node i, from top to bottom""" + """Sift down from node i in a heap of size n""" while True: - # Determine the largest node among i, l, r, noted as ma + # Find the node with the largest value among i, l, and r, and store its index in ma l = 2 * i + 1 r = 2 * i + 2 ma = i @@ -16,29 +16,29 @@ def sift_down(nums: list[int], n: int, i: int): ma = l if r < n and nums[r] > nums[ma]: ma = r - # If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + # Stop if node i is largest or both child indices l and r are out of bounds if ma == i: break - # Swap two nodes + # Swap the two nodes nums[i], nums[ma] = nums[ma], nums[i] - # Loop downwards heapification + # Continue sifting down i = ma def heap_sort(nums: list[int]): """Heap sort""" - # Build heap operation: heapify all nodes except leaves + # Build the heap by sifting down from every non-leaf node for i in range(len(nums) // 2 - 1, -1, -1): sift_down(nums, len(nums), i) - # Extract the largest element from the heap and repeat for n-1 rounds + # Extract the largest element from the heap, repeating for n-1 rounds for i in range(len(nums) - 1, 0, -1): - # Swap the root node with the rightmost leaf node (swap the first element with the last element) + # Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) nums[0], nums[i] = nums[i], nums[0] - # Start heapifying the root node, from top to bottom + # Sift down starting at the root sift_down(nums, i, 0) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] heap_sort(nums) diff --git a/en/codes/python/chapter_sorting/insertion_sort.py b/en/codes/python/chapter_sorting/insertion_sort.py index a750a60fb..62800c846 100644 --- a/en/codes/python/chapter_sorting/insertion_sort.py +++ b/en/codes/python/chapter_sorting/insertion_sort.py @@ -7,18 +7,18 @@ Author: timi (xisunyy@163.com) def insertion_sort(nums: list[int]): """Insertion sort""" - # Outer loop: sorted interval is [0, i-1] + # Outer loop: the sorted range is [0, i-1] for i in range(1, len(nums)): base = nums[i] j = i - 1 - # Inner loop: insert base into the correct position within the sorted interval [0, i-1] + # Inner loop: insert base at the correct position in the sorted range [0, i-1] while j >= 0 and nums[j] > base: - nums[j + 1] = nums[j] # Move nums[j] to the right by one position + nums[j + 1] = nums[j] # Shift nums[j] one position to the right j -= 1 - nums[j + 1] = base # Assign base to the correct position + nums[j + 1] = base # Place base at the correct position -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] insertion_sort(nums) diff --git a/en/codes/python/chapter_sorting/merge_sort.py b/en/codes/python/chapter_sorting/merge_sort.py index 4bf2a5d51..7a7e70ccf 100644 --- a/en/codes/python/chapter_sorting/merge_sort.py +++ b/en/codes/python/chapter_sorting/merge_sort.py @@ -6,13 +6,13 @@ Author: timi (xisunyy@163.com), krahets (krahets@163.com) def merge(nums: list[int], left: int, mid: int, right: int): - """Merge left subarray and right subarray""" - # Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - # Create a temporary array tmp to store the merged results + """Merge the left and right subarrays""" + # The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + # Create a temporary array tmp to store the merged result tmp = [0] * (right - left + 1) - # Initialize the start indices of the left and right subarrays + # Initialize the starting indices of the left and right subarrays i, j, k = left, mid + 1, 0 - # While both subarrays still have elements, compare and copy the smaller element into the temporary array + # While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while i <= mid and j <= right: if nums[i] <= nums[j]: tmp[k] = nums[i] @@ -30,25 +30,25 @@ def merge(nums: list[int], left: int, mid: int, right: int): tmp[k] = nums[j] j += 1 k += 1 - # Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + # Copy the elements of temporary array tmp back into the corresponding range of the original array nums for k in range(0, len(tmp)): nums[left + k] = tmp[k] def merge_sort(nums: list[int], left: int, right: int): """Merge sort""" - # Termination condition + # Base case if left >= right: - return # Terminate recursion when subarray length is 1 - # Divide and conquer stage - mid = (left + right) // 2 # Calculate midpoint - merge_sort(nums, left, mid) # Recursively process the left subarray - merge_sort(nums, mid + 1, right) # Recursively process the right subarray - # Merge stage + return # Stop recursing when the subarray has at most one element + # Divide phase + mid = (left + right) // 2 # Calculate the midpoint + merge_sort(nums, left, mid) # Recurse on the left subarray + merge_sort(nums, mid + 1, right) # Recurse on the right subarray + # Merge phase merge(nums, left, mid, right) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [7, 3, 2, 6, 0, 1, 5, 4] merge_sort(nums, 0, len(nums) - 1) diff --git a/en/codes/python/chapter_sorting/quick_sort.py b/en/codes/python/chapter_sorting/quick_sort.py index c3d2aa639..954fe9e94 100644 --- a/en/codes/python/chapter_sorting/quick_sort.py +++ b/en/codes/python/chapter_sorting/quick_sort.py @@ -9,28 +9,28 @@ class QuickSort: """Quick sort class""" def partition(self, nums: list[int], left: int, right: int) -> int: - """Sentinel partition""" + """Two-pointer partitioning""" # Use nums[left] as the pivot i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # Search from right to left for the first element smaller than the pivot + j -= 1 # Scan from right to left for the first element smaller than the pivot while i < j and nums[i] <= nums[left]: - i += 1 # Search from left to right for the first element greater than the pivot - # Swap elements + i += 1 # Scan from left to right for the first element larger than the pivot + # Swap the elements nums[i], nums[j] = nums[j], nums[i] - # Swap the pivot to the boundary between the two subarrays + # Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i # Return the index of the pivot + return i # Return the pivot index def quick_sort(self, nums: list[int], left: int, right: int): """Quick sort""" - # Terminate recursion when subarray length is 1 + # Stop recursing when the subarray has at most one element if left >= right: return - # Sentinel partition + # Two-pointer partitioning pivot = self.partition(nums, left, right) - # Recursively process the left subarray and right subarray + # Recurse on the left and right subarrays self.quick_sort(nums, left, pivot - 1) self.quick_sort(nums, pivot + 1, right) @@ -42,38 +42,38 @@ class QuickSortMedian: """Select the median of three candidate elements""" l, m, r = nums[left], nums[mid], nums[right] if (l <= m <= r) or (r <= m <= l): - return mid # m is between l and r + return mid # m lies between l and r if (m <= l <= r) or (r <= l <= m): - return left # l is between m and r + return left # l lies between m and r return right def partition(self, nums: list[int], left: int, right: int) -> int: - """Sentinel partition (median of three)""" - # Use nums[left] as the pivot + """Two-pointer partitioning (median of three)""" + # Select the median of the leftmost, middle, and rightmost elements as the pivot med = self.median_three(nums, left, (left + right) // 2, right) - # Swap the median to the array's leftmost position + # Swap the median into the leftmost position of the current subarray nums[left], nums[med] = nums[med], nums[left] # Use nums[left] as the pivot i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # Search from right to left for the first element smaller than the pivot + j -= 1 # Scan from right to left for the first element smaller than the pivot while i < j and nums[i] <= nums[left]: - i += 1 # Search from left to right for the first element greater than the pivot - # Swap elements + i += 1 # Scan from left to right for the first element larger than the pivot + # Swap the elements nums[i], nums[j] = nums[j], nums[i] - # Swap the pivot to the boundary between the two subarrays + # Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i # Return the index of the pivot + return i # Return the pivot index def quick_sort(self, nums: list[int], left: int, right: int): """Quick sort""" - # Terminate recursion when subarray length is 1 + # Stop recursing when the subarray has at most one element if left >= right: return - # Sentinel partition + # Two-pointer partitioning pivot = self.partition(nums, left, right) - # Recursively process the left subarray and right subarray + # Recurse on the left and right subarrays self.quick_sort(nums, left, pivot - 1) self.quick_sort(nums, pivot + 1, right) @@ -82,36 +82,36 @@ class QuickSortTailCall: """Quick sort class (recursion depth optimization)""" def partition(self, nums: list[int], left: int, right: int) -> int: - """Sentinel partition""" + """Two-pointer partitioning""" # Use nums[left] as the pivot i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # Search from right to left for the first element smaller than the pivot + j -= 1 # Scan from right to left for the first element smaller than the pivot while i < j and nums[i] <= nums[left]: - i += 1 # Search from left to right for the first element greater than the pivot - # Swap elements + i += 1 # Scan from left to right for the first element larger than the pivot + # Swap the elements nums[i], nums[j] = nums[j], nums[i] - # Swap the pivot to the boundary between the two subarrays + # Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i # Return the index of the pivot + return i # Return the pivot index def quick_sort(self, nums: list[int], left: int, right: int): """Quick sort (recursion depth optimization)""" - # Terminate when subarray length is 1 + # Stop when the subarray has at most one element while left < right: - # Sentinel partition operation + # Perform two-pointer partitioning pivot = self.partition(nums, left, right) - # Perform quick sort on the shorter of the two subarrays + # Apply quick sort to the shorter of the two subarrays if pivot - left < right - pivot: self.quick_sort(nums, left, pivot - 1) # Recursively sort the left subarray - left = pivot + 1 # Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1 # The remaining unsorted range is [pivot + 1, right] else: self.quick_sort(nums, pivot + 1, right) # Recursively sort the right subarray - right = pivot - 1 # Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1 # The remaining unsorted range is [left, pivot - 1] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": # Quick sort nums = [2, 4, 1, 0, 3, 5] @@ -121,9 +121,9 @@ if __name__ == "__main__": # Quick sort (median pivot optimization) nums1 = [2, 4, 1, 0, 3, 5] QuickSortMedian().quick_sort(nums1, 0, len(nums1) - 1) - print("After quick sort (median pivot optimization), nums =", nums1) + print("After quick sort (median pivot optimization), nums1 =", nums1) # Quick sort (recursion depth optimization) nums2 = [2, 4, 1, 0, 3, 5] QuickSortTailCall().quick_sort(nums2, 0, len(nums2) - 1) - print("After quick sort (recursion depth optimization), nums =", nums2) + print("After quick sort (recursion depth optimization), nums2 =", nums2) diff --git a/en/codes/python/chapter_sorting/radix_sort.py b/en/codes/python/chapter_sorting/radix_sort.py index 0fd1abe22..ac244a3df 100644 --- a/en/codes/python/chapter_sorting/radix_sort.py +++ b/en/codes/python/chapter_sorting/radix_sort.py @@ -6,51 +6,51 @@ Author: krahets (krahets@163.com) def digit(num: int, exp: int) -> int: - """Get the k-th digit of element num, where exp = 10^(k-1)""" - # Passing exp instead of k can avoid repeated expensive exponentiation here + """Get the k-th digit of num, where exp = 10^(k-1)""" + # Passing exp instead of k avoids repeatedly performing expensive exponentiation here return (num // exp) % 10 def counting_sort_digit(nums: list[int], exp: int): - """Counting sort (based on nums k-th digit)""" - # Decimal digit range is 0~9, therefore need a bucket array of length 10 + """Counting sort (sort nums by the k-th digit)""" + # Decimal digits range from 0 to 9, so the bucket array needs length 10 counter = [0] * 10 n = len(nums) - # Count the occurrence of digits 0~9 + # Count the occurrences of each digit from 0 to 9 for i in range(n): - d = digit(nums[i], exp) # Get the k-th digit of nums[i], noted as d - counter[d] += 1 # Count the occurrence of digit d - # Calculate prefix sum, converting "occurrence count" into "array index" + d = digit(nums[i], exp) # Get the k-th digit of nums[i] and store it in d + counter[d] += 1 # Count the occurrences of digit d + # Compute prefix sums to convert occurrence counts into array positions for i in range(1, 10): counter[i] += counter[i - 1] - # Traverse in reverse, based on bucket statistics, place each element into res + # Traverse in reverse and place each element into res according to the bucket counts res = [0] * n for i in range(n - 1, -1, -1): d = digit(nums[i], exp) - j = counter[d] - 1 # Get the index j for d in the array + j = counter[d] - 1 # Get the destination index j for digit d in the array res[j] = nums[i] # Place the current element at index j - counter[d] -= 1 # Decrease the count of d by 1 - # Use result to overwrite the original array nums + counter[d] -= 1 # Decrease the count for d by 1 + # Overwrite the original array nums with the result for i in range(n): nums[i] = res[i] def radix_sort(nums: list[int]): """Radix sort""" - # Get the maximum element of the array, used to determine the maximum number of digits + # Get the largest array element to determine the maximum number of digits m = max(nums) - # Traverse from the lowest to the highest digit + # Process digits from least significant to most significant exp = 1 while exp <= m: - # Perform counting sort on the k-th digit of array elements + # Apply counting sort to the k-th digit of the array elements # k = 1 -> exp = 1 # k = 2 -> exp = 10 - # i.e., exp = 10^(k-1) + # That is, exp = 10^(k-1) counting_sort_digit(nums, exp) exp *= 10 -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": # Radix sort nums = [ diff --git a/en/codes/python/chapter_sorting/selection_sort.py b/en/codes/python/chapter_sorting/selection_sort.py index f614f21a1..770a49134 100644 --- a/en/codes/python/chapter_sorting/selection_sort.py +++ b/en/codes/python/chapter_sorting/selection_sort.py @@ -8,18 +8,18 @@ Author: krahets (krahets@163.com) def selection_sort(nums: list[int]): """Selection sort""" n = len(nums) - # Outer loop: unsorted interval is [i, n-1] + # Outer loop: the unsorted range is [i, n-1] for i in range(n - 1): - # Inner loop: find the smallest element within the unsorted interval + # Inner loop: find the smallest element in the unsorted range k = i for j in range(i + 1, n): if nums[j] < nums[k]: k = j # Record the index of the smallest element - # Swap the smallest element with the first element of the unsorted interval + # Swap the smallest element with the first element of the unsorted range nums[i], nums[k] = nums[k], nums[i] -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] selection_sort(nums) diff --git a/en/codes/python/chapter_stack_and_queue/array_deque.py b/en/codes/python/chapter_stack_and_queue/array_deque.py index 414a7e0c8..bc94bd2a0 100644 --- a/en/codes/python/chapter_stack_and_queue/array_deque.py +++ b/en/codes/python/chapter_stack_and_queue/array_deque.py @@ -6,7 +6,7 @@ Author: krahets (krahets@163.com) class ArrayDeque: - """Double-ended queue based on circular array implementation""" + """Deque implemented with a circular array""" def __init__(self, capacity: int): """Constructor""" @@ -15,115 +15,115 @@ class ArrayDeque: self._size: int = 0 def capacity(self) -> int: - """Get the capacity of the double-ended queue""" + """Get the deque capacity""" return len(self._nums) def size(self) -> int: - """Get the length of the double-ended queue""" + """Get the deque size""" return self._size def is_empty(self) -> bool: - """Check if the double-ended queue is empty""" + """Check whether the deque is empty""" return self._size == 0 def index(self, i: int) -> int: - """Calculate circular array index""" - # Use modulo operation to wrap the array head and tail together - # When i passes the tail of the array, return to the head - # When i passes the head of the array, return to the tail + """Calculate an index in the circular array""" + # Use modulo to connect the end of the array to its beginning + # When i passes the end of the array, wrap around to the beginning + # When i passes the beginning of the array, wrap around to the end return (i + self.capacity()) % self.capacity() def push_first(self, num: int): - """Front of the queue enqueue""" + """Enqueue at the front""" if self._size == self.capacity(): - print("Double-ended queue is full") + print("Deque is full") return - # Front pointer moves one position to the left - # Use modulo operation to wrap front around to the tail after passing the head of the array + # Move the front pointer one position to the left + # Use modulo to wrap front to the end when it passes the beginning of the array self._front = self.index(self._front - 1) - # Add num to the front of the queue + # Add num to the front self._nums[self._front] = num self._size += 1 def push_last(self, num: int): - """Rear of the queue enqueue""" + """Enqueue at the rear""" if self._size == self.capacity(): - print("Double-ended queue is full") + print("Deque is full") return - # Calculate rear pointer, points to rear index + 1 + # Calculate the rear pointer, which points one position past the last element rear = self.index(self._front + self._size) - # Add num to the rear of the queue + # Add num to the rear self._nums[rear] = num self._size += 1 def pop_first(self) -> int: - """Front of the queue dequeue""" + """Dequeue from the front""" num = self.peek_first() - # Front pointer moves one position backward + # Advance the front pointer by one position self._front = self.index(self._front + 1) self._size -= 1 return num def pop_last(self) -> int: - """Rear of the queue dequeue""" + """Dequeue from the rear""" num = self.peek_last() self._size -= 1 return num def peek_first(self) -> int: - """Access front of the queue element""" + """Access the front element""" if self.is_empty(): - raise IndexError("Double-ended queue is empty") + raise IndexError("Deque is empty") return self._nums[self._front] def peek_last(self) -> int: - """Access rear of the queue element""" + """Access the rear element""" if self.is_empty(): - raise IndexError("Double-ended queue is empty") - # Calculate tail element index + raise IndexError("Deque is empty") + # Calculate the index of the last element last = self.index(self._front + self._size - 1) return self._nums[last] def to_array(self) -> list[int]: - """Return array for printing""" - # Only convert list elements within the valid length range + """Return an array for printing""" + # Convert only the list elements within the occupied range res = [] for i in range(self._size): res.append(self._nums[self.index(self._front + i)]) return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize double-ended queue + # Initialize the deque deque = ArrayDeque(10) deque.push_last(3) deque.push_last(2) deque.push_last(5) - print("double-ended queue deque =", deque.to_array()) + print("Deque deque =", deque.to_array()) - # Access elements + # Access an element peek_first: int = deque.peek_first() - print("Front of the queue element peek_first =", peek_first) + print("Front element peek_first =", peek_first) peek_last: int = deque.peek_last() - print("Rear of the queue element peek_last =", peek_last) + print("Rear element peek_last =", peek_last) - # Elements enqueue + # Enqueue elements deque.push_last(4) - print("Element 4 rear enqueue after deque =", deque.to_array()) + print("After enqueuing element 4 at the rear, deque =", deque.to_array()) deque.push_first(1) - print("Element 1 front enqueue after deque =", deque.to_array()) + print("After enqueuing element 1 at the front, deque =", deque.to_array()) - # Elements dequeue + # Dequeue an element pop_last: int = deque.pop_last() - print("Rear dequeued element =", pop_last, ", rear dequeue after deque =", deque.to_array()) + print("Element dequeued from the rear =", pop_last, ", after dequeuing from the rear, deque =", deque.to_array()) pop_first: int = deque.pop_first() - print("Front dequeued element =", pop_first, ", front dequeue after deque =", deque.to_array()) + print("Element dequeued from the front =", pop_first, ", after dequeuing from the front, deque =", deque.to_array()) - # Get the length of the double-ended queue + # Get the deque size size: int = deque.size() - print("Length of the double-ended queue size =", size) + print("Deque size =", size) - # Check if the double-ended queue is empty + # Check whether the deque is empty is_empty: bool = deque.is_empty() - print("Is the double-ended queue empty =", is_empty) + print("Is the deque empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/array_queue.py b/en/codes/python/chapter_stack_and_queue/array_queue.py index ca06790f8..3feb3583c 100644 --- a/en/codes/python/chapter_stack_and_queue/array_queue.py +++ b/en/codes/python/chapter_stack_and_queue/array_queue.py @@ -6,53 +6,53 @@ Author: Peng Chen (pengchzn@gmail.com) class ArrayQueue: - """Queue based on circular array implementation""" + """Queue implemented with a circular array""" def __init__(self, size: int): """Constructor""" - self._nums: list[int] = [0] * size # Array for storing queue elements - self._front: int = 0 # Front pointer, points to the front of the queue element - self._size: int = 0 # Queue length + self._nums: list[int] = [0] * size # Array that stores the queue elements + self._front: int = 0 # Front pointer, pointing to the first element + self._size: int = 0 # Queue size def capacity(self) -> int: - """Get the capacity of the queue""" + """Get the queue capacity""" return len(self._nums) def size(self) -> int: - """Get the length of the queue""" + """Get the queue size""" return self._size def is_empty(self) -> bool: - """Check if the queue is empty""" + """Check whether the queue is empty""" return self._size == 0 def push(self, num: int): """Enqueue""" if self._size == self.capacity(): raise IndexError("Queue is full") - # Calculate rear pointer, points to rear index + 1 - # Use modulo operation to wrap rear around to the head after passing the tail of the array + # Calculate the rear pointer, which points one position past the last element + # Use modulo to wrap rear to the beginning when it passes the end of the array rear: int = (self._front + self._size) % self.capacity() - # Add num to the rear of the queue + # Add num to the rear self._nums[rear] = num self._size += 1 def pop(self) -> int: """Dequeue""" num: int = self.peek() - # Front pointer moves one position backward, if it passes the tail, return to the head of the array + # Advance the front pointer by one position, wrapping to the beginning if it passes the end self._front = (self._front + 1) % self.capacity() self._size -= 1 return num def peek(self) -> int: - """Access front of the queue element""" + """Access the front element""" if self.is_empty(): raise IndexError("Queue is empty") return self._nums[self._front] def to_list(self) -> list[int]: - """Return list for printing""" + """Return a list for printing""" res = [0] * self.size() j: int = self._front for i in range(self.size()): @@ -61,38 +61,38 @@ class ArrayQueue: return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize queue + # Initialize the queue queue = ArrayQueue(10) - # Elements enqueue + # Enqueue elements queue.push(1) queue.push(3) queue.push(2) queue.push(5) queue.push(4) - print("queue =", queue.to_list()) + print("Queue queue =", queue.to_list()) - # Access front of the queue element + # Access the front element peek: int = queue.peek() - print("Front of the queue element peek =", peek) + print("Front element peek =", peek) - # Element dequeue + # Dequeue an element pop: int = queue.pop() print("Dequeued element pop =", pop) - print("After dequeue queue =", queue.to_list()) + print("After dequeuing, queue =", queue.to_list()) - # Get the length of the queue + # Get the queue size size: int = queue.size() - print("Length of the queue size =", size) + print("Queue size =", size) - # Check if the queue is empty + # Check whether the queue is empty is_empty: bool = queue.is_empty() print("Is the queue empty =", is_empty) - # Test circular array + # Test the circular array for i in range(10): queue.push(i) queue.pop() - print("Round", i, "enqueue + dequeue after queue = ", queue.to_list()) + print("Round", i, ": after enqueue + dequeue, queue = ", queue.to_list()) diff --git a/en/codes/python/chapter_stack_and_queue/array_stack.py b/en/codes/python/chapter_stack_and_queue/array_stack.py index ca227a37e..22d521f8d 100644 --- a/en/codes/python/chapter_stack_and_queue/array_stack.py +++ b/en/codes/python/chapter_stack_and_queue/array_stack.py @@ -6,18 +6,18 @@ Author: Peng Chen (pengchzn@gmail.com) class ArrayStack: - """Stack based on array implementation""" + """Stack implemented with an array""" def __init__(self): """Constructor""" self._stack: list[int] = [] def size(self) -> int: - """Get the length of the stack""" + """Get the stack size""" return len(self._stack) def is_empty(self) -> bool: - """Check if the stack is empty""" + """Check whether the stack is empty""" return self.size() == 0 def push(self, item: int): @@ -31,42 +31,42 @@ class ArrayStack: return self._stack.pop() def peek(self) -> int: - """Access top of the stack element""" + """Access the top element""" if self.is_empty(): raise IndexError("Stack is empty") return self._stack[-1] def to_list(self) -> list[int]: - """Return list for printing""" + """Return a list for printing""" return self._stack -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize stack + # Initialize the stack stack = ArrayStack() - # Elements push onto stack + # Push elements onto the stack stack.push(1) stack.push(3) stack.push(2) stack.push(5) stack.push(4) - print("stack =", stack.to_list()) + print("Stack stack =", stack.to_list()) - # Access top of the stack element + # Access the top element peek: int = stack.peek() - print("Top of the stack element peek =", peek) + print("Top element peek =", peek) - # Element pop from stack + # Pop an element from the stack pop: int = stack.pop() print("Popped element pop =", pop) - print("After pop stack =", stack.to_list()) + print("After popping, stack =", stack.to_list()) - # Get the length of the stack + # Get the stack size size: int = stack.size() - print("Length of the stack size =", size) + print("Stack size =", size) - # Check if it is empty + # Check whether it is empty is_empty: bool = stack.is_empty() print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/deque.py b/en/codes/python/chapter_stack_and_queue/deque.py index c6f59f2fd..af4e203cf 100644 --- a/en/codes/python/chapter_stack_and_queue/deque.py +++ b/en/codes/python/chapter_stack_and_queue/deque.py @@ -6,37 +6,37 @@ Author: Peng Chen (pengchzn@gmail.com) from collections import deque -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize double-ended queue + # Initialize the deque deq: deque[int] = deque() - # Elements enqueue - deq.append(2) # Add to the rear of the queue + # Enqueue elements + deq.append(2) # Add to the rear deq.append(5) deq.append(4) - deq.appendleft(3) # Add to the front of the queue + deq.appendleft(3) # Add to the front deq.appendleft(1) - print("double-ended queue deque =", deq) + print("Deque deq =", deq) - # Access elements - front: int = deq[0] # Front of the queue element - print("Front of the queue element front =", front) - rear: int = deq[-1] # Rear of the queue element - print("Rear of the queue element rear =", rear) + # Access an element + front: int = deq[0] # Front element + print("Front element front =", front) + rear: int = deq[-1] # Rear element + print("Rear element rear =", rear) - # Elements dequeue - pop_front: int = deq.popleft() # Front of the queue element dequeues - print("Front dequeued element pop_front =", pop_front) - print("After front dequeue deque =", deq) - pop_rear: int = deq.pop() # Rear of the queue element dequeues - print("Rear dequeued element pop_rear =", pop_rear) - print("After rear dequeue deque =", deq) + # Dequeue an element + pop_front: int = deq.popleft() # Dequeue the front element + print("Element dequeued from the front, pop_front =", pop_front) + print("After dequeuing from the front, deq =", deq) + pop_rear: int = deq.pop() # Dequeue the rear element + print("Element dequeued from the rear, pop_rear =", pop_rear) + print("After dequeuing from the rear, deq =", deq) - # Get the length of the double-ended queue + # Get the deque size size: int = len(deq) - print("Length of the double-ended queue size =", size) + print("Deque size =", size) - # Check if the double-ended queue is empty + # Check whether the deque is empty is_empty: bool = len(deq) == 0 - print("Is the double-ended queue empty =", is_empty) + print("Is the deque empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py b/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py index 081669661..7c1cac75b 100644 --- a/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py @@ -11,25 +11,25 @@ class ListNode: def __init__(self, val: int): """Constructor""" self.val: int = val - self.next: ListNode | None = None # Successor node reference - self.prev: ListNode | None = None # Predecessor node reference + self.next: ListNode | None = None # Reference to the next node + self.prev: ListNode | None = None # Reference to the previous node class LinkedListDeque: - """Double-ended queue based on doubly linked list implementation""" + """Deque implemented with a doubly linked list""" def __init__(self): """Constructor""" self._front: ListNode | None = None # Head node front self._rear: ListNode | None = None # Tail node rear - self._size: int = 0 # Length of the double-ended queue + self._size: int = 0 # Deque size def size(self) -> int: - """Get the length of the double-ended queue""" + """Get the deque size""" return self._size def is_empty(self) -> bool: - """Check if the double-ended queue is empty""" + """Check whether the deque is empty""" return self._size == 0 def push(self, num: int, is_front: bool): @@ -38,78 +38,78 @@ class LinkedListDeque: # If the linked list is empty, make both front and rear point to node if self.is_empty(): self._front = self._rear = node - # Front of the queue enqueue operation + # Enqueue at the front elif is_front: # Add node to the head of the linked list self._front.prev = node node.next = self._front - self._front = node # Update head node - # Rear of the queue enqueue operation + self._front = node # Update the head node + # Enqueue at the rear else: # Add node to the tail of the linked list self._rear.next = node node.prev = self._rear - self._rear = node # Update tail node - self._size += 1 # Update queue length + self._rear = node # Update the tail node + self._size += 1 # Update the deque size def push_first(self, num: int): - """Front of the queue enqueue""" + """Enqueue at the front""" self.push(num, True) def push_last(self, num: int): - """Rear of the queue enqueue""" + """Enqueue at the rear""" self.push(num, False) def pop(self, is_front: bool) -> int: """Dequeue operation""" if self.is_empty(): - raise IndexError("Double-ended queue is empty") - # When the deque has one element, front and rear point to the same node + raise IndexError("Deque is empty") + # When the deque size is 1, the head and tail point to the same node if self._size == 1: val: int = self._front.val self._front = None self._rear = None - # Front of the queue dequeue operation + # Dequeue from the front elif is_front: - val: int = self._front.val # Temporarily store head node value - # Delete head node + val: int = self._front.val # Save the head node value + # Remove the head node fnext: ListNode | None = self._front.next fnext.prev = None self._front.next = None - self._front = fnext # Update head node - # Rear of the queue dequeue operation + self._front = fnext # Update the head node + # Dequeue from the rear else: - val: int = self._rear.val # Temporarily store tail node value - # Delete tail node + val: int = self._rear.val # Save the tail node value + # Remove the tail node rprev: ListNode | None = self._rear.prev rprev.next = None self._rear.prev = None - self._rear = rprev # Update tail node - self._size -= 1 # Update queue length + self._rear = rprev # Update the tail node + self._size -= 1 # Update the deque size return val def pop_first(self) -> int: - """Front of the queue dequeue""" + """Dequeue from the front""" return self.pop(True) def pop_last(self) -> int: - """Rear of the queue dequeue""" + """Dequeue from the rear""" return self.pop(False) def peek_first(self) -> int: - """Access front of the queue element""" + """Access the front element""" if self.is_empty(): - raise IndexError("Double-ended queue is empty") + raise IndexError("Deque is empty") return self._front.val def peek_last(self) -> int: - """Access rear of the queue element""" + """Access the rear element""" if self.is_empty(): - raise IndexError("Double-ended queue is empty") + raise IndexError("Deque is empty") return self._rear.val def to_array(self) -> list[int]: - """Return array for printing""" + """Return an array for printing""" node = self._front res = [0] * self.size() for i in range(self.size()): @@ -118,37 +118,37 @@ class LinkedListDeque: return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize double-ended queue + # Initialize the deque deque = LinkedListDeque() deque.push_last(3) deque.push_last(2) deque.push_last(5) - print("double-ended queue deque =", deque.to_array()) + print("Deque deque =", deque.to_array()) - # Access elements + # Access an element peek_first: int = deque.peek_first() - print("Front of the queue element peek_first =", peek_first) + print("Front element peek_first =", peek_first) peek_last: int = deque.peek_last() - print("Rear of the queue element peek_last =", peek_last) + print("Rear element peek_last =", peek_last) - # Elements enqueue + # Enqueue elements deque.push_last(4) - print("Element 4 rear enqueue after deque =", deque.to_array()) + print("After enqueuing element 4 at the rear, deque =", deque.to_array()) deque.push_first(1) - print("Element 1 front enqueue after deque =", deque.to_array()) + print("After enqueuing element 1 at the front, deque =", deque.to_array()) - # Elements dequeue + # Dequeue an element pop_last: int = deque.pop_last() - print("Rear dequeued element =", pop_last, ", rear dequeue after deque =", deque.to_array()) + print("Element dequeued from the rear =", pop_last, ", after dequeuing from the rear, deque =", deque.to_array()) pop_first: int = deque.pop_first() - print("Front dequeued element =", pop_first, ", front dequeue after deque =", deque.to_array()) + print("Element dequeued from the front =", pop_first, ", after dequeuing from the front, deque =", deque.to_array()) - # Get the length of the double-ended queue + # Get the deque size size: int = deque.size() - print("Length of the double-ended queue size =", size) + print("Deque size =", size) - # Check if the double-ended queue is empty + # Check whether the deque is empty is_empty: bool = deque.is_empty() - print("Is the double-ended queue empty =", is_empty) + print("Is the deque empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py b/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py index ab5c397d6..2aed67aee 100644 --- a/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py @@ -12,7 +12,7 @@ from modules import ListNode class LinkedListQueue: - """Queue based on linked list implementation""" + """Queue implemented with a linked list""" def __init__(self): """Constructor""" @@ -21,22 +21,22 @@ class LinkedListQueue: self._size: int = 0 def size(self) -> int: - """Get the length of the queue""" + """Get the queue size""" return self._size def is_empty(self) -> bool: - """Check if the queue is empty""" + """Check whether the queue is empty""" return self._size == 0 def push(self, num: int): """Enqueue""" # Add num after the tail node node = ListNode(num) - # If the queue is empty, make both front and rear point to the node + # If the queue is empty, make both the head and tail point to this node if self._front is None: self._front = node self._rear = node - # If the queue is not empty, add the node after the tail node + # If the queue is not empty, add this node after the tail node else: self._rear.next = node self._rear = node @@ -45,19 +45,19 @@ class LinkedListQueue: def pop(self) -> int: """Dequeue""" num = self.peek() - # Delete head node + # Remove the head node self._front = self._front.next self._size -= 1 return num def peek(self) -> int: - """Access front of the queue element""" + """Access the front element""" if self.is_empty(): raise IndexError("Queue is empty") return self._front.val def to_list(self) -> list[int]: - """Convert to list for printing""" + """Convert to a list for printing""" queue = [] temp = self._front while temp: @@ -66,32 +66,32 @@ class LinkedListQueue: return queue -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize queue + # Initialize the queue queue = LinkedListQueue() - # Elements enqueue + # Enqueue elements queue.push(1) queue.push(3) queue.push(2) queue.push(5) queue.push(4) - print("queue =", queue.to_list()) + print("Queue queue =", queue.to_list()) - # Access front of the queue element + # Access the front element peek: int = queue.peek() - print("Front of the queue element front =", peek) + print("Front element peek =", peek) - # Element dequeue + # Dequeue an element pop_front: int = queue.pop() - print("Dequeued element pop =", pop_front) - print("After dequeue queue =", queue.to_list()) + print("Dequeued element pop_front =", pop_front) + print("After dequeuing, queue =", queue.to_list()) - # Get the length of the queue + # Get the queue size size: int = queue.size() - print("Length of the queue size =", size) + print("Queue size =", size) - # Check if the queue is empty + # Check whether the queue is empty is_empty: bool = queue.is_empty() print("Is the queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py b/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py index 94afea86f..a0d37c5cc 100644 --- a/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py @@ -12,7 +12,7 @@ from modules import ListNode class LinkedListStack: - """Stack based on linked list implementation""" + """Stack implemented with a linked list""" def __init__(self): """Constructor""" @@ -20,11 +20,11 @@ class LinkedListStack: self._size: int = 0 def size(self) -> int: - """Get the length of the stack""" + """Get the stack size""" return self._size def is_empty(self) -> bool: - """Check if the stack is empty""" + """Check whether the stack is empty""" return self._size == 0 def push(self, val: int): @@ -42,13 +42,13 @@ class LinkedListStack: return num def peek(self) -> int: - """Access top of the stack element""" + """Access the top element""" if self.is_empty(): raise IndexError("Stack is empty") return self._peek.val def to_list(self) -> list[int]: - """Convert to list for printing""" + """Convert to a list for printing""" arr = [] node = self._peek while node: @@ -58,32 +58,32 @@ class LinkedListStack: return arr -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize stack + # Initialize the stack stack = LinkedListStack() - # Elements push onto stack + # Push elements onto the stack stack.push(1) stack.push(3) stack.push(2) stack.push(5) stack.push(4) - print("stack =", stack.to_list()) + print("Stack stack =", stack.to_list()) - # Access top of the stack element + # Access the top element peek: int = stack.peek() - print("Top of the stack element peek =", peek) + print("Top element peek =", peek) - # Element pop from stack + # Pop an element from the stack pop: int = stack.pop() print("Popped element pop =", pop) - print("After pop stack =", stack.to_list()) + print("After popping, stack =", stack.to_list()) - # Get the length of the stack + # Get the stack size size: int = stack.size() - print("Length of the stack size =", size) + print("Stack size =", size) - # Check if it is empty + # Check whether it is empty is_empty: bool = stack.is_empty() print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/queue.py b/en/codes/python/chapter_stack_and_queue/queue.py index efb350f55..e721df6b4 100644 --- a/en/codes/python/chapter_stack_and_queue/queue.py +++ b/en/codes/python/chapter_stack_and_queue/queue.py @@ -6,34 +6,34 @@ Author: Peng Chen (pengchzn@gmail.com) from collections import deque -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize queue - # In Python, we generally use the double-ended queue class deque as a queue - # Although queue.Queue() is a proper queue class, it is not very convenient to use + # Initialize the queue + # In Python, we generally use the deque class as a queue + # Although queue.Queue() is a dedicated queue class, deque is more convenient for this example que: deque[int] = deque() - # Elements enqueue + # Enqueue elements que.append(1) que.append(3) que.append(2) que.append(5) que.append(4) - print("queue que =", que) + print("Queue que =", que) - # Access front of the queue element + # Access the front element front: int = que[0] - print("Front of the queue element front =", front) + print("Front element front =", front) - # Element dequeue + # Dequeue an element pop: int = que.popleft() print("Dequeued element pop =", pop) - print("After dequeue que =", que) + print("After dequeuing, que =", que) - # Get the length of the queue + # Get the queue size size: int = len(que) - print("Length of the queue size =", size) + print("Queue size =", size) - # Check if the queue is empty + # Check whether the queue is empty is_empty: bool = len(que) == 0 print("Is the queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/stack.py b/en/codes/python/chapter_stack_and_queue/stack.py index c2fba9782..6e3e55d1c 100644 --- a/en/codes/python/chapter_stack_and_queue/stack.py +++ b/en/codes/python/chapter_stack_and_queue/stack.py @@ -4,33 +4,33 @@ Created Time: 2022-11-29 Author: Peng Chen (pengchzn@gmail.com) """ -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize stack - # Python does not have a built-in stack class, we can use list as a stack + # Initialize the stack + # Python has no built-in stack class; a list can be used as a stack stack: list[int] = [] - # Elements push onto stack + # Push elements onto the stack stack.append(1) stack.append(3) stack.append(2) stack.append(5) stack.append(4) - print("stack =", stack) + print("Stack stack =", stack) - # Access top of the stack element + # Access the top element peek: int = stack[-1] - print("Top of the stack element peek =", peek) + print("Top element peek =", peek) - # Element pop from stack + # Pop an element from the stack pop: int = stack.pop() print("Popped element pop =", pop) - print("After pop stack =", stack) + print("After popping, stack =", stack) - # Get the length of the stack + # Get the stack size size: int = len(stack) - print("Length of the stack size =", size) + print("Stack size =", size) - # Check if it is empty + # Check whether it is empty is_empty: bool = len(stack) == 0 print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_tree/array_binary_tree.py b/en/codes/python/chapter_tree/array_binary_tree.py index 6e2603b66..48435c94b 100644 --- a/en/codes/python/chapter_tree/array_binary_tree.py +++ b/en/codes/python/chapter_tree/array_binary_tree.py @@ -12,39 +12,39 @@ from modules import TreeNode, list_to_tree, print_tree class ArrayBinaryTree: - """Binary tree class represented by array""" + """Binary tree class using an array representation""" def __init__(self, arr: list[int | None]): """Constructor""" self._tree = list(arr) def size(self): - """List capacity""" + """Length of the backing list""" return len(self._tree) def val(self, i: int) -> int | None: - """Get value of node at index i""" - # If index is out of bounds, return None, representing empty position + """Get the value of the node at index i""" + # If the index is out of bounds, return None to represent an empty position if i < 0 or i >= self.size(): return None return self._tree[i] def left(self, i: int) -> int | None: - """Get index of left child node of node at index i""" + """Get the left child index of the node at index i""" return 2 * i + 1 def right(self, i: int) -> int | None: - """Get index of right child node of node at index i""" + """Get the right child index of the node at index i""" return 2 * i + 2 def parent(self, i: int) -> int | None: - """Get index of parent node of node at index i""" + """Get the parent index of the node at index i""" return (i - 1) // 2 def level_order(self) -> list[int]: """Level-order traversal""" self.res = [] - # Traverse array directly + # Traverse the array directly for i in range(self.size()): if self.val(i) is not None: self.res.append(self.val(i)) @@ -85,35 +85,35 @@ class ArrayBinaryTree: return self.res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize binary tree - # Here we use a function to generate a binary tree directly from an array + # Initialize the binary tree + # Use a helper function that builds a binary tree directly from an array arr = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] root = list_to_tree(arr) - print("\nInitialize binary tree\n") - print("Array representation of binary tree:") + print("\nInitialize the binary tree\n") + print("Array representation of the binary tree:") print(arr) - print("Linked list representation of binary tree:") + print("Linked representation of the binary tree:") print_tree(root) - # Binary tree class represented by array + # Binary tree class using an array representation abt = ArrayBinaryTree(arr) - # Access nodes + # Access a node i = 1 l, r, p = abt.left(i), abt.right(i), abt.parent(i) - print(f"\nCurrent node's index is {i}, value is {abt.val(i)}") - print(f"Its left child node's index is {l}, value is {abt.val(l)}") - print(f"Its right child node's index is {r}, value is {abt.val(r)}") - print(f"Its parent node's index is {p}, value is {abt.val(p)}") + print(f"\nCurrent node index: {i}, value: {abt.val(i)}") + print(f"Left child index: {l}, value: {abt.val(l)}") + print(f"Right child index: {r}, value: {abt.val(r)}") + print(f"Parent index: {p}, value: {abt.val(p)}") - # Traverse tree + # Traverse the tree res = abt.level_order() - print("\nLevel-order traversal is:", res) + print("\nLevel-order traversal:", res) res = abt.pre_order() - print("Preorder traversal is:", res) + print("Preorder traversal:", res) res = abt.in_order() - print("Inorder traversal is:", res) + print("Inorder traversal:", res) res = abt.post_order() - print("Postorder traversal is:", res) + print("Postorder traversal:", res) diff --git a/en/codes/python/chapter_tree/avl_tree.py b/en/codes/python/chapter_tree/avl_tree.py index f3707870a..14d2c0e41 100644 --- a/en/codes/python/chapter_tree/avl_tree.py +++ b/en/codes/python/chapter_tree/avl_tree.py @@ -19,110 +19,110 @@ class AVLTree: self._root = None def get_root(self) -> TreeNode | None: - """Get binary tree root node""" + """Get the root of the binary tree""" return self._root def height(self, node: TreeNode | None) -> int: - """Get node height""" - # Empty node height is -1, leaf node height is 0 + """Get the node height""" + # An empty subtree has height -1; a leaf has height 0 if node is not None: return node.height return -1 def update_height(self, node: TreeNode | None): - """Update node height""" - # Node height equals the height of the tallest subtree + 1 + """Update the node height""" + # The node height equals the height of its taller subtree + 1 node.height = max([self.height(node.left), self.height(node.right)]) + 1 def balance_factor(self, node: TreeNode | None) -> int: - """Get balance factor""" - # Empty node balance factor is 0 + """Get the balance factor""" + # An empty subtree has balance factor 0 if node is None: return 0 # Node balance factor = left subtree height - right subtree height return self.height(node.left) - self.height(node.right) def right_rotate(self, node: TreeNode | None) -> TreeNode | None: - """Right rotation operation""" + """Right rotation""" child = node.left grand_child = child.right - # Using child as pivot, rotate node to the right + # Rotate node to the right around child child.right = node node.left = grand_child - # Update node height + # Update the node height self.update_height(node) self.update_height(child) - # Return root node of subtree after rotation + # Return the root of the rotated subtree return child def left_rotate(self, node: TreeNode | None) -> TreeNode | None: - """Left rotation operation""" + """Left rotation""" child = node.right grand_child = child.left - # Using child as pivot, rotate node to the left + # Rotate node to the left around child child.left = node node.right = grand_child - # Update node height + # Update the node height self.update_height(node) self.update_height(child) - # Return root node of subtree after rotation + # Return the root of the rotated subtree return child def rotate(self, node: TreeNode | None) -> TreeNode | None: - """Perform rotation operation to restore balance to this subtree""" - # Get balance factor of node + """Perform rotations to restore balance in this subtree""" + # Get the balance factor of node balance_factor = self.balance_factor(node) - # Left-leaning tree + # Left-heavy tree if balance_factor > 1: if self.balance_factor(node.left) >= 0: - # Right rotation + # Rotate right return self.right_rotate(node) else: - # First left rotation then right rotation + # Rotate left, then right node.left = self.left_rotate(node.left) return self.right_rotate(node) - # Right-leaning tree + # Right-heavy tree elif balance_factor < -1: if self.balance_factor(node.right) <= 0: - # Left rotation + # Rotate left return self.left_rotate(node) else: - # First right rotation then left rotation + # Rotate right, then left node.right = self.right_rotate(node.right) return self.left_rotate(node) - # Balanced tree, no rotation needed, return directly + # The tree is balanced; return without rotating return node def insert(self, val): - """Insert node""" + """Insert a node""" self._root = self.insert_helper(self._root, val) def insert_helper(self, node: TreeNode | None, val: int) -> TreeNode: - """Recursively insert node (helper method)""" + """Insert a node recursively (helper method)""" if node is None: return TreeNode(val) - # 1. Find insertion position and insert node + # 1. Find the insertion position and insert the node if val < node.val: node.left = self.insert_helper(node.left, val) elif val > node.val: node.right = self.insert_helper(node.right, val) else: - # Duplicate node not inserted, return directly + # Return without inserting a duplicate node return node - # Update node height + # Update the node height self.update_height(node) - # 2. Perform rotation operation to restore balance to this subtree + # 2. Perform rotations to restore balance in this subtree return self.rotate(node) def remove(self, val: int): - """Delete node""" + """Remove a node""" self._root = self.remove_helper(self._root, val) def remove_helper(self, node: TreeNode | None, val: int) -> TreeNode | None: - """Recursively delete node (helper method)""" + """Remove a node recursively (helper method)""" if node is None: return None - # 1. Find node and delete + # 1. Find and remove the node if val < node.val: node.left = self.remove_helper(node.left, val) elif val > node.val: @@ -130,71 +130,71 @@ class AVLTree: else: if node.left is None or node.right is None: child = node.left or node.right - # Number of child nodes = 0, delete node directly and return + # Number of children = 0: remove node and return if child is None: return None - # Number of child nodes = 1, delete node directly + # Number of children = 1: remove node directly else: node = child else: - # Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + # Number of children = 2: remove the inorder successor and use its value to replace the current node value temp = node.right while temp.left is not None: temp = temp.left node.right = self.remove_helper(node.right, temp.val) node.val = temp.val - # Update node height + # Update the node height self.update_height(node) - # 2. Perform rotation operation to restore balance to this subtree + # 2. Perform rotations to restore balance in this subtree return self.rotate(node) def search(self, val: int) -> TreeNode | None: - """Search node""" + """Find a node""" cur = self._root - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes None while cur is not None: - # Target node is in cur's right subtree + # If present, the target node lies in the right subtree of cur if cur.val < val: cur = cur.right - # Target node is in cur's left subtree + # If present, the target node lies in the left subtree of cur elif cur.val > val: cur = cur.left - # Found target node, exit loop + # The target node was found; exit the loop else: break - # Return target node + # Return the target node, or None if it was not found return cur -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": def test_insert(tree: AVLTree, val: int): tree.insert(val) - print("\nAfter inserting node {}, AVL tree is".format(val)) + print("\nAVL tree after inserting node {}:".format(val)) print_tree(tree.get_root()) def test_remove(tree: AVLTree, val: int): tree.remove(val) - print("\nAfter deleting node {}, AVL tree is".format(val)) + print("\nAVL tree after removing node {}:".format(val)) print_tree(tree.get_root()) - # Initialize empty AVL tree + # Initialize an empty AVL tree avl_tree = AVLTree() - # Insert nodes - # Please pay attention to how the AVL tree maintains balance after inserting nodes + # Insert a node + # Observe how the AVL tree maintains balance after a node is inserted for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6]: test_insert(avl_tree, val) - # Insert duplicate node + # Insert a duplicate node test_insert(avl_tree, 7) - # Delete nodes - # Please pay attention to how the AVL tree maintains balance after deleting nodes - test_remove(avl_tree, 8) # Delete node with degree 0 - test_remove(avl_tree, 5) # Delete node with degree 1 - test_remove(avl_tree, 4) # Delete node with degree 2 + # Remove a node + # Observe how the AVL tree maintains balance after a node is removed + test_remove(avl_tree, 8) # Remove a node with 0 children + test_remove(avl_tree, 5) # Remove a node with 1 child + test_remove(avl_tree, 4) # Remove a node with 2 children result_node = avl_tree.search(7) - print("\nFound node object is {}, node value = {}".format(result_node, result_node.val)) + print("\nFound node object: {}, node value = {}".format(result_node, result_node.val)) diff --git a/en/codes/python/chapter_tree/binary_search_tree.py b/en/codes/python/chapter_tree/binary_search_tree.py index f9b1da735..56f96fb7a 100644 --- a/en/codes/python/chapter_tree/binary_search_tree.py +++ b/en/codes/python/chapter_tree/binary_search_tree.py @@ -16,49 +16,49 @@ class BinarySearchTree: def __init__(self): """Constructor""" - # Initialize empty tree + # Initialize an empty tree self._root = None def get_root(self) -> TreeNode | None: - """Get binary tree root node""" + """Get the root of the binary tree""" return self._root def search(self, num: int) -> TreeNode | None: - """Search node""" + """Find a node""" cur = self._root - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes None while cur is not None: - # Target node is in cur's right subtree + # If present, the target node is in the right subtree of cur if cur.val < num: cur = cur.right - # Target node is in cur's left subtree + # If present, the target node is in the left subtree of cur elif cur.val > num: cur = cur.left - # Found target node, exit loop + # The target node was found; exit the loop else: break return cur def insert(self, num: int): - """Insert node""" - # If tree is empty, initialize root node + """Insert a node""" + # If the tree is empty, initialize the root node if self._root is None: self._root = TreeNode(num) return - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes None cur, pre = self._root, None while cur is not None: - # Found duplicate node, return directly + # Return immediately if a duplicate node is found if cur.val == num: return pre = cur - # Insertion position is in cur's right subtree + # The insertion position lies in the right subtree of cur if cur.val < num: cur = cur.right - # Insertion position is in cur's left subtree + # The insertion position lies in the left subtree of cur else: cur = cur.left - # Insert node + # Insert a node node = TreeNode(num) if pre.val < num: pre.right = node @@ -66,81 +66,81 @@ class BinarySearchTree: pre.left = node def remove(self, num: int): - """Delete node""" - # If tree is empty, return directly + """Remove a node""" + # Return immediately if the tree is empty if self._root is None: return - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes None cur, pre = self._root, None while cur is not None: - # Found node to delete, exit loop + # The node to remove was found; exit the loop if cur.val == num: break pre = cur - # Node to delete is in cur's right subtree + # If present, the node to remove is in the right subtree of cur if cur.val < num: cur = cur.right - # Node to delete is in cur's left subtree + # If present, the node to remove is in the left subtree of cur else: cur = cur.left - # If no node to delete, return directly + # Return immediately if the node to remove does not exist if cur is None: return - # Number of child nodes = 0 or 1 + # Number of children = 0 or 1 if cur.left is None or cur.right is None: - # When number of child nodes = 0 / 1, child = null / that child node + # With 0 or 1 child, child is None or that child, respectively child = cur.left or cur.right - # Delete node cur + # Remove node cur if cur != self._root: if pre.left == cur: pre.left = child else: pre.right = child else: - # If deleted node is root node, reassign root node + # If the removed node is the root, assign the new root self._root = child - # Number of child nodes = 2 + # Number of children = 2 else: - # Get next node of cur in inorder traversal + # Get the inorder successor of cur tmp: TreeNode = cur.right while tmp.left is not None: tmp = tmp.left - # Recursively delete node tmp + # Recursively remove node tmp self.remove(tmp.val) - # Replace cur with tmp + # Replace the value of cur with the value of tmp cur.val = tmp.val -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize binary search tree + # Initialize the binary search tree bst = BinarySearchTree() nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] - # Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + # Different insertion orders produce different binary trees; this sequence produces a perfect binary tree for num in nums: bst.insert(num) - print("\nInitialized binary tree is\n") + print("\nThe initialized binary tree is:\n") print_tree(bst.get_root()) - # Search node + # Find a node node = bst.search(7) - print("\nFound node object is: {}, node value = {}".format(node, node.val)) + print("\nFound node object: {}, node value = {}".format(node, node.val)) - # Insert node + # Insert a node bst.insert(16) - print("\nAfter inserting node 16, binary tree is\n") + print("\nBinary tree after inserting node 16:\n") print_tree(bst.get_root()) - # Delete node + # Remove a node bst.remove(1) - print("\nAfter deleting node 1, binary tree is\n") + print("\nBinary tree after removing node 1:\n") print_tree(bst.get_root()) bst.remove(2) - print("\nAfter deleting node 2, binary tree is\n") + print("\nBinary tree after removing node 2:\n") print_tree(bst.get_root()) bst.remove(4) - print("\nAfter deleting node 4, binary tree is\n") + print("\nBinary tree after removing node 4:\n") print_tree(bst.get_root()) diff --git a/en/codes/python/chapter_tree/binary_tree.py b/en/codes/python/chapter_tree/binary_tree.py index 0bc591f62..e6d81e748 100644 --- a/en/codes/python/chapter_tree/binary_tree.py +++ b/en/codes/python/chapter_tree/binary_tree.py @@ -11,31 +11,31 @@ sys.path.append(str(Path(__file__).parent.parent)) from modules import TreeNode, print_tree -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize binary tree - # Initialize nodes + # Initialize the binary tree + # Initialize the nodes n1 = TreeNode(val=1) n2 = TreeNode(val=2) n3 = TreeNode(val=3) n4 = TreeNode(val=4) n5 = TreeNode(val=5) - # Build references (pointers) between nodes + # Set up references (pointers) between the nodes n1.left = n2 n1.right = n3 n2.left = n4 n2.right = n5 - print("\nInitialize binary tree\n") + print("\nInitialize the binary tree\n") print_tree(n1) - # Insert and delete nodes + # Insert and remove nodes P = TreeNode(0) - # Insert node P between n1 -> n2 + # Insert node P between n1 and n2 in n1 -> n2 n1.left = P P.left = n2 - print("\nAfter inserting node P\n") + print("\nAfter inserting node P:\n") print_tree(n1) - # Delete node + # Remove a node n1.left = n2 - print("\nAfter deleting node P\n") + print("\nAfter removing node P:\n") print_tree(n1) diff --git a/en/codes/python/chapter_tree/binary_tree_bfs.py b/en/codes/python/chapter_tree/binary_tree_bfs.py index 95d596afc..63eb0855b 100644 --- a/en/codes/python/chapter_tree/binary_tree_bfs.py +++ b/en/codes/python/chapter_tree/binary_tree_bfs.py @@ -14,29 +14,29 @@ from collections import deque def level_order(root: TreeNode | None) -> list[int]: """Level-order traversal""" - # Initialize queue, add root node + # Initialize a queue and enqueue the root queue: deque[TreeNode] = deque() queue.append(root) - # Initialize a list to save the traversal sequence + # Initialize a list to store the traversal sequence res = [] while queue: - node: TreeNode = queue.popleft() # Dequeue - res.append(node.val) # Save node value + node: TreeNode = queue.popleft() # Dequeue from the queue + res.append(node.val) # Store the node value if node.left is not None: - queue.append(node.left) # Left child node enqueue + queue.append(node.left) # Enqueue the left child if node.right is not None: - queue.append(node.right) # Right child node enqueue + queue.append(node.right) # Enqueue the right child return res -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize binary tree - # Here we use a function to generate a binary tree directly from an array + # Initialize the binary tree + # Use a helper function that builds a binary tree directly from an array root: TreeNode = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree\n") + print("\nInitialize the binary tree\n") print_tree(root) # Level-order traversal res: list[int] = level_order(root) - print("\nLevel-order traversal node print sequence = ", res) + print("\nNode sequence from level-order traversal = ", res) diff --git a/en/codes/python/chapter_tree/binary_tree_dfs.py b/en/codes/python/chapter_tree/binary_tree_dfs.py index c20e97708..b7ddd3cb3 100644 --- a/en/codes/python/chapter_tree/binary_tree_dfs.py +++ b/en/codes/python/chapter_tree/binary_tree_dfs.py @@ -15,7 +15,7 @@ def pre_order(root: TreeNode | None): """Preorder traversal""" if root is None: return - # Visit priority: root node -> left subtree -> right subtree + # Visit order: root -> left subtree -> right subtree res.append(root.val) pre_order(root=root.left) pre_order(root=root.right) @@ -25,7 +25,7 @@ def in_order(root: TreeNode | None): """Inorder traversal""" if root is None: return - # Visit priority: left subtree -> root node -> right subtree + # Visit order: left subtree -> root -> right subtree in_order(root=root.left) res.append(root.val) in_order(root=root.right) @@ -35,31 +35,31 @@ def post_order(root: TreeNode | None): """Postorder traversal""" if root is None: return - # Visit priority: left subtree -> right subtree -> root node + # Visit order: left subtree -> right subtree -> root post_order(root=root.left) post_order(root=root.right) res.append(root.val) -"""Driver Code""" +"""Driver code""" if __name__ == "__main__": - # Initialize binary tree - # Here we use a function to generate a binary tree directly from an array + # Initialize the binary tree + # Use a helper function that builds a binary tree directly from an array root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree\n") + print("\nInitialize the binary tree\n") print_tree(root) # Preorder traversal res = [] pre_order(root) - print("\nPreorder traversal node print sequence = ", res) + print("\nNode sequence from preorder traversal = ", res) # Inorder traversal res.clear() in_order(root) - print("\nInorder traversal node print sequence = ", res) + print("\nNode sequence from inorder traversal = ", res) # Postorder traversal res.clear() post_order(root) - print("\nPostorder traversal node print sequence = ", res) + print("\nNode sequence from postorder traversal = ", res) diff --git a/en/codes/python/modules/__init__.py b/en/codes/python/modules/__init__.py index b10799e3c..c5dc66b9a 100644 --- a/en/codes/python/modules/__init__.py +++ b/en/codes/python/modules/__init__.py @@ -1,8 +1,8 @@ -# Follow the PEP 585 - Type Hinting Generics In Standard Collections +# Follow PEP 585: Type Hinting Generics in Standard Collections # https://peps.python.org/pep-0585/ from __future__ import annotations -# Import common libs here to simplify the code by `from module import *` +# Import common utilities here to simplify code with `from modules import *` from .list_node import ( ListNode, list_to_linked_list, diff --git a/en/codes/python/modules/list_node.py b/en/codes/python/modules/list_node.py index 61e3a9a57..136ee2099 100644 --- a/en/codes/python/modules/list_node.py +++ b/en/codes/python/modules/list_node.py @@ -10,7 +10,7 @@ class ListNode: def __init__(self, val: int): self.val: int = val # Node value - self.next: ListNode | None = None # Reference to next node + self.next: ListNode | None = None # Reference to the next node def list_to_linked_list(arr: list[int]) -> ListNode | None: diff --git a/en/codes/python/modules/print_util.py b/en/codes/python/modules/print_util.py index 0cf92db5d..1668d83d7 100644 --- a/en/codes/python/modules/print_util.py +++ b/en/codes/python/modules/print_util.py @@ -9,7 +9,7 @@ from .list_node import ListNode, linked_list_to_list def print_matrix(mat: list[list[int]]): - """Print matrix""" + """Print a matrix""" s = [] for arr in mat: s.append(" " + str(arr)) @@ -17,7 +17,7 @@ def print_matrix(mat: list[list[int]]): def print_linked_list(head: ListNode | None): - """Print linked list""" + """Print a linked list""" arr: list[int] = linked_list_to_list(head) print(" -> ".join([str(a) for a in arr])) @@ -39,7 +39,7 @@ def print_tree( root: TreeNode | None, prev: Trunk | None = None, is_right: bool = False ): """ - Print binary tree + Print a binary tree This tree printer is borrowed from TECHIE DELIGHT https://www.techiedelight.com/c-program-print-binary-tree/ """ @@ -68,13 +68,13 @@ def print_tree( def print_dict(hmap: dict): - """Print dictionary""" + """Print a dictionary""" for key, value in hmap.items(): print(key, "->", value) def print_heap(heap: list[int]): - """Print heap""" + """Print a heap""" print("Array representation of the heap:", heap) print("Tree representation of the heap:") root: TreeNode | None = list_to_tree(heap) diff --git a/en/codes/python/modules/tree_node.py b/en/codes/python/modules/tree_node.py index f19230482..138db0c02 100644 --- a/en/codes/python/modules/tree_node.py +++ b/en/codes/python/modules/tree_node.py @@ -13,14 +13,14 @@ class TreeNode: def __init__(self, val: int = 0): self.val: int = val # Node value self.height: int = 0 # Node height - self.left: TreeNode | None = None # Reference to left child node - self.right: TreeNode | None = None # Reference to right child node + self.left: TreeNode | None = None # Reference to the left child + self.right: TreeNode | None = None # Reference to the right child - # For the serialization encoding rules, please refer to: - # https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ - # Array representation of binary tree: + # For the serialization rules, see: + # https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ + # Array representation of the binary tree: # [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] - # Linked list representation of binary tree: + # Linked representation of the binary tree: # /——— 15 # /——— 7 # /——— 3 @@ -35,10 +35,10 @@ class TreeNode: def list_to_tree_dfs(arr: list[int], i: int) -> TreeNode | None: """Deserialize a list into a binary tree: recursion""" - # If the index exceeds the array length, or the corresponding element is None, return None + # Return None if the index is out of bounds or the corresponding element is None if i < 0 or i >= len(arr) or arr[i] is None: return None - # Build the current node + # Create the current node root = TreeNode(arr[i]) # Recursively build the left and right subtrees root.left = list_to_tree_dfs(arr, 2 * i + 1) diff --git a/en/codes/python/modules/vertex.py b/en/codes/python/modules/vertex.py index 6da527db0..40e58abb4 100644 --- a/en/codes/python/modules/vertex.py +++ b/en/codes/python/modules/vertex.py @@ -11,10 +11,10 @@ class Vertex: def vals_to_vets(vals: list[int]) -> list["Vertex"]: - """Input value list vals, return vertex list vets""" + """Given the list of values vals, return the list of vertices vets""" return [Vertex(val) for val in vals] def vets_to_vals(vets: list["Vertex"]) -> list[int]: - """Input vertex list vets, return value list vals""" + """Given the list of vertices vets, return the list of values vals""" return [vet.val for vet in vets] diff --git a/en/codes/python/test_all.py b/en/codes/python/test_all.py index 7c585de87..bbf4b0a51 100644 --- a/en/codes/python/test_all.py +++ b/en/codes/python/test_all.py @@ -6,11 +6,11 @@ env = os.environ.copy() env["PYTHONIOENCODING"] = "utf-8" if __name__ == "__main__": - # find source code files + # Find source code files src_paths = sorted(glob.glob("chapter_*/*.py")) errors = [] - # run python code + # Run Python code for src_path in src_paths: process = subprocess.Popen( ["python", src_path], @@ -20,7 +20,7 @@ if __name__ == "__main__": env=env, encoding='utf-8' ) - # Wait for the process to complete, and get the output and error messages + # Wait for the process to finish and collect its output and error messages stdout, stderr = process.communicate() # Check the exit status exit_status = process.returncode @@ -28,6 +28,6 @@ if __name__ == "__main__": errors.append(stderr) print(f"Tested {len(src_paths)} files") - print(f"Found exception in {len(errors)} files") + print(f"Found exceptions in {len(errors)} files") if len(errors) > 0: raise RuntimeError("\n\n".join(errors)) diff --git a/en/codes/pythontutor/chapter_array_and_linkedlist/array.md b/en/codes/pythontutor/chapter_array_and_linkedlist/array.md new file mode 100644 index 000000000..3816fd17c --- /dev/null +++ b/en/codes/pythontutor/chapter_array_and_linkedlist/array.md @@ -0,0 +1,23 @@ + + + +https://pythontutor.com/render.html#code=import%20random%0A%0Adef%20random_access%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Access%20an%20element%20at%20a%20random%20index%22%22%22%0A%20%20%20%20%23%20Choose%20a%20random%20number%20in%20the%20range%20%5B0,%20len%28nums%29-1%5D%0A%20%20%20%20random_index%20%3D%20random.randint%280,%20len%28nums%29%20-%201%29%0A%20%20%20%20%23%20Get%20and%20return%20the%20randomly%20selected%20element%0A%20%20%20%20random_num%20%3D%20nums%5Brandom_index%5D%0A%20%20%20%20return%20random_num%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20array%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20print%28%22Array%20nums%20%3D%22,%20nums%29%0A%0A%20%20%20%20%23%20Random%20access%0A%20%20%20%20random_num%3A%20int%20%3D%20random_access%28nums%29%0A%20%20%20%20print%28%22Randomly%20selected%20element%20from%20nums%3A%22,%20random_num%29%0A&cumulative=false&curInstr=7&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20insert%28nums%3A%20list%5Bint%5D,%20num%3A%20int,%20index%3A%20int%29%3A%0A%20%20%20%20%22%22%22Insert%20element%20num%20at%20index%20%60index%60%20in%20the%20array%22%22%22%0A%20%20%20%20%23%20Shift%20all%20elements%20at%20and%20after%20index%20%60index%60%20one%20position%20to%20the%20right%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201,%20index,%20-1%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20nums%5Bi%20-%201%5D%0A%20%20%20%20%23%20Assign%20num%20to%20the%20element%20at%20index%20%60index%60%0A%20%20%20%20nums%5Bindex%5D%20%3D%20num%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20array%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20print%28%22Array%20nums%20%3D%22,%20nums%29%0A%0A%20%20%20%20%23%20Insert%20an%20element%0A%20%20%20%20insert%28nums,%206,%203%29%0A%20%20%20%20print%28%22Insert%20the%20number%206%20at%20index%203,%20giving%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20remove%28nums%3A%20list%5Bint%5D,%20index%3A%20int%29%3A%0A%20%20%20%20%22%22%22Remove%20the%20element%20at%20index%20%60index%60%22%22%22%0A%20%20%20%20%23%20Shift%20all%20elements%20after%20index%20%60index%60%20one%20position%20to%20the%20left%0A%20%20%20%20for%20i%20in%20range%28index,%20len%28nums%29%20-%201%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20nums%5Bi%20%2B%201%5D%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20array%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20print%28%22Array%20nums%20%3D%22,%20nums%29%0A%0A%20%20%20%20%23%20Remove%20an%20element%0A%20%20%20%20remove%28nums,%202%29%0A%20%20%20%20print%28%22Remove%20the%20element%20at%20index%202,%20giving%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20traverse%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Traverse%20the%20array%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20Traverse%20the%20array%20by%20index%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20nums%5Bi%5D%0A%20%20%20%20%23%20Iterate%20directly%20over%20the%20array%20elements%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20num%0A%20%20%20%20%23%20Iterate%20over%20both%20the%20indices%20and%20the%20elements%0A%20%20%20%20for%20i,%20num%20in%20enumerate%28nums%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20num%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20array%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20print%28%22Array%20nums%20%3D%22,%20nums%29%0A%0A%20%20%20%20%23%20Traverse%20the%20array%0A%20%20%20%20traverse%28nums%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20find%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20a%20specified%20element%20in%20the%20array%22%22%22%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3D%3D%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20i%0A%20%20%20%20return%20-1%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20array%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20print%28%22Array%20nums%20%3D%22,%20nums%29%0A%0A%20%20%20%20%23%20Find%20an%20element%0A%20%20%20%20index%3A%20int%20%3D%20find%28nums,%203%29%0A%20%20%20%20print%28%22Find%20element%203%20in%20nums,%20giving%20index%20%3D%22,%20index%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=%23%20Note%20that%20a%20Python%20list%20is%20a%20dynamic%20array%20and%20can%20be%20extended%20directly%0A%23%20For%20learning%20purposes,%20this%20function%20treats%20a%20list%20as%20a%20fixed-length%20array%0Adef%20extend%28nums%3A%20list%5Bint%5D,%20enlarge%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22Extend%20the%20array%20length%22%22%22%0A%20%20%20%20%23%20Initialize%20an%20array%20with%20the%20extended%20length%0A%20%20%20%20res%20%3D%20%5B0%5D%20%2A%20%28len%28nums%29%20%2B%20enlarge%29%0A%20%20%20%20%23%20Copy%20all%20elements%20from%20the%20original%20array%20to%20the%20new%20array%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20res%5Bi%5D%20%3D%20nums%5Bi%5D%0A%20%20%20%20%23%20Return%20the%20new,%20extended%20array%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20array%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20print%28%22Array%20nums%20%3D%22,%20nums%29%0A%0A%20%20%20%20%23%20Extend%20the%20length%0A%20%20%20%20nums%20%3D%20extend%28nums,%203%29%0A%20%20%20%20print%28%22Extend%20the%20array%20length%20to%208,%20giving%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_array_and_linkedlist/linked_list.md b/en/codes/pythontutor/chapter_array_and_linkedlist/linked_list.md new file mode 100644 index 000000000..cbec64867 --- /dev/null +++ b/en/codes/pythontutor/chapter_array_and_linkedlist/linked_list.md @@ -0,0 +1,17 @@ + + + +https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0Adef%20insert%28n0%3A%20ListNode,%20P%3A%20ListNode%29%3A%0A%20%20%20%20%22%22%22Insert%20node%20P%20after%20node%20n0%20in%20the%20linked%20list%22%22%22%0A%20%20%20%20n1%20%3D%20n0.next%0A%20%20%20%20P.next%20%3D%20n1%0A%20%20%20%20n0.next%20%3D%20P%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20linked%20list%0A%20%20%20%20%23%20Initialize%20the%20individual%20nodes%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20Set%20up%20references%20between%20the%20nodes%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4%0A%0A%20%20%20%20%23%20Insert%20a%20node%0A%20%20%20%20p%20%3D%20ListNode%280%29%0A%20%20%20%20insert%28n0,%20p%29&cumulative=false&curInstr=39&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0Adef%20remove%28n0%3A%20ListNode%29%3A%0A%20%20%20%20%22%22%22Remove%20the%20first%20node%20after%20node%20n0%20in%20the%20linked%20list%22%22%22%0A%20%20%20%20if%20not%20n0.next%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20n0%20-%3E%20P%20-%3E%20n1%0A%20%20%20%20P%20%3D%20n0.next%0A%20%20%20%20n1%20%3D%20P.next%0A%20%20%20%20n0.next%20%3D%20n1%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20linked%20list%0A%20%20%20%20%23%20Initialize%20the%20individual%20nodes%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20Set%20up%20references%20between%20the%20nodes%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4%0A%0A%20%20%20%20%23%20Remove%20a%20node%0A%20%20%20%20remove%28n0%29&cumulative=false&curInstr=34&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0Adef%20access%28head%3A%20ListNode,%20index%3A%20int%29%20-%3E%20ListNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Access%20the%20node%20at%20index%20%60index%60%20in%20the%20linked%20list%22%22%22%0A%20%20%20%20for%20_%20in%20range%28index%29%3A%0A%20%20%20%20%20%20%20%20if%20not%20head%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20head%20%3D%20head.next%0A%20%20%20%20return%20head%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20linked%20list%0A%20%20%20%20%23%20Initialize%20the%20individual%20nodes%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20Set%20up%20references%20between%20the%20nodes%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4%0A%0A%20%20%20%20%23%20Access%20a%20node%0A%20%20%20%20node%20%3D%20access%28n0,%203%29%0A%20%20%20%20print%28%22Value%20of%20the%20node%20at%20index%203%20in%20the%20linked%20list%20%3D%20%7B%7D%22.format%28node.val%29%29&cumulative=false&curInstr=34&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0Adef%20find%28head%3A%20ListNode,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20the%20first%20node%20with%20value%20target%20in%20the%20linked%20list%22%22%22%0A%20%20%20%20index%20%3D%200%0A%20%20%20%20while%20head%3A%0A%20%20%20%20%20%20%20%20if%20head.val%20%3D%3D%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20index%0A%20%20%20%20%20%20%20%20head%20%3D%20head.next%0A%20%20%20%20%20%20%20%20index%20%2B%3D%201%0A%20%20%20%20return%20-1%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20linked%20list%0A%20%20%20%20%23%20Initialize%20the%20individual%20nodes%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20Set%20up%20references%20between%20the%20nodes%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4%0A%0A%20%20%20%20%23%20Find%20a%20node%0A%20%20%20%20index%20%3D%20find%28n0,%202%29%0A%20%20%20%20print%28%22Index%20of%20the%20node%20with%20value%202%20in%20the%20linked%20list%20%3D%20%7B%7D%22.format%28index%29%29&cumulative=false&curInstr=34&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_array_and_linkedlist/my_list.md b/en/codes/pythontutor/chapter_array_and_linkedlist/my_list.md new file mode 100644 index 000000000..ae176ca12 --- /dev/null +++ b/en/codes/pythontutor/chapter_array_and_linkedlist/my_list.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20MyList%3A%0A%20%20%20%20%22%22%22List%20class%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self._capacity%3A%20int%20%3D%2010%0A%20%20%20%20%20%20%20%20self._arr%3A%20list%5Bint%5D%20%3D%20%5B0%5D%20%2A%20self._capacity%0A%20%20%20%20%20%20%20%20self._size%3A%20int%20%3D%200%0A%20%20%20%20%20%20%20%20self._extend_ratio%3A%20int%20%3D%202%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20list%20length%20%28current%20number%20of%20elements%29%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._size%0A%0A%20%20%20%20def%20capacity%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20list%20capacity%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._capacity%0A%0A%20%20%20%20def%20get%28self,%20index%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Access%20an%20element%22%22%22%0A%20%20%20%20%20%20%20%20if%20index%20%3C%200%20or%20index%20%3E%3D%20self._size%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Index%20out%20of%20bounds%22%29%0A%20%20%20%20%20%20%20%20return%20self._arr%5Bindex%5D%0A%0A%20%20%20%20def%20set%28self,%20num%3A%20int,%20index%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Update%20an%20element%22%22%22%0A%20%20%20%20%20%20%20%20if%20index%20%3C%200%20or%20index%20%3E%3D%20self._size%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Index%20out%20of%20bounds%22%29%0A%20%20%20%20%20%20%20%20self._arr%5Bindex%5D%20%3D%20num%0A%0A%20%20%20%20def%20add%28self,%20num%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Append%20an%20element%20at%20the%20end%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.size%28%29%20%3D%3D%20self.capacity%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.extend_capacity%28%29%0A%20%20%20%20%20%20%20%20self._arr%5Bself._size%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20self._size%20%2B%3D%201%0A%0A%20%20%20%20def%20insert%28self,%20num%3A%20int,%20index%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Insert%20an%20element%20in%20the%20middle%22%22%22%0A%20%20%20%20%20%20%20%20if%20index%20%3C%200%20or%20index%20%3E%3D%20self._size%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Index%20out%20of%20bounds%22%29%0A%20%20%20%20%20%20%20%20%20%20%20%20self.extend_capacity%28%29%0A%20%20%20%20%20%20%20%20%23%20Shift%20all%20elements%20at%20and%20after%20index%20%60index%60%20one%20position%20to%20the%20right%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28self._size%20-%201,%20index%20-%201,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self._arr%5Bj%20%2B%201%5D%20%3D%20self._arr%5Bj%5D%0A%20%20%20%20%20%20%20%20self._arr%5Bindex%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20self._size%20%2B%3D%201%0A%0A%20%20%20%20def%20remove%28self,%20index%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20an%20element%22%22%22%0A%20%20%20%20%20%20%20%20if%20index%20%3C%200%20or%20index%20%3E%3D%20self._size%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Index%20out%20of%20bounds%22%29%0A%20%20%20%20%20%20%20%20num%20%3D%20self._arr%5Bindex%5D%0A%20%20%20%20%20%20%20%20%23%20Shift%20all%20elements%20after%20index%20%60index%60%20one%20position%20to%20the%20left%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28index,%20self._size%20-%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self._arr%5Bj%5D%20%3D%20self._arr%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20self._size%20-%3D%201%0A%20%20%20%20%20%20%20%20return%20num%0A%0A%20%20%20%20def%20extend_capacity%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Grow%20the%20list%20capacity%22%22%22%0A%20%20%20%20%20%20%20%20self._arr%20%3D%20self._arr%20%2B%20%5B0%5D%20%2A%20self.capacity%28%29%20%2A%20%28self._extend_ratio%20-%201%29%0A%20%20%20%20%20%20%20%20self._capacity%20%3D%20len%28self._arr%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20list%0A%20%20%20%20nums%20%3D%20MyList%28%29%0A%20%20%20%20%23%20Append%20an%20element%20at%20the%20end%0A%20%20%20%20nums.add%281%29%0A%20%20%20%20nums.add%283%29%0A%20%20%20%20nums.add%282%29%0A%20%20%20%20nums.add%285%29%0A%20%20%20%20nums.add%284%29%0A%0A%20%20%20%20%23%20Insert%20an%20element%20in%20the%20middle%0A%20%20%20%20nums.insert%286,%20index%3D3%29%0A%0A%20%20%20%20%23%20Remove%20an%20element%0A%20%20%20%20nums.remove%283%29%0A%0A%20%20%20%20%23%20Access%20an%20element%0A%20%20%20%20num%20%3D%20nums.get%281%29%0A%0A%20%20%20%20%23%20Update%20an%20element%0A%20%20%20%20nums.set%280,%201%29%0A%0A%20%20%20%20%23%20Test%20capacity%20growth%0A%20%20%20%20for%20i%20in%20range%2810%29%3A%0A%20%20%20%20%20%20%20%20nums.add%28i%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/n_queens.md b/en/codes/pythontutor/chapter_backtracking/n_queens.md new file mode 100644 index 000000000..7f6136b4f --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/n_queens.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20backtrack%28%0A%20%20%20%20row%3A%20int,%0A%20%20%20%20n%3A%20int,%0A%20%20%20%20state%3A%20list%5Blist%5Bstr%5D%5D,%0A%20%20%20%20res%3A%20list%5Blist%5Blist%5Bstr%5D%5D%5D,%0A%20%20%20%20cols%3A%20list%5Bbool%5D,%0A%20%20%20%20diags1%3A%20list%5Bbool%5D,%0A%20%20%20%20diags2%3A%20list%5Bbool%5D,%0A%29%3A%0A%20%20%20%20%22%22%22Backtracking%3A%20N-queens%22%22%22%0A%20%20%20%20%23%20Record%20a%20solution%20when%20a%20queen%20has%20been%20placed%20in%20every%20row%0A%20%20%20%20if%20row%20%3D%3D%20n%3A%0A%20%20%20%20%20%20%20%20res.append%28%5Blist%28row%29%20for%20row%20in%20state%5D%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Iterate%20over%20all%20columns%0A%20%20%20%20for%20col%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20%23%20Calculate%20the%20main-diagonal%20and%20anti-diagonal%20indices%20for%20this%20square%0A%20%20%20%20%20%20%20%20diag1%20%3D%20row%20-%20col%20%2B%20n%20-%201%0A%20%20%20%20%20%20%20%20diag2%20%3D%20row%20%2B%20col%0A%20%20%20%20%20%20%20%20%23%20Prune%3A%20the%20square%20must%20have%20no%20queen%20in%20its%20column,%20main%20diagonal,%20or%20anti-diagonal%0A%20%20%20%20%20%20%20%20if%20not%20cols%5Bcol%5D%20and%20not%20diags1%5Bdiag1%5D%20and%20not%20diags2%5Bdiag2%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Try%3A%20place%20a%20queen%20on%20this%20square%0A%20%20%20%20%20%20%20%20%20%20%20%20state%5Brow%5D%5Bcol%5D%20%3D%20%22Q%22%0A%20%20%20%20%20%20%20%20%20%20%20%20cols%5Bcol%5D%20%3D%20diags1%5Bdiag1%5D%20%3D%20diags2%5Bdiag2%5D%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Place%20a%20queen%20in%20the%20next%20row%0A%20%20%20%20%20%20%20%20%20%20%20%20backtrack%28row%20%2B%201,%20n,%20state,%20res,%20cols,%20diags1,%20diags2%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Backtrack%3A%20restore%20this%20square%20to%20empty%0A%20%20%20%20%20%20%20%20%20%20%20%20state%5Brow%5D%5Bcol%5D%20%3D%20%22%23%22%0A%20%20%20%20%20%20%20%20%20%20%20%20cols%5Bcol%5D%20%3D%20diags1%5Bdiag1%5D%20%3D%20diags2%5Bdiag2%5D%20%3D%20False%0A%0A%0Adef%20n_queens%28n%3A%20int%29%20-%3E%20list%5Blist%5Blist%5Bstr%5D%5D%5D%3A%0A%20%20%20%20%22%22%22Solve%20the%20N-queens%20problem%22%22%22%0A%20%20%20%20%23%20Initialize%20an%20n%2An%20board,%20where%20%27Q%27%20represents%20a%20queen%20and%20%27%23%27%20represents%20an%20empty%20square%0A%20%20%20%20state%20%3D%20%5B%5B%22%23%22%20for%20_%20in%20range%28n%29%5D%20for%20_%20in%20range%28n%29%5D%0A%20%20%20%20cols%20%3D%20%5BFalse%5D%20%2A%20n%20%20%23%20Track%20whether%20a%20column%20contains%20a%20queen%0A%20%20%20%20diags1%20%3D%20%5BFalse%5D%20%2A%20%282%20%2A%20n%20-%201%29%20%20%23%20Track%20whether%20a%20main%20diagonal%20contains%20a%20queen%0A%20%20%20%20diags2%20%3D%20%5BFalse%5D%20%2A%20%282%20%2A%20n%20-%201%29%20%20%23%20Track%20whether%20an%20anti-diagonal%20contains%20a%20queen%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20backtrack%280,%20n,%20state,%20res,%20cols,%20diags1,%20diags2%29%0A%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%204%0A%20%20%20%20res%20%3D%20n_queens%28n%29%0A%0A%20%20%20%20print%28f%22The%20input%20board%20has%20width%20and%20height%20%7Bn%7D%22%29%0A%20%20%20%20print%28f%22Number%20of%20ways%20to%20place%20the%20queens%3A%20%7Blen%28res%29%7D%22%29%0A%20%20%20%20for%20state%20in%20res%3A%0A%20%20%20%20%20%20%20%20print%28%22--------------------%22%29%0A%20%20%20%20%20%20%20%20for%20row%20in%20state%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print%28row%29&cumulative=false&curInstr=61&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/permutations_i.md b/en/codes/pythontutor/chapter_backtracking/permutations_i.md new file mode 100644 index 000000000..26bdb380c --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/permutations_i.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20backtrack%28%0A%20%20%20%20state%3A%20list%5Bint%5D,%20choices%3A%20list%5Bint%5D,%20selected%3A%20list%5Bbool%5D,%20res%3A%20list%5Blist%5Bint%5D%5D%0A%29%3A%0A%20%20%20%20%22%22%22Backtracking%3A%20permutations%20I%22%22%22%0A%20%20%20%20%23%20Record%20a%20solution%20when%20the%20state%20length%20equals%20the%20number%20of%20elements%0A%20%20%20%20if%20len%28state%29%20%3D%3D%20len%28choices%29%3A%0A%20%20%20%20%20%20%20%20res.append%28list%28state%29%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Iterate%20over%20all%20choices%0A%20%20%20%20for%20i,%20choice%20in%20enumerate%28choices%29%3A%0A%20%20%20%20%20%20%20%20%23%20Prune%3A%20do%20not%20choose%20an%20element%20more%20than%20once%0A%20%20%20%20%20%20%20%20if%20not%20selected%5Bi%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Try%3A%20make%20a%20choice%20and%20update%20the%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20selected%5Bi%5D%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20state.append%28choice%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Proceed%20to%20the%20next%20choice%0A%20%20%20%20%20%20%20%20%20%20%20%20backtrack%28state,%20choices,%20selected,%20res%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Backtrack%3A%20undo%20the%20choice%20and%20restore%20the%20previous%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20selected%5Bi%5D%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20state.pop%28%29%0A%0A%0Adef%20permutations_i%28nums%3A%20list%5Bint%5D%29%20-%3E%20list%5Blist%5Bint%5D%5D%3A%0A%20%20%20%20%22%22%22Permutations%20I%22%22%22%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20backtrack%28state%3D%5B%5D,%20choices%3Dnums,%20selected%3D%5BFalse%5D%20%2A%20len%28nums%29,%20res%3Dres%29%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%202,%203%5D%0A%0A%20%20%20%20res%20%3D%20permutations_i%28nums%29%0A%0A%20%20%20%20print%28f%22Input%20array%20nums%20%3D%20%7Bnums%7D%22%29%0A%20%20%20%20print%28f%22All%20permutations%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=13&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/permutations_ii.md b/en/codes/pythontutor/chapter_backtracking/permutations_ii.md new file mode 100644 index 000000000..060aa88b2 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/permutations_ii.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20backtrack%28%0A%20%20%20%20state%3A%20list%5Bint%5D,%20choices%3A%20list%5Bint%5D,%20selected%3A%20list%5Bbool%5D,%20res%3A%20list%5Blist%5Bint%5D%5D%0A%29%3A%0A%20%20%20%20%22%22%22Backtracking%3A%20permutations%20II%22%22%22%0A%20%20%20%20%23%20Record%20a%20solution%20when%20the%20state%20length%20equals%20the%20number%20of%20elements%0A%20%20%20%20if%20len%28state%29%20%3D%3D%20len%28choices%29%3A%0A%20%20%20%20%20%20%20%20res.append%28list%28state%29%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Iterate%20over%20all%20choices%0A%20%20%20%20duplicated%20%3D%20set%5Bint%5D%28%29%0A%20%20%20%20for%20i,%20choice%20in%20enumerate%28choices%29%3A%0A%20%20%20%20%20%20%20%20%23%20Prune%3A%20do%20not%20reuse%20an%20element%20or%20choose%20the%20same%20value%20again%20at%20this%20depth%0A%20%20%20%20%20%20%20%20if%20not%20selected%5Bi%5D%20and%20choice%20not%20in%20duplicated%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Try%3A%20make%20a%20choice%20and%20update%20the%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20duplicated.add%28choice%29%20%20%23%20Record%20the%20chosen%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20selected%5Bi%5D%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20state.append%28choice%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Proceed%20to%20the%20next%20choice%0A%20%20%20%20%20%20%20%20%20%20%20%20backtrack%28state,%20choices,%20selected,%20res%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Backtrack%3A%20undo%20the%20choice%20and%20restore%20the%20previous%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20selected%5Bi%5D%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20state.pop%28%29%0A%0A%0Adef%20permutations_ii%28nums%3A%20list%5Bint%5D%29%20-%3E%20list%5Blist%5Bint%5D%5D%3A%0A%20%20%20%20%22%22%22Permutations%20II%22%22%22%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20backtrack%28state%3D%5B%5D,%20choices%3Dnums,%20selected%3D%5BFalse%5D%20%2A%20len%28nums%29,%20res%3Dres%29%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%202,%202%5D%0A%0A%20%20%20%20res%20%3D%20permutations_ii%28nums%29%0A%0A%20%20%20%20print%28f%22Input%20array%20nums%20%3D%20%7Bnums%7D%22%29%0A%20%20%20%20print%28f%22All%20permutations%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=13&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/preorder_traversal_i_compact.md b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_i_compact.md new file mode 100644 index 000000000..d3d2d5ec1 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_i_compact.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%20%3D%200%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D,%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Return%20None%20if%20the%20index%20is%20out%20of%20bounds%20or%20the%20corresponding%20element%20is%20None%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%23%20Create%20the%20current%20node%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20%23%20Recursively%20build%20the%20left%20and%20right%20subtrees%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%22%22%22%0A%20%20%20%20return%20list_to_tree_dfs%28arr,%200%29%0A%0A%0Adef%20pre_order%28root%3A%20TreeNode%29%3A%0A%20%20%20%20%22%22%22Preorder%20traversal%3A%20example%201%22%22%22%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20if%20root.val%20%3D%3D%207%3A%0A%20%20%20%20%20%20%20%20%23%20Record%20the%20solution%0A%20%20%20%20%20%20%20%20res.append%28root%29%0A%20%20%20%20pre_order%28root.left%29%0A%20%20%20%20pre_order%28root.right%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28%5B1,%207,%203,%204,%205,%206,%207%5D%29%0A%0A%20%20%20%20%23%20Preorder%20traversal%0A%20%20%20%20res%20%3D%20list%5BTreeNode%5D%28%29%0A%20%20%20%20pre_order%28root%29%0A%0A%20%20%20%20print%28%22%5CnOutput%20all%20nodes%20with%20value%207%22%29%0A%20%20%20%20print%28%5Bnode.val%20for%20node%20in%20res%5D%29&cumulative=false&curInstr=126&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/preorder_traversal_ii_compact.md b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_ii_compact.md new file mode 100644 index 000000000..c1a990a43 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_ii_compact.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%20%3D%200%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D,%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Return%20None%20if%20the%20index%20is%20out%20of%20bounds%20or%20the%20corresponding%20element%20is%20None%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%23%20Create%20the%20current%20node%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20%23%20Recursively%20build%20the%20left%20and%20right%20subtrees%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%22%22%22%0A%20%20%20%20return%20list_to_tree_dfs%28arr,%200%29%0A%0A%0Adef%20pre_order%28root%3A%20TreeNode%29%3A%0A%20%20%20%20%22%22%22Preorder%20traversal%3A%20example%202%22%22%22%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Try%0A%20%20%20%20path.append%28root%29%0A%20%20%20%20if%20root.val%20%3D%3D%207%3A%0A%20%20%20%20%20%20%20%20%23%20Record%20the%20solution%0A%20%20%20%20%20%20%20%20res.append%28list%28path%29%29%0A%20%20%20%20pre_order%28root.left%29%0A%20%20%20%20pre_order%28root.right%29%0A%20%20%20%20%23%20Backtrack%0A%20%20%20%20path.pop%28%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28%5B1,%207,%203,%204,%205,%206,%207%5D%29%0A%0A%20%20%20%20%23%20Preorder%20traversal%0A%20%20%20%20path%20%3D%20list%5BTreeNode%5D%28%29%0A%20%20%20%20res%20%3D%20list%5Blist%5BTreeNode%5D%5D%28%29%0A%20%20%20%20pre_order%28root%29%0A%0A%20%20%20%20print%28%22%5CnOutput%20all%20paths%20from%20the%20root%20to%20nodes%20with%20value%207%22%29%0A%20%20%20%20for%20path%20in%20res%3A%0A%20%20%20%20%20%20%20%20print%28%5Bnode.val%20for%20node%20in%20path%5D%29&cumulative=false&curInstr=126&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/preorder_traversal_iii_compact.md b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_iii_compact.md new file mode 100644 index 000000000..d12a64fa7 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_iii_compact.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%20%3D%200%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D,%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Return%20None%20if%20the%20index%20is%20out%20of%20bounds%20or%20the%20corresponding%20element%20is%20None%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%23%20Create%20the%20current%20node%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20%23%20Recursively%20build%20the%20left%20and%20right%20subtrees%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%22%22%22%0A%20%20%20%20return%20list_to_tree_dfs%28arr,%200%29%0A%0A%0Adef%20pre_order%28root%3A%20TreeNode%29%3A%0A%20%20%20%20%22%22%22Preorder%20traversal%3A%20example%203%22%22%22%0A%20%20%20%20%23%20Prune%0A%20%20%20%20if%20root%20is%20None%20or%20root.val%20%3D%3D%203%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Try%0A%20%20%20%20path.append%28root%29%0A%20%20%20%20if%20root.val%20%3D%3D%207%3A%0A%20%20%20%20%20%20%20%20%23%20Record%20the%20solution%0A%20%20%20%20%20%20%20%20res.append%28list%28path%29%29%0A%20%20%20%20pre_order%28root.left%29%0A%20%20%20%20pre_order%28root.right%29%0A%20%20%20%20%23%20Backtrack%0A%20%20%20%20path.pop%28%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28%5B1,%207,%203,%204,%205,%206,%207%5D%29%0A%0A%20%20%20%20%23%20Preorder%20traversal%0A%20%20%20%20path%20%3D%20list%5BTreeNode%5D%28%29%0A%20%20%20%20res%20%3D%20list%5Blist%5BTreeNode%5D%5D%28%29%0A%20%20%20%20pre_order%28root%29%0A%0A%20%20%20%20print%28%22%5CnOutput%20all%20paths%20from%20the%20root%20to%20nodes%20with%20value%207%20that%20contain%20no%20node%20with%20value%203%22%29%0A%20%20%20%20for%20path%20in%20res%3A%0A%20%20%20%20%20%20%20%20print%28%5Bnode.val%20for%20node%20in%20path%5D%29&cumulative=false&curInstr=126&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/preorder_traversal_iii_template.md b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_iii_template.md new file mode 100644 index 000000000..4cd17b4a2 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/preorder_traversal_iii_template.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%20%3D%200%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D,%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Return%20None%20if%20the%20index%20is%20out%20of%20bounds%20or%20the%20corresponding%20element%20is%20None%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%23%20Create%20the%20current%20node%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20%23%20Recursively%20build%20the%20left%20and%20right%20subtrees%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%22%22%22%0A%20%20%20%20return%20list_to_tree_dfs%28arr,%200%29%0A%0A%0Adef%20is_solution%28state%3A%20list%5BTreeNode%5D%29%20-%3E%20bool%3A%0A%20%20%20%20%22%22%22Check%20whether%20the%20current%20state%20is%20a%20solution%22%22%22%0A%20%20%20%20return%20state%20and%20state%5B-1%5D.val%20%3D%3D%207%0A%0Adef%20record_solution%28state%3A%20list%5BTreeNode%5D,%20res%3A%20list%5Blist%5BTreeNode%5D%5D%29%3A%0A%20%20%20%20%22%22%22Record%20the%20solution%22%22%22%0A%20%20%20%20res.append%28list%28state%29%29%0A%0Adef%20is_valid%28state%3A%20list%5BTreeNode%5D,%20choice%3A%20TreeNode%29%20-%3E%20bool%3A%0A%20%20%20%20%22%22%22Check%20whether%20the%20choice%20is%20valid%20in%20the%20current%20state%22%22%22%0A%20%20%20%20return%20choice%20is%20not%20None%20and%20choice.val%20%21%3D%203%0A%0Adef%20make_choice%28state%3A%20list%5BTreeNode%5D,%20choice%3A%20TreeNode%29%3A%0A%20%20%20%20%22%22%22Update%20the%20state%22%22%22%0A%20%20%20%20state.append%28choice%29%0A%0Adef%20undo_choice%28state%3A%20list%5BTreeNode%5D,%20choice%3A%20TreeNode%29%3A%0A%20%20%20%20%22%22%22Restore%20the%20state%22%22%22%0A%20%20%20%20state.pop%28%29%0A%0Adef%20backtrack%28%0A%20%20%20%20state%3A%20list%5BTreeNode%5D,%20choices%3A%20list%5BTreeNode%5D,%20res%3A%20list%5Blist%5BTreeNode%5D%5D%0A%29%3A%0A%20%20%20%20%22%22%22Backtracking%3A%20example%203%22%22%22%0A%20%20%20%20%23%20Check%20whether%20this%20is%20a%20solution%0A%20%20%20%20if%20is_solution%28state%29%3A%0A%20%20%20%20%20%20%20%20%23%20Record%20the%20solution%0A%20%20%20%20%20%20%20%20record_solution%28state,%20res%29%0A%20%20%20%20%23%20Iterate%20over%20all%20choices%0A%20%20%20%20for%20choice%20in%20choices%3A%0A%20%20%20%20%20%20%20%20%23%20Prune%3A%20check%20whether%20the%20choice%20is%20valid%0A%20%20%20%20%20%20%20%20if%20is_valid%28state,%20choice%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Try%3A%20make%20a%20choice%20and%20update%20the%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20make_choice%28state,%20choice%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Proceed%20to%20the%20next%20choice%0A%20%20%20%20%20%20%20%20%20%20%20%20backtrack%28state,%20%5Bchoice.left,%20choice.right%5D,%20res%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Backtrack%3A%20undo%20the%20choice%20and%20restore%20the%20previous%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20undo_choice%28state,%20choice%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28%5B1,%207,%203,%204,%205,%206,%207%5D%29%0A%0A%20%20%20%20%23%20Backtracking%20algorithm%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20backtrack%28state%3D%5B%5D,%20choices%3D%5Broot%5D,%20res%3Dres%29%0A%20%20%20%20print%28%22%5CnOutput%20all%20paths%22%29%0A%20%20%20%20for%20path%20in%20res%3A%0A%20%20%20%20%20%20%20%20print%28%5Bnode.val%20for%20node%20in%20path%5D%29&cumulative=false&curInstr=138&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/subset_sum_i.md b/en/codes/pythontutor/chapter_backtracking/subset_sum_i.md new file mode 100644 index 000000000..783653458 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/subset_sum_i.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20backtrack%28%0A%20%20%20%20state%3A%20list%5Bint%5D,%20target%3A%20int,%20choices%3A%20list%5Bint%5D,%20start%3A%20int,%20res%3A%20list%5Blist%5Bint%5D%5D%0A%29%3A%0A%20%20%20%20%22%22%22Backtracking%3A%20subset%20sum%20I%22%22%22%0A%20%20%20%20%23%20Record%20a%20solution%20when%20the%20remaining%20target%20is%20zero%0A%20%20%20%20if%20target%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20res.append%28list%28state%29%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Iterate%20over%20all%20choices%0A%20%20%20%20%23%20Pruning%202%3A%20iterate%20from%20start%20to%20avoid%20generating%20duplicate%20subsets%0A%20%20%20%20for%20i%20in%20range%28start,%20len%28choices%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20Pruning%201%3A%20exit%20the%20loop%20if%20the%20chosen%20value%20exceeds%20the%20remaining%20target%0A%20%20%20%20%20%20%20%20%23%20The%20array%20is%20sorted,%20so%20later%20elements%20are%20at%20least%20as%20large%20and%20also%20exceed%20the%20remaining%20target%0A%20%20%20%20%20%20%20%20if%20target%20-%20choices%5Bi%5D%20%3C%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%23%20Try%3A%20make%20a%20choice%20and%20update%20target%20and%20start%0A%20%20%20%20%20%20%20%20state.append%28choices%5Bi%5D%29%0A%20%20%20%20%20%20%20%20%23%20Proceed%20to%20the%20next%20choice%0A%20%20%20%20%20%20%20%20backtrack%28state,%20target%20-%20choices%5Bi%5D,%20choices,%20i,%20res%29%0A%20%20%20%20%20%20%20%20%23%20Backtrack%3A%20undo%20the%20choice%20and%20restore%20the%20previous%20state%0A%20%20%20%20%20%20%20%20state.pop%28%29%0A%0A%0Adef%20subset_sum_i%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20list%5Blist%5Bint%5D%5D%3A%0A%20%20%20%20%22%22%22Solve%20subset%20sum%20I%22%22%22%0A%20%20%20%20state%20%3D%20%5B%5D%20%20%23%20State%20%28subset%29%0A%20%20%20%20nums.sort%28%29%20%20%23%20Sort%20nums%0A%20%20%20%20start%20%3D%200%20%20%23%20Starting%20index%20for%20iteration%0A%20%20%20%20res%20%3D%20%5B%5D%20%20%23%20Result%20list%20%28list%20of%20subsets%29%0A%20%20%20%20backtrack%28state,%20target,%20nums,%20start,%20res%29%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B3,%204,%205%5D%0A%20%20%20%20target%20%3D%209%0A%20%20%20%20res%20%3D%20subset_sum_i%28nums,%20target%29%0A%0A%20%20%20%20print%28f%22Input%20array%20nums%20%3D%20%7Bnums%7D,%20target%20%3D%20%7Btarget%7D%22%29%0A%20%20%20%20print%28f%22All%20subsets%20with%20sum%20%7Btarget%7D%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=16&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/subset_sum_i_naive.md b/en/codes/pythontutor/chapter_backtracking/subset_sum_i_naive.md new file mode 100644 index 000000000..1746627f2 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/subset_sum_i_naive.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20backtrack%28%0A%20%20%20%20state%3A%20list%5Bint%5D,%0A%20%20%20%20target%3A%20int,%0A%20%20%20%20total%3A%20int,%0A%20%20%20%20choices%3A%20list%5Bint%5D,%0A%20%20%20%20res%3A%20list%5Blist%5Bint%5D%5D,%0A%29%3A%0A%20%20%20%20%22%22%22Backtracking%3A%20subset%20sum%20I%22%22%22%0A%20%20%20%20%23%20Record%20a%20solution%20when%20the%20subset%20sum%20equals%20target%0A%20%20%20%20if%20total%20%3D%3D%20target%3A%0A%20%20%20%20%20%20%20%20res.append%28list%28state%29%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Iterate%20over%20all%20choices%0A%20%20%20%20for%20i%20in%20range%28len%28choices%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20Prune%3A%20skip%20this%20choice%20if%20the%20subset%20sum%20would%20exceed%20target%0A%20%20%20%20%20%20%20%20if%20total%20%2B%20choices%5Bi%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%20%20%23%20Try%3A%20make%20a%20choice%20and%20update%20the%20sum%20total%0A%20%20%20%20%20%20%20%20state.append%28choices%5Bi%5D%29%0A%20%20%20%20%20%20%20%20%23%20Proceed%20to%20the%20next%20choice%0A%20%20%20%20%20%20%20%20backtrack%28state,%20target,%20total%20%2B%20choices%5Bi%5D,%20choices,%20res%29%0A%20%20%20%20%20%20%20%20%23%20Backtrack%3A%20undo%20the%20choice%20and%20restore%20the%20previous%20state%0A%20%20%20%20%20%20%20%20state.pop%28%29%0A%0A%0Adef%20subset_sum_i_naive%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20list%5Blist%5Bint%5D%5D%3A%0A%20%20%20%20%22%22%22Solve%20subset%20sum%20I%20%28with%20duplicate%20subsets%29%22%22%22%0A%20%20%20%20state%20%3D%20%5B%5D%20%20%23%20State%20%28subset%29%0A%20%20%20%20total%20%3D%200%20%20%23%20Subset%20sum%0A%20%20%20%20res%20%3D%20%5B%5D%20%20%23%20Result%20list%20%28list%20of%20subsets%29%0A%20%20%20%20backtrack%28state,%20target,%20total,%20nums,%20res%29%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B3,%204,%205%5D%0A%20%20%20%20target%20%3D%209%0A%20%20%20%20res%20%3D%20subset_sum_i_naive%28nums,%20target%29%0A%0A%20%20%20%20print%28f%22Input%20array%20nums%20%3D%20%7Bnums%7D,%20target%20%3D%20%7Btarget%7D%22%29%0A%20%20%20%20print%28f%22All%20subsets%20with%20sum%20%7Btarget%7D%3A%20res%20%3D%20%7Bres%7D%22%29%0A%20%20%20%20print%28f%22Note%20that%20this%20method%20produces%20duplicate%20subsets%22%29&cumulative=false&curInstr=16&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_backtracking/subset_sum_ii.md b/en/codes/pythontutor/chapter_backtracking/subset_sum_ii.md new file mode 100644 index 000000000..a8068c5d0 --- /dev/null +++ b/en/codes/pythontutor/chapter_backtracking/subset_sum_ii.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20backtrack%28%0A%20%20%20%20state%3A%20list%5Bint%5D,%20target%3A%20int,%20choices%3A%20list%5Bint%5D,%20start%3A%20int,%20res%3A%20list%5Blist%5Bint%5D%5D%0A%29%3A%0A%20%20%20%20%22%22%22Backtracking%3A%20subset%20sum%20II%22%22%22%0A%20%20%20%20%23%20Record%20a%20solution%20when%20the%20remaining%20target%20is%20zero%0A%20%20%20%20if%20target%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20res.append%28list%28state%29%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Iterate%20over%20all%20choices%0A%20%20%20%20%23%20Pruning%202%3A%20iterate%20from%20start%20to%20avoid%20generating%20duplicate%20subsets%0A%20%20%20%20%23%20Pruning%203%3A%20start%20after%20the%20previously%20chosen%20index%20to%20avoid%20choosing%20the%20same%20element%20again%0A%20%20%20%20for%20i%20in%20range%28start,%20len%28choices%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20Pruning%201%3A%20exit%20the%20loop%20if%20the%20chosen%20value%20exceeds%20the%20remaining%20target%0A%20%20%20%20%20%20%20%20%23%20The%20array%20is%20sorted,%20so%20later%20elements%20are%20at%20least%20as%20large%20and%20also%20exceed%20the%20remaining%20target%0A%20%20%20%20%20%20%20%20if%20target%20-%20choices%5Bi%5D%20%3C%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%23%20Pruning%204%3A%20if%20this%20element%20equals%20its%20left%20neighbor%20at%20the%20same%20search%20depth,%20the%20branch%20is%20a%20duplicate%3B%20skip%20it%0A%20%20%20%20%20%20%20%20if%20i%20%3E%20start%20and%20choices%5Bi%5D%20%3D%3D%20choices%5Bi%20-%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%20%20%23%20Try%3A%20make%20a%20choice%20and%20update%20target%20and%20start%0A%20%20%20%20%20%20%20%20state.append%28choices%5Bi%5D%29%0A%20%20%20%20%20%20%20%20%23%20Proceed%20to%20the%20next%20choice%0A%20%20%20%20%20%20%20%20backtrack%28state,%20target%20-%20choices%5Bi%5D,%20choices,%20i%20%2B%201,%20res%29%0A%20%20%20%20%20%20%20%20%23%20Backtrack%3A%20undo%20the%20choice%20and%20restore%20the%20previous%20state%0A%20%20%20%20%20%20%20%20state.pop%28%29%0A%0A%0Adef%20subset_sum_ii%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20list%5Blist%5Bint%5D%5D%3A%0A%20%20%20%20%22%22%22Solve%20subset%20sum%20II%22%22%22%0A%20%20%20%20state%20%3D%20%5B%5D%20%20%23%20State%20%28subset%29%0A%20%20%20%20nums.sort%28%29%20%20%23%20Sort%20nums%0A%20%20%20%20start%20%3D%200%20%20%23%20Starting%20index%20for%20iteration%0A%20%20%20%20res%20%3D%20%5B%5D%20%20%23%20Result%20list%20%28list%20of%20subsets%29%0A%20%20%20%20backtrack%28state,%20target,%20nums,%20start,%20res%29%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%204,%205%5D%0A%20%20%20%20target%20%3D%209%0A%20%20%20%20res%20%3D%20subset_sum_ii%28nums,%20target%29%0A%0A%20%20%20%20print%28f%22Input%20array%20nums%20%3D%20%7Bnums%7D,%20target%20%3D%20%7Btarget%7D%22%29%0A%20%20%20%20print%28f%22All%20subsets%20with%20sum%20%7Btarget%7D%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=16&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_computational_complexity/iteration.md b/en/codes/pythontutor/chapter_computational_complexity/iteration.md new file mode 100644 index 000000000..be832b1f3 --- /dev/null +++ b/en/codes/pythontutor/chapter_computational_complexity/iteration.md @@ -0,0 +1,29 @@ + + + +https://pythontutor.com/render.html#code=def%20for_loop%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22for%20loop%22%22%22%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20Sum%201,%202,%20...,%20n-1,%20n%20in%20a%20loop%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20res%20%2B%3D%20i%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20for_loop%28n%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20the%20for%20loop%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D& + + +https://pythontutor.com/render.html#code=def%20while_loop%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22while%20loop%22%22%22%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20i%20%3D%201%20%20%23%20Initialize%20the%20loop%20control%20variable%0A%20%20%20%20%23%20Sum%201,%202,%20...,%20n-1,%20n%20in%20a%20loop%0A%20%20%20%20while%20i%20%3C%3D%20n%3A%0A%20%20%20%20%20%20%20%20res%20%2B%3D%20i%0A%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20Update%20the%20loop%20control%20variable%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20while_loop%28n%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20the%20while%20loop%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20while_loop_ii%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22while%20loop%20%28two%20updates%29%22%22%22%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20i%20%3D%201%20%20%23%20Initialize%20the%20loop%20control%20variable%0A%20%20%20%20%23%20Sum%201,%204,%2010,%20...%20in%20a%20loop%0A%20%20%20%20while%20i%20%3C%3D%20n%3A%0A%20%20%20%20%20%20%20%20res%20%2B%3D%20i%0A%20%20%20%20%20%20%20%20%23%20Update%20the%20loop%20control%20variable%0A%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20i%20%2A%3D%202%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20while_loop_ii%28n%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20the%20while%20loop%20%28two%20updates%29%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20nested_for_loop%28n%3A%20int%29%20-%3E%20str%3A%0A%20%20%20%20%22%22%22Nested%20for%20loops%22%22%22%0A%20%20%20%20res%20%3D%20%22%22%0A%20%20%20%20%23%20Loop%20over%20i%20%3D%201,%202,%20...,%20n-1,%20n%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20Loop%20over%20j%20%3D%201,%202,%20...,%20n-1,%20n%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20f%22%28%7Bi%7D,%20%7Bj%7D%29,%20%22%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20nested_for_loop%28n%29%0A%20%20%20%20print%28f%22%5CnTraversal%20from%20the%20nested%20for%20loops%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Recursion%22%22%22%0A%20%20%20%20%23%20Base%20case%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20%23%20Recursive-call%20phase%0A%20%20%20%20res%20%3D%20recur%28n%20-%201%29%0A%20%20%20%20%23%20Unwinding%20phase%3A%20compute%20the%20result%0A%20%20%20%20return%20n%20%2B%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20recur%28n%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20the%20recursive%20function%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20tail_recur%28n,%20res%29%3A%0A%20%20%20%20%22%22%22Tail%20recursion%22%22%22%0A%20%20%20%20%23%20Base%20case%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20res%0A%20%20%20%20%23%20Tail-recursive%20call%0A%20%20%20%20return%20tail_recur%28n%20-%201,%20res%20%2B%20n%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20tail_recur%28n,%200%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20the%20tail-recursive%20function%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20fib%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Fibonacci%20sequence%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Base%20cases%3A%20f%281%29%20%3D%200,%20f%282%29%20%3D%201%0A%20%20%20%20if%20n%20%3D%3D%201%20or%20n%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20n%20-%201%0A%20%20%20%20%23%20Recursive%20calls%3A%20f%28n%29%20%3D%20f%28n-1%29%20%2B%20f%28n-2%29%0A%20%20%20%20res%20%3D%20fib%28n%20-%201%29%20%2B%20fib%28n%20-%202%29%0A%20%20%20%20%23%20Return%20the%20result%20f%28n%29%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20fib%28n%29%0A%20%20%20%20print%28f%22%5CnTerm%20%7Bn%7D%20of%20the%20Fibonacci%20sequence%20is%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20for_loop_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Simulate%20recursion%20using%20iteration%22%22%22%0A%20%20%20%20%23%20Use%20an%20explicit%20stack%20to%20simulate%20the%20system%20call%20stack%0A%20%20%20%20stack%20%3D%20%5B%5D%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20Recursive-call%20phase%0A%20%20%20%20for%20i%20in%20range%28n,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20Simulate%20recursive%20calls%20by%20pushing%20onto%20the%20stack%0A%20%20%20%20%20%20%20%20stack.append%28i%29%0A%20%20%20%20%23%20Unwinding%20phase%3A%20compute%20the%20result%0A%20%20%20%20while%20stack%3A%0A%20%20%20%20%20%20%20%20%23%20Simulate%20unwinding%20by%20popping%20from%20the%20stack%0A%20%20%20%20%20%20%20%20res%20%2B%3D%20stack.pop%28%29%0A%20%20%20%20%23%20res%20%3D%201%2B2%2B3%2B...%2Bn%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20for_loop_recur%28n%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20simulating%20recursion%20using%20iteration%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_computational_complexity/recursion.md b/en/codes/pythontutor/chapter_computational_complexity/recursion.md new file mode 100644 index 000000000..da1bb4e01 --- /dev/null +++ b/en/codes/pythontutor/chapter_computational_complexity/recursion.md @@ -0,0 +1,17 @@ + + + +https://pythontutor.com/render.html#code=def%20recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Recursion%22%22%22%0A%20%20%20%20%23%20Base%20case%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20%23%20Recursive-call%20phase%0A%20%20%20%20res%20%3D%20recur%28n%20-%201%29%0A%20%20%20%20%23%20Unwinding%20phase%3A%20compute%20the%20result%0A%20%20%20%20return%20n%20%2B%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20recur%28n%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20the%20recursive%20function%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20tail_recur%28n,%20res%29%3A%0A%20%20%20%20%22%22%22Tail%20recursion%22%22%22%0A%20%20%20%20%23%20Base%20case%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20res%0A%20%20%20%20%23%20Tail-recursive%20call%0A%20%20%20%20return%20tail_recur%28n%20-%201,%20res%20%2B%20n%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20tail_recur%28n,%200%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20the%20tail-recursive%20function%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20fib%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Fibonacci%20sequence%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Base%20cases%3A%20f%281%29%20%3D%200,%20f%282%29%20%3D%201%0A%20%20%20%20if%20n%20%3D%3D%201%20or%20n%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20n%20-%201%0A%20%20%20%20%23%20Recursive%20calls%3A%20f%28n%29%20%3D%20f%28n-1%29%20%2B%20f%28n-2%29%0A%20%20%20%20res%20%3D%20fib%28n%20-%201%29%20%2B%20fib%28n%20-%202%29%0A%20%20%20%20%23%20Return%20the%20result%20f%28n%29%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20fib%28n%29%0A%20%20%20%20print%28f%22%5CnTerm%20%7Bn%7D%20of%20the%20Fibonacci%20sequence%20is%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20for_loop_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Simulate%20recursion%20using%20iteration%22%22%22%0A%20%20%20%20%23%20Use%20an%20explicit%20stack%20to%20simulate%20the%20system%20call%20stack%0A%20%20%20%20stack%20%3D%20%5B%5D%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20Recursive-call%20phase%0A%20%20%20%20for%20i%20in%20range%28n,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20Simulate%20recursive%20calls%20by%20pushing%20onto%20the%20stack%0A%20%20%20%20%20%20%20%20stack.append%28i%29%0A%20%20%20%20%23%20Unwinding%20phase%3A%20compute%20the%20result%0A%20%20%20%20while%20stack%3A%0A%20%20%20%20%20%20%20%20%23%20Simulate%20unwinding%20by%20popping%20from%20the%20stack%0A%20%20%20%20%20%20%20%20res%20%2B%3D%20stack.pop%28%29%0A%20%20%20%20%23%20res%20%3D%201%2B2%2B3%2B...%2Bn%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20for_loop_recur%28n%29%0A%20%20%20%20print%28f%22%5CnSum%20from%20simulating%20recursion%20using%20iteration%3A%20res%20%3D%20%7Bres%7D%22%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_computational_complexity/space_complexity.md b/en/codes/pythontutor/chapter_computational_complexity/space_complexity.md new file mode 100644 index 000000000..e86bc7935 --- /dev/null +++ b/en/codes/pythontutor/chapter_computational_complexity/space_complexity.md @@ -0,0 +1,23 @@ + + + +https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0Adef%20function%28%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Function%22%22%22%0A%20%20%20%20%23%20Perform%20some%20operations%0A%20%20%20%20return%200%0A%0Adef%20constant%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22Constant%20complexity%22%22%22%0A%20%20%20%20%23%20Constants,%20variables,%20and%20objects%20occupy%20O%281%29%20space%0A%20%20%20%20a%20%3D%200%0A%20%20%20%20nums%20%3D%20%5B0%5D%20%2A%2010%0A%20%20%20%20node%20%3D%20ListNode%280%29%0A%20%20%20%20%23%20Variables%20in%20the%20loop%20occupy%20O%281%29%20space%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20c%20%3D%200%0A%20%20%20%20%23%20Function%20calls%20in%20the%20loop%20occupy%20O%281%29%20space%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20function%28%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20%23%20Constant%20complexity%0A%20%20%20%20constant%28n%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20linear%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22Linear%20complexity%22%22%22%0A%20%20%20%20%23%20A%20list%20of%20length%20n%20occupies%20O%28n%29%20space%0A%20%20%20%20nums%20%3D%20%5B0%5D%20%2A%20n%0A%20%20%20%20%23%20A%20hash%20table%20with%20n%20entries%20occupies%20O%28n%29%20space%0A%20%20%20%20hmap%20%3D%20dict%5Bint,%20str%5D%28%29%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20hmap%5Bi%5D%20%3D%20str%28i%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20%23%20Linear%20complexity%0A%20%20%20%20linear%28n%29&cumulative=false&curInstr=20&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20linear_recur%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22Linear%20complexity%20%28recursive%20implementation%29%22%22%22%0A%20%20%20%20print%28%22Recursion%3A%20n%20%3D%22,%20n%29%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20linear_recur%28n%20-%201%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20%23%20Linear%20complexity%0A%20%20%20%20linear_recur%28n%29&cumulative=false&curInstr=25&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20quadratic%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22Quadratic%20complexity%22%22%22%0A%20%20%20%20%23%20The%20two-dimensional%20list%20occupies%20O%28n%5E2%29%20space%0A%20%20%20%20num_matrix%20%3D%20%5B%5B0%5D%20%2A%20n%20for%20_%20in%20range%28n%29%5D%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20%23%20Quadratic%20complexity%0A%20%20%20%20quadratic%28n%29&cumulative=false&curInstr=16&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20quadratic_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Quadratic%20complexity%20%28recursive%20implementation%29%22%22%22%0A%20%20%20%20if%20n%20%3C%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20%23%20The%20lengths%20of%20array%20nums%20are%20n,%20n-1,%20...,%202,%201%0A%20%20%20%20nums%20%3D%20%5B0%5D%20%2A%20n%0A%20%20%20%20return%20quadratic_recur%28n%20-%201%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20%23%20Quadratic%20complexity%0A%20%20%20%20quadratic_recur%28n%29&cumulative=false&curInstr=28&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%20%3D%200%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20build_tree%28n%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Exponential%20complexity%20%28building%20a%20perfect%20binary%20tree%29%22%22%22%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20root%20%3D%20TreeNode%280%29%0A%20%20%20%20root.left%20%3D%20build_tree%28n%20-%201%29%0A%20%20%20%20root.right%20%3D%20build_tree%28n%20-%201%29%0A%20%20%20%20return%20root%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20%23%20Exponential%20complexity%0A%20%20%20%20root%20%3D%20build_tree%28n%29&cumulative=false&curInstr=507&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_computational_complexity/time_complexity.md b/en/codes/pythontutor/chapter_computational_complexity/time_complexity.md new file mode 100644 index 000000000..5c775b829 --- /dev/null +++ b/en/codes/pythontutor/chapter_computational_complexity/time_complexity.md @@ -0,0 +1,38 @@ + + + +https://pythontutor.com/render.html#code=def%20constant%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Constant%20complexity%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20size%20%3D%2010%0A%20%20%20%20for%20_%20in%20range%28size%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20constant%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20constant%20complexity%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20linear%28n%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Linear%20complexity%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20linear%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20linear%20complexity%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20array_traversal%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Linear%20complexity%20%28array%20traversal%29%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20The%20number%20of%20iterations%20is%20proportional%20to%20the%20array%20length%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20array_traversal%28%5B0%5D%20%2A%20n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20linear%20complexity%20%28array%20traversal%29%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20quadratic%28n%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Quadratic%20complexity%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20The%20number%20of%20iterations%20is%20proportional%20to%20the%20square%20of%20the%20input%20size%20n%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20quadratic%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20quadratic%20complexity%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20bubble_sort%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Quadratic%20complexity%20%28bubble%20sort%29%22%22%22%0A%20%20%20%20count%20%3D%200%20%20%23%20Counter%0A%20%20%20%20%23%20Outer%20loop%3A%20the%20unsorted%20range%20is%20%5B0,%20i%5D%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20Inner%20loop%3A%20move%20the%20largest%20element%20in%20the%20unsorted%20range%20%5B0,%20i%5D%20to%20its%20right%20end%20through%20swaps%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Swap%20nums%5Bj%5D%20and%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tmp%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D%20%3D%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20tmp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%20%2B%3D%203%20%20%23%20Swapping%20two%20elements%20takes%203%20elementary%20operations%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20nums%20%3D%20%5Bi%20for%20i%20in%20range%28n,%200,%20-1%29%5D%20%20%23%20%5Bn,%20n-1,%20...,%202,%201%5D%0A%20%20%20%20count%20%3D%20bubble_sort%28nums%29%0A%20%20%20%20print%28%22Operation%20count%20for%20quadratic%20complexity%20%28bubble%20sort%29%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20exponential%28n%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Exponential%20complexity%20%28iterative%20implementation%29%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20base%20%3D%201%0A%20%20%20%20%23%20Each%20cell%20divides%20into%20two%20per%20round,%20giving%20the%20sequence%201,%202,%204,%208,%20...,%202%5E%28n-1%29%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20for%20_%20in%20range%28base%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20%20%20%20%20base%20%2A%3D%202%0A%20%20%20%20%23%20count%20%3D%201%20%2B%202%20%2B%204%20%2B%208%20%2B%20..%20%2B%202%5E%28n-1%29%20%3D%202%5En%20-%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20exponential%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20exponential%20complexity%20%28iterative%20implementation%29%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20exp_recur%28n%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Exponential%20complexity%20%28recursive%20implementation%29%22%22%22%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20exp_recur%28n%20-%201%29%20%2B%20exp_recur%28n%20-%201%29%20%2B%201%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%207%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20exp_recur%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20exponential%20complexity%20%28recursive%20implementation%29%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20logarithmic%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Logarithmic%20complexity%20%28iterative%20implementation%29%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20while%20n%20%3E%201%3A%0A%20%20%20%20%20%20%20%20n%20%3D%20n%20/%202%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20logarithmic%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20logarithmic%20complexity%20%28iterative%20implementation%29%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20log_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Logarithmic%20complexity%20%28recursive%20implementation%29%22%22%22%0A%20%20%20%20if%20n%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20return%20log_recur%28n%20/%202%29%20%2B%201%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20log_recur%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20logarithmic%20complexity%20%28recursive%20implementation%29%20%3D%22,%20count%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20linear_log_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Linearithmic%20complexity%22%22%22%0A%20%20%20%20if%20n%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20count%20%3D%20linear_log_recur%28n%20//%202%29%20%2B%20linear_log_recur%28n%20//%202%29%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20linear_log_recur%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20linearithmic%20complexity%20%28recursive%20implementation%29%20%3D%22,%20count%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20factorial_recur%28n%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Factorial%20complexity%20%28recursive%20implementation%29%22%22%22%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20Split%20one%20problem%20into%20n%20subproblems%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20factorial_recur%28n%20-%201%29%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%204%0A%20%20%20%20print%28%22Input%20size%20n%20%3D%22,%20n%29%0A%0A%20%20%20%20count%20%3D%20factorial_recur%28n%29%0A%20%20%20%20print%28%22Operation%20count%20for%20factorial%20complexity%20%28recursive%20implementation%29%20%3D%22,%20count%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_computational_complexity/worst_best_time_complexity.md b/en/codes/pythontutor/chapter_computational_complexity/worst_best_time_complexity.md new file mode 100644 index 000000000..55620fa5b --- /dev/null +++ b/en/codes/pythontutor/chapter_computational_complexity/worst_best_time_complexity.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=import%20random%0A%0Adef%20random_numbers%28n%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22Generate%20an%20array%20containing%201,%202,%20...,%20n%20in%20shuffled%20order%22%22%22%0A%20%20%20%20%23%20Generate%20the%20array%20nums%20%3D%201,%202,%203,%20...,%20n%0A%20%20%20%20nums%20%3D%20%5Bi%20for%20i%20in%20range%281,%20n%20%2B%201%29%5D%0A%20%20%20%20%23%20Randomly%20shuffle%20the%20array%20elements%0A%20%20%20%20random.shuffle%28nums%29%0A%20%20%20%20return%20nums%0A%0Adef%20find_one%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20the%20index%20of%20the%20number%201%20in%20array%20nums%22%22%22%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20When%201%20is%20at%20the%20start%20of%20the%20array,%20the%20best-case%20time%20complexity%20is%20O%281%29%0A%20%20%20%20%20%20%20%20%23%20When%201%20is%20at%20the%20end%20of%20the%20array,%20the%20worst-case%20time%20complexity%20is%20O%28n%29%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20i%0A%20%20%20%20return%20-1%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2010%0A%20%20%20%20nums%20%3D%20random_numbers%28n%29%0A%20%20%20%20index%20%3D%20find_one%28nums%29%0A%20%20%20%20print%28%22%5CnArray%20%5B%201,%202,%20...,%20n%20%5D%20after%20shuffling%20%3D%22,%20nums%29%0A%20%20%20%20print%28%22The%20index%20of%20the%20number%201%20is%22,%20index%29&cumulative=false&curInstr=25&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_divide_and_conquer/binary_search_recur.md b/en/codes/pythontutor/chapter_divide_and_conquer/binary_search_recur.md new file mode 100644 index 000000000..718205ec3 --- /dev/null +++ b/en/codes/pythontutor/chapter_divide_and_conquer/binary_search_recur.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20dfs%28nums%3A%20list%5Bint%5D,%20target%3A%20int,%20i%3A%20int,%20j%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Binary%20search%3A%20problem%20f%28i,%20j%29%22%22%22%0A%20%20%20%20%23%20If%20the%20interval%20is%20empty,%20the%20target%20is%20absent%3B%20return%20-1%0A%20%20%20%20if%20i%20%3E%20j%3A%0A%20%20%20%20%20%20%20%20return%20-1%0A%20%20%20%20%23%20Calculate%20the%20midpoint%20index%20m%0A%20%20%20%20m%20%3D%20%28i%20%2B%20j%29%20//%202%0A%20%20%20%20if%20nums%5Bm%5D%20%3C%20target%3A%0A%20%20%20%20%20%20%20%20%23%20Recurse%20on%20subproblem%20f%28m%2B1,%20j%29%0A%20%20%20%20%20%20%20%20return%20dfs%28nums,%20target,%20m%20%2B%201,%20j%29%0A%20%20%20%20elif%20nums%5Bm%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%23%20Recurse%20on%20subproblem%20f%28i,%20m-1%29%0A%20%20%20%20%20%20%20%20return%20dfs%28nums,%20target,%20i,%20m%20-%201%29%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%23%20The%20target%20element%20was%20found%3B%20return%20its%20index%0A%20%20%20%20%20%20%20%20return%20m%0A%0Adef%20binary_search%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Binary%20search%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20Solve%20problem%20f%280,%20n-1%29%0A%20%20%20%20return%20dfs%28nums,%20target,%200,%20n%20-%201%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20target%20%3D%206%0A%20%20%20%20nums%20%3D%20%5B1,%203,%206,%208,%2012,%2015,%2023,%2026,%2031,%2035%5D%0A%0A%20%20%20%20%23%20Binary%20search%20%28closed%20interval%29%0A%20%20%20%20index%20%3D%20binary_search%28nums,%20target%29%0A%20%20%20%20print%28%22Index%20of%20target%20element%206%20%3D%20%22,%20index%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_divide_and_conquer/build_tree.md b/en/codes/pythontutor/chapter_divide_and_conquer/build_tree.md new file mode 100644 index 000000000..96d6d0702 --- /dev/null +++ b/en/codes/pythontutor/chapter_divide_and_conquer/build_tree.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%20%3D%200%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20dfs%28%0A%20%20%20%20preorder%3A%20list%5Bint%5D,%0A%20%20%20%20inorder_map%3A%20dict%5Bint,%20int%5D,%0A%20%20%20%20i%3A%20int,%0A%20%20%20%20l%3A%20int,%0A%20%20%20%20r%3A%20int,%0A%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Build%20a%20binary%20tree%3A%20divide%20and%20conquer%22%22%22%0A%20%20%20%20%23%20Stop%20when%20the%20subtree%20interval%20is%20empty%0A%20%20%20%20if%20r%20-%20l%20%3C%200%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%23%20Initialize%20the%20root%20node%0A%20%20%20%20root%20%3D%20TreeNode%28preorder%5Bi%5D%29%0A%20%20%20%20%23%20Look%20up%20m%20to%20separate%20the%20left%20and%20right%20subtrees%0A%20%20%20%20m%20%3D%20inorder_map%5Bpreorder%5Bi%5D%5D%0A%20%20%20%20%23%20Subproblem%3A%20build%20the%20left%20subtree%0A%20%20%20%20root.left%20%3D%20dfs%28preorder,%20inorder_map,%20i%20%2B%201,%20l,%20m%20-%201%29%0A%20%20%20%20%23%20Subproblem%3A%20build%20the%20right%20subtree%0A%20%20%20%20root.right%20%3D%20dfs%28preorder,%20inorder_map,%20i%20%2B%201%20%2B%20m%20-%20l,%20m%20%2B%201,%20r%29%0A%20%20%20%20%23%20Return%20the%20root%20node%0A%20%20%20%20return%20root%0A%0A%0Adef%20build_tree%28preorder%3A%20list%5Bint%5D,%20inorder%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Build%20a%20binary%20tree%22%22%22%0A%20%20%20%20%23%20Initialize%20a%20hash%20table%20mapping%20each%20element%20in%20inorder%20to%20its%20index%0A%20%20%20%20inorder_map%20%3D%20%7Bval%3A%20i%20for%20i,%20val%20in%20enumerate%28inorder%29%7D%0A%20%20%20%20root%20%3D%20dfs%28preorder,%20inorder_map,%200,%200,%20len%28inorder%29%20-%201%29%0A%20%20%20%20return%20root%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20preorder%20%3D%20%5B3,%209,%202,%201,%207%5D%0A%20%20%20%20inorder%20%3D%20%5B9,%203,%201,%202,%207%5D%0A%20%20%20%20print%28f%22Preorder%20traversal%20%3D%20%7Bpreorder%7D%22%29%0A%20%20%20%20print%28f%22Inorder%20traversal%20%3D%20%7Binorder%7D%22%29%0A%20%20%20%20root%20%3D%20build_tree%28preorder,%20inorder%29&cumulative=false&curInstr=21&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_divide_and_conquer/hanota.md b/en/codes/pythontutor/chapter_divide_and_conquer/hanota.md new file mode 100644 index 000000000..4778ed352 --- /dev/null +++ b/en/codes/pythontutor/chapter_divide_and_conquer/hanota.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20move%28src%3A%20list%5Bint%5D,%20tar%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Move%20one%20disk%22%22%22%0A%20%20%20%20%23%20Take%20a%20disk%20from%20the%20top%20of%20src%0A%20%20%20%20pan%20%3D%20src.pop%28%29%0A%20%20%20%20%23%20Place%20the%20disk%20on%20top%20of%20tar%0A%20%20%20%20tar.append%28pan%29%0A%0A%0Adef%20dfs%28i%3A%20int,%20src%3A%20list%5Bint%5D,%20buf%3A%20list%5Bint%5D,%20tar%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Solve%20the%20Tower%20of%20Hanoi%20problem%20f%28i%29%22%22%22%0A%20%20%20%20%23%20If%20the%20current%20subproblem%20has%20only%20one%20disk,%20move%20it%20from%20src%20directly%20to%20tar%0A%20%20%20%20if%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20move%28src,%20tar%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Subproblem%20f%28i-1%29%3A%20move%20the%20top%20i-1%20disks%20from%20src%20to%20buf%20using%20tar%0A%20%20%20%20dfs%28i%20-%201,%20src,%20tar,%20buf%29%0A%20%20%20%20%23%20Subproblem%20f%281%29%3A%20move%20the%20remaining%20disk%20of%20this%20subproblem%20from%20src%20to%20tar%0A%20%20%20%20move%28src,%20tar%29%0A%20%20%20%20%23%20Subproblem%20f%28i-1%29%3A%20move%20the%20top%20i-1%20disks%20from%20buf%20to%20tar%20using%20src%0A%20%20%20%20dfs%28i%20-%201,%20buf,%20src,%20tar%29%0A%0A%0Adef%20solve_hanota%28A%3A%20list%5Bint%5D,%20B%3A%20list%5Bint%5D,%20C%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Solve%20the%20Tower%20of%20Hanoi%20problem%22%22%22%0A%20%20%20%20n%20%3D%20len%28A%29%0A%20%20%20%20%23%20Move%20the%20top%20n%20disks%20from%20A%20to%20C%20using%20B%0A%20%20%20%20dfs%28n,%20A,%20B,%20C%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20The%20end%20of%20each%20list%20represents%20the%20top%20of%20the%20peg%0A%20%20%20%20A%20%3D%20%5B5,%204,%203,%202,%201%5D%0A%20%20%20%20B%20%3D%20%5B%5D%0A%20%20%20%20C%20%3D%20%5B%5D%0A%20%20%20%20print%28%22Initial%20state%3A%22%29%0A%20%20%20%20print%28f%22A%20%3D%20%7BA%7D%22%29%0A%20%20%20%20print%28f%22B%20%3D%20%7BB%7D%22%29%0A%20%20%20%20print%28f%22C%20%3D%20%7BC%7D%22%29%0A%0A%20%20%20%20solve_hanota%28A,%20B,%20C%29%0A%0A%20%20%20%20print%28%22After%20moving%20the%20disks%3A%22%29%0A%20%20%20%20print%28f%22A%20%3D%20%7BA%7D%22%29%0A%20%20%20%20print%28f%22B%20%3D%20%7BB%7D%22%29%0A%20%20%20%20print%28f%22C%20%3D%20%7BC%7D%22%29&cumulative=false&curInstr=12&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_backtrack.md b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_backtrack.md new file mode 100644 index 000000000..8e15d6bb4 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_backtrack.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20backtrack%28choices%3A%20list%5Bint%5D,%20state%3A%20int,%20n%3A%20int,%20res%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Backtracking%22%22%22%0A%20%20%20%20%23%20When%20step%20n%20is%20reached,%20increase%20the%20number%20of%20ways%20by%201%0A%20%20%20%20if%20state%20%3D%3D%20n%3A%0A%20%20%20%20%20%20%20%20res%5B0%5D%20%2B%3D%201%0A%20%20%20%20%23%20Iterate%20over%20all%20choices%0A%20%20%20%20for%20choice%20in%20choices%3A%0A%20%20%20%20%20%20%20%20%23%20Prune%3A%20do%20not%20go%20beyond%20step%20n%0A%20%20%20%20%20%20%20%20if%20state%20%2B%20choice%20%3E%20n%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%20%20%23%20Try%3A%20make%20a%20choice%20and%20update%20the%20state%0A%20%20%20%20%20%20%20%20backtrack%28choices,%20state%20%2B%20choice,%20n,%20res%29%0A%20%20%20%20%20%20%20%20%23%20Backtrack%0A%0A%0Adef%20climbing_stairs_backtrack%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Climbing%20stairs%3A%20backtracking%22%22%22%0A%20%20%20%20choices%20%3D%20%5B1,%202%5D%20%20%23%20Choose%20to%20climb%201%20or%202%20steps%0A%20%20%20%20state%20%3D%200%20%20%23%20Start%20climbing%20from%20step%200%0A%20%20%20%20res%20%3D%20%5B0%5D%20%20%23%20Use%20res%5B0%5D%20to%20record%20the%20number%20of%20ways%0A%20%20%20%20backtrack%28choices,%20state,%20n,%20res%29%0A%20%20%20%20return%20res%5B0%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%204%0A%0A%20%20%20%20res%20%3D%20climbing_stairs_backtrack%28n%29%0A%20%20%20%20print%28f%22Number%20of%20ways%20to%20climb%20%7Bn%7D%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_constraint_dp.md b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_constraint_dp.md new file mode 100644 index 000000000..839137ed3 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_constraint_dp.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20climbing_stairs_constraint_dp%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Climbing%20stairs%20with%20constraints%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20if%20n%20%3D%3D%201%20or%20n%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%20to%20store%20subproblem%20solutions%0A%20%20%20%20dp%20%3D%20%5B%5B0%5D%20%2A%203%20for%20_%20in%20range%28n%20%2B%201%29%5D%0A%20%20%20%20%23%20Initial%20states%3A%20set%20the%20solutions%20to%20the%20smallest%20subproblems%0A%20%20%20%20dp%5B1%5D%5B1%5D,%20dp%5B1%5D%5B2%5D%20%3D%201,%200%0A%20%20%20%20dp%5B2%5D%5B1%5D,%20dp%5B2%5D%5B2%5D%20%3D%200,%201%0A%20%20%20%20%23%20State%20transitions%3A%20solve%20progressively%20larger%20subproblems%20using%20smaller%20ones%0A%20%20%20%20for%20i%20in%20range%283,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bi%5D%5B1%5D%20%3D%20dp%5Bi%20-%201%5D%5B2%5D%0A%20%20%20%20%20%20%20%20dp%5Bi%5D%5B2%5D%20%3D%20dp%5Bi%20-%202%5D%5B1%5D%20%2B%20dp%5Bi%20-%202%5D%5B2%5D%0A%20%20%20%20return%20dp%5Bn%5D%5B1%5D%20%2B%20dp%5Bn%5D%5B2%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%209%0A%0A%20%20%20%20res%20%3D%20climbing_stairs_constraint_dp%28n%29%0A%20%20%20%20print%28f%22Number%20of%20ways%20to%20climb%20%7Bn%7D%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dfs.md b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dfs.md new file mode 100644 index 000000000..357b83e60 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dfs.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20dfs%28i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Search%22%22%22%0A%20%20%20%20%23%20The%20values%20of%20dp%5B1%5D%20and%20dp%5B2%5D%20are%20known%3B%20return%20the%20corresponding%20value%0A%20%20%20%20if%20i%20%3D%3D%201%20or%20i%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20i%0A%20%20%20%20%23%20dp%5Bi%5D%20%3D%20dp%5Bi-1%5D%20%2B%20dp%5Bi-2%5D%0A%20%20%20%20count%20%3D%20dfs%28i%20-%201%29%20%2B%20dfs%28i%20-%202%29%0A%20%20%20%20return%20count%0A%0A%0Adef%20climbing_stairs_dfs%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Climbing%20stairs%3A%20search%22%22%22%0A%20%20%20%20return%20dfs%28n%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%209%0A%0A%20%20%20%20res%20%3D%20climbing_stairs_dfs%28n%29%0A%20%20%20%20print%28f%22Number%20of%20ways%20to%20climb%20%7Bn%7D%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dfs_mem.md b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dfs_mem.md new file mode 100644 index 000000000..fcd4d46b0 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dfs_mem.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20dfs%28i%3A%20int,%20mem%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Memoized%20search%22%22%22%0A%20%20%20%20%23%20The%20values%20of%20dp%5B1%5D%20and%20dp%5B2%5D%20are%20known%3B%20return%20the%20corresponding%20value%0A%20%20%20%20if%20i%20%3D%3D%201%20or%20i%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20i%0A%20%20%20%20%23%20If%20dp%5Bi%5D%20has%20been%20recorded,%20return%20it%20directly%0A%20%20%20%20if%20mem%5Bi%5D%20%21%3D%20-1%3A%0A%20%20%20%20%20%20%20%20return%20mem%5Bi%5D%0A%20%20%20%20%23%20dp%5Bi%5D%20%3D%20dp%5Bi-1%5D%20%2B%20dp%5Bi-2%5D%0A%20%20%20%20count%20%3D%20dfs%28i%20-%201,%20mem%29%20%2B%20dfs%28i%20-%202,%20mem%29%0A%20%20%20%20%23%20Record%20dp%5Bi%5D%0A%20%20%20%20mem%5Bi%5D%20%3D%20count%0A%20%20%20%20return%20count%0A%0A%0Adef%20climbing_stairs_dfs_mem%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Climbing%20stairs%3A%20memoized%20search%22%22%22%0A%20%20%20%20%23%20mem%5Bi%5D%20stores%20the%20number%20of%20ways%20to%20reach%20step%20i%3B%20-1%20means%20no%20result%20has%20been%20recorded%0A%20%20%20%20mem%20%3D%20%5B-1%5D%20%2A%20%28n%20%2B%201%29%0A%20%20%20%20return%20dfs%28n,%20mem%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%209%0A%0A%20%20%20%20res%20%3D%20climbing_stairs_dfs_mem%28n%29%0A%20%20%20%20print%28f%22Number%20of%20ways%20to%20climb%20%7Bn%7D%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dp.md b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dp.md new file mode 100644 index 000000000..66c17e8ea --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/climbing_stairs_dp.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20climbing_stairs_dp%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Climbing%20stairs%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20if%20n%20%3D%3D%201%20or%20n%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20n%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%20to%20store%20subproblem%20solutions%0A%20%20%20%20dp%20%3D%20%5B0%5D%20%2A%20%28n%20%2B%201%29%0A%20%20%20%20%23%20Initial%20states%3A%20set%20the%20solutions%20to%20the%20smallest%20subproblems%0A%20%20%20%20dp%5B1%5D,%20dp%5B2%5D%20%3D%201,%202%0A%20%20%20%20%23%20State%20transitions%3A%20solve%20progressively%20larger%20subproblems%20using%20smaller%20ones%0A%20%20%20%20for%20i%20in%20range%283,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bi%5D%20%3D%20dp%5Bi%20-%201%5D%20%2B%20dp%5Bi%20-%202%5D%0A%20%20%20%20return%20dp%5Bn%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%209%0A%0A%20%20%20%20res%20%3D%20climbing_stairs_dp%28n%29%0A%20%20%20%20print%28f%22Number%20of%20ways%20to%20climb%20%7Bn%7D%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20climbing_stairs_dp_comp%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Climbing%20stairs%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20if%20n%20%3D%3D%201%20or%20n%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20n%0A%20%20%20%20a,%20b%20%3D%201,%202%0A%20%20%20%20for%20_%20in%20range%283,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20a,%20b%20%3D%20b,%20a%20%2B%20b%0A%20%20%20%20return%20b%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%209%0A%0A%20%20%20%20res%20%3D%20climbing_stairs_dp_comp%28n%29%0A%20%20%20%20print%28f%22Number%20of%20ways%20to%20climb%20%7Bn%7D%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/coin_change.md b/en/codes/pythontutor/chapter_dynamic_programming/coin_change.md new file mode 100644 index 000000000..6da555a0c --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/coin_change.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20coin_change_dp%28coins%3A%20list%5Bint%5D,%20amt%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Coin%20change%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28coins%29%0A%20%20%20%20MAX%20%3D%20amt%20%2B%201%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B%5B0%5D%20%2A%20%28amt%20%2B%201%29%20for%20_%20in%20range%28n%20%2B%201%29%5D%0A%20%20%20%20%23%20State%20transitions%3A%20first%20row%20and%20first%20column%0A%20%20%20%20for%20a%20in%20range%281,%20amt%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5B0%5D%5Ba%5D%20%3D%20MAX%0A%20%20%20%20%23%20State%20transitions%3A%20remaining%20rows%20and%20columns%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20a%20in%20range%281,%20amt%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20coins%5Bi%20-%201%5D%20%3E%20a%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20the%20coin%20value%20exceeds%20the%20current%20amount%20a,%20do%20not%20choose%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Ba%5D%20%3D%20dp%5Bi%20-%201%5D%5Ba%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Take%20the%20smaller%20result%20of%20not%20choosing%20and%20choosing%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Ba%5D%20%3D%20min%28dp%5Bi%20-%201%5D%5Ba%5D,%20dp%5Bi%5D%5Ba%20-%20coins%5Bi%20-%201%5D%5D%20%2B%201%29%0A%20%20%20%20return%20dp%5Bn%5D%5Bamt%5D%20if%20dp%5Bn%5D%5Bamt%5D%20%21%3D%20MAX%20else%20-1%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20coins%20%3D%20%5B1,%202,%205%5D%0A%20%20%20%20amt%20%3D%204%0A%0A%20%20%20%20%23%20Dynamic%20programming%0A%20%20%20%20res%20%3D%20coin_change_dp%28coins,%20amt%29%0A%20%20%20%20print%28f%22Minimum%20number%20of%20coins%20needed%20to%20make%20the%20target%20amount%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20coin_change_dp_comp%28coins%3A%20list%5Bint%5D,%20amt%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Coin%20change%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28coins%29%0A%20%20%20%20MAX%20%3D%20amt%20%2B%201%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5BMAX%5D%20%2A%20%28amt%20%2B%201%29%0A%20%20%20%20dp%5B0%5D%20%3D%200%0A%20%20%20%20%23%20State%20transitions%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20Traverse%20in%20increasing%20order%0A%20%20%20%20%20%20%20%20for%20a%20in%20range%281,%20amt%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20coins%5Bi%20-%201%5D%20%3E%20a%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20the%20coin%20value%20exceeds%20the%20current%20amount%20a,%20do%20not%20choose%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Ba%5D%20%3D%20dp%5Ba%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Take%20the%20smaller%20result%20of%20not%20choosing%20and%20choosing%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Ba%5D%20%3D%20min%28dp%5Ba%5D,%20dp%5Ba%20-%20coins%5Bi%20-%201%5D%5D%20%2B%201%29%0A%20%20%20%20return%20dp%5Bamt%5D%20if%20dp%5Bamt%5D%20%21%3D%20MAX%20else%20-1%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20coins%20%3D%20%5B1,%202,%205%5D%0A%20%20%20%20amt%20%3D%204%0A%0A%20%20%20%20%23%20Space-optimized%20dynamic%20programming%0A%20%20%20%20res%20%3D%20coin_change_dp_comp%28coins,%20amt%29%0A%20%20%20%20print%28f%22Minimum%20number%20of%20coins%20needed%20to%20make%20the%20target%20amount%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/coin_change_ii.md b/en/codes/pythontutor/chapter_dynamic_programming/coin_change_ii.md new file mode 100644 index 000000000..adb7b181f --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/coin_change_ii.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20coin_change_ii_dp%28coins%3A%20list%5Bint%5D,%20amt%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Coin%20change%20II%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28coins%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B%5B0%5D%20%2A%20%28amt%20%2B%201%29%20for%20_%20in%20range%28n%20%2B%201%29%5D%0A%20%20%20%20%23%20Initialize%20the%20first%20column%0A%20%20%20%20for%20i%20in%20range%28n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bi%5D%5B0%5D%20%3D%201%0A%20%20%20%20%23%20State%20transitions%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20a%20in%20range%281,%20amt%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20coins%5Bi%20-%201%5D%20%3E%20a%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20the%20coin%20value%20exceeds%20the%20current%20amount%20a,%20do%20not%20choose%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Ba%5D%20%3D%20dp%5Bi%20-%201%5D%5Ba%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Add%20the%20numbers%20of%20ways%20without%20and%20with%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Ba%5D%20%3D%20dp%5Bi%20-%201%5D%5Ba%5D%20%2B%20dp%5Bi%5D%5Ba%20-%20coins%5Bi%20-%201%5D%5D%0A%20%20%20%20return%20dp%5Bn%5D%5Bamt%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20coins%20%3D%20%5B1,%202,%205%5D%0A%20%20%20%20amt%20%3D%205%0A%0A%20%20%20%20%23%20Dynamic%20programming%0A%20%20%20%20res%20%3D%20coin_change_ii_dp%28coins,%20amt%29%0A%20%20%20%20print%28f%22Number%20of%20coin%20combinations%20that%20make%20the%20target%20amount%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20coin_change_ii_dp_comp%28coins%3A%20list%5Bint%5D,%20amt%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Coin%20change%20II%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28coins%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B0%5D%20%2A%20%28amt%20%2B%201%29%0A%20%20%20%20dp%5B0%5D%20%3D%201%0A%20%20%20%20%23%20State%20transitions%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20Traverse%20in%20increasing%20order%0A%20%20%20%20%20%20%20%20for%20a%20in%20range%281,%20amt%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20coins%5Bi%20-%201%5D%20%3E%20a%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20the%20coin%20value%20exceeds%20the%20current%20amount%20a,%20do%20not%20choose%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Ba%5D%20%3D%20dp%5Ba%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Add%20the%20numbers%20of%20ways%20without%20and%20with%20coin%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Ba%5D%20%3D%20dp%5Ba%5D%20%2B%20dp%5Ba%20-%20coins%5Bi%20-%201%5D%5D%0A%20%20%20%20return%20dp%5Bamt%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20coins%20%3D%20%5B1,%202,%205%5D%0A%20%20%20%20amt%20%3D%205%0A%0A%20%20%20%20%23%20Space-optimized%20dynamic%20programming%0A%20%20%20%20res%20%3D%20coin_change_ii_dp_comp%28coins,%20amt%29%0A%20%20%20%20print%28f%22Number%20of%20coin%20combinations%20that%20make%20the%20target%20amount%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/edit_distance.md b/en/codes/pythontutor/chapter_dynamic_programming/edit_distance.md new file mode 100644 index 000000000..eb36b8657 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/edit_distance.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20edit_distance_dp%28s%3A%20str,%20t%3A%20str%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Edit%20distance%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20n,%20m%20%3D%20len%28s%29,%20len%28t%29%0A%20%20%20%20dp%20%3D%20%5B%5B0%5D%20%2A%20%28m%20%2B%201%29%20for%20_%20in%20range%28n%20%2B%201%29%5D%0A%20%20%20%20%23%20State%20transitions%3A%20first%20row%20and%20first%20column%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bi%5D%5B0%5D%20%3D%20i%0A%20%20%20%20for%20j%20in%20range%281,%20m%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5B0%5D%5Bj%5D%20%3D%20j%0A%20%20%20%20%23%20State%20transitions%3A%20remaining%20rows%20and%20columns%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%281,%20m%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20s%5Bi%20-%201%5D%20%3D%3D%20t%5Bj%20-%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20the%20two%20characters%20are%20equal,%20skip%20both%20of%20them%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Bj%5D%20%3D%20dp%5Bi%20-%201%5D%5Bj%20-%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Minimum%20edits%20%3D%20the%20minimum%20among%20the%20insertion,%20deletion,%20and%20replacement%20subproblems%20%2B%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Bj%5D%20%3D%20min%28dp%5Bi%5D%5Bj%20-%201%5D,%20dp%5Bi%20-%201%5D%5Bj%5D,%20dp%5Bi%20-%201%5D%5Bj%20-%201%5D%29%20%2B%201%0A%20%20%20%20return%20dp%5Bn%5D%5Bm%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20s%20%3D%20%22bag%22%0A%20%20%20%20t%20%3D%20%22pack%22%0A%20%20%20%20n,%20m%20%3D%20len%28s%29,%20len%28t%29%0A%0A%20%20%20%20%23%20Dynamic%20programming%0A%20%20%20%20res%20%3D%20edit_distance_dp%28s,%20t%29%0A%20%20%20%20print%28f%22Changing%20%7Bs%7D%20to%20%7Bt%7D%20requires%20at%20least%20%7Bres%7D%20edits%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20edit_distance_dp_comp%28s%3A%20str,%20t%3A%20str%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Edit%20distance%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20n,%20m%20%3D%20len%28s%29,%20len%28t%29%0A%20%20%20%20dp%20%3D%20%5B0%5D%20%2A%20%28m%20%2B%201%29%0A%20%20%20%20%23%20State%20transitions%3A%20first%20row%0A%20%20%20%20for%20j%20in%20range%281,%20m%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bj%5D%20%3D%20j%0A%20%20%20%20%23%20State%20transitions%3A%20remaining%20rows%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20State%20transitions%3A%20first%20column%0A%20%20%20%20%20%20%20%20leftup%20%3D%20dp%5B0%5D%20%20%23%20Temporarily%20store%20dp%5Bi-1,%20j-1%5D%0A%20%20%20%20%20%20%20%20dp%5B0%5D%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%23%20State%20transitions%3A%20remaining%20columns%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%281,%20m%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20temp%20%3D%20dp%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20s%5Bi%20-%201%5D%20%3D%3D%20t%5Bj%20-%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20the%20two%20characters%20are%20equal,%20skip%20both%20of%20them%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bj%5D%20%3D%20leftup%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Minimum%20edits%20%3D%20the%20minimum%20among%20the%20insertion,%20deletion,%20and%20replacement%20subproblems%20%2B%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bj%5D%20%3D%20min%28dp%5Bj%20-%201%5D,%20dp%5Bj%5D,%20leftup%29%20%2B%201%0A%20%20%20%20%20%20%20%20%20%20%20%20leftup%20%3D%20temp%20%20%23%20Update%20to%20dp%5Bi-1,%20j-1%5D%20for%20the%20next%20iteration%0A%20%20%20%20return%20dp%5Bm%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20s%20%3D%20%22bag%22%0A%20%20%20%20t%20%3D%20%22pack%22%0A%20%20%20%20n,%20m%20%3D%20len%28s%29,%20len%28t%29%0A%0A%20%20%20%20%23%20Space-optimized%20dynamic%20programming%0A%20%20%20%20res%20%3D%20edit_distance_dp_comp%28s,%20t%29%0A%20%20%20%20print%28f%22Changing%20%7Bs%7D%20to%20%7Bt%7D%20requires%20at%20least%20%7Bres%7D%20edits%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/knapsack.md b/en/codes/pythontutor/chapter_dynamic_programming/knapsack.md new file mode 100644 index 000000000..90351f2bd --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/knapsack.md @@ -0,0 +1,17 @@ + + + +https://pythontutor.com/render.html#code=def%20knapsack_dfs%28wgt%3A%20list%5Bint%5D,%20val%3A%20list%5Bint%5D,%20i%3A%20int,%20c%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%220-1%20knapsack%3A%20brute-force%20search%22%22%22%0A%20%20%20%20%23%20If%20all%20items%20have%20been%20considered%20or%20no%20capacity%20remains,%20return%20value%200%0A%20%20%20%20if%20i%20%3D%3D%200%20or%20c%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20%23%20If%20the%20item%20exceeds%20the%20remaining%20capacity,%20it%20must%20be%20excluded%0A%20%20%20%20if%20wgt%5Bi%20-%201%5D%20%3E%20c%3A%0A%20%20%20%20%20%20%20%20return%20knapsack_dfs%28wgt,%20val,%20i%20-%201,%20c%29%0A%20%20%20%20%23%20Calculate%20the%20maximum%20values%20obtained%20by%20excluding%20and%20including%20item%20i%0A%20%20%20%20no%20%3D%20knapsack_dfs%28wgt,%20val,%20i%20-%201,%20c%29%0A%20%20%20%20yes%20%3D%20knapsack_dfs%28wgt,%20val,%20i%20-%201,%20c%20-%20wgt%5Bi%20-%201%5D%29%20%2B%20val%5Bi%20-%201%5D%0A%20%20%20%20%23%20Return%20the%20larger%20value%20from%20the%20two%20choices%0A%20%20%20%20return%20max%28no,%20yes%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10,%2020,%2030,%2040,%2050%5D%0A%20%20%20%20val%20%3D%20%5B50,%20120,%20150,%20210,%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20Brute-force%20search%0A%20%20%20%20res%20%3D%20knapsack_dfs%28wgt,%20val,%20n,%20cap%29%0A%20%20%20%20print%28f%22Maximum%20total%20value%20within%20the%20knapsack%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=7&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20knapsack_dfs_mem%28%0A%20%20%20%20wgt%3A%20list%5Bint%5D,%20val%3A%20list%5Bint%5D,%20mem%3A%20list%5Blist%5Bint%5D%5D,%20i%3A%20int,%20c%3A%20int%0A%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%220-1%20knapsack%3A%20memoized%20search%22%22%22%0A%20%20%20%20%23%20If%20all%20items%20have%20been%20considered%20or%20no%20capacity%20remains,%20return%20value%200%0A%20%20%20%20if%20i%20%3D%3D%200%20or%20c%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20%23%20If%20a%20result%20has%20already%20been%20recorded,%20return%20it%20directly%0A%20%20%20%20if%20mem%5Bi%5D%5Bc%5D%20%21%3D%20-1%3A%0A%20%20%20%20%20%20%20%20return%20mem%5Bi%5D%5Bc%5D%0A%20%20%20%20%23%20If%20the%20item%20exceeds%20the%20remaining%20capacity,%20it%20must%20be%20excluded%0A%20%20%20%20if%20wgt%5Bi%20-%201%5D%20%3E%20c%3A%0A%20%20%20%20%20%20%20%20return%20knapsack_dfs_mem%28wgt,%20val,%20mem,%20i%20-%201,%20c%29%0A%20%20%20%20%23%20Calculate%20the%20maximum%20values%20obtained%20by%20excluding%20and%20including%20item%20i%0A%20%20%20%20no%20%3D%20knapsack_dfs_mem%28wgt,%20val,%20mem,%20i%20-%201,%20c%29%0A%20%20%20%20yes%20%3D%20knapsack_dfs_mem%28wgt,%20val,%20mem,%20i%20-%201,%20c%20-%20wgt%5Bi%20-%201%5D%29%20%2B%20val%5Bi%20-%201%5D%0A%20%20%20%20%23%20Record%20and%20return%20the%20larger%20value%20from%20the%20two%20choices%0A%20%20%20%20mem%5Bi%5D%5Bc%5D%20%3D%20max%28no,%20yes%29%0A%20%20%20%20return%20mem%5Bi%5D%5Bc%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10,%2020,%2030,%2040,%2050%5D%0A%20%20%20%20val%20%3D%20%5B50,%20120,%20150,%20210,%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20Memoized%20search%0A%20%20%20%20mem%20%3D%20%5B%5B-1%5D%20%2A%20%28cap%20%2B%201%29%20for%20_%20in%20range%28n%20%2B%201%29%5D%0A%20%20%20%20res%20%3D%20knapsack_dfs_mem%28wgt,%20val,%20mem,%20n,%20cap%29%0A%20%20%20%20print%28f%22Maximum%20total%20value%20within%20the%20knapsack%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=20&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20knapsack_dp%28wgt%3A%20list%5Bint%5D,%20val%3A%20list%5Bint%5D,%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%220-1%20knapsack%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B%5B0%5D%20%2A%20%28cap%20%2B%201%29%20for%20_%20in%20range%28n%20%2B%201%29%5D%0A%20%20%20%20%23%20State%20transitions%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20c%20in%20range%281,%20cap%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20wgt%5Bi%20-%201%5D%20%3E%20c%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20item%20i%20exceeds%20the%20remaining%20capacity,%20do%20not%20choose%20it%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Bc%5D%20%3D%20dp%5Bi%20-%201%5D%5Bc%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Take%20the%20larger%20value%20obtained%20by%20excluding%20and%20including%20item%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Bc%5D%20%3D%20max%28dp%5Bi%20-%201%5D%5Bc%5D,%20dp%5Bi%20-%201%5D%5Bc%20-%20wgt%5Bi%20-%201%5D%5D%20%2B%20val%5Bi%20-%201%5D%29%0A%20%20%20%20return%20dp%5Bn%5D%5Bcap%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10,%2020,%2030,%2040,%2050%5D%0A%20%20%20%20val%20%3D%20%5B50,%20120,%20150,%20210,%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20Dynamic%20programming%0A%20%20%20%20res%20%3D%20knapsack_dp%28wgt,%20val,%20cap%29%0A%20%20%20%20print%28f%22Maximum%20total%20value%20within%20the%20knapsack%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=7&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20knapsack_dp_comp%28wgt%3A%20list%5Bint%5D,%20val%3A%20list%5Bint%5D,%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%220-1%20knapsack%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B0%5D%20%2A%20%28cap%20%2B%201%29%0A%20%20%20%20%23%20State%20transitions%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20Traverse%20in%20decreasing%20order%0A%20%20%20%20%20%20%20%20for%20c%20in%20range%28cap,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20wgt%5Bi%20-%201%5D%20%3E%20c%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20item%20i%20exceeds%20the%20remaining%20capacity,%20do%20not%20choose%20it%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bc%5D%20%3D%20dp%5Bc%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Take%20the%20larger%20value%20obtained%20by%20excluding%20and%20including%20item%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bc%5D%20%3D%20max%28dp%5Bc%5D,%20dp%5Bc%20-%20wgt%5Bi%20-%201%5D%5D%20%2B%20val%5Bi%20-%201%5D%29%0A%20%20%20%20return%20dp%5Bcap%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10,%2020,%2030,%2040,%2050%5D%0A%20%20%20%20val%20%3D%20%5B50,%20120,%20150,%20210,%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20Space-optimized%20dynamic%20programming%0A%20%20%20%20res%20%3D%20knapsack_dp_comp%28wgt,%20val,%20cap%29%0A%20%20%20%20print%28f%22Maximum%20total%20value%20within%20the%20knapsack%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=7&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/min_cost_climbing_stairs_dp.md b/en/codes/pythontutor/chapter_dynamic_programming/min_cost_climbing_stairs_dp.md new file mode 100644 index 000000000..2f8fb5e85 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/min_cost_climbing_stairs_dp.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20min_cost_climbing_stairs_dp%28cost%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Minimum%20cost%20to%20climb%20stairs%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28cost%29%20-%201%0A%20%20%20%20if%20n%20%3D%3D%201%20or%20n%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20cost%5Bn%5D%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%20to%20store%20subproblem%20solutions%0A%20%20%20%20dp%20%3D%20%5B0%5D%20%2A%20%28n%20%2B%201%29%0A%20%20%20%20%23%20Initial%20states%3A%20set%20the%20solutions%20to%20the%20smallest%20subproblems%0A%20%20%20%20dp%5B1%5D,%20dp%5B2%5D%20%3D%20cost%5B1%5D,%20cost%5B2%5D%0A%20%20%20%20%23%20State%20transitions%3A%20solve%20progressively%20larger%20subproblems%20using%20smaller%20ones%0A%20%20%20%20for%20i%20in%20range%283,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bi%5D%20%3D%20min%28dp%5Bi%20-%201%5D,%20dp%5Bi%20-%202%5D%29%20%2B%20cost%5Bi%5D%0A%20%20%20%20return%20dp%5Bn%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20cost%20%3D%20%5B0,%201,%2010,%201,%201,%201,%2010,%201,%201,%2010,%201%5D%0A%20%20%20%20print%28f%22Input%20list%20of%20stair%20costs%3A%20%7Bcost%7D%22%29%0A%0A%20%20%20%20res%20%3D%20min_cost_climbing_stairs_dp%28cost%29%0A%20%20%20%20print%28f%22Minimum%20cost%20to%20finish%20climbing%20the%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20min_cost_climbing_stairs_dp_comp%28cost%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Minimum%20cost%20to%20climb%20stairs%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28cost%29%20-%201%0A%20%20%20%20if%20n%20%3D%3D%201%20or%20n%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20return%20cost%5Bn%5D%0A%20%20%20%20a,%20b%20%3D%20cost%5B1%5D,%20cost%5B2%5D%0A%20%20%20%20for%20i%20in%20range%283,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20a,%20b%20%3D%20b,%20min%28a,%20b%29%20%2B%20cost%5Bi%5D%0A%20%20%20%20return%20b%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20cost%20%3D%20%5B0,%201,%2010,%201,%201,%201,%2010,%201,%201,%2010,%201%5D%0A%20%20%20%20print%28f%22Input%20list%20of%20stair%20costs%3A%20%7Bcost%7D%22%29%0A%0A%20%20%20%20res%20%3D%20min_cost_climbing_stairs_dp_comp%28cost%29%0A%20%20%20%20print%28f%22Minimum%20cost%20to%20finish%20climbing%20the%20stairs%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/min_path_sum.md b/en/codes/pythontutor/chapter_dynamic_programming/min_path_sum.md new file mode 100644 index 000000000..c0fb85d70 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/min_path_sum.md @@ -0,0 +1,17 @@ + + + +https://pythontutor.com/render.html#code=from%20math%20import%20inf%0A%0Adef%20min_path_sum_dfs%28grid%3A%20list%5Blist%5Bint%5D%5D,%20i%3A%20int,%20j%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Minimum%20path%20sum%3A%20brute-force%20search%22%22%22%0A%20%20%20%20%23%20Stop%20searching%20at%20the%20top-left%20cell%0A%20%20%20%20if%20i%20%3D%3D%200%20and%20j%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20grid%5B0%5D%5B0%5D%0A%20%20%20%20%23%20If%20a%20row%20or%20column%20index%20is%20out%20of%20bounds,%20return%20a%20cost%20of%20%2B%E2%88%9E%0A%20%20%20%20if%20i%20%3C%200%20or%20j%20%3C%200%3A%0A%20%20%20%20%20%20%20%20return%20inf%0A%20%20%20%20%23%20Calculate%20the%20minimum%20path%20costs%20from%20the%20top-left%20corner%20to%20%28i-1,%20j%29%20and%20%28i,%20j-1%29%0A%20%20%20%20up%20%3D%20min_path_sum_dfs%28grid,%20i%20-%201,%20j%29%0A%20%20%20%20left%20%3D%20min_path_sum_dfs%28grid,%20i,%20j%20-%201%29%0A%20%20%20%20%23%20Return%20the%20minimum%20path%20cost%20from%20the%20top-left%20corner%20to%20%28i,%20j%29%0A%20%20%20%20return%20min%28left,%20up%29%20%2B%20grid%5Bi%5D%5Bj%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20grid%20%3D%20%5B%5B1,%203,%201,%205%5D,%20%5B2,%202,%204,%202%5D,%20%5B5,%203,%202,%201%5D,%20%5B4,%203,%205,%202%5D%5D%0A%20%20%20%20n,%20m%20%3D%20len%28grid%29,%20len%28grid%5B0%5D%29%0A%0A%20%20%20%20%23%20Brute-force%20search%0A%20%20%20%20res%20%3D%20min_path_sum_dfs%28grid,%20n%20-%201,%20m%20-%201%29%0A%20%20%20%20print%28f%22Minimum%20path%20sum%20from%20the%20top-left%20to%20the%20bottom-right%20corner%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=from%20math%20import%20inf%0A%0Adef%20min_path_sum_dfs_mem%28%0A%20%20%20%20grid%3A%20list%5Blist%5Bint%5D%5D,%20mem%3A%20list%5Blist%5Bint%5D%5D,%20i%3A%20int,%20j%3A%20int%0A%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Minimum%20path%20sum%3A%20memoized%20search%22%22%22%0A%20%20%20%20%23%20Stop%20searching%20at%20the%20top-left%20cell%0A%20%20%20%20if%20i%20%3D%3D%200%20and%20j%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20grid%5B0%5D%5B0%5D%0A%20%20%20%20%23%20If%20a%20row%20or%20column%20index%20is%20out%20of%20bounds,%20return%20a%20cost%20of%20%2B%E2%88%9E%0A%20%20%20%20if%20i%20%3C%200%20or%20j%20%3C%200%3A%0A%20%20%20%20%20%20%20%20return%20inf%0A%20%20%20%20%23%20If%20a%20result%20has%20already%20been%20recorded,%20return%20it%20directly%0A%20%20%20%20if%20mem%5Bi%5D%5Bj%5D%20%21%3D%20-1%3A%0A%20%20%20%20%20%20%20%20return%20mem%5Bi%5D%5Bj%5D%0A%20%20%20%20%23%20Minimum%20path%20costs%20to%20the%20cells%20on%20the%20left%20and%20above%0A%20%20%20%20up%20%3D%20min_path_sum_dfs_mem%28grid,%20mem,%20i%20-%201,%20j%29%0A%20%20%20%20left%20%3D%20min_path_sum_dfs_mem%28grid,%20mem,%20i,%20j%20-%201%29%0A%20%20%20%20%23%20Record%20and%20return%20the%20minimum%20path%20cost%20from%20the%20top-left%20corner%20to%20%28i,%20j%29%0A%20%20%20%20mem%5Bi%5D%5Bj%5D%20%3D%20min%28left,%20up%29%20%2B%20grid%5Bi%5D%5Bj%5D%0A%20%20%20%20return%20mem%5Bi%5D%5Bj%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20grid%20%3D%20%5B%5B1,%203,%201,%205%5D,%20%5B2,%202,%204,%202%5D,%20%5B5,%203,%202,%201%5D,%20%5B4,%203,%205,%202%5D%5D%0A%20%20%20%20n,%20m%20%3D%20len%28grid%29,%20len%28grid%5B0%5D%29%0A%0A%20%20%20%23%20Memoized%20search%0A%20%20%20%20mem%20%3D%20%5B%5B-1%5D%20%2A%20m%20for%20_%20in%20range%28n%29%5D%0A%20%20%20%20res%20%3D%20min_path_sum_dfs_mem%28grid,%20mem,%20n%20-%201,%20m%20-%201%29%0A%20%20%20%20print%28f%22Minimum%20path%20sum%20from%20the%20top-left%20to%20the%20bottom-right%20corner%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=16&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=from%20math%20import%20inf%0A%0Adef%20min_path_sum_dp%28grid%3A%20list%5Blist%5Bint%5D%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Minimum%20path%20sum%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20n,%20m%20%3D%20len%28grid%29,%20len%28grid%5B0%5D%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B%5B0%5D%20%2A%20m%20for%20_%20in%20range%28n%29%5D%0A%20%20%20%20dp%5B0%5D%5B0%5D%20%3D%20grid%5B0%5D%5B0%5D%0A%20%20%20%20%23%20State%20transitions%3A%20first%20row%0A%20%20%20%20for%20j%20in%20range%281,%20m%29%3A%0A%20%20%20%20%20%20%20%20dp%5B0%5D%5Bj%5D%20%3D%20dp%5B0%5D%5Bj%20-%201%5D%20%2B%20grid%5B0%5D%5Bj%5D%0A%20%20%20%20%23%20State%20transitions%3A%20first%20column%0A%20%20%20%20for%20i%20in%20range%281,%20n%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bi%5D%5B0%5D%20%3D%20dp%5Bi%20-%201%5D%5B0%5D%20%2B%20grid%5Bi%5D%5B0%5D%0A%20%20%20%20%23%20State%20transitions%3A%20remaining%20rows%20and%20columns%0A%20%20%20%20for%20i%20in%20range%281,%20n%29%3A%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%281,%20m%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Bj%5D%20%3D%20min%28dp%5Bi%5D%5Bj%20-%201%5D,%20dp%5Bi%20-%201%5D%5Bj%5D%29%20%2B%20grid%5Bi%5D%5Bj%5D%0A%20%20%20%20return%20dp%5Bn%20-%201%5D%5Bm%20-%201%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20grid%20%3D%20%5B%5B1,%203,%201,%205%5D,%20%5B2,%202,%204,%202%5D,%20%5B5,%203,%202,%201%5D,%20%5B4,%203,%205,%202%5D%5D%0A%20%20%20%20n,%20m%20%3D%20len%28grid%29,%20len%28grid%5B0%5D%29%0A%0A%20%20%20%20%23%20Dynamic%20programming%0A%20%20%20%20res%20%3D%20min_path_sum_dp%28grid%29%0A%20%20%20%20print%28f%22Minimum%20path%20sum%20from%20the%20top-left%20to%20the%20bottom-right%20corner%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=from%20math%20import%20inf%0A%0Adef%20min_path_sum_dp_comp%28grid%3A%20list%5Blist%5Bint%5D%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Minimum%20path%20sum%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20n,%20m%20%3D%20len%28grid%29,%20len%28grid%5B0%5D%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B0%5D%20%2A%20m%0A%20%20%20%20%23%20State%20transitions%3A%20first%20row%0A%20%20%20%20dp%5B0%5D%20%3D%20grid%5B0%5D%5B0%5D%0A%20%20%20%20for%20j%20in%20range%281,%20m%29%3A%0A%20%20%20%20%20%20%20%20dp%5Bj%5D%20%3D%20dp%5Bj%20-%201%5D%20%2B%20grid%5B0%5D%5Bj%5D%0A%20%20%20%20%23%20State%20transitions%3A%20remaining%20rows%0A%20%20%20%20for%20i%20in%20range%281,%20n%29%3A%0A%20%20%20%20%20%20%20%20%23%20State%20transitions%3A%20first%20column%0A%20%20%20%20%20%20%20%20dp%5B0%5D%20%3D%20dp%5B0%5D%20%2B%20grid%5Bi%5D%5B0%5D%0A%20%20%20%20%20%20%20%20%23%20State%20transitions%3A%20remaining%20columns%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%281,%20m%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bj%5D%20%3D%20min%28dp%5Bj%20-%201%5D,%20dp%5Bj%5D%29%20%2B%20grid%5Bi%5D%5Bj%5D%0A%20%20%20%20return%20dp%5Bm%20-%201%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20grid%20%3D%20%5B%5B1,%203,%201,%205%5D,%20%5B2,%202,%204,%202%5D,%20%5B5,%203,%202,%201%5D,%20%5B4,%203,%205,%202%5D%5D%0A%20%20%20%20n,%20m%20%3D%20len%28grid%29,%20len%28grid%5B0%5D%29%0A%0A%20%20%20%20%23%20Space-optimized%20dynamic%20programming%0A%20%20%20%20res%20%3D%20min_path_sum_dp_comp%28grid%29%0A%20%20%20%20print%28f%22Minimum%20path%20sum%20from%20the%20top-left%20to%20the%20bottom-right%20corner%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_dynamic_programming/unbounded_knapsack.md b/en/codes/pythontutor/chapter_dynamic_programming/unbounded_knapsack.md new file mode 100644 index 000000000..bb7af8972 --- /dev/null +++ b/en/codes/pythontutor/chapter_dynamic_programming/unbounded_knapsack.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20unbounded_knapsack_dp%28wgt%3A%20list%5Bint%5D,%20val%3A%20list%5Bint%5D,%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Unbounded%20knapsack%3A%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B%5B0%5D%20%2A%20%28cap%20%2B%201%29%20for%20_%20in%20range%28n%20%2B%201%29%5D%0A%20%20%20%20%23%20State%20transitions%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20c%20in%20range%281,%20cap%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20wgt%5Bi%20-%201%5D%20%3E%20c%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20item%20i%20exceeds%20the%20remaining%20capacity,%20do%20not%20choose%20it%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Bc%5D%20%3D%20dp%5Bi%20-%201%5D%5Bc%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Take%20the%20larger%20value%20obtained%20by%20excluding%20and%20including%20item%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bi%5D%5Bc%5D%20%3D%20max%28dp%5Bi%20-%201%5D%5Bc%5D,%20dp%5Bi%5D%5Bc%20-%20wgt%5Bi%20-%201%5D%5D%20%2B%20val%5Bi%20-%201%5D%29%0A%20%20%20%20return%20dp%5Bn%5D%5Bcap%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B1,%202,%203%5D%0A%20%20%20%20val%20%3D%20%5B5,%2011,%2015%5D%0A%20%20%20%20cap%20%3D%204%0A%0A%20%20%20%20%23%20Dynamic%20programming%0A%20%20%20%20res%20%3D%20unbounded_knapsack_dp%28wgt,%20val,%20cap%29%0A%20%20%20%20print%28f%22Maximum%20total%20value%20within%20the%20knapsack%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20unbounded_knapsack_dp_comp%28wgt%3A%20list%5Bint%5D,%20val%3A%20list%5Bint%5D,%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Unbounded%20knapsack%3A%20space-optimized%20dynamic%20programming%22%22%22%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%20%20%20%20%23%20Initialize%20the%20dp%20table%0A%20%20%20%20dp%20%3D%20%5B0%5D%20%2A%20%28cap%20%2B%201%29%0A%20%20%20%20%23%20State%20transitions%0A%20%20%20%20for%20i%20in%20range%281,%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20Traverse%20in%20increasing%20order%0A%20%20%20%20%20%20%20%20for%20c%20in%20range%281,%20cap%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20wgt%5Bi%20-%201%5D%20%3E%20c%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20item%20i%20exceeds%20the%20remaining%20capacity,%20do%20not%20choose%20it%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bc%5D%20%3D%20dp%5Bc%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Take%20the%20larger%20value%20obtained%20by%20excluding%20and%20including%20item%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%5Bc%5D%20%3D%20max%28dp%5Bc%5D,%20dp%5Bc%20-%20wgt%5Bi%20-%201%5D%5D%20%2B%20val%5Bi%20-%201%5D%29%0A%20%20%20%20return%20dp%5Bcap%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B1,%202,%203%5D%0A%20%20%20%20val%20%3D%20%5B5,%2011,%2015%5D%0A%20%20%20%20cap%20%3D%204%0A%0A%20%20%20%20%23%20Space-optimized%20dynamic%20programming%0A%20%20%20%20res%20%3D%20unbounded_knapsack_dp_comp%28wgt,%20val,%20cap%29%0A%20%20%20%20print%28f%22Maximum%20total%20value%20within%20the%20knapsack%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_graph/graph_adjacency_list.md b/en/codes/pythontutor/chapter_graph/graph_adjacency_list.md new file mode 100644 index 000000000..17a9db60f --- /dev/null +++ b/en/codes/pythontutor/chapter_graph/graph_adjacency_list.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20Vertex%3A%0A%20%20%20%20%22%22%22Vertex%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%0Adef%20vals_to_vets%28vals%3A%20list%5Bint%5D%29%20-%3E%20list%5B%22Vertex%22%5D%3A%0A%20%20%20%20%22%22%22Given%20the%20list%20of%20values%20vals,%20return%20the%20list%20of%20vertices%20vets%22%22%22%0A%20%20%20%20return%20%5BVertex%28val%29%20for%20val%20in%20vals%5D%0A%0A%0Aclass%20GraphAdjList%3A%0A%20%20%20%20%22%22%22Undirected%20graph%20class%20implemented%20with%20an%20adjacency%20list%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20edges%3A%20list%5Blist%5BVertex%5D%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self.adj_list%20%3D%20dict%5BVertex,%20list%5BVertex%5D%5D%28%29%0A%20%20%20%20%20%20%20%20%23%20Add%20all%20vertices%20and%20edges%0A%20%20%20%20%20%20%20%20for%20edge%20in%20edges%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_vertex%28edge%5B0%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_vertex%28edge%5B1%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_edge%28edge%5B0%5D,%20edge%5B1%5D%29%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20number%20of%20vertices%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self.adj_list%29%0A%0A%20%20%20%20def%20add_edge%28self,%20vet1%3A%20Vertex,%20vet2%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20an%20edge%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet1%20not%20in%20self.adj_list%20or%20vet2%20not%20in%20self.adj_list%20or%20vet1%20%3D%3D%20vet2%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%29%0A%20%20%20%20%20%20%20%20%23%20Add%20edge%20vet1%20-%20vet2%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet1%5D.append%28vet2%29%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet2%5D.append%28vet1%29%0A%0A%20%20%20%20def%20remove_edge%28self,%20vet1%3A%20Vertex,%20vet2%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20an%20edge%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet1%20not%20in%20self.adj_list%20or%20vet2%20not%20in%20self.adj_list%20or%20vet1%20%3D%3D%20vet2%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%29%0A%20%20%20%20%20%20%20%20%23%20Remove%20edge%20vet1%20-%20vet2%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet1%5D.remove%28vet2%29%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet2%5D.remove%28vet1%29%0A%0A%20%20%20%20def%20add_vertex%28self,%20vet%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20a%20vertex%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet%20in%20self.adj_list%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%23%20Add%20a%20new%20list%20to%20the%20adjacency%20list%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet%5D%20%3D%20%5B%5D%0A%0A%20%20%20%20def%20remove_vertex%28self,%20vet%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20a%20vertex%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet%20not%20in%20self.adj_list%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%29%0A%20%20%20%20%20%20%20%20%23%20Remove%20the%20list%20corresponding%20to%20vertex%20vet%20from%20the%20adjacency%20list%0A%20%20%20%20%20%20%20%20self.adj_list.pop%28vet%29%0A%20%20%20%20%20%20%20%20%23%20Traverse%20the%20lists%20of%20the%20other%20vertices%20and%20remove%20all%20edges%20incident%20to%20vet%0A%20%20%20%20%20%20%20%20for%20vertex%20in%20self.adj_list%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20vet%20in%20self.adj_list%5Bvertex%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.adj_list%5Bvertex%5D.remove%28vet%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20undirected%20graph%0A%20%20%20%20v%20%3D%20vals_to_vets%28%5B1,%203,%202,%205,%204%5D%29%0A%20%20%20%20edges%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%5Bv%5B0%5D,%20v%5B1%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B0%5D,%20v%5B3%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B1%5D,%20v%5B2%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B2%5D,%20v%5B3%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B2%5D,%20v%5B4%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B3%5D,%20v%5B4%5D%5D,%0A%20%20%20%20%5D%0A%20%20%20%20graph%20%3D%20GraphAdjList%28edges%29%0A%20%20%20%20del%20edges%0A%0A%20%20%20%20%23%20Add%20an%20edge%0A%20%20%20%20graph.add_edge%28v%5B0%5D,%20v%5B2%5D%29%0A%0A%20%20%20%20%23%20Remove%20an%20edge%0A%20%20%20%20graph.remove_edge%28v%5B0%5D,%20v%5B1%5D%29%0A%0A%20%20%20%20%23%20Add%20a%20vertex%0A%20%20%20%20v5%20%3D%20Vertex%286%29%0A%20%20%20%20graph.add_vertex%28v5%29%0A%0A%20%20%20%20%23%20Remove%20a%20vertex%0A%20%20%20%20graph.remove_vertex%28v%5B1%5D%29&cumulative=false&curInstr=39&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_graph/graph_adjacency_matrix.md b/en/codes/pythontutor/chapter_graph/graph_adjacency_matrix.md new file mode 100644 index 000000000..a50f44044 --- /dev/null +++ b/en/codes/pythontutor/chapter_graph/graph_adjacency_matrix.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20GraphAdjMat%3A%0A%20%20%20%20%22%22%22Undirected%20graph%20class%20implemented%20with%20an%20adjacency%20matrix%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20vertices%3A%20list%5Bint%5D,%20edges%3A%20list%5Blist%5Bint%5D%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self.vertices%3A%20list%5Bint%5D%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20self.adj_mat%3A%20list%5Blist%5Bint%5D%5D%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%23%20Add%20a%20vertex%0A%20%20%20%20%20%20%20%20for%20val%20in%20vertices%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_vertex%28val%29%0A%20%20%20%20%20%20%20%20%23%20Add%20an%20edge%0A%20%20%20%20%20%20%20%20for%20e%20in%20edges%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_edge%28e%5B0%5D,%20e%5B1%5D%29%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20number%20of%20vertices%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self.vertices%29%0A%0A%20%20%20%20def%20add_vertex%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20a%20vertex%22%22%22%0A%20%20%20%20%20%20%20%20n%20%3D%20self.size%28%29%0A%20%20%20%20%20%20%20%20%23%20Append%20the%20new%20vertex%20value%20to%20the%20vertex%20list%0A%20%20%20%20%20%20%20%20self.vertices.append%28val%29%0A%20%20%20%20%20%20%20%20%23%20Add%20a%20row%20to%20the%20adjacency%20matrix%0A%20%20%20%20%20%20%20%20new_row%20%3D%20%5B0%5D%20%2A%20n%0A%20%20%20%20%20%20%20%20self.adj_mat.append%28new_row%29%0A%20%20%20%20%20%20%20%20%23%20Add%20a%20column%20to%20the%20adjacency%20matrix%0A%20%20%20%20%20%20%20%20for%20row%20in%20self.adj_mat%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20row.append%280%29%0A%0A%20%20%20%20def%20remove_vertex%28self,%20index%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20a%20vertex%22%22%22%0A%20%20%20%20%20%20%20%20if%20index%20%3E%3D%20self.size%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%29%0A%20%20%20%20%20%20%20%20%23%20Remove%20the%20vertex%20at%20index%20%60index%60%20from%20the%20vertex%20list%0A%20%20%20%20%20%20%20%20self.vertices.pop%28index%29%0A%20%20%20%20%20%20%20%20%23%20Remove%20row%20%60index%60%20from%20the%20adjacency%20matrix%0A%20%20%20%20%20%20%20%20self.adj_mat.pop%28index%29%0A%20%20%20%20%20%20%20%20%23%20Remove%20column%20%60index%60%20from%20the%20adjacency%20matrix%0A%20%20%20%20%20%20%20%20for%20row%20in%20self.adj_mat%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20row.pop%28index%29%0A%0A%20%20%20%20def%20add_edge%28self,%20i%3A%20int,%20j%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20an%20edge%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Handle%20out-of-bounds%20or%20equal%20indices%0A%20%20%20%20%20%20%20%20if%20i%20%3C%200%20or%20j%20%3C%200%20or%20i%20%3E%3D%20self.size%28%29%20or%20j%20%3E%3D%20self.size%28%29%20or%20i%20%3D%3D%20j%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%29%0A%20%20%20%20%20%20%20%20self.adj_mat%5Bi%5D%5Bj%5D%20%3D%201%0A%20%20%20%20%20%20%20%20self.adj_mat%5Bj%5D%5Bi%5D%20%3D%201%0A%0A%20%20%20%20def%20remove_edge%28self,%20i%3A%20int,%20j%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20an%20edge%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Handle%20out-of-bounds%20or%20equal%20indices%0A%20%20%20%20%20%20%20%20if%20i%20%3C%200%20or%20j%20%3C%200%20or%20i%20%3E%3D%20self.size%28%29%20or%20j%20%3E%3D%20self.size%28%29%20or%20i%20%3D%3D%20j%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%29%0A%20%20%20%20%20%20%20%20self.adj_mat%5Bi%5D%5Bj%5D%20%3D%200%0A%20%20%20%20%20%20%20%20self.adj_mat%5Bj%5D%5Bi%5D%20%3D%200%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20undirected%20graph%0A%20%20%20%20vertices%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20edges%20%3D%20%5B%5B0,%201%5D,%20%5B0,%203%5D,%20%5B1,%202%5D,%20%5B2,%203%5D,%20%5B2,%204%5D,%20%5B3,%204%5D%5D%0A%20%20%20%20graph%20%3D%20GraphAdjMat%28vertices,%20edges%29%0A%0A%20%20%20%20%23%20Add%20an%20edge%0A%20%20%20%20%23%20Vertices%201%20and%202%20have%20indices%200%20and%202,%20respectively%0A%20%20%20%20graph.add_edge%280,%202%29%0A%0A%20%20%20%20%23%20Remove%20an%20edge%0A%20%20%20%20%23%20Vertices%201%20and%203%20have%20indices%200%20and%201,%20respectively%0A%20%20%20%20graph.remove_edge%280,%201%29%0A%0A%20%20%20%20%23%20Add%20a%20vertex%0A%20%20%20%20graph.add_vertex%286%29%0A%0A%20%20%20%20%23%20Remove%20a%20vertex%0A%20%20%20%20%23%20Vertex%203%20has%20index%201%0A%20%20%20%20graph.remove_vertex%281%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_graph/graph_bfs.md b/en/codes/pythontutor/chapter_graph/graph_bfs.md new file mode 100644 index 000000000..6ad3dca08 --- /dev/null +++ b/en/codes/pythontutor/chapter_graph/graph_bfs.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0Aclass%20Vertex%3A%0A%20%20%20%20%22%22%22Vertex%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%0Adef%20vals_to_vets%28vals%3A%20list%5Bint%5D%29%20-%3E%20list%5B%22Vertex%22%5D%3A%0A%20%20%20%20%22%22%22Given%20the%20list%20of%20values%20vals,%20return%20the%20list%20of%20vertices%20vets%22%22%22%0A%20%20%20%20return%20%5BVertex%28val%29%20for%20val%20in%20vals%5D%0A%0Aclass%20GraphAdjList%3A%0A%20%20%20%20%22%22%22Undirected%20graph%20class%20implemented%20with%20an%20adjacency%20list%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20edges%3A%20list%5Blist%5BVertex%5D%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self.adj_list%20%3D%20dict%5BVertex,%20list%5BVertex%5D%5D%28%29%0A%20%20%20%20%20%20%20%20for%20edge%20in%20edges%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_vertex%28edge%5B0%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_vertex%28edge%5B1%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_edge%28edge%5B0%5D,%20edge%5B1%5D%29%0A%0A%20%20%20%20def%20add_edge%28self,%20vet1%3A%20Vertex,%20vet2%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20an%20edge%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet1%20not%20in%20self.adj_list%20or%20vet2%20not%20in%20self.adj_list%20or%20vet1%20%3D%3D%20vet2%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%29%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet1%5D.append%28vet2%29%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet2%5D.append%28vet1%29%0A%0A%20%20%20%20def%20add_vertex%28self,%20vet%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20a%20vertex%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet%20in%20self.adj_list%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet%5D%20%3D%20%5B%5D%0A%0A%0Adef%20graph_bfs%28graph%3A%20GraphAdjList,%20start_vet%3A%20Vertex%29%20-%3E%20list%5BVertex%5D%3A%0A%20%20%20%20%22%22%22Breadth-first%20traversal%22%22%22%0A%20%20%20%20%23%20Vertex%20traversal%20sequence%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20%23%20Hash%20set%20for%20recording%20visited%20vertices%0A%20%20%20%20visited%20%3D%20set%5BVertex%5D%28%5Bstart_vet%5D%29%0A%20%20%20%20%23%20Use%20a%20queue%20to%20implement%20BFS%0A%20%20%20%20que%20%3D%20deque%5BVertex%5D%28%5Bstart_vet%5D%29%0A%20%20%20%20%23%20Starting%20from%20start_vet,%20loop%20until%20all%20reachable%20vertices%20have%20been%20visited%0A%20%20%20%20while%20len%28que%29%20%3E%200%3A%0A%20%20%20%20%20%20%20%20vet%20%3D%20que.popleft%28%29%20%20%23%20Dequeue%20the%20front%20vertex%0A%20%20%20%20%20%20%20%20res.append%28vet%29%20%20%23%20Record%20the%20visited%20vertex%0A%20%20%20%20%20%20%20%20%23%20Traverse%20all%20neighbors%20of%20this%20vertex%0A%20%20%20%20%20%20%20%20for%20adj_vet%20in%20graph.adj_list%5Bvet%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20adj_vet%20in%20visited%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%20%20%23%20Skip%20vertices%20that%20have%20already%20been%20visited%0A%20%20%20%20%20%20%20%20%20%20%20%20que.append%28adj_vet%29%20%20%23%20Enqueue%20only%20unvisited%20vertices%0A%20%20%20%20%20%20%20%20%20%20%20%20visited.add%28adj_vet%29%20%20%23%20Mark%20this%20vertex%20as%20visited%0A%20%20%20%20%23%20Return%20the%20vertex%20traversal%20sequence%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20undirected%20graph%0A%20%20%20%20v%20%3D%20vals_to_vets%28%5B0,%201,%202,%203,%204%5D%29%0A%20%20%20%20edges%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%5Bv%5B0%5D,%20v%5B1%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B0%5D,%20v%5B3%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B1%5D,%20v%5B2%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B1%5D,%20v%5B4%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B3%5D,%20v%5B4%5D%5D,%0A%20%20%20%20%5D%0A%20%20%20%20graph%20%3D%20GraphAdjList%28edges%29%0A%20%20%20%20del%20edges%0A%0A%20%20%20%20%23%20Breadth-first%20traversal%0A%20%20%20%20res%20%3D%20graph_bfs%28graph,%20v%5B0%5D%29&cumulative=false&curInstr=131&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_graph/graph_dfs.md b/en/codes/pythontutor/chapter_graph/graph_dfs.md new file mode 100644 index 000000000..3c393eb6d --- /dev/null +++ b/en/codes/pythontutor/chapter_graph/graph_dfs.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20Vertex%3A%0A%20%20%20%20%22%22%22Vertex%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%0Adef%20vals_to_vets%28vals%3A%20list%5Bint%5D%29%20-%3E%20list%5B%22Vertex%22%5D%3A%0A%20%20%20%20%22%22%22Given%20the%20list%20of%20values%20vals,%20return%20the%20list%20of%20vertices%20vets%22%22%22%0A%20%20%20%20return%20%5BVertex%28val%29%20for%20val%20in%20vals%5D%0A%0Aclass%20GraphAdjList%3A%0A%20%20%20%20%22%22%22Undirected%20graph%20class%20implemented%20with%20an%20adjacency%20list%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20edges%3A%20list%5Blist%5BVertex%5D%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self.adj_list%20%3D%20dict%5BVertex,%20list%5BVertex%5D%5D%28%29%0A%20%20%20%20%20%20%20%20for%20edge%20in%20edges%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_vertex%28edge%5B0%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_vertex%28edge%5B1%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20self.add_edge%28edge%5B0%5D,%20edge%5B1%5D%29%0A%0A%20%20%20%20def%20add_edge%28self,%20vet1%3A%20Vertex,%20vet2%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20an%20edge%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet1%20not%20in%20self.adj_list%20or%20vet2%20not%20in%20self.adj_list%20or%20vet1%20%3D%3D%20vet2%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%29%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet1%5D.append%28vet2%29%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet2%5D.append%28vet1%29%0A%0A%20%20%20%20def%20add_vertex%28self,%20vet%3A%20Vertex%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Add%20a%20vertex%22%22%22%0A%20%20%20%20%20%20%20%20if%20vet%20in%20self.adj_list%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20self.adj_list%5Bvet%5D%20%3D%20%5B%5D%0A%0A%0Adef%20dfs%28graph%3A%20GraphAdjList,%20visited%3A%20set%5BVertex%5D,%20res%3A%20list%5BVertex%5D,%20vet%3A%20Vertex%29%3A%0A%20%20%20%20%22%22%22Depth-first%20traversal%20helper%20function%22%22%22%0A%20%20%20%20res.append%28vet%29%20%20%23%20Record%20the%20visited%20vertex%0A%20%20%20%20visited.add%28vet%29%20%20%23%20Mark%20this%20vertex%20as%20visited%0A%20%20%20%20%23%20Traverse%20all%20neighbors%20of%20this%20vertex%0A%20%20%20%20for%20adjVet%20in%20graph.adj_list%5Bvet%5D%3A%0A%20%20%20%20%20%20%20%20if%20adjVet%20in%20visited%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%20%20%23%20Skip%20vertices%20that%20have%20already%20been%20visited%0A%20%20%20%20%20%20%20%20%23%20Recursively%20visit%20the%20neighboring%20vertex%0A%20%20%20%20%20%20%20%20dfs%28graph,%20visited,%20res,%20adjVet%29%0A%0A%0Adef%20graph_dfs%28graph%3A%20GraphAdjList,%20start_vet%3A%20Vertex%29%20-%3E%20list%5BVertex%5D%3A%0A%20%20%20%20%22%22%22Depth-first%20traversal%22%22%22%0A%20%20%20%20%23%20Represent%20the%20graph%20with%20an%20adjacency%20list%20to%20access%20all%20neighbors%20of%20a%20given%20vertex%0A%20%20%20%20%23%20Vertex%20traversal%20sequence%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20%23%20Hash%20set%20for%20recording%20visited%20vertices%0A%20%20%20%20visited%20%3D%20set%5BVertex%5D%28%29%0A%20%20%20%20dfs%28graph,%20visited,%20res,%20start_vet%29%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20undirected%20graph%0A%20%20%20%20v%20%3D%20vals_to_vets%28%5B0,%201,%202,%203,%204%5D%29%0A%20%20%20%20edges%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%5Bv%5B0%5D,%20v%5B1%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B0%5D,%20v%5B3%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B1%5D,%20v%5B2%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B1%5D,%20v%5B4%5D%5D,%0A%20%20%20%20%20%20%20%20%5Bv%5B3%5D,%20v%5B4%5D%5D,%0A%20%20%20%20%5D%0A%20%20%20%20graph%20%3D%20GraphAdjList%28edges%29%0A%0A%20%20%20%20%23%20Depth-first%20traversal%0A%20%20%20%20res%20%3D%20graph_dfs%28graph,%20v%5B0%5D%29&cumulative=false&curInstr=130&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_greedy/coin_change_greedy.md b/en/codes/pythontutor/chapter_greedy/coin_change_greedy.md new file mode 100644 index 000000000..6e58f1560 --- /dev/null +++ b/en/codes/pythontutor/chapter_greedy/coin_change_greedy.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20coin_change_greedy%28coins%3A%20list%5Bint%5D,%20amt%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Coin%20change%3A%20greedy%20algorithm%22%22%22%0A%20%20%20%20%23%20Assume%20the%20coins%20list%20is%20sorted%0A%20%20%20%20i%20%3D%20len%28coins%29%20-%201%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20Repeatedly%20make%20greedy%20choices%20until%20no%20amount%20remains%0A%20%20%20%20while%20amt%20%3E%200%3A%0A%20%20%20%20%20%20%20%20%23%20Find%20the%20largest%20coin%20no%20greater%20than%20the%20remaining%20amount%0A%20%20%20%20%20%20%20%20while%20i%20%3E%200%20and%20coins%5Bi%5D%20%3E%20amt%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20-%3D%201%0A%20%20%20%20%20%20%20%20%23%20Choose%20coins%5Bi%5D%0A%20%20%20%20%20%20%20%20amt%20-%3D%20coins%5Bi%5D%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20%23%20Return%20-1%20if%20no%20feasible%20solution%20was%20found%0A%20%20%20%20return%20count%20if%20amt%20%3D%3D%200%20else%20-1%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Greedy%20algorithm%3A%20guaranteed%20to%20find%20a%20globally%20optimal%20solution%0A%20%20%20%20coins%20%3D%20%5B1,%205,%2010,%2020,%2050,%20100%5D%0A%20%20%20%20amt%20%3D%20186%0A%20%20%20%20res%20%3D%20coin_change_greedy%28coins,%20amt%29%0A%20%20%20%20print%28f%22%5Cncoins%20%3D%20%7Bcoins%7D,%20amt%20%3D%20%7Bamt%7D%22%29%0A%20%20%20%20print%28f%22Number%20of%20coins%20chosen%20by%20the%20greedy%20algorithm%20for%20amount%20%7Bamt%7D%3A%20%7Bres%7D%22%29%0A%0A%20%20%20%20%23%20Greedy%20algorithm%3A%20not%20guaranteed%20to%20find%20a%20globally%20optimal%20solution%0A%20%20%20%20coins%20%3D%20%5B1,%2020,%2050%5D%0A%20%20%20%20amt%20%3D%2060%0A%20%20%20%20res%20%3D%20coin_change_greedy%28coins,%20amt%29%0A%20%20%20%20print%28f%22%5Cncoins%20%3D%20%7Bcoins%7D,%20amt%20%3D%20%7Bamt%7D%22%29%0A%20%20%20%20print%28f%22Number%20of%20coins%20chosen%20by%20the%20greedy%20algorithm%20for%20amount%20%7Bamt%7D%3A%20%7Bres%7D%22%29%0A%20%20%20%20print%28f%22The%20actual%20minimum%20is%203%20coins%3A%2020%20%2B%2020%20%2B%2020%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_greedy/fractional_knapsack.md b/en/codes/pythontutor/chapter_greedy/fractional_knapsack.md new file mode 100644 index 000000000..878489e81 --- /dev/null +++ b/en/codes/pythontutor/chapter_greedy/fractional_knapsack.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20Item%3A%0A%20%20%20%20%22%22%22Item%22%22%22%0A%20%20%20%20def%20__init__%28self,%20w%3A%20int,%20v%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.w%20%3D%20w%20%20%23%20Item%20weight%0A%20%20%20%20%20%20%20%20self.v%20%3D%20v%20%20%23%20Item%20value%0A%0Adef%20fractional_knapsack%28wgt%3A%20list%5Bint%5D,%20val%3A%20list%5Bint%5D,%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Fractional%20knapsack%3A%20greedy%20algorithm%22%22%22%0A%20%20%20%20%23%20Create%20a%20list%20of%20items,%20each%20with%20two%20attributes%3A%20weight%20and%20value%0A%20%20%20%20items%20%3D%20%5BItem%28w,%20v%29%20for%20w,%20v%20in%20zip%28wgt,%20val%29%5D%0A%20%20%20%20%23%20Sort%20by%20value%20per%20unit%20weight,%20item.v%20/%20item.w,%20in%20descending%20order%0A%20%20%20%20items.sort%28key%3Dlambda%20item%3A%20item.v%20/%20item.w,%20reverse%3DTrue%29%0A%20%20%20%20%23%20Repeatedly%20make%20greedy%20choices%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20for%20item%20in%20items%3A%0A%20%20%20%20%20%20%20%20if%20item.w%20%3C%3D%20cap%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20enough%20capacity%20remains,%20put%20the%20entire%20current%20item%20in%20the%20knapsack%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20item.v%0A%20%20%20%20%20%20%20%20%20%20%20%20cap%20-%3D%20item.w%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20the%20remaining%20capacity%20is%20insufficient,%20put%20part%20of%20the%20current%20item%20in%20the%20knapsack%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20%28item.v%20/%20item.w%29%20%2A%20cap%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20No%20capacity%20remains,%20so%20exit%20the%20loop%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10,%2020,%2030,%2040,%2050%5D%0A%20%20%20%20val%20%3D%20%5B50,%20120,%20150,%20210,%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20Greedy%20algorithm%0A%20%20%20%20res%20%3D%20fractional_knapsack%28wgt,%20val,%20cap%29%0A%20%20%20%20print%28f%22Maximum%20total%20value%20within%20the%20knapsack%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_greedy/max_capacity.md b/en/codes/pythontutor/chapter_greedy/max_capacity.md new file mode 100644 index 000000000..3324c7c67 --- /dev/null +++ b/en/codes/pythontutor/chapter_greedy/max_capacity.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20max_capacity%28ht%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Maximum%20container%20capacity%3A%20greedy%20algorithm%22%22%22%0A%20%20%20%20%23%20Initialize%20i%20and%20j%20at%20opposite%20ends%20of%20the%20array%0A%20%20%20%20i,%20j%20%3D%200,%20len%28ht%29%20-%201%0A%20%20%20%20%23%20The%20initial%20maximum%20capacity%20is%200%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20Repeatedly%20make%20greedy%20choices%20until%20the%20two%20sides%20meet%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20%23%20Update%20the%20maximum%20capacity%0A%20%20%20%20%20%20%20%20cap%20%3D%20min%28ht%5Bi%5D,%20ht%5Bj%5D%29%20%2A%20%28j%20-%20i%29%0A%20%20%20%20%20%20%20%20res%20%3D%20max%28res,%20cap%29%0A%20%20%20%20%20%20%20%20%23%20Move%20the%20shorter%20side%20inward%0A%20%20%20%20%20%20%20%20if%20ht%5Bi%5D%20%3C%20ht%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20ht%20%3D%20%5B3,%208,%205,%202,%207,%207,%203,%204%5D%0A%0A%20%20%20%20%23%20Greedy%20algorithm%0A%20%20%20%20res%20%3D%20max_capacity%28ht%29%0A%20%20%20%20print%28f%22Maximum%20capacity%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_greedy/max_product_cutting.md b/en/codes/pythontutor/chapter_greedy/max_product_cutting.md new file mode 100644 index 000000000..74363743e --- /dev/null +++ b/en/codes/pythontutor/chapter_greedy/max_product_cutting.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=import%20math%0A%0Adef%20max_product_cutting%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Maximum%20product%20after%20splitting%3A%20greedy%20algorithm%22%22%22%0A%20%20%20%20%23%20When%20n%20%3C%3D%203,%20one%20part%20must%20be%201%0A%20%20%20%20if%20n%20%3C%3D%203%3A%0A%20%20%20%20%20%20%20%20return%201%20%2A%20%28n%20-%201%29%0A%20%20%20%20%23%20Greedily%20split%20off%20parts%20of%20size%203%3B%20a%20is%20the%20number%20of%203s%20and%20b%20is%20the%20remainder%0A%20%20%20%20a,%20b%20%3D%20n%20//%203,%20n%20%25%203%0A%20%20%20%20if%20b%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20%23%20When%20the%20remainder%20is%201,%20replace%20one%20pair%201%20%2A%203%20with%202%20%2A%202%0A%20%20%20%20%20%20%20%20return%20int%28math.pow%283,%20a%20-%201%29%29%20%2A%202%20%2A%202%0A%20%20%20%20if%20b%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20%23%20When%20the%20remainder%20is%202,%20no%20adjustment%20is%20needed%0A%20%20%20%20%20%20%20%20return%20int%28math.pow%283,%20a%29%29%20%2A%202%0A%20%20%20%20%23%20When%20the%20remainder%20is%200,%20no%20adjustment%20is%20needed%0A%20%20%20%20return%20int%28math.pow%283,%20a%29%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2058%0A%0A%20%20%20%20%23%20Greedy%20algorithm%0A%20%20%20%20res%20%3D%20max_product_cutting%28n%29%0A%20%20%20%20print%28f%22Maximum%20product%20after%20splitting%3A%20%7Bres%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_hashing/array_hash_map.md b/en/codes/pythontutor/chapter_hashing/array_hash_map.md new file mode 100644 index 000000000..559e1233f --- /dev/null +++ b/en/codes/pythontutor/chapter_hashing/array_hash_map.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20Pair%3A%0A%20%20%20%20%22%22%22Key-value%20pair%22%22%22%0A%20%20%20%20def%20__init__%28self,%20key%3A%20int,%20val%3A%20str%29%3A%0A%20%20%20%20%20%20%20%20self.key%20%3D%20key%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%0A%0Aclass%20ArrayHashMap%3A%0A%20%20%20%20%22%22%22Hash%20table%20implemented%20with%20an%20array%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Initialize%20an%20array%20containing%2020%20buckets%0A%20%20%20%20%20%20%20%20self.buckets%3A%20list%5BPair%20%7C%20None%5D%20%3D%20%5BNone%5D%20%2A%2020%0A%0A%20%20%20%20def%20hash_func%28self,%20key%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Hash%20function%22%22%22%0A%20%20%20%20%20%20%20%20index%20%3D%20key%20%25%2020%0A%20%20%20%20%20%20%20%20return%20index%0A%0A%20%20%20%20def%20get%28self,%20key%3A%20int%29%20-%3E%20str%3A%0A%20%20%20%20%20%20%20%20%22%22%22Lookup%20operation%22%22%22%0A%20%20%20%20%20%20%20%20index%3A%20int%20%3D%20self.hash_func%28key%29%0A%20%20%20%20%20%20%20%20pair%3A%20Pair%20%3D%20self.buckets%5Bindex%5D%0A%20%20%20%20%20%20%20%20if%20pair%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20return%20pair.val%0A%0A%20%20%20%20def%20put%28self,%20key%3A%20int,%20val%3A%20str%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Insert%20operation%22%22%22%0A%20%20%20%20%20%20%20%20pair%20%3D%20Pair%28key,%20val%29%0A%20%20%20%20%20%20%20%20index%3A%20int%20%3D%20self.hash_func%28key%29%0A%20%20%20%20%20%20%20%20self.buckets%5Bindex%5D%20%3D%20pair%0A%0A%20%20%20%20def%20remove%28self,%20key%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20operation%22%22%22%0A%20%20%20%20%20%20%20%20index%3A%20int%20%3D%20self.hash_func%28key%29%0A%20%20%20%20%20%20%20%20%23%20Set%20to%20None%20to%20mark%20removal%0A%20%20%20%20%20%20%20%20self.buckets%5Bindex%5D%20%3D%20None%0A%0A%20%20%20%20def%20entry_set%28self%29%20-%3E%20list%5BPair%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20all%20key-value%20pairs%22%22%22%0A%20%20%20%20%20%20%20%20result%3A%20list%5BPair%5D%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20for%20pair%20in%20self.buckets%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pair%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20result.append%28pair%29%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20def%20key_set%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20all%20keys%22%22%22%0A%20%20%20%20%20%20%20%20result%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20for%20pair%20in%20self.buckets%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pair%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20result.append%28pair.key%29%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20def%20value_set%28self%29%20-%3E%20list%5Bstr%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20all%20values%22%22%22%0A%20%20%20%20%20%20%20%20result%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20for%20pair%20in%20self.buckets%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pair%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20result.append%28pair.val%29%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20def%20print%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Print%20the%20hash%20table%22%22%22%0A%20%20%20%20%20%20%20%20for%20pair%20in%20self.buckets%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pair%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print%28pair.key,%20%22-%3E%22,%20pair.val%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20hash%20table%0A%20%20%20%20hmap%20%3D%20ArrayHashMap%28%29%0A%0A%20%20%20%20%23%20Insert%20operation%0A%20%20%20%20hmap.put%2812836,%20%22%E5%B0%8F%E5%93%88%22%29%0A%20%20%20%20hmap.put%2815937,%20%22%E5%B0%8F%E5%95%B0%22%29%0A%20%20%20%20hmap.put%2816750,%20%22%E5%B0%8F%E7%AE%97%22%29%0A%20%20%20%20hmap.put%2813276,%20%22%E5%B0%8F%E6%B3%95%22%29%0A%20%20%20%20hmap.put%2810583,%20%22%E5%B0%8F%E9%B8%AD%22%29%0A%0A%20%20%20%20%23%20Lookup%20operation%0A%20%20%20%20name%20%3D%20hmap.get%2815937%29%0A%0A%20%20%20%20%23%20Remove%20operation%0A%20%20%20%20hmap.remove%2810583%29%0A%0A%20%20%20%20%23%20Traverse%20the%20hash%20table%0A%20%20%20%20print%28%22%5CnTraverse%20key-value%20pairs%3A%20Key-%3EValue%22%29%0A%20%20%20%20for%20pair%20in%20hmap.entry_set%28%29%3A%0A%20%20%20%20%20%20%20%20print%28pair.key,%20%22-%3E%22,%20pair.val%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_hashing/hash_map_chaining.md b/en/codes/pythontutor/chapter_hashing/hash_map_chaining.md new file mode 100644 index 000000000..4797ddb3c --- /dev/null +++ b/en/codes/pythontutor/chapter_hashing/hash_map_chaining.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20Pair%3A%0A%20%20%20%20%22%22%22Key-value%20pair%22%22%22%0A%20%20%20%20def%20__init__%28self,%20key%3A%20int,%20val%3A%20str%29%3A%0A%20%20%20%20%20%20%20%20self.key%20%3D%20key%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%0Aclass%20HashMapChaining%3A%0A%20%20%20%20%22%22%22Hash%20table%20with%20separate%20chaining%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self.size%20%3D%200%0A%20%20%20%20%20%20%20%20self.capacity%20%3D%204%0A%20%20%20%20%20%20%20%20self.load_thres%20%3D%202.0%20/%203.0%0A%20%20%20%20%20%20%20%20self.extend_ratio%20%3D%202%0A%20%20%20%20%20%20%20%20self.buckets%20%3D%20%5B%5B%5D%20for%20_%20in%20range%28self.capacity%29%5D%0A%0A%20%20%20%20def%20hash_func%28self,%20key%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Hash%20function%22%22%22%0A%20%20%20%20%20%20%20%20return%20key%20%25%20self.capacity%0A%0A%20%20%20%20def%20load_factor%28self%29%20-%3E%20float%3A%0A%20%20%20%20%20%20%20%20%22%22%22Load%20factor%22%22%22%0A%20%20%20%20%20%20%20%20return%20self.size%20/%20self.capacity%0A%0A%20%20%20%20def%20get%28self,%20key%3A%20int%29%20-%3E%20str%20%7C%20None%3A%0A%20%20%20%20%20%20%20%20%22%22%22Lookup%20operation%22%22%22%0A%20%20%20%20%20%20%20%20index%20%3D%20self.hash_func%28key%29%0A%20%20%20%20%20%20%20%20bucket%20%3D%20self.buckets%5Bindex%5D%0A%20%20%20%20%20%20%20%20for%20pair%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pair.key%20%3D%3D%20key%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20pair.val%0A%20%20%20%20%20%20%20%20return%20None%0A%0A%20%20%20%20def%20put%28self,%20key%3A%20int,%20val%3A%20str%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Insert%20operation%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.load_factor%28%29%20%3E%20self.load_thres%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.extend%28%29%0A%20%20%20%20%20%20%20%20index%20%3D%20self.hash_func%28key%29%0A%20%20%20%20%20%20%20%20bucket%20%3D%20self.buckets%5Bindex%5D%0A%20%20%20%20%20%20%20%20for%20pair%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pair.key%20%3D%3D%20key%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pair.val%20%3D%20val%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20pair%20%3D%20Pair%28key,%20val%29%0A%20%20%20%20%20%20%20%20bucket.append%28pair%29%0A%20%20%20%20%20%20%20%20self.size%20%2B%3D%201%0A%0A%20%20%20%20def%20remove%28self,%20key%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20operation%22%22%22%0A%20%20%20%20%20%20%20%20index%20%3D%20self.hash_func%28key%29%0A%20%20%20%20%20%20%20%20bucket%20%3D%20self.buckets%5Bindex%5D%0A%20%20%20%20%20%20%20%20for%20pair%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pair.key%20%3D%3D%20key%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bucket.remove%28pair%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.size%20-%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%0A%20%20%20%20def%20extend%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Grow%20the%20hash%20table%20capacity%22%22%22%0A%20%20%20%20%20%20%20%20buckets%20%3D%20self.buckets%0A%20%20%20%20%20%20%20%20self.capacity%20%2A%3D%20self.extend_ratio%0A%20%20%20%20%20%20%20%20self.buckets%20%3D%20%5B%5B%5D%20for%20_%20in%20range%28self.capacity%29%5D%0A%20%20%20%20%20%20%20%20self.size%20%3D%200%0A%20%20%20%20%20%20%20%20for%20bucket%20in%20buckets%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20pair%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.put%28pair.key,%20pair.val%29%0A%0A%20%20%20%20def%20print%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Print%20the%20hash%20table%22%22%22%0A%20%20%20%20%20%20%20%20for%20bucket%20in%20self.buckets%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20pair%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20res.append%28str%28pair.key%29%20%2B%20%22%20-%3E%20%22%20%2B%20pair.val%29%0A%20%20%20%20%20%20%20%20%20%20%20%20print%28res%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20hash%20table%0A%20%20%20%20hashmap%20%3D%20HashMapChaining%28%29%0A%0A%20%20%20%20%23%20Insert%20operation%0A%20%20%20%20hashmap.put%2812836,%20%22%E5%B0%8F%E5%93%88%22%29%0A%20%20%20%20hashmap.put%2815937,%20%22%E5%B0%8F%E5%95%B0%22%29%0A%20%20%20%20hashmap.put%2816750,%20%22%E5%B0%8F%E7%AE%97%22%29%0A%20%20%20%20hashmap.put%2813276,%20%22%E5%B0%8F%E6%B3%95%22%29%0A%20%20%20%20hashmap.put%2810583,%20%22%E5%B0%8F%E9%B8%AD%22%29%0A%0A%20%20%20%20%23%20Lookup%20operation%0A%20%20%20%20name%20%3D%20hashmap.get%2813276%29%0A%0A%20%20%20%20%23%20Remove%20operation%0A%20%20%20%20hashmap.remove%2812836%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_hashing/simple_hash.md b/en/codes/pythontutor/chapter_hashing/simple_hash.md new file mode 100644 index 000000000..b9ccb872e --- /dev/null +++ b/en/codes/pythontutor/chapter_hashing/simple_hash.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20add_hash%28key%3A%20str%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Additive%20hash%22%22%22%0A%20%20%20%20hash%20%3D%200%0A%20%20%20%20modulus%20%3D%201000000007%0A%20%20%20%20for%20c%20in%20key%3A%0A%20%20%20%20%20%20%20%20hash%20%2B%3D%20ord%28c%29%0A%20%20%20%20return%20hash%20%25%20modulus%0A%0A%0Adef%20mul_hash%28key%3A%20str%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Multiplicative%20hash%22%22%22%0A%20%20%20%20hash%20%3D%200%0A%20%20%20%20modulus%20%3D%201000000007%0A%20%20%20%20for%20c%20in%20key%3A%0A%20%20%20%20%20%20%20%20hash%20%3D%2031%20%2A%20hash%20%2B%20ord%28c%29%0A%20%20%20%20return%20hash%20%25%20modulus%0A%0A%0Adef%20xor_hash%28key%3A%20str%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22XOR%20hash%22%22%22%0A%20%20%20%20hash%20%3D%200%0A%20%20%20%20modulus%20%3D%201000000007%0A%20%20%20%20for%20c%20in%20key%3A%0A%20%20%20%20%20%20%20%20hash%20%5E%3D%20ord%28c%29%0A%20%20%20%20return%20hash%20%25%20modulus%0A%0A%0Adef%20rot_hash%28key%3A%20str%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Rotating%20hash%22%22%22%0A%20%20%20%20hash%20%3D%200%0A%20%20%20%20modulus%20%3D%201000000007%0A%20%20%20%20for%20c%20in%20key%3A%0A%20%20%20%20%20%20%20%20hash%20%3D%20%28hash%20%3C%3C%204%29%20%5E%20%28hash%20%3E%3E%2028%29%20%5E%20ord%28c%29%0A%20%20%20%20return%20hash%20%25%20modulus%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20key%20%3D%20%22Hello%20%E7%AE%97%E6%B3%95%22%0A%0A%20%20%20%20hash%20%3D%20add_hash%28key%29%0A%20%20%20%20print%28f%22Additive%20hash%20value%3A%20%7Bhash%7D%22%29%0A%0A%20%20%20%20hash%20%3D%20mul_hash%28key%29%0A%20%20%20%20print%28f%22Multiplicative%20hash%20value%3A%20%7Bhash%7D%22%29%0A%0A%20%20%20%20hash%20%3D%20xor_hash%28key%29%0A%20%20%20%20print%28f%22XOR%20hash%20value%3A%20%7Bhash%7D%22%29%0A%0A%20%20%20%20hash%20%3D%20rot_hash%28key%29%0A%20%20%20%20print%28f%22Rotating%20hash%20value%3A%20%7Bhash%7D%22%29%0A&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_heap/my_heap.md b/en/codes/pythontutor/chapter_heap/my_heap.md new file mode 100644 index 000000000..1acaf59d6 --- /dev/null +++ b/en/codes/pythontutor/chapter_heap/my_heap.md @@ -0,0 +1,20 @@ + + + +https://pythontutor.com/render.html#code=class%20MaxHeap%3A%0A%20%20%20%20%22%22%22Max%20heap%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%3A%20build%20a%20heap%20from%20the%20input%20list%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Place%20the%20list%20elements%20in%20the%20heap%20without%20changing%20their%20order%0A%20%20%20%20%20%20%20%20self.max_heap%20%3D%20nums%0A%20%20%20%20%20%20%20%20%23%20Sift%20down%20from%20every%20non-leaf%20node%0A%20%20%20%20%20%20%20%20for%20i%20in%20range%28self.parent%28self.size%28%29%20-%201%29,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.sift_down%28i%29%0A%0A%20%20%20%20def%20left%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20left%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%201%0A%0A%20%20%20%20def%20right%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20right%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%202%0A%0A%20%20%20%20def%20parent%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20parent%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%20%28i%20-%201%29%20//%202%20%20%23%20Floor%20division%0A%0A%20%20%20%20def%20swap%28self,%20i%3A%20int,%20j%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Swap%20elements%22%22%22%0A%20%20%20%20%20%20%20%20self.max_heap%5Bi%5D,%20self.max_heap%5Bj%5D%20%3D%20self.max_heap%5Bj%5D,%20self.max_heap%5Bi%5D%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20heap%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self.max_heap%29%0A%0A%20%20%20%20def%20sift_down%28self,%20i%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Sift%20down%20starting%20from%20node%20i%22%22%22%0A%20%20%20%20%20%20%20%20while%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Find%20the%20node%20with%20the%20largest%20value%20among%20i,%20l,%20and%20r,%20and%20store%20its%20index%20in%20ma%0A%20%20%20%20%20%20%20%20%20%20%20%20l,%20r,%20ma%20%3D%20self.left%28i%29,%20self.right%28i%29,%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20l%20%3C%20self.size%28%29%20and%20self.max_heap%5Bl%5D%20%3E%20self.max_heap%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20l%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20r%20%3C%20self.size%28%29%20and%20self.max_heap%5Br%5D%20%3E%20self.max_heap%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20r%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Stop%20if%20node%20i%20is%20largest%20or%20both%20child%20indices%20l%20and%20r%20are%20out%20of%20bounds%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20ma%20%3D%3D%20i%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Swap%20the%20two%20nodes%0A%20%20%20%20%20%20%20%20%20%20%20%20self.swap%28i,%20ma%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Continue%20sifting%20down%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20ma%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20max%20heap%0A%20%20%20%20max_heap%20%3D%20MaxHeap%28%5B1,%202,%203,%204,%205%5D%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + + + + +https://pythontutor.com/render.html#code=class%20MaxHeap%3A%0A%20%20%20%20%22%22%22Max%20heap%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Place%20the%20list%20elements%20in%20the%20heap%20without%20changing%20their%20order%0A%20%20%20%20%20%20%20%20self.max_heap%20%3D%20nums%0A%0A%20%20%20%20def%20left%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20left%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%201%0A%0A%20%20%20%20def%20right%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20right%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%202%0A%0A%20%20%20%20def%20parent%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20parent%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%20%28i%20-%201%29%20//%202%20%20%23%20Floor%20division%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20heap%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self.max_heap%29%0A%0A%20%20%20%20def%20is_empty%28self%29%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%22%22%22Check%20whether%20the%20heap%20is%20empty%22%22%22%0A%20%20%20%20%20%20%20%20return%20self.size%28%29%20%3D%3D%200%0A%0A%20%20%20%20def%20peek%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Access%20the%20top%20element%20of%20the%20heap%22%22%22%0A%20%20%20%20%20%20%20%20return%20self.max_heap%5B0%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20max%20heap%0A%20%20%20%20%23%20Note%20that%20the%20input%20array%20is%20already%20a%20valid%20heap%20%0A%20%20%20%20max_heap%20%3D%20MaxHeap%28%5B9,%208,%206,%206,%207,%205,%202,%201,%204,%203,%206,%202%5D%29%0A%0A%20%20%20%20%23%20Get%20the%20top%20element%20of%20the%20heap%0A%20%20%20%20peek%20%3D%20max_heap.peek%28%29%0A%20%20%20%20print%28f%22%5CnTop%20element%20of%20the%20heap%3A%20%7Bpeek%7D%22%29&cumulative=false&curInstr=7&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20MaxHeap%3A%0A%20%20%20%20%22%22%22Max%20heap%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Place%20the%20list%20elements%20in%20the%20heap%20without%20changing%20their%20order%0A%20%20%20%20%20%20%20%20self.max_heap%20%3D%20nums%0A%0A%20%20%20%20def%20left%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20left%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%201%0A%0A%20%20%20%20def%20right%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20right%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%202%0A%0A%20%20%20%20def%20parent%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20parent%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%20%28i%20-%201%29%20//%202%20%20%23%20Floor%20division%0A%0A%20%20%20%20def%20swap%28self,%20i%3A%20int,%20j%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Swap%20elements%22%22%22%0A%20%20%20%20%20%20%20%20self.max_heap%5Bi%5D,%20self.max_heap%5Bj%5D%20%3D%20self.max_heap%5Bj%5D,%20self.max_heap%5Bi%5D%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20heap%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self.max_heap%29%0A%0A%20%20%20%20def%20is_empty%28self%29%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%22%22%22Check%20whether%20the%20heap%20is%20empty%22%22%22%0A%20%20%20%20%20%20%20%20return%20self.size%28%29%20%3D%3D%200%0A%0A%20%20%20%20def%20push%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Push%20an%20element%20onto%20the%20heap%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Add%20a%20node%0A%20%20%20%20%20%20%20%20self.max_heap.append%28val%29%0A%20%20%20%20%20%20%20%20%23%20Sift%20up%0A%20%20%20%20%20%20%20%20self.sift_up%28self.size%28%29%20-%201%29%0A%0A%20%20%20%20def%20sift_up%28self,%20i%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Sift%20up%20starting%20from%20node%20i%22%22%22%0A%20%20%20%20%20%20%20%20while%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Get%20the%20parent%20of%20node%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20p%20%3D%20self.parent%28i%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Stop%20sifting%20when%20the%20root%20has%20been%20passed%20or%20the%20node%20needs%20no%20repair%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20p%20%3C%200%20or%20self.max_heap%5Bi%5D%20%3C%3D%20self.max_heap%5Bp%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Swap%20the%20two%20nodes%0A%20%20%20%20%20%20%20%20%20%20%20%20self.swap%28i,%20p%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Continue%20sifting%20up%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20p%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20max%20heap%0A%20%20%20%20%23%20Note%20that%20the%20input%20array%20is%20already%20a%20valid%20heap%0A%20%20%20%20max_heap%20%3D%20MaxHeap%28%5B9,%208,%206,%206,%207,%205,%202,%201,%204,%203,%206,%202%5D%29%0A%0A%20%20%20%20%23%20Push%20an%20element%20onto%20the%20heap%0A%20%20%20%20val%20%3D%207%0A%20%20%20%20max_heap.push%28val%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20MaxHeap%3A%0A%20%20%20%20%22%22%22Max%20heap%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Place%20the%20list%20elements%20in%20the%20heap%20without%20changing%20their%20order%0A%20%20%20%20%20%20%20%20self.max_heap%20%3D%20nums%0A%0A%20%20%20%20def%20left%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20left%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%201%0A%0A%20%20%20%20def%20right%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20right%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%202%0A%0A%20%20%20%20def%20parent%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20parent%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%20%28i%20-%201%29%20//%202%20%20%23%20Floor%20division%0A%0A%20%20%20%20def%20swap%28self,%20i%3A%20int,%20j%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Swap%20elements%22%22%22%0A%20%20%20%20%20%20%20%20self.max_heap%5Bi%5D,%20self.max_heap%5Bj%5D%20%3D%20self.max_heap%5Bj%5D,%20self.max_heap%5Bi%5D%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20heap%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self.max_heap%29%0A%0A%20%20%20%20def%20is_empty%28self%29%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%22%22%22Check%20whether%20the%20heap%20is%20empty%22%22%22%0A%20%20%20%20%20%20%20%20return%20self.size%28%29%20%3D%3D%200%0A%0A%20%20%20%20def%20pop%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Pop%20an%20element%20from%20the%20heap%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Handle%20an%20empty%20heap%0A%20%20%20%20%20%20%20%20if%20self.is_empty%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Heap%20is%20empty%22%29%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20root%20with%20the%20rightmost%20leaf%20on%20the%20last%20level%20%28swap%20the%20first%20and%20last%20elements%29%0A%20%20%20%20%20%20%20%20self.swap%280,%20self.size%28%29%20-%201%29%0A%20%20%20%20%20%20%20%20%23%20Remove%20a%20node%0A%20%20%20%20%20%20%20%20val%20%3D%20self.max_heap.pop%28%29%0A%20%20%20%20%20%20%20%20%23%20Sift%20down%0A%20%20%20%20%20%20%20%20self.sift_down%280%29%0A%20%20%20%20%20%20%20%20%23%20Return%20the%20top%20element%20of%20the%20heap%0A%20%20%20%20%20%20%20%20return%20val%0A%0A%20%20%20%20def%20sift_down%28self,%20i%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Sift%20down%20starting%20from%20node%20i%22%22%22%0A%20%20%20%20%20%20%20%20while%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Find%20the%20node%20with%20the%20largest%20value%20among%20i,%20l,%20and%20r,%20and%20store%20its%20index%20in%20ma%0A%20%20%20%20%20%20%20%20%20%20%20%20l,%20r,%20ma%20%3D%20self.left%28i%29,%20self.right%28i%29,%20i%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20l%20%3C%20self.size%28%29%20and%20self.max_heap%5Bl%5D%20%3E%20self.max_heap%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20l%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20r%20%3C%20self.size%28%29%20and%20self.max_heap%5Br%5D%20%3E%20self.max_heap%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20r%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Stop%20if%20node%20i%20is%20largest%20or%20both%20child%20indices%20l%20and%20r%20are%20out%20of%20bounds%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20ma%20%3D%3D%20i%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Swap%20the%20two%20nodes%0A%20%20%20%20%20%20%20%20%20%20%20%20self.swap%28i,%20ma%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Continue%20sifting%20down%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20ma%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20max%20heap%0A%20%20%20%20%23%20Note%20that%20the%20input%20array%20is%20already%20a%20valid%20heap%0A%20%20%20%20max_heap%20%3D%20MaxHeap%28%5B9,%208,%207,%206,%207,%206,%202,%201,%204,%203,%206,%202,%205%5D%29%0A%0A%20%20%20%20%23%20Pop%20the%20top%20element%20from%20the%20heap%0A%20%20%20%20peek%20%3D%20max_heap.pop%28%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_heap/top_k.md b/en/codes/pythontutor/chapter_heap/top_k.md new file mode 100644 index 000000000..9462adaf1 --- /dev/null +++ b/en/codes/pythontutor/chapter_heap/top_k.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=import%20heapq%0A%0Adef%20top_k_heap%28nums%3A%20list%5Bint%5D,%20k%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22Use%20a%20heap%20to%20find%20the%20k%20largest%20elements%20in%20an%20array%22%22%22%0A%20%20%20%20%23%20Initialize%20a%20min%20heap%0A%20%20%20%20heap%20%3D%20%5B%5D%0A%20%20%20%20%23%20Push%20the%20first%20k%20array%20elements%20onto%20the%20heap%0A%20%20%20%20for%20i%20in%20range%28k%29%3A%0A%20%20%20%20%20%20%20%20heapq.heappush%28heap,%20nums%5Bi%5D%29%0A%20%20%20%20%23%20Starting%20with%20element%20k%2B1,%20keep%20the%20heap%20size%20at%20k%0A%20%20%20%20for%20i%20in%20range%28k,%20len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20If%20the%20current%20element%20is%20larger%20than%20the%20heap%20top,%20pop%20the%20top%20element%20and%20push%20the%20current%20element%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3E%20heap%5B0%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20heapq.heappop%28heap%29%0A%20%20%20%20%20%20%20%20%20%20%20%20heapq.heappush%28heap,%20nums%5Bi%5D%29%0A%20%20%20%20return%20heap%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%207,%206,%203,%202%5D%0A%20%20%20%20k%20%3D%203%0A%0A%20%20%20%20res%20%3D%20top_k_heap%28nums,%20k%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_searching/binary_search.md b/en/codes/pythontutor/chapter_searching/binary_search.md new file mode 100644 index 000000000..c88015687 --- /dev/null +++ b/en/codes/pythontutor/chapter_searching/binary_search.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20binary_search%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Binary%20search%20%28closed%20interval%29%22%22%22%0A%20%20%20%20%23%20Initialize%20the%20closed%20interval%20%5B0,%20n-1%5D,%20with%20i%20and%20j%20pointing%20to%20the%20first%20and%20last%20elements%0A%20%20%20%20i,%20j%20%3D%200,%20len%28nums%29%20-%201%0A%20%20%20%20%23%20Loop%20until%20the%20search%20interval%20is%20empty%20%28when%20i%20%3E%20j%29%0A%20%20%20%20while%20i%20%3C%3D%20j%3A%0A%20%20%20%20%20%20%20%20%23%20Python%20integers%20can%20grow%20arbitrarily%20large,%20subject%20to%20available%20memory,%20so%20integer%20overflow%20is%20not%20a%20concern%0A%20%20%20%20%20%20%20%20m%20%3D%20%28i%20%2B%20j%29%20//%202%20%20%23%20Calculate%20the%20midpoint%20index%20m%0A%20%20%20%20%20%20%20%20if%20nums%5Bm%5D%20%3C%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20m%20%2B%201%20%20%23%20In%20this%20case,%20target%20must%20be%20in%20%5Bm%2B1,%20j%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20elif%20nums%5Bm%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20In%20this%20case,%20target%20must%20be%20in%20%5Bi,%20m-1%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20m%20%20%23%20The%20target%20element%20was%20found%3B%20return%20its%20index%0A%20%20%20%20return%20-1%20%20%23%20The%20target%20element%20was%20not%20found%3B%20return%20-1%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20target%20%3D%206%0A%20%20%20%20nums%20%3D%20%5B1,%203,%206,%208,%2012,%2015,%2023,%2026,%2031,%2035%5D%0A%0A%20%20%20%20%23%20Binary%20search%20%28closed%20interval%29%0A%20%20%20%20index%20%3D%20binary_search%28nums,%20target%29%0A%20%20%20%20print%28%22Index%20of%20target%20element%206%20%3D%20%22,%20index%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20binary_search_lcro%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Binary%20search%20%28left-closed,%20right-open%20interval%29%22%22%22%0A%20%20%20%20%23%20Initialize%20the%20left-closed,%20right-open%20interval%20%5B0,%20n%29,%20with%20i%20at%20the%20first%20element%20and%20j%20one%20past%20the%20last%0A%20%20%20%20i,%20j%20%3D%200,%20len%28nums%29%0A%20%20%20%20%23%20Loop%20until%20the%20search%20interval%20is%20empty%20%28when%20i%20%3D%20j%29%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20%28i%20%2B%20j%29%20//%202%20%20%23%20Calculate%20the%20midpoint%20index%20m%0A%20%20%20%20%20%20%20%20if%20nums%5Bm%5D%20%3C%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20m%20%2B%201%20%20%23%20In%20this%20case,%20target%20must%20be%20in%20%5Bm%2B1,%20j%29%20if%20it%20exists%0A%20%20%20%20%20%20%20%20elif%20nums%5Bm%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20%20%23%20In%20this%20case,%20target%20must%20be%20in%20%5Bi,%20m%29%20if%20it%20exists%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20m%20%20%23%20The%20target%20element%20was%20found%3B%20return%20its%20index%0A%20%20%20%20return%20-1%20%20%23%20The%20target%20element%20was%20not%20found%3B%20return%20-1%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20target%20%3D%206%0A%20%20%20%20nums%20%3D%20%5B1,%203,%206,%208,%2012,%2015,%2023,%2026,%2031,%2035%5D%0A%0A%20%20%20%20%23%20Binary%20search%20%28left-closed,%20right-open%20interval%29%0A%20%20%20%20index%20%3D%20binary_search_lcro%28nums,%20target%29%0A%20%20%20%20print%28%22Index%20of%20target%20element%206%20%3D%20%22,%20index%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_searching/binary_search_edge.md b/en/codes/pythontutor/chapter_searching/binary_search_edge.md new file mode 100644 index 000000000..406c1ade7 --- /dev/null +++ b/en/codes/pythontutor/chapter_searching/binary_search_edge.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20binary_search_insertion%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20an%20insertion%20point%20using%20binary%20search%20%28with%20duplicates%29%22%22%22%0A%20%20%20%20i,%20j%20%3D%200,%20len%28nums%29%20-%201%20%20%23%20Initialize%20the%20closed%20interval%20%5B0,%20n-1%5D%0A%20%20%20%20while%20i%20%3C%3D%20j%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20%28i%20%2B%20j%29%20//%202%20%20%23%20Calculate%20the%20midpoint%20index%20m%0A%20%20%20%20%20%20%20%20if%20nums%5Bm%5D%20%3C%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20m%20%2B%201%20%20%23%20Target%20must%20be%20in%20%5Bm%2B1,%20j%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20elif%20nums%5Bm%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20Target%20must%20be%20in%20%5Bi,%20m-1%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20Search%20%5Bi,%20m-1%5D%20for%20the%20insertion%20point%20before%20equal%20elements%0A%20%20%20%20%23%20Return%20insertion%20point%20i%0A%20%20%20%20return%20i%0A%0Adef%20binary_search_left_edge%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20the%20leftmost%20occurrence%20of%20target%20using%20binary%20search%22%22%22%0A%20%20%20%20%23%20This%20is%20equivalent%20to%20finding%20the%20insertion%20point%20of%20target%0A%20%20%20%20i%20%3D%20binary_search_insertion%28nums,%20target%29%0A%20%20%20%20%23%20If%20target%20was%20not%20found,%20return%20-1%0A%20%20%20%20if%20i%20%3D%3D%20len%28nums%29%20or%20nums%5Bi%5D%20%21%3D%20target%3A%0A%20%20%20%20%20%20%20%20return%20-1%0A%20%20%20%20%23%20Target%20was%20found%3B%20return%20index%20i%0A%20%20%20%20return%20i%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Array%20containing%20duplicate%20elements%0A%20%20%20%20nums%20%3D%20%5B1,%203,%206,%206,%206,%206,%206,%2010,%2012,%2015%5D%0A%20%20%20%20%23%20Find%20a%20boundary%20using%20binary%20search%0A%20%20%20%20target%20%3D%206%0A%20%20%20%20index%20%3D%20binary_search_left_edge%28nums,%20target%29%0A%20%20%20%20print%28f%22Index%20of%20the%20leftmost%20occurrence%20of%20%7Btarget%7D%3A%20%7Bindex%7D%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20binary_search_insertion%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20an%20insertion%20point%20using%20binary%20search%20%28with%20duplicates%29%22%22%22%0A%20%20%20%20i,%20j%20%3D%200,%20len%28nums%29%20-%201%20%20%23%20Initialize%20the%20closed%20interval%20%5B0,%20n-1%5D%0A%20%20%20%20while%20i%20%3C%3D%20j%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20%28i%20%2B%20j%29%20//%202%20%20%23%20Calculate%20the%20midpoint%20index%20m%0A%20%20%20%20%20%20%20%20if%20nums%5Bm%5D%20%3C%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20m%20%2B%201%20%20%23%20Target%20must%20be%20in%20%5Bm%2B1,%20j%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20elif%20nums%5Bm%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20Target%20must%20be%20in%20%5Bi,%20m-1%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20Search%20%5Bi,%20m-1%5D%20for%20the%20insertion%20point%20before%20equal%20elements%0A%20%20%20%20%23%20Return%20insertion%20point%20i%0A%20%20%20%20return%20i%0A%0Adef%20binary_search_right_edge%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20the%20rightmost%20occurrence%20of%20target%20using%20binary%20search%22%22%22%0A%20%20%20%20%23%20Reduce%20the%20problem%20to%20finding%20the%20leftmost%20insertion%20point%20of%20target%20%2B%201%0A%20%20%20%20i%20%3D%20binary_search_insertion%28nums,%20target%20%2B%201%29%0A%20%20%20%20%23%20j%20is%20the%20candidate%20index%20of%20the%20rightmost%20target%3B%20i%20is%20the%20index%20of%20the%20first%20element%20greater%20than%20target,%20or%20len%28nums%29%0A%20%20%20%20j%20%3D%20i%20-%201%0A%20%20%20%20%23%20If%20target%20was%20not%20found,%20return%20-1%0A%20%20%20%20if%20j%20%3D%3D%20-1%20or%20nums%5Bj%5D%20%21%3D%20target%3A%0A%20%20%20%20%20%20%20%20return%20-1%0A%20%20%20%20%23%20Target%20was%20found%3B%20return%20index%20j%0A%20%20%20%20return%20j%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Array%20containing%20duplicate%20elements%0A%20%20%20%20nums%20%3D%20%5B1,%203,%206,%206,%206,%206,%206,%2010,%2012,%2015%5D%0A%20%20%20%20%23%20Find%20a%20boundary%20using%20binary%20search%0A%20%20%20%20target%20%3D%206%0A%20%20%20%20index%20%3D%20binary_search_right_edge%28nums,%20target%29%0A%20%20%20%20print%28f%22Index%20of%20the%20rightmost%20occurrence%20of%20%7Btarget%7D%3A%20%7Bindex%7D%22%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_searching/binary_search_insertion.md b/en/codes/pythontutor/chapter_searching/binary_search_insertion.md new file mode 100644 index 000000000..582e86647 --- /dev/null +++ b/en/codes/pythontutor/chapter_searching/binary_search_insertion.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20binary_search_insertion_simple%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20an%20insertion%20point%20using%20binary%20search%20%28no%20duplicates%29%22%22%22%0A%20%20%20%20i,%20j%20%3D%200,%20len%28nums%29%20-%201%20%20%23%20Initialize%20the%20closed%20interval%20%5B0,%20n-1%5D%0A%20%20%20%20while%20i%20%3C%3D%20j%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20%28i%20%2B%20j%29%20//%202%20%20%23%20Calculate%20the%20midpoint%20index%20m%0A%20%20%20%20%20%20%20%20if%20nums%5Bm%5D%20%3C%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20m%20%2B%201%20%20%23%20Target%20must%20be%20in%20%5Bm%2B1,%20j%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20elif%20nums%5Bm%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20Target%20must%20be%20in%20%5Bi,%20m-1%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20m%20%20%23%20Target%20was%20found%3B%20return%20insertion%20point%20m%0A%20%20%20%20%23%20Target%20was%20not%20found%3B%20return%20insertion%20point%20i%0A%20%20%20%20return%20i%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Array%20with%20no%20duplicate%20elements%0A%20%20%20%20nums%20%3D%20%5B1,%203,%206,%208,%2012,%2015,%2023,%2026,%2031,%2035%5D%0A%20%20%20%20%23%20Find%20an%20insertion%20point%20using%20binary%20search%0A%20%20%20%20target%20%3D%206%0A%20%20%20%20index%20%3D%20binary_search_insertion_simple%28nums,%20target%29%0A%20%20%20%20print%28f%22Insertion%20index%20for%20element%20%7Btarget%7D%3A%20%7Bindex%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20binary_search_insertion%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Find%20an%20insertion%20point%20using%20binary%20search%20%28with%20duplicates%29%22%22%22%0A%20%20%20%20i,%20j%20%3D%200,%20len%28nums%29%20-%201%20%20%23%20Initialize%20the%20closed%20interval%20%5B0,%20n-1%5D%0A%20%20%20%20while%20i%20%3C%3D%20j%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20%28i%20%2B%20j%29%20//%202%20%20%23%20Calculate%20the%20midpoint%20index%20m%0A%20%20%20%20%20%20%20%20if%20nums%5Bm%5D%20%3C%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%3D%20m%20%2B%201%20%20%23%20Target%20must%20be%20in%20%5Bm%2B1,%20j%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20elif%20nums%5Bm%5D%20%3E%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20Target%20must%20be%20in%20%5Bi,%20m-1%5D%20if%20it%20exists%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%3D%20m%20-%201%20%20%23%20Search%20%5Bi,%20m-1%5D%20for%20the%20insertion%20point%20before%20equal%20elements%0A%20%20%20%20%23%20Return%20insertion%20point%20i%0A%20%20%20%20return%20i%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Array%20containing%20duplicate%20elements%0A%20%20%20%20nums%20%3D%20%5B1,%203,%206,%206,%206,%206,%206,%2010,%2012,%2015%5D%0A%20%20%20%20%23%20Find%20an%20insertion%20point%20using%20binary%20search%0A%20%20%20%20target%20%3D%206%0A%20%20%20%20index%20%3D%20binary_search_insertion%28nums,%20target%29%0A%20%20%20%20print%28f%22Insertion%20index%20for%20element%20%7Btarget%7D%3A%20%7Bindex%7D%22%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_searching/two_sum.md b/en/codes/pythontutor/chapter_searching/two_sum.md new file mode 100644 index 000000000..917528f7a --- /dev/null +++ b/en/codes/pythontutor/chapter_searching/two_sum.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20two_sum_brute_force%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22Method%201%3A%20brute-force%20enumeration%22%22%22%0A%20%20%20%20%23%20Two%20nested%20loops%20give%20a%20time%20complexity%20of%20O%28n%5E2%29%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201%29%3A%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%20%2B%201,%20len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%2B%20nums%5Bj%5D%20%3D%3D%20target%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%5Bi,%20j%5D%0A%20%20%20%20return%20%5B%5D%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B2,%207,%2011,%2015%5D%0A%20%20%20%20target%20%3D%2013%0A%20%20%20%20res%20%3D%20two_sum_brute_force%28nums,%20target%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20two_sum_hash_table%28nums%3A%20list%5Bint%5D,%20target%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22Method%202%3A%20auxiliary%20hash%20table%22%22%22%0A%20%20%20%20%23%20The%20auxiliary%20hash%20table%20uses%20O%28n%29%20space%0A%20%20%20%20dic%20%3D%20%7B%7D%0A%20%20%20%20%23%20A%20single%20loop%20gives%20average%20O%28n%29%20time%20with%20average%20O%281%29%20hash-table%20operations%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20if%20target%20-%20nums%5Bi%5D%20in%20dic%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%5Bdic%5Btarget%20-%20nums%5Bi%5D%5D,%20i%5D%0A%20%20%20%20%20%20%20%20dic%5Bnums%5Bi%5D%5D%20%3D%20i%0A%20%20%20%20return%20%5B%5D%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B2,%207,%2011,%2015%5D%0A%20%20%20%20target%20%3D%2013%0A%20%20%20%20res%20%3D%20two_sum_hash_table%28nums,%20target%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/bubble_sort.md b/en/codes/pythontutor/chapter_sorting/bubble_sort.md new file mode 100644 index 000000000..ec75be9ae --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/bubble_sort.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20bubble_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Bubble%20sort%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20Outer%20loop%3A%20the%20unsorted%20range%20is%20%5B0,%20i%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20Inner%20loop%3A%20move%20the%20largest%20element%20in%20the%20unsorted%20range%20%5B0,%20i%5D%20to%20its%20right%20end%20through%20swaps%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Swap%20nums%5Bj%5D%20and%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D,%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%20%2B%201%5D,%20nums%5Bj%5D%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20bubble_sort%28nums%29%0A%20%20%20%20print%28%22After%20bubble%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20bubble_sort_with_flag%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Bubble%20sort%20%28flag%20optimization%29%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20Outer%20loop%3A%20the%20unsorted%20range%20is%20%5B0,%20i%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20flag%20%3D%20False%20%20%23%20Initialize%20the%20flag%0A%20%20%20%20%20%20%20%20%23%20Inner%20loop%3A%20move%20the%20largest%20element%20in%20the%20unsorted%20range%20%5B0,%20i%5D%20to%20its%20right%20end%20through%20swaps%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Swap%20nums%5Bj%5D%20and%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D,%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%20%2B%201%5D,%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20flag%20%3D%20True%20%20%23%20Record%20that%20a%20swap%20occurred%0A%20%20%20%20%20%20%20%20if%20not%20flag%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%20%20%23%20Exit%20immediately%20if%20this%20pass%20made%20no%20swaps%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20bubble_sort_with_flag%28nums%29%0A%20%20%20%20print%28%22After%20bubble%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/bucket_sort.md b/en/codes/pythontutor/chapter_sorting/bucket_sort.md new file mode 100644 index 000000000..e32c4d209 --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/bucket_sort.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20bucket_sort%28nums%3A%20list%5Bfloat%5D%29%3A%0A%20%20%20%20%22%22%22Bucket%20sort%22%22%22%0A%20%20%20%20%23%20Initialize%20k%20%3D%20n/2%20buckets,%20expecting%202%20elements%20per%20bucket%0A%20%20%20%20k%20%3D%20len%28nums%29%20//%202%0A%20%20%20%20buckets%20%3D%20%5B%5B%5D%20for%20_%20in%20range%28k%29%5D%0A%20%20%20%20%23%201.%20Distribute%20the%20array%20elements%20among%20the%20buckets%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20%23%20The%20input%20range%20is%20%5B0,%201%29%3B%20use%20num%20%2A%20k%20to%20map%20each%20value%20to%20an%20index%20in%20%5B0,%20k-1%5D%0A%20%20%20%20%20%20%20%20i%20%3D%20int%28num%20%2A%20k%29%0A%20%20%20%20%20%20%20%20%23%20Add%20num%20to%20bucket%20i%0A%20%20%20%20%20%20%20%20buckets%5Bi%5D.append%28num%29%0A%20%20%20%20%23%202.%20Sort%20each%20bucket%0A%20%20%20%20for%20bucket%20in%20buckets%3A%0A%20%20%20%20%20%20%20%20%23%20Use%20the%20built-in%20sorting%20function,%20or%20replace%20it%20with%20another%20sorting%20algorithm%0A%20%20%20%20%20%20%20%20bucket.sort%28%29%0A%20%20%20%20%23%203.%20Traverse%20the%20buckets%20to%20combine%20the%20results%0A%20%20%20%20i%20%3D%200%0A%20%20%20%20for%20bucket%20in%20buckets%3A%0A%20%20%20%20%20%20%20%20for%20num%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Assume%20the%20input%20consists%20of%20floating-point%20numbers%20in%20%5B0,%201%29%0A%20%20%20%20nums%20%3D%20%5B0.49,%200.96,%200.82,%200.09,%200.57,%200.43,%200.91,%200.75,%200.15,%200.37%5D%0A%20%20%20%20bucket_sort%28nums%29%0A%20%20%20%20print%28%22After%20bucket%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/counting_sort.md b/en/codes/pythontutor/chapter_sorting/counting_sort.md new file mode 100644 index 000000000..9fa31d2da --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/counting_sort.md @@ -0,0 +1,11 @@ + + + +https://pythontutor.com/render.html#code=def%20counting_sort_naive%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Counting%20sort%22%22%22%0A%20%20%20%20%23%20Simple%20implementation%3B%20cannot%20be%20used%20to%20sort%20objects%0A%20%20%20%20%23%201.%20Find%20the%20maximum%20array%20element%20m%0A%20%20%20%20m%20%3D%200%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20max%28m,%20num%29%0A%20%20%20%20%23%202.%20Count%20the%20occurrences%20of%20each%20number%0A%20%20%20%20%23%20counter%5Bnum%5D%20is%20the%20number%20of%20occurrences%20of%20num%0A%20%20%20%20counter%20%3D%20%5B0%5D%20%2A%20%28m%20%2B%201%29%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20%2B%3D%201%0A%20%20%20%20%23%203.%20Traverse%20counter%20and%20fill%20the%20original%20array%20nums%20with%20the%20elements%0A%20%20%20%20i%20%3D%200%0A%20%20%20%20for%20num%20in%20range%28m%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20_%20in%20range%28counter%5Bnum%5D%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%200,%201,%202,%200,%204,%200,%202,%202,%204%5D%0A%20%20%20%20counting_sort_naive%28nums%29%0A%20%20%20%20print%28f%22After%20counting%20sort%20%28without%20support%20for%20objects%29,%20nums%20%3D%20%7Bnums%7D%22%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20counting_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Counting%20sort%22%22%22%0A%20%20%20%20%23%20Full%20implementation%3B%20stable%20and%20adaptable%20to%20sorting%20objects%0A%20%20%20%20%23%201.%20Find%20the%20maximum%20array%20element%20m%0A%20%20%20%20m%20%3D%20max%28nums%29%0A%20%20%20%20%23%202.%20Count%20the%20occurrences%20of%20each%20number%0A%20%20%20%20%23%20counter%5Bnum%5D%20is%20the%20number%20of%20occurrences%20of%20num%0A%20%20%20%20counter%20%3D%20%5B0%5D%20%2A%20%28m%20%2B%201%29%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20%2B%3D%201%0A%20%20%20%20%23%203.%20Compute%20prefix%20sums%20of%20counter%20to%20convert%20occurrence%20counts%20into%20end%20positions%0A%20%20%20%20%23%20That%20is,%20counter%5Bnum%5D-1%20is%20the%20index%20of%20the%20last%20occurrence%20of%20num%20in%20res%0A%20%20%20%20for%20i%20in%20range%28m%29%3A%0A%20%20%20%20%20%20%20%20counter%5Bi%20%2B%201%5D%20%2B%3D%20counter%5Bi%5D%0A%20%20%20%20%23%204.%20Traverse%20nums%20in%20reverse%20and%20place%20each%20element%20in%20the%20result%20array%20res%0A%20%20%20%20%23%20Initialize%20array%20res%20to%20store%20the%20result%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20res%20%3D%20%5B0%5D%20%2A%20n%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20num%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20res%5Bcounter%5Bnum%5D%20-%201%5D%20%3D%20num%20%20%23%20Place%20num%20at%20the%20corresponding%20index%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20-%3D%201%20%20%23%20Decrement%20the%20prefix%20sum%20by%201%20to%20determine%20where%20to%20place%20the%20next%20occurrence%20of%20num%0A%20%20%20%20%23%20Overwrite%20the%20original%20array%20nums%20with%20the%20result%20array%20res%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20res%5Bi%5D%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%200,%201,%202,%200,%204,%200,%202,%202,%204%5D%0A%20%20%20%20counting_sort%28nums%29%0A%20%20%20%20print%28f%22After%20counting%20sort,%20nums%20%3D%20%7Bnums%7D%22%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/heap_sort.md b/en/codes/pythontutor/chapter_sorting/heap_sort.md new file mode 100644 index 000000000..4ba5fee1c --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/heap_sort.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20sift_down%28nums%3A%20list%5Bint%5D,%20n%3A%20int,%20i%3A%20int%29%3A%0A%20%20%20%20%22%22%22Sift%20down%20from%20node%20i%20in%20a%20heap%20of%20size%20n%22%22%22%0A%20%20%20%20while%20True%3A%0A%20%20%20%20%20%20%20%20%23%20Find%20the%20node%20with%20the%20largest%20value%20among%20i,%20l,%20and%20r,%20and%20store%20its%20index%20in%20ma%0A%20%20%20%20%20%20%20%20l%20%3D%202%20%2A%20i%20%2B%201%0A%20%20%20%20%20%20%20%20r%20%3D%202%20%2A%20i%20%2B%202%0A%20%20%20%20%20%20%20%20ma%20%3D%20i%0A%20%20%20%20%20%20%20%20if%20l%20%3C%20n%20and%20nums%5Bl%5D%20%3E%20nums%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20l%0A%20%20%20%20%20%20%20%20if%20r%20%3C%20n%20and%20nums%5Br%5D%20%3E%20nums%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20r%0A%20%20%20%20%20%20%20%20%23%20Stop%20if%20node%20i%20is%20largest%20or%20both%20child%20indices%20l%20and%20r%20are%20out%20of%20bounds%0A%20%20%20%20%20%20%20%20if%20ma%20%3D%3D%20i%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20two%20nodes%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bma%5D%20%3D%20nums%5Bma%5D,%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20%23%20Continue%20sifting%20down%0A%20%20%20%20%20%20%20%20i%20%3D%20ma%0A%0Adef%20heap_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Heap%20sort%22%22%22%0A%20%20%20%20%23%20Build%20the%20heap%20by%20sifting%20down%20from%20every%20non-leaf%20node%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20//%202%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20sift_down%28nums,%20len%28nums%29,%20i%29%0A%20%20%20%20%23%20Extract%20the%20largest%20element%20from%20the%20heap,%20repeating%20for%20n-1%20rounds%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20root%20with%20the%20rightmost%20leaf%20on%20the%20last%20level%20%28the%20first%20and%20last%20elements%20of%20the%20current%20heap%29%0A%20%20%20%20%20%20%20%20nums%5B0%5D,%20nums%5Bi%5D%20%3D%20nums%5Bi%5D,%20nums%5B0%5D%0A%20%20%20%20%20%20%20%20%23%20Sift%20down%20starting%20at%20the%20root%0A%20%20%20%20%20%20%20%20sift_down%28nums,%20i,%200%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20heap_sort%28nums%29%0A%20%20%20%20print%28%22After%20heap%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/insertion_sort.md b/en/codes/pythontutor/chapter_sorting/insertion_sort.md new file mode 100644 index 000000000..f43381fb9 --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/insertion_sort.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20insertion_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Insertion%20sort%22%22%22%0A%20%20%20%20%23%20Outer%20loop%3A%20the%20sorted%20range%20is%20%5B0,%20i-1%5D%0A%20%20%20%20for%20i%20in%20range%281,%20len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20base%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20j%20%3D%20i%20-%201%0A%20%20%20%20%20%20%20%20%23%20Inner%20loop%3A%20insert%20base%20at%20the%20correct%20position%20in%20the%20sorted%20range%20%5B0,%20i-1%5D%0A%20%20%20%20%20%20%20%20while%20j%20%3E%3D%200%20and%20nums%5Bj%5D%20%3E%20base%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%5D%20%20%23%20Shift%20nums%5Bj%5D%20one%20position%20to%20the%20right%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20base%20%20%23%20Place%20base%20at%20the%20correct%20position%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20insertion_sort%28nums%29%0A%20%20%20%20print%28%22After%20insertion%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/merge_sort.md b/en/codes/pythontutor/chapter_sorting/merge_sort.md new file mode 100644 index 000000000..be2a75922 --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/merge_sort.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20merge%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20mid%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22Merge%20the%20left%20and%20right%20subarrays%22%22%22%0A%20%20%20%20%23%20The%20left%20subarray%20spans%20%5Bleft,%20mid%5D,%20and%20the%20right%20subarray%20spans%20%5Bmid%2B1,%20right%5D%0A%20%20%20%20%23%20Create%20a%20temporary%20array%20tmp%20to%20store%20the%20merged%20result%0A%20%20%20%20tmp%20%3D%20%5B0%5D%20%2A%20%28right%20-%20left%20%2B%201%29%0A%20%20%20%20%23%20Initialize%20the%20starting%20indices%20of%20the%20left%20and%20right%20subarrays%0A%20%20%20%20i,%20j,%20k%20%3D%20left,%20mid%20%2B%201,%200%0A%20%20%20%20%23%20While%20both%20subarrays%20have%20elements%20remaining,%20compare%20them%20and%20copy%20the%20smaller%20element%20into%20the%20temporary%20array%0A%20%20%20%20while%20i%20%3C%3D%20mid%20and%20j%20%3C%3D%20right%3A%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3C%3D%20nums%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20%23%20Copy%20the%20remaining%20elements%20of%20the%20left%20and%20right%20subarrays%20into%20the%20temporary%20array%0A%20%20%20%20while%20i%20%3C%3D%20mid%3A%0A%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20while%20j%20%3C%3D%20right%3A%0A%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20j%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20%23%20Copy%20the%20elements%20of%20temporary%20array%20tmp%20back%20into%20the%20corresponding%20range%20of%20the%20original%20array%20nums%0A%20%20%20%20for%20k%20in%20range%280,%20len%28tmp%29%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bleft%20%2B%20k%5D%20%3D%20tmp%5Bk%5D%0A%0A%0Adef%20merge_sort%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22Merge%20sort%22%22%22%0A%20%20%20%20%23%20Base%20case%0A%20%20%20%20if%20left%20%3E%3D%20right%3A%0A%20%20%20%20%20%20%20%20return%20%20%23%20Stop%20recursing%20when%20the%20subarray%20has%20at%20most%20one%20element%0A%20%20%20%20%23%20Divide%20phase%0A%20%20%20%20mid%20%3D%20%28left%20%2B%20right%29%20//%202%20%20%23%20Calculate%20the%20midpoint%0A%20%20%20%20merge_sort%28nums,%20left,%20mid%29%20%20%23%20Recurse%20on%20the%20left%20subarray%0A%20%20%20%20merge_sort%28nums,%20mid%20%2B%201,%20right%29%20%20%23%20Recurse%20on%20the%20right%20subarray%0A%20%20%20%20%23%20Merge%20phase%0A%20%20%20%20merge%28nums,%20left,%20mid,%20right%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B7,%203,%202,%206,%200,%201,%205,%204%5D%0A%20%20%20%20merge_sort%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22After%20merge%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/quick_sort.md b/en/codes/pythontutor/chapter_sorting/quick_sort.md new file mode 100644 index 000000000..1ea8a657a --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/quick_sort.md @@ -0,0 +1,17 @@ + + + +https://pythontutor.com/render.html#code=def%20partition%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Two-pointer%20partitioning%22%22%22%0A%20%20%20%20%23%20Partition%20using%20the%20pivot%20stored%20at%20nums%5Bleft%5D%0A%20%20%20%20i,%20j%20%3D%20left,%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20Scan%20from%20right%20to%20left%20for%20the%20first%20element%20smaller%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20Scan%20from%20left%20to%20right%20for%20the%20first%20element%20larger%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20elements%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bj%5D%20%3D%20nums%5Bj%5D,%20nums%5Bi%5D%0A%20%20%20%20%23%20Swap%20the%20pivot%20into%20the%20boundary%20position%20between%20the%20two%20subarrays%0A%20%20%20%20nums%5Bi%5D,%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D,%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20Return%20the%20pivot%20index%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B2,%204,%201,%200,%203,%205%5D%0A%20%20%20%20partition%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22After%20two-pointer%20partitioning,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20partition%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Two-pointer%20partitioning%22%22%22%0A%20%20%20%20%23%20Partition%20using%20the%20pivot%20stored%20at%20nums%5Bleft%5D%0A%20%20%20%20i,%20j%20%3D%20left,%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20Scan%20from%20right%20to%20left%20for%20the%20first%20element%20smaller%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20Scan%20from%20left%20to%20right%20for%20the%20first%20element%20larger%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20elements%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bj%5D%20%3D%20nums%5Bj%5D,%20nums%5Bi%5D%0A%20%20%20%20%23%20Swap%20the%20pivot%20into%20the%20boundary%20position%20between%20the%20two%20subarrays%0A%20%20%20%20nums%5Bi%5D,%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D,%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20Return%20the%20pivot%20index%0A%0Adef%20quick_sort%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22Quick%20sort%22%22%22%0A%20%20%20%20%23%20Stop%20recursing%20when%20the%20subarray%20has%20at%20most%20one%20element%0A%20%20%20%20if%20left%20%3E%3D%20right%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Two-pointer%20partitioning%0A%20%20%20%20pivot%20%3D%20partition%28nums,%20left,%20right%29%0A%20%20%20%20%23%20Recurse%20on%20the%20left%20and%20right%20subarrays%0A%20%20%20%20quick_sort%28nums,%20left,%20pivot%20-%201%29%0A%20%20%20%20quick_sort%28nums,%20pivot%20%2B%201,%20right%29%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Quick%20sort%0A%20%20%20%20nums%20%3D%20%5B2,%204,%201,%200,%203,%205%5D%0A%20%20%20%20quick_sort%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22After%20quick%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20median_three%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20mid%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Select%20the%20median%20of%20three%20candidate%20elements%22%22%22%0A%20%20%20%20l,%20m,%20r%20%3D%20nums%5Bleft%5D,%20nums%5Bmid%5D,%20nums%5Bright%5D%0A%20%20%20%20if%20%28l%20%3C%3D%20m%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20m%20%3C%3D%20l%29%3A%0A%20%20%20%20%20%20%20%20return%20mid%20%20%23%20m%20lies%20between%20l%20and%20r%0A%20%20%20%20if%20%28m%20%3C%3D%20l%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20l%20%3C%3D%20m%29%3A%0A%20%20%20%20%20%20%20%20return%20left%20%20%23%20l%20lies%20between%20m%20and%20r%0A%20%20%20%20return%20right%0A%0Adef%20partition%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Two-pointer%20partitioning%20%28median%20of%20three%29%22%22%22%0A%20%20%20%20%23%20Partition%20using%20the%20pivot%20stored%20at%20nums%5Bleft%5D%0A%20%20%20%20med%20%3D%20median_three%28nums,%20left,%20%28left%20%2B%20right%29%20//%202,%20right%29%0A%20%20%20%20%23%20Swap%20the%20median%20into%20the%20leftmost%20position%20of%20the%20current%20subarray%0A%20%20%20%20nums%5Bleft%5D,%20nums%5Bmed%5D%20%3D%20nums%5Bmed%5D,%20nums%5Bleft%5D%0A%20%20%20%20%23%20Partition%20using%20the%20pivot%20stored%20at%20nums%5Bleft%5D%0A%20%20%20%20i,%20j%20%3D%20left,%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20Scan%20from%20right%20to%20left%20for%20the%20first%20element%20smaller%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20Scan%20from%20left%20to%20right%20for%20the%20first%20element%20larger%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20elements%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bj%5D%20%3D%20nums%5Bj%5D,%20nums%5Bi%5D%0A%20%20%20%20%23%20Swap%20the%20pivot%20into%20the%20boundary%20position%20between%20the%20two%20subarrays%0A%20%20%20%20nums%5Bi%5D,%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D,%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20Return%20the%20pivot%20index%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Median-of-three%20pivot%20optimization%0A%20%20%20%20nums%20%3D%20%5B2,%204,%201,%200,%203,%205%5D%0A%20%20%20%20partition%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22After%20partitioning%20with%20a%20median-of-three%20pivot,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=def%20partition%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Two-pointer%20partitioning%22%22%22%0A%20%20%20%20%23%20Partition%20using%20the%20pivot%20stored%20at%20nums%5Bleft%5D%0A%20%20%20%20i,%20j%20%3D%20left,%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20Scan%20from%20right%20to%20left%20for%20the%20first%20element%20smaller%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20Scan%20from%20left%20to%20right%20for%20the%20first%20element%20larger%20than%20the%20pivot%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20elements%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bj%5D%20%3D%20nums%5Bj%5D,%20nums%5Bi%5D%0A%20%20%20%20%23%20Swap%20the%20pivot%20into%20the%20boundary%20position%20between%20the%20two%20subarrays%0A%20%20%20%20nums%5Bi%5D,%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D,%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20Return%20the%20pivot%20index%0A%0Adef%20quick_sort%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22Quick%20sort%20%28recursion%20depth%20optimization%29%22%22%22%0A%20%20%20%20%23%20Stop%20when%20the%20subarray%20has%20at%20most%20one%20element%0A%20%20%20%20while%20left%20%3C%20right%3A%0A%20%20%20%20%20%20%20%20%23%20Perform%20two-pointer%20partitioning%0A%20%20%20%20%20%20%20%20pivot%20%3D%20partition%28nums,%20left,%20right%29%0A%20%20%20%20%20%20%20%20%23%20Apply%20quick%20sort%20to%20the%20shorter%20of%20the%20two%20subarrays%0A%20%20%20%20%20%20%20%20if%20pivot%20-%20left%20%3C%20right%20-%20pivot%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20quick_sort%28nums,%20left,%20pivot%20-%201%29%20%20%23%20Recursively%20sort%20the%20left%20subarray%0A%20%20%20%20%20%20%20%20%20%20%20%20left%20%3D%20pivot%20%2B%201%20%20%23%20The%20remaining%20unsorted%20range%20is%20%5Bpivot%20%2B%201,%20right%5D%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20quick_sort%28nums,%20pivot%20%2B%201,%20right%29%20%20%23%20Recursively%20sort%20the%20right%20subarray%0A%20%20%20%20%20%20%20%20%20%20%20%20right%20%3D%20pivot%20-%201%20%20%23%20The%20remaining%20unsorted%20range%20is%20%5Bleft,%20pivot%20-%201%5D%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Quick%20sort%20%28recursion%20depth%20optimization%29%0A%20%20%20%20nums%20%3D%20%5B2,%204,%201,%200,%203,%205%5D%0A%20%20%20%20quick_sort%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22After%20quick%20sort%20with%20recursion%20depth%20optimization,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/radix_sort.md b/en/codes/pythontutor/chapter_sorting/radix_sort.md new file mode 100644 index 000000000..7d7f88752 --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/radix_sort.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20digit%28num%3A%20int,%20exp%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22Get%20the%20k-th%20digit%20of%20num,%20where%20exp%20%3D%2010%5E%28k-1%29%22%22%22%0A%20%20%20%20%23%20Passing%20exp%20instead%20of%20k%20avoids%20repeatedly%20performing%20expensive%20exponentiation%20here%0A%20%20%20%20return%20%28num%20//%20exp%29%20%25%2010%0A%0Adef%20counting_sort_digit%28nums%3A%20list%5Bint%5D,%20exp%3A%20int%29%3A%0A%20%20%20%20%22%22%22Counting%20sort%20%28sort%20nums%20by%20the%20k-th%20digit%29%22%22%22%0A%20%20%20%20%23%20Decimal%20digits%20range%20from%200%20to%209,%20so%20the%20bucket%20array%20needs%20length%2010%0A%20%20%20%20counter%20%3D%20%5B0%5D%20%2A%2010%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20Count%20the%20occurrences%20of%20each%20digit%20from%200%20to%209%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20d%20%3D%20digit%28nums%5Bi%5D,%20exp%29%20%20%23%20Get%20the%20k-th%20digit%20of%20nums%5Bi%5D%20and%20store%20it%20in%20d%0A%20%20%20%20%20%20%20%20counter%5Bd%5D%20%2B%3D%201%20%20%23%20Count%20the%20occurrences%20of%20digit%20d%0A%20%20%20%20%23%20Compute%20prefix%20sums%20to%20convert%20occurrence%20counts%20into%20array%20positions%0A%20%20%20%20for%20i%20in%20range%281,%2010%29%3A%0A%20%20%20%20%20%20%20%20counter%5Bi%5D%20%2B%3D%20counter%5Bi%20-%201%5D%0A%20%20%20%20%23%20Traverse%20in%20reverse%20and%20place%20each%20element%20into%20res%20according%20to%20the%20bucket%20counts%0A%20%20%20%20res%20%3D%20%5B0%5D%20%2A%20n%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20d%20%3D%20digit%28nums%5Bi%5D,%20exp%29%0A%20%20%20%20%20%20%20%20j%20%3D%20counter%5Bd%5D%20-%201%20%20%23%20Get%20the%20destination%20index%20j%20for%20digit%20d%20in%20the%20array%0A%20%20%20%20%20%20%20%20res%5Bj%5D%20%3D%20nums%5Bi%5D%20%20%23%20Place%20the%20current%20element%20at%20index%20j%0A%20%20%20%20%20%20%20%20counter%5Bd%5D%20-%3D%201%20%20%23%20Decrease%20the%20count%20for%20d%20by%201%0A%20%20%20%20%23%20Overwrite%20the%20original%20array%20nums%20with%20the%20result%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20res%5Bi%5D%0A%0Adef%20radix_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Radix%20sort%22%22%22%0A%20%20%20%20%23%20Get%20the%20largest%20array%20element%20to%20determine%20the%20maximum%20number%20of%20digits%0A%20%20%20%20m%20%3D%20max%28nums%29%0A%20%20%20%20%23%20Process%20digits%20from%20least%20significant%20to%20most%20significant%0A%20%20%20%20exp%20%3D%201%0A%20%20%20%20while%20exp%20%3C%3D%20m%3A%0A%20%20%20%20%20%20%20%20%23%20Apply%20counting%20sort%20to%20the%20k-th%20digit%20of%20the%20array%20elements%0A%20%20%20%20%20%20%20%20%23%20k%20%3D%201%20-%3E%20exp%20%3D%201%0A%20%20%20%20%20%20%20%20%23%20k%20%3D%202%20-%3E%20exp%20%3D%2010%0A%20%20%20%20%20%20%20%20%23%20That%20is,%20exp%20%3D%2010%5E%28k-1%29%0A%20%20%20%20%20%20%20%20counting_sort_digit%28nums,%20exp%29%0A%20%20%20%20%20%20%20%20exp%20%2A%3D%2010%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Radix%20sort%0A%20%20%20%20nums%20%3D%20%5B%0A%20%20%20%20%20%20%20%20105,%0A%20%20%20%20%20%20%20%20356,%0A%20%20%20%20%20%20%20%20428,%0A%20%20%20%20%20%20%20%20348,%0A%20%20%20%20%20%20%20%20818,%0A%20%20%20%20%5D%0A%20%20%20%20radix_sort%28nums%29%0A%20%20%20%20print%28%22After%20radix%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_sorting/selection_sort.md b/en/codes/pythontutor/chapter_sorting/selection_sort.md new file mode 100644 index 000000000..f833f6047 --- /dev/null +++ b/en/codes/pythontutor/chapter_sorting/selection_sort.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=def%20selection_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22Selection%20sort%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20Outer%20loop%3A%20the%20unsorted%20range%20is%20%5Bi,%20n-1%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20Inner%20loop%3A%20find%20the%20smallest%20element%20in%20the%20unsorted%20range%0A%20%20%20%20%20%20%20%20k%20%3D%20i%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%20%2B%201,%20n%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3C%20nums%5Bk%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20k%20%3D%20j%20%20%23%20Record%20the%20index%20of%20the%20smallest%20element%0A%20%20%20%20%20%20%20%20%23%20Swap%20the%20smallest%20element%20with%20the%20first%20element%20of%20the%20unsorted%20range%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bk%5D%20%3D%20nums%5Bk%5D,%20nums%5Bi%5D%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20selection_sort%28nums%29%0A%20%20%20%20print%28%22After%20selection%20sort,%20nums%20%3D%22,%20nums%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_stack_and_queue/array_queue.md b/en/codes/pythontutor/chapter_stack_and_queue/array_queue.md new file mode 100644 index 000000000..9dc655b16 --- /dev/null +++ b/en/codes/pythontutor/chapter_stack_and_queue/array_queue.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20ArrayQueue%3A%0A%20%20%20%20%22%22%22Queue%20implemented%20with%20a%20circular%20array%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20size%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self._nums%3A%20list%5Bint%5D%20%3D%20%5B0%5D%20%2A%20size%20%20%23%20Array%20that%20stores%20the%20queue%20elements%0A%20%20%20%20%20%20%20%20self._front%3A%20int%20%3D%200%20%20%23%20Front%20pointer,%20pointing%20to%20the%20first%20element%0A%20%20%20%20%20%20%20%20self._size%3A%20int%20%3D%200%20%20%23%20Queue%20size%0A%0A%20%20%20%20def%20capacity%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20queue%20capacity%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self._nums%29%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20queue%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._size%0A%0A%20%20%20%20def%20is_empty%28self%29%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%22%22%22Check%20whether%20the%20queue%20is%20empty%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._size%20%3D%3D%200%0A%0A%20%20%20%20def%20push%28self,%20num%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Enqueue%22%22%22%0A%20%20%20%20%20%20%20%20if%20self._size%20%3D%3D%20self.capacity%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Queue%20is%20full%22%29%0A%20%20%20%20%20%20%20%20%23%20Calculate%20the%20rear%20pointer,%20which%20points%20one%20position%20past%20the%20last%20element%0A%20%20%20%20%20%20%20%20%23%20Use%20modulo%20to%20wrap%20rear%20to%20the%20beginning%20when%20it%20passes%20the%20end%20of%20the%20array%0A%20%20%20%20%20%20%20%20rear%3A%20int%20%3D%20%28self._front%20%2B%20self._size%29%20%25%20self.capacity%28%29%0A%20%20%20%20%20%20%20%20%23%20Add%20num%20to%20the%20rear%0A%20%20%20%20%20%20%20%20self._nums%5Brear%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20self._size%20%2B%3D%201%0A%0A%20%20%20%20def%20pop%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Dequeue%22%22%22%0A%20%20%20%20%20%20%20%20num%3A%20int%20%3D%20self.peek%28%29%0A%20%20%20%20%20%20%20%20%23%20Advance%20the%20front%20pointer%20by%20one%20position,%20wrapping%20to%20the%20beginning%20if%20it%20passes%20the%20end%0A%20%20%20%20%20%20%20%20self._front%20%3D%20%28self._front%20%2B%201%29%20%25%20self.capacity%28%29%0A%20%20%20%20%20%20%20%20self._size%20-%3D%201%0A%20%20%20%20%20%20%20%20return%20num%0A%0A%20%20%20%20def%20peek%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Access%20the%20front%20element%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.is_empty%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Queue%20is%20empty%22%29%0A%20%20%20%20%20%20%20%20return%20self._nums%5Bself._front%5D%0A%0A%20%20%20%20def%20to_list%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Return%20a%20list%20for%20printing%22%22%22%0A%20%20%20%20%20%20%20%20res%20%3D%20%5B0%5D%20%2A%20self.size%28%29%0A%20%20%20%20%20%20%20%20j%3A%20int%20%3D%20self._front%0A%20%20%20%20%20%20%20%20for%20i%20in%20range%28self.size%28%29%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20res%5Bi%5D%20%3D%20self._nums%5B%28j%20%25%20self.capacity%28%29%29%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%2B%3D%201%0A%20%20%20%20%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20queue%0A%20%20%20%20queue%20%3D%20ArrayQueue%2810%29%0A%0A%20%20%20%20%23%20Enqueue%20elements%0A%20%20%20%20queue.push%281%29%0A%20%20%20%20queue.push%283%29%0A%20%20%20%20queue.push%282%29%0A%20%20%20%20queue.push%285%29%0A%20%20%20%20queue.push%284%29%0A%0A%20%20%20%20%23%20Access%20the%20front%20element%0A%20%20%20%20peek%20%3D%20queue.peek%28%29%0A%20%20%20%20print%28%22Front%20element%20peek%20%3D%22,%20peek%29%0A%0A%20%20%20%20%23%20Dequeue%20an%20element%0A%20%20%20%20pop%20%3D%20queue.pop%28%29%0A%20%20%20%20print%28%22Dequeued%20element%20pop%20%3D%22,%20pop%29%0A%0A%20%20%20%20%23%20Get%20the%20queue%20size%0A%20%20%20%20size%20%3D%20queue.size%28%29%0A%20%20%20%20print%28%22Queue%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20Check%20whether%20the%20queue%20is%20empty%0A%20%20%20%20is_empty%20%3D%20queue.is_empty%28%29%0A%20%20%20%20print%28%22Is%20the%20queue%20empty%20%3D%22,%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_stack_and_queue/array_stack.md b/en/codes/pythontutor/chapter_stack_and_queue/array_stack.md new file mode 100644 index 000000000..c98295a5e --- /dev/null +++ b/en/codes/pythontutor/chapter_stack_and_queue/array_stack.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20ArrayStack%3A%0A%20%20%20%20%22%22%22Stack%20implemented%20with%20an%20array%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self._stack%3A%20list%5Bint%5D%20%3D%20%5B%5D%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20stack%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self._stack%29%0A%0A%20%20%20%20def%20is_empty%28self%29%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%22%22%22Check%20whether%20the%20stack%20is%20empty%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._stack%20%3D%3D%20%5B%5D%0A%0A%20%20%20%20def%20push%28self,%20item%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Push%22%22%22%0A%20%20%20%20%20%20%20%20self._stack.append%28item%29%0A%0A%20%20%20%20def%20pop%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Pop%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.is_empty%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Stack%20is%20empty%22%29%0A%20%20%20%20%20%20%20%20return%20self._stack.pop%28%29%0A%0A%20%20%20%20def%20peek%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Access%20the%20top%20element%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.is_empty%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Stack%20is%20empty%22%29%0A%20%20%20%20%20%20%20%20return%20self._stack%5B-1%5D%0A%0A%20%20%20%20def%20to_list%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Return%20a%20list%20for%20printing%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._stack%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20stack%0A%20%20%20%20stack%20%3D%20ArrayStack%28%29%0A%0A%20%20%20%20%23%20Push%20elements%20onto%20the%20stack%0A%20%20%20%20stack.push%281%29%0A%20%20%20%20stack.push%283%29%0A%20%20%20%20stack.push%282%29%0A%20%20%20%20stack.push%285%29%0A%20%20%20%20stack.push%284%29%0A%20%20%20%20print%28%22Stack%20stack%20%3D%22,%20stack.to_list%28%29%29%0A%0A%20%20%20%20%23%20Access%20the%20top%20element%0A%20%20%20%20peek%20%3D%20stack.peek%28%29%0A%20%20%20%20print%28%22Top%20element%20peek%20%3D%22,%20peek%29%0A%0A%20%20%20%20%23%20Pop%20an%20element%20from%20the%20stack%0A%20%20%20%20pop%20%3D%20stack.pop%28%29%0A%20%20%20%20print%28%22Popped%20element%20pop%20%3D%22,%20pop%29%0A%20%20%20%20print%28%22After%20popping,%20stack%20%3D%22,%20stack.to_list%28%29%29%0A%0A%20%20%20%20%23%20Get%20the%20stack%20size%0A%20%20%20%20size%20%3D%20stack.size%28%29%0A%20%20%20%20print%28%22Stack%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20Check%20whether%20it%20is%20empty%0A%20%20%20%20is_empty%20%3D%20stack.is_empty%28%29%0A%20%20%20%20print%28%22Is%20the%20stack%20empty%20%3D%22,%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_stack_and_queue/linkedlist_queue.md b/en/codes/pythontutor/chapter_stack_and_queue/linkedlist_queue.md new file mode 100644 index 000000000..811c8da10 --- /dev/null +++ b/en/codes/pythontutor/chapter_stack_and_queue/linkedlist_queue.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0A%0Aclass%20LinkedListQueue%3A%0A%20%20%20%20%22%22%22Queue%20implemented%20with%20a%20linked%20list%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self._front%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Head%20node%20front%0A%20%20%20%20%20%20%20%20self._rear%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Tail%20node%20rear%0A%20%20%20%20%20%20%20%20self._size%3A%20int%20%3D%200%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20queue%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._size%0A%0A%20%20%20%20def%20is_empty%28self%29%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%22%22%22Check%20whether%20the%20queue%20is%20empty%22%22%22%0A%20%20%20%20%20%20%20%20return%20not%20self._front%0A%0A%20%20%20%20def%20push%28self,%20num%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Enqueue%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Add%20num%20after%20the%20tail%20node%0A%20%20%20%20%20%20%20%20node%20%3D%20ListNode%28num%29%0A%20%20%20%20%20%20%20%20%23%20If%20the%20queue%20is%20empty,%20make%20both%20the%20head%20and%20tail%20point%20to%20this%20node%0A%20%20%20%20%20%20%20%20if%20self._front%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self._front%20%3D%20node%0A%20%20%20%20%20%20%20%20%20%20%20%20self._rear%20%3D%20node%0A%20%20%20%20%20%20%20%20%23%20If%20the%20queue%20is%20not%20empty,%20add%20this%20node%20after%20the%20tail%20node%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self._rear.next%20%3D%20node%0A%20%20%20%20%20%20%20%20%20%20%20%20self._rear%20%3D%20node%0A%20%20%20%20%20%20%20%20self._size%20%2B%3D%201%0A%0A%20%20%20%20def%20pop%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Dequeue%22%22%22%0A%20%20%20%20%20%20%20%20num%20%3D%20self.peek%28%29%0A%20%20%20%20%20%20%20%20%23%20Remove%20the%20head%20node%0A%20%20%20%20%20%20%20%20self._front%20%3D%20self._front.next%0A%20%20%20%20%20%20%20%20self._size%20-%3D%201%0A%20%20%20%20%20%20%20%20return%20num%0A%0A%20%20%20%20def%20peek%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Access%20the%20front%20element%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.is_empty%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Queue%20is%20empty%22%29%0A%20%20%20%20%20%20%20%20return%20self._front.val%0A%0A%20%20%20%20def%20to_list%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Convert%20to%20a%20list%20for%20printing%22%22%22%0A%20%20%20%20%20%20%20%20queue%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20temp%20%3D%20self._front%0A%20%20%20%20%20%20%20%20while%20temp%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20queue.append%28temp.val%29%0A%20%20%20%20%20%20%20%20%20%20%20%20temp%20%3D%20temp.next%0A%20%20%20%20%20%20%20%20return%20queue%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20queue%0A%20%20%20%20queue%20%3D%20LinkedListQueue%28%29%0A%0A%20%20%20%20%23%20Enqueue%20elements%0A%20%20%20%20queue.push%281%29%0A%20%20%20%20queue.push%283%29%0A%20%20%20%20queue.push%282%29%0A%20%20%20%20queue.push%285%29%0A%20%20%20%20queue.push%284%29%0A%20%20%20%20print%28%22Queue%20queue%20%3D%22,%20queue.to_list%28%29%29%0A%0A%20%20%20%20%23%20Access%20the%20front%20element%0A%20%20%20%20peek%20%3D%20queue.peek%28%29%0A%20%20%20%20print%28%22Front%20element%20peek%20%3D%22,%20peek%29%0A%0A%20%20%20%20%23%20Dequeue%20an%20element%0A%20%20%20%20pop_front%20%3D%20queue.pop%28%29%0A%20%20%20%20print%28%22Dequeued%20element%20pop_front%20%3D%22,%20pop_front%29%0A%20%20%20%20print%28%22After%20dequeuing,%20queue%20%3D%22,%20queue.to_list%28%29%29%0A%0A%20%20%20%20%23%20Get%20the%20queue%20size%0A%20%20%20%20size%20%3D%20queue.size%28%29%0A%20%20%20%20print%28%22Queue%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20Check%20whether%20the%20queue%20is%20empty%0A%20%20%20%20is_empty%20%3D%20queue.is_empty%28%29%0A%20%20%20%20print%28%22Is%20the%20queue%20empty%20%3D%22,%20is_empty%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_stack_and_queue/linkedlist_stack.md b/en/codes/pythontutor/chapter_stack_and_queue/linkedlist_stack.md new file mode 100644 index 000000000..72ea6ba91 --- /dev/null +++ b/en/codes/pythontutor/chapter_stack_and_queue/linkedlist_stack.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0A%0Aclass%20LinkedListStack%3A%0A%20%20%20%20%22%22%22Stack%20implemented%20with%20a%20linked%20list%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self._peek%3A%20ListNode%20%7C%20None%20%3D%20None%0A%20%20%20%20%20%20%20%20self._size%3A%20int%20%3D%200%0A%0A%20%20%20%20def%20size%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20stack%20size%22%22%22%0A%20%20%20%20%20%20%20%20return%20self._size%0A%0A%20%20%20%20def%20is_empty%28self%29%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%22%22%22Check%20whether%20the%20stack%20is%20empty%22%22%22%0A%20%20%20%20%20%20%20%20return%20not%20self._peek%0A%0A%20%20%20%20def%20push%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Push%22%22%22%0A%20%20%20%20%20%20%20%20node%20%3D%20ListNode%28val%29%0A%20%20%20%20%20%20%20%20node.next%20%3D%20self._peek%0A%20%20%20%20%20%20%20%20self._peek%20%3D%20node%0A%20%20%20%20%20%20%20%20self._size%20%2B%3D%201%0A%0A%20%20%20%20def%20pop%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Pop%22%22%22%0A%20%20%20%20%20%20%20%20num%20%3D%20self.peek%28%29%0A%20%20%20%20%20%20%20%20self._peek%20%3D%20self._peek.next%0A%20%20%20%20%20%20%20%20self._size%20-%3D%201%0A%20%20%20%20%20%20%20%20return%20num%0A%0A%20%20%20%20def%20peek%28self%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Access%20the%20top%20element%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.is_empty%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20IndexError%28%22Stack%20is%20empty%22%29%0A%20%20%20%20%20%20%20%20return%20self._peek.val%0A%0A%20%20%20%20def%20to_list%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Convert%20to%20a%20list%20for%20printing%22%22%22%0A%20%20%20%20%20%20%20%20arr%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20node%20%3D%20self._peek%0A%20%20%20%20%20%20%20%20while%20node%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20arr.append%28node.val%29%0A%20%20%20%20%20%20%20%20%20%20%20%20node%20%3D%20node.next%0A%20%20%20%20%20%20%20%20arr.reverse%28%29%0A%20%20%20%20%20%20%20%20return%20arr%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20stack%0A%20%20%20%20stack%20%3D%20LinkedListStack%28%29%0A%0A%20%20%20%20%23%20Push%20elements%20onto%20the%20stack%0A%20%20%20%20stack.push%281%29%0A%20%20%20%20stack.push%283%29%0A%20%20%20%20stack.push%282%29%0A%20%20%20%20stack.push%285%29%0A%20%20%20%20stack.push%284%29%0A%20%20%20%20print%28%22Stack%20stack%20%3D%22,%20stack.to_list%28%29%29%0A%0A%20%20%20%20%23%20Access%20the%20top%20element%0A%20%20%20%20peek%20%3D%20stack.peek%28%29%0A%20%20%20%20print%28%22Top%20element%20peek%20%3D%22,%20peek%29%0A%0A%20%20%20%20%23%20Pop%20an%20element%20from%20the%20stack%0A%20%20%20%20pop%20%3D%20stack.pop%28%29%0A%20%20%20%20print%28%22Popped%20element%20pop%20%3D%22,%20pop%29%0A%20%20%20%20print%28%22After%20popping,%20stack%20%3D%22,%20stack.to_list%28%29%29%0A%0A%20%20%20%20%23%20Get%20the%20stack%20size%0A%20%20%20%20size%20%3D%20stack.size%28%29%0A%20%20%20%20print%28%22Stack%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20Check%20whether%20it%20is%20empty%0A%20%20%20%20is_empty%20%3D%20stack.is_empty%28%29%0A%20%20%20%20print%28%22Is%20the%20stack%20empty%20%3D%22,%20is_empty%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_tree/array_binary_tree.md b/en/codes/pythontutor/chapter_tree/array_binary_tree.md new file mode 100644 index 000000000..1d9ec104e --- /dev/null +++ b/en/codes/pythontutor/chapter_tree/array_binary_tree.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%29%3A%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%20%3D%20None%0A%0Aclass%20ArrayBinaryTree%3A%0A%20%20%20%20%22%22%22Binary%20tree%20class%20using%20an%20array%20representation%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20arr%3A%20list%5Bint%20%7C%20None%5D%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self._tree%20%3D%20list%28arr%29%0A%0A%20%20%20%20def%20size%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Length%20of%20the%20backing%20list%22%22%22%0A%20%20%20%20%20%20%20%20return%20len%28self._tree%29%0A%0A%20%20%20%20def%20val%28self,%20i%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20value%20of%20the%20node%20at%20index%20i%22%22%22%0A%20%20%20%20%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20self.size%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20return%20self._tree%5Bi%5D%0A%0A%20%20%20%20def%20left%28self,%20i%3A%20int%29%20-%3E%20int%20%7C%20None%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20left%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%201%0A%0A%20%20%20%20def%20right%28self,%20i%3A%20int%29%20-%3E%20int%20%7C%20None%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20right%20child%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%202%20%2A%20i%20%2B%202%0A%0A%20%20%20%20def%20parent%28self,%20i%3A%20int%29%20-%3E%20int%20%7C%20None%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20the%20parent%20index%22%22%22%0A%20%20%20%20%20%20%20%20return%20%28i%20-%201%29%20//%202%0A%0A%20%20%20%20def%20level_order%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Level-order%20traversal%22%22%22%0A%20%20%20%20%20%20%20%20self.res%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%23%20Traverse%20the%20array%20directly%0A%20%20%20%20%20%20%20%20for%20i%20in%20range%28self.size%28%29%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20self.val%28i%29%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.res.append%28self.val%28i%29%29%0A%20%20%20%20%20%20%20%20return%20self.res%0A%0A%20%20%20%20def%20dfs%28self,%20i%3A%20int,%20order%3A%20str%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Depth-first%20traversal%22%22%22%0A%20%20%20%20%20%20%20%20if%20self.val%28i%29%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%23%20Preorder%20traversal%0A%20%20%20%20%20%20%20%20if%20order%20%3D%3D%20%22pre%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.res.append%28self.val%28i%29%29%0A%20%20%20%20%20%20%20%20self.dfs%28self.left%28i%29,%20order%29%0A%20%20%20%20%20%20%20%20%23%20Inorder%20traversal%0A%20%20%20%20%20%20%20%20if%20order%20%3D%3D%20%22in%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.res.append%28self.val%28i%29%29%0A%20%20%20%20%20%20%20%20self.dfs%28self.right%28i%29,%20order%29%0A%20%20%20%20%20%20%20%20%23%20Postorder%20traversal%0A%20%20%20%20%20%20%20%20if%20order%20%3D%3D%20%22post%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.res.append%28self.val%28i%29%29%0A%0A%20%20%20%20def%20pre_order%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Preorder%20traversal%22%22%22%0A%20%20%20%20%20%20%20%20self.res%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20self.dfs%280,%20order%3D%22pre%22%29%0A%20%20%20%20%20%20%20%20return%20self.res%0A%0A%20%20%20%20def%20in_order%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Inorder%20traversal%22%22%22%0A%20%20%20%20%20%20%20%20self.res%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20self.dfs%280,%20order%3D%22in%22%29%0A%20%20%20%20%20%20%20%20return%20self.res%0A%0A%20%20%20%20def%20post_order%28self%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22Postorder%20traversal%22%22%22%0A%20%20%20%20%20%20%20%20self.res%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20self.dfs%280,%20order%3D%22post%22%29%0A%20%20%20%20%20%20%20%20return%20self.res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20binary%20tree%0A%20%20%20%20arr%20%3D%20%5B1,%202,%203,%204,%20None,%206,%20None%5D%0A%20%20%20%20abt%20%3D%20ArrayBinaryTree%28arr%29%0A%0A%20%20%20%20%23%20Access%20a%20node%0A%20%20%20%20i%20%3D%201%0A%20%20%20%20l,%20r,%20p%20%3D%20abt.left%28i%29,%20abt.right%28i%29,%20abt.parent%28i%29%0A%0A%20%20%20%20%23%20Traverse%20the%20tree%0A%20%20%20%20res%20%3D%20abt.level_order%28%29%0A%20%20%20%20res%20%3D%20abt.pre_order%28%29%0A%20%20%20%20res%20%3D%20abt.in_order%28%29%0A%20%20%20%20res%20%3D%20abt.post_order%28%29&cumulative=false&curInstr=5&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_tree/binary_search_tree.md b/en/codes/pythontutor/chapter_tree/binary_search_tree.md new file mode 100644 index 000000000..485a67820 --- /dev/null +++ b/en/codes/pythontutor/chapter_tree/binary_search_tree.md @@ -0,0 +1,14 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%29%3A%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%20%3D%20None%0A%0A%0Aclass%20BinarySearchTree%3A%0A%20%20%20%20%22%22%22Binary%20search%20tree%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Initialize%20an%20empty%20tree%0A%20%20%20%20%20%20%20%20self._root%20%3D%20None%0A%0A%20%20%20%20def%20search%28self,%20num%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%20%20%20%20%22%22%22Find%20a%20node%22%22%22%0A%20%20%20%20%20%20%20%20cur%20%3D%20self._root%0A%20%20%20%20%20%20%20%20%23%20Search%20in%20a%20loop,%20stopping%20if%20cur%20becomes%20None%0A%20%20%20%20%20%20%20%20while%20cur%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20present,%20the%20target%20node%20is%20in%20the%20right%20subtree%20of%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.right%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20If%20present,%20the%20target%20node%20is%20in%20the%20left%20subtree%20of%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20elif%20cur.val%20%3E%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.left%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20target%20node%20was%20found%3B%20exit%20the%20loop%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20return%20cur%0A%0A%20%20%20%20def%20insert%28self,%20num%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Insert%20a%20node%22%22%22%0A%20%20%20%20%20%20%20%20%23%20If%20the%20tree%20is%20empty,%20initialize%20the%20root%20node%0A%20%20%20%20%20%20%20%20if%20self._root%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self._root%20%3D%20TreeNode%28num%29%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%23%20Search%20in%20a%20loop,%20stopping%20if%20cur%20becomes%20None%0A%20%20%20%20%20%20%20%20cur,%20pre%20%3D%20self._root,%20None%0A%20%20%20%20%20%20%20%20while%20cur%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Return%20immediately%20if%20a%20duplicate%20node%20is%20found%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3D%3D%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%20%20%20%20pre%20%3D%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20insertion%20position%20lies%20in%20the%20right%20subtree%20of%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.right%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20insertion%20position%20lies%20in%20the%20left%20subtree%20of%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.left%0A%20%20%20%20%20%20%20%20%23%20Insert%20a%20node%0A%20%20%20%20%20%20%20%20node%20%3D%20TreeNode%28num%29%0A%20%20%20%20%20%20%20%20if%20pre.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pre.right%20%3D%20node%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pre.left%20%3D%20node%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20binary%20search%20tree%0A%20%20%20%20bst%20%3D%20BinarySearchTree%28%29%0A%20%20%20%20nums%20%3D%20%5B4,%202,%206,%201,%203,%205,%207%5D%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20bst.insert%28num%29%0A%0A%20%20%20%20%23%20Find%20a%20node%0A%20%20%20%20node%20%3D%20bst.search%287%29%0A%20%20%20%20print%28%22%5CnFound%20node%20object%3A%20%7B%7D,%20node%20value%20%3D%20%7B%7D%22.format%28node,%20node.val%29%29&cumulative=false&curInstr=162&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%29%3A%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%20%3D%20None%0A%0A%0Aclass%20BinarySearchTree%3A%0A%20%20%20%20%22%22%22Binary%20search%20tree%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Initialize%20an%20empty%20tree%0A%20%20%20%20%20%20%20%20self._root%20%3D%20None%0A%0A%20%20%20%20def%20insert%28self,%20num%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Insert%20a%20node%22%22%22%0A%20%20%20%20%20%20%20%20%23%20If%20the%20tree%20is%20empty,%20initialize%20the%20root%20node%0A%20%20%20%20%20%20%20%20if%20self._root%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self._root%20%3D%20TreeNode%28num%29%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%23%20Search%20in%20a%20loop,%20stopping%20if%20cur%20becomes%20None%0A%20%20%20%20%20%20%20%20cur,%20pre%20%3D%20self._root,%20None%0A%20%20%20%20%20%20%20%20while%20cur%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Return%20immediately%20if%20a%20duplicate%20node%20is%20found%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3D%3D%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%20%20%20%20pre%20%3D%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20insertion%20position%20lies%20in%20the%20right%20subtree%20of%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.right%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20insertion%20position%20lies%20in%20the%20left%20subtree%20of%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.left%0A%20%20%20%20%20%20%20%20%23%20Insert%20a%20node%0A%20%20%20%20%20%20%20%20node%20%3D%20TreeNode%28num%29%0A%20%20%20%20%20%20%20%20if%20pre.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pre.right%20%3D%20node%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pre.left%20%3D%20node%0A%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20binary%20search%20tree%0A%20%20%20%20bst%20%3D%20BinarySearchTree%28%29%0A%20%20%20%20nums%20%3D%20%5B4,%202,%206,%201,%203,%205,%207%5D%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20bst.insert%28num%29%0A%0A%20%20%20%20%23%20Insert%20a%20node%0A%20%20%20%20bst.insert%2816%29&cumulative=false&curInstr=162&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%29%3A%0A%20%20%20%20%20%20%20%20self.val%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%20%3D%20None%0A%0Aclass%20BinarySearchTree%3A%0A%20%20%20%20%22%22%22Binary%20search%20tree%22%22%22%0A%0A%20%20%20%20def%20__init__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Constructor%22%22%22%0A%20%20%20%20%20%20%20%20self._root%20%3D%20None%0A%0A%20%20%20%20def%20insert%28self,%20num%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Insert%20a%20node%22%22%22%0A%20%20%20%20%20%20%20%20if%20self._root%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self._root%20%3D%20TreeNode%28num%29%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20cur,%20pre%20%3D%20self._root,%20None%0A%20%20%20%20%20%20%20%20while%20cur%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3D%3D%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%20%20%20%20pre%20%3D%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.right%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.left%0A%20%20%20%20%20%20%20%20node%20%3D%20TreeNode%28num%29%0A%20%20%20%20%20%20%20%20if%20pre.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pre.right%20%3D%20node%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pre.left%20%3D%20node%0A%0A%20%20%20%20def%20remove%28self,%20num%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Remove%20a%20node%22%22%22%0A%20%20%20%20%20%20%20%20if%20self._root%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%23%20Find%20a%20node%0A%20%20%20%20%20%20%20%20cur,%20pre%20%3D%20self._root,%20None%0A%20%20%20%20%20%20%20%20while%20cur%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3D%3D%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%20%20%20%20pre%20%3D%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur.val%20%3C%20num%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.right%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20%3D%20cur.left%0A%20%20%20%20%20%20%20%20if%20cur%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%0A%20%20%20%20%20%20%20%20%23%20Number%20of%20children%20%3D%200%20or%201%0A%20%20%20%20%20%20%20%20if%20cur.left%20is%20None%20or%20cur.right%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20With%200%20or%201%20child,%20child%20is%20None%20or%20that%20child,%20respectively%0A%20%20%20%20%20%20%20%20%20%20%20%20child%20%3D%20cur.left%20or%20cur.right%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Remove%20node%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cur%20%21%3D%20self._root%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20pre.left%20%3D%3D%20cur%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pre.left%20%3D%20child%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pre.right%20%3D%20child%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self._root%20%3D%20child%0A%20%20%20%20%20%20%20%20%23%20Number%20of%20children%20%3D%202%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Get%20the%20inorder%20successor%20of%20cur%0A%20%20%20%20%20%20%20%20%20%20%20%20tmp%3A%20TreeNode%20%3D%20cur.right%0A%20%20%20%20%20%20%20%20%20%20%20%20while%20tmp.left%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tmp%20%3D%20tmp.left%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Recursively%20remove%20node%20tmp%0A%20%20%20%20%20%20%20%20%20%20%20%20self.remove%28tmp.val%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Replace%20the%20value%20of%20cur%20with%20the%20value%20of%20tmp%0A%20%20%20%20%20%20%20%20%20%20%20%20cur.val%20%3D%20tmp.val%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20binary%20search%20tree%0A%20%20%20%20bst%20%3D%20BinarySearchTree%28%29%0A%20%20%20%20nums%20%3D%20%5B4,%202,%206,%201,%203,%205,%207%5D%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20bst.insert%28num%29%0A%0A%20%20%20%20%23%20Remove%20a%20node%0A%20%20%20%20bst.remove%281%29%20%23%20Degree%200%0A%20%20%20%20bst.remove%282%29%20%23%20Degree%201%0A%20%20%20%20bst.remove%284%29%20%23%20Degree%202&cumulative=false&curInstr=162&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_tree/binary_tree_bfs.md b/en/codes/pythontutor/chapter_tree/binary_tree_bfs.md new file mode 100644 index 000000000..cb4b1225e --- /dev/null +++ b/en/codes/pythontutor/chapter_tree/binary_tree_bfs.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0Aclass%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D,%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Return%20None%20if%20the%20index%20is%20out%20of%20bounds%20or%20the%20corresponding%20element%20is%20None%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%23%20Create%20the%20current%20node%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20%23%20Recursively%20build%20the%20left%20and%20right%20subtrees%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%22%22%22%0A%20%20%20%20return%20list_to_tree_dfs%28arr,%200%29%0A%0A%0Adef%20level_order%28root%3A%20TreeNode%20%7C%20None%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22Level-order%20traversal%22%22%22%0A%20%20%20%20%23%20Initialize%20a%20queue%20and%20enqueue%20the%20root%0A%20%20%20%20queue%3A%20deque%5BTreeNode%5D%20%3D%20deque%28%29%0A%20%20%20%20queue.append%28root%29%0A%20%20%20%20%23%20Initialize%20a%20list%20to%20store%20the%20traversal%20sequence%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20while%20queue%3A%0A%20%20%20%20%20%20%20%20node%3A%20TreeNode%20%3D%20queue.popleft%28%29%20%20%23%20Dequeue%20from%20the%20queue%0A%20%20%20%20%20%20%20%20res.append%28node.val%29%20%20%23%20Store%20the%20node%20value%0A%20%20%20%20%20%20%20%20if%20node.left%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20queue.append%28node.left%29%20%20%23%20Enqueue%20the%20left%20child%0A%20%20%20%20%20%20%20%20if%20node.right%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20queue.append%28node.right%29%20%20%23%20Enqueue%20the%20right%20child%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20binary%20tree%0A%20%20%20%20%23%20Use%20a%20helper%20function%20that%20builds%20a%20binary%20tree%20directly%20from%20an%20array%0A%20%20%20%20root%20%3D%20list_to_tree%28arr%3D%5B1,%202,%203,%204,%205,%206,%207%5D%29%0A%0A%20%20%20%20%23%20Level-order%20traversal%0A%20%20%20%20res%20%3D%20level_order%28root%29%0A%20%20%20%20print%28%22%5CnNode%20sequence%20from%20level-order%20traversal%20%3D%20%22,%20res%29&cumulative=false&curInstr=127&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/pythontutor/chapter_tree/binary_tree_dfs.md b/en/codes/pythontutor/chapter_tree/binary_tree_dfs.md new file mode 100644 index 000000000..b195bc7a0 --- /dev/null +++ b/en/codes/pythontutor/chapter_tree/binary_tree_dfs.md @@ -0,0 +1,8 @@ + + + +https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20left%20child%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20Reference%20to%20the%20right%20child%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D,%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%3A%20recursion%22%22%22%0A%20%20%20%20%23%20Return%20None%20if%20the%20index%20is%20out%20of%20bounds%20or%20the%20corresponding%20element%20is%20None%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%23%20Create%20the%20current%20node%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20%23%20Recursively%20build%20the%20left%20and%20right%20subtrees%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr,%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20%22%22%22Deserialize%20a%20list%20into%20a%20binary%20tree%22%22%22%0A%20%20%20%20return%20list_to_tree_dfs%28arr,%200%29%0A%0A%0Adef%20pre_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20%22%22%22Preorder%20traversal%22%22%22%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Visit%20order%3A%20root%20-%3E%20left%20subtree%20-%3E%20right%20subtree%0A%20%20%20%20res.append%28root.val%29%0A%20%20%20%20pre_order%28root%3Droot.left%29%0A%20%20%20%20pre_order%28root%3Droot.right%29%0A%0Adef%20in_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20%22%22%22Inorder%20traversal%22%22%22%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Visit%20order%3A%20left%20subtree%20-%3E%20root%20-%3E%20right%20subtree%0A%20%20%20%20in_order%28root%3Droot.left%29%0A%20%20%20%20res.append%28root.val%29%0A%20%20%20%20in_order%28root%3Droot.right%29%0A%0Adef%20post_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20%22%22%22Postorder%20traversal%22%22%22%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20Visit%20order%3A%20left%20subtree%20-%3E%20right%20subtree%20-%3E%20root%0A%20%20%20%20post_order%28root%3Droot.left%29%0A%20%20%20%20post_order%28root%3Droot.right%29%0A%20%20%20%20res.append%28root.val%29%0A%0A%22%22%22Driver%20code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20binary%20tree%0A%20%20%20%20%23%20Use%20a%20helper%20function%20that%20builds%20a%20binary%20tree%20directly%20from%20an%20array%0A%20%20%20%20root%20%3D%20list_to_tree%28arr%3D%5B1,%202,%203,%204,%205,%206,%207%5D%29%0A%0A%20%20%20%20%23%20Preorder%20traversal%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20pre_order%28root%29%0A%20%20%20%20print%28%22%5CnNode%20sequence%20from%20preorder%20traversal%20%3D%20%22,%20res%29%0A%0A%20%20%20%20%23%20Inorder%20traversal%0A%20%20%20%20res.clear%28%29%0A%20%20%20%20in_order%28root%29%0A%20%20%20%20print%28%22%5CnNode%20sequence%20from%20inorder%20traversal%20%3D%20%22,%20res%29%0A%0A%20%20%20%20%23%20Postorder%20traversal%0A%20%20%20%20res.clear%28%29%0A%20%20%20%20post_order%28root%29%0A%20%20%20%20print%28%22%5CnNode%20sequence%20from%20postorder%20traversal%20%3D%20%22,%20res%29&cumulative=false&curInstr=129&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/codes/ruby/chapter_array_and_linkedlist/array.rb b/en/codes/ruby/chapter_array_and_linkedlist/array.rb index 15bab7d77..34d7774d0 100644 --- a/en/codes/ruby/chapter_array_and_linkedlist/array.rb +++ b/en/codes/ruby/chapter_array_and_linkedlist/array.rb @@ -4,21 +4,21 @@ Created Time: 2024-03-18 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Random access element ### +### Access an element at a random index ### def random_access(nums) - # Randomly select a number in the interval [0, nums.length) + # Choose a random index in the interval [0, nums.length) random_index = Random.rand(0...nums.length) - # Retrieve and return the random element + # Get and return the randomly selected element nums[random_index] end -### 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 +### Extend the array length ### +# Ruby Array is a dynamic array that can grow directly +# For learning purposes, this function treats Array as a fixed-length array def extend(nums, enlarge) - # Initialize an array with extended length + # Initialize an array with the extended length res = Array.new(nums.length + enlarge, 0) # Copy all elements from the original array to the new array @@ -26,46 +26,46 @@ def extend(nums, enlarge) res[i] = nums[i] end - # Return the extended new array + # Return the new, extended array res end -### Insert element num at index in array ### +### Insert element num at index `index` in the array ### def insert(nums, num, index) - # Move all elements at and after index index backward by one position + # Shift all elements at and after index `index` one position to the right for i in (nums.length - 1).downto(index + 1) nums[i] = nums[i - 1] end - # Assign num to the element at index index + # Assign num to the element at index `index` nums[index] = num end -### Delete element at index ### +### Remove the element at index `index` ### def remove(nums, index) - # Move all elements after index index forward by one position + # Shift all elements after index `index` one position to the left for i in index...(nums.length - 1) nums[i] = nums[i + 1] end end -### Traverse array ### +### Traverse the array ### def traverse(nums) count = 0 - # Traverse array by index + # Traverse the array by index for i in 0...nums.length count += nums[i] end - # Direct traversal of array elements + # Iterate directly over the array elements for num in nums count += num end end -### Find specified element in array ### +### Find a specified element in the array ### def find(nums, target) for i in 0...nums.length return i if nums[i] == target @@ -75,34 +75,34 @@ def find(nums, target) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize array + # Initialize the array arr = Array.new(5, 0) puts "Array arr = #{arr}" nums = [1, 3, 2, 5, 4] puts "Array nums = #{nums}" - # Insert element + # Random access random_num = random_access(nums) - puts "Get random element #{random_num} from nums" + puts "Random element from nums: #{random_num}" - # Traverse array + # Extend the length nums = extend(nums, 3) - puts "Extend array length to 8, get nums = #{nums}" + puts "After extending the array length to 8, nums = #{nums}" - # Insert element + # Insert an element insert(nums, 6, 3) - puts "Insert number 6 at index 3, get nums = #{nums}" + puts "After inserting 6 at index 3, nums = #{nums}" - # Remove element + # Remove an element remove(nums, 2) - puts "Delete element at index 2, get nums = #{nums}" + puts "After removing the element at index 2, nums = #{nums}" - # Traverse array + # Traverse the array traverse(nums) - # Find element + # Find an element index = find(nums, 3) - puts "Find element 3 in nums, index = #{index}" + puts "Search for 3 in nums: index = #{index}" end diff --git a/en/codes/ruby/chapter_array_and_linkedlist/linked_list.rb b/en/codes/ruby/chapter_array_and_linkedlist/linked_list.rb index 030b72ace..77de6d099 100644 --- a/en/codes/ruby/chapter_array_and_linkedlist/linked_list.rb +++ b/en/codes/ruby/chapter_array_and_linkedlist/linked_list.rb @@ -7,15 +7,15 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/list_node' require_relative '../utils/print_util' -### 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 +### Insert node _p after node n0 in the linked list ### +# In Ruby, `p` is a built-in method and `P` denotes a constant, so use `_p` instead def insert(n0, _p) n1 = n0.next _p.next = n1 n0.next = _p end -### Delete first node after node n0 in linked list ### +### Remove the first node after node n0 in the linked list ### def remove(n0) return if n0.next.nil? @@ -25,7 +25,7 @@ def remove(n0) n0.next = n1 end -### Access node at index in linked list ### +### Access the node at index `index` in the linked list ### def access(head, index) for i in 0...index return nil if head.nil? @@ -35,7 +35,7 @@ def access(head, index) head end -### Find first node with value target in linked list ### +### Find the first node with value target in the linked list ### def find(head, target) index = 0 while head @@ -47,37 +47,37 @@ def find(head, target) -1 end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize linked list - # Initialize each node + # Initialize the linked list + # Initialize the individual nodes n0 = ListNode.new(1) n1 = ListNode.new(3) n2 = ListNode.new(2) n3 = ListNode.new(5) n4 = ListNode.new(4) - # Build references between nodes + # Set up references between the nodes n0.next = n1 n1.next = n2 n2.next = n3 n3.next = n4 - puts "Initialized linked list is" + puts "The initialized linked list is" print_linked_list(n0) - # Insert node + # Insert a node insert(n0, ListNode.new(0)) print_linked_list n0 - # Remove node + # Remove a node remove(n0) - puts "Linked list after removing node is" + puts "The linked list after removing a node is" print_linked_list(n0) - # Access node + # Access a node node = access(n0, 3) - puts "Value of node at index 3 in linked list = #{node.val}" + puts "Value of the node at index 3 in the linked list = #{node.val}" - # Search node + # Find a node index = find(n0, 2) - puts "Index of node with value 2 in linked list = #{index}" + puts "Index of the node with value 2 in the linked list = #{index}" end diff --git a/en/codes/ruby/chapter_array_and_linkedlist/list.rb b/en/codes/ruby/chapter_array_and_linkedlist/list.rb index 6bb01e8bf..3b2d25f72 100644 --- a/en/codes/ruby/chapter_array_and_linkedlist/list.rb +++ b/en/codes/ruby/chapter_array_and_linkedlist/list.rb @@ -4,25 +4,25 @@ Created Time: 2024-03-18 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize list + # Initialize the list nums = [1, 3, 2, 5, 4] puts "List nums = #{nums}" - # Update element + # Access an element num = nums[1] - puts "Access element at index 1, get num = #{num}" + puts "Access the element at index 1: num = #{num}" - # Add elements at the end + # Update an element nums[1] = 0 - puts "Update element at index 1 to 0, get nums = #{nums}" + puts "After updating the element at index 1 to 0, nums = #{nums}" - # Remove element + # Clear the list nums.clear - puts "After clearing list, nums = #{nums}" + puts "After clearing the list, nums = #{nums}" - # Direct traversal of list elements + # Append an element at the end nums << 1 nums << 3 nums << 2 @@ -30,21 +30,21 @@ if __FILE__ == $0 nums << 4 puts "After adding elements, nums = #{nums}" - # Sort list + # Insert an element in the middle nums.insert(3, 6) - puts "Insert element 6 at index 3, get nums = #{nums}" + puts "After inserting element 6 at index 3, nums = #{nums}" - # Remove element + # Remove an element nums.delete_at(3) - puts "Delete element at index 3, get nums = #{nums}" + puts "After removing the element at index 3, nums = #{nums}" - # Traverse list by index + # Traverse the list by index count = 0 for i in 0...nums.length count += nums[i] end - # Directly traverse list elements + # Iterate directly over the list elements count = 0 nums.each do |x| count += x @@ -53,8 +53,8 @@ if __FILE__ == $0 # Concatenate two lists nums1 = [6, 8, 7, 10, 9] nums += nums1 - puts "After concatenating list nums1 to nums, get nums = #{nums}" + puts "After concatenating nums1 onto nums, nums = #{nums}" nums = nums.sort { |a, b| a <=> b } - puts "After sorting list, nums = #{nums}" + puts "After sorting the list, nums = #{nums}" end diff --git a/en/codes/ruby/chapter_array_and_linkedlist/my_list.rb b/en/codes/ruby/chapter_array_and_linkedlist/my_list.rb index ecd0a5159..ff99953ce 100644 --- a/en/codes/ruby/chapter_array_and_linkedlist/my_list.rb +++ b/en/codes/ruby/chapter_array_and_linkedlist/my_list.rb @@ -6,8 +6,8 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### List class ### class MyList - attr_reader :size # Get list length (current number of elements) - attr_reader :capacity # Get list capacity + attr_reader :size # Get the list length (current number of elements) + attr_reader :capacity # Get the list capacity ### Constructor ### def initialize @@ -17,22 +17,22 @@ class MyList @arr = Array.new(capacity) end - ### Access element ### + ### Access an element ### def get(index) - # If the index is out of bounds, throw an exception, as below + # Raise an exception if the index is out of bounds; the same applies below raise IndexError, "Index out of bounds" if index < 0 || index >= size @arr[index] end - ### Access element ### + ### Update an element ### def set(index, num) raise IndexError, "Index out of bounds" if index < 0 || index >= size @arr[index] = num end - ### Add element at end ### + ### Append an element at the end ### def add(num) - # When the number of elements exceeds capacity, trigger the extension mechanism + # Grow the capacity when adding an element would exceed it extend_capacity if size == capacity @arr[size] = num @@ -40,14 +40,14 @@ class MyList @size += 1 end - ### Insert element in middle ### + ### Insert an element in the 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 + # Grow the capacity when adding an element would exceed it extend_capacity if size == capacity - # Move all elements after index index forward by one position + # Shift all elements at and after index `index` one position to the right for j in (size - 1).downto(index) @arr[j + 1] = @arr[j] end @@ -57,12 +57,12 @@ class MyList @size += 1 end - ### Delete element ### + ### Remove an 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 + # Shift all elements after index one position to the left for j in index...size @arr[j] = @arr[j + 1] end @@ -74,18 +74,18 @@ class MyList num end - ### Expand list capacity ### + ### Grow the list capacity ### def extend_capacity - # Create new array with length extend_ratio times original, copy original array to new array + # Duplicate the original array and append capacity * (extend_ratio - 1) empty slots arr = @arr.dup + Array.new(capacity * (@extend_ratio - 1)) - # Add elements at the end + # Update the list capacity @capacity = arr.length end - ### Convert list to array ### + ### Convert the list to an array ### def to_array sz = size - # Elements enqueue + # Convert only the list elements within the occupied range arr = Array.new(sz) for i in 0...sz arr[i] = get(i) @@ -94,39 +94,39 @@ class MyList end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize list + # Initialize the list nums = MyList.new - # Direct traversal of list elements + # Append an element at the end nums.add(1) nums.add(3) nums.add(2) nums.add(5) nums.add(4) - puts "List nums = #{nums.to_array}, capacity = #{nums.capacity}, length = #{nums.size}" + puts "List nums = #{nums.to_array}, capacity = #{nums.capacity}, size = #{nums.size}" - # Sort list + # Insert an element in the middle nums.insert(3, 6) - puts "Insert number 6 at index 3, get nums = #{nums.to_array}" + puts "After inserting 6 at index 3, nums = #{nums.to_array}" - # Remove element + # Remove an element nums.remove(3) - puts "Delete element at index 3, get nums = #{nums.to_array}" + puts "After removing the element at index 3, nums = #{nums.to_array}" - # Update element + # Access an element num = nums.get(1) - puts "Access element at index 1, get num = #{num}" + puts "Access the element at index 1: num = #{num}" - # Add elements at the end + # Update an element nums.set(1, 0) - puts "Update element at index 1 to 0, get nums = #{nums.to_array}" + puts "After updating the element at index 1 to 0, nums = #{nums.to_array}" - # Test capacity expansion mechanism + # Test capacity growth for i in 0...10 - # At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + # When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i) end - puts "After expansion, list nums = #{nums.to_array}, capacity = #{nums.capacity}, length = #{nums.size}" + puts "After capacity expansion, list nums = #{nums.to_array}, capacity = #{nums.capacity}, size = #{nums.size}" end diff --git a/en/codes/ruby/chapter_backtracking/n_queens.rb b/en/codes/ruby/chapter_backtracking/n_queens.rb index 5876751ea..89d0bd615 100644 --- a/en/codes/ruby/chapter_backtracking/n_queens.rb +++ b/en/codes/ruby/chapter_backtracking/n_queens.rb @@ -4,53 +4,53 @@ Created Time: 2024-05-21 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Backtracking: n queens ### +### Backtracking: n-queens ### def backtrack(row, n, state, res, cols, diags1, diags2) - # When all rows are placed, record the solution + # Record a solution when a queen has been placed in every row if row == n res << state.map { |row| row.dup } return end - # Traverse all columns + # Iterate over all columns for col in 0...n - # Calculate the main diagonal and anti-diagonal corresponding to this cell + # Calculate the main-diagonal and anti-diagonal indices for this square diag1 = row - col + n - 1 diag2 = row + col - # Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + # Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if !cols[col] && !diags1[diag1] && !diags2[diag2] - # Attempt: place the queen in this cell + # Try: place a queen on this square state[row][col] = "Q" cols[col] = diags1[diag1] = diags2[diag2] = true - # Place the next row + # Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2) - # Backtrack: restore this cell to an empty cell + # Backtrack: restore this square to empty state[row][col] = "#" cols[col] = diags1[diag1] = diags2[diag2] = false end end end -### Solve n queens ### +### Solve the n-queens problem ### def n_queens(n) - # Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + # Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square state = Array.new(n) { Array.new(n, "#") } - cols = Array.new(n, false) # Record whether there is a queen in the column - diags1 = Array.new(2 * n - 1, false) # Record whether there is a queen on the main diagonal - diags2 = Array.new(2 * n - 1, false) # Record whether there is a queen on the anti-diagonal + cols = Array.new(n, false) # Track whether a column contains a queen + diags1 = Array.new(2 * n - 1, false) # Track whether a main diagonal contains a queen + diags2 = Array.new(2 * n - 1, false) # Track whether an anti-diagonal contains a queen res = [] backtrack(0, n, state, res, cols, diags1, diags2) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 4 res = n_queens(n) - puts "Input board size is #{n}" - puts "Total queen placement solutions: #{res.length}" + puts "Input board width and height: #{n}" + puts "Number of queen arrangements: #{res.length}" for state in res puts "--------------------" diff --git a/en/codes/ruby/chapter_backtracking/permutations_i.rb b/en/codes/ruby/chapter_backtracking/permutations_i.rb index e26a6d6d0..eec2af3f6 100644 --- a/en/codes/ruby/chapter_backtracking/permutations_i.rb +++ b/en/codes/ruby/chapter_backtracking/permutations_i.rb @@ -6,22 +6,22 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Backtracking: permutations I ### def backtrack(state, choices, selected, res) - # When the state length equals the number of elements, record the solution + # Record a solution when the state length equals the number of elements if state.length == choices.length res << state.dup return end - # Traverse all choices + # Iterate over all choices choices.each_with_index do |choice, i| - # Pruning: do not allow repeated selection of elements + # Prune: do not choose an element more than once unless selected[i] - # Attempt: make choice, update state + # Try: make a choice and update the state selected[i] = true state << choice - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, choices, selected, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state selected[i] = false state.pop end @@ -35,7 +35,7 @@ def permutations_i(nums) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [1, 2, 3] diff --git a/en/codes/ruby/chapter_backtracking/permutations_ii.rb b/en/codes/ruby/chapter_backtracking/permutations_ii.rb index 288f74474..1a3097fa7 100644 --- a/en/codes/ruby/chapter_backtracking/permutations_ii.rb +++ b/en/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -8,24 +8,24 @@ require 'set' ### Backtracking: permutations II ### def backtrack(state, choices, selected, res) - # When the state length equals the number of elements, record the solution + # Record a solution when the state length equals the number of elements if state.length == choices.length res << state.dup return end - # Traverse all choices + # Iterate over all choices duplicated = Set.new choices.each_with_index do |choice, i| - # Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + # Prune: do not reuse an element or choose the same value again at this depth if !selected[i] && !duplicated.include?(choice) - # Attempt: make choice, update state + # Try: make a choice and update the state duplicated.add(choice) selected[i] = true state << choice - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, choices, selected, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state selected[i] = false state.pop end @@ -39,7 +39,7 @@ def permutations_ii(nums) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [1, 2, 2] diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb index 1508430c2..71d812544 100644 --- a/en/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb @@ -7,21 +7,21 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Pre-order traversal: example 1 ### +### Preorder traversal: example 1 ### def pre_order(root) return unless root - # Record solution + # Record the solution $res << root if root.val == 7 pre_order(root.left) pre_order(root.right) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) - puts "\nInitialize binary tree" + puts "\nInitialize the binary tree" print_tree(root) # Preorder traversal diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb index c744ab484..0f11e3ac7 100644 --- a/en/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb @@ -7,14 +7,14 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Pre-order traversal: example 2 ### +### Preorder traversal: example 2 ### def pre_order(root) return unless root - # Attempt + # Try $path << root - # Record solution + # Record the solution $res << $path.dup if root.val == 7 pre_order(root.left) @@ -24,17 +24,17 @@ def pre_order(root) $path.pop end -### Driver Code ### +### Driver code ### if __FILE__ == $0 root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) - puts "\nInitialize binary tree" + puts "\nInitialize the binary tree" print_tree(root) # Preorder traversal $path, $res = [], [] pre_order(root) - puts "\nOutput all paths from root node to node 7" + puts "\nOutput all paths from the root to nodes with value 7" for path in $res p path.map { |node| node.val } end diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb index cf6f7d0c3..c71c583f5 100644 --- a/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb @@ -7,15 +7,15 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Pre-order traversal: example 3 ### +### Preorder traversal: example 3 ### def pre_order(root) - # Pruning + # Prune return if !root || root.val == 3 - # Attempt + # Try $path.append(root) - # Record solution + # Record the solution $res << $path.dup if root.val == 7 pre_order(root.left) @@ -25,17 +25,17 @@ def pre_order(root) $path.pop end -### Driver Code ### +### Driver code ### if __FILE__ == $0 root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) - puts "\nInitialize binary tree" + puts "\nInitialize the binary tree" print_tree(root) # Preorder traversal $path, $res = [], [] pre_order(root) - puts "\nOutput all paths from root node to node 7, paths do not include nodes with value 3" + puts "\nOutput all paths from the root to nodes with value 7 that contain no node with value 3" for path in $res p path.map { |node| node.val } end diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb index 8022d8ca8..94db839a7 100644 --- a/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb @@ -7,61 +7,61 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Check if current state is solution ### +### Check whether the current state is a solution ### def is_solution?(state) !state.empty? && state.last.val == 7 end -### Record solution ### +### Record the solution ### def record_solution(state, res) res << state.dup end -### Check if choice is valid in current state ### +### Check whether the choice is valid in the current state ### def is_valid?(state, choice) choice && choice.val != 3 end -### Update state ### +### Update the state ### def make_choice(state, choice) state << choice end -### Restore state ### +### Restore the state ### def undo_choice(state, choice) state.pop end ### Backtracking: example 3 ### def backtrack(state, choices, res) - # Check if it is a solution + # Check whether this is a solution record_solution(state, res) if is_solution?(state) - # Traverse all choices + # Iterate over all choices for choice in choices - # Pruning: check if the choice is valid + # Prune: check whether the choice is valid if is_valid?(state, choice) - # Attempt: make choice, update state + # Try: make a choice and update the state make_choice(state, choice) - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, [choice.left, choice.right], res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state undo_choice(state, choice) end end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) - puts "\nInitialize binary tree" + puts "\nInitialize the binary tree" print_tree(root) # Backtracking algorithm res = [] backtrack([], [root], res) - puts "\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3" + puts "\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3" for path in res p path.map { |node| node.val } end diff --git a/en/codes/ruby/chapter_backtracking/subset_sum_i.rb b/en/codes/ruby/chapter_backtracking/subset_sum_i.rb index 9966e851e..96d31ceab 100644 --- a/en/codes/ruby/chapter_backtracking/subset_sum_i.rb +++ b/en/codes/ruby/chapter_backtracking/subset_sum_i.rb @@ -6,22 +6,22 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Backtracking: subset sum I ### def backtrack(state, target, choices, start, res) - # When the subset sum equals target, record the solution + # Record a solution when the remaining target reaches zero if target.zero? res << state.dup return end - # Traverse all choices - # Pruning 2: start traversing from start to avoid generating duplicate subsets + # Iterate over all choices + # Pruning 2: iterate from start to avoid generating duplicate subsets for i in start...choices.length - # Pruning 1: if the subset sum exceeds target, end the loop directly - # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + # Pruning 1: exit the loop if this choice would exceed the remaining target + # The array is sorted, so later elements are at least as large and would also exceed the remaining target break if target - choices[i] < 0 - # Attempt: make choice, update target, start + # Try: make a choice and update target and start state << choices[i] - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, target - choices[i], choices, i, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state state.pop end end @@ -30,18 +30,18 @@ end def subset_sum_i(nums, target) state = [] # State (subset) nums.sort! # Sort nums - start = 0 # Start point for traversal - res = [] # Result list (subset list) + start = 0 # Starting index for iteration + res = [] # Result list (list of subsets) backtrack(state, target, nums, start, res) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [3, 4, 5] target = 9 res = subset_sum_i(nums, target) puts "Input array = #{nums}, target = #{target}" - puts "All subsets with sum equal to #{target} res = #{res}" + puts "All subsets with sum #{target}: res = #{res}" end diff --git a/en/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb b/en/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb index 344350eb4..77db197a0 100644 --- a/en/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb +++ b/en/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb @@ -6,41 +6,41 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Backtracking: subset sum I ### def backtrack(state, target, total, choices, res) - # When the subset sum equals target, record the solution + # Record a solution when the subset sum equals target if total == target res << state.dup return end - # Traverse all choices + # Iterate over all choices for i in 0...choices.length - # Pruning: if the subset sum exceeds target, skip this choice + # Prune: skip this choice if the subset sum would exceed target next if total + choices[i] > target - # Attempt: make choice, update element sum total + # Try: make a choice and update the sum total state << choices[i] - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, target, total + choices[i], choices, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state state.pop end end -### Solve subset sum I (with duplicate subsets) ### +### Solve subset sum I (with duplicate subsets)### def subset_sum_i_naive(nums, target) state = [] # State (subset) total = 0 # Subset sum - res = [] # Result list (subset list) + res = [] # Result list (list of subsets) backtrack(state, target, total, nums, res) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [3, 4, 5] target = 9 res = subset_sum_i_naive(nums, target) puts "Input array nums = #{nums}, target = #{target}" - puts "All subsets with sum equal to #{target} res = #{res}" - puts "Please note that this method outputs results containing duplicate sets" + puts "All subsets with sum #{target}: res = #{res}" + puts "Note that this method produces duplicate subsets" end diff --git a/en/codes/ruby/chapter_backtracking/subset_sum_ii.rb b/en/codes/ruby/chapter_backtracking/subset_sum_ii.rb index 9b66cbaab..a43fe2785 100644 --- a/en/codes/ruby/chapter_backtracking/subset_sum_ii.rb +++ b/en/codes/ruby/chapter_backtracking/subset_sum_ii.rb @@ -6,26 +6,26 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Backtracking: subset sum II ### def backtrack(state, target, choices, start, res) - # When the subset sum equals target, record the solution + # Record a solution when the remaining target reaches zero if target.zero? res << state.dup return end - # Traverse all choices - # Pruning 2: start traversing from start to avoid generating duplicate subsets - # Pruning 3: start traversing from start to avoid repeatedly selecting the same element + # Iterate over all choices + # Pruning 2: iterate from start to avoid generating duplicate subsets + # Pruning 3: start after the previously chosen index to avoid choosing the same element again for i in start...choices.length - # Pruning 1: if the subset sum exceeds target, end the loop directly - # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + # Pruning 1: exit the loop if this choice would exceed the remaining target + # The array is sorted, so later elements are at least as large and would also exceed the remaining target break if target - choices[i] < 0 - # Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + # Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it next if i > start && choices[i] == choices[i - 1] - # Attempt: make choice, update target, start + # Try: make a choice and update target and start state << choices[i] - # Proceed to the next round of selection + # Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res) - # Backtrack: undo choice, restore to previous state + # Backtrack: undo the choice and restore the previous state state.pop end end @@ -34,18 +34,18 @@ end def subset_sum_ii(nums, target) state = [] # State (subset) nums.sort! # Sort nums - start = 0 # Start point for traversal - res = [] # Result list (subset list) + start = 0 # Starting index for iteration + res = [] # Result list (list of subsets) backtrack(state, target, nums, start, res) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [4, 4, 5] target = 9 res = subset_sum_ii(nums, target) puts "Input array nums = #{nums}, target = #{target}" - puts "All subsets with sum equal to #{target} res = #{res}" + puts "All subsets with sum #{target}: res = #{res}" end diff --git a/en/codes/ruby/chapter_computational_complexity/complexity_exercises.rb b/en/codes/ruby/chapter_computational_complexity/complexity_exercises.rb index f1593d354..9b459c76b 100644 --- a/en/codes/ruby/chapter_computational_complexity/complexity_exercises.rb +++ b/en/codes/ruby/chapter_computational_complexity/complexity_exercises.rb @@ -4,7 +4,7 @@ Created Time: 2026-08-18 Author: Hello Algo Team =end -### Iterative summation ### +### Sum using iteration ### def sum_iter(n) res = 0 for i in 1..n @@ -13,14 +13,14 @@ def sum_iter(n) res end -### Recursive summation ### +### Sum using recursion ### def sum_recur(n) return 1 if n == 1 n + sum_recur(n - 1) end -### Linear loop ### +### Linear-time loop ### def linear_loop(n) res = 0 for i in 0...n @@ -29,7 +29,7 @@ def linear_loop(n) res end -### Quadratic loop ### +### Quadratic-time loop ### def quadratic_loop(n) res = 0 for i in 0...n @@ -40,7 +40,7 @@ def quadratic_loop(n) res end -### Logarithmic loop ### +### Logarithmic-time loop ### def logarithmic_loop(n) n /= 2 while n > 1 n diff --git a/en/codes/ruby/chapter_computational_complexity/iteration.rb b/en/codes/ruby/chapter_computational_complexity/iteration.rb index d303eb336..7a216241c 100644 --- a/en/codes/ruby/chapter_computational_complexity/iteration.rb +++ b/en/codes/ruby/chapter_computational_complexity/iteration.rb @@ -8,7 +8,7 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com), Cy (9738314@gmail.com) def for_loop(n) res = 0 - # Sum 1, 2, ..., n-1, n + # Sum 1, 2, ..., n-1, n in a loop for i in 1..n res += i end @@ -19,26 +19,26 @@ end ### while loop ### def while_loop(n) res = 0 - i = 1 # Initialize condition variable + i = 1 # Initialize the loop control variable - # Sum 1, 2, ..., n-1, n + # Sum 1, 2, ..., n-1, n in a loop while i <= n res += i - i += 1 # Update condition variable + i += 1 # Update the loop control variable end res end -### while loop (two updates) ### +### while loop (two updates)### def while_loop_ii(n) res = 0 - i = 1 # Initialize condition variable + i = 1 # Initialize the loop control variable - # Sum 1, 4, 10, ... + # Sum 1, 4, 10, ... in a loop while i <= n res += i - # Update condition variable + # Update the loop control variable i += 1 i *= 2 end @@ -46,13 +46,13 @@ def while_loop_ii(n) res end -### Nested for loop ### +### Nested for loops ### def nested_for_loop(n) res = "" - # Loop i = 1, 2, ..., n-1, n + # Loop over i = 1, 2, ..., n-1, n for i in 1..n - # Loop j = 1, 2, ..., n-1, n + # Loop over j = 1, 2, ..., n-1, n for j in 1..n res += "(#{i}, #{j}), " end @@ -61,19 +61,19 @@ def nested_for_loop(n) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 5 res = for_loop(n) - puts "\nFor loop sum result res = #{res}" + puts "\nSum from the for loop: res = #{res}" res = while_loop(n) - puts "\nWhile loop sum result res = #{res}" + puts "\nSum from the while loop: res = #{res}" res = while_loop_ii(n) - puts "\nWhile loop (two updates) sum result res = #{res}" + puts "\nSum from the while loop with two updates: res = #{res}" res = nested_for_loop(n) - puts "\nNested for loop traversal result #{res}" + puts "\nTraversal result from nested for loops: #{res}" end diff --git a/en/codes/ruby/chapter_computational_complexity/recursion.rb b/en/codes/ruby/chapter_computational_complexity/recursion.rb index 3f53cc5be..2e38b6198 100644 --- a/en/codes/ruby/chapter_computational_complexity/recursion.rb +++ b/en/codes/ruby/chapter_computational_complexity/recursion.rb @@ -6,26 +6,26 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Recursion ### def recur(n) - # Termination condition + # Base case return 1 if n == 1 - # Recurse: recursive call + # Recurse: make a recursive call res = recur(n - 1) - # Return: return result + # Unwind: return the result n + res end -### Use iteration to simulate recursion ### +### Simulate recursion using iteration ### def for_loop_recur(n) # Use an explicit stack to simulate the system call stack stack = [] res = 0 - # Recurse: recursive call + # Recurse: make a recursive call for i in n.downto(0) - # Simulate "recurse" with "push" + # Simulate recursive calls by pushing onto the stack stack << i end - # Return: return result + # Unwind: return the result while !stack.empty? res += stack.pop end @@ -36,35 +36,35 @@ end ### Tail recursion ### def tail_recur(n, res) - # Termination condition + # Base case return res if n == 0 - # Tail recursive call + # Tail-recursive call tail_recur(n - 1, res + n) end ### Fibonacci sequence: recursion ### def fib(n) - # Termination condition f(1) = 0, f(2) = 1 + # Base cases: f(1) = 0, f(2) = 1 return n - 1 if n == 1 || n == 2 - # Recursive call f(n) = f(n-1) + f(n-2) + # Recursive calls: f(n) = f(n-1) + f(n-2) res = fib(n - 1) + fib(n - 2) - # Return result f(n) + # Return the result f(n) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 5 res = recur(n) - puts "\nRecursion sum result res = #{res}" + puts "\nSum from recursion: res = #{res}" res = for_loop_recur(n) - puts "\nUsing iteration to simulate recursion sum result res = #{res}" + puts "\nSum from simulating recursion with iteration: res = #{res}" res = tail_recur(n, 0) - puts "\nTail recursion sum result res = #{res}" + puts "\nSum from tail recursion: res = #{res}" res = fib(n) - puts "\nThe #{n}th Fibonacci number is #{res}" + puts "\nFibonacci term #{n} is #{res}" end diff --git a/en/codes/ruby/chapter_computational_complexity/space_complexity.rb b/en/codes/ruby/chapter_computational_complexity/space_complexity.rb index c50ce0da5..8bd8eeb31 100644 --- a/en/codes/ruby/chapter_computational_complexity/space_complexity.rb +++ b/en/codes/ruby/chapter_computational_complexity/space_complexity.rb @@ -14,54 +14,54 @@ def function 0 end -### Constant time ### +### Constant complexity ### def constant(n) - # Constants, variables, objects occupy O(1) space + # Constants, variables, and objects occupy O(1) space a = 0 nums = [0] * 10000 node = ListNode.new # Variables in the loop occupy O(1) space (0...n).each { c = 0 } - # Functions in the loop occupy O(1) space + # Function calls in the loop occupy O(1) space (0...n).each { function } end -### Linear time ### +### Linear complexity ### def linear(n) # A list of length n occupies O(n) space nums = Array.new(n, 0) - # A hash table of length n occupies O(n) space + # A hash table with n entries occupies O(n) space hmap = {} for i in 0...n hmap[i] = i.to_s end end -### Linear space (recursive) ### +### Linear complexity (recursive implementation)### def linear_recur(n) - puts "Recursion n = #{n}" + puts "Recursion: n = #{n}" return if n == 1 linear_recur(n - 1) end -### Quadratic time ### +### Quadratic complexity ### def quadratic(n) - # 2D list uses O(n^2) space + # The two-dimensional list occupies O(n^2) space Array.new(n) { Array.new(n, 0) } end -### Quadratic space (recursive) ### +### Quadratic complexity (recursive implementation)### def quadratic_recur(n) return 0 unless n > 0 - # Array nums has length n, n-1, ..., 2, 1 + # The lengths of array nums are n, n-1, ..., 2, 1 nums = Array.new(n, 0) quadratic_recur(n - 1) end -### Exponential space (build full binary tree) ### +### Exponential complexity (building a perfect binary tree)### def build_tree(n) return if n == 0 @@ -71,22 +71,22 @@ def build_tree(n) end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 5 - # Constant order + # Constant complexity constant(n) - # Linear order + # Linear complexity linear(n) linear_recur(n) - # Exponential order + # Quadratic complexity quadratic(n) quadratic_recur(n) - # Exponential order + # Exponential complexity root = build_tree(n) print_tree(root) end diff --git a/en/codes/ruby/chapter_computational_complexity/time_complexity.rb b/en/codes/ruby/chapter_computational_complexity/time_complexity.rb index c9da271dc..7624632ee 100644 --- a/en/codes/ruby/chapter_computational_complexity/time_complexity.rb +++ b/en/codes/ruby/chapter_computational_complexity/time_complexity.rb @@ -4,7 +4,7 @@ Created Time: 2024-03-30 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Constant time ### +### Constant complexity ### def constant(n) count = 0 size = 100000 @@ -14,18 +14,18 @@ def constant(n) count end -### Linear time ### +### Linear complexity ### def linear(n) count = 0 (0...n).each { count += 1 } count end -### Linear time (array traversal) ### +### Linear complexity (array traversal)### def array_traversal(nums) count = 0 - # Number of iterations is proportional to the array length + # The number of iterations is proportional to the array length for num in nums count += 1 end @@ -33,11 +33,11 @@ def array_traversal(nums) count end -### Quadratic time ### +### Quadratic complexity ### def quadratic(n) count = 0 - # Number of iterations is quadratically related to the data size n + # The number of iterations is proportional to the square of the input size n for i in 0...n for j in 0...n count += 1 @@ -47,20 +47,20 @@ def quadratic(n) count end -### Quadratic time (bubble sort) ### +### Quadratic complexity (bubble sort)### def bubble_sort(nums) count = 0 # Counter - # Outer loop: unsorted range is [0, i] + # Outer loop: the unsorted range is [0, i] for i in (nums.length - 1).downto(0) - # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + # Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0...i if nums[j] > nums[j + 1] # Swap nums[j] and nums[j + 1] tmp = nums[j] nums[j] = nums[j + 1] nums[j + 1] = tmp - count += 3 # Element swap includes 3 unit operations + count += 3 # Swapping two elements takes 3 elementary operations end end end @@ -68,11 +68,11 @@ def bubble_sort(nums) count end -### Exponential time (iterative) ### +### Exponential complexity (iterative implementation)### def exponential(n) count, base = 0, 1 - # Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + # Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) (0...n).each do (0...base).each { count += 1 } base *= 2 @@ -82,13 +82,13 @@ def exponential(n) count end -### Exponential time (recursive) ### +### Exponential complexity (recursive implementation)### def exp_recur(n) return 1 if n == 1 exp_recur(n - 1) + exp_recur(n - 1) + 1 end -### Logarithmic time (iterative) ### +### Logarithmic complexity (iterative implementation)### def logarithmic(n) count = 0 @@ -100,13 +100,13 @@ def logarithmic(n) count end -### Logarithmic time (recursive) ### +### Logarithmic complexity (recursive implementation)### def log_recur(n) return 0 unless n > 1 log_recur(n / 2) + 1 end -### Linearithmic time ### +### Linearithmic complexity ### def linear_log_recur(n) return 1 unless n > 1 @@ -116,50 +116,50 @@ def linear_log_recur(n) count end -### Factorial time (recursive) ### +### Factorial complexity (recursive implementation)### def factorial_recur(n) return 1 if n == 0 count = 0 - # Split from 1 into n + # Split one problem into n subproblems (0...n).each { count += factorial_recur(n - 1) } count end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # You can modify n to run and observe the trend of the number of operations for various complexities + # Try different values of n to see how the operation count grows for each complexity n = 8 - puts "Input data size n = #{n}" + puts "Input size n = #{n}" count = constant(n) - puts "Constant-time operations count = #{count}" + puts "Operation count for constant time = #{count}" count = linear(n) - puts "Linear-time operations count = #{count}" + puts "Operation count for linear time = #{count}" count = array_traversal(Array.new(n, 0)) - puts "Linear-time (array traversal) operations count = #{count}" + puts "Operation count for linear time (array traversal) = #{count}" count = quadratic(n) - puts "Quadratic-time operations count = #{count}" + puts "Operation count for quadratic time = #{count}" nums = Array.new(n) { |i| n - i } # [n, n-1, ..., 2, 1] count = bubble_sort(nums) - puts "Quadratic-time (bubble sort) operations count = #{count}" + puts "Operation count for quadratic time (bubble sort) = #{count}" count = exponential(n) - puts "Exponential-time (iterative) operations count = #{count}" + puts "Operation count for exponential time (iteration) = #{count}" count = exp_recur(n) - puts "Exponential-time (recursive) operations count = #{count}" + puts "Operation count for exponential time (recursion) = #{count}" count = logarithmic(n) - puts "Logarithmic-time (iterative) operations count = #{count}" + puts "Operation count for logarithmic time (iteration) = #{count}" count = log_recur(n) - puts "Logarithmic-time (recursive) operations count = #{count}" + puts "Operation count for logarithmic time (recursion) = #{count}" count = linear_log_recur(n) - puts "Linearithmic-time (recursive) operations count = #{count}" + puts "Operation count for linearithmic time (recursion) = #{count}" count = factorial_recur(n) - puts "Factorial-time (recursive) operations count = #{count}" + puts "Operation count for factorial time (recursion) = #{count}" end diff --git a/en/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb b/en/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb index f5d1f06cc..a8c783eab 100644 --- a/en/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb +++ b/en/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb @@ -4,32 +4,32 @@ Created Time: 2024-03-30 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Generate array with elements: 1, 2, ..., n, shuffled ### +### Generate an array containing 1, 2, ..., n in shuffled order ### def random_numbers(n) - # Generate array nums =: 1, 2, 3, ..., n + # Generate the array nums = 1, 2, 3, ..., n nums = Array.new(n) { |i| i + 1 } - # Randomly shuffle array elements + # Randomly shuffle the array elements nums.shuffle! end -### Find index of number 1 in array nums ### +### Find the index of the number 1 in array nums ### def find_one(nums) for i in 0...nums.length - # When element 1 is at the head of the array, best time complexity O(1) is achieved - # When element 1 is at the tail of the array, worst time complexity O(n) is achieved + # When 1 is at the start of the array, the best-case time complexity is O(1) + # When 1 is at the end of the array, the worst-case time complexity is O(n) return i if nums[i] == 1 end -1 end -### Driver Code ### +### Driver code ### if __FILE__ == $0 for i in 0...10 n = 100 nums = random_numbers(n) index = find_one(nums) - puts "\nArray [ 1, 2, ..., n ] after shuffling = #{nums}" - puts "Index of number 1 is #{index}" + puts "\nArray [1, 2, ..., n] after shuffling = #{nums}" + puts "Index of 1: #{index}" end end diff --git a/en/codes/ruby/chapter_divide_and_conquer/binary_search_recur.rb b/en/codes/ruby/chapter_divide_and_conquer/binary_search_recur.rb index 398a752dc..26f0922c3 100644 --- a/en/codes/ruby/chapter_divide_and_conquer/binary_search_recur.rb +++ b/en/codes/ruby/chapter_divide_and_conquer/binary_search_recur.rb @@ -6,20 +6,20 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Binary search: problem f(i, j) ### def dfs(nums, target, i, j) - # If the interval is empty, it means there is no target element, return -1 + # If the interval is empty, the target is absent; return -1 return -1 if i > j # Calculate the midpoint index m m = (i + j) / 2 if nums[m] < target - # Recursion subproblem f(m+1, j) + # Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j) elsif nums[m] > target - # Recursion subproblem f(i, m-1) + # Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1) else - # Found the target element, return its index + # The target element was found; return its index return m end end @@ -27,16 +27,16 @@ end ### Binary search ### def binary_search(nums, target) n = nums.length - # Solve the problem f(0, n-1) + # Solve problem f(0, n-1) dfs(nums, target, 0, n - 1) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 target = 6 nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] - # Binary search (closed interval on both sides) + # Binary search (closed interval) index = binary_search(nums, target) - puts "Index of target element 6 is #{index}" + puts "Index of target element 6 = #{index}" end diff --git a/en/codes/ruby/chapter_divide_and_conquer/build_tree.rb b/en/codes/ruby/chapter_divide_and_conquer/build_tree.rb index 007a160da..90919966b 100644 --- a/en/codes/ruby/chapter_divide_and_conquer/build_tree.rb +++ b/en/codes/ruby/chapter_divide_and_conquer/build_tree.rb @@ -7,14 +7,14 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Build binary tree: divide and conquer ### +### Build a binary tree: divide and conquer ### def dfs(preorder, inorder_map, i, l, r) - # Terminate when the subtree interval is empty + # Stop when the subtree interval is empty return if r - l < 0 # Initialize the root node root = TreeNode.new(preorder[i]) - # Query m to divide the left and right subtrees + # Look up m to separate the left and right subtrees m = inorder_map[preorder[i]] # Subproblem: build the left subtree root.left = dfs(preorder, inorder_map, i + 1, l, m - 1) @@ -25,20 +25,20 @@ def dfs(preorder, inorder_map, i, l, r) root end -### Build binary tree ### +### Build a binary tree ### def build_tree(preorder, inorder) - # Initialize hash map, storing the mapping from inorder elements to indices + # Initialize a hash table mapping each element in inorder to its index inorder_map = {} inorder.each_with_index { |val, i| inorder_map[val] = i } dfs(preorder, inorder_map, 0, 0, inorder.length - 1) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 preorder = [3, 9, 2, 1, 7] inorder = [9, 3, 1, 2, 7] - puts "Pre-order traversal = #{preorder}" - puts "In-order traversal = #{inorder}" + puts "Preorder traversal = #{preorder}" + puts "Inorder traversal = #{inorder}" root = build_tree(preorder, inorder) puts "The constructed binary tree is:" diff --git a/en/codes/ruby/chapter_divide_and_conquer/fast_power.rb b/en/codes/ruby/chapter_divide_and_conquer/fast_power.rb index 062dd0fa7..ed3854a0f 100644 --- a/en/codes/ruby/chapter_divide_and_conquer/fast_power.rb +++ b/en/codes/ruby/chapter_divide_and_conquer/fast_power.rb @@ -4,7 +4,7 @@ Created Time: 2026-08-18 Author: Hello Algo Team =end -### Exponentiation by squaring ### +### Fast exponentiation ### def fast_pow(x, n) return 1 if n == 0 @@ -15,7 +15,7 @@ def fast_pow(x, n) end if __FILE__ == $0 - raise 'fast power check failed' unless fast_pow(7, 0) == 1 - raise 'fast power check failed' unless fast_pow(3, 5) == 243 - raise 'fast power check failed' unless fast_pow(2, 6) == 64 + raise 'Fast power check failed' unless fast_pow(7, 0) == 1 + raise 'Fast power check failed' unless fast_pow(3, 5) == 243 + raise 'Fast power check failed' unless fast_pow(2, 6) == 64 end diff --git a/en/codes/ruby/chapter_divide_and_conquer/hanota.rb b/en/codes/ruby/chapter_divide_and_conquer/hanota.rb index 7fae969b1..5894b0665 100644 --- a/en/codes/ruby/chapter_divide_and_conquer/hanota.rb +++ b/en/codes/ruby/chapter_divide_and_conquer/hanota.rb @@ -6,15 +6,15 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Move one disk ### def move(src, tar) - # Take out a disk from the top of src + # Take a disk from the top of src pan = src.pop # Place the disk on top of tar tar << pan end -### Solve Tower of Hanoi f(i) ### +### Solve the Tower of Hanoi problem f(i) ### def dfs(i, src, buf, tar) - # If there is only one disk left in src, move it directly to tar + # If only one disk remains to be moved from src, move it directly to tar if i == 1 move(src, tar) return @@ -22,33 +22,33 @@ def dfs(i, src, buf, tar) # Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf) - # Subproblem f(1): move the remaining disk from src to tar + # Subproblem f(1): move the remaining disk in the current subproblem from src to tar move(src, tar) # Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar) end -### Solve Tower of Hanoi ### +### Solve the Tower of Hanoi problem ### def solve_hanota(_A, _B, _C) n = _A.length # Move the top n disks from A to C using B dfs(n, _A, _B, _C) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # The tail of the list is the top of the rod + # The end of each list represents the top of the peg A = [5, 4, 3, 2, 1] B = [] C = [] - puts "In initial state:" + puts "Initial state:" puts "A = #{A}" puts "B = #{B}" puts "C = #{C}" solve_hanota(A, B, C) - puts "After disk movement is complete:" + puts "After moving the disks:" puts "A = #{A}" puts "B = #{B}" puts "C = #{C}" diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_backtrack.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_backtrack.rb index 9f9956204..3a466ca70 100644 --- a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_backtrack.rb +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_backtrack.rb @@ -6,14 +6,14 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Backtracking ### def backtrack(choices, state, n, res) - # When climbing to the n-th stair, add 1 to the solution count + # When step n is reached, increase the number of ways by 1 res[0] += 1 if state == n - # Traverse all choices + # Iterate over all choices for choice in choices - # Pruning: not allowed to go beyond the n-th stair + # Prune: do not go beyond step n next if state + choice > n - # Attempt: make choice, update state + # Try: make a choice and update the state backtrack(choices, state + choice, n, res) end # Backtrack @@ -21,17 +21,17 @@ end ### Climbing stairs: backtracking ### def climbing_stairs_backtrack(n) - choices = [1, 2] # Can choose to climb up 1 or 2 stairs - state = 0 # Start climbing from the 0-th stair - res = [0] # Use res[0] to record the solution count + choices = [1, 2] # Choose to climb 1 or 2 steps + state = 0 # Start climbing from step 0 + res = [0] # Use res[0] to record the number of ways backtrack(choices, state, n, res) res.first end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 9 res = climbing_stairs_backtrack(n) - puts "Climbing #{n} stairs has #{res} solutions" + puts "Number of ways to climb #{n} stairs: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_constraint_dp.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_constraint_dp.rb index 984751db3..8477b328d 100644 --- a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_constraint_dp.rb +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_constraint_dp.rb @@ -4,16 +4,16 @@ Created Time: 2024-05-29 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Climbing stairs with constraint: DP ### +### Climbing stairs with constraints: dynamic programming ### def climbing_stairs_constraint_dp(n) return 1 if n == 1 || n == 2 - # Initialize dp table, used to store solutions to subproblems + # Initialize the dp table to store subproblem solutions dp = Array.new(n + 1) { Array.new(3, 0) } - # Initial state: preset the solution to the smallest subproblem + # Initial states: set the solutions to the smallest subproblems dp[1][1], dp[1][2] = 1, 0 dp[2][1], dp[2][2] = 0, 1 - # State transition: gradually solve larger subproblems from smaller ones + # State transitions: solve progressively larger subproblems using smaller ones for i in 3...(n + 1) dp[i][1] = dp[i - 1][2] dp[i][2] = dp[i - 2][1] + dp[i - 2][2] @@ -22,10 +22,10 @@ def climbing_stairs_constraint_dp(n) dp[n][1] + dp[n][2] end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 9 res = climbing_stairs_constraint_dp(n) - puts "Climbing #{n} stairs has #{res} solutions" + puts "Number of ways to climb #{n} stairs: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb index e2ea8b03e..fd283522e 100644 --- a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb @@ -6,7 +6,7 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Search ### def dfs(i) - # Known dp[1] and dp[2], return them + # The values of dp[1] and dp[2] are known; return them return i if i == 1 || i == 2 # dp[i] = dp[i-1] + dp[i-2] dfs(i - 1) + dfs(i - 2) @@ -17,10 +17,10 @@ def climbing_stairs_dfs(n) dfs(n) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 9 res = climbing_stairs_dfs(n) - puts "Climbing #{n} stairs has #{res} solutions" + puts "Number of ways to climb #{n} stairs: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb index aa9c0fc3f..c443487fc 100644 --- a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb @@ -4,11 +4,11 @@ Created Time: 2024-05-29 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Memoization search ### +### Memoized search ### def dfs(i, mem) - # Known dp[1] and dp[2], return them + # The values of dp[1] and dp[2] are known; return them return i if i == 1 || i == 2 - # If record dp[i] exists, return it directly + # If dp[i] has been recorded, return it directly return mem[i] if mem[i] != -1 # dp[i] = dp[i-1] + dp[i-2] @@ -17,17 +17,17 @@ def dfs(i, mem) mem[i] = count end -### Climbing stairs: memoization search ### +### Climbing stairs: memoized search ### def climbing_stairs_dfs_mem(n) - # mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + # mem[i] stores the number of ways to reach step i; -1 means no result has been recorded mem = Array.new(n + 1, -1) dfs(n, mem) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 9 res = climbing_stairs_dfs_mem(n) - puts "Climbing #{n} stairs has #{res} solutions" + puts "Number of ways to climb #{n} stairs: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb index e759d4135..a8d6d5c73 100644 --- a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb @@ -8,17 +8,17 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) def climbing_stairs_dp(n) return n if n == 1 || n == 2 - # Initialize dp table, used to store solutions to subproblems + # Initialize the dp table to store subproblem solutions dp = Array.new(n + 1, 0) - # Initial state: preset the solution to the smallest subproblem + # Initial states: set the solutions to the smallest subproblems dp[1], dp[2] = 1, 2 - # State transition: gradually solve larger subproblems from smaller ones + # State transitions: solve progressively larger subproblems using smaller ones (3...(n + 1)).each { |i| dp[i] = dp[i - 1] + dp[i - 2] } dp[n] end -### Climbing stairs: space-optimized DP ### +### Climbing stairs: space-optimized dynamic programming ### def climbing_stairs_dp_comp(n) return n if n == 1 || n == 2 @@ -28,13 +28,13 @@ def climbing_stairs_dp_comp(n) b end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 9 res = climbing_stairs_dp(n) - puts "Climbing #{n} stairs has #{res} solutions" + puts "Number of ways to climb #{n} stairs: #{res}" res = climbing_stairs_dp_comp(n) - puts "Climbing #{n} stairs has #{res} solutions" + puts "Number of ways to climb #{n} stairs: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/coin_change.rb b/en/codes/ruby/chapter_dynamic_programming/coin_change.rb index 782a17441..9915f35db 100644 --- a/en/codes/ruby/chapter_dynamic_programming/coin_change.rb +++ b/en/codes/ruby/chapter_dynamic_programming/coin_change.rb @@ -8,18 +8,18 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) def coin_change_dp(coins, amt) n = coins.length _MAX = amt + 1 - # Initialize dp table + # Initialize the dp table dp = Array.new(n + 1) { Array.new(amt + 1, 0) } - # State transition: first row and first column + # State transitions: first row and first column (1...(amt + 1)).each { |a| dp[0][a] = _MAX } - # State transition: rest of the rows and columns + # State transitions: remaining rows and columns for i in 1...(n + 1) for a in 1...(amt + 1) if coins[i - 1] > a - # If exceeds target amount, don't select coin i + # If the coin exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a] else - # The smaller value between not selecting and selecting coin i + # Take the smaller result of not choosing and choosing coin i dp[i][a] = [dp[i - 1][a], dp[i][a - coins[i - 1]] + 1].min end end @@ -27,22 +27,22 @@ def coin_change_dp(coins, amt) dp[n][amt] != _MAX ? dp[n][amt] : -1 end -### Coin change: space-optimized DP ### +### Coin change: space-optimized dynamic programming ### def coin_change_dp_comp(coins, amt) n = coins.length _MAX = amt + 1 - # Initialize dp table + # Initialize the dp table dp = Array.new(amt + 1, _MAX) dp[0] = 0 - # State transition + # State transitions for i in 1...(n + 1) - # Traverse in forward order + # Traverse in increasing order for a in 1...(amt + 1) if coins[i - 1] > a - # If exceeds target amount, don't select coin i + # If the coin exceeds the current amount a, do not choose coin i dp[a] = dp[a] else - # The smaller value between not selecting and selecting coin i + # Take the smaller result of not choosing and choosing coin i dp[a] = [dp[a], dp[a - coins[i - 1]] + 1].min end end @@ -50,16 +50,16 @@ def coin_change_dp_comp(coins, amt) dp[amt] != _MAX ? dp[amt] : -1 end -### Driver Code ### +### Driver code ### if __FILE__ == $0 coins = [1, 2, 5] amt = 4 # Dynamic programming res = coin_change_dp(coins, amt) - puts "Minimum coins needed to make target amount is #{res}" + puts "Minimum number of coins needed to make the target amount: #{res}" # Space-optimized dynamic programming res = coin_change_dp_comp(coins, amt) - puts "Minimum coins needed to make target amount is #{res}" + puts "Minimum number of coins needed to make the target amount: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/coin_change_ii.rb b/en/codes/ruby/chapter_dynamic_programming/coin_change_ii.rb index 77ba8d3b1..6682a513e 100644 --- a/en/codes/ruby/chapter_dynamic_programming/coin_change_ii.rb +++ b/en/codes/ruby/chapter_dynamic_programming/coin_change_ii.rb @@ -7,18 +7,18 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Coin change II: dynamic programming ### def coin_change_ii_dp(coins, amt) n = coins.length - # Initialize dp table + # Initialize the dp table dp = Array.new(n + 1) { Array.new(amt + 1, 0) } - # Initialize first column + # Initialize the first column (0...(n + 1)).each { |i| dp[i][0] = 1 } - # State transition + # State transitions for i in 1...(n + 1) for a in 1...(amt + 1) if coins[i - 1] > a - # If exceeds target amount, don't select coin i + # If the coin exceeds the current amount a, do not choose coin i dp[i][a] = dp[i - 1][a] else - # Sum of the two options: not selecting and selecting coin i + # Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] end end @@ -26,21 +26,21 @@ def coin_change_ii_dp(coins, amt) dp[n][amt] end -### Coin change II: space-optimized DP ### +### Coin change II: space-optimized dynamic programming ### def coin_change_ii_dp_comp(coins, amt) n = coins.length - # Initialize dp table + # Initialize the dp table dp = Array.new(amt + 1, 0) dp[0] = 1 - # State transition + # State transitions for i in 1...(n + 1) - # Traverse in forward order + # Traverse in increasing order for a in 1...(amt + 1) if coins[i - 1] > a - # If exceeds target amount, don't select coin i + # If the coin exceeds the current amount a, do not choose coin i dp[a] = dp[a] else - # Sum of the two options: not selecting and selecting coin i + # Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]] end end @@ -48,16 +48,16 @@ def coin_change_ii_dp_comp(coins, amt) dp[amt] end -### Driver Code ### +### Driver code ### if __FILE__ == $0 coins = [1, 2, 5] amt = 5 # Dynamic programming res = coin_change_ii_dp(coins, amt) - puts "Number of coin combinations to make target amount is #{res}" + puts "Number of coin combinations that make the target amount: #{res}" # Space-optimized dynamic programming res = coin_change_ii_dp_comp(coins, amt) - puts "Number of coin combinations to make target amount is #{res}" + puts "Number of coin combinations that make the target amount: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/edit_distance.rb b/en/codes/ruby/chapter_dynamic_programming/edit_distance.rb index e216c0f8f..2789c10f0 100644 --- a/en/codes/ruby/chapter_dynamic_programming/edit_distance.rb +++ b/en/codes/ruby/chapter_dynamic_programming/edit_distance.rb @@ -4,40 +4,40 @@ Created Time: 2024-05-29 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Edit distance: brute force search ### +### Edit distance: brute-force search ### def edit_distance_dfs(s, t, i, j) - # If both s and t are empty, return 0 + # Return 0 if both prefixes s[0...i] and t[0...j] are empty return 0 if i == 0 && j == 0 - # If s is empty, return length of t + # If the prefix of s is empty, return the length j of the prefix of t return j if i == 0 - # If t is empty, return length of s + # If the prefix of t is empty, return the length i of the prefix of s return i if j == 0 - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them return edit_distance_dfs(s, t, i - 1, j - 1) if s[i - 1] == t[j - 1] - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 insert = edit_distance_dfs(s, t, i, j - 1) delete = edit_distance_dfs(s, t, i - 1, j) replace = edit_distance_dfs(s, t, i - 1, j - 1) - # Return minimum edit steps + # Return the minimum number of edits [insert, delete, replace].min + 1 end def edit_distance_dfs_mem(s, t, mem, i, j) - # If both s and t are empty, return 0 + # Return 0 if both prefixes s[0...i] and t[0...j] are empty return 0 if i == 0 && j == 0 - # If s is empty, return length of t + # If the prefix of s is empty, return the length j of the prefix of t return j if i == 0 - # If t is empty, return length of s + # If the prefix of t is empty, return the length i of the prefix of s return i if j == 0 - # If there's a record, return it directly + # If a result has already been recorded, return it directly return mem[i][j] if mem[i][j] != -1 - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) if s[i - 1] == t[j - 1] - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 insert = edit_distance_dfs_mem(s, t, mem, i, j - 1) delete = edit_distance_dfs_mem(s, t, mem, i - 1, j) replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) - # Record and return minimum edit steps + # Record and return the minimum number of edits mem[i][j] = [insert, delete, replace].min + 1 end @@ -45,17 +45,17 @@ end def edit_distance_dp(s, t) n, m = s.length, t.length dp = Array.new(n + 1) { Array.new(m + 1, 0) } - # State transition: first row and first column + # State transitions: first row and first column (1...(n + 1)).each { |i| dp[i][0] = i } (1...(m + 1)).each { |j| dp[0][j] = j } - # State transition: rest of the rows and columns + # State transitions: remaining rows and columns for i in 1...(n + 1) for j in 1...(m +1) if s[i - 1] == t[j - 1] - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1] else - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = [dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]].min + 1 end end @@ -63,34 +63,34 @@ def edit_distance_dp(s, t) dp[n][m] end -### Edit distance: space-optimized DP ### +### Edit distance: space-optimized dynamic programming ### def edit_distance_dp_comp(s, t) n, m = s.length, t.length dp = Array.new(m + 1, 0) - # State transition: first row + # State transitions: first row (1...(m + 1)).each { |j| dp[j] = j } - # State transition: rest of the rows + # State transitions: remaining rows for i in 1...(n + 1) - # State transition: first column + # State transitions: first column leftup = dp.first # Temporarily store dp[i-1, j-1] dp[0] += 1 - # State transition: rest of the columns + # State transitions: remaining columns for j in 1...(m + 1) temp = dp[j] if s[i - 1] == t[j - 1] - # If two characters are equal, skip both characters + # If the two characters are equal, skip both of them dp[j] = leftup else - # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + # Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = [dp[j - 1], dp[j], leftup].min + 1 end - leftup = temp # Update for next round's dp[i-1, j-1] + leftup = temp # Update to dp[i-1, j-1] for the next iteration end end dp[m] end -### Driver Code ### +### Driver code ### if __FILE__ == $0 s = 'bag' t = 'pack' @@ -98,18 +98,18 @@ if __FILE__ == $0 # Brute-force search res = edit_distance_dfs(s, t, n, m) - puts "Changing #{s} to #{t} requires minimum #{res} edits" + puts "Minimum edits needed to change #{s} into #{t}: #{res}" - # Memoization search + # Memoized search mem = Array.new(n + 1) { Array.new(m + 1, -1) } res = edit_distance_dfs_mem(s, t, mem, n, m) - puts "Changing #{s} to #{t} requires minimum #{res} edits" + puts "Minimum edits needed to change #{s} into #{t}: #{res}" # Dynamic programming res = edit_distance_dp(s, t) - puts "Changing #{s} to #{t} requires minimum #{res} edits" + puts "Minimum edits needed to change #{s} into #{t}: #{res}" # Space-optimized dynamic programming res = edit_distance_dp_comp(s, t) - puts "Changing #{s} to #{t} requires minimum #{res} edits" + puts "Minimum edits needed to change #{s} into #{t}: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/knapsack.rb b/en/codes/ruby/chapter_dynamic_programming/knapsack.rb index 949d41d28..1da4a247e 100644 --- a/en/codes/ruby/chapter_dynamic_programming/knapsack.rb +++ b/en/codes/ruby/chapter_dynamic_programming/knapsack.rb @@ -4,47 +4,47 @@ Created Time: 2024-05-29 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### 0-1 knapsack: brute force search ### +### 0-1 knapsack: brute-force search ### def knapsack_dfs(wgt, val, i, c) - # If all items have been selected or knapsack has no remaining capacity, return value 0 + # If all items have been considered or no capacity remains, return value 0 return 0 if i == 0 || c == 0 - # If exceeds knapsack capacity, can only choose not to put it in + # If the item exceeds the remaining capacity, it must be excluded return knapsack_dfs(wgt, val, i - 1, c) if wgt[i - 1] > c - # Calculate the maximum value of not putting in and putting in item i + # Calculate the maximum values obtained by excluding and including item i no = knapsack_dfs(wgt, val, i - 1, c) yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] - # Return the larger value of the two options + # Return the larger value from the two choices [no, yes].max end -### 0-1 knapsack: memoization search ### +### 0-1 knapsack: memoized search ### def knapsack_dfs_mem(wgt, val, mem, i, c) - # If all items have been selected or knapsack has no remaining capacity, return value 0 + # If all items have been considered or no capacity remains, return value 0 return 0 if i == 0 || c == 0 - # If there's a record, return it directly + # If a result has already been recorded, return it directly return mem[i][c] if mem[i][c] != -1 - # If exceeds knapsack capacity, can only choose not to put it in + # If the item exceeds the remaining capacity, it must be excluded return knapsack_dfs_mem(wgt, val, mem, i - 1, c) if wgt[i - 1] > c - # Calculate the maximum value of not putting in and putting in item i + # Calculate the maximum values obtained by excluding and including item i no = knapsack_dfs_mem(wgt, val, mem, i - 1, c) yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1] - # Record and return the larger value of the two options + # Record and return the larger value from the two choices mem[i][c] = [no, yes].max end ### 0-1 knapsack: dynamic programming ### def knapsack_dp(wgt, val, cap) n = wgt.length - # Initialize dp table + # Initialize the dp table dp = Array.new(n + 1) { Array.new(cap + 1, 0) } - # State transition + # State transitions for i in 1...(n + 1) for c in 1...(cap + 1) if wgt[i - 1] > c - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] else - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[i][c] = [dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]].max end end @@ -52,20 +52,20 @@ def knapsack_dp(wgt, val, cap) dp[n][cap] end -### 0-1 knapsack: space-optimized DP ### +### 0-1 knapsack: space-optimized dynamic programming ### def knapsack_dp_comp(wgt, val, cap) n = wgt.length - # Initialize dp table + # Initialize the dp table dp = Array.new(cap + 1, 0) - # State transition + # State transitions for i in 1...(n + 1) - # Traverse in reverse order + # Traverse in decreasing order for c in cap.downto(1) if wgt[i - 1] > c - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c] else - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[c] = [dp[c], dp[c - wgt[i - 1]] + val[i - 1]].max end end @@ -73,7 +73,7 @@ def knapsack_dp_comp(wgt, val, cap) dp[cap] end -### Driver Code ### +### Driver code ### if __FILE__ == $0 wgt = [10, 20, 30, 40, 50] val = [50, 120, 150, 210, 240] @@ -82,18 +82,18 @@ if __FILE__ == $0 # Brute-force search res = knapsack_dfs(wgt, val, n, cap) - puts "Maximum item value not exceeding knapsack capacity is #{res}" + puts "Maximum total item value within the knapsack capacity: #{res}" - # Memoization search + # Memoized search mem = Array.new(n + 1) { Array.new(cap + 1, -1) } res = knapsack_dfs_mem(wgt, val, mem, n, cap) - puts "Maximum item value not exceeding knapsack capacity is #{res}" + puts "Maximum total item value within the knapsack capacity: #{res}" # Dynamic programming res = knapsack_dp(wgt, val, cap) - puts "Maximum item value not exceeding knapsack capacity is #{res}" + puts "Maximum total item value within the knapsack capacity: #{res}" # Space-optimized dynamic programming res = knapsack_dp_comp(wgt, val, cap) - puts "Maximum item value not exceeding knapsack capacity is #{res}" + puts "Maximum total item value within the knapsack capacity: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rb b/en/codes/ruby/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rb index fe0fd6c12..83551d609 100644 --- a/en/codes/ruby/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rb +++ b/en/codes/ruby/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rb @@ -4,20 +4,20 @@ Created Time: 2024-05-29 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Minimum cost climbing stairs: DP ### +### Minimum cost to climb stairs: dynamic programming ### def min_cost_climbing_stairs_dp(cost) n = cost.length - 1 return cost[n] if n == 1 || n == 2 - # Initialize dp table, used to store solutions to subproblems + # Initialize the dp table to store subproblem solutions dp = Array.new(n + 1, 0) - # Initial state: preset the solution to the smallest subproblem + # Initial states: set the solutions to the smallest subproblems dp[1], dp[2] = cost[1], cost[2] - # State transition: gradually solve larger subproblems from smaller ones + # State transitions: solve progressively larger subproblems using smaller ones (3...(n + 1)).each { |i| dp[i] = [dp[i - 1], dp[i - 2]].min + cost[i] } dp[n] end -# Minimum cost climbing stairs: Space-optimized dynamic programming +# Minimum cost to climb stairs: space-optimized dynamic programming def min_cost_climbing_stairs_dp_comp(cost) n = cost.length - 1 return cost[n] if n == 1 || n == 2 @@ -26,14 +26,14 @@ def min_cost_climbing_stairs_dp_comp(cost) b end -### Driver Code ### +### Driver code ### if __FILE__ == $0 cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1] - puts "Input stair cost list is #{cost}" + puts "Input stair cost list: #{cost}" res = min_cost_climbing_stairs_dp(cost) - puts "Minimum cost to climb stairs is #{res}" + puts "Minimum cost to climb all the stairs: #{res}" res = min_cost_climbing_stairs_dp_comp(cost) - puts "Minimum cost to climb stairs is #{res}" + puts "Minimum cost to climb all the stairs: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/min_path_sum.rb b/en/codes/ruby/chapter_dynamic_programming/min_path_sum.rb index e256795e0..0dc425343 100644 --- a/en/codes/ruby/chapter_dynamic_programming/min_path_sum.rb +++ b/en/codes/ruby/chapter_dynamic_programming/min_path_sum.rb @@ -4,45 +4,45 @@ Created Time: 2024-05-29 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Minimum path sum: brute force search ### +### Minimum path sum: brute-force search ### def min_path_sum_dfs(grid, i, j) - # If it's the top-left cell, terminate the search + # Stop searching at the top-left cell return grid[i][j] if i == 0 && j == 0 - # If row or column index is out of bounds, return +∞ cost + # If a row or column index is out of bounds, return a cost of +∞ return Float::INFINITY if i < 0 || j < 0 - # Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + # Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) up = min_path_sum_dfs(grid, i - 1, j) left = min_path_sum_dfs(grid, i, j - 1) - # Return the minimum path cost from top-left to (i, j) + # Return the minimum path cost from the top-left corner to (i, j) [left, up].min + grid[i][j] end -### Minimum path sum: memoization search ### +### Minimum path sum: memoized search ### def min_path_sum_dfs_mem(grid, mem, i, j) - # If it's the top-left cell, terminate the search + # Stop searching at the top-left cell return grid[0][0] if i == 0 && j == 0 - # If row or column index is out of bounds, return +∞ cost + # If a row or column index is out of bounds, return a cost of +∞ return Float::INFINITY if i < 0 || j < 0 - # If there's a record, return it directly + # If a result has already been recorded, return it directly return mem[i][j] if mem[i][j] != -1 - # Minimum path cost for left and upper cells + # Minimum path costs to the cells on the left and above up = min_path_sum_dfs_mem(grid, mem, i - 1, j) left = min_path_sum_dfs_mem(grid, mem, i, j - 1) - # Record and return the minimum path cost from top-left to (i, j) + # Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = [left, up].min + grid[i][j] end ### Minimum path sum: dynamic programming ### def min_path_sum_dp(grid) n, m = grid.length, grid.first.length - # Initialize dp table + # Initialize the dp table dp = Array.new(n) { Array.new(m, 0) } dp[0][0] = grid[0][0] - # State transition: first row + # State transitions: first row (1...m).each { |j| dp[0][j] = dp[0][j - 1] + grid[0][j] } - # State transition: first column + # State transitions: first column (1...n).each { |i| dp[i][0] = dp[i - 1][0] + grid[i][0] } - # State transition: rest of the rows and columns + # State transitions: remaining rows and columns for i in 1...n for j in 1...m dp[i][j] = [dp[i][j - 1], dp[i - 1][j]].min + grid[i][j] @@ -51,43 +51,43 @@ def min_path_sum_dp(grid) dp[n -1][m -1] end -### Minimum path sum: space-optimized DP ### +### Minimum path sum: space-optimized dynamic programming ### def min_path_sum_dp_comp(grid) n, m = grid.length, grid.first.length - # Initialize dp table + # Initialize the dp table dp = Array.new(m, 0) - # State transition: first row + # State transitions: first row dp[0] = grid[0][0] (1...m).each { |j| dp[j] = dp[j - 1] + grid[0][j] } - # State transition: rest of the rows + # State transitions: remaining rows for i in 1...n - # State transition: first column + # State transitions: first column dp[0] = dp[0] + grid[i][0] - # State transition: rest of the columns + # State transitions: remaining columns (1...m).each { |j| dp[j] = [dp[j - 1], dp[j]].min + grid[i][j] } end dp[m - 1] end -### Driver Code ### +### Driver code ### if __FILE__ == $0 grid = [[1, 3, 1, 5], [2, 2, 4, 2], [5, 3, 2, 1], [4, 3, 5, 2]] n, m = grid.length, grid.first.length # Brute-force search res = min_path_sum_dfs(grid, n - 1, m - 1) - puts "Minimum path sum from top-left to bottom-right is #{res}" + puts "Minimum path sum from the top-left to the bottom-right corner: #{res}" - # Memoization search + # Memoized search mem = Array.new(n) { Array.new(m, - 1) } res = min_path_sum_dfs_mem(grid, mem, n - 1, m -1) - puts "Minimum path sum from top-left to bottom-right is #{res}" + puts "Minimum path sum from the top-left to the bottom-right corner: #{res}" # Dynamic programming res = min_path_sum_dp(grid) - puts "Minimum path sum from top-left to bottom-right is #{res}" + puts "Minimum path sum from the top-left to the bottom-right corner: #{res}" # Space-optimized dynamic programming res = min_path_sum_dp_comp(grid) - puts "Minimum path sum from top-left to bottom-right is #{res}" + puts "Minimum path sum from the top-left to the bottom-right corner: #{res}" end diff --git a/en/codes/ruby/chapter_dynamic_programming/unbounded_knapsack.rb b/en/codes/ruby/chapter_dynamic_programming/unbounded_knapsack.rb index af9d77afc..ec8954c67 100644 --- a/en/codes/ruby/chapter_dynamic_programming/unbounded_knapsack.rb +++ b/en/codes/ruby/chapter_dynamic_programming/unbounded_knapsack.rb @@ -7,16 +7,16 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Unbounded knapsack: dynamic programming ### def unbounded_knapsack_dp(wgt, val, cap) n = wgt.length - # Initialize dp table + # Initialize the dp table dp = Array.new(n + 1) { Array.new(cap + 1, 0) } - # State transition + # State transitions for i in 1...(n + 1) for c in 1...(cap + 1) if wgt[i - 1] > c - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] else - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[i][c] = [dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]].max end end @@ -24,20 +24,20 @@ def unbounded_knapsack_dp(wgt, val, cap) dp[n][cap] end -### Unbounded knapsack: space-optimized DP ### +### Unbounded knapsack: space-optimized dynamic programming ### def unbounded_knapsack_dp_comp(wgt, val, cap) n = wgt.length - # Initialize dp table + # Initialize the dp table dp = Array.new(cap + 1, 0) - # State transition + # State transitions for i in 1...(n + 1) - # Traverse in forward order + # Traverse in increasing order for c in 1...(cap + 1) if wgt[i -1] > c - # If exceeds knapsack capacity, don't select item i + # If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c] else - # The larger value between not selecting and selecting item i + # Take the larger value obtained by excluding and including item i dp[c] = [dp[c], dp[c - wgt[i - 1]] + val[i - 1]].max end end @@ -45,7 +45,7 @@ def unbounded_knapsack_dp_comp(wgt, val, cap) dp[cap] end -### Driver Code ### +### Driver code ### if __FILE__ == $0 wgt = [1, 2, 3] val = [5, 11, 15] @@ -53,9 +53,9 @@ if __FILE__ == $0 # Dynamic programming res = unbounded_knapsack_dp(wgt, val, cap) - puts "Maximum item value not exceeding knapsack capacity is #{res}" + puts "Maximum total item value within the knapsack capacity: #{res}" # Space-optimized dynamic programming res = unbounded_knapsack_dp_comp(wgt, val, cap) - puts "Maximum item value not exceeding knapsack capacity is #{res}" + puts "Maximum total item value within the knapsack capacity: #{res}" end diff --git a/en/codes/ruby/chapter_graph/graph_adjacency_list.rb b/en/codes/ruby/chapter_graph/graph_adjacency_list.rb index 1907942fe..72a89da0a 100644 --- a/en/codes/ruby/chapter_graph/graph_adjacency_list.rb +++ b/en/codes/ruby/chapter_graph/graph_adjacency_list.rb @@ -6,13 +6,13 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/vertex' -### Undirected graph class based on adjacency list ### +### Undirected graph class implemented with an adjacency list ### class GraphAdjList attr_reader :adj_list ### Constructor ### def initialize(edges) - # Adjacency list, key: vertex, value: all adjacent vertices of that vertex + # Adjacency list: key is a vertex, value is the list of all its neighbors @adj_list = {} # Add all vertices and edges for edge in edges @@ -22,12 +22,12 @@ class GraphAdjList end end - ### Get number of vertices ### + ### Get the number of vertices ### def size @adj_list.length end - ### Add edge ### + ### Add an edge ### def add_edge(vet1, vet2) raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) @@ -35,7 +35,7 @@ class GraphAdjList @adj_list[vet2] << vet1 end - ### Delete edge ### + ### Remove an edge ### def remove_edge(vet1, vet2) raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) @@ -44,27 +44,27 @@ class GraphAdjList @adj_list[vet2].delete(vet1) end - ### Add vertex ### + ### Add a vertex ### def add_vertex(vet) return if @adj_list.include?(vet) - # Add a new linked list in the adjacency list + # Add a new neighbor list to the adjacency list @adj_list[vet] = [] end - ### Delete vertex ### + ### Remove a vertex ### def remove_vertex(vet) raise ArgumentError unless @adj_list.include?(vet) - # Remove the linked list corresponding to vertex vet in the adjacency list + # Remove the neighbor list for vertex vet from the adjacency list @adj_list.delete(vet) - # Traverse the linked lists of other vertices and remove all edges containing vet + # Traverse the other neighbor lists and remove all edges incident to vet for vertex in @adj_list @adj_list[vertex.first].delete(vet) if @adj_list[vertex.first].include?(vet) end end - ### Print adjacency list ### + ### Print the adjacency list ### def __print__ puts 'Adjacency list =' for vertex in @adj_list @@ -74,9 +74,9 @@ class GraphAdjList end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Add edge + # Initialize the undirected graph v = vals_to_vets([1, 3, 2, 5, 4]) edges = [ [v[0], v[1]], @@ -87,30 +87,30 @@ if __FILE__ == $0 [v[3], v[4]], ] graph = GraphAdjList.new(edges) - puts "\nAfter initialization, graph is" + puts "\nGraph after initialization:" graph.__print__ - # Add edge - # Vertices 1, 2 are v[0], v[2] + # Add an edge + # Vertices 1 and 2 are v[0] and v[2] graph.add_edge(v[0], v[2]) - puts "\nAfter adding edge 1-2, graph is" + puts "\nGraph after adding edge 1-2:" graph.__print__ - # Remove edge - # Vertices 1, 3 are v[0], v[1] + # Remove an edge + # Vertices 1 and 3 are v[0] and v[1] graph.remove_edge(v[0], v[1]) - puts "\nAfter removing edge 1-3, graph is" + puts "\nGraph after removing edge 1-3:" graph.__print__ - # Add vertex + # Add a vertex v5 = Vertex.new(6) graph.add_vertex(v5) - puts "\nAfter adding vertex 6, graph is" + puts "\nGraph after adding vertex 6:" graph.__print__ - # Remove vertex + # Remove a vertex # Vertex 3 is v[1] graph.remove_vertex(v[1]) - puts "\nAfter removing vertex 3, graph is" + puts "\nGraph after removing vertex 3:" graph.__print__ end diff --git a/en/codes/ruby/chapter_graph/graph_adjacency_matrix.rb b/en/codes/ruby/chapter_graph/graph_adjacency_matrix.rb index 56c513680..88e3571c2 100644 --- a/en/codes/ruby/chapter_graph/graph_adjacency_matrix.rb +++ b/en/codes/ruby/chapter_graph/graph_adjacency_matrix.rb @@ -6,30 +6,30 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/print_util' -### Undirected graph class based on adjacency matrix ### +### Undirected graph class implemented with an adjacency matrix ### class GraphAdjMat def initialize(vertices, edges) ### Constructor ### - # Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + # Vertex list: elements are vertex values, and indices are vertex indices @vertices = [] - # Adjacency matrix, where the row and column indices correspond to the "vertex index" + # Adjacency matrix: row and column indices correspond to vertex indices @adj_mat = [] - # Add vertex + # Add a vertex vertices.each { |val| add_vertex(val) } - # Add edge - # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + # Add an edge + # Note that the entries in edges are vertex indices, corresponding to element indices in vertices edges.each { |e| add_edge(e[0], e[1]) } end - ### Get number of vertices ### + ### Get the number of vertices ### def size @vertices.length end - ### Add vertex ### + ### Add a vertex ### def add_vertex(val) n = size - # Add the value of the new vertex to the vertex list + # Append the new vertex value to the vertex list @vertices << val # Add a row to the adjacency matrix new_row = Array.new(n, 0) @@ -38,34 +38,34 @@ class GraphAdjMat @adj_mat.each { |row| row << 0 } end - ### Delete vertex ### + ### Remove a vertex ### def remove_vertex(index) raise IndexError if index >= size - # Remove the vertex at index from the vertex list + # Remove the vertex at index `index` from the vertex list @vertices.delete_at(index) - # Remove the row at index from the adjacency matrix + # Remove row `index` from the adjacency matrix @adj_mat.delete_at(index) - # Remove the column at index from the adjacency matrix + # Remove column `index` from the adjacency matrix @adj_mat.each { |row| row.delete_at(index) } end - ### Add edge ### + ### Add an edge ### def add_edge(i, j) - # Parameters i, j correspond to the vertices element indices - # Handle index out of bounds and equality + # Parameters i and j are element indices in vertices + # Handle out-of-bounds or equal indices if i < 0 || j < 0 || i >= size || j >= size || i == j raise IndexError end - # In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + # For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal @adj_mat[i][j] = 1 @adj_mat[j][i] = 1 end - ### Delete edge ### + ### Remove an edge ### def remove_edge(i, j) - # Parameters i, j correspond to the vertices element indices - # Handle index out of bounds and equality + # Parameters i and j are element indices in vertices + # Handle out-of-bounds or equal indices if i < 0 || j < 0 || i >= size || j >= size || i == j raise IndexError end @@ -73,7 +73,7 @@ class GraphAdjMat @adj_mat[j][i] = 0 end - ### Print adjacency matrix ### + ### Print the adjacency matrix ### def __print__ puts "Vertex list = #{@vertices}" puts 'Adjacency matrix =' @@ -81,36 +81,36 @@ class GraphAdjMat end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Add edge - # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + # Initialize the undirected graph + # Note that the entries in edges are vertex indices, corresponding to element indices in vertices vertices = [1, 3, 2, 5, 4] edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]] graph = GraphAdjMat.new(vertices, edges) - puts "\nAfter initialization, graph is" + puts "\nGraph after initialization:" graph.__print__ - # Add edge - # Add vertex + # Add an edge + # Vertices 1 and 2 have indices 0 and 2, respectively graph.add_edge(0, 2) - puts "\nAfter adding edge 1-2, graph is" + puts "\nGraph after adding edge 1-2:" graph.__print__ - # Remove edge - # Vertices 1, 3 have indices 0, 1 respectively + # Remove an edge + # The indices of vertices 1 and 3 are 0 and 1, respectively graph.remove_edge(0, 1) - puts "\nAfter removing edge 1-3, graph is" + puts "\nGraph after removing edge 1-3:" graph.__print__ - # Add vertex + # Add a vertex graph.add_vertex(6) - puts "\nAfter adding vertex 6, graph is" + puts "\nGraph after adding vertex 6:" graph.__print__ - # Remove vertex + # Remove a vertex # Vertex 3 has index 1 graph.remove_vertex(1) - puts "\nAfter removing vertex 3, graph is" + puts "\nGraph after removing vertex 3:" graph.__print__ end diff --git a/en/codes/ruby/chapter_graph/graph_bfs.rb b/en/codes/ruby/chapter_graph/graph_bfs.rb index 39efa879d..8e99a9c1d 100644 --- a/en/codes/ruby/chapter_graph/graph_bfs.rb +++ b/en/codes/ruby/chapter_graph/graph_bfs.rb @@ -10,31 +10,31 @@ require_relative '../utils/vertex' ### Breadth-first traversal ### def graph_bfs(graph, start_vet) - # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + # Represent the graph with an adjacency list to access all neighbors of a given vertex # Vertex traversal sequence res = [] - # Hash set for recording vertices that have been visited + # Hash set recording visited vertices visited = Set.new([start_vet]) - # Queue used to implement BFS + # Use a queue to implement BFS que = [start_vet] - # Starting from vertex vet, loop until all vertices are visited + # Start from start_vet and loop until all reachable vertices have been visited while que.length > 0 vet = que.shift # Dequeue the front vertex - res << vet # Record visited vertex - # Traverse all adjacent vertices of this vertex + res << vet # Record the visited vertex + # Traverse all neighbors of this vertex for adj_vet in graph.adj_list[vet] - next if visited.include?(adj_vet) # Skip vertices that have been visited - que << adj_vet # Only enqueue unvisited vertices + next if visited.include?(adj_vet) # Skip vertices that have already been visited + que << adj_vet # Enqueue only unvisited vertices visited.add(adj_vet) # Mark this vertex as visited end end - # Return vertex traversal sequence + # Return the vertex traversal sequence res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Add edge + # Initialize the undirected graph v = vals_to_vets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) edges = [ [v[0], v[1]], @@ -51,11 +51,11 @@ if __FILE__ == $0 [v[7], v[8]], ] graph = GraphAdjList.new(edges) - puts "\nAfter initialization, graph is" + puts "\nGraph after initialization:" graph.__print__ # Breadth-first traversal res = graph_bfs(graph, v.first) - puts "\nBreadth-first traversal (BFS) vertex sequence is" + puts "\nBreadth-first traversal (BFS) vertex sequence:" p vets_to_vals(res) end diff --git a/en/codes/ruby/chapter_graph/graph_dfs.rb b/en/codes/ruby/chapter_graph/graph_dfs.rb index 066bb70cf..172c96352 100644 --- a/en/codes/ruby/chapter_graph/graph_dfs.rb +++ b/en/codes/ruby/chapter_graph/graph_dfs.rb @@ -10,30 +10,30 @@ require_relative '../utils/vertex' ### Depth-first traversal helper function ### def dfs(graph, visited, res, vet) - res << vet # Record visited vertex + res << vet # Record the visited vertex visited.add(vet) # Mark this vertex as visited - # Traverse all adjacent vertices of this vertex + # Traverse all neighbors of this vertex for adj_vet in graph.adj_list[vet] - next if visited.include?(adj_vet) # Skip vertices that have been visited - # Recursively visit adjacent vertices + next if visited.include?(adj_vet) # Skip vertices that have already been visited + # Recursively visit the neighboring vertex dfs(graph, visited, res, adj_vet) end end ### Depth-first traversal ### def graph_dfs(graph, start_vet) - # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + # Represent the graph with an adjacency list to access all neighbors of a given vertex # Vertex traversal sequence res = [] - # Hash set for recording vertices that have been visited + # Hash set recording visited vertices visited = Set.new dfs(graph, visited, res, start_vet) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Add edge + # Initialize the undirected graph v = vals_to_vets([0, 1, 2, 3, 4, 5, 6]) edges = [ [v[0], v[1]], @@ -44,11 +44,11 @@ if __FILE__ == $0 [v[5], v[6]], ] graph = GraphAdjList.new(edges) - puts "\nAfter initialization, graph is" + puts "\nGraph after initialization:" graph.__print__ # Depth-first traversal res = graph_dfs(graph, v[0]) - puts "\nDepth-first traversal (DFS) vertex sequence is" + puts "\nVertex sequence from depth-first traversal (DFS):" p vets_to_vals(res) end diff --git a/en/codes/ruby/chapter_greedy/coin_change_greedy.rb b/en/codes/ruby/chapter_greedy/coin_change_greedy.rb index 6aa136324..25a00abac 100644 --- a/en/codes/ruby/chapter_greedy/coin_change_greedy.rb +++ b/en/codes/ruby/chapter_greedy/coin_change_greedy.rb @@ -4,14 +4,14 @@ Created Time: 2024-05-07 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Coin change: greedy ### +### Coin change: greedy algorithm ### def coin_change_greedy(coins, amt) - # Assume coins list is sorted + # Assume the coins list is sorted in ascending order i = coins.length - 1 count = 0 - # Loop to make greedy choices until no remaining amount + # Repeatedly make greedy choices until no amount remains while amt > 0 - # Find the coin that is less than and closest to the remaining amount + # Find the largest coin no greater than the remaining amount while i > 0 && coins[i] > amt i -= 1 end @@ -19,32 +19,32 @@ def coin_change_greedy(coins, amt) amt -= coins[i] count += 1 end - # Return -1 if no solution found + # Return -1 if no feasible solution was found amt == 0 ? count : -1 end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Greedy algorithm: Can guarantee finding the global optimal solution + # Greedy algorithm: guaranteed to find a globally optimal solution coins = [1, 5, 10, 20, 50, 100] amt = 186 res = coin_change_greedy(coins, amt) puts "\ncoins = #{coins}, amt = #{amt}" - puts "Minimum coins needed to make #{amt} is #{res}" + puts "Greedy coin count for amount #{amt}: #{res}" - # Greedy algorithm: Cannot guarantee finding the global optimal solution + # Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 20, 50] amt = 60 res = coin_change_greedy(coins, amt) puts "\ncoins = #{coins}, amt = #{amt}" - puts "Minimum coins needed to make #{amt} is #{res}" - puts "Actually minimum needed is 3, i.e., 20 + 20 + 20" + puts "Greedy coin count for amount #{amt}: #{res}" + puts "The actual minimum is 3 coins: 20 + 20 + 20" - # Greedy algorithm: Cannot guarantee finding the global optimal solution + # Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 49, 50] amt = 98 res = coin_change_greedy(coins, amt) puts "\ncoins = #{coins}, amt = #{amt}" - puts "Minimum coins needed to make #{amt} is #{res}" - puts "Actually minimum needed is 2, i.e., 49 + 49" + puts "Greedy coin count for amount #{amt}: #{res}" + puts "The actual minimum is 2 coins: 49 + 49" end diff --git a/en/codes/ruby/chapter_greedy/fractional_knapsack.rb b/en/codes/ruby/chapter_greedy/fractional_knapsack.rb index 26cba27c3..7bb82e975 100644 --- a/en/codes/ruby/chapter_greedy/fractional_knapsack.rb +++ b/en/codes/ruby/chapter_greedy/fractional_knapsack.rb @@ -15,30 +15,30 @@ class Item end end -### Fractional knapsack: greedy ### +### Fractional knapsack: greedy algorithm ### def fractional_knapsack(wgt, val, cap) - # Create item list with two attributes: weight, value + # Create a list of items, each with two attributes: weight and value items = wgt.each_with_index.map { |w, i| Item.new(w, val[i]) } - # Sort by unit value item.v / item.w from high to low + # Sort by value per unit weight, item.v / item.w, in descending order items.sort! { |a, b| (b.v.to_f / b.w) <=> (a.v.to_f / a.w) } - # Loop for greedy selection + # Repeatedly make greedy choices res = 0 for item in items if item.w <= cap - # If remaining capacity is sufficient, put the entire current item into the knapsack + # If enough capacity remains, put the entire current item in the knapsack res += item.v cap -= item.w else - # If remaining capacity is insufficient, put part of the current item into the knapsack + # If the remaining capacity is insufficient, put part of the current item in the knapsack res += (item.v.to_f / item.w) * cap - # No remaining capacity, so break out of the loop + # No capacity remains, so exit the loop break end end res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 wgt = [10, 20, 30, 40, 50] val = [50, 120, 150, 210, 240] @@ -47,5 +47,5 @@ if __FILE__ == $0 # Greedy algorithm res = fractional_knapsack(wgt, val, cap) - puts "Maximum item value not exceeding knapsack capacity is #{res}" + puts "Maximum total item value within the knapsack capacity: #{res}" end diff --git a/en/codes/ruby/chapter_greedy/max_capacity.rb b/en/codes/ruby/chapter_greedy/max_capacity.rb index 5002eeef6..c2875a54c 100644 --- a/en/codes/ruby/chapter_greedy/max_capacity.rb +++ b/en/codes/ruby/chapter_greedy/max_capacity.rb @@ -4,19 +4,19 @@ Created Time: 2024-05-07 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Maximum capacity: greedy ### +### Maximum container capacity: greedy algorithm ### def max_capacity(ht) - # Initialize i, j to be at both ends of the array + # Initialize i and j at opposite ends of the array i, j = 0, ht.length - 1 - # Initial max capacity is 0 + # The initial maximum capacity is 0 res = 0 - # Loop for greedy selection until the two boards meet + # Repeatedly make greedy choices until the two sides meet while i < j - # Update max capacity + # Update the maximum capacity cap = [ht[i], ht[j]].min * (j - i) res = [res, cap].max - # Move the shorter board inward + # Move the shorter side inward if ht[i] < ht[j] i += 1 else @@ -27,11 +27,11 @@ def max_capacity(ht) res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 ht = [3, 8, 5, 2, 7, 7, 3, 4] # Greedy algorithm res = max_capacity(ht) - puts "Maximum capacity is #{res}" + puts "Maximum capacity: #{res}" end diff --git a/en/codes/ruby/chapter_greedy/max_product_cutting.rb b/en/codes/ruby/chapter_greedy/max_product_cutting.rb index bf5d752cd..7159e9374 100644 --- a/en/codes/ruby/chapter_greedy/max_product_cutting.rb +++ b/en/codes/ruby/chapter_greedy/max_product_cutting.rb @@ -4,25 +4,25 @@ Created Time: 2024-05-07 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Maximum cutting product: greedy ### +### Maximum product after splitting: greedy algorithm ### def max_product_cutting(n) - # When n <= 3, must cut out a 1 + # When n <= 3, one part must be 1 return 1 * (n - 1) if n <= 3 - # Greedily cut out 3, a is the number of 3s, b is the remainder + # Greedily split off parts of size 3; a is the number of 3s and b is the remainder a, b = n / 3, n % 3 - # When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + # When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return (3.pow(a - 1) * 2 * 2).to_i if b == 1 - # When the remainder is 2, do nothing + # When the remainder is 2, no adjustment is needed return (3.pow(a) * 2).to_i if b == 2 - # When the remainder is 0, do nothing + # When the remainder is 0, no adjustment is needed 3.pow(a).to_i end -### Driver Code ### +### Driver code ### if __FILE__ == $0 n = 58 # Greedy algorithm res = max_product_cutting(n) - puts "Maximum cutting product is #{res}" + puts "Maximum product after cutting: #{res}" end diff --git a/en/codes/ruby/chapter_hashing/array_hash_map.rb b/en/codes/ruby/chapter_hashing/array_hash_map.rb index 5ceb92edd..9dd3c6c1e 100644 --- a/en/codes/ruby/chapter_hashing/array_hash_map.rb +++ b/en/codes/ruby/chapter_hashing/array_hash_map.rb @@ -14,11 +14,11 @@ class Pair end end -### Hash map based on array ### +### Hash table implemented with an array ### class ArrayHashMap ### Constructor ### def initialize - # Initialize array with 100 buckets + # Initialize an array containing 100 buckets @buckets = Array.new(100) end @@ -27,7 +27,7 @@ class ArrayHashMap index = key % 100 end - ### Query operation ### + ### Lookup operation ### def get(key) index = hash_func(key) pair = @buckets[index] @@ -36,17 +36,17 @@ class ArrayHashMap pair.val end - ### Add operation ### + ### Insert operation ### def put(key, val) pair = Pair.new(key, val) index = hash_func(key) @buckets[index] = pair end - ### Delete operation ### + ### Remove operation ### def remove(key) index = hash_func(key) - # Set to nil to delete + # Set the bucket to nil to indicate removal @buckets[index] = nil end @@ -71,50 +71,50 @@ class ArrayHashMap result end - ### Print hash table ### + ### Print the hash table ### def print @buckets.each { |pair| puts "#{pair.key} -> #{pair.val}" unless pair.nil? } end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize hash table + # Initialize the hash table hmap = ArrayHashMap.new - # Add operation - # Add key-value pair (key, value) to the hash table - hmap.put(12836, "Xiao Ha") - hmap.put(15937, "Xiao Luo") - hmap.put(16750, "Xiao Suan") - hmap.put(13276, "Xiao Fa") - hmap.put(10583, "Xiao Ya") - puts "\nAfter adding is complete, hash table is\nKey -> Value" + # Insert operation + # Insert the key-value pair (key, value) into the hash table + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + puts "\nHash table after insertion:\nKey -> Value" hmap.print - # Query operation - # Input key to hash table, get value + # Lookup operation + # Look up key in the hash table to obtain value name = hmap.get(15937) - puts "\nInput student ID 15937, found name #{name}" + puts "\nLook up student ID 15937: name = #{name}" # Remove operation - # Delete key-value pair (key, value) from hash table + # Remove the key-value pair (key, value) from the hash table hmap.remove(10583) - puts "\nAfter removing 10583, hash table is\nKey -> Value" + puts "\nHash table after removing 10583:\nKey -> Value" hmap.print - # Traverse hash table - puts "\nTraverse key-value pairs Key->Value" + # Traverse the hash table + puts "\nTraverse key-value pairs: Key->Value" for pair in hmap.entry_set puts "#{pair.key} -> #{pair.val}" end - puts "\nTraverse keys separately" + puts "\nTraverse the keys only" for key in hmap.key_set puts key end - puts "\nTraverse values only Value" + puts "\nTraverse values only: Value" for val in hmap.value_set puts val end diff --git a/en/codes/ruby/chapter_hashing/built_in_hash.rb b/en/codes/ruby/chapter_hashing/built_in_hash.rb index f9d410b7b..097807841 100644 --- a/en/codes/ruby/chapter_hashing/built_in_hash.rb +++ b/en/codes/ruby/chapter_hashing/built_in_hash.rb @@ -6,29 +6,29 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/list_node' -### Driver Code ### +### Driver code ### if __FILE__ == $0 num = 3 hash_num = num.hash - puts "Hash value of integer #{num} is #{hash_num}" + puts "Hash value of integer #{num}: #{hash_num}" bol = true hash_bol = bol.hash - puts "Hash value of boolean #{bol} is #{hash_bol}" + puts "Hash value of Boolean #{bol}: #{hash_bol}" dec = 3.14159 hash_dec = dec.hash - puts "Hash value of decimal #{dec} is #{hash_dec}" + puts "Hash value of floating-point number #{dec}: #{hash_dec}" - str = "Hello Algo" + str = "Hello 算法" hash_str = str.hash - puts "Hash value of string #{str} is #{hash_str}" + puts "Hash value of string #{str}: #{hash_str}" - tup = [12836, 'Xiao Ha'] + tup = [12836, '小哈'] hash_tup = tup.hash - puts "Hash value of tuple #{tup} is #{hash_tup}" + puts "Hash value of array #{tup}: #{hash_tup}" obj = ListNode.new(0) hash_obj = obj.hash - puts "Hash value of object #{obj} is #{hash_obj}" + puts "Hash value of node object #{obj}: #{hash_obj}" end diff --git a/en/codes/ruby/chapter_hashing/hash_map.rb b/en/codes/ruby/chapter_hashing/hash_map.rb index 9c4086cf1..10359c82e 100644 --- a/en/codes/ruby/chapter_hashing/hash_map.rb +++ b/en/codes/ruby/chapter_hashing/hash_map.rb @@ -6,39 +6,39 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/print_util' -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize hash table + # Initialize the hash table hmap = {} - # Add operation - # Add key-value pair (key, value) to the hash table - hmap[12836] = "Xiao Ha" - hmap[15937] = "Xiao Luo" - hmap[16750] = "Xiao Suan" - hmap[13276] = "Xiao Fa" - hmap[10583] = "Xiao Ya" - puts "\nAfter adding is complete, hash table is\nKey -> Value" + # Insert operation + # Insert the key-value pair (key, value) into the hash table + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" + puts "\nHash table after insertion:\nKey -> Value" print_hash_map(hmap) - # Query operation - # Input key into hash table to get value + # Lookup operation + # Look up key in the hash table to obtain value name = hmap[15937] - puts "\nInput student ID 15937, found name #{name}" + puts "\nLook up student ID 15937: name = #{name}" # Remove operation - # Remove key-value pair (key, value) from hash table + # Remove the key-value pair (key, value) from the hash table hmap.delete(10583) - puts "\nAfter removing 10583, hash table is\nKey -> Value" + puts "\nHash table after removing 10583:\nKey -> Value" print_hash_map(hmap) - # Traverse hash table - puts "\nTraverse key-value pairs Key->Value" + # Traverse the hash table + puts "\nTraverse key-value pairs: Key->Value" hmap.entries.each { |key, value| puts "#{key} -> #{value}" } - puts "\nTraverse keys only Key" + puts "\nTraverse keys only: Key" hmap.keys.each { |key| puts key } - puts "\nTraverse values only Value" + puts "\nTraverse values only: Value" hmap.values.each { |val| puts val } end diff --git a/en/codes/ruby/chapter_hashing/hash_map_chaining.rb b/en/codes/ruby/chapter_hashing/hash_map_chaining.rb index f439e733a..5a68f7ea4 100644 --- a/en/codes/ruby/chapter_hashing/hash_map_chaining.rb +++ b/en/codes/ruby/chapter_hashing/hash_map_chaining.rb @@ -6,14 +6,14 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative './array_hash_map' -### Hash map with chaining ### +### Hash table using chaining ### class HashMapChaining ### Constructor ### def initialize @size = 0 # Number of key-value pairs @capacity = 4 # Hash table capacity - @load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion - @extend_ratio = 2 # Expansion multiplier + @load_thres = 2.0 / 3.0 # Load factor threshold that triggers capacity growth + @extend_ratio = 2 # Capacity growth factor @buckets = Array.new(@capacity) { [] } # Bucket array end @@ -27,42 +27,42 @@ class HashMapChaining @size / @capacity end - ### Query operation ### + ### Lookup operation ### def get(key) index = hash_func(key) bucket = @buckets[index] - # Traverse bucket, if key is found, return corresponding val + # Traverse the bucket; if key is found, return its corresponding val for pair in bucket return pair.val if pair.key == key end - # Return nil if key not found + # Return nil if key is not found nil end - ### Add operation ### + ### Insert operation ### def put(key, val) - # When load factor exceeds threshold, perform expansion + # Grow the capacity when the load factor exceeds the threshold extend if load_factor > @load_thres index = hash_func(key) bucket = @buckets[index] - # Traverse bucket, if specified key is encountered, update corresponding val and return + # Traverse the bucket; if the specified key is found, update its val and return for pair in bucket if pair.key == key pair.val = val return end end - # If key does not exist, append key-value pair to the end + # If key is absent, append the key-value pair at the end pair = Pair.new(key, val) bucket << pair @size += 1 end - ### Delete operation ### + ### Remove operation ### def remove(key) index = hash_func(key) bucket = @buckets[index] - # Traverse bucket and remove key-value pair from it + # Traverse the bucket and remove the key-value pair for pair in bucket if pair.key == key bucket.delete(pair) @@ -72,15 +72,15 @@ class HashMapChaining end end - ### Expand hash table ### + ### Grow the hash table capacity ### def extend - # Temporarily store original hash table + # Temporarily store the original hash table buckets = @buckets - # Initialize expanded new hash table + # Initialize a new hash table with the expanded capacity @capacity *= @extend_ratio @buckets = Array.new(@capacity) { [] } @size = 0 - # Move key-value pairs from original hash table to new hash table + # Move the key-value pairs from the original hash table to the new one for bucket in buckets for pair in bucket put(pair.key, pair.val) @@ -88,7 +88,7 @@ class HashMapChaining end end - ### Print hash table ### + ### Print the hash table ### def print for bucket in @buckets res = [] @@ -100,29 +100,29 @@ class HashMapChaining end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - ### Initialize hash table + ### Initialize the hash table hashmap = HashMapChaining.new - # Add operation - # Add key-value pair (key, value) to the hash table - hashmap.put(12836, "Xiao Ha") - hashmap.put(15937, "Xiao Luo") - hashmap.put(16750, "Xiao Suan") - hashmap.put(13276, "Xiao Fa") - hashmap.put(10583, "Xiao Ya") - puts "\nAfter adding, hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]" + # Insert operation + # Insert the key-value pair (key, value) into the hash table + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + puts "\nHash table after insertion:\n[Key1 -> Value1, Key2 -> Value2, ...]" hashmap.print - # Query operation - # Input key into hash table to get value + # Lookup operation + # Look up key in the hash table to obtain value name = hashmap.get(13276) - puts "\nInput student ID 13276, found name #{name}" + puts "\nLook up student ID 13276: name = #{name}" # Remove operation - # Remove key-value pair (key, value) from hash table + # Remove the key-value pair (key, value) from the hash table hashmap.remove(12836) - puts "\nAfter deleting 12836, hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]" + puts "\nHash table after removing 12836:\n[Key1 -> Value1, Key2 -> Value2, ...]" hashmap.print end diff --git a/en/codes/ruby/chapter_hashing/hash_map_open_addressing.rb b/en/codes/ruby/chapter_hashing/hash_map_open_addressing.rb index 078776632..b9221cd65 100644 --- a/en/codes/ruby/chapter_hashing/hash_map_open_addressing.rb +++ b/en/codes/ruby/chapter_hashing/hash_map_open_addressing.rb @@ -6,16 +6,16 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative './array_hash_map' -### Hash map with open addressing ### +### Hash table with open addressing ### class HashMapOpenAddressing - TOMBSTONE = Pair.new(-1, '-1') # Removal marker + TOMBSTONE = Pair.new(-1, '-1') # Tombstone marker ### Constructor ### def initialize @size = 0 # Number of key-value pairs @capacity = 4 # Hash table capacity - @load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion - @extend_ratio = 2 # Expansion multiplier + @load_thres = 2.0 / 3.0 # Load factor threshold that triggers capacity growth + @extend_ratio = 2 # Capacity growth factor @buckets = Array.new(@capacity) # Bucket array end @@ -29,83 +29,83 @@ class HashMapOpenAddressing @size / @capacity end - ### Search bucket index for key ### + ### Find the bucket index for key ### def find_bucket(key) index = hash_func(key) first_tombstone = -1 - # Linear probing, break when encountering an empty bucket + # Probe linearly until an empty bucket is reached while !@buckets[index].nil? - # If key is encountered, return the corresponding bucket index + # If key is found, return its bucket index if @buckets[index].key == key - # If a removal marker was encountered before, move the key-value pair to that index + # If a tombstone was encountered earlier, move the key-value pair to that index if first_tombstone != -1 @buckets[first_tombstone] = @buckets[index] @buckets[index] = TOMBSTONE - return first_tombstone # Return the moved bucket index + return first_tombstone # Return the bucket index after the move end - return index # Return bucket index + return index # Return the bucket index end - # Record the first removal marker encountered + # Record the first tombstone encountered first_tombstone = index if first_tombstone == -1 && @buckets[index] == TOMBSTONE - # Calculate bucket index, wrap around to the head if past the tail + # Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % @capacity end - # If key does not exist, return the index for insertion + # If key is absent, return the index for insertion first_tombstone == -1 ? index : first_tombstone end - ### Query operation ### + ### Lookup operation ### def get(key) - # Search for bucket index corresponding to key + # Find the bucket index for key index = find_bucket(key) - # If key-value pair is found, return corresponding val + # If the key-value pair is found, return its val return @buckets[index].val unless [nil, TOMBSTONE].include?(@buckets[index]) - # Return nil if key-value pair does not exist + # Return nil if the key-value pair does not exist nil end - ### Add operation ### + ### Insert operation ### def put(key, val) - # When load factor exceeds threshold, perform expansion + # Grow the capacity when the load factor exceeds the threshold extend if load_factor > @load_thres - # Search for bucket index corresponding to key + # Find the bucket index for key index = find_bucket(key) - # If key-value pair found, overwrite val and return + # If the key-value pair is found, overwrite val and return unless [nil, TOMBSTONE].include?(@buckets[index]) @buckets[index].val = val return end - # If key-value pair does not exist, add the key-value pair + # If the key-value pair is absent, insert it @buckets[index] = Pair.new(key, val) @size += 1 end - ### Delete operation ### + ### Remove operation ### def remove(key) - # Search for bucket index corresponding to key + # Find the bucket index for key index = find_bucket(key) - # If key-value pair is found, overwrite it with removal marker + # If the key-value pair is found, replace it with a tombstone unless [nil, TOMBSTONE].include?(@buckets[index]) @buckets[index] = TOMBSTONE @size -= 1 end end - ### Expand hash table ### + ### Grow the hash table capacity ### def extend - # Temporarily store the original hash table + # Keep a reference to the original hash table buckets_tmp = @buckets - # Initialize expanded new hash table + # Initialize a new hash table with the expanded capacity @capacity *= @extend_ratio @buckets = Array.new(@capacity) @size = 0 - # Move key-value pairs from original hash table to new hash table + # Move the key-value pairs from the original hash table to the new one for pair in buckets_tmp put(pair.key, pair.val) unless [nil, TOMBSTONE].include?(pair) end end - ### Print hash table ### + ### Print the hash table ### def print for pair in @buckets if pair.nil? @@ -119,29 +119,29 @@ class HashMapOpenAddressing end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize hash table + # Initialize the hash table hashmap = HashMapOpenAddressing.new - # Add operation - # Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Xiao Ha") - hashmap.put(15937, "Xiao Luo") - hashmap.put(16750, "Xiao Suan") - hashmap.put(13276, "Xiao Fa") - hashmap.put(10583, "Xiao Ya") - puts "\nAfter adding is complete, hash table is\nKey -> Value" + # Insert operation + # Insert the key-value pair (key, val) into the hash table + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + puts "\nHash table after insertion:\nKey -> Value" hashmap.print - # Query operation - # Input key into hash table to get value val + # Lookup operation + # Look up key in the hash table to obtain val name = hashmap.get(13276) - puts "\nInput student ID 13276, found name #{name}" + puts "\nLook up student ID 13276: name = #{name}" # Remove operation - # Remove key-value pair (key, val) from hash table + # Remove the key-value pair (key, val) from the hash table hashmap.remove(16750) - puts "\nAfter removing 16750, hash table is\nKey -> Value" + puts "\nHash table after removing 16750:\nKey -> Value" hashmap.print end diff --git a/en/codes/ruby/chapter_hashing/simple_hash.rb b/en/codes/ruby/chapter_hashing/simple_hash.rb index c3bd3d789..49c4b0e3f 100644 --- a/en/codes/ruby/chapter_hashing/simple_hash.rb +++ b/en/codes/ruby/chapter_hashing/simple_hash.rb @@ -34,7 +34,7 @@ def xor_hash(key) hash % modulus end -### Rotational hash ### +### Rotating hash ### def rot_hash(key) hash = 0 modulus = 1_000_000_007 @@ -44,19 +44,19 @@ def rot_hash(key) hash % modulus end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - key = "Hello Algo" + key = "Hello 算法" hash = add_hash(key) - puts "Additive hash value is #{hash}" + puts "Additive hash value: #{hash}" hash = mul_hash(key) - puts "Multiplicative hash value is #{hash}" + puts "Multiplicative hash value: #{hash}" hash = xor_hash(key) - puts "XOR hash value is #{hash}" + puts "XOR hash value: #{hash}" hash = rot_hash(key) - puts "Rotational hash value is #{hash}" + puts "Rotating hash value: #{hash}" end diff --git a/en/codes/ruby/chapter_heap/my_heap.rb b/en/codes/ruby/chapter_heap/my_heap.rb index ef029db86..a3608a6f2 100644 --- a/en/codes/ruby/chapter_heap/my_heap.rb +++ b/en/codes/ruby/chapter_heap/my_heap.rb @@ -10,27 +10,27 @@ require_relative '../utils/print_util' class MaxHeap attr_reader :max_heap - ### Constructor, build heap from input list ### + ### Constructor: build a heap from the input list ### def initialize(nums) - # Add list elements to heap as is + # Place the list elements in the heap without changing their order @max_heap = nums - # Heapify all nodes except leaf nodes + # Sift down from every non-leaf node parent(size - 1).downto(0) do |i| sift_down(i) end end - ### Get left child index ### + ### Get the left child index ### def left(i) 2 * i + 1 end - ### Get right child index ### + ### Get the right child index ### def right(i) 2 * i + 2 end - ### Get parent node index ### + ### Get the parent index ### def parent(i) (i - 1) / 2 # Floor division end @@ -40,108 +40,108 @@ class MaxHeap @max_heap[i], @max_heap[j] = @max_heap[j], @max_heap[i] end - ### Get heap size ### + ### Get the heap size ### def size @max_heap.length end - ### Check if heap is empty ### + ### Check whether the heap is empty ### def is_empty? size == 0 end - ### Access heap top element ### + ### Access the top element of the heap ### def peek @max_heap[0] end - ### Push element to heap ### + ### Push an element onto the heap ### def push(val) - # Add node + # Add a node @max_heap << val - # Heapify from bottom to top + # Sift up sift_up(size - 1) end - ### Heapify from node i, bottom to top ### + ### Sift up starting from node i ### def sift_up(i) loop do - # Get parent node of node i + # Get the parent of node i p = parent(i) - # When "crossing root node" or "node needs no repair", end heapify + # Stop sifting when the root has been passed or the node needs no repair break if p < 0 || @max_heap[i] <= @max_heap[p] - # Swap two nodes + # Swap the two nodes swap(i, p) - # Loop upward heapify + # Continue sifting up i = p end end - ### Pop element from heap ### + ### Pop an element from the heap ### def pop - # Handle empty case + # Handle an empty heap raise IndexError, "Heap is empty" if is_empty? - # Delete node + # Swap the root with the rightmost leaf on the last level (swap the first and last elements) swap(0, size - 1) - # Remove node + # Remove a node val = @max_heap.pop - # Return top element + # Sift down sift_down(0) - # Return heap top element + # Return the top element of the heap val end - ### Heapify from node i, top to bottom ### + ### Sift down starting from node i ### def sift_down(i) loop do - # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + # Find the node with the largest value among i, l, and r, and store its index in ma l, r, ma = left(i), right(i), i ma = l if l < size && @max_heap[l] > @max_heap[ma] ma = r if r < size && @max_heap[r] > @max_heap[ma] - # Swap two nodes + # Stop if node i is largest or both child indices l and r are out of bounds break if ma == i - # Swap two nodes + # Swap the two nodes swap(i, ma) - # Loop downwards heapification + # Continue sifting down i = ma end end - ### Print heap (binary tree) ### + ### Print the heap (binary tree)### def __print__ print_heap(@max_heap) end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + # Initialize a max heap max_heap = MaxHeap.new([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) - puts "\nAfter inputting list and building heap" + puts "\nAfter building a heap from the input list:" max_heap.__print__ - # Check if heap is empty + # Get the top element of the heap peek = max_heap.peek - puts "\nHeap top element is #{peek}" + puts "\nHeap top: #{peek}" - # Element enters heap + # Push an element onto the heap val = 7 max_heap.push(val) - puts "\nAfter element #{val} pushes to heap" + puts "\nAfter pushing #{val} onto the heap" max_heap.__print__ - # Time complexity is O(n), not O(nlogn) + # Pop the top element from the heap peek = max_heap.pop - puts "\nAfter heap top element #{peek} pops from heap" + puts "\nAfter popping heap top #{peek}" max_heap.__print__ - # Get heap size + # Get the heap size size = max_heap.size - puts "\nHeap size is #{size}" + puts "\nNumber of elements in the heap: #{size}" - # Check if heap is empty + # Check whether the heap is empty is_empty = max_heap.is_empty? - puts "\nIs heap empty #{is_empty}" + puts "\nIs the heap empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_heap/top_k.rb b/en/codes/ruby/chapter_heap/top_k.rb index 5578dbb35..95804e01d 100644 --- a/en/codes/ruby/chapter_heap/top_k.rb +++ b/en/codes/ruby/chapter_heap/top_k.rb @@ -6,44 +6,44 @@ Author: Blue Bean (lonnnnnnner@gmail.com) require_relative "./my_heap" -### Push element to heap ### +### Push an element onto the heap ### def push_min_heap(heap, val) - # Negate element + # Negate the value heap.push(-val) end -### Pop element from heap ### +### Pop an element from the heap ### def pop_min_heap(heap) - # Negate element + # Negate the value -heap.pop end -### Access heap top element ### +### Access the top element of the heap ### def peek_min_heap(heap) - # Negate element + # Negate the value -heap.peek end -### Get elements from heap ### +### Get the elements in the heap ### def get_min_heap(heap) - # Negate all elements in heap + # Negate all elements in the heap heap.max_heap.map { |x| -x } end -### Find largest k elements in array using heap ### +### Use a heap to find the k largest elements in an array ### def top_k_heap(nums, k) - # Python's heapq module implements min heap by default - # Note: We negate all heap elements to simulate min heap using max heap + # Initialize a min heap + # Note: negate all values in the heap to simulate a min heap with a max heap max_heap = MaxHeap.new([]) - # Enter the first k elements of array into heap + # Push the first k array elements onto the heap for i in 0...k push_min_heap(max_heap, nums[i]) end - # Starting from the (k+1)th element, maintain heap length as k + # Starting with element k+1, keep the heap size at k for i in k...nums.length - # If current element is greater than top element, top element exits heap, current element enters heap + # If the current element is larger than the heap top, pop the top element and push the current element if nums[i] > peek_min_heap(max_heap) pop_min_heap(max_heap) push_min_heap(max_heap, nums[i]) @@ -53,12 +53,12 @@ def top_k_heap(nums, k) get_min_heap(max_heap) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [1, 7, 6, 3, 2] k = 3 res = top_k_heap(nums, k) - puts "The largest #{k} elements are" + puts "The largest #{k} elements are:" print_heap(res) end diff --git a/en/codes/ruby/chapter_searching/binary_search.rb b/en/codes/ruby/chapter_searching/binary_search.rb index 072f2a8ff..648ca8558 100644 --- a/en/codes/ruby/chapter_searching/binary_search.rb +++ b/en/codes/ruby/chapter_searching/binary_search.rb @@ -6,58 +6,58 @@ Author: Blue Bean (lonnnnnnner@gmail.com) ### Binary search (closed interval) ### def binary_search(nums, target) - # Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + # Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements i, j = 0, nums.length - 1 - # Loop, exit when the search interval is empty (empty when i > j) + # Loop until the search interval is empty (when i > j) while i <= j - # In theory, Ruby numbers can be infinitely large (limited by memory), no need to consider overflow + # Ruby integers can grow arbitrarily large, limited by available memory, so integer overflow is not a concern here m = (i + j) / 2 # Calculate the midpoint index m if nums[m] < target - i = m + 1 # This means target is in the interval [m+1, j] + i = m + 1 # In this case, target must be in [m+1, j] if it exists elsif nums[m] > target - j = m - 1 # This means target is in the interval [i, m-1] + j = m - 1 # In this case, target must be in [i, m-1] if it exists else - return m # Found the target element, return its index + return m # The target element was found; return its index end end - -1 # Target element not found, return -1 + -1 # The target element was not found; return -1 end -### Binary search (left-closed right-open interval) ### +### Binary search (left-closed, right-open interval) ### def binary_search_lcro(nums, target) - # Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + # Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last i, j = 0, nums.length - # Loop, exit when the search interval is empty (empty when i = j) + # Loop until the search interval is empty (when i = j) while i < j # Calculate the midpoint index m m = (i + j) / 2 if nums[m] < target - i = m + 1 # This means target is in the interval [m+1, j) + i = m + 1 # In this case, target must be in [m+1, j) if it exists elsif nums[m] > target - j = m - 1 # This means target is in the interval [i, m) + j = m - 1 # In this case, target must be in [i, m) if it exists else - return m # Found the target element, return its index + return m # The target element was found; return its index end end - -1 # Target element not found, return -1 + -1 # The target element was not found; return -1 end -### Driver Code ### +### Driver code ### if __FILE__ == $0 target = 6 nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] - # Binary search (closed interval on both sides) + # Binary search (closed interval) index = binary_search(nums, target) - puts "Index of target element 6 is #{index}" + puts "Index of target element 6 = #{index}" - # Binary search (left-closed right-open interval) + # Binary search (left-closed, right-open interval) index = binary_search_lcro(nums, target) - puts "Index of target element 6 is #{index}" + puts "Index of target element 6 = #{index}" end diff --git a/en/codes/ruby/chapter_searching/binary_search_edge.rb b/en/codes/ruby/chapter_searching/binary_search_edge.rb index 9dadd0368..16e628d8c 100644 --- a/en/codes/ruby/chapter_searching/binary_search_edge.rb +++ b/en/codes/ruby/chapter_searching/binary_search_edge.rb @@ -6,42 +6,42 @@ Author: Blue Bean (lonnnnnnner@gmail.com) require_relative './binary_search_insertion' -### Binary search leftmost target ### +### Find the leftmost occurrence of target using binary search ### def binary_search_left_edge(nums, target) - # Equivalent to finding the insertion point of target + # This is equivalent to finding the insertion point of target i = binary_search_insertion(nums, target) - # Target not found, return -1 + # If target was not found, return -1 return -1 if i == nums.length || nums[i] != target - i # Found target, return index i + i # Target was found; return index i end -### Binary search rightmost target ### +### Find the rightmost occurrence of target using binary search ### def binary_search_right_edge(nums, target) - # Convert to finding the leftmost target + 1 + # For integer elements, find the leftmost insertion point of target + 1 i = binary_search_insertion(nums, target + 1) - # j points to the rightmost target, i points to the first element greater than target + # i is the index of the first element greater than target (or the array length); j is the candidate rightmost target index j = i - 1 - # Target not found, return -1 + # If target was not found, return -1 return -1 if j == -1 || nums[j] != target - j # Found target, return index j + j # Target was found; return index j end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Array with duplicate elements + # Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] puts "\nArray nums = #{nums}" - # Binary search left and right boundaries + # Find the left and right boundaries using binary search for target in [6, 7] index = binary_search_left_edge(nums, target) - puts "Leftmost element #{target} index is #{index}" + puts "Index of the leftmost occurrence of #{target}: #{index}" index = binary_search_right_edge(nums, target) - puts "Rightmost element #{target} index is #{index}" + puts "Index of the rightmost occurrence of #{target}: #{index}" end end diff --git a/en/codes/ruby/chapter_searching/binary_search_insertion.rb b/en/codes/ruby/chapter_searching/binary_search_insertion.rb index 33f13a3c5..9fb98edc0 100644 --- a/en/codes/ruby/chapter_searching/binary_search_insertion.rb +++ b/en/codes/ruby/chapter_searching/binary_search_insertion.rb @@ -4,9 +4,9 @@ Created Time: 2024-04-09 Author: Blue Bean (lonnnnnnner@gmail.com) =end -### Binary search insertion point (no duplicates) ### +### Find an insertion point using binary search (no duplicates) ### def binary_search_insertion_simple(nums, target) - # Initialize closed interval [0, n-1] + # Initialize the closed interval [0, n-1] i, j = 0, nums.length - 1 while i <= j @@ -14,20 +14,20 @@ def binary_search_insertion_simple(nums, target) m = (i + j) / 2 if nums[m] < target - i = m + 1 # target is in the interval [m+1, j] + i = m + 1 # Target must be in [m+1, j] if it exists elsif nums[m] > target - j = m - 1 # target is in the interval [i, m-1] + j = m - 1 # Target must be in [i, m-1] if it exists else - return m # Found target, return insertion point m + return m # Target was found; return insertion point m end end - i # Target not found, return insertion point i + i # Target was not found; return insertion point i end -### Binary search insertion point (with duplicates) ### +### Find an insertion point using binary search (with duplicates) ### def binary_search_insertion(nums, target) - # Initialize closed interval [0, n-1] + # Initialize the closed interval [0, n-1] i, j = 0, nums.length - 1 while i <= j @@ -35,34 +35,34 @@ def binary_search_insertion(nums, target) m = (i + j) / 2 if nums[m] < target - i = m + 1 # target is in the interval [m+1, j] + i = m + 1 # Target must be in [m+1, j] if it exists elsif nums[m] > target - j = m - 1 # target is in the interval [i, m-1] + j = m - 1 # Target must be in [i, m-1] if it exists else - j = m - 1 # The rightmost element less than target is in the interval [i, m-1] + j = m - 1 # Continue in [i, m-1] to find the rightmost element smaller than target end end i # Return insertion point i end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Array without duplicate elements + # Array with no duplicate elements nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] puts "\nArray nums = #{nums}" - # Binary search for insertion point + # Find an insertion point using binary search for target in [6, 9] index = binary_search_insertion_simple(nums, target) - puts "Insertion point index for element #{target} is #{index}" + puts "Insertion index for element #{target}: #{index}" end - # Array with duplicate elements + # Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] puts "\nArray nums = #{nums}" - # Binary search for insertion point + # Find an insertion point using binary search for target in [2, 6, 20] index = binary_search_insertion(nums, target) - puts "Insertion point index for element #{target} is #{index}" + puts "Insertion index for element #{target}: #{index}" end end diff --git a/en/codes/ruby/chapter_searching/hashing_search.rb b/en/codes/ruby/chapter_searching/hashing_search.rb index c8f4b60af..56523f1e5 100644 --- a/en/codes/ruby/chapter_searching/hashing_search.rb +++ b/en/codes/ruby/chapter_searching/hashing_search.rb @@ -6,42 +6,42 @@ Author: Blue Bean (lonnnnnnner@gmail.com) require_relative '../utils/list_node' -### Hash search (array) ### +### Hash-based search (array) ### def hashing_search_array(hmap, target) - # Hash table's key: target element, value: index - # If this key does not exist in the hash table, return -1 + # Hash table key: target element; value: index + # Return -1 if the hash table does not contain this key hmap[target] || -1 end -### Hash search (linked list) ### +### Hash-based search (linked list) ### def hashing_search_linkedlist(hmap, target) - # Hash table's key: target element, value: node object - # If this key does not exist in the hash table, return None + # Hash table key: target element; value: node object + # Return nil if the hash table does not contain this key hmap[target] || nil end -### Driver Code ### +### Driver code ### if __FILE__ == $0 target = 3 - # Hash search (array) + # Hash-based search (array) nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] - # Initialize hash table + # Initialize the hash table map0 = {} for i in 0...nums.length - map0[nums[i]] = i # key: element, value: index + map0[nums[i]] = i # key: element; value: index end index = hashing_search_array(map0, target) puts "Index of target element 3 = #{index}" - # Hash search (linked list) + # Hash-based search (linked list) head = arr_to_linked_list(nums) - # Initialize hash table + # Initialize the hash table map1 = {} while head map1[head.val] = head head = head.next end node = hashing_search_linkedlist(map1, target) - puts "Node object for target value 3 is #{node}" + puts "Node object for target value 3: #{node}" end diff --git a/en/codes/ruby/chapter_searching/linear_search.rb b/en/codes/ruby/chapter_searching/linear_search.rb index 6f585a7e8..84684ecd1 100644 --- a/en/codes/ruby/chapter_searching/linear_search.rb +++ b/en/codes/ruby/chapter_searching/linear_search.rb @@ -8,37 +8,37 @@ require_relative '../utils/list_node' ### Linear search (array) ### def linear_search_array(nums, target) - # Traverse array + # Traverse the array for i in 0...nums.length - return i if nums[i] == target # Found the target element, return its index + return i if nums[i] == target # The target element was found; return its index end - -1 # Target element not found, return -1 + -1 # The target element was not found; return -1 end ### Linear search (linked list) ### def linear_search_linkedlist(head, target) # Traverse the linked list while head - return head if head.val == target # Found the target node, return it + return head if head.val == target # The target node was found; return it head = head.next end - nil # Target node not found, return None + nil # The target node was not found; return nil end -### Driver Code ### +### Driver code ### if __FILE__ == $0 target = 3 - # Perform linear search in array + # Perform linear search on the array nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] index = linear_search_array(nums, target) puts "Index of target element 3 = #{index}" - # Perform linear search in linked list + # Perform linear search on the linked list head = arr_to_linked_list(nums) node = linear_search_linkedlist(head, target) - puts "Node object for target value 3 is #{node}" + puts "Node object for target value 3: #{node}" end diff --git a/en/codes/ruby/chapter_searching/two_sum.rb b/en/codes/ruby/chapter_searching/two_sum.rb index 2b0d30703..f58d63b83 100644 --- a/en/codes/ruby/chapter_searching/two_sum.rb +++ b/en/codes/ruby/chapter_searching/two_sum.rb @@ -4,9 +4,9 @@ Created Time: 2024-04-09 Author: Blue Bean (lonnnnnnner@gmail.com) =end -### Method 1: Brute force enumeration ### +### Method 1: brute-force enumeration ### def two_sum_brute_force(nums, target) - # Two nested loops, time complexity is O(n^2) + # Two nested loops give a time complexity of O(n^2) for i in 0...(nums.length - 1) for j in (i + 1)...nums.length return [i, j] if nums[i] + nums[j] == target @@ -16,11 +16,11 @@ def two_sum_brute_force(nums, target) [] end -### Method 2: Auxiliary hash table ### +### Method 2: auxiliary hash table ### def two_sum_hash_table(nums, target) - # Auxiliary hash table, space complexity is O(n) + # The auxiliary hash table uses O(n) space dic = {} - # Single loop, time complexity is O(n) + # A single loop gives a time complexity of O(n) for i in 0...nums.length return [dic[target - nums[i]], i] if dic.has_key?(target - nums[i]) @@ -30,17 +30,17 @@ def two_sum_hash_table(nums, target) [] end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # ======= Test Case ======= + # ======= Test case ======= nums = [2, 7, 11, 15] target = 13 - # ====== Driver Code ====== + # ====== Driver code ====== # Method 1 res = two_sum_brute_force(nums, target) - puts "Method 1 res = #{res}" + puts "Method 1: res = #{res}" # Method 2 res = two_sum_hash_table(nums, target) - puts "Method 2 res = #{res}" + puts "Method 2: res = #{res}" end diff --git a/en/codes/ruby/chapter_sorting/bubble_sort.rb b/en/codes/ruby/chapter_sorting/bubble_sort.rb index 03b2ddc5e..1170bafbc 100644 --- a/en/codes/ruby/chapter_sorting/bubble_sort.rb +++ b/en/codes/ruby/chapter_sorting/bubble_sort.rb @@ -7,9 +7,9 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Bubble sort ### def bubble_sort(nums) n = nums.length - # Outer loop: unsorted range is [0, i] + # Outer loop: the unsorted range is [0, i] for i in (n - 1).downto(1) - # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + # Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0...i if nums[j] > nums[j + 1] # Swap nums[j] and nums[j + 1] @@ -19,27 +19,27 @@ def bubble_sort(nums) end end -### Bubble sort (flag optimization) ### +### Bubble sort (flag optimization)### def bubble_sort_with_flag(nums) n = nums.length - # Outer loop: unsorted range is [0, i] + # Outer loop: the unsorted range is [0, i] for i in (n - 1).downto(1) - flag = false # Initialize flag + flag = false # Initialize the flag - # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + # Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0...i if nums[j] > nums[j + 1] # Swap nums[j] and nums[j + 1] nums[j], nums[j + 1] = nums[j + 1], nums[j] - flag = true # Record element swap + flag = true # Record that a swap occurred end end - break unless flag # No elements were swapped in this round of "bubbling", exit directly + break unless flag # Exit immediately if this pass made no swaps end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [4, 1, 3, 1, 5, 2] bubble_sort(nums) @@ -47,5 +47,5 @@ if __FILE__ == $0 nums1 = [4, 1, 3, 1, 5, 2] bubble_sort_with_flag(nums1) - puts "After bubble sort, nums = #{nums1}" + puts "After bubble sort, nums1 = #{nums1}" end diff --git a/en/codes/ruby/chapter_sorting/bucket_sort.rb b/en/codes/ruby/chapter_sorting/bucket_sort.rb index 47569176c..d5eec33c6 100644 --- a/en/codes/ruby/chapter_sorting/bucket_sort.rb +++ b/en/codes/ruby/chapter_sorting/bucket_sort.rb @@ -6,13 +6,13 @@ Author: Martin Xu (martin.xus@gmail.com) ### Bucket sort ### def bucket_sort(nums) - # Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + # Initialize k = floor(n/2) buckets, aiming for about 2 elements per bucket k = nums.length / 2 buckets = Array.new(k) { [] } - # 1. Distribute array elements into various buckets + # 1. Distribute the array elements among the buckets nums.each do |num| - # Input data range is [0, 1), use num * k to map to index range [0, k-1] + # The input range is [0, 1); use num * k to map each value to an index in [0, k-1] i = (num * k).to_i # Add num to bucket i buckets[i] << num @@ -20,11 +20,11 @@ def bucket_sort(nums) # 2. Sort each bucket buckets.each do |bucket| - # Use built-in sorting function, can also replace with other sorting algorithms + # Use the built-in sorting function, or replace it with another sorting algorithm bucket.sort! end - # 3. Traverse buckets to merge results + # 3. Traverse the buckets to combine the results i = 0 buckets.each do |bucket| bucket.each do |num| @@ -34,9 +34,9 @@ def bucket_sort(nums) end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Assume input data is floating point, interval [0, 1) + # Assume the input consists of floating-point numbers in [0, 1) nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37] bucket_sort(nums) puts "After bucket sort, nums = #{nums}" diff --git a/en/codes/ruby/chapter_sorting/counting_sort.rb b/en/codes/ruby/chapter_sorting/counting_sort.rb index 27d0edf3a..3353946d5 100644 --- a/en/codes/ruby/chapter_sorting/counting_sort.rb +++ b/en/codes/ruby/chapter_sorting/counting_sort.rb @@ -6,15 +6,15 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Counting sort ### def counting_sort_naive(nums) - # Simple implementation, cannot be used for sorting objects - # 1. Count the maximum element m in the array + # Simple implementation; cannot be used to sort objects + # 1. Find the maximum array element m m = 0 nums.each { |num| m = [m, num].max } - # 2. Count the occurrence of each number - # counter[num] represents the occurrence of num + # 2. Count the occurrences of each number + # counter[num] is the number of occurrences of num counter = Array.new(m + 1, 0) nums.each { |num| counter[num] += 1 } - # 3. Traverse counter, filling each element back into the original array nums + # 3. Traverse counter and fill the original array nums with the elements i = 0 for num in 0...(m + 1) (0...counter[num]).each do @@ -26,30 +26,30 @@ end ### Counting sort ### def counting_sort(nums) - # Complete implementation, can sort objects and is a stable sort - # 1. Count the maximum element m in the array + # Full, stable implementation; the approach can be adapted to sort objects + # 1. Find the maximum array element m m = nums.max - # 2. Count the occurrence of each number - # counter[num] represents the occurrence of num + # 2. Count the occurrences of each number + # counter[num] is the number of occurrences of num counter = Array.new(m + 1, 0) nums.each { |num| counter[num] += 1 } - # 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - # counter[num]-1 is the last index where num appears in res + # 3. Compute prefix sums of counter to convert occurrence counts into end positions + # That is, counter[num]-1 is the index of the last occurrence of num in res (0...m).each { |i| counter[i + 1] += counter[i] } - # 4. Traverse nums in reverse, fill elements into result array res - # Initialize the array res to record results + # 4. Traverse nums in reverse and place each element in the result array res + # Initialize array res to store the result n = nums.length res = Array.new(n, 0) (n - 1).downto(0).each do |i| num = nums[i] res[counter[num] - 1] = num # Place num at the corresponding index - counter[num] -= 1 # Decrement the prefix sum by 1, getting the next index to place num + counter[num] -= 1 # Decrement the prefix sum by 1 to determine where to place the next occurrence of num end - # Use result array res to overwrite the original array nums + # Overwrite the original array nums with the result array res (0...n).each { |i| nums[i] = res[i] } end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] diff --git a/en/codes/ruby/chapter_sorting/heap_sort.rb b/en/codes/ruby/chapter_sorting/heap_sort.rb index 63e36a22e..78958e3e0 100644 --- a/en/codes/ruby/chapter_sorting/heap_sort.rb +++ b/en/codes/ruby/chapter_sorting/heap_sort.rb @@ -4,40 +4,40 @@ Created Time: 2024-04-10 Author: junminhong (junminhong1110@gmail.com) =end -### Heap length is n, heapify from node i, top to bottom ### +### Sift down from node i in a heap of size n ### def sift_down(nums, n, i) while true - # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + # Find the node with the largest value among i, l, and r, and store its index in ma l = 2 * i + 1 r = 2 * i + 2 ma = i ma = l if l < n && nums[l] > nums[ma] ma = r if r < n && nums[r] > nums[ma] - # Swap two nodes + # Stop if node i is largest or both child indices l and r are out of bounds break if ma == i - # Swap two nodes + # Swap the two nodes nums[i], nums[ma] = nums[ma], nums[i] - # Loop downwards heapification + # Continue sifting down i = ma end end ### Heap sort ### def heap_sort(nums) - # Build heap operation: heapify all nodes except leaves + # Build the heap by sifting down from every non-leaf node (nums.length / 2 - 1).downto(0) do |i| sift_down(nums, nums.length, i) end - # Extract the largest element from the heap and repeat for n-1 rounds + # Extract the largest element from the heap, repeating for n-1 rounds (nums.length - 1).downto(1) do |i| - # Delete node + # Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) nums[0], nums[i] = nums[i], nums[0] - # Start heapifying the root node, from top to bottom + # Sift down starting at the root sift_down(nums, i, 0) end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [4, 1, 3, 1, 5, 2] heap_sort(nums) diff --git a/en/codes/ruby/chapter_sorting/insertion_sort.rb b/en/codes/ruby/chapter_sorting/insertion_sort.rb index d9e205e56..d5f267c7d 100644 --- a/en/codes/ruby/chapter_sorting/insertion_sort.rb +++ b/en/codes/ruby/chapter_sorting/insertion_sort.rb @@ -7,20 +7,20 @@ Author: Cy (3739004@gmail.com), Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Insertion sort ### def insertion_sort(nums) n = nums.length - # Outer loop: sorted interval is [0, i-1] + # Outer loop: the sorted range is [0, i-1] for i in 1...n base = nums[i] j = i - 1 - # Inner loop: insert base into the correct position within the sorted interval [0, i-1] + # Inner loop: insert base at the correct position in the sorted range [0, i-1] while j >= 0 && nums[j] > base - nums[j + 1] = nums[j] # Move nums[j] to the right by one position + nums[j + 1] = nums[j] # Shift nums[j] one position to the right j -= 1 end - nums[j + 1] = base # Assign base to the correct position + nums[j + 1] = base # Place base at the correct position end end -### Driver Code ### +### Driver code ### nums = [4, 1, 3, 1, 5, 2] insertion_sort(nums) puts "After insertion sort, nums = #{nums}" diff --git a/en/codes/ruby/chapter_sorting/merge_sort.rb b/en/codes/ruby/chapter_sorting/merge_sort.rb index 8682c1d79..1925f48a5 100644 --- a/en/codes/ruby/chapter_sorting/merge_sort.rb +++ b/en/codes/ruby/chapter_sorting/merge_sort.rb @@ -4,14 +4,14 @@ Created Time: 2024-04-10 Author: junminhong (junminhong1110@gmail.com) =end -### Merge left and right subarrays ### +### Merge the left and right subarrays ### def merge(nums, left, mid, right) - # Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - # Create temporary array tmp to store merged result + # The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + # Create a temporary array tmp to store the merged result tmp = Array.new(right - left + 1, 0) - # Initialize the start indices of the left and right subarrays + # Initialize the starting indices of the left and right subarrays i, j, k = left, mid + 1, 0 - # While both subarrays still have elements, compare and copy the smaller element into the temporary array + # While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while i <= mid && j <= right if nums[i] <= nums[j] tmp[k] = nums[i] @@ -33,7 +33,7 @@ def merge(nums, left, mid, right) j += 1 k += 1 end - # Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + # Copy the elements of temporary array tmp back into the corresponding range of the original array nums (0...tmp.length).each do |k| nums[left + k] = tmp[k] end @@ -41,18 +41,18 @@ end ### Merge sort ### def merge_sort(nums, left, right) - # Termination condition - # Terminate recursion when subarray length is 1 + # Base case + # Stop recursing when the subarray contains at most one element return if left >= right - # Divide and conquer stage - mid = left + (right - left) / 2 # Calculate midpoint - merge_sort(nums, left, mid) # Recursively process the left subarray - merge_sort(nums, mid + 1, right) # Recursively process the right subarray - # Merge stage + # Divide phase + mid = left + (right - left) / 2 # Calculate the midpoint + merge_sort(nums, left, mid) # Recurse on the left subarray + merge_sort(nums, mid + 1, right) # Recurse on the right subarray + # Merge phase merge(nums, left, mid, right) end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [7, 3, 2, 6, 0, 1, 5, 4] merge_sort(nums, 0, nums.length - 1) diff --git a/en/codes/ruby/chapter_sorting/quick_sort.rb b/en/codes/ruby/chapter_sorting/quick_sort.rb index b01341565..01cfe71a2 100644 --- a/en/codes/ruby/chapter_sorting/quick_sort.rb +++ b/en/codes/ruby/chapter_sorting/quick_sort.rb @@ -7,83 +7,32 @@ Author: Cy (3739004@gmail.com), Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Quick sort class ### class QuickSort class << self - ### Sentinel partition ### + ### Two-pointer partitioning ### def partition(nums, left, right) # Use nums[left] as the pivot i, j = left, right while i < j while i < j && nums[j] >= nums[left] - j -= 1 # Search from right to left for the first element smaller than the pivot + j -= 1 # Scan from right to left for the first element smaller than the pivot end while i < j && nums[i] <= nums[left] - i += 1 # Search from left to right for the first element greater than the pivot + i += 1 # Scan from left to right for the first element larger than the pivot end - # Swap elements + # Swap the elements nums[i], nums[j] = nums[j], nums[i] end - # Swap the pivot to the boundary between the two subarrays + # Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - i # Return the index of the pivot - end - - ### Quick sort class ### - def quick_sort(nums, left, right) - # Recurse when subarray length is not 1 - if left < right - # Sentinel partition - pivot = partition(nums, left, right) - # Recursively process the left subarray and right subarray - quick_sort(nums, left, pivot - 1) - quick_sort(nums, pivot + 1, right) - end - nums - end - end -end - -### Quick sort class (median optimization) ### -class QuickSortMedian - class << self - ### Select median of three candidate elements ### - def median_three(nums, left, mid, right) - # Select the median of three candidate elements - _l, _m, _r = nums[left], nums[mid], nums[right] - # m is between l and r - return mid if (_l <= _m && _m <= _r) || (_r <= _m && _m <= _l) - # l is between m and r - return left if (_m <= _l && _l <= _r) || (_r <= _l && _l <= _m) - return right - end - - ### Sentinel partition (median of three) ### - def partition(nums, left, right) - ### Use nums[left] as pivot - med = median_three(nums, left, (left + right) / 2, right) - # Swap median to leftmost position of array - nums[left], nums[med] = nums[med], nums[left] - i, j = left, right - while i < j - while i < j && nums[j] >= nums[left] - j -= 1 # Search from right to left for the first element smaller than the pivot - end - while i < j && nums[i] <= nums[left] - i += 1 # Search from left to right for the first element greater than the pivot - end - # Swap elements - nums[i], nums[j] = nums[j], nums[i] - end - # Swap the pivot to the boundary between the two subarrays - nums[i], nums[left] = nums[left], nums[i] - i # Return the index of the pivot + i # Return the pivot index end ### Quick sort ### def quick_sort(nums, left, right) - # Recurse when subarray length is not 1 + # Continue while the subarray contains more than one element if left < right - # Sentinel partition + # Two-pointer partitioning pivot = partition(nums, left, right) - # Recursively process the left subarray and right subarray + # Recurse on the left and right subarrays quick_sort(nums, left, pivot - 1) quick_sort(nums, pivot + 1, right) end @@ -92,62 +41,113 @@ class QuickSortMedian end end -### Quick sort class (recursion depth optimization) ### +### Quick sort class (median optimization)### +class QuickSortMedian + class << self + ### Select the median of three candidate elements ### + def median_three(nums, left, mid, right) + # Select the median of three candidate elements + _l, _m, _r = nums[left], nums[mid], nums[right] + # _m lies between _l and _r + return mid if (_l <= _m && _m <= _r) || (_r <= _m && _m <= _l) + # _l lies between _m and _r + return left if (_m <= _l && _l <= _r) || (_r <= _l && _l <= _m) + return right + end + + ### Two-pointer partitioning (median of three)### + def partition(nums, left, right) + ### Use nums[left] as the pivot + med = median_three(nums, left, (left + right) / 2, right) + # Swap the median into the leftmost position of the current subarray + nums[left], nums[med] = nums[med], nums[left] + i, j = left, right + while i < j + while i < j && nums[j] >= nums[left] + j -= 1 # Scan from right to left for the first element smaller than the pivot + end + while i < j && nums[i] <= nums[left] + i += 1 # Scan from left to right for the first element larger than the pivot + end + # Swap the elements + nums[i], nums[j] = nums[j], nums[i] + end + # Swap the pivot into the boundary position between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + i # Return the pivot index + end + + ### Quick sort ### + def quick_sort(nums, left, right) + # Continue while the subarray contains more than one element + if left < right + # Two-pointer partitioning + pivot = partition(nums, left, right) + # Recurse on the left and right subarrays + quick_sort(nums, left, pivot - 1) + quick_sort(nums, pivot + 1, right) + end + nums + end + end +end + +### Quick sort class (recursion depth optimization)### class QuickSortTailCall class << self - ### Sentinel partition ### + ### Two-pointer partitioning ### def partition(nums, left, right) - # Use nums[left] as pivot + # Use nums[left] as the pivot i = left j = right while i < j while i < j && nums[j] >= nums[left] - j -= 1 # Search from right to left for the first element smaller than the pivot + j -= 1 # Scan from right to left for the first element smaller than the pivot end while i < j && nums[i] <= nums[left] - i += 1 # Search from left to right for the first element greater than the pivot + i += 1 # Scan from left to right for the first element larger than the pivot end - # Swap elements + # Swap the elements nums[i], nums[j] = nums[j], nums[i] end - # Swap the pivot to the boundary between the two subarrays + # Swap the pivot into the boundary position between the two subarrays nums[i], nums[left] = nums[left], nums[i] - i # Return the index of the pivot + i # Return the pivot index end - ### Quick sort (recursion depth optimization) ### + ### Quick sort (recursion depth optimization)### def quick_sort(nums, left, right) - # Recurse when subarray length is not 1 + # Continue while the subarray contains more than one element while left < right - # Sentinel partition + # Two-pointer partitioning pivot = partition(nums, left, right) - # Perform quick sort on the shorter of the two subarrays + # Apply quick sort to the shorter of the two subarrays if pivot - left < right - pivot quick_sort(nums, left, pivot - 1) - left = pivot + 1 # Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1 # The remaining unsorted range is [pivot + 1, right] else quick_sort(nums, pivot + 1, right) - right = pivot - 1 # Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1 # The remaining unsorted range is [left, pivot - 1] end end end end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 # Quick sort nums = [2, 4, 1, 0, 3, 5] QuickSort.quick_sort(nums, 0, nums.length - 1) puts "After quick sort, nums = #{nums}" - # Quick sort (recursion depth optimization) + # Quick sort (median pivot optimization) nums1 = [2, 4, 1, 0, 3, 5] QuickSortMedian.quick_sort(nums1, 0, nums1.length - 1) - puts "After quick sort (median pivot optimization), nums1 = #{nums1}" + puts "After quick sort with median pivot optimization, nums1 = #{nums1}" # Quick sort (recursion depth optimization) nums2 = [2, 4, 1, 0, 3, 5] QuickSortTailCall.quick_sort(nums2, 0, nums2.length - 1) - puts "After quick sort (recursion depth optimization), nums2 = #{nums2}" + puts "After quick sort with recursion depth optimization, nums2 = #{nums2}" end diff --git a/en/codes/ruby/chapter_sorting/radix_sort.rb b/en/codes/ruby/chapter_sorting/radix_sort.rb index 56e5289a2..2c20aa6c0 100644 --- a/en/codes/ruby/chapter_sorting/radix_sort.rb +++ b/en/codes/ruby/chapter_sorting/radix_sort.rb @@ -4,53 +4,53 @@ Created Time: 2024-05-03 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Get k-th digit of element num, where exp = 10^(k-1) ### +### Get the k-th digit of num, where exp = 10^(k-1) ### def digit(num, exp) - # Passing exp instead of k avoids expensive exponentiation calculations + # Passing exp instead of k avoids repeatedly performing expensive exponentiation here (num / exp) % 10 end -### Counting sort (sort by k-th digit of nums) ### +### Counting sort (sort nums by the k-th digit)### def counting_sort_digit(nums, exp) - # Decimal digit range is 0~9, therefore need a bucket array of length 10 + # Decimal digits range from 0 to 9, so the bucket array needs length 10 counter = Array.new(10, 0) n = nums.length - # Count the occurrence of digits 0~9 + # Count the occurrences of each digit from 0 to 9 for i in 0...n - d = digit(nums[i], exp) # Get the k-th digit of nums[i], noted as d - counter[d] += 1 # Count the occurrence of digit d + d = digit(nums[i], exp) # Get the k-th digit of nums[i] and store it in d + counter[d] += 1 # Count the occurrences of digit d end - # Calculate prefix sum, converting "occurrence count" into "array index" + # Compute prefix sums to convert occurrence counts into array positions (1...10).each { |i| counter[i] += counter[i - 1] } - # Traverse in reverse, based on bucket statistics, place each element into res + # Traverse in reverse and place each element into res according to the bucket counts res = Array.new(n, 0) for i in (n - 1).downto(0) d = digit(nums[i], exp) - j = counter[d] - 1 # Get the index j for d in the array + j = counter[d] - 1 # Get the destination index j for digit d in the array res[j] = nums[i] # Place the current element at index j - counter[d] -= 1 # Decrease the count of d by 1 + counter[d] -= 1 # Decrease the count for d by 1 end - # Use result to overwrite the original array nums + # Overwrite the original array nums with the result (0...n).each { |i| nums[i] = res[i] } end ### Radix sort ### def radix_sort(nums) - # Get the maximum element of the array, used to determine the maximum number of digits + # Get the largest array element to determine the maximum number of digits m = nums.max - # Traverse from the lowest to the highest digit + # Process digits from least significant to most significant exp = 1 while exp <= m - # Perform counting sort on the k-th digit of array elements + # Apply counting sort to the k-th digit of the array elements # k = 1 -> exp = 1 # k = 2 -> exp = 10 - # i.e., exp = 10^(k-1) + # That is, exp = 10^(k-1) counting_sort_digit(nums, exp) exp *= 10 end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 # Radix sort nums = [ diff --git a/en/codes/ruby/chapter_sorting/selection_sort.rb b/en/codes/ruby/chapter_sorting/selection_sort.rb index 8c1ab0475..7404283ef 100644 --- a/en/codes/ruby/chapter_sorting/selection_sort.rb +++ b/en/codes/ruby/chapter_sorting/selection_sort.rb @@ -7,21 +7,21 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Selection sort ### def selection_sort(nums) n = nums.length - # Outer loop: unsorted interval is [i, n-1] + # Outer loop: the unsorted range is [i, n-1] for i in 0...(n - 1) - # Inner loop: find the smallest element within the unsorted interval + # Inner loop: find the smallest element in the unsorted range k = i for j in (i + 1)...n if nums[j] < nums[k] k = j # Record the index of the smallest element end end - # Swap the smallest element with the first element of the unsorted interval + # Swap the smallest element with the first element of the unsorted range nums[i], nums[k] = nums[k], nums[i] end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 nums = [4, 1, 3, 1, 5, 2] selection_sort(nums) diff --git a/en/codes/ruby/chapter_stack_and_queue/array_deque.rb b/en/codes/ruby/chapter_stack_and_queue/array_deque.rb index 7e505b36e..ee1e97c6f 100644 --- a/en/codes/ruby/chapter_stack_and_queue/array_deque.rb +++ b/en/codes/ruby/chapter_stack_and_queue/array_deque.rb @@ -4,9 +4,9 @@ Created Time: 2024-04-05 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Deque based on circular array ### +### Deque implemented with a circular array ### class ArrayDeque - ### Get deque length ### + ### Get the deque size ### attr_reader :size ### Constructor ### @@ -16,80 +16,80 @@ class ArrayDeque @size = 0 end - ### Get deque capacity ### + ### Get the deque capacity ### def capacity @nums.length end - ### Check if deque is empty ### + ### Check whether the deque is empty ### def is_empty? size.zero? end - ### Enqueue at front ### + ### Enqueue at the front ### def push_first(num) if size == capacity - puts 'Double-ended queue is full' + puts 'Deque is full' return end - # Use modulo operation to wrap front around to the tail after passing the head of the array - # Add num to the front of the queue + # Move the front pointer one position to the left + # Use modulo to wrap front to the end when it passes the beginning of the array @front = index(@front - 1) - # Add num to front of queue + # Add num to the front @nums[@front] = num @size += 1 end - ### Enqueue at rear ### + ### Enqueue at the rear ### def push_last(num) if size == capacity - puts 'Double-ended queue is full' + puts 'Deque is full' return end - # Use modulo operation to wrap rear around to the head after passing the tail of the array + # Calculate the rear pointer, which points one position past the last element rear = index(@front + size) - # Front pointer moves one position backward + # Add num to the rear @nums[rear] = num @size += 1 end - ### Dequeue from front ### + ### Dequeue from the front ### def pop_first num = peek_first - # Move front pointer backward by one position + # Advance the front pointer by one position @front = index(@front + 1) @size -= 1 num end - ### Dequeue from rear ### + ### Dequeue from the rear ### def pop_last num = peek_last @size -= 1 num end - ### Access front element ### + ### Access the front element ### def peek_first raise IndexError, 'Deque is empty' if is_empty? @nums[@front] end - ### Access rear element ### + ### Access the rear element ### def peek_last raise IndexError, 'Deque is empty' if is_empty? - # Initialize double-ended queue + # Calculate the index of the last element last = index(@front + size - 1) @nums[last] end - ### Return array for printing ### + ### Return an array for printing ### def to_array - # Elements enqueue + # Convert only the list elements within the occupied range res = [] for i in 0...size res << @nums[index(@front + i)] @@ -99,47 +99,47 @@ class ArrayDeque private - ### Calculate circular array index ### + ### Calculate an index in the circular array ### def index(i) - # Use modulo operation to wrap the array head and tail together - # When i passes the tail of the array, return to the head - # When i passes the head of the array, return to the tail + # Use modulo to connect the end of the array to its beginning + # When i passes the end of the array, wrap around to the beginning + # When i passes the beginning of the array, wrap around to the end (i + capacity) % capacity end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Get the length of the double-ended queue + # Initialize the deque deque = ArrayDeque.new(10) deque.push_last(3) deque.push_last(2) deque.push_last(5) puts "Deque deque = #{deque.to_array}" - # Update element + # Access an element peek_first = deque.peek_first puts "Front element peek_first = #{peek_first}" peek_last = deque.peek_last puts "Rear element peek_last = #{peek_last}" - # Elements enqueue + # Enqueue elements deque.push_last(4) - puts "After element 4 enqueues at rear, deque = #{deque.to_array}" + puts "After enqueueing 4 at the rear, deque = #{deque.to_array}" deque.push_first(1) - puts "After element 1 enqueues at rear, deque = #{deque.to_array}" + puts "After enqueueing 1 at the front, deque = #{deque.to_array}" - # Element dequeue + # Dequeue an element pop_last = deque.pop_last - puts "Dequeue rear element = #{pop_last}, after dequeue deque = #{deque.to_array}" + puts "Element dequeued from the rear = #{pop_last}; deque = #{deque.to_array}" pop_first = deque.pop_first - puts "Dequeue front element = #{pop_first}, after dequeue deque = #{deque.to_array}" + puts "Element dequeued from the front = #{pop_first}; deque = #{deque.to_array}" - # Get the length of the double-ended queue + # Get the deque size size = deque.size - puts "Deque length size = #{size}" + puts "Deque size = #{size}" - # Check if the double-ended queue is empty + # Check whether the deque is empty is_empty = deque.is_empty? - puts "Is deque empty = #{is_empty}" + puts "Is the deque empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_stack_and_queue/array_queue.rb b/en/codes/ruby/chapter_stack_and_queue/array_queue.rb index 1303a5f8b..19f46b428 100644 --- a/en/codes/ruby/chapter_stack_and_queue/array_queue.rb +++ b/en/codes/ruby/chapter_stack_and_queue/array_queue.rb @@ -4,24 +4,24 @@ Created Time: 2024-04-05 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Queue based on circular array ### +### Queue implemented with a circular array ### class ArrayQueue - ### Get queue length ### + ### Get the queue size ### attr_reader :size ### Constructor ### def initialize(size) - @nums = Array.new(size, 0) # Array for storing queue elements - @front = 0 # Front pointer, points to the front of the queue element - @size = 0 # Queue length + @nums = Array.new(size, 0) # Array that stores the queue elements + @front = 0 # Front pointer, pointing to the first element + @size = 0 # Queue size end - ### Get queue capacity ### + ### Get the queue capacity ### def capacity @nums.length end - ### Check if queue is empty ### + ### Check whether the queue is empty ### def is_empty? size.zero? end @@ -30,10 +30,10 @@ class ArrayQueue def push(num) raise IndexError, 'Queue is full' if size == capacity - # Use modulo operation to wrap rear around to the head after passing the tail of the array - # Add num to the rear of the queue + # Calculate the rear pointer, which points one position past the last element + # Use modulo to wrap rear to the beginning when it passes the end of the array rear = (@front + size) % capacity - # Front pointer moves one position backward + # Add num to the rear @nums[rear] = num @size += 1 end @@ -41,20 +41,20 @@ class ArrayQueue ### Dequeue ### def pop num = peek - # Move front pointer backward by one position, if it passes the tail, return to array head + # Advance the front pointer by one position, wrapping to the beginning if it passes the end @front = (@front + 1) % capacity @size -= 1 num end - ### Access front element ### + ### Access the front element ### def peek raise IndexError, 'Queue is empty' if is_empty? @nums[@front] end - ### Return list for printing ### + ### Return a list for printing ### def to_array res = Array.new(size, 0) j = @front @@ -68,12 +68,12 @@ class ArrayQueue end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Access front of the queue element + # Initialize the queue queue = ArrayQueue.new(10) - # Elements enqueue + # Enqueue elements queue.push(1) queue.push(3) queue.push(2) @@ -81,27 +81,27 @@ if __FILE__ == $0 queue.push(4) puts "Queue queue = #{queue.to_array}" - # Return list for printing + # Access the front element peek = queue.peek puts "Front element peek = #{peek}" - # Element dequeue + # Dequeue an element pop = queue.pop - puts "Dequeue element pop = #{pop}" - puts "After dequeue, queue = #{queue.to_array}" + puts "Dequeued element pop = #{pop}" + puts "After dequeueing, queue = #{queue.to_array}" - # Get the length of the queue + # Get the queue size size = queue.size - puts "Queue length size = #{size}" + puts "Queue size = #{size}" - # Check if the queue is empty + # Check whether the queue is empty is_empty = queue.is_empty? - puts "Is queue empty = #{is_empty}" + puts "Is the queue empty? #{is_empty}" - # Test circular array + # Test the circular array for i in 0...10 queue.push(i) queue.pop - puts "After round #{i} of enqueue + dequeue, queue = #{queue.to_array}" + puts "After enqueueing and dequeueing in round #{i}, queue = #{queue.to_array}" end end diff --git a/en/codes/ruby/chapter_stack_and_queue/array_stack.rb b/en/codes/ruby/chapter_stack_and_queue/array_stack.rb index cb4adf3f0..9d253aee4 100644 --- a/en/codes/ruby/chapter_stack_and_queue/array_stack.rb +++ b/en/codes/ruby/chapter_stack_and_queue/array_stack.rb @@ -4,19 +4,19 @@ Created Time: 2024-04-06 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Stack based on array ### +### Stack implemented with an array ### class ArrayStack ### Constructor ### def initialize @stack = [] end - ### Get stack length ### + ### Get the stack size ### def size @stack.length end - ### Check if stack is empty ### + ### Check whether the stack is empty ### def is_empty? @stack.empty? end @@ -33,25 +33,25 @@ class ArrayStack @stack.pop end - ### Access top element ### + ### Access the top element ### def peek raise IndexError, 'Stack is empty' if is_empty? @stack.last end - ### Return list for printing ### + ### Return a list for printing ### def to_array @stack end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Access top of the stack element + # Initialize the stack stack = ArrayStack.new - # Elements push onto stack + # Push elements onto the stack stack.push(1) stack.push(3) stack.push(2) @@ -59,20 +59,20 @@ if __FILE__ == $0 stack.push(4) puts "Stack stack = #{stack.to_array}" - # Return list for printing + # Access the top element peek = stack.peek puts "Top element peek = #{peek}" - # Element pop from stack + # Pop an element from the stack pop = stack.pop - puts "Pop element pop = #{pop}" - puts "After pop, stack = #{stack.to_array}" + puts "Popped element pop = #{pop}" + puts "After popping, stack = #{stack.to_array}" - # Get the length of the stack + # Get the stack size size = stack.size - puts "Stack length size = #{size}" + puts "Stack size = #{size}" - # Check if empty + # Check whether it is empty is_empty = stack.is_empty? - puts "Is stack empty = #{is_empty}" + puts "Is the stack empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_stack_and_queue/deque.rb b/en/codes/ruby/chapter_stack_and_queue/deque.rb index 016b94f08..cf99ca232 100644 --- a/en/codes/ruby/chapter_stack_and_queue/deque.rb +++ b/en/codes/ruby/chapter_stack_and_queue/deque.rb @@ -4,39 +4,39 @@ Created Time: 2024-04-06 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Get the length of the double-ended queue - # Ruby has no built-in deque, can only use Array as deque + # Initialize the deque + # Ruby has no built-in deque class; use an Array as a deque deque = [] - # Element enqueues + # Enqueue elements deque << 2 deque << 5 deque << 4 - # Note: due to array, Array#unshift method has O(n) time complexity + # Front insertion in a simple array takes O(n); Ruby implementations can optimize Array#unshift deque.unshift(3) deque.unshift(1) puts "Deque deque = #{deque}" - # Update element + # Access an element peek_first = deque.first puts "Front element peek_first = #{peek_first}" peek_last = deque.last puts "Rear element peek_last = #{peek_last}" - # Element dequeue - # Note: due to array, Array#shift method has O(n) time complexity + # Dequeue an element + # Front removal in a simple array takes O(n); Ruby implementations can optimize Array#shift pop_front = deque.shift - puts "Dequeue front element pop_front = #{pop_front}, after dequeue deque = #{deque}" + puts "Element dequeued from the front: pop_front = #{pop_front}; deque = #{deque}" pop_back = deque.pop - puts "Dequeue rear element pop_back = #{pop_back}, after dequeue deque = #{deque}" + puts "Element dequeued from the rear: pop_back = #{pop_back}; deque = #{deque}" - # Get the length of the double-ended queue + # Get the deque size size = deque.length - puts "Deque length size = #{size}" + puts "Deque size = #{size}" - # Check if the double-ended queue is empty + # Check whether the deque is empty is_empty = size.zero? - puts "Is deque empty = #{is_empty}" + puts "Is the deque empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb b/en/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb index f6b499d34..9e9e15c5c 100644 --- a/en/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb +++ b/en/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb @@ -7,8 +7,8 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Doubly linked list node class ListNode attr_accessor :val - attr_accessor :next # Successor node reference - attr_accessor :prev # Predecessor node reference + attr_accessor :next # Reference to the next node + attr_accessor :prev # Reference to the previous node ### Constructor ### def initialize(val) @@ -16,19 +16,19 @@ class ListNode end end -### Deque based on doubly linked list ### +### Deque implemented with a doubly linked list ### class LinkedListDeque - ### Get deque length ### + ### Get the deque size ### attr_reader :size ### Constructor ### def initialize @front = nil # Head node front @rear = nil # Tail node rear - @size = 0 # Length of the double-ended queue + @size = 0 # Deque size end - ### Check if deque is empty ### + ### Check whether the deque is empty ### def is_empty? size.zero? end @@ -36,31 +36,31 @@ class LinkedListDeque ### Enqueue operation ### def push(num, is_front) node = ListNode.new(num) - # If list is empty, set both front and rear to node + # If the list is empty, point both front and rear to node if is_empty? @front = @rear = node - # Front of the queue enqueue operation + # Enqueue at the front elsif is_front # Add node to the head of the linked list @front.prev = node node.next = @front - @front = node # Update head node - # Rear of the queue enqueue operation + @front = node # Update the head node + # Enqueue at the rear else # Add node to the tail of the linked list @rear.next = node node.prev = @rear - @rear = node # Update tail node + @rear = node # Update the tail node end - @size += 1 # Update queue length + @size += 1 # Update the queue size end - ### Enqueue at front ### + ### Enqueue at the front ### def push_first(num) push(num, true) end - ### Enqueue at rear ### + ### Enqueue at the rear ### def push_last(num) push(num, false) end @@ -69,58 +69,58 @@ class LinkedListDeque def pop(is_front) raise IndexError, 'Deque is empty' if is_empty? - # When the deque has one element, front and rear point to the same node + # When the queue size is 1, the head and tail point to the same node if @size == 1 val = @front.val @front = nil @rear = nil - # Temporarily store head node value + # Dequeue from the front elsif is_front - val = @front.val # Delete head node - # Delete head node + val = @front.val # Save the head node value + # Remove the head node fnext = @front.next fnext.prev = nil @front.next = nil - @front = fnext # Update head node - # Temporarily store tail node value + @front = fnext # Update the head node + # Dequeue from the rear else - val = @rear.val # Delete tail node - # Update tail node + val = @rear.val # Save the tail node value + # Remove the tail node rprev = @rear.prev rprev.next = nil @rear.prev = nil - @rear = rprev # Update tail node + @rear = rprev # Update the tail node end - @size -= 1 # Update queue length + @size -= 1 # Update the queue size val end - ### Dequeue from front ### + ### Dequeue from the front ### def pop_first pop(true) end - ### Dequeue from front ### + ### Dequeue from the rear ### def pop_last pop(false) end - ### Access front element ### + ### Access the front element ### def peek_first raise IndexError, 'Deque is empty' if is_empty? @front.val end - ### Access rear element ### + ### Access the rear element ### def peek_last raise IndexError, 'Deque is empty' if is_empty? @rear.val end - ### Return array for printing ### + ### Return an array for printing ### def to_array node = @front res = Array.new(size, 0) @@ -132,38 +132,38 @@ class LinkedListDeque end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Get the length of the double-ended queue + # Initialize the deque deque = LinkedListDeque.new deque.push_last(3) deque.push_last(2) deque.push_last(5) puts "Deque deque = #{deque.to_array}" - # Update element + # Access an element peek_first = deque.peek_first puts "Front element peek_first = #{peek_first}" peek_last = deque.peek_last puts "Rear element peek_last = #{peek_last}" - # Elements enqueue + # Enqueue elements deque.push_last(4) - puts "After element 4 enqueues at rear, deque = #{deque.to_array}" + puts "After enqueueing 4 at the rear, deque = #{deque.to_array}" deque.push_first(1) - puts "After element 1 enqueues at front, deque = #{deque.to_array}" + puts "After enqueueing 1 at the front, deque = #{deque.to_array}" - # Element dequeue + # Dequeue an element pop_last = deque.pop_last - puts "Dequeue rear element = #{pop_last}, after dequeue deque = #{deque.to_array}" + puts "Element dequeued from the rear = #{pop_last}; deque = #{deque.to_array}" pop_first = deque.pop_first - puts "Dequeue front element = #{pop_first}, after dequeue deque = #{deque.to_array}" + puts "Element dequeued from the front = #{pop_first}; deque = #{deque.to_array}" - # Get the length of the double-ended queue + # Get the deque size size = deque.size - puts "Deque length size = #{size}" + puts "Deque size = #{size}" - # Check if the double-ended queue is empty + # Check whether the deque is empty is_empty = deque.is_empty? - puts "Is deque empty = #{is_empty}" + puts "Is the deque empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_stack_and_queue/linkedlist_queue.rb b/en/codes/ruby/chapter_stack_and_queue/linkedlist_queue.rb index c140455de..372b1c2e7 100644 --- a/en/codes/ruby/chapter_stack_and_queue/linkedlist_queue.rb +++ b/en/codes/ruby/chapter_stack_and_queue/linkedlist_queue.rb @@ -6,9 +6,9 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/list_node' -### Queue based on linked list ### +### Queue implemented with a linked list ### class LinkedListQueue - ### Get queue length ### + ### Get the queue size ### attr_reader :size ### Constructor ### @@ -18,7 +18,7 @@ class LinkedListQueue @size = 0 end - ### Check if queue is empty ### + ### Check whether the queue is empty ### def is_empty? @front.nil? end @@ -28,11 +28,11 @@ class LinkedListQueue # Add num after the tail node node = ListNode.new(num) - # If queue is empty, set both front and rear to this node + # If the queue is empty, point both the head and tail to this node if @front.nil? @front = node @rear = node - # If queue is not empty, add this node after rear + # If the queue is not empty, append this node after the tail else @rear.next = node @rear = node @@ -44,20 +44,20 @@ class LinkedListQueue ### Dequeue ### def pop num = peek - # Delete head node + # Remove the head node @front = @front.next @size -= 1 num end - ### Access front element ### + ### Access the front element ### def peek raise IndexError, 'Queue is empty' if is_empty? @front.val end - ### Convert linked list to Array and return ### + ### Convert the linked list to an Array and return it ### def to_array queue = [] temp = @front @@ -69,12 +69,12 @@ class LinkedListQueue end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Access front of the queue element + # Initialize the queue queue = LinkedListQueue.new - # Element enqueues + # Enqueue elements queue.push(1) queue.push(3) queue.push(2) @@ -82,20 +82,20 @@ if __FILE__ == $0 queue.push(4) puts "Queue queue = #{queue.to_array}" - # Return list for printing + # Access the front element peek = queue.peek - puts "Front element = #{peek}" + puts "Front element peek = #{peek}" - # Element dequeue + # Dequeue an element pop_front = queue.pop - puts "Dequeue element pop = #{pop_front}" - puts "After dequeue, queue = #{queue.to_array}" + puts "Dequeued element pop_front = #{pop_front}" + puts "After dequeueing, queue = #{queue.to_array}" - # Get the length of the queue + # Get the queue size size = queue.size - puts "Queue length size = #{size}" + puts "Queue size = #{size}" - # Check if the queue is empty + # Check whether the queue is empty is_empty = queue.is_empty? - puts "Is queue empty = #{is_empty}" + puts "Is the queue empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_stack_and_queue/linkedlist_stack.rb b/en/codes/ruby/chapter_stack_and_queue/linkedlist_stack.rb index 2831e9a9c..622fb83dd 100644 --- a/en/codes/ruby/chapter_stack_and_queue/linkedlist_stack.rb +++ b/en/codes/ruby/chapter_stack_and_queue/linkedlist_stack.rb @@ -6,7 +6,7 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/list_node' -### Stack based on linked list ### +### Stack implemented with a linked list ### class LinkedListStack attr_reader :size @@ -15,7 +15,7 @@ class LinkedListStack @size = 0 end - ### Check if stack is empty ### + ### Check whether the stack is empty ### def is_empty? @peek.nil? end @@ -36,14 +36,14 @@ class LinkedListStack num end - ### Access top element ### + ### Access the top element ### def peek raise IndexError, 'Stack is empty' if is_empty? @peek.val end - ### Convert linked list to Array and return ### + ### Convert the linked list to an Array and return it ### def to_array arr = [] node = @peek @@ -55,12 +55,12 @@ class LinkedListStack end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Access top of the stack element + # Initialize the stack stack = LinkedListStack.new - # Elements push onto stack + # Push elements onto the stack stack.push(1) stack.push(3) stack.push(2) @@ -68,20 +68,20 @@ if __FILE__ == $0 stack.push(4) puts "Stack stack = #{stack.to_array}" - # Return list for printing + # Access the top element peek = stack.peek puts "Top element peek = #{peek}" - # Element pop from stack + # Pop an element from the stack pop = stack.pop - puts "Pop element pop = #{pop}" - puts "After pop, stack = #{stack.to_array}" + puts "Popped element pop = #{pop}" + puts "After popping, stack = #{stack.to_array}" - # Get the length of the stack + # Get the stack size size = stack.size - puts "Stack length size = #{size}" + puts "Stack size = #{size}" - # Check if empty + # Check whether it is empty is_empty = stack.is_empty? - puts "Is stack empty = #{is_empty}" + puts "Is the stack empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_stack_and_queue/queue.rb b/en/codes/ruby/chapter_stack_and_queue/queue.rb index 3fc5f1fc3..fe0366804 100644 --- a/en/codes/ruby/chapter_stack_and_queue/queue.rb +++ b/en/codes/ruby/chapter_stack_and_queue/queue.rb @@ -4,13 +4,13 @@ Created Time: 2024-04-06 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Access front of the queue element - # Ruby's built-in queue (Thread::Queue) has no peek and traversal methods, can use Array as queue + # Initialize the queue + # The built-in Ruby queue (Thread::Queue) has no peek or traversal methods; use an Array as a queue queue = [] - # Elements enqueue + # Enqueue elements queue.push(1) queue.push(3) queue.push(2) @@ -18,21 +18,21 @@ if __FILE__ == $0 queue.push(4) puts "Queue queue = #{queue}" - # Access queue elements + # Access a queue element peek = queue.first puts "Front element peek = #{peek}" - # Element dequeue - # Note: due to array, Array#shift method has O(n) time complexity + # Dequeue an element + # Front removal in a simple array takes O(n); Ruby implementations can optimize Array#shift pop = queue.shift - puts "Dequeue element pop = #{pop}" - puts "After dequeue, queue = #{queue}" + puts "Dequeued element pop = #{pop}" + puts "After dequeueing, queue = #{queue}" - # Get the length of the queue + # Get the queue size size = queue.length - puts "Queue length size = #{size}" + puts "Queue size = #{size}" - # Check if the queue is empty + # Check whether the queue is empty is_empty = queue.empty? - puts "Is queue empty = #{is_empty}" + puts "Is the queue empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_stack_and_queue/stack.rb b/en/codes/ruby/chapter_stack_and_queue/stack.rb index 8f4e0b7d4..979fdc41a 100644 --- a/en/codes/ruby/chapter_stack_and_queue/stack.rb +++ b/en/codes/ruby/chapter_stack_and_queue/stack.rb @@ -4,13 +4,13 @@ Created Time: 2024-04-06 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Access top of the stack element - # Ruby has no built-in stack class, can use Array as stack + # Initialize the stack + # Ruby has no built-in stack class; use an Array as a stack stack = [] - # Elements push onto stack + # Push elements onto the stack stack << 1 stack << 3 stack << 2 @@ -18,20 +18,20 @@ if __FILE__ == $0 stack << 4 puts "Stack stack = #{stack}" - # Return list for printing + # Access the top element peek = stack.last puts "Top element peek = #{peek}" - # Element pop from stack + # Pop an element from the stack pop = stack.pop - puts "Pop element pop = #{pop}" - puts "After pop, stack = #{stack}" + puts "Popped element pop = #{pop}" + puts "After popping, stack = #{stack}" - # Get the length of the stack + # Get the stack size size = stack.length - puts "Stack length size = #{size}" + puts "Stack size = #{size}" - # Check if empty + # Check whether it is empty is_empty = stack.empty? - puts "Is stack empty = #{is_empty}" + puts "Is the stack empty? #{is_empty}" end diff --git a/en/codes/ruby/chapter_tree/array_binary_tree.rb b/en/codes/ruby/chapter_tree/array_binary_tree.rb index 6a5515147..a9f6708b7 100644 --- a/en/codes/ruby/chapter_tree/array_binary_tree.rb +++ b/en/codes/ruby/chapter_tree/array_binary_tree.rb @@ -7,37 +7,37 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Array representation of binary tree class ### +### Binary tree class using an array representation ### class ArrayBinaryTree ### Constructor ### def initialize(arr) @tree = arr.to_a end - ### List capacity ### + ### Length of the array representation ### def size @tree.length end - ### Get value of node at index i ### + ### Get the value of the node at index i ### def val(i) - # Return nil if index out of bounds, representing empty position + # If the index is out of bounds, return nil to indicate an empty position return if i < 0 || i >= size @tree[i] end - ### Get left child index of node at index i ### + ### Get the left child index of the node at index i ### def left(i) 2 * i + 1 end - ### Get right child index of node at index i ### + ### Get the right child index of the node at index i ### def right(i) 2 * i + 2 end - ### Get parent node index of node at index i ### + ### Get the parent index of the node at index i ### def parent(i) (i - 1) / 2 end @@ -46,7 +46,7 @@ class ArrayBinaryTree def level_order @res = [] - # Traverse array directly + # Traverse the array directly for i in 0...size @res << val(i) unless val(i).nil? end @@ -67,21 +67,21 @@ class ArrayBinaryTree @res << val(i) if order == :post end - ### Pre-order traversal ### + ### Preorder traversal ### def pre_order @res = [] dfs(0, :pre) @res end - ### In-order traversal ### + ### Inorder traversal ### def in_order @res = [] dfs(0, :in) @res end - ### Post-order traversal ### + ### Postorder traversal ### def post_order @res = [] dfs(0, :post) @@ -89,36 +89,36 @@ class ArrayBinaryTree end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize binary tree - # Here we use a function to generate a binary tree directly from an array + # Initialize the binary tree + # Use a helper function that builds a binary tree directly from an array arr = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] root = arr_to_tree(arr) - puts "\nInitialize binary tree\n\n" - puts 'Array representation of binary tree:' + puts "\nInitialize the binary tree\n\n" + puts 'Array representation of the binary tree:' pp arr - puts 'Linked list representation of binary tree:' + puts 'Linked representation of the binary tree:' print_tree(root) - # Binary tree class represented by array + # Binary tree class using an array representation abt = ArrayBinaryTree.new(arr) - # Access node + # Access a node i = 1 l, r, _p = abt.left(i), abt.right(i), abt.parent(i) - puts "\nCurrent node index is #{i}, value is #{abt.val(i).inspect}" - puts "Left child index is #{l}, value is #{abt.val(l).inspect}" - puts "Right child index is #{r}, value is #{abt.val(r).inspect}" - puts "Parent node index is #{_p}, value is #{abt.val(_p).inspect}" + puts "\nCurrent node: index = #{i}, value = #{abt.val(i).inspect}" + puts "Left child: index = #{l}, value = #{abt.val(l).inspect}" + puts "Right child: index = #{r}, value = #{abt.val(r).inspect}" + puts "Parent: index = #{_p}, value = #{abt.val(_p).inspect}" - # Traverse tree + # Traverse the tree res = abt.level_order - puts "\nLevel-order traversal is: #{res}" + puts "\nLevel-order traversal: #{res}" res = abt.pre_order - puts "Pre-order traversal is: #{res}" + puts "Preorder traversal: #{res}" res = abt.in_order - puts "In-order traversal is: #{res}" + puts "Inorder traversal: #{res}" res = abt.post_order - puts "Post-order traversal is: #{res}" + puts "Postorder traversal: #{res}" end diff --git a/en/codes/ruby/chapter_tree/avl_tree.rb b/en/codes/ruby/chapter_tree/avl_tree.rb index e6c6f0544..b3a3f6003 100644 --- a/en/codes/ruby/chapter_tree/avl_tree.rb +++ b/en/codes/ruby/chapter_tree/avl_tree.rb @@ -14,28 +14,28 @@ class AVLTree @root = nil end - ### Get binary tree root node ### + ### Get the root of the binary tree ### def get_root @root end - ### Get node height ### + ### Get the node height ### def height(node) - # Empty node height is -1, leaf node height is 0 + # A nil node has height -1; a leaf has height 0 return node.height unless node.nil? -1 end - ### Update node height ### + ### Update the node height ### def update_height(node) - # Node height equals the height of the tallest subtree + 1 + # The node height equals the height of its taller subtree + 1 node.height = [height(node.left), height(node.right)].max + 1 end - ### Get balance factor ### + ### Get the balance factor ### def balance_factor(node) - # Empty node balance factor is 0 + # A nil node has balance factor 0 return 0 if node.nil? # Node balance factor = left subtree height - right subtree height @@ -46,13 +46,13 @@ class AVLTree def right_rotate(node) child = node.left grand_child = child.right - # Using child as pivot, rotate node to the right + # Rotate node to the right around child child.right = node node.left = grand_child - # Update node height + # Update the node height update_height(node) update_height(child) - # Return root node of subtree after rotation + # Return the root of the rotated subtree child end @@ -60,77 +60,77 @@ class AVLTree def left_rotate(node) child = node.right grand_child = child.left - # Using child as pivot, rotate node to the left + # Rotate node to the left around child child.left = node node.right = grand_child - # Update node height + # Update the node height update_height(node) update_height(child) - # Return root node of subtree after rotation + # Return the root of the rotated subtree child end - ### Perform rotation to rebalance subtree ### + ### Perform rotations to restore balance in this subtree ### def rotate(node) - # Get balance factor of node + # Get the balance factor of node balance_factor = balance_factor(node) # Left-heavy tree if balance_factor > 1 if balance_factor(node.left) >= 0 - # Right rotation + # Rotate right return right_rotate(node) else - # First left rotation then right rotation + # Rotate left, then right node.left = left_rotate(node.left) return right_rotate(node) end # Right-heavy tree elsif balance_factor < -1 if balance_factor(node.right) <= 0 - # Left rotation + # Rotate left return left_rotate(node) else - # First right rotation then left rotation + # Rotate right, then left node.right = right_rotate(node.right) return left_rotate(node) end end - # Balanced tree, no rotation needed, return directly + # The tree is balanced; return without rotating node end - ### Insert node ### + ### Insert a node ### def insert(val) @root = insert_helper(@root, val) end - ### Recursively insert node (helper method) ### + ### Insert a node recursively (helper method)### def insert_helper(node, val) return TreeNode.new(val) if node.nil? - # 1. Find insertion position and insert node + # 1. Find the insertion position and insert the node if val < node.val node.left = insert_helper(node.left, val) elsif val > node.val node.right = insert_helper(node.right, val) else - # Duplicate node not inserted, return directly + # Return without inserting a duplicate node return node end - # Update node height + # Update the node height update_height(node) - # 2. Perform rotation operation to restore balance to this subtree + # 2. Perform rotations to restore balance in this subtree rotate(node) end - ### Delete node ### + ### Remove a node ### def remove(val) @root = remove_helper(@root, val) end - ### Recursively delete node (helper method) ### + ### Remove a node recursively (helper method)### def remove_helper(node, val) return if node.nil? - # 1. Find node and delete + # 1. Find and remove the node if val < node.val node.left = remove_helper(node.left, val) elsif val > node.val @@ -138,12 +138,12 @@ class AVLTree else if node.left.nil? || node.right.nil? child = node.left || node.right - # Number of child nodes = 0, delete node directly and return + # Number of children = 0: remove node and return return if child.nil? - # Number of child nodes = 1, delete node directly + # Number of children = 1: remove node directly node = child else - # Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + # Number of children = 2: remove the inorder successor and use its value to replace the current node value temp = node.right while !temp.left.nil? temp = temp.left @@ -152,65 +152,65 @@ class AVLTree node.val = temp.val end end - # Update node height + # Update the node height update_height(node) - # 2. Perform rotation operation to restore balance to this subtree + # 2. Perform rotations to restore balance in this subtree rotate(node) end - ### Search node ### + ### Find a node ### def search(val) cur = @root - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes nil while !cur.nil? - # Target node is in cur's right subtree + # The target node lies in the right subtree of cur if cur.val < val cur = cur.right - # Target node is in cur's left subtree + # The target node lies in the left subtree of cur elsif cur.val > val cur = cur.left - # Found target node, exit loop + # The target node was found; exit the loop else break end end - # Return target node + # Return the target node cur end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 def test_insert(tree, val) tree.insert(val) - puts "\nAfter inserting node #{val}, AVL tree is" + puts "\nAVL tree after inserting node #{val}:" print_tree(tree.get_root) end def test_remove(tree, val) tree.remove(val) - puts "\nAfter deleting node #{val}, AVL tree is" + puts "\nAVL tree after removing node #{val}:" print_tree(tree.get_root) end - # Please pay attention to how the AVL tree maintains balance after inserting nodes + # Initialize an empty AVL tree avl_tree = AVLTree.new - # Insert node - # Delete nodes + # Insert a node + # Observe how the AVL tree maintains balance after a node is inserted for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6] test_insert(avl_tree, val) end - # Please pay attention to how the AVL tree maintains balance after deleting nodes + # Insert a duplicate node test_insert(avl_tree, 7) - # Remove node - # Delete node with degree 1 - test_remove(avl_tree, 8) # Delete node with degree 2 - test_remove(avl_tree, 5) # Remove node with degree 1 - test_remove(avl_tree, 4) # Remove node with degree 2 + # Remove a node + # Observe how the AVL tree maintains balance after a node is removed + test_remove(avl_tree, 8) # Remove a node with 0 children + test_remove(avl_tree, 5) # Remove a node with 1 child + test_remove(avl_tree, 4) # Remove a node with 2 children result_node = avl_tree.search(7) - puts "\nFound node object #{result_node}, node value = #{result_node.val}" + puts "\nNode found: #{result_node}, node value = #{result_node.val}" end diff --git a/en/codes/ruby/chapter_tree/binary_search_tree.rb b/en/codes/ruby/chapter_tree/binary_search_tree.rb index 5717fcb2d..01234961c 100644 --- a/en/codes/ruby/chapter_tree/binary_search_tree.rb +++ b/en/codes/ruby/chapter_tree/binary_search_tree.rb @@ -11,28 +11,28 @@ require_relative '../utils/print_util' class BinarySearchTree ### Constructor ### def initialize - # Initialize empty tree + # Initialize an empty tree @root = nil end - ### Get binary tree root node ### + ### Get the root of the binary tree ### def get_root @root end - ### Search node ### + ### Find a node ### def search(num) cur = @root - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes nil while !cur.nil? - # Target node is in cur's right subtree + # The target node lies in the right subtree of cur if cur.val < num cur = cur.right - # Target node is in cur's left subtree + # The target node lies in the left subtree of cur elsif cur.val > num cur = cur.left - # Found target node, exit loop + # The target node was found; exit the loop else break end @@ -41,31 +41,31 @@ class BinarySearchTree cur end - ### Insert node ### + ### Insert a node ### def insert(num) - # If tree is empty, initialize root node + # If the tree is empty, initialize the root node if @root.nil? @root = TreeNode.new(num) return end - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes nil cur, pre = @root, nil while !cur.nil? - # Found duplicate node, return directly + # Return immediately if a duplicate node is found return if cur.val == num pre = cur - # Insertion position is in cur's right subtree + # The insertion position lies in the right subtree of cur if cur.val < num cur = cur.right - # Insertion position is in cur's left subtree + # The insertion position lies in the left subtree of cur else cur = cur.left end end - # Insert node + # Insert a node node = TreeNode.new(num) if pre.val < num pre.right = node @@ -74,34 +74,34 @@ class BinarySearchTree end end - ### Delete node ### + ### Remove a node ### def remove(num) - # If tree is empty, return directly + # Return immediately if the tree is empty return if @root.nil? - # Loop search, exit after passing leaf node + # Search in a loop, stopping if cur becomes nil cur, pre = @root, nil while !cur.nil? - # Found node to delete, exit loop + # The node to remove was found; exit the loop break if cur.val == num pre = cur - # Node to delete is in cur's right subtree + # The node to remove lies in the right subtree of cur if cur.val < num cur = cur.right - # Node to delete is in cur's left subtree + # The node to remove lies in the left subtree of cur else cur = cur.left end end - # If no node to delete, return directly + # Return immediately if the node to remove does not exist return if cur.nil? - # Number of child nodes = 0 or 1 + # Number of children = 0 or 1 if cur.left.nil? || cur.right.nil? - # When number of child nodes = 0 / 1, child = null / that child node + # With 0 / 1 children, child is nil / the child node, respectively child = cur.left || cur.right - # Delete node cur + # Remove node cur if cur != @root if pre.left == cur pre.left = child @@ -109,53 +109,53 @@ class BinarySearchTree pre.right = child end else - # If deleted node is root node, reassign root node + # If the removed node is the root, assign the new root @root = child end - # Number of child nodes = 2 + # Number of children = 2 else - # Get next node of cur in inorder traversal + # Get the inorder successor of cur tmp = cur.right while !tmp.left.nil? tmp = tmp.left end - # Recursively delete node tmp + # Recursively remove node tmp remove(tmp.val) - # Replace cur with tmp + # Replace the value of cur with the value of tmp cur.val = tmp.val end end end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize binary search tree + # Initialize the binary search tree bst = BinarySearchTree.new nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] - # Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + # Different insertion orders produce different binary trees; this sequence produces a perfect binary tree nums.each { |num| bst.insert(num) } - puts "\nInitialized binary tree is\n" + puts "\nThe initialized binary tree is:\n" print_tree(bst.get_root) - # Search node + # Find a node node = bst.search(7) - puts "\nFound node object: #{node}, node value = #{node.val}" + puts "\nNode found: #{node}, node value = #{node.val}" - # Insert node + # Insert a node bst.insert(16) - puts "\nAfter inserting node 16, binary tree is\n" + puts "\nBinary tree after inserting node 16:\n" print_tree(bst.get_root) - # Remove node + # Remove a node bst.remove(1) - puts "\nAfter removing node 1, binary tree is\n" + puts "\nBinary tree after removing node 1:\n" print_tree(bst.get_root) bst.remove(2) - puts "\nAfter removing node 2, binary tree is\n" + puts "\nBinary tree after removing node 2:\n" print_tree(bst.get_root) bst.remove(4) - puts "\nAfter removing node 4, binary tree is\n" + puts "\nBinary tree after removing node 4:\n" print_tree(bst.get_root) end diff --git a/en/codes/ruby/chapter_tree/binary_tree.rb b/en/codes/ruby/chapter_tree/binary_tree.rb index 80c3b1470..7aabe8e8f 100644 --- a/en/codes/ruby/chapter_tree/binary_tree.rb +++ b/en/codes/ruby/chapter_tree/binary_tree.rb @@ -7,32 +7,32 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize binary tree - # Initialize nodes + # Initialize the binary tree + # Initialize the nodes n1 = TreeNode.new(1) n2 = TreeNode.new(2) n3 = TreeNode.new(3) n4 = TreeNode.new(4) n5 = TreeNode.new(5) - # Build references (pointers) between nodes + # Set up references (pointers) between the nodes n1.left = n2 n1.right = n3 n2.left = n4 n2.right = n5 - puts "\nInitialize binary tree\n\n" + puts "\nInitialize the binary tree\n\n" print_tree(n1) - # Insert node P between n1 -> n2 + # Insert and remove nodes _p = TreeNode.new(0) - # Insert node _p between n1 -> n2 + # Insert node _p between n1 and n2 n1.left = _p _p.left = n2 puts "\nAfter inserting node _p\n\n" print_tree(n1) - # Remove node + # Remove a node n1.left = n2 - puts "\nAfter deleting node _p\n\n" + puts "\nAfter removing node _p\n\n" print_tree(n1) end diff --git a/en/codes/ruby/chapter_tree/binary_tree_bfs.rb b/en/codes/ruby/chapter_tree/binary_tree_bfs.rb index 00312e864..a9f4e62b6 100644 --- a/en/codes/ruby/chapter_tree/binary_tree_bfs.rb +++ b/en/codes/ruby/chapter_tree/binary_tree_bfs.rb @@ -9,28 +9,28 @@ require_relative '../utils/print_util' ### Level-order traversal ### def level_order(root) - # Initialize queue, add root node + # Initialize a queue and enqueue the root queue = [root] - # Initialize a list to save the traversal sequence + # Initialize a list to store the traversal sequence res = [] while !queue.empty? - node = queue.shift # Dequeue - res << node.val # Save node value - queue << node.left unless node.left.nil? # Left child node enqueue - queue << node.right unless node.right.nil? # Right child node enqueue + node = queue.shift # Dequeue from the queue + res << node.val # Store the node value + queue << node.left unless node.left.nil? # Enqueue the left child + queue << node.right unless node.right.nil? # Enqueue the right child end res end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize binary tree - # Here we use a function to generate a binary tree directly from an array + # Initialize the binary tree + # Use a helper function that builds a binary tree directly from an array root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) - puts "\nInitialize binary tree\n\n" + puts "\nInitialize the binary tree\n\n" print_tree(root) # Level-order traversal res = level_order(root) - puts "\nLevel-order traversal node sequence = #{res}" + puts "\nNode sequence from level-order traversal = #{res}" end diff --git a/en/codes/ruby/chapter_tree/binary_tree_dfs.rb b/en/codes/ruby/chapter_tree/binary_tree_dfs.rb index 3e480f1c5..cbf4d4a0f 100644 --- a/en/codes/ruby/chapter_tree/binary_tree_dfs.rb +++ b/en/codes/ruby/chapter_tree/binary_tree_dfs.rb @@ -7,56 +7,56 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative '../utils/tree_node' require_relative '../utils/print_util' -### Pre-order traversal ### +### Preorder traversal ### def pre_order(root) return if root.nil? - # Visit priority: root node -> left subtree -> right subtree + # Visit order: root -> left subtree -> right subtree $res << root.val pre_order(root.left) pre_order(root.right) end -### In-order traversal ### +### Inorder traversal ### def in_order(root) return if root.nil? - # Visit priority: left subtree -> root node -> right subtree + # Visit order: left subtree -> root -> right subtree in_order(root.left) $res << root.val in_order(root.right) end -### Post-order traversal ### +### Postorder traversal ### def post_order(root) return if root.nil? - # Visit priority: left subtree -> right subtree -> root node + # Visit order: left subtree -> right subtree -> root post_order(root.left) post_order(root.right) $res << root.val end -### Driver Code ### +### Driver code ### if __FILE__ == $0 - # Initialize binary tree - # Here we use a function to generate a binary tree directly from an array + # Initialize the binary tree + # Use a helper function that builds a binary tree directly from an array root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) - puts "\nInitialize binary tree\n\n" + puts "\nInitialize the binary tree\n\n" print_tree(root) # Preorder traversal $res = [] pre_order(root) - puts "\nPre-order traversal node sequence = #{$res}" + puts "\nNode sequence from preorder traversal = #{$res}" # Inorder traversal $res.clear in_order(root) - puts "\nIn-order traversal node sequence = #{$res}" + puts "\nNode sequence from inorder traversal = #{$res}" # Postorder traversal $res.clear post_order(root) - puts "\nPost-order traversal node sequence = #{$res}" + puts "\nNode sequence from postorder traversal = #{$res}" end diff --git a/en/codes/ruby/test_all.rb b/en/codes/ruby/test_all.rb index a4d417800..0e3a51c2c 100644 --- a/en/codes/ruby/test_all.rb +++ b/en/codes/ruby/test_all.rb @@ -14,10 +14,10 @@ end puts "\x1b[34mTested #{files.count} files\x1b[m" unless errors.empty? - puts "\x1b[33mFound exception in #{errors.length} files\x1b[m" + puts "\x1b[33mFound exceptions in #{errors.length} files\x1b[m" raise errors.join("\n\n") else puts "\x1b[32mPASS\x1b[m" end -puts "Testing finishes after #{((Time.now - start_time) * 1000).round} ms" +puts "Testing finished after #{((Time.now - start_time) * 1000).round} ms" diff --git a/en/codes/ruby/utils/list_node.rb b/en/codes/ruby/utils/list_node.rb index 9912998bd..d85c3cb72 100644 --- a/en/codes/ruby/utils/list_node.rb +++ b/en/codes/ruby/utils/list_node.rb @@ -7,7 +7,7 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) ### Linked list node class ### class ListNode attr_accessor :val # Node value - attr_accessor :next # Reference to next node + attr_accessor :next # Reference to the next node def initialize(val=0, next_node=nil) @val = val @@ -15,7 +15,7 @@ class ListNode end end -### Deserialize list to linked list ### +### Deserialize a list into a linked list ### def arr_to_linked_list(arr) head = current = ListNode.new(arr[0]) @@ -27,7 +27,7 @@ def arr_to_linked_list(arr) head end -### Serialize linked list to list ### +### Serialize a linked list into a list ### def linked_list_to_arr(head) arr = [] diff --git a/en/codes/ruby/utils/print_util.rb b/en/codes/ruby/utils/print_util.rb index ab3f8f657..c32acd8ac 100644 --- a/en/codes/ruby/utils/print_util.rb +++ b/en/codes/ruby/utils/print_util.rb @@ -6,14 +6,14 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) require_relative "./tree_node" -### Print matrix ### +### Print a matrix ### def print_matrix(mat) s = [] mat.each { |arr| s << " #{arr.to_s}" } puts "[\n#{s.join(",\n")}\n]" end -### Print linked list ### +### Print a linked list ### def print_linked_list(head) list = [] while head @@ -39,7 +39,7 @@ def show_trunk(p) print p.str end -### Print binary tree ### +### Print the binary tree ### # This tree printer is borrowed from TECHIE DELIGHT # https://www.techiedelight.com/c-program-print-binary-tree/ def print_tree(root, prev=nil, is_right=false) @@ -66,15 +66,15 @@ def print_tree(root, prev=nil, is_right=false) print_tree(root.left, trunk, false) end -### Print hash table ### +### Print the hash table ### def print_hash_map(hmap) hmap.entries.each { |key, value| puts "#{key} -> #{value}" } end -### Print heap ### +### Print a heap ### def print_heap(heap) - puts "Array representation of heap: #{heap}" - puts "Heap tree representation:" + puts "Array representation of the heap: #{heap}" + puts "Tree representation of the heap:" root = arr_to_tree(heap) print_tree(root) end diff --git a/en/codes/ruby/utils/tree_node.rb b/en/codes/ruby/utils/tree_node.rb index 862415325..105a3e151 100644 --- a/en/codes/ruby/utils/tree_node.rb +++ b/en/codes/ruby/utils/tree_node.rb @@ -8,8 +8,8 @@ Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) class TreeNode attr_accessor :val # Node value attr_accessor :height # Node height - attr_accessor :left # Reference to left child node - attr_accessor :right # Reference to right child node + attr_accessor :left # Reference to the left child + attr_accessor :right # Reference to the right child def initialize(val=0) @val = val @@ -17,11 +17,11 @@ class TreeNode end end -### Deserialize list to binary tree: recursion ### +### Deserialize a list into a binary tree: recursion ### def arr_to_tree_dfs(arr, i) - # Return nil if index exceeds array length or element is nil + # Return nil if the index is out of bounds or the corresponding element is nil return if i < 0 || i >= arr.length || arr[i].nil? - # Build the current node + # Create the current node root = TreeNode.new(arr[i]) # Recursively build the left and right subtrees root.left = arr_to_tree_dfs(arr, 2 * i + 1) @@ -29,12 +29,12 @@ def arr_to_tree_dfs(arr, i) root end -### Deserialize list to binary tree ### +### Deserialize a list into a binary tree ### def arr_to_tree(arr) arr_to_tree_dfs(arr, 0) end -### Serialize binary tree to list: recursion ### +### Serialize a binary tree into a list: recursion ### def tree_to_arr_dfs(root, i, res) return if root.nil? @@ -45,7 +45,7 @@ def tree_to_arr_dfs(root, i, res) tree_to_arr_dfs(root.right, 2 * i + 2, res) end -### Serialize binary tree to list ### +### Serialize a binary tree into a list ### def tree_to_arr(root) res = [] tree_to_arr_dfs(root, 0, res) diff --git a/en/codes/ruby/utils/vertex.rb b/en/codes/ruby/utils/vertex.rb index c340f5ea6..7b4c858d7 100644 --- a/en/codes/ruby/utils/vertex.rb +++ b/en/codes/ruby/utils/vertex.rb @@ -13,12 +13,12 @@ class Vertex end end -### Input value list vals, return vertex list vets ### +### Given the list of values vals, return the list of vertices vets ### def vals_to_vets(vals) Array.new(vals.length) { |i| Vertex.new(vals[i]) } end -### Input vertex list vets, return value list vals ### +### Given the list of vertices vets, return the list of values vals ### def vets_to_vals(vets) Array.new(vets.length) { |i| vets[i].val } end diff --git a/en/codes/rust/chapter_array_and_linkedlist/array.rs b/en/codes/rust/chapter_array_and_linkedlist/array.rs index 288aadd6b..228687a6d 100644 --- a/en/codes/rust/chapter_array_and_linkedlist/array.rs +++ b/en/codes/rust/chapter_array_and_linkedlist/array.rs @@ -7,59 +7,59 @@ use hello_algo_rust::include::print_util; use rand::Rng; -/* Random access to element */ +/* Access an element at a random index */ fn random_access(nums: &[i32]) -> i32 { - // Randomly select a number in interval [0, nums.len()) + // Choose a random index in [0, nums.len()) let random_index = rand::thread_rng().gen_range(0..nums.len()); - // Retrieve and return the random element + // Get and return the randomly selected element let random_num = nums[random_index]; random_num } -/* Extend array length */ +/* Extend the array length */ fn extend(nums: &[i32], enlarge: usize) -> Vec { - // Initialize an array with extended length + // Initialize an array with the extended length let mut res: Vec = vec![0; nums.len() + enlarge]; - // Copy all elements from original array to new + // Copy all elements from the original array to the new one res[0..nums.len()].copy_from_slice(nums); - // Return the extended new array + // Return the new, extended array res } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for i in (index + 1..nums.len()).rev() { nums[i] = nums[i - 1]; } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num; } -/* Remove the element at index index */ +/* Remove the element at index `index` */ fn remove(nums: &mut [i32], index: usize) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for i in index..nums.len() - 1 { nums[i] = nums[i + 1]; } } -/* Traverse array */ +/* Traverse the array */ fn traverse(nums: &[i32]) { let mut _count = 0; - // Traverse array by index + // Traverse the array by index for i in 0..nums.len() { _count += nums[i]; } - // Direct traversal of array elements + // Iterate directly over the array elements _count = 0; for &num in nums { _count += num; } } -/* Find the specified element in the array */ +/* Find a specified element in the array */ fn find(nums: &[i32], target: i32) -> Option { for i in 0..nums.len() { if nums[i] == target { @@ -69,43 +69,43 @@ fn find(nums: &[i32], target: i32) -> Option { None } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize array */ + /* Initialize the array */ let arr: [i32; 5] = [0; 5]; print!("Array arr = "); print_util::print_array(&arr); - // In Rust, specifying length ([i32; 5]) is an array, without length (&[i32]) is a slice - // Since Rust arrays are designed to have compile-time determined length, only constants can specify length - // Vector is the type Rust generally uses as a dynamic array - // To facilitate implementing the extend() method, the following treats vector as array + // In Rust, [i32; 5] is an array type with a fixed length, whereas &[i32] is a reference to a slice + // Rust array lengths are determined at compile time, so their lengths must be constant expressions + // Vec is the type normally used for dynamic arrays in Rust + // To simplify the extend() method, the following examples use a vector to represent an array let nums: Vec = vec![1, 3, 2, 5, 4]; print!("\nArray nums = "); print_util::print_array(&nums); - // Insert element + // Random access let random_num = random_access(&nums); - println!("\nGet random element {} from nums", random_num); + println!("\nRandom element from nums: {}", random_num); - // Traverse array + // Extend the length let mut nums: Vec = extend(&nums, 3); - print!("Extend array length to 8, resulting in nums = "); + print!("Extend the array length to 8, giving nums = "); print_util::print_array(&nums); - // Insert element + // Insert an element insert(&mut nums, 6, 3); - print!("\nInsert number 6 at index 3, get nums = "); + print!("\nInsert the number 6 at index 3, giving nums = "); print_util::print_array(&nums); - // Remove element + // Remove an element remove(&mut nums, 2); - print!("\nDelete element at index 2, get nums = "); + print!("\nAfter removing the element at index 2, nums = "); print_util::print_array(&nums); - // Traverse array + // Traverse the array traverse(&nums); - // Find element + // Find an element let index = find(&nums, 3).unwrap(); - println!("\nFind element 3 in nums, index = {}", index); + println!("\nSearching nums for element 3 gives index = {}", index); } diff --git a/en/codes/rust/chapter_array_and_linkedlist/linked_list.rs b/en/codes/rust/chapter_array_and_linkedlist/linked_list.rs index b4809bd7e..7935a55ea 100644 --- a/en/codes/rust/chapter_array_and_linkedlist/linked_list.rs +++ b/en/codes/rust/chapter_array_and_linkedlist/linked_list.rs @@ -27,7 +27,7 @@ pub fn remove(n0: &Rc>>) { } } -/* Access the node at index index in the linked list */ +/* Access the node at index `index` in the linked list */ pub fn access(head: Rc>>, index: i32) -> Option>>> { fn dfs( head: Option<&Rc>>>, @@ -63,38 +63,38 @@ pub fn find(head: Rc>>, target: T) -> i32 { find(Some(head).as_ref(), target, 0) } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize linked list */ - // Initialize each node + /* Initialize the linked list */ + // Initialize the individual nodes let n0 = ListNode::new(1); let n1 = ListNode::new(3); let n2 = ListNode::new(2); let n3 = ListNode::new(5); let n4 = ListNode::new(4); - // Build references between nodes + // Set up references between the nodes n0.borrow_mut().next = Some(n1.clone()); n1.borrow_mut().next = Some(n2.clone()); n2.borrow_mut().next = Some(n3.clone()); n3.borrow_mut().next = Some(n4.clone()); - print!("Initialized linked list is "); + print!("The initialized linked list is "); print_util::print_linked_list(&n0); - /* Insert node */ + /* Insert a node */ insert(&n0, ListNode::new(0)); - print!("After inserting node, linked list is "); + print!("The linked list after inserting a node is "); print_util::print_linked_list(&n0); - /* Remove node */ + /* Remove a node */ remove(&n0); - print!("After deleting node, linked list is "); + print!("The linked list after removing a node is "); print_util::print_linked_list(&n0); - /* Access node */ + /* Access a node */ let node = access(n0.clone(), 3); - println!("Value of node at index 3 in linked list = {}", node.unwrap().borrow().val); + println!("Value of the node at index 3 in the linked list = {}", node.unwrap().borrow().val); - /* Search node */ + /* Find a node */ let index = find(n0.clone(), 2); - println!("Index of node with value 2 in linked list = {}", index); + println!("Index of the node with value 2 in the linked list = {}", index); } diff --git a/en/codes/rust/chapter_array_and_linkedlist/list.rs b/en/codes/rust/chapter_array_and_linkedlist/list.rs index 3a367c78c..727b8dcbf 100644 --- a/en/codes/rust/chapter_array_and_linkedlist/list.rs +++ b/en/codes/rust/chapter_array_and_linkedlist/list.rs @@ -5,28 +5,28 @@ */ use hello_algo_rust::include::print_util; -/* Driver Code */ +/* Driver code */ fn main() { - // Initialize list + // Initialize the list let mut nums: Vec = vec![1, 3, 2, 5, 4]; print!("List nums = "); print_util::print_array(&nums); - // Update element + // Access an element let num = nums[1]; - println!("\nAccess element at index 1, get num = {num}"); + println!("\nAccessing the element at index 1 gives num = {num}"); - // Add elements at the end + // Update an element nums[1] = 0; - print!("Update element at index 1 to 0, resulting in nums = "); + print!("Update the element at index 1 to 0, giving nums = "); print_util::print_array(&nums); - // Remove element + // Clear the list nums.clear(); - print!("\nAfter clearing list, nums = "); + print!("\nAfter clearing the list, nums = "); print_util::print_array(&nums); - // Direct traversal of list elements + // Append an element at the end nums.push(1); nums.push(3); nums.push(2); @@ -35,22 +35,22 @@ fn main() { print!("\nAfter adding elements, nums = "); print_util::print_array(&nums); - // Sort list + // Insert an element in the middle nums.insert(3, 6); - print!("\nInsert number 6 at index 3, get nums = "); + print!("\nInsert the number 6 at index 3, giving nums = "); print_util::print_array(&nums); - // Remove element + // Remove an element nums.remove(3); - print!("\nDelete element at index 3, get nums = "); + print!("\nRemove the element at index 3, giving nums = "); print_util::print_array(&nums); - // Traverse list by index + // Traverse the list by index let mut _count = 0; for i in 0..nums.len() { _count += nums[i]; } - // Directly traverse list elements + // Iterate directly over the list elements _count = 0; for x in &nums { _count += x; @@ -58,14 +58,14 @@ fn main() { // Concatenate two lists let mut nums1 = vec![6, 8, 7, 10, 9]; - nums.append(&mut nums1); // After append (move), nums1 is empty! + nums.append(&mut nums1); // append moves the elements, leaving nums1 empty! - // nums.extend(&nums1); // extend (borrow) allows nums1 to continue being used - print!("\nAfter concatenating list nums1 to nums, get nums = "); + // nums.extend(&nums1); // extend borrows nums1, leaving its elements available + print!("\nAppend list nums1 to nums, giving nums = "); print_util::print_array(&nums); - // Sort list + // Sort the list nums.sort(); - print!("\nAfter sorting list, nums = "); + print!("\nAfter sorting the list, nums = "); print_util::print_array(&nums); } diff --git a/en/codes/rust/chapter_array_and_linkedlist/my_list.rs b/en/codes/rust/chapter_array_and_linkedlist/my_list.rs index 33d35c088..9b8dd2e05 100644 --- a/en/codes/rust/chapter_array_and_linkedlist/my_list.rs +++ b/en/codes/rust/chapter_array_and_linkedlist/my_list.rs @@ -6,13 +6,13 @@ use hello_algo_rust::include::print_util; -/* List class */ +/* List type */ #[allow(dead_code)] struct MyList { - arr: Vec, // Array (stores list elements) + arr: Vec, // Array (stores the 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 + extend_ratio: usize, // Factor by which the list capacity grows each time } #[allow(unused, unused_comparisons)] @@ -28,26 +28,26 @@ impl MyList { } } - /* Get list length (current number of elements) */ + /* Get the list length (current number of elements)*/ pub fn size(&self) -> usize { return self.size; } - /* Get list capacity */ + /* Get the list capacity */ pub fn capacity(&self) -> usize { return self.capacity; } - /* Update element */ + /* Access an element */ pub fn get(&self, index: usize) -> i32 { - // If the index is out of bounds, throw an exception, as below + // Panic if the index is out of bounds; the same applies below if index >= self.size { panic!("Index out of bounds") }; return self.arr[index]; } - /* Add elements at the end */ + /* Update an element */ pub fn set(&mut self, index: usize, num: i32) { if index >= self.size { panic!("Index out of bounds") @@ -55,9 +55,9 @@ impl MyList { self.arr[index] = num; } - /* Direct traversal of list elements */ + /* Append an element at the end */ pub fn add(&mut self, num: i32) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if self.size == self.capacity() { self.extend_capacity(); } @@ -66,16 +66,16 @@ impl MyList { self.size += 1; } - /* Sort list */ + /* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if self.size == self.capacity() { self.extend_capacity(); } - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for j in (index..self.size).rev() { self.arr[j + 1] = self.arr[j]; } @@ -84,13 +84,13 @@ impl MyList { self.size += 1; } - /* Remove element */ + /* Remove an 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 + // Shift all elements after index `index` one position to the left for j in index..self.size - 1 { self.arr[j] = self.arr[j + 1]; } @@ -100,18 +100,18 @@ impl MyList { return num; } - /* Driver Code */ + /* Grow the list capacity */ pub fn extend_capacity(&mut self) { - // Create new array with length extend_ratio times original, copy original array to new array + // Grow the backing vector to extend_ratio times its original length, preserving its elements let new_capacity = self.capacity * self.extend_ratio; self.arr.resize(new_capacity, 0); - // Add elements at the end + // Update the list capacity self.capacity = new_capacity; } - /* Convert list to array */ + /* Copy the list elements into a vector */ pub fn to_array(&self) -> Vec { - // Elements enqueue + // Convert only the list elements within the occupied range let mut arr = Vec::new(); for i in 0..self.size { arr.push(self.get(i)); @@ -120,11 +120,11 @@ impl MyList { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize list */ + /* Initialize the list */ let mut nums = MyList::new(10); - /* Direct traversal of list elements */ + /* Append an element at the end */ nums.add(1); nums.add(3); nums.add(2); @@ -132,33 +132,33 @@ fn main() { nums.add(4); print!("List nums = "); print_util::print_array(&nums.to_array()); - print!(", capacity = {}, length = {}", nums.capacity(), nums.size()); + print!(" , capacity = {}, size = {}", nums.capacity(), nums.size()); - /* Sort list */ + /* Insert an element in the middle */ nums.insert(3, 6); - print!("\nInsert number 6 at index 3, get nums = "); + print!("\nInsert the number 6 at index 3, giving nums = "); print_util::print_array(&nums.to_array()); - /* Remove element */ + /* Remove an element */ nums.remove(3); - print!("\nDelete element at index 3, get nums = "); + print!("\nRemove the element at index 3, giving nums = "); print_util::print_array(&nums.to_array()); - /* Update element */ + /* Access an element */ let num = nums.get(1); - println!("\nAccess element at index 1, get num = {num}"); + println!("\nAccessing the element at index 1 gives num = {num}"); - /* Add elements at the end */ + /* Update an element */ nums.set(1, 0); - print!("Update element at index 1 to 0, resulting in nums = "); + print!("Update the element at index 1 to 0, giving nums = "); print_util::print_array(&nums.to_array()); - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for i in 0..10 { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i); } - print!("\nAfter expanding list, nums = "); + print!("\nAfter expanding capacity, list nums = "); print_util::print_array(&nums.to_array()); - print!(", capacity = {}, length = {}", nums.capacity(), nums.size()); + print!(" , capacity = {}, size = {}", nums.capacity(), nums.size()); } diff --git a/en/codes/rust/chapter_backtracking/n_queens.rs b/en/codes/rust/chapter_backtracking/n_queens.rs index 26d489ab2..38dcfe503 100644 --- a/en/codes/rust/chapter_backtracking/n_queens.rs +++ b/en/codes/rust/chapter_backtracking/n_queens.rs @@ -4,7 +4,7 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ fn backtrack( row: usize, n: usize, @@ -14,37 +14,37 @@ fn backtrack( diags1: &mut [bool], diags2: &mut [bool], ) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if row == n { res.push(state.clone()); return; } - // Traverse all columns + // Iterate over all columns for col in 0..n { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square let diag1 = row + n - 1 - col; let diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if !cols[col] && !diags1[diag1] && !diags2[diag2] { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = "Q".into(); (cols[col], diags1[diag1], diags2[diag2]) = (true, true, true); - // Place the next row + // Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = "#".into(); (cols[col], diags1[diag1], diags2[diag2]) = (false, false, false); } } } -/* Solve N queens */ +/* Solve the n-queens problem */ fn n_queens(n: usize) -> Vec>> { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square let mut state: Vec> = vec![vec!["#".to_string(); n]; n]; - let mut cols = vec![false; n]; // Record whether there is a queen in the column - let mut diags1 = vec![false; 2 * n - 1]; // Record whether there is a queen on the main diagonal - let mut diags2 = vec![false; 2 * n - 1]; // Record whether there is a queen on the anti-diagonal + let mut cols = vec![false; n]; // Track whether a column contains a queen + let mut diags1 = vec![false; 2 * n - 1]; // Track whether a main diagonal contains a queen + let mut diags2 = vec![false; 2 * n - 1]; // Track whether an anti-diagonal contains a queen let mut res: Vec>> = Vec::new(); backtrack( @@ -60,13 +60,13 @@ fn n_queens(n: usize) -> Vec>> { res } -/* Driver Code */ +/* Driver code */ pub fn main() { let n: usize = 4; let res = n_queens(n); - println!("Input board size is {n}"); - println!("Total queen placement solutions: {}", res.len()); + println!("The input board has width and height {n}"); + println!("Number of queen placements: {}", res.len()); for state in res.iter() { println!("--------------------"); for row in state.iter() { diff --git a/en/codes/rust/chapter_backtracking/permutations_i.rs b/en/codes/rust/chapter_backtracking/permutations_i.rs index 5525d2dad..20e1c6922 100644 --- a/en/codes/rust/chapter_backtracking/permutations_i.rs +++ b/en/codes/rust/chapter_backtracking/permutations_i.rs @@ -4,24 +4,24 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if state.len() == choices.len() { res.push(state); return; } - // Traverse all choices + // Iterate over all choices for i in 0..choices.len() { let choice = choices[i]; - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if !selected[i] { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state.push(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state.clone(), choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop(); } @@ -30,12 +30,12 @@ fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: & /* Permutations I */ fn permutations_i(nums: &mut [i32]) -> Vec> { - let mut res = Vec::new(); // State (subset) + let mut res = Vec::new(); // Result list (permutations) backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res); res } -/* Driver Code */ +/* Driver code */ pub fn main() { let mut nums = [1, 2, 3]; diff --git a/en/codes/rust/chapter_backtracking/permutations_ii.rs b/en/codes/rust/chapter_backtracking/permutations_ii.rs index 7cf2cd470..94d489e0a 100644 --- a/en/codes/rust/chapter_backtracking/permutations_ii.rs +++ b/en/codes/rust/chapter_backtracking/permutations_ii.rs @@ -6,26 +6,26 @@ use std::collections::HashSet; -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if state.len() == choices.len() { res.push(state); return; } - // Traverse all choices + // Iterate over all choices let mut duplicated = HashSet::::new(); for i in 0..choices.len() { let choice = choices[i]; - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if !selected[i] && !duplicated.contains(&choice) { - // Attempt: make choice, update state - duplicated.insert(choice); // Record the selected element value + // Try: make a choice and update the state + duplicated.insert(choice); // Record the chosen value selected[i] = true; state.push(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state.clone(), choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop(); } @@ -39,7 +39,7 @@ fn permutations_ii(nums: &mut [i32]) -> Vec> { res } -/* Driver Code */ +/* Driver code */ pub fn main() { let mut nums = [1, 2, 2]; diff --git a/en/codes/rust/chapter_backtracking/preorder_traversal_i_compact.rs b/en/codes/rust/chapter_backtracking/preorder_traversal_i_compact.rs index 1b08d7017..1af06a380 100644 --- a/en/codes/rust/chapter_backtracking/preorder_traversal_i_compact.rs +++ b/en/codes/rust/chapter_backtracking/preorder_traversal_i_compact.rs @@ -7,14 +7,14 @@ use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; use std::{cell::RefCell, rc::Rc}; -/* Preorder traversal: Example 1 */ +/* 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 + // Record the solution res.push(node.clone()); } pre_order(res, node.borrow().left.as_ref()); @@ -22,10 +22,10 @@ fn pre_order(res: &mut Vec>>, root: Option<&Rc>>>, path: &mut Vec>>, @@ -17,10 +17,10 @@ fn pre_order( return; } if let Some(node) = root { - // Attempt + // Try path.push(node.clone()); if node.borrow().val == 7 { - // Record solution + // Record the solution res.push(path.clone()); } pre_order(res, path, node.borrow().left.as_ref()); @@ -30,10 +30,10 @@ fn pre_order( } } -/* Driver Code */ +/* Driver code */ pub fn main() { let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec()); - println!("Initialize binary tree"); + println!("Initialize the binary tree"); print_util::print_tree(root.as_ref().unwrap()); // Preorder traversal @@ -41,7 +41,7 @@ pub fn main() { let mut res = Vec::new(); pre_order(&mut res, &mut path, root.as_ref()); - println!("\nOutput all paths from root node to node 7"); + println!("\nOutput all paths from the root to nodes with value 7"); for path in res { let mut vals = Vec::new(); for node in path { diff --git a/en/codes/rust/chapter_backtracking/preorder_traversal_iii_compact.rs b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_compact.rs index faa0340db..dc05b1b23 100644 --- a/en/codes/rust/chapter_backtracking/preorder_traversal_iii_compact.rs +++ b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_compact.rs @@ -7,21 +7,21 @@ use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; use std::{cell::RefCell, rc::Rc}; -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ fn pre_order( res: &mut Vec>>>, path: &mut Vec>>, root: Option<&Rc>>, ) { - // Pruning + // Prune if root.is_none() || root.as_ref().unwrap().borrow().val == 3 { return; } if let Some(node) = root { - // Attempt + // Try path.push(node.clone()); if node.borrow().val == 7 { - // Record solution + // Record the solution res.push(path.clone()); } pre_order(res, path, node.borrow().left.as_ref()); @@ -31,10 +31,10 @@ fn pre_order( } } -/* Driver Code */ +/* Driver code */ pub fn main() { let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec()); - println!("Initialize binary tree"); + println!("Initialize the binary tree"); print_util::print_tree(root.as_ref().unwrap()); // Preorder traversal @@ -42,7 +42,7 @@ pub fn main() { let mut res = Vec::new(); pre_order(&mut res, &mut path, root.as_ref()); - println!("\nOutput all paths from root node to node 7, paths do not include nodes with value 3"); + println!("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3"); for path in res { let mut vals = Vec::new(); for node in path { diff --git a/en/codes/rust/chapter_backtracking/preorder_traversal_iii_template.rs b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_template.rs index fd27e6a1d..9b0665d33 100644 --- a/en/codes/rust/chapter_backtracking/preorder_traversal_iii_template.rs +++ b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_template.rs @@ -7,12 +7,12 @@ use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; use std::{cell::RefCell, rc::Rc}; -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ fn is_solution(state: &mut Vec>>) -> bool { return !state.is_empty() && state.last().unwrap().borrow().val == 7; } -/* Record solution */ +/* Record the solution */ fn record_solution( state: &mut Vec>>, res: &mut Vec>>>, @@ -20,39 +20,39 @@ fn record_solution( res.push(state.clone()); } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ fn is_valid(_: &mut Vec>>, choice: Option<&Rc>>) -> bool { return choice.is_some() && choice.unwrap().borrow().val != 3; } -/* Update state */ +/* Update the state */ fn make_choice(state: &mut Vec>>, choice: Rc>) { state.push(choice); } -/* Restore state */ +/* Restore the state */ fn undo_choice(state: &mut Vec>>, _: Rc>) { state.pop(); } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ fn backtrack( state: &mut Vec>>, choices: &Vec>>>, res: &mut Vec>>>, ) { - // Check if it is a solution + // Check whether this is a solution if is_solution(state) { - // Record solution + // Record the solution record_solution(state, res); } - // Traverse all choices + // Iterate over all choices for &choice in choices.iter() { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if is_valid(state, choice) { - // Attempt: make choice, update state + // Try: make a choice and update the state make_choice(state, choice.unwrap().clone()); - // Proceed to the next round of selection + // Proceed to the next choice backtrack( state, &vec![ @@ -61,23 +61,23 @@ fn backtrack( ], res, ); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undo_choice(state, choice.unwrap().clone()); } } } -/* Driver Code */ +/* Driver code */ pub fn main() { let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec()); - println!("Initialize binary tree"); + println!("Initialize the binary tree"); print_util::print_tree(root.as_ref().unwrap()); // Backtracking algorithm let mut res = Vec::new(); backtrack(&mut Vec::new(), &mut vec![root.as_ref()], &mut res); - println!("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); + println!("\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3"); for path in res { let mut vals = Vec::new(); for node in path { diff --git a/en/codes/rust/chapter_backtracking/subset_sum_i.rs b/en/codes/rust/chapter_backtracking/subset_sum_i.rs index ce00e53a9..788679f94 100644 --- a/en/codes/rust/chapter_backtracking/subset_sum_i.rs +++ b/en/codes/rust/chapter_backtracking/subset_sum_i.rs @@ -4,7 +4,7 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ fn backtrack( state: &mut Vec, target: i32, @@ -12,24 +12,24 @@ fn backtrack( start: usize, res: &mut Vec>, ) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is zero if target == 0 { res.push(state.clone()); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for i in start..choices.len() { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if the next element exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if target - choices[i] < 0 { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } @@ -38,13 +38,13 @@ fn backtrack( fn subset_sum_i(nums: &mut [i32], target: i32) -> Vec> { let mut state = Vec::new(); // State (subset) nums.sort(); // Sort nums - let start = 0; // Start point for traversal - let mut res = Vec::new(); // Result list (subset list) + let start = 0; // Starting index for iteration + let mut res = Vec::new(); // Result list (list of subsets) backtrack(&mut state, target, nums, start, &mut res); res } -/* Driver Code */ +/* Driver code */ pub fn main() { let mut nums = [3, 4, 5]; let target = 9; @@ -52,5 +52,5 @@ pub fn main() { let res = subset_sum_i(&mut nums, target); println!("Input array nums = {:?}, target = {}", &nums, target); - println!("All subsets with sum equal to {} res = {:?}", target, &res); + println!("All subsets with sum {}: res = {:?}", target, &res); } diff --git a/en/codes/rust/chapter_backtracking/subset_sum_i_naive.rs b/en/codes/rust/chapter_backtracking/subset_sum_i_naive.rs index 3f397b0a4..0f5841059 100644 --- a/en/codes/rust/chapter_backtracking/subset_sum_i_naive.rs +++ b/en/codes/rust/chapter_backtracking/subset_sum_i_naive.rs @@ -4,7 +4,7 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ fn backtrack( state: &mut Vec, target: i32, @@ -12,36 +12,36 @@ fn backtrack( choices: &[i32], res: &mut Vec>, ) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if total == target { res.push(state.clone()); return; } - // Traverse all choices + // Iterate over all choices for i in 0..choices.len() { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if total + choices[i] > target { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target, total + choices[i], choices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ fn subset_sum_i_naive(nums: &[i32], target: i32) -> Vec> { let mut state = Vec::new(); // State (subset) let total = 0; // Subset sum - let mut res = Vec::new(); // Result list (subset list) + let mut res = Vec::new(); // Result list (list of subsets) backtrack(&mut state, target, total, nums, &mut res); res } -/* Driver Code */ +/* Driver code */ pub fn main() { let nums = [3, 4, 5]; let target = 9; @@ -49,6 +49,6 @@ pub fn main() { let res = subset_sum_i_naive(&nums, target); println!("Input array nums = {:?}, target = {}", &nums, target); - println!("All subsets with sum equal to {} res = {:?}", target, &res); - println!("Please note that this method outputs results containing duplicate sets"); + println!("All subsets with sum {}: res = {:?}", target, &res); + println!("Note that this method produces duplicate subsets"); } diff --git a/en/codes/rust/chapter_backtracking/subset_sum_ii.rs b/en/codes/rust/chapter_backtracking/subset_sum_ii.rs index ad217a170..6dc21ea3d 100644 --- a/en/codes/rust/chapter_backtracking/subset_sum_ii.rs +++ b/en/codes/rust/chapter_backtracking/subset_sum_ii.rs @@ -4,7 +4,7 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ fn backtrack( state: &mut Vec, target: i32, @@ -12,29 +12,29 @@ fn backtrack( start: usize, res: &mut Vec>, ) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target is zero if target == 0 { res.push(state.clone()); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: iterate from start to avoid choosing the same element again for i in start..choices.len() { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if the next element exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if target - choices[i] < 0 { break; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if i > start && choices[i] == choices[i - 1] { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } @@ -43,13 +43,13 @@ fn backtrack( fn subset_sum_ii(nums: &mut [i32], target: i32) -> Vec> { let mut state = Vec::new(); // State (subset) nums.sort(); // Sort nums - let start = 0; // Start point for traversal - let mut res = Vec::new(); // Result list (subset list) + let start = 0; // Starting index for iteration + let mut res = Vec::new(); // Result list (list of subsets) backtrack(&mut state, target, nums, start, &mut res); res } -/* Driver Code */ +/* Driver code */ pub fn main() { let mut nums = [4, 4, 5]; let target = 9; @@ -57,5 +57,5 @@ pub fn main() { let res = subset_sum_ii(&mut nums, target); println!("Input array nums = {:?}, target = {}", &nums, target); - println!("All subsets with sum equal to {} res = {:?}", target, &res); + println!("All subsets with sum {}: res = {:?}", target, &res); } diff --git a/en/codes/rust/chapter_computational_complexity/complexity_exercises.rs b/en/codes/rust/chapter_computational_complexity/complexity_exercises.rs index 6366936c2..287194dfe 100644 --- a/en/codes/rust/chapter_computational_complexity/complexity_exercises.rs +++ b/en/codes/rust/chapter_computational_complexity/complexity_exercises.rs @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Iterative summation */ +/* Sum using iteration */ fn sum_iter(n: i32) -> i32 { let mut res = 0; for i in 1..=n { @@ -13,7 +13,7 @@ fn sum_iter(n: i32) -> i32 { res } -/* Recursive summation */ +/* Sum using recursion */ fn sum_recur(n: i32) -> i32 { if n == 1 { return 1; @@ -21,7 +21,7 @@ fn sum_recur(n: i32) -> i32 { n + sum_recur(n - 1) } -/* Linear loop */ +/* Linear-time loop */ fn linear_loop(n: i32) -> i32 { let mut res = 0; for i in 0..n { @@ -30,7 +30,7 @@ fn linear_loop(n: i32) -> i32 { res } -/* Quadratic loop */ +/* Quadratic-time loop */ fn quadratic_loop(n: i32) -> i32 { let mut res = 0; for i in 0..n { @@ -41,7 +41,7 @@ fn quadratic_loop(n: i32) -> i32 { res } -/* Logarithmic loop */ +/* Logarithmic-time loop */ fn logarithmic_loop(mut n: i32) -> i32 { while n > 1 { n /= 2; diff --git a/en/codes/rust/chapter_computational_complexity/iteration.rs b/en/codes/rust/chapter_computational_complexity/iteration.rs index dedd9e20e..dc892a2e2 100644 --- a/en/codes/rust/chapter_computational_complexity/iteration.rs +++ b/en/codes/rust/chapter_computational_complexity/iteration.rs @@ -7,7 +7,7 @@ /* for loop */ fn for_loop(n: i32) -> i32 { let mut res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for i in 1..=n { res += i; } @@ -17,12 +17,12 @@ fn for_loop(n: i32) -> i32 { /* while loop */ fn while_loop(n: i32) -> i32 { let mut res = 0; - let mut i = 1; // Initialize condition variable + let mut i = 1; // Initialize the loop control variable - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop while i <= n { res += i; - i += 1; // Update condition variable + i += 1; // Update the loop control variable } res } @@ -30,24 +30,24 @@ fn while_loop(n: i32) -> i32 { /* while loop (two updates) */ fn while_loop_ii(n: i32) -> i32 { let mut res = 0; - let mut i = 1; // Initialize condition variable + let mut i = 1; // Initialize the loop control variable - // Sum 1, 4, 10, ... + // Sum 1, 4, 10, ... in a loop while i <= n { res += i; - // Update condition variable + // Update the loop control variable i += 1; i *= 2; } res } -/* Nested for loop */ +/* Nested for loops */ fn nested_for_loop(n: i32) -> String { let mut res = vec![]; - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for i in 1..=n { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for j in 1..=n { res.push(format!("({}, {}), ", i, j)); } @@ -55,20 +55,20 @@ fn nested_for_loop(n: i32) -> String { res.join("") } -/* Driver Code */ +/* Driver code */ fn main() { let n = 5; let mut res; res = for_loop(n); - println!("\nFor loop sum result res = {res}"); + println!("\nSum from the for loop: res = {res}"); res = while_loop(n); - println!("\nWhile loop sum result res = {res}"); + println!("\nSum from the while loop: res = {res}"); res = while_loop_ii(n); - println!("\nWhile loop (two updates) sum result res = {}", res); + println!("\nSum from the while loop with two updates, res = {}", res); let res = nested_for_loop(n); - println!("\nNested for loop traversal result {res}"); + println!("\nTraversal from the nested for loops: {res}"); } diff --git a/en/codes/rust/chapter_computational_complexity/recursion.rs b/en/codes/rust/chapter_computational_complexity/recursion.rs index 5d45c8e7a..f9317c02c 100644 --- a/en/codes/rust/chapter_computational_complexity/recursion.rs +++ b/en/codes/rust/chapter_computational_complexity/recursion.rs @@ -6,13 +6,13 @@ /* Recursion */ fn recur(n: i32) -> i32 { - // Termination condition + // Base case if n == 1 { return 1; } - // Recurse: recursive call + // Recurse: make a recursive call let res = recur(n - 1); - // Return: return result + // Unwind: return the result n + res } @@ -21,14 +21,14 @@ fn for_loop_recur(n: i32) -> i32 { // Use an explicit stack to simulate the system call stack let mut stack = Vec::new(); let mut res = 0; - // Recurse: recursive call + // Recurse: make a recursive call for i in (1..=n).rev() { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.push(i); } - // Return: return result + // Unwind: return the result while !stack.is_empty() { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.pop().unwrap(); } // res = 1+2+3+...+n @@ -37,40 +37,40 @@ fn for_loop_recur(n: i32) -> i32 { /* Tail recursion */ fn tail_recur(n: i32, res: i32) -> i32 { - // Termination condition + // Base case if n == 0 { return res; } - // Tail recursive call + // Tail-recursive call tail_recur(n - 1, res + n) } /* Fibonacci sequence: recursion */ fn fib(n: i32) -> i32 { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if n == 1 || n == 2 { return n - 1; } - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) let res = fib(n - 1) + fib(n - 2); - // Return result + // Return the result res } -/* Driver Code */ +/* Driver code */ fn main() { let n = 5; let mut res; res = recur(n); - println!("\nRecursion sum result res = {res}"); + println!("\nSum from the recursive function: res = {res}"); res = for_loop_recur(n); - println!("\nUsing iteration to simulate recursion sum result res = {res}"); + println!("\nSum from simulating recursion using iteration: res = {res}"); res = tail_recur(n, 0); - println!("\nTail recursion sum result res = {res}"); + println!("\nSum from the tail-recursive function: res = {res}"); res = fib(n); - println!("\nThe {n}th Fibonacci number is {res}"); + println!("\nTerm {n} of the Fibonacci sequence is {res}"); } diff --git a/en/codes/rust/chapter_computational_complexity/space_complexity.rs b/en/codes/rust/chapter_computational_complexity/space_complexity.rs index efee1f1e2..bc38f7e6e 100644 --- a/en/codes/rust/chapter_computational_complexity/space_complexity.rs +++ b/en/codes/rust/chapter_computational_complexity/space_complexity.rs @@ -15,10 +15,10 @@ fn function() -> i32 { return 0; } -/* Constant order */ +/* Constant complexity */ #[allow(unused)] fn constant(n: i32) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space const A: i32 = 0; let b = 0; let nums = vec![0; 10000]; @@ -27,44 +27,44 @@ fn constant(n: i32) { for i in 0..n { let c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for i in 0..n { function(); } } -/* Linear order */ +/* Linear complexity */ #[allow(unused)] fn linear(n: i32) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space let mut nums = vec![0; n as usize]; // A list of length n occupies O(n) space let mut nodes = Vec::new(); for i in 0..n { nodes.push(ListNode::new(i)) } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space let mut map = HashMap::new(); for i in 0..n { map.insert(i, i.to_string()); } } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ fn linear_recur(n: i32) { - println!("Recursion n = {}", n); + println!("Recursive call n = {}", n); if n == 1 { return; }; linear_recur(n - 1); } -/* Exponential order */ +/* Quadratic complexity */ #[allow(unused)] fn quadratic(n: i32) { - // Matrix uses O(n^2) space + // The matrix uses O(n^2) space let num_matrix = vec![vec![0; n as usize]; n as usize]; - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space let mut num_list = Vec::new(); for i in 0..n { let mut tmp = Vec::new(); @@ -75,18 +75,18 @@ fn quadratic(n: i32) { } } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ fn quadratic_recur(n: i32) -> i32 { if n <= 0 { return 0; }; - // Array nums has length n, n-1, ..., 2, 1 + // The lengths of array nums are n, n-1, ..., 2, 1 let nums = vec![0; n as usize]; - println!("In recursion n = {}, nums length = {}", n, nums.len()); + println!("In recursive call n = {}, nums has length {}", n, nums.len()); return quadratic_recur(n - 1); } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ fn build_tree(n: i32) -> Option>> { if n == 0 { return None; @@ -97,18 +97,18 @@ fn build_tree(n: i32) -> Option>> { return Some(root); } -/* Driver Code */ +/* Driver code */ fn main() { let n = 5; - // Constant order + // Constant complexity constant(n); - // Linear order + // Linear complexity linear(n); linear_recur(n); - // Exponential order + // Quadratic complexity quadratic(n); quadratic_recur(n); - // Exponential order + // Exponential complexity let root = build_tree(n); print_util::print_tree(&root.unwrap()); } diff --git a/en/codes/rust/chapter_computational_complexity/time_complexity.rs b/en/codes/rust/chapter_computational_complexity/time_complexity.rs index 6f355cc23..c6d8088cf 100644 --- a/en/codes/rust/chapter_computational_complexity/time_complexity.rs +++ b/en/codes/rust/chapter_computational_complexity/time_complexity.rs @@ -4,7 +4,7 @@ * Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com) */ -/* Constant order */ +/* Constant complexity */ fn constant(n: i32) -> i32 { _ = n; let mut count = 0; @@ -15,7 +15,7 @@ fn constant(n: i32) -> i32 { count } -/* Linear order */ +/* Linear complexity */ fn linear(n: i32) -> i32 { let mut count = 0; for _ in 0..n { @@ -24,20 +24,20 @@ fn linear(n: i32) -> i32 { count } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ fn array_traversal(nums: &[i32]) -> i32 { let mut count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for _ in nums { count += 1; } count } -/* Exponential order */ +/* Quadratic complexity */ fn quadratic(n: i32) -> i32 { let mut count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for _ in 0..n { for _ in 0..n { count += 1; @@ -46,31 +46,31 @@ fn quadratic(n: i32) -> i32 { count } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ fn bubble_sort(nums: &mut [i32]) -> i32 { let mut count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i in (1..nums.len()).rev() { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0..i { if nums[j] > nums[j + 1] { // Swap nums[j] and nums[j + 1] let tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } count } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ fn exponential(n: i32) -> i32 { let mut count = 0; let mut base = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for _ in 0..n { for _ in 0..base { count += 1 @@ -81,7 +81,7 @@ fn exponential(n: i32) -> i32 { count } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation) */ fn exp_recur(n: i32) -> i32 { if n == 1 { return 1; @@ -89,7 +89,7 @@ fn exp_recur(n: i32) -> i32 { exp_recur(n - 1) + exp_recur(n - 1) + 1 } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation) */ fn logarithmic(mut n: i32) -> i32 { let mut count = 0; while n > 1 { @@ -99,7 +99,7 @@ fn logarithmic(mut n: i32) -> i32 { count } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation) */ fn log_recur(n: i32) -> i32 { if n <= 1 { return 0; @@ -107,7 +107,7 @@ fn log_recur(n: i32) -> i32 { log_recur(n / 2) + 1 } -/* Linearithmic order */ +/* Linearithmic complexity */ fn linear_log_recur(n: i32) -> i32 { if n <= 1 { return 1; @@ -119,52 +119,52 @@ fn linear_log_recur(n: i32) -> i32 { return count; } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ fn factorial_recur(n: i32) -> i32 { if n == 0 { return 1; } let mut count = 0; - // Split from 1 into n + // Split one problem into n subproblems for _ in 0..n { count += factorial_recur(n - 1); } count } -/* Driver Code */ +/* Driver code */ fn main() { - // You can modify n to run and observe the trend of the number of operations for various complexities + // Try different values of n to see how the operation count grows for each complexity let n: i32 = 8; - println!("Input data size n = {}", n); + println!("Input size n = {}", n); let mut count = constant(n); - println!("Constant-time operations count = {}", count); + println!("Operation count for constant time = {}", count); count = linear(n); - println!("Linear-time operations count = {}", count); + println!("Operation count for linear time = {}", count); count = array_traversal(&vec![0; n as usize]); - println!("Linear-time (array traversal) operations count = {}", count); + println!("Operation count for linear time (array traversal) = {}", count); count = quadratic(n); - println!("Quadratic-time operations count = {}", count); + println!("Operation count for quadratic time = {}", count); let mut nums = (1..=n).rev().collect::>(); // [n,n-1,...,2,1] count = bubble_sort(&mut nums); - println!("Quadratic-time (bubble sort) operations count = {}", count); + println!("Operation count for quadratic time (bubble sort) = {}", count); count = exponential(n); - println!("Exponential-time (iterative) operations count = {}", count); + println!("Operation count for exponential time (iteration) = {}", count); count = exp_recur(n); - println!("Exponential-time (recursive) operations count = {}", count); + println!("Operation count for exponential time (recursion) = {}", count); count = logarithmic(n); - println!("Logarithmic-time (iterative) operations count = {}", count); + println!("Operation count for logarithmic time (iteration) = {}", count); count = log_recur(n); - println!("Logarithmic-time (recursive) operations count = {}", count); + println!("Operation count for logarithmic time (recursion) = {}", count); count = linear_log_recur(n); - println!("Linearithmic-time (recursive) operations count = {}", count); + println!("Operation count for linearithmic time (recursion) = {}", count); count = factorial_recur(n); - println!("Factorial-time (recursive) operations count = {}", count); + println!("Operation count for factorial time (recursion) = {}", count); } diff --git a/en/codes/rust/chapter_computational_complexity/worst_best_time_complexity.rs b/en/codes/rust/chapter_computational_complexity/worst_best_time_complexity.rs index ada71c112..d4c19aa06 100644 --- a/en/codes/rust/chapter_computational_complexity/worst_best_time_complexity.rs +++ b/en/codes/rust/chapter_computational_complexity/worst_best_time_complexity.rs @@ -8,20 +8,20 @@ use hello_algo_rust::include::print_util; use rand::seq::SliceRandom; use rand::thread_rng; -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ fn random_numbers(n: i32) -> Vec { - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } let mut nums = (1..=n).collect::>(); - // Randomly shuffle array elements + // Randomly shuffle the array elements nums.shuffle(&mut thread_rng()); nums } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ fn find_one(nums: &[i32]) -> Option { for i in 0..nums.len() { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if nums[i] == 1 { return Some(i); } @@ -29,7 +29,7 @@ fn find_one(nums: &[i32]) -> Option { None } -/* Driver Code */ +/* Driver code */ fn main() { for _ in 0..10 { let n = 100; @@ -37,6 +37,6 @@ fn main() { let index = find_one(&nums).unwrap(); print!("\nArray [ 1, 2, ..., n ] after shuffling = "); print_util::print_array(&nums); - println!("\nIndex of number 1 is {}", index); + println!("\nIndex of number 1: {}", index); } } diff --git a/en/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs b/en/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs index b5a395f14..ef3dc0179 100644 --- a/en/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs +++ b/en/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs @@ -6,19 +6,19 @@ /* Binary search: problem f(i, j) */ fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if i > j { return -1; } let m: i32 = i + (j - i) / 2; if nums[m as usize] < target { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if nums[m as usize] > target { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -26,16 +26,16 @@ fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 { /* Binary search */ fn binary_search(nums: &[i32], target: i32) -> i32 { let n = nums.len() as i32; - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) dfs(nums, target, 0, n - 1) } -/* Driver Code */ +/* Driver code */ pub fn main() { let target = 6; let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; - // Binary search (closed interval on both sides) + // Binary search (closed interval) let index = binary_search(&nums, target); - println!("Index of target element 6 is {index}"); + println!("Index of target element 6 = {index}"); } diff --git a/en/codes/rust/chapter_divide_and_conquer/build_tree.rs b/en/codes/rust/chapter_divide_and_conquer/build_tree.rs index da83e565f..6889c565c 100644 --- a/en/codes/rust/chapter_divide_and_conquer/build_tree.rs +++ b/en/codes/rust/chapter_divide_and_conquer/build_tree.rs @@ -8,7 +8,7 @@ use hello_algo_rust::include::{print_util, TreeNode}; use std::collections::HashMap; use std::{cell::RefCell, rc::Rc}; -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ fn dfs( preorder: &[i32], inorder_map: &HashMap, @@ -16,13 +16,13 @@ fn dfs( l: i32, r: i32, ) -> Option>> { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if r - l < 0 { return None; } // Initialize the root node let root = TreeNode::new(preorder[i as usize]); - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees let m = inorder_map.get(&preorder[i as usize]).unwrap(); // Subproblem: build the left subtree root.borrow_mut().left = dfs(preorder, inorder_map, i + 1, l, m - 1); @@ -32,9 +32,9 @@ fn dfs( Some(root) } -/* Build binary tree */ +/* Build a binary tree */ fn build_tree(preorder: &[i32], inorder: &[i32]) -> Option>> { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index let mut inorder_map: HashMap = HashMap::new(); for i in 0..inorder.len() { inorder_map.insert(inorder[i], i as i32); @@ -43,12 +43,12 @@ fn build_tree(preorder: &[i32], inorder: &[i32]) -> Option> root } -/* Driver Code */ +/* Driver code */ fn main() { let preorder = [3, 9, 2, 1, 7]; let inorder = [9, 3, 1, 2, 7]; - println!("In-order traversal = {:?}", preorder); - println!("Pre-order traversal = {:?}", inorder); + println!("Preorder traversal = {:?}", preorder); + println!("Inorder traversal = {:?}", inorder); let root = build_tree(&preorder, &inorder); println!("The constructed binary tree is:"); diff --git a/en/codes/rust/chapter_divide_and_conquer/fast_power.rs b/en/codes/rust/chapter_divide_and_conquer/fast_power.rs index 659116a54..0fa633539 100644 --- a/en/codes/rust/chapter_divide_and_conquer/fast_power.rs +++ b/en/codes/rust/chapter_divide_and_conquer/fast_power.rs @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Exponentiation by squaring */ +/* Fast exponentiation */ fn fast_pow(x: i32, n: i32) -> i32 { if n == 0 { return 1; diff --git a/en/codes/rust/chapter_divide_and_conquer/hanota.rs b/en/codes/rust/chapter_divide_and_conquer/hanota.rs index 4501fbc0d..2666c4fa1 100644 --- a/en/codes/rust/chapter_divide_and_conquer/hanota.rs +++ b/en/codes/rust/chapter_divide_and_conquer/hanota.rs @@ -6,9 +6,9 @@ #![allow(non_snake_case)] -/* Move a disk */ +/* Move one disk */ fn move_pan(src: &mut Vec, tar: &mut Vec) { - // Take out a disk from the top of src + // Take a disk from the top of src let pan = src.pop().unwrap(); // Place the disk on top of tar tar.push(pan); @@ -16,14 +16,14 @@ fn move_pan(src: &mut Vec, tar: &mut Vec) { /* Solve the Tower of Hanoi problem f(i) */ fn dfs(i: i32, src: &mut Vec, buf: &mut Vec, tar: &mut Vec) { - // If there is only one disk left in src, move it directly to tar + // If only one disk remains to be moved from src, move it directly to tar if i == 1 { move_pan(src, tar); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move_pan(src, tar); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); @@ -36,19 +36,19 @@ fn solve_hanota(A: &mut Vec, B: &mut Vec, C: &mut Vec) { dfs(n, A, B, C); } -/* Driver Code */ +/* Driver code */ pub fn main() { let mut A = vec![5, 4, 3, 2, 1]; let mut B = Vec::new(); let mut C = Vec::new(); - println!("In initial state:"); + println!("Initial state:"); println!("A = {:?}", A); println!("B = {:?}", B); println!("C = {:?}", C); solve_hanota(&mut A, &mut B, &mut C); - println!("After disk movement is complete:"); + println!("After moving the disks:"); println!("A = {:?}", A); println!("B = {:?}", B); println!("C = {:?}", C); diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_backtrack.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_backtrack.rs index 8bbd005b7..854abf6c5 100644 --- a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_backtrack.rs +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_backtrack.rs @@ -6,36 +6,36 @@ /* Backtracking */ fn backtrack(choices: &[i32], state: i32, n: i32, res: &mut [i32]) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if state == n { res[0] = res[0] + 1; } - // Traverse all choices + // Iterate over all choices for &choice in choices { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if state + choice > n { continue; } - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res); // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ fn climbing_stairs_backtrack(n: usize) -> i32 { - let choices = vec![1, 2]; // Can choose to climb up 1 or 2 stairs - let state = 0; // Start climbing from the 0-th stair + let choices = vec![1, 2]; // Choose to climb 1 or 2 steps + let state = 0; // Start climbing from step 0 let mut res = Vec::new(); - res.push(0); // Use res[0] to record the solution count + res.push(0); // Use res[0] to record the number of ways backtrack(&choices, state, n as i32, &mut res); res[0] } -/* Driver Code */ +/* Driver code */ pub fn main() { let n: usize = 9; let res = climbing_stairs_backtrack(n); - println!("Climbing {n} stairs has {res} solutions"); + println!("Number of ways to climb {n} stairs: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_constraint_dp.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_constraint_dp.rs index cfc008424..ada5bee7d 100644 --- a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_constraint_dp.rs +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_constraint_dp.rs @@ -4,19 +4,19 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ fn climbing_stairs_constraint_dp(n: usize) -> i32 { if n == 1 || n == 2 { return 1; }; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions let mut dp = vec![vec![-1; 3]; n + 1]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i in 3..=n { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -24,10 +24,10 @@ fn climbing_stairs_constraint_dp(n: usize) -> i32 { dp[n][1] + dp[n][2] } -/* Driver Code */ +/* Driver code */ pub fn main() { let n: usize = 9; let res = climbing_stairs_constraint_dp(n); - println!("Climbing {n} stairs has {res} solutions"); + println!("Number of ways to climb {n} stairs: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs.rs index d25a751e4..8471128d9 100644 --- a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs.rs +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs.rs @@ -6,7 +6,7 @@ /* Search */ fn dfs(i: usize) -> i32 { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if i == 1 || i == 2 { return i as i32; } @@ -15,15 +15,15 @@ fn dfs(i: usize) -> i32 { count } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ fn climbing_stairs_dfs(n: usize) -> i32 { dfs(n) } -/* Driver Code */ +/* Driver code */ pub fn main() { let n: usize = 9; let res = climbing_stairs_dfs(n); - println!("Climbing {n} stairs has {res} solutions"); + println!("Number of ways to climb {n} stairs: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs_mem.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs_mem.rs index d3f5e02c7..9c67a748e 100644 --- a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs_mem.rs +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs_mem.rs @@ -4,13 +4,13 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Memoization search */ +/* Memoized search */ fn dfs(i: usize, mem: &mut [i32]) -> i32 { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if i == 1 || i == 2 { return i as i32; } - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if mem[i] != -1 { return mem[i]; } @@ -21,17 +21,17 @@ fn dfs(i: usize, mem: &mut [i32]) -> i32 { count } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ fn climbing_stairs_dfs_mem(n: usize) -> i32 { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded let mut mem = vec![-1; n + 1]; dfs(n, &mut mem) } -/* Driver Code */ +/* Driver code */ pub fn main() { let n: usize = 9; let res = climbing_stairs_dfs_mem(n); - println!("Climbing {n} stairs has {res} solutions"); + println!("Number of ways to climb {n} stairs: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dp.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dp.rs index 9c4eb59ad..24e709566 100644 --- a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dp.rs +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dp.rs @@ -4,25 +4,25 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ fn climbing_stairs_dp(n: usize) -> i32 { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if n == 1 || n == 2 { return n as i32; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions let mut dp = vec![-1; n + 1]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i in 3..=n { dp[i] = dp[i - 1] + dp[i - 2]; } dp[n] } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ fn climbing_stairs_dp_comp(n: usize) -> i32 { if n == 1 || n == 2 { return n as i32; @@ -36,13 +36,13 @@ fn climbing_stairs_dp_comp(n: usize) -> i32 { b } -/* Driver Code */ +/* Driver code */ pub fn main() { let n: usize = 9; let res = climbing_stairs_dp(n); - println!("Climbing {n} stairs has {res} solutions"); + println!("Number of ways to climb {n} stairs: {res}"); let res = climbing_stairs_dp_comp(n); - println!("Climbing {n} stairs has {res} solutions"); + println!("Number of ways to climb {n} stairs: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/coin_change.rs b/en/codes/rust/chapter_dynamic_programming/coin_change.rs index 82237ffc0..4d2bf51d5 100644 --- a/en/codes/rust/chapter_dynamic_programming/coin_change.rs +++ b/en/codes/rust/chapter_dynamic_programming/coin_change.rs @@ -4,24 +4,24 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ fn coin_change_dp(coins: &[i32], amt: usize) -> i32 { let n = coins.len(); let max = amt + 1; - // Initialize dp table + // Initialize the dp table let mut dp = vec![vec![0; amt + 1]; n + 1]; - // State transition: first row and first column + // State transitions: first row and first column for a in 1..=amt { dp[0][a] = max; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i in 1..=n { for a in 1..=amt { if coins[i - 1] > a as i32 { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = std::cmp::min(dp[i - 1][a], dp[i][a - coins[i - 1] as usize] + 1); } } @@ -33,22 +33,22 @@ fn coin_change_dp(coins: &[i32], amt: usize) -> i32 { } } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ fn coin_change_dp_comp(coins: &[i32], amt: usize) -> i32 { let n = coins.len(); let max = amt + 1; - // Initialize dp table + // Initialize the dp table let mut dp = vec![0; amt + 1]; dp.fill(max); dp[0] = 0; - // State transition + // State transitions for i in 1..=n { for a in 1..=amt { if coins[i - 1] > a as i32 { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = std::cmp::min(dp[a], dp[a - coins[i - 1] as usize] + 1); } } @@ -60,16 +60,16 @@ fn coin_change_dp_comp(coins: &[i32], amt: usize) -> i32 { } } -/* Driver Code */ +/* Driver code */ pub fn main() { let coins = [1, 2, 5]; let amt: usize = 4; // Dynamic programming let res = coin_change_dp(&coins, amt); - println!("Minimum coins needed to make target amount is {res}"); + println!("Minimum number of coins needed to make the target amount: {res}"); // Space-optimized dynamic programming let res = coin_change_dp_comp(&coins, amt); - println!("Minimum coins needed to make target amount is {res}"); + println!("Minimum number of coins needed to make the target amount: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/coin_change_ii.rs b/en/codes/rust/chapter_dynamic_programming/coin_change_ii.rs index f275df1a9..235fa3b81 100644 --- a/en/codes/rust/chapter_dynamic_programming/coin_change_ii.rs +++ b/en/codes/rust/chapter_dynamic_programming/coin_change_ii.rs @@ -4,23 +4,23 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ fn coin_change_ii_dp(coins: &[i32], amt: usize) -> i32 { let n = coins.len(); - // Initialize dp table + // Initialize the dp table let mut dp = vec![vec![0; amt + 1]; n + 1]; - // Initialize first column + // Initialize the first column for i in 0..=n { dp[i][0] = 1; } - // State transition + // State transitions for i in 1..=n { for a in 1..=amt { if coins[i - 1] > a as i32 { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1] as usize]; } } @@ -28,20 +28,20 @@ fn coin_change_ii_dp(coins: &[i32], amt: usize) -> i32 { dp[n][amt] } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ fn coin_change_ii_dp_comp(coins: &[i32], amt: usize) -> i32 { let n = coins.len(); - // Initialize dp table + // Initialize the dp table let mut dp = vec![0; amt + 1]; dp[0] = 1; - // State transition + // State transitions for i in 1..=n { for a in 1..=amt { if coins[i - 1] > a as i32 { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1] as usize]; } } @@ -49,16 +49,16 @@ fn coin_change_ii_dp_comp(coins: &[i32], amt: usize) -> i32 { dp[amt] } -/* Driver Code */ +/* Driver code */ pub fn main() { let coins = [1, 2, 5]; let amt: usize = 5; // Dynamic programming let res = coin_change_ii_dp(&coins, amt); - println!("Number of coin combinations to make target amount is {res}"); + println!("Number of coin combinations that make the target amount: {res}"); // Space-optimized dynamic programming let res = coin_change_ii_dp_comp(&coins, amt); - println!("Number of coin combinations to make target amount is {res}"); + println!("Number of coin combinations that make the target amount: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/edit_distance.rs b/en/codes/rust/chapter_dynamic_programming/edit_distance.rs index c11134598..c1ac15a09 100644 --- a/en/codes/rust/chapter_dynamic_programming/edit_distance.rs +++ b/en/codes/rust/chapter_dynamic_programming/edit_distance.rs @@ -4,82 +4,82 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ fn edit_distance_dfs(s: &str, t: &str, i: usize, j: usize) -> i32 { - // If both s and t are empty, return 0 + // If both prefixes are empty, return 0 if i == 0 && j == 0 { return 0; } - // If s is empty, return length of t + // If the prefix of s is empty, return the length j of the prefix of t if i == 0 { return j as i32; } - // If t is empty, return length of s + // If the prefix of t is empty, return the length i of the prefix of s if j == 0 { return i as i32; } - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if s.chars().nth(i - 1) == t.chars().nth(j - 1) { return edit_distance_dfs(s, t, i - 1, j - 1); } - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 let insert = edit_distance_dfs(s, t, i, j - 1); let delete = edit_distance_dfs(s, t, i - 1, j); let replace = edit_distance_dfs(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits std::cmp::min(std::cmp::min(insert, delete), replace) + 1 } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ fn edit_distance_dfs_mem(s: &str, t: &str, mem: &mut Vec>, i: usize, j: usize) -> i32 { - // If both s and t are empty, return 0 + // If both prefixes are empty, return 0 if i == 0 && j == 0 { return 0; } - // If s is empty, return length of t + // If the prefix of s is empty, return the length j of the prefix of t if i == 0 { return j as i32; } - // If t is empty, return length of s + // If the prefix of t is empty, return the length i of the prefix of s if j == 0 { return i as i32; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][j] != -1 { return mem[i][j]; } - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if s.chars().nth(i - 1) == t.chars().nth(j - 1) { return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1); } - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 let insert = edit_distance_dfs_mem(s, t, mem, i, j - 1); let delete = edit_distance_dfs_mem(s, t, mem, i - 1, j); let replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = std::cmp::min(std::cmp::min(insert, delete), replace) + 1; mem[i][j] } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ fn edit_distance_dp(s: &str, t: &str) -> i32 { let (n, m) = (s.len(), t.len()); let mut dp = vec![vec![0; m + 1]; n + 1]; - // State transition: first row and first column + // State transitions: first row and first column for i in 1..=n { dp[i][0] = i as i32; } for j in 1..m { dp[0][j] = j as i32; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i in 1..=n { for j in 1..=m { if s.chars().nth(i - 1) == t.chars().nth(j - 1) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = std::cmp::min(std::cmp::min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } @@ -88,36 +88,36 @@ fn edit_distance_dp(s: &str, t: &str) -> i32 { dp[n][m] } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ fn edit_distance_dp_comp(s: &str, t: &str) -> i32 { let (n, m) = (s.len(), t.len()); let mut dp = vec![0; m + 1]; - // State transition: first row + // State transitions: first row for j in 1..m { dp[j] = j as i32; } - // State transition: rest of the rows + // State transitions: remaining rows for i in 1..=n { - // State transition: first column + // State transitions: first column let mut leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i as i32; - // State transition: rest of the columns + // State transitions: remaining columns for j in 1..=m { let temp = dp[j]; if s.chars().nth(i - 1) == t.chars().nth(j - 1) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = std::cmp::min(std::cmp::min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } dp[m] } -/* Driver Code */ +/* Driver code */ pub fn main() { let s = "bag"; let t = "pack"; @@ -125,21 +125,21 @@ pub fn main() { // Brute-force search let res = edit_distance_dfs(s, t, n, m); - println!("Changing {s} to {t} requires minimum {res} edits"); + println!("Changing {s} to {t} requires at least {res} edits"); - // Memoization search + // Memoized search let mut mem = vec![vec![0; m + 1]; n + 1]; for row in mem.iter_mut() { row.fill(-1); } let res = edit_distance_dfs_mem(s, t, &mut mem, n, m); - println!("Changing {s} to {t} requires minimum {res} edits"); + println!("Changing {s} to {t} requires at least {res} edits"); // Dynamic programming let res = edit_distance_dp(s, t); - println!("Changing {s} to {t} requires minimum {res} edits"); + println!("Changing {s} to {t} requires at least {res} edits"); // Space-optimized dynamic programming let res = edit_distance_dp_comp(s, t); - println!("Changing {s} to {t} requires minimum {res} edits"); + println!("Changing {s} to {t} requires at least {res} edits"); } diff --git a/en/codes/rust/chapter_dynamic_programming/knapsack.rs b/en/codes/rust/chapter_dynamic_programming/knapsack.rs index 80eaaf5af..08760f198 100644 --- a/en/codes/rust/chapter_dynamic_programming/knapsack.rs +++ b/en/codes/rust/chapter_dynamic_programming/knapsack.rs @@ -4,58 +4,58 @@ * Author: codingonion (coderonion@gmail.com) */ -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ fn knapsack_dfs(wgt: &[i32], val: &[i32], i: usize, c: usize) -> i32 { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if i == 0 || c == 0 { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if wgt[i - 1] > c as i32 { return knapsack_dfs(wgt, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i let no = knapsack_dfs(wgt, val, i - 1, c); let yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1] as usize) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices std::cmp::max(no, yes) } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ fn knapsack_dfs_mem(wgt: &[i32], val: &[i32], mem: &mut Vec>, i: usize, c: usize) -> i32 { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if i == 0 || c == 0 { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][c] != -1 { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if wgt[i - 1] > c as i32 { return knapsack_dfs_mem(wgt, val, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i let no = knapsack_dfs_mem(wgt, val, mem, i - 1, c); let yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1] as usize) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = std::cmp::max(no, yes); mem[i][c] } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ fn knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { let n = wgt.len(); - // Initialize dp table + // Initialize the dp table let mut dp = vec![vec![0; cap + 1]; n + 1]; - // State transition + // State transitions for i in 1..=n { for c in 1..=cap { if wgt[i - 1] > c as i32 { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = std::cmp::max( dp[i - 1][c], dp[i - 1][c - wgt[i - 1] as usize] + val[i - 1], @@ -66,17 +66,17 @@ fn knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { dp[n][cap] } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ fn knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { let n = wgt.len(); - // Initialize dp table + // Initialize the dp table let mut dp = vec![0; cap + 1]; - // State transition + // State transitions for i in 1..=n { - // Traverse in reverse order + // Traverse in decreasing order for c in (1..=cap).rev() { if wgt[i - 1] <= c as i32 { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]); } } @@ -84,7 +84,7 @@ fn knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { dp[cap] } -/* Driver Code */ +/* Driver code */ pub fn main() { let wgt = [10, 20, 30, 40, 50]; let val = [50, 120, 150, 210, 240]; @@ -93,21 +93,21 @@ pub fn main() { // Brute-force search let res = knapsack_dfs(&wgt, &val, n, cap); - println!("Maximum item value not exceeding knapsack capacity is {res}"); + println!("Maximum total item value within the knapsack capacity: {res}"); - // Memoization search + // Memoized search let mut mem = vec![vec![0; cap + 1]; n + 1]; for row in mem.iter_mut() { row.fill(-1); } let res = knapsack_dfs_mem(&wgt, &val, &mut mem, n, cap); - println!("Maximum item value not exceeding knapsack capacity is {res}"); + println!("Maximum total item value within the knapsack capacity: {res}"); // Dynamic programming let res = knapsack_dp(&wgt, &val, cap); - println!("Maximum item value not exceeding knapsack capacity is {res}"); + println!("Maximum total item value within the knapsack capacity: {res}"); // Space-optimized dynamic programming let res = knapsack_dp_comp(&wgt, &val, cap); - println!("Maximum item value not exceeding knapsack capacity is {res}"); + println!("Maximum total item value within the knapsack capacity: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs b/en/codes/rust/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs index 7930f3501..0c8b29f9d 100644 --- a/en/codes/rust/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs +++ b/en/codes/rust/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs @@ -6,25 +6,25 @@ use std::cmp; -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ fn min_cost_climbing_stairs_dp(cost: &[i32]) -> i32 { let n = cost.len() - 1; if n == 1 || n == 2 { return cost[n]; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions let mut dp = vec![-1; n + 1]; - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i in 3..=n { dp[i] = cmp::min(dp[i - 1], dp[i - 2]) + cost[i]; } dp[n] } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ fn min_cost_climbing_stairs_dp_comp(cost: &[i32]) -> i32 { let n = cost.len() - 1; if n == 1 || n == 2 { @@ -39,14 +39,14 @@ fn min_cost_climbing_stairs_dp_comp(cost: &[i32]) -> i32 { b } -/* Driver Code */ +/* Driver code */ pub fn main() { let cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; - println!("Input stair cost list is {:?}", &cost); + println!("Input stair costs: {:?}", &cost); let res = min_cost_climbing_stairs_dp(&cost); - println!("Minimum cost to climb stairs is {res}"); + println!("Minimum cost to finish climbing the stairs: {res}"); let res = min_cost_climbing_stairs_dp_comp(&cost); - println!("Minimum cost to climb stairs is {res}"); + println!("Minimum cost to finish climbing the stairs: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/min_path_sum.rs b/en/codes/rust/chapter_dynamic_programming/min_path_sum.rs index ff4b3153e..f79628768 100644 --- a/en/codes/rust/chapter_dynamic_programming/min_path_sum.rs +++ b/en/codes/rust/chapter_dynamic_programming/min_path_sum.rs @@ -4,60 +4,60 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ fn min_path_sum_dfs(grid: &Vec>, i: i32, j: i32) -> i32 { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if i == 0 && j == 0 { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return i32::MAX to represent infinite cost if i < 0 || j < 0 { return i32::MAX; } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) let up = min_path_sum_dfs(grid, i - 1, j); let left = min_path_sum_dfs(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) std::cmp::min(left, up) + grid[i as usize][j as usize] } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ fn min_path_sum_dfs_mem(grid: &Vec>, mem: &mut Vec>, i: i32, j: i32) -> i32 { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if i == 0 && j == 0 { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return i32::MAX to represent infinite cost if i < 0 || j < 0 { return i32::MAX; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i as usize][j as usize] != -1 { return mem[i as usize][j as usize]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above let up = min_path_sum_dfs_mem(grid, mem, i - 1, j); let left = min_path_sum_dfs_mem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i as usize][j as usize] = std::cmp::min(left, up) + grid[i as usize][j as usize]; mem[i as usize][j as usize] } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ fn min_path_sum_dp(grid: &Vec>) -> i32 { let (n, m) = (grid.len(), grid[0].len()); - // Initialize dp table + // Initialize the dp table let mut dp = vec![vec![0; m]; n]; dp[0][0] = grid[0][0]; - // State transition: first row + // State transitions: first row for j in 1..m { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for i in 1..n { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i in 1..n { for j in 1..m { dp[i][j] = std::cmp::min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -66,21 +66,21 @@ fn min_path_sum_dp(grid: &Vec>) -> i32 { dp[n - 1][m - 1] } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ fn min_path_sum_dp_comp(grid: &Vec>) -> i32 { let (n, m) = (grid.len(), grid[0].len()); - // Initialize dp table + // Initialize the dp table let mut dp = vec![0; m]; - // State transition: first row + // State transitions: first row dp[0] = grid[0][0]; for j in 1..m { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for i in 1..n { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for j in 1..m { dp[j] = std::cmp::min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -88,7 +88,7 @@ fn min_path_sum_dp_comp(grid: &Vec>) -> i32 { dp[m - 1] } -/* Driver Code */ +/* Driver code */ pub fn main() { let grid = vec![ vec![1, 3, 1, 5], @@ -100,21 +100,21 @@ pub fn main() { // Brute-force search let res = min_path_sum_dfs(&grid, n as i32 - 1, m as i32 - 1); - println!("Minimum path sum from top-left to bottom-right is {res}"); + println!("Minimum path sum from the top-left to the bottom-right corner: {res}"); - // Memoization search + // Memoized search let mut mem = vec![vec![0; m]; n]; for row in mem.iter_mut() { row.fill(-1); } let res = min_path_sum_dfs_mem(&grid, &mut mem, n as i32 - 1, m as i32 - 1); - println!("Minimum path sum from top-left to bottom-right is {res}"); + println!("Minimum path sum from the top-left to the bottom-right corner: {res}"); // Dynamic programming let res = min_path_sum_dp(&grid); - println!("Minimum path sum from top-left to bottom-right is {res}"); + println!("Minimum path sum from the top-left to the bottom-right corner: {res}"); // Space-optimized dynamic programming let res = min_path_sum_dp_comp(&grid); - println!("Minimum path sum from top-left to bottom-right is {res}"); + println!("Minimum path sum from the top-left to the bottom-right corner: {res}"); } diff --git a/en/codes/rust/chapter_dynamic_programming/unbounded_knapsack.rs b/en/codes/rust/chapter_dynamic_programming/unbounded_knapsack.rs index 32f90845a..bafbcb326 100644 --- a/en/codes/rust/chapter_dynamic_programming/unbounded_knapsack.rs +++ b/en/codes/rust/chapter_dynamic_programming/unbounded_knapsack.rs @@ -4,19 +4,19 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ fn unbounded_knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { let n = wgt.len(); - // Initialize dp table + // Initialize the dp table let mut dp = vec![vec![0; cap + 1]; n + 1]; - // State transition + // State transitions for i in 1..=n { for c in 1..=cap { if wgt[i - 1] > c as i32 { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = std::cmp::max(dp[i - 1][c], dp[i][c - wgt[i - 1] as usize] + val[i - 1]); } } @@ -24,19 +24,19 @@ fn unbounded_knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { return dp[n][cap]; } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ fn unbounded_knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { let n = wgt.len(); - // Initialize dp table + // Initialize the dp table let mut dp = vec![0; cap + 1]; - // State transition + // State transitions for i in 1..=n { for c in 1..=cap { if wgt[i - 1] > c as i32 { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]); } } @@ -44,7 +44,7 @@ fn unbounded_knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { dp[cap] } -/* Driver Code */ +/* Driver code */ pub fn main() { let wgt = [1, 2, 3]; let val = [5, 11, 15]; @@ -52,9 +52,9 @@ pub fn main() { // Dynamic programming let res = unbounded_knapsack_dp(&wgt, &val, cap); - println!("Maximum item value not exceeding knapsack capacity is {res}"); + println!("Maximum total item value within the knapsack capacity: {res}"); // Space-optimized dynamic programming let res = unbounded_knapsack_dp_comp(&wgt, &val, cap); - println!("Maximum item value not exceeding knapsack capacity is {res}"); + println!("Maximum total item value within the knapsack capacity: {res}"); } diff --git a/en/codes/rust/chapter_graph/graph_adjacency_list.rs b/en/codes/rust/chapter_graph/graph_adjacency_list.rs index e51e5c39b..4e78916c1 100644 --- a/en/codes/rust/chapter_graph/graph_adjacency_list.rs +++ b/en/codes/rust/chapter_graph/graph_adjacency_list.rs @@ -8,10 +8,10 @@ pub use hello_algo_rust::include::{vals_to_vets, vets_to_vals, Vertex}; use std::collections::HashMap; -/* Undirected graph type based on adjacency list */ +/* Undirected graph type implemented with an adjacency list */ pub struct GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex - pub adj_list: HashMap>, // maybe HashSet for value part is better? + // Adjacency list: key is a vertex, value is the list of all its neighbors + pub adj_list: HashMap>, // A HashSet may be a better choice for the neighbor collection } impl GraphAdjList { @@ -36,21 +36,21 @@ impl GraphAdjList { self.adj_list.len() } - /* Add edge */ + /* Add an edge */ pub fn add_edge(&mut self, vet1: Vertex, vet2: Vertex) { if vet1 == vet2 { - panic!("value error"); + panic!("Vertices must be distinct"); } // Add edge vet1 - vet2 self.adj_list.entry(vet1).or_default().push(vet2); self.adj_list.entry(vet2).or_default().push(vet1); } - /* Remove edge */ + /* Remove an edge */ #[allow(unused)] pub fn remove_edge(&mut self, vet1: Vertex, vet2: Vertex) { if vet1 == vet2 { - panic!("value error"); + panic!("Vertices must be distinct"); } // Remove edge vet1 - vet2 self.adj_list @@ -61,27 +61,27 @@ impl GraphAdjList { .and_modify(|v| v.retain(|&e| e != vet1)); } - /* Add vertex */ + /* Add a vertex */ pub fn add_vertex(&mut self, vet: Vertex) { if self.adj_list.contains_key(&vet) { return; } - // Add a new linked list in the adjacency list + // Add a new neighbor list to the adjacency list self.adj_list.insert(vet, vec![]); } - /* Remove vertex */ + /* Remove a vertex */ #[allow(unused)] pub fn remove_vertex(&mut self, vet: Vertex) { - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the neighbor list for vertex vet from the adjacency list self.adj_list.remove(&vet); - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the other neighbor lists and remove every edge incident to vet for list in self.adj_list.values_mut() { list.retain(|&v| v != vet); } } - /* Print adjacency list */ + /* Print the adjacency list */ pub fn print(&self) { println!("Adjacency list ="); for (vertex, list) in &self.adj_list { @@ -91,10 +91,10 @@ impl GraphAdjList { } } -/* Driver Code */ +/* Driver code */ #[allow(unused)] fn main() { - /* Add edge */ + /* Initialize the undirected graph */ let v = vals_to_vets(vec![1, 3, 2, 5, 4]); let edges = vec![ [v[0], v[1]], @@ -106,30 +106,30 @@ fn main() { ]; let mut graph = GraphAdjList::new(edges); - println!("\nAfter initialization, graph is"); + println!("\nGraph after initialization:"); graph.print(); - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.add_edge(v[0], v[2]); - println!("\nAfter adding edge 1-2, graph is"); + println!("\nGraph after adding edge 1-2:"); graph.print(); - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.remove_edge(v[0], v[1]); - println!("\nAfter removing edge 1-3, graph is"); + println!("\nGraph after removing edge 1-3:"); graph.print(); - /* Add vertex */ + /* Add a vertex */ let v5 = Vertex { val: 6 }; graph.add_vertex(v5); - println!("\nAfter adding vertex 6, graph is"); + println!("\nGraph after adding vertex 6:"); graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.remove_vertex(v[1]); - println!("\nAfter removing vertex 3, graph is"); + println!("\nGraph after removing vertex 3:"); graph.print(); } diff --git a/en/codes/rust/chapter_graph/graph_adjacency_matrix.rs b/en/codes/rust/chapter_graph/graph_adjacency_matrix.rs index 1f5269e85..0d95b23b6 100644 --- a/en/codes/rust/chapter_graph/graph_adjacency_matrix.rs +++ b/en/codes/rust/chapter_graph/graph_adjacency_matrix.rs @@ -4,11 +4,11 @@ * Author: night-cruise (2586447362@qq.com) */ -/* Undirected graph type based on adjacency matrix */ +/* Undirected graph type implemented with an adjacency matrix */ pub struct GraphAdjMat { - // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + // Vertex list: elements are vertex values, and indices are vertex indices pub vertices: Vec, - // Adjacency matrix, where the row and column indices correspond to the "vertex index" + // Adjacency matrix: row and column indices correspond to vertex indices pub adj_mat: Vec>, } @@ -19,12 +19,12 @@ impl GraphAdjMat { vertices: vec![], adj_mat: vec![], }; - // Add vertex + // Add a vertex for val in vertices { graph.add_vertex(val); } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for edge in edges { graph.add_edge(edge[0], edge[1]) } @@ -37,10 +37,10 @@ impl GraphAdjMat { self.vertices.len() } - /* Add vertex */ + /* Add a vertex */ pub fn add_vertex(&mut self, val: i32) { let n = self.size(); - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list self.vertices.push(val); // Add a row to the adjacency matrix self.adj_mat.push(vec![0; n]); @@ -50,46 +50,46 @@ impl GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ pub fn remove_vertex(&mut self, index: usize) { if index >= self.size() { - panic!("index error") + panic!("Invalid vertex index") } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list self.vertices.remove(index); - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix self.adj_mat.remove(index); - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for row in self.adj_mat.iter_mut() { row.remove(index); } } - /* Add edge */ + /* Add an edge */ pub fn add_edge(&mut self, i: usize, j: usize) { - // Parameters i, j correspond to the vertices element indices - // Handle index out of bounds and equality + // Parameters i and j are element indices in vertices + // Handle out-of-bounds or equal indices if i >= self.size() || j >= self.size() || i == j { - panic!("index error") + panic!("Invalid vertex index") } - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal self.adj_mat[i][j] = 1; self.adj_mat[j][i] = 1; } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices pub fn remove_edge(&mut self, i: usize, j: usize) { - // Parameters i, j correspond to the vertices element indices - // Handle index out of bounds and equality + // Parameters i and j are element indices in vertices + // Handle out-of-bounds or equal indices if i >= self.size() || j >= self.size() || i == j { - panic!("index error") + panic!("Invalid vertex index") } self.adj_mat[i][j] = 0; self.adj_mat[j][i] = 0; } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ pub fn print(&self) { println!("Vertex list = {:?}", self.vertices); println!("Adjacency matrix ="); @@ -101,36 +101,36 @@ impl GraphAdjMat { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices let vertices = vec![1, 3, 2, 5, 4]; let edges = vec![[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]]; let mut graph = GraphAdjMat::new(vertices, edges); - println!("\nAfter initialization, graph is"); + println!("\nGraph after initialization:"); graph.print(); - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.add_edge(0, 2); - println!("\nAfter adding edge 1-2, graph is"); + println!("\nGraph after adding edge 1-2:"); graph.print(); - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.remove_edge(0, 1); - println!("\nAfter removing edge 1-3, graph is"); + println!("\nGraph after removing edge 1-3:"); graph.print(); - /* Add vertex */ + /* Add a vertex */ graph.add_vertex(6); - println!("\nAfter adding vertex 6, graph is"); + println!("\nGraph after adding vertex 6:"); graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.remove_vertex(1); - println!("\nAfter removing vertex 3, graph is"); + println!("\nGraph after removing vertex 3:"); graph.print(); } diff --git a/en/codes/rust/chapter_graph/graph_bfs.rs b/en/codes/rust/chapter_graph/graph_bfs.rs index c28a118fe..42184ab2e 100644 --- a/en/codes/rust/chapter_graph/graph_bfs.rs +++ b/en/codes/rust/chapter_graph/graph_bfs.rs @@ -11,38 +11,38 @@ use graph_adjacency_list::{vals_to_vets, vets_to_vals, Vertex}; use std::collections::{HashSet, VecDeque}; /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec { // Vertex traversal sequence let mut res = vec![]; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices let mut visited = HashSet::new(); visited.insert(start_vet); - // Queue used to implement BFS + // Use a queue to implement BFS let mut que = VecDeque::new(); que.push_back(start_vet); - // Starting from vertex vet, loop until all vertices are visited + // Start at start_vet and loop until all reachable vertices have been visited while let Some(vet) = que.pop_front() { - res.push(vet); // Record visited vertex + res.push(vet); // Record the visited vertex - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex if let Some(adj_vets) = graph.adj_list.get(&vet) { for &adj_vet in adj_vets { if visited.contains(&adj_vet) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - que.push_back(adj_vet); // Only enqueue unvisited vertices + que.push_back(adj_vet); // Enqueue only unvisited vertices visited.insert(adj_vet); // Mark this vertex as visited } } } - // Return vertex traversal sequence + // Return the vertex traversal sequence res } -/* Driver Code */ +/* Driver code */ fn main() { - /* Add edge */ + /* Initialize the undirected graph */ let v = vals_to_vets(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); let edges = vec![ [v[0], v[1]], @@ -59,11 +59,11 @@ fn main() { [v[7], v[8]], ]; let graph = GraphAdjList::new(edges); - println!("\nAfter initialization, graph is"); + println!("\nGraph after initialization:"); graph.print(); /* Breadth-first traversal */ let res = graph_bfs(graph, v[0]); - println!("\nBreadth-first traversal (BFS) vertex sequence is"); + println!("\nVertex sequence from breadth-first traversal (BFS):"); println!("{:?}", vets_to_vals(res)); } diff --git a/en/codes/rust/chapter_graph/graph_dfs.rs b/en/codes/rust/chapter_graph/graph_dfs.rs index 3f405659c..1893f69f4 100644 --- a/en/codes/rust/chapter_graph/graph_dfs.rs +++ b/en/codes/rust/chapter_graph/graph_dfs.rs @@ -12,35 +12,35 @@ use std::collections::HashSet; /* Depth-first traversal helper function */ fn dfs(graph: &GraphAdjList, visited: &mut HashSet, res: &mut Vec, vet: Vertex) { - res.push(vet); // Record visited vertex + res.push(vet); // Record the visited vertex visited.insert(vet); // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex if let Some(adj_vets) = graph.adj_list.get(&vet) { for &adj_vet in adj_vets { if visited.contains(&adj_vet) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex dfs(graph, visited, res, adj_vet); } } } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex fn graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> Vec { // Vertex traversal sequence let mut res = vec![]; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices let mut visited = HashSet::new(); dfs(&graph, &mut visited, &mut res, start_vet); res } -/* Driver Code */ +/* Driver code */ fn main() { - /* Add edge */ + /* Initialize the undirected graph */ let v = vals_to_vets(vec![0, 1, 2, 3, 4, 5, 6]); let edges = vec![ [v[0], v[1]], @@ -51,11 +51,11 @@ fn main() { [v[5], v[6]], ]; let graph = GraphAdjList::new(edges); - println!("\nAfter initialization, graph is"); + println!("\nGraph after initialization:"); graph.print(); /* Depth-first traversal */ let res = graph_dfs(graph, v[0]); - println!("\nDepth-first traversal (DFS) vertex sequence is"); + println!("\nVertex sequence from depth-first traversal (DFS):"); println!("{:?}", vets_to_vals(res)); } diff --git a/en/codes/rust/chapter_greedy/coin_change_greedy.rs b/en/codes/rust/chapter_greedy/coin_change_greedy.rs index 7a25cedf8..eb307cebb 100644 --- a/en/codes/rust/chapter_greedy/coin_change_greedy.rs +++ b/en/codes/rust/chapter_greedy/coin_change_greedy.rs @@ -4,14 +4,14 @@ * Author: night-cruise (2586447362@qq.com) */ -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 { - // Assume coins list is sorted + // Assume the coins list is sorted let mut i = coins.len() - 1; let mut count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while amt > 0 { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin that does not exceed the remaining amount while i > 0 && coins[i] > amt { i -= 1; } @@ -19,7 +19,7 @@ fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 { amt -= coins[i]; count += 1; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found if amt == 0 { count } else { @@ -27,28 +27,28 @@ fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 { } } -/* Driver Code */ +/* Driver code */ fn main() { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution let coins = [1, 5, 10, 20, 50, 100]; let amt = 186; let res = coin_change_greedy(&coins, amt); println!("\ncoins = {:?}, amt = {}", coins, amt); - println!("Minimum coins needed to make {} is {}", amt, res); + println!("Greedy coin count for amount {}: {}", amt, res); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution let coins = [1, 20, 50]; let amt = 60; let res = coin_change_greedy(&coins, amt); println!("\ncoins = {:?}, amt = {}", coins, amt); - println!("Minimum coins needed to make {} is {}", amt, res); - println!("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); + println!("Greedy coin count for amount {}: {}", amt, res); + println!("The actual minimum is 3 coins: 20 + 20 + 20"); - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution let coins = [1, 49, 50]; let amt = 98; let res = coin_change_greedy(&coins, amt); println!("\ncoins = {:?}, amt = {}", coins, amt); - println!("Minimum coins needed to make {} is {}", amt, res); - println!("Actually the minimum number needed is 2, i.e., 49 + 49"); + println!("Greedy coin count for amount {}: {}", amt, res); + println!("The actual minimum is 2 coins: 49 + 49"); } diff --git a/en/codes/rust/chapter_greedy/fractional_knapsack.rs b/en/codes/rust/chapter_greedy/fractional_knapsack.rs index eea768d18..b2f09d194 100644 --- a/en/codes/rust/chapter_greedy/fractional_knapsack.rs +++ b/en/codes/rust/chapter_greedy/fractional_knapsack.rs @@ -16,38 +16,38 @@ impl Item { } } -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ fn fractional_knapsack(wgt: &[i32], val: &[i32], mut cap: i32) -> f64 { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value let mut items = wgt .iter() .zip(val.iter()) .map(|(&w, &v)| Item::new(w, v)) .collect::>(); - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order items.sort_by(|a, b| { (b.v as f64 / b.w as f64) .partial_cmp(&(a.v as f64 / a.w as f64)) .unwrap() }); - // Loop for greedy selection + // Repeatedly make greedy choices let mut res = 0.0; for item in &items { if item.w <= cap { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v as f64; cap -= item.w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += item.v as f64 / item.w as f64 * cap as f64; - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break; } } res } -/* Driver Code */ +/* Driver code */ fn main() { let wgt = [10, 20, 30, 40, 50]; let val = [50, 120, 150, 210, 240]; @@ -55,5 +55,5 @@ fn main() { // Greedy algorithm let res = fractional_knapsack(&wgt, &val, cap); - println!("Maximum item value not exceeding knapsack capacity is {}", res); + println!("Maximum total item value within the knapsack capacity: {}", res); } diff --git a/en/codes/rust/chapter_greedy/max_capacity.rs b/en/codes/rust/chapter_greedy/max_capacity.rs index 5e7fcb68b..996f26e93 100644 --- a/en/codes/rust/chapter_greedy/max_capacity.rs +++ b/en/codes/rust/chapter_greedy/max_capacity.rs @@ -4,19 +4,19 @@ * Author: night-cruise (2586447362@qq.com) */ -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ fn max_capacity(ht: &[i32]) -> i32 { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array let mut i = 0; let mut j = ht.len() - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 let mut res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while i < j { - // Update max capacity + // Update the maximum capacity let cap = std::cmp::min(ht[i], ht[j]) * (j - i) as i32; res = std::cmp::max(res, cap); - // Move the shorter board inward + // Move the shorter side inward if ht[i] < ht[j] { i += 1; } else { @@ -26,11 +26,11 @@ fn max_capacity(ht: &[i32]) -> i32 { res } -/* Driver Code */ +/* Driver code */ fn main() { let ht = [3, 8, 5, 2, 7, 7, 3, 4]; // Greedy algorithm let res = max_capacity(&ht); - println!("Maximum capacity is {}", res); + println!("Maximum capacity: {}", res); } diff --git a/en/codes/rust/chapter_greedy/max_product_cutting.rs b/en/codes/rust/chapter_greedy/max_product_cutting.rs index c8348230c..22e65c719 100644 --- a/en/codes/rust/chapter_greedy/max_product_cutting.rs +++ b/en/codes/rust/chapter_greedy/max_product_cutting.rs @@ -4,32 +4,32 @@ * Author: night-cruise (2586447362@qq.com) */ -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ fn max_product_cutting(n: i32) -> i32 { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if n <= 3 { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder let a = n / 3; let b = n % 3; if b == 1 { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 3_i32.pow(a as u32 - 1) * 2 * 2 } else if b == 2 { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed 3_i32.pow(a as u32) * 2 } else { - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed 3_i32.pow(a as u32) } } -/* Driver Code */ +/* Driver code */ fn main() { let n = 58; // Greedy algorithm let res = max_product_cutting(n); - println!("Maximum cutting product is {}", res); + println!("Maximum product after cutting: {}", res); } diff --git a/en/codes/rust/chapter_hashing/array_hash_map.rs b/en/codes/rust/chapter_hashing/array_hash_map.rs index 6f3dfb0ea..3b861e388 100644 --- a/en/codes/rust/chapter_hashing/array_hash_map.rs +++ b/en/codes/rust/chapter_hashing/array_hash_map.rs @@ -10,14 +10,14 @@ pub struct Pair { pub key: i32, pub val: String, } -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ pub struct ArrayHashMap { buckets: Vec>, } impl ArrayHashMap { pub fn new() -> ArrayHashMap { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets Self { buckets: vec![None; 100], } @@ -28,13 +28,13 @@ impl ArrayHashMap { key as usize % 100 } - /* Query operation */ + /* Lookup operation */ pub fn get(&self, key: i32) -> Option<&String> { let index = self.hash_func(key); self.buckets[index].as_ref().map(|pair| &pair.val) } - /* Add operation */ + /* Insert operation */ pub fn put(&mut self, key: i32, val: &str) { let index = self.hash_func(key); self.buckets[index] = Some(Pair { @@ -46,7 +46,7 @@ impl ArrayHashMap { /* Remove operation */ pub fn remove(&mut self, key: i32) { let index = self.hash_func(key); - // Set to None to represent removal + // Set to None to mark removal self.buckets[index] = None; } @@ -74,7 +74,7 @@ impl ArrayHashMap { .collect() } - /* Print hash table */ + /* Print the hash table */ pub fn print(&self) { for pair in self.entry_set() { println!("{} -> {}", pair.key, pair.val); @@ -83,41 +83,41 @@ impl ArrayHashMap { } fn main() { - /* Initialize hash table */ + /* Initialize the hash table */ let mut map = ArrayHashMap::new(); - /* Add operation */ - // Add key-value pair (key, value) to hash table - map.put(12836, "Xiao Ha"); - map.put(15937, "Xiao Luo"); - map.put(16750, "Xiao Suan"); - map.put(13276, "Xiao Fa"); - map.put(10583, "Xiao Ya"); - println!("\nAfter adding is complete, hash table is\nKey -> Value"); + /*Insert operation */ + // Add key-value pairs (key, value) to the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + println!("\nHash table after insertion:\nKey -> Value"); map.print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value let name = map.get(15937).unwrap(); - println!("\nInput student ID 15937, found name {}", name); + println!("\nLooking up student ID 15937 gives name {}", name); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(10583); - println!("\nAfter removing 10583, hash table is\nKey -> Value"); + println!("\nHash table after removing 10583:\nKey -> Value"); map.print(); - /* Traverse hash table */ - println!("\nTraverse key-value pairs Key->Value"); + /* Traverse the hash table */ + println!("\nTraverse key-value pairs: Key->Value"); for pair in map.entry_set() { println!("{} -> {}", pair.key, pair.val); } - println!("\nTraverse keys only Key"); + println!("\nTraverse keys only: Key"); for key in map.key_set() { println!("{}", key); } - println!("\nTraverse values only Value"); + println!("\nTraverse values only: Value"); for val in map.value_set() { println!("{}", val); } diff --git a/en/codes/rust/chapter_hashing/build_in_hash.rs b/en/codes/rust/chapter_hashing/build_in_hash.rs index 1ecd0296c..24c84e1ec 100644 --- a/en/codes/rust/chapter_hashing/build_in_hash.rs +++ b/en/codes/rust/chapter_hashing/build_in_hash.rs @@ -9,41 +9,41 @@ use hello_algo_rust::include::ListNode; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; -/* Driver Code */ +/* Driver code */ fn main() { let num = 3; let mut num_hasher = DefaultHasher::new(); num.hash(&mut num_hasher); let hash_num = num_hasher.finish(); - println!("Hash value of integer {} is {}", num, hash_num); + println!("Hash of integer {}: {}", num, hash_num); let bol = true; let mut bol_hasher = DefaultHasher::new(); bol.hash(&mut bol_hasher); let hash_bol = bol_hasher.finish(); - println!("Hash value of boolean {} is {}", bol, hash_bol); + println!("Hash of boolean {}: {}", bol, hash_bol); let dec: f32 = 3.14159; let mut dec_hasher = DefaultHasher::new(); dec.to_bits().hash(&mut dec_hasher); let hash_dec = dec_hasher.finish(); - println!("Hash value of decimal {} is {}", dec, hash_dec); + println!("Hash of the bit representation of floating-point value {}: {}", dec, hash_dec); - let str = "Hello Algo"; + let str = "Hello 算法"; let mut str_hasher = DefaultHasher::new(); str.hash(&mut str_hasher); let hash_str = str_hasher.finish(); - println!("Hash value of string {} is {}", str, hash_str); + println!("Hash of string {}: {}", str, hash_str); - let arr = (&12836, &"Xiao Ha"); + let arr = (&12836, &"小哈"); let mut tup_hasher = DefaultHasher::new(); arr.hash(&mut tup_hasher); let hash_tup = tup_hasher.finish(); - println!("Hash value of tuple {:?} is {}", arr, hash_tup); + println!("Hash of tuple {:?}: {}", arr, hash_tup); let node = ListNode::new(42); let mut hasher = DefaultHasher::new(); node.borrow().val.hash(&mut hasher); let hash = hasher.finish(); - println!("Hash value of node object {:?} is {}", node, hash); + println!("Hash of the value in node {:?}: {}", node, hash); } diff --git a/en/codes/rust/chapter_hashing/hash_map.rs b/en/codes/rust/chapter_hashing/hash_map.rs index cd39b3828..9015d685e 100644 --- a/en/codes/rust/chapter_hashing/hash_map.rs +++ b/en/codes/rust/chapter_hashing/hash_map.rs @@ -8,40 +8,40 @@ use hello_algo_rust::include::print_util; use std::collections::HashMap; -/* Driver Code */ +/* Driver code */ pub fn main() { - // Initialize hash table + // Initialize the hash table let mut map = HashMap::new(); - // Add operation - // Add key-value pair (key, value) to the hash table - map.insert(12836, "Xiao Ha"); - map.insert(15937, "Xiao Luo"); - map.insert(16750, "Xiao Suan"); - map.insert(13276, "Xiao Fa"); - map.insert(10583, "Xiao Ya"); - println!("\nAfter adding is complete, hash table is\nKey -> Value"); + // Insert operation + // Insert the key-value pair (key, value) into the hash table + map.insert(12836, "小哈"); + map.insert(15937, "小啰"); + map.insert(16750, "小算"); + map.insert(13276, "小法"); + map.insert(10583, "小鸭"); + println!("\nHash table after insertion:\nKey -> Value"); print_util::print_hash_map(&map); - // Query operation - // Input key into hash table to get value + // Lookup operation + // Look up key in the hash table to obtain value let name = map.get(&15937).copied().unwrap(); - println!("\nInput student ID 15937, found name {name}"); + println!("\nLooking up student ID 15937 gives name {name}"); // Remove operation - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table _ = map.remove(&10583); - println!("\nAfter removing 10583, hash table is\nKey -> Value"); + println!("\nHash table after removing 10583:\nKey -> Value"); print_util::print_hash_map(&map); - // Traverse hash table - println!("\nTraverse key-value pairs Key->Value"); + // Traverse the hash table + println!("\nTraverse key-value pairs: Key->Value"); print_util::print_hash_map(&map); - println!("\nTraverse keys only Key"); + println!("\nTraverse keys only: Key"); for key in map.keys() { println!("{key}"); } - println!("\nTraverse values separately"); + println!("\nIterate over values only"); for value in map.values() { println!("{value}"); } diff --git a/en/codes/rust/chapter_hashing/hash_map_chaining.rs b/en/codes/rust/chapter_hashing/hash_map_chaining.rs index 4fc4bf1f8..c47348af0 100644 --- a/en/codes/rust/chapter_hashing/hash_map_chaining.rs +++ b/en/codes/rust/chapter_hashing/hash_map_chaining.rs @@ -46,7 +46,7 @@ impl HashMapChaining { fn remove(&mut self, key: i32) -> Option { let index = self.hash_func(key); - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (i, p) in self.buckets[index].iter_mut().enumerate() { if p.key == key { let pair = self.buckets[index].remove(i); @@ -55,21 +55,21 @@ impl HashMapChaining { } } - // If key is not found, return None + // Return None if key is not found None } - /* Expand hash table */ + /* Grow the hash table capacity */ fn extend(&mut self) { - // Temporarily store the original hash table + // Temporarily take ownership of the original buckets let buckets_tmp = std::mem::take(&mut self.buckets); - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity self.capacity *= self.extend_ratio; self.buckets = vec![Vec::new(); self.capacity as usize]; self.size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for bucket in buckets_tmp { for pair in bucket { self.put(pair.key, pair.val); @@ -77,7 +77,7 @@ impl HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ fn print(&self) { for bucket in &self.buckets { let mut res = Vec::new(); @@ -88,16 +88,16 @@ impl HashMapChaining { } } - /* Add operation */ + /* Insert operation */ fn put(&mut self, key: i32, val: String) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if self.load_factor() > self.load_thres { self.extend(); } let index = self.hash_func(key); - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for pair in self.buckets[index].iter_mut() { if pair.key == key { pair.val = val; @@ -105,56 +105,56 @@ impl HashMapChaining { } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end let pair = Pair { key, val }; self.buckets[index].push(pair); self.size += 1; } - /* Query operation */ + /* Lookup operation */ fn get(&self, key: i32) -> Option<&str> { let index = self.hash_func(key); - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for pair in self.buckets[index].iter() { if pair.key == key { return Some(&pair.val); } } - // If key is not found, return None + // Return None if key is not found None } } -/* Driver Code */ +/* Driver code */ pub fn main() { - /* Initialize hash table */ + /* Initialize the hash table */ let mut map = HashMapChaining::new(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(12836, "Xiao Ha".to_string()); - map.put(15937, "Xiao Luo".to_string()); - map.put(16750, "Xiao Suan".to_string()); - map.put(13276, "Xiao Fa".to_string()); - map.put(10583, "Xiao Ya".to_string()); - println!("\nAfter adding is complete, hash table is\nKey -> Value"); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(12836, "小哈".to_string()); + map.put(15937, "小啰".to_string()); + map.put(16750, "小算".to_string()); + map.put(13276, "小法".to_string()); + map.put(10583, "小鸭".to_string()); + println!("\nHash table after insertion:\nKey -> Value"); map.print(); - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value println!( - "\nInput student ID 13276, found name {}", + "\nLooking up student ID 13276 gives name {}", match map.get(13276) { Some(value) => value, - None => "Not a valid Key", + None => "Key not found", } ); /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(12836); - println!("\nAfter removing 12836, hash table is\nKey -> Value"); + println!("\nHash table after removing 12836:\nKey -> Value"); map.print(); } diff --git a/en/codes/rust/chapter_hashing/hash_map_open_addressing.rs b/en/codes/rust/chapter_hashing/hash_map_open_addressing.rs index 999b652f4..58d9243ee 100644 --- a/en/codes/rust/chapter_hashing/hash_map_open_addressing.rs +++ b/en/codes/rust/chapter_hashing/hash_map_open_addressing.rs @@ -14,10 +14,10 @@ use array_hash_map::Pair; struct HashMapOpenAddressing { size: usize, // Number of key-value pairs capacity: usize, // Hash table capacity - load_thres: f64, // Load factor threshold for triggering expansion - extend_ratio: usize, // Expansion multiplier + load_thres: f64, // Load factor threshold that triggers capacity growth + extend_ratio: usize, // Capacity growth factor buckets: Vec>, // Bucket array - TOMBSTONE: Option, // Removal marker + TOMBSTONE: Option, // Tombstone marker } impl HashMapOpenAddressing { @@ -46,30 +46,30 @@ impl HashMapOpenAddressing { self.size as f64 / self.capacity as f64 } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ fn find_bucket(&mut self, key: i32) -> usize { let mut index = self.hash_func(key); let mut first_tombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while self.buckets[index].is_some() { - // If key is found, return corresponding bucket index + // If key is found, return the corresponding bucket index if self.buckets[index].as_ref().unwrap().key == key { - // If deletion marker was encountered before, move key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if first_tombstone != -1 { self.buckets[first_tombstone as usize] = self.buckets[index].take(); self.buckets[index] = self.TOMBSTONE.clone(); - return first_tombstone as usize; // Return the moved bucket index + return first_tombstone as usize; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE { first_tombstone = index as i32; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % self.capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion if first_tombstone == -1 { index } else { @@ -77,57 +77,57 @@ impl HashMapOpenAddressing { } } - /* Query operation */ + /* Lookup operation */ fn get(&mut self, key: i32) -> Option<&str> { - // Search for bucket index corresponding to key + // Find the bucket index for key let index = self.find_bucket(key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { return self.buckets[index].as_ref().map(|pair| &pair.val as &str); } - // If key-value pair does not exist, return null + // If the key-value pair does not exist, return None None } - /* Add operation */ + /* Insert operation */ fn put(&mut self, key: i32, val: String) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if self.load_factor() > self.load_thres { self.extend(); } - // Search for bucket index corresponding to key + // Find the bucket index for key let index = self.find_bucket(key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { self.buckets[index].as_mut().unwrap().val = val; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it self.buckets[index] = Some(Pair { key, val }); self.size += 1; } /* Remove operation */ fn remove(&mut self, key: i32) { - // Search for bucket index corresponding to key + // Find the bucket index for key let index = self.find_bucket(key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { self.buckets[index] = self.TOMBSTONE.clone(); self.size -= 1; } } - /* Expand hash table */ + /* Grow the hash table capacity */ fn extend(&mut self) { - // Temporarily store the original hash table + // Temporarily save a copy of the original buckets let buckets_tmp = self.buckets.clone(); - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity self.capacity *= self.extend_ratio; self.buckets = vec![None; self.capacity]; self.size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for pair in buckets_tmp { if pair.is_none() || pair == self.TOMBSTONE { continue; @@ -137,7 +137,7 @@ impl HashMapOpenAddressing { self.put(pair.key, pair.val); } } - /* Print hash table */ + /* Print the hash table */ fn print(&self) { for pair in &self.buckets { if pair.is_none() { @@ -152,30 +152,30 @@ impl HashMapOpenAddressing { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize hash table */ + /* Initialize the hash table */ let mut hashmap = HashMapOpenAddressing::new(); - /* Add operation */ - // Add key-value pair (key, value) to the hash table - hashmap.put(12836, "Xiao Ha".to_string()); - hashmap.put(15937, "Xiao Luo".to_string()); - hashmap.put(16750, "Xiao Suan".to_string()); - hashmap.put(13276, "Xiao Fa".to_string()); - hashmap.put(10583, "Xiao Ya".to_string()); + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + hashmap.put(12836, "小哈".to_string()); + hashmap.put(15937, "小啰".to_string()); + hashmap.put(16750, "小算".to_string()); + hashmap.put(13276, "小法".to_string()); + hashmap.put(10583, "小鸭".to_string()); - println!("\nAfter adding is complete, hash table is\nKey -> Value"); + println!("\nHash table after insertion:\nKey -> Value"); hashmap.print(); - /* Query operation */ - // Input key into hash table to get value val + /* Lookup operation */ + // Look up key in the hash table to obtain val let name = hashmap.get(13276).unwrap(); - println!("\nInput student ID 13276, found name {}", name); + println!("\nLooking up student ID 13276 gives name {}", name); /* Remove operation */ - // Remove key-value pair (key, val) from hash table + // Remove the key-value pair (key, val) from the hash table hashmap.remove(16750); - println!("\nAfter removing 16750, hash table is\nKey -> Value"); + println!("\nHash table after removing 16750:\nKey -> Value"); hashmap.print(); } diff --git a/en/codes/rust/chapter_hashing/simple_hash.rs b/en/codes/rust/chapter_hashing/simple_hash.rs index 373620351..864d5eadb 100644 --- a/en/codes/rust/chapter_hashing/simple_hash.rs +++ b/en/codes/rust/chapter_hashing/simple_hash.rs @@ -40,7 +40,7 @@ fn xor_hash(key: &str) -> i32 { (hash & MODULUS) as i32 } -/* Rotational hash */ +/* Rotating hash */ fn rot_hash(key: &str) -> i32 { let mut hash = 0_i64; const MODULUS: i64 = 1000000007; @@ -52,19 +52,19 @@ fn rot_hash(key: &str) -> i32 { hash as i32 } -/* Driver Code */ +/* Driver code */ fn main() { - let key = "Hello Algo"; + let key = "Hello 算法"; let hash = add_hash(key); - println!("Additive hash value is {hash}"); + println!("Additive hash value: {hash}"); let hash = mul_hash(key); - println!("Multiplicative hash value is {hash}"); + println!("Multiplicative hash value: {hash}"); let hash = xor_hash(key); - println!("XOR hash value is {hash}"); + println!("XOR hash value: {hash}"); let hash = rot_hash(key); - println!("Rotational hash value is {hash}"); + println!("Rotating hash value: {hash}"); } diff --git a/en/codes/rust/chapter_heap/heap.rs b/en/codes/rust/chapter_heap/heap.rs index b7320d4f8..c898129a3 100644 --- a/en/codes/rust/chapter_heap/heap.rs +++ b/en/codes/rust/chapter_heap/heap.rs @@ -9,63 +9,63 @@ use hello_algo_rust::include::print_util; use std::{cmp::Reverse, collections::BinaryHeap}; fn test_push_max(heap: &mut BinaryHeap, val: i32) { - heap.push(val); // Element enters heap - println!("\nAfter element {} pushes to heap", val); + heap.push(val); // Push an element onto the heap + println!("\nAfter pushing element {} into the heap", val); print_util::print_heap(heap.iter().map(|&val| val).collect()); } fn test_pop_max(heap: &mut BinaryHeap) { let val = heap.pop().unwrap(); - println!("\nAfter heap top element {} pops from heap", val); + println!("\nAfter removing the top element {} from the heap", val); print_util::print_heap(heap.iter().map(|&val| val).collect()); } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize heap */ - // Python's heapq module implements min heap by default + /* Initialize the heap */ + // Initialize a min heap #[allow(unused_assignments)] let mut min_heap = BinaryHeap::new(); - // Rust's BinaryHeap is a max heap, min heap typically wraps elements with Reverse - // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + // Rust's BinaryHeap is a max heap; wrapping elements in Reverse gives min heap ordering + // Initialize a max heap let mut max_heap = BinaryHeap::new(); - println!("\nThe following test cases are for max heap"); + println!("\nThe following example uses a max heap"); - /* Element enters heap */ + /* Push an element onto the heap */ test_push_max(&mut max_heap, 1); test_push_max(&mut max_heap, 3); test_push_max(&mut max_heap, 2); test_push_max(&mut max_heap, 5); test_push_max(&mut max_heap, 4); - /* Check if heap is empty */ + /* Get the top element of the heap */ let peek = max_heap.peek().unwrap(); - println!("\nHeap top element is {}", peek); + println!("\nTop element: {}", peek); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ test_pop_max(&mut max_heap); test_pop_max(&mut max_heap); test_pop_max(&mut max_heap); test_pop_max(&mut max_heap); test_pop_max(&mut max_heap); - /* Get heap size */ + /* Get the heap size */ let size = max_heap.len(); - println!("\nHeap size is {}", size); + println!("\nNumber of heap elements: {}", size); - /* Check if heap is empty */ + /* Check whether the heap is empty */ let is_empty = max_heap.is_empty(); - println!("\nIs heap empty {}", is_empty); + println!("\nIs the heap empty? {}", is_empty); - /* Input list and build heap */ - // Time complexity is O(n), not O(nlogn) + /* Build a heap from an input list */ + // The time complexity is O(n), not O(nlogn) min_heap = BinaryHeap::from( vec![1, 3, 2, 5, 4] .into_iter() .map(|val| Reverse(val)) .collect::>>(), ); - println!("\nAfter inputting list and building min heap"); + println!("\nAfter building a min heap from the input list:"); print_util::print_heap(min_heap.iter().map(|&val| val.0).collect()); } diff --git a/en/codes/rust/chapter_heap/my_heap.rs b/en/codes/rust/chapter_heap/my_heap.rs index 07706c7d6..f5fdb3c1e 100644 --- a/en/codes/rust/chapter_heap/my_heap.rs +++ b/en/codes/rust/chapter_heap/my_heap.rs @@ -8,33 +8,33 @@ use hello_algo_rust::include::print_util; /* Max heap */ struct MaxHeap { - // Use vector instead of array to avoid capacity concerns + // Use a vector instead of a fixed-size array so that capacity grows automatically max_heap: Vec, } impl MaxHeap { - /* Constructor, build heap based on input list */ + /* Constructor: build a heap from the input list */ fn new(nums: Vec) -> Self { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order let mut heap = MaxHeap { max_heap: nums }; - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for i in (0..=Self::parent(heap.size() - 1)).rev() { heap.sift_down(i); } heap } - /* Get index of left child node */ + /* Get the left child index */ fn left(i: usize) -> usize { 2 * i + 1 } - /* Get index of right child node */ + /* Get the right child index */ fn right(i: usize) -> usize { 2 * i + 2 } - /* Get index of parent node */ + /* Get the parent index */ fn parent(i: usize) -> usize { (i - 1) / 2 // Floor division } @@ -44,69 +44,69 @@ impl MaxHeap { self.max_heap.swap(i, j); } - /* Get heap size */ + /* Get the heap size */ fn size(&self) -> usize { self.max_heap.len() } - /* Check if heap is empty */ + /* Check whether the heap is empty */ fn is_empty(&self) -> bool { self.max_heap.is_empty() } - /* Access top element */ + /* Access the top element of the heap */ fn peek(&self) -> Option { self.max_heap.first().copied() } - /* Element enters heap */ + /* Push an element onto the heap */ fn push(&mut self, val: i32) { - // Add node + // Add a node self.max_heap.push(val); - // Heapify from bottom to top + // Sift up self.sift_up(self.size() - 1); } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ fn sift_up(&mut self, mut i: usize) { loop { - // Node i is already the heap root, end heapification + // Stop sifting up if node i is already the root if i == 0 { break; } - // Get parent node of node i + // Get the parent of node i let p = Self::parent(i); - // When "node needs no repair", end heapification + // Stop sifting up when the node already satisfies the heap property if self.max_heap[i] <= self.max_heap[p] { break; } - // Swap two nodes + // Swap the two nodes self.swap(i, p); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Element exits heap */ + /* Pop an element from the heap */ fn pop(&mut self) -> i32 { - // Handle empty case + // Handle an empty heap if self.is_empty() { - panic!("index out of bounds"); + panic!("Index out of bounds"); } - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) self.swap(0, self.size() - 1); - // Remove node + // Remove a node let val = self.max_heap.pop().unwrap(); - // Return top element + // Sift down self.sift_down(0); - // Return heap top element + // Return the top element of the heap val } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ fn sift_down(&mut self, mut i: usize) { loop { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma let (l, r, mut ma) = (Self::left(i), Self::right(i), i); if l < self.size() && self.max_heap[l] > self.max_heap[ma] { ma = l; @@ -114,52 +114,52 @@ impl MaxHeap { if r < self.size() && self.max_heap[r] > self.max_heap[ma] { ma = r; } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if ma == i { break; } - // Swap two nodes + // Swap the two nodes self.swap(i, ma); - // Loop downwards heapification + // Continue sifting down i = ma; } } - /* Driver Code */ + /* Print the heap (binary tree) */ fn print(&self) { print_util::print_heap(self.max_heap.clone()); } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ let mut max_heap = MaxHeap::new(vec![9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); - println!("\nAfter inputting list and building heap"); + println!("\nAfter building a heap from the input list:"); max_heap.print(); - /* Check if heap is empty */ + /* Get the top element of the heap */ let peek = max_heap.peek(); if let Some(peek) = peek { - println!("\nHeap top element is {}", peek); + println!("\nTop element: {}", peek); } - /* Element enters heap */ + /* Push an element onto the heap */ let val = 7; max_heap.push(val); - println!("\nAfter element {} pushes to heap", val); + println!("\nAfter pushing element {} into the heap", val); max_heap.print(); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ let peek = max_heap.pop(); - println!("\nAfter heap top element {} pops from heap", peek); + println!("\nAfter removing the top element {} from the heap", peek); max_heap.print(); - /* Get heap size */ + /* Get the heap size */ let size = max_heap.size(); - println!("\nHeap size is {}", size); + println!("\nNumber of heap elements: {}", size); - /* Check if heap is empty */ + /* Check whether the heap is empty */ let is_empty = max_heap.is_empty(); - println!("\nIs heap empty {}", is_empty); + println!("\nIs the heap empty? {}", is_empty); } diff --git a/en/codes/rust/chapter_heap/top_k.rs b/en/codes/rust/chapter_heap/top_k.rs index 6362da5a9..d33930c49 100644 --- a/en/codes/rust/chapter_heap/top_k.rs +++ b/en/codes/rust/chapter_heap/top_k.rs @@ -9,17 +9,17 @@ use hello_algo_rust::include::print_util; use std::cmp::Reverse; use std::collections::BinaryHeap; -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ fn top_k_heap(nums: Vec, k: usize) -> BinaryHeap> { - // BinaryHeap is a max heap, use Reverse to negate elements to implement min heap + // BinaryHeap is a max heap; Reverse reverses the comparison order to implement a min heap let mut heap = BinaryHeap::>::new(); - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for &num in nums.iter().take(k) { heap.push(Reverse(num)); } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for &num in nums.iter().skip(k) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if num > heap.peek().unwrap().0 { heap.pop(); heap.push(Reverse(num)); @@ -28,7 +28,7 @@ fn top_k_heap(nums: Vec, k: usize) -> BinaryHeap> { heap } -/* Driver Code */ +/* Driver code */ fn main() { let nums = vec![1, 7, 6, 3, 2]; let k = 3; diff --git a/en/codes/rust/chapter_searching/binary_search.rs b/en/codes/rust/chapter_searching/binary_search.rs index 12f833c9a..c17ce3838 100644 --- a/en/codes/rust/chapter_searching/binary_search.rs +++ b/en/codes/rust/chapter_searching/binary_search.rs @@ -4,62 +4,62 @@ * Author: codingonion (coderonion@gmail.com) */ -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ fn binary_search(nums: &[i32], target: i32) -> i32 { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements let mut i = 0; let mut j = nums.len() as i32 - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while i <= j { let m = i + (j - i) / 2; // Calculate the midpoint index m if nums[m as usize] < target { - // This means target is in the interval [m+1, j] + // In this case, target must be in [m+1, j] if it exists i = m + 1; } else if nums[m as usize] > target { - // This means target is in the interval [i, m-1] + // In this case, target must be in [i, m-1] if it exists j = m - 1; } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ fn binary_search_lcro(nums: &[i32], target: i32) -> i32 { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last let mut i = 0; let mut j = nums.len() as i32; - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while i < j { let m = i + (j - i) / 2; // Calculate the midpoint index m if nums[m as usize] < target { - // This means target is in the interval [m+1, j) + // In this case, target must be in [m+1, j) if it exists i = m + 1; } else if nums[m as usize] > target { - // This means target is in the interval [i, m) + // In this case, target must be in [i, m) if it exists j = m; } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Driver Code */ +/* Driver code */ pub fn main() { let target = 6; let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; - // Binary search (closed interval on both sides) + // Binary search (closed interval) let mut index = binary_search(&nums, target); - println!("Index of target element 6 is {index}"); + println!("Index of target element 6 = {index}"); - // Binary search (left-closed right-open interval) + // Binary search (left-closed, right-open interval) index = binary_search_lcro(&nums, target); - println!("Index of target element 6 is {index}"); + println!("Index of target element 6 = {index}"); } diff --git a/en/codes/rust/chapter_searching/binary_search_edge.rs b/en/codes/rust/chapter_searching/binary_search_edge.rs index 7ec3cbad8..ef920940d 100644 --- a/en/codes/rust/chapter_searching/binary_search_edge.rs +++ b/en/codes/rust/chapter_searching/binary_search_edge.rs @@ -8,43 +8,43 @@ mod binary_search_insertion; use binary_search_insertion::binary_search_insertion; -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ fn binary_search_left_edge(nums: &[i32], target: i32) -> i32 { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target let i = binary_search_insertion(nums, target); - // Target not found, return -1 + // If target was not found, return -1 if i == nums.len() as i32 || nums[i as usize] != target { return -1; } - // Found target, return index i + // Target was found; return index i i } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ fn binary_search_right_edge(nums: &[i32], target: i32) -> i32 { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 let i = binary_search_insertion(nums, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // j points to the rightmost target, while i points to the first element greater than target let j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if j == -1 || nums[j as usize] != target { return -1; } - // Found target, return index j + // Target was found; return index j j } -/* Driver Code */ +/* Driver code */ fn main() { - // Array with duplicate elements + // Array containing duplicate elements let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; println!("\nArray nums = {:?}", nums); - // Binary search left and right boundaries + // Find the left and right boundaries using binary search for target in [6, 7] { let index = binary_search_left_edge(&nums, target); - println!("Leftmost element {} index is {}", target, index); + println!("Index of the leftmost occurrence of {}: {}", target, index); let index = binary_search_right_edge(&nums, target); - println!("Rightmost element {} index is {}", target, index); + println!("Index of the rightmost occurrence of {}: {}", target, index); } } diff --git a/en/codes/rust/chapter_searching/binary_search_insertion.rs b/en/codes/rust/chapter_searching/binary_search_insertion.rs index 4063de451..1e51987f6 100644 --- a/en/codes/rust/chapter_searching/binary_search_insertion.rs +++ b/en/codes/rust/chapter_searching/binary_search_insertion.rs @@ -5,57 +5,57 @@ */ #![allow(unused)] -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ fn binary_search_insertion_simple(nums: &[i32], target: i32) -> i32 { - let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize closed interval [0, n-1] + let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize the closed interval [0, n-1] while i <= j { let m = i + (j - i) / 2; // Calculate the midpoint index m if nums[m as usize] < target { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if nums[m as usize] > target { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { return m; } } - // Target not found, return insertion point i + // Target was not found; return insertion point i i } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { - let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize closed interval [0, n-1] + let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize the closed interval [0, n-1] while i <= j { let m = i + (j - i) / 2; // Calculate the midpoint index m if nums[m as usize] < target { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if nums[m as usize] > target { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue searching [i, m-1] for the insertion point before any equal elements } } // Return insertion point i i } -/* Driver Code */ +/* Driver code */ fn main() { - // Array without duplicate elements + // Array with no duplicate elements let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; println!("\nArray nums = {:?}", nums); - // Binary search for insertion point + // Find an insertion point using binary search for target in [6, 9] { let index = binary_search_insertion_simple(&nums, target); - println!("Insertion point index for element {} is {}", target, index); + println!("Insertion index for element {}: {}", target, index); } - // Array with duplicate elements + // Array containing duplicate elements let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; println!("\nArray nums = {:?}", nums); - // Binary search for insertion point + // Find an insertion point using binary search for target in [2, 6, 20] { let index = binary_search_insertion(&nums, target); - println!("Insertion point index for element {} is {}", target, index); + println!("Insertion index for element {}: {}", target, index); } } diff --git a/en/codes/rust/chapter_searching/hashing_search.rs b/en/codes/rust/chapter_searching/hashing_search.rs index 53316910e..9d354fc8c 100644 --- a/en/codes/rust/chapter_searching/hashing_search.rs +++ b/en/codes/rust/chapter_searching/hashing_search.rs @@ -9,42 +9,42 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -/* Hash search (array) */ +/* Hash-based search (array) */ fn hashing_search_array<'a>(map: &'a HashMap, target: i32) -> Option<&'a usize> { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return None + // Hash table key: target element; value: index + // Return None if the hash table does not contain this key map.get(&target) } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ fn hashing_search_linked_list( map: &HashMap>>>, target: i32, ) -> Option<&Rc>>> { - // Hash table key: target node value, value: node object - // If this key does not exist in the hash table, return None + // Hash table key: target node value; value: node object + // Return None if the hash table does not contain this key map.get(&target) } -/* Driver Code */ +/* Driver code */ pub fn main() { let target = 3; - /* Hash search (array) */ + /* Hash-based search (array) */ let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; - // Initialize hash table + // Initialize the hash table let mut map = HashMap::new(); for (i, num) in nums.iter().enumerate() { - map.insert(*num, i); // key: element, value: index + map.insert(*num, i); // key: element; value: index } let index = hashing_search_array(&map, target); println!("Index of target element 3 = {}", index.unwrap()); - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ let head = ListNode::arr_to_linked_list(&nums); - // Initialize hash table + // Initialize the hash table // let mut map1 = HashMap::new(); let map1 = ListNode::linked_list_to_hashmap(head); let node = hashing_search_linked_list(&map1, target); - println!("Node object corresponding to target node value 3 is {:?}", node); + println!("Node corresponding to target value 3: {:?}", node); } diff --git a/en/codes/rust/chapter_searching/linear_search.rs b/en/codes/rust/chapter_searching/linear_search.rs index bcc584634..7d30c72a6 100644 --- a/en/codes/rust/chapter_searching/linear_search.rs +++ b/en/codes/rust/chapter_searching/linear_search.rs @@ -10,14 +10,14 @@ use std::rc::Rc; /* Linear search (array) */ fn linear_search_array(nums: &[i32], target: i32) -> i32 { - // Traverse array + // Traverse the array for (i, num) in nums.iter().enumerate() { - // Found the target element, return its index + // The target element was found; return its index if num == &target { return i as i32; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } @@ -26,29 +26,29 @@ fn linear_search_linked_list( head: Rc>>, target: i32, ) -> Option>>> { - // Found the target node, return it + // Search for the target node and return it if found if head.borrow().val == target { return Some(head); }; - // Found the target node, return it + // Search for the target node and return it if found if let Some(node) = &head.borrow_mut().next { return linear_search_linked_list(node.clone(), target); } - // Target node not found, return None + // The target node was not found; return None return None; } -/* Driver Code */ +/* Driver code */ pub fn main() { let target = 3; - /* Perform linear search in array */ + /* Perform linear search on the array */ let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; let index = linear_search_array(&nums, target); println!("Index of target element 3 = {}", index); - /* Perform linear search in linked list */ + /* Perform linear search on the linked list */ let head = ListNode::arr_to_linked_list(&nums); let node = linear_search_linked_list(head.unwrap(), target); - println!("Node object corresponding to target node value 3 is {:?}", node); + println!("Node corresponding to target value 3: {:?}", node); } diff --git a/en/codes/rust/chapter_searching/two_sum.rs b/en/codes/rust/chapter_searching/two_sum.rs index 9641c7f91..968ca37a0 100644 --- a/en/codes/rust/chapter_searching/two_sum.rs +++ b/en/codes/rust/chapter_searching/two_sum.rs @@ -7,10 +7,10 @@ use hello_algo_rust::include::print_util; use std::collections::HashMap; -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ pub fn two_sum_brute_force(nums: &Vec, target: i32) -> Option> { let size = nums.len(); - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for i in 0..size - 1 { for j in i + 1..size { if nums[i] + nums[j] == target { @@ -21,11 +21,11 @@ pub fn two_sum_brute_force(nums: &Vec, target: i32) -> Option> { None } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ pub fn two_sum_hash_table(nums: &Vec, target: i32) -> Option> { - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space let mut dic = HashMap::new(); - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (i, num) in nums.iter().enumerate() { match dic.get(&(target - num)) { Some(v) => return Some(vec![*v as i32, i as i32]), @@ -36,14 +36,14 @@ pub fn two_sum_hash_table(nums: &Vec, target: i32) -> Option> { } fn main() { - // ======= Test Case ======= + // ======= Test case ======= let nums = vec![2, 7, 11, 15]; let target = 13; - // ====== Driver Code ====== + // ====== Driver code ====== // Method 1 let res = two_sum_brute_force(&nums, target).unwrap(); - print!("Method 1 res = "); + print!("Method 1: res = "); print_util::print_array(&res); // Method 2 let res = two_sum_hash_table(&nums, target).unwrap(); diff --git a/en/codes/rust/chapter_sorting/bubble_sort.rs b/en/codes/rust/chapter_sorting/bubble_sort.rs index bc8c5b81a..2c514f6ee 100644 --- a/en/codes/rust/chapter_sorting/bubble_sort.rs +++ b/en/codes/rust/chapter_sorting/bubble_sort.rs @@ -8,9 +8,9 @@ use hello_algo_rust::include::print_util; /* Bubble sort */ fn bubble_sort(nums: &mut [i32]) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i in (1..nums.len()).rev() { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0..i { if nums[j] > nums[j + 1] { // Swap nums[j] and nums[j + 1] @@ -22,28 +22,28 @@ fn bubble_sort(nums: &mut [i32]) { /* Bubble sort (flag optimization) */ fn bubble_sort_with_flag(nums: &mut [i32]) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i in (1..nums.len()).rev() { - let mut flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + let mut flag = false; // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0..i { if nums[j] > nums[j + 1] { // Swap nums[j] and nums[j + 1] nums.swap(j, j + 1); - flag = true; // Record element swap + flag = true; // Record that a swap occurred } } if !flag { - break; // No elements were swapped in this round of "bubbling", exit directly + break; // Exit immediately if this pass made no swaps }; } } -/* Driver Code */ +/* Driver code */ pub fn main() { let mut nums = [4, 1, 3, 1, 5, 2]; bubble_sort(&mut nums); - print!("After bubble sort completes, nums = "); + print!("After bubble sort, nums = "); print_util::print_array(&nums); let mut nums1 = [4, 1, 3, 1, 5, 2]; diff --git a/en/codes/rust/chapter_sorting/bucket_sort.rs b/en/codes/rust/chapter_sorting/bucket_sort.rs index c8b310283..fa0590a23 100644 --- a/en/codes/rust/chapter_sorting/bucket_sort.rs +++ b/en/codes/rust/chapter_sorting/bucket_sort.rs @@ -8,22 +8,22 @@ use hello_algo_rust::include::print_util; /* Bucket sort */ fn bucket_sort(nums: &mut [f64]) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = n/2 buckets, expecting 2 elements per bucket let k = nums.len() / 2; let mut buckets = vec![vec![]; k]; - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for &num in nums.iter() { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); use num * k to map each value to an index in [0, k-1] let i = (num * k as f64) as usize; // Add num to bucket i buckets[i].push(num); } // 2. Sort each bucket for bucket in &mut buckets { - // Use built-in sorting function, can also replace with other sorting algorithms + // Use the built-in sorting function, or replace it with another sorting algorithm bucket.sort_by(|a, b| a.partial_cmp(b).unwrap()); } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results let mut i = 0; for bucket in buckets.iter() { for &num in bucket.iter() { @@ -33,11 +33,11 @@ fn bucket_sort(nums: &mut [f64]) { } } -/* Driver Code */ +/* Driver code */ fn main() { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) let mut nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; bucket_sort(&mut nums); - print!("After bucket sort completes, nums = "); + print!("After bucket sort, nums = "); print_util::print_array(&nums); } diff --git a/en/codes/rust/chapter_sorting/counting_sort.rs b/en/codes/rust/chapter_sorting/counting_sort.rs index 0253ecf64..02656bcf5 100644 --- a/en/codes/rust/chapter_sorting/counting_sort.rs +++ b/en/codes/rust/chapter_sorting/counting_sort.rs @@ -7,17 +7,17 @@ use hello_algo_rust::include::print_util; /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects fn counting_sort_naive(nums: &mut [i32]) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m = *nums.iter().max().unwrap(); - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num let mut counter = vec![0; m as usize + 1]; for &num in nums.iter() { counter[num as usize] += 1; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements let mut i = 0; for num in 0..m + 1 { for _ in 0..counter[num as usize] { @@ -28,39 +28,39 @@ fn counting_sort_naive(nums: &mut [i32]) { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Stable implementation that can be adapted to sort objects fn counting_sort(nums: &mut [i32]) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m = *nums.iter().max().unwrap() as usize; - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num let mut counter = vec![0; m + 1]; for &num in nums.iter() { counter[num as usize] += 1; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for i in 0..m { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result let n = nums.len(); let mut res = vec![0; n]; for i in (0..n).rev() { let num = nums[i]; res[counter[num as usize] - 1] = num; // Place num at the corresponding index - counter[num as usize] -= 1; // Decrement the prefix sum by 1, getting the next index to place num + counter[num as usize] -= 1; // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res nums.copy_from_slice(&res) } -/* Driver Code */ +/* Driver code */ fn main() { let mut nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; counting_sort_naive(&mut nums); - print!("After counting sort (cannot sort objects) completes, nums = "); + print!("After counting sort (without support for objects), nums = "); print_util::print_array(&nums); let mut nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; diff --git a/en/codes/rust/chapter_sorting/heap_sort.rs b/en/codes/rust/chapter_sorting/heap_sort.rs index 4160228e6..49b0198d8 100644 --- a/en/codes/rust/chapter_sorting/heap_sort.rs +++ b/en/codes/rust/chapter_sorting/heap_sort.rs @@ -6,10 +6,10 @@ use hello_algo_rust::include::print_util; -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ fn sift_down(nums: &mut [i32], n: usize, mut i: usize) { loop { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma let l = 2 * i + 1; let r = 2 * i + 2; let mut ma = i; @@ -19,36 +19,36 @@ fn sift_down(nums: &mut [i32], n: usize, mut i: usize) { if r < n && nums[r] > nums[ma] { ma = r; } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if ma == i { break; } - // Swap two nodes + // Swap the two nodes nums.swap(i, ma); - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ fn heap_sort(nums: &mut [i32]) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for i in (0..nums.len() / 2).rev() { sift_down(nums, nums.len(), i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for i in (1..nums.len()).rev() { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) nums.swap(0, i); - // Start heapifying the root node, from top to bottom + // Sift down starting at the root sift_down(nums, i, 0); } } -/* Driver Code */ +/* Driver code */ fn main() { let mut nums = [4, 1, 3, 1, 5, 2]; heap_sort(&mut nums); - print!("After heap sort completes, nums = "); + print!("After heap sort, nums = "); print_util::print_array(&nums); } diff --git a/en/codes/rust/chapter_sorting/insertion_sort.rs b/en/codes/rust/chapter_sorting/insertion_sort.rs index c4cca52e8..6e7e85123 100644 --- a/en/codes/rust/chapter_sorting/insertion_sort.rs +++ b/en/codes/rust/chapter_sorting/insertion_sort.rs @@ -8,22 +8,22 @@ use hello_algo_rust::include::print_util; /* Insertion sort */ fn insertion_sort(nums: &mut [i32]) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for i in 1..nums.len() { let (base, mut j) = (nums[i], (i - 1) as i32); - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while j >= 0 && nums[j as usize] > base { - nums[(j + 1) as usize] = nums[j as usize]; // Move nums[j] to the right by one position + nums[(j + 1) as usize] = nums[j as usize]; // Shift nums[j] one position to the right j -= 1; } - nums[(j + 1) as usize] = base; // Assign base to the correct position + nums[(j + 1) as usize] = base; // Place base at the correct position } } -/* Driver Code */ +/* Driver code */ fn main() { let mut nums = [4, 1, 3, 1, 5, 2]; insertion_sort(&mut nums); - print!("After insertion sort completes, nums = "); + print!("After insertion sort, nums = "); print_util::print_array(&nums); } diff --git a/en/codes/rust/chapter_sorting/merge_sort.rs b/en/codes/rust/chapter_sorting/merge_sort.rs index bbef475be..b2945fadf 100644 --- a/en/codes/rust/chapter_sorting/merge_sort.rs +++ b/en/codes/rust/chapter_sorting/merge_sort.rs @@ -4,15 +4,15 @@ * Author: xBLACKICEx (xBLACKICEx@outlook.com) */ -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result let tmp_size = right - left + 1; let mut tmp = vec![0; tmp_size]; - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays let (mut i, mut j, mut k) = (left, mid + 1, 0); - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while i <= mid && j <= right { if nums[i] <= nums[j] { tmp[k] = nums[i]; @@ -34,7 +34,7 @@ fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { k += 1; j += 1; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for k in 0..tmp_size { nums[left + k] = tmp[k]; } @@ -42,21 +42,21 @@ fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { /* Merge sort */ fn merge_sort(nums: &mut [i32], left: usize, right: usize) { - // Termination condition + // Base case if left >= right { - return; // Terminate recursion when subarray length is 1 + return; // Stop recursing when the subarray has at most one element } - // Divide and conquer stage - let mid = left + (right - left) / 2; // Calculate midpoint - merge_sort(nums, left, mid); // Recursively process the left subarray - merge_sort(nums, mid + 1, right); // Recursively process the right subarray + // Divide phase + let mid = left + (right - left) / 2; // Calculate the midpoint + merge_sort(nums, left, mid); // Recurse on the left subarray + merge_sort(nums, mid + 1, right); // Recurse on the right subarray - // Merge stage + // Merge phase merge(nums, left, mid, right); } -/* Driver Code */ +/* Driver code */ fn main() { /* Merge sort */ let mut nums = [7, 3, 2, 6, 0, 1, 5, 4]; diff --git a/en/codes/rust/chapter_sorting/quick_sort.rs b/en/codes/rust/chapter_sorting/quick_sort.rs index a534307e8..f3f8d697e 100644 --- a/en/codes/rust/chapter_sorting/quick_sort.rs +++ b/en/codes/rust/chapter_sorting/quick_sort.rs @@ -8,38 +8,38 @@ struct QuickSort; impl QuickSort { - /* Sentinel partition */ + /* Two-pointer partitioning */ fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { // Use nums[left] as the pivot let (mut i, mut j) = (left, right); while i < j { while i < j && nums[j] >= nums[left] { - j -= 1; // Search from right to left for the first element smaller than the pivot + j -= 1; // Scan from right to left for the first element smaller than the pivot } while i < j && nums[i] <= nums[left] { - i += 1; // Search from left to right for the first element greater than the pivot + i += 1; // Scan from left to right for the first element larger than the pivot } nums.swap(i, j); // Swap these two elements } - nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays - i // Return the index of the pivot + nums.swap(i, left); // Swap the pivot into the boundary position between the two subarrays + i // Return the pivot index } /* Quick sort */ pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if left >= right { return; } - // Sentinel partition + // Two-pointer partitioning let pivot = Self::partition(nums, left as usize, right as usize) as i32; - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays Self::quick_sort(left, pivot - 1, nums); Self::quick_sort(pivot + 1, right, nums); } } -/* Quick sort (recursion depth optimization) */ +/* Quick sort (median pivot optimization) */ struct QuickSortMedian; impl QuickSortMedian { @@ -47,44 +47,44 @@ impl QuickSortMedian { fn median_three(nums: &mut [i32], left: usize, mid: usize, right: usize) -> usize { let (l, m, r) = (nums[left], nums[mid], nums[right]); if (l <= m && m <= r) || (r <= m && m <= l) { - return mid; // m is between l and r + return mid; // m lies between l and r } if (m <= l && l <= r) || (r <= l && l <= m) { - return left; // l is between m and r + return left; // l lies between m and r } right } - /* Sentinel partition (median of three) */ + /* Two-pointer partitioning (median of three) */ fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { // Select the median of three candidate elements let med = Self::median_three(nums, left, (left + right) / 2, right); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray nums.swap(left, med); // Use nums[left] as the pivot let (mut i, mut j) = (left, right); while i < j { while i < j && nums[j] >= nums[left] { - j -= 1; // Search from right to left for the first element smaller than the pivot + j -= 1; // Scan from right to left for the first element smaller than the pivot } while i < j && nums[i] <= nums[left] { - i += 1; // Search from left to right for the first element greater than the pivot + i += 1; // Scan from left to right for the first element larger than the pivot } nums.swap(i, j); // Swap these two elements } - nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays - i // Return the index of the pivot + nums.swap(i, left); // Swap the pivot into the boundary position between the two subarrays + i // Return the pivot index } /* Quick sort */ pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if left >= right { return; } - // Sentinel partition + // Two-pointer partitioning let pivot = Self::partition(nums, left as usize, right as usize) as i32; - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays Self::quick_sort(left, pivot - 1, nums); Self::quick_sort(pivot + 1, right, nums); } @@ -94,55 +94,55 @@ impl QuickSortMedian { struct QuickSortTailCall; impl QuickSortTailCall { - /* Sentinel partition */ + /* Two-pointer partitioning */ fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { // Use nums[left] as the pivot let (mut i, mut j) = (left, right); while i < j { while i < j && nums[j] >= nums[left] { - j -= 1; // Search from right to left for the first element smaller than the pivot + j -= 1; // Scan from right to left for the first element smaller than the pivot } while i < j && nums[i] <= nums[left] { - i += 1; // Search from left to right for the first element greater than the pivot + i += 1; // Scan from left to right for the first element larger than the pivot } nums.swap(i, j); // Swap these two elements } - nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays - i // Return the index of the pivot + nums.swap(i, left); // Swap the pivot into the boundary position between the two subarrays + i // Return the pivot index } /* Quick sort (recursion depth optimization) */ pub fn quick_sort(mut left: i32, mut right: i32, nums: &mut [i32]) { - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while left < right { - // Sentinel partition operation + // Perform two-pointer partitioning let pivot = Self::partition(nums, left as usize, right as usize) as i32; - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if pivot - left < right - pivot { Self::quick_sort(left, pivot - 1, nums); // Recursively sort the left subarray - left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] } else { Self::quick_sort(pivot + 1, right, nums); // Recursively sort the right subarray - right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] } } } } -/* Driver Code */ +/* Driver code */ fn main() { /* Quick sort */ let mut nums = [2, 4, 1, 0, 3, 5]; QuickSort::quick_sort(0, (nums.len() - 1) as i32, &mut nums); println!("After quick sort, nums = {:?}", nums); - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ let mut nums = [2, 4, 1, 0, 3, 5]; QuickSortMedian::quick_sort(0, (nums.len() - 1) as i32, &mut nums); - println!("After quick sort (median pivot optimization), nums = {:?}", nums); + println!("After quick sort with median-of-three pivot selection, nums = {:?}", nums); /* Quick sort (recursion depth optimization) */ let mut nums = [2, 4, 1, 0, 3, 5]; QuickSortTailCall::quick_sort(0, (nums.len() - 1) as i32, &mut nums); - println!("After quick sort (recursion depth optimization), nums = {:?}", nums); + println!("After quick sort with recursion depth optimization, nums = {:?}", nums); } diff --git a/en/codes/rust/chapter_sorting/radix_sort.rs b/en/codes/rust/chapter_sorting/radix_sort.rs index 8c06a7939..511ad6ee5 100644 --- a/en/codes/rust/chapter_sorting/radix_sort.rs +++ b/en/codes/rust/chapter_sorting/radix_sort.rs @@ -6,43 +6,43 @@ use hello_algo_rust::include::print_util; -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ fn digit(num: i32, exp: i32) -> usize { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return ((num / exp) % 10) as usize; } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ fn counting_sort_digit(nums: &mut [i32], exp: i32) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 let mut counter = [0; 10]; let n = nums.len(); - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for i in 0..n { - let d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d - counter[d] += 1; // Count the occurrence of digit d + let d = digit(nums[i], exp); // Get the k-th digit of nums[i] and store it in d + counter[d] += 1; // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for i in 1..10 { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts let mut res = vec![0; n]; for i in (0..n).rev() { let d = digit(nums[i], exp); - let j = counter[d] - 1; // Get the index j for d in the array + let j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d] -= 1; // Decrease the count of d by 1 + counter[d] -= 1; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result nums.copy_from_slice(&res); } /* Radix sort */ fn radix_sort(nums: &mut [i32]) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits let m = *nums.into_iter().max().unwrap(); - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant let mut exp = 1; while exp <= m { counting_sort_digit(nums, exp); @@ -50,7 +50,7 @@ fn radix_sort(nums: &mut [i32]) { } } -/* Driver Code */ +/* Driver code */ fn main() { // Radix sort let mut nums = [ @@ -58,6 +58,6 @@ fn main() { 63832996, ]; radix_sort(&mut nums); - print!("After radix sort completes, nums = "); + print!("After radix sort, nums = "); print_util::print_array(&nums); } diff --git a/en/codes/rust/chapter_sorting/selection_sort.rs b/en/codes/rust/chapter_sorting/selection_sort.rs index b03e0f399..26ddf52fc 100644 --- a/en/codes/rust/chapter_sorting/selection_sort.rs +++ b/en/codes/rust/chapter_sorting/selection_sort.rs @@ -12,21 +12,21 @@ fn selection_sort(nums: &mut [i32]) { return; } let n = nums.len(); - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for i in 0..n - 1 { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range let mut k = i; for j in i + 1..n { if nums[j] < nums[k] { k = j; // Record the index of the smallest element } } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range nums.swap(i, k); } } -/* Driver Code */ +/* Driver code */ pub fn main() { let mut nums = [4, 1, 3, 1, 5, 2]; selection_sort(&mut nums); diff --git a/en/codes/rust/chapter_stack_and_queue/array_deque.rs b/en/codes/rust/chapter_stack_and_queue/array_deque.rs index 02c5f18be..1ac1dca6b 100644 --- a/en/codes/rust/chapter_stack_and_queue/array_deque.rs +++ b/en/codes/rust/chapter_stack_and_queue/array_deque.rs @@ -4,11 +4,11 @@ * Author: codingonion (coderonion@gmail.com) */ use hello_algo_rust::include::print_util; -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ struct ArrayDeque { - nums: Vec, // Array for storing double-ended queue elements - front: usize, // Front pointer, points to the front of the queue element - que_size: usize, // Double-ended queue length + nums: Vec, // Array for storing deque elements + front: usize, // Front index, identifying the first element + que_size: usize, // Deque size } impl ArrayDeque { @@ -21,73 +21,73 @@ impl ArrayDeque { } } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ pub fn capacity(&self) -> usize { self.nums.len() } - /* Get the length of the double-ended queue */ + /* Get the deque size */ pub fn size(&self) -> usize { self.que_size } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ pub fn is_empty(&self) -> bool { self.que_size == 0 } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ fn index(&self, i: i32) -> usize { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end ((i + self.capacity() as i32) % self.capacity() as i32) as usize } - /* Front of the queue enqueue */ + /* Enqueue at the front */ pub fn push_first(&mut self, num: T) { if self.que_size == self.capacity() { - println!("Double-ended queue is full"); + println!("Deque is full"); return; } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front index one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array self.front = self.index(self.front as i32 - 1); - // Add num to front of queue + // Add num to the front self.nums[self.front] = num; self.que_size += 1; } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ pub fn push_last(&mut self, num: T) { if self.que_size == self.capacity() { - println!("Double-ended queue is full"); + println!("Deque is full"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear index, one position past the last element let rear = self.index(self.front as i32 + self.que_size as i32); - // Front pointer moves one position backward + // Add num to the rear self.nums[rear] = num; self.que_size += 1; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ fn pop_first(&mut self) -> T { let num = self.peek_first(); - // Move front pointer backward by one position + // Advance the front index by one position self.front = self.index(self.front as i32 + 1); self.que_size -= 1; num } - /* Access rear of the queue element */ + /* Dequeue from the rear */ fn pop_last(&mut self) -> T { let num = self.peek_last(); self.que_size -= 1; num } - /* Return list for printing */ + /* Access the front element */ fn peek_first(&self) -> T { if self.is_empty() { panic!("Deque is empty") @@ -95,19 +95,19 @@ impl ArrayDeque { self.nums[self.front] } - /* Driver Code */ + /* Access the rear element */ fn peek_last(&self) -> T { if self.is_empty() { panic!("Deque is empty") }; - // Initialize double-ended queue + // Calculate the index of the last element let last = self.index(self.front as i32 + self.que_size as i32 - 1); self.nums[last] } - /* Return array for printing */ + /* Return a vector for printing */ fn to_array(&self) -> Vec { - // Elements enqueue + // Convert only the list elements within the occupied range let mut res = vec![T::default(); self.que_size]; let mut j = self.front; for i in 0..self.que_size { @@ -118,43 +118,43 @@ impl ArrayDeque { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ let mut deque = ArrayDeque::new(10); deque.push_last(3); deque.push_last(2); deque.push_last(5); - print!("Double-ended queue deque = "); + print!("Deque deque = "); print_util::print_array(&deque.to_array()); - /* Update element */ + /* Access an element */ let peek_first = deque.peek_first(); print!("\nFront element peek_first = {}", peek_first); let peek_last = deque.peek_last(); print!("\nRear element peek_last = {}", peek_last); - /* Elements enqueue */ + /* Enqueue elements */ deque.push_last(4); - print!("\nAfter element 4 enqueues at rear, deque = "); + print!("\nAfter enqueuing element 4 at the rear, deque = "); print_util::print_array(&deque.to_array()); deque.push_first(1); - print!("\nAfter element 1 enqueues at front, deque = "); + print!("\nAfter enqueuing element 1 at the front, deque = "); print_util::print_array(&deque.to_array()); - /* Element dequeue */ + /* Dequeue an element */ let pop_last = deque.pop_last(); - print!("\nDequeue rear element = {}, after dequeue deque = ", pop_last); + print!("\nElement removed from the rear = {}; after removal, deque = ", pop_last); print_util::print_array(&deque.to_array()); let pop_first = deque.pop_first(); - print!("\nDequeue front element = {}, after dequeue deque = ", pop_first); + print!("\nElement removed from the front = {}; after removal, deque = ", pop_first); print_util::print_array(&deque.to_array()); - /* Get the length of the double-ended queue */ + /* Get the deque size */ let size = deque.size(); - print!("\nDeque length size = {}", size); + print!("\nDeque size = {}", size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ let is_empty = deque.is_empty(); - print!("\nIs deque empty = {}", is_empty); + print!("\nIs the deque empty? {}", is_empty); } diff --git a/en/codes/rust/chapter_stack_and_queue/array_queue.rs b/en/codes/rust/chapter_stack_and_queue/array_queue.rs index 7ad2cbd59..8ee72b1a0 100644 --- a/en/codes/rust/chapter_stack_and_queue/array_queue.rs +++ b/en/codes/rust/chapter_stack_and_queue/array_queue.rs @@ -4,11 +4,11 @@ * Author: WSL0809 (wslzzy@outlook.com) */ -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ struct ArrayQueue { - nums: Vec, // Array for storing queue elements - front: i32, // Front pointer, points to the front of the queue element - que_size: i32, // Queue length + nums: Vec, // Array that stores the queue elements + front: i32, // Front index, identifying the first element + que_size: i32, // Queue size que_capacity: i32, // Queue capacity } @@ -23,17 +23,17 @@ impl ArrayQueue { } } - /* Get the capacity of the queue */ + /* Get the queue capacity */ fn capacity(&self) -> i32 { self.que_capacity } - /* Get the length of the queue */ + /* Get the queue size */ fn size(&self) -> i32 { self.que_size } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ fn is_empty(&self) -> bool { self.que_size == 0 } @@ -44,10 +44,10 @@ impl ArrayQueue { println!("Queue is full"); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear index, one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array let rear = (self.front + self.que_size) % self.que_capacity; - // Front pointer moves one position backward + // Add num to the rear self.nums[rear as usize] = num; self.que_size += 1; } @@ -55,21 +55,21 @@ impl ArrayQueue { /* Dequeue */ fn pop(&mut self) -> T { let num = self.peek(); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front index by one position, wrapping to the beginning if it passes the end self.front = (self.front + 1) % self.que_capacity; self.que_size -= 1; num } - /* Return list for printing */ + /* Access the front element */ fn peek(&self) -> T { if self.is_empty() { - panic!("index out of bounds"); + panic!("Index out of bounds"); } self.nums[self.front as usize] } - /* Return array */ + /* Return a vector */ fn to_vector(&self) -> Vec { let cap = self.que_capacity; let mut j = self.front; @@ -82,13 +82,13 @@ impl ArrayQueue { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Access front of the queue element */ + /* Initialize the queue */ let capacity = 10; let mut queue = ArrayQueue::new(capacity); - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -96,30 +96,30 @@ fn main() { queue.push(4); println!("Queue queue = {:?}", queue.to_vector()); - /* Return list for printing */ + /* Access the front element */ let peek = queue.peek(); println!("Front element peek = {}", peek); - /* Element dequeue */ + /* Dequeue an element */ let pop = queue.pop(); println!( - "Dequeue element pop = {:?}, after dequeue queue = {:?}", + "Dequeued element pop = {:?}; after dequeueing, queue = {:?}", pop, queue.to_vector() ); - /* Get the length of the queue */ + /* Get the queue size */ let size = queue.size(); - println!("Queue length size = {}", size); + println!("Queue size = {}", size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ let is_empty = queue.is_empty(); - println!("Is queue empty = {}", is_empty); + println!("Is the queue empty? {}", is_empty); - /* Test circular array */ + /* Test the circular array */ for i in 0..10 { queue.push(i); queue.pop(); - println!("After round {:?} of enqueue + dequeue, queue = {:?}", i, queue.to_vector()); + println!("After enqueueing and dequeueing in round {:?}, queue = {:?}", i, queue.to_vector()); } } diff --git a/en/codes/rust/chapter_stack_and_queue/array_stack.rs b/en/codes/rust/chapter_stack_and_queue/array_stack.rs index c4e91f2ea..dccbc58b2 100644 --- a/en/codes/rust/chapter_stack_and_queue/array_stack.rs +++ b/en/codes/rust/chapter_stack_and_queue/array_stack.rs @@ -6,25 +6,25 @@ use hello_algo_rust::include::print_util; -/* Stack based on array implementation */ +/* Stack implemented with an array */ struct ArrayStack { stack: Vec, } impl ArrayStack { - /* Access top of the stack element */ + /* Initialize the stack */ fn new() -> ArrayStack { ArrayStack:: { stack: Vec::::new(), } } - /* Get the length of the stack */ + /* Get the stack size */ fn size(&self) -> usize { self.stack.len() } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ fn is_empty(&self) -> bool { self.size() == 0 } @@ -39,7 +39,7 @@ impl ArrayStack { self.stack.pop() } - /* Return list for printing */ + /* Access the top element */ fn peek(&self) -> Option<&T> { if self.is_empty() { panic!("Stack is empty") @@ -47,18 +47,18 @@ impl ArrayStack { self.stack.last() } - /* Return &Vec */ + /* Return a reference to the vector */ fn to_array(&self) -> &Vec { &self.stack } } -/* Driver Code */ +/* Driver code */ fn main() { - // Access top of the stack element + // Initialize the stack let mut stack = ArrayStack::::new(); - // Elements push onto stack + // Push elements onto the stack stack.push(1); stack.push(3); stack.push(2); @@ -67,20 +67,20 @@ fn main() { print!("Stack stack = "); print_util::print_array(stack.to_array()); - // Return list for printing + //Access the top element let peek = stack.peek().unwrap(); print!("\nTop element peek = {}", peek); - // Element pop from stack + // Pop an element from the stack let pop = stack.pop().unwrap(); - print!("\nPop element pop = {pop}, after pop stack = "); + print!("\nPopped element pop = {pop}; after popping, stack = "); print_util::print_array(stack.to_array()); - // Get the length of the stack + // Get the stack size let size = stack.size(); - print!("\nStack length size = {size}"); + print!("\nStack size = {size}"); - // Check if empty + // Check whether it is empty let is_empty = stack.is_empty(); - print!("\nIs stack empty = {is_empty}"); + print!("\nIs the stack empty? {is_empty}"); } diff --git a/en/codes/rust/chapter_stack_and_queue/deque.rs b/en/codes/rust/chapter_stack_and_queue/deque.rs index 56d0f23f3..4e80571f4 100644 --- a/en/codes/rust/chapter_stack_and_queue/deque.rs +++ b/en/codes/rust/chapter_stack_and_queue/deque.rs @@ -7,43 +7,43 @@ use hello_algo_rust::include::print_util; use std::collections::VecDeque; -/* Driver Code */ +/* Driver code */ pub fn main() { - // Get the length of the double-ended queue + // Initialize the deque let mut deque: VecDeque = VecDeque::new(); deque.push_back(3); deque.push_back(2); deque.push_back(5); - print!("Double-ended queue deque = "); + print!("Deque deque = "); print_util::print_queue(&deque); - // Update element + // Access an element let peek_first = deque.front().unwrap(); - print!("\nFront element peekFirst = {peek_first}"); + print!("\nFront element peek_first = {peek_first}"); let peek_last = deque.back().unwrap(); - print!("\nRear element peekLast = {peek_last}"); + print!("\nRear element peek_last = {peek_last}"); - /* Elements enqueue */ + /* Enqueue elements */ deque.push_back(4); - print!("\nAfter element 4 enqueues at rear, deque = "); + print!("\nAfter enqueuing element 4 at the rear, deque = "); print_util::print_queue(&deque); deque.push_front(1); - print!("\nAfter element 1 enqueues at front, deque = "); + print!("\nAfter enqueuing element 1 at the front, deque = "); print_util::print_queue(&deque); - // Element dequeue + // Dequeue an element let pop_last = deque.pop_back().unwrap(); - print!("\nDequeue rear element = {pop_last}, after dequeue deque = "); + print!("\nElement removed from the rear = {pop_last}; after removal, deque = "); print_util::print_queue(&deque); let pop_first = deque.pop_front().unwrap(); - print!("\nDequeue front element = {pop_first}, after dequeue deque = "); + print!("\nElement removed from the front = {pop_first}; after removal, deque = "); print_util::print_queue(&deque); - // Get the length of the double-ended queue + // Get the deque size let size = deque.len(); - print!("\nDeque length size = {size}"); + print!("\nDeque size = {size}"); - // Check if the double-ended queue is empty + // Check whether the deque is empty let is_empty = deque.is_empty(); - print!("\nIs deque empty = {is_empty}"); + print!("\nIs the deque empty? {is_empty}"); } diff --git a/en/codes/rust/chapter_stack_and_queue/linkedlist_deque.rs b/en/codes/rust/chapter_stack_and_queue/linkedlist_deque.rs index 6a08c134c..c02bf2bce 100644 --- a/en/codes/rust/chapter_stack_and_queue/linkedlist_deque.rs +++ b/en/codes/rust/chapter_stack_and_queue/linkedlist_deque.rs @@ -12,8 +12,8 @@ use std::rc::Rc; /* Doubly linked list node */ pub struct ListNode { pub val: T, // Node value - pub next: Option>>>, // Successor node pointer - pub prev: Option>>>, // Predecessor node pointer + pub next: Option>>>, // Pointer to the next node + pub prev: Option>>>, // Pointer to the previous node } impl ListNode { @@ -26,12 +26,12 @@ impl ListNode { } } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ #[allow(dead_code)] pub struct LinkedListDeque { front: Option>>>, // Head node front rear: Option>>>, // Tail node rear - que_size: usize, // Length of the double-ended queue + que_size: usize, // Deque size } impl LinkedListDeque { @@ -43,12 +43,12 @@ impl LinkedListDeque { } } - /* Get the length of the double-ended queue */ + /* Get the deque size */ pub fn size(&self) -> usize { return self.que_size; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ pub fn is_empty(&self) -> bool { return self.que_size == 0; } @@ -56,7 +56,7 @@ impl LinkedListDeque { /* Enqueue operation */ fn push(&mut self, num: T, is_front: bool) { let node = ListNode::new(num); - // Front of the queue enqueue operation + // Enqueue at the front if is_front { match self.front.take() { // If the linked list is empty, make both front and rear point to node @@ -68,11 +68,11 @@ impl LinkedListDeque { Some(old_front) => { old_front.borrow_mut().prev = Some(node.clone()); node.borrow_mut().next = Some(old_front); - self.front = Some(node); // Update head node + self.front = Some(node); // Update the head node } } } - // Rear of the queue enqueue operation + // Enqueue at the rear else { match self.rear.take() { // If the linked list is empty, make both front and rear point to node @@ -84,84 +84,84 @@ impl LinkedListDeque { Some(old_rear) => { old_rear.borrow_mut().next = Some(node.clone()); node.borrow_mut().prev = Some(old_rear); - self.rear = Some(node); // Update tail node + self.rear = Some(node); // Update the tail node } } } - self.que_size += 1; // Update queue length + self.que_size += 1; // Update the deque size } - /* Front of the queue enqueue */ + /* Enqueue at the front */ pub fn push_first(&mut self, num: T) { self.push(num, true); } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ pub fn push_last(&mut self, num: T) { self.push(num, false); } /* Dequeue operation */ fn pop(&mut self, is_front: bool) -> Option { - // If queue is empty, return None directly + // If the deque is empty, return None immediately if self.is_empty() { return None; }; - // Temporarily store head node value + // Dequeue from the front if is_front { self.front.take().map(|old_front| { match old_front.borrow_mut().next.take() { Some(new_front) => { new_front.borrow_mut().prev.take(); - self.front = Some(new_front); // Update head node + self.front = Some(new_front); // Update the head node } None => { self.rear.take(); } } - self.que_size -= 1; // Update queue length + self.que_size -= 1; // Update the deque size old_front.borrow().val }) } - // Temporarily store tail node value + // Dequeue from the rear else { self.rear.take().map(|old_rear| { match old_rear.borrow_mut().prev.take() { Some(new_rear) => { new_rear.borrow_mut().next.take(); - self.rear = Some(new_rear); // Update tail node + self.rear = Some(new_rear); // Update the tail node } None => { self.front.take(); } } - self.que_size -= 1; // Update queue length + self.que_size -= 1; // Update the deque size old_rear.borrow().val }) } } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ pub fn pop_first(&mut self) -> Option { return self.pop(true); } - /* Access rear of the queue element */ + /* Dequeue from the rear */ pub fn pop_last(&mut self) -> Option { return self.pop(false); } - /* Return list for printing */ + /* Access the front element */ pub fn peek_first(&self) -> Option<&Rc>>> { self.front.as_ref() } - /* Driver Code */ + /* Access the rear element */ pub fn peek_last(&self) -> Option<&Rc>>> { self.rear.as_ref() } - /* Return array for printing */ + /* Return a vector for printing */ pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { let mut res: Vec = Vec::new(); fn recur(cur: Option<&Rc>>>, res: &mut Vec) { @@ -176,43 +176,43 @@ impl LinkedListDeque { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ let mut deque = LinkedListDeque::new(); deque.push_last(3); deque.push_last(2); deque.push_last(5); - print!("Double-ended queue deque = "); + print!("Deque deque = "); print_util::print_array(&deque.to_array(deque.peek_first())); - /* Update element */ + /* Access an element */ let peek_first = deque.peek_first().unwrap().borrow().val; print!("\nFront element peek_first = {}", peek_first); let peek_last = deque.peek_last().unwrap().borrow().val; print!("\nRear element peek_last = {}", peek_last); - /* Elements enqueue */ + /* Enqueue elements */ deque.push_last(4); - print!("\nAfter element 4 enqueues at rear, deque = "); + print!("\nAfter enqueuing element 4 at the rear, deque = "); print_util::print_array(&deque.to_array(deque.peek_first())); deque.push_first(1); - print!("\nAfter element 1 enqueues at front, deque = "); + print!("\nAfter enqueuing element 1 at the front, deque = "); print_util::print_array(&deque.to_array(deque.peek_first())); - /* Element dequeue */ + /* Dequeue an element */ let pop_last = deque.pop_last().unwrap(); - print!("\nDequeue rear element = {}, after dequeue deque = ", pop_last); + print!("\nElement removed from the rear = {}; after removal, deque = ", pop_last); print_util::print_array(&deque.to_array(deque.peek_first())); let pop_first = deque.pop_first().unwrap(); - print!("\nDequeue front element = {}, after dequeue deque = ", pop_first); + print!("\nElement removed from the front = {}; after removal, deque = ", pop_first); print_util::print_array(&deque.to_array(deque.peek_first())); - /* Get the length of the double-ended queue */ + /* Get the deque size */ let size = deque.size(); - print!("\nDeque length size = {}", size); + print!("\nDeque size = {}", size); - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ let is_empty = deque.is_empty(); - print!("\nIs deque empty = {}", is_empty); + print!("\nIs the deque empty? {}", is_empty); } diff --git a/en/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs b/en/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs index ca7d2351f..1d2f40f6c 100644 --- a/en/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs +++ b/en/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs @@ -9,12 +9,12 @@ use hello_algo_rust::include::{print_util, ListNode}; use std::cell::RefCell; use std::rc::Rc; -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ #[allow(dead_code)] pub struct LinkedListQueue { front: Option>>>, // Head node front rear: Option>>>, // Tail node rear - que_size: usize, // Queue length + que_size: usize, // Queue size } impl LinkedListQueue { @@ -26,12 +26,12 @@ impl LinkedListQueue { } } - /* Get the length of the queue */ + /* Get the queue size */ pub fn size(&self) -> usize { return self.que_size; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ pub fn is_empty(&self) -> bool { return self.que_size == 0; } @@ -41,12 +41,12 @@ impl LinkedListQueue { // Add num after the tail node let new_rear = ListNode::new(num); match self.rear.take() { - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node Some(old_rear) => { old_rear.borrow_mut().next = Some(new_rear.clone()); self.rear = Some(new_rear); } - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node None => { self.front = Some(new_rear.clone()); self.rear = Some(new_rear); @@ -71,12 +71,12 @@ impl LinkedListQueue { }) } - /* Return list for printing */ + /* Access the front element */ pub fn peek(&self) -> Option<&Rc>>> { self.front.as_ref() } - /* Convert linked list to Array and return */ + /* Convert the linked list to a vector and return it */ pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { let mut res: Vec = Vec::new(); @@ -93,12 +93,12 @@ impl LinkedListQueue { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Access front of the queue element */ + /* Initialize the queue */ let mut queue = LinkedListQueue::new(); - /* Elements enqueue */ + /* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -107,20 +107,20 @@ fn main() { print!("Queue queue = "); print_util::print_array(&queue.to_array(queue.peek())); - /* Return list for printing */ + /* Access the front element */ let peek = queue.peek().unwrap().borrow().val; print!("\nFront element peek = {}", peek); - /* Element dequeue */ + /* Dequeue an element */ let pop = queue.pop().unwrap(); - print!("\nDequeue element pop = {}, after dequeue queue = ", pop); + print!("\nDequeued element pop = {}; after dequeueing, queue = ", pop); print_util::print_array(&queue.to_array(queue.peek())); - /* Get the length of the queue */ + /* Get the queue size */ let size = queue.size(); - print!("\nQueue length size = {}", size); + print!("\nQueue size = {}", size); - /* Check if the queue is empty */ + /* Check whether the queue is empty */ let is_empty = queue.is_empty(); - print!("\nIs queue empty = {}", is_empty); + print!("\nIs the queue empty? {}", is_empty); } diff --git a/en/codes/rust/chapter_stack_and_queue/linkedlist_stack.rs b/en/codes/rust/chapter_stack_and_queue/linkedlist_stack.rs index cc4d73f79..ab293379c 100644 --- a/en/codes/rust/chapter_stack_and_queue/linkedlist_stack.rs +++ b/en/codes/rust/chapter_stack_and_queue/linkedlist_stack.rs @@ -9,11 +9,11 @@ use hello_algo_rust::include::{print_util, ListNode}; use std::cell::RefCell; use std::rc::Rc; -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ #[allow(dead_code)] pub struct LinkedListStack { - stack_peek: Option>>>, // Use head node as stack top - stk_size: usize, // Stack length + stack_peek: Option>>>, // Use the head node as the stack top + stk_size: usize, // Stack size } impl LinkedListStack { @@ -24,12 +24,12 @@ impl LinkedListStack { } } - /* Get the length of the stack */ + /* Get the stack size */ pub fn size(&self) -> usize { return self.stk_size; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ pub fn is_empty(&self) -> bool { return self.size() == 0; } @@ -52,12 +52,12 @@ impl LinkedListStack { }) } - /* Return list for printing */ + /* Access the top element */ pub fn peek(&self) -> Option<&Rc>>> { self.stack_peek.as_ref() } - /* Convert List to Array and return */ + /* Convert the linked list to a vector and return it */ pub fn to_array(&self) -> Vec { fn _to_array(head: Option<&Rc>>>) -> Vec { if let Some(node) = head { @@ -72,12 +72,12 @@ impl LinkedListStack { } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Access top of the stack element */ + /* Initialize the stack */ let mut stack = LinkedListStack::new(); - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -86,20 +86,20 @@ fn main() { print!("Stack stack = "); print_util::print_array(&stack.to_array()); - /* Return list for printing */ + /* Access the top element */ let peek = stack.peek().unwrap().borrow().val; print!("\nTop element peek = {}", peek); - /* Element pop from stack */ + /* Pop an element from the stack */ let pop = stack.pop().unwrap(); - print!("\nPop element pop = {}, after pop stack = ", pop); + print!("\nPopped element pop = {}; after popping, stack = ", pop); print_util::print_array(&stack.to_array()); - /* Get the length of the stack */ + /* Get the stack size */ let size = stack.size(); - print!("\nStack length size = {}", size); + print!("\nStack size = {}", size); - /* Check if empty */ + /* Check whether it is empty */ let is_empty = stack.is_empty(); - print!("\nIs stack empty = {}", is_empty); + print!("\nIs the stack empty? {}", is_empty); } diff --git a/en/codes/rust/chapter_stack_and_queue/queue.rs b/en/codes/rust/chapter_stack_and_queue/queue.rs index 208df0ede..8dd68088e 100644 --- a/en/codes/rust/chapter_stack_and_queue/queue.rs +++ b/en/codes/rust/chapter_stack_and_queue/queue.rs @@ -8,12 +8,12 @@ use hello_algo_rust::include::print_util; use std::collections::VecDeque; -/* Driver Code */ +/* Driver code */ pub fn main() { - // Access front of the queue element + // Initialize the queue let mut queue: VecDeque = VecDeque::new(); - // Elements enqueue + // Enqueue elements queue.push_back(1); queue.push_back(3); queue.push_back(2); @@ -22,20 +22,20 @@ pub fn main() { print!("Queue queue = "); print_util::print_queue(&queue); - // Return list for printing + // Access the front element let peek = queue.front().unwrap(); println!("\nFront element peek = {peek}"); - // Element dequeue + // Dequeue an element let pop = queue.pop_front().unwrap(); - print!("Dequeue element pop = {pop}, after dequeue queue = "); + print!("Dequeued element pop = {pop}; after dequeueing, queue = "); print_util::print_queue(&queue); - // Get the length of the queue + // Get the queue size let size = queue.len(); - print!("\nQueue length size = {size}"); + print!("\nQueue size = {size}"); - // Check if the queue is empty + // Check whether the queue is empty let is_empty = queue.is_empty(); - print!("\nIs queue empty = {is_empty}"); + print!("\nIs the queue empty? {is_empty}"); } diff --git a/en/codes/rust/chapter_stack_and_queue/stack.rs b/en/codes/rust/chapter_stack_and_queue/stack.rs index a4a4bb2a1..64663b578 100644 --- a/en/codes/rust/chapter_stack_and_queue/stack.rs +++ b/en/codes/rust/chapter_stack_and_queue/stack.rs @@ -6,13 +6,13 @@ use hello_algo_rust::include::print_util; -/* Driver Code */ +/* Driver code */ pub fn main() { - // Access top of the stack element - // In Rust, it's recommended to use Vec as a stack + // Initialize the stack + // In Rust, Vec is the recommended type for implementing a stack let mut stack: Vec = Vec::new(); - // Elements push onto stack + // Push elements onto the stack stack.push(1); stack.push(3); stack.push(2); @@ -21,20 +21,20 @@ pub fn main() { print!("Stack stack = "); print_util::print_array(&stack); - // Return list for printing + // Access the top element let peek = stack.last().unwrap(); print!("\nTop element peek = {peek}"); - // Element pop from stack + // Pop an element from the stack let pop = stack.pop().unwrap(); - print!("\nPop element pop = {pop}, after pop stack = "); + print!("\nPopped element pop = {pop}; after popping, stack = "); print_util::print_array(&stack); - // Get the length of the stack + // Get the stack size let size = stack.len(); - print!("\nStack length size = {size}"); + print!("\nStack size = {size}"); - // Check if the stack is empty + // Check whether the stack is empty let is_empty = stack.is_empty(); - print!("\nIs stack empty = {is_empty}"); + print!("\nIs the stack empty? {is_empty}"); } diff --git a/en/codes/rust/chapter_tree/array_binary_tree.rs b/en/codes/rust/chapter_tree/array_binary_tree.rs index bfed20915..207f1214c 100644 --- a/en/codes/rust/chapter_tree/array_binary_tree.rs +++ b/en/codes/rust/chapter_tree/array_binary_tree.rs @@ -6,7 +6,7 @@ use hello_algo_rust::include::{print_util, tree_node}; -/* Binary tree class represented by array */ +/* Binary tree type using an array representation */ struct ArrayBinaryTree { tree: Vec>, } @@ -17,14 +17,14 @@ impl ArrayBinaryTree { Self { tree: arr } } - /* List capacity */ + /* Number of positions in the array */ fn size(&self) -> i32 { self.tree.len() as i32 } - /* Get value of node at index i */ + /* Get the value of the node at index i */ fn val(&self, i: i32) -> Option { - // If index is out of bounds, return None, representing empty position + // If the index is out of bounds, return None to represent an empty position if i < 0 || i >= self.size() { None } else { @@ -32,17 +32,17 @@ impl ArrayBinaryTree { } } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ fn left(&self, i: i32) -> i32 { 2 * i + 1 } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ fn right(&self, i: i32) -> i32 { 2 * i + 2 } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ fn parent(&self, i: i32) -> i32 { (i - 1) / 2 } @@ -96,10 +96,10 @@ impl ArrayBinaryTree { } } -/* Driver Code */ +/* Driver code */ fn main() { - // Initialize binary tree - // Here we use a function to generate a binary tree directly from an array + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array let arr = vec![ Some(1), Some(2), @@ -119,8 +119,8 @@ fn main() { ]; let root = tree_node::vec_to_tree(arr.clone()).unwrap(); - println!("\nInitialize binary tree\n"); - println!("Array representation of binary tree:"); + println!("\nInitialize the binary tree\n"); + println!("Array representation of the binary tree:"); println!( "[{}]", arr.iter() @@ -132,19 +132,19 @@ fn main() { .collect::>() .join(", ") ); - println!("Linked list representation of binary tree:"); + println!("Linked representation of the binary tree:"); print_util::print_tree(&root); - // Binary tree class represented by array + // Binary tree type using an array representation let abt = ArrayBinaryTree::new(arr); - // Access node + // Access a node let i = 1; let l = abt.left(i); let r = abt.right(i); let p = abt.parent(i); println!( - "\nCurrent node index is {}, value is {}", + "\nCurrent node index: {}, value: {}", i, if let Some(val) = abt.val(i) { format!("{val}") @@ -153,7 +153,7 @@ fn main() { } ); println!( - "Left child index is {}, value is {}", + "Left child index: {}, value: {}", l, if let Some(val) = abt.val(l) { format!("{val}") @@ -162,7 +162,7 @@ fn main() { } ); println!( - "Right child index is {}, value is {}", + "Right child index: {}, value: {}", r, if let Some(val) = abt.val(r) { format!("{val}") @@ -171,7 +171,7 @@ fn main() { } ); println!( - "Parent node index is {}, value is {}", + "Parent index: {}, value: {}", p, if let Some(val) = abt.val(p) { format!("{val}") @@ -180,13 +180,13 @@ fn main() { } ); - // Traverse tree + // Traverse the tree let mut res = abt.level_order(); - println!("\nLevel-order traversal is: {:?}", res); + println!("\nLevel-order traversal: {:?}", res); res = abt.pre_order(); - println!("Pre-order traversal is: {:?}", res); + println!("Preorder traversal: {:?}", res); res = abt.in_order(); - println!("In-order traversal is: {:?}", res); + println!("Inorder traversal: {:?}", res); res = abt.post_order(); - println!("Post-order traversal is: {:?}", res); + println!("Postorder traversal: {:?}", res); } diff --git a/en/codes/rust/chapter_tree/avl_tree.rs b/en/codes/rust/chapter_tree/avl_tree.rs index 3df88424d..261ef9854 100644 --- a/en/codes/rust/chapter_tree/avl_tree.rs +++ b/en/codes/rust/chapter_tree/avl_tree.rs @@ -23,29 +23,29 @@ impl AVLTree { Self { root: None } } - /* Get node height */ + /* Get the node height */ fn height(node: OptionTreeNodeRc) -> i32 { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 match node { Some(node) => node.borrow().height, None => -1, } } - /* Update node height */ + /* Update the node height */ fn update_height(node: OptionTreeNodeRc) { if let Some(node) = node { let left = node.borrow().left.clone(); let right = node.borrow().right.clone(); - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node.borrow_mut().height = std::cmp::max(Self::height(left), Self::height(right)) + 1; } } - /* Get balance factor */ + /* Get the balance factor */ fn balance_factor(node: OptionTreeNodeRc) -> i32 { match node { - // Empty node balance factor is 0 + // A null node has balance factor 0 None => 0, // Node balance factor = left subtree height - right subtree height Some(node) => { @@ -54,89 +54,89 @@ impl AVLTree { } } - /* Right rotation operation */ + /* Right rotation */ fn right_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { match node { Some(node) => { let child = node.borrow().left.clone().unwrap(); let grand_child = child.borrow().right.clone(); - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.borrow_mut().right = Some(node.clone()); node.borrow_mut().left = grand_child; - // Update node height + // Update the node height Self::update_height(Some(node)); Self::update_height(Some(child.clone())); - // Return root node of subtree after rotation + // Return the root of the rotated subtree Some(child) } None => None, } } - /* Left rotation operation */ + /* Left rotation */ fn left_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { match node { Some(node) => { let child = node.borrow().right.clone().unwrap(); let grand_child = child.borrow().left.clone(); - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.borrow_mut().left = Some(node.clone()); node.borrow_mut().right = grand_child; - // Update node height + // Update the node height Self::update_height(Some(node)); Self::update_height(Some(child.clone())); - // Return root node of subtree after rotation + // Return the root of the rotated subtree Some(child) } None => None, } } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ fn rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { - // Get balance factor of node + // Get the balance factor of node let balance_factor = Self::balance_factor(node.clone()); - // Left-leaning tree + // Left-heavy tree if balance_factor > 1 { let node = node.unwrap(); if Self::balance_factor(node.borrow().left.clone()) >= 0 { - // Right rotation + // Rotate right Self::right_rotate(Some(node)) } else { - // First left rotation then right rotation + // Rotate left, then right let left = node.borrow().left.clone(); node.borrow_mut().left = Self::left_rotate(left); Self::right_rotate(Some(node)) } } - // Right-leaning tree + // Right-heavy tree else if balance_factor < -1 { let node = node.unwrap(); if Self::balance_factor(node.borrow().right.clone()) <= 0 { - // Left rotation + // Rotate left Self::left_rotate(Some(node)) } else { - // First right rotation then left rotation + // Rotate right, then left let right = node.borrow().right.clone(); node.borrow_mut().right = Self::right_rotate(right); Self::left_rotate(Some(node)) } } else { - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating node } } - /* Insert node */ + /* Insert a node */ fn insert(&mut self, val: i32) { self.root = Self::insert_helper(self.root.clone(), val); } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ fn insert_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { match node { Some(mut node) => { - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ match { let node_val = node.borrow().val; node_val @@ -152,30 +152,30 @@ impl AVLTree { node.borrow_mut().right = Self::insert_helper(right, val); } Ordering::Equal => { - return Some(node); // Duplicate node not inserted, return directly + return Some(node); // Return without inserting a duplicate node } } - Self::update_height(Some(node.clone())); // Update node height + Self::update_height(Some(node.clone())); // Update the node height - /* 2. Perform rotation operation to restore balance to this subtree */ + /* 2. Perform rotations to restore balance in this subtree */ node = Self::rotate(Some(node)).unwrap(); - // Return root node of subtree + // Return the root of the subtree Some(node) } None => Some(TreeNode::new(val)), } } - /* Remove node */ + /* Remove a node */ fn remove(&self, val: i32) { Self::remove_helper(self.root.clone(), val); } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ fn remove_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { match node { Some(mut node) => { - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if val < node.borrow().val { let left = node.borrow().left.clone(); node.borrow_mut().left = Self::remove_helper(left, val); @@ -189,15 +189,15 @@ impl AVLTree { node.borrow().right.clone() }; match child { - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return None => { return None; } - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly Some(child) => node = child, } } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value let mut temp = node.borrow().right.clone().unwrap(); loop { let temp_left = temp.borrow().left.clone(); @@ -210,61 +210,61 @@ impl AVLTree { node.borrow_mut().right = Self::remove_helper(right, temp.borrow().val); node.borrow_mut().val = temp.borrow().val; } - Self::update_height(Some(node.clone())); // Update node height + Self::update_height(Some(node.clone())); // Update the node height - /* 2. Perform rotation operation to restore balance to this subtree */ + /* 2. Perform rotations to restore balance in this subtree */ node = Self::rotate(Some(node)).unwrap(); - // Return root node of subtree + // Return the root of the subtree Some(node) } None => None, } } - /* Search node */ + /* Find a node */ fn search(&self, val: i32) -> OptionTreeNodeRc { let mut cur = self.root.clone(); - // Loop search, exit after passing leaf node + // Search while cur refers to a node while let Some(current) = cur.clone() { match current.borrow().val.cmp(&val) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur Ordering::Less => { cur = current.borrow().right.clone(); } - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur Ordering::Greater => { cur = current.borrow().left.clone(); } - // Found target node, exit loop + // The target node was found; exit the loop Ordering::Equal => { break; } } } - // Return target node + // Return the matching node, or None if no match was found cur } } -/* Driver Code */ +/* Driver code */ fn main() { fn test_insert(tree: &mut AVLTree, val: i32) { tree.insert(val); - println!("\nAfter inserting node {}, AVL tree is", val); + println!("\nAVL tree after inserting node {}:", val); print_util::print_tree(&tree.root.clone().unwrap()); } fn test_remove(tree: &mut AVLTree, val: i32) { tree.remove(val); - println!("\nAfter deleting node {}, AVL tree is", val); + println!("\nAVL tree after removing node {}:", val); print_util::print_tree(&tree.root.clone().unwrap()); } - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ let mut avl_tree = AVLTree::new(); - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted test_insert(&mut avl_tree, 1); test_insert(&mut avl_tree, 2); test_insert(&mut avl_tree, 3); @@ -276,20 +276,20 @@ fn main() { test_insert(&mut avl_tree, 10); test_insert(&mut avl_tree, 6); - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ test_insert(&mut avl_tree, 7); - /* Remove node */ - // Delete node with degree 1 - test_remove(&mut avl_tree, 8); // Delete node with degree 2 - test_remove(&mut avl_tree, 5); // Remove node with degree 1 - test_remove(&mut avl_tree, 4); // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + test_remove(&mut avl_tree, 8); // Remove a node with 0 children + test_remove(&mut avl_tree, 5); // Remove a node with 1 child + test_remove(&mut avl_tree, 4); // Remove a node with 2 children - /* Search node */ + /* Look up a node */ let node = avl_tree.search(7); if let Some(node) = node { println!( - "\nFound node object is {:?}, node value = {}", + "\nFound node: {:?}, node value = {}", &*node.borrow(), node.borrow().val ); diff --git a/en/codes/rust/chapter_tree/binary_search_tree.rs b/en/codes/rust/chapter_tree/binary_search_tree.rs index 96ca2478a..977dcd767 100644 --- a/en/codes/rust/chapter_tree/binary_search_tree.rs +++ b/en/codes/rust/chapter_tree/binary_search_tree.rs @@ -22,61 +22,61 @@ pub struct BinarySearchTree { impl BinarySearchTree { /* Constructor */ pub fn new() -> Self { - // Initialize empty tree + // Initialize an empty tree Self { root: None } } - /* Get binary tree root node */ + /* Get the root of the binary tree */ pub fn get_root(&self) -> OptionTreeNodeRc { self.root.clone() } - /* Search node */ + /* Find a node */ pub fn search(&self, num: i32) -> OptionTreeNodeRc { let mut cur = self.root.clone(); - // Loop search, exit after passing leaf node + // Search while cur refers to a node while let Some(node) = cur.clone() { match num.cmp(&node.borrow().val) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur Ordering::Greater => cur = node.borrow().right.clone(), - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur Ordering::Less => cur = node.borrow().left.clone(), - // Found target node, exit loop + // The target node was found; exit the loop Ordering::Equal => break, } } - // Return target node + // Return the matching node, or None if no match was found cur } - /* Insert node */ + /* Insert a node */ pub fn insert(&mut self, num: i32) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if self.root.is_none() { self.root = Some(TreeNode::new(num)); return; } let mut cur = self.root.clone(); let mut pre = None; - // Loop search, exit after passing leaf node + // Search while cur refers to a node while let Some(node) = cur.clone() { match num.cmp(&node.borrow().val) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found Ordering::Equal => return, - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur Ordering::Greater => { pre = cur.clone(); cur = node.borrow().right.clone(); } - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur Ordering::Less => { pre = cur.clone(); cur = node.borrow().left.clone(); } } } - // Insert node + // Insert a node let pre = pre.unwrap(); let node = Some(TreeNode::new(num)); if num > pre.borrow().val { @@ -86,44 +86,44 @@ impl BinarySearchTree { } } - /* Remove node */ + /* Remove a node */ pub fn remove(&mut self, num: i32) { - // If tree is empty, return directly + // Return immediately if the tree is empty if self.root.is_none() { return; } let mut cur = self.root.clone(); let mut pre = None; - // Loop search, exit after passing leaf node + // Search while cur refers to a node while let Some(node) = cur.clone() { match num.cmp(&node.borrow().val) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop Ordering::Equal => break, - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur Ordering::Greater => { pre = cur.clone(); cur = node.borrow().right.clone(); } - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur Ordering::Less => { pre = cur.clone(); cur = node.borrow().left.clone(); } } } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if cur.is_none() { return; } let cur = cur.unwrap(); let (left_child, right_child) = (cur.borrow().left.clone(), cur.borrow().right.clone()); match (left_child.clone(), right_child.clone()) { - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 (None, None) | (Some(_), None) | (None, Some(_)) => { - // When number of child nodes = 0 / 1, child = nullptr / that child node + // With 0 or 1 child, child is None or that child, respectively let child = left_child.or(right_child); let pre = pre.unwrap(); - // Delete node cur + // Remove node cur if !Rc::ptr_eq(&cur, self.root.as_ref().unwrap()) { let left = pre.borrow().left.clone(); if left.is_some() && Rc::ptr_eq(left.as_ref().unwrap(), &cur) { @@ -132,13 +132,13 @@ impl BinarySearchTree { pre.borrow_mut().right = child; } } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root self.root = child; } } - // Number of child nodes = 2 + // Number of children = 2 (Some(_), Some(_)) => { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur let mut tmp = cur.borrow().right.clone(); while let Some(node) = tmp.clone() { if node.borrow().left.is_some() { @@ -148,48 +148,48 @@ impl BinarySearchTree { } } let tmp_val = tmp.unwrap().borrow().val; - // Recursively delete node tmp + // Recursively remove node tmp self.remove(tmp_val); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur.borrow_mut().val = tmp_val; } } } } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ let mut bst = BinarySearchTree::new(); - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree let nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; for &num in &nums { bst.insert(num); } - println!("\nInitialized binary tree is\n"); + println!("\nThe initialized binary tree is:\n"); print_util::print_tree(bst.get_root().as_ref().unwrap()); - /* Search node */ + /* Find a node */ let node = bst.search(7); println!( - "\nFound node object is {:?}, node value = {}", + "\nFound node: {:?}, node value = {}", node.clone().unwrap(), node.clone().unwrap().borrow().val ); - /* Insert node */ + /* Insert a node */ bst.insert(16); - println!("\nAfter inserting node 16, binary tree is\n"); + println!("\nBinary tree after inserting node 16:\n"); print_util::print_tree(bst.get_root().as_ref().unwrap()); - /* Remove node */ + /* Remove a node */ bst.remove(1); - println!("\nAfter removing node 1, binary tree is\n"); + println!("\nBinary tree after removing node 1:\n"); print_util::print_tree(bst.get_root().as_ref().unwrap()); bst.remove(2); - println!("\nAfter removing node 2, binary tree is\n"); + println!("\nBinary tree after removing node 2:\n"); print_util::print_tree(bst.get_root().as_ref().unwrap()); bst.remove(4); - println!("\nAfter removing node 4, binary tree is\n"); + println!("\nBinary tree after removing node 4:\n"); print_util::print_tree(bst.get_root().as_ref().unwrap()); } diff --git a/en/codes/rust/chapter_tree/binary_tree.rs b/en/codes/rust/chapter_tree/binary_tree.rs index 096ddcb62..4e630bab7 100644 --- a/en/codes/rust/chapter_tree/binary_tree.rs +++ b/en/codes/rust/chapter_tree/binary_tree.rs @@ -6,33 +6,33 @@ use std::rc::Rc; use hello_algo_rust::include::{print_util, TreeNode}; -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes let n1 = TreeNode::new(1); let n2 = TreeNode::new(2); let n3 = TreeNode::new(3); let n4 = TreeNode::new(4); let n5 = TreeNode::new(5); - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1.borrow_mut().left = Some(Rc::clone(&n2)); n1.borrow_mut().right = Some(Rc::clone(&n3)); n2.borrow_mut().left = Some(Rc::clone(&n4)); n2.borrow_mut().right = Some(Rc::clone(&n5)); - println!("\nInitialize binary tree\n"); + println!("\nInitialize the binary tree\n"); print_util::print_tree(&n1); - // Insert node and delete node + // Insert and remove nodes let p = TreeNode::new(0); - // Delete node + // Insert node p between n1 and n2 in n1 -> n2 p.borrow_mut().left = Some(Rc::clone(&n2)); n1.borrow_mut().left = Some(Rc::clone(&p)); - println!("\nAfter inserting node P\n"); + println!("\nAfter inserting node p:\n"); print_util::print_tree(&n1); - // Remove node P + // Remove node p drop(p); n1.borrow_mut().left = Some(Rc::clone(&n2)); - println!("\nAfter removing node P\n"); + println!("\nAfter removing node p:\n"); print_util::print_tree(&n1); } diff --git a/en/codes/rust/chapter_tree/binary_tree_bfs.rs b/en/codes/rust/chapter_tree/binary_tree_bfs.rs index 95af73dbf..9dbb2d684 100644 --- a/en/codes/rust/chapter_tree/binary_tree_bfs.rs +++ b/en/codes/rust/chapter_tree/binary_tree_bfs.rs @@ -12,34 +12,34 @@ use std::{cell::RefCell, rc::Rc}; /* Level-order traversal */ fn level_order(root: &Rc>) -> Vec { - // Initialize queue, add root node + // Initialize a queue and enqueue the root let mut que = VecDeque::new(); que.push_back(root.clone()); - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence let mut vec = Vec::new(); while let Some(node) = que.pop_front() { - // Dequeue - vec.push(node.borrow().val); // Save node value + // Dequeue from the queue + vec.push(node.borrow().val); // Store the node value if let Some(left) = node.borrow().left.as_ref() { - que.push_back(left.clone()); // Left child node enqueue + que.push_back(left.clone()); // Enqueue the left child } if let Some(right) = node.borrow().right.as_ref() { - que.push_back(right.clone()); // Right child node enqueue + que.push_back(right.clone()); // Enqueue the right child }; } vec } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]).unwrap(); - println!("Initialize binary tree\n"); + println!("Initialize the binary tree\n"); print_util::print_tree(&root); /* Level-order traversal */ let vec = level_order(&root); - print!("\nLevel-order traversal node sequence = {:?}", vec); + print!("\nNode sequence from level-order traversal = {:?}", vec); } diff --git a/en/codes/rust/chapter_tree/binary_tree_dfs.rs b/en/codes/rust/chapter_tree/binary_tree_dfs.rs index d804c3d7b..d59cc45a7 100644 --- a/en/codes/rust/chapter_tree/binary_tree_dfs.rs +++ b/en/codes/rust/chapter_tree/binary_tree_dfs.rs @@ -16,7 +16,7 @@ fn pre_order(root: Option<&Rc>>) -> Vec { fn dfs(root: Option<&Rc>>, res: &mut Vec) { if let Some(node) = root { - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree let node = node.borrow(); res.push(node.val); dfs(node.left.as_ref(), res); @@ -34,7 +34,7 @@ fn in_order(root: Option<&Rc>>) -> Vec { fn dfs(root: Option<&Rc>>, res: &mut Vec) { if let Some(node) = root { - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree let node = node.borrow(); dfs(node.left.as_ref(), res); res.push(node.val); @@ -52,7 +52,7 @@ fn post_order(root: Option<&Rc>>) -> Vec { fn dfs(root: Option<&Rc>>, res: &mut Vec) { if let Some(node) = root { - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root let node = node.borrow(); dfs(node.left.as_ref(), res); dfs(node.right.as_ref(), res); @@ -65,23 +65,23 @@ fn post_order(root: Option<&Rc>>) -> Vec { result } -/* Driver Code */ +/* Driver code */ fn main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]); - println!("Initialize binary tree\n"); + println!("Initialize the binary tree\n"); print_util::print_tree(root.as_ref().unwrap()); /* Preorder traversal */ let vec = pre_order(root.as_ref()); - println!("\nPre-order traversal node sequence = {:?}", vec); + println!("\nNode sequence from preorder traversal = {:?}", vec); /* Inorder traversal */ let vec = in_order(root.as_ref()); - println!("\nIn-order traversal node sequence = {:?}", vec); + println!("\nNode sequence from inorder traversal = {:?}", vec); /* Postorder traversal */ let vec = post_order(root.as_ref()); - print!("\nPost-order traversal node sequence = {:?}", vec); + print!("\nNode sequence from postorder traversal = {:?}", vec); } diff --git a/en/codes/rust/src/include/list_node.rs b/en/codes/rust/src/include/list_node.rs index 27ed78409..143272994 100644 --- a/en/codes/rust/src/include/list_node.rs +++ b/en/codes/rust/src/include/list_node.rs @@ -19,13 +19,13 @@ impl ListNode { Rc::new(RefCell::new(ListNode { val, next: None })) } - /* Deserialize array to linked list */ + /* Deserialize an array into a linked list */ pub fn arr_to_linked_list(array: &[T]) -> Option>>> where T: Copy + Clone, { let mut head = None; - // insert in reverse order + // Insert in reverse order for item in array.iter().rev() { let node = Rc::new(RefCell::new(ListNode { val: *item, @@ -36,7 +36,7 @@ impl ListNode { head } - /* Convert linked list to hash table */ + /* Convert the linked list to a hash map */ pub fn linked_list_to_hashmap( linked_list: Option>>>, ) -> HashMap>>> diff --git a/en/codes/rust/src/include/mod.rs b/en/codes/rust/src/include/mod.rs index 6cba6f9a5..84164efd1 100644 --- a/en/codes/rust/src/include/mod.rs +++ b/en/codes/rust/src/include/mod.rs @@ -9,7 +9,7 @@ pub mod print_util; pub mod tree_node; pub mod vertex; -// rexport to include +// Re-export from the include module pub use list_node::*; pub use print_util::*; pub use tree_node::*; diff --git a/en/codes/rust/src/include/print_util.rs b/en/codes/rust/src/include/print_util.rs index 047579665..1c9779e71 100644 --- a/en/codes/rust/src/include/print_util.rs +++ b/en/codes/rust/src/include/print_util.rs @@ -17,7 +17,7 @@ struct Trunk<'a, 'b> { str: Cell<&'b str>, } -/* Print array */ +/* Print an array */ pub fn print_array(nums: &[T]) { print!("["); if nums.len() > 0 { @@ -29,14 +29,14 @@ pub fn print_array(nums: &[T]) { } } -/* Print hash table */ +/* Print the hash table */ pub fn print_hash_map(map: &HashMap) { for (key, value) in map { println!("{key} -> {value}"); } } -/* Print queue (deque) */ +/* Print a queue (deque) */ pub fn print_queue(queue: &VecDeque) { print!("["); let iter = queue.iter(); @@ -45,7 +45,7 @@ pub fn print_queue(queue: &VecDeque) { } } -/* Print linked list */ +/* Print a linked list */ pub fn print_linked_list(head: &Rc>>) { print!("{}{}", head.borrow().val, if head.borrow().next.is_none() {"\n"} else {" -> "}); if let Some(node) = &head.borrow().next { @@ -53,12 +53,12 @@ pub fn print_linked_list(head: &Rc>>) { } } -/* Print binary tree */ +/* Print a binary tree */ pub fn print_tree(root: &Rc>) { _print_tree(Some(root), None, false); } -/* Print binary tree */ +/* Print a binary tree */ fn _print_tree(root: Option<&Rc>>, prev: Option<&Trunk>, is_right: bool) { if let Some(node) = root { let mut prev_str = " "; @@ -93,10 +93,10 @@ fn show_trunks(trunk: Option<&Trunk>) { } } -/* Print heap */ +/* Print a heap */ pub fn print_heap(heap: Vec) { - println!("Array representation of heap: {:?}", heap); - println!("Heap tree representation:"); + println!("Array representation of the heap: {:?}", heap); + println!("Tree representation of the heap:"); if let Some(root) = vec_to_tree(heap.into_iter().map(|val| Some(val)).collect()) { print_tree(&root); } diff --git a/en/codes/rust/src/include/tree_node.rs b/en/codes/rust/src/include/tree_node.rs index 9410b9342..c2beec6da 100644 --- a/en/codes/rust/src/include/tree_node.rs +++ b/en/codes/rust/src/include/tree_node.rs @@ -39,11 +39,11 @@ macro_rules! op_vec { }; } -// For the serialization encoding rules, please refer to: -// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ -// Array representation of binary tree: +// For the serialization rules, see: +// https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ +// Array representation of the binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] -// Linked list representation of binary tree: +// Linked representation of the binary tree: // /——— 15 // /——— 7 // /——— 3 diff --git a/en/codes/rust/src/include/vertex.rs b/en/codes/rust/src/include/vertex.rs index e9420c53e..417c2fc91 100644 --- a/en/codes/rust/src/include/vertex.rs +++ b/en/codes/rust/src/include/vertex.rs @@ -16,12 +16,12 @@ impl From for Vertex { } } -/* Input value list vals, return vertex list vets */ +/* Given the list of values vals, return the list of vertices vets */ pub fn vals_to_vets(vals: Vec) -> Vec { vals.into_iter().map(|val| val.into()).collect() } -/* Input vertex list vets, return value list vals */ +/* Given the list of vertices vets, return the list of values vals */ pub fn vets_to_vals(vets: Vec) -> Vec { vets.into_iter().map(|vet| vet.val).collect() } diff --git a/en/codes/swift/chapter_array_and_linkedlist/array.swift b/en/codes/swift/chapter_array_and_linkedlist/array.swift index 54453f6ad..81f3ba0df 100644 --- a/en/codes/swift/chapter_array_and_linkedlist/array.swift +++ b/en/codes/swift/chapter_array_and_linkedlist/array.swift @@ -4,64 +4,64 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Random access to element */ +/* Access an element at a random index */ func randomAccess(nums: [Int]) -> Int { - // Randomly select a number in interval [0, nums.count) + // Pick a random number in the interval [0, nums.count) let randomIndex = nums.indices.randomElement()! - // Retrieve and return the random element + // Get and return the randomly selected element let randomNum = nums[randomIndex] return randomNum } -/* Extend array length */ +/* Extend the array length */ func extend(nums: [Int], enlarge: Int) -> [Int] { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for i in stride(from: nums.count - 1, to: index, by: -1) { nums[i] = nums[i - 1] } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num } -/* Remove the element at index index */ +/* Remove the element at index `index` */ func remove(nums: inout [Int], index: Int) { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for i in nums.indices.dropFirst(index).dropLast() { nums[i] = nums[i + 1] } } -/* Traverse array */ +/* Traverse the array */ func traverse(nums: [Int]) { var count = 0 - // Traverse array by index + // Traverse the array by index for i in nums.indices { count += nums[i] } - // Direct traversal of array elements + // Iterate directly over the array elements for num in nums { count += num } - // Traverse simultaneously data index and elements + // Iterate over both the indices and the elements for (i, num) in nums.enumerated() { count += nums[i] count += num } } -/* Find the specified element in the array */ +/* Find a specified element in the array */ func find(nums: [Int], target: Int) -> Int { for i in nums.indices { if nums[i] == target { @@ -73,35 +73,35 @@ func find(nums: [Int], target: Int) -> Int { @main enum _Array { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize array */ + /* Initialize the array */ let arr = Array(repeating: 0, count: 5) print("Array arr = \(arr)") var nums = [1, 3, 2, 5, 4] print("Array nums = \(nums)") - /* Insert element */ + /* Random access */ let randomNum = randomAccess(nums: nums) - print("Get random element \(randomNum) from nums") + print("Randomly selected element from nums: \(randomNum)") - /* Traverse array */ + /* Extend the length */ nums = extend(nums: nums, enlarge: 3) - print("Extend array length to 8, get nums = \(nums)") + print("After extending the array length to 8, nums = \(nums)") - /* Insert element */ + /* Insert an element */ insert(nums: &nums, num: 6, index: 3) - print("Insert number 6 at index 3, get nums = \(nums)") + print("After inserting 6 at index 3, nums = \(nums)") - /* Remove element */ + /* Remove an element */ remove(nums: &nums, index: 2) - print("Delete element at index 2, get nums = \(nums)") + print("After removing the element at index 2, nums = \(nums)") - /* Traverse array */ + /* Traverse the array */ traverse(nums: nums) - /* Find element */ + /* Find an element */ let index = find(nums: nums, target: 3) - print("Find element 3 in nums, index = \(index)") + print("Find element 3 in nums; index = \(index)") } } diff --git a/en/codes/swift/chapter_array_and_linkedlist/linked_list.swift b/en/codes/swift/chapter_array_and_linkedlist/linked_list.swift index 448623a55..1fb0b7c8a 100644 --- a/en/codes/swift/chapter_array_and_linkedlist/linked_list.swift +++ b/en/codes/swift/chapter_array_and_linkedlist/linked_list.swift @@ -24,7 +24,7 @@ func remove(n0: ListNode) { n0.next = n1 } -/* Access the node at index index in the linked list */ +/* 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 { @@ -52,39 +52,39 @@ func find(head: ListNode, target: Int) -> Int { @main enum LinkedList { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize linked list */ - // Initialize each node + /* Initialize the linked list */ + // Initialize the individual nodes let n0 = ListNode(x: 1) let n1 = ListNode(x: 3) let n2 = ListNode(x: 2) let n3 = ListNode(x: 5) let n4 = ListNode(x: 4) - // Build references between nodes + // Set up references between the nodes n0.next = n1 n1.next = n2 n2.next = n3 n3.next = n4 - print("Initialized linked list is") + print("The initialized linked list is") PrintUtil.printLinkedList(head: n0) - /* Insert node */ + /* Insert a node */ insert(n0: n0, P: ListNode(x: 0)) - print("Linked list after inserting node is") + print("The linked list after inserting a node is") PrintUtil.printLinkedList(head: n0) - /* Remove node */ + /* Remove a node */ remove(n0: n0) - print("Linked list after removing node is") + print("The linked list after removing a node is") PrintUtil.printLinkedList(head: n0) - /* Access node */ + /* Access a node */ let node = access(head: n0, index: 3) - print("Value of node at index 3 in linked list = \(node!.val)") + print("Value of the node at index 3 in the linked list = \(node!.val)") - /* Search node */ + /* Find a node */ let index = find(head: n0, target: 2) - print("Index of node with value 2 in linked list = \(index)") + print("Index of the node with value 2 in the linked list = \(index)") } } diff --git a/en/codes/swift/chapter_array_and_linkedlist/list.swift b/en/codes/swift/chapter_array_and_linkedlist/list.swift index 00ffef9a5..cfac9b76e 100644 --- a/en/codes/swift/chapter_array_and_linkedlist/list.swift +++ b/en/codes/swift/chapter_array_and_linkedlist/list.swift @@ -6,46 +6,46 @@ @main enum List { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize list */ + /* Initialize the list */ var nums = [1, 3, 2, 5, 4] print("List nums = \(nums)") - /* Update element */ + /* Access an element */ let num = nums[1] - print("Access element at index 1, get num = \(num)") + print("Access the element at index 1; num = \(num)") - /* Add elements at the end */ + /* Update an element */ nums[1] = 0 - print("Update element at index 1 to 0, get nums = \(nums)") + print("After updating the element at index 1 to 0, nums = \(nums)") - /* Remove element */ + /* Clear the list */ nums.removeAll() - print("After clearing list, nums = \(nums)") + print("After clearing the list, nums = \(nums)") - /* Direct traversal of list elements */ + /* Append at the end */ nums.append(1) nums.append(3) nums.append(2) nums.append(5) nums.append(4) - print("After adding elements, nums = \(nums)") + print("After appending elements, nums = \(nums)") - /* Sort list */ + /* Insert an element in the middle */ nums.insert(6, at: 3) - print("Insert number 6 at index 3, get nums = \(nums)") + print("After inserting 6 at index 3, nums = \(nums)") - /* Remove element */ + /* Remove an element */ nums.remove(at: 3) - print("Delete element at index 3, get nums = \(nums)") + print("After removing the element at index 3, nums = \(nums)") - /* Traverse list by index */ + /* Traverse the list by index */ var count = 0 for i in nums.indices { count += nums[i] } - /* Directly traverse list elements */ + /* Iterate directly over the list elements */ count = 0 for x in nums { count += x @@ -54,10 +54,10 @@ enum List { /* Concatenate two lists */ let nums1 = [6, 8, 7, 10, 9] nums.append(contentsOf: nums1) - print("After concatenating list nums1 to nums, get nums = \(nums)") + print("After appending list nums1 to nums, nums = \(nums)") - /* Sort list */ + /* Sort the list */ nums.sort() - print("After sorting list, nums = \(nums)") + print("After sorting the list, nums = \(nums)") } } diff --git a/en/codes/swift/chapter_array_and_linkedlist/my_list.swift b/en/codes/swift/chapter_array_and_linkedlist/my_list.swift index dcdfeb505..bdf3e0169 100644 --- a/en/codes/swift/chapter_array_and_linkedlist/my_list.swift +++ b/en/codes/swift/chapter_array_and_linkedlist/my_list.swift @@ -6,10 +6,10 @@ /* List class */ class MyList { - private var arr: [Int] // Array (stores list elements) + private var arr: [Int] // Array (stores the 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 + private let extendRatio: Int // Factor by which the list capacity grows each time /* Constructor */ init() { @@ -19,26 +19,26 @@ class MyList { arr = Array(repeating: 0, count: _capacity) } - /* Get list length (current number of elements) */ + /* Get the list length (current number of elements)*/ func size() -> Int { _size } - /* Get list capacity */ + /* Get the list capacity */ func capacity() -> Int { _capacity } - /* Update element */ + /* Access an element */ func get(index: Int) -> Int { - // Throw error if index out of bounds, same below + // An out-of-bounds index triggers a fatal error; the same check is used below if index < 0 || index >= size() { fatalError("Index out of bounds") } return arr[index] } - /* Add elements at the end */ + /* Update an element */ func set(index: Int, num: Int) { if index < 0 || index >= size() { fatalError("Index out of bounds") @@ -46,9 +46,9 @@ class MyList { arr[index] = num } - /* Direct traversal of list elements */ + /* Append at the end */ func add(num: Int) { - // When the number of elements exceeds capacity, trigger the extension mechanism + // Grow the capacity when adding an element would exceed it if size() == capacity() { extendCapacity() } @@ -57,16 +57,16 @@ class MyList { _size += 1 } - /* Sort list */ + /* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if size() == capacity() { extendCapacity() } - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for j in (index ..< size()).reversed() { arr[j + 1] = arr[j] } @@ -75,14 +75,14 @@ class MyList { _size += 1 } - /* Remove element */ + /* Remove an 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 + // Shift all elements after index `index` one position to the left for j in index ..< (size() - 1) { arr[j] = arr[j + 1] } @@ -92,15 +92,15 @@ class MyList { return num } - /* Driver Code */ + /* Grow the list capacity */ func extendCapacity() { - // Create a new array with length extendRatio times the original array and copy the original array to the new array + // Create an array extendRatio times as long as the original and copy the original array into it arr = arr + Array(repeating: 0, count: capacity() * (extendRatio - 1)) - // Add elements at the end + // Update the list capacity _capacity = arr.count } - /* Convert list to array */ + /* Convert the list to an array */ func toArray() -> [Int] { Array(arr.prefix(size())) } @@ -108,11 +108,11 @@ class MyList { @main enum _MyList { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize list */ + /* Initialize the list */ let nums = MyList() - /* Direct traversal of list elements */ + /* Append at the end */ nums.add(num: 1) nums.add(num: 3) nums.add(num: 2) @@ -120,27 +120,27 @@ enum _MyList { nums.add(num: 4) print("List nums = \(nums.toArray()), capacity = \(nums.capacity()), length = \(nums.size())") - /* Sort list */ + /* Insert an element in the middle */ nums.insert(index: 3, num: 6) - print("Insert number 6 at index 3, get nums = \(nums.toArray())") + print("After inserting 6 at index 3, nums = \(nums.toArray())") - /* Remove element */ + /* Remove an element */ nums.remove(index: 3) - print("Delete element at index 3, get nums = \(nums.toArray())") + print("After removing the element at index 3, nums = \(nums.toArray())") - /* Update element */ + /* Access an element */ let num = nums.get(index: 1) - print("Access element at index 1, get num = \(num)") + print("Access the element at index 1; num = \(num)") - /* Add elements at the end */ + /* Update an element */ nums.set(index: 1, num: 0) - print("Update element at index 1 to 0, get nums = \(nums.toArray())") + print("After updating the element at index 1 to 0, nums = \(nums.toArray())") - /* Test capacity expansion mechanism */ + /* Test capacity growth */ for i in 0 ..< 10 { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(num: i) } - print("After expansion, list nums = \(nums.toArray()), capacity = \(nums.capacity()), length = \(nums.size())") + print("After capacity growth, list nums = \(nums.toArray()), capacity = \(nums.capacity()), length = \(nums.size())") } } diff --git a/en/codes/swift/chapter_backtracking/n_queens.swift b/en/codes/swift/chapter_backtracking/n_queens.swift index d429a5706..d4b13318d 100644 --- a/en/codes/swift/chapter_backtracking/n_queens.swift +++ b/en/codes/swift/chapter_backtracking/n_queens.swift @@ -4,28 +4,28 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]]], cols: inout [Bool], diags1: inout [Bool], diags2: inout [Bool]) { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if row == n { res.append(state) return } - // Traverse all columns + // Iterate over all columns for col in 0 ..< n { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square let diag1 = row - col + n - 1 let diag2 = row + col - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if !cols[col] && !diags1[diag1] && !diags2[diag2] { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = "Q" cols[col] = true diags1[diag1] = true diags2[diag2] = true - // Place the next row + // Place a queen in the next row backtrack(row: row + 1, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2) - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = "#" cols[col] = false diags1[diag1] = false @@ -34,13 +34,13 @@ func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]] } } -/* Solve N queens */ +/* Solve the n-queens problem */ func nQueens(n: Int) -> [[[String]]] { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square var state = Array(repeating: Array(repeating: "#", count: n), count: n) - var cols = Array(repeating: false, count: n) // Record whether there is a queen in the column - var diags1 = Array(repeating: false, count: 2 * n - 1) // Record whether there is a queen on the main diagonal - var diags2 = Array(repeating: false, count: 2 * n - 1) // Record whether there is a queen on the anti-diagonal + var cols = Array(repeating: false, count: n) // Track whether a column contains a queen + var diags1 = Array(repeating: false, count: 2 * n - 1) // Track whether a main diagonal contains a queen + var diags2 = Array(repeating: false, count: 2 * n - 1) // Track whether an anti-diagonal contains a queen var res: [[[String]]] = [] backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2) @@ -50,13 +50,13 @@ func nQueens(n: Int) -> [[[String]]] { @main enum NQueens { - /* Driver Code */ + /* Driver code */ static func main() { let n = 4 let res = nQueens(n: n) - print("Input board size is \(n)") - print("Total queen placement solutions: \(res.count)") + print("Input board width and height: \(n)") + print("There are \(res.count) queen placements") for state in res { print("--------------------") for row in state { diff --git a/en/codes/swift/chapter_backtracking/permutations_i.swift b/en/codes/swift/chapter_backtracking/permutations_i.swift index 780e2f014..8e555c825 100644 --- a/en/codes/swift/chapter_backtracking/permutations_i.swift +++ b/en/codes/swift/chapter_backtracking/permutations_i.swift @@ -4,23 +4,23 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if state.count == choices.count { res.append(state) return } - // Traverse all choices + // Iterate over all choices for (i, choice) in choices.enumerated() { - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if !selected[i] { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true state.append(choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state: &state, choices: choices, selected: &selected, res: &res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false state.removeLast() } @@ -38,7 +38,7 @@ func permutationsI(nums: [Int]) -> [[Int]] { @main enum PermutationsI { - /* Driver Code */ + /* Driver code */ static func main() { let nums = [1, 2, 3] diff --git a/en/codes/swift/chapter_backtracking/permutations_ii.swift b/en/codes/swift/chapter_backtracking/permutations_ii.swift index 7dd43c1bd..8cf9e983a 100644 --- a/en/codes/swift/chapter_backtracking/permutations_ii.swift +++ b/en/codes/swift/chapter_backtracking/permutations_ii.swift @@ -4,25 +4,25 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if state.count == choices.count { res.append(state) return } - // Traverse all choices + // Iterate over all choices var duplicated: Set = [] for (i, choice) in choices.enumerated() { - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if !selected[i], !duplicated.contains(choice) { - // Attempt: make choice, update state - duplicated.insert(choice) // Record the selected element value + // Try: make a choice and update the state + duplicated.insert(choice) // Record the chosen value selected[i] = true state.append(choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state: &state, choices: choices, selected: &selected, res: &res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false state.removeLast() } @@ -40,7 +40,7 @@ func permutationsII(nums: [Int]) -> [[Int]] { @main enum PermutationsII { - /* Driver Code */ + /* Driver code */ static func main() { let nums = [1, 2, 3] diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_i_compact.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_i_compact.swift index ac0e459ed..2c63e826f 100644 --- a/en/codes/swift/chapter_backtracking/preorder_traversal_i_compact.swift +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_i_compact.swift @@ -8,13 +8,13 @@ import utils var res: [TreeNode] = [] -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ func preOrder(root: TreeNode?) { guard let root = root else { return } if root.val == 7 { - // Record solution + // Record the solution res.append(root) } preOrder(root: root.left) @@ -23,10 +23,10 @@ func preOrder(root: TreeNode?) { @main enum PreorderTraversalICompact { - /* Driver Code */ + /* Driver code */ static func main() { let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") PrintUtil.printTree(root: root) // Preorder traversal diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_ii_compact.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_ii_compact.swift index c34fa5cfd..cb1b6cd48 100644 --- a/en/codes/swift/chapter_backtracking/preorder_traversal_ii_compact.swift +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_ii_compact.swift @@ -9,15 +9,15 @@ import utils var path: [TreeNode] = [] var res: [[TreeNode]] = [] -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ func preOrder(root: TreeNode?) { guard let root = root else { return } - // Attempt + // Try path.append(root) if root.val == 7 { - // Record solution + // Record the solution res.append(path) } preOrder(root: root.left) @@ -28,10 +28,10 @@ func preOrder(root: TreeNode?) { @main enum PreorderTraversalIICompact { - /* Driver Code */ + /* Driver code */ static func main() { let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") PrintUtil.printTree(root: root) // Preorder traversal @@ -39,7 +39,7 @@ enum PreorderTraversalIICompact { res = [] preOrder(root: root) - print("\nOutput all paths from root node to node 7") + print("\nOutput all paths from the root to nodes with value 7") for path in res { var vals: [Int] = [] for node in path { diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_iii_compact.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_compact.swift index da3410b03..dd869d188 100644 --- a/en/codes/swift/chapter_backtracking/preorder_traversal_iii_compact.swift +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_compact.swift @@ -9,16 +9,16 @@ import utils var path: [TreeNode] = [] var res: [[TreeNode]] = [] -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ func preOrder(root: TreeNode?) { - // Pruning + // Prune guard let root = root, root.val != 3 else { return } - // Attempt + // Try path.append(root) if root.val == 7 { - // Record solution + // Record the solution res.append(path) } preOrder(root: root.left) @@ -29,10 +29,10 @@ func preOrder(root: TreeNode?) { @main enum PreorderTraversalIIICompact { - /* Driver Code */ + /* Driver code */ static func main() { let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") PrintUtil.printTree(root: root) // Preorder traversal @@ -40,7 +40,7 @@ enum PreorderTraversalIIICompact { res = [] preOrder(root: root) - print("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + print("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3") for path in res { var vals: [Int] = [] for node in path { diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_iii_template.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_template.swift index b825cb000..15a426d27 100644 --- a/en/codes/swift/chapter_backtracking/preorder_traversal_iii_template.swift +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_template.swift @@ -6,46 +6,46 @@ import utils -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ func isSolution(state: [TreeNode]) -> Bool { !state.isEmpty && state.last!.val == 7 } -/* Record solution */ +/* Record the solution */ func recordSolution(state: [TreeNode], res: inout [[TreeNode]]) { res.append(state) } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ func isValid(state: [TreeNode], choice: TreeNode?) -> Bool { choice != nil && choice!.val != 3 } -/* Update state */ +/* Update the state */ func makeChoice(state: inout [TreeNode], choice: TreeNode) { state.append(choice) } -/* Restore state */ +/* Restore the state */ func undoChoice(state: inout [TreeNode], choice: TreeNode) { state.removeLast() } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ func backtrack(state: inout [TreeNode], choices: [TreeNode], res: inout [[TreeNode]]) { - // Check if it is a solution + // Check whether this is a solution if isSolution(state: state) { recordSolution(state: state, res: &res) } - // Traverse all choices + // Iterate over all choices for choice in choices { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if isValid(state: state, choice: choice) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state: &state, choice: choice) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state: &state, choices: [choice.left, choice.right].compactMap { $0 }, res: &res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state: &state, choice: choice) } } @@ -53,10 +53,10 @@ func backtrack(state: inout [TreeNode], choices: [TreeNode], res: inout [[TreeNo @main enum PreorderTraversalIIITemplate { - /* Driver Code */ + /* Driver code */ static func main() { let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) - print("\nInitialize binary tree") + print("\nInitialize the binary tree") PrintUtil.printTree(root: root) // Backtracking algorithm @@ -64,7 +64,7 @@ enum PreorderTraversalIIITemplate { var res: [[TreeNode]] = [] backtrack(state: &state, choices: [root].compactMap { $0 }, res: &res) - print("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + print("\nOutput all paths from the root to nodes with value 7 that contain no node with value 3") for path in res { var vals: [Int] = [] for node in path { diff --git a/en/codes/swift/chapter_backtracking/subset_sum_i.swift b/en/codes/swift/chapter_backtracking/subset_sum_i.swift index 77f710563..a102fb0d9 100644 --- a/en/codes/swift/chapter_backtracking/subset_sum_i.swift +++ b/en/codes/swift/chapter_backtracking/subset_sum_i.swift @@ -4,26 +4,26 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target reaches zero if target == 0 { res.append(state) return } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for i in choices.indices.dropFirst(start) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if target - choices[i] < 0 { break } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.append(choices[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state: &state, target: target - choices[i], choices: choices, start: i, res: &res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeLast() } } @@ -32,15 +32,15 @@ func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: func subsetSumI(nums: [Int], target: Int) -> [[Int]] { var state: [Int] = [] // State (subset) let nums = nums.sorted() // Sort nums - let start = 0 // Start point for traversal - var res: [[Int]] = [] // Result list (subset list) + let start = 0 // Starting index for iteration + var res: [[Int]] = [] // Result list (list of subsets) backtrack(state: &state, target: target, choices: nums, start: start, res: &res) return res } @main enum SubsetSumI { - /* Driver Code */ + /* Driver code */ static func main() { let nums = [3, 4, 5] let target = 9 @@ -48,6 +48,6 @@ enum SubsetSumI { let res = subsetSumI(nums: nums, target: target) print("Input array nums = \(nums), target = \(target)") - print("All subsets with sum equal to \(target) res = \(res)") + print("All subsets with sum \(target): res = \(res)") } } diff --git a/en/codes/swift/chapter_backtracking/subset_sum_i_naive.swift b/en/codes/swift/chapter_backtracking/subset_sum_i_naive.swift index 9a544722b..d21b21769 100644 --- a/en/codes/swift/chapter_backtracking/subset_sum_i_naive.swift +++ b/en/codes/swift/chapter_backtracking/subset_sum_i_naive.swift @@ -4,40 +4,40 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ func backtrack(state: inout [Int], target: Int, total: Int, choices: [Int], res: inout [[Int]]) { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if total == target { res.append(state) return } - // Traverse all choices + // Iterate over all choices for i in choices.indices { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if total + choices[i] > target { continue } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.append(choices[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state: &state, target: target, total: total + choices[i], choices: choices, res: &res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeLast() } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ func subsetSumINaive(nums: [Int], target: Int) -> [[Int]] { var state: [Int] = [] // State (subset) let total = 0 // Subset sum - var res: [[Int]] = [] // Result list (subset list) + var res: [[Int]] = [] // Result list (list of subsets) backtrack(state: &state, target: target, total: total, choices: nums, res: &res) return res } @main enum SubsetSumINaive { - /* Driver Code */ + /* Driver code */ static func main() { let nums = [3, 4, 5] let target = 9 @@ -45,7 +45,7 @@ enum SubsetSumINaive { let res = subsetSumINaive(nums: nums, target: target) print("Input array nums = \(nums), target = \(target)") - print("All subsets with sum equal to \(target) res = \(res)") - print("Please note that this method outputs results containing duplicate sets") + print("All subsets with sum \(target): res = \(res)") + print("Note that this method produces duplicate subsets") } } diff --git a/en/codes/swift/chapter_backtracking/subset_sum_ii.swift b/en/codes/swift/chapter_backtracking/subset_sum_ii.swift index 6c67ea2cd..984c55b71 100644 --- a/en/codes/swift/chapter_backtracking/subset_sum_ii.swift +++ b/en/codes/swift/chapter_backtracking/subset_sum_ii.swift @@ -4,31 +4,31 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) { - // When the subset sum equals target, record the solution + // Record a solution when the remaining target reaches zero if target == 0 { res.append(state) return } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: start after the previously chosen index to avoid choosing the same element again for i in choices.indices.dropFirst(start) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice exceeds the remaining target + // The array is sorted, so later elements are at least as large and also exceed the remaining target if target - choices[i] < 0 { break } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if i > start, choices[i] == choices[i - 1] { continue } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.append(choices[i]) - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state: &state, target: target - choices[i], choices: choices, start: i + 1, res: &res) - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.removeLast() } } @@ -37,15 +37,15 @@ func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: func subsetSumII(nums: [Int], target: Int) -> [[Int]] { var state: [Int] = [] // State (subset) let nums = nums.sorted() // Sort nums - let start = 0 // Start point for traversal - var res: [[Int]] = [] // Result list (subset list) + let start = 0 // Starting index for iteration + var res: [[Int]] = [] // Result list (list of subsets) backtrack(state: &state, target: target, choices: nums, start: start, res: &res) return res } @main enum SubsetSumII { - /* Driver Code */ + /* Driver code */ static func main() { let nums = [4, 4, 5] let target = 9 @@ -53,6 +53,6 @@ enum SubsetSumII { let res = subsetSumII(nums: nums, target: target) print("Input array nums = \(nums), target = \(target)") - print("All subsets with sum equal to \(target) res = \(res)") + print("All subsets with sum \(target): res = \(res)") } } diff --git a/en/codes/swift/chapter_computational_complexity/complexity_exercises.swift b/en/codes/swift/chapter_computational_complexity/complexity_exercises.swift index ed70497d1..75ff9ba4e 100644 --- a/en/codes/swift/chapter_computational_complexity/complexity_exercises.swift +++ b/en/codes/swift/chapter_computational_complexity/complexity_exercises.swift @@ -4,7 +4,7 @@ Author: Hello Algo Team */ -/* Iterative summation */ +/* Sum using iteration */ func sumIter(n: Int) -> Int { var res = 0 for i in 1 ... n { @@ -13,7 +13,7 @@ func sumIter(n: Int) -> Int { return res } -/* Recursive summation */ +/* Sum using recursion */ func sumRecur(n: Int) -> Int { if n == 1 { return 1 @@ -21,7 +21,7 @@ func sumRecur(n: Int) -> Int { return n + sumRecur(n: n - 1) } -/* Linear loop */ +/* Linear-time loop */ func linearLoop(n: Int) -> Int { var res = 0 for i in 0 ..< n { @@ -30,7 +30,7 @@ func linearLoop(n: Int) -> Int { return res } -/* Quadratic loop */ +/* Quadratic-time loop */ func quadraticLoop(n: Int) -> Int { var res = 0 for i in 0 ..< n { @@ -41,7 +41,7 @@ func quadraticLoop(n: Int) -> Int { return res } -/* Logarithmic loop */ +/* Logarithmic-time loop */ func logarithmicLoop(n: Int) -> Int { var n = n while n > 1 { diff --git a/en/codes/swift/chapter_computational_complexity/iteration.swift b/en/codes/swift/chapter_computational_complexity/iteration.swift index 84c9e891c..3c93268d8 100644 --- a/en/codes/swift/chapter_computational_complexity/iteration.swift +++ b/en/codes/swift/chapter_computational_complexity/iteration.swift @@ -7,7 +7,7 @@ /* for loop */ func forLoop(n: Int) -> Int { var res = 0 - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for i in 1 ... n { res += i } @@ -17,11 +17,11 @@ func forLoop(n: Int) -> Int { /* while loop */ func whileLoop(n: Int) -> Int { var res = 0 - var i = 1 // Initialize condition variable - // Sum 1, 2, ..., n-1, n + var i = 1 // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while i <= n { res += i - i += 1 // Update condition variable + i += 1 // Update the loop control variable } return res } @@ -29,23 +29,23 @@ func whileLoop(n: Int) -> Int { /* while loop (two updates) */ func whileLoopII(n: Int) -> Int { var res = 0 - var i = 1 // Initialize condition variable - // Sum 1, 4, 10, ... + var i = 1 // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while i <= n { res += i - // Update condition variable + // Update the loop control variable i += 1 i *= 2 } return res } -/* Nested for loop */ +/* Nested for loops */ func nestedForLoop(n: Int) -> String { var res = "" - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for i in 1 ... n { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for j in 1 ... n { res.append("(\(i), \(j)), ") } @@ -55,21 +55,21 @@ func nestedForLoop(n: Int) -> String { @main enum Iteration { - /* Driver Code */ + /* Driver code */ static func main() { let n = 5 var res = 0 res = forLoop(n: n) - print("\nFor loop sum result res = \(res)") + print("\nSum from the for loop: res = \(res)") res = whileLoop(n: n) - print("\nWhile loop sum result res = \(res)") + print("\nSum from the while loop: res = \(res)") res = whileLoopII(n: n) - print("\nWhile loop (two updates) sum result res = \(res)") + print("\nSum from the while loop (two updates): res = \(res)") let resStr = nestedForLoop(n: n) - print("\nNested for loop traversal result \(resStr)") + print("\nTraversal result from nested for loops: \(resStr)") } } diff --git a/en/codes/swift/chapter_computational_complexity/recursion.swift b/en/codes/swift/chapter_computational_complexity/recursion.swift index 060473760..92694d6a1 100644 --- a/en/codes/swift/chapter_computational_complexity/recursion.swift +++ b/en/codes/swift/chapter_computational_complexity/recursion.swift @@ -6,13 +6,13 @@ /* Recursion */ func recur(n: Int) -> Int { - // Termination condition + // Base case if n == 1 { return 1 } - // Recurse: recursive call + // Recurse: make a recursive call let res = recur(n: n - 1) - // Return: return result + // Unwind: return the result return n + res } @@ -21,14 +21,14 @@ func forLoopRecur(n: Int) -> Int { // Use an explicit stack to simulate the system call stack var stack: [Int] = [] var res = 0 - // Recurse: recursive call + // Descending: simulate recursive calls for i in (1 ... n).reversed() { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.append(i) } - // Return: return result + // Unwinding: simulate returning results while !stack.isEmpty { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.removeLast() } // res = 1+2+3+...+n @@ -37,43 +37,43 @@ func forLoopRecur(n: Int) -> Int { /* Tail recursion */ func tailRecur(n: Int, res: Int) -> Int { - // Termination condition + // Base case if n == 0 { return res } - // Tail recursive call + // Tail-recursive call return tailRecur(n: n - 1, res: res + n) } /* Fibonacci sequence: recursion */ func fib(n: Int) -> Int { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if n == 1 || n == 2 { return n - 1 } - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) let res = fib(n: n - 1) + fib(n: n - 2) - // Return result f(n) + // Return the result f(n) return res } @main enum Recursion { - /* Driver Code */ + /* Driver code */ static func main() { let n = 5 var res = 0 res = recursion.recur(n: n) - print("\nRecursion sum result res = \(res)") + print("\nSum from recursion: res = \(res)") res = recursion.forLoopRecur(n: n) - print("\nUsing iteration to simulate recursion sum result res = \(res)") + print("\nSum from simulating recursion with iteration: res = \(res)") res = recursion.tailRecur(n: n, res: 0) - print("\nTail recursion sum result res = \(res)") + print("\nSum from tail recursion: res = \(res)") res = recursion.fib(n: n) - print("\nThe \(n)th Fibonacci number is \(res)") + print("\nFibonacci term \(n) is \(res)") } } diff --git a/en/codes/swift/chapter_computational_complexity/space_complexity.swift b/en/codes/swift/chapter_computational_complexity/space_complexity.swift index c4cdf0416..b13ac2357 100644 --- a/en/codes/swift/chapter_computational_complexity/space_complexity.swift +++ b/en/codes/swift/chapter_computational_complexity/space_complexity.swift @@ -13,9 +13,9 @@ func function() -> Int { return 0 } -/* Constant order */ +/* Constant complexity */ func constant(n: Int) { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space let a = 0 var b = 0 let nums = Array(repeating: 0, count: 10000) @@ -24,50 +24,50 @@ func constant(n: Int) { for _ in 0 ..< n { let c = 0 } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for _ in 0 ..< n { function() } } -/* Linear order */ +/* Linear complexity */ func linear(n: Int) { - // Array of length n uses O(n) space + // An array of length n uses O(n) space let nums = Array(repeating: 0, count: n) // A list of length n occupies O(n) space let nodes = (0 ..< n).map { ListNode(x: $0) } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") }) } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ func linearRecur(n: Int) { - print("Recursion n = \(n)") + print("Recursion: n = \(n)") if n == 1 { return } linearRecur(n: n - 1) } -/* Exponential order */ +/* Quadratic complexity */ func quadratic(n: Int) { - // 2D list uses O(n^2) space + // A fully materialized two-dimensional list uses O(n^2) space; Swift may share repeated row storage let numList = Array(repeating: Array(repeating: 0, count: n), count: n) } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ @discardableResult func quadraticRecur(n: Int) -> Int { if n <= 0 { return 0 } - // Array nums has length n, n-1, ..., 2, 1 + // The lengths of array nums are n, n-1, ..., 2, 1 let nums = Array(repeating: 0, count: n) - print("In recursion n = \(n), nums length = \(nums.count)") + print("At recursion n = \(n), nums has length \(nums.count)") return quadraticRecur(n: n - 1) } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ func buildTree(n: Int) -> TreeNode? { if n == 0 { return nil @@ -80,18 +80,18 @@ func buildTree(n: Int) -> TreeNode? { @main enum SpaceComplexity { - /* Driver Code */ + /* Driver code */ static func main() { let n = 5 - // Constant order + // Constant complexity constant(n: n) - // Linear order + // Linear complexity linear(n: n) linearRecur(n: n) - // Exponential order + // Quadratic complexity quadratic(n: n) quadraticRecur(n: n) - // Exponential order + // Exponential complexity let root = buildTree(n: n) PrintUtil.printTree(root: root) } diff --git a/en/codes/swift/chapter_computational_complexity/time_complexity.swift b/en/codes/swift/chapter_computational_complexity/time_complexity.swift index 877e1cd11..bcb168d77 100644 --- a/en/codes/swift/chapter_computational_complexity/time_complexity.swift +++ b/en/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -4,7 +4,7 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Constant order */ +/* Constant complexity */ func constant(n: Int) -> Int { var count = 0 let size = 100_000 @@ -14,7 +14,7 @@ func constant(n: Int) -> Int { return count } -/* Linear order */ +/* Linear complexity */ func linear(n: Int) -> Int { var count = 0 for _ in 0 ..< n { @@ -23,20 +23,20 @@ func linear(n: Int) -> Int { return count } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ func arrayTraversal(nums: [Int]) -> Int { var count = 0 - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for _ in nums { count += 1 } return count } -/* Exponential order */ +/* Quadratic complexity */ func quadratic(n: Int) -> Int { var count = 0 - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for _ in 0 ..< n { for _ in 0 ..< n { count += 1 @@ -45,30 +45,30 @@ func quadratic(n: Int) -> Int { return count } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ func bubbleSort(nums: inout [Int]) -> Int { var count = 0 // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i in nums.indices.dropFirst().reversed() { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0 ..< i { if nums[j] > nums[j + 1] { // Swap nums[j] and nums[j + 1] let tmp = nums[j] nums[j] = nums[j + 1] nums[j + 1] = tmp - count += 3 // Element swap includes 3 unit operations + count += 3 // Swapping two elements takes 3 elementary operations } } } return count } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ func exponential(n: Int) -> Int { var count = 0 var base = 1 - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for _ in 0 ..< n { for _ in 0 ..< base { count += 1 @@ -79,7 +79,7 @@ func exponential(n: Int) -> Int { return count } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation) */ func expRecur(n: Int) -> Int { if n == 1 { return 1 @@ -87,7 +87,7 @@ func expRecur(n: Int) -> Int { return expRecur(n: n - 1) + expRecur(n: n - 1) + 1 } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation) */ func logarithmic(n: Int) -> Int { var count = 0 var n = n @@ -98,7 +98,7 @@ func logarithmic(n: Int) -> Int { return count } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation) */ func logRecur(n: Int) -> Int { if n <= 1 { return 0 @@ -106,7 +106,7 @@ func logRecur(n: Int) -> Int { return logRecur(n: n / 2) + 1 } -/* Linearithmic order */ +/* Linearithmic complexity */ func linearLogRecur(n: Int) -> Int { if n <= 1 { return 1 @@ -118,13 +118,13 @@ func linearLogRecur(n: Int) -> Int { return count } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ func factorialRecur(n: Int) -> Int { if n == 0 { return 1 } var count = 0 - // Split from 1 into n + // Split one problem into n subproblems for _ in 0 ..< n { count += factorialRecur(n: n - 1) } @@ -133,40 +133,40 @@ func factorialRecur(n: Int) -> Int { @main enum TimeComplexity { - /* Driver Code */ + /* Driver code */ static func main() { - // You can modify n to run and observe the trend of the number of operations for various complexities + // Try different values of n to see how the operation count grows for each complexity let n = 8 - print("Input data size n = \(n)") + print("Input size n = \(n)") var count = constant(n: n) - print("Constant-time operations count = \(count)") + print("Operation count for constant complexity = \(count)") count = linear(n: n) - print("Linear-time operations count = \(count)") + print("Operation count for linear complexity = \(count)") count = arrayTraversal(nums: Array(repeating: 0, count: n)) - print("Linear-time (array traversal) operations count = \(count)") + print("Operation count for linear complexity (array traversal) = \(count)") count = quadratic(n: n) - print("Quadratic-time operations count = \(count)") + print("Operation count for quadratic complexity = \(count)") var nums = Array(stride(from: n, to: 0, by: -1)) // [n,n-1,...,2,1] count = bubbleSort(nums: &nums) - print("Quadratic-time (bubble sort) operations count = \(count)") + print("Operation count for quadratic complexity (bubble sort) = \(count)") count = exponential(n: n) - print("Exponential-time (iterative) operations count = \(count)") + print("Operation count for exponential complexity (iterative implementation) = \(count)") count = expRecur(n: n) - print("Exponential-time (recursive) operations count = \(count)") + print("Operation count for exponential complexity (recursive implementation) = \(count)") count = logarithmic(n: n) - print("Logarithmic-time (iterative) operations count = \(count)") + print("Operation count for logarithmic complexity (iterative implementation) = \(count)") count = logRecur(n: n) - print("Logarithmic-time (recursive) operations count = \(count)") + print("Operation count for logarithmic complexity (recursive implementation) = \(count)") count = linearLogRecur(n: n) - print("Linearithmic-time (recursive) operations count = \(count)") + print("Operation count for linearithmic complexity (recursive implementation) = \(count)") count = factorialRecur(n: n) - print("Factorial-time (recursive) operations count = \(count)") + print("Operation count for factorial complexity (recursive implementation) = \(count)") } } diff --git a/en/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift b/en/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift index 18eb82bfe..9f7099252 100644 --- a/en/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift +++ b/en/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift @@ -4,20 +4,20 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ func randomNumbers(n: Int) -> [Int] { - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } var nums = Array(1 ... n) - // Randomly shuffle array elements + // Randomly shuffle the array elements nums.shuffle() return nums } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ func findOne(nums: [Int]) -> Int { for i in nums.indices { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if nums[i] == 1 { return i } @@ -27,14 +27,14 @@ func findOne(nums: [Int]) -> Int { @main enum WorstBestTimeComplexity { - /* Driver Code */ + /* Driver code */ static func main() { for _ in 0 ..< 10 { let n = 100 let nums = randomNumbers(n: n) let index = findOne(nums: nums) - print("Array [ 1, 2, ..., n ] after shuffling = \(nums)") - print("Index of number 1 is \(index)") + print("Array [1, 2, ..., n] after shuffling = \(nums)") + print("The index of 1 is \(index)") } } } diff --git a/en/codes/swift/chapter_divide_and_conquer/binary_search_recur.swift b/en/codes/swift/chapter_divide_and_conquer/binary_search_recur.swift index 591f8db05..b3ef7ad8d 100644 --- a/en/codes/swift/chapter_divide_and_conquer/binary_search_recur.swift +++ b/en/codes/swift/chapter_divide_and_conquer/binary_search_recur.swift @@ -6,38 +6,38 @@ /* Binary search: problem f(i, j) */ func dfs(nums: [Int], target: Int, i: Int, j: Int) -> Int { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if i > j { return -1 } // Calculate the midpoint index m let m = (i + j) / 2 if nums[m] < target { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums: nums, target: target, i: m + 1, j: j) } else if nums[m] > target { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums: nums, target: target, i: i, j: m - 1) } else { - // Found the target element, return its index + // The target element was found; return its index return m } } /* Binary search */ func binarySearch(nums: [Int], target: Int) -> Int { - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) dfs(nums: nums, target: target, i: nums.startIndex, j: nums.endIndex - 1) } @main enum BinarySearchRecur { - /* Driver Code */ + /* Driver code */ static func main() { let target = 6 let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] - // Binary search (closed interval on both sides) + // Binary search (closed interval) let index = binarySearch(nums: nums, target: target) print("Index of target element 6 = \(index)") } diff --git a/en/codes/swift/chapter_divide_and_conquer/build_tree.swift b/en/codes/swift/chapter_divide_and_conquer/build_tree.swift index 9f22e3890..582f8e1a8 100644 --- a/en/codes/swift/chapter_divide_and_conquer/build_tree.swift +++ b/en/codes/swift/chapter_divide_and_conquer/build_tree.swift @@ -6,15 +6,15 @@ import utils -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ func dfs(preorder: [Int], inorderMap: [Int: Int], i: Int, l: Int, r: Int) -> TreeNode? { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if r - l < 0 { return nil } // Initialize the root node let root = TreeNode(x: preorder[i]) - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees let m = inorderMap[preorder[i]]! // Subproblem: build the left subtree root.left = dfs(preorder: preorder, inorderMap: inorderMap, i: i + 1, l: l, r: m - 1) @@ -24,21 +24,21 @@ func dfs(preorder: [Int], inorderMap: [Int: Int], i: Int, l: Int, r: Int) -> Tre return root } -/* Build binary tree */ +/* Build a binary tree */ func buildTree(preorder: [Int], inorder: [Int]) -> TreeNode? { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index let inorderMap = inorder.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset } return dfs(preorder: preorder, inorderMap: inorderMap, i: inorder.startIndex, l: inorder.startIndex, r: inorder.endIndex - 1) } @main enum BuildTree { - /* Driver Code */ + /* Driver code */ static func main() { let preorder = [3, 9, 2, 1, 7] let inorder = [9, 3, 1, 2, 7] - print("Pre-order traversal = \(preorder)") - print("In-order traversal = \(inorder)") + print("Preorder traversal = \(preorder)") + print("Inorder traversal = \(inorder)") let root = buildTree(preorder: preorder, inorder: inorder) print("The constructed binary tree is:") diff --git a/en/codes/swift/chapter_divide_and_conquer/fast_power.swift b/en/codes/swift/chapter_divide_and_conquer/fast_power.swift index 131bd88e7..f8a11f30f 100644 --- a/en/codes/swift/chapter_divide_and_conquer/fast_power.swift +++ b/en/codes/swift/chapter_divide_and_conquer/fast_power.swift @@ -4,7 +4,7 @@ Author: Hello Algo Team */ -/* Exponentiation by squaring */ +/* Fast exponentiation */ func fastPow(x: Int, n: Int) -> Int { if n == 0 { return 1 diff --git a/en/codes/swift/chapter_divide_and_conquer/hanota.swift b/en/codes/swift/chapter_divide_and_conquer/hanota.swift index 001b8c656..2878c3fd6 100644 --- a/en/codes/swift/chapter_divide_and_conquer/hanota.swift +++ b/en/codes/swift/chapter_divide_and_conquer/hanota.swift @@ -4,9 +4,9 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Move a disk */ +/* Move one disk */ func move(src: inout [Int], tar: inout [Int]) { - // Take out a disk from the top of src + // Take a disk from the top of src let pan = src.popLast()! // Place the disk on top of tar tar.append(pan) @@ -14,14 +14,14 @@ func move(src: inout [Int], tar: inout [Int]) { /* Solve the Tower of Hanoi problem f(i) */ func dfs(i: Int, src: inout [Int], buf: inout [Int], tar: inout [Int]) { - // If there is only one disk left in src, move it directly to tar + // If only one disk in this subproblem remains on src, move it directly to tar if i == 1 { move(src: &src, tar: &tar) return } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i: i - 1, src: &src, buf: &tar, tar: &buf) - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src: &src, tar: &tar) // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i: i - 1, src: &buf, buf: &src, tar: &tar) @@ -30,27 +30,27 @@ func dfs(i: Int, src: inout [Int], buf: inout [Int], tar: inout [Int]) { /* Solve the Tower of Hanoi problem */ func solveHanota(A: inout [Int], B: inout [Int], C: inout [Int]) { let n = A.count - // The tail of the list is the top of the rod - // Move top n disks from src to C using B + // The end of each list represents the top of the peg + // Move the top n disks from A to C using B dfs(i: n, src: &A, buf: &B, tar: &C) } @main enum Hanota { - /* Driver Code */ + /* Driver code */ static func main() { - // The tail of the list is the top of the rod + // The end of each list represents the top of the peg var A = [5, 4, 3, 2, 1] var B: [Int] = [] var C: [Int] = [] - print("In initial state:") + print("Initial state:") print("A = \(A)") print("B = \(B)") print("C = \(C)") solveHanota(A: &A, B: &B, C: &C) - print("After disk movement is complete:") + print("After moving the disks:") print("A = \(A)") print("B = \(B)") print("C = \(C)") diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_backtrack.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_backtrack.swift index 1b8adf98a..d7c32faed 100644 --- a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_backtrack.swift +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_backtrack.swift @@ -6,39 +6,39 @@ /* Backtracking */ func backtrack(choices: [Int], state: Int, n: Int, res: inout [Int]) { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if state == n { res[0] += 1 } - // Traverse all choices + // Iterate over all choices for choice in choices { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if state + choice > n { continue } - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices: choices, state: state + choice, n: n, res: &res) // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ func climbingStairsBacktrack(n: Int) -> Int { - let choices = [1, 2] // Can choose to climb up 1 or 2 stairs - let state = 0 // Start climbing from the 0-th stair + let choices = [1, 2] // Choose to climb 1 or 2 steps + let state = 0 // Start climbing from step 0 var res: [Int] = [] - res.append(0) // Use res[0] to record the solution count + res.append(0) // Use res[0] to record the number of ways backtrack(choices: choices, state: state, n: n, res: &res) return res[0] } @main enum ClimbingStairsBacktrack { - /* Driver Code */ + /* Driver code */ static func main() { let n = 9 let res = climbingStairsBacktrack(n: n) - print("Climbing \(n) stairs has \(res) solutions") + print("Number of ways to climb \(n) steps: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_constraint_dp.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_constraint_dp.swift index d8c0c56a7..dfe7679d5 100644 --- a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_constraint_dp.swift +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_constraint_dp.swift @@ -4,19 +4,19 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ func climbingStairsConstraintDP(n: Int) -> Int { if n == 1 || n == 2 { return 1 } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions var dp = Array(repeating: Array(repeating: 0, count: 3), count: n + 1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1 dp[1][2] = 0 dp[2][1] = 0 dp[2][2] = 1 - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i in 3 ... n { dp[i][1] = dp[i - 1][2] dp[i][2] = dp[i - 2][1] + dp[i - 2][2] @@ -26,11 +26,11 @@ func climbingStairsConstraintDP(n: Int) -> Int { @main enum ClimbingStairsConstraintDP { - /* Driver Code */ + /* Driver code */ static func main() { let n = 9 let res = climbingStairsConstraintDP(n: n) - print("Climbing \(n) stairs has \(res) solutions") + print("Number of ways to climb \(n) steps: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs.swift index 3eb993ec9..f29e0a742 100644 --- a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs.swift +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs.swift @@ -6,7 +6,7 @@ /* Search */ func dfs(i: Int) -> Int { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if i == 1 || i == 2 { return i } @@ -15,18 +15,18 @@ func dfs(i: Int) -> Int { return count } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ func climbingStairsDFS(n: Int) -> Int { dfs(i: n) } @main enum ClimbingStairsDFS { - /* Driver Code */ + /* Driver code */ static func main() { let n = 9 let res = climbingStairsDFS(n: n) - print("Climbing \(n) stairs has \(res) solutions") + print("Number of ways to climb \(n) steps: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs_mem.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs_mem.swift index bf16ac5f7..512b233ac 100644 --- a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs_mem.swift +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs_mem.swift @@ -4,13 +4,13 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Memoization search */ +/* Memoized search */ func dfs(i: Int, mem: inout [Int]) -> Int { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if i == 1 || i == 2 { return i } - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if mem[i] != -1 { return mem[i] } @@ -21,20 +21,20 @@ func dfs(i: Int, mem: inout [Int]) -> Int { return count } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ func climbingStairsDFSMem(n: Int) -> Int { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded var mem = Array(repeating: -1, count: n + 1) return dfs(i: n, mem: &mem) } @main enum ClimbingStairsDFSMem { - /* Driver Code */ + /* Driver code */ static func main() { let n = 9 let res = climbingStairsDFSMem(n: n) - print("Climbing \(n) stairs has \(res) solutions") + print("Number of ways to climb \(n) steps: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dp.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dp.swift index 2161e2b3b..bcf0a761e 100644 --- a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dp.swift +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dp.swift @@ -4,24 +4,24 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ func climbingStairsDP(n: Int) -> Int { if n == 1 || n == 2 { return n } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions var dp = Array(repeating: 0, count: n + 1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1 dp[2] = 2 - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i in 3 ... n { dp[i] = dp[i - 1] + dp[i - 2] } return dp[n] } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ func climbingStairsDPComp(n: Int) -> Int { if n == 1 || n == 2 { return n @@ -36,14 +36,14 @@ func climbingStairsDPComp(n: Int) -> Int { @main enum ClimbingStairsDP { - /* Driver Code */ + /* Driver code */ static func main() { let n = 9 var res = climbingStairsDP(n: n) - print("Climbing \(n) stairs has \(res) solutions") + print("Number of ways to climb \(n) steps: \(res)") res = climbingStairsDPComp(n: n) - print("Climbing \(n) stairs has \(res) solutions") + print("Number of ways to climb \(n) steps: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/coin_change.swift b/en/codes/swift/chapter_dynamic_programming/coin_change.swift index 9220a6abd..02440f164 100644 --- a/en/codes/swift/chapter_dynamic_programming/coin_change.swift +++ b/en/codes/swift/chapter_dynamic_programming/coin_change.swift @@ -4,24 +4,24 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ func coinChangeDP(coins: [Int], amt: Int) -> Int { let n = coins.count let MAX = amt + 1 - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 1) - // State transition: first row and first column + // State transitions: first row and first column for a in 1 ... amt { dp[0][a] = MAX } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i in 1 ... n { for a in 1 ... amt { if coins[i - 1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[i][a] = dp[i - 1][a] } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) } } @@ -29,21 +29,21 @@ func coinChangeDP(coins: [Int], amt: Int) -> Int { return dp[n][amt] != MAX ? dp[n][amt] : -1 } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ func coinChangeDPComp(coins: [Int], amt: Int) -> Int { let n = coins.count let MAX = amt + 1 - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: MAX, count: amt + 1) dp[0] = 0 - // State transition + // State transitions for i in 1 ... n { for a in 1 ... amt { if coins[i - 1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[a] = dp[a] } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) } } @@ -53,17 +53,17 @@ func coinChangeDPComp(coins: [Int], amt: Int) -> Int { @main enum CoinChange { - /* Driver Code */ + /* Driver code */ static func main() { let coins = [1, 2, 5] let amt = 4 // Dynamic programming var res = coinChangeDP(coins: coins, amt: amt) - print("Minimum coins needed to make target amount is \(res)") + print("Minimum number of coins needed to make the target amount: \(res)") // Space-optimized dynamic programming res = coinChangeDPComp(coins: coins, amt: amt) - print("Minimum coins needed to make target amount is \(res)") + print("Minimum number of coins needed to make the target amount: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/coin_change_ii.swift b/en/codes/swift/chapter_dynamic_programming/coin_change_ii.swift index bd962268f..e1b523985 100644 --- a/en/codes/swift/chapter_dynamic_programming/coin_change_ii.swift +++ b/en/codes/swift/chapter_dynamic_programming/coin_change_ii.swift @@ -4,23 +4,23 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ func coinChangeIIDP(coins: [Int], amt: Int) -> Int { let n = coins.count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 1) - // Initialize first column + // Initialize the first column for i in 0 ... n { dp[i][0] = 1 } - // State transition + // State transitions for i in 1 ... n { for a in 1 ... amt { if coins[i - 1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[i][a] = dp[i - 1][a] } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] } } @@ -28,20 +28,20 @@ func coinChangeIIDP(coins: [Int], amt: Int) -> Int { return dp[n][amt] } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ func coinChangeIIDPComp(coins: [Int], amt: Int) -> Int { let n = coins.count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: 0, count: amt + 1) dp[0] = 1 - // State transition + // State transitions for i in 1 ... n { for a in 1 ... amt { if coins[i - 1] > a { - // If exceeds target amount, don't select coin i + // If the coin exceeds the current target amount a, do not choose coin i dp[a] = dp[a] } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]] } } @@ -51,17 +51,17 @@ func coinChangeIIDPComp(coins: [Int], amt: Int) -> Int { @main enum CoinChangeII { - /* Driver Code */ + /* Driver code */ static func main() { let coins = [1, 2, 5] let amt = 5 // Dynamic programming var res = coinChangeIIDP(coins: coins, amt: amt) - print("Number of coin combinations to make target amount is \(res)") + print("Number of coin combinations that make the target amount: \(res)") // Space-optimized dynamic programming res = coinChangeIIDPComp(coins: coins, amt: amt) - print("Number of coin combinations to make target amount is \(res)") + print("Number of coin combinations that make the target amount: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/edit_distance.swift b/en/codes/swift/chapter_dynamic_programming/edit_distance.swift index 2b1fc4cd4..fc0c1b431 100644 --- a/en/codes/swift/chapter_dynamic_programming/edit_distance.swift +++ b/en/codes/swift/chapter_dynamic_programming/edit_distance.swift @@ -4,83 +4,83 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ func editDistanceDFS(s: String, t: String, i: Int, j: Int) -> Int { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if i == 0, j == 0 { return 0 } - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if i == 0 { return j } - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if j == 0 { return i } - // If two characters are equal, skip both characters + // If the two current UTF-8 bytes match, skip both if s.utf8CString[i - 1] == t.utf8CString[j - 1] { return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) } - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1) let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j) let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) - // Return minimum edit steps + // Return the minimum number of edits return min(min(insert, delete), replace) + 1 } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ func editDistanceDFSMem(s: String, t: String, mem: inout [[Int]], i: Int, j: Int) -> Int { - // If both s and t are empty, return 0 + // If both prefixes of s and t are empty, return 0 if i == 0, j == 0 { return 0 } - // If s is empty, return length of t + // If the prefix of s is empty, return the prefix length of t if i == 0 { return j } - // If t is empty, return length of s + // If the prefix of t is empty, return the prefix length of s if j == 0 { return i } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][j] != -1 { return mem[i][j] } - // If two characters are equal, skip both characters + // If the two current UTF-8 bytes match, skip both if s.utf8CString[i - 1] == t.utf8CString[j - 1] { return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) } - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1) let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j) let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = min(min(insert, delete), replace) + 1 return mem[i][j] } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ func editDistanceDP(s: String, t: String) -> Int { let n = s.utf8CString.count let m = t.utf8CString.count var dp = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1) - // State transition: first row and first column + // State transitions: first row and first column for i in 1 ... n { dp[i][0] = i } for j in 1 ... m { dp[0][j] = j } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i in 1 ... n { for j in 1 ... m { if s.utf8CString[i - 1] == t.utf8CString[j - 1] { - // If two characters are equal, skip both characters + // If the two current UTF-8 bytes match, skip both dp[i][j] = dp[i - 1][j - 1] } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1 } } @@ -88,31 +88,31 @@ func editDistanceDP(s: String, t: String) -> Int { return dp[n][m] } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ func editDistanceDPComp(s: String, t: String) -> Int { let n = s.utf8CString.count let m = t.utf8CString.count var dp = Array(repeating: 0, count: m + 1) - // State transition: first row + // State transitions: first row for j in 1 ... m { dp[j] = j } - // State transition: rest of the rows + // State transitions: remaining rows for i in 1 ... n { - // State transition: first column + // State transitions: first column var leftup = dp[0] // Temporarily store dp[i-1, j-1] dp[0] = i - // State transition: rest of the columns + // State transitions: remaining columns for j in 1 ... m { let temp = dp[j] if s.utf8CString[i - 1] == t.utf8CString[j - 1] { - // If two characters are equal, skip both characters + // If the two current UTF-8 bytes match, skip both dp[j] = leftup } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1 } - leftup = temp // Update for next round's dp[i-1, j-1] + leftup = temp // Update to dp[i-1, j-1] for the next iteration } } return dp[m] @@ -120,7 +120,7 @@ func editDistanceDPComp(s: String, t: String) -> Int { @main enum EditDistance { - /* Driver Code */ + /* Driver code */ static func main() { let s = "bag" let t = "pack" @@ -129,19 +129,19 @@ enum EditDistance { // Brute-force search var res = editDistanceDFS(s: s, t: t, i: n, j: m) - print("Changing \(s) to \(t) requires minimum \(res) edits") + print("Changing \(s) to \(t) requires at least \(res) edits") - // Memoization search + // Memoized search var mem = Array(repeating: Array(repeating: -1, count: m + 1), count: n + 1) res = editDistanceDFSMem(s: s, t: t, mem: &mem, i: n, j: m) - print("Changing \(s) to \(t) requires minimum \(res) edits") + print("Changing \(s) to \(t) requires at least \(res) edits") // Dynamic programming res = editDistanceDP(s: s, t: t) - print("Changing \(s) to \(t) requires minimum \(res) edits") + print("Changing \(s) to \(t) requires at least \(res) edits") // Space-optimized dynamic programming res = editDistanceDPComp(s: s, t: t) - print("Changing \(s) to \(t) requires minimum \(res) edits") + print("Changing \(s) to \(t) requires at least \(res) edits") } } diff --git a/en/codes/swift/chapter_dynamic_programming/knapsack.swift b/en/codes/swift/chapter_dynamic_programming/knapsack.swift index a7b1908d7..82d7a0630 100644 --- a/en/codes/swift/chapter_dynamic_programming/knapsack.swift +++ b/en/codes/swift/chapter_dynamic_programming/knapsack.swift @@ -4,58 +4,58 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ func knapsackDFS(wgt: [Int], val: [Int], i: Int, c: Int) -> Int { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if i == 0 || c == 0 { return 0 } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if wgt[i - 1] > c { return knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c) } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i let no = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c) let yes = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c - wgt[i - 1]) + val[i - 1] - // Return the larger value of the two options + // Return the larger value from the two choices return max(no, yes) } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ func knapsackDFSMem(wgt: [Int], val: [Int], mem: inout [[Int]], i: Int, c: Int) -> Int { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if i == 0 || c == 0 { return 0 } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][c] != -1 { return mem[i][c] } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if wgt[i - 1] > c { return knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c) } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i let no = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c) let yes = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c - wgt[i - 1]) + val[i - 1] - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = max(no, yes) return mem[i][c] } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ func knapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { let n = wgt.count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 1) - // State transition + // State transitions for i in 1 ... n { for c in 1 ... cap { if wgt[i - 1] > c { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]) } } @@ -63,17 +63,17 @@ func knapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { return dp[n][cap] } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ func knapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { let n = wgt.count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: 0, count: cap + 1) - // State transition + // State transitions for i in 1 ... n { - // Traverse in reverse order + // Traverse in decreasing order for c in (1 ... cap).reversed() { if wgt[i - 1] <= c { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) } } @@ -83,7 +83,7 @@ func knapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { @main enum Knapsack { - /* Driver Code */ + /* Driver code */ static func main() { let wgt = [10, 20, 30, 40, 50] let val = [50, 120, 150, 210, 240] @@ -92,19 +92,19 @@ enum Knapsack { // Brute-force search var res = knapsackDFS(wgt: wgt, val: val, i: n, c: cap) - print("Maximum item value not exceeding knapsack capacity is \(res)") + print("Maximum total item value within the knapsack capacity: \(res)") - // Memoization search + // Memoized search var mem = Array(repeating: Array(repeating: -1, count: cap + 1), count: n + 1) res = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: n, c: cap) - print("Maximum item value not exceeding knapsack capacity is \(res)") + print("Maximum total item value within the knapsack capacity: \(res)") // Dynamic programming res = knapsackDP(wgt: wgt, val: val, cap: cap) - print("Maximum item value not exceeding knapsack capacity is \(res)") + print("Maximum total item value within the knapsack capacity: \(res)") // Space-optimized dynamic programming res = knapsackDPComp(wgt: wgt, val: val, cap: cap) - print("Maximum item value not exceeding knapsack capacity is \(res)") + print("Maximum total item value within the knapsack capacity: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/min_cost_climbing_stairs_dp.swift b/en/codes/swift/chapter_dynamic_programming/min_cost_climbing_stairs_dp.swift index d6a7df175..54e96e994 100644 --- a/en/codes/swift/chapter_dynamic_programming/min_cost_climbing_stairs_dp.swift +++ b/en/codes/swift/chapter_dynamic_programming/min_cost_climbing_stairs_dp.swift @@ -4,25 +4,25 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ func minCostClimbingStairsDP(cost: [Int]) -> Int { let n = cost.count - 1 if n == 1 || n == 2 { return cost[n] } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions var dp = Array(repeating: 0, count: n + 1) - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1] dp[2] = cost[2] - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for i in 3 ... n { dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] } return dp[n] } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ func minCostClimbingStairsDPComp(cost: [Int]) -> Int { let n = cost.count - 1 if n == 1 || n == 2 { @@ -37,15 +37,15 @@ func minCostClimbingStairsDPComp(cost: [Int]) -> Int { @main enum MinCostClimbingStairsDP { - /* Driver Code */ + /* Driver code */ static func main() { let cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1] - print("Input stair cost list is \(cost)") + print("Input stair costs: \(cost)") var res = minCostClimbingStairsDP(cost: cost) - print("Minimum cost to climb stairs is \(res)") + print("Minimum cost to climb the stairs: \(res)") res = minCostClimbingStairsDPComp(cost: cost) - print("Minimum cost to climb stairs is \(res)") + print("Minimum cost to climb the stairs: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/min_path_sum.swift b/en/codes/swift/chapter_dynamic_programming/min_path_sum.swift index 1f1c3b3f3..4e0f74583 100644 --- a/en/codes/swift/chapter_dynamic_programming/min_path_sum.swift +++ b/en/codes/swift/chapter_dynamic_programming/min_path_sum.swift @@ -4,61 +4,61 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ func minPathSumDFS(grid: [[Int]], i: Int, j: Int) -> Int { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if i == 0, j == 0 { return grid[0][0] } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return Int.max to represent an infinite cost if i < 0 || j < 0 { return .max } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) let up = minPathSumDFS(grid: grid, i: i - 1, j: j) let left = minPathSumDFS(grid: grid, i: i, j: j - 1) - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return min(left, up) + grid[i][j] } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ func minPathSumDFSMem(grid: [[Int]], mem: inout [[Int]], i: Int, j: Int) -> Int { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if i == 0, j == 0 { return grid[0][0] } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return Int.max to represent an infinite cost if i < 0 || j < 0 { return .max } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if mem[i][j] != -1 { return mem[i][j] } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above let up = minPathSumDFSMem(grid: grid, mem: &mem, i: i - 1, j: j) let left = minPathSumDFSMem(grid: grid, mem: &mem, i: i, j: j - 1) - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = min(left, up) + grid[i][j] return mem[i][j] } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ func minPathSumDP(grid: [[Int]]) -> Int { let n = grid.count let m = grid[0].count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: Array(repeating: 0, count: m), count: n) dp[0][0] = grid[0][0] - // State transition: first row + // State transitions: first row for j in 1 ..< m { dp[0][j] = dp[0][j - 1] + grid[0][j] } - // State transition: first column + // State transitions: first column for i in 1 ..< n { dp[i][0] = dp[i - 1][0] + grid[i][0] } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for i in 1 ..< n { for j in 1 ..< m { dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] @@ -67,22 +67,22 @@ func minPathSumDP(grid: [[Int]]) -> Int { return dp[n - 1][m - 1] } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ func minPathSumDPComp(grid: [[Int]]) -> Int { let n = grid.count let m = grid[0].count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: 0, count: m) - // State transition: first row + // State transitions: first row dp[0] = grid[0][0] for j in 1 ..< m { dp[j] = dp[j - 1] + grid[0][j] } - // State transition: rest of the rows + // State transitions: remaining rows for i in 1 ..< n { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0] - // State transition: rest of the columns + // State transitions: remaining columns for j in 1 ..< m { dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] } @@ -92,7 +92,7 @@ func minPathSumDPComp(grid: [[Int]]) -> Int { @main enum MinPathSum { - /* Driver Code */ + /* Driver code */ static func main() { let grid = [ [1, 3, 1, 5], @@ -105,19 +105,19 @@ enum MinPathSum { // Brute-force search var res = minPathSumDFS(grid: grid, i: n - 1, j: m - 1) - print("Minimum path sum from top-left to bottom-right is \(res)") + print("Minimum path sum from the top-left to the bottom-right corner: \(res)") - // Memoization search + // Memoized search var mem = Array(repeating: Array(repeating: -1, count: m), count: n) res = minPathSumDFSMem(grid: grid, mem: &mem, i: n - 1, j: m - 1) - print("Minimum path sum from top-left to bottom-right is \(res)") + print("Minimum path sum from the top-left to the bottom-right corner: \(res)") // Dynamic programming res = minPathSumDP(grid: grid) - print("Minimum path sum from top-left to bottom-right is \(res)") + print("Minimum path sum from the top-left to the bottom-right corner: \(res)") // Space-optimized dynamic programming res = minPathSumDPComp(grid: grid) - print("Minimum path sum from top-left to bottom-right is \(res)") + print("Minimum path sum from the top-left to the bottom-right corner: \(res)") } } diff --git a/en/codes/swift/chapter_dynamic_programming/unbounded_knapsack.swift b/en/codes/swift/chapter_dynamic_programming/unbounded_knapsack.swift index a5049bfd3..73945e844 100644 --- a/en/codes/swift/chapter_dynamic_programming/unbounded_knapsack.swift +++ b/en/codes/swift/chapter_dynamic_programming/unbounded_knapsack.swift @@ -4,19 +4,19 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ func unboundedKnapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { let n = wgt.count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 1) - // State transition + // State transitions for i in 1 ... n { for c in 1 ... cap { if wgt[i - 1] > c { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]) } } @@ -24,19 +24,19 @@ func unboundedKnapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { return dp[n][cap] } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ func unboundedKnapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { let n = wgt.count - // Initialize dp table + // Initialize the dp table var dp = Array(repeating: 0, count: cap + 1) - // State transition + // State transitions for i in 1 ... n { for c in 1 ... cap { if wgt[i - 1] > c { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c] } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) } } @@ -46,7 +46,7 @@ func unboundedKnapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { @main enum UnboundedKnapsack { - /* Driver Code */ + /* Driver code */ static func main() { let wgt = [1, 2, 3] let val = [5, 11, 15] @@ -54,10 +54,10 @@ enum UnboundedKnapsack { // Dynamic programming var res = unboundedKnapsackDP(wgt: wgt, val: val, cap: cap) - print("Maximum item value not exceeding knapsack capacity is \(res)") + print("Maximum total item value within the knapsack capacity: \(res)") // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt: wgt, val: val, cap: cap) - print("Maximum item value not exceeding knapsack capacity is \(res)") + print("Maximum total item value within the knapsack capacity: \(res)") } } diff --git a/en/codes/swift/chapter_graph/graph_adjacency_list.swift b/en/codes/swift/chapter_graph/graph_adjacency_list.swift index 463609865..8cd4683cc 100644 --- a/en/codes/swift/chapter_graph/graph_adjacency_list.swift +++ b/en/codes/swift/chapter_graph/graph_adjacency_list.swift @@ -6,9 +6,9 @@ import utils -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ public class GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors public private(set) var adjList: [Vertex: [Vertex]] /* Constructor */ @@ -27,49 +27,49 @@ public class GraphAdjList { adjList.count } - /* Add edge */ + /* Add an edge */ public func addEdge(vet1: Vertex, vet2: Vertex) { if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { - fatalError("Invalid parameter") + fatalError("Invalid arguments") } // Add edge vet1 - vet2 adjList[vet1]?.append(vet2) adjList[vet2]?.append(vet1) } - /* Remove edge */ + /* Remove an edge */ public func removeEdge(vet1: Vertex, vet2: Vertex) { if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { - fatalError("Invalid parameter") + fatalError("Invalid arguments") } // Remove edge vet1 - vet2 adjList[vet1]?.removeAll { $0 == vet2 } adjList[vet2]?.removeAll { $0 == vet1 } } - /* Add vertex */ + /* Add a vertex */ public func addVertex(vet: Vertex) { if adjList[vet] != nil { return } - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list adjList[vet] = [] } - /* Remove vertex */ + /* Remove a vertex */ public func removeVertex(vet: Vertex) { if adjList[vet] == nil { - fatalError("Invalid parameter") + fatalError("Invalid arguments") } - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list adjList.removeValue(forKey: vet) - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for key in adjList.keys { adjList[key]?.removeAll { $0 == vet } } } - /* Print adjacency list */ + /* Print the adjacency list */ public func print() { Swift.print("Adjacency list =") for (vertex, list) in adjList { @@ -83,37 +83,37 @@ public class GraphAdjList { @main enum GraphAdjacencyList { - /* Driver Code */ + /* Driver code */ static func main() { - /* Add edge */ + /* Initialize the undirected graph */ let v = Vertex.valsToVets(vals: [1, 3, 2, 5, 4]) let edges = [[v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[2], v[3]], [v[2], v[4]], [v[3], v[4]]] let graph = GraphAdjList(edges: edges) - print("\nAfter initialization, graph is") + print("\nGraph after initialization:") graph.print() - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.addEdge(vet1: v[0], vet2: v[2]) - print("\nAfter adding edge 1-2, graph is") + print("\nGraph after adding edge 1-2:") graph.print() - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.removeEdge(vet1: v[0], vet2: v[1]) - print("\nAfter removing edge 1-3, graph is") + print("\nGraph after removing edge 1-3:") graph.print() - /* Add vertex */ + /* Add a vertex */ let v5 = Vertex(val: 6) graph.addVertex(vet: v5) - print("\nAfter adding vertex 6, graph is") + print("\nGraph after adding vertex 6:") graph.print() - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.removeVertex(vet: v[1]) - print("\nAfter removing vertex 3, graph is") + print("\nGraph after removing vertex 3:") graph.print() } } diff --git a/en/codes/swift/chapter_graph/graph_adjacency_list_target.swift b/en/codes/swift/chapter_graph/graph_adjacency_list_target.swift index 463609865..8cd4683cc 100644 --- a/en/codes/swift/chapter_graph/graph_adjacency_list_target.swift +++ b/en/codes/swift/chapter_graph/graph_adjacency_list_target.swift @@ -6,9 +6,9 @@ import utils -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ public class GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors public private(set) var adjList: [Vertex: [Vertex]] /* Constructor */ @@ -27,49 +27,49 @@ public class GraphAdjList { adjList.count } - /* Add edge */ + /* Add an edge */ public func addEdge(vet1: Vertex, vet2: Vertex) { if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { - fatalError("Invalid parameter") + fatalError("Invalid arguments") } // Add edge vet1 - vet2 adjList[vet1]?.append(vet2) adjList[vet2]?.append(vet1) } - /* Remove edge */ + /* Remove an edge */ public func removeEdge(vet1: Vertex, vet2: Vertex) { if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { - fatalError("Invalid parameter") + fatalError("Invalid arguments") } // Remove edge vet1 - vet2 adjList[vet1]?.removeAll { $0 == vet2 } adjList[vet2]?.removeAll { $0 == vet1 } } - /* Add vertex */ + /* Add a vertex */ public func addVertex(vet: Vertex) { if adjList[vet] != nil { return } - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list adjList[vet] = [] } - /* Remove vertex */ + /* Remove a vertex */ public func removeVertex(vet: Vertex) { if adjList[vet] == nil { - fatalError("Invalid parameter") + fatalError("Invalid arguments") } - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list adjList.removeValue(forKey: vet) - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for key in adjList.keys { adjList[key]?.removeAll { $0 == vet } } } - /* Print adjacency list */ + /* Print the adjacency list */ public func print() { Swift.print("Adjacency list =") for (vertex, list) in adjList { @@ -83,37 +83,37 @@ public class GraphAdjList { @main enum GraphAdjacencyList { - /* Driver Code */ + /* Driver code */ static func main() { - /* Add edge */ + /* Initialize the undirected graph */ let v = Vertex.valsToVets(vals: [1, 3, 2, 5, 4]) let edges = [[v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[2], v[3]], [v[2], v[4]], [v[3], v[4]]] let graph = GraphAdjList(edges: edges) - print("\nAfter initialization, graph is") + print("\nGraph after initialization:") graph.print() - /* Add edge */ - // Vertices 1, 3 are v[0], v[1] + /* Add an edge */ + // Vertices 1 and 2 are v[0] and v[2] graph.addEdge(vet1: v[0], vet2: v[2]) - print("\nAfter adding edge 1-2, graph is") + print("\nGraph after adding edge 1-2:") graph.print() - /* Remove edge */ - // Vertex 3 is v[1] + /* Remove an edge */ + // Vertices 1 and 3 are v[0] and v[1] graph.removeEdge(vet1: v[0], vet2: v[1]) - print("\nAfter removing edge 1-3, graph is") + print("\nGraph after removing edge 1-3:") graph.print() - /* Add vertex */ + /* Add a vertex */ let v5 = Vertex(val: 6) graph.addVertex(vet: v5) - print("\nAfter adding vertex 6, graph is") + print("\nGraph after adding vertex 6:") graph.print() - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v[1] graph.removeVertex(vet: v[1]) - print("\nAfter removing vertex 3, graph is") + print("\nGraph after removing vertex 3:") graph.print() } } diff --git a/en/codes/swift/chapter_graph/graph_adjacency_matrix.swift b/en/codes/swift/chapter_graph/graph_adjacency_matrix.swift index a199d8120..c51d4bcfa 100644 --- a/en/codes/swift/chapter_graph/graph_adjacency_matrix.swift +++ b/en/codes/swift/chapter_graph/graph_adjacency_matrix.swift @@ -6,21 +6,21 @@ import utils -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat { - private var vertices: [Int] // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" - private var adjMat: [[Int]] // Adjacency matrix, where the row and column indices correspond to the "vertex index" + private var vertices: [Int] // Vertex list: elements are vertex values, and indices are vertex indices + private var adjMat: [[Int]] // Adjacency matrix: row and column indices correspond to vertex indices /* Constructor */ init(vertices: [Int], edges: [[Int]]) { self.vertices = [] adjMat = [] - // Add vertex + // Add all vertices for val in vertices { addVertex(val: val) } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add all edges + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for e in edges { addEdge(i: e[0], j: e[1]) } @@ -31,10 +31,10 @@ class GraphAdjMat { vertices.count } - /* Add vertex */ + /* Add a vertex */ func addVertex(val: Int) { let n = size() - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list vertices.append(val) // Add a row to the adjacency matrix let newRow = Array(repeating: 0, count: n) @@ -45,45 +45,45 @@ class GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ func removeVertex(index: Int) { if index >= size() { - fatalError("Out of bounds") + fatalError("Invalid index") } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list vertices.remove(at: index) - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix adjMat.remove(at: index) - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for i in adjMat.indices { adjMat[i].remove(at: index) } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices func addEdge(i: Int, j: Int) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if i < 0 || j < 0 || i >= size() || j >= size() || i == j { - fatalError("Out of bounds") + fatalError("Invalid index") } - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + // For an undirected graph, the adjacency matrix is symmetric about its main diagonal: entries (i, j) and (j, i) are equal adjMat[i][j] = 1 adjMat[j][i] = 1 } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices func removeEdge(i: Int, j: Int) { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if i < 0 || j < 0 || i >= size() || j >= size() || i == j { - fatalError("Out of bounds") + fatalError("Invalid index") } adjMat[i][j] = 0 adjMat[j][i] = 0 } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ func print() { Swift.print("Vertex list = ", terminator: "") Swift.print(vertices) @@ -94,37 +94,37 @@ class GraphAdjMat { @main enum GraphAdjacencyMatrix { - /* Driver Code */ + /* Driver code */ static func main() { - /* Add edge */ - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + /* Initialize the undirected graph */ + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices let vertices = [1, 3, 2, 5, 4] let edges = [[0, 1], [1, 2], [2, 3], [0, 3], [2, 4], [3, 4]] let graph = GraphAdjMat(vertices: vertices, edges: edges) - print("\nAfter initialization, graph is") + print("\nGraph after initialization:") graph.print() - /* Add edge */ - // Add vertex + /* Add an edge */ + // Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(i: 0, j: 2) - print("\nAfter adding edge 1-2, graph is") + print("\nGraph after adding edge 1-2:") graph.print() - /* Remove edge */ - // Vertices 1, 3 have indices 0, 1 respectively + /* Remove an edge */ + // Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(i: 0, j: 1) - print("\nAfter removing edge 1-3, graph is") + print("\nGraph after removing edge 1-3:") graph.print() - /* Add vertex */ + /* Add a vertex */ graph.addVertex(val: 6) - print("\nAfter adding vertex 6, graph is") + print("\nGraph after adding vertex 6:") graph.print() - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(index: 1) - print("\nAfter removing vertex 3, graph is") + print("\nGraph after removing vertex 3:") graph.print() } } diff --git a/en/codes/swift/chapter_graph/graph_bfs.swift b/en/codes/swift/chapter_graph/graph_bfs.swift index 9bce713a3..0307b5c7a 100644 --- a/en/codes/swift/chapter_graph/graph_bfs.swift +++ b/en/codes/swift/chapter_graph/graph_bfs.swift @@ -8,36 +8,36 @@ import graph_adjacency_list_target import utils /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { // Vertex traversal sequence var res: [Vertex] = [] - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices var visited: Set = [startVet] - // Queue used to implement BFS + // Use a queue to implement BFS var que: [Vertex] = [startVet] - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited while !que.isEmpty { let vet = que.removeFirst() // Dequeue the front vertex - res.append(vet) // Record visited vertex - // Traverse all adjacent vertices of this vertex + res.append(vet) // Record the visited vertex + // Traverse all neighbors of this vertex for adjVet in graph.adjList[vet] ?? [] { if visited.contains(adjVet) { - continue // Skip vertices that have been visited + continue // Skip vertices that have already been visited } - que.append(adjVet) // Only enqueue unvisited vertices + que.append(adjVet) // Enqueue only unvisited vertices visited.insert(adjVet) // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res } @main enum GraphBFS { - /* Driver Code */ + /* Driver code */ static func main() { - /* Add edge */ + /* Initialize the undirected graph */ let v = Vertex.valsToVets(vals: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) let edges = [ [v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[1], v[4]], @@ -45,12 +45,12 @@ enum GraphBFS { [v[4], v[7]], [v[5], v[8]], [v[6], v[7]], [v[7], v[8]], ] let graph = GraphAdjList(edges: edges) - print("\nAfter initialization, graph is") + print("\nGraph after initialization:") graph.print() /* Breadth-first traversal */ let res = graphBFS(graph: graph, startVet: v[0]) - print("\nBreadth-first traversal (BFS) vertex sequence is") + print("\nVertex sequence from breadth-first traversal (BFS):") print(Vertex.vetsToVals(vets: res)) } } diff --git a/en/codes/swift/chapter_graph/graph_dfs.swift b/en/codes/swift/chapter_graph/graph_dfs.swift index 359bce6c7..d4e2abd39 100644 --- a/en/codes/swift/chapter_graph/graph_dfs.swift +++ b/en/codes/swift/chapter_graph/graph_dfs.swift @@ -9,24 +9,24 @@ import utils /* Depth-first traversal helper function */ func dfs(graph: GraphAdjList, visited: inout Set, res: inout [Vertex], vet: Vertex) { - res.append(vet) // Record visited vertex + res.append(vet) // Record the visited vertex visited.insert(vet) // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for adjVet in graph.adjList[vet] ?? [] { if visited.contains(adjVet) { - continue // Skip vertices that have been visited + continue // Skip vertices that have already been visited } - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex dfs(graph: graph, visited: &visited, res: &res, vet: adjVet) } } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { // Vertex traversal sequence var res: [Vertex] = [] - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices var visited: Set = [] dfs(graph: graph, visited: &visited, res: &res, vet: startVet) return res @@ -34,21 +34,21 @@ func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { @main enum GraphDFS { - /* Driver Code */ + /* Driver code */ static func main() { - /* Add edge */ + /* Initialize the undirected graph */ let v = Vertex.valsToVets(vals: [0, 1, 2, 3, 4, 5, 6]) let edges = [ [v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[2], v[5]], [v[4], v[5]], [v[5], v[6]], ] let graph = GraphAdjList(edges: edges) - print("\nAfter initialization, graph is") + print("\nGraph after initialization:") graph.print() /* Depth-first traversal */ let res = graphDFS(graph: graph, startVet: v[0]) - print("\nDepth-first traversal (DFS) vertex sequence is") + print("\nVertex sequence from depth-first traversal (DFS):") print(Vertex.vetsToVals(vets: res)) } } diff --git a/en/codes/swift/chapter_greedy/coin_change_greedy.swift b/en/codes/swift/chapter_greedy/coin_change_greedy.swift index f88d36af2..c1cd01a6c 100644 --- a/en/codes/swift/chapter_greedy/coin_change_greedy.swift +++ b/en/codes/swift/chapter_greedy/coin_change_greedy.swift @@ -4,15 +4,15 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ func coinChangeGreedy(coins: [Int], amt: Int) -> Int { - // Assume coins list is sorted + // Assume the coins list is sorted in ascending order var i = coins.count - 1 var count = 0 var amt = amt - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while amt > 0 { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while i > 0 && coins[i] > amt { i -= 1 } @@ -20,35 +20,35 @@ func coinChangeGreedy(coins: [Int], amt: Int) -> Int { amt -= coins[i] count += 1 } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt == 0 ? count : -1 } @main enum CoinChangeGreedy { - /* Driver Code */ + /* Driver code */ static func main() { - // Greedy algorithm: Can guarantee finding the global optimal solution + // Greedy algorithm: guaranteed to find a globally optimal solution var coins = [1, 5, 10, 20, 50, 100] var amt = 186 var res = coinChangeGreedy(coins: coins, amt: amt) print("\ncoins = \(coins), amount = \(amt)") - print("Minimum coins needed to make \(amt) is \(res)") + print("Greedy coin count for amount \(amt): \(res)") - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 20, 50] amt = 60 res = coinChangeGreedy(coins: coins, amt: amt) print("\ncoins = \(coins), amount = \(amt)") - print("Minimum coins needed to make \(amt) is \(res)") - print("Actually the minimum number needed is 3, i.e., 20 + 20 + 20") + print("Greedy coin count for amount \(amt): \(res)") + print("The actual minimum is 3 coins: 20 + 20 + 20") - // Greedy algorithm: Cannot guarantee finding the global optimal solution + // Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 49, 50] amt = 98 res = coinChangeGreedy(coins: coins, amt: amt) print("\ncoins = \(coins), amount = \(amt)") - print("Minimum coins needed to make \(amt) is \(res)") - print("Actually the minimum number needed is 2, i.e., 49 + 49") + print("Greedy coin count for amount \(amt): \(res)") + print("The actual minimum is 2 coins: 49 + 49") } } diff --git a/en/codes/swift/chapter_greedy/fractional_knapsack.swift b/en/codes/swift/chapter_greedy/fractional_knapsack.swift index 8defc2985..85be255ac 100644 --- a/en/codes/swift/chapter_greedy/fractional_knapsack.swift +++ b/en/codes/swift/chapter_greedy/fractional_knapsack.swift @@ -15,24 +15,24 @@ class Item { } } -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ func fractionalKnapsack(wgt: [Int], val: [Int], cap: Int) -> Double { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value var items = zip(wgt, val).map { Item(w: $0, v: $1) } - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order items.sort { -(Double($0.v) / Double($0.w)) < -(Double($1.v) / Double($1.w)) } - // Loop for greedy selection + // Repeatedly make greedy choices var res = 0.0 var cap = cap for item in items { if item.w <= cap { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += Double(item.v) cap -= item.w } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += Double(item.v) / Double(item.w) * Double(cap) - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break } } @@ -41,7 +41,7 @@ func fractionalKnapsack(wgt: [Int], val: [Int], cap: Int) -> Double { @main enum FractionalKnapsack { - /* Driver Code */ + /* Driver code */ static func main() { // Item weight let wgt = [10, 20, 30, 40, 50] @@ -52,6 +52,6 @@ enum FractionalKnapsack { // Greedy algorithm let res = fractionalKnapsack(wgt: wgt, val: val, cap: cap) - print("Maximum item value not exceeding knapsack capacity is \(res)") + print("Maximum total item value within the knapsack capacity: \(res)") } } diff --git a/en/codes/swift/chapter_greedy/max_capacity.swift b/en/codes/swift/chapter_greedy/max_capacity.swift index e6fa56edd..70a28e4e4 100644 --- a/en/codes/swift/chapter_greedy/max_capacity.swift +++ b/en/codes/swift/chapter_greedy/max_capacity.swift @@ -4,18 +4,18 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ func maxCapacity(ht: [Int]) -> Int { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array var i = ht.startIndex, j = ht.endIndex - 1 - // Initial max capacity is 0 + // The initial maximum capacity is 0 var res = 0 - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while i < j { - // Update max capacity + // Update the maximum capacity let cap = min(ht[i], ht[j]) * (j - i) res = max(res, cap) - // Move the shorter board inward + // Move the shorter side inward if ht[i] < ht[j] { i += 1 } else { @@ -27,12 +27,12 @@ func maxCapacity(ht: [Int]) -> Int { @main enum MaxCapacity { - /* Driver Code */ + /* Driver code */ static func main() { let ht = [3, 8, 5, 2, 7, 7, 3, 4] // Greedy algorithm let res = maxCapacity(ht: ht) - print("Maximum capacity is \(res)") + print("Maximum container capacity: \(res)") } } diff --git a/en/codes/swift/chapter_greedy/max_product_cutting.swift b/en/codes/swift/chapter_greedy/max_product_cutting.swift index f70f17c72..c79edf748 100644 --- a/en/codes/swift/chapter_greedy/max_product_cutting.swift +++ b/en/codes/swift/chapter_greedy/max_product_cutting.swift @@ -10,24 +10,24 @@ func pow(_ x: Int, _ y: Int) -> Int { Int(Double(truncating: pow(Decimal(x), y) as NSDecimalNumber)) } -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ func maxProductCutting(n: Int) -> Int { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if n <= 3 { return 1 * (n - 1) } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder let a = n / 3 let b = n % 3 if b == 1 { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return pow(3, a - 1) * 2 * 2 } if b == 2 { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return pow(3, a) * 2 } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return pow(3, a) } @@ -38,6 +38,6 @@ enum MaxProductCutting { // Greedy algorithm let res = maxProductCutting(n: n) - print("Maximum cutting product is \(res)") + print("Maximum product after splitting: \(res)") } } diff --git a/en/codes/swift/chapter_hashing/array_hash_map.swift b/en/codes/swift/chapter_hashing/array_hash_map.swift index d70a742e1..af0f02368 100644 --- a/en/codes/swift/chapter_hashing/array_hash_map.swift +++ b/en/codes/swift/chapter_hashing/array_hash_map.swift @@ -6,12 +6,12 @@ import utils -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { private var buckets: [Pair?] init() { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets buckets = Array(repeating: nil, count: 100) } @@ -21,14 +21,14 @@ class ArrayHashMap { return index } - /* Query operation */ + /* Lookup operation */ func get(key: Int) -> String? { let index = hashFunc(key: key) let pair = buckets[index] return pair?.val } - /* Add operation */ + /* Insert operation */ func put(key: Int, val: String) { let pair = Pair(key: key, val: val) let index = hashFunc(key: key) @@ -38,7 +38,7 @@ class ArrayHashMap { /* Remove operation */ func remove(key: Int) { let index = hashFunc(key: key) - // Set to nil to delete + // Set to nil to mark the entry as removed buckets[index] = nil } @@ -57,7 +57,7 @@ class ArrayHashMap { buckets.compactMap { $0?.val } } - /* Print hash table */ + /* Print the hash table */ func print() { for pair in pairSet() { Swift.print("\(pair.key) -> \(pair.val)") @@ -67,42 +67,42 @@ class ArrayHashMap { @main enum _ArrayHashMap { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize hash table */ + /* Initialize the hash table */ let map = ArrayHashMap() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(key: 12836, val: "Xiao Ha") - map.put(key: 15937, val: "Xiao Luo") - map.put(key: 16750, val: "Xiao Suan") - map.put(key: 13276, val: "Xiao Fa") - map.put(key: 10583, val: "Xiao Ya") - print("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(key: 12836, val: "小哈") + map.put(key: 15937, val: "小啰") + map.put(key: 16750, val: "小算") + map.put(key: 13276, val: "小法") + map.put(key: 10583, val: "小鸭") + print("\nHash table after insertion:\nKey -> Value") map.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value let name = map.get(key: 15937)! - print("\nInput student ID 15937, found name \(name)") + print("\nLooking up student ID 15937 gives the name \(name)") /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(key: 10583) - print("\nAfter removing 10583, hash table is\nKey -> Value") + print("\nHash table after removing 10583:\nKey -> Value") map.print() - /* Traverse hash table */ - print("\nTraverse key-value pairs Key->Value") + /* Traverse the hash table */ + print("\nTraverse key-value pairs: Key->Value") for pair in map.pairSet() { print("\(pair.key) -> \(pair.val)") } - print("\nTraverse keys only Key") + print("\nTraverse keys only: Key") for key in map.keySet() { print(key) } - print("\nTraverse values only Value") + print("\nTraverse values only: Value") for val in map.valueSet() { print(val) } diff --git a/en/codes/swift/chapter_hashing/built_in_hash.swift b/en/codes/swift/chapter_hashing/built_in_hash.swift index e4946b345..fa1198681 100644 --- a/en/codes/swift/chapter_hashing/built_in_hash.swift +++ b/en/codes/swift/chapter_hashing/built_in_hash.swift @@ -8,30 +8,30 @@ import utils @main enum BuiltInHash { - /* Driver Code */ + /* Driver code */ static func main() { let num = 3 let hashNum = num.hashValue - print("Hash value of integer \(num) is \(hashNum)") + print("Hash value of integer \(num): \(hashNum)") let bol = true let hashBol = bol.hashValue - print("Hash value of boolean \(bol) is \(hashBol)") + print("Hash value of Boolean \(bol): \(hashBol)") let dec = 3.14159 let hashDec = dec.hashValue - print("Hash value of decimal \(dec) is \(hashDec)") + print("Hash value of floating-point number \(dec): \(hashDec)") - let str = "Hello Algo" + let str = "Hello 算法" let hashStr = str.hashValue - print("Hash value of string \(str) is \(hashStr)") + print("Hash value of string \(str): \(hashStr)") - let arr = [AnyHashable(12836), AnyHashable("Xiao Ha")] + let arr = [AnyHashable(12836), AnyHashable("小哈")] let hashTup = arr.hashValue - print("Hash value of array \(arr) is \(hashTup)") + print("Hash value of array \(arr): \(hashTup)") let obj = ListNode(x: 0) let hashObj = obj.hashValue - print("Hash value of node object \(obj) is \(hashObj)") + print("Hash value of node object \(obj): \(hashObj)") } } diff --git a/en/codes/swift/chapter_hashing/hash_map.swift b/en/codes/swift/chapter_hashing/hash_map.swift index 1e35ce043..18db0a712 100644 --- a/en/codes/swift/chapter_hashing/hash_map.swift +++ b/en/codes/swift/chapter_hashing/hash_map.swift @@ -8,42 +8,42 @@ import utils @main enum HashMap { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize hash table */ + /* Initialize the hash table */ var map: [Int: String] = [:] - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map[12836] = "Xiao Ha" - map[15937] = "Xiao Luo" - map[16750] = "Xiao Suan" - map[13276] = "Xiao Fa" - map[10583] = "Xiao Ya" - print("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map[12836] = "小哈" + map[15937] = "小啰" + map[16750] = "小算" + map[13276] = "小法" + map[10583] = "小鸭" + print("\nHash table after insertion:\nKey -> Value") PrintUtil.printHashMap(map: map) - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value let name = map[15937]! - print("\nInput student ID 15937, found name \(name)") + print("\nLooking up student ID 15937 gives the name \(name)") /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.removeValue(forKey: 10583) - print("\nAfter removing 10583, hash table is\nKey -> Value") + print("\nHash table after removing 10583:\nKey -> Value") PrintUtil.printHashMap(map: map) - /* Traverse hash table */ - print("\nTraverse key-value pairs Key->Value") + /* Traverse the hash table */ + print("\nTraverse key-value pairs: Key->Value") for (key, value) in map { print("\(key) -> \(value)") } - print("\nTraverse keys only Key") + print("\nTraverse keys only: Key") for key in map.keys { print(key) } - print("\nTraverse values only Value") + print("\nTraverse values only: Value") for value in map.values { print(value) } diff --git a/en/codes/swift/chapter_hashing/hash_map_chaining.swift b/en/codes/swift/chapter_hashing/hash_map_chaining.swift index ab6687f0b..76de99360 100644 --- a/en/codes/swift/chapter_hashing/hash_map_chaining.swift +++ b/en/codes/swift/chapter_hashing/hash_map_chaining.swift @@ -10,8 +10,8 @@ import utils class HashMapChaining { var size: Int // Number of key-value pairs var capacity: Int // Hash table capacity - var loadThres: Double // Load factor threshold for triggering expansion - var extendRatio: Int // Expansion multiplier + var loadThres: Double // Load factor threshold that triggers capacity growth + var extendRatio: Int // Capacity growth factor var buckets: [[Pair]] // Bucket array /* Constructor */ @@ -33,36 +33,36 @@ class HashMapChaining { Double(size) / Double(capacity) } - /* Query operation */ + /* Lookup operation */ func get(key: Int) -> String? { let index = hashFunc(key: key) let bucket = buckets[index] - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for pair in bucket { if pair.key == key { return pair.val } } - // Return nil if key not found + // Return nil if key is not found return nil } - /* Add operation */ + /* Insert operation */ func put(key: Int, val: String) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if loadFactor() > loadThres { extend() } let index = hashFunc(key: key) let bucket = buckets[index] - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for pair in bucket { if pair.key == key { pair.val = val return } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end let pair = Pair(key: key, val: val) buckets[index].append(pair) size += 1 @@ -72,7 +72,7 @@ class HashMapChaining { func remove(key: Int) { let index = hashFunc(key: key) let bucket = buckets[index] - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (pairIndex, pair) in bucket.enumerated() { if pair.key == key { buckets[index].remove(at: pairIndex) @@ -82,15 +82,15 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ func extend() { - // Temporarily store the original hash table + // Save the original bucket array let bucketsTmp = buckets - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio buckets = Array(repeating: [], count: capacity) size = 0 - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for bucket in bucketsTmp { for pair in bucket { put(key: pair.key, val: pair.val) @@ -98,7 +98,7 @@ class HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ func print() { for bucket in buckets { let res = bucket.map { "\($0.key) -> \($0.val)" } @@ -109,30 +109,30 @@ class HashMapChaining { @main enum _HashMapChaining { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize hash table */ + /* Initialize the hash table */ let map = HashMapChaining() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(key: 12836, val: "Xiao Ha") - map.put(key: 15937, val: "Xiao Luo") - map.put(key: 16750, val: "Xiao Suan") - map.put(key: 13276, val: "Xiao Fa") - map.put(key: 10583, val: "Xiao Ya") - print("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(key: 12836, val: "小哈") + map.put(key: 15937, val: "小啰") + map.put(key: 16750, val: "小算") + map.put(key: 13276, val: "小法") + map.put(key: 10583, val: "小鸭") + print("\nHash table after insertion:\nKey -> Value") map.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value let name = map.get(key: 13276) - print("\nInput student ID 13276, found name \(name!)") + print("\nLooking up student ID 13276 gives the name \(name!)") /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(key: 12836) - print("\nAfter removing 12836, hash table is\nKey -> Value") + print("\nHash table after removing 12836:\nKey -> Value") map.print() } } diff --git a/en/codes/swift/chapter_hashing/hash_map_open_addressing.swift b/en/codes/swift/chapter_hashing/hash_map_open_addressing.swift index 7d5f21297..f918ca30d 100644 --- a/en/codes/swift/chapter_hashing/hash_map_open_addressing.swift +++ b/en/codes/swift/chapter_hashing/hash_map_open_addressing.swift @@ -10,10 +10,10 @@ import utils class HashMapOpenAddressing { var size: Int // Number of key-value pairs var capacity: Int // Hash table capacity - var loadThres: Double // Load factor threshold for triggering expansion - var extendRatio: Int // Expansion multiplier + var loadThres: Double // Load factor threshold that triggers capacity growth + var extendRatio: Int // Capacity growth factor var buckets: [Pair?] // Bucket array - var TOMBSTONE: Pair // Removal marker + var TOMBSTONE: Pair // Tombstone marker /* Constructor */ init() { @@ -35,83 +35,83 @@ class HashMapOpenAddressing { Double(size) / Double(capacity) } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ func findBucket(key: Int) -> Int { var index = hashFunc(key: key) var firstTombstone = -1 - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while buckets[index] != nil { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if buckets[index]!.key == key { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if firstTombstone != -1 { buckets[firstTombstone] = buckets[index] buckets[index] = TOMBSTONE - return firstTombstone // Return the moved bucket index + return firstTombstone // Return the bucket index after the move } - return index // Return bucket index + return index // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if firstTombstone == -1 && buckets[index] == TOMBSTONE { firstTombstone = index } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % capacity } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone == -1 ? index : firstTombstone } - /* Query operation */ + /* Lookup operation */ func get(key: Int) -> String? { - // Search for bucket index corresponding to key + // Find the bucket index for key let index = findBucket(key: key) - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if buckets[index] != nil, buckets[index] != TOMBSTONE { return buckets[index]!.val } - // If key-value pair does not exist, return null + // Return nil if the key-value pair is absent return nil } - /* Add operation */ + /* Insert operation */ func put(key: Int, val: String) { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if loadFactor() > loadThres { extend() } - // Search for bucket index corresponding to key + // Find the bucket index for key let index = findBucket(key: key) - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if buckets[index] != nil, buckets[index] != TOMBSTONE { buckets[index]!.val = val return } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it buckets[index] = Pair(key: key, val: val) size += 1 } /* Remove operation */ func remove(key: Int) { - // Search for bucket index corresponding to key + // Find the bucket index for key let index = findBucket(key: key) - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if buckets[index] != nil, buckets[index] != TOMBSTONE { buckets[index] = TOMBSTONE size -= 1 } } - /* Expand hash table */ + /* Grow the hash table capacity */ func extend() { - // Temporarily store the original hash table + // Save the original bucket array let bucketsTmp = buckets - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity capacity *= extendRatio buckets = Array(repeating: nil, count: capacity) size = 0 - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for pair in bucketsTmp { if let pair, pair != TOMBSTONE { put(key: pair.key, val: pair.val) @@ -119,7 +119,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ func print() { for pair in buckets { if pair == nil { @@ -135,30 +135,30 @@ class HashMapOpenAddressing { @main enum _HashMapOpenAddressing { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize hash table */ + /* Initialize the hash table */ let map = HashMapOpenAddressing() - /* Add operation */ - // Add key-value pair (key, value) to the hash table - map.put(key: 12836, val: "Xiao Ha") - map.put(key: 15937, val: "Xiao Luo") - map.put(key: 16750, val: "Xiao Suan") - map.put(key: 13276, val: "Xiao Fa") - map.put(key: 10583, val: "Xiao Ya") - print("\nAfter adding is complete, hash table is\nKey -> Value") + /* Insert operation */ + // Insert the key-value pair (key, value) into the hash table + map.put(key: 12836, val: "小哈") + map.put(key: 15937, val: "小啰") + map.put(key: 16750, val: "小算") + map.put(key: 13276, val: "小法") + map.put(key: 10583, val: "小鸭") + print("\nHash table after insertion:\nKey -> Value") map.print() - /* Query operation */ - // Input key into hash table to get value + /* Lookup operation */ + // Look up key in the hash table to obtain value let name = map.get(key: 13276) - print("\nInput student ID 13276, found name \(name!)") + print("\nLooking up student ID 13276 gives the name \(name!)") /* Remove operation */ - // Remove key-value pair (key, value) from hash table + // Remove the key-value pair (key, value) from the hash table map.remove(key: 16750) - print("\nAfter removing 16750, hash table is\nKey -> Value") + print("\nHash table after removing 16750:\nKey -> Value") map.print() } } diff --git a/en/codes/swift/chapter_hashing/simple_hash.swift b/en/codes/swift/chapter_hashing/simple_hash.swift index d69c4d7f7..d26d5ef22 100644 --- a/en/codes/swift/chapter_hashing/simple_hash.swift +++ b/en/codes/swift/chapter_hashing/simple_hash.swift @@ -40,7 +40,7 @@ func xorHash(key: String) -> Int { return hash & MODULUS } -/* Rotational hash */ +/* Rotating hash */ func rotHash(key: String) -> Int { var hash = 0 let MODULUS = 1_000_000_007 @@ -54,20 +54,20 @@ func rotHash(key: String) -> Int { @main enum SimpleHash { - /* Driver Code */ + /* Driver code */ static func main() { - let key = "Hello Algo" + let key = "Hello 算法" var hash = addHash(key: key) - print("Additive hash value is \(hash)") + print("Additive hash value: \(hash)") hash = mulHash(key: key) - print("Multiplicative hash value is \(hash)") + print("Multiplicative hash value: \(hash)") hash = xorHash(key: key) - print("XOR hash value is \(hash)") + print("XOR hash value: \(hash)") hash = rotHash(key: key) - print("Rotational hash value is \(hash)") + print("Rotating hash value: \(hash)") } } diff --git a/en/codes/swift/chapter_heap/heap.swift b/en/codes/swift/chapter_heap/heap.swift index 2b8c13f7a..0819b47a6 100644 --- a/en/codes/swift/chapter_heap/heap.swift +++ b/en/codes/swift/chapter_heap/heap.swift @@ -9,54 +9,54 @@ import utils func testPush(heap: inout Heap, val: Int) { heap.insert(val) - print("\nAfter element \(val) pushes to heap\n") + print("\nAfter inserting element \(val) into the heap:\n") PrintUtil.printHeap(queue: heap.unordered) } func testPop(heap: inout Heap) { let val = heap.removeMax() - print("\nAfter heap top element \(val) pops from heap\n") + print("\nAfter removing the maximum element \(val) from the heap:\n") PrintUtil.printHeap(queue: heap.unordered) } @main enum _Heap { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize heap */ - // Swift's Heap type supports both max heap and min heap + /* Initialize the heap */ + // The Heap type in Swift Collections supports both max heap and min heap operations var heap = Heap() - /* Element enters heap */ + /* Push an element onto the heap */ testPush(heap: &heap, val: 1) testPush(heap: &heap, val: 3) testPush(heap: &heap, val: 2) testPush(heap: &heap, val: 5) testPush(heap: &heap, val: 4) - /* Check if heap is empty */ + /* Get the maximum element of the heap */ let peek = heap.max() - print("\nHeap top element is \(peek!)\n") + print("\nMaximum heap element: \(peek!)\n") - /* Time complexity is O(n), not O(nlogn) */ + /* Remove the maximum element from the heap */ testPop(heap: &heap) testPop(heap: &heap) testPop(heap: &heap) testPop(heap: &heap) testPop(heap: &heap) - /* Get heap size */ + /* Get the heap size */ let size = heap.count - print("\nHeap size is \(size)\n") + print("\nNumber of heap elements: \(size)\n") - /* Check if heap is empty */ + /* Check whether the heap is empty */ let isEmpty = heap.isEmpty - print("\nIs heap empty \(isEmpty)\n") + print("\nIs the heap empty: \(isEmpty)\n") - /* Input list and build heap */ - // Time complexity is O(n), not O(nlogn) + /* Build a heap from an input list */ + // The time complexity is O(n), not O(nlogn) let heap2 = Heap([1, 3, 2, 5, 4]) - print("\nAfter input list and build heap") + print("\nAfter building a heap from the input list:") PrintUtil.printHeap(queue: heap2.unordered) } } diff --git a/en/codes/swift/chapter_heap/my_heap.swift b/en/codes/swift/chapter_heap/my_heap.swift index 86e00d07e..8901952b7 100644 --- a/en/codes/swift/chapter_heap/my_heap.swift +++ b/en/codes/swift/chapter_heap/my_heap.swift @@ -10,29 +10,29 @@ import utils class MaxHeap { private var maxHeap: [Int] - /* Constructor, build heap based on input list */ + /* Constructor: build a heap from the input list */ init(nums: [Int]) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order maxHeap = nums - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for i in (0 ... parent(i: size() - 1)).reversed() { siftDown(i: i) } } - /* Get index of left child node */ + /* Get the left child index */ private func left(i: Int) -> Int { 2 * i + 1 } - /* Get index of right child node */ + /* Get the right child index */ private func right(i: Int) -> Int { 2 * i + 2 } - /* Get index of parent node */ + /* Get the parent index */ private func parent(i: Int) -> Int { - (i - 1) / 2 // Floor division + (i - 1) / 2 // Integer division truncates toward zero } /* Swap elements */ @@ -40,67 +40,67 @@ class MaxHeap { maxHeap.swapAt(i, j) } - /* Get heap size */ + /* Get the heap size */ func size() -> Int { maxHeap.count } - /* Check if heap is empty */ + /* Check whether the heap is empty */ func isEmpty() -> Bool { size() == 0 } - /* Access top element */ + /* Access the top element of the heap */ func peek() -> Int { maxHeap[0] } - /* Element enters heap */ + /* Push an element onto the heap */ func push(val: Int) { - // Add node + // Add a node maxHeap.append(val) - // Heapify from bottom to top + // Sift up siftUp(i: size() - 1) } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ private func siftUp(i: Int) { var i = i while true { - // Get parent node of node i + // Get the parent of node i let p = parent(i: i) - // When "crossing root node" or "node needs no repair", end heapify + // Stop when the parent index is invalid or the node already satisfies the heap order if p < 0 || maxHeap[i] <= maxHeap[p] { break } - // Swap two nodes + // Swap the two nodes swap(i: i, j: p) - // Loop upward heapify + // Continue sifting up i = p } } - /* Element exits heap */ + /* Pop an element from the heap */ func pop() -> Int { - // Handle empty case + // Handle an empty heap if isEmpty() { fatalError("Heap is empty") } - // Delete node + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) swap(i: 0, j: size() - 1) - // Remove node + // Remove a node let val = maxHeap.remove(at: size() - 1) - // Return top element + // Sift down siftDown(i: 0) - // Return heap top element + // Return the top element of the heap return val } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ private func siftDown(i: Int) { var i = i while true { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma let l = left(i: i) let r = right(i: i) var ma = i @@ -110,18 +110,18 @@ class MaxHeap { if r < size(), maxHeap[r] > maxHeap[ma] { ma = r } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if ma == i { break } - // Swap two nodes + // Swap the two nodes swap(i: i, j: ma) - // Loop downwards heapification + // Continue sifting down i = ma } } - /* Driver Code */ + /* Print the heap (binary tree) */ func print() { let queue = maxHeap PrintUtil.printHeap(queue: queue) @@ -130,34 +130,34 @@ class MaxHeap { @main enum MyHeap { - /* Driver Code */ + /* Driver code */ static func main() { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ let maxHeap = MaxHeap(nums: [9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) - print("\nAfter inputting list and building heap") + print("\nAfter building a heap from the input list:") maxHeap.print() - /* Check if heap is empty */ + /* Get the top element of the heap */ var peek = maxHeap.peek() - print("\nHeap top element is \(peek)") + print("\nTop heap element: \(peek)") - /* Element enters heap */ + /* Push an element onto the heap */ let val = 7 maxHeap.push(val: val) - print("\nAfter element \(val) pushes to heap") + print("\nAfter inserting element \(val) into the heap:") maxHeap.print() - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.pop() - print("\nAfter heap top element \(peek) pops from heap") + print("\nAfter removing the top element \(peek) from the heap:") maxHeap.print() - /* Get heap size */ + /* Get the heap size */ let size = maxHeap.size() - print("\nHeap size is \(size)") + print("\nNumber of heap elements: \(size)") - /* Check if heap is empty */ + /* Check whether the heap is empty */ let isEmpty = maxHeap.isEmpty() - print("\nIs heap empty \(isEmpty)") + print("\nIs the heap empty: \(isEmpty)") } } diff --git a/en/codes/swift/chapter_heap/top_k.swift b/en/codes/swift/chapter_heap/top_k.swift index 9581e791d..fb3315916 100644 --- a/en/codes/swift/chapter_heap/top_k.swift +++ b/en/codes/swift/chapter_heap/top_k.swift @@ -7,13 +7,13 @@ import HeapModule import utils -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ func topKHeap(nums: [Int], k: Int) -> [Int] { - // Initialize min heap and build heap with first k elements + // Build a heap from the first k elements and use it as a min heap var heap = Heap(nums.prefix(k)) - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for i in nums.indices.dropFirst(k) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element exceeds the heap minimum, remove the minimum and insert the current element if nums[i] > heap.min()! { _ = heap.removeMin() heap.insert(nums[i]) @@ -24,13 +24,13 @@ func topKHeap(nums: [Int], k: Int) -> [Int] { @main enum TopK { - /* Driver Code */ + /* Driver code */ static func main() { let nums = [1, 7, 6, 3, 2] let k = 3 let res = topKHeap(nums: nums, k: k) - print("The largest \(k) elements are") + print("The \(k) largest elements are:") PrintUtil.printHeap(queue: res) } } diff --git a/en/codes/swift/chapter_searching/binary_search.swift b/en/codes/swift/chapter_searching/binary_search.swift index e473e8e9f..45ef1b274 100644 --- a/en/codes/swift/chapter_searching/binary_search.swift +++ b/en/codes/swift/chapter_searching/binary_search.swift @@ -4,58 +4,58 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ func binarySearch(nums: [Int], target: Int) -> Int { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements var i = nums.startIndex var j = nums.endIndex - 1 - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while i <= j { let m = i + (j - i) / 2 // Calculate the midpoint index m - if nums[m] < target { // This means target is in the interval [m+1, j] + if nums[m] < target { // In this case, target must be in [m+1, j] if it exists i = m + 1 - } else if nums[m] > target { // This means target is in the interval [i, m-1] + } else if nums[m] > target { // In this case, target must be in [i, m-1] if it exists j = m - 1 - } else { // Found the target element, return its index + } else { // The target element was found; return its index return m } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ func binarySearchLCRO(nums: [Int], target: Int) -> Int { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last var i = nums.startIndex var j = nums.endIndex - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while i < j { let m = i + (j - i) / 2 // Calculate the midpoint index m - if nums[m] < target { // This means target is in the interval [m+1, j) + if nums[m] < target { // In this case, target must be in [m+1, j) if it exists i = m + 1 - } else if nums[m] > target { // This means target is in the interval [i, m) + } else if nums[m] > target { // In this case, target must be in [i, m) if it exists j = m - } else { // Found the target element, return its index + } else { // The target element was found; return its index return m } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } @main enum BinarySearch { - /* Driver Code */ + /* Driver code */ static func main() { let target = 6 let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] - /* Binary search (closed interval on both sides) */ + /* Binary search (closed interval) */ var index = binarySearch(nums: nums, target: target) print("Index of target element 6 = \(index)") - /* Binary search (left-closed right-open interval) */ + /* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums: nums, target: target) print("Index of target element 6 = \(index)") } diff --git a/en/codes/swift/chapter_searching/binary_search_edge.swift b/en/codes/swift/chapter_searching/binary_search_edge.swift index 702e0dc41..03fc05a81 100644 --- a/en/codes/swift/chapter_searching/binary_search_edge.swift +++ b/en/codes/swift/chapter_searching/binary_search_edge.swift @@ -6,46 +6,46 @@ import binary_search_insertion_target -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ func binarySearchLeftEdge(nums: [Int], target: Int) -> Int { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target let i = binarySearchInsertion(nums: nums, target: target) - // Target not found, return -1 + // If target was not found, return -1 if i == nums.endIndex || nums[i] != target { return -1 } - // Found target, return index i + // Target was found; return index i return i } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ func binarySearchRightEdge(nums: [Int], target: Int) -> Int { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 let i = binarySearchInsertion(nums: nums, target: target + 1) - // j points to the rightmost target, i points to the first element greater than target + // i is the first index with a value greater than target (or nums.endIndex); j is the candidate index of the rightmost target let j = i - 1 - // Target not found, return -1 + // If target was not found, return -1 if j == -1 || nums[j] != target { return -1 } - // Found target, return index j + // Target was found; return index j return j } @main enum BinarySearchEdge { - /* Driver Code */ + /* Driver code */ static func main() { - // Array with duplicate elements + // Array containing duplicate elements let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] print("\nArray nums = \(nums)") - // Binary search left and right boundaries + // Find the left and right boundaries using binary search for target in [6, 7] { var index = binarySearchLeftEdge(nums: nums, target: target) - print("Leftmost element \(target) index is \(index)") + print("Index of the leftmost occurrence of \(target): \(index)") index = binarySearchRightEdge(nums: nums, target: target) - print("Rightmost element \(target) index is \(index)") + print("Index of the rightmost occurrence of \(target): \(index)") } } } diff --git a/en/codes/swift/chapter_searching/binary_search_insertion.swift b/en/codes/swift/chapter_searching/binary_search_insertion.swift index 52e10a5fc..f9f855827 100644 --- a/en/codes/swift/chapter_searching/binary_search_insertion.swift +++ b/en/codes/swift/chapter_searching/binary_search_insertion.swift @@ -4,38 +4,38 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int { - // Initialize closed interval [0, n-1] + // Initialize the closed interval [0, n-1] var i = nums.startIndex var j = nums.endIndex - 1 while i <= j { let m = i + (j - i) / 2 // Calculate the midpoint index m if nums[m] < target { - i = m + 1 // target is in the interval [m+1, j] + i = m + 1 // Target must be in [m+1, j] if it exists } else if nums[m] > target { - j = m - 1 // target is in the interval [i, m-1] + j = m - 1 // Target must be in [i, m-1] if it exists } else { - return m // Found target, return insertion point m + return m // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { - // Initialize closed interval [0, n-1] + // Initialize the closed interval [0, n-1] var i = nums.startIndex var j = nums.endIndex - 1 while i <= j { let m = i + (j - i) / 2 // Calculate the midpoint index m if nums[m] < target { - i = m + 1 // target is in the interval [m+1, j] + i = m + 1 // Target must be in [m+1, j] if it exists } else if nums[m] > target { - j = m - 1 // target is in the interval [i, m-1] + j = m - 1 // Target must be in [i, m-1] if it exists } else { - j = m - 1 // The rightmost element less than target is in the interval [i, m-1] + j = m - 1 // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i @@ -46,24 +46,24 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { @main enum BinarySearchInsertion { - /* Driver Code */ + /* Driver code */ static func main() { - // Array without duplicate elements + // Array with no duplicate elements var nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] print("\nArray nums = \(nums)") - // Binary search for insertion point + // Find an insertion point using binary search for target in [6, 9] { let index = binarySearchInsertionSimple(nums: nums, target: target) - print("Insertion point index for element \(target) is \(index)") + print("Insertion index for element \(target): \(index)") } - // Array with duplicate elements + // Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] print("\nArray nums = \(nums)") - // Binary search for insertion point + // Find an insertion point using binary search for target in [2, 6, 20] { let index = binarySearchInsertion(nums: nums, target: target) - print("Insertion point index for element \(target) is \(index)") + print("Insertion index for element \(target): \(index)") } } } diff --git a/en/codes/swift/chapter_searching/binary_search_insertion_target.swift b/en/codes/swift/chapter_searching/binary_search_insertion_target.swift index 52e10a5fc..f9f855827 100644 --- a/en/codes/swift/chapter_searching/binary_search_insertion_target.swift +++ b/en/codes/swift/chapter_searching/binary_search_insertion_target.swift @@ -4,38 +4,38 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int { - // Initialize closed interval [0, n-1] + // Initialize the closed interval [0, n-1] var i = nums.startIndex var j = nums.endIndex - 1 while i <= j { let m = i + (j - i) / 2 // Calculate the midpoint index m if nums[m] < target { - i = m + 1 // target is in the interval [m+1, j] + i = m + 1 // Target must be in [m+1, j] if it exists } else if nums[m] > target { - j = m - 1 // target is in the interval [i, m-1] + j = m - 1 // Target must be in [i, m-1] if it exists } else { - return m // Found target, return insertion point m + return m // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { - // Initialize closed interval [0, n-1] + // Initialize the closed interval [0, n-1] var i = nums.startIndex var j = nums.endIndex - 1 while i <= j { let m = i + (j - i) / 2 // Calculate the midpoint index m if nums[m] < target { - i = m + 1 // target is in the interval [m+1, j] + i = m + 1 // Target must be in [m+1, j] if it exists } else if nums[m] > target { - j = m - 1 // target is in the interval [i, m-1] + j = m - 1 // Target must be in [i, m-1] if it exists } else { - j = m - 1 // The rightmost element less than target is in the interval [i, m-1] + j = m - 1 // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i @@ -46,24 +46,24 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { @main enum BinarySearchInsertion { - /* Driver Code */ + /* Driver code */ static func main() { - // Array without duplicate elements + // Array with no duplicate elements var nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] print("\nArray nums = \(nums)") - // Binary search for insertion point + // Find an insertion point using binary search for target in [6, 9] { let index = binarySearchInsertionSimple(nums: nums, target: target) - print("Insertion point index for element \(target) is \(index)") + print("Insertion index for element \(target): \(index)") } - // Array with duplicate elements + // Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] print("\nArray nums = \(nums)") - // Binary search for insertion point + // Find an insertion point using binary search for target in [2, 6, 20] { let index = binarySearchInsertion(nums: nums, target: target) - print("Insertion point index for element \(target) is \(index)") + print("Insertion index for element \(target): \(index)") } } } diff --git a/en/codes/swift/chapter_searching/hashing_search.swift b/en/codes/swift/chapter_searching/hashing_search.swift index 44d4df989..bee532253 100644 --- a/en/codes/swift/chapter_searching/hashing_search.swift +++ b/en/codes/swift/chapter_searching/hashing_search.swift @@ -6,45 +6,45 @@ import utils -/* Hash search (array) */ +/* Hash-based search (array) */ func hashingSearchArray(map: [Int: Int], target: Int) -> Int { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key return map[target, default: -1] } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ func hashingSearchLinkedList(map: [Int: ListNode], target: Int) -> ListNode? { - // Hash table key: target node value, value: node object - // If key is not in hash table, return null + // Hash table key: target node value; value: node object + // Return nil if the hash table does not contain this key return map[target] } @main enum HashingSearch { - /* Driver Code */ + /* Driver code */ static func main() { let target = 3 - /* Hash search (array) */ + /* Hash-based search (array) */ let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] - // Initialize hash table + // Initialize the hash table var map: [Int: Int] = [:] for i in nums.indices { - map[nums[i]] = i // key: element, value: index + map[nums[i]] = i // key: element; value: index } let index = hashingSearchArray(map: map, target: target) print("Index of target element 3 = \(index)") - /* Hash search (linked list) */ + /* Hash-based search (linked list) */ var head = ListNode.arrToLinkedList(arr: nums) - // Initialize hash table + // Initialize the hash table var map1: [Int: ListNode] = [:] while head != nil { - map1[head!.val] = head! // key: node value, value: node + map1[head!.val] = head! // key: node value; value: node head = head?.next } let node = hashingSearchLinkedList(map: map1, target: target) - print("Node object corresponding to target node value 3 is \(node!)") + print("Node object with target value 3: \(node!)") } } diff --git a/en/codes/swift/chapter_searching/linear_search.swift b/en/codes/swift/chapter_searching/linear_search.swift index fc0cb8acb..0e780536a 100644 --- a/en/codes/swift/chapter_searching/linear_search.swift +++ b/en/codes/swift/chapter_searching/linear_search.swift @@ -8,14 +8,14 @@ import utils /* Linear search (array) */ func linearSearchArray(nums: [Int], target: Int) -> Int { - // Traverse array + // Traverse the array for i in nums.indices { - // Found the target element, return its index + // The target element was found; return its index if nums[i] == target { return i } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1 } @@ -24,30 +24,30 @@ func linearSearchLinkedList(head: ListNode?, target: Int) -> ListNode? { var head = head // Traverse the linked list while head != nil { - // Found the target node, return it + // The target node was found; return it if head?.val == target { return head } head = head?.next } - // Target node not found, return null + // The target node was not found; return nil return nil } @main enum LinearSearch { - /* Driver Code */ + /* Driver code */ static func main() { let target = 3 - /* Perform linear search in array */ + /* Perform linear search on the array */ let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] let index = linearSearchArray(nums: nums, target: target) print("Index of target element 3 = \(index)") - /* Perform linear search in linked list */ + /* Perform linear search on the linked list */ let head = ListNode.arrToLinkedList(arr: nums) let node = linearSearchLinkedList(head: head, target: target) - print("Node object corresponding to target node value 3 is \(node!)") + print("Node object with target value 3: \(node!)") } } diff --git a/en/codes/swift/chapter_searching/two_sum.swift b/en/codes/swift/chapter_searching/two_sum.swift index defd97bb7..1ef68d400 100644 --- a/en/codes/swift/chapter_searching/two_sum.swift +++ b/en/codes/swift/chapter_searching/two_sum.swift @@ -4,9 +4,9 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for i in nums.indices.dropLast() { for j in nums.indices.dropFirst(i + 1) { if nums[i] + nums[j] == target { @@ -17,11 +17,11 @@ func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { return [0] } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ func twoSumHashTable(nums: [Int], target: Int) -> [Int] { - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space var dic: [Int: Int] = [:] - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for i in nums.indices { if let j = dic[target - nums[i]] { return [j, i] @@ -33,17 +33,17 @@ func twoSumHashTable(nums: [Int], target: Int) -> [Int] { @main enum LeetcodeTwoSum { - /* Driver Code */ + /* Driver code */ static func main() { - // ======= Test Case ======= + // ======= Test case ======= let nums = [2, 7, 11, 15] let target = 13 - // ====== Driver Code ====== + // ====== Driver code ====== // Method 1 var res = twoSumBruteForce(nums: nums, target: target) - print("Method 1 res = \(res)") + print("Method 1: res = \(res)") // Method 2 res = twoSumHashTable(nums: nums, target: target) - print("Method 2 res = \(res)") + print("Method 2: res = \(res)") } } diff --git a/en/codes/swift/chapter_sorting/bubble_sort.swift b/en/codes/swift/chapter_sorting/bubble_sort.swift index 5b18b7528..089c374f8 100644 --- a/en/codes/swift/chapter_sorting/bubble_sort.swift +++ b/en/codes/swift/chapter_sorting/bubble_sort.swift @@ -6,9 +6,9 @@ /* Bubble sort */ func bubbleSort(nums: inout [Int]) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i in nums.indices.dropFirst().reversed() { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for j in 0 ..< i { if nums[j] > nums[j + 1] { // Swap nums[j] and nums[j + 1] @@ -18,19 +18,19 @@ func bubbleSort(nums: inout [Int]) { } } -/* Bubble sort (flag optimization) */ +/* Bubble sort (flag optimization)*/ func bubbleSortWithFlag(nums: inout [Int]) { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for i in nums.indices.dropFirst().reversed() { - var flag = false // Initialize flag + var flag = false // Initialize the flag for j in 0 ..< i { if nums[j] > nums[j + 1] { // Swap nums[j] and nums[j + 1] nums.swapAt(j, j + 1) - flag = true // Record element swap + flag = true // Record that a swap occurred } } - if !flag { // No elements were swapped in this round of "bubbling", exit directly + if !flag { // Exit immediately if this pass made no swaps break } } @@ -38,7 +38,7 @@ func bubbleSortWithFlag(nums: inout [Int]) { @main enum BubbleSort { - /* Driver Code */ + /* Driver code */ static func main() { var nums = [4, 1, 3, 1, 5, 2] bubbleSort(nums: &nums) diff --git a/en/codes/swift/chapter_sorting/bucket_sort.swift b/en/codes/swift/chapter_sorting/bucket_sort.swift index d29b24938..5b364ff19 100644 --- a/en/codes/swift/chapter_sorting/bucket_sort.swift +++ b/en/codes/swift/chapter_sorting/bucket_sort.swift @@ -6,22 +6,22 @@ /* Bucket sort */ func bucketSort(nums: inout [Double]) { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Initialize k = n/2 buckets, expecting 2 elements per bucket let k = nums.count / 2 var buckets = (0 ..< k).map { _ in [Double]() } - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for num in nums { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); use num * k to map each value to an index in [0, k-1] let i = Int(num * Double(k)) // Add num to bucket i buckets[i].append(num) } // 2. Sort each bucket for i in buckets.indices { - // Use built-in sorting function, can also replace with other sorting algorithms + // Use the built-in sorting function, or replace it with another sorting algorithm buckets[i].sort() } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results var i = nums.startIndex for bucket in buckets { for num in bucket { @@ -33,9 +33,9 @@ func bucketSort(nums: inout [Double]) { @main enum BucketSort { - /* Driver Code */ + /* Driver code */ static func main() { - // Assume input data is floating point, interval [0, 1) + // Assume the input consists of floating-point numbers in [0, 1) var nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37] bucketSort(nums: &nums) print("After bucket sort, nums = \(nums)") diff --git a/en/codes/swift/chapter_sorting/counting_sort.swift b/en/codes/swift/chapter_sorting/counting_sort.swift index 329852c98..90719cd73 100644 --- a/en/codes/swift/chapter_sorting/counting_sort.swift +++ b/en/codes/swift/chapter_sorting/counting_sort.swift @@ -5,17 +5,17 @@ */ /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects func countingSortNaive(nums: inout [Int]) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m = nums.max()! - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num var counter = Array(repeating: 0, count: m + 1) for num in nums { counter[num] += 1 } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements var i = 0 for num in 0 ..< m + 1 { for _ in 0 ..< counter[num] { @@ -26,30 +26,30 @@ func countingSortNaive(nums: inout [Int]) { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Full implementation; stable and adaptable to sorting objects func countingSort(nums: inout [Int]) { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m = nums.max()! - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num var counter = Array(repeating: 0, count: m + 1) for num in nums { counter[num] += 1 } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for i in 0 ..< m { counter[i + 1] += counter[i] } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result var res = Array(repeating: 0, count: nums.count) for i in nums.indices.reversed() { let num = nums[i] res[counter[num] - 1] = num // Place num at the corresponding index - counter[num] -= 1 // Decrement the prefix sum by 1, getting the next index to place num + counter[num] -= 1 // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res for i in nums.indices { nums[i] = res[i] } @@ -57,7 +57,7 @@ func countingSort(nums: inout [Int]) { @main enum CountingSort { - /* Driver Code */ + /* Driver code */ static func main() { var nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] countingSortNaive(nums: &nums) diff --git a/en/codes/swift/chapter_sorting/heap_sort.swift b/en/codes/swift/chapter_sorting/heap_sort.swift index cce644c80..e34f17e8d 100644 --- a/en/codes/swift/chapter_sorting/heap_sort.swift +++ b/en/codes/swift/chapter_sorting/heap_sort.swift @@ -4,11 +4,11 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ func siftDown(nums: inout [Int], n: Int, i: Int) { var i = i while true { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma let l = 2 * i + 1 let r = 2 * i + 2 var ma = i @@ -18,35 +18,35 @@ func siftDown(nums: inout [Int], n: Int, i: Int) { if r < n, nums[r] > nums[ma] { ma = r } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if ma == i { break } - // Swap two nodes + // Swap the two nodes nums.swapAt(i, ma) - // Loop downwards heapification + // Continue sifting down i = ma } } /* Heap sort */ func heapSort(nums: inout [Int]) { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for i in stride(from: nums.count / 2 - 1, through: 0, by: -1) { siftDown(nums: &nums, n: nums.count, i: i) } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for i in nums.indices.dropFirst().reversed() { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) nums.swapAt(0, i) - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums: &nums, n: i, i: 0) } } @main enum HeapSort { - /* Driver Code */ + /* Driver code */ static func main() { var nums = [4, 1, 3, 1, 5, 2] heapSort(nums: &nums) diff --git a/en/codes/swift/chapter_sorting/insertion_sort.swift b/en/codes/swift/chapter_sorting/insertion_sort.swift index 6b9f40be5..6aec3eb9c 100644 --- a/en/codes/swift/chapter_sorting/insertion_sort.swift +++ b/en/codes/swift/chapter_sorting/insertion_sort.swift @@ -6,22 +6,22 @@ /* Insertion sort */ func insertionSort(nums: inout [Int]) { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for i in nums.indices.dropFirst() { let base = nums[i] var j = i - 1 - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while j >= 0, nums[j] > base { - nums[j + 1] = nums[j] // Move nums[j] to the right by one position + nums[j + 1] = nums[j] // Shift nums[j] one position to the right j -= 1 } - nums[j + 1] = base // Assign base to the correct position + nums[j + 1] = base // Place base at the correct position } } @main enum InsertionSort { - /* Driver Code */ + /* Driver code */ static func main() { var nums = [4, 1, 3, 1, 5, 2] insertionSort(nums: &nums) diff --git a/en/codes/swift/chapter_sorting/merge_sort.swift b/en/codes/swift/chapter_sorting/merge_sort.swift index 580c58f7e..1c33d86e1 100644 --- a/en/codes/swift/chapter_sorting/merge_sort.swift +++ b/en/codes/swift/chapter_sorting/merge_sort.swift @@ -4,14 +4,14 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result var tmp = Array(repeating: 0, count: right - left + 1) - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays var i = left, j = mid + 1, k = 0 - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while i <= mid, j <= right { if nums[i] <= nums[j] { tmp[k] = nums[i] @@ -33,7 +33,7 @@ func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { j += 1 k += 1 } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for k in tmp.indices { nums[left + k] = tmp[k] } @@ -41,21 +41,21 @@ func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { /* Merge sort */ func mergeSort(nums: inout [Int], left: Int, right: Int) { - // Termination condition - if left >= right { // Terminate recursion when subarray length is 1 + // Base case + if left >= right { // Stop recursing when the subarray has at most one element return } - // Divide and conquer stage - let mid = left + (right - left) / 2 // Calculate midpoint - mergeSort(nums: &nums, left: left, right: mid) // Recursively process the left subarray - mergeSort(nums: &nums, left: mid + 1, right: right) // Recursively process the right subarray - // Merge stage + // Divide phase + let mid = left + (right - left) / 2 // Calculate the midpoint + mergeSort(nums: &nums, left: left, right: mid) // Recurse on the left subarray + mergeSort(nums: &nums, left: mid + 1, right: right) // Recurse on the right subarray + // Merge phase merge(nums: &nums, left: left, mid: mid, right: right) } @main enum MergeSort { - /* Driver Code */ + /* Driver code */ static func main() { /* Merge sort */ var nums = [7, 3, 2, 6, 0, 1, 5, 4] diff --git a/en/codes/swift/chapter_sorting/quick_sort.swift b/en/codes/swift/chapter_sorting/quick_sort.swift index 326be2507..6a9815006 100644 --- a/en/codes/swift/chapter_sorting/quick_sort.swift +++ b/en/codes/swift/chapter_sorting/quick_sort.swift @@ -4,71 +4,71 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Quick sort class */ -/* Sentinel partition */ +/* Quick sort functions */ +/* Two-pointer partitioning */ func partition(nums: inout [Int], left: Int, right: Int) -> Int { // Use nums[left] as the pivot var i = left var j = right while i < j { while i < j, nums[j] >= nums[left] { - j -= 1 // Search from right to left for the first element smaller than the pivot + j -= 1 // Scan from right to left for the first element smaller than the pivot } while i < j, nums[i] <= nums[left] { - i += 1 // Search from left to right for the first element greater than the pivot + i += 1 // Scan from left to right for the first element larger than the pivot } nums.swapAt(i, j) // Swap these two elements } - nums.swapAt(i, left) // Swap the pivot to the boundary between the two subarrays - return i // Return the index of the pivot + nums.swapAt(i, left) // Swap the pivot into the boundary position between the two subarrays + return i // Return the pivot index } /* Quick sort */ func quickSort(nums: inout [Int], left: Int, right: Int) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if left >= right { return } - // Sentinel partition + // Two-pointer partitioning let pivot = partition(nums: &nums, left: left, right: right) - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSort(nums: &nums, left: left, right: pivot - 1) quickSort(nums: &nums, left: pivot + 1, right: right) } -/* Quick sort class (median pivot optimization) */ +/* Quick sort functions (median pivot optimization) */ /* Select the median of three candidate elements */ func medianThree(nums: [Int], left: Int, mid: Int, right: Int) -> Int { let l = nums[left] let m = nums[mid] let r = nums[right] if (l <= m && m <= r) || (r <= m && m <= l) { - return mid // m is between l and r + return mid // m lies between l and r } if (m <= l && l <= r) || (r <= l && l <= m) { - return left // l is between m and r + return left // l lies between m and r } return right } -/* Sentinel partition (median of three) */ +/* Two-pointer partitioning (median of three) */ func partitionMedian(nums: inout [Int], left: Int, right: Int) -> Int { // Select the median of three candidate elements let med = medianThree(nums: nums, left: left, mid: left + (right - left) / 2, right: right) - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray nums.swapAt(left, med) return partition(nums: &nums, left: left, right: right) } -/* Quick sort (recursion depth optimization) */ +/* Quick sort (median pivot optimization) */ func quickSortMedian(nums: inout [Int], left: Int, right: Int) { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has at most one element if left >= right { return } - // Sentinel partition + // Two-pointer partitioning let pivot = partitionMedian(nums: &nums, left: left, right: right) - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays quickSortMedian(nums: &nums, left: left, right: pivot - 1) quickSortMedian(nums: &nums, left: pivot + 1, right: right) } @@ -77,31 +77,31 @@ func quickSortMedian(nums: inout [Int], left: Int, right: Int) { func quickSortTailCall(nums: inout [Int], left: Int, right: Int) { var left = left var right = right - // Terminate when subarray length is 1 + // Stop when the subarray has at most one element while left < right { - // Sentinel partition operation + // Perform two-pointer partitioning let pivot = partition(nums: &nums, left: left, right: right) - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left) < (right - pivot) { quickSortTailCall(nums: &nums, left: left, right: pivot - 1) // Recursively sort the left subarray - left = pivot + 1 // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1 // The remaining unsorted range is [pivot + 1, right] } else { quickSortTailCall(nums: &nums, left: pivot + 1, right: right) // Recursively sort the right subarray - right = pivot - 1 // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1 // The remaining unsorted range is [left, pivot - 1] } } } @main enum QuickSort { - /* Driver Code */ + /* Driver code */ static func main() { /* Quick sort */ var nums = [2, 4, 1, 0, 3, 5] quickSort(nums: &nums, left: nums.startIndex, right: nums.endIndex - 1) print("After quick sort, nums = \(nums)") - /* Quick sort (recursion depth optimization) */ + /* Quick sort (median pivot optimization) */ var nums1 = [2, 4, 1, 0, 3, 5] quickSortMedian(nums: &nums1, left: nums1.startIndex, right: nums1.endIndex - 1) print("After quick sort (median pivot optimization), nums1 = \(nums1)") diff --git a/en/codes/swift/chapter_sorting/radix_sort.swift b/en/codes/swift/chapter_sorting/radix_sort.swift index b677448bc..756ea68ca 100644 --- a/en/codes/swift/chapter_sorting/radix_sort.swift +++ b/en/codes/swift/chapter_sorting/radix_sort.swift @@ -4,34 +4,34 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ func digit(num: Int, exp: Int) -> Int { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here (num / exp) % 10 } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ func countingSortDigit(nums: inout [Int], exp: Int) { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 var counter = Array(repeating: 0, count: 10) - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for i in nums.indices { - let d = digit(num: nums[i], exp: exp) // Get the k-th digit of nums[i], noted as d - counter[d] += 1 // Count the occurrence of digit d + let d = digit(num: nums[i], exp: exp) // Get the k-th digit of nums[i] and store it in d + counter[d] += 1 // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for i in 1 ..< 10 { counter[i] += counter[i - 1] } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts var res = Array(repeating: 0, count: nums.count) for i in nums.indices.reversed() { let d = digit(num: nums[i], exp: exp) - let j = counter[d] - 1 // Get the index j for d in the array + let j = counter[d] - 1 // Get the destination index j for digit d in the array res[j] = nums[i] // Place the current element at index j - counter[d] -= 1 // Decrease the count of d by 1 + counter[d] -= 1 // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for i in nums.indices { nums[i] = res[i] } @@ -39,26 +39,26 @@ func countingSortDigit(nums: inout [Int], exp: Int) { /* Radix sort */ func radixSort(nums: inout [Int]) { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits var m = Int.min for num in nums { if num > m { m = num } } - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for exp in sequence(first: 1, next: { m >= ($0 * 10) ? $0 * 10 : nil }) { - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums: &nums, exp: exp) } } @main enum RadixSort { - /* Driver Code */ + /* Driver code */ static func main() { // Radix sort var nums = [ diff --git a/en/codes/swift/chapter_sorting/selection_sort.swift b/en/codes/swift/chapter_sorting/selection_sort.swift index 3424d3c93..6dabb201a 100644 --- a/en/codes/swift/chapter_sorting/selection_sort.swift +++ b/en/codes/swift/chapter_sorting/selection_sort.swift @@ -6,23 +6,23 @@ /* Selection sort */ func selectionSort(nums: inout [Int]) { - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for i in nums.indices.dropLast() { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range var k = i for j in nums.indices.dropFirst(i + 1) { if nums[j] < nums[k] { k = j // Record the index of the smallest element } } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range nums.swapAt(i, k) } } @main enum SelectionSort { - /* Driver Code */ + /* Driver code */ static func main() { var nums = [4, 1, 3, 1, 5, 2] selectionSort(nums: &nums) diff --git a/en/codes/swift/chapter_stack_and_queue/array_deque.swift b/en/codes/swift/chapter_stack_and_queue/array_deque.swift index 822948eec..65bbe9395 100644 --- a/en/codes/swift/chapter_stack_and_queue/array_deque.swift +++ b/en/codes/swift/chapter_stack_and_queue/array_deque.swift @@ -4,11 +4,11 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ class ArrayDeque { - private var nums: [Int] // Array for storing double-ended queue elements - private var front: Int // Front pointer, points to the front of the queue element - private var _size: Int // Double-ended queue length + private var nums: [Int] // Array that stores the deque elements + private var front: Int // Front index, identifying the first element + private var _size: Int // Deque size /* Constructor */ init(capacity: Int) { @@ -17,73 +17,73 @@ class ArrayDeque { _size = 0 } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ func capacity() -> Int { nums.count } - /* Get the length of the double-ended queue */ + /* Get the deque size */ func size() -> Int { _size } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ func isEmpty() -> Bool { size() == 0 } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ private func index(i: Int) -> Int { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end (i + capacity()) % capacity() } - /* Front of the queue enqueue */ + /* Enqueue at the front */ func pushFirst(num: Int) { if size() == capacity() { - print("Double-ended queue is full") + print("Deque is full") return } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front index one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array front = index(i: front - 1) - // Add num to front of queue + // Add num to the front nums[front] = num _size += 1 } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ func pushLast(num: Int) { if size() == capacity() { - print("Double-ended queue is full") + print("Deque is full") return } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear index, one position past the last element let rear = index(i: front + size()) - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num _size += 1 } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ func popFirst() -> Int { let num = peekFirst() - // Move front pointer backward by one position + // Advance the front index by one position front = index(i: front + 1) _size -= 1 return num } - /* Access rear of the queue element */ + /* Dequeue from the rear */ func popLast() -> Int { let num = peekLast() _size -= 1 return num } - /* Return list for printing */ + /* Access the front element */ func peekFirst() -> Int { if isEmpty() { fatalError("Deque is empty") @@ -91,58 +91,58 @@ class ArrayDeque { return nums[front] } - /* Driver Code */ + /* Access the rear element */ func peekLast() -> Int { if isEmpty() { fatalError("Deque is empty") } - // Initialize double-ended queue + // Calculate the index of the last element let last = index(i: front + size() - 1) return nums[last] } - /* Return array for printing */ + /* Return an array for printing */ func toArray() -> [Int] { - // Elements enqueue + // Include only the elements in the occupied range (front ..< front + size()).map { nums[index(i: $0)] } } } @main enum _ArrayDeque { - /* Driver Code */ + /* Driver code */ static func main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ let deque = ArrayDeque(capacity: 10) deque.pushLast(num: 3) deque.pushLast(num: 2) deque.pushLast(num: 5) print("Deque deque = \(deque.toArray())") - /* Update element */ + /* Access an element */ let peekFirst = deque.peekFirst() print("Front element peekFirst = \(peekFirst)") let peekLast = deque.peekLast() print("Rear element peekLast = \(peekLast)") - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(num: 4) - print("After element 4 enqueues at rear, deque = \(deque.toArray())") + print("After enqueueing 4 at the rear, deque = \(deque.toArray())") deque.pushFirst(num: 1) - print("After element 1 enqueues at front, deque = \(deque.toArray())") + print("After enqueueing 1 at the front, deque = \(deque.toArray())") - /* Element dequeue */ + /* Dequeue an element */ let popLast = deque.popLast() - print("Dequeue rear element = \(popLast), after rear dequeue deque = \(deque.toArray())") + print("Element dequeued from the rear = \(popLast); after dequeueing, deque = \(deque.toArray())") let popFirst = deque.popFirst() - print("Dequeue front element = \(popFirst), after front dequeue deque = \(deque.toArray())") + print("Element dequeued from the front = \(popFirst); after dequeueing, deque = \(deque.toArray())") - /* Get the length of the double-ended queue */ + /* Get the deque size */ let size = deque.size() - print("Deque length size = \(size)") + print("Deque size = \(size)") - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ let isEmpty = deque.isEmpty() - print("Is deque empty = \(isEmpty)") + print("Is the deque empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_stack_and_queue/array_queue.swift b/en/codes/swift/chapter_stack_and_queue/array_queue.swift index c3ae8a828..fe5a0aa28 100644 --- a/en/codes/swift/chapter_stack_and_queue/array_queue.swift +++ b/en/codes/swift/chapter_stack_and_queue/array_queue.swift @@ -4,30 +4,30 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ class ArrayQueue { - private var nums: [Int] // Array for storing queue elements - private var front: Int // Front pointer, points to the front of the queue element - private var _size: Int // Queue length + private var nums: [Int] // Array that stores the queue elements + private var front: Int // Front index, identifying the first element + private var _size: Int // Queue size init(capacity: Int) { - // Initialize array + // Initialize the array nums = Array(repeating: 0, count: capacity) front = 0 _size = 0 } - /* Get the capacity of the queue */ + /* Get the queue capacity */ func capacity() -> Int { nums.count } - /* Get the length of the queue */ + /* Get the queue size */ func size() -> Int { _size } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ func isEmpty() -> Bool { size() == 0 } @@ -38,10 +38,10 @@ class ArrayQueue { print("Queue is full") return } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear index, one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array let rear = (front + size()) % capacity() - // Front pointer moves one position backward + // Add num to the rear nums[rear] = num _size += 1 } @@ -50,13 +50,13 @@ class ArrayQueue { @discardableResult func pop() -> Int { let num = peek() - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front index by one position, wrapping to the beginning if it passes the end front = (front + 1) % capacity() _size -= 1 return num } - /* Return list for printing */ + /* Access the front element */ func peek() -> Int { if isEmpty() { fatalError("Queue is empty") @@ -64,22 +64,22 @@ class ArrayQueue { return nums[front] } - /* Return array */ + /* Return an array */ func toArray() -> [Int] { - // Elements enqueue + // Include only the elements in the occupied range (front ..< front + size()).map { nums[$0 % capacity()] } } } @main enum _ArrayQueue { - /* Driver Code */ + /* Driver code */ static func main() { - /* Access front of the queue element */ + /* Initialize the queue */ let capacity = 10 let queue = ArrayQueue(capacity: capacity) - /* Elements enqueue */ + /* Enqueue elements */ queue.push(num: 1) queue.push(num: 3) queue.push(num: 2) @@ -87,27 +87,27 @@ enum _ArrayQueue { queue.push(num: 4) print("Queue queue = \(queue.toArray())") - /* Return list for printing */ + /* Access the front element */ let peek = queue.peek() print("Front element peek = \(peek)") - /* Element dequeue */ + /* Dequeue an element */ let pop = queue.pop() - print("Dequeue element pop = \(pop), after dequeue queue = \(queue.toArray())") + print("Dequeued element pop = \(pop); after dequeueing, queue = \(queue.toArray())") - /* Get the length of the queue */ + /* Get the queue size */ let size = queue.size() - print("Queue length size = \(size)") + print("Queue size = \(size)") - /* Check if the queue is empty */ + /* Check whether the queue is empty */ let isEmpty = queue.isEmpty() - print("Is queue empty = \(isEmpty)") + print("Is the queue empty? \(isEmpty)") - /* Test circular array */ + /* Test the circular array */ for i in 0 ..< 10 { queue.push(num: i) queue.pop() - print("After round \(i) enqueue + dequeue, queue = \(queue.toArray())") + print("After enqueueing and dequeueing in round \(i), queue = \(queue.toArray())") } } } diff --git a/en/codes/swift/chapter_stack_and_queue/array_stack.swift b/en/codes/swift/chapter_stack_and_queue/array_stack.swift index 2d184cf39..a41027d48 100644 --- a/en/codes/swift/chapter_stack_and_queue/array_stack.swift +++ b/en/codes/swift/chapter_stack_and_queue/array_stack.swift @@ -4,21 +4,21 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* Stack based on array implementation */ +/* Stack implemented with an array */ class ArrayStack { private var stack: [Int] init() { - // Initialize list (dynamic array) + // Initialize the list (dynamic array) stack = [] } - /* Get the length of the stack */ + /* Get the stack size */ func size() -> Int { stack.count } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ func isEmpty() -> Bool { stack.isEmpty } @@ -37,7 +37,7 @@ class ArrayStack { return stack.removeLast() } - /* Return list for printing */ + /* Access the top element */ func peek() -> Int { if isEmpty() { fatalError("Stack is empty") @@ -45,7 +45,7 @@ class ArrayStack { return stack.last! } - /* Convert List to Array and return */ + /* Return the stack elements as an array */ func toArray() -> [Int] { stack } @@ -53,12 +53,12 @@ class ArrayStack { @main enum _ArrayStack { - /* Driver Code */ + /* Driver code */ static func main() { - /* Access top of the stack element */ + /* Initialize the stack */ let stack = ArrayStack() - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(num: 1) stack.push(num: 3) stack.push(num: 2) @@ -66,20 +66,20 @@ enum _ArrayStack { stack.push(num: 4) print("Stack stack = \(stack.toArray())") - /* Return list for printing */ + /* Access the top element */ let peek = stack.peek() print("Top element peek = \(peek)") - /* Element pop from stack */ + /* Pop an element from the stack */ let pop = stack.pop() - print("Pop element pop = \(pop), after pop stack = \(stack.toArray())") + print("Popped element pop = \(pop); after popping, stack = \(stack.toArray())") - /* Get the length of the stack */ + /* Get the stack size */ let size = stack.size() - print("Stack length size = \(size)") + print("Stack size = \(size)") - /* Check if empty */ + /* Check whether it is empty */ let isEmpty = stack.isEmpty() - print("Is stack empty = \(isEmpty)") + print("Is the stack empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_stack_and_queue/deque.swift b/en/codes/swift/chapter_stack_and_queue/deque.swift index b825c685b..4a276a6a6 100644 --- a/en/codes/swift/chapter_stack_and_queue/deque.swift +++ b/en/codes/swift/chapter_stack_and_queue/deque.swift @@ -6,13 +6,13 @@ @main enum Deque { - /* Driver Code */ + /* Driver code */ static func main() { - /* Get the length of the double-ended queue */ - // Swift has no built-in deque class, can use Array as deque + /* Initialize the deque */ + // The Swift standard library has no built-in deque class; Array can be used as a deque var deque: [Int] = [] - /* Elements enqueue */ + /* Enqueue elements */ deque.append(2) deque.append(5) deque.append(4) @@ -20,25 +20,25 @@ enum Deque { deque.insert(1, at: 0) print("Deque deque = \(deque)") - /* Update element */ + /* Access an element */ let peekFirst = deque.first! print("Front element peekFirst = \(peekFirst)") let peekLast = deque.last! print("Rear element peekLast = \(peekLast)") - /* Element dequeue */ - // When simulating with Array, popFirst complexity is O(n) + /* Dequeue an element */ + // When using Array as a deque, removing the first element takes O(n) time let popFirst = deque.removeFirst() - print("Dequeue front element popFirst = \(popFirst), after front dequeue deque = \(deque)") + print("Element dequeued from the front, popFirst = \(popFirst); after dequeueing, deque = \(deque)") let popLast = deque.removeLast() - print("Dequeue rear element popLast = \(popLast), after rear dequeue deque = \(deque)") + print("Element dequeued from the rear, popLast = \(popLast); after dequeueing, deque = \(deque)") - /* Get the length of the double-ended queue */ + /* Get the deque size */ let size = deque.count - print("Deque length size = \(size)") + print("Deque size = \(size)") - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ let isEmpty = deque.isEmpty - print("Is deque empty = \(isEmpty)") + print("Is the deque empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_stack_and_queue/linkedlist_deque.swift b/en/codes/swift/chapter_stack_and_queue/linkedlist_deque.swift index 50b100131..015efec18 100644 --- a/en/codes/swift/chapter_stack_and_queue/linkedlist_deque.swift +++ b/en/codes/swift/chapter_stack_and_queue/linkedlist_deque.swift @@ -7,30 +7,30 @@ /* Doubly linked list node */ class ListNode { var val: Int // Node value - var next: ListNode? // Successor node reference - weak var prev: ListNode? // Predecessor node reference + var next: ListNode? // Reference to the next node + weak var prev: ListNode? // Reference to the previous node init(val: Int) { self.val = val } } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ class LinkedListDeque { private var front: ListNode? // Head node front private var rear: ListNode? // Tail node rear - private var _size: Int // Length of the double-ended queue + private var _size: Int // Deque size init() { _size = 0 } - /* Get the length of the double-ended queue */ + /* Get the deque size */ func size() -> Int { _size } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ func isEmpty() -> Bool { size() == 0 } @@ -43,29 +43,29 @@ class LinkedListDeque { front = node rear = node } - // Front of the queue enqueue operation + // Enqueue at the front else if isFront { // Add node to the head of the linked list front?.prev = node node.next = front - front = node // Update head node + front = node // Update the head node } - // Rear of the queue enqueue operation + // Enqueue at the rear else { // Add node to the tail of the linked list rear?.next = node node.prev = rear - rear = node // Update tail node + rear = node // Update the tail node } - _size += 1 // Update queue length + _size += 1 // Update the deque size } - /* Front of the queue enqueue */ + /* Enqueue at the front */ func pushFirst(num: Int) { push(num: num, isFront: true) } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ func pushLast(num: Int) { push(num: num, isFront: false) } @@ -76,45 +76,45 @@ class LinkedListDeque { fatalError("Deque is empty") } let val: Int - // When the deque has one element, front and rear point to the same node + // When the deque size is 1, the head and tail point to the same node if _size == 1 { val = front!.val front = nil rear = nil } - // Temporarily store head node value + // Dequeue from the front else if isFront { - val = front!.val // Delete head node - // Delete head node + val = front!.val // Save the head node value + // Remove the head node let fNext = front?.next fNext?.prev = nil front?.next = nil - front = fNext // Update head node + front = fNext // Update the head node } - // Temporarily store tail node value + // Dequeue from the rear else { - val = rear!.val // Delete tail node - // Update tail node + val = rear!.val // Save the tail node value + // Remove the tail node let rPrev = rear?.prev rPrev?.next = nil rear?.prev = nil - rear = rPrev // Update tail node + rear = rPrev // Update the tail node } - _size -= 1 // Update queue length + _size -= 1 // Update the deque size return val } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ func popFirst() -> Int { pop(isFront: true) } - /* Access rear of the queue element */ + /* Dequeue from the rear */ func popLast() -> Int { pop(isFront: false) } - /* Return list for printing */ + /* Access the front element */ func peekFirst() -> Int { if isEmpty() { fatalError("Deque is empty") @@ -122,7 +122,7 @@ class LinkedListDeque { return front!.val } - /* Driver Code */ + /* Access the rear element */ func peekLast() -> Int { if isEmpty() { fatalError("Deque is empty") @@ -130,7 +130,7 @@ class LinkedListDeque { return rear!.val } - /* Return array for printing */ + /* Return an array for printing */ func toArray() -> [Int] { var node = front var res = Array(repeating: 0, count: size()) @@ -144,39 +144,39 @@ class LinkedListDeque { @main enum _LinkedListDeque { - /* Driver Code */ + /* Driver code */ static func main() { - /* Get the length of the double-ended queue */ + /* Initialize the deque */ let deque = LinkedListDeque() deque.pushLast(num: 3) deque.pushLast(num: 2) deque.pushLast(num: 5) print("Deque deque = \(deque.toArray())") - /* Update element */ + /* Access an element */ let peekFirst = deque.peekFirst() print("Front element peekFirst = \(peekFirst)") let peekLast = deque.peekLast() print("Rear element peekLast = \(peekLast)") - /* Elements enqueue */ + /* Enqueue elements */ deque.pushLast(num: 4) - print("After element 4 enqueues at rear, deque = \(deque.toArray())") + print("After enqueueing 4 at the rear, deque = \(deque.toArray())") deque.pushFirst(num: 1) - print("After element 1 enqueues at front, deque = \(deque.toArray())") + print("After enqueueing 1 at the front, deque = \(deque.toArray())") - /* Element dequeue */ + /* Dequeue an element */ let popLast = deque.popLast() - print("Dequeue rear element = \(popLast), after rear dequeue deque = \(deque.toArray())") + print("Element dequeued from the rear = \(popLast); after dequeueing, deque = \(deque.toArray())") let popFirst = deque.popFirst() - print("Dequeue front element = \(popFirst), after front dequeue deque = \(deque.toArray())") + print("Element dequeued from the front = \(popFirst); after dequeueing, deque = \(deque.toArray())") - /* Get the length of the double-ended queue */ + /* Get the deque size */ let size = deque.size() - print("Deque length size = \(size)") + print("Deque size = \(size)") - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ let isEmpty = deque.isEmpty() - print("Is deque empty = \(isEmpty)") + print("Is the deque empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift b/en/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift index ab57d8d63..a3a98ad5a 100644 --- a/en/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift +++ b/en/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift @@ -6,7 +6,7 @@ import utils -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue { private var front: ListNode? // Head node private var rear: ListNode? // Tail node @@ -16,12 +16,12 @@ class LinkedListQueue { _size = 0 } - /* Get the length of the queue */ + /* Get the queue size */ func size() -> Int { _size } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ func isEmpty() -> Bool { size() == 0 } @@ -30,12 +30,12 @@ class LinkedListQueue { func push(num: Int) { // Add num after the tail node let node = ListNode(x: num) - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if front == nil { front = node rear = node } - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node else { rear?.next = node rear = node @@ -47,13 +47,13 @@ class LinkedListQueue { @discardableResult func pop() -> Int { let num = peek() - // Delete head node + // Remove the head node front = front?.next _size -= 1 return num } - /* Return list for printing */ + /* Access the front element */ func peek() -> Int { if isEmpty() { fatalError("Queue is empty") @@ -61,7 +61,7 @@ class LinkedListQueue { return front!.val } - /* Convert linked list to Array and return */ + /* Convert the linked list to an array and return it */ func toArray() -> [Int] { var node = front var res = Array(repeating: 0, count: size()) @@ -75,12 +75,12 @@ class LinkedListQueue { @main enum _LinkedListQueue { - /* Driver Code */ + /* Driver code */ static func main() { - /* Access front of the queue element */ + /* Initialize the queue */ let queue = LinkedListQueue() - /* Elements enqueue */ + /* Enqueue elements */ queue.push(num: 1) queue.push(num: 3) queue.push(num: 2) @@ -88,20 +88,20 @@ enum _LinkedListQueue { queue.push(num: 4) print("Queue queue = \(queue.toArray())") - /* Return list for printing */ + /* Access the front element */ let peek = queue.peek() print("Front element peek = \(peek)") - /* Element dequeue */ + /* Dequeue an element */ let pop = queue.pop() - print("Dequeue element pop = \(pop), after dequeue queue = \(queue.toArray())") + print("Dequeued element pop = \(pop); after dequeueing, queue = \(queue.toArray())") - /* Get the length of the queue */ + /* Get the queue size */ let size = queue.size() - print("Queue length size = \(size)") + print("Queue size = \(size)") - /* Check if the queue is empty */ + /* Check whether the queue is empty */ let isEmpty = queue.isEmpty() - print("Is queue empty = \(isEmpty)") + print("Is the queue empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift b/en/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift index eefcc86a3..2cdd07e3b 100644 --- a/en/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift +++ b/en/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift @@ -6,21 +6,21 @@ import utils -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ class LinkedListStack { - private var _peek: ListNode? // Use head node as stack top - private var _size: Int // Stack length + private var _peek: ListNode? // Use the head node as the stack top + private var _size: Int // Stack size init() { _size = 0 } - /* Get the length of the stack */ + /* Get the stack size */ func size() -> Int { _size } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ func isEmpty() -> Bool { size() == 0 } @@ -42,7 +42,7 @@ class LinkedListStack { return num } - /* Return list for printing */ + /* Access the top element */ func peek() -> Int { if isEmpty() { fatalError("Stack is empty") @@ -50,7 +50,7 @@ class LinkedListStack { return _peek!.val } - /* Convert List to Array and return */ + /* Convert the linked list to an array and return it */ func toArray() -> [Int] { var node = _peek var res = Array(repeating: 0, count: size()) @@ -64,12 +64,12 @@ class LinkedListStack { @main enum _LinkedListStack { - /* Driver Code */ + /* Driver code */ static func main() { - /* Access top of the stack element */ + /* Initialize the stack */ let stack = LinkedListStack() - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.push(num: 1) stack.push(num: 3) stack.push(num: 2) @@ -77,20 +77,20 @@ enum _LinkedListStack { stack.push(num: 4) print("Stack stack = \(stack.toArray())") - /* Return list for printing */ + /* Access the top element */ let peek = stack.peek() print("Top element peek = \(peek)") - /* Element pop from stack */ + /* Pop an element from the stack */ let pop = stack.pop() - print("Pop element pop = \(pop), after pop stack = \(stack.toArray())") + print("Popped element pop = \(pop); after popping, stack = \(stack.toArray())") - /* Get the length of the stack */ + /* Get the stack size */ let size = stack.size() - print("Stack length size = \(size)") + print("Stack size = \(size)") - /* Check if empty */ + /* Check whether it is empty */ let isEmpty = stack.isEmpty() - print("Is stack empty = \(isEmpty)") + print("Is the stack empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_stack_and_queue/queue.swift b/en/codes/swift/chapter_stack_and_queue/queue.swift index c9bea78d3..69f3ae37c 100644 --- a/en/codes/swift/chapter_stack_and_queue/queue.swift +++ b/en/codes/swift/chapter_stack_and_queue/queue.swift @@ -6,13 +6,13 @@ @main enum Queue { - /* Driver Code */ + /* Driver code */ static func main() { - /* Access front of the queue element */ - // Swift has no built-in queue class, can use Array as queue + /* Initialize the queue */ + // The Swift standard library has no built-in queue class; Array can be used as a queue var queue: [Int] = [] - /* Elements enqueue */ + /* Enqueue elements */ queue.append(1) queue.append(3) queue.append(2) @@ -20,21 +20,21 @@ enum Queue { queue.append(4) print("Queue queue = \(queue)") - /* Return list for printing */ + /* Access the front element */ let peek = queue.first! print("Front element peek = \(peek)") - /* Element dequeue */ - // When simulating with Array, pop complexity is O(n) + /* Dequeue an element */ + // When using Array as a queue, dequeueing takes O(n) time let pop = queue.removeFirst() - print("Dequeue element pop = \(pop), after dequeue queue = \(queue)") + print("Dequeued element pop = \(pop); after dequeueing, queue = \(queue)") - /* Get the length of the queue */ + /* Get the queue size */ let size = queue.count - print("Queue length size = \(size)") + print("Queue size = \(size)") - /* Check if the queue is empty */ + /* Check whether the queue is empty */ let isEmpty = queue.isEmpty - print("Is queue empty = \(isEmpty)") + print("Is the queue empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_stack_and_queue/stack.swift b/en/codes/swift/chapter_stack_and_queue/stack.swift index 359491d06..47bd1a442 100644 --- a/en/codes/swift/chapter_stack_and_queue/stack.swift +++ b/en/codes/swift/chapter_stack_and_queue/stack.swift @@ -6,13 +6,13 @@ @main enum Stack { - /* Driver Code */ + /* Driver code */ static func main() { - /* Access top of the stack element */ - // Swift has no built-in stack class, can use Array as stack + /* Initialize the stack */ + // The Swift standard library has no built-in stack class; Array can be used as a stack var stack: [Int] = [] - /* Elements push onto stack */ + /* Push elements onto the stack */ stack.append(1) stack.append(3) stack.append(2) @@ -20,20 +20,20 @@ enum Stack { stack.append(4) print("Stack stack = \(stack)") - /* Return list for printing */ + /* Access the top element */ let peek = stack.last! print("Top element peek = \(peek)") - /* Element pop from stack */ + /* Pop an element from the stack */ let pop = stack.removeLast() - print("Pop element pop = \(pop), after pop stack = \(stack)") + print("Popped element pop = \(pop); after popping, stack = \(stack)") - /* Get the length of the stack */ + /* Get the stack size */ let size = stack.count - print("Stack length size = \(size)") + print("Stack size = \(size)") - /* Check if empty */ + /* Check whether it is empty */ let isEmpty = stack.isEmpty - print("Is stack empty = \(isEmpty)") + print("Is the stack empty? \(isEmpty)") } } diff --git a/en/codes/swift/chapter_tree/array_binary_tree.swift b/en/codes/swift/chapter_tree/array_binary_tree.swift index 0dea813fa..32d34bb9e 100644 --- a/en/codes/swift/chapter_tree/array_binary_tree.swift +++ b/en/codes/swift/chapter_tree/array_binary_tree.swift @@ -6,7 +6,7 @@ import utils -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ class ArrayBinaryTree { private var tree: [Int?] @@ -15,31 +15,31 @@ class ArrayBinaryTree { tree = arr } - /* List capacity */ + /* Length of the backing array */ func size() -> Int { tree.count } - /* Get value of node at index i */ + /* Get the value of the node at index i */ func val(i: Int) -> Int? { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return nil to represent an empty position if i < 0 || i >= size() { return nil } return tree[i] } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ func left(i: Int) -> Int { 2 * i + 1 } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ func right(i: Int) -> Int { 2 * i + 2 } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ func parent(i: Int) -> Int { (i - 1) / 2 } @@ -47,7 +47,7 @@ class ArrayBinaryTree { /* Level-order traversal */ func levelOrder() -> [Int] { var res: [Int] = [] - // Traverse array directly + // Traverse the array directly for i in 0 ..< size() { if let val = val(i: i) { res.append(val) @@ -58,7 +58,7 @@ class ArrayBinaryTree { /* Depth-first traversal */ private func dfs(i: Int, order: String, res: inout [Int]) { - // If empty position, return + // Return if this position is empty guard let val = val(i: i) else { return } @@ -102,40 +102,40 @@ class ArrayBinaryTree { @main enum _ArrayBinaryTree { - /* Driver Code */ + /* Driver code */ static func main() { - // Initialize binary tree - // Here we use a function to generate a binary tree directly from an array + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array let arr = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] let root = TreeNode.listToTree(arr: arr) - print("\nInitialize binary tree\n") - print("Array representation of binary tree:") + print("\nInitialize the binary tree\n") + print("Array representation of the binary tree:") print(arr) - print("Linked list representation of binary tree:") + print("Linked representation of the binary tree:") PrintUtil.printTree(root: root) - // Binary tree class represented by array + // Binary tree class using an array representation let abt = ArrayBinaryTree(arr: arr) - // Access node + // Access a node let i = 1 let l = abt.left(i: i) let r = abt.right(i: i) let p = abt.parent(i: i) - print("\nCurrent node index is \(i), value is \(abt.val(i: i) as Any)") - print("Its left child index is \(l), value is \(abt.val(i: l) as Any)") - print("Its right child index is \(r), value is \(abt.val(i: r) as Any)") - print("Its parent node index is \(p), value is \(abt.val(i: p) as Any)") + print("\nCurrent node: index = \(i), value = \(abt.val(i: i) as Any)") + print("Left child: index = \(l), value = \(abt.val(i: l) as Any)") + print("Right child: index = \(r), value = \(abt.val(i: r) as Any)") + print("Parent: index = \(p), value = \(abt.val(i: p) as Any)") - // Traverse tree + // Traverse the tree var res = abt.levelOrder() - print("\nLevel-order traversal is: \(res)") + print("\nLevel-order traversal: \(res)") res = abt.preOrder() - print("Pre-order traversal is: \(res)") + print("Preorder traversal: \(res)") res = abt.inOrder() - print("In-order traversal is: \(res)") + print("Inorder traversal: \(res)") res = abt.postOrder() - print("Post-order traversal is: \(res)") + print("Postorder traversal: \(res)") } } diff --git a/en/codes/swift/chapter_tree/avl_tree.swift b/en/codes/swift/chapter_tree/avl_tree.swift index fd70a8c63..dff5b0f29 100644 --- a/en/codes/swift/chapter_tree/avl_tree.swift +++ b/en/codes/swift/chapter_tree/avl_tree.swift @@ -12,122 +12,122 @@ class AVLTree { init() {} - /* Get node height */ + /* Get the node height */ func height(node: TreeNode?) -> Int { - // Empty node height is -1, leaf node height is 0 + // A nil node has height -1; a leaf has height 0 node?.height ?? -1 } - /* Update node height */ + /* Update the node height */ private func updateHeight(node: TreeNode?) { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node?.height = max(height(node: node?.left), height(node: node?.right)) + 1 } - /* Get balance factor */ + /* Get the balance factor */ func balanceFactor(node: TreeNode?) -> Int { - // Empty node balance factor is 0 + // A nil node has balance factor 0 guard let node = node else { return 0 } // Node balance factor = left subtree height - right subtree height return height(node: node.left) - height(node: node.right) } - /* Right rotation operation */ + /* Right rotation */ private func rightRotate(node: TreeNode?) -> TreeNode? { let child = node?.left let grandChild = child?.right - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child?.right = node node?.left = grandChild - // Update node height + // Update the node height updateHeight(node: node) updateHeight(node: child) - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child } - /* Left rotation operation */ + /* Left rotation */ private func leftRotate(node: TreeNode?) -> TreeNode? { let child = node?.right let grandChild = child?.left - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child?.left = node node?.right = grandChild - // Update node height + // Update the node height updateHeight(node: node) updateHeight(node: child) - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ private func rotate(node: TreeNode?) -> TreeNode? { - // Get balance factor of node + // Get the balance factor of node let balanceFactor = balanceFactor(node: node) - // Left-leaning tree + // Left-heavy tree if balanceFactor > 1 { if self.balanceFactor(node: node?.left) >= 0 { - // Right rotation + // Rotate right return rightRotate(node: node) } else { - // First left rotation then right rotation + // Rotate left, then right node?.left = leftRotate(node: node?.left) return rightRotate(node: node) } } - // Right-leaning tree + // Right-heavy tree if balanceFactor < -1 { if self.balanceFactor(node: node?.right) <= 0 { - // Left rotation + // Rotate left return leftRotate(node: node) } else { - // First right rotation then left rotation + // Rotate right, then left node?.right = rightRotate(node: node?.right) return leftRotate(node: node) } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node } - /* Insert node */ + /* Insert a node */ func insert(val: Int) { root = insertHelper(node: root, val: val) } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ private func insertHelper(node: TreeNode?, val: Int) -> TreeNode? { var node = node if node == nil { return TreeNode(x: val) } - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if val < node!.val { node?.left = insertHelper(node: node?.left, val: val) } else if val > node!.val { node?.right = insertHelper(node: node?.right, val: val) } else { - return node // Duplicate node not inserted, return directly + return node // Return without inserting a duplicate node } - updateHeight(node: node) // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + updateHeight(node: node) // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node: node) - // Return root node of subtree + // Return the subtree root return node } - /* Remove node */ + /* Remove a node */ func remove(val: Int) { root = removeHelper(node: root, val: val) } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ private func removeHelper(node: TreeNode?, val: Int) -> TreeNode? { var node = node if node == nil { return nil } - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if val < node!.val { node?.left = removeHelper(node: node?.left, val: val) } else if val > node!.val { @@ -135,16 +135,16 @@ class AVLTree { } else { if node?.left == nil || node?.right == nil { let child = node?.left ?? node?.right - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if child == nil { return nil } - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly else { node = child } } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value var temp = node?.right while temp?.left != nil { temp = temp?.left @@ -153,31 +153,31 @@ class AVLTree { node?.val = temp!.val } } - updateHeight(node: node) // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + updateHeight(node: node) // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = rotate(node: node) - // Return root node of subtree + // Return the subtree root return node } - /* Search node */ + /* Find a node */ func search(val: Int) -> TreeNode? { var cur = root while cur != nil { - // Target node is in cur's right subtree + // If present, the target node lies in the right subtree of cur if cur!.val < val { cur = cur?.right } - // Target node is in cur's left subtree + // If present, the target node lies in the left subtree of cur else if cur!.val > val { cur = cur?.left } - // Found target node, exit loop + // The target node was found; exit the loop else { break } } - // Return target node + // Return the target node, or nil if it was not found return cur } } @@ -186,23 +186,23 @@ class AVLTree { enum _AVLTree { static func testInsert(tree: AVLTree, val: Int) { tree.insert(val: val) - print("\nAfter inserting node \(val), AVL tree is") + print("\nAVL tree after inserting node \(val):") PrintUtil.printTree(root: tree.root) } static func testRemove(tree: AVLTree, val: Int) { tree.remove(val: val) - print("\nAfter deleting node \(val), AVL tree is") + print("\nAVL tree after removing node \(val):") PrintUtil.printTree(root: tree.root) } - /* Driver Code */ + /* Driver code */ static func main() { - /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + /* Initialize an empty AVL tree */ let avlTree = AVLTree() - /* Insert node */ - // Delete nodes + /* Insert a node */ + // Observe how the AVL tree maintains balance after a node is inserted testInsert(tree: avlTree, val: 1) testInsert(tree: avlTree, val: 2) testInsert(tree: avlTree, val: 3) @@ -214,17 +214,17 @@ enum _AVLTree { testInsert(tree: avlTree, val: 10) testInsert(tree: avlTree, val: 6) - /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + /* Insert a duplicate node */ testInsert(tree: avlTree, val: 7) - /* Remove node */ - // Delete node with degree 1 - testRemove(tree: avlTree, val: 8) // Delete node with degree 2 - testRemove(tree: avlTree, val: 5) // Remove node with degree 1 - testRemove(tree: avlTree, val: 4) // Remove node with degree 2 + /* Remove a node */ + // Observe how the AVL tree maintains balance after a node is removed + testRemove(tree: avlTree, val: 8) // Remove a node with 0 children + testRemove(tree: avlTree, val: 5) // Remove a node with 1 child + testRemove(tree: avlTree, val: 4) // Remove a node with 2 children - /* Search node */ + /* Look up a node */ let node = avlTree.search(val: 7) - print("\nFound node object is \(node!), node value = \(node!.val)") + print("\nFound node object: \(node!), node value = \(node!.val)") } } diff --git a/en/codes/swift/chapter_tree/binary_search_tree.swift b/en/codes/swift/chapter_tree/binary_search_tree.swift index 918fc0ca3..bc2a075f3 100644 --- a/en/codes/swift/chapter_tree/binary_search_tree.swift +++ b/en/codes/swift/chapter_tree/binary_search_tree.swift @@ -12,63 +12,63 @@ class BinarySearchTree { /* Constructor */ init() { - // Initialize empty tree + // Initialize an empty tree root = nil } - /* Get binary tree root node */ + /* Get the root of the binary tree */ func getRoot() -> TreeNode? { root } - /* Search node */ + /* Find a node */ func search(num: Int) -> TreeNode? { var cur = root - // Loop search, exit after passing leaf node + // Search in a loop, stopping if cur becomes nil while cur != nil { - // Target node is in cur's right subtree + // If present, the target node lies in the right subtree of cur if cur!.val < num { cur = cur?.right } - // Target node is in cur's left subtree + // If present, the target node lies in the left subtree of cur else if cur!.val > num { cur = cur?.left } - // Found target node, exit loop + // The target node was found; exit the loop else { break } } - // Return target node + // Return the target node, or nil if it was not found return cur } - /* Insert node */ + /* Insert a node */ func insert(num: Int) { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if root == nil { root = TreeNode(x: num) return } var cur = root var pre: TreeNode? - // Loop search, exit after passing leaf node + // Search in a loop, stopping if cur becomes nil while cur != nil { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if cur!.val == num { return } pre = cur - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur if cur!.val < num { cur = cur?.right } - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else { cur = cur?.left } } - // Insert node + // Insert a node let node = TreeNode(x: num) if pre!.val < num { pre?.right = node @@ -77,39 +77,39 @@ class BinarySearchTree { } } - /* Remove node */ + /* Remove a node */ func remove(num: Int) { - // If tree is empty, return directly + // Return immediately if the tree is empty if root == nil { return } var cur = root var pre: TreeNode? - // Loop search, exit after passing leaf node + // Search in a loop, stopping if cur becomes nil while cur != nil { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if cur!.val == num { break } pre = cur - // Node to delete is in cur's right subtree + // If present, the node to remove lies in the right subtree of cur if cur!.val < num { cur = cur?.right } - // Node to delete is in cur's left subtree + // If present, the node to remove lies in the left subtree of cur else { cur = cur?.left } } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if cur == nil { return } - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if cur?.left == nil || cur?.right == nil { - // When number of child nodes = 0 / 1, child = null / that child node + // With 0 / 1 children, child is nil / the child node, respectively let child = cur?.left ?? cur?.right - // Delete node cur + // Remove node cur if cur !== root { if pre?.left === cur { pre?.left = child @@ -117,20 +117,20 @@ class BinarySearchTree { pre?.right = child } } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root root = child } } - // Number of child nodes = 2 + // Number of children = 2 else { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur var tmp = cur?.right while tmp?.left != nil { tmp = tmp?.left } - // Recursively delete node tmp + // Recursively remove node tmp remove(num: tmp!.val) - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur?.val = tmp!.val } } @@ -138,36 +138,36 @@ class BinarySearchTree { @main enum _BinarySearchTree { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize binary search tree */ + /* Initialize the binary search tree */ let bst = BinarySearchTree() - // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + // Different insertion orders produce different binary trees; this sequence produces a perfect binary tree let nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] for num in nums { bst.insert(num: num) } - print("\nInitialized binary tree is\n") + print("\nThe initialized binary tree is:\n") PrintUtil.printTree(root: bst.getRoot()) - /* Search node */ + /* Find a node */ let node = bst.search(num: 7) - print("\nFound node object is \(node!), node value = \(node!.val)") + print("\nFound node object: \(node!), node value = \(node!.val)") - /* Insert node */ + /* Insert a node */ bst.insert(num: 16) - print("\nAfter inserting node 16, binary tree is\n") + print("\nBinary tree after inserting node 16:\n") PrintUtil.printTree(root: bst.getRoot()) - /* Remove node */ + /* Remove a node */ bst.remove(num: 1) - print("\nAfter removing node 1, binary tree is\n") + print("\nBinary tree after removing node 1:\n") PrintUtil.printTree(root: bst.getRoot()) bst.remove(num: 2) - print("\nAfter removing node 2, binary tree is\n") + print("\nBinary tree after removing node 2:\n") PrintUtil.printTree(root: bst.getRoot()) bst.remove(num: 4) - print("\nAfter removing node 4, binary tree is\n") + print("\nBinary tree after removing node 4:\n") PrintUtil.printTree(root: bst.getRoot()) } } diff --git a/en/codes/swift/chapter_tree/binary_tree.swift b/en/codes/swift/chapter_tree/binary_tree.swift index f4ba90f25..e45de0cb8 100644 --- a/en/codes/swift/chapter_tree/binary_tree.swift +++ b/en/codes/swift/chapter_tree/binary_tree.swift @@ -8,33 +8,33 @@ import utils @main enum BinaryTree { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize binary tree */ - // Initialize nodes + /* Initialize the binary tree */ + // Initialize the nodes let n1 = TreeNode(x: 1) let n2 = TreeNode(x: 2) let n3 = TreeNode(x: 3) let n4 = TreeNode(x: 4) let n5 = TreeNode(x: 5) - // Build references (pointers) between nodes + // Set up references (pointers) between the nodes n1.left = n2 n1.right = n3 n2.left = n4 n2.right = n5 - print("\nInitialize binary tree\n") + print("\nInitialize the binary tree\n") PrintUtil.printTree(root: n1) - /* Insert node P between n1 -> n2 */ + /* Insert and remove nodes */ let P = TreeNode(x: 0) - // Delete node + // Insert node P between n1 and n2 in n1 -> n2 n1.left = P P.left = n2 - print("\nAfter inserting node P\n") + print("\nAfter inserting node P:\n") PrintUtil.printTree(root: n1) // Remove node P n1.left = n2 - print("\nAfter removing node P\n") + print("\nAfter removing node P:\n") PrintUtil.printTree(root: n1) } } diff --git a/en/codes/swift/chapter_tree/binary_tree_bfs.swift b/en/codes/swift/chapter_tree/binary_tree_bfs.swift index 6d2b2bffa..5a327b24a 100644 --- a/en/codes/swift/chapter_tree/binary_tree_bfs.swift +++ b/en/codes/swift/chapter_tree/binary_tree_bfs.swift @@ -8,18 +8,18 @@ import utils /* Level-order traversal */ func levelOrder(root: TreeNode) -> [Int] { - // Initialize queue, add root node + // Initialize a queue and enqueue the root var queue: [TreeNode] = [root] - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence var list: [Int] = [] while !queue.isEmpty { - let node = queue.removeFirst() // Dequeue - list.append(node.val) // Save node value + let node = queue.removeFirst() // Dequeue from the queue + list.append(node.val) // Store the node value if let left = node.left { - queue.append(left) // Left child node enqueue + queue.append(left) // Enqueue the left child } if let right = node.right { - queue.append(right) // Right child node enqueue + queue.append(right) // Enqueue the right child } } return list @@ -27,16 +27,16 @@ func levelOrder(root: TreeNode) -> [Int] { @main enum BinaryTreeBFS { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array let node = TreeNode.listToTree(arr: [1, 2, 3, 4, 5, 6, 7])! - print("\nInitialize binary tree\n") + print("\nInitialize the binary tree\n") PrintUtil.printTree(root: node) /* Level-order traversal */ let list = levelOrder(root: node) - print("\nLevel-order traversal node print sequence = \(list)") + print("\nNode values in level order = \(list)") } } diff --git a/en/codes/swift/chapter_tree/binary_tree_dfs.swift b/en/codes/swift/chapter_tree/binary_tree_dfs.swift index 6ce977943..9232e3acb 100644 --- a/en/codes/swift/chapter_tree/binary_tree_dfs.swift +++ b/en/codes/swift/chapter_tree/binary_tree_dfs.swift @@ -6,7 +6,7 @@ import utils -// Initialize list for storing traversal sequence +// Initialize a list to store the traversal sequence var list: [Int] = [] /* Preorder traversal */ @@ -14,7 +14,7 @@ func preOrder(root: TreeNode?) { guard let root = root else { return } - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree list.append(root.val) preOrder(root: root.left) preOrder(root: root.right) @@ -25,7 +25,7 @@ func inOrder(root: TreeNode?) { guard let root = root else { return } - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(root: root.left) list.append(root.val) inOrder(root: root.right) @@ -36,7 +36,7 @@ func postOrder(root: TreeNode?) { guard let root = root else { return } - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(root: root.left) postOrder(root: root.right) list.append(root.val) @@ -44,27 +44,27 @@ func postOrder(root: TreeNode?) { @main enum BinaryTreeDFS { - /* Driver Code */ + /* Driver code */ static func main() { - /* Initialize binary tree */ - // Here we use a function to generate a binary tree directly from an array + /* Initialize the binary tree */ + // Use a helper function that builds a binary tree directly from an array let root = TreeNode.listToTree(arr: [1, 2, 3, 4, 5, 6, 7])! - print("\nInitialize binary tree\n") + print("\nInitialize the binary tree\n") PrintUtil.printTree(root: root) /* Preorder traversal */ list.removeAll() preOrder(root: root) - print("\nPre-order traversal node print sequence = \(list)") + print("\nNode values in preorder = \(list)") /* Inorder traversal */ list.removeAll() inOrder(root: root) - print("\nIn-order traversal node print sequence = \(list)") + print("\nNode values in inorder = \(list)") /* Postorder traversal */ list.removeAll() postOrder(root: root) - print("\nPost-order traversal node print sequence = \(list)") + print("\nNode values in postorder = \(list)") } } diff --git a/en/codes/swift/utils/ListNode.swift b/en/codes/swift/utils/ListNode.swift index ffa47de12..f5243aa4d 100644 --- a/en/codes/swift/utils/ListNode.swift +++ b/en/codes/swift/utils/ListNode.swift @@ -6,7 +6,7 @@ public class ListNode: Hashable { public var val: Int // Node value - public var next: ListNode? // Successor node reference + public var next: ListNode? // Reference to the next node public init(x: Int) { val = x diff --git a/en/codes/swift/utils/PrintUtil.swift b/en/codes/swift/utils/PrintUtil.swift index c2662634e..ef1dac872 100644 --- a/en/codes/swift/utils/PrintUtil.swift +++ b/en/codes/swift/utils/PrintUtil.swift @@ -76,9 +76,9 @@ public enum PrintUtil { } public static func printHeap(queue: [Int]) { - print("Heap array representation:", terminator: "") + print("Array representation of the heap:", terminator: "") print(queue) - print("Heap tree representation:") + print("Tree representation of the heap:") let root = TreeNode.listToTree(arr: queue) printTree(root: root) } diff --git a/en/codes/swift/utils/TreeNode.swift b/en/codes/swift/utils/TreeNode.swift index 4e8c48f28..24d30a50d 100644 --- a/en/codes/swift/utils/TreeNode.swift +++ b/en/codes/swift/utils/TreeNode.swift @@ -8,8 +8,8 @@ public class TreeNode { public var val: Int // Node value public var height: Int // Node height - public var left: TreeNode? // Reference to left child node - public var right: TreeNode? // Reference to right child node + public var left: TreeNode? // Reference to the left child + public var right: TreeNode? // Reference to the right child /* Constructor */ public init(x: Int) { @@ -17,11 +17,11 @@ public class TreeNode { height = 0 } - // For the serialization encoding rules, please refer to: - // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ - // Array representation of binary tree: + // For the serialization rules, see: + // https://www.hello-algo.com/en/chapter_tree/array_representation_of_tree/ + // Array representation of the binary tree: // [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] - // Linked list representation of binary tree: + // Linked representation of the binary tree: // /——— 15 // /——— 7 // /——— 3 diff --git a/en/codes/swift/utils/Vertex.swift b/en/codes/swift/utils/Vertex.swift index fdc3c9a9e..b07fde708 100644 --- a/en/codes/swift/utils/Vertex.swift +++ b/en/codes/swift/utils/Vertex.swift @@ -20,12 +20,12 @@ public class Vertex: Hashable { hasher.combine(val) } - /* Input value list vals, return vertex list vets */ + /* Given the array of values vals, return the array of vertices vets */ public static func valsToVets(vals: [Int]) -> [Vertex] { vals.map { Vertex(val: $0) } } - /* Input vertex list vets, return value list vals */ + /* Given the array of vertices vets, return the array of values vals */ public static func vetsToVals(vets: [Vertex]) -> [Int] { vets.map { $0.val } } diff --git a/en/codes/typescript/chapter_array_and_linkedlist/array.ts b/en/codes/typescript/chapter_array_and_linkedlist/array.ts index f775320e0..6ec66c013 100644 --- a/en/codes/typescript/chapter_array_and_linkedlist/array.ts +++ b/en/codes/typescript/chapter_array_and_linkedlist/array.ts @@ -4,61 +4,61 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Random access to element */ +/* Access an element at a random index */ function randomAccess(nums: number[]): number { - // Randomly select a number in the interval [0, nums.length) + // Choose a random index in the interval [0, nums.length) const random_index = Math.floor(Math.random() * nums.length); - // Retrieve and return the random element + // Get and return the randomly selected element const random_num = nums[random_index]; return random_num; } -/* 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 +/* Extend the array length */ +// Note: TypeScript Array is a dynamic array and can grow directly +// For learning purposes, this function treats Array as a fixed-length array function extend(nums: number[], enlarge: number): number[] { - // Initialize an array with extended length + // Initialize an array with the 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 the new, extended array return res; } -/* Insert element num at index index in the array */ +/* 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 + // Shift all elements at and after index `index` one position to the right for (let i = nums.length - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index index + // Assign num to the element at index `index` nums[index] = num; } -/* Remove the element at index index */ +/* Remove the element at index `index` */ function remove(nums: number[], index: number): void { - // Move all elements after index index forward by one position + // Shift all elements after index `index` one position to the left for (let i = index; i < nums.length - 1; i++) { nums[i] = nums[i + 1]; } } -/* Traverse array */ +/* Traverse the array */ function traverse(nums: number[]): void { let count = 0; - // Traverse array by index + // Traverse the array by index for (let i = 0; i < nums.length; i++) { count += nums[i]; } - // Direct traversal of array elements + // Iterate directly over the array elements for (const num of nums) { count += num; } } -/* Find the specified element in the array */ +/* Find a specified element in the array */ function find(nums: number[], target: number): number { for (let i = 0; i < nums.length; i++) { if (nums[i] === target) { @@ -68,34 +68,34 @@ function find(nums: number[], target: number): number { return -1; } -/* Driver Code */ -/* Initialize array */ +/* Driver code */ +/* Initialize the array */ const arr: number[] = new Array(5).fill(0); console.log('Array arr =', arr); let nums: number[] = [1, 3, 2, 5, 4]; console.log('Array nums =', nums); -/* Insert element */ +/* Random access */ let random_num = randomAccess(nums); -console.log('Get random element in nums', random_num); +console.log('Randomly selected element from nums:', random_num); -/* Traverse array */ +/* Extend the length */ nums = extend(nums, 3); -console.log('Extend array length to 8, get nums =', nums); +console.log('Extend the array length to 8, giving nums =', nums); -/* Insert element */ +/* Insert an element */ insert(nums, 6, 3); -console.log('Insert number 6 at index 3, get nums =', nums); +console.log('Insert the number 6 at index 3, giving nums =', nums); -/* Remove element */ +/* Remove an element */ remove(nums, 2); -console.log('Remove element at index 2, get nums =', nums); +console.log('Remove the element at index 2, giving nums =', nums); -/* Traverse array */ +/* Traverse the array */ traverse(nums); -/* Find element */ +/* Find an element */ let index = find(nums, 3); -console.log('Find element 3 in nums, get index =', index); +console.log('Find element 3 in nums, giving index =', index); export {}; diff --git a/en/codes/typescript/chapter_array_and_linkedlist/linked_list.ts b/en/codes/typescript/chapter_array_and_linkedlist/linked_list.ts index 44c431b3f..887062cdf 100644 --- a/en/codes/typescript/chapter_array_and_linkedlist/linked_list.ts +++ b/en/codes/typescript/chapter_array_and_linkedlist/linked_list.ts @@ -25,7 +25,7 @@ function remove(n0: ListNode): void { n0.next = n1; } -/* Access the node at index index in the linked list */ +/* 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) { @@ -49,38 +49,38 @@ function find(head: ListNode | null, target: number): number { return -1; } -/* Driver Code */ -/* Initialize linked list */ -// Initialize each node +/* Driver code */ +/* Initialize the linked list */ +// Initialize the individual nodes const n0 = new ListNode(1); const n1 = new ListNode(3); const n2 = new ListNode(2); const n3 = new ListNode(5); const n4 = new ListNode(4); -// Build references between nodes +// Set up references between the nodes n0.next = n1; n1.next = n2; n2.next = n3; n3.next = n4; -console.log('Initialized linked list is'); +console.log('The initialized linked list is'); printLinkedList(n0); -/* Insert node */ +/* Insert a node */ insert(n0, new ListNode(0)); -console.log('Linked list after inserting node is'); +console.log('The linked list after inserting a node is'); printLinkedList(n0); -/* Remove node */ +/* Remove a node */ remove(n0); -console.log('Linked list after removing node is'); +console.log('The linked list after removing a node is'); printLinkedList(n0); -/* Access node */ +/* Access a node */ const node = access(n0, 3); -console.log(`Value of node at index 3 in linked list = ${node?.val}`); +console.log(`Value of the node at index 3 in the linked list = ${node?.val}`); -/* Search node */ +/* Find a node */ const index = find(n0, 2); -console.log(`Index of node with value 2 in linked list = ${index}`); +console.log(`Index of the node with value 2 in the linked list = ${index}`); export {}; diff --git a/en/codes/typescript/chapter_array_and_linkedlist/list.ts b/en/codes/typescript/chapter_array_and_linkedlist/list.ts index a94f04de6..938fb22db 100644 --- a/en/codes/typescript/chapter_array_and_linkedlist/list.ts +++ b/en/codes/typescript/chapter_array_and_linkedlist/list.ts @@ -4,44 +4,44 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Initialize list */ +/* Initialize the list */ const nums: number[] = [1, 3, 2, 5, 4]; console.log(`List nums = ${nums}`); -/* Update element */ +/* Access an element */ const num: number = nums[1]; -console.log(`Access element at index 1, get num = ${num}`); +console.log(`Access the element at index 1, giving num = ${num}`); -/* Add elements at the end */ +/* Update an element */ nums[1] = 0; -console.log(`Update element at index 1 to 0, get nums = ${nums}`); +console.log(`Update the element at index 1 to 0, giving nums = ${nums}`); -/* Remove element */ +/* Clear the list */ nums.length = 0; -console.log(`After clearing list, nums = ${nums}`); +console.log(`After clearing the list, nums = ${nums}`); -/* Direct traversal of list elements */ +/* Append an element at the end */ nums.push(1); nums.push(3); nums.push(2); nums.push(5); nums.push(4); -console.log(`After adding elements, nums = ${nums}`); +console.log(`After appending elements, nums = ${nums}`); -/* Sort list */ +/* Insert an element in the middle */ nums.splice(3, 0, 6); -console.log(`Insert number 6 at index 3, get nums = ${nums}`); +console.log(`Insert the number 6 at index 3, giving nums = ${nums}`); -/* Remove element */ +/* Remove an element */ nums.splice(3, 1); -console.log(`Delete element at index 3, get nums = ${nums}`); +console.log(`Remove the element at index 3, giving nums = ${nums}`); -/* Traverse list by index */ +/* Traverse the list by index */ let count = 0; for (let i = 0; i < nums.length; i++) { count += nums[i]; } -/* Directly traverse list elements */ +/* Iterate directly over the list elements */ count = 0; for (const x of nums) { count += x; @@ -50,10 +50,10 @@ for (const x of nums) { /* Concatenate two lists */ const nums1: number[] = [6, 8, 7, 10, 9]; nums.push(...nums1); -console.log(`After concatenating list nums1 to nums, get nums = ${nums}`); +console.log(`Append list nums1 to nums, giving nums = ${nums}`); -/* Sort list */ +/* Sort the list */ nums.sort((a, b) => a - b); -console.log(`After sorting list, nums = ${nums}`); +console.log(`After sorting the list, nums = ${nums}`); export {}; diff --git a/en/codes/typescript/chapter_array_and_linkedlist/my_list.ts b/en/codes/typescript/chapter_array_and_linkedlist/my_list.ts index 4407efc82..22c1cd2a7 100644 --- a/en/codes/typescript/chapter_array_and_linkedlist/my_list.ts +++ b/en/codes/typescript/chapter_array_and_linkedlist/my_list.ts @@ -6,56 +6,56 @@ /* List class */ class MyList { - private arr: Array; // Array (stores list elements) + private arr: Array; // Array (stores the 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 + private extendRatio: number = 2; // Factor by which the list capacity grows each time /* Constructor */ constructor() { this.arr = new Array(this._capacity); } - /* Get list length (current number of elements) */ + /* Get the list length (current number of elements)*/ public size(): number { return this._size; } - /* Get list capacity */ + /* Get the list capacity */ public capacity(): number { return this._capacity; } - /* Update element */ + /* Access an element */ public get(index: number): number { - // If the index is out of bounds, throw an exception, as below + // Throw an exception if the index is out of bounds; the same applies below if (index < 0 || index >= this._size) throw new Error('Index out of bounds'); return this.arr[index]; } - /* Add elements at the end */ + /* Update an element */ 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 */ + /* Append an element at the end */ public add(num: number): void { - // If length equals capacity, need to expand + // Grow the capacity if the length equals the capacity if (this._size === this._capacity) this.extendCapacity(); - // Add new element to end of list + // Append the new element to the end of the list this.arr[this._size] = num; this._size++; } - /* Sort list */ + /* Insert an element in the middle */ 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 + // Grow the capacity when adding an element would exceed it if (this._size === this._capacity) { this.extendCapacity(); } - // Move all elements after index index forward by one position + // Shift all elements at and after index `index` one position to the right for (let j = this._size - 1; j >= index; j--) { this.arr[j + 1] = this.arr[j]; } @@ -64,11 +64,11 @@ class MyList { this._size++; } - /* Remove element */ + /* Remove an 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 + // Shift all elements after index one position to the left for (let j = index; j < this._size - 1; j++) { this.arr[j] = this.arr[j + 1]; } @@ -78,20 +78,20 @@ class MyList { return num; } - /* Driver Code */ + /* Grow the list capacity */ public extendCapacity(): void { - // Create new array of length size and copy original array to new array + // Create a larger array by appending empty slots to a copy of the original array this.arr = this.arr.concat( new Array(this.capacity() * (this.extendRatio - 1)) ); - // Add elements at the end + // Update the list capacity this._capacity = this.arr.length; } - /* Convert list to array */ + /* Convert the list to an array */ public toArray(): number[] { let size = this.size(); - // Elements enqueue + // Convert only the list elements within the occupied range const arr = new Array(size); for (let i = 0; i < size; i++) { arr[i] = this.get(i); @@ -100,10 +100,10 @@ class MyList { } } -/* Driver Code */ -/* Initialize list */ +/* Driver code */ +/* Initialize the list */ const nums = new MyList(); -/* Direct traversal of list elements */ +/* Append an element at the end */ nums.add(1); nums.add(3); nums.add(2); @@ -113,29 +113,29 @@ console.log( `List nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` ); -/* Sort list */ +/* Insert an element in the middle */ nums.insert(3, 6); -console.log(`Insert number 6 at index 3, get nums = ${nums.toArray()}`); +console.log(`Insert the number 6 at index 3, giving nums = ${nums.toArray()}`); -/* Remove element */ +/* Remove an element */ nums.remove(3); -console.log(`Delete element at index 3, get nums = ${nums.toArray()}`); +console.log(`Remove the element at index 3, giving nums = ${nums.toArray()}`); -/* Update element */ +/* Access an element */ const num = nums.get(1); -console.log(`Access element at index 1, get num = ${num}`); +console.log(`Access the element at index 1, giving num = ${num}`); -/* Add elements at the end */ +/* Update an element */ nums.set(1, 0); -console.log(`Update element at index 1 to 0, get nums = ${nums.toArray()}`); +console.log(`Update the element at index 1 to 0, giving nums = ${nums.toArray()}`); -/* Test capacity expansion mechanism */ +/* Test capacity growth */ for (let i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + // When i = 5, the list length would exceed its capacity, triggering capacity growth nums.add(i); } console.log( - `After expansion, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` + `After capacity growth, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` ); export {}; diff --git a/en/codes/typescript/chapter_backtracking/n_queens.ts b/en/codes/typescript/chapter_backtracking/n_queens.ts index 33cbb231d..e982c8b9a 100644 --- a/en/codes/typescript/chapter_backtracking/n_queens.ts +++ b/en/codes/typescript/chapter_backtracking/n_queens.ts @@ -4,7 +4,7 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Backtracking algorithm: N queens */ +/* Backtracking: n-queens */ function backtrack( row: number, n: number, @@ -14,49 +14,49 @@ function backtrack( diags1: boolean[], diags2: boolean[] ): void { - // When all rows are placed, record the solution + // Record a solution when a queen has been placed in every row if (row === n) { res.push(state.map((row) => row.slice())); return; } - // Traverse all columns + // Iterate over all columns for (let col = 0; col < n; col++) { - // Calculate the main diagonal and anti-diagonal corresponding to this cell + // Calculate the main-diagonal and anti-diagonal indices for this square const diag1 = row - col + n - 1; const diag2 = row + col; - // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + // Prune: the square must have no queen in its column, main diagonal, or anti-diagonal if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in this cell + // Try: place a queen on this square state[row][col] = 'Q'; cols[col] = diags1[diag1] = diags2[diag2] = true; - // Place the next row + // Place a queen in the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Backtrack: restore this cell to an empty cell + // Backtrack: restore this square to empty state[row][col] = '#'; cols[col] = diags1[diag1] = diags2[diag2] = false; } } } -/* Solve N queens */ +/* Solve the n-queens problem */ function nQueens(n: number): string[][][] { - // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + // Initialize an n*n board, where 'Q' represents a queen and '#' represents an empty square const state = Array.from({ length: n }, () => Array(n).fill('#')); - const cols = Array(n).fill(false); // Record whether there is a queen in the column - const diags1 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the main diagonal - const diags2 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the anti-diagonal + const cols = Array(n).fill(false); // Track whether a column contains a queen + const diags1 = Array(2 * n - 1).fill(false); // Track whether a main diagonal contains a queen + const diags2 = Array(2 * n - 1).fill(false); // Track whether an anti-diagonal contains a queen const res: string[][][] = []; backtrack(0, n, state, res, cols, diags1, diags2); return res; } -// Driver Code +// Driver code const n = 4; const res = nQueens(n); -console.log(`Input board size is ${n}`); -console.log(`Total queen placement solutions: ${res.length}`); +console.log(`Input board side length: ${n}`); +console.log(`Number of queen arrangements: ${res.length}`); res.forEach((state) => { console.log('--------------------'); state.forEach((row) => console.log(row)); diff --git a/en/codes/typescript/chapter_backtracking/permutations_i.ts b/en/codes/typescript/chapter_backtracking/permutations_i.ts index 2d0828ab5..522f8d787 100644 --- a/en/codes/typescript/chapter_backtracking/permutations_i.ts +++ b/en/codes/typescript/chapter_backtracking/permutations_i.ts @@ -4,28 +4,28 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Backtracking algorithm: Permutations I */ +/* Backtracking: permutations I */ function backtrack( state: number[], choices: number[], selected: boolean[], res: number[][] ): void { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.length === choices.length) { res.push([...state]); return; } - // Traverse all choices + // Iterate over all choices choices.forEach((choice, i) => { - // Pruning: do not allow repeated selection of elements + // Prune: do not choose an element more than once if (!selected[i]) { - // Attempt: make choice, update state + // Try: make a choice and update the state selected[i] = true; state.push(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop(); } @@ -39,7 +39,7 @@ function permutationsI(nums: number[]): number[][] { return res; } -// Driver Code +// Driver code const nums: number[] = [1, 2, 3]; const res: number[][] = permutationsI(nums); diff --git a/en/codes/typescript/chapter_backtracking/permutations_ii.ts b/en/codes/typescript/chapter_backtracking/permutations_ii.ts index df2544bdd..e6096bd55 100644 --- a/en/codes/typescript/chapter_backtracking/permutations_ii.ts +++ b/en/codes/typescript/chapter_backtracking/permutations_ii.ts @@ -4,30 +4,30 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Backtracking algorithm: Permutations II */ +/* Backtracking: permutations II */ function backtrack( state: number[], choices: number[], selected: boolean[], res: number[][] ): void { - // When the state length equals the number of elements, record the solution + // Record a solution when the state length equals the number of elements if (state.length === choices.length) { res.push([...state]); return; } - // Traverse all choices + // Iterate over all choices const duplicated = new Set(); choices.forEach((choice, i) => { - // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + // Prune: do not reuse an element or choose the same value again at this depth if (!selected[i] && !duplicated.has(choice)) { - // Attempt: make choice, update state - duplicated.add(choice); // Record the selected element value + // Try: make a choice and update the state + duplicated.add(choice); // Record the chosen value selected[i] = true; state.push(choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, choices, selected, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state selected[i] = false; state.pop(); } @@ -41,7 +41,7 @@ function permutationsII(nums: number[]): number[][] { return res; } -// Driver Code +// Driver code const nums: number[] = [1, 2, 2]; const res: number[][] = permutationsII(nums); diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_i_compact.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_i_compact.ts index 39fbe1d6e..ad1ac5e99 100644 --- a/en/codes/typescript/chapter_backtracking/preorder_traversal_i_compact.ts +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_i_compact.ts @@ -8,22 +8,22 @@ import { type TreeNode } from '../modules/TreeNode'; import { arrToTree } from '../modules/TreeNode'; import { printTree } from '../modules/PrintUtil'; -/* Preorder traversal: Example 1 */ +/* Preorder traversal: example 1 */ function preOrder(root: TreeNode | null, res: TreeNode[]): void { if (root === null) { return; } if (root.val === 7) { - // Record solution + // Record the solution res.push(root); } preOrder(root.left, res); preOrder(root.right, res); } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Preorder traversal diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_ii_compact.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_ii_compact.ts index 0508a4f91..a2d7f070e 100644 --- a/en/codes/typescript/chapter_backtracking/preorder_traversal_ii_compact.ts +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_ii_compact.ts @@ -8,7 +8,7 @@ import { type TreeNode } from '../modules/TreeNode'; import { arrToTree } from '../modules/TreeNode'; import { printTree } from '../modules/PrintUtil'; -/* Preorder traversal: Example 2 */ +/* Preorder traversal: example 2 */ function preOrder( root: TreeNode | null, path: TreeNode[], @@ -17,10 +17,10 @@ function preOrder( if (root === null) { return; } - // Attempt + // Try path.push(root); if (root.val === 7) { - // Record solution + // Record the solution res.push([...path]); } preOrder(root.left, path, res); @@ -29,9 +29,9 @@ function preOrder( path.pop(); } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Preorder traversal @@ -39,7 +39,7 @@ const path: TreeNode[] = []; const res: TreeNode[][] = []; preOrder(root, path, res); -console.log('\nOutput all paths from root node to node 7'); +console.log('\nOutput all paths from the root to nodes with value 7'); res.forEach((path) => { console.log(path.map((node) => node.val)); }); diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_compact.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_compact.ts index b8dfe14f7..99ffb6ece 100644 --- a/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_compact.ts +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_compact.ts @@ -8,20 +8,20 @@ import { type TreeNode } from '../modules/TreeNode'; import { arrToTree } from '../modules/TreeNode'; import { printTree } from '../modules/PrintUtil'; -/* Preorder traversal: Example 3 */ +/* Preorder traversal: example 3 */ function preOrder( root: TreeNode | null, path: TreeNode[], res: TreeNode[][] ): void { - // Pruning + // Prune if (root === null || root.val === 3) { return; } - // Attempt + // Try path.push(root); if (root.val === 7) { - // Record solution + // Record the solution res.push([...path]); } preOrder(root.left, path, res); @@ -30,9 +30,9 @@ function preOrder( path.pop(); } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Preorder traversal @@ -40,7 +40,7 @@ const path: TreeNode[] = []; const res: TreeNode[][] = []; preOrder(root, path, res); -console.log('\nOutput all paths from root node to node 7, paths do not include nodes with value 3'); +console.log('\nOutput all paths from the root to nodes with value 7 that contain no node with value 3'); res.forEach((path) => { console.log(path.map((node) => node.val)); }); diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_template.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_template.ts index 3ac4c38b5..1c6f92fe2 100644 --- a/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_template.ts +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_template.ts @@ -8,66 +8,66 @@ import { type TreeNode } from '../modules/TreeNode'; import { arrToTree } from '../modules/TreeNode'; import { printTree } from '../modules/PrintUtil'; -/* Check if the current state is a solution */ +/* Check whether the current state is a solution */ function isSolution(state: TreeNode[]): boolean { return state && state[state.length - 1]?.val === 7; } -/* Record solution */ +/* Record the solution */ function recordSolution(state: TreeNode[], res: TreeNode[][]): void { res.push([...state]); } -/* Check if the choice is valid under the current state */ +/* Check whether the choice is valid in the current state */ function isValid(state: TreeNode[], choice: TreeNode): boolean { return choice !== null && choice.val !== 3; } -/* Update state */ +/* Update the state */ function makeChoice(state: TreeNode[], choice: TreeNode): void { state.push(choice); } -/* Restore state */ +/* Restore the state */ function undoChoice(state: TreeNode[]): void { state.pop(); } -/* Backtracking algorithm: Example 3 */ +/* Backtracking: example 3 */ function backtrack( state: TreeNode[], choices: TreeNode[], res: TreeNode[][] ): void { - // Check if it is a solution + // Check whether this is a solution if (isSolution(state)) { - // Record solution + // Record the solution recordSolution(state, res); } - // Traverse all choices + // Iterate over all choices for (const choice of choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make choice, update state + // Try: make a choice and update the state makeChoice(state, choice); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, [choice.left, choice.right], res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state); } } } -// Driver Code +// Driver code const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree'); +console.log('\nInitialize the binary tree'); printTree(root); // Backtracking algorithm const res: TreeNode[][] = []; backtrack([], [root], res); -console.log('\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3'); +console.log('\nOutput all paths from the root to nodes with value 7, requiring that no node on the path has value 3'); res.forEach((path) => { console.log(path.map((node) => node.val)); }); diff --git a/en/codes/typescript/chapter_backtracking/subset_sum_i.ts b/en/codes/typescript/chapter_backtracking/subset_sum_i.ts index ed0fde0f9..ac8dc50f1 100644 --- a/en/codes/typescript/chapter_backtracking/subset_sum_i.ts +++ b/en/codes/typescript/chapter_backtracking/subset_sum_i.ts @@ -4,7 +4,7 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ function backtrack( state: number[], target: number, @@ -12,24 +12,24 @@ function backtrack( start: number, res: number[][] ): void { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target (remaining target is 0) if (target === 0) { res.push([...state]); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets for (let i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice would exceed the remaining target + // The array is sorted, so later elements are at least as large and would also exceed the remaining target if (target - choices[i] < 0) { break; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } @@ -38,17 +38,17 @@ function backtrack( function subsetSumI(nums: number[], target: number): number[][] { const state = []; // State (subset) nums.sort((a, b) => a - b); // Sort nums - const start = 0; // Start point for traversal - const res = []; // Result list (subset list) + const start = 0; // Starting index for iteration + const res = []; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ const nums = [3, 4, 5]; const target = 9; const res = subsetSumI(nums, target); console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); -console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); +console.log(`All subsets with sum ${target}: res = ${JSON.stringify(res)}`); export {}; diff --git a/en/codes/typescript/chapter_backtracking/subset_sum_i_naive.ts b/en/codes/typescript/chapter_backtracking/subset_sum_i_naive.ts index c5d8d1249..9376a8613 100644 --- a/en/codes/typescript/chapter_backtracking/subset_sum_i_naive.ts +++ b/en/codes/typescript/chapter_backtracking/subset_sum_i_naive.ts @@ -4,7 +4,7 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Backtracking algorithm: Subset sum I */ +/* Backtracking: subset sum I */ function backtrack( state: number[], target: number, @@ -12,41 +12,41 @@ function backtrack( choices: number[], res: number[][] ): void { - // When the subset sum equals target, record the solution + // Record a solution when the subset sum equals target if (total === target) { res.push([...state]); return; } - // Traverse all choices + // Iterate over all choices for (let i = 0; i < choices.length; i++) { - // Pruning: if the subset sum exceeds target, skip this choice + // Prune: skip this choice if the subset sum would exceed target if (total + choices[i] > target) { continue; } - // Attempt: make choice, update element sum total + // Try: make a choice and update the sum total state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target, total + choices[i], choices, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } -/* Solve subset sum I (including duplicate subsets) */ +/* Solve subset sum I (with duplicate subsets) */ function subsetSumINaive(nums: number[], target: number): number[][] { const state = []; // State (subset) const total = 0; // Subset sum - const res = []; // Result list (subset list) + const res = []; // Result list (list of subsets) backtrack(state, target, total, nums, res); return res; } -/* Driver Code */ +/* Driver code */ const nums = [3, 4, 5]; const target = 9; const res = subsetSumINaive(nums, target); console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); -console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); -console.log('Please note that this method outputs results containing duplicate sets'); +console.log(`All subsets with sum ${target}: res = ${JSON.stringify(res)}`); +console.log('Note that this method produces duplicate subsets'); export {}; diff --git a/en/codes/typescript/chapter_backtracking/subset_sum_ii.ts b/en/codes/typescript/chapter_backtracking/subset_sum_ii.ts index b5449fee4..a0b67e277 100644 --- a/en/codes/typescript/chapter_backtracking/subset_sum_ii.ts +++ b/en/codes/typescript/chapter_backtracking/subset_sum_ii.ts @@ -4,7 +4,7 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Backtracking algorithm: Subset sum II */ +/* Backtracking: subset sum II */ function backtrack( state: number[], target: number, @@ -12,29 +12,29 @@ function backtrack( start: number, res: number[][] ): void { - // When the subset sum equals target, record the solution + // Record a solution when the subset reaches the original target (remaining target is 0) if (target === 0) { res.push([...state]); return; } - // Traverse all choices - // Pruning 2: start traversing from start to avoid generating duplicate subsets - // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + // Iterate over all choices + // Pruning 2: iterate from start to avoid generating duplicate subsets + // Pruning 3: start after the previously chosen index to avoid choosing the same element again for (let i = start; i < choices.length; i++) { - // Pruning 1: if the subset sum exceeds target, end the loop directly - // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + // Pruning 1: exit the loop if this choice would exceed the remaining target + // The array is sorted, so later elements are at least as large and would also exceed the remaining target if (target - choices[i] < 0) { break; } - // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + // Pruning 4: if this element equals its left neighbor at the same search depth, the branch is a duplicate; skip it if (i > start && choices[i] === choices[i - 1]) { continue; } - // Attempt: make choice, update target, start + // Try: make a choice and update target and start state.push(choices[i]); - // Proceed to the next round of selection + // Proceed to the next choice backtrack(state, target - choices[i], choices, i + 1, res); - // Backtrack: undo choice, restore to previous state + // Backtrack: undo the choice and restore the previous state state.pop(); } } @@ -43,17 +43,17 @@ function backtrack( function subsetSumII(nums: number[], target: number): number[][] { const state = []; // State (subset) nums.sort((a, b) => a - b); // Sort nums - const start = 0; // Start point for traversal - const res = []; // Result list (subset list) + const start = 0; // Starting index for iteration + const res = []; // Result list (list of subsets) backtrack(state, target, nums, start, res); return res; } -/* Driver Code */ +/* Driver code */ const nums = [4, 4, 5]; const target = 9; const res = subsetSumII(nums, target); console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); -console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); +console.log(`All subsets with sum ${target}: res = ${JSON.stringify(res)}`); export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/complexity_exercises.ts b/en/codes/typescript/chapter_computational_complexity/complexity_exercises.ts index 72ced4a3d..840b9caed 100644 --- a/en/codes/typescript/chapter_computational_complexity/complexity_exercises.ts +++ b/en/codes/typescript/chapter_computational_complexity/complexity_exercises.ts @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Iterative summation */ +/* Sum using iteration */ function sumIter(n: number): number { let res = 0; for (let i = 1; i <= n; i++) { @@ -13,7 +13,7 @@ function sumIter(n: number): number { return res; } -/* Recursive summation */ +/* Sum using recursion */ function sumRecur(n: number): number { if (n === 1) { return 1; @@ -21,7 +21,7 @@ function sumRecur(n: number): number { return n + sumRecur(n - 1); } -/* Linear loop */ +/* Linear-time loop */ function linearLoop(n: number): number { let res = 0; for (let i = 0; i < n; i++) { @@ -30,7 +30,7 @@ function linearLoop(n: number): number { return res; } -/* Quadratic loop */ +/* Quadratic-time loop */ function quadraticLoop(n: number): number { let res = 0; for (let i = 0; i < n; i++) { @@ -41,7 +41,7 @@ function quadraticLoop(n: number): number { return res; } -/* Logarithmic loop */ +/* Logarithmic-time loop */ function logarithmicLoop(n: number): number { while (n > 1) { n = Math.floor(n / 2); @@ -59,7 +59,7 @@ if ( logarithmicLoop(4) !== 1 || logarithmicLoop(5) !== 1 ) { - throw new Error('complexity exercise check failed'); + throw new Error('Complexity exercise check failed'); } export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/iteration.ts b/en/codes/typescript/chapter_computational_complexity/iteration.ts index 5fd3dfc74..af302a62b 100644 --- a/en/codes/typescript/chapter_computational_complexity/iteration.ts +++ b/en/codes/typescript/chapter_computational_complexity/iteration.ts @@ -7,7 +7,7 @@ /* for loop */ function forLoop(n: number): number { let res = 0; - // Sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n in a loop for (let i = 1; i <= n; i++) { res += i; } @@ -17,11 +17,11 @@ function forLoop(n: number): number { /* while loop */ function whileLoop(n: number): number { let res = 0; - let i = 1; // Initialize condition variable - // Sum 1, 2, ..., n-1, n + let i = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop while (i <= n) { res += i; - i++; // Update condition variable + i++; // Update the loop control variable } return res; } @@ -29,23 +29,23 @@ function whileLoop(n: number): number { /* while loop (two updates) */ function whileLoopII(n: number): number { let res = 0; - let i = 1; // Initialize condition variable - // Sum 1, 4, 10, ... + let i = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop while (i <= n) { res += i; - // Update condition variable + // Update the loop control variable i++; i *= 2; } return res; } -/* Nested for loop */ +/* Nested for loops */ function nestedForLoop(n: number): string { let res = ''; - // Loop i = 1, 2, ..., n-1, n + // Loop over i = 1, 2, ..., n-1, n for (let i = 1; i <= n; i++) { - // Loop j = 1, 2, ..., n-1, n + // Loop over j = 1, 2, ..., n-1, n for (let j = 1; j <= n; j++) { res += `(${i}, ${j}), `; } @@ -53,20 +53,20 @@ function nestedForLoop(n: number): string { return res; } -/* Driver Code */ +/* Driver code */ const n = 5; let res: number; res = forLoop(n); -console.log(`For loop sum result res = ${res}`); +console.log(`Sum from the for loop: res = ${res}`); res = whileLoop(n); -console.log(`While loop sum result res = ${res}`); +console.log(`Sum from the while loop: res = ${res}`); res = whileLoopII(n); -console.log(`While loop (two updates) sum result res = ${res}`); +console.log(`Sum from the while loop with two updates: res = ${res}`); const resStr = nestedForLoop(n); -console.log(`Nested for loop traversal result ${resStr}`); +console.log(`Traversal result from the nested for loops: ${resStr}`); export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/recursion.ts b/en/codes/typescript/chapter_computational_complexity/recursion.ts index d2b1de167..a6534ccc7 100644 --- a/en/codes/typescript/chapter_computational_complexity/recursion.ts +++ b/en/codes/typescript/chapter_computational_complexity/recursion.ts @@ -6,27 +6,27 @@ /* Recursion */ function recur(n: number): number { - // Termination condition + // Base case if (n === 1) return 1; - // Recurse: recursive call + // Recurse: make a recursive call const res = recur(n - 1); - // Return: return result + // Unwind: return the result return n + res; } /* Simulate recursion using iteration */ function forLoopRecur(n: number): number { - // Use an explicit stack to simulate the system call stack + // Use an explicit stack to simulate the system call stack const stack: number[] = []; let res: number = 0; - // Recurse: recursive call + // Recurse: make a recursive call for (let i = n; i > 0; i--) { - // Simulate "recurse" with "push" + // Simulate recursive calls by pushing onto the stack stack.push(i); } - // Return: return result + // Unwind: return the result while (stack.length) { - // Simulate "return" with "pop" + // Simulate unwinding by popping from the stack res += stack.pop(); } // res = 1+2+3+...+n @@ -35,36 +35,36 @@ function forLoopRecur(n: number): number { /* Tail recursion */ function tailRecur(n: number, res: number): number { - // Termination condition + // Base case if (n === 0) return res; - // Tail recursive call + // Tail-recursive call return tailRecur(n - 1, res + n); } /* Fibonacci sequence: recursion */ function fib(n: number): number { - // Termination condition f(1) = 0, f(2) = 1 + // Base cases: f(1) = 0, f(2) = 1 if (n === 1 || n === 2) return n - 1; - // Recursive call f(n) = f(n-1) + f(n-2) + // Recursive calls: f(n) = f(n-1) + f(n-2) const res = fib(n - 1) + fib(n - 2); - // Return result f(n) + // Return the result f(n) return res; } -/* Driver Code */ +/* Driver code */ const n = 5; let res: number; res = recur(n); -console.log(`Recursion sum result res = ${res}`); +console.log(`Sum from the recursive function: res = ${res}`); res = forLoopRecur(n); -console.log(`Using iteration to simulate recursion sum result res = ${res}`); +console.log(`Sum from simulating recursion with iteration: res = ${res}`); res = tailRecur(n, 0); -console.log(`Tail recursion sum result res = ${res}`); +console.log(`Sum from the tail-recursive function: res = ${res}`); res = fib(n); -console.log(`The ${n}th Fibonacci number is ${res}`); +console.log(`Fibonacci term ${n} is ${res}`); export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/space_complexity.ts b/en/codes/typescript/chapter_computational_complexity/space_complexity.ts index 1e772bca6..2d2ba1cc1 100644 --- a/en/codes/typescript/chapter_computational_complexity/space_complexity.ts +++ b/en/codes/typescript/chapter_computational_complexity/space_complexity.ts @@ -14,9 +14,9 @@ function constFunc(): number { return 0; } -/* Constant order */ +/* Constant complexity */ function constant(n: number): void { - // Constants, variables, objects occupy O(1) space + // Constants, variables, and objects occupy O(1) space const a = 0; const b = 0; const nums = new Array(10000); @@ -25,42 +25,42 @@ function constant(n: number): void { for (let i = 0; i < n; i++) { const c = 0; } - // Functions in the loop occupy O(1) space + // Function calls in the loop occupy O(1) space for (let i = 0; i < n; i++) { constFunc(); } } -/* Linear order */ +/* Linear complexity */ function linear(n: number): void { - // Array of length n uses O(n) space + // An array of length n uses O(n) space const nums = new Array(n); // A list of length n occupies O(n) space const nodes: ListNode[] = []; for (let i = 0; i < n; i++) { nodes.push(new ListNode(i)); } - // A hash table of length n occupies O(n) space + // A hash table with n entries occupies O(n) space const map = new Map(); for (let i = 0; i < n; i++) { map.set(i, i.toString()); } } -/* Linear order (recursive implementation) */ +/* Linear complexity (recursive implementation) */ function linearRecur(n: number): void { - console.log(`Recursion n = ${n}`); + console.log(`Recursive call with n = ${n}`); if (n === 1) return; linearRecur(n - 1); } -/* Exponential order */ +/* Quadratic complexity */ function quadratic(n: number): void { - // Matrix uses O(n^2) space + // The matrix uses O(n^2) space const numMatrix = Array(n) .fill(null) .map(() => Array(n).fill(null)); - // 2D list uses O(n^2) space + // The two-dimensional list occupies O(n^2) space const numList = []; for (let i = 0; i < n; i++) { const tmp = []; @@ -71,15 +71,15 @@ function quadratic(n: number): void { } } -/* Quadratic order (recursive implementation) */ +/* Quadratic complexity (recursive implementation) */ function quadraticRecur(n: number): number { if (n <= 0) return 0; const nums = new Array(n); - console.log(`In recursion n = ${n}, nums length = ${nums.length}`); + console.log(`In the recursive call with n = ${n}, nums has length ${nums.length}`); return quadraticRecur(n - 1); } -/* Driver Code */ +/* Exponential complexity (building a perfect binary tree) */ function buildTree(n: number): TreeNode | null { if (n === 0) return null; const root = new TreeNode(0); @@ -88,16 +88,16 @@ function buildTree(n: number): TreeNode | null { return root; } -/* Driver Code */ +/* Driver code */ const n = 5; -// Constant order +// Constant complexity constant(n); -// Linear order +// Linear complexity linear(n); linearRecur(n); -// Exponential order +// Quadratic complexity quadratic(n); quadraticRecur(n); -// Exponential order +// Exponential complexity const root = buildTree(n); printTree(root); diff --git a/en/codes/typescript/chapter_computational_complexity/time_complexity.ts b/en/codes/typescript/chapter_computational_complexity/time_complexity.ts index 94fcc9128..3ba646bb1 100644 --- a/en/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/en/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -4,7 +4,7 @@ * Author: RiverTwilight (contact@rene.wang) */ -/* Constant order */ +/* Constant complexity */ function constant(n: number): number { let count = 0; const size = 100000; @@ -12,27 +12,27 @@ function constant(n: number): number { return count; } -/* Linear order */ +/* Linear complexity */ function linear(n: number): number { let count = 0; for (let i = 0; i < n; i++) count++; return count; } -/* Linear order (traversing array) */ +/* Linear complexity (array traversal) */ function arrayTraversal(nums: number[]): number { let count = 0; - // Number of iterations is proportional to the array length + // The number of iterations is proportional to the array length for (let i = 0; i < nums.length; i++) { count++; } return count; } -/* Exponential order */ +/* Quadratic complexity */ function quadratic(n: number): number { let count = 0; - // Number of iterations is quadratically related to the data size n + // The number of iterations is proportional to the square of the input size n for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { count++; @@ -41,30 +41,30 @@ function quadratic(n: number): number { return count; } -/* Quadratic order (bubble sort) */ +/* Quadratic complexity (bubble sort) */ function bubbleSort(nums: number[]): number { let count = 0; // Counter - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (let i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] let tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 unit operations + count += 3; // Swapping two elements takes 3 elementary operations } } } return count; } -/* Exponential order (loop implementation) */ +/* Exponential complexity (iterative implementation) */ function exponential(n: number): number { let count = 0, base = 1; - // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) for (let i = 0; i < n; i++) { for (let j = 0; j < base; j++) { count++; @@ -75,13 +75,13 @@ function exponential(n: number): number { return count; } -/* Exponential order (recursive implementation) */ +/* Exponential complexity (recursive implementation) */ function expRecur(n: number): number { if (n === 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } -/* Logarithmic order (loop implementation) */ +/* Logarithmic complexity (iterative implementation) */ function logarithmic(n: number): number { let count = 0; while (n > 1) { @@ -91,13 +91,13 @@ function logarithmic(n: number): number { return count; } -/* Logarithmic order (recursive implementation) */ +/* Logarithmic complexity (recursive implementation) */ function logRecur(n: number): number { if (n <= 1) return 0; return logRecur(n / 2) + 1; } -/* Linearithmic order */ +/* Linearithmic complexity */ function linearLogRecur(n: number): number { if (n <= 1) return 1; let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); @@ -107,51 +107,51 @@ function linearLogRecur(n: number): number { return count; } -/* Factorial order (recursive implementation) */ +/* Factorial complexity (recursive implementation) */ function factorialRecur(n: number): number { if (n === 0) return 1; let count = 0; - // Split from 1 into n + // Split one problem into n subproblems for (let i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; } -/* Driver Code */ -// You can modify n to run and observe the trend of the number of operations for various complexities +/* Driver code */ +// Try different values of n to see how the operation count grows for each complexity const n = 8; -console.log('Input data size n = ' + n); +console.log('Input size n = ' + n); let count = constant(n); -console.log('Constant order operation count = ' + count); +console.log('Operation count for constant complexity = ' + count); count = linear(n); -console.log('Linear order operation count = ' + count); +console.log('Operation count for linear complexity = ' + count); count = arrayTraversal(new Array(n)); -console.log('Linear order (array traversal) operation count = ' + count); +console.log('Operation count for linear complexity (array traversal) = ' + count); count = quadratic(n); -console.log('Quadratic order operation count = ' + count); +console.log('Operation count for quadratic complexity = ' + count); var nums = new Array(n); for (let i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = bubbleSort(nums); -console.log('Quadratic order (bubble sort) operation count = ' + count); +console.log('Operation count for quadratic complexity (bubble sort) = ' + count); count = exponential(n); -console.log('Exponential order (loop implementation) operation count = ' + count); +console.log('Operation count for exponential complexity (iterative implementation) = ' + count); count = expRecur(n); -console.log('Exponential order (recursive implementation) operation count = ' + count); +console.log('Operation count for exponential complexity (recursive implementation) = ' + count); count = logarithmic(n); -console.log('Logarithmic order (loop implementation) operation count = ' + count); +console.log('Operation count for logarithmic complexity (iterative implementation) = ' + count); count = logRecur(n); -console.log('Logarithmic order (recursive implementation) operation count = ' + count); +console.log('Operation count for logarithmic complexity (recursive implementation) = ' + count); count = linearLogRecur(n); -console.log('Linearithmic order (recursive implementation) operation count = ' + count); +console.log('Operation count for linearithmic complexity (recursive implementation) = ' + count); count = factorialRecur(n); -console.log('Factorial order (recursive implementation) operation count = ' + count); +console.log('Operation count for factorial complexity (recursive implementation) = ' + count); export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts b/en/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts index d95614b4a..2b1e7d2e8 100644 --- a/en/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts +++ b/en/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts @@ -4,14 +4,14 @@ * Author: RiverTwilight (contact@rene.wang) */ -/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +/* Generate an array containing { 1, 2, ..., n } in shuffled order */ function randomNumbers(n: number): number[] { const nums = Array(n); - // Generate array nums = { 1, 2, 3, ..., n } + // Generate the array nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } - // Randomly shuffle array elements + // Randomly shuffle the array elements for (let i = 0; i < n; i++) { const r = Math.floor(Math.random() * (i + 1)); const temp = nums[i]; @@ -21,11 +21,11 @@ function randomNumbers(n: number): number[] { return nums; } -/* Find the index of number 1 in array nums */ +/* Find the index of the number 1 in array nums */ function findOne(nums: number[]): number { for (let i = 0; i < nums.length; i++) { - // When element 1 is at the head of the array, best time complexity O(1) is achieved - // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) if (nums[i] === 1) { return i; } @@ -33,13 +33,13 @@ function findOne(nums: number[]): number { return -1; } -/* Driver Code */ +/* Driver code */ for (let i = 0; i < 10; i++) { const n = 100; const nums = randomNumbers(n); const index = findOne(nums); console.log('\nArray [ 1, 2, ..., n ] after shuffling = [' + nums.join(', ') + ']'); - console.log('Index of number 1 is ' + index); + console.log('The index of the number 1 is ' + index); } export {}; diff --git a/en/codes/typescript/chapter_divide_and_conquer/binary_search_recur.ts b/en/codes/typescript/chapter_divide_and_conquer/binary_search_recur.ts index 482358356..4aa3ee5e0 100644 --- a/en/codes/typescript/chapter_divide_and_conquer/binary_search_recur.ts +++ b/en/codes/typescript/chapter_divide_and_conquer/binary_search_recur.ts @@ -6,20 +6,20 @@ /* Binary search: problem f(i, j) */ function dfs(nums: number[], target: number, i: number, j: number): number { - // If the interval is empty, it means there is no target element, return -1 + // If the interval is empty, the target is absent; return -1 if (i > j) { return -1; } // Calculate the midpoint index m const m = i + ((j - i) >> 1); if (nums[m] < target) { - // Recursion subproblem f(m+1, j) + // Recurse on subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursion subproblem f(i, m-1) + // Recurse on subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } @@ -27,15 +27,15 @@ function dfs(nums: number[], target: number, i: number, j: number): number { /* Binary search */ function binarySearch(nums: number[], target: number): number { const n = nums.length; - // Solve the problem f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1); } -/* Driver Code */ +/* Driver code */ const target = 6; const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; -// Binary search (closed interval on both sides) +// Binary search (closed interval) const index = binarySearch(nums, target); -console.log(`Index of target element 6 is ${index}`); +console.log(`Index of target element 6 = ${index}`); export {}; diff --git a/en/codes/typescript/chapter_divide_and_conquer/build_tree.ts b/en/codes/typescript/chapter_divide_and_conquer/build_tree.ts index 3221a3f2c..19de75238 100644 --- a/en/codes/typescript/chapter_divide_and_conquer/build_tree.ts +++ b/en/codes/typescript/chapter_divide_and_conquer/build_tree.ts @@ -7,7 +7,7 @@ import { printTree } from '../modules/PrintUtil'; import { TreeNode } from '../modules/TreeNode'; -/* Build binary tree: divide and conquer */ +/* Build a binary tree: divide and conquer */ function dfs( preorder: number[], inorderMap: Map, @@ -15,11 +15,11 @@ function dfs( l: number, r: number ): TreeNode | null { - // Terminate when the subtree interval is empty + // Stop when the subtree interval is empty if (r - l < 0) return null; // Initialize the root node const root: TreeNode = new TreeNode(preorder[i]); - // Query m to divide the left and right subtrees + // Look up m to separate the left and right subtrees const m = inorderMap.get(preorder[i]); // Subproblem: build the left subtree root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); @@ -29,9 +29,9 @@ function dfs( return root; } -/* Build binary tree */ +/* Build a binary tree */ function buildTree(preorder: number[], inorder: number[]): TreeNode | null { - // Initialize hash map, storing the mapping from inorder elements to indices + // Initialize a hash table mapping each element in inorder to its index let inorderMap = new Map(); for (let i = 0; i < inorder.length; i++) { inorderMap.set(inorder[i], i); @@ -40,7 +40,7 @@ function buildTree(preorder: number[], inorder: number[]): TreeNode | null { return root; } -/* Driver Code */ +/* Driver code */ const preorder = [3, 9, 2, 1, 7]; const inorder = [9, 3, 1, 2, 7]; console.log('Preorder traversal = ' + JSON.stringify(preorder)); diff --git a/en/codes/typescript/chapter_divide_and_conquer/fast_power.ts b/en/codes/typescript/chapter_divide_and_conquer/fast_power.ts index 15693d4dc..2e6e50207 100644 --- a/en/codes/typescript/chapter_divide_and_conquer/fast_power.ts +++ b/en/codes/typescript/chapter_divide_and_conquer/fast_power.ts @@ -4,7 +4,7 @@ * Author: Hello Algo Team */ -/* Exponentiation by squaring */ +/* Fast exponentiation */ function fastPow(x: number, n: number): number { if (n === 0) { return 1; @@ -17,7 +17,7 @@ function fastPow(x: number, n: number): number { } if (fastPow(7, 0) !== 1 || fastPow(3, 5) !== 243 || fastPow(2, 6) !== 64) { - throw new Error('fast power check failed'); + throw new Error('Fast power check failed'); } export {}; diff --git a/en/codes/typescript/chapter_divide_and_conquer/hanota.ts b/en/codes/typescript/chapter_divide_and_conquer/hanota.ts index 0739de439..877cf52cc 100644 --- a/en/codes/typescript/chapter_divide_and_conquer/hanota.ts +++ b/en/codes/typescript/chapter_divide_and_conquer/hanota.ts @@ -4,9 +4,9 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Move a disk */ +/* Move one disk */ function move(src: number[], tar: number[]): void { - // Take out a disk from the top of src + // Take a disk from the top of src const pan = src.pop(); // Place the disk on top of tar tar.push(pan); @@ -14,14 +14,14 @@ function move(src: number[], tar: number[]): void { /* Solve the Tower of Hanoi problem f(i) */ function dfs(i: number, src: number[], buf: number[], tar: number[]): void { - // If there is only one disk left in src, move it directly to tar + // If only one disk remains to be moved from src, move it directly to tar if (i === 1) { move(src, tar); return; } // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining disk from src to tar + // Subproblem f(1): move the remaining disk of this subproblem from src to tar move(src, tar); // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); @@ -34,19 +34,19 @@ function solveHanota(A: number[], B: number[], C: number[]): void { dfs(n, A, B, C); } -/* Driver Code */ -// The tail of the list is the top of the rod +/* Driver code */ +// The end of each list represents the top of the peg const A = [5, 4, 3, 2, 1]; const B = []; const C = []; -console.log('In initial state:'); +console.log('Initial state:'); console.log(`A = ${JSON.stringify(A)}`); console.log(`B = ${JSON.stringify(B)}`); console.log(`C = ${JSON.stringify(C)}`); solveHanota(A, B, C); -console.log('After disk movement is complete:'); +console.log('After moving the disks:'); console.log(`A = ${JSON.stringify(A)}`); console.log(`B = ${JSON.stringify(B)}`); console.log(`C = ${JSON.stringify(C)}`); diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_backtrack.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_backtrack.ts index 6582417cb..0b4394c17 100644 --- a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_backtrack.ts +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_backtrack.ts @@ -11,31 +11,31 @@ function backtrack( n: number, res: Map<0, any> ): void { - // When climbing to the n-th stair, add 1 to the solution count + // When step n is reached, increase the number of ways by 1 if (state === n) res.set(0, res.get(0) + 1); - // Traverse all choices + // Iterate over all choices for (const choice of choices) { - // Pruning: not allowed to go beyond the n-th stair + // Prune: do not go beyond step n if (state + choice > n) continue; - // Attempt: make choice, update state + // Try: make a choice and update the state backtrack(choices, state + choice, n, res); // Backtrack } } -/* Climbing stairs: Backtracking */ +/* Climbing stairs: backtracking */ function climbingStairsBacktrack(n: number): number { - const choices = [1, 2]; // Can choose to climb up 1 or 2 stairs - const state = 0; // Start climbing from the 0-th stair + const choices = [1, 2]; // Choose to climb 1 or 2 steps + const state = 0; // Start climbing from step 0 const res = new Map(); - res.set(0, 0); // Use res[0] to record the solution count + res.set(0, 0); // Store the number of ways at key 0 in res backtrack(choices, state, n, res); return res.get(0); } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsBacktrack(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_constraint_dp.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_constraint_dp.ts index 6e4a3c162..e17907b7a 100644 --- a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_constraint_dp.ts +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_constraint_dp.ts @@ -4,19 +4,19 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Climbing stairs with constraint: Dynamic programming */ +/* Climbing stairs with constraints: dynamic programming */ function climbingStairsConstraintDP(n: number): number { if (n === 1 || n === 2) { return 1; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions const dp = Array.from({ length: n + 1 }, () => new Array(3)); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (let i = 3; i <= n; i++) { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -24,9 +24,9 @@ function climbingStairsConstraintDP(n: number): number { return dp[n][1] + dp[n][2]; } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsConstraintDP(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs.ts index 97411f158..7cd797587 100644 --- a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs.ts +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs.ts @@ -6,21 +6,21 @@ /* Search */ function dfs(i: number): number { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i === 1 || i === 2) return i; // dp[i] = dp[i-1] + dp[i-2] const count = dfs(i - 1) + dfs(i - 2); return count; } -/* Climbing stairs: Search */ +/* Climbing stairs: search */ function climbingStairsDFS(n: number): number { return dfs(n); } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsDFS(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs_mem.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs_mem.ts index 9bce393bf..e08d1aaac 100644 --- a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs_mem.ts +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs_mem.ts @@ -4,11 +4,11 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Memoization search */ +/* Memoized search */ function dfs(i: number, mem: number[]): number { - // Known dp[1] and dp[2], return them + // The values of dp[1] and dp[2] are known; return them if (i === 1 || i === 2) return i; - // If record dp[i] exists, return it directly + // If dp[i] has been recorded, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] const count = dfs(i - 1, mem) + dfs(i - 2, mem); @@ -17,16 +17,16 @@ function dfs(i: number, mem: number[]): number { return count; } -/* Climbing stairs: Memoization search */ +/* Climbing stairs: memoized search */ function climbingStairsDFSMem(n: number): number { - // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded const mem = new Array(n + 1).fill(-1); return dfs(n, mem); } -/* Driver Code */ +/* Driver code */ const n = 9; const res = climbingStairsDFSMem(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dp.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dp.ts index 896aa7f7b..1b873fa02 100644 --- a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dp.ts +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dp.ts @@ -4,22 +4,22 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Climbing stairs: Dynamic programming */ +/* Climbing stairs: dynamic programming */ function climbingStairsDP(n: number): number { if (n === 1 || n === 2) return n; - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions const dp = new Array(n + 1).fill(-1); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = 1; dp[2] = 2; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (let i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } -/* Climbing stairs: Space-optimized dynamic programming */ +/* Climbing stairs: space-optimized dynamic programming */ function climbingStairsDPComp(n: number): number { if (n === 1 || n === 2) return n; let a = 1, @@ -32,11 +32,11 @@ function climbingStairsDPComp(n: number): number { return b; } -/* Driver Code */ +/* Driver code */ const n = 9; let res = climbingStairsDP(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); res = climbingStairsDPComp(n); -console.log(`Climbing ${n} stairs has ${res} solutions`); +console.log(`Number of ways to climb ${n} stairs: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/coin_change.ts b/en/codes/typescript/chapter_dynamic_programming/coin_change.ts index 9af6fc82a..af131c67f 100644 --- a/en/codes/typescript/chapter_dynamic_programming/coin_change.ts +++ b/en/codes/typescript/chapter_dynamic_programming/coin_change.ts @@ -4,26 +4,26 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Coin change: Dynamic programming */ +/* Coin change: dynamic programming */ function coinChangeDP(coins: Array, amt: number): number { const n = coins.length; const MAX = amt + 1; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n + 1 }, () => Array.from({ length: amt + 1 }, () => 0) ); - // State transition: first row and first column + // State transitions: first row and first column for (let a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -31,21 +31,21 @@ function coinChangeDP(coins: Array, amt: number): number { return dp[n][amt] !== MAX ? dp[n][amt] : -1; } -/* Coin change: Space-optimized dynamic programming */ +/* Coin change: space-optimized dynamic programming */ function coinChangeDPComp(coins: Array, amt: number): number { const n = coins.length; const MAX = amt + 1; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: amt + 1 }, () => MAX); dp[0] = 0; - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // The smaller value between not selecting and selecting coin i + // Take the smaller result of not choosing and choosing coin i dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -53,16 +53,16 @@ function coinChangeDPComp(coins: Array, amt: number): number { return dp[amt] !== MAX ? dp[amt] : -1; } -/* Driver Code */ +/* Driver code */ const coins = [1, 2, 5]; const amt = 4; // Dynamic programming let res = coinChangeDP(coins, amt); -console.log(`Minimum coins needed to make target amount is ${res}`); +console.log(`Minimum number of coins needed to make the target amount: ${res}`); // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt); -console.log(`Minimum coins needed to make target amount is ${res}`); +console.log(`Minimum number of coins needed to make the target amount: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/coin_change_ii.ts b/en/codes/typescript/chapter_dynamic_programming/coin_change_ii.ts index 3f3c3a1ba..79219d330 100644 --- a/en/codes/typescript/chapter_dynamic_programming/coin_change_ii.ts +++ b/en/codes/typescript/chapter_dynamic_programming/coin_change_ii.ts @@ -4,25 +4,25 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Coin change II: Dynamic programming */ +/* Coin change II: dynamic programming */ function coinChangeIIDP(coins: Array, amt: number): number { const n = coins.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n + 1 }, () => Array.from({ length: amt + 1 }, () => 0) ); - // Initialize first column + // Initialize the first column for (let i = 0; i <= n; i++) { dp[i][0] = 1; } - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -30,20 +30,20 @@ function coinChangeIIDP(coins: Array, amt: number): number { return dp[n][amt]; } -/* Coin change II: Space-optimized dynamic programming */ +/* Coin change II: space-optimized dynamic programming */ function coinChangeIIDPComp(coins: Array, amt: number): number { const n = coins.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: amt + 1 }, () => 0); dp[0] = 1; - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeds target amount, don't select coin i + // If the coin exceeds the target amount, do not choose coin i dp[a] = dp[a]; } else { - // Sum of the two options: not selecting and selecting coin i + // Add the numbers of ways without and with coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -51,16 +51,16 @@ function coinChangeIIDPComp(coins: Array, amt: number): number { return dp[amt]; } -/* Driver Code */ +/* Driver code */ const coins = [1, 2, 5]; const amt = 5; // Dynamic programming let res = coinChangeIIDP(coins, amt); -console.log(`Number of coin combinations to make target amount is ${res}`); +console.log(`Number of coin combinations that make the target amount: ${res}`); // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt); -console.log(`Number of coin combinations to make target amount is ${res}`); +console.log(`Number of coin combinations that make the target amount: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/edit_distance.ts b/en/codes/typescript/chapter_dynamic_programming/edit_distance.ts index 5c1b7ce20..02efba4f8 100644 --- a/en/codes/typescript/chapter_dynamic_programming/edit_distance.ts +++ b/en/codes/typescript/chapter_dynamic_programming/edit_distance.ts @@ -4,30 +4,30 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Edit distance: Brute-force search */ +/* Edit distance: brute-force search */ function editDistanceDFS(s: string, t: string, i: number, j: number): number { - // If both s and t are empty, return 0 + // If both current prefixes of s and t are empty, return 0 if (i === 0 && j === 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return j, the prefix length of t if (i === 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return i, the prefix length of s if (j === 0) return i; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s.charAt(i - 1) === t.charAt(j - 1)) return editDistanceDFS(s, t, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 const insert = editDistanceDFS(s, t, i, j - 1); const del = editDistanceDFS(s, t, i - 1, j); const replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return minimum edit steps + // Return the minimum number of edits return Math.min(insert, del, replace) + 1; } -/* Edit distance: Memoization search */ +/* Edit distance: memoized search */ function editDistanceDFSMem( s: string, t: string, @@ -35,53 +35,53 @@ function editDistanceDFSMem( i: number, j: number ): number { - // If both s and t are empty, return 0 + // If both current prefixes of s and t are empty, return 0 if (i === 0 && j === 0) return 0; - // If s is empty, return length of t + // If the current prefix of s is empty, return j, the prefix length of t if (i === 0) return j; - // If t is empty, return length of s + // If the current prefix of t is empty, return i, the prefix length of s if (j === 0) return i; - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] !== -1) return mem[i][j]; - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them if (s.charAt(i - 1) === t.charAt(j - 1)) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 const insert = editDistanceDFSMem(s, t, mem, i, j - 1); const del = editDistanceDFSMem(s, t, mem, i - 1, j); const replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return minimum edit steps + // Record and return the minimum number of edits mem[i][j] = Math.min(insert, del, replace) + 1; return mem[i][j]; } -/* Edit distance: Dynamic programming */ +/* Edit distance: dynamic programming */ function editDistanceDP(s: string, t: string): number { const n = s.length, m = t.length; const dp = Array.from({ length: n + 1 }, () => Array.from({ length: m + 1 }, () => 0) ); - // State transition: first row and first column + // State transitions: first row and first column for (let i = 1; i <= n; i++) { dp[i][0] = i; } for (let j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (let i = 1; i <= n; i++) { for (let j = 1; j <= m; j++) { if (s.charAt(i - 1) === t.charAt(j - 1)) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[i][j] = dp[i - 1][j - 1]; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1; } @@ -90,37 +90,37 @@ function editDistanceDP(s: string, t: string): number { return dp[n][m]; } -/* Edit distance: Space-optimized dynamic programming */ +/* Edit distance: space-optimized dynamic programming */ function editDistanceDPComp(s: string, t: string): number { const n = s.length, m = t.length; const dp = new Array(m + 1).fill(0); - // State transition: first row + // State transitions: first row for (let j = 1; j <= m; j++) { dp[j] = j; } - // State transition: rest of the rows + // State transitions: remaining rows for (let i = 1; i <= n; i++) { - // State transition: first column + // State transitions: first column let leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: rest of the columns + // State transitions: remaining columns for (let j = 1; j <= m; j++) { const temp = dp[j]; if (s.charAt(i - 1) === t.charAt(j - 1)) { - // If two characters are equal, skip both characters + // If the two characters are equal, skip both of them dp[j] = leftup; } else { - // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 dp[j] = Math.min(dp[j - 1], dp[j], leftup) + 1; } - leftup = temp; // Update for next round's dp[i-1, j-1] + leftup = temp; // Update to dp[i-1, j-1] for the next iteration } } return dp[m]; } -/* Driver Code */ +/* Driver code */ const s = 'bag'; const t = 'pack'; const n = s.length, @@ -128,21 +128,21 @@ const n = s.length, // Brute-force search let res = editDistanceDFS(s, t, n, m); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); -// Memoization search +// Memoized search const mem = Array.from({ length: n + 1 }, () => Array.from({ length: m + 1 }, () => -1) ); res = editDistanceDFSMem(s, t, mem, n, m); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); // Dynamic programming res = editDistanceDP(s, t); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); // Space-optimized dynamic programming res = editDistanceDPComp(s, t); -console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); +console.log(`Minimum number of edits to transform ${s} into ${t}: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/knapsack.ts b/en/codes/typescript/chapter_dynamic_programming/knapsack.ts index 5107c59ab..380fbc631 100644 --- a/en/codes/typescript/chapter_dynamic_programming/knapsack.ts +++ b/en/codes/typescript/chapter_dynamic_programming/knapsack.ts @@ -4,29 +4,29 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* 0-1 knapsack: Brute-force search */ +/* 0-1 knapsack: brute-force search */ function knapsackDFS( wgt: Array, val: Array, i: number, c: number ): number { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i === 0 || c === 0) { return 0; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i const no = knapsackDFS(wgt, val, i - 1, c); const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the larger value of the two options + // Return the larger value from the two choices return Math.max(no, yes); } -/* 0-1 knapsack: Memoization search */ +/* 0-1 knapsack: memoized search */ function knapsackDFSMem( wgt: Array, val: Array, @@ -34,46 +34,46 @@ function knapsackDFSMem( i: number, c: number ): number { - // If all items have been selected or knapsack has no remaining capacity, return value 0 + // If all items have been considered or no capacity remains, return value 0 if (i === 0 || c === 0) { return 0; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][c] !== -1) { return mem[i][c]; } - // If exceeds knapsack capacity, can only choose not to put it in + // If the item exceeds the remaining capacity, it must be excluded if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } - // Calculate the maximum value of not putting in and putting in item i + // Calculate the maximum values obtained by excluding and including item i const no = knapsackDFSMem(wgt, val, mem, i - 1, c); const yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the larger value of the two options + // Record and return the larger value from the two choices mem[i][c] = Math.max(no, yes); return mem[i][c]; } -/* 0-1 knapsack: Dynamic programming */ +/* 0-1 knapsack: dynamic programming */ function knapsackDP( wgt: Array, val: Array, cap: number ): number { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n + 1 }, () => Array.from({ length: cap + 1 }, () => 0) ); - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = Math.max( dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1] @@ -84,21 +84,21 @@ function knapsackDP( return dp[n][cap]; } -/* 0-1 knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: space-optimized dynamic programming */ function knapsackDPComp( wgt: Array, val: Array, cap: number ): number { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array(cap + 1).fill(0); - // State transition + // State transitions for (let i = 1; i <= n; i++) { - // Traverse in reverse order + // Traverse in decreasing order for (let c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -106,7 +106,7 @@ function knapsackDPComp( return dp[cap]; } -/* Driver Code */ +/* Driver code */ const wgt = [10, 20, 30, 40, 50]; const val = [50, 120, 150, 210, 240]; const cap = 50; @@ -114,21 +114,21 @@ const n = wgt.length; // Brute-force search let res = knapsackDFS(wgt, val, n, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum total item value within the knapsack capacity: ${res}`); -// Memoization search +// Memoized search const mem = Array.from({ length: n + 1 }, () => Array.from({ length: cap + 1 }, () => -1) ); res = knapsackDFSMem(wgt, val, mem, n, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum total item value within the knapsack capacity: ${res}`); // Dynamic programming res = knapsackDP(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum total item value within the knapsack capacity: ${res}`); // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum total item value within the knapsack capacity: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.ts b/en/codes/typescript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.ts index 6266990ce..8bea67075 100644 --- a/en/codes/typescript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.ts +++ b/en/codes/typescript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.ts @@ -4,25 +4,25 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Minimum cost climbing stairs: Dynamic programming */ +/* Minimum cost to climb stairs: dynamic programming */ function minCostClimbingStairsDP(cost: Array): number { const n = cost.length - 1; if (n === 1 || n === 2) { return cost[n]; } - // Initialize dp table, used to store solutions to subproblems + // Initialize the dp table to store subproblem solutions const dp = new Array(n + 1); - // Initial state: preset the solution to the smallest subproblem + // Initial states: set the solutions to the smallest subproblems dp[1] = cost[1]; dp[2] = cost[2]; - // State transition: gradually solve larger subproblems from smaller ones + // State transitions: solve progressively larger subproblems using smaller ones for (let i = 3; i <= n; i++) { dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; } return dp[n]; } -/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +/* Minimum cost to climb stairs: space-optimized dynamic programming */ function minCostClimbingStairsDPComp(cost: Array): number { const n = cost.length - 1; if (n === 1 || n === 2) { @@ -38,14 +38,14 @@ function minCostClimbingStairsDPComp(cost: Array): number { return b; } -/* Driver Code */ +/* Driver code */ const cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; -console.log(`Input stair cost list is: ${cost}`); +console.log(`Input stair cost list: ${cost}`); let res = minCostClimbingStairsDP(cost); -console.log(`Minimum cost to climb stairs is: ${res}`); +console.log(`Minimum cost to climb the stairs: ${res}`); res = minCostClimbingStairsDPComp(cost); -console.log(`Minimum cost to climb stairs is: ${res}`); +console.log(`Minimum cost to climb the stairs: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/min_path_sum.ts b/en/codes/typescript/chapter_dynamic_programming/min_path_sum.ts index a94f3140c..c41f22377 100644 --- a/en/codes/typescript/chapter_dynamic_programming/min_path_sum.ts +++ b/en/codes/typescript/chapter_dynamic_programming/min_path_sum.ts @@ -4,72 +4,72 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Minimum path sum: Brute-force search */ +/* Minimum path sum: brute-force search */ function minPathSumDFS( grid: Array>, i: number, j: number ): number { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i === 0 && j == 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return Infinity; } - // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) const up = minPathSumDFS(grid, i - 1, j); const left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from top-left to (i, j) + // Return the minimum path cost from the top-left corner to (i, j) return Math.min(left, up) + grid[i][j]; } -/* Minimum path sum: Memoization search */ +/* Minimum path sum: memoized search */ function minPathSumDFSMem( grid: Array>, mem: Array>, i: number, j: number ): number { - // If it's the top-left cell, terminate the search + // Stop searching at the top-left cell if (i === 0 && j === 0) { return grid[0][0]; } - // If row or column index is out of bounds, return +∞ cost + // If a row or column index is out of bounds, return a cost of +∞ if (i < 0 || j < 0) { return Infinity; } - // If there's a record, return it directly + // If a result has already been recorded, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // Minimum path cost for left and upper cells + // Minimum path costs to the cells on the left and above const up = minPathSumDFSMem(grid, mem, i - 1, j); const left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from top-left to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) mem[i][j] = Math.min(left, up) + grid[i][j]; return mem[i][j]; } -/* Minimum path sum: Dynamic programming */ +/* Minimum path sum: dynamic programming */ function minPathSumDP(grid: Array>): number { const n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n }, () => Array.from({ length: m }, () => 0) ); dp[0][0] = grid[0][0]; - // State transition: first row + // State transitions: first row for (let j = 1; j < m; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // State transition: first column + // State transitions: first column for (let i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: rest of the rows and columns + // State transitions: remaining rows and columns for (let i = 1; i < n; i++) { for (let j: number = 1; j < m; j++) { dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -78,22 +78,22 @@ function minPathSumDP(grid: Array>): number { return dp[n - 1][m - 1]; } -/* Minimum path sum: Space-optimized dynamic programming */ +/* Minimum path sum: space-optimized dynamic programming */ function minPathSumDPComp(grid: Array>): number { const n = grid.length, m = grid[0].length; - // Initialize dp table + // Initialize the dp table const dp = new Array(m); - // State transition: first row + // State transitions: first row dp[0] = grid[0][0]; for (let j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: rest of the rows + // State transitions: remaining rows for (let i = 1; i < n; i++) { - // State transition: first column + // State transitions: first column dp[0] = dp[0] + grid[i][0]; - // State transition: rest of the columns + // State transitions: remaining columns for (let j = 1; j < m; j++) { dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -101,7 +101,7 @@ function minPathSumDPComp(grid: Array>): number { return dp[m - 1]; } -/* Driver Code */ +/* Driver code */ const grid = [ [1, 3, 1, 5], [2, 2, 4, 2], @@ -112,21 +112,21 @@ const n = grid.length, m = grid[0].length; // Brute-force search let res = minPathSumDFS(grid, n - 1, m - 1); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); -// Memoization search +// Memoized search const mem = Array.from({ length: n }, () => Array.from({ length: m }, () => -1) ); res = minPathSumDFSMem(grid, mem, n - 1, m - 1); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); // Dynamic programming res = minPathSumDP(grid); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); // Space-optimized dynamic programming res = minPathSumDPComp(grid); -console.log(`Minimum path sum from top-left to bottom-right is ${res}`); +console.log(`Minimum path sum from the top-left to the bottom-right: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/unbounded_knapsack.ts b/en/codes/typescript/chapter_dynamic_programming/unbounded_knapsack.ts index 6f026465f..d067ec557 100644 --- a/en/codes/typescript/chapter_dynamic_programming/unbounded_knapsack.ts +++ b/en/codes/typescript/chapter_dynamic_programming/unbounded_knapsack.ts @@ -4,25 +4,25 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Unbounded knapsack: Dynamic programming */ +/* Unbounded knapsack: dynamic programming */ function unboundedKnapsackDP( wgt: Array, val: Array, cap: number ): number { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: n + 1 }, () => Array.from({ length: cap + 1 }, () => 0) ); - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[i][c] = dp[i - 1][c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[i][c] = Math.max( dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1] @@ -33,23 +33,23 @@ function unboundedKnapsackDP( return dp[n][cap]; } -/* Unbounded knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: space-optimized dynamic programming */ function unboundedKnapsackDPComp( wgt: Array, val: Array, cap: number ): number { const n = wgt.length; - // Initialize dp table + // Initialize the dp table const dp = Array.from({ length: cap + 1 }, () => 0); - // State transition + // State transitions for (let i = 1; i <= n; i++) { for (let c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeds knapsack capacity, don't select item i + // If item i exceeds the remaining capacity, do not choose it dp[c] = dp[c]; } else { - // The larger value between not selecting and selecting item i + // Take the larger value obtained by excluding and including item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -57,17 +57,17 @@ function unboundedKnapsackDPComp( return dp[cap]; } -/* Driver Code */ +/* Driver code */ const wgt = [1, 2, 3]; const val = [5, 11, 15]; const cap = 4; // Dynamic programming let res = unboundedKnapsackDP(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum total item value within the knapsack capacity: ${res}`); // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum total item value within the knapsack capacity: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_graph/graph_adjacency_list.ts b/en/codes/typescript/chapter_graph/graph_adjacency_list.ts index 6f4fe359c..df1aa5a7c 100644 --- a/en/codes/typescript/chapter_graph/graph_adjacency_list.ts +++ b/en/codes/typescript/chapter_graph/graph_adjacency_list.ts @@ -6,9 +6,9 @@ import { Vertex } from '../modules/Vertex'; -/* Undirected graph class based on adjacency list */ +/* Undirected graph class implemented with an adjacency list */ class GraphAdjList { - // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + // Adjacency list: key is a vertex, value is the list of all its neighbors adjList: Map; /* Constructor */ @@ -27,21 +27,21 @@ class GraphAdjList { return this.adjList.size; } - /* Add edge */ + /* Add an edge */ addEdge(vet1: Vertex, vet2: Vertex): void { if ( !this.adjList.has(vet1) || !this.adjList.has(vet2) || vet1 === vet2 ) { - throw new Error('Illegal Argument Exception'); + throw new Error('Invalid argument'); } // Add edge vet1 - vet2 this.adjList.get(vet1).push(vet2); this.adjList.get(vet2).push(vet1); } - /* Remove edge */ + /* Remove an edge */ removeEdge(vet1: Vertex, vet2: Vertex): void { if ( !this.adjList.has(vet1) || @@ -49,28 +49,28 @@ class GraphAdjList { vet1 === vet2 || this.adjList.get(vet1).indexOf(vet2) === -1 ) { - throw new Error('Illegal Argument Exception'); + throw new Error('Invalid argument'); } // Remove edge vet1 - vet2 this.adjList.get(vet1).splice(this.adjList.get(vet1).indexOf(vet2), 1); this.adjList.get(vet2).splice(this.adjList.get(vet2).indexOf(vet1), 1); } - /* Add vertex */ + /* Add a vertex */ addVertex(vet: Vertex): void { if (this.adjList.has(vet)) return; - // Add a new linked list in the adjacency list + // Add a new list to the adjacency list this.adjList.set(vet, []); } - /* Remove vertex */ + /* Remove a vertex */ removeVertex(vet: Vertex): void { if (!this.adjList.has(vet)) { - throw new Error('Illegal Argument Exception'); + throw new Error('Invalid argument'); } - // Remove the linked list corresponding to vertex vet in the adjacency list + // Remove the list corresponding to vertex vet from the adjacency list this.adjList.delete(vet); - // Traverse the linked lists of other vertices and remove all edges containing vet + // Traverse the lists of the other vertices and remove all edges incident to vet for (const set of this.adjList.values()) { const index: number = set.indexOf(vet); if (index > -1) { @@ -79,7 +79,7 @@ class GraphAdjList { } } - /* Print adjacency list */ + /* Print the adjacency list */ print(): void { console.log('Adjacency list ='); for (const [key, value] of this.adjList.entries()) { @@ -92,9 +92,9 @@ class GraphAdjList { } } -/* Driver Code */ +/* Driver code */ if (import.meta.url.endsWith(process.argv[1])) { - /* Add edge */ + /* Initialize the undirected graph */ const v0 = new Vertex(1), v1 = new Vertex(3), v2 = new Vertex(2), @@ -109,31 +109,31 @@ if (import.meta.url.endsWith(process.argv[1])) { [v3, v4], ]; const graph = new GraphAdjList(edges); - console.log('\nAfter initialization, graph is'); + console.log('\nGraph after initialization:'); graph.print(); - /* Add edge */ - // Vertices 1, 2 are v0, v2 + /* Add an edge */ + // Vertices 1 and 2 are v0 and v2 graph.addEdge(v0, v2); - console.log('\nAfter adding edge 1-2, graph is'); + console.log('\nGraph after adding edge 1-2:'); graph.print(); - /* Remove edge */ - // Vertices 1, 3 are v0, v1 + /* Remove an edge */ + // Vertices 1 and 3 are v0 and v1 graph.removeEdge(v0, v1); - console.log('\nAfter removing edge 1-3, graph is'); + console.log('\nGraph after removing edge 1-3:'); graph.print(); - /* Add vertex */ + /* Add a vertex */ const v5 = new Vertex(6); graph.addVertex(v5); - console.log('\nAfter adding vertex 6, graph is'); + console.log('\nGraph after adding vertex 6:'); graph.print(); - /* Remove vertex */ + /* Remove a vertex */ // Vertex 3 is v1 graph.removeVertex(v1); - console.log('\nAfter removing vertex 3, graph is'); + console.log('\nGraph after removing vertex 3:'); graph.print(); } diff --git a/en/codes/typescript/chapter_graph/graph_adjacency_matrix.ts b/en/codes/typescript/chapter_graph/graph_adjacency_matrix.ts index 986a9df9a..7632ca9a5 100644 --- a/en/codes/typescript/chapter_graph/graph_adjacency_matrix.ts +++ b/en/codes/typescript/chapter_graph/graph_adjacency_matrix.ts @@ -4,21 +4,21 @@ * Author: Zhuo Qinyue (1403450829@qq.com) */ -/* Undirected graph class based on adjacency matrix */ +/* Undirected graph class implemented with an adjacency matrix */ class GraphAdjMat { - vertices: number[]; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" - adjMat: number[][]; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + vertices: number[]; // Vertex list: elements are vertex values, and indices are vertex indices + adjMat: number[][]; // Adjacency matrix: row and column indices correspond to vertex indices /* Constructor */ constructor(vertices: number[], edges: number[][]) { this.vertices = []; this.adjMat = []; - // Add vertex + // Add a vertex for (const val of vertices) { this.addVertex(val); } - // Add edge - // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + // Add an edge + // Note that the entries in edges are vertex indices, corresponding to element indices in vertices for (const e of edges) { this.addEdge(e[0], e[1]); } @@ -29,10 +29,10 @@ class GraphAdjMat { return this.vertices.length; } - /* Add vertex */ + /* Add a vertex */ addVertex(val: number): void { const n: number = this.size(); - // Add the value of the new vertex to the vertex list + // Append the new vertex value to the vertex list this.vertices.push(val); // Add a row to the adjacency matrix const newRow: number[] = []; @@ -46,55 +46,55 @@ class GraphAdjMat { } } - /* Remove vertex */ + /* Remove a vertex */ removeVertex(index: number): void { if (index >= this.size()) { - throw new RangeError('Index Out Of Bounds Exception'); + throw new RangeError('Index out of bounds'); } - // Remove the vertex at index from the vertex list + // Remove the vertex at index `index` from the vertex list this.vertices.splice(index, 1); - // Remove the row at index from the adjacency matrix + // Remove row `index` from the adjacency matrix this.adjMat.splice(index, 1); - // Remove the column at index from the adjacency matrix + // Remove column `index` from the adjacency matrix for (const row of this.adjMat) { row.splice(index, 1); } } - /* Add edge */ - // Parameters i, j correspond to the vertices element indices + /* Add an edge */ + // Parameters i and j are element indices in vertices addEdge(i: number, j: number): void { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); + throw new RangeError('Index out of bounds'); } - // In undirected graph, adjacency matrix is symmetric about main diagonal, i.e., satisfies (i, j) === (j, i) + // In an undirected graph, the adjacency matrix is symmetric about its main diagonal: (i, j) === (j, i) this.adjMat[i][j] = 1; this.adjMat[j][i] = 1; } - /* Remove edge */ - // Parameters i, j correspond to the vertices element indices + /* Remove an edge */ + // Parameters i and j are element indices in vertices removeEdge(i: number, j: number): void { - // Handle index out of bounds and equality + // Handle out-of-bounds or equal indices if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); + throw new RangeError('Index out of bounds'); } this.adjMat[i][j] = 0; this.adjMat[j][i] = 0; } - /* Print adjacency matrix */ + /* Print the adjacency matrix */ print(): void { console.log('Vertex list = ', this.vertices); console.log('Adjacency matrix =', this.adjMat); } } -/* Driver Code */ -/* Add edge */ -// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices +/* Driver code */ +/* Initialize the undirected graph */ +// Note that the entries in edges are vertex indices, corresponding to element indices in vertices const vertices: number[] = [1, 3, 2, 5, 4]; const edges: number[][] = [ [0, 1], @@ -105,30 +105,30 @@ const edges: number[][] = [ [3, 4], ]; const graph: GraphAdjMat = new GraphAdjMat(vertices, edges); -console.log('\nAfter initialization, graph is'); +console.log('\nGraph after initialization:'); graph.print(); -/* Add edge */ -// Add vertex +/* Add an edge */ +// Vertices 1 and 2 have indices 0 and 2, respectively graph.addEdge(0, 2); -console.log('\nAfter adding edge 1-2, graph is'); +console.log('\nGraph after adding edge 1-2:'); graph.print(); -/* Remove edge */ -// Vertices 1, 3 have indices 0, 1 respectively +/* Remove an edge */ +// Vertices 1 and 3 have indices 0 and 1, respectively graph.removeEdge(0, 1); -console.log('\nAfter removing edge 1-3, graph is'); +console.log('\nGraph after removing edge 1-3:'); graph.print(); -/* Add vertex */ +/* Add a vertex */ graph.addVertex(6); -console.log('\nAfter adding vertex 6, graph is'); +console.log('\nGraph after adding vertex 6:'); graph.print(); -/* Remove vertex */ +/* Remove a vertex */ // Vertex 3 has index 1 graph.removeVertex(1); -console.log('\nAfter removing vertex 3, graph is'); +console.log('\nGraph after removing vertex 3:'); graph.print(); export {}; diff --git a/en/codes/typescript/chapter_graph/graph_bfs.ts b/en/codes/typescript/chapter_graph/graph_bfs.ts index 0274440f9..9de88c5c8 100644 --- a/en/codes/typescript/chapter_graph/graph_bfs.ts +++ b/en/codes/typescript/chapter_graph/graph_bfs.ts @@ -8,34 +8,34 @@ import { GraphAdjList } from './graph_adjacency_list'; import { Vertex } from '../modules/Vertex'; /* Breadth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex function graphBFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { // Vertex traversal sequence const res: Vertex[] = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices const visited: Set = new Set(); visited.add(startVet); - // Queue used to implement BFS + // Use a queue to implement BFS const que = [startVet]; - // Starting from vertex vet, loop until all vertices are visited + // Starting from startVet, loop until all reachable vertices have been visited while (que.length) { const vet = que.shift(); // Dequeue the front vertex - res.push(vet); // Record visited vertex - // Traverse all adjacent vertices of this vertex + res.push(vet); // Record the visited vertex + // Traverse all neighbors of this vertex for (const adjVet of graph.adjList.get(vet) ?? []) { if (visited.has(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - que.push(adjVet); // Only enqueue unvisited + que.push(adjVet); // Enqueue only unvisited vertices visited.add(adjVet); // Mark this vertex as visited } } - // Return vertex traversal sequence + // Return the vertex traversal sequence return res; } -/* Driver Code */ -/* Add edge */ +/* Driver code */ +/* Initialize the undirected graph */ const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); const edges = [ [v[0], v[1]], @@ -52,10 +52,10 @@ const edges = [ [v[7], v[8]], ]; const graph = new GraphAdjList(edges); -console.log('\nAfter initialization, graph is'); +console.log('\nGraph after initialization:'); graph.print(); /* Breadth-first traversal */ const res = graphBFS(graph, v[0]); -console.log('\nBreadth-first traversal (BFS) vertex sequence is'); +console.log('\nVertex sequence from breadth-first traversal (BFS):'); console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/typescript/chapter_graph/graph_dfs.ts b/en/codes/typescript/chapter_graph/graph_dfs.ts index 736d82890..382afb445 100644 --- a/en/codes/typescript/chapter_graph/graph_dfs.ts +++ b/en/codes/typescript/chapter_graph/graph_dfs.ts @@ -14,31 +14,31 @@ function dfs( res: Vertex[], vet: Vertex ): void { - res.push(vet); // Record visited vertex + res.push(vet); // Record the visited vertex visited.add(vet); // Mark this vertex as visited - // Traverse all adjacent vertices of this vertex + // Traverse all neighbors of this vertex for (const adjVet of graph.adjList.get(vet)) { if (visited.has(adjVet)) { - continue; // Skip vertices that have been visited + continue; // Skip vertices that have already been visited } - // Recursively visit adjacent vertices + // Recursively visit the neighboring vertex dfs(graph, visited, res, adjVet); } } /* Depth-first traversal */ -// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +// Represent the graph with an adjacency list to access all neighbors of a given vertex function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { // Vertex traversal sequence const res: Vertex[] = []; - // Hash set for recording vertices that have been visited + // Hash set recording visited vertices const visited: Set = new Set(); dfs(graph, visited, res, startVet); return res; } -/* Driver Code */ -/* Add edge */ +/* Driver code */ +/* Initialize the undirected graph */ const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6]); const edges = [ [v[0], v[1]], @@ -49,10 +49,10 @@ const edges = [ [v[5], v[6]], ]; const graph = new GraphAdjList(edges); -console.log('\nAfter initialization, graph is'); +console.log('\nGraph after initialization:'); graph.print(); /* Depth-first traversal */ const res = graphDFS(graph, v[0]); -console.log('\nDepth-first traversal (DFS) vertex sequence is'); +console.log('\nVertex sequence from depth-first traversal (DFS):'); console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/typescript/chapter_greedy/coin_change_greedy.ts b/en/codes/typescript/chapter_greedy/coin_change_greedy.ts index 7a31304d0..ee23bb044 100644 --- a/en/codes/typescript/chapter_greedy/coin_change_greedy.ts +++ b/en/codes/typescript/chapter_greedy/coin_change_greedy.ts @@ -4,14 +4,14 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Coin change: Greedy algorithm */ +/* Coin change: greedy algorithm */ function coinChangeGreedy(coins: number[], amt: number): number { - // Assume coins array is sorted + // Assume coins is sorted in ascending order let i = coins.length - 1; let count = 0; - // Loop to make greedy choices until no remaining amount + // Repeatedly make greedy choices until no amount remains while (amt > 0) { - // Find the coin that is less than and closest to the remaining amount + // Find the largest coin no greater than the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -19,32 +19,32 @@ function coinChangeGreedy(coins: number[], amt: number): number { amt -= coins[i]; count++; } - // If no feasible solution is found, return -1 + // Return -1 if no feasible solution was found return amt === 0 ? count : -1; } -/* Driver Code */ -// Greedy algorithm: Can guarantee finding the global optimal solution +/* Driver code */ +// Greedy algorithm: guaranteed to find a globally optimal solution let coins: number[] = [1, 5, 10, 20, 50, 100]; let amt: number = 186; let res: number = coinChangeGreedy(coins, amt); console.log(`\ncoins = ${coins}, amt = ${amt}`); -console.log(`Minimum coins needed to make ${amt} is ${res}`); +console.log(`Number of coins chosen by the greedy algorithm to make ${amt}: ${res}`); -// Greedy algorithm: Cannot guarantee finding the global optimal solution +// Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 20, 50]; amt = 60; res = coinChangeGreedy(coins, amt); console.log(`\ncoins = ${coins}, amt = ${amt}`); -console.log(`Minimum coins needed to make ${amt} is ${res}`); -console.log('Actually the minimum number needed is 3, i.e., 20 + 20 + 20'); +console.log(`Number of coins chosen by the greedy algorithm to make ${amt}: ${res}`); +console.log('The actual minimum is 3 coins: 20 + 20 + 20'); -// Greedy algorithm: Cannot guarantee finding the global optimal solution +// Greedy algorithm: not guaranteed to find a globally optimal solution coins = [1, 49, 50]; amt = 98; res = coinChangeGreedy(coins, amt); console.log(`\ncoins = ${coins}, amt = ${amt}`); -console.log(`Minimum coins needed to make ${amt} is ${res}`); -console.log('Actually the minimum number needed is 2, i.e., 49 + 49'); +console.log(`Number of coins chosen by the greedy algorithm to make ${amt}: ${res}`); +console.log('The actual minimum is 2 coins: 49 + 49'); export {}; diff --git a/en/codes/typescript/chapter_greedy/fractional_knapsack.ts b/en/codes/typescript/chapter_greedy/fractional_knapsack.ts index 43236d262..b2e51bf6b 100644 --- a/en/codes/typescript/chapter_greedy/fractional_knapsack.ts +++ b/en/codes/typescript/chapter_greedy/fractional_knapsack.ts @@ -15,36 +15,36 @@ class Item { } } -/* Fractional knapsack: Greedy algorithm */ +/* Fractional knapsack: greedy algorithm */ function fractionalKnapsack(wgt: number[], val: number[], cap: number): number { - // Create item list with two attributes: weight, value + // Create a list of items, each with two attributes: weight and value const items: Item[] = wgt.map((w, i) => new Item(w, val[i])); - // Sort by unit value item.v / item.w from high to low + // Sort by value per unit weight, item.v / item.w, in descending order items.sort((a, b) => b.v / b.w - a.v / a.w); - // Loop for greedy selection + // Repeatedly make greedy choices let res = 0; for (const item of items) { if (item.w <= cap) { - // If remaining capacity is sufficient, put the entire current item into the knapsack + // If enough capacity remains, put the entire current item in the knapsack res += item.v; cap -= item.w; } else { - // If remaining capacity is insufficient, put part of the current item into the knapsack + // If the remaining capacity is insufficient, put part of the current item in the knapsack res += (item.v / item.w) * cap; - // No remaining capacity, so break out of the loop + // No capacity remains, so exit the loop break; } } return res; } -/* Driver Code */ +/* Driver code */ const wgt: number[] = [10, 20, 30, 40, 50]; const val: number[] = [50, 120, 150, 210, 240]; const cap: number = 50; // Greedy algorithm const res: number = fractionalKnapsack(wgt, val, cap); -console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); +console.log(`Maximum total item value within the knapsack capacity: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_greedy/max_capacity.ts b/en/codes/typescript/chapter_greedy/max_capacity.ts index 7da8d703d..0762b7198 100644 --- a/en/codes/typescript/chapter_greedy/max_capacity.ts +++ b/en/codes/typescript/chapter_greedy/max_capacity.ts @@ -4,19 +4,19 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Max capacity: Greedy algorithm */ +/* Maximum container capacity: greedy algorithm */ function maxCapacity(ht: number[]): number { - // Initialize i, j to be at both ends of the array + // Initialize i and j at opposite ends of the array let i = 0, j = ht.length - 1; - // Initial max capacity is 0 + // The initial maximum capacity is 0 let res = 0; - // Loop for greedy selection until the two boards meet + // Repeatedly make greedy choices until the two sides meet while (i < j) { - // Update max capacity + // Update the maximum capacity const cap: number = Math.min(ht[i], ht[j]) * (j - i); res = Math.max(res, cap); - // Move the shorter board inward + // Move the shorter side inward if (ht[i] < ht[j]) { i += 1; } else { @@ -26,11 +26,11 @@ function maxCapacity(ht: number[]): number { return res; } -/* Driver Code */ +/* Driver code */ const ht: number[] = [3, 8, 5, 2, 7, 7, 3, 4]; // Greedy algorithm const res: number = maxCapacity(ht); -console.log(`Maximum capacity is ${res}`); +console.log(`Maximum capacity: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_greedy/max_product_cutting.ts b/en/codes/typescript/chapter_greedy/max_product_cutting.ts index 7041f6625..8cce7d318 100644 --- a/en/codes/typescript/chapter_greedy/max_product_cutting.ts +++ b/en/codes/typescript/chapter_greedy/max_product_cutting.ts @@ -4,32 +4,32 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Max product cutting: Greedy algorithm */ +/* Maximum product after splitting: greedy algorithm */ function maxProductCutting(n: number): number { - // When n <= 3, must cut out a 1 + // When n <= 3, one part must be 1 if (n <= 3) { return 1 * (n - 1); } - // Greedily cut out 3, a is the number of 3s, b is the remainder + // Greedily split off parts of size 3; a is the number of 3s and b is the remainder let a: number = Math.floor(n / 3); let b: number = n % 3; if (b === 1) { - // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + // When the remainder is 1, replace one pair 1 * 3 with 2 * 2 return Math.pow(3, a - 1) * 2 * 2; } if (b === 2) { - // When the remainder is 2, do nothing + // When the remainder is 2, no adjustment is needed return Math.pow(3, a) * 2; } - // When the remainder is 0, do nothing + // When the remainder is 0, no adjustment is needed return Math.pow(3, a); } -/* Driver Code */ +/* Driver code */ let n: number = 58; // Greedy algorithm let res: number = maxProductCutting(n); -console.log(`Maximum cutting product is ${res}`); +console.log(`Maximum product after splitting: ${res}`); export {}; diff --git a/en/codes/typescript/chapter_hashing/array_hash_map.ts b/en/codes/typescript/chapter_hashing/array_hash_map.ts index faf263f5c..d6580eb8a 100644 --- a/en/codes/typescript/chapter_hashing/array_hash_map.ts +++ b/en/codes/typescript/chapter_hashing/array_hash_map.ts @@ -4,7 +4,7 @@ * Author: Daniel (better.sunjian@gmail.com) */ -/* Key-value pair Number -> String */ +/* Key-value pair: Number -> String */ class Pair { public key: number; public val: string; @@ -15,12 +15,12 @@ class Pair { } } -/* Hash table based on array implementation */ +/* Hash table implemented with an array */ class ArrayHashMap { private readonly buckets: (Pair | null)[]; constructor() { - // Initialize array with 100 buckets + // Initialize an array containing 100 buckets this.buckets = new Array(100).fill(null); } @@ -29,7 +29,7 @@ class ArrayHashMap { return key % 100; } - /* Query operation */ + /* Lookup operation */ public get(key: number): string | null { let index = this.hashFunc(key); let pair = this.buckets[index]; @@ -37,7 +37,7 @@ class ArrayHashMap { return pair.val; } - /* Add operation */ + /* Insert operation */ public set(key: number, val: string) { let index = this.hashFunc(key); this.buckets[index] = new Pair(key, val); @@ -46,7 +46,7 @@ class ArrayHashMap { /* Remove operation */ public delete(key: number) { let index = this.hashFunc(key); - // Set to null to represent deletion + // Set the bucket to null to mark it as deleted this.buckets[index] = null; } @@ -83,7 +83,7 @@ class ArrayHashMap { return arr; } - /* Print hash table */ + /* Print the hash table */ public print() { let pairSet = this.entries(); for (const pair of pairSet) { @@ -92,41 +92,41 @@ class ArrayHashMap { } } -/* Driver Code */ -/* Initialize hash table */ +/* Driver code */ +/* Initialize the hash table */ const map = new ArrayHashMap(); -/* Add operation */ -// Add key-value pair (key, value) to the hash table -map.set(12836, 'Xiao Ha'); -map.set(15937, 'Xiao Luo'); -map.set(16750, 'Xiao Suan'); -map.set(13276, 'Xiao Fa'); -map.set(10583, 'Xiao Ya'); -console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +/* Insert operation */ +// Insert the key-value pair (key, value) into the hash table +map.set(12836, '小哈'); +map.set(15937, '小啰'); +map.set(16750, '小算'); +map.set(13276, '小法'); +map.set(10583, '小鸭'); +console.info('\nHash table after insertion:\nKey -> Value'); map.print(); -/* Query operation */ -// Input key into hash table to get value +/* Lookup operation */ +// Look up key in the hash table to obtain value let name = map.get(15937); -console.info('\nInput student ID 15937, query name ' + name); +console.info('\nLook up student ID 15937 to obtain the name ' + name); /* Remove operation */ -// Remove key-value pair (key, value) from hash table +// Remove the key-value pair (key, value) from the hash table map.delete(10583); -console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +console.info('\nHash table after removing 10583:\nKey -> Value'); map.print(); -/* Traverse hash table */ -console.info('\nTraverse key-value pairs Key->Value'); +/* Traverse the hash table */ +console.info('\nTraverse key-value pairs: Key->Value'); for (const pair of map.entries()) { if (!pair) continue; console.info(pair.key + ' -> ' + pair.val); } -console.info('\nTraverse keys only Key'); +console.info('\nTraverse keys only: Key'); for (const key of map.keys()) { console.info(key); } -console.info('\nTraverse values only Value'); +console.info('\nTraverse values only: Value'); for (const val of map.values()) { console.info(val); } diff --git a/en/codes/typescript/chapter_hashing/hash_map.ts b/en/codes/typescript/chapter_hashing/hash_map.ts index 31c3205f5..1ba7d4059 100644 --- a/en/codes/typescript/chapter_hashing/hash_map.ts +++ b/en/codes/typescript/chapter_hashing/hash_map.ts @@ -4,41 +4,41 @@ * Author: Daniel (better.sunjian@gmail.com) */ -/* Driver Code */ -/* Initialize hash table */ +/* Driver code */ +/* Initialize the hash table */ const map = new Map(); -/* Add operation */ -// Add key-value pair (key, value) to the hash table -map.set(12836, 'Xiao Ha'); -map.set(15937, 'Xiao Luo'); -map.set(16750, 'Xiao Suan'); -map.set(13276, 'Xiao Fa'); -map.set(10583, 'Xiao Ya'); -console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +/* Insert operation */ +// Insert the key-value pair (key, value) into the hash table +map.set(12836, '小哈'); +map.set(15937, '小啰'); +map.set(16750, '小算'); +map.set(13276, '小法'); +map.set(10583, '小鸭'); +console.info('\nHash table after insertion:\nKey -> Value'); console.info(map); -/* Query operation */ -// Input key into hash table to get value +/* Lookup operation */ +// Look up key in the hash table to obtain value let name = map.get(15937); -console.info('\nInput student ID 15937, query name ' + name); +console.info('\nLook up student ID 15937 to obtain the name ' + name); /* Remove operation */ -// Remove key-value pair (key, value) from hash table +// Remove the key-value pair (key, value) from the hash table map.delete(10583); -console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +console.info('\nHash table after removing 10583:\nKey -> Value'); console.info(map); -/* Traverse hash table */ -console.info('\nTraverse key-value pairs Key->Value'); +/* Traverse the hash table */ +console.info('\nTraverse key-value pairs: Key->Value'); for (const [k, v] of map.entries()) { console.info(k + ' -> ' + v); } -console.info('\nTraverse keys only Key'); +console.info('\nTraverse keys only: Key'); for (const k of map.keys()) { console.info(k); } -console.info('\nTraverse values only Value'); +console.info('\nTraverse values only: Value'); for (const v of map.values()) { console.info(v); } diff --git a/en/codes/typescript/chapter_hashing/hash_map_chaining.ts b/en/codes/typescript/chapter_hashing/hash_map_chaining.ts index 48bac7f32..ceb4abfd7 100644 --- a/en/codes/typescript/chapter_hashing/hash_map_chaining.ts +++ b/en/codes/typescript/chapter_hashing/hash_map_chaining.ts @@ -4,7 +4,7 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -/* Key-value pair Number -> String */ +/* Key-value pair: Number -> String */ class Pair { key: number; val: string; @@ -18,8 +18,8 @@ class Pair { class HashMapChaining { #size: number; // Number of key-value pairs #capacity: number; // Hash table capacity - #loadThres: number; // Load factor threshold for triggering expansion - #extendRatio: number; // Expansion multiplier + #loadThres: number; // Load factor threshold that triggers capacity growth + #extendRatio: number; // Capacity growth factor #buckets: Pair[][]; // Bucket array /* Constructor */ @@ -41,36 +41,36 @@ class HashMapChaining { return this.#size / this.#capacity; } - /* Query operation */ + /* Lookup operation */ get(key: number): string | null { const index = this.#hashFunc(key); const bucket = this.#buckets[index]; - // Traverse bucket, if key is found, return corresponding val + // Traverse the bucket; if key is found, return its corresponding val for (const pair of bucket) { if (pair.key === key) { return pair.val; } } - // If key is not found, return null + // Return null if key was not found return null; } - /* Add operation */ + /* Insert operation */ put(key: number, val: string): void { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (this.#loadFactor() > this.#loadThres) { this.#extend(); } const index = this.#hashFunc(key); const bucket = this.#buckets[index]; - // Traverse bucket, if specified key is encountered, update corresponding val and return + // Traverse the bucket; if the specified key is found, update its val and return for (const pair of bucket) { if (pair.key === key) { pair.val = val; return; } } - // If key does not exist, append key-value pair to the end + // If key is absent, append the key-value pair at the end const pair = new Pair(key, val); bucket.push(pair); this.#size++; @@ -80,7 +80,7 @@ class HashMapChaining { remove(key: number): void { const index = this.#hashFunc(key); let bucket = this.#buckets[index]; - // Traverse bucket and remove key-value pair from it + // Traverse the bucket and remove the key-value pair for (let i = 0; i < bucket.length; i++) { if (bucket[i].key === key) { bucket.splice(i, 1); @@ -90,15 +90,15 @@ class HashMapChaining { } } - /* Expand hash table */ + /* Grow the hash table capacity */ #extend(): void { - // Temporarily store the original hash table + // Keep a reference to the original hash table const bucketsTmp = this.#buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity this.#capacity *= this.#extendRatio; this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); this.#size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (const bucket of bucketsTmp) { for (const pair of bucket) { this.put(pair.key, pair.val); @@ -106,7 +106,7 @@ class HashMapChaining { } } - /* Print hash table */ + /* Print the hash table */ print(): void { for (const bucket of this.#buckets) { let res = []; @@ -118,29 +118,29 @@ class HashMapChaining { } } -/* Driver Code */ -/* Initialize hash table */ +/* Driver code */ +/* Initialize the hash table */ const map = new HashMapChaining(); -/* Add operation */ -// Add key-value pair (key, value) to the hash table -map.put(12836, 'Xiao Ha'); -map.put(15937, 'Xiao Luo'); -map.put(16750, 'Xiao Suan'); -map.put(13276, 'Xiao Fa'); -map.put(10583, 'Xiao Ya'); -console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +/* Insert operation */ +// Insert the key-value pair (key, value) into the hash table +map.put(12836, '小哈'); +map.put(15937, '小啰'); +map.put(16750, '小算'); +map.put(13276, '小法'); +map.put(10583, '小鸭'); +console.log('\nHash table after insertion:\nKey -> Value'); map.print(); -/* Query operation */ -// Input key into hash table to get value +/* Lookup operation */ +// Look up key in the hash table to obtain value const name = map.get(13276); -console.log('\nInput student ID 13276, query name ' + name); +console.log('\nLook up student ID 13276 to obtain the name ' + name); /* Remove operation */ -// Remove key-value pair (key, value) from hash table +// Remove the key-value pair (key, value) from the hash table map.remove(12836); -console.log('\nAfter removing 12836, hash table is\nKey -> Value'); +console.log('\nHash table after removing 12836:\nKey -> Value'); map.print(); export {}; diff --git a/en/codes/typescript/chapter_hashing/hash_map_open_addressing.ts b/en/codes/typescript/chapter_hashing/hash_map_open_addressing.ts index e755d60b8..4efe71b59 100644 --- a/en/codes/typescript/chapter_hashing/hash_map_open_addressing.ts +++ b/en/codes/typescript/chapter_hashing/hash_map_open_addressing.ts @@ -4,7 +4,7 @@ * Author: yuan0221 (yl1452491917@gmail.com), krahets (krahets@163.com) */ -/* Key-value pair Number -> String */ +/* Key-value pair: Number -> String */ class Pair { key: number; val: string; @@ -19,19 +19,19 @@ class Pair { class HashMapOpenAddressing { private size: number; // Number of key-value pairs private capacity: number; // Hash table capacity - private loadThres: number; // Load factor threshold for triggering expansion - private extendRatio: number; // Expansion multiplier + private loadThres: number; // Load factor threshold that triggers capacity growth + private extendRatio: number; // Capacity growth factor private buckets: Array; // Bucket array - private TOMBSTONE: Pair; // Removal marker + private TOMBSTONE: Pair; // Tombstone marker /* Constructor */ constructor() { this.size = 0; // Number of key-value pairs this.capacity = 4; // Hash table capacity - this.loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion - this.extendRatio = 2; // Expansion multiplier + this.loadThres = 2.0 / 3.0; // Load factor threshold that triggers capacity growth + this.extendRatio = 2; // Capacity growth factor this.buckets = Array(this.capacity).fill(null); // Bucket array - this.TOMBSTONE = new Pair(-1, '-1'); // Removal marker + this.TOMBSTONE = new Pair(-1, '-1'); // Tombstone marker } /* Hash function */ @@ -44,60 +44,60 @@ class HashMapOpenAddressing { return this.size / this.capacity; } - /* Search for bucket index corresponding to key */ + /* Find the bucket index for key */ private findBucket(key: number): number { let index = this.hashFunc(key); let firstTombstone = -1; - // Linear probing, break when encountering an empty bucket + // Probe linearly until an empty bucket is reached while (this.buckets[index] !== null) { - // If key is encountered, return the corresponding bucket index + // If key is found, return its bucket index if (this.buckets[index]!.key === key) { - // If a removal marker was encountered before, move the key-value pair to that index + // If a tombstone was encountered earlier, move the key-value pair to that index if (firstTombstone !== -1) { this.buckets[firstTombstone] = this.buckets[index]; this.buckets[index] = this.TOMBSTONE; - return firstTombstone; // Return the moved bucket index + return firstTombstone; // Return the bucket index after the move } - return index; // Return bucket index + return index; // Return the bucket index } - // Record the first removal marker encountered + // Record the first tombstone encountered if ( firstTombstone === -1 && this.buckets[index] === this.TOMBSTONE ) { firstTombstone = index; } - // Calculate bucket index, wrap around to the head if past the tail + // Calculate the bucket index, wrapping to the beginning after the end index = (index + 1) % this.capacity; } - // If key does not exist, return the index for insertion + // If key is absent, return the index for insertion return firstTombstone === -1 ? index : firstTombstone; } - /* Query operation */ + /* Lookup operation */ get(key: number): string | null { - // Search for bucket index corresponding to key + // Find the bucket index for key const index = this.findBucket(key); - // If key-value pair is found, return corresponding val + // If the key-value pair is found, return its val if ( this.buckets[index] !== null && this.buckets[index] !== this.TOMBSTONE ) { return this.buckets[index]!.val; } - // If key-value pair does not exist, return null + // Return null if the key-value pair does not exist return null; } - /* Add operation */ + /* Insert operation */ put(key: number, val: string): void { - // When load factor exceeds threshold, perform expansion + // Grow the capacity when the load factor exceeds the threshold if (this.loadFactor() > this.loadThres) { this.extend(); } - // Search for bucket index corresponding to key + // Find the bucket index for key const index = this.findBucket(key); - // If key-value pair is found, overwrite val and return + // If the key-value pair is found, overwrite val and return if ( this.buckets[index] !== null && this.buckets[index] !== this.TOMBSTONE @@ -105,16 +105,16 @@ class HashMapOpenAddressing { this.buckets[index]!.val = val; return; } - // If key-value pair does not exist, add the key-value pair + // If the key-value pair is absent, insert it this.buckets[index] = new Pair(key, val); this.size++; } /* Remove operation */ remove(key: number): void { - // Search for bucket index corresponding to key + // Find the bucket index for key const index = this.findBucket(key); - // If key-value pair is found, overwrite it with removal marker + // If the key-value pair is found, replace it with a tombstone if ( this.buckets[index] !== null && this.buckets[index] !== this.TOMBSTONE @@ -124,15 +124,15 @@ class HashMapOpenAddressing { } } - /* Expand hash table */ + /* Grow the hash table capacity */ private extend(): void { - // Temporarily store the original hash table + // Keep a reference to the original hash table const bucketsTmp = this.buckets; - // Initialize expanded new hash table + // Initialize a new hash table with the expanded capacity this.capacity *= this.extendRatio; this.buckets = Array(this.capacity).fill(null); this.size = 0; - // Move key-value pairs from original hash table to new hash table + // Move the key-value pairs from the original hash table to the new one for (const pair of bucketsTmp) { if (pair !== null && pair !== this.TOMBSTONE) { this.put(pair.key, pair.val); @@ -140,7 +140,7 @@ class HashMapOpenAddressing { } } - /* Print hash table */ + /* Print the hash table */ print(): void { for (const pair of this.buckets) { if (pair === null) { @@ -154,29 +154,29 @@ class HashMapOpenAddressing { } } -/* Driver Code */ -// Initialize hash table +/* Driver code */ +// Initialize the hash table const hashmap = new HashMapOpenAddressing(); -// Add operation -// Add key-value pair (key, val) to the hash table -hashmap.put(12836, 'Xiao Ha'); -hashmap.put(15937, 'Xiao Luo'); -hashmap.put(16750, 'Xiao Suan'); -hashmap.put(13276, 'Xiao Fa'); -hashmap.put(10583, 'Xiao Ya'); -console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +// Insert operation +// Insert the key-value pair (key, val) into the hash table +hashmap.put(12836, '小哈'); +hashmap.put(15937, '小啰'); +hashmap.put(16750, '小算'); +hashmap.put(13276, '小法'); +hashmap.put(10583, '小鸭'); +console.log('\nHash table after insertion:\nKey -> Value'); hashmap.print(); -// Query operation -// Input key into hash table to get value val +// Lookup operation +// Look up key in the hash table to obtain val const name = hashmap.get(13276); -console.log('\nInput student ID 13276, query name ' + name); +console.log('\nLook up student ID 13276 to obtain the name ' + name); // Remove operation -// Remove key-value pair (key, val) from hash table +// Remove the key-value pair (key, val) from the hash table hashmap.remove(16750); -console.log('\nAfter removing 16750, hash table is\nKey -> Value'); +console.log('\nHash table after removing 16750:\nKey -> Value'); hashmap.print(); export {}; diff --git a/en/codes/typescript/chapter_hashing/simple_hash.ts b/en/codes/typescript/chapter_hashing/simple_hash.ts index ecc80a0be..efa83103b 100644 --- a/en/codes/typescript/chapter_hashing/simple_hash.ts +++ b/en/codes/typescript/chapter_hashing/simple_hash.ts @@ -34,7 +34,7 @@ function xorHash(key: string): number { return hash % MODULUS; } -/* Rotational hash */ +/* Rotating hash */ function rotHash(key: string): number { let hash = 0; const MODULUS = 1000000007; @@ -44,17 +44,17 @@ function rotHash(key: string): number { return hash; } -/* Driver Code */ -const key = 'Hello Algo'; +/* Driver code */ +const key = 'Hello 算法'; let hash = addHash(key); -console.log('Additive hash value is ' + hash); +console.log('Additive hash value: ' + hash); hash = mulHash(key); -console.log('Multiplicative hash value is ' + hash); +console.log('Multiplicative hash value: ' + hash); hash = xorHash(key); -console.log('XOR hash value is ' + hash); +console.log('XOR hash value: ' + hash); hash = rotHash(key); -console.log('Rotational hash value is ' + hash); +console.log('Rotating hash value: ' + hash); diff --git a/en/codes/typescript/chapter_heap/my_heap.ts b/en/codes/typescript/chapter_heap/my_heap.ts index 3947b5c81..04979af6d 100644 --- a/en/codes/typescript/chapter_heap/my_heap.ts +++ b/en/codes/typescript/chapter_heap/my_heap.ts @@ -9,27 +9,27 @@ import { printHeap } from '../modules/PrintUtil'; /* Max heap class */ class MaxHeap { private maxHeap: number[]; - /* Constructor, build empty heap or build heap from input list */ + /* Constructor: create an empty heap or build a heap from the input list */ constructor(nums?: number[]) { - // Add list elements to heap as is + // Place the list elements in the heap without changing their order this.maxHeap = nums === undefined ? [] : [...nums]; - // Heapify all nodes except leaf nodes + // Sift down from every non-leaf node for (let i = this.parent(this.size() - 1); i >= 0; i--) { this.siftDown(i); } } - /* Get index of left child node */ + /* Get the left child index */ private left(i: number): number { return 2 * i + 1; } - /* Get index of right child node */ + /* Get the right child index */ private right(i: number): number { return 2 * i + 2; } - /* Get index of parent node */ + /* Get the parent index */ private parent(i: number): number { return Math.floor((i - 1) / 2); // Floor division } @@ -41,115 +41,115 @@ class MaxHeap { this.maxHeap[j] = tmp; } - /* Get heap size */ + /* Get the heap size */ public size(): number { return this.maxHeap.length; } - /* Check if heap is empty */ + /* Check whether the heap is empty */ public isEmpty(): boolean { return this.size() === 0; } - /* Access top element */ + /* Access the top element of the heap */ public peek(): number { return this.maxHeap[0]; } - /* Element enters heap */ + /* Push an element onto the heap */ public push(val: number): void { - // Add node + // Add a node this.maxHeap.push(val); - // Heapify from bottom to top + // Sift up this.siftUp(this.size() - 1); } - /* Starting from node i, heapify from bottom to top */ + /* Sift up starting from node i */ private siftUp(i: number): void { while (true) { - // Get parent node of node i + // Get the parent of node i const p = this.parent(i); - // When "crossing root node" or "node needs no repair", end heapify + // Stop sifting when the root has been passed or the node needs no repair if (p < 0 || this.maxHeap[i] <= this.maxHeap[p]) break; - // Swap two nodes + // Swap the two nodes this.swap(i, p); - // Loop upward heapify + // Continue sifting up i = p; } } - /* Element exits heap */ + /* Pop an element from the heap */ public pop(): number { - // Handle empty case - if (this.isEmpty()) throw new RangeError('Heap is empty.'); - // Delete node + // Handle an empty heap + if (this.isEmpty()) throw new RangeError('Heap is empty'); + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) this.swap(0, this.size() - 1); - // Remove node + // Remove a node const val = this.maxHeap.pop(); - // Return top element + // Sift down this.siftDown(0); - // Return heap top element + // Return the top element of the heap return val; } - /* Starting from node i, heapify from top to bottom */ + /* Sift down starting from node i */ private siftDown(i: number): void { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma const l = this.left(i), r = this.right(i); let ma = i; if (l < this.size() && this.maxHeap[l] > this.maxHeap[ma]) ma = l; if (r < this.size() && this.maxHeap[r] > this.maxHeap[ma]) ma = r; - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma === i) break; - // Swap two nodes + // Swap the two nodes this.swap(i, ma); - // Loop downwards heapification + // Continue sifting down i = ma; } } - /* Driver Code */ + /* Print the heap (binary tree) */ public print(): void { printHeap(this.maxHeap); } - /* Extract elements from heap */ + /* Get the elements in the heap */ public getMaxHeap(): number[] { return this.maxHeap; } } -/* Driver Code */ +/* Driver code */ if (import.meta.url.endsWith(process.argv[1])) { - /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + /* Initialize a max heap */ const maxHeap = new MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); - console.log('\nAfter inputting list and building heap'); + console.log('\nAfter building a heap from the input list:'); maxHeap.print(); - /* Check if heap is empty */ + /* Get the top element of the heap */ let peek = maxHeap.peek(); - console.log(`\nHeap top element is ${peek}`); + console.log(`\nHeap top: ${peek}`); - /* Element enters heap */ + /* Push an element onto the heap */ const val = 7; maxHeap.push(val); - console.log(`\nAfter element ${val} pushes to heap`); + console.log(`\nAfter pushing ${val} into the heap:`); maxHeap.print(); - /* Time complexity is O(n), not O(nlogn) */ + /* Pop the top element from the heap */ peek = maxHeap.pop(); - console.log(`\nAfter heap top element ${peek} pops from heap`); + console.log(`\nAfter popping the heap top ${peek}:`); maxHeap.print(); - /* Get heap size */ + /* Get the heap size */ const size = maxHeap.size(); - console.log(`\nHeap size is ${size}`); + console.log(`\nNumber of elements in the heap: ${size}`); - /* Check if heap is empty */ + /* Check whether the heap is empty */ const isEmpty = maxHeap.isEmpty(); - console.log(`\nIs heap empty ${isEmpty}`); + console.log(`\nIs the heap empty? ${isEmpty}`); } export { MaxHeap }; diff --git a/en/codes/typescript/chapter_heap/top_k.ts b/en/codes/typescript/chapter_heap/top_k.ts index 0884a752d..0192c578e 100644 --- a/en/codes/typescript/chapter_heap/top_k.ts +++ b/en/codes/typescript/chapter_heap/top_k.ts @@ -6,52 +6,52 @@ import { MaxHeap } from './my_heap'; -/* Element enters heap */ +/* Push an element onto the heap */ function pushMinHeap(maxHeap: MaxHeap, val: number): void { - // Negate element + // Negate the value maxHeap.push(-val); } -/* Element exits heap */ +/* Pop an element from the heap */ function popMinHeap(maxHeap: MaxHeap): number { - // Negate element + // Negate the value return -maxHeap.pop(); } -/* Access top element */ +/* Access the top element of the heap */ function peekMinHeap(maxHeap: MaxHeap): number { - // Negate element + // Negate the value return -maxHeap.peek(); } -/* Extract elements from heap */ +/* Get the elements in the heap */ function getMinHeap(maxHeap: MaxHeap): number[] { - // Negate element + // Negate the value return maxHeap.getMaxHeap().map((num: number) => -num); } -/* Find the largest k elements in array based on heap */ +/* Use a heap to find the k largest elements in an array */ function topKHeap(nums: number[], k: number): number[] { - // Python's heapq module implements min heap by default - // Note: We negate all heap elements to simulate min heap using max heap + // Initialize a min heap + // Note: negate all values in the heap to simulate a min heap with a max heap const maxHeap = new MaxHeap([]); - // Enter the first k elements of array into heap + // Push the first k array elements onto the heap for (let i = 0; i < k; i++) { pushMinHeap(maxHeap, nums[i]); } - // Starting from the (k+1)th element, maintain heap length as k + // Starting with element k+1, keep the heap size at k for (let i = k; i < nums.length; i++) { - // If current element is greater than top element, top element exits heap, current element enters heap + // If the current element is larger than the heap top, pop the top element and push the current element if (nums[i] > peekMinHeap(maxHeap)) { popMinHeap(maxHeap); pushMinHeap(maxHeap, nums[i]); } } - // Return elements in heap + // Return the elements in the heap return getMinHeap(maxHeap); } -/* Driver Code */ +/* Driver code */ const nums = [1, 7, 6, 3, 2]; const k = 3; const res = topKHeap(nums, k); diff --git a/en/codes/typescript/chapter_searching/binary_search.ts b/en/codes/typescript/chapter_searching/binary_search.ts index 341cce37b..685df9d25 100644 --- a/en/codes/typescript/chapter_searching/binary_search.ts +++ b/en/codes/typescript/chapter_searching/binary_search.ts @@ -4,62 +4,62 @@ * Author: Daniel (better.sunjian@gmail.com) */ -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ function binarySearch(nums: number[], target: number): number { - // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements let i = 0, j = nums.length - 1; - // Loop, exit when the search interval is empty (empty when i > j) + // Loop until the search interval is empty (when i > j) while (i <= j) { // Calculate the midpoint index m const m = Math.floor(i + (j - i) / 2); if (nums[m] < target) { - // This means target is in the interval [m+1, j] + // In this case, target must be in [m+1, j] if it exists i = m + 1; } else if (nums[m] > target) { - // This means target is in the interval [i, m-1] + // In this case, target must be in [i, m-1] if it exists j = m - 1; } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } - return -1; // Target element not found, return -1 + return -1; // The target element was not found; return -1 } -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ function binarySearchLCRO(nums: number[], target: number): number { - // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last let i = 0, j = nums.length; - // Loop, exit when the search interval is empty (empty when i = j) + // Loop until the search interval is empty (when i = j) while (i < j) { // Calculate the midpoint index m const m = Math.floor(i + (j - i) / 2); if (nums[m] < target) { - // This means target is in the interval [m+1, j) + // In this case, target must be in [m+1, j) if it exists i = m + 1; } else if (nums[m] > target) { - // This means target is in the interval [i, m) + // In this case, target must be in [i, m) if it exists j = m; } else { - // Found the target element, return its index + // The target element was found; return its index return m; } } - return -1; // Target element not found, return -1 + return -1; // The target element was not found; return -1 } -/* Driver Code */ +/* Driver code */ const target = 6; const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; -/* Binary search (closed interval on both sides) */ +/* Binary search (closed interval) */ let index = binarySearch(nums, target); -console.info('Index of target element 6 is %d', index); +console.info('Index of target element 6 = %d', index); -/* Binary search (left-closed right-open interval) */ +/* Binary search (left-closed, right-open interval) */ index = binarySearchLCRO(nums, target); -console.info('Index of target element 6 is %d', index); +console.info('Index of target element 6 = %d', index); export {}; diff --git a/en/codes/typescript/chapter_searching/binary_search_edge.ts b/en/codes/typescript/chapter_searching/binary_search_edge.ts index dfdd270bc..e3fd24e28 100644 --- a/en/codes/typescript/chapter_searching/binary_search_edge.ts +++ b/en/codes/typescript/chapter_searching/binary_search_edge.ts @@ -5,42 +5,42 @@ */ import { binarySearchInsertion } from './binary_search_insertion'; -/* Binary search for the leftmost target */ +/* Find the leftmost occurrence of target using binary search */ function binarySearchLeftEdge(nums: Array, target: number): number { - // Equivalent to finding the insertion point of target + // This is equivalent to finding the insertion point of target const i = binarySearchInsertion(nums, target); - // Target not found, return -1 + // If target was not found, return -1 if (i === nums.length || nums[i] !== target) { return -1; } - // Found target, return index i + // Target was found; return index i return i; } -/* Binary search for the rightmost target */ +/* Find the rightmost occurrence of target using binary search */ function binarySearchRightEdge(nums: Array, target: number): number { - // Convert to finding the leftmost target + 1 + // Reduce the problem to finding the leftmost insertion point of target + 1 const i = binarySearchInsertion(nums, target + 1); - // j points to the rightmost target, i points to the first element greater than target + // For integer elements, i is the first index greater than target (or the array length); j is the candidate rightmost target index const j = i - 1; - // Target not found, return -1 + // If target was not found, return -1 if (j === -1 || nums[j] !== target) { return -1; } - // Found target, return index j + // Target was found; return index j return j; } -/* Driver Code */ -// Array with duplicate elements +/* Driver code */ +// Array containing duplicate elements let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; console.log('\nArray nums = ' + nums); -// Binary search left and right boundaries +// Find the left and right boundaries using binary search for (const target of [6, 7]) { let index = binarySearchLeftEdge(nums, target); - console.log('Leftmost element ' + target + ' has index ' + index); + console.log('Leftmost occurrence of ' + target + ' has index ' + index); index = binarySearchRightEdge(nums, target); - console.log('Rightmost element ' + target + ' has index ' + index); + console.log('Rightmost occurrence of ' + target + ' has index ' + index); } export {}; diff --git a/en/codes/typescript/chapter_searching/binary_search_insertion.ts b/en/codes/typescript/chapter_searching/binary_search_insertion.ts index 14a416ff9..54ce12f9d 100644 --- a/en/codes/typescript/chapter_searching/binary_search_insertion.ts +++ b/en/codes/typescript/chapter_searching/binary_search_insertion.ts @@ -4,62 +4,62 @@ * Author: Gaofer Chou (gaofer-chou@qq.com) */ -/* Binary search for insertion point (no duplicate elements) */ +/* Find an insertion point using binary search (no duplicates) */ function binarySearchInsertionSimple( nums: Array, target: number ): number { let i = 0, - j = nums.length - 1; // Initialize closed interval [0, n-1] + j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, using Math.floor() to round down if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - return m; // Found target, return insertion point m + return m; // Target was found; return insertion point m } } - // Target not found, return insertion point i + // Target was not found; return insertion point i return i; } -/* Binary search for insertion point (with duplicate elements) */ +/* Find an insertion point using binary search (with duplicates) */ function binarySearchInsertion(nums: Array, target: number): number { let i = 0, - j = nums.length - 1; // Initialize closed interval [0, n-1] + j = nums.length - 1; // Initialize the closed interval [0, n-1] while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, using Math.floor() to round down if (nums[m] < target) { - i = m + 1; // target is in the interval [m+1, j] + i = m + 1; // Target must be in [m+1, j] if it exists } else if (nums[m] > target) { - j = m - 1; // target is in the interval [i, m-1] + j = m - 1; // Target must be in [i, m-1] if it exists } else { - j = m - 1; // The rightmost element less than target is in the interval [i, m-1] + j = m - 1; // Continue in [i, m-1] to find the rightmost element smaller than target } } // Return insertion point i return i; } -/* Driver Code */ -// Array without duplicate elements +/* Driver code */ +// Array with no duplicate elements let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; console.log('\nArray nums = ' + nums); -// Binary search for insertion point +// Find an insertion point using binary search for (const target of [6, 9]) { const index = binarySearchInsertionSimple(nums, target); - console.log('Element ' + target + "'s insertion point index is " + index); + console.log('Element ' + target + ' has insertion index ' + index); } -// Array with duplicate elements +// Array containing duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; console.log('\nArray nums = ' + nums); -// Binary search for insertion point +// Find an insertion point using binary search for (const target of [2, 6, 20]) { const index = binarySearchInsertion(nums, target); - console.log('Element ' + target + "'s insertion point index is " + index); + console.log('Element ' + target + ' has insertion index ' + index); } export { binarySearchInsertion }; diff --git a/en/codes/typescript/chapter_searching/hashing_search.ts b/en/codes/typescript/chapter_searching/hashing_search.ts index 51e4f8add..780675e93 100644 --- a/en/codes/typescript/chapter_searching/hashing_search.ts +++ b/en/codes/typescript/chapter_searching/hashing_search.ts @@ -6,45 +6,45 @@ import { ListNode, arrToLinkedList } from '../modules/ListNode'; -/* Hash search (array) */ +/* Hash-based search (array) */ function hashingSearchArray(map: Map, target: number): number { - // Hash table's key: target element, value: index - // If this key does not exist in the hash table, return -1 + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key return map.has(target) ? (map.get(target) as number) : -1; } -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ function hashingSearchLinkedList( map: Map, target: number ): ListNode | null { - // Hash table key: target node value, value: node object - // If key is not in hash table, return null + // Hash table key: target node value; value: node object + // Return null if this key is absent from the hash table return map.has(target) ? (map.get(target) as ListNode) : null; } -/* Driver Code */ +/* Driver code */ const target = 3; -/* Hash search (array) */ +/* Hash-based search (array) */ const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; -// Initialize hash table +// Initialize the hash table const map = new Map(); for (let i = 0; i < nums.length; i++) { - map.set(nums[i], i); // key: element, value: index + map.set(nums[i], i); // key: element; value: index } const index = hashingSearchArray(map, target); console.log('Index of target element 3 = ' + index); -/* Hash search (linked list) */ +/* Hash-based search (linked list) */ let head = arrToLinkedList(nums); -// Initialize hash table +// Initialize the hash table const map1 = new Map(); while (head != null) { - map1.set(head.val, head); // key: node value, value: node + map1.set(head.val, head); // key: node value; value: node head = head.next; } const node = hashingSearchLinkedList(map1, target); -console.log('Node object with target value 3 is', node); +console.log('The node object corresponding to target value 3 is', node); export {}; diff --git a/en/codes/typescript/chapter_searching/linear_search.ts b/en/codes/typescript/chapter_searching/linear_search.ts index fd068d932..ebaf81bda 100644 --- a/en/codes/typescript/chapter_searching/linear_search.ts +++ b/en/codes/typescript/chapter_searching/linear_search.ts @@ -6,47 +6,47 @@ import { ListNode, arrToLinkedList } from '../modules/ListNode'; -/* Linear search (array) */ +/* Linear search (array)*/ function linearSearchArray(nums: number[], target: number): number { - // Traverse array + // Traverse the array for (let i = 0; i < nums.length; i++) { - // Found the target element, return its index + // The target element was found; return its index if (nums[i] === target) { return i; } } - // Target element not found, return -1 + // The target element was not found; return -1 return -1; } -/* Linear search (linked list) */ +/* Linear search (linked list)*/ function linearSearchLinkedList( head: ListNode | null, target: number ): ListNode | null { // Traverse the linked list while (head) { - // Found the target node, return it + // The target node was found; return it if (head.val === target) { return head; } head = head.next; } - // Target node not found, return null + // Target node not found; return null return null; } -/* Driver Code */ +/* Driver code */ const target = 3; -/* Perform linear search in array */ +/* Perform linear search on the array */ const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; const index = linearSearchArray(nums, target); console.log('Index of target element 3 =', index); -/* Perform linear search in linked list */ +/* Perform linear search on the linked list */ const head = arrToLinkedList(nums); const node = linearSearchLinkedList(head, target); -console.log('Node object with target value 3 is', node); +console.log('The node object corresponding to target value 3 is', node); export {}; diff --git a/en/codes/typescript/chapter_searching/two_sum.ts b/en/codes/typescript/chapter_searching/two_sum.ts index de2f05412..a2403cb0b 100644 --- a/en/codes/typescript/chapter_searching/two_sum.ts +++ b/en/codes/typescript/chapter_searching/two_sum.ts @@ -4,10 +4,10 @@ * Author: gyt95 (gytkwan@gmail.com) */ -/* Method 1: Brute force enumeration */ +/* Method 1: brute-force enumeration */ function twoSumBruteForce(nums: number[], target: number): number[] { const n = nums.length; - // Two nested loops, time complexity is O(n^2) + // Two nested loops give a time complexity of O(n^2) for (let i = 0; i < n; i++) { for (let j = i + 1; j < n; j++) { if (nums[i] + nums[j] === target) { @@ -18,11 +18,11 @@ function twoSumBruteForce(nums: number[], target: number): number[] { return []; } -/* Method 2: Auxiliary hash table */ +/* Method 2: auxiliary hash table */ function twoSumHashTable(nums: number[], target: number): number[] { - // Auxiliary hash table, space complexity is O(n) + // The auxiliary hash table uses O(n) space let m: Map = new Map(); - // Single loop, time complexity is O(n) + // A single loop gives a time complexity of O(n) for (let i = 0; i < nums.length; i++) { let index = m.get(target - nums[i]); if (index !== undefined) { @@ -34,16 +34,16 @@ function twoSumHashTable(nums: number[], target: number): number[] { return []; } -/* Driver Code */ +/* Driver code */ // Method 1 const nums = [2, 7, 11, 15], target = 13; let res = twoSumBruteForce(nums, target); -console.log('Method 1 res = ', res); +console.log('Method 1: res = ', res); // Method 2 res = twoSumHashTable(nums, target); -console.log('Method 2 res = ', res); +console.log('Method 2: res = ', res); export {}; diff --git a/en/codes/typescript/chapter_sorting/bubble_sort.ts b/en/codes/typescript/chapter_sorting/bubble_sort.ts index 0c8d9cdef..014c781ec 100644 --- a/en/codes/typescript/chapter_sorting/bubble_sort.ts +++ b/en/codes/typescript/chapter_sorting/bubble_sort.ts @@ -6,9 +6,9 @@ /* Bubble sort */ function bubbleSort(nums: number[]): void { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (let i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] @@ -20,32 +20,32 @@ function bubbleSort(nums: number[]): void { } } -/* Bubble sort (flag optimization) */ +/* Bubble sort (flag optimization)*/ function bubbleSortWithFlag(nums: number[]): void { - // Outer loop: unsorted range is [0, i] + // Outer loop: the unsorted range is [0, i] for (let i = nums.length - 1; i > 0; i--) { - let flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + let flag = false; // Initialize the flag + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] let tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - flag = true; // Record element swap + flag = true; // Record that a swap occurred } } - if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + if (!flag) break; // Exit immediately if this pass made no swaps } } -/* Driver Code */ +/* Driver code */ const nums = [4, 1, 3, 1, 5, 2]; bubbleSort(nums); -console.log('After bubble sort, nums =', nums); +console.log('After bubble sort, array =', nums); const nums1 = [4, 1, 3, 1, 5, 2]; bubbleSortWithFlag(nums1); -console.log('After bubble sort, nums =', nums1); +console.log('After bubble sort, array =', nums1); export {}; diff --git a/en/codes/typescript/chapter_sorting/bucket_sort.ts b/en/codes/typescript/chapter_sorting/bucket_sort.ts index 52fc54857..db213eb3c 100644 --- a/en/codes/typescript/chapter_sorting/bucket_sort.ts +++ b/en/codes/typescript/chapter_sorting/bucket_sort.ts @@ -6,25 +6,25 @@ /* Bucket sort */ function bucketSort(nums: number[]): void { - // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + // Set k = n/2 and create ceil(k) buckets, aiming for about 2 elements per bucket const k = nums.length / 2; const buckets: number[][] = []; for (let i = 0; i < k; i++) { buckets.push([]); } - // 1. Distribute array elements into various buckets + // 1. Distribute the array elements among the buckets for (const num of nums) { - // Input data range is [0, 1), use num * k to map to index range [0, k-1] + // The input range is [0, 1); floor(num * k) maps each value to an index in [0, ceil(k)-1] const i = Math.floor(num * k); // Add num to bucket i buckets[i].push(num); } // 2. Sort each bucket for (const bucket of buckets) { - // Use built-in sorting function, can also replace with other sorting algorithms + // Use the built-in sorting function, or replace it with another sorting algorithm bucket.sort((a, b) => a - b); } - // 3. Traverse buckets to merge results + // 3. Traverse the buckets to combine the results let i = 0; for (const bucket of buckets) { for (const num of bucket) { @@ -33,7 +33,7 @@ function bucketSort(nums: number[]): void { } } -/* Driver Code */ +/* Driver code */ const nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; bucketSort(nums); console.log('After bucket sort, nums =', nums); diff --git a/en/codes/typescript/chapter_sorting/counting_sort.ts b/en/codes/typescript/chapter_sorting/counting_sort.ts index da30e112f..32de97252 100644 --- a/en/codes/typescript/chapter_sorting/counting_sort.ts +++ b/en/codes/typescript/chapter_sorting/counting_sort.ts @@ -5,17 +5,17 @@ */ /* Counting sort */ -// Simple implementation, cannot be used for sorting objects +// Simple implementation; cannot be used to sort objects function countingSortNaive(nums: number[]): void { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m: number = Math.max(...nums); - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num const counter: number[] = new Array(m + 1).fill(0); for (const num of nums) { counter[num]++; } - // 3. Traverse counter, filling each element back into the original array nums + // 3. Traverse counter and fill the original array nums with the elements let i = 0; for (let num = 0; num < m + 1; num++) { for (let j = 0; j < counter[num]; j++, i++) { @@ -25,37 +25,37 @@ function countingSortNaive(nums: number[]): void { } /* Counting sort */ -// Complete implementation, can sort objects and is a stable sort +// Full stable implementation; this approach can be adapted to sort objects function countingSort(nums: number[]): void { - // 1. Count the maximum element m in the array + // 1. Find the maximum array element m let m: number = Math.max(...nums); - // 2. Count the occurrence of each number - // counter[num] represents the occurrence of num + // 2. Count the occurrences of each number + // counter[num] is the number of occurrences of num const counter: number[] = new Array(m + 1).fill(0); for (const num of nums) { counter[num]++; } - // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" - // counter[num]-1 is the last index where num appears in res + // 3. Compute prefix sums of counter to convert occurrence counts into end positions + // That is, counter[num]-1 is the index of the last occurrence of num in res for (let i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. Traverse nums in reverse order, placing each element into the result array res - // Initialize the array res to record results + // 4. Traverse nums in reverse and place each element in the result array res + // Initialize array res to store the result const n = nums.length; const res: number[] = new Array(n); for (let i = n - 1; i >= 0; i--) { const num = nums[i]; res[counter[num] - 1] = num; // Place num at the corresponding index - counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + counter[num]--; // Decrement the prefix sum by 1 to determine where to place the next occurrence of num } - // Use result array res to overwrite the original array nums + // Overwrite the original array nums with the result array res for (let i = 0; i < n; i++) { nums[i] = res[i]; } } -/* Driver Code */ +/* Driver code */ const nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; countingSortNaive(nums); console.log('After counting sort (cannot sort objects), nums =', nums); diff --git a/en/codes/typescript/chapter_sorting/heap_sort.ts b/en/codes/typescript/chapter_sorting/heap_sort.ts index 9cff66aa9..7870ab009 100644 --- a/en/codes/typescript/chapter_sorting/heap_sort.ts +++ b/en/codes/typescript/chapter_sorting/heap_sort.ts @@ -4,10 +4,10 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Heap length is n, start heapifying node i, from top to bottom */ +/* Sift down from node i in a heap of size n */ function siftDown(nums: number[], n: number, i: number): void { while (true) { - // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + // Find the node with the largest value among i, l, and r, and store its index in ma let l = 2 * i + 1; let r = 2 * i + 2; let ma = i; @@ -17,33 +17,33 @@ function siftDown(nums: number[], n: number, i: number): void { if (r < n && nums[r] > nums[ma]) { ma = r; } - // Swap two nodes + // Stop if node i is largest or both child indices l and r are out of bounds if (ma === i) { break; } - // Swap two nodes + // Swap the two nodes [nums[i], nums[ma]] = [nums[ma], nums[i]]; - // Loop downwards heapification + // Continue sifting down i = ma; } } /* Heap sort */ function heapSort(nums: number[]): void { - // Build heap operation: heapify all nodes except leaves + // Build the heap by sifting down from every non-leaf node for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) { siftDown(nums, nums.length, i); } - // Extract the largest element from the heap and repeat for n-1 rounds + // Extract the largest element from the heap, repeating for n-1 rounds for (let i = nums.length - 1; i > 0; i--) { - // Delete node + // Swap the root with the rightmost leaf on the last level (the first and last elements of the current heap) [nums[0], nums[i]] = [nums[i], nums[0]]; - // Start heapifying the root node, from top to bottom + // Sift down starting at the root siftDown(nums, i, 0); } } -/* Driver Code */ +/* Driver code */ const nums: number[] = [4, 1, 3, 1, 5, 2]; heapSort(nums); console.log('After heap sort, nums =', nums); diff --git a/en/codes/typescript/chapter_sorting/insertion_sort.ts b/en/codes/typescript/chapter_sorting/insertion_sort.ts index 8a0ea037c..461266385 100644 --- a/en/codes/typescript/chapter_sorting/insertion_sort.ts +++ b/en/codes/typescript/chapter_sorting/insertion_sort.ts @@ -6,20 +6,20 @@ /* Insertion sort */ function insertionSort(nums: number[]): void { - // Outer loop: sorted interval is [0, i-1] + // Outer loop: the sorted range is [0, i-1] for (let i = 1; i < nums.length; i++) { const base = nums[i]; let j = i - 1; - // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + // Inner loop: insert base at the correct position in the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + nums[j + 1] = nums[j]; // Shift nums[j] one position to the right j--; } - nums[j + 1] = base; // Assign base to the correct position + nums[j + 1] = base; // Place base at the correct position } } -/* Driver Code */ +/* Driver code */ const nums = [4, 1, 3, 1, 5, 2]; insertionSort(nums); console.log('After insertion sort, nums =', nums); diff --git a/en/codes/typescript/chapter_sorting/merge_sort.ts b/en/codes/typescript/chapter_sorting/merge_sort.ts index ded2e4c8f..69fc73582 100644 --- a/en/codes/typescript/chapter_sorting/merge_sort.ts +++ b/en/codes/typescript/chapter_sorting/merge_sort.ts @@ -4,16 +4,16 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Merge left subarray and right subarray */ +/* Merge the left and right subarrays */ function merge(nums: number[], left: number, mid: number, right: number): void { - // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] - // Create a temporary array tmp to store the merged results + // The left subarray spans [left, mid], and the right subarray spans [mid+1, right] + // Create a temporary array tmp to store the merged result const tmp = new Array(right - left + 1); - // Initialize the start indices of the left and right subarrays + // Initialize the starting indices of the left and right subarrays let i = left, j = mid + 1, k = 0; - // While both subarrays still have elements, compare and copy the smaller element into the temporary array + // While both subarrays have elements remaining, compare them and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { tmp[k++] = nums[i++]; @@ -28,7 +28,7 @@ function merge(nums: number[], left: number, mid: number, right: number): void { while (j <= right) { tmp[k++] = nums[j++]; } - // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + // Copy the elements of temporary array tmp back into the corresponding range of the original array nums for (k = 0; k < tmp.length; k++) { nums[left + k] = tmp[k]; } @@ -36,17 +36,17 @@ function merge(nums: number[], left: number, mid: number, right: number): void { /* Merge sort */ function mergeSort(nums: number[], left: number, right: number): void { - // Termination condition - if (left >= right) return; // Terminate recursion when subarray length is 1 - // Divide and conquer stage - let mid = Math.floor(left + (right - left) / 2); // Calculate midpoint - mergeSort(nums, left, mid); // Recursively process the left subarray - mergeSort(nums, mid + 1, right); // Recursively process the right subarray - // Merge stage + // Base case + if (left >= right) return; // Stop recursing when the subarray has zero or one element + // Divide phase + let mid = Math.floor(left + (right - left) / 2); // Calculate the midpoint + mergeSort(nums, left, mid); // Recurse on the left subarray + mergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase merge(nums, left, mid, right); } -/* Driver Code */ +/* Driver code */ const nums = [7, 3, 2, 6, 0, 1, 5, 4]; mergeSort(nums, 0, nums.length - 1); console.log('After merge sort, nums =', nums); diff --git a/en/codes/typescript/chapter_sorting/quick_sort.ts b/en/codes/typescript/chapter_sorting/quick_sort.ts index 354965b11..10d107120 100644 --- a/en/codes/typescript/chapter_sorting/quick_sort.ts +++ b/en/codes/typescript/chapter_sorting/quick_sort.ts @@ -6,41 +6,41 @@ /* Quick sort class */ class QuickSort { - /* Swap elements */ + /* Swap the elements */ swap(nums: number[], i: number, j: number): void { let tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ partition(nums: number[], left: number, right: number): number { // Use nums[left] as the pivot let i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) { - j -= 1; // Search from right to left for the first element smaller than the pivot + j -= 1; // Scan from right to left for the first element smaller than the pivot } while (i < j && nums[i] <= nums[left]) { - i += 1; // Search from left to right for the first element greater than the pivot + i += 1; // Scan from left to right for the first element larger than the pivot } - // Swap elements + // Swap the elements this.swap(nums, i, j); // Swap these two elements } - this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + this.swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ quickSort(nums: number[], left: number, right: number): void { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has zero or one element if (left >= right) { return; } - // Sentinel partition + // Two-pointer partitioning const pivot = this.partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays this.quickSort(nums, left, pivot - 1); this.quickSort(nums, pivot + 1, right); } @@ -48,7 +48,7 @@ class QuickSort { /* Quick sort class (median pivot optimization) */ class QuickSortMedian { - /* Swap elements */ + /* Swap the elements */ swap(nums: number[], i: number, j: number): void { let tmp = nums[i]; nums[i] = nums[j]; @@ -65,14 +65,14 @@ class QuickSortMedian { let l = nums[left], m = nums[mid], r = nums[right]; - // m is between l and r + // m lies between l and r if ((l <= m && m <= r) || (r <= m && m <= l)) return mid; - // l is between m and r + // l lies between m and r if ((m <= l && l <= r) || (r <= l && l <= m)) return left; return right; } - /* Sentinel partition (median of three) */ + /* Two-pointer partitioning (median of three) */ partition(nums: number[], left: number, right: number): number { // Select the median of three candidate elements let med = this.medianThree( @@ -81,33 +81,33 @@ class QuickSortMedian { Math.floor((left + right) / 2), right ); - // Swap the median to the array's leftmost position + // Swap the median into the leftmost position of the current subarray this.swap(nums, left, med); // Use nums[left] as the pivot let i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) { - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot } while (i < j && nums[i] <= nums[left]) { - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot } this.swap(nums, i, j); // Swap these two elements } - this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + this.swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort */ quickSort(nums: number[], left: number, right: number): void { - // Terminate recursion when subarray length is 1 + // Stop recursing when the subarray has zero or one element if (left >= right) { return; } - // Sentinel partition + // Two-pointer partitioning const pivot = this.partition(nums, left, right); - // Recursively process the left subarray and right subarray + // Recurse on the left and right subarrays this.quickSort(nums, left, pivot - 1); this.quickSort(nums, pivot + 1, right); } @@ -115,66 +115,66 @@ class QuickSortMedian { /* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { - /* Swap elements */ + /* Swap the elements */ swap(nums: number[], i: number, j: number): void { let tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* Sentinel partition */ + /* Two-pointer partitioning */ partition(nums: number[], left: number, right: number): number { // Use nums[left] as the pivot let i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) { - j--; // Search from right to left for the first element smaller than the pivot + j--; // Scan from right to left for the first element smaller than the pivot } while (i < j && nums[i] <= nums[left]) { - i++; // Search from left to right for the first element greater than the pivot + i++; // Scan from left to right for the first element larger than the pivot } this.swap(nums, i, j); // Swap these two elements } - this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + this.swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index } /* Quick sort (recursion depth optimization) */ quickSort(nums: number[], left: number, right: number): void { - // Terminate when subarray length is 1 + // Stop when the subarray has zero or one element while (left < right) { - // Sentinel partition operation + // Perform two-pointer partitioning let pivot = this.partition(nums, left, right); - // Perform quick sort on the shorter of the two subarrays + // Apply quick sort to the shorter of the two subarrays if (pivot - left < right - pivot) { this.quickSort(nums, left, pivot - 1); // Recursively sort the left subarray - left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] } else { this.quickSort(nums, pivot + 1, right); // Recursively sort the right subarray - right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] } } } } -/* Driver Code */ -/* Quick sort */ +/* Driver code */ +/* Quick sort */ const nums = [2, 4, 1, 0, 3, 5]; const quickSort = new QuickSort(); quickSort.quickSort(nums, 0, nums.length - 1); console.log('After quick sort, nums =', nums); -/* Quick sort (recursion depth optimization) */ +/* Quick sort (median pivot optimization) */ const nums1 = [2, 4, 1, 0, 3, 5]; const quickSortMedian = new QuickSortMedian(); quickSortMedian.quickSort(nums1, 0, nums1.length - 1); -console.log('After quick sort (median pivot optimization), nums =', nums1); +console.log('After quick sort (median pivot optimization), nums1 =', nums1); /* Quick sort (recursion depth optimization) */ const nums2 = [2, 4, 1, 0, 3, 5]; const quickSortTailCall = new QuickSortTailCall(); quickSortTailCall.quickSort(nums2, 0, nums2.length - 1); -console.log('After quick sort (recursion depth optimization), nums =', nums2); +console.log('After quick sort (recursion depth optimization), nums2 =', nums2); export {}; diff --git a/en/codes/typescript/chapter_sorting/radix_sort.ts b/en/codes/typescript/chapter_sorting/radix_sort.ts index df0137ef5..dbfa8506a 100644 --- a/en/codes/typescript/chapter_sorting/radix_sort.ts +++ b/en/codes/typescript/chapter_sorting/radix_sort.ts @@ -4,35 +4,35 @@ * Author: Justin (xiefahit@gmail.com) */ -/* Get the k-th digit of element num, where exp = 10^(k-1) */ +/* Get the k-th digit of num, where exp = 10^(k-1) */ function digit(num: number, exp: number): number { - // Passing exp instead of k can avoid repeated expensive exponentiation here + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here return Math.floor(num / exp) % 10; } -/* Counting sort (based on nums k-th digit) */ +/* Counting sort (sort nums by the k-th digit) */ function countingSortDigit(nums: number[], exp: number): void { - // Decimal digit range is 0~9, therefore need a bucket array of length 10 + // Decimal digits range from 0 to 9, so the bucket array needs length 10 const counter = new Array(10).fill(0); const n = nums.length; - // Count the occurrence of digits 0~9 + // Count the occurrences of each digit from 0 to 9 for (let i = 0; i < n; i++) { - const d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d - counter[d]++; // Count the occurrence of digit d + const d = digit(nums[i], exp); // Get the k-th digit of nums[i] and store it in d + counter[d]++; // Count the occurrences of digit d } - // Calculate prefix sum, converting "occurrence count" into "array index" + // Compute prefix sums to convert occurrence counts into array positions for (let i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // Traverse in reverse, based on bucket statistics, place each element into res + // Traverse in reverse and place each element into res according to the bucket counts const res = new Array(n).fill(0); for (let i = n - 1; i >= 0; i--) { const d = digit(nums[i], exp); - const j = counter[d] - 1; // Get the index j for d in the array + const j = counter[d] - 1; // Get the destination index j for digit d in the array res[j] = nums[i]; // Place the current element at index j - counter[d]--; // Decrease the count of d by 1 + counter[d]--; // Decrease the count for d by 1 } - // Use result to overwrite the original array nums + // Overwrite the original array nums with the result for (let i = 0; i < n; i++) { nums[i] = res[i]; } @@ -40,19 +40,19 @@ function countingSortDigit(nums: number[], exp: number): void { /* Radix sort */ function radixSort(nums: number[]): void { - // Get the maximum element of the array, used to determine the maximum number of digits + // Get the largest array element to determine the maximum number of digits let m: number = Math.max(... nums); - // Traverse from the lowest to the highest digit + // Process digits from least significant to most significant for (let exp = 1; exp <= m; exp *= 10) { - // Perform counting sort on the k-th digit of array elements + // Apply counting sort to the k-th digit of the array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // i.e., exp = 10^(k-1) + // That is, exp = 10^(k-1) countingSortDigit(nums, exp); } } -/* Driver Code */ +/* Driver code */ const nums = [ 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996, diff --git a/en/codes/typescript/chapter_sorting/selection_sort.ts b/en/codes/typescript/chapter_sorting/selection_sort.ts index 8d1ecf41d..533aceae7 100644 --- a/en/codes/typescript/chapter_sorting/selection_sort.ts +++ b/en/codes/typescript/chapter_sorting/selection_sort.ts @@ -7,21 +7,21 @@ /* Selection sort */ function selectionSort(nums: number[]): void { let n = nums.length; - // Outer loop: unsorted interval is [i, n-1] + // Outer loop: the unsorted range is [i, n-1] for (let i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted interval + // Inner loop: find the smallest element in the unsorted range let k = i; for (let j = i + 1; j < n; j++) { if (nums[j] < nums[k]) { k = j; // Record the index of the smallest element } } - // Swap the smallest element with the first element of the unsorted interval + // Swap the smallest element with the first element of the unsorted range [nums[i], nums[k]] = [nums[k], nums[i]]; } } -/* Driver Code */ +/* Driver code */ const nums: number[] = [4, 1, 3, 1, 5, 2]; selectionSort(nums); console.log('After selection sort, nums =', nums); diff --git a/en/codes/typescript/chapter_stack_and_queue/array_deque.ts b/en/codes/typescript/chapter_stack_and_queue/array_deque.ts index ae3331ee4..ad6928c70 100644 --- a/en/codes/typescript/chapter_stack_and_queue/array_deque.ts +++ b/en/codes/typescript/chapter_stack_and_queue/array_deque.ts @@ -4,11 +4,11 @@ * Author: Zhuo Qinyue (1403450829@qq.com) */ -/* Double-ended queue based on circular array implementation */ +/* Deque implemented with a circular array */ class ArrayDeque { - private nums: number[]; // Array for storing double-ended queue elements - private front: number; // Front pointer, points to the front of the queue element - private queSize: number; // Double-ended queue length + private nums: number[]; // Array for storing deque elements + private front: number; // Front pointer, pointing to the first element + private queSize: number; // Deque size /* Constructor */ constructor(capacity: number) { @@ -17,89 +17,89 @@ class ArrayDeque { this.queSize = 0; } - /* Get the capacity of the double-ended queue */ + /* Get the deque capacity */ capacity(): number { return this.nums.length; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ size(): number { return this.queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ isEmpty(): boolean { return this.queSize === 0; } - /* Calculate circular array index */ + /* Calculate an index in the circular array */ index(i: number): number { - // Use modulo operation to wrap the array head and tail together - // When i passes the tail of the array, return to the head - // When i passes the head of the array, return to the tail + // Use modulo to connect the end of the array to its beginning + // When i passes the end of the array, wrap around to the beginning + // When i passes the beginning of the array, wrap around to the end return (i + this.capacity()) % this.capacity(); } - /* Front of the queue enqueue */ + /* Enqueue at the front */ pushFirst(num: number): void { if (this.queSize === this.capacity()) { - console.log('Double-ended queue is full'); + console.log('Deque is full'); return; } - // Use modulo operation to wrap front around to the tail after passing the head of the array - // Add num to the front of the queue + // Move the front pointer one position to the left + // Use modulo to wrap front to the end when it passes the beginning of the array this.front = this.index(this.front - 1); - // Add num to front of queue + // Add num to the front this.nums[this.front] = num; this.queSize++; } - /* Rear of the queue enqueue */ + /* Enqueue at the rear */ pushLast(num: number): void { if (this.queSize === this.capacity()) { - console.log('Double-ended queue is full'); + console.log('Deque is full'); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Calculate the rear pointer, which points one position past the last element const rear: number = this.index(this.front + this.queSize); - // Front pointer moves one position backward + // Add num to the rear this.nums[rear] = num; this.queSize++; } - /* Rear of the queue dequeue */ + /* Dequeue from the front */ popFirst(): number { const num: number = this.peekFirst(); - // Move front pointer backward by one position + // Advance the front pointer by one position this.front = this.index(this.front + 1); this.queSize--; return num; } - /* Access rear of the queue element */ + /* Dequeue from the rear */ popLast(): number { const num: number = this.peekLast(); this.queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ peekFirst(): number { - if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + if (this.isEmpty()) throw new Error('Deque is empty'); return this.nums[this.front]; } - /* Driver Code */ + /* Access the rear element */ peekLast(): number { - if (this.isEmpty()) throw new Error('The Deque Is Empty.'); - // Initialize double-ended queue + if (this.isEmpty()) throw new Error('Deque is empty'); + // Calculate the index of the last element const last = this.index(this.front + this.queSize - 1); return this.nums[last]; } - /* Return array for printing */ + /* Return an array for printing */ toArray(): number[] { - // Elements enqueue + // Convert only the list elements within the occupied range const res: number[] = []; for (let i = 0, j = this.front; i < this.queSize; i++, j++) { res[i] = this.nums[this.index(j)]; @@ -108,8 +108,8 @@ class ArrayDeque { } } -/* Driver Code */ -/* Get the length of the double-ended queue */ +/* Driver code */ +/* Initialize the deque */ const capacity = 5; const deque: ArrayDeque = new ArrayDeque(capacity); deque.pushLast(3); @@ -117,42 +117,42 @@ deque.pushLast(2); deque.pushLast(5); console.log('Deque deque = [' + deque.toArray() + ']'); -/* Update element */ +/* Access an element */ const peekFirst = deque.peekFirst(); console.log('Front element peekFirst = ' + peekFirst); const peekLast = deque.peekLast(); console.log('Rear element peekLast = ' + peekLast); -/* Elements enqueue */ +/* Enqueue elements */ deque.pushLast(4); -console.log('After element 4 enqueues at rear, deque = [' + deque.toArray() + ']'); +console.log('After enqueuing 4 at the rear, deque = [' + deque.toArray() + ']'); deque.pushFirst(1); -console.log('After element 1 enqueues at front, deque = [' + deque.toArray() + ']'); +console.log('After enqueuing 1 at the front, deque = [' + deque.toArray() + ']'); -/* Element dequeue */ +/* Dequeue an element */ const popLast = deque.popLast(); console.log( - 'Rear dequeue element = ' + + 'Element dequeued from the rear = ' + popLast + - ', after rear dequeue deque = [' + + '; after removing from the rear, deque = [' + deque.toArray() + ']' ); const popFirst = deque.popFirst(); console.log( - 'Front dequeue element = ' + + 'Element dequeued from the front = ' + popFirst + - ', after front dequeue deque = [' + + '; after removing from the front, deque = [' + deque.toArray() + ']' ); -/* Get the length of the double-ended queue */ +/* Get the deque size */ const size = deque.size(); -console.log('Double-ended queue length size = ' + size); +console.log('Deque size = ' + size); -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ const isEmpty = deque.isEmpty(); -console.log('Double-ended queue is empty = ' + isEmpty); +console.log('Is the deque empty = ' + isEmpty); export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/array_queue.ts b/en/codes/typescript/chapter_stack_and_queue/array_queue.ts index 5b2b25d01..373a5f2a5 100644 --- a/en/codes/typescript/chapter_stack_and_queue/array_queue.ts +++ b/en/codes/typescript/chapter_stack_and_queue/array_queue.ts @@ -4,28 +4,28 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Queue based on circular array implementation */ +/* Queue implemented with a circular array */ class ArrayQueue { - private nums: number[]; // Array for storing queue elements - private front: number; // Front pointer, points to the front of the queue element - private queSize: number; // Queue length + private nums: number[]; // Array that stores the queue elements + private front: number; // Front pointer, pointing to the first element + private queSize: number; // Queue size constructor(capacity: number) { this.nums = new Array(capacity); this.front = this.queSize = 0; } - /* Get the capacity of the queue */ + /* Get the queue capacity */ get capacity(): number { return this.nums.length; } - /* Get the length of the queue */ + /* Get the queue size */ get size(): number { return this.queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ isEmpty(): boolean { return this.queSize === 0; } @@ -36,10 +36,10 @@ class ArrayQueue { console.log('Queue is full'); return; } - // Use modulo operation to wrap rear around to the head after passing the tail of the array - // Add num to the rear of the queue + // Calculate the rear pointer, which points one position past the last element + // Use modulo to wrap rear to the beginning when it passes the end of the array const rear = (this.front + this.queSize) % this.capacity; - // Front pointer moves one position backward + // Add num to the rear this.nums[rear] = num; this.queSize++; } @@ -47,21 +47,21 @@ class ArrayQueue { /* Dequeue */ pop(): number { const num = this.peek(); - // Move front pointer backward by one position, if it passes the tail, return to array head + // Advance the front pointer by one position, wrapping to the beginning if it passes the end this.front = (this.front + 1) % this.capacity; this.queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ peek(): number { if (this.isEmpty()) throw new Error('Queue is empty'); return this.nums[this.front]; } - /* Return Array */ + /* Return an Array */ toArray(): number[] { - // Elements enqueue + // Convert only the list elements within the occupied range const arr = new Array(this.size); for (let i = 0, j = this.front; i < this.size; i++, j++) { arr[i] = this.nums[j % this.capacity]; @@ -70,12 +70,12 @@ class ArrayQueue { } } -/* Driver Code */ -/* Access front of the queue element */ +/* Driver code */ +/* Initialize the queue */ const capacity = 10; const queue = new ArrayQueue(capacity); -/* Elements enqueue */ +/* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -83,27 +83,27 @@ queue.push(5); queue.push(4); console.log('Queue queue =', queue.toArray()); -/* Return list for printing */ +/* Access the front element */ const peek = queue.peek(); console.log('Front element peek = ' + peek); -/* Element dequeue */ +/* Dequeue an element */ const pop = queue.pop(); -console.log('Dequeue element pop = ' + pop + ', after dequeue queue =', queue.toArray()); +console.log('Dequeued element pop = ' + pop + '; after dequeuing, queue =', queue.toArray()); -/* Get the length of the queue */ +/* Get the queue size */ const size = queue.size; -console.log('Queue length size = ' + size); +console.log('Queue size = ' + size); -/* Check if the queue is empty */ +/* Check whether the queue is empty */ const isEmpty = queue.isEmpty(); -console.log('Queue is empty = ' + isEmpty); +console.log('Is the queue empty = ' + isEmpty); -/* Test circular array */ +/* Test the circular array */ for (let i = 0; i < 10; i++) { queue.push(i); queue.pop(); - console.log('Round ' + i + ' rounds of enqueue + dequeue, queue =', queue.toArray()); + console.log('Round ' + i + ' : after enqueue + dequeue, queue =', queue.toArray()); } export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/array_stack.ts b/en/codes/typescript/chapter_stack_and_queue/array_stack.ts index e98274c57..f76c37b52 100644 --- a/en/codes/typescript/chapter_stack_and_queue/array_stack.ts +++ b/en/codes/typescript/chapter_stack_and_queue/array_stack.ts @@ -4,19 +4,19 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Stack based on array implementation */ +/* Stack implemented with an array */ class ArrayStack { private stack: number[]; constructor() { this.stack = []; } - /* Get the length of the stack */ + /* Get the stack size */ get size(): number { return this.stack.length; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ isEmpty(): boolean { return this.stack.length === 0; } @@ -32,23 +32,23 @@ class ArrayStack { return this.stack.pop(); } - /* Return list for printing */ + /* Access the top element */ top(): number | undefined { if (this.isEmpty()) throw new Error('Stack is empty'); return this.stack[this.stack.length - 1]; } - /* Return Array */ + /* Return an Array */ toArray() { return this.stack; } } -/* Driver Code */ -/* Access top of the stack element */ +/* Driver code */ +/* Initialize the stack */ const stack = new ArrayStack(); -/* Elements push onto stack */ +/* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -57,21 +57,21 @@ stack.push(4); console.log('Stack stack = '); console.log(stack.toArray()); -/* Return list for printing */ +/* Access the top element */ const top = stack.top(); -console.log('Stack top element top = ' + top); +console.log('Top element top = ' + top); -/* Element pop from stack */ +/* Pop an element from the stack */ const pop = stack.pop(); -console.log('Pop element pop = ' + pop + ', after pop, stack = '); +console.log('Popped element pop = ' + pop + '; after popping, stack = '); console.log(stack.toArray()); -/* Get the length of the stack */ +/* Get the stack size */ const size = stack.size; -console.log('Stack length size = ' + size); +console.log('Stack size = ' + size); -/* Check if empty */ +/* Check whether it is empty */ const isEmpty = stack.isEmpty(); -console.log('Stack is empty = ' + isEmpty); +console.log('Is the stack empty = ' + isEmpty); export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/deque.ts b/en/codes/typescript/chapter_stack_and_queue/deque.ts index f8cd205de..3404dc3cc 100644 --- a/en/codes/typescript/chapter_stack_and_queue/deque.ts +++ b/en/codes/typescript/chapter_stack_and_queue/deque.ts @@ -4,43 +4,43 @@ * Author: Zhuo Qinyue (1403450829@qq.com) */ -/* Driver Code */ -/* Get the length of the double-ended queue */ -// TypeScript has no built-in deque, can only use Array as deque +/* Driver code */ +/* Initialize the deque */ +// TypeScript has no built-in deque; use an Array as a deque instead const deque: number[] = []; -/* Elements enqueue */ +/* Enqueue elements */ deque.push(2); deque.push(5); deque.push(4); -// Note: due to array, unshift() method has O(n) time complexity +// Note: because this uses an array, unshift() has O(n) time complexity deque.unshift(3); deque.unshift(1); -console.log('Double-ended queue deque = ', deque); +console.log('Deque deque = ', deque); -/* Update element */ +/* Access an element */ const peekFirst: number = deque[0]; console.log('Front element peekFirst = ' + peekFirst); const peekLast: number = deque[deque.length - 1]; console.log('Rear element peekLast = ' + peekLast); -/* Element dequeue */ -// Note: due to array, shift() method has O(n) time complexity +/* Dequeue an element */ +// Note: because this uses an array, shift() has O(n) time complexity const popFront: number = deque.shift() as number; console.log( - 'Front dequeue element popFront = ' + popFront + ', after front dequeue, deque = ' + deque + 'Element removed from the front, popFront = ' + popFront + ', after dequeuing from the front, deque = ' + deque ); const popBack: number = deque.pop() as number; console.log( - 'Dequeue rear element popBack = ' + popBack + ', after rear dequeue, deque = ' + deque + 'Element removed from the rear, popBack = ' + popBack + ', after dequeuing from the rear, deque = ' + deque ); -/* Get the length of the double-ended queue */ +/* Get the deque size */ const size: number = deque.length; -console.log('Double-ended queue length size = ' + size); +console.log('Deque size = ' + size); -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ const isEmpty: boolean = size === 0; -console.log('Double-ended queue is empty = ' + isEmpty); +console.log('Is the deque empty = ' + isEmpty); export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/linkedlist_deque.ts b/en/codes/typescript/chapter_stack_and_queue/linkedlist_deque.ts index 88680fad8..f66c9ae39 100644 --- a/en/codes/typescript/chapter_stack_and_queue/linkedlist_deque.ts +++ b/en/codes/typescript/chapter_stack_and_queue/linkedlist_deque.ts @@ -6,8 +6,8 @@ /* Doubly linked list node */ class ListNode { - prev: ListNode; // Predecessor node reference (pointer) - next: ListNode; // Successor node reference (pointer) + prev: ListNode; // Reference (pointer) to the previous node + next: ListNode; // Reference (pointer) to the next node val: number; // Node value constructor(val: number) { @@ -17,11 +17,11 @@ class ListNode { } } -/* Double-ended queue based on doubly linked list implementation */ +/* Deque implemented with a doubly linked list */ class LinkedListDeque { private front: ListNode; // Head node front private rear: ListNode; // Tail node rear - private queSize: number; // Length of the double-ended queue + private queSize: number; // Deque size constructor() { this.front = null; @@ -29,7 +29,7 @@ class LinkedListDeque { this.queSize = 0; } - /* Rear of the queue enqueue operation */ + /* Enqueue at the rear */ pushLast(val: number): void { const node: ListNode = new ListNode(val); // If the linked list is empty, make both front and rear point to node @@ -40,12 +40,12 @@ class LinkedListDeque { // Add node to the tail of the linked list this.rear.next = node; node.prev = this.rear; - this.rear = node; // Update tail node + this.rear = node; // Update the tail node } this.queSize++; } - /* Front of the queue enqueue operation */ + /* Enqueue at the front */ pushFirst(val: number): void { const node: ListNode = new ListNode(val); // If the linked list is empty, make both front and rear point to node @@ -56,76 +56,76 @@ class LinkedListDeque { // Add node to the head of the linked list this.front.prev = node; node.next = this.front; - this.front = node; // Update head node + this.front = node; // Update the head node } this.queSize++; } - /* Temporarily store tail node value */ + /* Dequeue from the rear */ popLast(): number { if (this.queSize === 0) { return null; } - const value: number = this.rear!.val; // Store tail node value - // When the deque has one element, front and rear point to the same node + const value: number = this.rear!.val; // Store the tail node value + // When the queue size is 1, the head and tail point to the same node if (this.queSize === 1) { this.front = null; this.rear = null; } - // Update tail node + // Remove the tail node else { let temp: ListNode = this.rear!.prev; temp!.next = null; this.rear!.prev = null; - this.rear = temp; // Update tail node + this.rear = temp; // Update the tail node } this.queSize--; return value; } - /* Temporarily store head node value */ + /* Dequeue from the front */ popFirst(): number { if (this.queSize === 0) { return null; } const value: number = this.front.val; // Store the head node value - // When the deque has one element, front and rear point to the same node + // When the queue size is 1, the head and tail point to the same node if (this.queSize === 1) { this.front = null; this.rear = null; } - // Delete head node + // Remove the head node else { let temp: ListNode = this.front!.next; temp!.prev = null; this.front!.next = null; - this.front = temp; // Update head node + this.front = temp; // Update the head node } this.queSize--; return value; } - /* Driver Code */ + /* Access the rear element */ peekLast(): number { return this.queSize === 0 ? null : this.rear.val; } - /* Return list for printing */ + /* Access the front element */ peekFirst(): number { return this.queSize === 0 ? null : this.front.val; } - /* Get the length of the double-ended queue */ + /* Get the deque size */ size(): number { return this.queSize; } - /* Check if the double-ended queue is empty */ + /* Check whether the deque is empty */ isEmpty(): boolean { return this.queSize === 0; } - /* Print deque */ + /* Print the deque */ print(): void { const arr: number[] = []; let temp: ListNode = this.front; @@ -137,8 +137,8 @@ class LinkedListDeque { } } -/* Driver Code */ -/* Get the length of the double-ended queue */ +/* Driver code */ +/* Initialize the deque */ const linkedListDeque: LinkedListDeque = new LinkedListDeque(); linkedListDeque.pushLast(3); linkedListDeque.pushLast(2); @@ -146,32 +146,32 @@ linkedListDeque.pushLast(5); console.log('Deque linkedListDeque = '); linkedListDeque.print(); -/* Update element */ +/* Access an element */ const peekFirst: number = linkedListDeque.peekFirst(); console.log('Front element peekFirst = ' + peekFirst); const peekLast: number = linkedListDeque.peekLast(); console.log('Rear element peekLast = ' + peekLast); -/* Elements enqueue */ +/* Enqueue elements */ linkedListDeque.pushLast(4); -console.log('After element 4 enqueues at rear, linkedListDeque = '); +console.log('After enqueuing 4 at the rear, linkedListDeque = '); linkedListDeque.print(); linkedListDeque.pushFirst(1); -console.log('After element 1 enqueues at front, linkedListDeque = '); +console.log('After enqueuing 1 at the front, linkedListDeque = '); linkedListDeque.print(); -/* Element dequeue */ +/* Dequeue an element */ const popLast: number = linkedListDeque.popLast(); -console.log('Rear dequeue element = ' + popLast + ', after rear dequeue linkedListDeque = '); +console.log('Element dequeued from the rear = ' + popLast + '; after removing from the rear, linkedListDeque = '); linkedListDeque.print(); const popFirst: number = linkedListDeque.popFirst(); -console.log('Front dequeue element = ' + popFirst + ', after front dequeue linkedListDeque = '); +console.log('Element dequeued from the front = ' + popFirst + '; after removing from the front, linkedListDeque = '); linkedListDeque.print(); -/* Get the length of the double-ended queue */ +/* Get the deque size */ const size: number = linkedListDeque.size(); -console.log('Double-ended queue length size = ' + size); +console.log('Deque size = ' + size); -/* Check if the double-ended queue is empty */ +/* Check whether the deque is empty */ const isEmpty: boolean = linkedListDeque.isEmpty(); -console.log('Double-ended queue is empty = ' + isEmpty); +console.log('Is the deque empty = ' + isEmpty); diff --git a/en/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts b/en/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts index 9f7bd37ee..ef602a8f6 100644 --- a/en/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts +++ b/en/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts @@ -6,7 +6,7 @@ import { ListNode } from '../modules/ListNode'; -/* Queue based on linked list implementation */ +/* Queue implemented with a linked list */ class LinkedListQueue { private front: ListNode | null; // Head node front private rear: ListNode | null; // Tail node rear @@ -17,12 +17,12 @@ class LinkedListQueue { this.rear = null; } - /* Get the length of the queue */ + /* Get the queue size */ get size(): number { return this.queSize; } - /* Check if the queue is empty */ + /* Check whether the queue is empty */ isEmpty(): boolean { return this.size === 0; } @@ -31,11 +31,11 @@ class LinkedListQueue { push(num: number): void { // Add num after the tail node const node = new ListNode(num); - // If the queue is empty, make both front and rear point to the node + // If the queue is empty, make both the head and tail point to this node if (!this.front) { this.front = node; this.rear = node; - // If the queue is not empty, add the node after the tail node + // If the queue is not empty, add this node after the tail node } else { this.rear!.next = node; this.rear = node; @@ -47,19 +47,19 @@ class LinkedListQueue { pop(): number { const num = this.peek(); if (!this.front) throw new Error('Queue is empty'); - // Delete head node + // Remove the head node this.front = this.front.next; this.queSize--; return num; } - /* Return list for printing */ + /* Access the front element */ peek(): number { if (this.size === 0) throw new Error('Queue is empty'); return this.front!.val; } - /* Convert linked list to Array and return */ + /* Convert the linked list to an Array and return it */ toArray(): number[] { let node = this.front; const res = new Array(this.size); @@ -71,11 +71,11 @@ class LinkedListQueue { } } -/* Driver Code */ -/* Access front of the queue element */ +/* Driver code */ +/* Initialize the queue */ const queue = new LinkedListQueue(); -/* Elements enqueue */ +/* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -83,20 +83,20 @@ queue.push(5); queue.push(4); console.log('Queue queue = ' + queue.toArray()); -/* Return list for printing */ +/* Access the front element */ const peek = queue.peek(); console.log('Front element peek = ' + peek); -/* Element dequeue */ +/* Dequeue an element */ const pop = queue.pop(); -console.log('Dequeue element pop = ' + pop + ', after dequeue, queue = ' + queue.toArray()); +console.log('Dequeued element pop = ' + pop + '; after dequeuing, queue = ' + queue.toArray()); -/* Get the length of the queue */ +/* Get the queue size */ const size = queue.size; -console.log('Queue length size = ' + size); +console.log('Queue size = ' + size); -/* Check if the queue is empty */ +/* Check whether the queue is empty */ const isEmpty = queue.isEmpty(); -console.log('Queue is empty = ' + isEmpty); +console.log('Is the queue empty = ' + isEmpty); export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/linkedlist_stack.ts b/en/codes/typescript/chapter_stack_and_queue/linkedlist_stack.ts index d42f99c06..86add1f0e 100644 --- a/en/codes/typescript/chapter_stack_and_queue/linkedlist_stack.ts +++ b/en/codes/typescript/chapter_stack_and_queue/linkedlist_stack.ts @@ -6,21 +6,21 @@ import { ListNode } from '../modules/ListNode'; -/* Stack based on linked list implementation */ +/* Stack implemented with a linked list */ class LinkedListStack { - private stackPeek: ListNode | null; // Use head node as stack top - private stkSize: number = 0; // Stack length + private stackPeek: ListNode | null; // Use the head node as the stack top + private stkSize: number = 0; // Stack size constructor() { this.stackPeek = null; } - /* Get the length of the stack */ + /* Get the stack size */ get size(): number { return this.stkSize; } - /* Check if the stack is empty */ + /* Check whether the stack is empty */ isEmpty(): boolean { return this.size === 0; } @@ -42,13 +42,13 @@ class LinkedListStack { return num; } - /* Return list for printing */ + /* Access the top element */ peek(): number { if (!this.stackPeek) throw new Error('Stack is empty'); return this.stackPeek.val; } - /* Convert linked list to Array and return */ + /* Convert the linked list to an Array and return it */ toArray(): number[] { let node = this.stackPeek; const res = new Array(this.size); @@ -60,11 +60,11 @@ class LinkedListStack { } } -/* Driver Code */ -/* Access top of the stack element */ +/* Driver code */ +/* Initialize the stack */ const stack = new LinkedListStack(); -/* Elements push onto stack */ +/* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -72,20 +72,20 @@ stack.push(5); stack.push(4); console.log('Stack stack = ' + stack.toArray()); -/* Return list for printing */ +/* Access the top element */ const peek = stack.peek(); -console.log('Stack top element peek = ' + peek); +console.log('Top element peek = ' + peek); -/* Element pop from stack */ +/* Pop an element from the stack */ const pop = stack.pop(); -console.log('Pop element pop = ' + pop + ', after pop, stack = ' + stack.toArray()); +console.log('Popped element pop = ' + pop + '; after popping, stack = ' + stack.toArray()); -/* Get the length of the stack */ +/* Get the stack size */ const size = stack.size; -console.log('Stack length size = ' + size); +console.log('Stack size = ' + size); -/* Check if empty */ +/* Check whether it is empty */ const isEmpty = stack.isEmpty(); -console.log('Stack is empty = ' + isEmpty); +console.log('Is the stack empty = ' + isEmpty); export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/queue.ts b/en/codes/typescript/chapter_stack_and_queue/queue.ts index 71e4f0fbd..89bd9aba6 100644 --- a/en/codes/typescript/chapter_stack_and_queue/queue.ts +++ b/en/codes/typescript/chapter_stack_and_queue/queue.ts @@ -4,12 +4,12 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Driver Code */ -/* Access front of the queue element */ -// TypeScript has no built-in queue, can use Array as queue +/* Driver code */ +/* Initialize the queue */ +// TypeScript has no built-in queue; an Array can be used as a queue const queue: number[] = []; -/* Elements enqueue */ +/* Enqueue elements */ queue.push(1); queue.push(3); queue.push(2); @@ -17,21 +17,21 @@ queue.push(5); queue.push(4); console.log('Queue queue =', queue); -/* Return list for printing */ +/* Access the front element */ const peek = queue[0]; console.log('Front element peek =', peek); -/* Element dequeue */ -// Underlying is array, so shift() method has O(n) time complexity +/* Dequeue an element */ +// The underlying structure is an array, so shift() has O(n) time complexity const pop = queue.shift(); -console.log('Dequeue element pop =', pop, ', after dequeue, queue = ', queue); +console.log('Dequeued element pop =', pop, '; after dequeuing, queue = ', queue); -/* Get the length of the queue */ +/* Get the queue size */ const size = queue.length; -console.log('Queue length size =', size); +console.log('Queue size =', size); -/* Check if the queue is empty */ +/* Check whether the queue is empty */ const isEmpty = queue.length === 0; -console.log('Queue is empty = ', isEmpty); +console.log('Is the queue empty = ', isEmpty); export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/stack.ts b/en/codes/typescript/chapter_stack_and_queue/stack.ts index a503dcf5d..73b6e79ce 100644 --- a/en/codes/typescript/chapter_stack_and_queue/stack.ts +++ b/en/codes/typescript/chapter_stack_and_queue/stack.ts @@ -4,12 +4,12 @@ * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) */ -/* Driver Code */ -/* Access top of the stack element */ -// TypeScript has no built-in stack class, can use Array as stack +/* Driver code */ +/* Initialize the stack */ +// TypeScript has no built-in stack class; an Array can be used as a stack const stack: number[] = []; -/* Elements push onto stack */ +/* Push elements onto the stack */ stack.push(1); stack.push(3); stack.push(2); @@ -17,21 +17,21 @@ stack.push(5); stack.push(4); console.log('Stack stack =', stack); -/* Return list for printing */ +/* Access the top element */ const peek = stack[stack.length - 1]; -console.log('Stack top element peek =', peek); +console.log('Top element peek =', peek); -/* Element pop from stack */ +/* Pop an element from the stack */ const pop = stack.pop(); -console.log('Pop element pop =', pop); -console.log('After pop, stack =', stack); +console.log('Popped element pop =', pop); +console.log('After popping, stack =', stack); -/* Get the length of the stack */ +/* Get the stack size */ const size = stack.length; -console.log('Stack length size =', size); +console.log('Stack size =', size); -/* Check if empty */ +/* Check whether it is empty */ const isEmpty = stack.length === 0; -console.log('Is stack empty =', isEmpty); +console.log('Is the stack empty =', isEmpty); export {}; diff --git a/en/codes/typescript/chapter_tree/array_binary_tree.ts b/en/codes/typescript/chapter_tree/array_binary_tree.ts index c97205536..9ff764756 100644 --- a/en/codes/typescript/chapter_tree/array_binary_tree.ts +++ b/en/codes/typescript/chapter_tree/array_binary_tree.ts @@ -9,7 +9,7 @@ import { printTree } from '../modules/PrintUtil'; type Order = 'pre' | 'in' | 'post'; -/* Binary tree class represented by array */ +/* Binary tree class using an array representation */ class ArrayBinaryTree { #tree: (number | null)[]; @@ -18,29 +18,29 @@ class ArrayBinaryTree { this.#tree = arr; } - /* List capacity */ + /* Array length, including empty positions */ size(): number { return this.#tree.length; } - /* Get value of node at index i */ + /* Get the value of the node at index i */ val(i: number): number | null { - // If index out of bounds, return null to represent empty position + // If the index is out of bounds, return null to indicate an empty position if (i < 0 || i >= this.size()) return null; return this.#tree[i]; } - /* Get index of left child node of node at index i */ + /* Get the left child index of the node at index i */ left(i: number): number { return 2 * i + 1; } - /* Get index of right child node of node at index i */ + /* Get the right child index of the node at index i */ right(i: number): number { return 2 * i + 2; } - /* Get index of parent node of node at index i */ + /* Get the parent index of the node at index i */ parent(i: number): number { return Math.floor((i - 1) / 2); // Floor division } @@ -48,7 +48,7 @@ class ArrayBinaryTree { /* Level-order traversal */ levelOrder(): number[] { let res = []; - // Traverse array directly + // Traverse the array directly for (let i = 0; i < this.size(); i++) { if (this.val(i) !== null) res.push(this.val(i)); } @@ -57,7 +57,7 @@ class ArrayBinaryTree { /* Depth-first traversal */ #dfs(i: number, order: Order, res: (number | null)[]): void { - // If empty position, return + // Return if this position is empty if (this.val(i) === null) return; // Preorder traversal if (order === 'pre') res.push(this.val(i)); @@ -91,9 +91,9 @@ class ArrayBinaryTree { } } -/* Driver Code */ -// Initialize binary tree -// Here we use a function to generate a binary tree directly from an array +/* Driver code */ +// Initialize the binary tree +// Use a helper function that builds a binary tree directly from an array const arr = Array.of( 1, 2, @@ -113,39 +113,39 @@ const arr = Array.of( ); const root = arrToTree(arr); -console.log('\nInitialize binary tree\n'); -console.log('Array representation of binary tree:'); +console.log('\nInitialize the binary tree\n'); +console.log('Array representation of the binary tree:'); console.log(arr); -console.log('Linked list representation of binary tree:'); +console.log('Linked representation of the binary tree:'); printTree(root); -// Binary tree class represented by array +// Binary tree class using an array representation const abt = new ArrayBinaryTree(arr); -// Access node +// Access a node const i = 1; const l = abt.left(i); const r = abt.right(i); const p = abt.parent(i); -console.log('\nCurrent node index is ' + i + ', value is ' + abt.val(i)); +console.log('\nCurrent node index: ' + i + ' with value ' + abt.val(i)); console.log( - 'Its left child node index is ' + l + ', value is ' + (l === null ? 'null' : abt.val(l)) + 'Left child index: ' + l + ' with value ' + (l === null ? 'null' : abt.val(l)) ); console.log( - 'Its right child node index is ' + r + ', value is ' + (r === null ? 'null' : abt.val(r)) + 'Right child index: ' + r + ' with value ' + (r === null ? 'null' : abt.val(r)) ); console.log( - 'Its parent node index is ' + p + ', value is ' + (p === null ? 'null' : abt.val(p)) + 'Parent index: ' + p + ' with value ' + (p === null ? 'null' : abt.val(p)) ); -// Traverse tree +// Traverse the tree let res = abt.levelOrder(); -console.log('\nLevel-order traversal is:' + res); +console.log('\nLevel-order traversal:' + res); res = abt.preOrder(); -console.log('Preorder traversal is:' + res); +console.log('Preorder traversal:' + res); res = abt.inOrder(); -console.log('Inorder traversal is:' + res); +console.log('Inorder traversal:' + res); res = abt.postOrder(); -console.log('Postorder traversal is:' + res); +console.log('Postorder traversal:' + res); export {}; diff --git a/en/codes/typescript/chapter_tree/avl_tree.ts b/en/codes/typescript/chapter_tree/avl_tree.ts index 39cc02d15..cbf760704 100644 --- a/en/codes/typescript/chapter_tree/avl_tree.ts +++ b/en/codes/typescript/chapter_tree/avl_tree.ts @@ -7,125 +7,125 @@ import { TreeNode } from '../modules/TreeNode'; import { printTree } from '../modules/PrintUtil'; -/* AVL tree */ +/* AVL tree*/ class AVLTree { root: TreeNode; /* Constructor */ constructor() { - this.root = null; // Root node + this.root = null; //Root node } - /* Get node height */ + /* Get the node height */ height(node: TreeNode): number { - // Empty node height is -1, leaf node height is 0 + // A null node has height -1; a leaf has height 0 return node === null ? -1 : node.height; } - /* Update node height */ + /* Update the node height */ private updateHeight(node: TreeNode): void { - // Node height equals the height of the tallest subtree + 1 + // The node height equals the height of its taller subtree + 1 node.height = Math.max(this.height(node.left), this.height(node.right)) + 1; } - /* Get balance factor */ + /* Get the balance factor */ balanceFactor(node: TreeNode): number { - // Empty node balance factor is 0 + // A null node has balance factor 0 if (node === null) return 0; // Node balance factor = left subtree height - right subtree height return this.height(node.left) - this.height(node.right); } - /* Right rotation operation */ + /* Right rotation */ private rightRotate(node: TreeNode): TreeNode { const child = node.left; const grandChild = child.right; - // Using child as pivot, rotate node to the right + // Rotate node to the right around child child.right = node; node.left = grandChild; - // Update node height + // Update the node height this.updateHeight(node); this.updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Left rotation operation */ + /* Left rotation */ private leftRotate(node: TreeNode): TreeNode { const child = node.right; const grandChild = child.left; - // Using child as pivot, rotate node to the left + // Rotate node to the left around child child.left = node; node.right = grandChild; - // Update node height + // Update the node height this.updateHeight(node); this.updateHeight(child); - // Return root node of subtree after rotation + // Return the root of the rotated subtree return child; } - /* Perform rotation operation to restore balance to this subtree */ + /* Perform rotations to restore balance in this subtree */ private rotate(node: TreeNode): TreeNode { - // Get balance factor of node + // Get the balance factor of node const balanceFactor = this.balanceFactor(node); - // Left-leaning tree + // Left-heavy tree if (balanceFactor > 1) { if (this.balanceFactor(node.left) >= 0) { - // Right rotation + // Rotate right return this.rightRotate(node); } else { - // First left rotation then right rotation + // Rotate left, then right node.left = this.leftRotate(node.left); return this.rightRotate(node); } } - // Right-leaning tree + // Right-heavy tree if (balanceFactor < -1) { if (this.balanceFactor(node.right) <= 0) { - // Left rotation + // Rotate left return this.leftRotate(node); } else { - // First right rotation then left rotation + // Rotate right, then left node.right = this.rightRotate(node.right); return this.leftRotate(node); } } - // Balanced tree, no rotation needed, return directly + // The tree is balanced; return without rotating return node; } - /* Insert node */ + /* Insert a node */ insert(val: number): void { this.root = this.insertHelper(this.root, val); } - /* Recursively insert node (helper method) */ + /* Insert a node recursively (helper method) */ private insertHelper(node: TreeNode, val: number): TreeNode { if (node === null) return new TreeNode(val); - /* 1. Find insertion position and insert node */ + /* 1. Find the insertion position and insert the node */ if (val < node.val) { node.left = this.insertHelper(node.left, val); } else if (val > node.val) { node.right = this.insertHelper(node.right, val); } else { - return node; // Duplicate node not inserted, return directly + return node; // Return without inserting a duplicate node } - this.updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + this.updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = this.rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } - /* Remove node */ + /* Remove a node */ remove(val: number): void { this.root = this.removeHelper(this.root, val); } - /* Recursively delete node (helper method) */ + /* Remove a node recursively (helper method) */ private removeHelper(node: TreeNode, val: number): TreeNode { if (node === null) return null; - /* 1. Find node and delete */ + /* 1. Find and remove the node */ if (val < node.val) { node.left = this.removeHelper(node.left, val); } else if (val > node.val) { @@ -133,15 +133,15 @@ class AVLTree { } else { if (node.left === null || node.right === null) { const child = node.left !== null ? node.left : node.right; - // Number of child nodes = 0, delete node directly and return + // Number of children = 0: remove node and return if (child === null) { return null; } else { - // Number of child nodes = 1, delete node directly + // Number of children = 1: remove node directly node = child; } } else { - // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + // Number of children = 2: remove the inorder successor and use its value to replace the current node value let temp = node.right; while (temp.left !== null) { temp = temp.left; @@ -150,51 +150,51 @@ class AVLTree { node.val = temp.val; } } - this.updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to this subtree */ + this.updateHeight(node); // Update the node height + /* 2. Perform rotations to restore balance in this subtree */ node = this.rotate(node); - // Return root node of subtree + // Return the root of the subtree return node; } - /* Search node */ + /* Find a node */ search(val: number): TreeNode { let cur = this.root; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur !== null) { if (cur.val < val) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur cur = cur.right; } else if (cur.val > val) { - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur cur = cur.left; } else { - // Found target node, exit loop + // The target node was found; exit the loop break; } } - // Return target node + // Return the target node return cur; } } function testInsert(tree: AVLTree, val: number): void { tree.insert(val); - console.log('\nInsert node ' + val + ', AVL tree is'); + console.log('\nAfter inserting node ' + val + ' the AVL tree is:'); printTree(tree.root); } function testRemove(tree: AVLTree, val: number): void { tree.remove(val); - console.log('\nRemove node ' + val + ', AVL tree is'); + console.log('\nAfter removing node ' + val + ' the AVL tree is:'); printTree(tree.root); } -/* Driver Code */ -/* Please pay attention to how the AVL tree maintains balance after inserting nodes */ +/* Driver code */ +/* Initialize an empty AVL tree */ const avlTree = new AVLTree(); -/* Insert node */ -// Delete nodes +/* Insert a node */ +// Observe how the AVL tree maintains balance after a node is inserted testInsert(avlTree, 1); testInsert(avlTree, 2); testInsert(avlTree, 3); @@ -206,17 +206,17 @@ testInsert(avlTree, 9); testInsert(avlTree, 10); testInsert(avlTree, 6); -/* Please pay attention to how the AVL tree maintains balance after deleting nodes */ +/* Insert a duplicate node */ testInsert(avlTree, 7); -/* Remove node */ -// Delete node with degree 1 -testRemove(avlTree, 8); // Delete node with degree 2 -testRemove(avlTree, 5); // Remove node with degree 1 -testRemove(avlTree, 4); // Remove node with degree 2 +/* Remove a node */ +// Observe how the AVL tree maintains balance after a node is removed +testRemove(avlTree, 8); // Remove a node with 0 children +testRemove(avlTree, 5); // Remove a node with 1 child +testRemove(avlTree, 4); // Remove a node with 2 children -/* Search node */ +/* Look up a node */ const node = avlTree.search(7); -console.log('\nFound node object is', node, ', node value = ' + node.val); +console.log('\nFound node object:', node, ', node value = ' + node.val); export {}; diff --git a/en/codes/typescript/chapter_tree/binary_search_tree.ts b/en/codes/typescript/chapter_tree/binary_search_tree.ts index 25005d8e9..6e4c64c4e 100644 --- a/en/codes/typescript/chapter_tree/binary_search_tree.ts +++ b/en/codes/typescript/chapter_tree/binary_search_tree.ts @@ -13,134 +13,134 @@ class BinarySearchTree { /* Constructor */ constructor() { - // Initialize empty tree + // Initialize an empty tree this.root = null; } - /* Get binary tree root node */ + /* Get the root of the binary tree */ getRoot(): TreeNode | null { return this.root; } - /* Search node */ + /* Find a node */ search(num: number): TreeNode | null { let cur = this.root; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur !== null) { - // Target node is in cur's right subtree + // The target node lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Target node is in cur's left subtree + // The target node lies in the left subtree of cur else if (cur.val > num) cur = cur.left; - // Found target node, exit loop + // The target node was found; exit the loop else break; } - // Return target node + // Return the target node return cur; } - /* Insert node */ + /* Insert a node */ insert(num: number): void { - // If tree is empty, initialize root node + // If the tree is empty, initialize the root node if (this.root === null) { this.root = new TreeNode(num); return; } let cur: TreeNode | null = this.root, pre: TreeNode | null = null; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur !== null) { - // Found duplicate node, return directly + // Return immediately if a duplicate node is found if (cur.val === num) return; pre = cur; - // Insertion position is in cur's right subtree + // The insertion position lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Insertion position is in cur's left subtree + // The insertion position lies in the left subtree of cur else cur = cur.left; } - // Insert node + // Insert a node const node = new TreeNode(num); if (pre!.val < num) pre!.right = node; else pre!.left = node; } - /* Remove node */ + /* Remove a node */ remove(num: number): void { - // If tree is empty, return directly + // Return immediately if the tree is empty if (this.root === null) return; let cur: TreeNode | null = this.root, pre: TreeNode | null = null; - // Loop search, exit after passing leaf node + // Search in a loop, stopping if a null reference is reached while (cur !== null) { - // Found node to delete, exit loop + // The node to remove was found; exit the loop if (cur.val === num) break; pre = cur; - // Node to delete is in cur's right subtree + // The node to remove lies in the right subtree of cur if (cur.val < num) cur = cur.right; - // Node to delete is in cur's left subtree + // The node to remove lies in the left subtree of cur else cur = cur.left; } - // If no node to delete, return directly + // Return immediately if the node to remove does not exist if (cur === null) return; - // Number of child nodes = 0 or 1 + // Number of children = 0 or 1 if (cur.left === null || cur.right === null) { - // When number of child nodes = 0 / 1, child = null / that child node + // With 0 / 1 children, child is null / the child node, respectively const child: TreeNode | null = cur.left !== null ? cur.left : cur.right; - // Delete node cur + // Remove node cur if (cur !== this.root) { if (pre!.left === cur) pre!.left = child; else pre!.right = child; } else { - // If deleted node is root node, reassign root node + // If the removed node is the root, assign the new root this.root = child; } } - // Number of child nodes = 2 + // Number of children = 2 else { - // Get next node of cur in inorder traversal + // Get the inorder successor of cur let tmp: TreeNode | null = cur.right; while (tmp!.left !== null) { tmp = tmp!.left; } - // Recursively delete node tmp + // Recursively remove node tmp this.remove(tmp!.val); - // Replace cur with tmp + // Replace the value of cur with the value of tmp cur.val = tmp!.val; } } } -/* Driver Code */ -/* Initialize binary search tree */ +/* Driver code */ +/* Initialize the binary search tree */ const bst = new BinarySearchTree(); -// Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree +// Different insertion orders produce different binary trees; this sequence produces a perfect binary tree const nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; for (const num of nums) { bst.insert(num); } -console.log('\nInitialized binary tree is\n'); +console.log('\nThe initialized binary tree is:\n'); printTree(bst.getRoot()); -/* Search node */ +/* Find a node */ const node = bst.search(7); console.log( - '\nFound node object is ' + node + ', node value = ' + (node ? node.val : 'null') + '\nFound node object: ' + node + ', node value = ' + (node ? node.val : 'null') ); -/* Insert node */ +/* Insert a node */ bst.insert(16); -console.log('\nAfter inserting node 16, binary tree is\n'); +console.log('\nBinary tree after inserting node 16:\n'); printTree(bst.getRoot()); -/* Remove node */ +/* Remove a node */ bst.remove(1); -console.log('\nAfter removing node 1, binary tree is\n'); +console.log('\nBinary tree after removing node 1:\n'); printTree(bst.getRoot()); bst.remove(2); -console.log('\nAfter removing node 2, binary tree is\n'); +console.log('\nBinary tree after removing node 2:\n'); printTree(bst.getRoot()); bst.remove(4); -console.log('\nAfter removing node 4, binary tree is\n'); +console.log('\nBinary tree after removing node 4:\n'); printTree(bst.getRoot()); export {}; diff --git a/en/codes/typescript/chapter_tree/binary_tree.ts b/en/codes/typescript/chapter_tree/binary_tree.ts index 5e82a6b4b..ebe10d486 100644 --- a/en/codes/typescript/chapter_tree/binary_tree.ts +++ b/en/codes/typescript/chapter_tree/binary_tree.ts @@ -7,31 +7,31 @@ import { TreeNode } from '../modules/TreeNode'; import { printTree } from '../modules/PrintUtil'; -/* Initialize binary tree */ -// Initialize nodes +/* Initialize the binary tree */ +// Initialize the nodes let n1 = new TreeNode(1), n2 = new TreeNode(2), n3 = new TreeNode(3), n4 = new TreeNode(4), n5 = new TreeNode(5); -// Build references (pointers) between nodes +// Set up references (pointers) between the nodes n1.left = n2; n1.right = n3; n2.left = n4; n2.right = n5; -console.log('\nInitialize binary tree\n'); +console.log('\nInitialize the binary tree\n'); printTree(n1); -/* Insert node P between n1 -> n2 */ +/* Insert and remove nodes */ const P = new TreeNode(0); -// Delete node +// Insert node P between n1 and n2 in n1 -> n2 n1.left = P; P.left = n2; -console.log('\nAfter inserting node P\n'); +console.log('\nAfter inserting node P:\n'); printTree(n1); // Remove node P n1.left = n2; -console.log('\nAfter removing node P\n'); +console.log('\nAfter removing node P:\n'); printTree(n1); export {}; diff --git a/en/codes/typescript/chapter_tree/binary_tree_bfs.ts b/en/codes/typescript/chapter_tree/binary_tree_bfs.ts index 5ebc90a28..ccface1ba 100644 --- a/en/codes/typescript/chapter_tree/binary_tree_bfs.ts +++ b/en/codes/typescript/chapter_tree/binary_tree_bfs.ts @@ -10,32 +10,32 @@ import { printTree } from '../modules/PrintUtil'; /* Level-order traversal */ function levelOrder(root: TreeNode | null): number[] { - // Initialize queue, add root node + // Initialize a queue and enqueue the root const queue = [root]; - // Initialize a list to save the traversal sequence + // Initialize a list to store the traversal sequence const list: number[] = []; while (queue.length) { - let node = queue.shift() as TreeNode; // Dequeue - list.push(node.val); // Save node value + let node = queue.shift() as TreeNode; // Dequeue from the queue + list.push(node.val); // Store the node value if (node.left) { - queue.push(node.left); // Left child node enqueue + queue.push(node.left); // Enqueue the left child } if (node.right) { - queue.push(node.right); // Right child node enqueue + queue.push(node.right); // Enqueue the right child } } return list; } -/* Driver Code */ -/* Initialize binary tree */ -// Here we use a function to generate a binary tree directly from an array +/* Driver code */ +/* Initialize the binary tree */ +// Use a helper function that builds a binary tree directly from an array const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree\n'); +console.log('\nInitialize the binary tree\n'); printTree(root); /* Level-order traversal */ const list = levelOrder(root); -console.log('\nLevel-order traversal node print sequence = ' + list); +console.log('\nNode sequence from level-order traversal = ' + list); export {}; diff --git a/en/codes/typescript/chapter_tree/binary_tree_dfs.ts b/en/codes/typescript/chapter_tree/binary_tree_dfs.ts index 32555af3d..73d58dc08 100644 --- a/en/codes/typescript/chapter_tree/binary_tree_dfs.ts +++ b/en/codes/typescript/chapter_tree/binary_tree_dfs.ts @@ -8,7 +8,7 @@ import { type TreeNode } from '../modules/TreeNode'; import { arrToTree } from '../modules/TreeNode'; import { printTree } from '../modules/PrintUtil'; -// Initialize list for storing traversal sequence +// Initialize a list to store the traversal sequence const list: number[] = []; /* Preorder traversal */ @@ -16,7 +16,7 @@ function preOrder(root: TreeNode | null): void { if (root === null) { return; } - // Visit priority: root node -> left subtree -> right subtree + // Visit order: root -> left subtree -> right subtree list.push(root.val); preOrder(root.left); preOrder(root.right); @@ -27,7 +27,7 @@ function inOrder(root: TreeNode | null): void { if (root === null) { return; } - // Visit priority: left subtree -> root node -> right subtree + // Visit order: left subtree -> root -> right subtree inOrder(root.left); list.push(root.val); inOrder(root.right); @@ -38,32 +38,32 @@ function postOrder(root: TreeNode | null): void { if (root === null) { return; } - // Visit priority: left subtree -> right subtree -> root node + // Visit order: left subtree -> right subtree -> root postOrder(root.left); postOrder(root.right); list.push(root.val); } -/* Driver Code */ -/* Initialize binary tree */ -// Here we use a function to generate a binary tree directly from an array +/* Driver code */ +/* Initialize the binary tree */ +// Use a helper function that builds a binary tree directly from an array const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); -console.log('\nInitialize binary tree\n'); +console.log('\nInitialize the binary tree\n'); printTree(root); /* Preorder traversal */ list.length = 0; preOrder(root); -console.log('\nPreorder traversal node print sequence = ' + list); +console.log('\nNode sequence from preorder traversal = ' + list); /* Inorder traversal */ list.length = 0; inOrder(root); -console.log('\nInorder traversal node print sequence = ' + list); +console.log('\nNode sequence from inorder traversal = ' + list); /* Postorder traversal */ list.length = 0; postOrder(root); -console.log('\nPostorder traversal node print sequence = ' + list); +console.log('\nNode sequence from postorder traversal = ' + list); export {}; diff --git a/en/codes/typescript/modules/ListNode.ts b/en/codes/typescript/modules/ListNode.ts index b20fb01b5..20275c3ae 100644 --- a/en/codes/typescript/modules/ListNode.ts +++ b/en/codes/typescript/modules/ListNode.ts @@ -14,7 +14,7 @@ class ListNode { } } -/* Deserialize array to linked list */ +/* Deserialize an array into a linked list */ function arrToLinkedList(arr: number[]): ListNode | null { const dum: ListNode = new ListNode(0); let head = dum; diff --git a/en/codes/typescript/modules/PrintUtil.ts b/en/codes/typescript/modules/PrintUtil.ts index 6cb0abc61..552dc9a0d 100644 --- a/en/codes/typescript/modules/PrintUtil.ts +++ b/en/codes/typescript/modules/PrintUtil.ts @@ -7,7 +7,7 @@ import { ListNode } from './ListNode'; import { TreeNode, arrToTree } from './TreeNode'; -/* Print linked list */ +/* Print a linked list */ function printLinkedList(head: ListNode | null): void { const list: string[] = []; while (head !== null) { @@ -28,7 +28,7 @@ class Trunk { } /** - * Print binary tree + * Print the binary tree * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -36,7 +36,7 @@ function printTree(root: TreeNode | null) { printTreeHelper(root, null, false); } -/* Print binary tree */ +/* Print the binary tree */ function printTreeHelper( root: TreeNode | null, prev: Trunk | null, @@ -81,11 +81,11 @@ function showTrunks(p: Trunk | null) { process.stdout.write(p.str); } -/* Print heap */ +/* Print a heap */ function printHeap(arr: number[]): void { - console.log('Heap array representation:'); + console.log('Array representation of the heap:'); console.log(arr); - console.log('Heap tree representation:'); + console.log('Tree representation of the heap:'); const root = arrToTree(arr); printTree(root); } diff --git a/en/codes/typescript/modules/TreeNode.ts b/en/codes/typescript/modules/TreeNode.ts index ae854402d..555c60b20 100644 --- a/en/codes/typescript/modules/TreeNode.ts +++ b/en/codes/typescript/modules/TreeNode.ts @@ -8,8 +8,8 @@ class TreeNode { val: number; // Node value height: number; // Node height - left: TreeNode | null; // Left child pointer - right: TreeNode | null; // Right child pointer + left: TreeNode | null; // Pointer to the left child + right: TreeNode | null; // Pointer to the right child constructor( val?: number, height?: number, @@ -23,7 +23,7 @@ class TreeNode { } } -/* Deserialize array to binary tree */ +/* Deserialize an array into a binary tree */ function arrToTree(arr: (number | null)[], i: number = 0): TreeNode | null { if (i < 0 || i >= arr.length || arr[i] === null) { return null; diff --git a/en/codes/typescript/modules/Vertex.ts b/en/codes/typescript/modules/Vertex.ts index 7c1f167dc..9c9b0af78 100644 --- a/en/codes/typescript/modules/Vertex.ts +++ b/en/codes/typescript/modules/Vertex.ts @@ -11,7 +11,7 @@ class Vertex { this.val = val; } - /* Input value list vals, return vertex list vets */ + /* Given the list of values vals, return the list of vertices vets */ public static valsToVets(vals: number[]): Vertex[] { const vets: Vertex[] = []; for (let i = 0; i < vals.length; i++) { @@ -20,7 +20,7 @@ class Vertex { return vets; } - /* Input vertex list vets, return value list vals */ + /* Given the list of vertices vets, return the list of values vals */ public static vetsToVals(vets: Vertex[]): number[] { const vals: number[] = []; for (const vet of vets) { diff --git a/en/codes/zig/.gitignore b/en/codes/zig/.gitignore new file mode 100644 index 000000000..76bab71be --- /dev/null +++ b/en/codes/zig/.gitignore @@ -0,0 +1,4 @@ +zig-out +zig-cache +.zig-cache +!/.vscode/ \ No newline at end of file diff --git a/en/codes/zig/.vscode/launch.json b/en/codes/zig/.vscode/launch.json new file mode 100644 index 000000000..cfeec2388 --- /dev/null +++ b/en/codes/zig/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/zig-out/bin/${fileBasenameNoExtension}", + "args": [], + "cwd": "${workspaceFolder}", + "preLaunchTask": "build" + } + ] +} \ No newline at end of file diff --git a/en/codes/zig/.vscode/settings.json b/en/codes/zig/.vscode/settings.json new file mode 100644 index 000000000..a1ccb9aa3 --- /dev/null +++ b/en/codes/zig/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "zig.testArgs": ["build", "test", "-Dtest-filter=${filter}"] +} \ No newline at end of file diff --git a/en/codes/zig/.vscode/tasks.json b/en/codes/zig/.vscode/tasks.json new file mode 100644 index 000000000..a66ac10e2 --- /dev/null +++ b/en/codes/zig/.vscode/tasks.json @@ -0,0 +1,10 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "zig build", + } + ] +} \ No newline at end of file diff --git a/en/codes/zig/build.zig b/en/codes/zig/build.zig new file mode 100644 index 000000000..80d306495 --- /dev/null +++ b/en/codes/zig/build.zig @@ -0,0 +1,169 @@ +// File: build.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +//! Zig Version: 0.14.1 +//! Build Command: zig build +//! Run Command: zig build run | zig build run_* +//! Test Command: zig build test | zig build test -Dtest-filter=* + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const chapters = [_][]const u8{ + "chapter_computational_complexity", + "chapter_array_and_linkedlist", + "chapter_stack_and_queue", + "chapter_hashing", + "chapter_tree", + "chapter_heap", + "chapter_searching", + "chapter_sorting", + "chapter_dynamic_programming", + }; + + const test_step = b.step("test", "Run unit tests"); + const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{}; + + buildChapterExeModules(b, target, optimize, &chapters, test_step, test_filters); + buildMainExeModule(b, target, optimize); +} + +fn buildChapterExeModules( + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + chapter_dirs: []const []const u8, + test_step: *std.Build.Step, + test_filters: []const []const u8, +) void { + for (chapter_dirs) |chapter_dir_name| { + const chapter_dir_path = std.fs.path.join(b.allocator, &[_][]const u8{chapter_dir_name}) catch continue; + var chapter_dir = std.fs.cwd().openDir(chapter_dir_path, .{ .iterate = true }) catch continue; + defer chapter_dir.close(); + + var it = chapter_dir.iterate(); + while (it.next() catch continue) |chapter_dir_entry| { + if (chapter_dir_entry.kind != .file or !std.mem.endsWith(u8, chapter_dir_entry.name, ".zig")) continue; + const exe_mod = buildExeModuleFromChapterDirEntry(b, target, optimize, chapter_dir_name, chapter_dir_entry) catch continue; + addTestStepToExeModule(b, test_step, exe_mod, test_filters); + } + } +} + +fn buildExeModuleFromChapterDirEntry( + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + chapter_dir_name: []const u8, + chapter_dir_entry: std.fs.Dir.Entry, +) !*std.Build.Module { + const zig_file_path = try std.fs.path.join(b.allocator, &[_][]const u8{ chapter_dir_name, chapter_dir_entry.name }); + const zig_file_name = chapter_dir_entry.name[0 .. chapter_dir_entry.name.len - 4]; // Extract the base name from xxx.zig + + // Temporarily enable only the updated examples listed below; enable the rest once they have been migrated + const new_algo_names = [_][]const u8{ + "array", + "linked_list", + "list", + "my_list", + "iteration", + "recursion", + "space_complexity", + "time_complexity", + "worst_best_time_complexity", + }; + var can_run = false; + for (new_algo_names) |name| { + if (std.mem.eql(u8, zig_file_name, name)) { + can_run = true; + } + } + if (!can_run) { + return error.CanNotRunUseOldZigCodes; + } + + // std.debug.print("now run zig file name = {s}\n", .{zig_file_name}); + + const exe_mod = b.createModule(.{ + .root_source_file = b.path(zig_file_path), + .target = target, + .optimize = optimize, + }); + + const exe = b.addExecutable(.{ + .name = zig_file_name, + .root_module = exe_mod, + }); + + const utils_mod = createUtilsModule(b, target, optimize); + exe_mod.addImport("utils", utils_mod); + + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const step_name = try std.fmt.allocPrint(b.allocator, "run_{s}", .{zig_file_name}); + const step_desc = try std.fmt.allocPrint(b.allocator, "Run {s}/{s}.zig", .{ chapter_dir_name, zig_file_name }); + const run_step = b.step(step_name, step_desc); + run_step.dependOn(&run_cmd.step); + + return exe_mod; +} + +fn buildMainExeModule( + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, +) void { + const exe_mod = b.createModule(.{ + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = optimize, + }); + + const utils_mod = createUtilsModule(b, target, optimize); + exe_mod.addImport("utils", utils_mod); + + const exe = b.addExecutable(.{ + .name = "main", + .root_module = exe_mod, + }); + + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run all enabled Hello Algo Zig examples"); + run_step.dependOn(&run_cmd.step); +} + +fn createUtilsModule(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + const utils_mod = b.createModule(.{ + .root_source_file = b.path("utils/utils.zig"), + .target = target, + .optimize = optimize, + }); + return utils_mod; +} + +fn addTestStepToExeModule(b: *std.Build, test_step: *std.Build.Step, exe_mod: *std.Build.Module, test_filters: []const []const u8) void { + const exe_unit_tests = b.addTest(.{ + .root_module = exe_mod, + .filters = test_filters, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/en/codes/zig/chapter_array_and_linkedlist/array.zig b/en/codes/zig/chapter_array_and_linkedlist/array.zig new file mode 100644 index 000000000..ec1782d1d --- /dev/null +++ b/en/codes/zig/chapter_array_and_linkedlist/array.zig @@ -0,0 +1,131 @@ +// File: array.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const utils = @import("utils"); + +// Access an element at a random index +pub fn randomAccess(nums: []const i32) i32 { + // Choose a random integer in the interval [0, nums.len) + const random_index = std.crypto.random.intRangeLessThan(usize, 0, nums.len); + // Get and return the randomly selected element + const randomNum = nums[random_index]; + return randomNum; +} + +// Extend the array length +pub fn extend(allocator: std.mem.Allocator, nums: []const i32, enlarge: usize) ![]i32 { + // Initialize an array with the extended length + const res = try allocator.alloc(i32, nums.len + enlarge); + @memset(res, 0); + + // Copy all elements from the original array to the new array + std.mem.copyForwards(i32, res, nums); + + // Return the new, extended array + return res; +} + +// Insert element num at index `index` in the array +pub fn insert(nums: []i32, num: i32, index: usize) void { + // Shift all elements at and after index `index` one position to the right + var i = nums.len - 1; + while (i > index) : (i -= 1) { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index `index` + nums[index] = num; +} + +// Remove the element at index `index` +pub fn remove(nums: []i32, index: usize) void { + // Shift all elements after index `index` one position to the left + var i = index; + while (i < nums.len - 1) : (i += 1) { + nums[i] = nums[i + 1]; + } +} + +// Traverse the array +pub fn traverse(nums: []const i32) void { + var count: i32 = 0; + + // Traverse the array by index + var i: usize = 0; + while (i < nums.len) : (i += 1) { + count += nums[i]; + } + + // Iterate directly over the array elements + count = 0; + for (nums) |num| { + count += num; + } + + // Iterate over both the indices and the elements + for (nums, 0..) |num, index| { + count += nums[index]; + count += num; + } +} + +// Find a specified element in the array +pub fn find(nums: []i32, target: i32) i32 { + for (nums, 0..) |num, i| { + if (num == target) return @intCast(i); + } + return -1; +} + +// Driver code +pub fn run() !void { + // Initialize the array + const arr = [_]i32{0} ** 5; + std.debug.print("Array arr = {}\n", .{utils.fmt.slice(&arr)}); + + // Array slice + var array = [_]i32{ 1, 3, 2, 5, 4 }; + var known_at_runtime_zero: usize = 0; + _ = &known_at_runtime_zero; + var nums = array[known_at_runtime_zero..array.len]; // Use the runtime variable known_at_runtime_zero to obtain a slice from the array pointer + std.debug.print("Array nums = {}\n", .{utils.fmt.slice(nums)}); + + // Random access + const randomNum = randomAccess(nums); + std.debug.print("Random element from nums: {}\n", .{randomNum}); + + // Initialize the allocator + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + // Extend the length + nums = try extend(allocator, nums, 3); + std.debug.print("Extend the array length to 8: nums = {}\n", .{utils.fmt.slice(nums)}); + + // Insert an element + insert(nums, 6, 3); + std.debug.print("Insert 6 at index 3: nums = {}\n", .{utils.fmt.slice(nums)}); + + // Remove an element + remove(nums, 2); + std.debug.print("Remove the element at index 2: nums = {}\n", .{utils.fmt.slice(nums)}); + + // Traverse the array + traverse(nums); + + // Find an element + const index = find(nums, 3); + std.debug.print("Find 3 in nums: index = {}\n", .{index}); + + std.debug.print("\n", .{}); +} + +pub fn main() !void { + try run(); +} + +test "array" { + try run(); +} diff --git a/en/codes/zig/chapter_array_and_linkedlist/linked_list.zig b/en/codes/zig/chapter_array_and_linkedlist/linked_list.zig new file mode 100644 index 000000000..7cd6bc975 --- /dev/null +++ b/en/codes/zig/chapter_array_and_linkedlist/linked_list.zig @@ -0,0 +1,106 @@ +// File: linked_list.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const utils = @import("utils"); +const ListNode = utils.ListNode; + +// Insert node P after node n0 in the linked list +pub fn insert(comptime T: type, n0: *ListNode(T), P: *ListNode(T)) void { + const n1 = n0.next; + P.next = n1; + n0.next = P; +} + +// Remove the first node after node n0 in the linked list +pub fn remove(comptime T: type, n0: *ListNode(T)) void { + // n0 -> P -> n1 => n0 -> n1 + const P = n0.next; + const n1 = P.?.next; + n0.next = n1; +} + +// Access the node at index `index` in the linked list +pub fn access(comptime T: type, node: *ListNode(T), index: i32) ?*ListNode(T) { + var head: ?*ListNode(T) = node; + var i: i32 = 0; + while (i < index) : (i += 1) { + if (head) |cur| { + head = cur.next; + } else { + return null; + } + } + return head; +} + +// Find the first node with value target in the linked list +pub fn find(comptime T: type, node: *ListNode(T), target: T) i32 { + var head: ?*ListNode(T) = node; + var index: i32 = 0; + while (head) |cur| { + if (cur.val == target) return index; + head = cur.next; + index += 1; + } + return -1; +} + +// Driver code +pub fn run() void { + // Initialize the individual nodes + var n0 = ListNode(i32){ .val = 1 }; + var n1 = ListNode(i32){ .val = 3 }; + var n2 = ListNode(i32){ .val = 2 }; + var n3 = ListNode(i32){ .val = 5 }; + var n4 = ListNode(i32){ .val = 4 }; + // Set up references between the nodes + n0.next = &n1; + n1.next = &n2; + n2.next = &n3; + n3.next = &n4; + std.debug.print( + "Initial linked list: {}\n", + .{utils.fmt.linkedList(i32, &n0)}, + ); + + // Insert a node + var tmp = ListNode(i32){ .val = 0 }; + insert(i32, &n0, &tmp); + std.debug.print( + "Linked list after inserting a node: {}\n", + .{utils.fmt.linkedList(i32, &n0)}, + ); + + // Remove a node + remove(i32, &n0); + std.debug.print( + "Linked list after removing a node: {}\n", + .{utils.fmt.linkedList(i32, &n0)}, + ); + + // Access a node + const node = access(i32, &n0, 3); + std.debug.print( + "Value of the linked-list node at index 3 = {}\n", + .{node.?.val}, + ); + + // Find a node + const index = find(i32, &n0, 2); + std.debug.print( + "Index of the linked-list node with value 2 = {}\n", + .{index}, + ); + + std.debug.print("\n", .{}); +} + +pub fn main() void { + run(); +} + +test "linked_list" { + run(); +} diff --git a/en/codes/zig/chapter_array_and_linkedlist/list.zig b/en/codes/zig/chapter_array_and_linkedlist/list.zig new file mode 100644 index 000000000..ad69389b8 --- /dev/null +++ b/en/codes/zig/chapter_array_and_linkedlist/list.zig @@ -0,0 +1,78 @@ +// File: list.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const utils = @import("utils"); + +// Driver code +pub fn run() !void { + // Initialize the list + var nums = std.ArrayList(i32).init(std.heap.page_allocator); + defer nums.deinit(); // Defer releasing the memory + + try nums.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 }); + std.debug.print("List nums = {}\n", .{utils.fmt.slice(nums.items)}); + + // Access an element + const num = nums.items[1]; + std.debug.print("Access the element at index 1: num = {}\n", .{num}); + + // Update an element + nums.items[1] = 0; + std.debug.print("Update the element at index 1 to 0: nums = {}\n", .{utils.fmt.slice(nums.items)}); + + // Clear the list + nums.clearRetainingCapacity(); + std.debug.print("After clearing the list: nums = {}\n", .{utils.fmt.slice(nums.items)}); + + // Append an element at the end + try nums.append(1); + try nums.append(3); + try nums.append(2); + try nums.append(5); + try nums.append(4); + std.debug.print("After appending elements: nums = {}\n", .{utils.fmt.slice(nums.items)}); + + // Insert an element in the middle + try nums.insert(3, 6); + std.debug.print("Insert 6 at index 3: nums = {}\n", .{utils.fmt.slice(nums.items)}); + + // Remove an element + _ = nums.orderedRemove(3); + std.debug.print("Remove the element at index 3: nums = {}\n", .{utils.fmt.slice(nums.items)}); + + // Traverse the list by index + var count: i32 = 0; + var i: usize = 0; + while (i < nums.items.len) : (i += 1) { + count += nums.items[i]; + } + + // Iterate directly over the list elements + count = 0; + for (nums.items) |x| { + count += x; + } + + // 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); + std.debug.print("Append nums1 to nums: nums = {}\n", .{utils.fmt.slice(nums.items)}); + + // Sort the list + std.mem.sort(i32, nums.items, {}, comptime std.sort.asc(i32)); + std.debug.print("After sorting the list: nums = {}\n", .{utils.fmt.slice(nums.items)}); + + std.debug.print("\n", .{}); +} + +pub fn main() !void { + try run(); +} + +test "list" { + try run(); +} diff --git a/en/codes/zig/chapter_array_and_linkedlist/my_list.zig b/en/codes/zig/chapter_array_and_linkedlist/my_list.zig new file mode 100644 index 000000000..979000c2d --- /dev/null +++ b/en/codes/zig/chapter_array_and_linkedlist/my_list.zig @@ -0,0 +1,233 @@ +// File: my_list.zig +// Created Time: 2023-01-08 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const utils = @import("utils"); + +// List type +const MyList = struct { + const Self = @This(); + + items: []i32, // Slice storing the list elements + capacity: usize, // List capacity + allocator: std.mem.Allocator, // Allocator + + extend_ratio: usize = 2, // Factor by which the list capacity grows each time + + // Initialize an empty list with an allocator + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .items = &[_]i32{}, + .capacity = 0, + .allocator = allocator, + }; + } + + // Deinitialize the list and release its memory + pub fn deinit(self: Self) void { + self.allocator.free(self.allocatedSlice()); + } + + // Append an element at the end + pub fn add(self: *Self, item: i32) !void { + // Grow the capacity when adding an element would exceed it + const newlen = self.items.len + 1; + try self.ensureTotalCapacity(newlen); + + // Update an element + self.items.len += 1; + const new_item_ptr = &self.items[self.items.len - 1]; + new_item_ptr.* = item; + } + + // Get the list length (current number of elements) + pub fn getSize(self: *Self) usize { + return self.items.len; + } + + // Get the list capacity + pub fn getCapacity(self: *Self) usize { + return self.capacity; + } + + // Access an element + pub fn get(self: *Self, index: usize) i32 { + // Panic if the index is out of bounds; the same applies below + if (index < 0 or index >= self.items.len) { + @panic("Index out of bounds"); + } + return self.items[index]; + } + + // Update an element + pub fn set(self: *Self, index: usize, num: i32) void { + // Panic if the index is out of bounds; the same applies below + if (index < 0 or index >= self.items.len) { + @panic("Index out of bounds"); + } + self.items[index] = num; + } + + // Insert an element in the middle + pub fn insert(self: *Self, index: usize, item: i32) !void { + if (index < 0 or index >= self.items.len) { + @panic("Index out of bounds"); + } + + // Grow the capacity when adding an element would exceed it + const newlen = self.items.len + 1; + try self.ensureTotalCapacity(newlen); + + // Shift all elements at and after index `index` one position to the right + self.items.len += 1; + var i = self.items.len - 1; + while (i >= index) : (i -= 1) { + self.items[i] = self.items[i - 1]; + } + self.items[index] = item; + } + + // Remove an element + pub fn remove(self: *Self, index: usize) i32 { + if (index < 0 or index >= self.getSize()) { + @panic("Index out of bounds"); + } + // Shift all elements after index `index` one position to the left + const item = self.items[index]; + var i = index; + while (i < self.items.len - 1) : (i += 1) { + self.items[i] = self.items[i + 1]; + } + self.items.len -= 1; + // Return the removed element + return item; + } + + // Return the list elements as a slice + pub fn toArraySlice(self: *Self) ![]i32 { + return self.toOwnedSlice(false); + } + + // Return a slice of the elements, optionally resetting the list + pub fn toOwnedSlice(self: *Self, clear: bool) ![]i32 { + const allocator = self.allocator; + const old_memory = self.allocatedSlice(); + if (allocator.remap(old_memory, self.items.len)) |new_items| { + if (clear) { + self.* = init(allocator); + } + return new_items; + } + + const new_memory = try allocator.alloc(i32, self.items.len); + @memcpy(new_memory, self.items); + if (clear) { + self.clearAndFree(); + } + return new_memory; + } + + // Grow the list capacity + fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void { + if (self.capacity >= new_capacity) return; + const capcacity = if (self.capacity == 0) 10 else self.capacity; + const better_capacity = capcacity * self.extend_ratio; + + const old_memory = self.allocatedSlice(); + if (self.allocator.remap(old_memory, better_capacity)) |new_memory| { + self.items.ptr = new_memory.ptr; + self.capacity = new_memory.len; + } else { + const new_memory = try self.allocator.alloc(i32, better_capacity); + @memcpy(new_memory[0..self.items.len], self.items); + self.allocator.free(old_memory); + self.items.ptr = new_memory.ptr; + self.capacity = new_memory.len; + } + } + + fn clearAndFree(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.allocatedSlice()); + self.items.len = 0; + self.capacity = 0; + } + + fn allocatedSlice(self: Self) []i32 { + return self.items.ptr[0..self.capacity]; + } +}; + +// Driver code +pub fn run() !void { + var gpa = std.heap.DebugAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + // Initialize the list + var nums = MyList.init(allocator); + // Defer releasing the memory + defer nums.deinit(); + + // Append an element at the end + try nums.add(1); + try nums.add(3); + try nums.add(2); + try nums.add(5); + try nums.add(4); + std.debug.print("List nums = {}, capacity = {}, length = {}\n", .{ + utils.fmt.slice(nums.items), + nums.getCapacity(), + nums.getSize(), + }); + + // Insert an element in the middle + try nums.insert(3, 6); + std.debug.print( + "Insert 6 at index 3: nums = {}\n", + .{utils.fmt.slice(nums.items)}, + ); + + // Remove an element + _ = nums.remove(3); + std.debug.print( + "Remove the element at index 3: nums = {}\n", + .{utils.fmt.slice(nums.items)}, + ); + + // Access an element + const num = nums.get(1); + std.debug.print("Access the element at index 1: num = {}\n", .{num}); + + // Update an element + nums.set(1, 0); + std.debug.print( + "Update the element at index 1 to 0: nums = {}\n", + .{utils.fmt.slice(nums.items)}, + ); + + // Test capacity growth + var i: i32 = 0; + while (i < 10) : (i += 1) { + // The initial capacity is 20 in this implementation, so these appends do not trigger further growth + try nums.add(i); + } + std.debug.print( + "After appending: list nums = {}, capacity = {}, length = {}\n", + .{ + utils.fmt.slice(nums.items), + nums.getCapacity(), + nums.getSize(), + }, + ); + + std.debug.print("\n", .{}); +} + +pub fn main() !void { + try run(); +} + +test "my_list" { + try run(); +} diff --git a/en/codes/zig/chapter_computational_complexity/iteration.zig b/en/codes/zig/chapter_computational_complexity/iteration.zig new file mode 100644 index 000000000..3993af869 --- /dev/null +++ b/en/codes/zig/chapter_computational_complexity/iteration.zig @@ -0,0 +1,91 @@ +// File: iteration.zig +// Created Time: 2023-09-27 +// Author: QiLOL (pikaqqpika@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +// for loop +fn forLoop(n: usize) i32 { + var res: i32 = 0; + // Sum 1, 2, ..., n-1, n in a loop + for (1..n + 1) |i| { + res += @intCast(i); + } + return res; +} + +// while loop +fn whileLoop(n: i32) i32 { + var res: i32 = 0; + var i: i32 = 1; // Initialize the loop control variable + // Sum 1, 2, ..., n-1, n in a loop + while (i <= n) : (i += 1) { + res += @intCast(i); + } + return res; +} + +// while loop (two updates) +fn whileLoopII(n: i32) i32 { + var res: i32 = 0; + var i: i32 = 1; // Initialize the loop control variable + // Sum 1, 4, 10, ... in a loop + while (i <= n) : ({ + // Update the loop control variable + i += 1; + i *= 2; + }) { + res += @intCast(i); + } + return res; +} + +// Nested for loops +fn nestedForLoop(allocator: Allocator, n: usize) ![]const u8 { + var res = std.ArrayList(u8).init(allocator); + defer res.deinit(); + var buffer: [20]u8 = undefined; + // Loop over i = 1, 2, ..., n-1, n + for (1..n + 1) |i| { + // Loop over j = 1, 2, ..., n-1, n + for (1..n + 1) |j| { + const str = try std.fmt.bufPrint(&buffer, "({d}, {d}), ", .{ i, j }); + try res.appendSlice(str); + } + } + return res.toOwnedSlice(); +} + +// Driver code +pub fn run() !void { + var gpa = std.heap.DebugAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + const n: i32 = 5; + var res: i32 = 0; + + res = forLoop(n); + std.debug.print("Sum computed by the for loop: res = {}\n", .{res}); + + res = whileLoop(n); + std.debug.print("Sum computed by the while loop: res = {}\n", .{res}); + + res = whileLoopII(n); + std.debug.print("Sum computed by the while loop with two updates: res = {}\n", .{res}); + + const resStr = try nestedForLoop(allocator, n); + std.debug.print("Nested for-loop traversal: {s}\n", .{resStr}); + allocator.free(resStr); + + std.debug.print("\n", .{}); +} + +pub fn main() !void { + try run(); +} + +test "interation" { + try run(); +} diff --git a/en/codes/zig/chapter_computational_complexity/recursion.zig b/en/codes/zig/chapter_computational_complexity/recursion.zig new file mode 100644 index 000000000..eb377a9fe --- /dev/null +++ b/en/codes/zig/chapter_computational_complexity/recursion.zig @@ -0,0 +1,88 @@ +// File: recursion.zig +// Created Time: 2023-09-27 +// Author: QiLOL (pikaqqpika@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); + +// Recursive function +fn recur(n: i32) i32 { + // Base case + if (n == 1) { + return 1; + } + // Recurse: make a recursive call + const res = recur(n - 1); + // Unwind: return the result + return n + res; +} + +// Simulate recursion using iteration +fn forLoopRecur(comptime n: i32) i32 { + // Use an explicit stack to simulate the system call stack + var stack: [n]i32 = undefined; + var res: i32 = 0; + // Simulate recursive descent by filling the explicit stack + var i: usize = n; + while (i > 0) { + stack[i - 1] = @intCast(i); + i -= 1; + } + // Simulate unwinding by accumulating the stored values + var index: usize = n; + while (index > 0) { + index -= 1; + res += stack[index]; + } + // res = 1+2+3+...+n + return res; +} + +// Tail-recursive function +fn tailRecur(n: i32, res: i32) i32 { + // Base case + if (n == 0) { + return res; + } + // Tail-recursive call + return tailRecur(n - 1, res + n); +} + +// Fibonacci sequence +fn fib(n: i32) i32 { + // Base cases: f(1) = 0, f(2) = 1 + if (n == 1 or n == 2) { + return n - 1; + } + // Recursive calls: f(n) = f(n-1) + f(n-2) + const res: i32 = fib(n - 1) + fib(n - 2); + // Return the result f(n) + return res; +} + +// Driver code +pub fn run() void { + const n: i32 = 5; + var res: i32 = 0; + + res = recur(n); + std.debug.print("Sum computed by recursion: res = {}\n", .{recur(n)}); + + res = forLoopRecur(n); + std.debug.print("Sum computed by simulating recursion with iteration: res = {}\n", .{forLoopRecur(n)}); + + res = tailRecur(n, 0); + std.debug.print("Sum computed by tail recursion: res = {}\n", .{tailRecur(n, 0)}); + + res = fib(n); + std.debug.print("Term {} of the Fibonacci sequence is {}\n", .{ n, fib(n) }); + + std.debug.print("\n", .{}); +} + +pub fn main() void { + run(); +} + +test "recursion" { + run(); +} diff --git a/en/codes/zig/chapter_computational_complexity/space_complexity.zig b/en/codes/zig/chapter_computational_complexity/space_complexity.zig new file mode 100644 index 000000000..652640cba --- /dev/null +++ b/en/codes/zig/chapter_computational_complexity/space_complexity.zig @@ -0,0 +1,142 @@ +// File: space_complexity.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const utils = @import("utils"); +const ListNode = utils.ListNode; +const TreeNode = utils.TreeNode; + +// Function +fn function() i32 { + // Perform some operations + return 0; +} + +// Constant complexity +fn constant(n: i32) void { + // Constants, variables, and objects occupy O(1) space + const a: i32 = 0; + const b: i32 = 0; + const nums = [_]i32{0} ** 10000; + const node = ListNode(i32){ .val = 0 }; + var i: i32 = 0; + // Variables in the loop occupy O(1) space + while (i < n) : (i += 1) { + const c: i32 = 0; + _ = c; + } + // Function calls in the loop occupy O(1) space + i = 0; + while (i < n) : (i += 1) { + _ = function(); + } + _ = a; + _ = b; + _ = nums; + _ = node; +} + +// Linear complexity +fn linear(comptime n: i32) !void { + // An array of length n uses O(n) space + const nums = [_]i32{0} ** n; + // A list of length n occupies O(n) space + var nodes = std.ArrayList(i32).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + try nodes.append(i); + } + // A hash table containing n entries would occupy O(n) space + var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator); + defer map.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + const string = try std.fmt.allocPrint(std.heap.page_allocator, "{d}", .{j}); + defer std.heap.page_allocator.free(string); + try map.put(i, string); + } + _ = nums; +} + +// Linear complexity (recursive implementation) +fn linearRecur(comptime n: i32) void { + std.debug.print("Recursion: n = {}\n", .{n}); + if (n == 1) return; + linearRecur(n - 1); +} + +// Quadratic complexity +fn quadratic(n: i32) !void { + // A two-dimensional list retaining n by n elements would occupy O(n^2) space + var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + var tmp = std.ArrayList(i32).init(std.heap.page_allocator); + defer tmp.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + try tmp.append(0); + } + try nodes.append(tmp); + } +} + +// Quadratic complexity (recursive implementation) +fn quadraticRecur(comptime n: i32) i32 { + if (n <= 0) return 0; + const nums = [_]i32{0} ** n; + std.debug.print("In the recursive call with n = {}, nums has length {}\n", .{ n, nums.len }); + return quadraticRecur(n - 1); +} + +// Exponential complexity (building a perfect binary tree) +fn buildTree(allocator: std.mem.Allocator, n: i32) !?*TreeNode(i32) { + if (n == 0) return null; + const root = try allocator.create(TreeNode(i32)); + root.init(0); + root.left = try buildTree(allocator, n - 1); + root.right = try buildTree(allocator, n - 1); + return root; +} + +// Free the tree's memory +fn freeTree(allocator: std.mem.Allocator, root: ?*const TreeNode(i32)) void { + if (root == null) return; + freeTree(allocator, root.?.left); + freeTree(allocator, root.?.right); + allocator.destroy(root.?); +} + +// Driver code +pub fn run() !void { + var gpa = std.heap.DebugAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + const n: i32 = 5; + // Constant complexity + constant(n); + // Linear complexity + try linear(n); + linearRecur(n); + // Quadratic complexity + try quadratic(n); + _ = quadraticRecur(n); + // Exponential complexity + const root = try buildTree(allocator, n); + defer freeTree(allocator, root); + std.debug.print("{}\n", .{utils.fmt.tree(i32, root)}); + + std.debug.print("\n", .{}); +} + +pub fn main() !void { + try run(); +} + +test "space_complexity" { + try run(); +} diff --git a/en/codes/zig/chapter_computational_complexity/time_complexity.zig b/en/codes/zig/chapter_computational_complexity/time_complexity.zig new file mode 100644 index 000000000..d32b91e38 --- /dev/null +++ b/en/codes/zig/chapter_computational_complexity/time_complexity.zig @@ -0,0 +1,184 @@ +// File: time_complexity.zig +// Created Time: 2022-12-28 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); + +// Constant complexity +fn constant(n: i32) i32 { + _ = n; + var count: i32 = 0; + const size: i32 = 100_000; + var i: i32 = 0; + while (i < size) : (i += 1) { + count += 1; + } + return count; +} + +// Linear complexity +fn linear(n: i32) i32 { + var count: i32 = 0; + var i: i32 = 0; + while (i < n) : (i += 1) { + count += 1; + } + return count; +} + +// Linear complexity (array traversal) +fn arrayTraversal(nums: []i32) i32 { + var count: i32 = 0; + // The number of iterations is proportional to the array length + for (nums) |_| { + count += 1; + } + return count; +} + +// Quadratic complexity +fn quadratic(n: i32) i32 { + var count: i32 = 0; + var i: i32 = 0; + // The number of iterations is proportional to the square of the input size n + while (i < n) : (i += 1) { + var j: i32 = 0; + while (j < n) : (j += 1) { + count += 1; + } + } + return count; +} + +// Quadratic complexity (bubble sort) +fn bubbleSort(nums: []i32) i32 { + var count: i32 = 0; // Counter + // Outer loop: the unsorted range is [0, i] + var i: i32 = @as(i32, @intCast(nums.len)) - 1; + while (i > 0) : (i -= 1) { + var j: usize = 0; + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps + while (j < i) : (j += 1) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + const tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // Swapping two elements takes 3 elementary operations + } + } + } + return count; +} + +// Exponential complexity (iterative implementation) +fn exponential(n: i32) i32 { + var count: i32 = 0; + var bas: i32 = 1; + var i: i32 = 0; + // Each cell divides into two per round, giving the sequence 1, 2, 4, 8, ..., 2^(n-1) + while (i < n) : (i += 1) { + var j: i32 = 0; + while (j < bas) : (j += 1) { + count += 1; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +// Exponential complexity (recursive implementation) +fn expRecur(n: i32) i32 { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +// Logarithmic complexity (iterative implementation) +fn logarithmic(n: i32) i32 { + var count: i32 = 0; + var n_var: i32 = n; + while (n_var > 1) : (n_var = @divTrunc(n_var, 2)) { + count += 1; + } + return count; +} + +// Logarithmic complexity (recursive implementation) +fn logRecur(n: i32) i32 { + if (n <= 1) return 0; + return logRecur(@divTrunc(n, 2)) + 1; +} + +// Linearithmic complexity +fn linearLogRecur(n: i32) i32 { + if (n <= 1) return 1; + var count: i32 = linearLogRecur(@divTrunc(n, 2)) + linearLogRecur(@divTrunc(n, 2)); + var i: i32 = 0; + while (i < n) : (i += 1) { + count += 1; + } + return count; +} + +// Factorial complexity (recursive implementation) +fn factorialRecur(n: i32) i32 { + if (n == 0) return 1; + var count: i32 = 0; + var i: i32 = 0; + // Split one problem into n subproblems + while (i < n) : (i += 1) { + count += factorialRecur(n - 1); + } + return count; +} + +// Driver code +pub fn run() void { + // Try different values of n to see how the operation count grows for each complexity + const n: i32 = 8; + std.debug.print("Input size n = {}\n", .{n}); + + var count = constant(n); + std.debug.print("Constant-time operation count = {}\n", .{count}); + + count = linear(n); + std.debug.print("Linear-time operation count = {}\n", .{count}); + var nums = [_]i32{0} ** n; + count = arrayTraversal(&nums); + std.debug.print("Linear-time operation count (array traversal) = {}\n", .{count}); + + count = quadratic(n); + std.debug.print("Quadratic-time operation count = {}\n", .{count}); + for (&nums, 0..) |*num, i| { + num.* = n - @as(i32, @intCast(i)); // [n,n-1,...,2,1] + } + count = bubbleSort(&nums); + std.debug.print("Quadratic-time operation count (bubble sort) = {}\n", .{count}); + + count = exponential(n); + std.debug.print("Exponential-time operation count (iterative) = {}\n", .{count}); + count = expRecur(n); + std.debug.print("Exponential-time operation count (recursive) = {}\n", .{count}); + + count = logarithmic(n); + std.debug.print("Logarithmic-time operation count (iterative) = {}\n", .{count}); + count = logRecur(n); + std.debug.print("Logarithmic-time operation count (recursive) = {}\n", .{count}); + + count = linearLogRecur(n); + std.debug.print("Linearithmic-time operation count (recursive) = {}\n", .{count}); + + count = factorialRecur(n); + std.debug.print("Factorial-time operation count (recursive) = {}\n", .{count}); + + std.debug.print("\n", .{}); +} + +pub fn main() !void { + run(); +} + +test "time_complexity" { + run(); +} diff --git a/en/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig b/en/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig new file mode 100644 index 000000000..cdf2f6fec --- /dev/null +++ b/en/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig @@ -0,0 +1,53 @@ +// File: worst_best_time_complexity.zig +// Created Time: 2022-12-28 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const utils = @import("utils"); + +// Generate an array containing { 1, 2, ..., n } in shuffled order +pub fn randomNumbers(comptime n: usize) [n]i32 { + var nums: [n]i32 = undefined; + // Generate the array nums = { 1, 2, 3, ..., n } + for (&nums, 0..) |*num, i| { + num.* = @as(i32, @intCast(i)) + 1; + } + // Randomly shuffle the array elements + const rand = std.crypto.random; + rand.shuffle(i32, &nums); + return nums; +} + +// Find the index of the number 1 in array nums +pub fn findOne(nums: []i32) i32 { + for (nums, 0..) |num, i| { + // When 1 is at the start of the array, the best-case time complexity is O(1) + // When 1 is at the end of the array, the worst-case time complexity is O(n) + if (num == 1) return @intCast(i); + } + return -1; +} + +// Driver code +pub fn run() void { + var i: i32 = 0; + while (i < 10) : (i += 1) { + const n: usize = 100; + var nums = randomNumbers(n); + const index = findOne(&nums); + std.debug.print("Shuffled array [1, 2, ..., n] = ", .{}); + std.debug.print("{}\n", .{utils.fmt.slice(nums)}); + + std.debug.print("Index of 1: {}\n", .{index}); + } + + std.debug.print("\n", .{}); +} + +pub fn main() !void { + run(); +} + +test "worst_best_time_complexity" { + run(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/climbing_stairs_backtrack.zig b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_backtrack.zig new file mode 100644 index 000000000..e697ebcdb --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_backtrack.zig @@ -0,0 +1,44 @@ +// File: climbing_stairs_backtrack.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Backtracking +fn backtrack(choices: []i32, state: i32, n: i32, res: std.ArrayList(i32)) void { + // When step n is reached, increase the number of ways by 1 + if (state == n) { + res.items[0] = res.items[0] + 1; + } + // Iterate over all choices + for (choices) |choice| { + // Prune: do not go beyond step n + if (state + choice > n) { + continue; + } + // Try: make a choice and update the state + backtrack(choices, state + choice, n, res); + // Backtrack + } +} + +// Climbing stairs: backtracking +fn climbingStairsBacktrack(n: usize) !i32 { + var choices = [_]i32{ 1, 2 }; // Choose to climb 1 or 2 steps + var state: i32 = 0; // Start climbing from step 0 + var res = std.ArrayList(i32).init(std.heap.page_allocator); + defer res.deinit(); + try res.append(0); // Use res.items[0] to record the number of solutions + backtrack(&choices, state, @intCast(n), res); + return res.items[0]; +} + +// Driver code +pub fn main() !void { + var n: usize = 9; + + var res = try climbingStairsBacktrack(n); + std.debug.print("Ways to climb {} stairs: {}\n", .{ n, res }); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/climbing_stairs_constraint_dp.zig b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_constraint_dp.zig new file mode 100644 index 000000000..c750f2fe7 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_constraint_dp.zig @@ -0,0 +1,35 @@ +// File: climbing_stairs_constraint_dp.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Climbing stairs with constraints: dynamic programming +fn climbingStairsConstraintDP(comptime n: usize) i32 { + if (n == 1 or n == 2) { + return 1; + } + // Initialize the dp table to store subproblem solutions + var dp = [_][3]i32{ [_]i32{ -1, -1, -1 } } ** (n + 1); + // Initial states: set the solutions to the smallest subproblems + dp[1][1] = 1; + dp[1][2] = 0; + dp[2][1] = 0; + dp[2][2] = 1; + // State transitions: solve progressively larger subproblems using smaller ones + for (3..n + 1) |i| { + dp[i][1] = dp[i - 1][2]; + dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; + } + return dp[n][1] + dp[n][2]; +} + +// Driver code +pub fn main() !void { + comptime var n: usize = 9; + + var res = climbingStairsConstraintDP(n); + std.debug.print("Ways to climb {} stairs: {}\n", .{ n, res }); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dfs.zig b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dfs.zig new file mode 100644 index 000000000..600c3fde1 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dfs.zig @@ -0,0 +1,31 @@ +// File: climbing_stairs_dfs.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Search +fn dfs(i: usize) i32 { + // The values of dp[1] and dp[2] are known; return them + if (i == 1 or i == 2) { + return @intCast(i); + } + // dp[i] = dp[i-1] + dp[i-2] + var count = dfs(i - 1) + dfs(i - 2); + return count; +} + +// Climbing stairs: search +fn climbingStairsDFS(comptime n: usize) i32 { + return dfs(n); +} + +// Driver code +pub fn main() !void { + comptime var n: usize = 9; + + var res = climbingStairsDFS(n); + std.debug.print("Ways to climb {} stairs: {}\n", .{ n, res }); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dfs_mem.zig b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dfs_mem.zig new file mode 100644 index 000000000..dd9f7ed78 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dfs_mem.zig @@ -0,0 +1,39 @@ +// File: climbing_stairs_dfs_mem.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Memoized search +fn dfs(i: usize, mem: []i32) i32 { + // The values of dp[1] and dp[2] are known; return them + if (i == 1 or i == 2) { + return @intCast(i); + } + // If dp[i] has been recorded, return it directly + if (mem[i] != -1) { + return mem[i]; + } + // dp[i] = dp[i-1] + dp[i-2] + var count = dfs(i - 1, mem) + dfs(i - 2, mem); + // Record dp[i] + mem[i] = count; + return count; +} + +// Climbing stairs: memoized search +fn climbingStairsDFSMem(comptime n: usize) i32 { + // mem[i] stores the number of ways to reach step i; -1 means no result has been recorded + var mem = [_]i32{ -1 } ** (n + 1); + return dfs(n, &mem); +} + +// Driver code +pub fn main() !void { + comptime var n: usize = 9; + + var res = climbingStairsDFSMem(n); + std.debug.print("Ways to climb {} stairs: {}\n", .{ n, res }); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dp.zig b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dp.zig new file mode 100644 index 000000000..8a0596622 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/climbing_stairs_dp.zig @@ -0,0 +1,51 @@ +// File: climbing_stairs_dp.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Climbing stairs: dynamic programming +fn climbingStairsDP(comptime n: usize) i32 { + // The values of dp[1] and dp[2] are known; return them + if (n == 1 or n == 2) { + return @intCast(n); + } + // Initialize the dp table to store subproblem solutions + var dp = [_]i32{-1} ** (n + 1); + // Initial states: set the solutions to the smallest subproblems + dp[1] = 1; + dp[2] = 2; + // State transitions: solve progressively larger subproblems using smaller ones + for (3..n + 1) |i| { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} + +// Climbing stairs: space-optimized dynamic programming +fn climbingStairsDPComp(comptime n: usize) i32 { + if (n == 1 or n == 2) { + return @intCast(n); + } + var a: i32 = 1; + var b: i32 = 2; + for (3..n + 1) |_| { + var tmp = b; + b = a + b; + a = tmp; + } + return b; +} + +// Driver code +pub fn main() !void { + comptime var n: usize = 9; + + var res = climbingStairsDP(n); + std.debug.print("Ways to climb {} stairs: {}\n", .{ n, res }); + + res = climbingStairsDPComp(n); + std.debug.print("Ways to climb {} stairs: {}\n", .{ n, res }); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/coin_change.zig b/en/codes/zig/chapter_dynamic_programming/coin_change.zig new file mode 100644 index 000000000..522ce42a1 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/coin_change.zig @@ -0,0 +1,77 @@ +// File: coin_change.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Coin change: dynamic programming +fn coinChangeDP(comptime coins: []i32, comptime amt: usize) i32 { + comptime var n = coins.len; + comptime var max = amt + 1; + // Initialize the dp table + var dp = [_][amt + 1]i32{[_]i32{0} ** (amt + 1)} ** (n + 1); + // State transitions: first row and first column + for (1..amt + 1) |a| { + dp[0][a] = max; + } + // State transitions: remaining rows and columns + for (1..n + 1) |i| { + for (1..amt + 1) |a| { + if (coins[i - 1] > @as(i32, @intCast(a))) { + // If the coin exceeds the target amount, do not choose coin i + dp[i][a] = dp[i - 1][a]; + } else { + // Take the smaller result of not choosing and choosing coin i + dp[i][a] = @min(dp[i - 1][a], dp[i][a - @as(usize, @intCast(coins[i - 1]))] + 1); + } + } + } + if (dp[n][amt] != max) { + return @intCast(dp[n][amt]); + } else { + return -1; + } +} + +// Coin change: space-optimized dynamic programming +fn coinChangeDPComp(comptime coins: []i32, comptime amt: usize) i32 { + comptime var n = coins.len; + comptime var max = amt + 1; + // Initialize the dp table + var dp = [_]i32{0} ** (amt + 1); + @memset(&dp, max); + dp[0] = 0; + // State transitions + for (1..n + 1) |i| { + for (1..amt + 1) |a| { + if (coins[i - 1] > @as(i32, @intCast(a))) { + // If the coin exceeds the target amount, do not choose coin i + dp[a] = dp[a]; + } else { + // Take the smaller result of not choosing and choosing coin i + dp[a] = @min(dp[a], dp[a - @as(usize, @intCast(coins[i - 1]))] + 1); + } + } + } + if (dp[amt] != max) { + return @intCast(dp[amt]); + } else { + return -1; + } +} + +// Driver code +pub fn main() !void { + comptime var coins = [_]i32{ 1, 2, 5 }; + comptime var amt: usize = 4; + + // Dynamic programming + var res = coinChangeDP(&coins, amt); + std.debug.print("Minimum number of coins needed for the target amount: {}\n", .{res}); + + // Space-optimized dynamic programming + res = coinChangeDPComp(&coins, amt); + std.debug.print("Minimum number of coins needed for the target amount: {}\n", .{res}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/coin_change_ii.zig b/en/codes/zig/chapter_dynamic_programming/coin_change_ii.zig new file mode 100644 index 000000000..3f3145d9b --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/coin_change_ii.zig @@ -0,0 +1,66 @@ +// File: coin_change_ii.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Coin change II: dynamic programming +fn coinChangeIIDP(comptime coins: []i32, comptime amt: usize) i32 { + comptime var n = coins.len; + // Initialize the dp table + var dp = [_][amt + 1]i32{[_]i32{0} ** (amt + 1)} ** (n + 1); + // Initialize the first column + for (0..n + 1) |i| { + dp[i][0] = 1; + } + // State transitions + for (1..n + 1) |i| { + for (1..amt + 1) |a| { + if (coins[i - 1] > @as(i32, @intCast(a))) { + // If the coin exceeds the target amount, do not choose coin i + dp[i][a] = dp[i - 1][a]; + } else { + // Add the counts for excluding coin i and including coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - @as(usize, @intCast(coins[i - 1]))]; + } + } + } + return dp[n][amt]; +} + +// Coin change II: space-optimized dynamic programming +fn coinChangeIIDPComp(comptime coins: []i32, comptime amt: usize) i32 { + comptime var n = coins.len; + // Initialize the dp table + var dp = [_]i32{0} ** (amt + 1); + dp[0] = 1; + // State transitions + for (1..n + 1) |i| { + for (1..amt + 1) |a| { + if (coins[i - 1] > @as(i32, @intCast(a))) { + // If the coin exceeds the target amount, do not choose coin i + dp[a] = dp[a]; + } else { + // Add the counts for excluding coin i and including coin i + dp[a] = dp[a] + dp[a - @as(usize, @intCast(coins[i - 1]))]; + } + } + } + return dp[amt]; +} + +// Driver code +pub fn main() !void { + comptime var coins = [_]i32{ 1, 2, 5 }; + comptime var amt: usize = 5; + + // Dynamic programming + var res = coinChangeIIDP(&coins, amt); + std.debug.print("Number of coin combinations for the target amount: {}\n", .{res}); + + // Space-optimized dynamic programming + res = coinChangeIIDPComp(&coins, amt); + std.debug.print("Number of coin combinations for the target amount: {}\n", .{res}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/edit_distance.zig b/en/codes/zig/chapter_dynamic_programming/edit_distance.zig new file mode 100644 index 000000000..29cfc5c16 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/edit_distance.zig @@ -0,0 +1,146 @@ +// File: edit_distance.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Edit distance: brute-force search +fn editDistanceDFS(comptime s: []const u8, comptime t: []const u8, i: usize, j: usize) i32 { + // If both prefixes s[0..i] and t[0..j] are empty, return 0 + if (i == 0 and j == 0) { + return 0; + } + // If the prefix s[0..i] is empty, return the length j of the other prefix + if (i == 0) { + return @intCast(j); + } + // If the prefix t[0..j] is empty, return the length i of the other prefix + if (j == 0) { + return @intCast(i); + } + // If the two characters are equal, skip both of them + if (s[i - 1] == t[j - 1]) { + return editDistanceDFS(s, t, i - 1, j - 1); + } + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 + var insert = editDistanceDFS(s, t, i, j - 1); + var delete = editDistanceDFS(s, t, i - 1, j); + var replace = editDistanceDFS(s, t, i - 1, j - 1); + // Return the minimum number of edits + return @min(@min(insert, delete), replace) + 1; +} + +// Edit distance: memoized search +fn editDistanceDFSMem(comptime s: []const u8, comptime t: []const u8, mem: anytype, i: usize, j: usize) i32 { + // If both prefixes s[0..i] and t[0..j] are empty, return 0 + if (i == 0 and j == 0) { + return 0; + } + // If the prefix s[0..i] is empty, return the length j of the other prefix + if (i == 0) { + return @intCast(j); + } + // If the prefix t[0..j] is empty, return the length i of the other prefix + if (j == 0) { + return @intCast(i); + } + // If a result has already been recorded, return it directly + if (mem[i][j] != -1) { + return mem[i][j]; + } + // If the two characters are equal, skip both of them + if (s[i - 1] == t[j - 1]) { + return editDistanceDFSMem(s, t, mem, i - 1, j - 1); + } + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 + var insert = editDistanceDFSMem(s, t, mem, i, j - 1); + var delete = editDistanceDFSMem(s, t, mem, i - 1, j); + var replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); + // Record and return the minimum number of edits + mem[i][j] = @min(@min(insert, delete), replace) + 1; + return mem[i][j]; +} + +// Edit distance: dynamic programming +fn editDistanceDP(comptime s: []const u8, comptime t: []const u8) i32 { + comptime var n = s.len; + comptime var m = t.len; + var dp = [_][m + 1]i32{[_]i32{0} ** (m + 1)} ** (n + 1); + // State transitions: first row and first column + for (1..n + 1) |i| { + dp[i][0] = @intCast(i); + } + for (1..m + 1) |j| { + dp[0][j] = @intCast(j); + } + // State transitions: remaining rows and columns + for (1..n + 1) |i| { + for (1..m + 1) |j| { + if (s[i - 1] == t[j - 1]) { + // If the two characters are equal, skip both of them + dp[i][j] = dp[i - 1][j - 1]; + } else { + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 + dp[i][j] = @min(@min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + } + } + } + return dp[n][m]; +} + +// Edit distance: space-optimized dynamic programming +fn editDistanceDPComp(comptime s: []const u8, comptime t: []const u8) i32 { + comptime var n = s.len; + comptime var m = t.len; + var dp = [_]i32{0} ** (m + 1); + // State transitions: first row + for (1..m + 1) |j| { + dp[j] = @intCast(j); + } + // State transitions: remaining rows + for (1..n + 1) |i| { + // State transitions: first column + var leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = @intCast(i); + // State transitions: remaining columns + for (1..m + 1) |j| { + var temp = dp[j]; + if (s[i - 1] == t[j - 1]) { + // If the two characters are equal, skip both of them + dp[j] = leftup; + } else { + // Minimum edits = the minimum among the insertion, deletion, and replacement subproblems + 1 + dp[j] = @min(@min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // Update to dp[i-1, j-1] for the next iteration + } + } + return dp[m]; +} + +// Driver code +pub fn main() !void { + const s = "bag"; + const t = "pack"; + comptime var n = s.len; + comptime var m = t.len; + + // Brute-force search + var res = editDistanceDFS(s, t, n, m); + std.debug.print("Changing {s} to {s} requires at least {} edits\n", .{ s, t, res }); + + // Memoized search + var mem = [_][m + 1]i32{[_]i32{-1} ** (m + 1)} ** (n + 1); + res = editDistanceDFSMem(s, t, @constCast(&mem), n, m); + std.debug.print("Changing {s} to {s} requires at least {} edits\n", .{ s, t, res }); + + // Dynamic programming + res = editDistanceDP(s, t); + std.debug.print("Changing {s} to {s} requires at least {} edits\n", .{ s, t, res }); + + // Space-optimized dynamic programming + res = editDistanceDPComp(s, t); + std.debug.print("Changing {s} to {s} requires at least {} edits\n", .{ s, t, res }); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/knapsack.zig b/en/codes/zig/chapter_dynamic_programming/knapsack.zig new file mode 100644 index 000000000..f24d45c42 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/knapsack.zig @@ -0,0 +1,110 @@ +// File: knapsack.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// 0-1 knapsack: brute-force search +fn knapsackDFS(wgt: []i32, val: []i32, i: usize, c: usize) i32 { + // If all items have been considered or no capacity remains, return value 0 + if (i == 0 or c == 0) { + return 0; + } + // If the item exceeds the remaining capacity, it must be excluded + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, val, i - 1, c); + } + // Calculate the maximum values obtained by excluding and including item i + var no = knapsackDFS(wgt, val, i - 1, c); + var yes = knapsackDFS(wgt, val, i - 1, c - @as(usize, @intCast(wgt[i - 1]))) + val[i - 1]; + // Return the larger value from the two choices + return @max(no, yes); +} + +// 0-1 knapsack: memoized search +fn knapsackDFSMem(wgt: []i32, val: []i32, mem: anytype, i: usize, c: usize) i32 { + // If all items have been considered or no capacity remains, return value 0 + if (i == 0 or c == 0) { + return 0; + } + // If a result has already been recorded, return it directly + if (mem[i][c] != -1) { + return mem[i][c]; + } + // If the item exceeds the remaining capacity, it must be excluded + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, val, mem, i - 1, c); + } + // Calculate the maximum values obtained by excluding and including item i + var no = knapsackDFSMem(wgt, val, mem, i - 1, c); + var yes = knapsackDFSMem(wgt, val, mem, i - 1, c - @as(usize, @intCast(wgt[i - 1]))) + val[i - 1]; + // Record and return the larger value from the two choices + mem[i][c] = @max(no, yes); + return mem[i][c]; +} + +// 0-1 knapsack: dynamic programming +fn knapsackDP(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 { + comptime var n = wgt.len; + // Initialize the dp table + var dp = [_][cap + 1]i32{[_]i32{0} ** (cap + 1)} ** (n + 1); + // State transitions + for (1..n + 1) |i| { + for (1..cap + 1) |c| { + if (wgt[i - 1] > c) { + // If item i exceeds the remaining capacity, do not choose it + dp[i][c] = dp[i - 1][c]; + } else { + // Take the larger value obtained by excluding and including item i + dp[i][c] = @max(dp[i - 1][c], dp[i - 1][c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +// 0-1 knapsack: space-optimized dynamic programming +fn knapsackDPComp(wgt: []i32, val: []i32, comptime cap: usize) i32 { + var n = wgt.len; + // Initialize the dp table + var dp = [_]i32{0} ** (cap + 1); + // State transitions + for (1..n + 1) |i| { + // Traverse in decreasing order + var c = cap; + while (c > 0) : (c -= 1) { + if (wgt[i - 1] < c) { + // Take the larger value obtained by excluding and including item i + dp[c] = @max(dp[c], dp[c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); + } + } + } + return dp[cap]; +} + +// Driver code +pub fn main() !void { + comptime var wgt = [_]i32{ 10, 20, 30, 40, 50 }; + comptime var val = [_]i32{ 50, 120, 150, 210, 240 }; + comptime var cap = 50; + comptime var n = wgt.len; + + // Brute-force search + var res = knapsackDFS(&wgt, &val, n, cap); + std.debug.print("Maximum total item value within the knapsack capacity: {}\n", .{res}); + + // Memoized search + var mem = [_][cap + 1]i32{[_]i32{-1} ** (cap + 1)} ** (n + 1); + res = knapsackDFSMem(&wgt, &val, @constCast(&mem), n, cap); + std.debug.print("Maximum total item value within the knapsack capacity: {}\n", .{res}); + + // Dynamic programming + res = knapsackDP(&wgt, &val, cap); + std.debug.print("Maximum total item value within the knapsack capacity: {}\n", .{res}); + + // Space-optimized dynamic programming + res = knapsackDPComp(&wgt, &val, cap); + std.debug.print("Maximum total item value within the knapsack capacity: {}\n", .{res}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/min_cost_climbing_stairs_dp.zig b/en/codes/zig/chapter_dynamic_programming/min_cost_climbing_stairs_dp.zig new file mode 100644 index 000000000..ef97e478b --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/min_cost_climbing_stairs_dp.zig @@ -0,0 +1,54 @@ +// File: min_cost_climbing_stairs_dp.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Minimum cost to climb stairs: dynamic programming +fn minCostClimbingStairsDP(comptime cost: []i32) i32 { + comptime var n = cost.len - 1; + if (n == 1 or n == 2) { + return cost[n]; + } + // Initialize the dp table to store subproblem solutions + var dp = [_]i32{-1} ** (n + 1); + // Initial states: set the solutions to the smallest subproblems + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transitions: solve progressively larger subproblems using smaller ones + for (3..n + 1) |i| { + dp[i] = @min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return dp[n]; +} + +// Minimum cost to climb stairs: space-optimized dynamic programming +fn minCostClimbingStairsDPComp(cost: []i32) i32 { + var n = cost.len - 1; + if (n == 1 or n == 2) { + return cost[n]; + } + var a = cost[1]; + var b = cost[2]; + // State transitions: solve progressively larger subproblems using smaller ones + for (3..n + 1) |i| { + var tmp = b; + b = @min(a, tmp) + cost[i]; + a = tmp; + } + return b; +} + +// Driver code +pub fn main() !void { + comptime var cost = [_]i32{ 0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1 }; + std.debug.print("Input stair costs: {any}\n", .{cost}); + + var res = minCostClimbingStairsDP(&cost); + std.debug.print("Minimum cost to climb the stairs: {}\n", .{res}); + + res = minCostClimbingStairsDPComp(&cost); + std.debug.print("Minimum cost to climb the stairs: {}\n", .{res}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/min_path_sum.zig b/en/codes/zig/chapter_dynamic_programming/min_path_sum.zig new file mode 100644 index 000000000..261edbf40 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/min_path_sum.zig @@ -0,0 +1,122 @@ +// File: min_path_sum.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Minimum path sum: brute-force search +fn minPathSumDFS(grid: anytype, i: i32, j: i32) i32 { + // Stop searching at the top-left cell + if (i == 0 and j == 0) { + return grid[0][0]; + } + // If a row or column index is out of bounds, use the maximum i32 value to represent infinite cost + if (i < 0 or j < 0) { + return std.math.maxInt(i32); + } + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) + var up = minPathSumDFS(grid, i - 1, j); + var left = minPathSumDFS(grid, i, j - 1); + // Return the minimum path cost from the top-left corner to (i, j) + return @min(left, up) + grid[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; +} + +// Minimum path sum: memoized search +fn minPathSumDFSMem(grid: anytype, mem: anytype, i: i32, j: i32) i32 { + // Stop searching at the top-left cell + if (i == 0 and j == 0) { + return grid[0][0]; + } + // If a row or column index is out of bounds, use the maximum i32 value to represent infinite cost + if (i < 0 or j < 0) { + return std.math.maxInt(i32); + } + // If a result has already been recorded, return it directly + if (mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))] != -1) { + return mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; + } + // Calculate the minimum path costs from the top-left corner to (i-1, j) and (i, j-1) + var up = minPathSumDFSMem(grid, mem, i - 1, j); + var left = minPathSumDFSMem(grid, mem, i, j - 1); + // Return the minimum path cost from the top-left corner to (i, j) + // Record and return the minimum path cost from the top-left corner to (i, j) + mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))] = @min(left, up) + grid[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; + return mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; +} + +// Minimum path sum: dynamic programming +fn minPathSumDP(comptime grid: anytype) i32 { + comptime var n = grid.len; + comptime var m = grid[0].len; + // Initialize the dp table + var dp = [_][m]i32{[_]i32{0} ** m} ** n; + dp[0][0] = grid[0][0]; + // State transitions: first row + for (1..m) |j| { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // State transitions: first column + for (1..n) |i| { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // State transitions: remaining rows and columns + for (1..n) |i| { + for (1..m) |j| { + dp[i][j] = @min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[n - 1][m - 1]; +} + +// Minimum path sum: space-optimized dynamic programming +fn minPathSumDPComp(comptime grid: anytype) i32 { + comptime var n = grid.len; + comptime var m = grid[0].len; + // Initialize the dp table + var dp = [_]i32{0} ** m; + // State transitions: first row + dp[0] = grid[0][0]; + for (1..m) |j| { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transitions: remaining rows + for (1..n) |i| { + // State transitions: first column + dp[0] = dp[0] + grid[i][0]; + for (1..m) |j| { + dp[j] = @min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; +} + +// Driver code +pub fn main() !void { + comptime var grid = [_][4]i32{ + [_]i32{ 1, 3, 1, 5 }, + [_]i32{ 2, 2, 4, 2 }, + [_]i32{ 5, 3, 2, 1 }, + [_]i32{ 4, 3, 5, 2 }, + }; + comptime var n = grid.len; + comptime var m = grid[0].len; + + // Brute-force search + var res = minPathSumDFS(&grid, n - 1, m - 1); + std.debug.print("Minimum path sum from the top-left to the bottom-right corner: {}\n", .{res}); + + // Memoized search + var mem = [_][m]i32{[_]i32{-1} ** m} ** n; + res = minPathSumDFSMem(&grid, &mem, n - 1, m - 1); + std.debug.print("Minimum path sum from the top-left to the bottom-right corner: {}\n", .{res}); + + // Dynamic programming + res = minPathSumDP(&grid); + std.debug.print("Minimum path sum from the top-left to the bottom-right corner: {}\n", .{res}); + + // Space-optimized dynamic programming + res = minPathSumDPComp(&grid); + std.debug.print("Minimum path sum from the top-left to the bottom-right corner: {}\n", .{res}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_dynamic_programming/unbounded_knapsack.zig b/en/codes/zig/chapter_dynamic_programming/unbounded_knapsack.zig new file mode 100644 index 000000000..2cc32f819 --- /dev/null +++ b/en/codes/zig/chapter_dynamic_programming/unbounded_knapsack.zig @@ -0,0 +1,62 @@ +// File: unbounded_knapsack.zig +// Created Time: 2023-07-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); + +// Unbounded knapsack: dynamic programming +fn unboundedKnapsackDP(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 { + comptime var n = wgt.len; + // Initialize the dp table + var dp = [_][cap + 1]i32{[_]i32{0} ** (cap + 1)} ** (n + 1); + // State transitions + for (1..n + 1) |i| { + for (1..cap + 1) |c| { + if (wgt[i - 1] > c) { + // If item i exceeds the remaining capacity, do not choose it + dp[i][c] = dp[i - 1][c]; + } else { + // Take the larger value obtained by excluding and including item i + dp[i][c] = @max(dp[i - 1][c], dp[i][c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +// Unbounded knapsack: space-optimized dynamic programming +fn unboundedKnapsackDPComp(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 { + comptime var n = wgt.len; + // Initialize the dp table + var dp = [_]i32{0} ** (cap + 1); + // State transitions + for (1..n + 1) |i| { + for (1..cap + 1) |c| { + if (wgt[i - 1] > c) { + // If item i exceeds the remaining capacity, do not choose it + dp[c] = dp[c]; + } else { + // Take the larger value obtained by excluding and including item i + dp[c] = @max(dp[c], dp[c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); + } + } + } + return dp[cap]; +} + +// Driver code +pub fn main() !void { + comptime var wgt = [_]i32{ 1, 2, 3 }; + comptime var val = [_]i32{ 5, 11, 15 }; + comptime var cap = 4; + + // Dynamic programming + var res = unboundedKnapsackDP(&wgt, &val, cap); + std.debug.print("Maximum total item value within the knapsack capacity: {}\n", .{res}); + + // Space-optimized dynamic programming + res = unboundedKnapsackDPComp(&wgt, &val, cap); + std.debug.print("Maximum total item value within the knapsack capacity: {}\n", .{res}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_hashing/array_hash_map.zig b/en/codes/zig/chapter_hashing/array_hash_map.zig new file mode 100644 index 000000000..69f3585b3 --- /dev/null +++ b/en/codes/zig/chapter_hashing/array_hash_map.zig @@ -0,0 +1,162 @@ +// File: array_hash_map.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Key-value pair +const Pair = struct { + key: usize = undefined, + val: []const u8 = undefined, + + pub fn init(key: usize, val: []const u8) Pair { + return Pair { + .key = key, + .val = val, + }; + } +}; + +// Hash table implemented with an array +pub fn ArrayHashMap(comptime T: type) type { + return struct { + bucket: ?std.ArrayList(?T) = null, + mem_allocator: std.mem.Allocator = undefined, + + const Self = @This(); + + // Initialize the hash map + pub fn init(self: *Self, allocator: std.mem.Allocator) !void { + self.mem_allocator = allocator; + // Initialize the bucket array with 100 slots + self.bucket = std.ArrayList(?T).init(self.mem_allocator); + var i: i32 = 0; + while (i < 100) : (i += 1) { + try self.bucket.?.append(null); + } + } + + // Deinitialize the hash map + pub fn deinit(self: *Self) void { + if (self.bucket != null) self.bucket.?.deinit(); + } + + // Hash function + fn hashFunc(key: usize) usize { + var index = key % 100; + return index; + } + + // Lookup operation + pub fn get(self: *Self, key: usize) []const u8 { + var index = hashFunc(key); + var pair = self.bucket.?.items[index]; + return pair.?.val; + } + + // Insert operation + pub fn put(self: *Self, key: usize, val: []const u8) !void { + var pair = Pair.init(key, val); + var index = hashFunc(key); + self.bucket.?.items[index] = pair; + } + + // Remove operation + pub fn remove(self: *Self, key: usize) !void { + var index = hashFunc(key); + // Set the slot to null to mark it as removed + self.bucket.?.items[index] = null; + } + + // Get all key-value pairs + pub fn pairSet(self: *Self) !std.ArrayList(T) { + var entry_set = std.ArrayList(T).init(self.mem_allocator); + for (self.bucket.?.items) |item| { + if (item == null) continue; + try entry_set.append(item.?); + } + return entry_set; + } + + // Get all keys + pub fn keySet(self: *Self) !std.ArrayList(usize) { + var key_set = std.ArrayList(usize).init(self.mem_allocator); + for (self.bucket.?.items) |item| { + if (item == null) continue; + try key_set.append(item.?.key); + } + return key_set; + } + + // Get all values + pub fn valueSet(self: *Self) !std.ArrayList([]const u8) { + var value_set = std.ArrayList([]const u8).init(self.mem_allocator); + for (self.bucket.?.items) |item| { + if (item == null) continue; + try value_set.append(item.?.val); + } + return value_set; + } + + // Print the hash table + pub fn print(self: *Self) !void { + var entry_set = try self.pairSet(); + defer entry_set.deinit(); + for (entry_set.items) |item| { + std.debug.print("{} -> {s}\n", .{item.key, item.val}); + } + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the hash table + var map = ArrayHashMap(Pair){}; + try map.init(std.heap.page_allocator); + defer map.deinit(); + + // Insert operation + // Insert the key-value pair (key, value) into the hash table + try map.put(12836, "小哈"); + try map.put(15937, "小啰"); + try map.put(16750, "小算"); + try map.put(13276, "小法"); + try map.put(10583, "小鸭"); + std.debug.print("\nHash table after insertion:\nKey -> Value\n", .{}); + try map.print(); + + // Lookup operation + // Look up key in the hash table to obtain value + var name = map.get(15937); + std.debug.print("\nLook up student ID 15937: name = {s}\n", .{name}); + + // Remove operation + // Remove the key-value pair (key, value) from the hash table + try map.remove(10583); + std.debug.print("\nHash table after removing 10583:\nKey -> Value\n", .{}); + try map.print(); + + // Traverse the hash table + std.debug.print("\nIterate over key-value pairs, Key->Value\n", .{}); + var entry_set = try map.pairSet(); + for (entry_set.items) |kv| { + std.debug.print("{} -> {s}\n", .{kv.key, kv.val}); + } + defer entry_set.deinit(); + std.debug.print("\nIterate over keys only, Key\n", .{}); + var key_set = try map.keySet(); + for (key_set.items) |key| { + std.debug.print("{}\n", .{key}); + } + defer key_set.deinit(); + std.debug.print("\nIterate over the values only\n", .{}); + var value_set = try map.valueSet(); + for (value_set.items) |val| { + std.debug.print("{s}\n", .{val}); + } + defer value_set.deinit(); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_hashing/hash_map.zig b/en/codes/zig/chapter_hashing/hash_map.zig new file mode 100644 index 000000000..12b7b3007 --- /dev/null +++ b/en/codes/zig/chapter_hashing/hash_map.zig @@ -0,0 +1,54 @@ +// File: hash_map.zig +// Created Time: 2023-01-13 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Driver code +pub fn main() !void { + // Initialize the hash table + var map = std.AutoHashMap(i32, []const u8).init(std.heap.page_allocator); + // Defer releasing the memory + defer map.deinit(); + + // Insert operation + // Insert the key-value pair (key, value) into the hash table + try map.put(12836, "小哈"); + try map.put(15937, "小啰"); + try map.put(16750, "小算"); + try map.put(13276, "小法"); + try map.put(10583, "小鸭"); + std.debug.print("\nHash table after insertion:\nKey -> Value\n", .{}); + inc.PrintUtil.printHashMap(i32, []const u8, map); + + // Lookup operation + // Look up key in the hash table to obtain value + var name = map.get(15937).?; + std.debug.print("\nLook up student ID 15937: name = {s}\n", .{name}); + + // Remove operation + // Remove the key-value pair (key, value) from the hash table + _ = map.remove(10583); + std.debug.print("\nHash table after removing 10583:\nKey -> Value\n", .{}); + inc.PrintUtil.printHashMap(i32, []const u8, map); + + // Traverse the hash table + std.debug.print("\nIterate over key-value pairs, Key->Value\n", .{}); + inc.PrintUtil.printHashMap(i32, []const u8, map); + + std.debug.print("\nIterate over keys only, Key\n", .{}); + var it = map.iterator(); + while (it.next()) |kv| { + std.debug.print("{}\n", .{kv.key_ptr.*}); + } + + std.debug.print("\nIterate over the values only\n", .{}); + it = map.iterator(); + while (it.next()) |kv| { + std.debug.print("{s}\n", .{kv.value_ptr.*}); + } + + _ = try std.io.getStdIn().reader().readByte(); +} + diff --git a/en/codes/zig/chapter_heap/heap.zig b/en/codes/zig/chapter_heap/heap.zig new file mode 100644 index 000000000..2c3fc316c --- /dev/null +++ b/en/codes/zig/chapter_heap/heap.zig @@ -0,0 +1,80 @@ +// File: heap.zig +// Created Time: 2023-01-14 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +fn lessThan(context: void, a: i32, b: i32) std.math.Order { + _ = context; + return std.math.order(a, b); +} + +fn greaterThan(context: void, a: i32, b: i32) std.math.Order { + return lessThan(context, a, b).invert(); +} + +fn testPush(comptime T: type, mem_allocator: std.mem.Allocator, heap: anytype, val: T) !void { + try heap.add(val); //Push an element onto the heap + std.debug.print("\nAfter pushing {} onto the heap\n", .{val}); + try inc.PrintUtil.printHeap(T, mem_allocator, heap); +} + +fn testPop(comptime T: type, mem_allocator: std.mem.Allocator, heap: anytype) !void { + var val = heap.remove(); //Pop the top element from the heap + std.debug.print("\nAfter popping the top element {}\n", .{val}); + try inc.PrintUtil.printHeap(T, mem_allocator, heap); +} + +// Driver code +pub fn main() !void { + // Initialize the allocator + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + + // Initialize the heap + // Initialize a min heap + const PQlt = std.PriorityQueue(i32, void, lessThan); + var min_heap = PQlt.init(std.heap.page_allocator, {}); + defer min_heap.deinit(); + // Initialize a max heap + const PQgt = std.PriorityQueue(i32, void, greaterThan); + var max_heap = PQgt.init(std.heap.page_allocator, {}); + defer max_heap.deinit(); + + std.debug.print("\nThe following example uses a max heap", .{}); + + // Push an element onto the heap + try testPush(i32, mem_allocator, &max_heap, 1); + try testPush(i32, mem_allocator, &max_heap, 3); + try testPush(i32, mem_allocator, &max_heap, 2); + try testPush(i32, mem_allocator, &max_heap, 5); + try testPush(i32, mem_allocator, &max_heap, 4); + + // Get the top element of the heap + var peek = max_heap.peek().?; + std.debug.print("\nHeap top = {}\n", .{peek}); + + // Pop the top element from the heap + try testPop(i32, mem_allocator, &max_heap); + try testPop(i32, mem_allocator, &max_heap); + try testPop(i32, mem_allocator, &max_heap); + try testPop(i32, mem_allocator, &max_heap); + try testPop(i32, mem_allocator, &max_heap); + + // Get the heap size + var size = max_heap.len; + std.debug.print("\nNumber of heap elements = {}\n", .{size}); + + // Check whether the heap is empty + var is_empty = if (max_heap.len == 0) true else false; + std.debug.print("\nIs the heap empty? {}\n", .{is_empty}); + + // Build a heap from an input list + try min_heap.addSlice(&[_]i32{ 1, 3, 2, 5, 4 }); + std.debug.print("\nAfter building a min heap from the input list\n", .{}); + try inc.PrintUtil.printHeap(i32, mem_allocator, min_heap); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_heap/my_heap.zig b/en/codes/zig/chapter_heap/my_heap.zig new file mode 100644 index 000000000..6cd4663b3 --- /dev/null +++ b/en/codes/zig/chapter_heap/my_heap.zig @@ -0,0 +1,186 @@ +// File: my_heap.zig +// Created Time: 2023-01-14 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Simple heap implementation +pub fn MaxHeap(comptime T: type) type { + return struct { + const Self = @This(); + + max_heap: ?std.ArrayList(T) = null, // Use a dynamic list so that capacity growth is handled automatically + + // Initialize the heap from the input list + pub fn init(self: *Self, allocator: std.mem.Allocator, nums: []const T) !void { + if (self.max_heap != null) return; + self.max_heap = std.ArrayList(T).init(allocator); + // Place the list elements in the heap without changing their order + try self.max_heap.?.appendSlice(nums); + // Sift down from every non-leaf node + var i: usize = parent(self.size() - 1) + 1; + while (i > 0) : (i -= 1) { + try self.siftDown(i - 1); + } + } + + // Deinitialize and release the memory + pub fn deinit(self: *Self) void { + if (self.max_heap != null) self.max_heap.?.deinit(); + } + + // Get the left child index + fn left(i: usize) usize { + return 2 * i + 1; + } + + // Get the right child index + fn right(i: usize) usize { + return 2 * i + 2; + } + + // Get the parent index + fn parent(i: usize) usize { + // return (i - 1) / 2; // Floor division + return @divFloor(i - 1, 2); + } + + // Swap elements + fn swap(self: *Self, i: usize, j: usize) !void { + var tmp = self.max_heap.?.items[i]; + try self.max_heap.?.replaceRange(i, 1, &[_]T{self.max_heap.?.items[j]}); + try self.max_heap.?.replaceRange(j, 1, &[_]T{tmp}); + } + + // Get the heap size + pub fn size(self: *Self) usize { + return self.max_heap.?.items.len; + } + + // Check whether the heap is empty + pub fn isEmpty(self: *Self) bool { + return self.size() == 0; + } + + // Access the top element of the heap + pub fn peek(self: *Self) T { + return self.max_heap.?.items[0]; + } + + // Push an element onto the heap + pub fn push(self: *Self, val: T) !void { + // Add a node + try self.max_heap.?.append(val); + // Sift up + try self.siftUp(self.size() - 1); + } + + // Sift up starting from node i + fn siftUp(self: *Self, i_: usize) !void { + var i = i_; + while (true) { + // Get the parent of node i + var p = parent(i); + // Stop sifting when the root has been passed or the node needs no repair + if (p < 0 or self.max_heap.?.items[i] <= self.max_heap.?.items[p]) break; + // Swap the two nodes + try self.swap(i, p); + // Continue sifting up + i = p; + } + } + + // Pop an element from the heap + pub fn pop(self: *Self) !T { + // Check for an empty heap + if (self.isEmpty()) unreachable; + // Swap the root with the rightmost leaf on the last level (swap the first and last elements) + try self.swap(0, self.size() - 1); + // Remove a node + var val = self.max_heap.?.pop(); + // Sift down + try self.siftDown(0); + // Return the top element of the heap + return val; + } + + // Sift down starting from node i + fn siftDown(self: *Self, i_: usize) !void { + var i = i_; + while (true) { + // Find the node with the largest value among i, l, and r, and store its index in ma + var l = left(i); + var r = right(i); + var ma = i; + if (l < self.size() and self.max_heap.?.items[l] > self.max_heap.?.items[ma]) ma = l; + if (r < self.size() and self.max_heap.?.items[r] > self.max_heap.?.items[ma]) ma = r; + // Stop if node i is largest or both child indices l and r are out of bounds + if (ma == i) break; + // Swap the two nodes + try self.swap(i, ma); + // Continue sifting down + i = ma; + } + } + + fn lessThan(context: void, a: T, b: T) std.math.Order { + _ = context; + return std.math.order(a, b); + } + + fn greaterThan(context: void, a: T, b: T) std.math.Order { + return lessThan(context, a, b).invert(); + } + + // Print the heap (binary tree) + pub fn print(self: *Self, mem_allocator: std.mem.Allocator) !void { + const PQgt = std.PriorityQueue(T, void, greaterThan); + var queue = PQgt.init(std.heap.page_allocator, {}); + defer queue.deinit(); + try queue.addSlice(self.max_heap.?.items); + try inc.PrintUtil.printHeap(T, mem_allocator, queue); + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the allocator + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + + // Initialize a max heap + var max_heap = MaxHeap(i32){}; + try max_heap.init(std.heap.page_allocator, &[_]i32{ 9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2 }); + defer max_heap.deinit(); + std.debug.print("\nAfter building the heap from the input list\n", .{}); + try max_heap.print(mem_allocator); + + // Get the top element of the heap + var peek = max_heap.peek(); + std.debug.print("\nHeap top = {}\n", .{peek}); + + // Push an element onto the heap + const val = 7; + try max_heap.push(val); + std.debug.print("\nAfter pushing {} onto the heap\n", .{val}); + try max_heap.print(mem_allocator); + + // Pop the top element from the heap + peek = try max_heap.pop(); + std.debug.print("\nAfter popping the top element {}\n", .{peek}); + try max_heap.print(mem_allocator); + + // Get the heap size + var size = max_heap.size(); + std.debug.print("\nNumber of heap elements: {}", .{size}); + + // Check whether the heap is empty + var is_empty = max_heap.isEmpty(); + std.debug.print("\nIs the heap empty? {}\n", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} + diff --git a/en/codes/zig/chapter_searching/binary_search.zig b/en/codes/zig/chapter_searching/binary_search.zig new file mode 100644 index 000000000..0d882ff6d --- /dev/null +++ b/en/codes/zig/chapter_searching/binary_search.zig @@ -0,0 +1,64 @@ +// File: binary_search.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Binary search (closed interval) +fn binarySearch(comptime T: type, nums: std.ArrayList(T), target: T) T { + // Initialize the closed interval [0, n-1], with i and j pointing to the first and last elements + var i: usize = 0; + var j: usize = nums.items.len - 1; + // Loop until the search interval is empty (when i > j) + while (i <= j) { + var m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums.items[m] < target) { // In this case, target must be in [m+1, j] if it exists + i = m + 1; + } else if (nums.items[m] > target) { // In this case, target must be in [i, m-1] if it exists + j = m - 1; + } else { // The target element was found; return its index + return @intCast(m); + } + } + // The target element was not found; return -1 + return -1; +} + +// Binary search (left-closed, right-open interval) +fn binarySearchLCRO(comptime T: type, nums: std.ArrayList(T), target: T) T { + // Initialize the left-closed, right-open interval [0, n), with i at the first element and j one past the last + var i: usize = 0; + var j: usize = nums.items.len; + // Search the half-open interval; it is empty when i = j + while (i <= j) { + var m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums.items[m] < target) { // In this case, target must be in [m+1, j) if it exists + i = m + 1; + } else if (nums.items[m] > target) { // In this case, target must be in [i, m) if it exists + j = m; + } else { // The target element was found; return its index + return @intCast(m); + } + } + // The target element was not found; return -1 + return -1; +} + +// Driver code +pub fn main() !void { + var target: i32 = 6; + var nums = std.ArrayList(i32).init(std.heap.page_allocator); + defer nums.deinit(); + try nums.appendSlice(&[_]i32{ 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }); + + // Binary search (closed interval) + var index = binarySearch(i32, nums, target); + std.debug.print("Index of target element 6 = {}\n", .{index}); + + // Binary search (left-closed, right-open interval) + index = binarySearchLCRO(i32, nums, target); + std.debug.print("Index of target element 6 = {}\n", .{index}); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_searching/hashing_search.zig b/en/codes/zig/chapter_searching/hashing_search.zig new file mode 100644 index 000000000..5d0f7abde --- /dev/null +++ b/en/codes/zig/chapter_searching/hashing_search.zig @@ -0,0 +1,57 @@ +// File: hashing_search.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Hash-based search (array) +fn hashingSearchArray(comptime T: type, map: std.AutoHashMap(T, T), target: T) T { + // Hash table key: target element; value: index + // Return -1 if the hash table does not contain this key + if (map.getKey(target) == null) return -1; + return map.get(target).?; +} + +// Hash-based search (linked list) +fn hashingSearchLinkedList(comptime T: type, map: std.AutoHashMap(T, *inc.ListNode(T)), target: T) ?*inc.ListNode(T) { + // Hash table key: target node value; value: node object + // If the hash table does not contain this key, return null + if (map.getKey(target) == null) return null; + return map.get(target); +} + +// Driver code +pub fn main() !void { + var target: i32 = 3; + + // Hash-based search (array) + var nums = [_]i32{ 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; + // Initialize the hash table + var map = std.AutoHashMap(i32, i32).init(std.heap.page_allocator); + defer map.deinit(); + for (nums, 0..) |num, i| { + try map.put(num, @as(i32, @intCast(i))); // key: element; value: index + } + var index = hashingSearchArray(i32, map, target); + std.debug.print("Index of target element 3 = {}\n", .{index}); + + // Hash-based search (linked list) + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + var head = try inc.ListUtil.arrToLinkedList(i32, mem_allocator, &nums); + // Initialize the hash table + var map1 = std.AutoHashMap(i32, *inc.ListNode(i32)).init(std.heap.page_allocator); + defer map1.deinit(); + while (head != null) { + try map1.put(head.?.val, head.?); + head = head.?.next; + } + var node = hashingSearchLinkedList(i32, map1, target); + std.debug.print("The node object corresponding to target value 3 is ", .{}); + try inc.PrintUtil.printLinkedList(i32, node); + + _ = try std.io.getStdIn().reader().readByte(); +} + diff --git a/en/codes/zig/chapter_searching/linear_search.zig b/en/codes/zig/chapter_searching/linear_search.zig new file mode 100644 index 000000000..395c27ce4 --- /dev/null +++ b/en/codes/zig/chapter_searching/linear_search.zig @@ -0,0 +1,54 @@ +// File: linear_search.zig +// Created Time: 2023-01-13 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Linear search (array) +fn linearSearchArray(comptime T: type, nums: std.ArrayList(T), target: T) T { + // Traverse the array + for (nums.items, 0..) |num, i| { + // If the target element is found, return its index + if (num == target) { + return @intCast(i); + } + } + // The target element was not found; return -1 + return -1; +} + +// Linear search (linked list) +pub fn linearSearchLinkedList(comptime T: type, node: ?*inc.ListNode(T), target: T) ?*inc.ListNode(T) { + var head = node; + // Traverse the linked list + while (head != null) { + // The target node was found; return it + if (head.?.val == target) return head; + head = head.?.next; + } + return null; +} + +// Driver code +pub fn main() !void { + var target: i32 = 3; + + // Perform linear search on the array + var nums = std.ArrayList(i32).init(std.heap.page_allocator); + defer nums.deinit(); + try nums.appendSlice(&[_]i32{ 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }); + var index = linearSearchArray(i32, nums, target); + std.debug.print("Index of target element 3 = {}\n", .{index}); + + // Perform linear search on the linked list + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + var head = try inc.ListUtil.listToLinkedList(i32, mem_allocator, nums); + var node = linearSearchLinkedList(i32, head, target); + std.debug.print("The node object corresponding to target value 3 is ", .{}); + try inc.PrintUtil.printLinkedList(i32, node); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_searching/two_sum.zig b/en/codes/zig/chapter_searching/two_sum.zig new file mode 100644 index 000000000..4c10d9abd --- /dev/null +++ b/en/codes/zig/chapter_searching/two_sum.zig @@ -0,0 +1,58 @@ +// File: two_sum.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Method 1: brute-force enumeration +pub fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 { + var size: usize = nums.len; + var i: usize = 0; + // Two nested loops give a time complexity of O(n^2) + while (i < size - 1) : (i += 1) { + var j = i + 1; + while (j < size) : (j += 1) { + if (nums[i] + nums[j] == target) { + return [_]i32{@intCast(i), @intCast(j)}; + } + } + } + return null; +} + +// Method 2: auxiliary hash table +pub fn twoSumHashTable(nums: []i32, target: i32) !?[2]i32 { + var size: usize = nums.len; + // The auxiliary hash table uses O(n) space + var dic = std.AutoHashMap(i32, i32).init(std.heap.page_allocator); + defer dic.deinit(); + var i: usize = 0; + // A single loop gives a time complexity of O(n) + while (i < size) : (i += 1) { + if (dic.contains(target - nums[i])) { + return [_]i32{dic.get(target - nums[i]).?, @intCast(i)}; + } + try dic.put(nums[i], @intCast(i)); + } + return null; +} + + +pub fn main() !void { + // ======= Test case ======= + var nums = [_]i32{ 2, 7, 11, 15 }; + var target: i32 = 9; + + // ====== Driver code ====== + // Method 1 + var res = twoSumBruteForce(&nums, target).?; + std.debug.print("Method 1: res = ", .{}); + inc.PrintUtil.printArray(i32, &res); + // Method 2 + res = (try twoSumHashTable(&nums, target)).?; + std.debug.print("\nMethod 2 res = ", .{}); + inc.PrintUtil.printArray(i32, &res); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_sorting/bubble_sort.zig b/en/codes/zig/chapter_sorting/bubble_sort.zig new file mode 100644 index 000000000..d6c1e7f4a --- /dev/null +++ b/en/codes/zig/chapter_sorting/bubble_sort.zig @@ -0,0 +1,61 @@ +// File: bubble_sort.zig +// Created Time: 2023-01-08 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Bubble sort +fn bubbleSort(nums: []i32) void { + // Outer loop: the unsorted range is [0, i] + var i: usize = nums.len - 1; + while (i > 0) : (i -= 1) { + var j: usize = 0; + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps + while (j < i) : (j += 1) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + var tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } +} + +// Bubble sort (flag optimization) +fn bubbleSortWithFlag(nums: []i32) void { + // Outer loop: the unsorted range is [0, i] + var i: usize = nums.len - 1; + while (i > 0) : (i -= 1) { + var flag = false; // Initialize the flag + var j: usize = 0; + // Inner loop: move the largest element in the unsorted range [0, i] to its right end through swaps + while (j < i) : (j += 1) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + var tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; + } + } + if (!flag) break; // Exit immediately if this pass made no swaps + } +} + +// Driver code +pub fn main() !void { + var nums = [_]i32{ 4, 1, 3, 1, 5, 2 }; + bubbleSort(&nums); + std.debug.print("After bubble sort, nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + var nums1 = [_]i32{ 4, 1, 3, 1, 5, 2 }; + bubbleSortWithFlag(&nums1); + std.debug.print("\nAfter bubble sort, nums1 = ", .{}); + inc.PrintUtil.printArray(i32, &nums1); + + _ = try std.io.getStdIn().reader().readByte(); +} + diff --git a/en/codes/zig/chapter_sorting/insertion_sort.zig b/en/codes/zig/chapter_sorting/insertion_sort.zig new file mode 100644 index 000000000..10c463254 --- /dev/null +++ b/en/codes/zig/chapter_sorting/insertion_sort.zig @@ -0,0 +1,31 @@ +// File: insertion_sort.zig +// Created Time: 2023-01-08 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Insertion sort +fn insertionSort(nums: []i32) void { + // Outer loop: the sorted range is [0, i-1] + var i: usize = 1; + while (i < nums.len) : (i += 1) { + var base = nums[i]; + var j: usize = i; + // Inner loop: insert base at the correct position in the sorted range [0, i-1] + while (j >= 1 and nums[j - 1] > base) : (j -= 1) { + nums[j] = nums[j - 1]; // Move nums[j - 1] one position to the right + } + nums[j] = base; // Place base at the correct position + } +} + +// Driver code +pub fn main() !void { + var nums = [_]i32{ 4, 1, 3, 1, 5, 2 }; + insertionSort(&nums); + std.debug.print("After insertion sort, nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_sorting/merge_sort.zig b/en/codes/zig/chapter_sorting/merge_sort.zig new file mode 100644 index 000000000..c3c491771 --- /dev/null +++ b/en/codes/zig/chapter_sorting/merge_sort.zig @@ -0,0 +1,67 @@ +// File: merge_sort.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Merge the left and right subarrays +// Left subarray range: [left, mid] +// Right subarray range: [mid + 1, right] +fn merge(nums: []i32, left: usize, mid: usize, right: usize) !void { + // Initialize the auxiliary array + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + var tmp = try mem_allocator.alloc(i32, right + 1 - left); + std.mem.copy(i32, tmp, nums[left..right+1]); + // Start and end indices of the left subarray in tmp + var leftStart = left - left; + var leftEnd = mid - left; + // Start and end indices of the right subarray in tmp + var rightStart = mid + 1 - left; + var rightEnd = right - left; + // i and j point to the first elements of the left and right subarrays, respectively + var i = leftStart; + var j = rightStart; + // Merge the left and right subarrays by overwriting the original array nums + var k = left; + while (k <= right) : (k += 1) { + // If the left subarray has been exhausted, take an element from the right subarray and increment j + if (i > leftEnd) { + nums[k] = tmp[j]; + j += 1; + // Otherwise, if the right subarray is exhausted or the left element is no greater than the right element, take the left element and increment i + } else if (j > rightEnd or tmp[i] <= tmp[j]) { + nums[k] = tmp[i]; + i += 1; + // Otherwise, both subarrays still have elements and the left element is larger, so take the right element and increment j + } else { + nums[k] = tmp[j]; + j += 1; + } + } +} + +// Merge sort +fn mergeSort(nums: []i32, left: usize, right: usize) !void { + // Base case + if (left >= right) return; // Stop recursing when the subarray has at most one element + // Divide phase + var mid = left + (right - left) / 2; // Calculate the midpoint + try mergeSort(nums, left, mid); // Recurse on the left subarray + try mergeSort(nums, mid + 1, right); // Recurse on the right subarray + // Merge phase + try merge(nums, left, mid, right); +} + +// Driver code +pub fn main() !void { + // Merge sort + var nums = [_]i32{ 7, 3, 2, 6, 0, 1, 5, 4 }; + try mergeSort(&nums, 0, nums.len - 1); + std.debug.print("After merge sort, nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_sorting/quick_sort.zig b/en/codes/zig/chapter_sorting/quick_sort.zig new file mode 100644 index 000000000..f6c57af80 --- /dev/null +++ b/en/codes/zig/chapter_sorting/quick_sort.zig @@ -0,0 +1,162 @@ +// File: quick_sort.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Quick sort implementation +const QuickSort = struct { + + // Swap the elements + pub fn swap(nums: []i32, i: usize, j: usize) void { + var tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + // Two-pointer partitioning + pub fn partition(nums: []i32, left: usize, right: usize) usize { + // Use nums[left] as the pivot + var i = left; + var j = right; + while (i < j) { + while (i < j and nums[j] >= nums[left]) j -= 1; // Scan from right to left for the first element smaller than the pivot + while (i < j and nums[i] <= nums[left]) i += 1; // Scan from left to right for the first element larger than the pivot + swap(nums, i, j); // Swap these two elements + } + swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index + } + + // Quick sort + pub fn quickSort(nums: []i32, left: usize, right: usize) void { + // Stop when the current subarray has at most one element + if (left >= right) return; + // Two-pointer partitioning + var pivot = partition(nums, left, right); + // Recurse on the left and right subarrays + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +// Quick sort implementation (median-of-three pivot optimization) +const QuickSortMedian = struct { + + // Swap the elements + pub fn swap(nums: []i32, i: usize, j: usize) void { + var tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + // Select the median of three candidate elements + pub fn medianThree(nums: []i32, left: usize, mid: usize, right: usize) usize { + var l = nums[left]; + var m = nums[mid]; + var r = nums[right]; + if ((l <= m && m <= r) || (r <= m && m <= l)) + return mid; // m lies between l and r + if ((m <= l && l <= r) || (r <= l && l <= m)) + return left; // l lies between m and r + return right; + } + + // Two-pointer partitioning (median of three) + pub fn partition(nums: []i32, left: usize, right: usize) usize { + // Select the median of three candidate elements + var med = medianThree(nums, left, (left + right) / 2, right); + // Swap the median into the leftmost position of the current subarray + swap(nums, left, med); + // Use nums[left] as the pivot + var i = left; + var j = right; + while (i < j) { + while (i < j and nums[j] >= nums[left]) j -= 1; // Scan from right to left for the first element smaller than the pivot + while (i < j and nums[i] <= nums[left]) i += 1; // Scan from left to right for the first element larger than the pivot + swap(nums, i, j); // Swap these two elements + } + swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index + } + + // Quick sort + pub fn quickSort(nums: []i32, left: usize, right: usize) void { + // Stop when the current subarray has at most one element + if (left >= right) return; + // Two-pointer partitioning + var pivot = partition(nums, left, right); + if (pivot == 0) return; + // Recurse on the left and right subarrays + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +// Quick sort implementation (recursion-depth optimization) +const QuickSortTailCall = struct { + + // Swap the elements + pub fn swap(nums: []i32, i: usize, j: usize) void { + var tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + // Two-pointer partitioning + pub fn partition(nums: []i32, left: usize, right: usize) usize { + // Use nums[left] as the pivot + var i = left; + var j = right; + while (i < j) { + while (i < j and nums[j] >= nums[left]) j -= 1; // Scan from right to left for the first element smaller than the pivot + while (i < j and nums[i] <= nums[left]) i += 1; // Scan from left to right for the first element larger than the pivot + swap(nums, i, j); // Swap these two elements + } + swap(nums, i, left); // Swap the pivot into the boundary position between the two subarrays + return i; // Return the pivot index + } + + // Quick sort (recursion depth optimization) + pub fn quickSort(nums: []i32, left_: usize, right_: usize) void { + var left = left_; + var right = right_; + // Stop when the current subarray has at most one element + while (left < right) { + // Perform two-pointer partitioning + var pivot = partition(nums, left, right); + // Apply quick sort to the shorter of the two subarrays + if (pivot - left < right - pivot) { + quickSort(nums, left, pivot - 1); // Recursively sort the left subarray + left = pivot + 1; // The remaining unsorted range is [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, right); // Recursively sort the right subarray + right = pivot - 1; // The remaining unsorted range is [left, pivot - 1] + } + } + } +}; + +// Driver code +pub fn main() !void { + // Quick sort + var nums = [_]i32{ 2, 4, 1, 0, 3, 5 }; + QuickSort.quickSort(&nums, 0, nums.len - 1); + std.debug.print("After quick sort, nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + // Quick sort (median pivot optimization) + var nums1 = [_]i32{ 2, 4, 1, 0, 3, 5 }; + QuickSortMedian.quickSort(&nums1, 0, nums1.len - 1); + std.debug.print("\nAfter quick sort (median-of-three pivot optimization), nums1 = ", .{}); + inc.PrintUtil.printArray(i32, &nums1); + + // Quick sort (recursion depth optimization) + var nums2 = [_]i32{ 2, 4, 1, 0, 3, 5 }; + QuickSortTailCall.quickSort(&nums2, 0, nums2.len - 1); + std.debug.print("\nAfter quick sort (recursion-depth optimization), nums2 = ", .{}); + inc.PrintUtil.printArray(i32, &nums2); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_sorting/radix_sort.zig b/en/codes/zig/chapter_sorting/radix_sort.zig new file mode 100644 index 000000000..fee9e1071 --- /dev/null +++ b/en/codes/zig/chapter_sorting/radix_sort.zig @@ -0,0 +1,77 @@ +// File: radix_sort.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Get the k-th digit of num, where exp = 10^(k-1) +fn digit(num: i32, exp: i32) i32 { + // Passing exp instead of k avoids repeatedly performing expensive exponentiation here + return @mod(@divFloor(num, exp), 10); +} + +// Counting sort (sort nums by the k-th digit) +fn countingSortDigit(nums: []i32, exp: i32) !void { + // Decimal digits range from 0 to 9, so the bucket array needs length 10 + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + // defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + var counter = try mem_allocator.alloc(usize, 10); + @memset(counter, 0); + var n = nums.len; + // Count the occurrences of each digit from 0 to 9 + for (nums) |num| { + var d: u32 = @bitCast(digit(num, exp)); // Get the kth digit of the current element and store it in d + counter[d] += 1; // Count the occurrences of digit d + } + // Compute prefix sums to convert occurrence counts into array positions + var i: usize = 1; + while (i < 10) : (i += 1) { + counter[i] += counter[i - 1]; + } + // Traverse in reverse and place each element into res according to the bucket counts + var res = try mem_allocator.alloc(i32, n); + i = n - 1; + while (i >= 0) : (i -= 1) { + var d: u32 = @bitCast(digit(nums[i], exp)); + var j = counter[d] - 1; // Get the destination index j for digit d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d] -= 1; // Decrease the count for d by 1 + if (i == 0) break; + } + // Overwrite the original array nums with the result + i = 0; + while (i < n) : (i += 1) { + nums[i] = res[i]; + } +} + +// Radix sort +fn radixSort(nums: []i32) !void { + // Get the largest array element to determine the maximum number of digits + var m: i32 = std.math.minInt(i32); + for (nums) |num| { + if (num > m) m = num; + } + // Process digits from least significant to most significant + var exp: i32 = 1; + while (exp <= m) : (exp *= 10) { + // Apply counting sort to the k-th digit of the array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // That is, exp = 10^(k-1) + try countingSortDigit(nums, exp); + } +} + +// Driver code +pub fn main() !void { + // Radix sort + var nums = [_]i32{ 23, 12, 3, 4, 788, 192 }; + try radixSort(&nums); + std.debug.print("After radix sort, nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_stack_and_queue/array_queue.zig b/en/codes/zig/chapter_stack_and_queue/array_queue.zig new file mode 100644 index 000000000..cce805f95 --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/array_queue.zig @@ -0,0 +1,140 @@ +// File: array_queue.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Queue implemented with a circular array +pub fn ArrayQueue(comptime T: type) type { + return struct { + const Self = @This(); + + nums: []T = undefined, // Array that stores the queue elements + cap: usize = 0, // Queue capacity + front: usize = 0, // Index of the front element + queSize: usize = 0, // Current number of queue elements + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // Allocator + + // Allocate and initialize the backing array + pub fn init(self: *Self, allocator: std.mem.Allocator, cap: usize) !void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + self.cap = cap; + self.nums = try self.mem_allocator.alloc(T, self.cap); + @memset(self.nums, @as(T, 0)); + } + + // Deinitialize the arena and release its memory + pub fn deinit(self: *Self) void { + if (self.mem_arena == null) return; + self.mem_arena.?.deinit(); + } + + // Get the queue capacity + pub fn capacity(self: *Self) usize { + return self.cap; + } + + // Get the queue size + pub fn size(self: *Self) usize { + return self.queSize; + } + + // Check whether the queue is empty + pub fn isEmpty(self: *Self) bool { + return self.queSize == 0; + } + + // Enqueue + pub fn push(self: *Self, num: T) !void { + if (self.size() == self.capacity()) { + std.debug.print("Queue is full\n", .{}); + return; + } + // Calculate the index immediately after the rear element + // Use modulo to wrap rear to the beginning when it passes the end of the array + var rear = (self.front + self.queSize) % self.capacity(); + // Append num at the rear of the queue + self.nums[rear] = num; + self.queSize += 1; + } + + // Dequeue + pub fn pop(self: *Self) T { + var num = self.peek(); + // Advance the front index by one, wrapping to the start of the array if necessary + self.front = (self.front + 1) % self.capacity(); + self.queSize -= 1; + return num; + } + + // Access the front element + pub fn peek(self: *Self) T { + if (self.isEmpty()) @panic("Queue is empty"); + return self.nums[self.front]; + } + + // Return a copy of the queue elements as an array + pub fn toArray(self: *Self) ![]T { + // Copy only the occupied queue elements + var res = try self.mem_allocator.alloc(T, self.size()); + @memset(res, @as(T, 0)); + var i: usize = 0; + var j: usize = self.front; + while (i < self.size()) : ({ i += 1; j += 1; }) { + res[i] = self.nums[j % self.capacity()]; + } + return res; + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the queue + var capacity: usize = 10; + var queue = ArrayQueue(i32){}; + try queue.init(std.heap.page_allocator, capacity); + defer queue.deinit(); + + // Enqueue elements + try queue.push(1); + try queue.push(3); + try queue.push(2); + try queue.push(5); + try queue.push(4); + std.debug.print("Queue queue = ", .{}); + inc.PrintUtil.printArray(i32, try queue.toArray()); + + // Access the front element + var peek = queue.peek(); + std.debug.print("\nFront element peek = {}", .{peek}); + + // Dequeue an element + var pop = queue.pop(); + std.debug.print("\nDequeued element pop = {}; after dequeueing, queue = ", .{pop}); + inc.PrintUtil.printArray(i32, try queue.toArray()); + + // Get the queue size + var size = queue.size(); + std.debug.print("\nQueue size = {}", .{size}); + + // Check whether the queue is empty + var is_empty = queue.isEmpty(); + std.debug.print("\nIs the queue empty? {}", .{is_empty}); + + // Test the circular array + var i: i32 = 0; + while (i < 10) : (i += 1) { + try queue.push(i); + _ = queue.pop(); + std.debug.print("\nAfter enqueue/dequeue round {}, queue = ", .{i}); + inc.PrintUtil.printArray(i32, try queue.toArray()); + } + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_stack_and_queue/array_stack.zig b/en/codes/zig/chapter_stack_and_queue/array_stack.zig new file mode 100644 index 000000000..e57aa0459 --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/array_stack.zig @@ -0,0 +1,97 @@ +// File: array_stack.zig +// Created Time: 2023-01-08 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Stack implemented with an array +pub fn ArrayStack(comptime T: type) type { + return struct { + const Self = @This(); + + stack: ?std.ArrayList(T) = null, + + // Initialize the stack's ArrayList + pub fn init(self: *Self, allocator: std.mem.Allocator) void { + if (self.stack == null) { + self.stack = std.ArrayList(T).init(allocator); + } + } + + // Deinitialize and release the memory + pub fn deinit(self: *Self) void { + if (self.stack == null) return; + self.stack.?.deinit(); + } + + // Get the stack size + pub fn size(self: *Self) usize { + return self.stack.?.items.len; + } + + // Check whether the stack is empty + pub fn isEmpty(self: *Self) bool { + return self.size() == 0; + } + + // Access the top element + pub fn peek(self: *Self) T { + if (self.isEmpty()) @panic("Stack is empty"); + return self.stack.?.items[self.size() - 1]; + } + + // Push + pub fn push(self: *Self, num: T) !void { + try self.stack.?.append(num); + } + + // Pop + pub fn pop(self: *Self) T { + var num = self.stack.?.pop(); + return num; + } + + // Return the backing ArrayList + pub fn toList(self: *Self) std.ArrayList(T) { + return self.stack.?; + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the stack + var stack = ArrayStack(i32){}; + stack.init(std.heap.page_allocator); + // Defer releasing the memory + defer stack.deinit(); + + // Push elements onto the stack + try stack.push(1); + try stack.push(3); + try stack.push(2); + try stack.push(5); + try stack.push(4); + std.debug.print("Stack stack = ", .{}); + inc.PrintUtil.printList(i32, stack.toList()); + + // Access the top element + var peek = stack.peek(); + std.debug.print("\nTop element peek = {}", .{peek}); + + // Pop an element from the stack + var top = stack.pop(); + std.debug.print("\nPopped element top = {}; after popping, stack = ", .{top}); + inc.PrintUtil.printList(i32, stack.toList()); + + // Get the stack size + var size = stack.size(); + std.debug.print("\nStack size = {}", .{size}); + + // Check whether the stack is empty + var is_empty = stack.isEmpty(); + std.debug.print("\nIs the stack empty? {}", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_stack_and_queue/deque.zig b/en/codes/zig/chapter_stack_and_queue/deque.zig new file mode 100644 index 000000000..716020db5 --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/deque.zig @@ -0,0 +1,51 @@ +// File: deque.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Driver code +pub fn main() !void { + // Initialize the deque + const L = std.TailQueue(i32); + var deque = L{}; + + // Enqueue elements + var node1 = L.Node{ .data = 2 }; + var node2 = L.Node{ .data = 5 }; + var node3 = L.Node{ .data = 4 }; + var node4 = L.Node{ .data = 3 }; + var node5 = L.Node{ .data = 1 }; + deque.append(&node1); // Add to the rear + deque.append(&node2); + deque.append(&node3); + deque.prepend(&node4); // Add to the front + deque.prepend(&node5); + std.debug.print("Deque deque = ", .{}); + inc.PrintUtil.printQueue(i32, deque); + + // Access an element + var peek_first = deque.first.?.data; // Front element + std.debug.print("\nFront element peek_first = {}", .{peek_first}); + var peek_last = deque.last.?.data; // Rear element + std.debug.print("\nRear element peek_last = {}", .{peek_last}); + + // Dequeue an element + var pop_first = deque.popFirst().?.data; // Dequeue the front element + std.debug.print("\nRemoved front element pop_first = {}; deque = ", .{pop_first}); + inc.PrintUtil.printQueue(i32, deque); + var pop_last = deque.pop().?.data; // Dequeue the rear element + std.debug.print("\nRemoved rear element pop_last = {}; deque = ", .{pop_last}); + inc.PrintUtil.printQueue(i32, deque); + + // Get the deque size + var size = deque.len; + std.debug.print("\nDeque size = {}", .{size}); + + // Check whether the deque is empty + var is_empty = if (deque.len == 0) true else false; + std.debug.print("\nIs the deque empty? {}", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_stack_and_queue/linkedlist_deque.zig b/en/codes/zig/chapter_stack_and_queue/linkedlist_deque.zig new file mode 100644 index 000000000..ebe3645f1 --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/linkedlist_deque.zig @@ -0,0 +1,209 @@ +// File: linkedlist_deque.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Doubly linked list node +pub fn ListNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = undefined, // Node value + next: ?*Self = null, // Pointer to the next node + prev: ?*Self = null, // Pointer to the previous node + + // Initialize a list node with the specified value + pub fn init(self: *Self, x: i32) void { + self.val = x; + self.next = null; + self.prev = null; + } + }; +} + +// Deque implemented with a doubly linked list +pub fn LinkedListDeque(comptime T: type) type { + return struct { + const Self = @This(); + + front: ?*ListNode(T) = null, // Head node front + rear: ?*ListNode(T) = null, // Tail node rear + que_size: usize = 0, // Deque size + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // Allocator + + // Initialize an empty deque and its arena allocator + pub fn init(self: *Self, allocator: std.mem.Allocator) !void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + self.front = null; + self.rear = null; + self.que_size = 0; + } + + // Deinitialize the arena and release its memory + pub fn deinit(self: *Self) void { + if (self.mem_arena == null) return; + self.mem_arena.?.deinit(); + } + + // Get the deque size + pub fn size(self: *Self) usize { + return self.que_size; + } + + // Check whether the deque is empty + pub fn isEmpty(self: *Self) bool { + return self.size() == 0; + } + + // Enqueue operation + pub fn push(self: *Self, num: T, is_front: bool) !void { + var node = try self.mem_allocator.create(ListNode(T)); + node.init(num); + // If the linked list is empty, make both front and rear point to node + if (self.isEmpty()) { + self.front = node; + self.rear = node; + // Enqueue at the front + } else if (is_front) { + // Add node to the head of the linked list + self.front.?.prev = node; + node.next = self.front; + self.front = node; // Update the head node + // Enqueue at the rear + } else { + // Add node to the tail of the linked list + self.rear.?.next = node; + node.prev = self.rear; + self.rear = node; // Update the tail node + } + self.que_size += 1; // Update the queue size + } + + // Enqueue at the front + pub fn pushFirst(self: *Self, num: T) !void { + try self.push(num, true); + } + + // Enqueue at the rear + pub fn pushLast(self: *Self, num: T) !void { + try self.push(num, false); + } + + // Dequeue operation + pub fn pop(self: *Self, is_front: bool) T { + if (self.isEmpty()) @panic("Deque is empty"); + var val: T = undefined; + // When the queue size is 1, the head and tail point to the same node + if (self.que_size == 1) { + val = self.front.?.val; + self.front = null; + self.rear = null; + } + // Dequeue from the front + else if (is_front) { + val = self.front.?.val; // Save the head node value + // Remove the head node + var fNext = self.front.?.next; + fNext.?.prev = null; + self.front.?.next = null; + self.front = fNext; // Update the head node + // Dequeue from the rear + } else { + val = self.rear.?.val; // Save the tail node value + // Remove the tail node + var rPrev = self.rear.?.prev; + rPrev.?.next = null; + self.rear.?.prev = null; + self.rear = rPrev; // Update the tail node + } + self.que_size -= 1; // Update the queue size + return val; + } + + // Dequeue from the front + pub fn popFirst(self: *Self) T { + return self.pop(true); + } + + // Dequeue from the rear + pub fn popLast(self: *Self) T { + return self.pop(false); + } + + // Access the front element + pub fn peekFirst(self: *Self) T { + if (self.isEmpty()) @panic("Deque is empty"); + return self.front.?.val; + } + + // Access the rear element + pub fn peekLast(self: *Self) T { + if (self.isEmpty()) @panic("Deque is empty"); + return self.rear.?.val; + } + + // Return an array for printing + pub fn toArray(self: *Self) ![]T { + var node = self.front; + var res = try self.mem_allocator.alloc(T, self.size()); + @memset(res, @as(T, 0)); + var i: usize = 0; + while (i < res.len) : (i += 1) { + res[i] = node.?.val; + node = node.?.next; + } + return res; + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the deque + var deque = LinkedListDeque(i32){}; + try deque.init(std.heap.page_allocator); + defer deque.deinit(); + try deque.pushLast(3); + try deque.pushLast(2); + try deque.pushLast(5); + std.debug.print("Deque deque = ", .{}); + inc.PrintUtil.printArray(i32, try deque.toArray()); + + // Access an element + var peek_first = deque.peekFirst(); + std.debug.print("\nFront element peek_first = {}", .{peek_first}); + var peek_last = deque.peekLast(); + std.debug.print("\nRear element peek_last = {}", .{peek_last}); + + // Enqueue elements + try deque.pushLast(4); + std.debug.print("\nAfter enqueuing element 4 at the rear, deque = ", .{}); + inc.PrintUtil.printArray(i32, try deque.toArray()); + try deque.pushFirst(1); + std.debug.print("\nAfter enqueuing element 1 at the front, deque = ", .{}); + inc.PrintUtil.printArray(i32, try deque.toArray()); + + // Dequeue an element + var pop_last = deque.popLast(); + std.debug.print("\nElement removed from the rear = {}; after removal, deque = ", .{pop_last}); + inc.PrintUtil.printArray(i32, try deque.toArray()); + var pop_first = deque.popFirst(); + std.debug.print("\nElement removed from the front = {}; after removal, deque = ", .{pop_first}); + inc.PrintUtil.printArray(i32, try deque.toArray()); + + // Get the deque size + var size = deque.size(); + std.debug.print("\nDeque size = {}", .{size}); + + // Check whether the deque is empty + var is_empty = deque.isEmpty(); + std.debug.print("\nIs the deque empty? {}", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_stack_and_queue/linkedlist_queue.zig b/en/codes/zig/chapter_stack_and_queue/linkedlist_queue.zig new file mode 100644 index 000000000..01a23f243 --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/linkedlist_queue.zig @@ -0,0 +1,127 @@ +// File: linkedlist_queue.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Queue implemented with a linked list +pub fn LinkedListQueue(comptime T: type) type { + return struct { + const Self = @This(); + + front: ?*inc.ListNode(T) = null, // Head node front + rear: ?*inc.ListNode(T) = null, // Tail node rear + que_size: usize = 0, // Queue size + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // Allocator + + // Initialize an empty queue and its arena allocator + pub fn init(self: *Self, allocator: std.mem.Allocator) !void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + self.front = null; + self.rear = null; + self.que_size = 0; + } + + // Deinitialize the arena and release its memory + pub fn deinit(self: *Self) void { + if (self.mem_arena == null) return; + self.mem_arena.?.deinit(); + } + + // Get the queue size + pub fn size(self: *Self) usize { + return self.que_size; + } + + // Check whether the queue is empty + pub fn isEmpty(self: *Self) bool { + return self.size() == 0; + } + + // Access the front element + pub fn peek(self: *Self) T { + if (self.size() == 0) @panic("Queue is empty"); + return self.front.?.val; + } + + // Enqueue + pub fn push(self: *Self, num: T) !void { + // Add num after the tail node + var node = try self.mem_allocator.create(inc.ListNode(T)); + node.init(num); + // If the queue is empty, make both the head and tail point to this node + if (self.front == null) { + self.front = node; + self.rear = node; + // If the queue is not empty, add this node after the tail node + } else { + self.rear.?.next = node; + self.rear = node; + } + self.que_size += 1; + } + + // Dequeue + pub fn pop(self: *Self) T { + var num = self.peek(); + // Remove the head node + self.front = self.front.?.next; + self.que_size -= 1; + return num; + } + + // Copy the linked-list elements into an array + pub fn toArray(self: *Self) ![]T { + var node = self.front; + var res = try self.mem_allocator.alloc(T, self.size()); + @memset(res, @as(T, 0)); + var i: usize = 0; + while (i < res.len) : (i += 1) { + res[i] = node.?.val; + node = node.?.next; + } + return res; + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the queue + var queue = LinkedListQueue(i32){}; + try queue.init(std.heap.page_allocator); + defer queue.deinit(); + + // Enqueue elements + try queue.push(1); + try queue.push(3); + try queue.push(2); + try queue.push(5); + try queue.push(4); + std.debug.print("Queue queue = ", .{}); + inc.PrintUtil.printArray(i32, try queue.toArray()); + + // Access the front element + var peek = queue.peek(); + std.debug.print("\nFront element peek = {}", .{peek}); + + // Dequeue an element + var pop = queue.pop(); + std.debug.print("\nDequeued element pop = {}; after dequeueing, queue = ", .{pop}); + inc.PrintUtil.printArray(i32, try queue.toArray()); + + // Get the queue size + var size = queue.size(); + std.debug.print("\nQueue size = {}", .{size}); + + // Check whether the queue is empty + var is_empty = queue.isEmpty(); + std.debug.print("\nIs the queue empty? {}", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_stack_and_queue/linkedlist_stack.zig b/en/codes/zig/chapter_stack_and_queue/linkedlist_stack.zig new file mode 100644 index 000000000..9f2acb40b --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/linkedlist_stack.zig @@ -0,0 +1,118 @@ +// File: linkedlist_stack.zig +// Created Time: 2023-01-08 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Stack implemented with a linked list +pub fn LinkedListStack(comptime T: type) type { + return struct { + const Self = @This(); + + stack_top: ?*inc.ListNode(T) = null, // Use the head node as the stack top + stk_size: usize = 0, // Stack size + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // Allocator + + // Initialize an empty stack and its arena allocator + pub fn init(self: *Self, allocator: std.mem.Allocator) !void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + self.stack_top = null; + self.stk_size = 0; + } + + // Deinitialize the arena and release its memory + pub fn deinit(self: *Self) void { + if (self.mem_arena == null) return; + self.mem_arena.?.deinit(); + } + + // Get the stack size + pub fn size(self: *Self) usize { + return self.stk_size; + } + + // Check whether the stack is empty + pub fn isEmpty(self: *Self) bool { + return self.size() == 0; + } + + // Access the top element + pub fn peek(self: *Self) T { + if (self.size() == 0) @panic("Stack is empty"); + return self.stack_top.?.val; + } + + // Push + pub fn push(self: *Self, num: T) !void { + var node = try self.mem_allocator.create(inc.ListNode(T)); + node.init(num); + node.next = self.stack_top; + self.stack_top = node; + self.stk_size += 1; + } + + // Pop + pub fn pop(self: *Self) T { + var num = self.peek(); + self.stack_top = self.stack_top.?.next; + self.stk_size -= 1; + return num; + } + + // Copy the stack elements into an array + pub fn toArray(self: *Self) ![]T { + var node = self.stack_top; + var res = try self.mem_allocator.alloc(T, self.size()); + @memset(res, @as(T, 0)); + var i: usize = 0; + while (i < res.len) : (i += 1) { + res[res.len - i - 1] = node.?.val; + node = node.?.next; + } + return res; + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the stack + var stack = LinkedListStack(i32){}; + try stack.init(std.heap.page_allocator); + // Defer releasing the memory + defer stack.deinit(); + + // Push elements onto the stack + try stack.push(1); + try stack.push(3); + try stack.push(2); + try stack.push(5); + try stack.push(4); + std.debug.print("Stack stack = ", .{}); + inc.PrintUtil.printArray(i32, try stack.toArray()); + + // Access the top element + var peek = stack.peek(); + std.debug.print("\nTop element peek = {}", .{peek}); + + // Pop an element from the stack + var pop = stack.pop(); + std.debug.print("\nPopped element pop = {}; after popping, stack = ", .{pop}); + inc.PrintUtil.printArray(i32, try stack.toArray()); + + // Get the stack size + var size = stack.size(); + std.debug.print("\nStack size = {}", .{size}); + + // Check whether the stack is empty + var is_empty = stack.isEmpty(); + std.debug.print("\nIs the stack empty? {}", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} + diff --git a/en/codes/zig/chapter_stack_and_queue/queue.zig b/en/codes/zig/chapter_stack_and_queue/queue.zig new file mode 100644 index 000000000..184754606 --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/queue.zig @@ -0,0 +1,46 @@ +// File: queue.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Driver code +pub fn main() !void { + // Initialize the queue + const L = std.TailQueue(i32); + var queue = L{}; + + // Enqueue elements + var node1 = L.Node{ .data = 1 }; + var node2 = L.Node{ .data = 3 }; + var node3 = L.Node{ .data = 2 }; + var node4 = L.Node{ .data = 5 }; + var node5 = L.Node{ .data = 4 }; + queue.append(&node1); + queue.append(&node2); + queue.append(&node3); + queue.append(&node4); + queue.append(&node5); + std.debug.print("Queue queue = ", .{}); + inc.PrintUtil.printQueue(i32, queue); + + // Access the front element + var peek = queue.first.?.data; + std.debug.print("\nFront element peek = {}", .{peek}); + + // Dequeue an element + var pop = queue.popFirst().?.data; + std.debug.print("\nDequeued element pop = {}; after dequeueing, queue = ", .{pop}); + inc.PrintUtil.printQueue(i32, queue); + + // Get the queue size + var size = queue.len; + std.debug.print("\nQueue size = {}", .{size}); + + // Check whether the queue is empty + var is_empty = if (queue.len == 0) true else false; + std.debug.print("\nIs the queue empty? {}", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_stack_and_queue/stack.zig b/en/codes/zig/chapter_stack_and_queue/stack.zig new file mode 100644 index 000000000..e9a57ab8f --- /dev/null +++ b/en/codes/zig/chapter_stack_and_queue/stack.zig @@ -0,0 +1,43 @@ +// File: stack.zig +// Created Time: 2023-01-08 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Driver code +pub fn main() !void { + // Initialize the stack + // In Zig, ArrayList can be used as a stack + var stack = std.ArrayList(i32).init(std.heap.page_allocator); + // Defer releasing the memory + defer stack.deinit(); + + // Push elements onto the stack + try stack.append(1); + try stack.append(3); + try stack.append(2); + try stack.append(5); + try stack.append(4); + std.debug.print("Stack stack = ", .{}); + inc.PrintUtil.printList(i32, stack); + + // Access the top element + var peek = stack.items[stack.items.len - 1]; + std.debug.print("\nTop element peek = {}", .{peek}); + + // Pop an element from the stack + var pop = stack.pop(); + std.debug.print("\nPopped element pop = {}; after popping, stack = ", .{pop}); + inc.PrintUtil.printList(i32, stack); + + // Get the stack size + var size = stack.items.len; + std.debug.print("\nStack size = {}", .{size}); + + // Check whether the stack is empty + var is_empty = if (stack.items.len == 0) true else false; + std.debug.print("\nIs the stack empty? {}", .{is_empty}); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_tree/avl_tree.zig b/en/codes/zig/chapter_tree/avl_tree.zig new file mode 100644 index 000000000..2b741433a --- /dev/null +++ b/en/codes/zig/chapter_tree/avl_tree.zig @@ -0,0 +1,249 @@ +// File: avl_tree.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// AVL tree +pub fn AVLTree(comptime T: type) type { + return struct { + const Self = @This(); + + root: ?*inc.TreeNode(T) = null, // Root node + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // Allocator + + // Initialize the arena allocator + pub fn init(self: *Self, allocator: std.mem.Allocator) void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + } + + // Deinitialize the arena and release its memory + pub fn deinit(self: *Self) void { + if (self.mem_arena == null) return; + self.mem_arena.?.deinit(); + } + + // Get the node height + fn height(self: *Self, node: ?*inc.TreeNode(T)) i32 { + _ = self; + // A null node has height -1; a leaf has height 0 + return if (node == null) -1 else node.?.height; + } + + // Update the node height + fn updateHeight(self: *Self, node: ?*inc.TreeNode(T)) void { + // The node height equals the height of its taller subtree + 1 + node.?.height = @max(self.height(node.?.left), self.height(node.?.right)) + 1; + } + + // Get the balance factor + fn balanceFactor(self: *Self, node: ?*inc.TreeNode(T)) i32 { + // A null node has balance factor 0 + if (node == null) return 0; + // Node balance factor = left subtree height - right subtree height + return self.height(node.?.left) - self.height(node.?.right); + } + + // Right rotation + fn rightRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) { + var child = node.?.left; + var grandChild = child.?.right; + // Rotate node to the right around child + child.?.right = node; + node.?.left = grandChild; + // Update the node height + self.updateHeight(node); + self.updateHeight(child); + // Return the root of the rotated subtree + return child; + } + + // Left rotation + fn leftRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) { + var child = node.?.right; + var grandChild = child.?.left; + // Rotate node to the left around child + child.?.left = node; + node.?.right = grandChild; + // Update the node height + self.updateHeight(node); + self.updateHeight(child); + // Return the root of the rotated subtree + return child; + } + + // Perform rotations to restore balance in this subtree + fn rotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) { + // Get the balance factor of node + var balance_factor = self.balanceFactor(node); + // Left-heavy tree + if (balance_factor > 1) { + if (self.balanceFactor(node.?.left) >= 0) { + // Rotate right + return self.rightRotate(node); + } else { + // Rotate left, then right + node.?.left = self.leftRotate(node.?.left); + return self.rightRotate(node); + } + } + // Right-heavy tree + if (balance_factor < -1) { + if (self.balanceFactor(node.?.right) <= 0) { + // Rotate left + return self.leftRotate(node); + } else { + // Rotate right, then left + node.?.right = self.rightRotate(node.?.right); + return self.leftRotate(node); + } + } + // The tree is balanced; return without rotating + return node; + } + + // Insert a node + fn insert(self: *Self, val: T) !void { + self.root = (try self.insertHelper(self.root, val)).?; + } + + // Insert a node recursively (helper method) + 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. Find the insertion position and insert the node + 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; // Return without inserting a duplicate node + } + self.updateHeight(node); // Update the node height + // 2. Perform rotations to restore balance in this subtree + node = self.rotate(node); + // Return the root of the subtree + return node; + } + + // Remove a node + fn remove(self: *Self, val: T) void { + self.root = self.removeHelper(self.root, val).?; + } + + // Remove a node recursively (helper method) + fn removeHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) ?*inc.TreeNode(T) { + var node = node_; + if (node == null) return null; + // 1. Find and remove the node + 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; + // Number of children = 0: remove node and return + if (child == null) { + return null; + // Number of children = 1: remove node directly + } else { + node = child; + } + } else { + // Number of children = 2: remove the inorder successor and use its value to replace the current node value + 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); // Update the node height + // 2. Perform rotations to restore balance in this subtree + node = self.rotate(node); + // Return the root of the subtree + return node; + } + + // Find a node + fn search(self: *Self, val: T) ?*inc.TreeNode(T) { + var cur = self.root; + // Search while cur is not null + while (cur != null) { + // The target node lies in the right subtree of cur + if (cur.?.val < val) { + cur = cur.?.right; + // The target node lies in the left subtree of cur + } else if (cur.?.val > val) { + cur = cur.?.left; + // The target node was found; exit the loop + } else { + break; + } + } + // Return the matching node, or null if no match was found + return cur; + } + }; +} + +pub fn testInsert(comptime T: type, tree_: *AVLTree(T), val: T) !void { + var tree = tree_; + try tree.insert(val); + std.debug.print("\nAVL tree after inserting node {}:\n", .{val}); + try inc.PrintUtil.printTree(tree.root, null, false); +} + +pub fn testRemove(comptime T: type, tree_: *AVLTree(T), val: T) void { + var tree = tree_; + tree.remove(val); + std.debug.print("\nAVL tree after removing node {}:\n", .{val}); + try inc.PrintUtil.printTree(tree.root, null, false); +} + +// Driver code +pub fn main() !void { + // Initialize an empty AVL tree + var avl_tree = AVLTree(i32){}; + avl_tree.init(std.heap.page_allocator); + defer avl_tree.deinit(); + + // Insert a node + // Observe how the AVL tree maintains balance after a node is inserted + try testInsert(i32, &avl_tree, 1); + try testInsert(i32, &avl_tree, 2); + try testInsert(i32, &avl_tree, 3); + try testInsert(i32, &avl_tree, 4); + try testInsert(i32, &avl_tree, 5); + try testInsert(i32, &avl_tree, 8); + try testInsert(i32, &avl_tree, 7); + try testInsert(i32, &avl_tree, 9); + try testInsert(i32, &avl_tree, 10); + try testInsert(i32, &avl_tree, 6); + + // Insert a duplicate node + try testInsert(i32, &avl_tree, 7); + + // Remove a node + // Observe how the AVL tree maintains balance after a node is removed + testRemove(i32, &avl_tree, 8); // Remove a node with 0 children + testRemove(i32, &avl_tree, 5); // Remove a node with 1 child + testRemove(i32, &avl_tree, 4); // Remove a node with 2 children + + // Find a node + var node = avl_tree.search(7).?; + std.debug.print("\nFound node: {any}, node value = {}\n", .{node, node.val}); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_tree/binary_search_tree.zig b/en/codes/zig/chapter_tree/binary_search_tree.zig new file mode 100644 index 000000000..3136f6a62 --- /dev/null +++ b/en/codes/zig/chapter_tree/binary_search_tree.zig @@ -0,0 +1,182 @@ +// File: binary_search_tree.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Binary search tree +pub fn BinarySearchTree(comptime T: type) type { + return struct { + const Self = @This(); + + root: ?*inc.TreeNode(T) = null, + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // Allocator + + // Initialize the allocator and build the tree from the array + pub fn init(self: *Self, allocator: std.mem.Allocator, nums: []T) !void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + std.mem.sort(T, nums, {}, comptime std.sort.asc(T)); // Sort the array + self.root = try self.buildTree(nums, 0, nums.len - 1); // Build the binary search tree + } + + // Deinitialize the arena and release its memory + pub fn deinit(self: *Self) void { + if (self.mem_arena == null) return; + self.mem_arena.?.deinit(); + } + + // Build the binary search tree + fn buildTree(self: *Self, nums: []T, i: usize, j: usize) !?*inc.TreeNode(T) { + if (i > j) return null; + // Use the middle array element as the root value + var mid = i + (j - i) / 2; + var node = try self.mem_allocator.create(inc.TreeNode(T)); + node.init(nums[mid]); + // Recursively build the left and right subtrees + if (mid >= 1) node.left = try self.buildTree(nums, i, mid - 1); + node.right = try self.buildTree(nums, mid + 1, j); + return node; + } + + // Get the root of the binary tree + fn getRoot(self: *Self) ?*inc.TreeNode(T) { + return self.root; + } + + // Find a node + fn search(self: *Self, num: T) ?*inc.TreeNode(T) { + var cur = self.root; + // Search while cur is not null + while (cur != null) { + // The target node lies in the right subtree of cur + if (cur.?.val < num) { + cur = cur.?.right; + // The target node lies in the left subtree of cur + } else if (cur.?.val > num) { + cur = cur.?.left; + // The target node was found; exit the loop + } else { + break; + } + } + // Return the matching node, or null if no match was found + return cur; + } + + // Insert a node + fn insert(self: *Self, num: T) !void { + // If the tree is empty, allocate a root node + 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; + // Search while cur is not null + while (cur != null) { + // Return immediately if a duplicate node is found + if (cur.?.val == num) return; + pre = cur; + // The insertion position lies in the right subtree of cur + if (cur.?.val < num) { + cur = cur.?.right; + // The insertion position lies in the left subtree of cur + } else { + cur = cur.?.left; + } + } + // Insert a node + var node = try self.mem_allocator.create(inc.TreeNode(T)); + node.init(num); + if (pre.?.val < num) { + pre.?.right = node; + } else { + pre.?.left = node; + } + } + + // Remove a node + fn remove(self: *Self, num: T) void { + // Return immediately if the tree is empty + if (self.root == null) return; + var cur = self.root; + var pre: ?*inc.TreeNode(T) = null; + // Search while cur is not null + while (cur != null) { + // The node to remove was found; exit the loop + if (cur.?.val == num) break; + pre = cur; + // The node to remove lies in the right subtree of cur + if (cur.?.val < num) { + cur = cur.?.right; + // The node to remove lies in the left subtree of cur + } else { + cur = cur.?.left; + } + } + // Return immediately if the node to remove does not exist + if (cur == null) return; + // Number of children = 0 or 1 + if (cur.?.left == null or cur.?.right == null) { + // With 0 or 1 child, child is null or the existing child, respectively + var child = if (cur.?.left != null) cur.?.left else cur.?.right; + // Remove node cur + if (pre.?.left == cur) { + pre.?.left = child; + } else { + pre.?.right = child; + } + // Number of children = 2 + } else { + // Get the inorder successor of cur + var tmp = cur.?.right; + while (tmp.?.left != null) { + tmp = tmp.?.left; + } + var tmp_val = tmp.?.val; + // Recursively remove node tmp + self.remove(tmp.?.val); + // Replace the value of cur with the value of tmp + cur.?.val = tmp_val; + } + } + }; +} + +// Driver code +pub fn main() !void { + // Initialize the binary tree + var nums = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + var bst = BinarySearchTree(i32){}; + try bst.init(std.heap.page_allocator, &nums); + defer bst.deinit(); + std.debug.print("Binary tree after initialization:\n", .{}); + try inc.PrintUtil.printTree(bst.getRoot(), null, false); + + // Find a node + var node = bst.search(7); + std.debug.print("\nFound node: {any}, node value = {}\n", .{node, node.?.val}); + + // Insert a node + try bst.insert(16); + std.debug.print("\nBinary tree after inserting node 16:\n", .{}); + try inc.PrintUtil.printTree(bst.getRoot(), null, false); + + // Remove a node + bst.remove(1); + std.debug.print("\nBinary tree after removing node 1:\n", .{}); + try inc.PrintUtil.printTree(bst.getRoot(), null, false); + bst.remove(2); + std.debug.print("\nBinary tree after removing node 2:\n", .{}); + try inc.PrintUtil.printTree(bst.getRoot(), null, false); + bst.remove(4); + std.debug.print("\nBinary tree after removing node 4:\n", .{}); + try inc.PrintUtil.printTree(bst.getRoot(), null, false); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/en/codes/zig/chapter_tree/binary_tree.zig b/en/codes/zig/chapter_tree/binary_tree.zig new file mode 100644 index 000000000..a266016db --- /dev/null +++ b/en/codes/zig/chapter_tree/binary_tree.zig @@ -0,0 +1,39 @@ +// File: binary_tree.zig +// Created Time: 2023-01-14 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Driver code +pub fn main() !void { + // Initialize the binary tree + // Initialize the nodes + var n1 = inc.TreeNode(i32){ .val = 1 }; + var n2 = inc.TreeNode(i32){ .val = 2 }; + var n3 = inc.TreeNode(i32){ .val = 3 }; + var n4 = inc.TreeNode(i32){ .val = 4 }; + var n5 = inc.TreeNode(i32){ .val = 5 }; + // Set up references (pointers) between the nodes + n1.left = &n2; + n1.right = &n3; + n2.left = &n4; + n2.right = &n5; + std.debug.print("Initialize the binary tree\n", .{}); + try inc.PrintUtil.printTree(&n1, null, false); + + // Insert and remove nodes + var p = inc.TreeNode(i32){ .val = 0 }; + // Insert node p between n1 and n2 + n1.left = &p; + p.left = &n2; + std.debug.print("After inserting node p\n", .{}); + try inc.PrintUtil.printTree(&n1, null, false); + // Remove a node + n1.left = &n2; + std.debug.print("After removing node p\n", .{}); + try inc.PrintUtil.printTree(&n1, null, false); + + _ = try std.io.getStdIn().reader().readByte(); +} + diff --git a/en/codes/zig/chapter_tree/binary_tree_bfs.zig b/en/codes/zig/chapter_tree/binary_tree_bfs.zig new file mode 100644 index 000000000..3d5874c6b --- /dev/null +++ b/en/codes/zig/chapter_tree/binary_tree_bfs.zig @@ -0,0 +1,57 @@ +// File: binary_tree_bfs.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Level-order traversal +fn levelOrder(comptime T: type, mem_allocator: std.mem.Allocator, root: *inc.TreeNode(T)) !std.ArrayList(T) { + // Initialize a queue and enqueue the root + 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); + // Initialize a list to store the traversal sequence + var list = std.ArrayList(T).init(std.heap.page_allocator); + while (queue.len > 0) { + var queue_node = queue.popFirst().?; // Dequeue from the queue + var node = queue_node.data; + try list.append(node.val); // Store the node value + if (node.left != null) { + var tmp_node = try mem_allocator.create(L.Node); + tmp_node.data = node.left.?; + queue.append(tmp_node); // Enqueue the left child + } + if (node.right != null) { + var tmp_node = try mem_allocator.create(L.Node); + tmp_node.data = node.right.?; + queue.append(tmp_node); // Enqueue the right child + } + } + return list; +} + +// Driver code +pub fn main() !void { + // Initialize the allocator + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array + var nums = [_]i32{1, 2, 3, 4, 5, 6, 7}; + var root = try inc.TreeUtil.arrToTree(i32, mem_allocator, &nums); + std.debug.print("Initialize the binary tree\n", .{}); + try inc.PrintUtil.printTree(root, null, false); + + // Level-order traversal + var list = try levelOrder(i32, mem_allocator, root.?); + defer list.deinit(); + std.debug.print("\nNode sequence from level-order traversal = ", .{}); + inc.PrintUtil.printList(i32, list); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/chapter_tree/binary_tree_dfs.zig b/en/codes/zig/chapter_tree/binary_tree_dfs.zig new file mode 100644 index 000000000..ed4e4a895 --- /dev/null +++ b/en/codes/zig/chapter_tree/binary_tree_dfs.zig @@ -0,0 +1,70 @@ +// File: binary_tree_dfs.zig +// Created Time: 2023-01-15 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +var list = std.ArrayList(i32).init(std.heap.page_allocator); + +// Preorder traversal +fn preOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void { + if (root == null) return; + // Visit order: root -> left subtree -> right subtree + try list.append(root.?.val); + try preOrder(T, root.?.left); + try preOrder(T, root.?.right); +} + +// Inorder traversal +fn inOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void { + if (root == null) return; + // Visit order: left subtree -> root -> right subtree + try inOrder(T, root.?.left); + try list.append(root.?.val); + try inOrder(T, root.?.right); +} + +// Postorder traversal +fn postOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void { + if (root == null) return; + // Visit order: left subtree -> right subtree -> root + try postOrder(T, root.?.left); + try postOrder(T, root.?.right); + try list.append(root.?.val); +} + +// Driver code +pub fn main() !void { + // Initialize the allocator + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + + // Initialize the binary tree + // Use a helper function that builds a binary tree directly from an array + var nums = [_]i32{1, 2, 3, 4, 5, 6, 7}; + var root = try inc.TreeUtil.arrToTree(i32, mem_allocator, &nums); + std.debug.print("Initialize the binary tree\n", .{}); + try inc.PrintUtil.printTree(root, null, false); + + // Preorder traversal + list.clearRetainingCapacity(); + try preOrder(i32, root); + std.debug.print("\nNode sequence from preorder traversal = ", .{}); + inc.PrintUtil.printList(i32, list); + + // Inorder traversal + list.clearRetainingCapacity(); + try inOrder(i32, root); + std.debug.print("\nNode sequence from inorder traversal = ", .{}); + inc.PrintUtil.printList(i32, list); + + // Postorder traversal + list.clearRetainingCapacity(); + try postOrder(i32, root); + std.debug.print("\nPostorder traversal sequence = ", .{}); + inc.PrintUtil.printList(i32, list); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/en/codes/zig/include/PrintUtil.zig b/en/codes/zig/include/PrintUtil.zig new file mode 100644 index 000000000..c51440362 --- /dev/null +++ b/en/codes/zig/include/PrintUtil.zig @@ -0,0 +1,42 @@ +// File: PrintUtil.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com) + +const std = @import("std"); +pub const ListUtil = @import("ListNode.zig"); +pub const ListNode = ListUtil.ListNode; +pub const TreeUtil = @import("TreeNode.zig"); +pub const TreeNode = TreeUtil.TreeNode; + +// Print the queue +pub fn printQueue(comptime T: type, queue: std.TailQueue(T)) void { + var node = queue.first; + std.debug.print("[", .{}); + var i: i32 = 0; + while (node != null) : (i += 1) { + var data = node.?.data; + std.debug.print("{}{s}", .{ data, if (i == queue.len - 1) "]" else ", " }); + node = node.?.next; + } +} + +// Print the hash table +pub fn printHashMap(comptime TKey: type, comptime TValue: type, map: std.AutoHashMap(TKey, TValue)) void { + var it = map.iterator(); + while (it.next()) |kv| { + var key = kv.key_ptr.*; + var value = kv.value_ptr.*; + std.debug.print("{} -> {s}\n", .{ key, value }); + } +} + +// Print a heap +pub fn printHeap(comptime T: type, mem_allocator: std.mem.Allocator, queue: anytype) !void { + var arr = queue.items; + var len = queue.len; + std.debug.print("Array representation of the heap:", .{}); + printArray(T, arr[0..len]); + std.debug.print("\nTree representation of the heap:\n", .{}); + var root = try TreeUtil.arrToTree(T, mem_allocator, arr[0..len]); + try printTree(root, null, false); +} diff --git a/en/codes/zig/include/include.zig b/en/codes/zig/include/include.zig new file mode 100644 index 000000000..4c4ec8369 --- /dev/null +++ b/en/codes/zig/include/include.zig @@ -0,0 +1,7 @@ +// File: include.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com) + +pub const PrintUtil = @import("PrintUtil.zig"); +pub const TreeUtil = @import("TreeNode.zig"); +pub const TreeNode = TreeUtil.TreeNode; diff --git a/en/codes/zig/main.zig b/en/codes/zig/main.zig new file mode 100644 index 000000000..970449d97 --- /dev/null +++ b/en/codes/zig/main.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +const iteration = @import("chapter_computational_complexity/iteration.zig"); +const recursion = @import("chapter_computational_complexity/recursion.zig"); +const time_complexity = @import("chapter_computational_complexity/time_complexity.zig"); +const space_complexity = @import("chapter_computational_complexity/space_complexity.zig"); +const worst_best_time_complexity = @import("chapter_computational_complexity/worst_best_time_complexity.zig"); + +const array = @import("chapter_array_and_linkedlist/array.zig"); +const linked_list = @import("chapter_array_and_linkedlist/linked_list.zig"); +const list = @import("chapter_array_and_linkedlist/list.zig"); +const my_list = @import("chapter_array_and_linkedlist/my_list.zig"); + +pub fn main() !void { + try iteration.run(); + recursion.run(); + time_complexity.run(); + try space_complexity.run(); + worst_best_time_complexity.run(); + + try array.run(); + linked_list.run(); + try list.run(); + try my_list.run(); +} diff --git a/en/codes/zig/utils/ListNode.zig b/en/codes/zig/utils/ListNode.zig new file mode 100644 index 000000000..906368c7d --- /dev/null +++ b/en/codes/zig/utils/ListNode.zig @@ -0,0 +1,49 @@ +// File: ListNode.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); + +// Linked list node +pub fn ListNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = 0, + next: ?*Self = null, + + // Initialize a list node with the specified value + pub fn init(self: *Self, x: i32) void { + self.val = x; + self.next = null; + } + }; +} + +// Deserialize a list into a linked list +pub fn listToLinkedList(comptime T: type, allocator: std.mem.Allocator, list: std.ArrayList(T)) !?*ListNode(T) { + var dum = try allocator.create(ListNode(T)); + dum.init(0); + var head = dum; + for (list.items) |val| { + var tmp = try allocator.create(ListNode(T)); + tmp.init(val); + head.next = tmp; + head = head.next.?; + } + return dum.next; +} + +// Deserialize an array into a linked list +pub fn arrToLinkedList(comptime T: type, mem_allocator: std.mem.Allocator, arr: []T) !?*ListNode(T) { + var dum = try mem_allocator.create(ListNode(T)); + dum.init(0); + var head = dum; + for (arr) |val| { + var tmp = try mem_allocator.create(ListNode(T)); + tmp.init(val); + head.next = tmp; + head = head.next.?; + } + return dum.next; +} diff --git a/en/codes/zig/utils/TreeNode.zig b/en/codes/zig/utils/TreeNode.zig new file mode 100644 index 000000000..365a281bd --- /dev/null +++ b/en/codes/zig/utils/TreeNode.zig @@ -0,0 +1,63 @@ +// File: TreeNode.zig +// Created Time: 2023-01-07 +// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); + +// Binary tree node +pub fn TreeNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = undefined, // Node value + height: i32 = undefined, // Node height + left: ?*Self = null, // Pointer to the left child + right: ?*Self = null, // Pointer to the right child + + // Initialize a tree node with the specified value + pub fn init(self: *Self, x: i32) void { + self.val = x; + self.height = 0; + self.left = null; + self.right = null; + } + }; +} + +// Deserialize an array into a binary tree +pub fn arrToTree(comptime T: type, allocator: std.mem.Allocator, arr: []T) !?*TreeNode(T) { + if (arr.len == 0) return null; + var root = try allocator.create(TreeNode(T)); + root.init(arr[0]); + const L = std.TailQueue(*TreeNode(T)); + var que = L{}; + var root_node = try allocator.create(L.Node); + root_node.data = root; + que.append(root_node); + var index: usize = 0; + while (que.len > 0) { + const que_node = que.popFirst().?; + var node = que_node.data; + index += 1; + if (index >= arr.len) break; + if (index < arr.len) { + var tmp = try allocator.create(TreeNode(T)); + tmp.init(arr[index]); + node.left = tmp; + var tmp_node = try allocator.create(L.Node); + tmp_node.data = node.left.?; + que.append(tmp_node); + } + index += 1; + if (index >= arr.len) break; + if (index < arr.len) { + var tmp = try allocator.create(TreeNode(T)); + tmp.init(arr[index]); + node.right = tmp; + var tmp_node = try allocator.create(L.Node); + tmp_node.data = node.right.?; + que.append(tmp_node); + } + } + return root; +} diff --git a/en/codes/zig/utils/format.zig b/en/codes/zig/utils/format.zig new file mode 100644 index 000000000..77ca0782b --- /dev/null +++ b/en/codes/zig/utils/format.zig @@ -0,0 +1,140 @@ +// File: format.zig +// Created Time: 2025-07-19 +// Author: CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +const ListNode = @import("ListNode.zig").ListNode; +const TreeNode = @import("TreeNode.zig").TreeNode; + +pub fn slice(items: anytype) SliceFormatter(@TypeOf(items)) { + return .{ .items = items }; +} + +pub fn SliceFormatter(comptime SliceType: type) type { + return struct { + const Self = @This(); + + items: SliceType, + + pub fn format( + self: Self, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) !void { + try writer.writeAll("["); + + if (self.items.len > 0) { + for (self.items, 0..) |item, i| { + try std.fmt.format(writer, "{}", .{item}); + if (i != self.items.len - 1) { + try writer.writeAll(", "); + } + } + } + + try writer.writeAll("]"); + } + }; +} + +pub fn linkedList(comptime T: type, head: *const ListNode(T)) LinkedListFormatter(T) { + return .{ .head = head }; +} + +pub fn LinkedListFormatter(comptime T: type) type { + return struct { + const Self = @This(); + + head: *const ListNode(T), + + pub fn format( + self: Self, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) !void { + try printLinkedList(self.head, writer); + } + + pub fn printLinkedList(head: *const ListNode(T), writer: anytype) !void { + try std.fmt.format(writer, "{}", .{head.val}); + if (head.next) |next_node| { + try writer.writeAll("->"); + try printLinkedList(next_node, writer); + } + } + }; +} + +pub fn tree(comptime T: type, root: ?*const TreeNode(T)) TreeFormatter(T) { + return .{ .root = root }; +} + +pub fn TreeFormatter(comptime T: type) type { + return struct { + const Self = @This(); + + root: ?*const TreeNode(T), + + pub fn format( + self: Self, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) !void { + try printTree(self.root, null, false, writer); + } + + // Print a binary tree + fn printTree(root: ?*const TreeNode(T), prev: ?*Trunk, isRight: bool, writer: anytype) !void { + if (root == null) { + return; + } + + var prev_str = " "; + var trunk = Trunk{ .prev = prev, .str = prev_str }; + + try printTree(root.?.right, &trunk, true, writer); + + if (prev == null) { + trunk.str = "———"; + } else if (isRight) { + trunk.str = "/———"; + prev_str = " |"; + } else { + trunk.str = "\\———"; + prev.?.str = prev_str; + } + + try showTrunks(&trunk, writer); + try std.fmt.format(writer, "{d}\n", .{root.?.val}); + + if (prev) |_| { + prev.?.str = prev_str; + } + trunk.str = " |"; + + try printTree(root.?.left, &trunk, false, writer); + } + + // Print a binary tree + // This tree printer is borrowed from TECHIE DELIGHT + // https://www.techiedelight.com/c-program-print-binary-tree/ + const Trunk = struct { + prev: ?*Trunk = null, + str: []const u8 = undefined, + + pub fn init(self: *Trunk, prev: ?*Trunk, str: []const u8) void { + self.prev = prev; + self.str = str; + } + }; + + pub fn showTrunks(p: ?*Trunk, writer: anytype) !void { + if (p == null) return; + try showTrunks(p.?.prev, writer); + try std.fmt.format(writer, "{s}", .{p.?.str}); + } + }; +} diff --git a/en/codes/zig/utils/utils.zig b/en/codes/zig/utils/utils.zig new file mode 100644 index 000000000..a3d53797c --- /dev/null +++ b/en/codes/zig/utils/utils.zig @@ -0,0 +1,8 @@ +// File: format.zig +// Created Time: 2025-07-15 +// Author: CreatorMetaSky (creator_meta_sky@163.com) + +const std = @import("std"); +pub const fmt = @import("format.zig"); +pub const ListNode = @import("ListNode.zig").ListNode; +pub const TreeNode = @import("TreeNode.zig").TreeNode; diff --git a/en/docs/assets/avatar/avatar_Dr-XYZ.jpg b/en/docs/assets/avatar/avatar_Dr-XYZ.jpg new file mode 100644 index 000000000..d29b06aaf Binary files /dev/null and b/en/docs/assets/avatar/avatar_Dr-XYZ.jpg differ diff --git a/en/docs/assets/avatar/avatar_Gonglja.jpg b/en/docs/assets/avatar/avatar_Gonglja.jpg new file mode 100644 index 000000000..3bbcc6504 Binary files /dev/null and b/en/docs/assets/avatar/avatar_Gonglja.jpg differ diff --git a/en/docs/assets/avatar/avatar_K3v123.jpg b/en/docs/assets/avatar/avatar_K3v123.jpg new file mode 100644 index 000000000..2ae215d62 Binary files /dev/null and b/en/docs/assets/avatar/avatar_K3v123.jpg differ diff --git a/en/docs/assets/avatar/avatar_Phoenix0415.jpg b/en/docs/assets/avatar/avatar_Phoenix0415.jpg new file mode 100644 index 000000000..3de4ccd94 Binary files /dev/null and b/en/docs/assets/avatar/avatar_Phoenix0415.jpg differ diff --git a/en/docs/assets/avatar/avatar_QiLOL.jpg b/en/docs/assets/avatar/avatar_QiLOL.jpg new file mode 100644 index 000000000..4d916dbc8 Binary files /dev/null and b/en/docs/assets/avatar/avatar_QiLOL.jpg differ diff --git a/en/docs/assets/avatar/avatar_RafaelCaso.jpg b/en/docs/assets/avatar/avatar_RafaelCaso.jpg new file mode 100644 index 000000000..b9ba65d94 Binary files /dev/null and b/en/docs/assets/avatar/avatar_RafaelCaso.jpg differ diff --git a/en/docs/assets/avatar/avatar_Reanon.jpg b/en/docs/assets/avatar/avatar_Reanon.jpg new file mode 100644 index 000000000..cd426218f Binary files /dev/null and b/en/docs/assets/avatar/avatar_Reanon.jpg differ diff --git a/en/docs/assets/avatar/avatar_SamJin98.jpg b/en/docs/assets/avatar/avatar_SamJin98.jpg new file mode 100644 index 000000000..8321f13c6 Binary files /dev/null and b/en/docs/assets/avatar/avatar_SamJin98.jpg differ diff --git a/en/docs/assets/avatar/avatar_Shyam-Chen.jpg b/en/docs/assets/avatar/avatar_Shyam-Chen.jpg new file mode 100644 index 000000000..5273edf37 Binary files /dev/null and b/en/docs/assets/avatar/avatar_Shyam-Chen.jpg differ diff --git a/en/docs/assets/avatar/avatar_coderonion.jpg b/en/docs/assets/avatar/avatar_coderonion.jpg new file mode 100644 index 000000000..53410ddc4 Binary files /dev/null and b/en/docs/assets/avatar/avatar_coderonion.jpg differ diff --git a/en/docs/assets/avatar/avatar_curtishd.jpg b/en/docs/assets/avatar/avatar_curtishd.jpg new file mode 100644 index 000000000..40d47610b Binary files /dev/null and b/en/docs/assets/avatar/avatar_curtishd.jpg differ diff --git a/en/docs/assets/avatar/avatar_eltociear.jpg b/en/docs/assets/avatar/avatar_eltociear.jpg new file mode 100644 index 000000000..5af9de966 Binary files /dev/null and b/en/docs/assets/avatar/avatar_eltociear.jpg differ diff --git a/en/docs/assets/avatar/avatar_gvenusleo.jpg b/en/docs/assets/avatar/avatar_gvenusleo.jpg new file mode 100644 index 000000000..40f4dd251 Binary files /dev/null and b/en/docs/assets/avatar/avatar_gvenusleo.jpg differ diff --git a/en/docs/assets/avatar/avatar_hpstory.jpg b/en/docs/assets/avatar/avatar_hpstory.jpg new file mode 100644 index 000000000..fa98c89ab Binary files /dev/null and b/en/docs/assets/avatar/avatar_hpstory.jpg differ diff --git a/en/docs/assets/avatar/avatar_justin-tse.jpg b/en/docs/assets/avatar/avatar_justin-tse.jpg new file mode 100644 index 000000000..d3260f318 Binary files /dev/null and b/en/docs/assets/avatar/avatar_justin-tse.jpg differ diff --git a/en/docs/assets/avatar/avatar_khoaxuantu.jpg b/en/docs/assets/avatar/avatar_khoaxuantu.jpg new file mode 100644 index 000000000..f7e1e8f3c Binary files /dev/null and b/en/docs/assets/avatar/avatar_khoaxuantu.jpg differ diff --git a/en/docs/assets/avatar/avatar_krahets.jpg b/en/docs/assets/avatar/avatar_krahets.jpg new file mode 100644 index 000000000..9ab8259e7 Binary files /dev/null and b/en/docs/assets/avatar/avatar_krahets.jpg differ diff --git a/en/docs/assets/avatar/avatar_magentaqin.jpg b/en/docs/assets/avatar/avatar_magentaqin.jpg new file mode 100644 index 000000000..e8b7ddee5 Binary files /dev/null and b/en/docs/assets/avatar/avatar_magentaqin.jpg differ diff --git a/en/docs/assets/avatar/avatar_night-cruise.jpg b/en/docs/assets/avatar/avatar_night-cruise.jpg new file mode 100644 index 000000000..4dd6f70f7 Binary files /dev/null and b/en/docs/assets/avatar/avatar_night-cruise.jpg differ diff --git a/en/docs/assets/avatar/avatar_nuomi1.jpg b/en/docs/assets/avatar/avatar_nuomi1.jpg new file mode 100644 index 000000000..d7d3a67aa Binary files /dev/null and b/en/docs/assets/avatar/avatar_nuomi1.jpg differ diff --git a/en/docs/assets/avatar/avatar_pengchzn.jpg b/en/docs/assets/avatar/avatar_pengchzn.jpg new file mode 100644 index 000000000..73732dc83 Binary files /dev/null and b/en/docs/assets/avatar/avatar_pengchzn.jpg differ diff --git a/en/docs/assets/avatar/avatar_rongyi.jpg b/en/docs/assets/avatar/avatar_rongyi.jpg new file mode 100644 index 000000000..6ef6f0e0b Binary files /dev/null and b/en/docs/assets/avatar/avatar_rongyi.jpg differ diff --git a/en/docs/assets/avatar/avatar_shevkun.jpg b/en/docs/assets/avatar/avatar_shevkun.jpg new file mode 100644 index 000000000..aa660a246 Binary files /dev/null and b/en/docs/assets/avatar/avatar_shevkun.jpg differ diff --git a/en/docs/assets/avatar/avatar_thomasq0.jpg b/en/docs/assets/avatar/avatar_thomasq0.jpg new file mode 100644 index 000000000..beeb6d96c Binary files /dev/null and b/en/docs/assets/avatar/avatar_thomasq0.jpg differ diff --git a/en/docs/assets/avatar/avatar_yanedie.jpg b/en/docs/assets/avatar/avatar_yanedie.jpg new file mode 100644 index 000000000..8867a24fc Binary files /dev/null and b/en/docs/assets/avatar/avatar_yanedie.jpg differ diff --git a/en/docs/assets/avatar/avatar_yudongjin.jpg b/en/docs/assets/avatar/avatar_yudongjin.jpg new file mode 100644 index 000000000..865c9028f Binary files /dev/null and b/en/docs/assets/avatar/avatar_yudongjin.jpg differ diff --git a/en/docs/assets/avatar/avatar_yuelinxin.jpg b/en/docs/assets/avatar/avatar_yuelinxin.jpg new file mode 100644 index 000000000..f1ab8b96a Binary files /dev/null and b/en/docs/assets/avatar/avatar_yuelinxin.jpg differ diff --git a/en/docs/assets/avatar/avatar_yuyanhuang.jpg b/en/docs/assets/avatar/avatar_yuyanhuang.jpg new file mode 100644 index 000000000..55bbb528a Binary files /dev/null and b/en/docs/assets/avatar/avatar_yuyanhuang.jpg differ diff --git a/en/docs/assets/hero/astronaut.png b/en/docs/assets/hero/astronaut.png new file mode 100644 index 000000000..2d63a9f7a Binary files /dev/null and b/en/docs/assets/hero/astronaut.png differ diff --git a/en/docs/assets/hero/chapter_array_and_linkedlist.png b/en/docs/assets/hero/chapter_array_and_linkedlist.png new file mode 100644 index 000000000..376c1da62 Binary files /dev/null and b/en/docs/assets/hero/chapter_array_and_linkedlist.png differ diff --git a/en/docs/assets/hero/chapter_backtracking.png b/en/docs/assets/hero/chapter_backtracking.png new file mode 100644 index 000000000..af259be6f Binary files /dev/null and b/en/docs/assets/hero/chapter_backtracking.png differ diff --git a/en/docs/assets/hero/chapter_computational_complexity.png b/en/docs/assets/hero/chapter_computational_complexity.png new file mode 100644 index 000000000..7e5bf9eaf Binary files /dev/null and b/en/docs/assets/hero/chapter_computational_complexity.png differ diff --git a/en/docs/assets/hero/chapter_divide_and_conquer.png b/en/docs/assets/hero/chapter_divide_and_conquer.png new file mode 100644 index 000000000..63794b27c Binary files /dev/null and b/en/docs/assets/hero/chapter_divide_and_conquer.png differ diff --git a/en/docs/assets/hero/chapter_dynamic_programming.png b/en/docs/assets/hero/chapter_dynamic_programming.png new file mode 100644 index 000000000..77b05dc49 Binary files /dev/null and b/en/docs/assets/hero/chapter_dynamic_programming.png differ diff --git a/en/docs/assets/hero/chapter_graph.png b/en/docs/assets/hero/chapter_graph.png new file mode 100644 index 000000000..667ecb822 Binary files /dev/null and b/en/docs/assets/hero/chapter_graph.png differ diff --git a/en/docs/assets/hero/chapter_greedy.png b/en/docs/assets/hero/chapter_greedy.png new file mode 100644 index 000000000..2ffc33448 Binary files /dev/null and b/en/docs/assets/hero/chapter_greedy.png differ diff --git a/en/docs/assets/hero/chapter_hashing.png b/en/docs/assets/hero/chapter_hashing.png new file mode 100644 index 000000000..cf375aa7c Binary files /dev/null and b/en/docs/assets/hero/chapter_hashing.png differ diff --git a/en/docs/assets/hero/chapter_heap.png b/en/docs/assets/hero/chapter_heap.png new file mode 100644 index 000000000..97e3bef91 Binary files /dev/null and b/en/docs/assets/hero/chapter_heap.png differ diff --git a/en/docs/assets/hero/chapter_searching.png b/en/docs/assets/hero/chapter_searching.png new file mode 100644 index 000000000..c694bd145 Binary files /dev/null and b/en/docs/assets/hero/chapter_searching.png differ diff --git a/en/docs/assets/hero/chapter_sorting.png b/en/docs/assets/hero/chapter_sorting.png new file mode 100644 index 000000000..1863d864d Binary files /dev/null and b/en/docs/assets/hero/chapter_sorting.png differ diff --git a/en/docs/assets/hero/chapter_stack_and_queue.png b/en/docs/assets/hero/chapter_stack_and_queue.png new file mode 100644 index 000000000..c8581279a Binary files /dev/null and b/en/docs/assets/hero/chapter_stack_and_queue.png differ diff --git a/en/docs/assets/hero/chapter_tree.png b/en/docs/assets/hero/chapter_tree.png new file mode 100644 index 000000000..117fc603a Binary files /dev/null and b/en/docs/assets/hero/chapter_tree.png differ diff --git a/en/docs/assets/hero/cover_render.png b/en/docs/assets/hero/cover_render.png new file mode 100644 index 000000000..ec1364272 Binary files /dev/null and b/en/docs/assets/hero/cover_render.png differ diff --git a/en/docs/assets/hero/ground.png b/en/docs/assets/hero/ground.png new file mode 100644 index 000000000..eece5b0bd Binary files /dev/null and b/en/docs/assets/hero/ground.png differ diff --git a/en/docs/assets/hero/links.png b/en/docs/assets/hero/links.png new file mode 100644 index 000000000..a0189f949 Binary files /dev/null and b/en/docs/assets/hero/links.png differ diff --git a/en/docs/assets/hero/pdf_ipad.png b/en/docs/assets/hero/pdf_ipad.png new file mode 100644 index 000000000..f32bf06fe Binary files /dev/null and b/en/docs/assets/hero/pdf_ipad.png differ diff --git a/en/docs/assets/hero/universe_bg.png b/en/docs/assets/hero/universe_bg.png new file mode 100644 index 000000000..9da001555 Binary files /dev/null and b/en/docs/assets/hero/universe_bg.png differ diff --git a/en/docs/assets/hero/web_mac_iphone.png b/en/docs/assets/hero/web_mac_iphone.png new file mode 100644 index 000000000..514834eaf Binary files /dev/null and b/en/docs/assets/hero/web_mac_iphone.png differ diff --git a/en/docs/chapter_appendix/contribution.md b/en/docs/chapter_appendix/contribution.md index 440894580..d5801d5cc 100644 --- a/en/docs/chapter_appendix/contribution.md +++ b/en/docs/chapter_appendix/contribution.md @@ -1,40 +1,40 @@ -# Contributing Together +# Contribute to the book -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. +Despite the author's best efforts, this book may contain omissions and errors. Thank you for your understanding. If you find typos, broken links, missing content, ambiguous wording, unclear explanations, or poorly organized passages, please help us correct them so that we can provide a better learning resource. -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. +To thank everyone for their generous contributions to the open-source community, the GitHub IDs of all [contributors](https://github.com/krahets/hello-algo/graphs/contributors) are displayed on the home pages of the book's repository, website, and PDF edition. -!!! success "The Charm of Open Source" +!!! success "The beauty of open source" - The interval between two printings of a physical book is often quite long, making content updates very inconvenient. + Printed books often have long intervals between print runs, making their content difficult to update. + + In this open-source book, updates can be made within days or even hours. - In this open source book, the time for content updates has been shortened to just days or even hours. +### Small edits -### Minor Content Adjustments +As shown in the figure below, each page has an edit icon in its upper-right corner. Follow these steps to edit text or code. -As shown in the figure below, there is an "edit icon" in the top-right corner of each page. You can modify text or code by following these steps. - -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 new page loads, click the "Create pull request" button to submit your pull request. +1. Click the edit icon. If you are prompted to fork the repository, agree to do so. +2. Edit the Markdown source file, check that the content is correct, and keep the formatting as consistent as possible. +3. Enter a description of your changes at the bottom of the page, then click "Propose file change". On the next page, click "Create pull request" to submit your pull request. ![Page edit button](contribution.assets/edit_markdown.png) -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. +Images cannot be edited directly. Please describe the problem in a new [issue](https://github.com/krahets/hello-algo/issues) or a comment, and we will redraw and replace the image as soon as possible. -### Content Creation +### Creating content -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. +If you would like to contribute to this open-source project, for example by translating code into another programming language or expanding an article, follow this pull request workflow. -1. Log in to GitHub and Fork the book's [code repository](https://github.com/krahets/hello-algo) to your personal account. -2. Go to your forked repository page 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. +1. Sign in to GitHub and fork the book's [code repository](https://github.com/krahets/hello-algo) to your own account. +2. Open your fork's repository page and use `git clone` to clone it to your computer. +3. Create your content locally and test it thoroughly to verify that the code is correct. +4. Commit your local changes, then push them to your remote repository. +5. Refresh the repository page and click "Create pull request" to submit your pull request. -### Docker Deployment +### Deploying with Docker -From the root directory of `hello-algo`, run the following Docker command to access the project at `http://localhost:8000`: +Run the following Docker command in the `hello-algo` root directory to access the English edition at `http://localhost:8000/en/`: ```shell docker-compose up -d diff --git a/en/docs/chapter_appendix/installation.md b/en/docs/chapter_appendix/installation.md index eb62fac06..1634b6f90 100644 --- a/en/docs/chapter_appendix/installation.md +++ b/en/docs/chapter_appendix/installation.md @@ -1,68 +1,68 @@ -# Programming Environment Installation +# Setting up a programming environment -## Installing IDE +## Install an IDE -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. +We recommend VS Code, a lightweight, open-source integrated development environment (IDE). Visit the [VS Code website](https://code.visualstudio.com/), then download and install the version for your operating system. -![Download VS Code from the Official Website](installation.assets/vscode_installation.png) +![Downloading VS Code from the official website](installation.assets/vscode_installation.png) -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. +VS Code has an extensive ecosystem of extensions that support running and debugging code in most programming languages. For example, installing the Python Extension Pack lets you debug Python code. The installation steps are shown in the figure below. -![Install VS Code Extensions](installation.assets/vscode_extension_installation.png) +![Installing VS Code extensions](installation.assets/vscode_extension_installation.png) -## Installing Language Environments +## Install a language environment -### Python Environment +### Python -1. Download and install [Miniconda3](https://docs.conda.io/en/latest/miniconda.html) with Python 3.10 or later. -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. +1. Download and install [Miniconda3](https://docs.conda.io/en/latest/miniconda.html). Python 3.10 or later is required. +2. Search for `python` in the VS Code Extensions Marketplace and install Python Extension Pack. +3. (Optional) Run `pip install black` in a terminal to install a code formatter. -### C/C++ Environment +### C/C++ -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 }`. +1. On Windows, install [MinGW](https://sourceforge.net/projects/mingw-w64/files/) ([setup guide](https://blog.csdn.net/qq_33698226/article/details/129031241)). macOS comes with Clang, so no installation is needed. +2. Search for `c++` in the VS Code Extensions Marketplace and install C/C++ Extension Pack. +3. (Optional) Open Settings, search for the `Clang_format_fallback Style` formatting option, and set it to `{ BasedOnStyle: Microsoft, BreakBeforeBraces: Attach }`. -### Java Environment +### Java -1. Download and install [OpenJDK](https://jdk.java.net/18/) (version 10 or later). -2. Search for `java` in the VS Code extension marketplace and install the Extension Pack for Java. +1. Download and install [OpenJDK](https://jdk.java.net/18/) (a version later than JDK 9 is required). +2. Search for `java` in the VS Code Extensions Marketplace and install Extension Pack for Java. -### C# Environment +### C\# 1. Download and install [.NET 8.0](https://dotnet.microsoft.com/en-us/download). -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)). +2. Search for `C# Dev Kit` in the VS Code Extensions Marketplace and install C# Dev Kit ([setup guide](https://code.visualstudio.com/docs/csharp/get-started)). +3. You can also use Visual Studio ([installation guide](https://learn.microsoft.com/en-us/visualstudio/install/install-visual-studio?view=vs-2022)). -### Go Environment +### Go 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. +2. Search for `go` in the VS Code Extensions Marketplace and install Go. +3. Press `Ctrl + Shift + P` to open the Command Palette, enter go, select `Go: Install/Update Tools`, then select and install all the tools. -### Swift Environment +### Swift 1. Download and install [Swift](https://www.swift.org/download/). -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). +2. Search for `swift` in the VS Code Extensions Marketplace and install [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang). -### JavaScript Environment +### JavaScript 1. Download and install [Node.js](https://nodejs.org/en/). -2. (Optional) Search for `Prettier` in the VS Code extension marketplace and install the code formatter. +2. (Optional) Search for `Prettier` in the VS Code Extensions Marketplace and install this code formatter. -### TypeScript Environment +### TypeScript -1. Follow the same installation steps as the JavaScript environment. +1. Follow the JavaScript environment setup steps. 2. Install [TypeScript Execute (tsx)](https://github.com/privatenumber/tsx?tab=readme-ov-file#global-installation). -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). +3. Search for `typescript` in the VS Code Extensions Marketplace and install [Pretty TypeScript Errors](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors). -### Dart Environment +### Dart 1. Download and install [Dart](https://dart.dev/get-dart). -2. Search for `dart` in the VS Code extension marketplace and install [Dart](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code). +2. Search for `dart` in the VS Code Extensions Marketplace and install [Dart](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code). -### Rust Environment +### Rust 1. Download and install [Rust](https://www.rust-lang.org/tools/install). -2. Search for `rust` in the VS Code extension marketplace and install [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). +2. Search for `rust` in the VS Code Extensions 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 27120ceda..225e7e271 100644 --- a/en/docs/chapter_appendix/terminology.md +++ b/en/docs/chapter_appendix/terminology.md @@ -1,136 +1,136 @@ # Glossary -The following table lists important terms that appear in this book. +The table below lists important terms used in this book. Familiarity with their English names will help when reading technical literature in English. -

Table   Important Terms in Data Structures and Algorithms

+

Table   Key terms in data structures and algorithms

-| English | -| ------------------------------ | -| algorithm | -| data structure | -| code | -| file | -| function | -| method | -| variable | -| asymptotic complexity analysis | -| time complexity | -| space complexity | -| loop | -| iteration | -| recursion | -| tail recursion | -| recursion tree | -| big-$O$ notation | -| asymptotic upper bound | -| sign-magnitude | -| 1’s complement | -| 2’s complement | -| array | -| index | -| linked list | -| linked list node, list node | -| head node | -| tail node | -| list | -| dynamic array | -| hard disk | -| random-access memory (RAM) | -| cache memory | -| cache miss | -| cache hit rate | -| stack | -| top of the stack | -| bottom of the stack | -| queue | -| double-ended queue | -| front of the queue | -| rear of the queue | -| hash table | -| hash set | -| bucket | -| hash function | -| hash collision | -| load factor | -| separate chaining | -| open addressing | -| linear probing | -| lazy deletion | -| binary tree | -| tree node | -| left-child node | -| right-child node | -| parent node | -| left subtree | -| right subtree | -| root node | -| leaf node | -| edge | -| level | -| degree | -| height | -| depth | -| perfect binary tree | -| complete binary tree | -| full binary tree | -| balanced binary tree | -| binary search tree | -| AVL tree | -| red-black tree | -| level-order traversal | -| breadth-first traversal | -| depth-first traversal | -| pre-order traversal | -| in-order traversal | -| post-order traversal | -| balanced binary search tree | -| balance factor | -| heap | -| max heap | -| min heap | -| priority queue | -| heapify | -| top-$k$ problem | -| graph | -| vertex | -| undirected graph | -| directed graph | -| connected graph | -| disconnected graph | -| weighted graph | -| adjacency | -| path | -| in-degree | -| out-degree | -| adjacency matrix | -| adjacency list | -| breadth-first search | -| depth-first search | -| binary search | -| searching algorithm | -| sorting algorithm | -| selection sort | -| bubble sort | -| insertion sort | -| quick sort | -| merge sort | -| heap sort | -| bucket sort | -| counting sort | -| radix sort | -| divide and conquer | -| Tower of Hanoi problem | -| backtracking algorithm | -| constraint | -| solution | -| state | -| pruning | -| permutations problem | -| subset-sum problem | -| $n$-queens problem | -| dynamic programming | -| initial state | -| state-transition equation | -| knapsack problem | -| edit distance problem | -| greedy algorithm | +| English | Simplified Chinese | +| ------------------------------ | -------------- | +| algorithm | 算法 | +| data structure | 数据结构 | +| code | 代码 | +| file | 文件 | +| function | 函数 | +| method | 方法 | +| variable | 变量 | +| asymptotic complexity analysis | 渐近复杂度分析 | +| time complexity | 时间复杂度 | +| space complexity | 空间复杂度 | +| loop | 循环 | +| iteration | 迭代 | +| recursion | 递归 | +| tail recursion | 尾递归 | +| recursion tree | 递归树 | +| big-$O$ notation | 大 $O$ 记号 | +| asymptotic upper bound | 渐近上界 | +| sign-magnitude | 原码 | +| one's complement | 反码 | +| two's complement | 补码 | +| array | 数组 | +| index | 索引 | +| linked list | 链表 | +| linked list node, list node | 链表节点 | +| head node | 头节点 | +| tail node | 尾节点 | +| list | 列表 | +| dynamic array | 动态数组 | +| hard disk | 硬盘 | +| random-access memory (RAM) | 内存 | +| cache memory | 缓存 | +| cache miss | 缓存未命中 | +| cache hit rate | 缓存命中率 | +| stack | 栈 | +| top of the stack | 栈顶 | +| bottom of the stack | 栈底 | +| queue | 队列 | +| double-ended queue | 双向队列 | +| front of the queue | 队首 | +| rear of the queue | 队尾 | +| hash table | 哈希表 | +| hash set | 哈希集合 | +| bucket | 桶 | +| hash function | 哈希函数 | +| hash collision | 哈希冲突 | +| load factor | 负载因子 | +| separate chaining | 链式地址 | +| open addressing | 开放寻址 | +| linear probing | 线性探测 | +| lazy deletion | 懒删除 | +| binary tree | 二叉树 | +| tree node | 树节点 | +| left-child node | 左子节点 | +| right-child node | 右子节点 | +| parent node | 父节点 | +| left subtree | 左子树 | +| right subtree | 右子树 | +| root node | 根节点 | +| leaf node | 叶节点 | +| edge | 边 | +| level | 层 | +| degree | 度 | +| height | 高度 | +| depth | 深度 | +| perfect binary tree | 完美二叉树 | +| complete binary tree | 完全二叉树 | +| full binary tree | 完满二叉树 | +| balanced binary tree | 平衡二叉树 | +| binary search tree | 二叉搜索树 | +| AVL tree | AVL 树 | +| red-black tree | 红黑树 | +| level-order traversal | 层序遍历 | +| breadth-first traversal | 广度优先遍历 | +| depth-first traversal | 深度优先遍历 | +| preorder traversal | 前序遍历 | +| inorder traversal | 中序遍历 | +| postorder traversal | 后序遍历 | +| balanced binary search tree | 平衡二叉搜索树 | +| balance factor | 平衡因子 | +| heap | 堆 | +| max heap | 大顶堆 | +| min heap | 小顶堆 | +| priority queue | 优先队列 | +| heapify | 堆化 | +| top-$k$ problem | Top-$k$ 问题 | +| graph | 图 | +| vertex | 顶点 | +| undirected graph | 无向图 | +| directed graph | 有向图 | +| connected graph | 连通图 | +| disconnected graph | 非连通图 | +| weighted graph | 有权图 | +| adjacency | 邻接 | +| path | 路径 | +| in-degree | 入度 | +| out-degree | 出度 | +| adjacency matrix | 邻接矩阵 | +| adjacency list | 邻接表 | +| breadth-first search | 广度优先搜索 | +| depth-first search | 深度优先搜索 | +| binary search | 二分查找 | +| searching algorithm | 搜索算法 | +| sorting algorithm | 排序算法 | +| selection sort | 选择排序 | +| bubble sort | 冒泡排序 | +| insertion sort | 插入排序 | +| quick sort | 快速排序 | +| merge sort | 归并排序 | +| heap sort | 堆排序 | +| bucket sort | 桶排序 | +| counting sort | 计数排序 | +| radix sort | 基数排序 | +| divide and conquer | 分治 | +| Tower of Hanoi problem | 汉诺塔问题 | +| backtracking algorithm | 回溯算法 | +| constraint | 约束 | +| solution | 解 | +| state | 状态 | +| pruning | 剪枝 | +| permutations problem | 全排列问题 | +| subset-sum problem | 子集和问题 | +| $n$-queens problem | $n$ 皇后问题 | +| dynamic programming | 动态规划 | +| initial state | 初始状态 | +| state transition equation | 状态转移方程 | +| knapsack problem | 背包问题 | +| edit distance problem | 编辑距离问题 | +| greedy algorithm | 贪心算法 | diff --git a/en/docs/chapter_array_and_linkedlist/array.md b/en/docs/chapter_array_and_linkedlist/array.md index 6e27f36a9..784f685ab 100755 --- a/en/docs/chapter_array_and_linkedlist/array.md +++ b/en/docs/chapter_array_and_linkedlist/array.md @@ -1,19 +1,19 @@ -# Array +# Arrays -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. The figure below illustrates the main concepts and storage method of arrays. +An array is a linear data structure that stores elements of the same type in contiguous memory. An element's position in the array is its index. The figure below shows the main concepts and storage layout. -![Array definition and storage method](array.assets/array_definition.png) +![Array definition and storage layout](array.assets/array_definition.png) -## Common Array Operations +## Common array operations -### Initializing Arrays +### Initializing an array -We can choose between two array initialization methods based on our needs: with or without initial values. When no initial values are specified, most programming languages initialize array elements to $0$: +We can initialize an array with or without explicit values. When values are not specified, most programming languages initialize the elements to $0$: === "Python" ```python title="array.py" - # Initialize array + # Initialize arrays arr: list[int] = [0] * 5 # [ 0, 0, 0, 0, 0 ] nums: list[int] = [1, 3, 2, 5, 4] ``` @@ -21,11 +21,11 @@ We can choose between two array initialization methods based on our needs: with === "C++" ```cpp title="array.cpp" - /* Initialize array */ - // Stored on stack + /* Initialize arrays */ + // Stored on the stack int arr[5] = {}; int nums[5] = { 1, 3, 2, 5, 4 }; - // Stored on heap (requires manual memory release) + // Stored on the heap (memory must be freed manually) int* arr1 = new int[5] {}; int* nums1 = new int[5] { 1, 3, 2, 5, 4 }; ``` @@ -33,7 +33,7 @@ We can choose between two array initialization methods based on our needs: with === "Java" ```java title="array.java" - /* Initialize array */ + /* Initialize arrays */ int[] arr = new int[5]; // { 0, 0, 0, 0, 0 } int[] nums = { 1, 3, 2, 5, 4 }; ``` @@ -41,7 +41,7 @@ We can choose between two array initialization methods based on our needs: with === "C#" ```csharp title="array.cs" - /* Initialize array */ + /* Initialize arrays */ int[] arr = new int[5]; // [ 0, 0, 0, 0, 0 ] int[] nums = [1, 3, 2, 5, 4]; ``` @@ -49,18 +49,18 @@ We can choose between two array initialization methods based on our needs: with === "Go" ```go title="array.go" - /* Initialize array */ + /* Initialize arrays */ var arr [5]int - // 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 + // In Go, [5]int is an array with a specified length; []int is a slice + // Go array lengths are determined at compile time and must be constants + // To make extend() easier to implement, we treat slices as arrays below nums := []int{1, 3, 2, 5, 4} ``` === "Swift" ```swift title="array.swift" - /* Initialize array */ + /* Initialize arrays */ let arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0] let nums = [1, 3, 2, 5, 4] ``` @@ -68,7 +68,7 @@ We can choose between two array initialization methods based on our needs: with === "JS" ```javascript title="array.js" - /* Initialize array */ + /* Initialize arrays */ var arr = new Array(5).fill(0); var nums = [1, 3, 2, 5, 4]; ``` @@ -76,7 +76,7 @@ We can choose between two array initialization methods based on our needs: with === "TS" ```typescript title="array.ts" - /* Initialize array */ + /* Initialize arrays */ let arr: number[] = new Array(5).fill(0); let nums: number[] = [1, 3, 2, 5, 4]; ``` @@ -84,7 +84,7 @@ We can choose between two array initialization methods based on our needs: with === "Dart" ```dart title="array.dart" - /* Initialize array */ + /* Initialize arrays */ List arr = List.filled(5, 0); // [0, 0, 0, 0, 0] List nums = [1, 3, 2, 5, 4]; ``` @@ -92,20 +92,20 @@ We can choose between two array initialization methods based on our needs: with === "Rust" ```rust title="array.rs" - /* Initialize array */ + /* Initialize arrays */ let arr: [i32; 5] = [0; 5]; // [0, 0, 0, 0, 0] let slice: &[i32] = &[0; 5]; - // 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 + // In Rust, [i32; 5] is an array with a specified length; &[i32] is a slice + // Rust array lengths are determined at compile time and must be constants + // Vector is Rust's usual dynamic array type + // To make extend() easier to implement, we treat a vector as an array below let nums: Vec = vec![1, 3, 2, 5, 4]; ``` === "C" ```c title="array.c" - /* Initialize array */ + /* Initialize arrays */ int arr[5] = { 0 }; // { 0, 0, 0, 0, 0 } int nums[5] = { 1, 3, 2, 5, 4 }; ``` @@ -113,7 +113,7 @@ We can choose between two array initialization methods based on our needs: with === "Kotlin" ```kotlin title="array.kt" - /* Initialize array */ + /* Initialize arrays */ var arr = IntArray(5) // { 0, 0, 0, 0, 0 } var nums = intArrayOf(1, 3, 2, 5, 4) ``` @@ -121,107 +121,107 @@ We can choose between two array initialization methods based on our needs: with === "Ruby" ```ruby title="array.rb" - # Initialize array + # Initialize arrays arr = Array.new(5, 0) nums = [1, 3, 2, 5, 4] ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%0Aarr%20%3D%20%5B0%5D%20*%205%20%20%23%20%5B%200,%200,%200,%200,%200%20%5D%0Anums%20%3D%20%5B1,%203,%202,%205,%204%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%23%20Initialize%20arrays%0Aarr%20%3D%20%5B0%5D%20%2A%205%20%20%23%20%5B%200%2C%200%2C%200%2C%200%2C%200%20%5D%0Anums%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Accessing Elements +### Accessing an element -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 the figure below to calculate the element's memory address and directly access that element. +Because array elements occupy contiguous memory, their addresses are easy to calculate. Given the array's address (the address of its first element) and an index, the formula in the figure below gives the element's address, allowing direct access. -![Memory address calculation for array elements](array.assets/array_memory_location_calculation.png) +![Calculating an array element's memory address](array.assets/array_memory_location_calculation.png) -Observing the figure above, 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$. +The first element has index $0$, which may seem unintuitive because counting from $1$ feels more natural. However, **an index essentially describes an offset from the starting memory address**. The first element's offset is $0$, so index $0$ makes sense. -Accessing elements in an array is highly efficient; we can randomly access any element in the array in $O(1)$ time. +Array access is very efficient: any element can be accessed directly in $O(1)$ time. ```src [file]{array}-[class]{}-[func]{random_access} ``` -### Inserting Elements +### Inserting an element -Array elements are packed tightly together in memory, with no extra space between them for additional data. As shown in the figure below, if we want to insert an element in the middle of an array, we need to shift all subsequent elements one position to the right and then assign the value at that index. +Array elements sit next to one another, with no room between them for more data. As shown in the figure below, inserting into the middle requires shifting every element at or after the insertion point one position toward the end, then assigning the new element at the desired index. -![Example of inserting an element into an array](array.assets/array_insert_element.png) +![Inserting an element into an array](array.assets/array_insert_element.png) -It is worth noting that since the length of an array is fixed, inserting an element will inevitably push the last element out of the array. We will leave the solution to this problem for discussion in the "List" chapter. +Because an array's length is fixed, inserting an element causes the last element to be lost. We will discuss the solution in the section on lists. ```src [file]{array}-[class]{}-[func]{insert} ``` -### Removing Elements +### Removing an element -Similarly, as shown in the figure below, to delete the element at index $i$, we need to shift all elements after index $i$ forward by one position. +Similarly, as shown in the figure below, removing the element at index $i$ requires shifting every element after $i$ one position toward the beginning. -![Example of removing an element from an array](array.assets/array_remove_element.png) +![Removing an element from an array](array.assets/array_remove_element.png) -Note that after the deletion is complete, the original last element is no longer meaningful, so we do not need to modify it explicitly. +After removal, the old final entry is no longer meaningful, so we do not need to change it explicitly. ```src [file]{array}-[class]{}-[func]{remove} ``` -Overall, array insertion and deletion operations have the following drawbacks: +Overall, inserting and removing array elements have these drawbacks. -- **High time complexity**: The average time complexity for both insertion and deletion in arrays is $O(n)$, where $n$ is the length of the array. -- **Loss of elements**: Since the length of an array is immutable, after inserting an element, elements that exceed the array's length will be lost. -- **Memory waste**: We can initialize a relatively long array and use only the front portion, so that any overwritten tail elements are merely unused placeholders, but this wastes some memory space. +- **High time complexity**: Both operations take $O(n)$ time on average, where $n$ is the array length. +- **Lost elements**: Since the length is fixed, insertion loses any element pushed beyond the array's bounds. +- **Wasted memory**: We can allocate a longer array and use only its beginning, so any final entries lost during insertion are unused. However, this leaves some memory wasted. -### Traversing Arrays +### Traversing an array -In most programming languages, we can traverse an array either by index or by directly iterating through each element in the array: +Most programming languages let us traverse an array either by index or by visiting its elements directly: ```src [file]{array}-[class]{}-[func]{traverse} ``` -### Finding Elements +### Finding an element -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. +To find a specified element, traverse the array and compare each value with the target. Return the index when a match is found. -Since an array is a linear data structure, the above search operation is called a "linear search". +Because an array is a linear data structure, this is called linear search. ```src [file]{array}-[class]{}-[func]{find} ``` -### Expanding Arrays +### Expanding an array -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**. +In a complex system, a program cannot ensure that memory immediately after an array is available, so extending it in place is unsafe. Therefore, in most programming languages, **an array's length is fixed**. -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: +To expand an array, create a larger one and copy every element into it. This takes $O(n)$ time and can be expensive for a large array. The code is as follows: ```src [file]{array}-[class]{}-[func]{extend} ``` -## Advantages and Limitations of Arrays +## Strengths and limitations of arrays -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. +An array stores elements of one type in contiguous memory. These known properties give the system opportunities to optimize operations. -- **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. +- **Space efficiency**: Data occupies a contiguous block with no extra structural overhead. +- **Random access**: Any element can be accessed in $O(1)$ time. +- **Cache locality**: Accessing an element loads not only that element but also nearby data into the cache, speeding up later operations. -Contiguous space storage is a double-edged sword with the following limitations: +Contiguous storage also has limitations. -- **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. +- **Slow insertion and deletion**: With many elements, these operations require substantial shifting. +- **Fixed length**: Once initialized, an array cannot change length; expansion requires copying all data into a new array. +- **Wasted space**: Any allocated space beyond actual needs is unused. -## Typical Applications of Arrays +## Typical applications of arrays -Arrays are a fundamental and common data structure, frequently used in various algorithms and for implementing various complex data structures. +Arrays are fundamental, widely used data structures. They appear throughout algorithms and underpin more complex structures. -- **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. +- **Random access**: To draw random samples, store data in an array and generate random indices to select elements. +- **Sorting and searching**: Arrays are the most common structures for these algorithms. Quick sort, merge sort, and binary search operate primarily on arrays. +- **Lookup tables**: An array can provide fast access to an element or a mapping. For example, a character-to-ASCII mapping can use a character's ASCII value as an index and store its corresponding element there. +- **Machine learning**: Neural networks make extensive use of linear algebra on vectors, matrices, and tensors, all built from arrays. Arrays are the most common data structure in neural network programming. +- **Implementing data structures**: Arrays can implement stacks, queues, hash tables, heaps, graphs, and more. A graph's adjacency matrix, for example, is a two-dimensional array. diff --git a/en/docs/chapter_array_and_linkedlist/exercises.md b/en/docs/chapter_array_and_linkedlist/exercises.md index 1eb142c45..a34bee473 100644 --- a/en/docs/chapter_array_and_linkedlist/exercises.md +++ b/en/docs/chapter_array_and_linkedlist/exercises.md @@ -1,106 +1,106 @@ # Exercises -## Concept Review +## Check your understanding -### How Arrays and Linked Lists Find an Element +### Finding elements in arrays and linked lists -An array and a singly linked list both store `[A, B, C, D, E]` in order. You now need to access the fourth element, `D`: +An array and a singly linked list both store `[A, B, C, D, E]` in order. We want to read the fourth element, `D`: -1. Which index can the array use directly? -2. Starting from the head node `A`, which nodes are visited in order as you follow `next`? -3. As the requested element moves farther toward the end, how does the number of steps change for each structure? Which structure is better for repeated access by position, and why? +1. Which index can we use to access it directly in the array? +2. Starting at the head node `A`, which nodes must the singly linked list visit by following `next`? +3. As the requested element moves farther toward the end, how does the number of steps change for each structure? Which is better suited to repeated access by position, and why? -??? success "Answer" +??? success "Reference answer" - 1. With zero-based indexing, the fourth element has index 3, so the array can access `arr[3]` directly. + 1. With zero-based indexing, the fourth element has index 3, so the array can read `arr[3]` directly. - 2. The singly linked list must start at the head. Its access path is `A → B → C → D`, requiring three moves through `next`. + 2. The singly linked list must start at the head and follow the path `A → B → C → D`, advancing along `next` three times. - 3. An array can locate an element directly from its starting address and index, so access by position has a time complexity of $O(1)$. - To access the $k$th node, a singly linked list must start at the head node and follow `next` $k-1$ times, - which takes $O(n)$ time in the worst case. + 3. An array can locate an element directly from its starting address and the index, giving time complexity $O(1)$ for access by position. + To access the $k$-th node of a singly linked list, we must start at the head and follow `next` $k-1$ times, + taking $O(n)$ time in the worst case. - This comparison concerns only access by position; it does not mean that linked lists are slower for every operation. + This comparison concerns access by position only; it does not mean that linked lists are slower for every operation. -### How Arrays and Linked Lists Insert an Element +### Inserting elements into arrays and linked lists -An array and a singly linked list both store `A, B, C, D`. You now need to insert `X` after `B`: +An array and a singly linked list both store `A, B, C, D`. We want to insert `X` after `B`: -- The array has capacity 5 and is currently `[A, B, C, D, _]`. -- The linked list is `A → B → C → D`, and you already have a reference to node `B`. +- The array has capacity 5 and currently contains `[A, B, C, D, _]`. +- The linked list is `A → B → C → D`, and we already have a reference to node `B`. -1. Which elements must the array move? Write the array after insertion. -2. In what order should the linked list update `X.next` and `B.next`? Write the linked list after insertion. -3. When comparing insertion efficiency, why is it important to state that "you already have a reference to node B"? +1. Which elements must the array move? Write out the array after insertion. +2. In what order should the linked list update `X.next` and `B.next`? Write out the resulting list. +3. Why must a comparison of insertion efficiency specify that we already have a reference to node B? -??? success "Answer" +??? success "Reference answer" - 1. The array first moves `D` one position to the right, then moves `C` one position to the right, and finally places `X` at index 2, - producing `[A, B, X, C, D]`. + 1. First move `D` one position to the right, then move `C` one position to the right, and finally put `X` at index 2. + The result is `[A, B, X, C, D]`. - 2. `B.next` originally points to `C`. First set `X.next = B.next`, making `X` point to `C`; - then set `B.next = X`. The result is `A → B → X → C → D`. - If `B.next` is overwritten first without saving the original link, `C` may become unreachable. + 2. Initially, `B.next` points to `C`. First set `X.next = B.next`, making `X` point to `C`. + Then set `B.next = X`. The result is `A → B → X → C → D`. + If we overwrite `B.next` first without saving the original link, we may lose access to `C`. - 3. Once the location of `B` is known, insertion into the linked list only changes two links and takes $O(1)$ time. - If `B` must first be found from the head, that search alone may take $O(n)$ time. + 3. Once the location of `B` is known, insertion into the linked list requires changing only two links and takes $O(1)$ time. + If we first need to search for `B` from the head, that search itself may take $O(n)$ time. -### How a List's Capacity Grows +### How does a list's capacity grow? An array-based list currently contains `[A, B, C]`, with length `size = 3` and capacity `capacity = 4`. -When there is not enough capacity, a new array with twice the old capacity is created. +When capacity runs out, the new array is allocated with twice the previous capacity. -1. After appending `D`, what are the list's length and capacity? Is capacity expansion needed? +1. After appending `D`, what are the list's length and capacity? Does it need to grow? 2. When `E` is appended next, what does the capacity become? How many existing elements must be copied? -3. The length of the underlying array cannot change. Why does the list's capacity nevertheless appear able to grow? +3. If the length of the underlying array cannot change, why does the list's capacity appear to grow? -??? success "Answer" +??? success "Reference answer" - 1. `D` fits in the last empty position. The contents are now `[A, B, C, D]`, - with `size = 4` and `capacity = 4`, so no capacity expansion is needed. + 1. `D` fits in the last empty position. The contents become `[A, B, C, D]`, + with `size = 4` and `capacity = 4`. No capacity increase is needed. - 2. There is no empty position when `E` is appended, so a new array with capacity 8 must be created. - The four existing elements `A, B, C, D` are copied into it before `E` is added. - The result is `size = 5` and `capacity = 8`. + 2. Appending `E` finds no empty position, so a new array of capacity 8 is created. + The four existing elements `A, B, C, D` are copied into it, and then `E` is added. + The list now has `size = 5` and `capacity = 8`. - 3. The original array itself does not grow. The list creates a larger new array, copies the existing elements, - and then uses the new array as its underlying storage. From the user's perspective, the capacity has grown. + 3. The original array itself does not become longer. The list creates a larger array, copies the existing elements, + and switches to using the new array for storage. From the user's perspective, its capacity has grown. -## Programming Exercises +## Programming exercises -### Add One to a Large Integer Stored as an Array +### Adding one to a large integer stored in an array -The array `digits` stores the decimal digits of a non-negative integer from left to right. For example, `[3, 0, 8]` represents 308. -The number 0 is represented by `[0]`; for every other input, the first digit is not 0. +The array `digits` stores the digits of a nonnegative integer from left to right. For example, `[3, 0, 8]` represents 308. +Zero is represented by `[0]`; all other inputs have a nonzero first digit. -Simulate decimal column addition to add 1 to this integer, and return the result in the same array format. -You may modify `digits` directly. If a new carry appears at the front, you may return a longer array. +Simulate the usual digit-by-digit method for decimal addition to add 1 to this integer, and return the result in the same array format. +You may modify `digits` directly. If a carry creates a new leading digit, you may return a longer array. ??? tip "Hints" - 1. Start with the last digit of the array, just as in ordinary column addition - 2. If the current digit is less than 9, add one and return immediately - 3. If the current digit is 9, change it to 0; if every digit is 9, place a 1 at the front + 1. Start at the last digit of the array, just as you would when adding by hand. + 2. If the current digit is less than 9, increment it and return immediately. + 3. If the current digit is 9, change it to 0. If all digits are 9, add a leading 1. [LeetCode](https://leetcode.com/problems/plus-one/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } -### Reverse a Singly Linked List +### Reversing a singly linked list You are given the head node `head` of a singly linked list. Each node contains a value and a `next` reference to the following node. -Use an iterative approach to reverse all links between the nodes, and return the new head node. -Do not create any new linked list nodes. +Use iteration to reverse every link between nodes, and return the new head. +Do not create any new linked-list nodes. ??? tip "Hints" - 1. First draw three connected nodes and the two pointers prev and cur on paper - 2. Before changing cur.next, save the original next node in nxt - 3. After reversing cur.next, set prev = cur and then cur = nxt to continue with the next node in the original list + 1. First draw three connected nodes and the two pointers prev and cur on paper. + 2. Before changing cur.next, save the original next node in nxt. + 3. After reversing cur.next, set prev = cur and then cur = nxt to continue with the next node in the original list. [LeetCode](https://leetcode.com/problems/reverse-linked-list/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_array_and_linkedlist/index.md b/en/docs/chapter_array_and_linkedlist/index.md index aaa29b4c2..1e29aad4b 100644 --- a/en/docs/chapter_array_and_linkedlist/index.md +++ b/en/docs/chapter_array_and_linkedlist/index.md @@ -1,9 +1,9 @@ -# Arrays and Linked Lists +# Arrays and linked lists -![Arrays and Linked Lists](../assets/covers/chapter_array_and_linkedlist.jpg) +![Arrays and linked lists](../assets/covers/chapter_array_and_linkedlist.jpg) !!! abstract The world of data structures is like a solid brick wall. - The bricks of an array are neatly aligned, each pressed tightly against the next. The bricks of a linked list are scattered about, with connecting vines weaving freely through the gaps between them. + In an array, the bricks line up neatly, each right beside the next. In a linked list, the bricks are scattered, with connecting vines weaving freely through the gaps. diff --git a/en/docs/chapter_array_and_linkedlist/linked_list.md b/en/docs/chapter_array_and_linkedlist/linked_list.md index 1288329fb..3b75a4e8b 100755 --- a/en/docs/chapter_array_and_linkedlist/linked_list.md +++ b/en/docs/chapter_array_and_linkedlist/linked_list.md @@ -1,20 +1,20 @@ -# Linked List +# Linked lists -Memory is a shared resource for all programs. In a complex runtime environment, free memory may be scattered throughout the address space. We know that arrays require contiguous memory, and when an array is very large, the system may not be able to provide such a large contiguous block. This is where the flexibility of linked lists becomes apparent. +Memory is a resource used by all programs, and in a complex system, free space may be scattered throughout it. An array requires contiguous storage, which may be unavailable for a very large array. This is where a linked list's flexibility helps. -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. +A linked list is a linear data structure whose elements are node objects connected by references. Each reference stores the next node's memory address, allowing us to reach it from the current node. -This design allows linked-list nodes to be stored in different locations in memory, and their addresses do not need to be contiguous. +This design lets nodes occupy scattered memory locations; their addresses need not be contiguous. -![Linked list definition and storage method](linked_list.assets/linkedlist_definition.png) +![Linked list definition and storage layout](linked_list.assets/linkedlist_definition.png) -Observing the figure above, 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. +As shown above, the basic unit of a linked list is a node object. Each node holds a value and a reference to the next node. -- 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". +- The first node is called the head, and the last is called the tail. +- The tail points to a null value: `null` in Java, `nullptr` in C++, and `None` in Python. +- In languages supporting pointers, such as C, C++, Go, and Rust, these references are expressed as pointers. -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**. +As the code below shows, each `ListNode` stores a reference (pointer) in addition to its value. Thus, **a linked list uses more memory than an array for the same amount of data**. === "Python" @@ -67,7 +67,7 @@ As shown in the following code, a linked list node `ListNode` contains not only Next *ListNode // Pointer to the next node } - // NewListNode Constructor, creates a new linked list + // NewListNode constructor: create a new linked list func NewListNode(val int) *ListNode { return &ListNode{ Val: val, @@ -185,23 +185,23 @@ As shown in the following code, a linked list node `ListNode` contains not only end ``` -## Common Linked List Operations +## Common linked list operations -### Initializing a Linked List +### 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`. +Building a linked list takes two steps: initialize the node objects, then connect them with references. Afterward, start at the head and follow `next` references to visit every node in order. === "Python" ```python title="linked_list.py" - # Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 - # Initialize each node + # Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 + # Initialize the nodes n0 = ListNode(1) n1 = ListNode(3) n2 = ListNode(2) n3 = ListNode(5) n4 = ListNode(4) - # Build references between nodes + # Link the nodes with references n0.next = n1 n1.next = n2 n2.next = n3 @@ -211,14 +211,14 @@ Building a linked list involves two steps: first, initializing each node object; === "C++" ```cpp title="linked_list.cpp" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes ListNode* n0 = new ListNode(1); ListNode* n1 = new ListNode(3); ListNode* n2 = new ListNode(2); ListNode* n3 = new ListNode(5); ListNode* n4 = new ListNode(4); - // Build references between nodes + // Link the nodes with references n0->next = n1; n1->next = n2; n2->next = n3; @@ -228,14 +228,14 @@ Building a linked list involves two steps: first, initializing each node object; === "Java" ```java title="linked_list.java" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes ListNode n0 = new ListNode(1); ListNode n1 = new ListNode(3); ListNode n2 = new ListNode(2); ListNode n3 = new ListNode(5); ListNode n4 = new ListNode(4); - // Build references between nodes + // Link the nodes with references n0.next = n1; n1.next = n2; n2.next = n3; @@ -245,14 +245,14 @@ Building a linked list involves two steps: first, initializing each node object; === "C#" ```csharp title="linked_list.cs" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes ListNode n0 = new(1); ListNode n1 = new(3); ListNode n2 = new(2); ListNode n3 = new(5); ListNode n4 = new(4); - // Build references between nodes + // Link the nodes with references n0.next = n1; n1.next = n2; n2.next = n3; @@ -262,14 +262,14 @@ Building a linked list involves two steps: first, initializing each node object; === "Go" ```go title="linked_list.go" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes n0 := NewListNode(1) n1 := NewListNode(3) n2 := NewListNode(2) n3 := NewListNode(5) n4 := NewListNode(4) - // Build references between nodes + // Link the nodes with references n0.Next = n1 n1.Next = n2 n2.Next = n3 @@ -279,14 +279,14 @@ Building a linked list involves two steps: first, initializing each node object; === "Swift" ```swift title="linked_list.swift" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes let n0 = ListNode(x: 1) let n1 = ListNode(x: 3) let n2 = ListNode(x: 2) let n3 = ListNode(x: 5) let n4 = ListNode(x: 4) - // Build references between nodes + // Link the nodes with references n0.next = n1 n1.next = n2 n2.next = n3 @@ -296,14 +296,14 @@ Building a linked list involves two steps: first, initializing each node object; === "JS" ```javascript title="linked_list.js" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes const n0 = new ListNode(1); const n1 = new ListNode(3); const n2 = new ListNode(2); const n3 = new ListNode(5); const n4 = new ListNode(4); - // Build references between nodes + // Link the nodes with references n0.next = n1; n1.next = n2; n2.next = n3; @@ -313,14 +313,14 @@ Building a linked list involves two steps: first, initializing each node object; === "TS" ```typescript title="linked_list.ts" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes const n0 = new ListNode(1); const n1 = new ListNode(3); const n2 = new ListNode(2); const n3 = new ListNode(5); const n4 = new ListNode(4); - // Build references between nodes + // Link the nodes with references n0.next = n1; n1.next = n2; n2.next = n3; @@ -330,14 +330,14 @@ Building a linked list involves two steps: first, initializing each node object; === "Dart" ```dart title="linked_list.dart" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */\ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */\ + // Initialize the nodes ListNode n0 = ListNode(1); ListNode n1 = ListNode(3); ListNode n2 = ListNode(2); ListNode n3 = ListNode(5); ListNode n4 = ListNode(4); - // Build references between nodes + // Link the nodes with references n0.next = n1; n1.next = n2; n2.next = n3; @@ -347,15 +347,15 @@ Building a linked list involves two steps: first, initializing each node object; === "Rust" ```rust title="linked_list.rs" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes let n0 = Rc::new(RefCell::new(ListNode { val: 1, next: None })); let n1 = Rc::new(RefCell::new(ListNode { val: 3, next: None })); let n2 = Rc::new(RefCell::new(ListNode { val: 2, next: None })); let n3 = Rc::new(RefCell::new(ListNode { val: 5, next: None })); let n4 = Rc::new(RefCell::new(ListNode { val: 4, next: None })); - // Build references between nodes + // Link the nodes with references n0.borrow_mut().next = Some(n1.clone()); n1.borrow_mut().next = Some(n2.clone()); n2.borrow_mut().next = Some(n3.clone()); @@ -365,14 +365,14 @@ Building a linked list involves two steps: first, initializing each node object; === "C" ```c title="linked_list.c" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes ListNode* n0 = newListNode(1); ListNode* n1 = newListNode(3); ListNode* n2 = newListNode(2); ListNode* n3 = newListNode(5); ListNode* n4 = newListNode(4); - // Build references between nodes + // Link the nodes with references n0->next = n1; n1->next = n2; n2->next = n3; @@ -382,14 +382,14 @@ Building a linked list involves two steps: first, initializing each node object; === "Kotlin" ```kotlin title="linked_list.kt" - /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ - // Initialize each node + /* Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize the nodes val n0 = ListNode(1) val n1 = ListNode(3) val n2 = ListNode(2) val n3 = ListNode(5) val n4 = ListNode(4) - // Build references between nodes + // Link the nodes with references n0.next = n1; n1.next = n2; n2.next = n3; @@ -399,88 +399,88 @@ Building a linked list involves two steps: first, initializing each node object; === "Ruby" ```ruby title="linked_list.rb" - # Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 - # Initialize each node + # Initialize the linked list 1 -> 3 -> 2 -> 5 -> 4 + # Initialize the nodes n0 = ListNode.new(1) n1 = ListNode.new(3) n2 = ListNode.new(2) n3 = ListNode.new(5) n4 = ListNode.new(4) - # Build references between nodes + # Link the nodes with references n0.next = n1 n1.next = n2 n2.next = n3 n3.next = n4 ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%90%8E%E7%BB%A7%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E9%93%BE%E8%A1%A8%201%20-%3E%203%20-%3E%202%20-%3E%205%20-%3E%204%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%90%84%E4%B8%AA%E8%8A%82%E7%82%B9%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20successor%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20the%20linked%20list%201%20-%3E%203%20-%3E%202%20-%3E%205%20-%3E%204%0A%20%20%20%20%23%20Initialize%20the%20nodes%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20Link%20the%20nodes%20with%20references%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -An array is a single variable; for example, an array `nums` contains elements `nums[0]`, `nums[1]`, and so on. A linked list, by contrast, is composed of multiple independent node objects. **We usually use the head node as a stand-in for the entire linked list**; for example, the linked list in the above code can be referred to as linked list `n0`. +An array is a single variable, such as `nums`, containing entries `nums[0]`, `nums[1]`, and so on. A linked list consists of separate node objects. **We usually refer to the list by its head node**; for example, the list above can be called `n0`. -### Inserting a Node +### Inserting a node -Inserting a node in a linked list is very easy. As shown in the figure below, 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)$. +Inserting a node is straightforward. As shown in the figure below, inserting a new node `P` between adjacent nodes `n0` and `n1` **requires changing only two references (pointers)**, taking $O(1)$ time. -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. +In contrast, inserting an array element takes $O(n)$ time and is slower for large datasets. -![Example of inserting a node into a linked list](linked_list.assets/linkedlist_insert_node.png) +![Inserting a node into a linked list](linked_list.assets/linkedlist_insert_node.png) ```src [file]{linked_list}-[class]{}-[func]{insert} ``` -### Removing a Node +### Removing a node -As shown in the figure below, removing a node in a linked list is also very convenient. **We only need to change one node's reference (pointer)**. +Removing a node is also straightforward: as shown in the figure below, **only one reference (pointer) needs to change**. -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. +Although `P` still points to `n1` after removal, traversing the list can no longer reach `P`. It therefore no longer belongs to the list. -![Removing a node from a linked list](linked_list.assets/linkedlist_remove_node.png) +![Removing a linked list node](linked_list.assets/linkedlist_remove_node.png) ```src [file]{linked_list}-[class]{}-[func]{remove} ``` -### Accessing a Node +### Accessing a node -**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)$. +**Accessing a node in a linked list is relatively slow**. An array provides access to any element in $O(1)$ time, but a linked list must be traversed from its head until the desired node is reached. Accessing the $i$-th node requires $i - 1$ iterations, giving $O(n)$ time complexity. ```src [file]{linked_list}-[class]{}-[func]{access} ``` -### Finding a Node +### Finding a node -Traverse the linked list to find a node with value `target`, and output the index of that node in the linked list. This process is also a linear search. The code is shown below: +Traverse the list to find a node with value `target`, then return its index. This is also linear search. The code is as follows: ```src [file]{linked_list}-[class]{}-[func]{find} ``` -## Arrays vs. Linked Lists +## Arrays versus linked lists -The table below 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. +The table below compares their properties and operation costs. Their contrasting storage strategies give them contrasting strengths. -

Table   Comparison of array and linked list efficiencies

+

Table   Comparing array and linked list efficiency

-| | 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)$ | +| | Array | Linked list | +| -------- | ------------------------------ | -------------- | +| Storage | Contiguous memory | Scattered memory | +| Expansion | Fixed length | Flexible expansion | +| Memory efficiency | Less memory per element, but may waste space | More memory per element | +| Access an element | $O(1)$ | $O(n)$ | +| Add an element | $O(n)$ | $O(1)$ | +| Remove an element | $O(n)$ | $O(1)$ | -## Common Types of Linked Lists +## Common types of linked lists -As shown in the figure below, there are three common types of linked lists: +The figure below shows three common types. -- **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; the tail node points to `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. +- **Singly linked list**: The ordinary linked list introduced above. Each node stores a value and a reference to the next node. The first and last nodes are the head and tail, and the tail points to `None`. +- **Circular linked list**: Connecting a singly linked list's tail to its head forms a circular list. Any node can then be treated as the head. +- **Doubly linked list**: Each node stores references (pointers) in both directions, to its successor and predecessor. This makes it more flexible and allows traversal in either direction, at the cost of additional memory. === "Python" @@ -489,8 +489,8 @@ As shown in the figure below, there are three common types of linked lists: """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 the predecessor node + self.next: ListNode | None = None # Reference to the successor + self.prev: ListNode | None = None # Reference to the predecessor ``` === "C++" @@ -499,8 +499,8 @@ As shown in the figure below, there are three common types of linked lists: /* Doubly linked list node structure */ struct ListNode { int val; // Node value - ListNode *next; // Pointer to the successor node - ListNode *prev; // Pointer to the predecessor node + ListNode *next; // Pointer to the successor + ListNode *prev; // Pointer to the predecessor ListNode(int x) : val(x), next(nullptr), prev(nullptr) {} // Constructor }; ``` @@ -511,8 +511,8 @@ As shown in the figure below, there are three common types of linked lists: /* Doubly linked list node class */ class ListNode { int val; // Node value - ListNode next; // Reference to the successor node - ListNode prev; // Reference to the predecessor node + ListNode next; // Reference to the successor + ListNode prev; // Reference to the predecessor ListNode(int x) { val = x; } // Constructor } ``` @@ -523,8 +523,8 @@ As shown in the figure below, there are three common types of linked lists: /* Doubly linked list node class */ class ListNode(int x) { // Constructor int val = x; // Node value - ListNode next; // Reference to the successor node - ListNode prev; // Reference to the predecessor node + ListNode next; // Reference to the successor + ListNode prev; // Reference to the predecessor } ``` @@ -534,11 +534,11 @@ As shown in the figure below, there are three common types of linked lists: /* 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 + Next *DoublyListNode // Pointer to the successor + Prev *DoublyListNode // Pointer to the predecessor } - // NewDoublyListNode Initialization + // Initialize a node with NewDoublyListNode func NewDoublyListNode(val int) *DoublyListNode { return &DoublyListNode{ Val: val, @@ -554,8 +554,8 @@ As shown in the figure below, there are three common types of linked lists: /* Doubly linked list node class */ class ListNode { var val: Int // Node value - var next: ListNode? // Reference to the successor node - var prev: ListNode? // Reference to the predecessor node + var next: ListNode? // Reference to the successor + var prev: ListNode? // Reference to the predecessor init(x: Int) { // Constructor val = x @@ -570,8 +570,8 @@ As shown in the figure below, there are three common types of linked lists: class ListNode { constructor(val, next, prev) { this.val = val === undefined ? 0 : val; // Node value - this.next = next === undefined ? null : next; // Reference to the successor node - this.prev = prev === undefined ? null : prev; // Reference to the predecessor node + this.next = next === undefined ? null : next; // Reference to the successor + this.prev = prev === undefined ? null : prev; // Reference to the predecessor } } ``` @@ -586,8 +586,8 @@ As shown in the figure below, there are three common types of linked lists: prev: ListNode | null; constructor(val?: number, next?: ListNode | null, prev?: ListNode | null) { this.val = val === undefined ? 0 : val; // Node value - this.next = next === undefined ? null : next; // Reference to the successor node - this.prev = prev === undefined ? null : prev; // Reference to the predecessor node + this.next = next === undefined ? null : next; // Reference to the successor + this.prev = prev === undefined ? null : prev; // Reference to the predecessor } } ``` @@ -598,8 +598,8 @@ As shown in the figure below, there are three common types of linked lists: /* Doubly linked list node class */ class ListNode { int val; // Node value - ListNode? next; // Reference to the successor node - ListNode? prev; // Reference to the predecessor node + ListNode? next; // Reference to the successor + ListNode? prev; // Reference to the predecessor ListNode(this.val, [this.next, this.prev]); // Constructor } ``` @@ -614,8 +614,8 @@ As shown in the figure below, there are three common types of linked lists: #[derive(Debug)] struct ListNode { val: i32, // Node value - next: Option>>, // Pointer to the successor node - prev: Option>>, // Pointer to the predecessor node + next: Option>>, // Pointer to the successor + prev: Option>>, // Pointer to the predecessor } /* Constructor */ @@ -636,8 +636,8 @@ As shown in the figure below, there are three common types of linked lists: /* 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 + struct ListNode *next; // Pointer to the successor + struct ListNode *prev; // Pointer to the predecessor } ListNode; /* Constructor */ @@ -658,8 +658,8 @@ As shown in the figure below, there are three common types of linked lists: // Constructor class ListNode(x: Int) { val _val: Int = x // Node value - var next: ListNode? = null // Reference to the successor node - var prev: ListNode? = null // Reference to the predecessor node + var next: ListNode? = null // Reference to the successor + var prev: ListNode? = null // Reference to the predecessor } ``` @@ -669,8 +669,8 @@ As shown in the figure below, there are three common types of linked lists: # 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 + attr_accessor :next # Reference to the successor + attr_accessor :prev # Reference to the predecessor def initialize(val=0, next_node=nil, prev_node=nil) @val = val @@ -682,21 +682,21 @@ As shown in the figure below, there are three common types of linked lists: ![Common types of linked lists](linked_list.assets/linkedlist_common_types.png) -## Typical Applications of Linked Lists +## Typical applications of linked lists -Singly linked lists are commonly used to implement stacks, queues, hash tables, and graphs. +Singly linked lists commonly implement stacks, queues, hash tables, and graphs. -- **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. +- **Stacks and queues**: Inserting and removing at the same end gives last-in, first-out behavior, forming a stack. Inserting at one end and removing at the other gives first-in, first-out behavior, forming a queue. +- **Hash tables**: Separate chaining is a common collision-handling strategy that places colliding elements in a linked list. +- **Graphs**: An adjacency list associates each vertex with a linked list whose elements represent the other vertices connected to it. -Doubly linked lists are commonly used in scenarios where quick access to the previous and next elements is needed. +Doubly linked lists are useful when both the previous and next elements must be found quickly. -- **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. +- **Advanced data structures**: In structures such as red-black trees and B-trees, a node may need access to its parent. Storing a parent reference is similar to the backward link in a doubly linked list. +- **Browser history**: The forward and back buttons require access to the next and previous visited pages. A doubly linked list makes this straightforward. +- **LRU caching**: Least recently used eviction needs fast access to the least recently used data and efficient insertion and removal, making doubly linked lists a good fit. -Circular linked lists are commonly used in scenarios that require periodic operations, such as operating system resource scheduling. +Circular linked lists suit cyclic operations, such as operating-system resource scheduling. -- **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. +- **Round-robin scheduling**: This common CPU scheduling algorithm cycles through processes, giving each a time slice before moving to the next. A circular linked list can implement this cycle. +- **Data buffers**: Some buffers use circular linked lists. Audio and video players, for example, may divide a stream into buffer blocks arranged in a circular list for seamless playback. diff --git a/en/docs/chapter_array_and_linkedlist/list.md b/en/docs/chapter_array_and_linkedlist/list.md index 6ed3c545f..5052ef059 100755 --- a/en/docs/chapter_array_and_linkedlist/list.md +++ b/en/docs/chapter_array_and_linkedlist/list.md @@ -1,26 +1,26 @@ -# List +# Lists -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 list is an abstract data structure representing an ordered collection. It supports access, modification, insertion, removal, and traversal without requiring users to manage capacity limits. Lists can be implemented with linked lists or arrays. -- A linked list can naturally be viewed as a list: it supports insertion, deletion, search, and update, and can grow flexibly as needed. -- An array also supports insertion, deletion, search, and update, but because its length is fixed, it can only be regarded as a list with a capacity limit. +- A linked list naturally serves as a list, supporting these operations and flexible dynamic growth. +- An array also supports these operations, but its fixed length makes it a list with a length limit. -When a list is implemented with an array, **its fixed length makes it less practical**. This is because we usually cannot determine in advance how much data we need to store, making it difficult to choose an appropriate capacity. If the capacity is too small, it may fail to meet our needs; if it is too large, memory space will be wasted. +When an array implements a list, **its fixed length limits the list's usefulness**. We often cannot predict how much data we will store. Too small an allocation may not meet our needs; too large an allocation wastes memory. -To solve this problem, we can use a dynamic array to implement a list. It inherits all the advantages of arrays while supporting dynamic resizing during program execution. +A dynamic array solves this problem. It retains the advantages of an array while allowing capacity to grow during execution. -In fact, **the list types provided by the standard libraries of many programming languages are implemented with 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. +**Many standard-library lists use dynamic arrays**, including Python's `list`, Java's `ArrayList`, C++'s `vector`, and C#'s `List`. In the discussion that follows, we treat lists and dynamic arrays as equivalent concepts. -## Common List Operations +## Common list operations -### Initialize a List +### Initializing a list -We typically initialize a list in one of two ways: empty or with predefined values: +We usually initialize a list either without elements or with explicit initial values: === "Python" ```python title="list.py" - # Initialize a list + # Initialize lists # Without initial values nums1: list[int] = [] # With initial values @@ -30,8 +30,8 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "C++" ```cpp title="list.cpp" - /* Initialize a list */ - // Note that vector in C++ is equivalent to nums as described in this article + /* Initialize lists */ + // In C++, vector is the list type used for nums here // Without initial values vector nums1; // With initial values @@ -41,10 +41,10 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "Java" ```java title="list.java" - /* Initialize a list */ + /* Initialize lists */ // Without initial values List nums1 = new ArrayList<>(); - // With initial values (note that array elements should use the wrapper class Integer[] instead of int[]) + // With initial values (use Integer[], the boxed counterpart of int[]) Integer[] numbers = new Integer[] { 1, 3, 2, 5, 4 }; List nums = new ArrayList<>(Arrays.asList(numbers)); ``` @@ -52,7 +52,7 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "C#" ```csharp title="list.cs" - /* Initialize a list */ + /* Initialize lists */ // Without initial values List nums1 = []; // With initial values @@ -63,7 +63,7 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "Go" ```go title="list_test.go" - /* Initialize a list */ + /* Initialize lists */ // Without initial values nums1 := []int{} // With initial values @@ -73,7 +73,7 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "Swift" ```swift title="list.swift" - /* Initialize a list */ + /* Initialize lists */ // Without initial values let nums1: [Int] = [] // With initial values @@ -83,7 +83,7 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "JS" ```javascript title="list.js" - /* Initialize a list */ + /* Initialize lists */ // Without initial values const nums1 = []; // With initial values @@ -93,7 +93,7 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "TS" ```typescript title="list.ts" - /* Initialize a list */ + /* Initialize lists */ // Without initial values const nums1: number[] = []; // With initial values @@ -103,7 +103,7 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "Dart" ```dart title="list.dart" - /* Initialize a list */ + /* Initialize lists */ // Without initial values List nums1 = []; // With initial values @@ -113,7 +113,7 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "Rust" ```rust title="list.rs" - /* Initialize a list */ + /* Initialize lists */ // Without initial values let nums1: Vec = Vec::new(); // With initial values @@ -123,13 +123,13 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "C" ```c title="list.c" - // C does not provide built-in dynamic arrays + // C does not provide a built-in dynamic array ``` === "Kotlin" ```kotlin title="list.kt" - /* Initialize a list */ + /* Initialize lists */ // Without initial values var nums1 = listOf() // With initial values @@ -140,151 +140,151 @@ We typically initialize a list in one of two ways: empty or with predefined valu === "Ruby" ```ruby title="list.rb" - # Initialize a list + # Initialize lists # Without initial values nums1 = [] # With initial values nums = [1, 3, 2, 5, 4] ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20%23%20%E6%97%A0%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums1%20%3D%20%5B%5D%0A%20%20%20%20%23%20%E6%9C%89%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20lists%0A%20%20%20%20%23%20Without%20initial%20values%0A%20%20%20%20nums1%20%3D%20%5B%5D%0A%20%20%20%20%23%20With%20initial%20values%0A%20%20%20%20nums%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Access Elements +### Accessing an element -Since a list is essentially an array, we can access and update elements in $O(1)$ time complexity, which is very efficient. +Since the list is based on an array, accessing and updating an element are efficient $O(1)$ operations. === "Python" ```python title="list.py" # Access an element - num: int = nums[1] # Access element at index 1 + num: int = nums[1] # Access the element at index 1 # Update an element - nums[1] = 0 # Update element at index 1 to 0 + nums[1] = 0 # Set the element at index 1 to 0 ``` === "C++" ```cpp title="list.cpp" /* Access an element */ - int num = nums[1]; // Access element at index 1 + int num = nums[1]; // Access the element at index 1 /* Update an element */ - nums[1] = 0; // Update element at index 1 to 0 + nums[1] = 0; // Set the element at index 1 to 0 ``` === "Java" ```java title="list.java" /* Access an element */ - int num = nums.get(1); // Access element at index 1 + int num = nums.get(1); // Access the element at index 1 /* Update an element */ - nums.set(1, 0); // Update element at index 1 to 0 + nums.set(1, 0); // Set the element at index 1 to 0 ``` === "C#" ```csharp title="list.cs" /* Access an element */ - int num = nums[1]; // Access element at index 1 + int num = nums[1]; // Access the element at index 1 /* Update an element */ - nums[1] = 0; // Update element at index 1 to 0 + nums[1] = 0; // Set the element at index 1 to 0 ``` === "Go" ```go title="list_test.go" /* Access an element */ - num := nums[1] // Access element at index 1 + num := nums[1] // Access the element at index 1 /* Update an element */ - nums[1] = 0 // Update element at index 1 to 0 + nums[1] = 0 // Set the element at index 1 to 0 ``` === "Swift" ```swift title="list.swift" /* Access an element */ - let num = nums[1] // Access element at index 1 + let num = nums[1] // Access the element at index 1 /* Update an element */ - nums[1] = 0 // Update element at index 1 to 0 + nums[1] = 0 // Set the element at index 1 to 0 ``` === "JS" ```javascript title="list.js" /* Access an element */ - const num = nums[1]; // Access element at index 1 + const num = nums[1]; // Access the element at index 1 /* Update an element */ - nums[1] = 0; // Update element at index 1 to 0 + nums[1] = 0; // Set the element at index 1 to 0 ``` === "TS" ```typescript title="list.ts" /* Access an element */ - const num: number = nums[1]; // Access element at index 1 + const num: number = nums[1]; // Access the element at index 1 /* Update an element */ - nums[1] = 0; // Update element at index 1 to 0 + nums[1] = 0; // Set the element at index 1 to 0 ``` === "Dart" ```dart title="list.dart" /* Access an element */ - int num = nums[1]; // Access element at index 1 + int num = nums[1]; // Access the element at index 1 /* Update an element */ - nums[1] = 0; // Update element at index 1 to 0 + nums[1] = 0; // Set the element at index 1 to 0 ``` === "Rust" ```rust title="list.rs" /* Access an element */ - let num: i32 = nums[1]; // Access element at index 1 + let num: i32 = nums[1]; // Access the element at index 1 /* Update an element */ - nums[1] = 0; // Update element at index 1 to 0 + nums[1] = 0; // Set the element at index 1 to 0 ``` === "C" ```c title="list.c" - // C does not provide built-in dynamic arrays + // C does not provide a built-in dynamic array ``` === "Kotlin" ```kotlin title="list.kt" /* Access an element */ - val num = nums[1] // Access element at index 1 + val num = nums[1] // Access the element at index 1 /* Update an element */ - nums[1] = 0 // Update element at index 1 to 0 + nums[1] = 0 // Set the element at index 1 to 0 ``` === "Ruby" ```ruby title="list.rb" # Access an element - num = nums[1] # Access element at index 1 + num = nums[1] # Access the element at index 1 # Update an element - nums[1] = 0 # Update element at index 1 to 0 + nums[1] = 0 # Set the element at index 1 to 0 ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E5%85%83%E7%B4%A0%0A%20%20%20%20num%20%3D%20nums%5B1%5D%20%20%23%20%E8%AE%BF%E9%97%AE%E7%B4%A2%E5%BC%95%201%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0%0A%0A%20%20%20%20%23%20%E6%9B%B4%E6%96%B0%E5%85%83%E7%B4%A0%0A%20%20%20%20nums%5B1%5D%20%3D%200%20%20%20%20%23%20%E5%B0%86%E7%B4%A2%E5%BC%95%201%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0%E6%9B%B4%E6%96%B0%E4%B8%BA%200&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20lists%0A%20%20%20%20nums%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D%0A%0A%20%20%20%20%23%20Access%20an%20element%0A%20%20%20%20num%20%3D%20nums%5B1%5D%20%20%23%20Access%20the%20element%20at%20index%201%0A%0A%20%20%20%20%23%20Update%20an%20element%0A%20%20%20%20nums%5B1%5D%20%3D%200%20%20%20%20%23%20Set%20the%20element%20at%20index%201%20to%200&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Insert and Delete Elements +### Inserting and removing 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)$. +Unlike a fixed array, a list can freely add and remove elements. Appending takes $O(1)$ time, while insertion and removal retain the array's $O(n)$ time complexity. === "Python" @@ -292,7 +292,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element # Clear the list nums.clear() - # Add elements at the end + # Append elements nums.append(1) nums.append(3) nums.append(2) @@ -300,10 +300,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.append(4) # Insert an element in the middle - nums.insert(3, 6) # Insert number 6 at index 3 + nums.insert(3, 6) # Insert 6 at index 3 - # Delete an element - nums.pop(3) # Delete element at index 3 + # Remove an element + nums.pop(3) # Remove the element at index 3 ``` === "C++" @@ -312,7 +312,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.clear(); - /* Add elements at the end */ + /* Append elements */ nums.push_back(1); nums.push_back(3); nums.push_back(2); @@ -320,10 +320,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.push_back(4); /* Insert an element in the middle */ - nums.insert(nums.begin() + 3, 6); // Insert number 6 at index 3 + nums.insert(nums.begin() + 3, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.erase(nums.begin() + 3); // Delete element at index 3 + /* Remove an element */ + nums.erase(nums.begin() + 3); // Remove the element at index 3 ``` === "Java" @@ -332,7 +332,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.clear(); - /* Add elements at the end */ + /* Append elements */ nums.add(1); nums.add(3); nums.add(2); @@ -340,10 +340,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.add(4); /* Insert an element in the middle */ - nums.add(3, 6); // Insert number 6 at index 3 + nums.add(3, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.remove(3); // Delete element at index 3 + /* Remove an element */ + nums.remove(3); // Remove the element at index 3 ``` === "C#" @@ -352,7 +352,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.Clear(); - /* Add elements at the end */ + /* Append elements */ nums.Add(1); nums.Add(3); nums.Add(2); @@ -360,10 +360,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.Add(4); /* Insert an element in the middle */ - nums.Insert(3, 6); // Insert number 6 at index 3 + nums.Insert(3, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.RemoveAt(3); // Delete element at index 3 + /* Remove an element */ + nums.RemoveAt(3); // Remove the element at index 3 ``` === "Go" @@ -372,7 +372,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums = nil - /* Add elements at the end */ + /* Append elements */ nums = append(nums, 1) nums = append(nums, 3) nums = append(nums, 2) @@ -380,10 +380,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums = append(nums, 4) /* Insert an element in the middle */ - nums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert number 6 at index 3 + nums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert 6 at index 3 - /* Delete an element */ - nums = append(nums[:3], nums[4:]...) // Delete element at index 3 + /* Remove an element */ + nums = append(nums[:3], nums[4:]...) // Remove the element at index 3 ``` === "Swift" @@ -392,7 +392,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.removeAll() - /* Add elements at the end */ + /* Append elements */ nums.append(1) nums.append(3) nums.append(2) @@ -400,10 +400,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.append(4) /* Insert an element in the middle */ - nums.insert(6, at: 3) // Insert number 6 at index 3 + nums.insert(6, at: 3) // Insert 6 at index 3 - /* Delete an element */ - nums.remove(at: 3) // Delete element at index 3 + /* Remove an element */ + nums.remove(at: 3) // Remove the element at index 3 ``` === "JS" @@ -412,7 +412,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.length = 0; - /* Add elements at the end */ + /* Append elements */ nums.push(1); nums.push(3); nums.push(2); @@ -420,10 +420,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.push(4); /* Insert an element in the middle */ - nums.splice(3, 0, 6); // Insert number 6 at index 3 + nums.splice(3, 0, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.splice(3, 1); // Delete element at index 3 + /* Remove an element */ + nums.splice(3, 1); // Remove the element at index 3 ``` === "TS" @@ -432,7 +432,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.length = 0; - /* Add elements at the end */ + /* Append elements */ nums.push(1); nums.push(3); nums.push(2); @@ -440,10 +440,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.push(4); /* Insert an element in the middle */ - nums.splice(3, 0, 6); // Insert number 6 at index 3 + nums.splice(3, 0, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.splice(3, 1); // Delete element at index 3 + /* Remove an element */ + nums.splice(3, 1); // Remove the element at index 3 ``` === "Dart" @@ -452,7 +452,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.clear(); - /* Add elements at the end */ + /* Append elements */ nums.add(1); nums.add(3); nums.add(2); @@ -460,10 +460,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.add(4); /* Insert an element in the middle */ - nums.insert(3, 6); // Insert number 6 at index 3 + nums.insert(3, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.removeAt(3); // Delete element at index 3 + /* Remove an element */ + nums.removeAt(3); // Remove the element at index 3 ``` === "Rust" @@ -472,7 +472,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.clear(); - /* Add elements at the end */ + /* Append elements */ nums.push(1); nums.push(3); nums.push(2); @@ -480,16 +480,16 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.push(4); /* Insert an element in the middle */ - nums.insert(3, 6); // Insert number 6 at index 3 + nums.insert(3, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.remove(3); // Delete element at index 3 + /* Remove an element */ + nums.remove(3); // Remove the element at index 3 ``` === "C" ```c title="list.c" - // C does not provide built-in dynamic arrays + // C does not provide a built-in dynamic array ``` === "Kotlin" @@ -498,7 +498,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element /* Clear the list */ nums.clear(); - /* Add elements at the end */ + /* Append elements */ nums.add(1); nums.add(3); nums.add(2); @@ -506,10 +506,10 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums.add(4); /* Insert an element in the middle */ - nums.add(3, 6); // Insert number 6 at index 3 + nums.add(3, 6); // Insert 6 at index 3 - /* Delete an element */ - nums.remove(3); // Delete element at index 3 + /* Remove an element */ + nums.remove(3); // Remove the element at index 3 ``` === "Ruby" @@ -518,7 +518,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element # Clear the list nums.clear - # Add elements at the end + # Append elements nums << 1 nums << 3 nums << 2 @@ -526,19 +526,19 @@ Compared to arrays, lists can freely add and delete elements. Adding an element nums << 4 # Insert an element in the middle - nums.insert(3, 6) # Insert number 6 at index 3 + nums.insert(3, 6) # Insert 6 at index 3 - # Delete an element - nums.delete_at(3) # Delete element at index 3 + # Remove an element + nums.delete_at(3) # Remove the element at index 3 ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E6%9C%89%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B8%85%E7%A9%BA%E5%88%97%E8%A1%A8%0A%20%20%20%20nums.clear%28%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%9C%A8%E5%B0%BE%E9%83%A8%E6%B7%BB%E5%8A%A0%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.append%281%29%0A%20%20%20%20nums.append%283%29%0A%20%20%20%20nums.append%282%29%0A%20%20%20%20nums.append%285%29%0A%20%20%20%20nums.append%284%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%9C%A8%E4%B8%AD%E9%97%B4%E6%8F%92%E5%85%A5%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.insert%283,%206%29%20%20%23%20%E5%9C%A8%E7%B4%A2%E5%BC%95%203%20%E5%A4%84%E6%8F%92%E5%85%A5%E6%95%B0%E5%AD%97%206%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.pop%283%29%20%20%20%20%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E7%B4%A2%E5%BC%95%203%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20With%20initial%20values%0A%20%20%20%20nums%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20Clear%20the%20list%0A%20%20%20%20nums.clear%28%29%0A%20%20%20%20%0A%20%20%20%20%23%20Append%20elements%0A%20%20%20%20nums.append%281%29%0A%20%20%20%20nums.append%283%29%0A%20%20%20%20nums.append%282%29%0A%20%20%20%20nums.append%285%29%0A%20%20%20%20nums.append%284%29%0A%20%20%20%20%0A%20%20%20%20%23%20Insert%20an%20element%20in%20the%20middle%0A%20%20%20%20nums.insert%283%2C%206%29%20%20%23%20Insert%206%20at%20index%203%0A%20%20%20%20%0A%20%20%20%20%23%20Remove%20an%20element%0A%20%20%20%20nums.pop%283%29%20%20%20%20%20%20%20%20%23%20Remove%20the%20element%20at%20index%203&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Traverse a List +### Traversing a list -Like arrays, lists can be traversed by index or by directly iterating through elements. +Like an array, a list can be traversed by index or by visiting its elements directly. === "Python" @@ -699,7 +699,7 @@ Like arrays, lists can be traversed by index or by directly iterating through el === "C" ```c title="list.c" - // C does not provide built-in dynamic arrays + // C does not provide a built-in dynamic array ``` === "Kotlin" @@ -733,20 +733,20 @@ Like arrays, lists can be traversed by index or by directly iterating through el end ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E9%80%9A%E8%BF%87%E7%B4%A2%E5%BC%95%E9%81%8D%E5%8E%86%E5%88%97%E8%A1%A8%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20nums%5Bi%5D%0A%0A%20%20%20%20%23%20%E7%9B%B4%E6%8E%A5%E9%81%8D%E5%8E%86%E5%88%97%E8%A1%A8%E5%85%83%E7%B4%A0%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20num&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20lists%0A%20%20%20%20nums%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20Traverse%20the%20list%20by%20index%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20nums%5Bi%5D%0A%0A%20%20%20%20%23%20Traverse%20list%20elements%20directly%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20num&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Concatenate Lists +### Concatenating lists -Given a new list `nums1`, we can concatenate it to the end of the original list. +Given another list `nums1`, we can append its elements 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 list nums1 to the end of nums + nums += nums1 # Append nums1 to nums ``` === "C++" @@ -754,7 +754,7 @@ Given a new list `nums1`, we can concatenate it to the end of the original list. ```cpp title="list.cpp" /* Concatenate two lists */ vector nums1 = { 6, 8, 7, 10, 9 }; - // Concatenate list nums1 to the end of nums + // Append nums1 to nums nums.insert(nums.end(), nums1.begin(), nums1.end()); ``` @@ -763,7 +763,7 @@ Given a new list `nums1`, we can concatenate 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 list nums1 to the end of nums + nums.addAll(nums1); // Append nums1 to nums ``` === "C#" @@ -771,7 +771,7 @@ Given a new list `nums1`, we can concatenate 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 list nums1 to the end of nums + nums.AddRange(nums1); // Append nums1 to nums ``` === "Go" @@ -779,7 +779,7 @@ Given a new list `nums1`, we can concatenate 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 list nums1 to the end of nums + nums = append(nums, nums1...) // Append nums1 to nums ``` === "Swift" @@ -787,7 +787,7 @@ Given a new list `nums1`, we can concatenate 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 list nums1 to the end of nums + nums.append(contentsOf: nums1) // Append nums1 to nums ``` === "JS" @@ -795,7 +795,7 @@ Given a new list `nums1`, we can concatenate 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 list nums1 to the end of nums + nums.push(...nums1); // Append nums1 to nums ``` === "TS" @@ -803,7 +803,7 @@ Given a new list `nums1`, we can concatenate 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 list nums1 to the end of nums + nums.push(...nums1); // Append nums1 to nums ``` === "Dart" @@ -811,7 +811,7 @@ Given a new list `nums1`, we can concatenate 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 list nums1 to the end of nums + nums.addAll(nums1); // Append nums1 to nums ``` === "Rust" @@ -825,7 +825,7 @@ Given a new list `nums1`, we can concatenate it to the end of the original list. === "C" ```c title="list.c" - // C does not provide built-in dynamic arrays + // C does not provide a built-in dynamic array ``` === "Kotlin" @@ -833,7 +833,7 @@ Given a new list `nums1`, we can concatenate it to the end of the original list. ```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 + nums.addAll(nums1) // Append nums1 to nums ``` === "Ruby" @@ -844,117 +844,117 @@ Given a new list `nums1`, we can concatenate it to the end of the original list. nums += nums1 ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%8B%BC%E6%8E%A5%E4%B8%A4%E4%B8%AA%E5%88%97%E8%A1%A8%0A%20%20%20%20nums1%20%3D%20%5B6,%208,%207,%2010,%209%5D%0A%20%20%20%20nums%20%2B%3D%20nums1%20%20%23%20%E5%B0%86%E5%88%97%E8%A1%A8%20nums1%20%E6%8B%BC%E6%8E%A5%E5%88%B0%20nums%20%E4%B9%8B%E5%90%8E&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20lists%0A%20%20%20%20nums%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20Concatenate%20two%20lists%0A%20%20%20%20nums1%20%3D%20%5B6%2C%208%2C%207%2C%2010%2C%209%5D%0A%20%20%20%20nums%20%2B%3D%20nums1%20%20%23%20Append%20nums1%20to%20nums&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Sort a List +### Sorting a list -After sorting a list, we can use "binary search" and "two-pointer" algorithms, which are frequently tested in array algorithm problems. +Once the list is sorted, we can apply binary search and two-pointer algorithms, which often appear in array problems. === "Python" ```python title="list.py" - # Sort a list - nums.sort() # After sorting, list elements are arranged from smallest to largest + # Sort the list + nums.sort() # After sorting, elements are in ascending order ``` === "C++" ```cpp title="list.cpp" - /* Sort a list */ - sort(nums.begin(), nums.end()); // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + sort(nums.begin(), nums.end()); // After sorting, elements are in ascending order ``` === "Java" ```java title="list.java" - /* Sort a list */ - Collections.sort(nums); // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + Collections.sort(nums); // After sorting, elements are in ascending order ``` === "C#" ```csharp title="list.cs" - /* Sort a list */ - nums.Sort(); // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + nums.Sort(); // After sorting, elements are in ascending order ``` === "Go" ```go title="list_test.go" - /* Sort a list */ - sort.Ints(nums) // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + sort.Ints(nums) // After sorting, elements are in ascending order ``` === "Swift" ```swift title="list.swift" - /* Sort a list */ - nums.sort() // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + nums.sort() // After sorting, elements are in ascending order ``` === "JS" ```javascript title="list.js" - /* Sort a list */ - nums.sort((a, b) => a - b); // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + nums.sort((a, b) => a - b); // After sorting, elements are in ascending order ``` === "TS" ```typescript title="list.ts" - /* Sort a list */ - nums.sort((a, b) => a - b); // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + nums.sort((a, b) => a - b); // After sorting, elements are in ascending order ``` === "Dart" ```dart title="list.dart" - /* Sort a list */ - nums.sort(); // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + nums.sort(); // After sorting, elements are in ascending order ``` === "Rust" ```rust title="list.rs" - /* Sort a list */ - nums.sort(); // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + nums.sort(); // After sorting, elements are in ascending order ``` === "C" ```c title="list.c" - // C does not provide built-in dynamic arrays + // C does not provide a built-in dynamic array ``` === "Kotlin" ```kotlin title="list.kt" - /* Sort a list */ - nums.sort() // After sorting, list elements are arranged from smallest to largest + /* Sort the list */ + nums.sort() // After sorting, elements are in ascending order ``` === "Ruby" ```ruby title="list.rb" - # Sort a list - nums = nums.sort { |a, b| a <=> b } # After sorting, list elements are arranged from smallest to largest + # Sort the list + nums = nums.sort { |a, b| a <=> b } # After sorting, elements are in ascending order ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%8E%92%E5%BA%8F%E5%88%97%E8%A1%A8%0A%20%20%20%20nums.sort%28%29%20%20%23%20%E6%8E%92%E5%BA%8F%E5%90%8E%EF%BC%8C%E5%88%97%E8%A1%A8%E5%85%83%E7%B4%A0%E4%BB%8E%E5%B0%8F%E5%88%B0%E5%A4%A7%E6%8E%92%E5%88%97&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20lists%0A%20%20%20%20nums%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20Sort%20the%20list%0A%20%20%20%20nums.sort%28%29%20%20%23%20After%20sorting%2C%20elements%20are%20in%20ascending%20order&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## List Implementation +## Implementing a list -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. +Many languages, including Java, C++, and Python, provide lists. Their implementations are sophisticated, with carefully chosen parameters such as initial capacity and growth factor. Interested readers can study their source code. -To deepen our understanding of how lists work, we attempt to implement a simple list with three key design considerations: +To understand how a list works, let's implement a simplified version with three key design choices. -- **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. +- **Initial capacity**: Choose a reasonable starting array capacity. We use 10 here. +- **Element count**: Maintain `size`, the number of elements, updating it on insertion and removal. It identifies the list's end and tells us when expansion is needed. +- **Growth mechanism**: If insertion finds the capacity full, allocate a larger array using a growth factor and copy all current elements into it. Here, capacity doubles each time. ```src [file]{my_list}-[class]{my_list}-[func]{} diff --git a/en/docs/chapter_array_and_linkedlist/ram_and_cache.assets/computer_storage_devices.png b/en/docs/chapter_array_and_linkedlist/ram_and_cache.assets/computer_storage_devices.png index 87477bab6..3d0cf0b91 100644 Binary files a/en/docs/chapter_array_and_linkedlist/ram_and_cache.assets/computer_storage_devices.png and b/en/docs/chapter_array_and_linkedlist/ram_and_cache.assets/computer_storage_devices.png differ 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 512862a09..357846fb5 100644 --- a/en/docs/chapter_array_and_linkedlist/ram_and_cache.md +++ b/en/docs/chapter_array_and_linkedlist/ram_and_cache.md @@ -1,71 +1,71 @@ -# Random-Access Memory and Cache * +# Memory and cache * -In the first two sections of this chapter, we explored arrays and linked lists, two fundamental and important data structures that represent two physical layouts: "contiguous storage" and "distributed storage", respectively. +The first two sections of this chapter introduced arrays and linked lists, two fundamental structures representing contiguous and scattered storage, respectively. -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. +**Physical structure largely determines how efficiently a program uses memory and cache**, affecting its overall performance. -## Computer Storage Devices +## Computer storage devices -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. +Computers have three types of storage: disk storage, random-access memory (RAM), and cache memory. The table below compares their roles and performance characteristics. -

Table   Computer Storage Devices

+

Table   Computer storage devices

-| | 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 (CNY/GB) | Inexpensive, from a few tenths of a yuan to a few yuan per GB | Expensive, from tens to hundreds of yuan per GB | Very expensive, effectively bundled with the CPU package | +| | Disk storage | RAM | Cache | +| -------------- | ---------------------------------------- | -------------------------------------- | ------------------------------------------------- | +| Purpose | Long-term storage of the operating system, programs, files, and other data | Temporary storage of running programs and data being processed | Stores frequently accessed data and instructions to reduce CPU accesses to RAM | +| Volatility | Retains data without power | Loses data without power | Loses data without power | +| Capacity | Large, on the TB scale | Smaller, on the GB scale | Very small, on the MB scale | +| Speed | Slower, hundreds to thousands of MB/s | Faster, tens of GB/s | Very fast, tens to hundreds of GB/s | +| Price (Chinese yuan) | Inexpensive, fractions of a yuan to several yuan per GB | More expensive, tens to hundreds of yuan per GB | Very expensive, priced as part of the CPU | -We can imagine the computer storage system as a pyramid, as shown in the diagram below. Storage devices closer to the top are faster, have smaller capacity, and are more expensive. This multi-layered design is deliberate, the result of careful consideration by computer scientists and engineers. +We can picture computer storage as the pyramid in the figure below. Nearer the top, storage is faster, smaller, and more expensive. This hierarchy is a deliberate design by computer scientists and engineers. -- **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. +- **RAM cannot readily replace disk storage**. It loses data when power is removed, making it unsuitable for long-term storage. It also costs tens of times more than disk storage, making such a replacement impractical for consumers. +- **Cache capacity and speed are difficult to maximize together**. From L1 to L2 to L3, increasing capacity means greater physical size and distance from the CPU core, increasing transfer time and access latency. With current technology, a multilevel cache offers the best balance of capacity, speed, and cost. -![Computer Storage System](ram_and_cache.assets/storage_pyramid.png) +![Computer storage hierarchy](ram_and_cache.assets/storage_pyramid.png) !!! tip - 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. + The storage hierarchy balances speed, capacity, and cost. Such tradeoffs occur throughout industry, requiring us to find the best balance among competing strengths and limitations. -In summary, **hard disks are used for long-term storage of large amounts of data, RAM is used to temporarily store the data being processed during program execution, and cache is used to store frequently accessed data and instructions**, thereby improving program execution efficiency. The three work together to keep the computer system running efficiently. +In general, **disks store large amounts of data long term, RAM temporarily holds data being processed, and caches hold frequently accessed data and instructions** to speed up execution. The three work together to keep the computer efficient. -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. **By intelligently loading data from RAM**, it provides the CPU with high-speed access to data, significantly improving program execution efficiency and reducing reliance on slower RAM. +As shown in the figure below, a running program reads data from disk into RAM for the CPU to process. The cache can be viewed as part of the CPU: **it intelligently loads data from RAM** and provides faster access, improving execution speed and reducing dependence on slower memory. -![Data Flow Among Hard Disk, RAM, and Cache](ram_and_cache.assets/computer_storage_devices.png) +![Data flow between disk, RAM, and cache](ram_and_cache.assets/computer_storage_devices.png) -## Memory Efficiency of Data Structures +## Memory efficiency of data structures -In terms of memory space utilization, arrays and linked lists each have advantages and limitations. +Arrays and linked lists each have strengths and limitations in memory use. -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 one hand, **memory is finite, and the same region cannot be shared by multiple programs**, so data structures should use it efficiently. Arrays pack elements tightly without extra references (pointers) between nodes, making them more space-efficient. However, they require a sufficiently large contiguous allocation, which can waste memory, and expansion costs extra time and space. Linked lists instead allocate and reclaim memory node by node, providing greater flexibility. -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. +On the other hand, **repeated allocation and deallocation increasingly fragment free memory**, reducing its usability. Arrays' contiguous storage is less prone to causing fragmentation. Linked lists store elements in scattered locations, so frequent insertions and deletions are more likely to fragment memory. -## Cache Efficiency of Data Structures +## Cache efficiency of data structures -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. +A cache is much smaller than RAM but far faster, making it crucial to execution speed. Its limited capacity holds only a small portion of frequently accessed data. If the CPU requests data not in the cache, a cache miss occurs, and the data must be fetched from slower RAM. -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. +Clearly, **fewer cache misses mean more efficient CPU reads and writes** and better program performance. The fraction of accesses successfully served by the cache is the cache hit rate, a common measure of cache efficiency. -To achieve the highest efficiency possible, cache employs the following data loading mechanisms. +Caches use the following mechanisms to improve efficiency. -- **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. +- **Cache lines**: Data is stored and loaded in cache-line units rather than one byte at a time, making transfers more efficient. +- **Prefetching**: The processor tries to predict access patterns, such as sequential access or fixed strides, and loads matching data into the cache to improve the hit rate. +- **Spatial locality**: Accessing data suggests that nearby data may soon be needed, so the cache loads neighboring data as well. +- **Temporal locality**: Recently accessed data is likely to be accessed again soon, so keeping it in the cache improves the hit rate. -In fact, **arrays and linked lists differ in how efficiently they utilize cache**, mainly in the following respects. +**Arrays and linked lists use the cache differently**, mainly in the following ways. -- **Space occupied**: Linked-list elements occupy more space than array elements, so less useful data can fit 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. +- **Storage size**: Linked list elements require more space, leaving less room for useful data in the cache. +- **Cache lines**: Linked list data is scattered, so loading a whole cache line brings in a larger proportion of unneeded data. +- **Prefetching**: Array access patterns are more predictable, making it easier to anticipate the next data needed. +- **Spatial locality**: Array elements are stored together, so data near a loaded element is more likely to be accessed soon. -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. +Overall, **arrays have higher cache hit rates and usually faster operations than linked lists**. This makes array-based structures popular for algorithmic problems. -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. +However, **better cache efficiency does not make arrays superior in every situation**. Choose a structure based on the requirements. Both arrays and linked lists can implement a stack, discussed in the next chapter, but suit different situations. -- 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. +- For algorithm exercises, an array-based stack is often preferable because of faster operations and random access, at the cost of allocating some memory in advance. +- If the dataset is very large, changes frequently, and the stack's size is hard to predict, a linked-list-based stack may be more suitable. It can distribute data throughout memory and avoid the extra cost of array expansion. diff --git a/en/docs/chapter_array_and_linkedlist/summary.md b/en/docs/chapter_array_and_linkedlist/summary.md index 59c678e7c..06c45893f 100644 --- a/en/docs/chapter_array_and_linkedlist/summary.md +++ b/en/docs/chapter_array_and_linkedlist/summary.md @@ -1,86 +1,86 @@ # Summary -### Key Review +### Key takeaways -- Arrays and linked lists are two fundamental data structures, representing two different ways data can be stored in computer memory: contiguous storage and scattered storage. Their strengths and weaknesses 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 it may also waste some 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. +- Arrays and linked lists are fundamental structures representing contiguous and scattered memory storage. Their strengths are complementary. +- Arrays support random access and use less memory, but insertion and deletion are slow, and their length is fixed after initialization. +- Linked lists efficiently insert and remove nodes by changing references (pointers), and their length is flexible. However, accessing nodes is slow and they use more memory. Common forms include singly linked, circular, and doubly linked lists. +- A list is an ordered collection supporting insertion, removal, access, and modification. It is usually implemented as a dynamic array, retaining array advantages while allowing its length to change. +- Lists make arrays much more practical, though they may waste some memory. +- During execution, data is mainly stored in RAM. Arrays use memory more efficiently, while linked lists offer more flexible allocation. +- Cache lines, prefetching, spatial locality, and temporal locality provide fast CPU access to data, substantially improving execution speed. +- Arrays usually outperform linked lists because of higher cache hit rates. Choose a structure according to the actual requirements and setting. ### Q & A -**Q**: Does storing an array on the stack versus on the heap affect time efficiency and space efficiency? +**Q**: Does storing an array on the stack or heap affect time and space efficiency? -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. +Both use contiguous memory, so data operations have essentially the same efficiency. However, the stack and heap differ in these ways. -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. +1. Allocation and deallocation: Stack memory is relatively small and allocated automatically by the compiler. The heap is larger, supports dynamic allocation in code, and is more prone to fragmentation. Heap allocation and deallocation are therefore usually slower. +2. Size limits: Stack space is relatively small, while the heap is generally limited by available memory, making it better suited to large arrays. +3. Flexibility: Stack array sizes must be known at compile time, while heap array sizes can be determined at runtime. -**Q**: Why do arrays require elements of the same type, while linked lists do not emphasize this requirement? +**Q**: Why must array elements have the same type, while this requirement is not emphasized for linked lists? -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. +Linked lists consist of nodes connected by references (pointers). Different nodes can hold different types, such as `int`, `double`, `string`, and `object`. -In contrast, array elements must be of the same type so that their positions can be determined by calculating offsets. 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 sizes". +Array elements must have the same type so that their positions can be calculated from offsets. If an array contained both `int` and `long`, occupying 4 and 8 bytes respectively, the following formula would not work because there would be two element sizes. ```shell -# element address = array base address (address of the first element) + element size * element index +# Element address = array address (first element's address) + element size * element index ``` -**Q**: After deleting node `P`, do we need to set `P.next` to `None`? +**Q**: After removing node `P`, must we set `P.next` to `None`? -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. +Leaving `P.next` unchanged is acceptable. A traversal from the list's head to its tail no longer encounters `P`, so it has been removed. Whatever `P` points to no longer affects the list. -From an algorithms-and-problem-solving perspective, leaving the pointer connected is fine as long as the program logic is correct. From a standard-library implementation perspective, explicitly disconnecting it is safer and clearer. If it is not disconnected and the deleted node is not reclaimed properly, it may affect the reclamation of successor nodes. +For algorithm exercises, leaving it connected is fine as long as the logic is correct. For a standard library, disconnecting it is safer and clearer: if the removed node is not reclaimed properly, its reference may prevent its successor from being reclaimed. -**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)$? +**Q**: Insertion and deletion in a linked list take $O(1)$ time, but locating the element first takes $O(n)$. Why is the overall complexity not $O(n)$? -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)$. +Finding an element and then deleting it does take $O(n)$ time. However, $O(1)$ insertion and deletion are useful in other settings. A deque, for example, can use a linked list with pointers maintained at the head and tail, giving $O(1)$ operations. -**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? +**Q**: In the linked list storage diagram, does the light-blue pointer occupy a separate memory location, or share half the node's space with its value? -This diagram is a qualitative representation; a quantitative representation requires analysis based on the specific situation. +The diagram is qualitative. Exact sizes depend on the circumstances. -- 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. +- Node values of different types, such as `int`, `long`, `double`, and objects, occupy different amounts of space. +- A pointer's size depends on the operating system and compilation environment, and is usually 8 or 4 bytes. -**Q**: Is appending an element at the end of a list always $O(1)$? +**Q**: Does appending to a list always take $O(1)$ time? -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)$. +If appending exceeds the available capacity, the list must first expand. The system allocates new memory and copies all existing elements, making that operation $O(n)$. -**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? +**Q**: When lists waste memory, does this mean the extra variables for capacity, length, and growth factor? -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. +The main waste has two sources. First, the initial allocation may exceed what we need. Second, to avoid frequent resizing, capacity usually grows by a factor such as $\times 1.5$, leaving empty slots that may never all be filled. -**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? +**Q**: In Python, after `n = [1, 2, 3]`, the three elements seem to have consecutive addresses. But in `m = [2, 1, 3]`, their IDs are not consecutive and match those in `n`. Is `m` still an array? -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. +Consider storing linked list nodes instead: `n = [n1, n2, n3, n4, n5]`. These five objects are usually scattered in memory, yet an index still gives a node's address in $O(1)$ time. The array stores references to the nodes, not the nodes themselves. -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. +Unlike many languages, Python also wraps numbers in objects. A list stores references to those objects rather than the numbers themselves. Thus, the same numbers in the two arrays can have the same IDs, and their addresses need not be contiguous. -**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? +**Q**: C++ STL's `std::list` already implements a doubly linked list. Why do algorithm books rarely use it directly? Does it have limitations? -On one hand, we often prefer to use arrays for implementing algorithms and only use linked lists when necessary, mainly for two reasons. +First, we often favor arrays and use linked lists only when needed, mainly for two reasons. -- 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. +- Space overhead: Each element needs two extra pointers, to its predecessor and successor, so `std::list` usually consumes more space than `std::vector`. +- Poor cache locality: Its data is not contiguous, making cache use less efficient. In general, `std::vector` performs better. -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. +Second, linked structures are mainly needed for binary trees and graphs. For stacks and queues, we usually use the language's provided `stack` and `queue` rather than a linked list directly. -**Q**: Does the operation `res = [[0]] * n` create a 2D list where each `[0]` is independent? +**Q**: Does `res = [[0]] * n` create a two-dimensional list with independent `[0]` entries? -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. +No. Every `[0]` is a reference to the same object. Modifying an element through one reference changes the corresponding element seen through all the others. -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. +To create independent `[0]` lists, use `res = [[0] for _ in range(n)]`. This initializes $n$ separate list objects. -**Q**: Does the operation `res = [0] * n` create a list where each integer 0 is independent? +**Q**: Does `res = [0] * n` create independent integer zeros? -In this list, all the integer zeros reference 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. +All zeros in the list refer to the same object. Python caches small integers, usually -5 through 256, to maximize reuse and improve performance. -Although they all reference the same object, we can still modify each element in the list independently. This is because Python integers are "immutable objects". When we modify an element, we actually switch that element to reference a different object, rather than changing the original object itself. +Even so, each list entry can be changed independently because Python integers are immutable. Assigning a new value switches the entry to a different object reference instead of modifying the original object. -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. +When an element is mutable, such as a list, dictionary, or class instance, modifying the object itself is visible through every reference to it. diff --git a/en/docs/chapter_backtracking/backtracking_algorithm.md b/en/docs/chapter_backtracking/backtracking_algorithm.md index baea6c055..5c5b1400f 100644 --- a/en/docs/chapter_backtracking/backtracking_algorithm.md +++ b/en/docs/chapter_backtracking/backtracking_algorithm.md @@ -1,45 +1,45 @@ -# Backtracking Algorithm +# Backtracking algorithms -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. +A backtracking algorithm solves a problem through exhaustive search. Starting from an initial state, it explores possible solutions by brute force, recording valid solutions as it finds them, until a solution is found or every possible choice has been tried without success. -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. +Backtracking usually traverses the solution space through depth-first search. In the binary tree chapter, we learned that preorder, inorder, and postorder traversal are all forms of depth-first search. We will use preorder traversal to build a backtracking problem and explore the approach step by step. !!! question "Example 1" - Given a binary tree, search and record all nodes with value $7$, and return a list of these nodes. + Given a binary tree, find and record every node with value $7$. Return a list of these nodes. -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: +Traverse the tree in preorder and check whether the current node's value is $7$. If so, add that node's value to the result list `res`. The code and figure below show the process: ```src [file]{preorder_traversal_i_compact}-[class]{}-[func]{pre_order} ``` -![Search for nodes in preorder traversal](backtracking_algorithm.assets/preorder_find_nodes.png) +![Finding nodes during preorder traversal](backtracking_algorithm.assets/preorder_find_nodes.png) -## Attempt and Backtrack +## Trying choices and backtracking -**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. +**Backtracking gets its name from its pattern of trying choices and undoing them while searching the solution space**. When the search reaches a state that cannot advance or lead to a valid solution, it undoes the last choice, returns to the previous state, and tries other possibilities. -For Example 1, visiting each node represents an "attempt", while skipping over a leaf node or the `return` that brings the traversal back to the parent node represents a "backtrack". +In Example 1, visiting a node is a trial, while a `return` after passing a leaf or returning to a parent is backtracking. -It is worth noting that **backtracking is not limited to function returns alone**. To illustrate this, let's extend Example 1 slightly. +**Backtracking involves more than returning from a function**. To see why, let's extend Example 1. !!! question "Example 2" - In a binary tree, search all nodes with value $7$, **and return the paths from the root node to these nodes**. + Find every node with value $7$ in a binary tree, and **return the paths from the root to those nodes**. -Based on the code from Example 1, we need to use a list `path` to record the path of visited nodes. 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: +Extend the first solution with a list `path` recording the current path of visited nodes. When a node with value $7$ is reached, append a copy of `path` to the result list `res`. After traversal, `res` contains all solutions. The code is as follows: ```src [file]{preorder_traversal_ii_compact}-[class]{}-[func]{pre_order} ``` -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**. +Each time we try a node, append it to `path`. Before backtracking, pop that node from `path` **to restore the state from before the trial**. -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. +As the figure below illustrates, **trying a choice and backtracking are like moving forward and undoing that move**: they are inverse operations. === "<1>" - ![Attempt and backtrack](backtracking_algorithm.assets/preorder_find_paths_step1.png) + ![Trying choices and backtracking](backtracking_algorithm.assets/preorder_find_paths_step1.png) === "<2>" ![preorder_find_paths_step2](backtracking_algorithm.assets/preorder_find_paths_step2.png) @@ -73,70 +73,70 @@ Observing the process shown in the following figure, **we can understand attempt ## Pruning -Complex backtracking problems usually contain one or more constraints. **Constraints can typically be used for "pruning"**. +Complex backtracking problems usually have one or more constraints, **which can often be used for pruning**. !!! question "Example 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$**. + Find every node with value $7$ in a binary tree and return the paths from the root to those nodes, **requiring that no path contain a node with value $3$**. -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: +To enforce this constraint, **add a pruning step**: when a node with value $3$ is encountered, return immediately without searching farther. The code is as follows: ```src [file]{preorder_traversal_iii_compact}-[class]{}-[func]{pre_order} ``` -"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. +Pruning is an apt name. As shown in the figure below, **we cut off search branches that violate the constraints**, avoiding pointless trials and improving efficiency. ![Pruning according to constraints](backtracking_algorithm.assets/preorder_find_constrained_paths.png) -## Framework Code +## A code template -Next, we attempt to extract a general framework centered on backtracking's "attempt, backtrack, and pruning" to improve code generality. +We will now extract the core pattern of trying, undoing, and pruning into a more general backtracking template. -In the following framework code, `state` represents the current state of the problem, and `choices` represents the choices available in the current state: +In the following template, `state` is the current problem state, and `choices` contains the choices available from that state: === "Python" ```python title="" def backtrack(state: State, choices: list[choice], res: list[state]): - """Backtracking algorithm framework""" - # Check if it is a solution + """Backtracking template""" + # Check whether the state is a solution if is_solution(state): # Record the solution record_solution(state, res) - # Stop searching + # Stop searching this branch return - # Traverse all choices + # Try every choice for choice in choices: - # Pruning: check if the choice is valid + # Prune: check whether the choice is valid if is_valid(state, choice): - # Attempt: make a choice and update the state + # Try: make the choice and update the state make_choice(state, choice) backtrack(state, choices, res) - # Backtrack: undo the choice and restore to the previous state + # Backtrack: undo the choice and restore the previous state undo_choice(state, choice) ``` === "C++" ```cpp title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ void backtrack(State *state, vector &choices, vector &res) { - // Check if it is a solution + // Check whether the state is a solution if (isSolution(state)) { // Record the solution recordSolution(state, res); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice for (Choice choice : choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, choice); backtrack(state, choices, res); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } @@ -146,23 +146,23 @@ In the following framework code, `state` represents the current state of the pro === "Java" ```java title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ void backtrack(State state, List choices, List res) { - // Check if it is a solution + // Check whether the state is a solution if (isSolution(state)) { // Record the solution recordSolution(state, res); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice for (Choice choice : choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, choice); backtrack(state, choices, res); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } @@ -172,23 +172,23 @@ In the following framework code, `state` represents the current state of the pro === "C#" ```csharp title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ void Backtrack(State state, List choices, List res) { - // Check if it is a solution + // Check whether the state is a solution if (IsSolution(state)) { // Record the solution RecordSolution(state, res); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice foreach (Choice choice in choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (IsValid(state, choice)) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state MakeChoice(state, choice); Backtrack(state, choices, res); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state UndoChoice(state, choice); } } @@ -198,23 +198,23 @@ In the following framework code, `state` represents the current state of the pro === "Go" ```go title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ func backtrack(state *State, choices []Choice, res *[]State) { - // Check if it is a solution + // Check whether the state is a solution if isSolution(state) { // Record the solution recordSolution(state, res) - // Stop searching + // Stop searching this branch return } - // Traverse all choices + // Try every choice for _, choice := range choices { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if isValid(state, choice) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, choice) backtrack(state, choices, res) - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice) } } @@ -224,23 +224,23 @@ In the following framework code, `state` represents the current state of the pro === "Swift" ```swift title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ func backtrack(state: inout State, choices: [Choice], res: inout [State]) { - // Check if it is a solution + // Check whether the state is a solution if isSolution(state: state) { // Record the solution recordSolution(state: state, res: &res) - // Stop searching + // Stop searching this branch return } - // Traverse all choices + // Try every choice for choice in choices { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if isValid(state: state, choice: choice) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state: &state, choice: choice) backtrack(state: &state, choices: choices, res: &res) - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state: &state, choice: choice) } } @@ -250,23 +250,23 @@ In the following framework code, `state` represents the current state of the pro === "JS" ```javascript title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ function backtrack(state, choices, res) { - // Check if it is a solution + // Check whether the state is a solution if (isSolution(state)) { // Record the solution recordSolution(state, res); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice for (let choice of choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, choice); backtrack(state, choices, res); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } @@ -276,23 +276,23 @@ In the following framework code, `state` represents the current state of the pro === "TS" ```typescript title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ function backtrack(state: State, choices: Choice[], res: State[]): void { - // Check if it is a solution + // Check whether the state is a solution if (isSolution(state)) { // Record the solution recordSolution(state, res); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice for (let choice of choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, choice); backtrack(state, choices, res); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } @@ -302,23 +302,23 @@ In the following framework code, `state` represents the current state of the pro === "Dart" ```dart title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ void backtrack(State state, List, List res) { - // Check if it is a solution + // Check whether the state is a solution if (isSolution(state)) { // Record the solution recordSolution(state, res); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice for (Choice choice in choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, choice); backtrack(state, choices, res); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice); } } @@ -328,23 +328,23 @@ In the following framework code, `state` represents the current state of the pro === "Rust" ```rust title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ fn backtrack(state: &mut State, choices: &Vec, res: &mut Vec) { - // Check if it is a solution + // Check whether the state is a solution if is_solution(state) { // Record the solution record_solution(state, res); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice for choice in choices { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if is_valid(state, choice) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state make_choice(state, choice); backtrack(state, choices, res); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undo_choice(state, choice); } } @@ -354,23 +354,23 @@ In the following framework code, `state` represents the current state of the pro === "C" ```c title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ void backtrack(State *state, Choice *choices, int numChoices, State *res, int numRes) { - // Check if it is a solution + // Check whether the state is a solution if (isSolution(state)) { // Record the solution recordSolution(state, res, numRes); - // Stop searching + // Stop searching this branch return; } - // Traverse all choices + // Try every choice for (int i = 0; i < numChoices; i++) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, &choices[i])) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, &choices[i]); backtrack(state, choices, numChoices, res, numRes); - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, &choices[i]); } } @@ -380,23 +380,23 @@ In the following framework code, `state` represents the current state of the pro === "Kotlin" ```kotlin title="" - /* Backtracking algorithm framework */ + /* Backtracking template */ fun backtrack(state: State?, choices: List, res: List?) { - // Check if it is a solution + // Check whether the state is a solution if (isSolution(state)) { // Record the solution recordSolution(state, res) - // Stop searching + // Stop searching this branch return } - // Traverse all choices + // Try every choice for (choice in choices) { - // Pruning: check if the choice is valid + // Prune: check whether the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice and update the state + // Try: make the choice and update the state makeChoice(state, choice) backtrack(state, choices, res) - // Backtrack: undo the choice and restore to the previous state + // Backtrack: undo the choice and restore the previous state undoChoice(state, choice) } } @@ -406,98 +406,98 @@ In the following framework code, `state` represents the current state of the pro === "Ruby" ```ruby title="" - ### Backtracking algorithm framework ### + ### Backtracking template ### def backtrack(state, choices, res) - # Check if it is a solution + # Check whether the state is a solution if is_solution?(state) # Record the solution record_solution(state, res) return end - # Traverse all choices + # Try every choice for choice in choices - # Pruning: check if the choice is valid + # Prune: check whether the choice is valid if is_valid?(state, choice) - # Attempt: make a choice and update the state + # Try: make the choice and update the state make_choice(state, choice) backtrack(state, choices, res) - # Backtrack: undo the choice and restore to the previous state + # Backtrack: undo the choice and restore the previous state undo_choice(state, choice) end end end ``` -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: +Now apply this template to Example 3. The `state` is the current path of visited nodes, `choices` consists of the current node's left and right children, and `res` is the list of paths: ```src [file]{preorder_traversal_iii_template}-[class]{}-[func]{backtrack} ``` -As per the problem statement, we should continue searching after finding a node with value $7$. **Therefore, we need to remove the `return` statement after recording the solution**. The following figure compares the search process with and without the `return` statement. +The problem requires us to keep searching after finding a node with value $7$, **so remove the `return` after recording a solution**. The figure below compares the searches with and without that `return`. -![Comparison of search process with and without return statement](backtracking_algorithm.assets/backtrack_remove_return_or_not.png) +![Comparing searches with and without return](backtracking_algorithm.assets/backtrack_remove_return_or_not.png) -Compared to code based on preorder traversal, code based on the backtracking algorithm framework appears more verbose, but is more general. In fact, **many backtracking problems can be solved within this framework**. We only need to define `state` and `choices` for the specific problem and implement each method in the framework. +The template-based implementation is more verbose than the preorder version, but more general. **Many backtracking problems can be solved within this template**: define `state` and `choices` for the problem, then implement each helper method. -## Common Terminology +## Common terminology -To analyze algorithmic problems more clearly, we summarize the meanings of common terminology used in backtracking algorithms and provide corresponding examples from Example 3, as shown in the following table. +The table below summarizes common backtracking terms and illustrates them with Example 3, helping us analyze problems more clearly. -

Table   Common Backtracking Algorithm Terminology

+

Table   Common backtracking terminology

-| Term | Definition | Example 3 | -| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -| Solution (solution) | A solution is an answer that satisfies the specific conditions of a problem; there may be one or more solutions | All paths from root to nodes with value $7$ that satisfy the constraint | -| Constraint (constraint) | A constraint is a condition in the problem that limits the feasibility of solutions, typically used for pruning | Paths do not contain nodes with value $3$ | -| State (state) | State represents the situation of a problem at a certain moment, including the choices already made | The currently visited node path, i.e., the `path` list of nodes | -| Attempt (attempt) | An attempt is the process of exploring the solution space according to available choices, including making choices, updating state, and checking if it is a solution | Recursively visit left (right) child nodes, add nodes to `path`, check if node value is $7$ | -| Backtrack (backtracking) | Backtracking refers to undoing previous choices and returning to a previous state when encountering a state that does not satisfy constraints | Stop searching when passing over leaf nodes, ending node visits, or encountering nodes with value $3$; function returns | -| Pruning (pruning) | Pruning is a method of avoiding meaningless search paths according to problem characteristics and constraints, which can improve search efficiency | When encountering a node with value $3$, do not continue searching | +| Term | Definition | Example 3 | +| ---------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Solution | An answer satisfying the problem's conditions; there may be one or more | All paths from the root to a node valued $7$ that satisfy the constraint | +| Constraint | A condition restricting which solutions are feasible, often used for pruning | The path contains no node valued $3$ | +| State | The situation at a particular point, including the choices already made | The current path of visited nodes, stored in `path` | +| Trial | Exploring the solution space by making a choice, updating the state, and checking for a solution | Recursively visit a left or right child, append it to `path`, and check whether its value is $7$ | +| Backtracking | Undoing an earlier choice and returning to the previous state when a constraint cannot be met | Stop searching and return after passing a leaf, finishing a node, or encountering value $3$ | +| Pruning | Using problem properties and constraints to avoid pointless search paths and improve efficiency | Stop searching when a node valued $3$ is encountered | !!! tip - The concepts of problem, solution, state, etc. are universal and appear in divide-and-conquer, backtracking, dynamic programming, greedy algorithms, and others. + Concepts such as problems, solutions, and states are shared by divide and conquer, backtracking, dynamic programming, greedy algorithms, and other approaches. -## Advantages and Limitations +## Strengths and limitations -The backtracking algorithm is essentially a depth-first search algorithm that tries all possible solutions until it finds one that satisfies the conditions. The advantage of this approach is that it can find all possible solutions, and with reasonable pruning operations, it achieves high efficiency. +Backtracking is fundamentally a depth-first search that tries possible solutions until it finds ones meeting the conditions. It can find all possible solutions and can be highly efficient with effective pruning. -However, when dealing with large-scale or complex problems, **the running efficiency of the backtracking algorithm may be unacceptable**. +However, for large or complex problems, **backtracking may be unacceptably slow**. -- **Time**: The backtracking algorithm usually needs to traverse all possibilities in the state space, and the time complexity can reach exponential or factorial order. -- **Space**: During recursive calls, the current state needs to be saved (such as paths, auxiliary variables used for pruning, etc.), and when the depth is large, the space requirement can become very large. +- **Time**: Backtracking often explores every possibility in the state space, leading to exponential or factorial time complexity. +- **Space**: Recursive calls must preserve state, such as the path and auxiliary pruning variables. Deep recursion can therefore require substantial space. -Nevertheless, **the backtracking algorithm is still the best solution for certain search problems and constraint satisfaction problems**. For these problems, since we cannot predict which choices will generate valid solutions, we must traverse all possible choices. In this case, **the key is how to optimize efficiency**. There are two common efficiency optimization methods. +Even so, **backtracking remains the best approach for some search and constraint satisfaction problems**. When we cannot predict which choices lead to valid solutions, we must explore every possibility. **The key is then to improve efficiency**, commonly through two techniques. -- **Pruning**: Avoid searching paths that are guaranteed not to produce solutions, thereby saving time and space. -- **Heuristic search**: Introduce certain strategies or estimation values during the search process to prioritize searching paths that are most likely to produce valid solutions. +- **Pruning**: Avoid paths that cannot produce a solution, saving time and space. +- **Heuristic search**: Use strategies or estimates to explore paths most likely to yield valid solutions first. -## Typical Backtracking Examples +## Typical backtracking problems -The backtracking algorithm can be used to solve many search problems, constraint satisfaction problems, and combinatorial optimization problems. +Backtracking can solve many search, constraint satisfaction, and combinatorial optimization problems. -**Search problems**: The goal of these problems is to find solutions that satisfy specific conditions. +**Search problems** seek solutions satisfying specified conditions. -- Permutation problem: Given a set, find all possible permutations and combinations. -- Subset sum problem: Given a set and a target sum, find all subsets in the set whose elements sum to the target. -- Tower of Hanoi: Given three pegs and a series of disks of different sizes, move all disks from one peg to another, moving only one disk at a time, and never placing a larger disk on a smaller disk. +- Permutations: Given a collection, find every possible ordering of its elements. +- Subset sum: Given a collection and a target sum, find every subset whose sum equals the target. +- Tower of Hanoi: Given three rods and disks of different sizes, move all disks from one rod to another, moving only one at a time and never placing a larger disk on a smaller one. -**Constraint satisfaction problems**: The goal of these problems is to find solutions that satisfy all constraints. +**Constraint satisfaction problems** seek solutions meeting all constraints. -- N-Queens: Place $n$ queens on an $n \times n$ chessboard such that they do not attack each other. -- Sudoku: Fill numbers $1$ to $9$ in a $9 \times 9$ grid such that each row, column, and $3 \times 3$ subgrid contains no repeated digits. -- Graph coloring: Given an undirected graph, color each vertex with the minimum number of colors such that adjacent vertices have different colors. +- $n$-queens: Place $n$ queens on an $n \times n$ chessboard so that no two attack each other. +- Sudoku: Fill a $9 \times 9$ grid with digits $1$ ~ $9$ so that no digit repeats in any row, column, or $3 \times 3$ subgrid. +- Graph coloring: Color an undirected graph's vertices with as few colors as possible, ensuring adjacent vertices have different colors. -**Combinatorial optimization problems**: The goal of these problems is to find an optimal solution that satisfies certain conditions in a combinatorial space. +**Combinatorial optimization problems** seek an optimal solution satisfying conditions within a space of combinations. -- 0-1 Knapsack: Given a set of items and a knapsack, each item has a value and weight. Under the knapsack capacity constraint, select items to maximize total value. -- Traveling Salesman Problem: Starting from a point in a graph, visit all other points exactly once and return to the starting point, finding the shortest path. -- Maximum Clique: Given an undirected graph, find the largest complete subgraph, i.e., a subgraph where any two vertices are connected by an edge. +- 0-1 knapsack: Given items with values and weights and a knapsack, choose items to maximize total value within the capacity limit. +- Traveling salesperson: Starting at a vertex in a graph, visit every other vertex exactly once and return to the start along the shortest possible route. +- Maximum clique: Given an undirected graph, find its largest complete subgraph, in which every pair of vertices is connected by an edge. -Note that for many combinatorial optimization problems, backtracking is not the optimal solution. +For many combinatorial optimization problems, backtracking is not the best approach. -- The 0-1 Knapsack problem is usually solved using dynamic programming to achieve higher time efficiency. -- The Traveling Salesman Problem is a famous NP-Hard problem; common solutions include genetic algorithms and ant colony algorithms. -- The Maximum Clique problem is a classical problem in graph theory and can be solved using heuristic algorithms such as greedy algorithms. +- 0-1 knapsack is usually solved more efficiently with dynamic programming. +- The traveling salesperson problem is a well-known NP-hard problem. Common approaches include genetic algorithms and ant colony algorithms. +- Maximum clique is a classic graph theory problem that can be approached with heuristics such as greedy algorithms. diff --git a/en/docs/chapter_backtracking/exercises.md b/en/docs/chapter_backtracking/exercises.md index 1ae3334e3..f09db50de 100644 --- a/en/docs/chapter_backtracking/exercises.md +++ b/en/docs/chapter_backtracking/exercises.md @@ -1,87 +1,87 @@ # Exercises -## Concept Review +## Check your understanding -### Will This Permutation Algorithm Miss Results? +### Will this permutation algorithm miss results? -A backtracking algorithm tries to generate all permutations using `1, 2, 3` in that order. Each time it chooses a number `x`, it: +A backtracking algorithm tries the numbers `1, 2, 3` in order to generate all permutations. Whenever it chooses a number `x`, it: 1. Appends `x` to the current path. -2. Marks `x` as "used." +2. Marks `x` as used. 3. Recursively fills the next position. -When the recursive call returns, the student removes only `x` from the end of the path and then tries the next number. +After the recursive call returns, the student removes only `x` from the end of the path, then continues trying the next number. -1. Which permutation does the algorithm generate first? Can it still generate all 6 permutations? -2. Before returning to the previous level, is removing only the last number from the path enough? If not, what else must be done? Explain why. +1. Which permutation does the algorithm produce first? Can it still produce all six permutations? +2. Before returning to the previous recursion level, is removing the last number from the path enough? If not, what else is needed? Explain. -??? success "Answer" +??? success "Reference answer" - 1. It first generates `[1, 2, 3]`, but it cannot generate all the permutations. Although the path becomes shorter when calls return, - the markers for 1, 2, and 3 all remain "used," leaving no available number for later branches. + 1. It first produces `[1, 2, 3]`, but cannot generate every permutation. Although the path becomes shorter as calls return, + the numbers 1, 2, and 3 remain marked as used, leaving later branches with no available numbers. - 2. It is not enough. After removing `x` from the end of the path, the algorithm must also mark `x` as "unused" again. - The current path and the used markers together describe the search state. A choice changes both of them, so backtracking must restore both - before another branch can choose `x` again. + 2. No. After removing `x` from the end of the path, we must also mark `x` as unused again. + The current path and the used markers together describe the search state. Choosing a number changes both, so backtracking must restore both + to allow other branches to choose `x` again. -### Does the Order of Choosing Numbers Matter? +### Does the order of choosing numbers matter? -You are given the sorted array `[2, 3, 5]` and target value 5. Each number may be chosen repeatedly. -The algorithm requires the numbers along each search path to appear only in nondecreasing order. +Given the sorted array `[2, 3, 5]` and target value 5, each number may be chosen repeatedly. +The algorithm requires the numbers along each search path to appear in nondecreasing order. -1. Which distinct combinations can be found? -2. Why is there no need to search for the same group of numbers in different orders? What does the nondecreasing-order restriction accomplish? -3. Suppose the current path is `[3]`, the remaining amount is 2, and the next candidate is 3. Why can the algorithm stop checking all later candidates at this level? +1. Which distinct combinations can be obtained? +2. Why is it unnecessary to search the same set of numbers in different orders? What does the nondecreasing-order restriction achieve? +3. The current path is `[3]`, the remaining sum is 2, and the next candidate is 3. Why can we stop checking all further candidates at this level? -??? success "Answer" +??? success "Reference answer" 1. The distinct combinations are `[2, 3]` and `[5]`. - 2. This exercise treats `[2, 3]` and `[3, 2]` as the same combination; the order in which numbers are selected does not create a different answer. - Requiring numbers in a path to appear in nondecreasing order lets the search skip duplicate arrangements such as `[3, 2]`. + 2. This problem treats `[2, 3]` and `[3, 2]` as the same combination: selection order does not distinguish answers. + Requiring the path's numbers to appear in nondecreasing order lets the search skip duplicate combinations such as `[3, 2]` directly. - 3. The remaining amount is 2, while candidate 3 is already greater than 2. Because the array is sorted, - all later candidates are even larger and cannot be added to the current combination, so the algorithm can stop checking this level immediately. + 3. The remaining sum is 2, and candidate 3 already exceeds it. Since the array is sorted, + all candidates after 3 are larger still and cannot join the current combination. We can therefore stop checking this level. -### Where Can the Next Queen Be Placed? +### Where can the next queen be placed? -Place queens row by row on a `4 × 4` chessboard, with both row and column indices starting at 0. -Queens have already been placed at `(0, 1)` and `(1, 3)`. The next queen must be placed in row 2. +Place queens row by row on a `4 × 4` board, with both row and column indices starting at 0. +Queens are already at `(0, 1)` and `(1, 3)`. The next queen must be placed in row 2. 1. Which columns are ruled out because they already contain a queen? -2. Among the remaining columns, which positions are ruled out because they share a diagonal with an existing queen? +2. Among the remaining columns, which positions are ruled out by sharing a diagonal with a queen? 3. Which positions in row 2 remain available to try? -??? success "Answer" +??? success "Reference answer" - 1. Columns 1 and 3 already contain queens, so positions `(2, 1)` and `(2, 3)` are ruled out. + 1. Columns 1 and 3 already contain queens, ruling out positions `(2, 1)` and `(2, 3)`. - 2. Among the remaining positions, `(2, 2)` lies on the same diagonal as `(1, 3)`, so it is also ruled out. + 2. Of the remaining positions, `(2, 2)` shares a diagonal with `(1, 3)`, so it is also ruled out. Position `(2, 0)` shares neither a column nor a diagonal with either existing queen. 3. The only position to try in row 2 is `(2, 0)`. - This step shows only that the current placement is valid. If the board cannot be completed later, the algorithm must still backtrack and try an earlier alternative. + This step establishes only that the current placement is valid. If the board cannot be completed later, we must still backtrack and try other earlier choices. -## Programming Exercises +## Programming exercises -### Permutations of Distinct Elements +### All permutations of distinct elements The integer array `nums` contains at least one element, and all its elements are distinct. -List every possible order formed by using each element exactly once, and return each order as an array. +List every possible ordering that uses each element exactly once, returning each ordering as an array. The permutations may appear in any order in the result. -Use backtracking, with a Boolean array recording whether the element at each position has already been selected for the current permutation. +Use backtracking and a Boolean array to record whether the element at each position is already included in the current permutation. ??? tip "Hints" - 1. The recursion depth indicates which position of the permutation is currently being filled - 2. At each level, try only elements that have not yet been used - 3. When the path's length equals the length of `nums`, add a copy of the path to the answer + 1. The recursion depth indicates which position in the permutation is being filled. + 2. At each level, try only unused elements. + 3. When the path length reaches the length of `nums`, add a copy of the path to the answer. [LeetCode](https://leetcode.com/problems/permutations/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_backtracking/index.md b/en/docs/chapter_backtracking/index.md index 50f3f5c94..731534256 100644 --- a/en/docs/chapter_backtracking/index.md +++ b/en/docs/chapter_backtracking/index.md @@ -4,6 +4,6 @@ !!! abstract - We are like explorers in a maze, and may encounter difficulties on the path forward. - - The power of backtracking allows us to start over, keep trying, and eventually find the exit leading to light. + Like explorers in a maze, we may encounter obstacles as we move forward. + + Backtracking lets us start again, try new paths, and eventually find our way out into the light. diff --git a/en/docs/chapter_backtracking/n_queens_problem.md b/en/docs/chapter_backtracking/n_queens_problem.md index d3794a414..d7141c18c 100644 --- a/en/docs/chapter_backtracking/n_queens_problem.md +++ b/en/docs/chapter_backtracking/n_queens_problem.md @@ -1,53 +1,53 @@ -# N-Queens Problem +# N-queens problem !!! question - According to the rules of chess, a queen can attack any piece in the same row, column, or diagonal. Given $n$ queens and an $n \times n$ chessboard, find an arrangement such that no two queens can attack each other. + In chess, a queen can attack any piece in the same row, column, or diagonal. Given $n$ queens and an $n \times n$ chessboard, find all arrangements in which no two queens can attack each other. -As shown in the figure below, when $n = 4$, there are two solutions that can be found. From the perspective of the backtracking algorithm, an $n \times n$ chessboard has $n^2$ squares, which provide all the choices `choices`. During the process of placing queens one by one, the chessboard state changes continuously, and the chessboard at each moment represents the state `state`. +As shown in the figure below, there are two solutions when $n = 4$. From a backtracking perspective, the $n \times n$ board has $n^2$ squares, which provide all possible `choices`. As we place queens one by one, the board changes; its arrangement at any moment is the current `state`. -![Solution to the 4-queens problem](n_queens_problem.assets/solution_4_queens.png) +![Solutions to the 4-queens problem](n_queens_problem.assets/solution_4_queens.png) -The figure below illustrates the three constraints of this problem: **multiple queens cannot be in the same row, the same column, or on the same diagonal**. It is worth noting that diagonals are divided into two types: the main diagonal `\` and the anti-diagonal `/`. +The figure below shows the three constraints: **no two queens may share a row, column, or diagonal**. Diagonals run in two directions: the main-diagonal direction `\` and the antidiagonal direction `/`. ![Constraints of the n-queens problem](n_queens_problem.assets/n_queens_constraints.png) -### Row-By-Row Placement Strategy +### Placing queens row by row -Since both the number of queens and the number of rows on the chessboard are $n$, we can easily derive a conclusion: **each row of the chessboard allows one and only one queen to be placed**. +There are as many queens as rows, namely $n$, so we can immediately conclude that **each row must contain exactly one queen**. -This means we can adopt a row-by-row placement strategy: starting from the first row, place one queen in each row until the last row is completed. +We can therefore place queens row by row: start at the first row and place one queen in each row through the last. -The figure below shows the row-by-row placement process for the 4-queens problem. Due to space limitations, the figure only expands one search branch of the first row, and all schemes that violate the column or diagonal constraints are pruned. +The figure below illustrates this process for 4 queens. To fit the illustration, only one branch from the first row is expanded, and all arrangements violating the column or diagonal constraints are pruned. -![Row-by-row placement strategy](n_queens_problem.assets/n_queens_placing.png) +![Placing queens row by row](n_queens_problem.assets/n_queens_placing.png) -Essentially, **the row-by-row placement strategy serves a pruning function**, as it avoids all search branches where multiple queens appear in the same row. +In essence, **placing queens row by row is itself a form of pruning**: it eliminates every search branch that places multiple queens in the same row. -### Column and Diagonal Pruning +### Pruning by columns and diagonals -To satisfy the column constraint, we can use a boolean array `cols` of length $n$ to record whether each column has a queen. Before each placement decision, we use `cols` to prune columns that already have queens, and dynamically update the state of `cols` during backtracking. +To enforce the column constraint, use a Boolean array `cols` of length $n$ to record whether each column contains a queen. Before placing a queen, consult `cols` to prune columns already occupied, and update `cols` as the search proceeds and backtracks. !!! tip - Please note that the origin of the matrix is located in the upper-left corner, where the row index increases from top to bottom, and the column index increases from left to right. + The matrix starts at its top-left corner. Row indices increase downward, and column indices increase to the right. -So how do we handle diagonal constraints? Consider a square on the chessboard with row and column indices $(row, col)$. If we select a specific main diagonal in the matrix, we find that all squares on that diagonal have the same difference between their row and column indices, **meaning that $row - col$ is a constant value for all squares on the main diagonal**. +How do we enforce the diagonal constraints? Let $(row, col)$ be the row and column indices of a square. Along any diagonal in the main-diagonal direction, the row index minus the column index is the same: **$row - col$ is constant along that diagonal**. -In other words, if two squares satisfy $row_1 - col_1 = row_2 - col_2$, they must be on the same main diagonal. Using this pattern, we can use the array `diags1` shown in the figure below to record whether there is a queen on each main diagonal. +Thus, if two squares satisfy $row_1 - col_1 = row_2 - col_2$, they lie on the same main-direction diagonal. Using this property, the array `diags1` in the figure below records whether each such diagonal contains a queen. -Similarly, **for all squares on an anti-diagonal, the sum $row + col$ is a constant value**. We can likewise use the array `diags2` to handle anti-diagonal constraints. +Similarly, **$row + col$ is constant along each antidiagonal**. We can use an array `diags2` to enforce the antidiagonal constraint. -![Handling column and diagonal constraints](n_queens_problem.assets/n_queens_cols_diagonals.png) +![Enforcing column and diagonal constraints](n_queens_problem.assets/n_queens_cols_diagonals.png) -### Code Implementation +### Code implementation -Please note that in an $n \times n$ square matrix, the range of $row - col$ is $[-n + 1, n - 1]$, and the range of $row + col$ is $[0, 2n - 2]$. Therefore, the number of both main diagonals and anti-diagonals is $2n - 1$, meaning the length of both arrays `diags1` and `diags2` is $2n - 1$. +In a square matrix of order $n$, $row - col$ ranges over $[-n + 1, n - 1]$, and $row + col$ ranges over $[0, 2n - 2]$. There are therefore $2n - 1$ diagonals in each direction, so both `diags1` and `diags2` have length $2n - 1$. ```src [file]{n_queens}-[class]{}-[func]{n_queens} ``` -Placing $n$ queens row by row, considering the column constraint, from the first row to the last row there are $n$, $n-1$, $\dots$, $2$, $1$ choices, using $O(n!)$ time. When recording a solution, it is necessary to copy the matrix `state` and add it to `res`, and the copy operation uses $O(n^2)$ time. Therefore, **the overall time complexity is $O(n! \cdot n^2)$**. In practice, pruning based on diagonal constraints can also significantly reduce the search space, so the search efficiency is often better than the time complexity mentioned above. +We place a queen in each of $n$ rows. Considering the column constraint, the successive rows have $n$, $n-1$, $\dots$, $2$, and $1$ choices, giving $O(n!)$ time. Recording a solution requires copying the matrix `state` and appending the copy to `res`, which takes $O(n^2)$ time. **The overall time complexity is therefore $O(n! \cdot n^2)$**. In practice, diagonal pruning greatly reduces the search space, so the search is often faster than this bound suggests. -The array `state` uses $O(n^2)$ space, and the arrays `cols`, `diags1`, and `diags2` each use $O(n)$ space. The maximum recursion depth is $n$, using $O(n)$ stack frame space. Therefore, **the space complexity is $O(n^2)$**. +The array `state` uses $O(n^2)$ space, while `cols`, `diags1`, and `diags2` each use $O(n)$ space. The maximum recursion depth is $n$, requiring $O(n)$ space for stack frames. **The space complexity is therefore $O(n^2)$**. diff --git a/en/docs/chapter_backtracking/permutations_problem.md b/en/docs/chapter_backtracking/permutations_problem.md index e2ea59253..4f06cbdad 100644 --- a/en/docs/chapter_backtracking/permutations_problem.md +++ b/en/docs/chapter_backtracking/permutations_problem.md @@ -1,95 +1,95 @@ -# Permutations Problem +# Permutations problem -The permutations problem is a classic application of backtracking algorithms. It is defined as finding all possible arrangements of elements in a given collection (such as an array or string). +Generating permutations is a classic application of backtracking. Given a collection, such as an array or a string, the task is to find every possible ordering of its elements. -The table below shows several example datasets, including input arrays and their corresponding permutations. +The table below gives several input arrays and all their permutations. -

Table   Permutations Examples

+

Table   Examples of permutations

-| Input Array | All Permutations | +| Input array | All permutations | | :---------- | :----------------------------------------------------------------- | | $[1]$ | $[1]$ | | $[1, 2]$ | $[1, 2], [2, 1]$ | | $[1, 2, 3]$ | $[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]$ | -## Case with Distinct Elements +## Distinct elements !!! question Given an integer array with no duplicate elements, return all possible permutations. -From the perspective of backtracking algorithms, **we can imagine the process of generating permutations as the result of a series of choices**. Suppose the input array is $[1, 2, 3]$. If we first choose $1$, then choose $3$, and finally choose $2$, we obtain the permutation $[1, 3, 2]$. Backtracking means undoing a choice and then trying other choices. +From a backtracking perspective, **we can view a permutation as the result of a sequence of choices**. For input $[1, 2, 3]$, choosing $1$, then $3$, then $2$ produces $[1, 3, 2]$. Backtracking undoes a choice so that other choices can be tried. -From the perspective of backtracking code, the candidate set `choices` consists of all elements in the input array, and the state `state` is the elements that have been chosen so far. Note that each element can only be chosen once, **therefore all elements in `state` should be unique**. +In the code, `choices` contains all the input elements, and `state` contains the elements chosen so far. Each element may be chosen only once, **so every element in `state` must be unique**. -As shown in the figure below, we can unfold the search process into a recursion tree, where each node in the tree represents the current state `state`. Starting from the root node, after three rounds of choices, we reach a leaf node, and each leaf node corresponds to a permutation. +As shown in the figure below, we can expand the search into a recursion tree, where each node represents the current `state`. Starting from the root, three rounds of choices lead to a leaf, and each leaf corresponds to one permutation. -![Recursion tree of permutations](permutations_problem.assets/permutations_i.png) +![Recursion tree for permutations](permutations_problem.assets/permutations_i.png) -### Pruning Duplicate Choices +### Pruning repeated selections -To ensure that each element is chosen only once, we consider introducing a boolean array `selected`, where `selected[i]` indicates whether `choices[i]` has been chosen. We implement the following pruning operation based on it. +To ensure that each element is chosen only once, introduce a Boolean array `selected`, where `selected[i]` indicates whether `choices[i]` has been chosen. Use it for the following pruning steps. -- After making a choice `choices[i]`, we set `selected[i]` to $\text{True}$, indicating that it has been chosen. -- When traversing the candidate list `choices`, we skip all nodes that have been chosen, which is pruning. +- After choosing `choices[i]`, set `selected[i]` to $\text{True}$ to mark it as selected. +- When iterating over `choices`, skip all selected elements, pruning their branches. -As shown in the figure below, suppose we choose $1$ in the first round, $3$ in the second round, and $2$ in the third round. Then we need to prune the branch of element $1$ in the second round and prune the branches of elements $1$ and $3$ in the third round. +As shown in the figure below, if we choose 1 first, 3 second, and 2 third, we must prune the branch for 1 in the second round and the branches for 1 and 3 in the third round. -![Pruning example of permutations](permutations_problem.assets/permutations_i_pruning.png) +![Example of pruning permutations](permutations_problem.assets/permutations_i_pruning.png) -Observing the above figure, we find that this pruning operation reduces the search space size from $O(n^n)$ to $O(n!)$. +This pruning reduces the search space from $O(n^n)$ to $O(n!)$. -### Code Implementation +### Code implementation -After understanding the above information, we can fill in the blanks in the template code. To shorten the overall code, we do not implement each function in the template separately, but instead unfold them in the `backtrack()` function: +With these details established, we can fill in the backtracking template. To keep the code short, we inline the template's helper functions into `backtrack()`: ```src [file]{permutations_i}-[class]{}-[func]{permutations_i} ``` -## Case with Duplicate Elements +## Duplicate elements !!! question - Given an integer array that **may contain duplicate elements**, return all unique permutations. + Given an integer array that **may contain duplicate elements**, return all distinct permutations. -Suppose the input array is $[1, 1, 2]$. To distinguish the two duplicate elements $1$, we denote the second $1$ as $\hat{1}$. +Suppose the input is $[1, 1, 2]$. To distinguish the two occurrences of $1$, denote the second $1$ by $\hat{1}$. -As shown in the figure below, half of the permutations generated by the above method are duplicates. +As shown in the figure below, half the permutations generated by the previous method are duplicates. ![Duplicate permutations](permutations_problem.assets/permutations_ii.png) -So how do we remove duplicate permutations? The most direct approach is to use a hash set to directly deduplicate the permutation results. However, this is not elegant because **the search branches that generate duplicate permutations are unnecessary and should be identified and pruned early**, which can further improve algorithm efficiency. +How can we remove these duplicates? A straightforward approach is to use a hash set to deduplicate the results. However, this is inelegant: **branches that generate duplicate permutations are unnecessary and should be identified and pruned in advance**, improving efficiency. -### Pruning Equal Elements +### Pruning equal elements -Observe the figure below. In the first round, choosing $1$ or choosing $\hat{1}$ is equivalent. All permutations generated under these two choices are duplicates. Therefore, we should prune $\hat{1}$. +In the figure below, choosing $1$ or $\hat{1}$ in the first round is equivalent: every permutation produced under one choice is duplicated under the other. We should therefore prune $\hat{1}$. -Similarly, after choosing $2$ in the first round, the $1$ and $\hat{1}$ in the second round also produce duplicate branches, so the second round's $\hat{1}$ should also be pruned. +Similarly, after choosing $2$ in the first round, choosing either $1$ or $\hat{1}$ in the second produces duplicate branches. Prune the second-round choice of $\hat{1}$ as well. -Essentially, **our goal is to ensure that multiple equal elements are chosen only once in a certain round of choices**. +In essence, **within any one round of choices, we want to try only one of the elements with the same value**. ![Pruning duplicate permutations](permutations_problem.assets/permutations_ii_pruning.png) -### Code Implementation +### Code implementation -Building on the code from the previous problem, we initialize a hash set `duplicated` in each round of choices to record which elements have already been tried in that round, and prune equal elements: +Building on the previous solution, create a hash set `duplicated` in each round to record the values already tried in that round and prune repeated values: ```src [file]{permutations_ii}-[class]{}-[func]{permutations_ii} ``` -Assuming elements are pairwise distinct, there are $n!$ (factorial) permutations of $n$ elements. When recording results, we need to copy a list of length $n$, using $O(n)$ time. **Therefore, the time complexity is $O(n! \cdot n)$**. +If all elements are distinct, $n$ elements have $n!$ permutations, where the exclamation mark denotes factorial. Recording each result requires copying a list of length $n$, taking $O(n)$ time. **The time complexity is therefore $O(n!n)$**. -The maximum recursion depth is $n$, using $O(n)$ stack frame space. `selected` uses $O(n)$ space. At most $n$ `duplicated` sets exist simultaneously, using $O(n^2)$ space. **Therefore, the space complexity is $O(n^2)$**. +The maximum recursion depth is $n$, requiring $O(n)$ space for stack frames. The array `selected` uses $O(n)$ space. At most $n$ `duplicated` sets exist at once, using $O(n^2)$ space. **The space complexity is therefore $O(n^2)$**. -### Comparison of Two Pruning Methods +### Comparing the two pruning methods -Note that although both `selected` and `duplicated` are used for pruning, they have different objectives. +Although both `selected` and `duplicated` are used for pruning, they serve different purposes. -- **Pruning duplicate choices**: There is only one `selected` throughout the entire search process. It records which elements are included in the current state, and its purpose is to prevent an element from appearing repeatedly in `state`. -- **Pruning equal elements**: Each round of choices (each `backtrack` function call) contains a `duplicated` set. It records which elements have been chosen in this round's iteration (the `for` loop), and its purpose is to ensure that equal elements are chosen only once. +- **Pruning repeated selections**: There is only one `selected` array throughout the search. It records which elements are in the current state, preventing the same element from appearing twice in `state`. +- **Pruning equal elements**: Each round of choices (each call to `backtrack`) has its own `duplicated` set. It records the values already chosen in that round's `for` loop, ensuring that equal elements are tried only once in that round. -The figure below shows the effective scope of the two pruning conditions. Note that each node in the tree represents a choice, and the nodes on the path from the root to a leaf node form a permutation. +The figure below shows the scope of each pruning condition. Each node represents a choice, and the nodes along a path from root to leaf form a permutation. -![Effective scope of two pruning conditions](permutations_problem.assets/permutations_ii_pruning_summary.png) +![Scope of the two pruning conditions](permutations_problem.assets/permutations_ii_pruning_summary.png) diff --git a/en/docs/chapter_backtracking/subset_sum_problem.md b/en/docs/chapter_backtracking/subset_sum_problem.md index 1d3cc2ebf..85d2a5c3f 100644 --- a/en/docs/chapter_backtracking/subset_sum_problem.md +++ b/en/docs/chapter_backtracking/subset_sum_problem.md @@ -1,95 +1,95 @@ -# Subset-Sum Problem +# Subset sum problem -## Without Duplicate Elements +## Distinct elements !!! question - Given a positive integer array `nums` and a target positive integer `target`, find all possible combinations where the sum of elements in the combination equals `target`. The given array has no duplicate elements, and each element can be selected multiple times. Return these combinations in list form, where the list should not contain duplicate combinations. + Given an array of positive integers `nums` and a positive target integer `target`, find all combinations whose elements sum to `target`. The array has no duplicate elements, and each element may be chosen multiple times. Return the combinations as a list containing no duplicates. -For example, given the set $\{3, 4, 5\}$ and target integer $9$, the solutions are $\{3, 3, 3\}, \{4, 5\}$. Note the following two points: +For example, with input $\{3, 4, 5\}$ and target $9$, the solutions are $\{3, 3, 3\}, \{4, 5\}$. Keep two points in mind. -- Elements in the input set can be selected repeatedly without limit. -- Subsets do not distinguish element order; for example, $\{4, 5\}$ and $\{5, 4\}$ are the same subset. +- Each input element may be chosen an unlimited number of times. +- Element order does not matter: $\{4, 5\}$ and $\{5, 4\}$ represent the same subset. -### Using the Permutation Solution as a Reference +### Starting from the permutations solution -Similar to the permutation problem, we can view the process of generating subsets as the result of a series of choices and update the running sum during the selection process. When the sum equals `target`, we record the subset in the result list. +As with permutations, we can view generating a subset as a sequence of choices. Update the sum as we make choices, and record the subset when the sum equals `target`. -Unlike the permutation problem, **elements in this problem can be selected any number of times**, so we do not need to use a `selected` boolean list to track whether an element has already been selected. With a few small changes to the permutation code, we obtain an initial solution: +Unlike permutations, **each element may be chosen an unlimited number of times**, so we do not need a Boolean `selected` list. A small modification to the permutations code gives an initial solution: ```src [file]{subset_sum_i_naive}-[class]{}-[func]{subset_sum_i_naive} ``` -Running the above code on array $[3, 4, 5]$ with target value $9$ produces $[3, 3, 3], [4, 5], [5, 4]$. **Although we successfully found all subsets that sum to $9$, there are duplicate subsets $[4, 5]$ and $[5, 4]$**. +For input array $[3, 4, 5]$ and target $9$, this code returns $[3, 3, 3], [4, 5], [5, 4]$. **It finds all subsets summing to $9$, but includes the duplicate subsets $[4, 5]$ and $[5, 4]$**. -This is because the search process distinguishes the order of selections, but subsets do not distinguish selection order. As shown in the figure below, selecting 4 first and then 5 versus selecting 5 first and then 4 are different branches, but they correspond to the same subset. +The search distinguishes the order of choices, whereas subsets do not. As shown in the figure below, choosing $4$ then $5$ and choosing $5$ then $4$ follow different branches but produce the same subset. -![Subset search and boundary pruning](subset_sum_problem.assets/subset_sum_i_naive.png) +![Subset search and pruning when the sum exceeds the target](subset_sum_problem.assets/subset_sum_i_naive.png) -To eliminate duplicate subsets, **one straightforward idea is to deduplicate the result list**. However, this approach is very inefficient for two reasons: +**One straightforward way to remove duplicate subsets is to deduplicate the result list**. However, this is inefficient for two reasons. -- When there are many array elements, especially when `target` is large, the search process generates many duplicate subsets. -- Comparing subsets (arrays) is very time-consuming, requiring sorting the arrays first, then comparing each element in them. +- With many array elements, especially when `target` is large, the search generates many duplicate subsets. +- Comparing subsets (arrays) is expensive: we must sort them first, then compare their elements one by one. -### Pruning Duplicate Subsets +### Pruning duplicate subsets -**We consider deduplication through pruning during the search process**. Observing the figure below, duplicate subsets occur when array elements are selected in different orders, as in the following cases: +**Instead, we can eliminate duplicates by pruning during the search**. As shown in the figure below, duplicates arise when array elements are chosen in different orders, as in the following example. -1. When the first and second rounds select $3$ and $4$ respectively, all subsets containing these two elements are generated, denoted as $[3, 4, \dots]$. -2. Afterward, when the first round selects $4$, **the second round should skip $3$**, because the subset $[4, 3, \dots]$ generated by this choice is an exact duplicate of the subset generated in step `1.` +1. Choosing $3$ and then $4$ in the first two rounds generates all subsets beginning with these choices, denoted by $[3, 4, \dots]$. +2. Later, after choosing $4$ first, **we should skip $3$ in the second round**, because subsets $[4, 3, \dots]$ duplicate those generated in step `1.`. -In the search process, each level's choices are tried from left to right, so the rightmost branches are pruned more. +At each level of the search, choices are tried from left to right, so branches farther to the right have more choices pruned. -1. The first two rounds select $3$ and $5$, generating subset $[3, 5, \dots]$. -2. The first two rounds select $4$ and $5$, generating subset $[4, 5, \dots]$. -3. If the first round selects $5$, **the second round should skip $3$ and $4$**, because subsets $[5, 3, \dots]$ and $[5, 4, \dots]$ are exact duplicates of the subsets described in steps `1.` and `2.` +1. Choosing $3$ and then $5$ generates subsets $[3, 5, \dots]$. +2. Choosing $4$ and then $5$ generates subsets $[4, 5, \dots]$. +3. If $5$ is chosen first, **skip both $3$ and $4$ in the second round**, because $[5, 3, \dots]$ and $[5, 4, \dots]$ duplicate the subsets described in steps `1.` and `2.`. -![Different selection orders leading to duplicate subsets](subset_sum_problem.assets/subset_sum_i_pruning.png) +![Duplicate subsets caused by different choice orders](subset_sum_problem.assets/subset_sum_i_pruning.png) -In summary, given an input array $[x_1, x_2, \dots, x_n]$, let the selection sequence in the search process be $[x_{i_1}, x_{i_2}, \dots, x_{i_m}]$. This selection sequence must satisfy $i_1 \leq i_2 \leq \dots \leq i_m$; **any selection sequence that does not satisfy this condition will cause duplicates and should be pruned**. +In general, for input $[x_1, x_2, \dots, x_n]$, a sequence of choices $[x_{i_1}, x_{i_2}, \dots, x_{i_m}]$ must satisfy $i_1 \leq i_2 \leq \dots \leq i_m$. **Any sequence violating this condition creates a duplicate and should be pruned**. -### Code Implementation +### Code implementation -To implement this pruning, we initialize a variable `start` to indicate the starting point of traversal. **After making choice $x_{i}$, set the next round to start traversal from index $i$**. This ensures that the selection sequence satisfies $i_1 \leq i_2 \leq \dots \leq i_m$, guaranteeing subset uniqueness. +To implement this pruning, introduce `start` to specify where iteration begins. **After choosing $x_{i}$, begin the next round at index $i$**. This enforces $i_1 \leq i_2 \leq \dots \leq i_m$ and ensures that each subset is unique. -In addition, we have made the following two optimizations to the code: +We also make two further improvements. -- Before starting the search, first sort the array `nums`. When traversing all choices, **end the loop immediately when the subset sum exceeds `target`**, because subsequent elements are larger, and their subset sums must exceed `target`. -- Omit the element sum variable `total` and **use subtraction on `target` to track the sum of elements**. Record the solution when `target` equals $0$. +- Sort `nums` before starting the search. While iterating over choices, **stop the loop as soon as the subset sum exceeds `target`**, because all later elements are larger and would also exceed it. +- Remove the sum variable `total` and **track the sum by subtracting from `target`**. Record a solution when `target` reaches $0$. ```src [file]{subset_sum_i}-[class]{}-[func]{subset_sum_i} ``` -The figure below shows the complete backtracking process produced by running the above code on array $[3, 4, 5]$ with target value $9$. +The figure below shows the complete backtracking process for input $[3, 4, 5]$ and target $9$. -![Subset-sum I backtracking process](subset_sum_problem.assets/subset_sum_i.png) +![Backtracking for subset sum I](subset_sum_problem.assets/subset_sum_i.png) -## With Duplicate Elements in Array +## Duplicate elements !!! question - Given a positive integer array `nums` and a target positive integer `target`, find all possible combinations where the sum of elements in the combination equals `target`. **The given array may contain duplicate elements, and each element can be selected at most once**. Return these combinations in list form, where the list should not contain duplicate combinations. + Given an array of positive integers `nums` and a positive target integer `target`, find all combinations whose elements sum to `target`. **The array may contain duplicate elements, and each element may be chosen only once**. Return the combinations as a list containing no duplicates. -Compared to the previous problem, **the input array in this problem may contain duplicate elements**, which introduces a new issue. For example, given array $[4, \hat{4}, 5]$ and target value $9$, the output of the existing code is $[4, 5], [\hat{4}, 5]$, which contains duplicate subsets. +Unlike the previous problem, **the input may contain duplicate elements**, which introduces a new issue. For array $[4, \hat{4}, 5]$ and target $9$, for example, the existing code produces $[4, 5], [\hat{4}, 5]$: duplicate subsets. -**The reason for this duplication is that equal elements are selected multiple times in a certain round**. In the figure below, the first round has three choices, two of which are $4$, creating two duplicate search branches that output duplicate subsets. Similarly, the two $4$'s in the second round also produce duplicate subsets. +**These duplicates occur because equal elements are tried more than once in a single round**. In the figure below, the first round has three choices, two of which are $4$. They generate duplicate branches and therefore duplicate subsets. The two occurrences of $4$ in the second round cause the same problem. ![Duplicate subsets caused by equal elements](subset_sum_problem.assets/subset_sum_ii_repeat.png) -### Pruning Equal Elements +### Pruning equal elements -To solve this problem, **we need to limit equal elements to be selected only once in each round**. The implementation is quite clever: since the array is already sorted, equal elements are adjacent. This means that in a given round of selection, if the current element equals the element to its left, then the same value has already been chosen in this round, so we skip the current element directly. +To solve this, **try each value only once in each round**. The implementation uses the fact that equal elements are adjacent in a sorted array. Within a round, if an element equals the preceding element, that value has already been tried, so skip the current element. -At the same time, **this problem specifies that each array element can only be selected once**. Fortunately, we can also use the variable `start` to satisfy this constraint: after making choice $x_{i}$, set the next round to start traversal from index $i + 1$ onwards. This both eliminates duplicate subsets and avoids selecting elements multiple times. +In addition, **each array element may be chosen only once**. Fortunately, `start` can enforce this constraint too: after choosing $x_{i}$, begin the next round at index $i + 1$. This both eliminates duplicate subsets and prevents reusing an element. -### Code Implementation +### Code implementation ```src [file]{subset_sum_ii}-[class]{}-[func]{subset_sum_ii} ``` -The figure below shows the backtracking process for array $[4, 4, 5]$ with target value $9$, which includes four types of pruning operations. Combine the illustration with the code comments to understand the entire search process and how each pruning operation works. +The figure below shows backtracking for array $[4, 4, 5]$ and target $9$, including four types of pruning. Follow the illustration alongside the code comments to understand the full search and how each pruning rule works. -![Subset-sum II backtracking process](subset_sum_problem.assets/subset_sum_ii.png) +![Backtracking for subset sum II](subset_sum_problem.assets/subset_sum_ii.png) diff --git a/en/docs/chapter_backtracking/summary.md b/en/docs/chapter_backtracking/summary.md index 3b4a52701..1a96d9280 100644 --- a/en/docs/chapter_backtracking/summary.md +++ b/en/docs/chapter_backtracking/summary.md @@ -1,23 +1,23 @@ # Summary -### Key Review +### Key takeaways -- The backtracking algorithm is fundamentally an exhaustive search method. It finds solutions that meet specified conditions by performing a depth-first traversal of the solution space. During the search process, when a solution satisfying the conditions is found, it is recorded. The search ends either after finding all solutions or when the traversal is complete. -- The backtracking algorithm search process consists of two parts: attempting and backtracking. It tries various choices through depth-first search. When encountering situations that violate constraints, it reverts the previous choice, returns to the previous state, and continues exploring other options. Attempting and backtracking are operations in opposite directions. -- Backtracking problems typically contain multiple constraints, which can be utilized to implement pruning operations. Pruning can terminate unnecessary search branches early, significantly improving search efficiency. -- The backtracking algorithm is primarily used to solve search problems and constraint satisfaction problems. While combinatorial optimization problems can be solved with backtracking, there are often more efficient or better-performing solutions available. -- The permutation problem aims to find all possible permutations of elements in a given set. We use an array to record whether each element has been selected, thereby pruning search branches that attempt to select the same element repeatedly, ensuring each element is selected exactly once. -- In the permutation problem, if the set contains duplicate elements, the final result will contain duplicate permutations. We need to impose a constraint so that equal elements can only be selected once per round, which is typically achieved using a hash set. -- The subset-sum problem aims to find all subsets of a given set that sum to a target value. Since the set is unordered but the search process outputs results in all orders, duplicate subsets are generated. We sort the data before backtracking and use a variable to indicate the starting point of each round's traversal, thereby pruning search branches that generate duplicate subsets. -- For the subset-sum problem, equal elements in the array produce duplicate subsets. We leverage the precondition that the array is sorted by checking whether adjacent elements are equal to implement pruning, ensuring that equal elements can only be selected once per round. -- The $n$ queens problem aims to find placements of $n$ queens on an $n \times n$ chessboard such that no two queens can attack each other. The constraints of this problem include row constraints, column constraints, and main and anti-diagonal constraints. To satisfy row constraints, we adopt a row-by-row placement strategy, ensuring exactly one queen is placed in each row. -- The handling of column constraints and diagonal constraints is similar. For column constraints, we use an array to record whether each column has a queen, thereby indicating whether a selected cell is valid. For diagonal constraints, we use two arrays to separately record whether queens exist on each main or anti-diagonal. The challenge lies in finding the row-column index pattern that characterizes cells on the same main (anti-)diagonal. +- Backtracking is fundamentally exhaustive search. It traverses the solution space depth first to find solutions meeting the requirements, records them when found, and continues until all solutions are found or traversal is complete. +- Backtracking alternates between trying a choice and undoing it. It explores choices through depth-first search; when a constraint is violated, it undoes the last choice, restores the previous state, and tries another choice. Trying and undoing are opposite operations. +- Backtracking problems usually have multiple constraints that enable pruning. Pruning stops unnecessary branches early and can greatly improve search efficiency. +- Backtracking is mainly used for search and constraint satisfaction problems. It can also solve combinatorial optimization problems, though more efficient or more effective approaches often exist. +- The permutations problem asks for every possible ordering of a collection's elements. An array tracks whether each element has been selected, pruning branches that select the same element again and ensuring each is used only once. +- If the input contains equal elements, permutation generation produces duplicate results. We must try each value only once per round, usually by using a hash set. +- The subset sum problem asks for all subsets of a collection whose sum equals a target. Subsets disregard order, but the search generates all orderings, producing duplicates. Sorting the data before backtracking and using a variable to mark each round's starting index allows us to prune duplicate-generating branches. +- Equal elements also produce duplicate subsets. Because the array is sorted, we can prune by comparing adjacent elements, ensuring that each value is tried only once per round. +- The $n$-queens problem asks for arrangements of $n$ queens on an $n \times n$ chessboard such that no two queens can attack each other. It imposes row, column, and both diagonal constraints. Placing queens row by row ensures exactly one queen per row. +- Column and diagonal constraints are handled similarly. An array records whether each column contains a queen, indicating whether a chosen square is valid. Two more arrays track queens on diagonals in each direction. The main challenge is identifying the relationship between row and column indices for squares on the same diagonal. ### Q & A -**Q**: How can we understand the relationship between backtracking and recursion? +**Q**: How are backtracking and recursion related? -Overall, backtracking is an algorithmic strategy, while recursion is better viewed as a tool. +Broadly speaking, backtracking is an algorithmic strategy, while recursion is a tool. -- Backtracking is typically implemented with recursion. However, backtracking is only one application of recursion, specifically its use in search problems. -- The structure of recursion reflects a problem-solving paradigm based on decomposing a problem into subproblems, and it is commonly used in divide-and-conquer, backtracking, and dynamic programming (memoized recursion). +- Backtracking is usually implemented recursively. It is one application of recursion, specifically to search problems. +- Recursion expresses the idea of decomposing a problem into subproblems. It is commonly used in divide and conquer, backtracking, and dynamic programming (memoized recursion). diff --git a/en/docs/chapter_computational_complexity/exercises.md b/en/docs/chapter_computational_complexity/exercises.md index 19af4e5c3..b1571244a 100644 --- a/en/docs/chapter_computational_complexity/exercises.md +++ b/en/docs/chapter_computational_complexity/exercises.md @@ -1,11 +1,11 @@ # Exercises -## Concept Review +## Check your understanding -### Time and Space Complexity of Iteration and Recursion +### Time and space in iteration and recursion -The two functions below both calculate $1 + 2 + \dots + n$ (assume $n \ge 1$). Set `n` to 4, -answer the questions by following the program's actual execution order, and then compare the efficiency of the two approaches. +Both functions below compute $1 + 2 + \dots + n$ (assume $n \ge 1$). Set `n` to 4, +follow the program's execution order to answer the questions, and then compare the efficiency of the two approaches. ```src [file]{complexity_exercises}-[class]{}-[func]{sum_iter} @@ -13,75 +13,75 @@ answer the questions by following the program's actual execution order, and then -1. When the iterative function runs with `n = 4`, what is the value of the accumulator `res` after each loop iteration? -2. When the recursive function runs with `n = 4`, which values does the argument `n` take in order? As the calls return from the deepest level, how is the result obtained? -3. What are the time and space complexities of the two approaches? Explain your reasoning using the execution processes from Questions 1 and 2. +1. When the iterative function runs with `n = 4`, what is the value of the accumulator `res` at the end of each iteration? +2. When the recursive function runs with `n = 4`, what values does the parameter `n` take, in order? How is the result computed as the calls return, starting from the deepest call? +3. What are the time and space complexities of each approach? Explain using the execution steps from questions 1 and 2. -??? success "Answer" +??? success "Reference answer" - 1. The loop variable `i` takes the values `1, 2, 3, 4`. After each iteration, `res` becomes + 1. The loop variable `i` takes the values `1, 2, 3, 4` in order. At the end of each iteration, `res` becomes `1, 3, 6, 10`, respectively, so the iterative function returns 10. - 2. The argument `n` takes the values `4 → 3 → 2 → 1`. - The deepest call returns 1. The remaining calls then obtain `2 + 1 = 3`, `3 + 3 = 6`, and `4 + 6 = 10`, in that order. - At the deepest point, all four function calls are still unfinished. + 2. The parameter `n` follows the sequence `4 → 3 → 2 → 1`. + The deepest call returns 1. The remaining calls then compute `2 + 1 = 3`, `3 + 3 = 6`, and `4 + 6 = 10`, in order. + At the deepest point, all four function calls are still active. - 3. Both functions perform a number of loop iterations or calls proportional to $n$, so both have a time complexity of $O(n)$. - Their space complexities differ. The iterative version uses only a constant number of variables, so its space complexity is $O(1)$. - In the recursive version, earlier calls must wait for a result before returning, so the call stack holds up to $n$ calls at the same time. - Its space complexity is $O(n)$. + 3. Both functions perform a number of iterations or calls proportional to $n$, so both have time complexity $O(n)$. + Their space complexities differ: the iterative version uses only a constant number of variables, giving $O(1)$. + In the recursive version, earlier calls must wait for their results until the base case is reached. The call stack therefore holds up to $n$ calls at once, + giving space complexity $O(n)$. - When analyzing space complexity, remember to include the space used by recursive calls as well as the variables written in the code. + When analyzing space complexity, consider the space used by recursive calls as well as the variables in the code. -### Time Complexity of Three Code Fragments +### Time complexity of three code snippets -Each of the following code fragments takes a positive integer $n$ as input. Order them from lowest to highest time complexity, and give the complexity of each one. +Each of the following three code snippets takes a positive integer $n$ as input. Rank them from lowest to highest time complexity, and state each complexity. ```src [file]{complexity_exercises}-[class]{}-[func]{linear_loop} ``` -??? success "Answer" +??? success "Reference answer" - From lowest to highest, the order is Fragment 3 with $O(\log n)$, Fragment 1 with $O(n)$, and Fragment 2 with $O(n^2)$. - Fragment 3 halves $n$ in each iteration, so it runs about $\log_2 n$ times. - The loop in Fragment 1 runs exactly $n$ times. The inner loop in Fragment 2 runs - $n,n-1,\dots,1$ times, for a total of $n(n+1)/2$, so its time complexity is quadratic. + From lowest to highest: snippet 3, $O(\log n)$; snippet 1, $O(n)$; snippet 2, $O(n^2)$. + Snippet 3 halves $n$ at each iteration, so it runs approximately $\log_2 n$ times. + Snippet 1 runs its loop exactly $n$ times. In snippet 2, the inner loop runs + $n,n-1,\dots,1$ times in successive iterations, for a total of $n(n+1)/2$, giving quadratic time complexity. -### Which Reversal Uses Less Space? +### Which reversal method uses less space? -There are two ways to reverse all the elements in the array `nums`: +There are two ways to reverse all the elements in an array `nums`: -1. Create a new array `res` of the same length, copy the elements into it in reverse order, and return it. -2. Move two indices `i` and `j` inward from the beginning and end, swapping `nums[i]` and `nums[j]` at each step. +1. Create an array `res` of the same length, copy the elements into it in reverse order, and return it. +2. Move two indices, `i` and `j`, inward from the beginning and end, swapping `nums[i]` with `nums[j]` in pairs. - What is the space complexity of each approach? Which one is an "in-place" operation? + What is the space complexity of each approach? Which operation is in-place? -??? success "Answer" +??? success "Reference answer" - 1. This approach needs an auxiliary array with the same length as the input, so its space complexity is $O(n)$. + 1. An auxiliary array as long as the input is needed, giving space complexity $O(n)$. - 2. This approach uses only two index variables, - so its space complexity is $O(1)$. It is an in-place operation. + 2. Only two index variables are used, + giving space complexity $O(1)$. This is an in-place operation. - Note that an in-place reversal changes the input array, - so it should be preferred only when modifying the input is allowed. If the original array must be kept, the copying cost of the first approach is unavoidable. + Keep in mind that in-place reversal modifies the input array. + Prefer it only when modifying the input is allowed. If the original array must be preserved, the copying cost of approach 1 is unavoidable. -## Programming Exercises +## Programming exercises -### Fibonacci Number +### Fibonacci number -The Fibonacci sequence is defined by $F(0)=0$, $F(1)=1$, and, for $n\ge2$, +The Fibonacci sequence satisfies $F(0)=0$, $F(1)=1$, and, for $n\ge2$, $F(n)=F(n-1)+F(n-2)$. -Given a non-negative integer `n`, use a loop to calculate and return $F(n)$. Do not use recursion. +Given a nonnegative integer `n`, use a loop to compute and return $F(n)$ without recursion. ??? tip "Hints" - 1. Handle the cases where n is 0 or 1 separately - 2. Only the previous two terms are needed to calculate the next term; there is no need to store the entire sequence - 3. When updating the two variables, take care not to overwrite an old value before it is used + 1. Handle n equal to 0 or 1 separately first. + 2. Computing the next term requires only the previous two terms; there is no need to store the entire sequence. + 3. When updating the two variables, take care not to overwrite an old value before you have finished using it. [LeetCode](https://leetcode.com/problems/fibonacci-number/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_computational_complexity/index.md b/en/docs/chapter_computational_complexity/index.md index b68d5fd29..4fcb978ab 100644 --- a/en/docs/chapter_computational_complexity/index.md +++ b/en/docs/chapter_computational_complexity/index.md @@ -1,9 +1,9 @@ -# Complexity Analysis +# Complexity analysis ![Complexity analysis](../assets/covers/chapter_complexity_analysis.jpg) !!! abstract - Complexity analysis is like a space-time guide in the vast universe of algorithms. - - It leads us to explore deeply within the two dimensions of time and space, seeking more elegant solutions. + Complexity analysis is our guide to time and space in the vast universe of algorithms. + + It leads us to explore these two dimensions in depth as we search for more elegant solutions. diff --git a/en/docs/chapter_computational_complexity/iteration_and_recursion.md b/en/docs/chapter_computational_complexity/iteration_and_recursion.md index 305c32014..4a16cabf2 100644 --- a/en/docs/chapter_computational_complexity/iteration_and_recursion.md +++ b/en/docs/chapter_computational_complexity/iteration_and_recursion.md @@ -1,194 +1,194 @@ -# Iteration and Recursion +# Iteration and recursion -In algorithms, repeatedly executing a task is very common and closely related to complexity analysis. Therefore, before introducing time complexity and space complexity, let's first understand how to implement repeated task execution in programs, namely the two basic program control structures: iteration and recursion. +Algorithms often repeat tasks, and repetition is closely tied to complexity analysis. Before introducing time and space complexity, let's examine two basic ways to repeat work in a program: iteration and recursion. ## Iteration -Iteration is a control structure for repeatedly executing a task. In iteration, a program repeatedly executes a segment of code under certain conditions until those conditions are no longer satisfied. +Iteration is a control structure for repeating a task. A program repeatedly executes a block of code while a condition holds, stopping when it no longer does. -### For Loop +### For loops -The `for` loop is one of the most common forms of iteration, **suitable for use when the number of iterations is known in advance**. +A `for` loop is one of the most common forms of iteration, **especially when the number of iterations is known in advance**. -The following function implements the summation $1 + 2 + \dots + n$ using a `for` loop, with the result stored in the variable `res`. Note that in Python, `range(a, b)` corresponds to a "left-closed, right-open" interval, with the traversal range being $a, a + 1, \dots, b-1$: +The following function uses a `for` loop to compute $1 + 2 + \dots + n$, storing the result in `res`. In Python, `range(a, b)` includes the left endpoint but excludes the right, so it visits $a, a + 1, \dots, b-1$: ```src [file]{iteration}-[class]{}-[func]{for_loop} ``` -The figure below shows the flowchart of this summation function. +The figure below is a flowchart of this summation function. ![Flowchart of the summation function](iteration_and_recursion.assets/iteration.png) -The number of operations in this summation function is proportional to the input data size $n$, or has a "linear relationship". In fact, **time complexity describes precisely this "linear relationship"**. Related content will be introduced in detail in the next section. +Its operation count is proportional to the input size $n$: the relationship is linear. **Time complexity describes this relationship**, as the next section will explain. -### While Loop +### While loops -Similar to the `for` loop, the `while` loop is also a method for implementing iteration. In a `while` loop, the program first checks the condition in each round; if the condition is true, it continues execution, otherwise it ends the loop. +Like `for`, a `while` loop implements iteration. Before each iteration, it checks a condition: if true, execution continues; otherwise, the loop ends. -Below we use a `while` loop to implement the summation $1 + 2 + \dots + n$: +Here is a `while` loop computing $1 + 2 + \dots + n$: ```src [file]{iteration}-[class]{}-[func]{while_loop} ``` -**The `while` loop has greater flexibility than the `for` loop**. In a `while` loop, we can freely design the initialization and update steps of the condition variable. +**A `while` loop offers more flexibility than a `for` loop** in how its condition variables are initialized and updated. -For example, in the following code, the condition variable $i$ is updated twice per round, which is not convenient to implement using a `for` loop: +For example, the following code updates the condition variable $i$ twice per iteration, which is less convenient to express with a `for` loop: ```src [file]{iteration}-[class]{}-[func]{while_loop_ii} ``` -Overall, **`for` loops have more compact code, while `while` loops are more flexible**; both can implement iterative structures. The choice of which to use should be determined based on the requirements of the specific problem. +In general, **`for` loops are more compact, while `while` loops are more flexible**. Both implement iteration; choose according to the problem's needs. -### Nested Loops +### Nested loops -We can nest one loop structure inside another. Below is an example using `for` loops: +We can place one loop inside another, as in the following `for`-loop example: ```src [file]{iteration}-[class]{}-[func]{nested_for_loop} ``` -The figure below shows the flowchart of this nested loop. +The figure below shows the nested loop's flowchart. ![Flowchart of nested loops](iteration_and_recursion.assets/nested_iteration.png) -In this case, the number of operations of the function is proportional to $n^2$, or the algorithm's running time has a "quadratic relationship" with the input data size $n$. +Here, the operation count is proportional to $n^2$: runtime has a quadratic relationship with input size $n$. -We can continue adding nested loops, where each additional level of nesting can be viewed as an increase in dimensionality, raising the time complexity to a "cubic relationship", a "quartic relationship", and so on. +Adding further nesting increases the dimension each time, producing cubic, fourth-power, and higher-order time complexity. ## Recursion -Recursion is an algorithmic strategy that solves problems by having a function call itself. It mainly consists of two phases. + Recursion is an algorithmic strategy in which a function calls itself to solve a problem. It has two main phases. -1. **Descend**: The program continuously calls itself deeper, usually passing in smaller or more simplified parameters, until reaching a "termination condition". -2. **Ascend**: After triggering the "termination condition", the program returns layer by layer from the deepest recursive function, aggregating the result of each layer. +1. **Descent**: The function calls itself more deeply, usually with smaller or simpler arguments, until a base case is reached. +2. **Return**: Once the base case is reached, calls return from the deepest level upward, combining results along the way. -From an implementation perspective, recursive code mainly consists of three elements. +In implementation, recursive code has three main elements. -1. **Termination condition**: Used to determine when to switch from "descending" to "ascending". -2. **Recursive call**: Corresponds to "descending", where the function calls itself, usually with smaller or more simplified parameters. -3. **Return result**: Corresponds to "ascending", returning the result of the current recursion level to the previous layer. +1. **Base case**: Determines when descent stops and returning begins. +2. **Recursive call**: The function calls itself, usually with smaller or simpler arguments. +3. **Returned result**: The current level returns its result to the previous level. -Observe the following code. We only need to call the function `recur(n)` to complete the calculation of $1 + 2 + \dots + n$: +In the following code, calling `recur(n)` computes $1 + 2 + \dots + n$: ```src [file]{recursion}-[class]{}-[func]{recur} ``` -The figure below shows the recursive process of this function. +The figure below illustrates the recursive process. ![Recursive process of the summation function](iteration_and_recursion.assets/recursion_sum.png) -Although from a computational perspective, iteration and recursion can achieve the same results, **they represent two completely different paradigms for thinking about and solving problems**. +Iteration and recursion can produce the same answer, **but represent two different ways of thinking about and solving a problem**. -- **Iteration**: Solves problems "bottom-up". Starting from the most basic steps, these steps are then repeatedly executed or accumulated until the task is complete. -- **Recursion**: Solves problems "top-down". The original problem is decomposed into smaller subproblems that have the same form as the original problem. These subproblems continue to be decomposed into even smaller subproblems until reaching the base case (where the solution is known). +- **Iteration** works bottom up, starting with basic steps and repeatedly performing or accumulating them until the task is complete. +- **Recursion** works top down, dividing the original problem into smaller subproblems of the same form, then repeating the division until reaching base cases with known solutions. -Taking the above summation function as an example, let the problem be $f(n) = 1 + 2 + \dots + n$. +For the summation example, let $f(n) = 1 + 2 + \dots + n$. -- **Iteration**: Simulates the summation process in a loop, traversing from $1$ to $n$, performing the summation operation in each round to obtain $f(n)$. -- **Recursion**: Decomposes the problem into the subproblem $f(n) = n + f(n-1)$, continuously decomposing (recursively) until terminating at the base case $f(1) = 1$. +- **Iteration**: Loop from $1$ to $n$, accumulating the sum at each step to obtain $f(n)$. +- **Recursion**: Decompose the problem as $f(n) = n + f(n-1)$ and keep doing so recursively until the base case $f(1) = 1$. -### Call Stack +### The call stack -Each time a recursive function calls itself, the system allocates memory for the newly invoked function to store local variables, call addresses, and other information. This leads to two consequences. +Each time a recursive function calls itself, the system allocates memory for the new call's local variables, return address, and other information. This has two consequences. -- The function's context data is stored in a memory area called "stack frame space", which is not released until the function returns. Therefore, **recursion usually consumes more memory space than iteration**. -- Recursive function calls incur additional overhead. **Therefore, recursion is usually less time-efficient than loops**. +- The call's context is stored in a stack frame until it returns. **Recursion therefore usually uses more memory than iteration**. +- Each recursive call adds overhead. **Recursion is therefore usually slower than a loop**. -As shown in the figure below, before the termination condition is triggered, there are $n$ unreturned recursive functions existing simultaneously, with a **recursion depth of $n$**. +As shown in the figure below, before the base case is reached, $n$ recursive calls remain active at once: **the recursion depth is $n$**. -![Recursion call depth](iteration_and_recursion.assets/recursion_sum_depth.png) +![Depth of recursive calls](iteration_and_recursion.assets/recursion_sum_depth.png) -In practice, the recursion depth allowed by programming languages is usually limited, and excessively deep recursion may lead to stack overflow errors. +Programming languages usually limit recursion depth, and excessively deep recursion may cause a stack overflow. -### Tail Recursion +### Tail recursion -Interestingly, **if a function makes the recursive call as the very last step before returning**, the compiler or interpreter may optimize it so that its space efficiency is comparable to iteration. This case is called tail recursion. +Interestingly, **if the recursive call is the last operation before a function returns**, a compiler or interpreter can optimize it to use as little space as iteration. This is called tail recursion. -- **Regular recursion**: When a function returns to the previous level, it needs to continue executing code, so the system needs to save the context of the previous layer's call. -- **Tail recursion**: The recursive call is the last operation before the function returns, meaning that after returning to the previous level, there is no need to continue executing other operations, so the system does not need to save the context of the previous layer's function. +- **Ordinary recursion**: After a call returns, its caller must continue executing code, so the caller's context must be preserved. +- **Tail recursion**: Nothing remains to be done after the recursive call returns, so the caller's context need not be retained. -Taking the calculation of $1 + 2 + \dots + n$ as an example, we can set the result variable `res` as a function parameter to implement tail recursion: +For $1 + 2 + \dots + n$, passing the accumulated result `res` as a parameter gives a tail-recursive solution: ```src [file]{recursion}-[class]{}-[func]{tail_recur} ``` -The execution process of tail recursion is shown in the figure below. Comparing regular recursion and tail recursion, the summation operation is performed at different points. +The figure below illustrates tail recursion. Ordinary and tail recursion perform their additions at different points. -- **Regular recursion**: The summation operation is performed during the "ascending" process, requiring an additional summation operation after each layer returns. -- **Tail recursion**: The summation operation is performed during the "descending" process; the "ascending" process only needs to return layer by layer. +- **Ordinary recursion**: Addition occurs during the return phase, after each deeper call returns. +- **Tail recursion**: Addition occurs during descent; the return phase simply returns through the levels. -![Tail recursion process](iteration_and_recursion.assets/tail_recursion_sum.png) +![Tail-recursive summation](iteration_and_recursion.assets/tail_recursion_sum.png) !!! tip - Please note that many compilers or interpreters do not support tail recursion optimization. For example, Python does not support tail recursion optimization by default, so even if a function is in tail recursive form, it may still encounter stack overflow issues. + Many compilers and interpreters do not support tail-recursion optimization. Python, for example, does not optimize it by default, so even a tail-recursive function can run into a stack overflow. -### Recursion Tree +### Recursion trees -When dealing with algorithmic problems related to "divide and conquer", recursion often provides a more intuitive approach and more readable code than iteration. Taking the "Fibonacci sequence" as an example. +For divide-and-conquer problems, recursion often provides a more intuitive approach and more readable code than iteration. Consider the Fibonacci sequence. !!! question - Given a Fibonacci sequence $0, 1, 1, 2, 3, 5, 8, 13, \dots$, find the $n$-th number in the sequence. + Given the Fibonacci sequence $0, 1, 1, 2, 3, 5, 8, 13, \dots$, find its $n$-th number. -Let the $n$-th number of the Fibonacci sequence be $f(n)$. Two conclusions can be easily obtained. +Let $f(n)$ be its $n$-th number. Two facts follow. -- The first two numbers of the sequence are $f(1) = 0$ and $f(2) = 1$. -- Each number in the sequence is the sum of the previous two numbers, i.e., $f(n) = f(n - 1) + f(n - 2)$. +- The first two numbers are $f(1) = 0$ and $f(2) = 1$. +- Every later number is the sum of the two preceding numbers: $f(n) = f(n - 1) + f(n - 2)$. -Following the recurrence relation to make recursive calls, with the first two numbers as termination conditions, we can write the recursive code. Calling `fib(n)` will give us the $n$-th number of the Fibonacci sequence: +Use the recurrence for recursive calls and the first two numbers as base cases. Calling `fib(n)` then returns the $n$-th Fibonacci number: ```src [file]{recursion}-[class]{}-[func]{fib} ``` -Observing the above code, we make two recursive calls within the function, **meaning that one call produces two call branches**. As shown in the figure below, this repeated recursive calling eventually produces a recursion tree with $n$ levels. +The function makes two recursive calls, **so each call creates two branches**. Continuing this process produces a recursion tree with $n$ levels, as shown in the figure below. -![Recursion tree of the Fibonacci sequence](iteration_and_recursion.assets/recursion_tree.png) +![Recursion tree for the Fibonacci sequence](iteration_and_recursion.assets/recursion_tree.png) -Fundamentally, recursion embodies the paradigm of "decomposing a problem into smaller subproblems", and this divide-and-conquer strategy is crucial. +Recursion embodies the idea of breaking a problem into smaller subproblems, a crucial divide-and-conquer strategy. -- From an algorithmic perspective, many important algorithmic strategies such as searching, sorting, backtracking, divide and conquer, and dynamic programming directly or indirectly apply this way of thinking. -- From a data structure perspective, recursion is naturally suited for handling problems related to linked lists, trees, and graphs, because they are well-suited for analysis using divide-and-conquer thinking. +- In algorithms, searching, sorting, backtracking, divide and conquer, dynamic programming, and many other techniques use this idea directly or indirectly. +- In data structures, recursion naturally handles linked lists, trees, and graphs because divide-and-conquer reasoning suits them well. -## Comparison of the Two +## Comparing iteration and recursion -Summarizing the above content, as shown in the table below, iteration and recursion differ in implementation, performance, and applicability. +The table below summarizes their differences in implementation, performance, and suitability. -

Table   Comparison of iteration and recursion characteristics

+

Table   Comparing iteration and recursion

-| | Iteration | Recursion | -| -------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| Implementation | Loop structure | Function calls itself | -| Time efficiency | Generally more efficient, no function call overhead | Each function call incurs overhead | -| Memory usage | Usually uses a fixed amount of memory space | Accumulated function calls may use a large amount of stack frame space | -| Suitable problems | Suitable for simple loop tasks, with intuitive and readable code | Suitable for subproblem decomposition, such as trees, graphs, divide and conquer, backtracking, etc., with concise and clear code structure | +| | Iteration | Recursion | +| -------- | -------------------------------------- | ------------------------------------------------------------ | +| Implementation | Loops | A function calls itself | +| Time efficiency | Usually high, with no function-call overhead | Each call adds overhead | +| Memory use | Usually a fixed amount of memory | Accumulated calls may require many stack frames | +| Suitable problems | Simple repetitive tasks, with straightforward, readable code | Subproblem decomposition, including trees, graphs, divide and conquer, and backtracking; concise, clear structure | !!! tip - If you find the following content difficult to understand, you can review it after reading the "Stack" chapter. + If the following discussion feels difficult, return to it after reading the section on stacks. -What is the intrinsic relationship between iteration and recursion? Taking the above recursive function as an example, the summation operation is performed during the "ascending" phase of recursion. This means that the function called first actually completes its summation operation last, **and this working mechanism is similar to the "last-in, first-out" principle of stacks**. +How are iteration and recursion connected? In the summation example, additions occur during the return phase. The first function called is the last to finish its addition, **matching a stack's last-in, first-out behavior**. -In fact, recursive terminology such as "call stack" and "stack frame space" already hints at the close relationship between recursion and stacks. +The terms call stack and stack frame already suggest this close relationship. -1. **Descend**: When a function is called, the system allocates a new stack frame on the "call stack" for that function to store the function's local variables, parameters, return address, and other data. -2. **Ascend**: When the function completes execution and returns, the corresponding stack frame is removed from the "call stack", restoring the execution environment of the previous function. +1. **Descent**: A function call adds a new stack frame to the call stack, holding local variables, parameters, the return address, and other data. +2. **Return**: When the function finishes, its frame is removed, restoring the caller's execution context. -Therefore, **we can use an explicit stack to simulate the behavior of the call stack**, thus transforming recursion into iterative form: +Thus, **an explicit stack can simulate the call stack**, converting recursion into iteration: ```src [file]{recursion}-[class]{}-[func]{for_loop_recur} ``` -Observing the above code, when recursion is transformed into iteration, the code becomes more complex. Although iteration and recursion can be converted into each other in many cases, it may not be worthwhile to do so for the following two reasons. +The resulting iterative code is more complicated. Although iteration and recursion can often be converted into each other, doing so is not always worthwhile. -- The transformed code may be more difficult to understand and less readable. -- For some complex problems, simulating the behavior of the system call stack can be very difficult. +- The converted code may be harder to understand and less readable. +- Simulating the call stack can be very difficult for complex problems. -In summary, **choosing between iteration and recursion depends on the nature of the specific problem**. In programming practice, it is crucial to weigh the pros and cons of both and choose the appropriate method based on the context. +**The choice between iteration and recursion depends on the problem**. In practice, weigh their strengths and weaknesses and choose the approach that fits the situation. diff --git a/en/docs/chapter_computational_complexity/performance_evaluation.md b/en/docs/chapter_computational_complexity/performance_evaluation.md index 79583afda..567fcd9a9 100644 --- a/en/docs/chapter_computational_complexity/performance_evaluation.md +++ b/en/docs/chapter_computational_complexity/performance_evaluation.md @@ -1,49 +1,49 @@ -# Algorithm Efficiency Evaluation +# Evaluating algorithm efficiency -In algorithm design, we pursue the following two levels of objectives sequentially. +Algorithm design pursues two goals in sequence. -1. **Finding a solution to the problem**: The algorithm must reliably obtain the correct solution within the specified input range. -2. **Seeking the optimal solution**: Multiple solutions may exist for the same problem, and we hope to find an algorithm that is as efficient as possible. +1. **Find a solution**: The algorithm must reliably produce correct results across the specified input range. +2. **Find the best solution**: When several approaches exist, we want the most efficient algorithm possible. -In other words, under the premise of being able to solve the problem, algorithm efficiency has become the primary evaluation criterion for measuring the quality of algorithms. It includes the following two dimensions. +Once correctness is established, efficiency becomes a primary measure of an algorithm's quality. It has two dimensions. -- **Time efficiency**: The length of time the algorithm runs. -- **Space efficiency**: The size of memory space the algorithm occupies. +- **Time efficiency**: How long the algorithm takes to run. +- **Space efficiency**: How much memory it uses. -In short, **our goal is to design data structures and algorithms that are "both fast and memory-efficient"**. Effectively evaluating algorithm efficiency is crucial, because only in this way can we compare various algorithms and guide the algorithm design and optimization process. +**Our goal is to design data structures and algorithms that are both fast and economical with memory**. Evaluating efficiency effectively lets us compare algorithms and guide their design and optimization. -Efficiency evaluation methods are mainly divided into two types: actual testing and theoretical estimation. +There are two main evaluation methods: practical testing and theoretical estimation. -## Actual Testing +## Practical testing -Suppose we now have algorithm `A` and algorithm `B`, both of which can solve the same problem, and we need to compare their efficiency. The most direct method is to run them on a computer and measure their running time and memory usage. This evaluation approach can reflect real-world behavior, but it also has considerable limitations. +Suppose algorithms `A` and `B` solve the same problem. The most direct comparison is to run both on a computer and measure runtime and memory use. This reflects actual behavior, but has substantial limitations. -On one hand, **it is difficult to eliminate interference factors from the testing environment**. Hardware configuration affects algorithmic performance. For example, if an algorithm has a high degree of parallelism, it is more suitable for running on multi-core CPUs; if an algorithm performs memory-intensive operations, it will benefit more from high-performance memory. In other words, the test results of an algorithm on different machines may be inconsistent. This means we need to test on various machines and calculate average efficiency, which is impractical. +First, **it is difficult to remove the influence of the test environment**. Hardware affects performance: a highly parallel algorithm benefits from a multicore CPU, while a memory-intensive algorithm benefits from faster RAM. Results may differ across machines, so a fair average would require testing on many kinds of hardware, which is impractical. -On the other hand, **conducting complete testing is very resource-intensive**. As the input data volume changes, the algorithm will exhibit different efficiencies. For example, when the input data volume is small, the running time of algorithm `A` is shorter than algorithm `B`; but when the input data volume is large, the test results may be exactly the opposite. Therefore, to obtain convincing conclusions, we need to test input data of various scales, which requires a large amount of computational resources. +Second, **comprehensive testing consumes substantial resources**. Efficiency changes with input size. Algorithm `A` may be faster than `B` for small inputs, while the reverse holds for large inputs. A convincing conclusion requires testing many input sizes, consuming considerable computing resources. -## Theoretical Estimation +## Theoretical estimation -Since actual testing has considerable limitations, we can consider evaluating algorithm efficiency through theoretical calculation. This estimation method is called asymptotic complexity analysis, or complexity analysis for short. +Given these limitations, we can estimate efficiency through calculation alone. This is called asymptotic complexity analysis, or simply complexity analysis. -Complexity analysis can reflect the relationship between the time and space resources required for algorithm execution and the input data scale. **It describes the growth trend of the time and space required for algorithm execution as the input data scale increases**. This definition is a bit cumbersome, so we can break it down into three key points to understand. +Complexity analysis relates an algorithm's time and space requirements to its input size. **It describes how runtime and memory use grow as the input gets larger**. We can unpack this definition into three points. -- "Time and space resources" correspond to time complexity and space complexity, respectively. -- "As the input data scale increases" means that complexity reflects the relationship between algorithm running efficiency and input data scale. -- "Growth trend of time and space" indicates that complexity analysis focuses not on the specific values of running time or occupied space, but on how "fast" time or space grows. +- Time and space resources correspond to time complexity and space complexity. +- Growth in input size means complexity relates efficiency to the amount of input data. +- Growth trends mean we study how quickly time or space increases, rather than its exact value. -**Complexity analysis overcomes the drawbacks of the actual testing method**, reflected in the following aspects. +**Complexity analysis overcomes limitations of practical testing** in several ways. -- It does not need to actually run the code, making it more environmentally friendly and energy-efficient. -- It is independent of the testing environment, and the analysis results are applicable to all running platforms. -- It can reflect algorithm efficiency at different data volumes, especially algorithm performance at large data volumes. +- It does not require running code, saving energy and computing resources. +- It is independent of the test environment, so its conclusions apply across platforms. +- It reveals efficiency at different input sizes, especially for large inputs. !!! tip - If you are still confused about the concept of complexity, don't worry—we will introduce it in detail in subsequent chapters. + If complexity still feels unclear, do not worry. The following sections will explain it in detail. -Complexity analysis provides us with a "ruler" for evaluating algorithm efficiency, allowing us to measure the time and space resources required to execute a certain algorithm and compare the efficiency between different algorithms. +Complexity analysis gives us a yardstick for evaluating the time and space an algorithm needs and comparing different algorithms. -Complexity is a mathematical concept that may feel abstract and challenging for beginners. From this perspective, complexity analysis may not be the most suitable topic to introduce first. However, when we discuss the characteristics of a certain data structure or algorithm, it is difficult to avoid analyzing its running speed and space usage. +As a mathematical concept, complexity can be abstract and challenging for beginners. This makes it a difficult topic to introduce first. Yet discussing a data structure or algorithm's properties inevitably involves runtime and memory use. -In summary, it is recommended that before diving deep into data structures and algorithms, **you first establish a preliminary understanding of complexity analysis so that you can analyze the complexity of simple algorithms**. +Before studying data structures and algorithms in depth, **build an initial understanding of complexity analysis so that you can analyze simple algorithms**. diff --git a/en/docs/chapter_computational_complexity/space_complexity.md b/en/docs/chapter_computational_complexity/space_complexity.md index b84296576..78138ad6b 100644 --- a/en/docs/chapter_computational_complexity/space_complexity.md +++ b/en/docs/chapter_computational_complexity/space_complexity.md @@ -1,28 +1,28 @@ -# Space Complexity +# Space complexity -Space complexity measures the growth trend of memory space occupied by an algorithm as the data size increases. This concept is very similar to time complexity, except that "running time" is replaced with "occupied memory space". +Space complexity measures how an algorithm's memory use grows as the input gets larger. It is much like time complexity, with memory usage replacing runtime. -## Algorithm-Related Space +## Space used by an algorithm -The memory space used by an algorithm during execution mainly includes the following types. +The memory an algorithm uses during execution falls into these main categories. -- **Input space**: Used to store the input data of the algorithm. -- **Temporary space**: Used to store variables, objects, function contexts, and other data during the algorithm's execution. -- **Output space**: Used to store the output data of the algorithm. +- **Input space**: Stores the input data. +- **Temporary space**: Stores variables, objects, function contexts, and other working data. +- **Output space**: Stores the output data. -In general, the scope of space complexity statistics is "temporary space" plus "output space". +Space complexity generally counts temporary space plus output space. -Temporary space can be further divided into three parts. +Temporary space can be divided further into three parts. -- **Temporary data**: Used to save various constants, variables, objects, etc., during the algorithm's execution. -- **Stack frame space**: Used to save the context data of called functions. The system creates a stack frame at the top of the stack each time a function is called, and the stack frame space is released after the function returns. -- **Instruction space**: Used to save compiled program instructions, which are usually ignored in actual statistics. +- **Temporary data**: Constants, variables, objects, and other data used during execution. +- **Stack frames**: Context data for function calls. Each call creates a frame at the top of the stack, which is released when the function returns. +- **Instruction space**: Compiled program instructions, usually ignored in the analysis. -When analyzing the space complexity of a program, **we usually consider three parts: temporary data, stack frame space, and output data**, as shown in the following figure. +**We usually count temporary data, stack frames, and output data** when analyzing space complexity, as shown in the figure below. -![Algorithm-related space](space_complexity.assets/space_types.png) +![Space used by an algorithm](space_complexity.assets/space_types.png) -The related code is as follows: +The corresponding code is: === "Python" @@ -39,10 +39,10 @@ The related code is as follows: return 0 def algorithm(n) -> int: # Input data - A = 0 # Temporary data (constant, usually represented by uppercase letters) + A = 0 # Temporary data (constant, conventionally named in uppercase) b = 0 # Temporary data (variable) node = Node(0) # Temporary data (object) - c = function() # Stack frame space (function call) + c = function() # Stack frame (function call) return A + b + c # Output data ``` @@ -66,7 +66,7 @@ The related code is as follows: const int a = 0; // Temporary data (constant) int b = 0; // Temporary data (variable) Node* node = new Node(0); // Temporary data (object) - int c = func(); // Stack frame space (function call) + int c = func(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -80,18 +80,18 @@ The related code is as follows: Node next; Node(int x) { val = x; } } - + /* Function */ int function() { // Perform some operations... return 0; } - + int algorithm(int n) { // Input data final int a = 0; // Temporary data (constant) int b = 0; // Temporary data (variable) Node node = new Node(0); // Temporary data (object) - int c = function(); // Stack frame space (function call) + int c = function(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -115,7 +115,7 @@ The related code is as follows: const int a = 0; // Temporary data (constant) int b = 0; // Temporary data (variable) Node node = new(0); // Temporary data (object) - int c = Function(); // Stack frame space (function call) + int c = Function(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -129,11 +129,11 @@ The related code is as follows: next *node } - /* Create node structure */ + /* Create a node structure */ func newNode(val int) *node { return &node{val: val} } - + /* Function */ func function() int { // Perform some operations... @@ -144,7 +144,7 @@ The related code is as follows: const a = 0 // Temporary data (constant) b := 0 // Temporary data (variable) newNode(0) // Temporary data (object) - c := function() // Stack frame space (function call) + c := function() // Stack frame (function call) return a + b + c // Output data } ``` @@ -172,7 +172,7 @@ The related code is as follows: let a = 0 // Temporary data (constant) var b = 0 // Temporary data (variable) let node = Node(x: 0) // Temporary data (object) - let c = function() // Stack frame space (function call) + let c = function() // Stack frame (function call) return a + b + c // Output data } ``` @@ -200,7 +200,7 @@ The related code is as follows: const a = 0; // Temporary data (constant) let b = 0; // Temporary data (variable) const node = new Node(0); // Temporary data (object) - const c = constFunc(); // Stack frame space (function call) + const c = constFunc(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -228,7 +228,7 @@ The related code is as follows: const a = 0; // Temporary data (constant) let b = 0; // Temporary data (variable) const node = new Node(0); // Temporary data (object) - const c = constFunc(); // Stack frame space (function call) + const c = constFunc(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -253,7 +253,7 @@ The related code is as follows: const int a = 0; // Temporary data (constant) int b = 0; // Temporary data (variable) Node node = Node(0); // Temporary data (object) - int c = function(); // Stack frame space (function call) + int c = function(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -263,14 +263,14 @@ The related code is as follows: ```rust title="" use std::rc::Rc; use std::cell::RefCell; - + /* Structure */ struct Node { val: i32, next: Option>>, } - /* Create Node structure */ + /* Create a Node structure */ impl Node { fn new(val: i32) -> Self { Self { val: val, next: None } @@ -278,7 +278,7 @@ The related code is as follows: } /* Function */ - fn function() -> i32 { + fn function() -> i32 { // Perform some operations... return 0; } @@ -287,7 +287,7 @@ The related code is as follows: const a: i32 = 0; // Temporary data (constant) let mut b = 0; // Temporary data (variable) let node = Node::new(0); // Temporary data (object) - let c = function(); // Stack frame space (function call) + let c = function(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -304,7 +304,7 @@ The related code is as follows: int algorithm(int n) { // Input data const int a = 0; // Temporary data (constant) int b = 0; // Temporary data (variable) - int c = func(); // Stack frame space (function call) + int c = func(); // Stack frame (function call) return a + b + c; // Output data } ``` @@ -327,7 +327,7 @@ The related code is as follows: val a = 0 // Temporary data (constant) var b = 0 // Temporary data (variable) val node = Node(0) // Temporary data (object) - val c = function() // Stack frame space (function call) + val c = function() // Stack frame (function call) return a + b + c // Output data } ``` @@ -356,21 +356,21 @@ The related code is as follows: a = 0 # Temporary data (constant) b = 0 # Temporary data (variable) node = Node.new(0) # Temporary data (object) - c = function # Stack frame space (function call) + c = function # Stack frame (function call) a + b + c # Output data end ``` -## Calculation Method +## Deriving space complexity -The calculation method for space complexity is roughly the same as for time complexity, except that what we measure changes from the "number of operations" to the "amount of space used". +The approach is much like time complexity analysis, except that we count memory used rather than operations performed. -Unlike time complexity, **we usually only focus on the worst-case space complexity**. This is because memory space is a hard requirement, and we must ensure that sufficient memory space is reserved for all input data. +Unlike time complexity, **we usually focus only on worst-case space complexity**. Memory is a hard requirement: enough must be available for every valid input. -Observe the following code. Here, "worst case" in worst-case space complexity has two meanings. +In the following code, "worst case" has two meanings. -1. **Based on the worst input data**: When $n < 10$, the space complexity is $O(1)$; but when $n > 10$, the initialized array `nums` occupies $O(n)$ space, so the worst-case space complexity is $O(n)$. -2. **Based on the peak memory during algorithm execution**: For example, before executing the last line, the program occupies $O(1)$ space; when initializing the array `nums`, the program occupies $O(n)$ space, so the worst-case space complexity is $O(n)$. +1. **The worst input**: When $n < 10$, space complexity is $O(1)$. When $n > 10$, initializing `nums` requires $O(n)$ space, so worst-case space complexity is $O(n)$. +2. **Peak memory during execution**: Before the final line, the program uses $O(1)$ space. Initializing `nums` raises usage to $O(n)$, so worst-case space complexity is $O(n)$. === "Python" @@ -523,7 +523,7 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha end ``` -**In recursive functions, it is necessary to count the stack frame space**. Observe the following code: +**For recursive functions, remember to count stack frames**. Consider the following code: === "Python" @@ -533,12 +533,12 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha return 0 def loop(n: int): - """Loop has space complexity of O(1)""" + """The loop uses O(1) space""" for _ in range(n): function() def recur(n: int): - """Recursion has space complexity of O(n)""" + """The recursion uses O(n) space""" if n == 1: return return recur(n - 1) @@ -551,13 +551,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ void loop(int n) { for (int i = 0; i < n; i++) { func(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ void recur(int n) { if (n == 1) return; recur(n - 1); @@ -571,13 +571,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ void loop(int n) { for (int i = 0; i < n; i++) { function(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ void recur(int n) { if (n == 1) return; recur(n - 1); @@ -591,13 +591,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ void Loop(int n) { for (int i = 0; i < n; i++) { Function(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ int Recur(int n) { if (n == 1) return 1; return Recur(n - 1); @@ -611,15 +611,15 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0 } - - /* Loop has space complexity of O(1) */ + + /* The loop uses O(1) space */ func loop(n int) { for i := 0; i < n; i++ { function() } } - - /* Recursion has space complexity of O(n) */ + + /* The recursion uses O(n) space */ func recur(n int) { if n == 1 { return @@ -637,14 +637,14 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha return 0 } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ func loop(n: Int) { for _ in 0 ..< n { function() } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ func recur(n: Int) { if n == 1 { return @@ -660,13 +660,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ function loop(n) { for (let i = 0; i < n; i++) { constFunc(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ function recur(n) { if (n === 1) return; return recur(n - 1); @@ -680,13 +680,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ function loop(n: number): void { for (let i = 0; i < n; i++) { constFunc(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ function recur(n: number): void { if (n === 1) return; return recur(n - 1); @@ -700,13 +700,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ void loop(int n) { for (int i = 0; i < n; i++) { function(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ void recur(int n) { if (n == 1) return; recur(n - 1); @@ -720,13 +720,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ fn loop(n: i32) { for i in 0..n { function(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ fn recur(n: i32) { if n == 1 { return; @@ -742,13 +742,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0; } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ void loop(int n) { for (int i = 0; i < n; i++) { func(); } } - /* Recursion has space complexity of O(n) */ + /* The recursion uses O(n) space */ void recur(int n) { if (n == 1) return; recur(n - 1); @@ -762,13 +762,13 @@ Observe the following code. Here, "worst case" in worst-case space complexity ha // Perform some operations return 0 } - /* Loop has space complexity of O(1) */ + /* The loop uses O(1) space */ fun loop(n: Int) { for (i in 0..function can be executed independently, with all parameters passed explicitly. A method is associated with an object, is implicitly bound to the object that invokes it, and can operate on data contained in class instances. +A function can run independently, with all arguments passed explicitly. A method is associated with an object, receives that object implicitly when called, and can operate on the instance's data. -The following examples use several common programming languages for illustration. +Here are examples from several common languages. -- C is a procedural programming language without object-oriented concepts, so it only has functions. However, we can simulate object-oriented programming by creating structures (struct), and functions associated with structures are equivalent to methods in other programming languages. -- Java and C# are object-oriented programming languages where code blocks (methods) are typically part of a class. Static methods behave like functions because they are bound to the class and cannot access specific instance variables. -- C++ and Python support both procedural programming (functions) and object-oriented programming (methods). +- C is procedural and has no built-in object-oriented model, so it has functions only. We can simulate object-oriented programming with structures (`struct`); functions associated with a structure then act like methods in other languages. +- Java and C# are object-oriented, and executable blocks (methods) are usually part of a class. Static methods behave like functions: they belong to the class and cannot access a particular instance's variables directly. +- C++ and Python support both procedural programming with functions and object-oriented programming with methods. -**Q**: Does the diagram for "common space complexity types" reflect the absolute size of occupied space? +**Q**: Does the common space complexity chart show absolute memory usage? -No, the diagram shows space complexity, which reflects growth trends rather than the absolute size of occupied space. +No. It illustrates growth trends rather than absolute amounts of memory. -Assuming $n = 8$, you might find that the values of each curve do not correspond to the functions. This is because each curve contains a constant term used to compress the value range into a visually comfortable range. +For $n = 8$, the plotted values may not match the corresponding functions exactly. Each curve includes a constant term that adjusts it to a visually convenient range. -In practice, because we generally do not know the "constant-term" cost of each method, we usually cannot choose the optimal solution for cases like $n = 8$ based on complexity alone. But for $n = 8^5$, the choice is straightforward, because the growth trend already dominates. +In practice, these constant terms are usually unknown, so complexity alone cannot generally identify the best solution for $n = 8$. For $n = 8^5$, the choice is easier because growth rates dominate. -**Q**: Are there situations where algorithms are designed to sacrifice time (or space) based on actual use cases? +**Q**: Do algorithms sometimes trade time for space, or space for time, depending on the application? -In practical applications, most situations choose to sacrifice space for time. For example, with database indexes, we typically choose to build B+ trees or hash indexes, occupying substantial memory space in exchange for efficient queries of $O(\log n)$ or even $O(1)$. +Most practical applications trade space for time. Database indexes, for example, often use B+ trees or hash indexes, consuming substantial memory to achieve $O(\log n)$ or even $O(1)$ query time. -In scenarios where space resources are precious, time may be sacrificed for space. For example, in embedded development, device memory is precious, and engineers may forgo using hash tables and choose to use array sequential search to save memory usage, at the cost of slower searches. +When memory is scarce, the reverse tradeoff may be useful. In embedded development, an engineer might replace a hash table with sequential array search to save memory, accepting slower lookups. diff --git a/en/docs/chapter_computational_complexity/time_complexity.md b/en/docs/chapter_computational_complexity/time_complexity.md index 94d6aed6f..708c5ab25 100644 --- a/en/docs/chapter_computational_complexity/time_complexity.md +++ b/en/docs/chapter_computational_complexity/time_complexity.md @@ -1,22 +1,22 @@ -# Time Complexity +# Time complexity -Runtime can intuitively and accurately reflect the efficiency of an algorithm. If we want to accurately estimate the runtime of a piece of code, how should we proceed? +Runtime gives a direct, accurate picture of an algorithm's efficiency. How might we predict the runtime of a piece of code precisely? -1. **Determine the running platform**, including hardware configuration, programming language, system environment, etc., as these factors all affect code execution efficiency. -2. **Evaluate the runtime required for various computational operations**, for example, an addition operation `+` requires 1 ns, a multiplication operation `*` requires 10 ns, a print operation `print()` requires 5 ns, etc. -3. **Count all computational operations in the code**, and sum the execution times of all operations to obtain the runtime. +1. **Specify the execution platform**, including hardware, programming language, and system environment, all of which affect performance. +2. **Determine the time required by each operation**: for example, addition `+` might take 1 ns, multiplication `*` 10 ns, and `print()` 5 ns. +3. **Count every operation in the code** and sum their execution times to obtain the total runtime. -For example, in the following code, the input data size is $n$: +For example, consider the following code with input size $n$: === "Python" ```python title="" - # On a certain running platform + # On a particular execution platform def algorithm(n: int): a = 2 # 1 ns a = a + 1 # 1 ns a = a * 2 # 10 ns - # Loop n times + # Repeat n times for _ in range(n): # 1 ns print(0) # 5 ns ``` @@ -24,12 +24,12 @@ For example, in the following code, the input data size is $n$: === "C++" ```cpp title="" - // On a certain running platform + // On a particular execution platform void algorithm(int n) { int a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for (int i = 0; i < n; i++) { // 1 ns cout << 0 << endl; // 5 ns } @@ -39,12 +39,12 @@ For example, in the following code, the input data size is $n$: === "Java" ```java title="" - // On a certain running platform + // On a particular execution platform void algorithm(int n) { int a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for (int i = 0; i < n; i++) { // 1 ns System.out.println(0); // 5 ns } @@ -54,12 +54,12 @@ For example, in the following code, the input data size is $n$: === "C#" ```csharp title="" - // On a certain running platform + // On a particular execution platform void Algorithm(int n) { int a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for (int i = 0; i < n; i++) { // 1 ns Console.WriteLine(0); // 5 ns } @@ -69,12 +69,12 @@ For example, in the following code, the input data size is $n$: === "Go" ```go title="" - // On a certain running platform + // On a particular execution platform func algorithm(n int) { a := 2 // 1 ns a = a + 1 // 1 ns a = a * 2 // 10 ns - // Loop n times + // Repeat n times for i := 0; i < n; i++ { // 1 ns fmt.Println(a) // 5 ns } @@ -84,12 +84,12 @@ For example, in the following code, the input data size is $n$: === "Swift" ```swift title="" - // On a certain running platform + // On a particular execution platform func algorithm(n: Int) { var a = 2 // 1 ns a = a + 1 // 1 ns a = a * 2 // 10 ns - // Loop n times + // Repeat n times for _ in 0 ..< n { // 1 ns print(0) // 5 ns } @@ -99,12 +99,12 @@ For example, in the following code, the input data size is $n$: === "JS" ```javascript title="" - // On a certain running platform + // On a particular execution platform function algorithm(n) { var a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for(let i = 0; i < n; i++) { // 1 ns console.log(0); // 5 ns } @@ -114,12 +114,12 @@ For example, in the following code, the input data size is $n$: === "TS" ```typescript title="" - // On a certain running platform + // On a particular execution platform function algorithm(n: number): void { var a: number = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for(let i = 0; i < n; i++) { // 1 ns console.log(0); // 5 ns } @@ -129,12 +129,12 @@ For example, in the following code, the input data size is $n$: === "Dart" ```dart title="" - // On a certain running platform + // On a particular execution platform void algorithm(int n) { int a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for (int i = 0; i < n; i++) { // 1 ns print(0); // 5 ns } @@ -144,12 +144,12 @@ For example, in the following code, the input data size is $n$: === "Rust" ```rust title="" - // On a certain running platform + // On a particular execution platform fn algorithm(n: i32) { let mut a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for _ in 0..n { // 1 ns println!("{}", 0); // 5 ns } @@ -159,12 +159,12 @@ For example, in the following code, the input data size is $n$: === "C" ```c title="" - // On a certain running platform + // On a particular execution platform void algorithm(int n) { int a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - // Loop n times + // Repeat n times for (int i = 0; i < n; i++) { // 1 ns printf("%d", 0); // 5 ns } @@ -174,12 +174,12 @@ For example, in the following code, the input data size is $n$: === "Kotlin" ```kotlin title="" - // On a certain running platform + // On a particular execution platform fun algorithm(n: Int) { var a = 2 // 1 ns a = a + 1 // 1 ns a = a * 2 // 10 ns - // Loop n times + // Repeat n times for (i in 0.. 1$ it is slower than algorithm `A`, and when $n > 1000000$ it is slower than algorithm `C`. In fact, as long as the input data size $n$ is sufficiently large, an algorithm with "constant order" complexity will always be superior to one with "linear order" complexity, which is precisely the meaning of time growth trend. -- **The derivation method for time complexity is simpler**. Obviously, the running platform and the types of computational operations are both unrelated to the growth trend of the algorithm's runtime. Therefore, in time complexity analysis, we can simply treat the execution time of all computational operations as the same "unit time", reducing "tracking the runtime of each operation" to "counting the number of operations", which greatly reduces the difficulty of estimation. -- **Time complexity also has certain limitations**. For example, although algorithms `A` and `C` have the same time complexity, their actual runtimes differ significantly. Similarly, although algorithm `B` has a higher time complexity than `C`, when the input data size $n$ is small, algorithm `B` is clearly superior to algorithm `C`. In such cases, it is often difficult to judge the efficiency of algorithms based solely on time complexity. Of course, despite the above issues, complexity analysis remains the most effective and commonly used method for evaluating algorithm efficiency. +- **Time complexity evaluates efficiency effectively**. Algorithm `B` grows linearly, becoming slower than `A` when $n > 1$ and slower than `C` when $n > 1000000$. With sufficiently large input size $n$, a constant-time algorithm will outperform a linear-time one. This is what a growth trend tells us. +- **Time complexity is easier to derive**. The execution platform and kinds of elementary operations do not determine the growth trend. We can treat each elementary operation as taking the same unit of time, reducing runtime estimation to operation counting and greatly simplifying analysis. +- **Time complexity has limitations**. Algorithms `A` and `C` have the same complexity but very different runtimes. Although `B` has higher complexity than `C`, it is much faster for small $n$. Complexity alone may not settle such comparisons. Even so, it remains the most effective and widely used way to evaluate algorithm efficiency. -## Asymptotic Upper Bound of Functions +## Asymptotic upper bounds -Given a function with input size $n$: +Consider this function with input size $n$: === "Python" @@ -509,7 +509,7 @@ Given a function with input size $n$: a = 1 # +1 a = a + 1 # +1 a = a * 2 # +1 - # Loop n times + # Repeat n times for i in range(n): # +1 print(0) # +1 ``` @@ -521,8 +521,8 @@ Given a function with input size $n$: int a = 1; // +1 a = a + 1; // +1 a = a * 2; // +1 - // Loop n times - for (int i = 0; i < n; i++) { // +1 (i++ is executed each round) + // Repeat n times + for (int i = 0; i < n; i++) { // +1 (i ++ executes each iteration) cout << 0 << endl; // +1 } } @@ -535,8 +535,8 @@ Given a function with input size $n$: int a = 1; // +1 a = a + 1; // +1 a = a * 2; // +1 - // Loop n times - for (int i = 0; i < n; i++) { // +1 (i++ is executed each round) + // Repeat n times + for (int i = 0; i < n; i++) { // +1 (i ++ executes each iteration) System.out.println(0); // +1 } } @@ -549,8 +549,8 @@ Given a function with input size $n$: int a = 1; // +1 a = a + 1; // +1 a = a * 2; // +1 - // Loop n times - for (int i = 0; i < n; i++) { // +1 (i++ is executed each round) + // Repeat n times + for (int i = 0; i < n; i++) { // +1 (i ++ executes each iteration) Console.WriteLine(0); // +1 } } @@ -563,7 +563,7 @@ Given a function with input size $n$: a := 1 // +1 a = a + 1 // +1 a = a * 2 // +1 - // Loop n times + // Repeat n times for i := 0; i < n; i++ { // +1 fmt.Println(a) // +1 } @@ -577,7 +577,7 @@ Given a function with input size $n$: var a = 1 // +1 a = a + 1 // +1 a = a * 2 // +1 - // Loop n times + // Repeat n times for _ in 0 ..< n { // +1 print(0) // +1 } @@ -591,8 +591,8 @@ Given a function with input size $n$: var a = 1; // +1 a += 1; // +1 a *= 2; // +1 - // Loop n times - for(let i = 0; i < n; i++){ // +1 (i++ is executed each round) + // Repeat n times + for(let i = 0; i < n; i++){ // +1 (i ++ executes each iteration) console.log(0); // +1 } } @@ -605,8 +605,8 @@ Given a function with input size $n$: var a: number = 1; // +1 a += 1; // +1 a *= 2; // +1 - // Loop n times - for(let i = 0; i < n; i++){ // +1 (i++ is executed each round) + // Repeat n times + for(let i = 0; i < n; i++){ // +1 (i ++ executes each iteration) console.log(0); // +1 } } @@ -619,8 +619,8 @@ Given a function with input size $n$: int a = 1; // +1 a = a + 1; // +1 a = a * 2; // +1 - // Loop n times - for (int i = 0; i < n; i++) { // +1 (i++ is executed each round) + // Repeat n times + for (int i = 0; i < n; i++) { // +1 (i ++ executes each iteration) print(0); // +1 } } @@ -634,8 +634,8 @@ Given a function with input size $n$: a = a + 1; // +1 a = a * 2; // +1 - // Loop n times - for _ in 0..n { // +1 (i++ is executed each round) + // Repeat n times + for _ in 0..n { // +1 (i ++ executes each iteration) println!("{}", 0); // +1 } } @@ -648,8 +648,8 @@ Given a function with input size $n$: int a = 1; // +1 a = a + 1; // +1 a = a * 2; // +1 - // Loop n times - for (int i = 0; i < n; i++) { // +1 (i++ is executed each round) + // Repeat n times + for (int i = 0; i < n; i++) { // +1 (i ++ executes each iteration) printf("%d", 0); // +1 } } @@ -662,8 +662,8 @@ Given a function with input size $n$: var a = 1 // +1 a = a + 1 // +1 a = a * 2 // +1 - // Loop n times - for (i in 0..big-$O$ notation, representing the asymptotic upper bound of the function $T(n)$. +We write linear time complexity as $O(n)$. This is called big-$O$ notation, and it gives an asymptotic upper bound on $T(n)$. -Time complexity analysis essentially calculates the asymptotic upper bound of "the number of operations $T(n)$", which has a clear mathematical definition. +Time complexity analysis essentially finds an asymptotic upper bound on the operation count $T(n)$. It has a precise mathematical definition. -!!! note "Asymptotic upper bound of functions" +!!! note "Asymptotic upper bound" - If there exist positive real numbers $c$ and $n_0$ such that for all $n > n_0$, we have $T(n) \leq c \cdot f(n)$, then $f(n)$ can be considered as an asymptotic upper bound of $T(n)$, denoted as $T(n) = O(f(n))$. + If a positive real constant $c$ and a real number $n_0$ exist such that $T(n) \leq c \cdot f(n)$ for all $n > n_0$, then $f(n)$ is an asymptotic upper bound on $T(n)$, written $T(n) = O(f(n))$. -As shown in the figure below, calculating the asymptotic upper bound is to find a function $f(n)$ such that when $n$ tends to infinity, $T(n)$ and $f(n)$ are at the same growth level, differing only by a constant coefficient $c$. +As shown in the figure below, we seek a function $f(n)$ such that, as $n$ tends to infinity, $T(n)$ and $f(n)$ have the same order of growth, differing only by a constant factor $c$. ![Asymptotic upper bound of a function](time_complexity.assets/asymptotic_upper_bound.png) -## Derivation Method +## How to derive time complexity -The idea of an asymptotic upper bound is somewhat mathematical. If you feel you haven't fully understood it, don't worry. We can first master the derivation method, and gradually grasp its mathematical meaning through continuous practice. +The mathematics of asymptotic bounds may feel unfamiliar. Do not worry if it is not fully clear yet: learn the practical method first, and its meaning will become clearer with practice. -According to the definition, after determining $f(n)$, we can obtain the time complexity $O(f(n))$. So how do we determine the asymptotic upper bound $f(n)$? Overall, it is divided into two steps: first count the number of operations, then determine the asymptotic upper bound. +Once we identify $f(n)$, the time complexity is $O(f(n))$. Finding the asymptotic upper bound $f(n)$ takes two steps: count operations, then determine the bound. -### Step 1: Count the Number of Operations +### Step 1: Count operations -For code, count from top to bottom line by line. However, since the constant coefficient $c$ in $c \cdot f(n)$ above can be of any size, **coefficients and constant terms in the number of operations $T(n)$ can all be ignored**. According to this principle, the following counting simplification techniques can be summarized. +We can count code operations line by line. Since the constant factor $c$ in $c \cdot f(n)$ may be arbitrarily large, **coefficients and constant terms in $T(n)$ can be ignored**. This gives three useful shortcuts. -1. **Ignore constants in $T(n)$**. Because they are all independent of $n$, they do not affect time complexity. -2. **Omit all coefficients**. For example, looping $2n$ times, $5n + 1$ times, etc., can all be simplified as $n$ times, because the coefficient before $n$ does not affect time complexity. -3. **Use multiplication for nested loops**. The total number of operations equals the product of the number of operations in the outer and inner loops, with each layer of loop still able to apply techniques `1.` and `2.` separately. +1. **Ignore constant terms in $T(n)$**. They are independent of $n$ and do not affect complexity. +2. **Drop coefficients**. For example, $2n$ or $5n + 1$ iterations can be counted simply as $n$, because the coefficient of $n$ does not affect complexity. +3. **Multiply for nested loops**. Multiply the outer and inner iteration counts, applying shortcuts `1.` and `2.` to each level. -Given a function, we can use the above techniques to count the number of operations: +We can apply these shortcuts to the following function: === "Python" ```python title="" def algorithm(n: int): - a = 1 # +0 (Technique 1) - a = a + n # +0 (Technique 1) - # +n (Technique 2) + a = 1 # +0 (shortcut 1) + a = a + n # +0 (shortcut 1) + # +n (shortcut 2) for i in range(5 * n + 1): print(0) - # +n*n (Technique 3) + # +n*n (shortcut 3) for i in range(2 * n): for j in range(n + 1): print(0) @@ -738,13 +738,13 @@ Given a function, we can use the above techniques to count the number of operati ```cpp title="" void algorithm(int n) { - int a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) - // +n (Technique 2) + int a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) + // +n (shortcut 2) for (int i = 0; i < 5 * n + 1; i++) { cout << 0 << endl; } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (int i = 0; i < 2 * n; i++) { for (int j = 0; j < n + 1; j++) { cout << 0 << endl; @@ -757,13 +757,13 @@ Given a function, we can use the above techniques to count the number of operati ```java title="" void algorithm(int n) { - int a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) - // +n (Technique 2) + int a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) + // +n (shortcut 2) for (int i = 0; i < 5 * n + 1; i++) { System.out.println(0); } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (int i = 0; i < 2 * n; i++) { for (int j = 0; j < n + 1; j++) { System.out.println(0); @@ -776,13 +776,13 @@ Given a function, we can use the above techniques to count the number of operati ```csharp title="" void Algorithm(int n) { - int a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) - // +n (Technique 2) + int a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) + // +n (shortcut 2) for (int i = 0; i < 5 * n + 1; i++) { Console.WriteLine(0); } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (int i = 0; i < 2 * n; i++) { for (int j = 0; j < n + 1; j++) { Console.WriteLine(0); @@ -795,13 +795,13 @@ Given a function, we can use the above techniques to count the number of operati ```go title="" func algorithm(n int) { - a := 1 // +0 (Technique 1) - a = a + n // +0 (Technique 1) - // +n (Technique 2) + a := 1 // +0 (shortcut 1) + a = a + n // +0 (shortcut 1) + // +n (shortcut 2) for i := 0; i < 5 * n + 1; i++ { fmt.Println(0) } - // +n*n (Technique 3) + // +n*n (shortcut 3) for i := 0; i < 2 * n; i++ { for j := 0; j < n + 1; j++ { fmt.Println(0) @@ -814,13 +814,13 @@ Given a function, we can use the above techniques to count the number of operati ```swift title="" func algorithm(n: Int) { - var a = 1 // +0 (Technique 1) - a = a + n // +0 (Technique 1) - // +n (Technique 2) + var a = 1 // +0 (shortcut 1) + a = a + n // +0 (shortcut 1) + // +n (shortcut 2) for _ in 0 ..< (5 * n + 1) { print(0) } - // +n*n (Technique 3) + // +n*n (shortcut 3) for _ in 0 ..< (2 * n) { for _ in 0 ..< (n + 1) { print(0) @@ -833,13 +833,13 @@ Given a function, we can use the above techniques to count the number of operati ```javascript title="" function algorithm(n) { - let a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) - // +n (Technique 2) + let a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) + // +n (shortcut 2) for (let i = 0; i < 5 * n + 1; i++) { console.log(0); } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (let i = 0; i < 2 * n; i++) { for (let j = 0; j < n + 1; j++) { console.log(0); @@ -852,13 +852,13 @@ Given a function, we can use the above techniques to count the number of operati ```typescript title="" function algorithm(n: number): void { - let a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) - // +n (Technique 2) + let a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) + // +n (shortcut 2) for (let i = 0; i < 5 * n + 1; i++) { console.log(0); } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (let i = 0; i < 2 * n; i++) { for (let j = 0; j < n + 1; j++) { console.log(0); @@ -871,13 +871,13 @@ Given a function, we can use the above techniques to count the number of operati ```dart title="" void algorithm(int n) { - int a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) - // +n (Technique 2) + int a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) + // +n (shortcut 2) for (int i = 0; i < 5 * n + 1; i++) { print(0); } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (int i = 0; i < 2 * n; i++) { for (int j = 0; j < n + 1; j++) { print(0); @@ -890,15 +890,15 @@ Given a function, we can use the above techniques to count the number of operati ```rust title="" fn algorithm(n: i32) { - let mut a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) + let mut a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) - // +n (Technique 2) + // +n (shortcut 2) for i in 0..(5 * n + 1) { println!("{}", 0); } - // +n*n (Technique 3) + // +n*n (shortcut 3) for i in 0..(2 * n) { for j in 0..(n + 1) { println!("{}", 0); @@ -911,13 +911,13 @@ Given a function, we can use the above techniques to count the number of operati ```c title="" void algorithm(int n) { - int a = 1; // +0 (Technique 1) - a = a + n; // +0 (Technique 1) - // +n (Technique 2) + int a = 1; // +0 (shortcut 1) + a = a + n; // +0 (shortcut 1) + // +n (shortcut 2) for (int i = 0; i < 5 * n + 1; i++) { printf("%d", 0); } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (int i = 0; i < 2 * n; i++) { for (int j = 0; j < n + 1; j++) { printf("%d", 0); @@ -930,13 +930,13 @@ Given a function, we can use the above techniques to count the number of operati ```kotlin title="" fun algorithm(n: Int) { - var a = 1 // +0 (Technique 1) - a = a + n // +0 (Technique 1) - // +n (Technique 2) + var a = 1 // +0 (shortcut 1) + a = a + n // +0 (shortcut 1) + // +n (shortcut 2) for (i in 0..<5 * n + 1) { println(0) } - // +n*n (Technique 3) + // +n*n (shortcut 3) for (i in 0..<2 * n) { for (j in 0.. Table   Time complexities corresponding to different numbers of operations

+

Table   Operation counts and their time complexities

-| Number of Operations $T(n)$ | Time Complexity $O(f(n))$ | +| Operation count $T(n)$ | Time complexity $O(f(n))$ | | ---------------------- | -------------------- | | $100000$ | $O(1)$ | | $3n + 2$ | $O(n)$ | @@ -986,9 +986,9 @@ The table below shows some examples, where some exaggerated values are used to e | $n^3 + 10000n^2$ | $O(n^3)$ | | $2^n + 10000n^{10000}$ | $O(2^n)$ | -## Common Types +## Common complexity classes -Let the input data size be $n$. Common time complexity types are shown in the figure below (arranged in order from low to high). +For input size $n$, common time complexities are shown below in increasing order of growth. $$ \begin{aligned} @@ -997,155 +997,155 @@ $$ \end{aligned} $$ -![Common time complexity types](time_complexity.assets/time_complexity_common_types.png) +![Common time complexity classes](time_complexity.assets/time_complexity_common_types.png) -### Constant Order $O(1)$ +### Constant time $O(1)$ -The number of operations in constant order is independent of the input data size $n$, meaning it does not change as $n$ changes. +A constant operation count is independent of input size $n$: changing $n$ does not change the count. -In the following function, although the value of `size` may be large, it is independent of the input data size $n$, so the time complexity remains $O(1)$: +In the following function, `size` may be large, but it is independent of input size $n$, so the time complexity remains $O(1)$: ```src [file]{time_complexity}-[class]{}-[func]{constant} ``` -### Linear Order $O(n)$ +### Linear time $O(n)$ -The number of operations in linear order grows linearly relative to the input data size $n$. Linear order typically appears in single-layer loops: +A linear operation count grows proportionally to input size $n$. It commonly appears in a single loop: ```src [file]{time_complexity}-[class]{}-[func]{linear} ``` -Operations such as traversing arrays and traversing linked lists have a time complexity of $O(n)$, where $n$ is the length of the array or linked list: +Traversing an array or linked list takes $O(n)$ time, where $n$ is its length: ```src [file]{time_complexity}-[class]{}-[func]{array_traversal} ``` -It is worth noting that **the input data size $n$ should be determined according to the type of input data**. For example, in the first example, the variable $n$ is the input data size; in the second example, the array length $n$ is the data size. +**The meaning of input size $n$ depends on the input type**. In the first example, it is the variable $n$; in the second, it is the array length $n$. -### Quadratic Order $O(n^2)$ +### Quadratic time $O(n^2)$ -The number of operations in quadratic order grows quadratically relative to the input data size $n$. Quadratic order typically appears in nested loops, where both the outer and inner loops have a time complexity of $O(n)$, resulting in an overall time complexity of $O(n^2)$: +A quadratic operation count grows with the square of input size $n$. It commonly appears in nested loops where both levels take $O(n)$ time, giving $O(n^2)$ overall: ```src [file]{time_complexity}-[class]{}-[func]{quadratic} ``` -The figure below compares constant order, linear order, and quadratic order time complexities. +The figure below compares constant, linear, and quadratic time complexity. -![Time complexities of constant, linear, and quadratic orders](time_complexity.assets/time_complexity_constant_linear_quadratic.png) +![Constant, linear, and quadratic time complexity](time_complexity.assets/time_complexity_constant_linear_quadratic.png) -Taking bubble sort as an example, the outer loop executes $n - 1$ times, and the inner loop executes $n-1$, $n-2$, $\dots$, $2$, $1$ times, averaging $n / 2$ times, resulting in a time complexity of $O((n - 1) n / 2) = O(n^2)$: +In bubble sort, for example, the outer loop runs $n - 1$ times, while the inner loop runs $n-1$, $n-2$, $\dots$, $2$, and $1$ times, averaging $n / 2$. The resulting complexity is $O((n - 1) n / 2) = O(n^2)$: ```src [file]{time_complexity}-[class]{}-[func]{bubble_sort} ``` -### Exponential Order $O(2^n)$ +### Exponential time $O(2^n)$ -Biological "cell division" is a typical example of exponential order growth: the initial state is $1$ cell, after one round of division it becomes $2$, after two rounds it becomes $4$, and so on; after $n$ rounds of division there are $2^n$ cells. +Cell division is a classic example of exponential growth. Starting with $1$ cell, one round produces $2$, two rounds produce $4$, and after $n$ rounds there are $2^n$ cells. -The figure below and the following code simulate the cell division process, with a time complexity of $O(2^n)$. Note that the input $n$ represents the number of division rounds, and the return value `count` represents the total number of divisions. +The figure and code below simulate cell division in $O(2^n)$ time. The input $n$ is the number of rounds, while the returned `count` is the total number of individual divisions. ```src [file]{time_complexity}-[class]{}-[func]{exponential} ``` -![Time complexity of exponential order](time_complexity.assets/time_complexity_exponential.png) +![Exponential time complexity](time_complexity.assets/time_complexity_exponential.png) -In actual algorithms, exponential order often appears in recursive functions. For example, in the following code, it recursively splits in two, stopping after $n$ splits: +Exponential time often appears in recursive algorithms. The following function branches into two calls at each level, stopping after $n$ levels: ```src [file]{time_complexity}-[class]{}-[func]{exp_recur} ``` -Exponential order growth is very rapid and is common in exhaustive methods (brute force search, backtracking, etc.). For problems with large data scales, exponential order is unacceptable and typically requires dynamic programming or greedy algorithms to solve. +Exponential growth is very rapid and common in exhaustive approaches such as brute-force search and backtracking. It is unacceptable for large inputs, which usually require approaches such as dynamic programming or greedy algorithms. -### Logarithmic Order $O(\log n)$ +### Logarithmic time $O(\log n)$ -In contrast to exponential order, logarithmic order reflects the situation of "reducing to half each round". Let the input data size be $n$. Since it is reduced to half each round, the number of loops is $\log_2 n$, which is the inverse function of $2^n$. +Logarithmic growth corresponds to repeatedly halving a quantity, the reverse of exponential growth. Starting with input size $n$, halving each round gives $\log_2 n$ iterations, the inverse of $2^n$. -The figure below and the following code simulate the process of "reducing to half each round", with a time complexity of $O(\log_2 n)$, abbreviated as $O(\log n)$: +The figure and code below simulate repeated halving, taking $O(\log_2 n)$ time, usually written $O(\log n)$: ```src [file]{time_complexity}-[class]{}-[func]{logarithmic} ``` -![Time complexity of logarithmic order](time_complexity.assets/time_complexity_logarithmic.png) +![Logarithmic time complexity](time_complexity.assets/time_complexity_logarithmic.png) -Like exponential order, logarithmic order also commonly appears in recursive functions. The following code forms a recursion tree of height $\log_2 n$: +Like exponential growth, logarithmic growth often occurs in recursion. The following code produces a recursion tree of height $\log_2 n$: ```src [file]{time_complexity}-[class]{}-[func]{log_recur} ``` -Logarithmic order commonly appears in algorithms based on the divide-and-conquer strategy, reflecting the idea of repeatedly splitting a problem and simplifying it. It grows slowly and is the ideal time complexity second only to constant order. +Logarithmic time often arises in divide-and-conquer algorithms, reflecting the idea of dividing a problem into smaller, simpler parts. It grows slowly and is highly desirable, second only to constant time among these classes. !!! tip "What is the base of $O(\log n)$?" - To be precise, "dividing into $m$" corresponds to a time complexity of $O(\log_m n)$. And through the logarithmic base change formula, we can obtain time complexities with different bases that are equal: + More precisely, reducing the size by a factor of $m$ gives $O(\log_m n)$. The change-of-base formula shows that different fixed bases yield the same complexity: $$ O(\log_m n) = O(\log_k n / \log_k m) = O(\log_k n) $$ - That is to say, the base $m$ can be converted without affecting the complexity. Therefore, we usually omit the base $m$ and denote logarithmic order simply as $O(\log n)$. + Thus, changing the base $m$ does not affect the complexity. We usually omit $m$ and simply write $O(\log n)$. -### Linearithmic Order $O(n \log n)$ +### Linearithmic time $O(n \log n)$ -Linearithmic order commonly appears in nested loops, where the time complexities of the two layers of loops are $O(\log n)$ and $O(n)$ respectively. The relevant code is as follows: +Linearithmic time often appears when one level of a nested process takes $O(\log n)$ and another takes $O(n)$. An example follows: ```src [file]{time_complexity}-[class]{}-[func]{linear_log_recur} ``` -The figure below shows how linearithmic order is generated. Each level of the binary tree has a total of $n$ operations, and the tree has $\log_2 n + 1$ levels, resulting in a time complexity of $O(n \log n)$. +The figure below shows how linearithmic growth arises. Each level of the binary tree performs $n$ operations, and there are $\log_2 n + 1$ levels, giving $O(n \log n)$ time. -![Time complexity of linearithmic order](time_complexity.assets/time_complexity_logarithmic_linear.png) +![Linearithmic time complexity](time_complexity.assets/time_complexity_logarithmic_linear.png) -Mainstream sorting algorithms typically have a time complexity of $O(n \log n)$, such as quicksort, merge sort, and heap sort. +Common sorting algorithms, including quick sort, merge sort, and heap sort, typically take $O(n \log n)$ time. -### Factorial Order $O(n!)$ +### Factorial time $O(n!)$ -Factorial order corresponds to the mathematical "permutation" problem. Given $n$ distinct elements, find all possible permutation schemes; the number of schemes is: +Factorial growth arises when generating every permutation of $n$ distinct elements. The number of permutations is: $$ n! = n \times (n - 1) \times (n - 2) \times \dots \times 2 \times 1 $$ -Factorials are typically implemented using recursion. As shown in the figure below and the following code, the first level splits into $n$ branches, the second level splits into $n - 1$ branches, and so on, until the $n$-th level when splitting stops: +Algorithms with factorial time complexity are usually implemented recursively. As the figure and code below show, each call creates $n$ branches at the first level, $n - 1$ at the second, and so on until level $n$: ```src [file]{time_complexity}-[class]{}-[func]{factorial_recur} ``` -![Time complexity of factorial order](time_complexity.assets/time_complexity_factorial.png) +![Factorial time complexity](time_complexity.assets/time_complexity_factorial.png) -Note that because when $n \geq 4$ we always have $n! > 2^n$, factorial order grows faster than exponential order, and is also unacceptable for large $n$. +Since $n! > 2^n$ whenever $n \geq 4$, factorial growth is faster than exponential growth and is also unacceptable for large $n$. -## Worst, Best, and Average Time Complexities +## Worst-case, best-case, and average-case time complexity -**The time efficiency of an algorithm is often not fixed, but is related to the distribution of the input data**. Suppose we input an array `nums` of length $n$, where `nums` consists of numbers from $1$ to $n$, with each number appearing only once, but the element order is randomly shuffled. The task is to return the index of element $1$. We can draw the following conclusions. +**An algorithm's efficiency often depends on how its input data is arranged**. Suppose `nums` is an array of length $n$ containing every integer from $1$ through $n$ exactly once in a random order. The task is to return the index of $1$. We can conclude the following. -- When `nums = [?, ?, ..., 1]`, i.e., when the last element is $1$, it requires a complete traversal of the array, **reaching worst-case time complexity $O(n)$**. -- When `nums = [1, ?, ?, ...]`, i.e., when the first element is $1$, no matter how long the array is, there is no need to continue traversing, **reaching best-case time complexity $\Omega(1)$**. +- If `nums = [?, ?, ..., 1]`, with $1$ last, we must traverse the whole array, **giving worst-case time complexity $O(n)$**. +- If `nums = [1, ?, ?, ...]`, with $1$ first, no further traversal is needed regardless of array length, **giving best-case time complexity $\Omega(1)$**. -The "worst-case time complexity" corresponds to the function's asymptotic upper bound, denoted using big-$O$ notation. Correspondingly, the "best-case time complexity" corresponds to the function's asymptotic lower bound, denoted using $\Omega$ notation: +Worst-case time complexity corresponds to an asymptotic upper bound, expressed with big-$O$ notation. Best-case time complexity corresponds to an asymptotic lower bound, expressed with $\Omega$: ```src [file]{worst_best_time_complexity}-[class]{}-[func]{find_one} ``` -It is worth noting that we rarely use best-case time complexity in practice, because it can usually only be achieved with a very small probability and may be somewhat misleading. **The worst-case time complexity is more practical because it gives a safety value for efficiency**, allowing us to use the algorithm with confidence. +Best-case complexity is rarely used in practice because it may occur only with low probability and can be misleading. **Worst-case complexity is more useful because it gives a safe performance bound**, allowing us to use the algorithm with confidence. -From the above example, we can see that both worst-case and best-case time complexities arise only under particular input distributions, which may occur with very low probability and may not truly reflect the algorithm's running efficiency. In contrast, **average time complexity can reflect the algorithm's running efficiency under random input data**, denoted using the $\Theta$ notation. +In this example, both the worst and best cases occur only for particular arrangements, perhaps with low probability, and may not reflect ordinary performance. **Average-case complexity describes performance on random inputs** and is expressed with $\Theta$. -For some algorithms, we can simply derive the average case under random data distribution. For example, in the above example, since the input array is shuffled, the probability of element $1$ appearing at any index is equal, so the algorithm's average number of loops is half the array length $n / 2$, giving an average time complexity of $\Theta(n / 2) = \Theta(n)$. +For some algorithms, the average case is easy to derive. In the example above, $1$ is equally likely to occur at any index in the shuffled array. The average number of iterations is half the array length, $n / 2$, giving $\Theta(n / 2) = \Theta(n)$. -But for more complex algorithms, calculating average time complexity is often quite difficult, because it is hard to analyze the overall mathematical expectation under data distribution. In this case, we usually use worst-case time complexity as the criterion for judging algorithm efficiency. +For more complex algorithms, average-case complexity can be difficult to calculate because the overall expectation under the input distribution is hard to derive. In such cases, we usually judge efficiency by worst-case complexity. -!!! question "Why is the $\Theta$ symbol rarely seen?" +!!! question "Why is $\Theta$ rarely seen?" - This may be because the $O$ symbol is too catchy, so we often use it to represent average time complexity. But strictly speaking, this practice is not standard. In this book and other materials, if you encounter expressions like "average time complexity $O(n)$", please understand it directly as $\Theta(n)$. + Perhaps because $O$ is so familiar, it is often used for average-case complexity too. Strictly speaking, this usage is not standard. In this book and other resources, read a statement such as "average-case time complexity $O(n)$" as $\Theta(n)$. diff --git a/en/docs/chapter_data_structure/basic_data_types.md b/en/docs/chapter_data_structure/basic_data_types.md index 0658d3d1b..65bc139c2 100644 --- a/en/docs/chapter_data_structure/basic_data_types.md +++ b/en/docs/chapter_data_structure/basic_data_types.md @@ -1,66 +1,66 @@ -# Basic Data Types +# Basic data types -When we talk about data stored in computers, we think of various forms such as text, images, videos, audio, 3D models, and more. Although these kinds of data are organized in different ways, they are all composed of various basic data types. +Computer data comes in many forms: text, images, video, audio, 3D models, and more. Although organized differently, all these forms are built from basic data types. -**Basic data types are types that the CPU can directly operate on**, and they are directly used in algorithms, mainly including the following. +**Basic data types are types on which the CPU can operate directly** and which algorithms use directly. The main types include the following. -- Integer types `byte`, `short`, `int`, `long`. -- Floating-point types `float`, `double`, used to represent decimal numbers. -- Character type `char`, used to represent letters, punctuation marks, and even emojis in various languages. -- Boolean type `bool`, used to represent "yes" and "no" judgments. +- Integer types: `byte`, `short`, `int`, and `long`. +- Floating-point types: `float` and `double`, used to represent fractional numbers. +- The character type `char`, used for letters in different languages, punctuation, and even emoji. +- The Boolean type `bool`, used for yes-or-no values. -**Basic data types are stored in binary form in computers**. A binary digit is one bit. In most modern operating systems, $1$ byte consists of $8$ bits. +**Basic data types are stored in binary form**. One binary digit is $1$ bit. In the vast majority of modern operating systems, $1$ byte consists of $8$ bits. -The range of values for basic data types depends on the size of the space they occupy. Below is an example using Java. +A basic data type's value range depends on the space it occupies. Taking Java as an example: -- Integer type `byte` occupies $1$ byte = $8$ bits, and can represent $2^{8}$ numbers. -- Integer type `int` occupies $4$ bytes = $32$ bits, and can represent $2^{32}$ numbers. +- An integer `byte` occupies $1$ byte = $8$ bits and can represent $2^{8}$ distinct values. +- An integer `int` occupies $4$ bytes = $32$ bits and can represent $2^{32}$ distinct values. -The following table lists the space occupied, value ranges, and default values of various basic data types in Java. You don't need to memorize this table; a general understanding is sufficient, and you can refer to it when needed. +The table below lists storage sizes, value ranges, and default values for Java's basic data types. There is no need to memorize it; understand the general picture and refer back when needed. -

Table   Space occupied and value ranges of basic data types

+

Table   Storage sizes and value ranges of basic data types

-| Type | Symbol | Space Occupied | Minimum Value | Maximum Value | Default Value | -| ---------- | -------- | -------------- | ------------------------ | ----------------------- | -------------- | -| Integer | `byte` | 1 byte | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | $0$ | -| | `short` | 2 bytes | $-2^{15}$ | $2^{15} - 1$ | $0$ | -| | `int` | 4 bytes | $-2^{31}$ | $2^{31} - 1$ | $0$ | -| | `long` | 8 bytes | $-2^{63}$ | $2^{63} - 1$ | $0$ | -| Float | `float` | 4 bytes | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0\text{f}$ | -| | `double` | 8 bytes | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ | -| Character | `char` | 2 bytes | $0$ | $2^{16} - 1$ | $0$ | -| Boolean | `bool` | 1 byte | $\text{false}$ | $\text{true}$ | $\text{false}$ | +| Type | Name | Storage size | Minimum value | Maximum value | Default value | +| ------ | -------- | -------- | ------------------------ | ----------------------- | -------------- | +| Integer | `byte` | 1 byte | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | $0$ | +| | `short` | 2 bytes | $-2^{15}$ | $2^{15} - 1$ | $0$ | +| | `int` | 4 bytes | $-2^{31}$ | $2^{31} - 1$ | $0$ | +| | `long` | 8 bytes | $-2^{63}$ | $2^{63} - 1$ | $0$ | +| Floating-point | `float` | 4 bytes | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0\text{f}$ | +| | `double` | 8 bytes | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ | +| Character | `char` | 2 bytes | $0$ | $2^{16} - 1$ | $0$ | +| Boolean | `bool` | 1 byte | $\text{false}$ | $\text{true}$ | $\text{false}$ | -Please note that the table above applies specifically to Java's basic data types. Each programming language has its own type definitions, and their space usage, value ranges, and default values may vary. +The table above describes Java's basic data types. Each programming language defines its own types, whose sizes, value ranges, and default values may differ. -- In Python, the integer type `int` can be of any size, limited only by available memory; the floating-point type `float` is double-precision 64-bit; there is no `char` type, a single character is actually a string `str` of length 1. -- C and C++ do not explicitly specify the size of basic data types, which varies by implementation and platform. The above table follows the LP64 [data model](https://en.cppreference.com/w/cpp/language/types#Properties), which is used in Unix 64-bit operating systems including Linux and macOS. -- The size of character `char` is 1 byte in C and C++, and in most programming languages it depends on the specific character encoding method, as detailed in the "Character Encoding" section. -- Even though representing a boolean value requires only 1 bit ($0$ or $1$), it is usually stored as 1 byte in memory. This is because modern computer CPUs typically use 1 byte as the minimum addressable memory unit. +- In Python, an `int` can be arbitrarily large, limited only by available memory. A `float` is a 64-bit double-precision number. There is no `char` type: a single character is a `str` of length 1. +- C and C++ do not fix the sizes of basic data types; they vary by implementation and platform. The table above follows the LP64 [data model](https://en.cppreference.com/w/cpp/language/types#Properties), used by 64-bit Unix operating systems including Linux and macOS. +- A `char` occupies 1 byte in C and C++. In most programming languages, character size depends on the encoding used; see the character encoding section. +- Although a Boolean needs only 1 bit ($0$ or $1$), it usually occupies 1 byte in memory because modern CPUs typically use a byte as the smallest addressable memory unit. -So, what is the relationship between basic data types and data structures? We know that data structures are ways of organizing and storing data in computers. Here, the emphasis is on the "structure", not the "data". +How do basic data types relate to data structures? A data structure is a way to organize and store data in a computer. The emphasis is on structure, rather than the data itself. -If we want to represent "a row of numbers", we naturally think of using an array. This is because the linear structure of an array can represent the adjacency and order relationships of numbers, but whether the stored content is integer `int`, floating-point `float`, or character `char` is unrelated to the "data structure". +To represent a row of numbers, an array comes naturally to mind. Its linear structure captures adjacency and ordering, regardless of whether the stored values are integers (`int`), fractional numbers (`float`), or characters (`char`). -In other words, **basic data types provide the "content type" of data, while data structures provide the "organization method" of data**. For example, in the following code, we use the same data structure (array) to store and represent different basic data types, including `int`, `float`, `char`, `bool`, etc. +In other words, **basic data types specify what kind of data is stored, while data structures specify how it is organized**. The following code uses the same data structure, an array, for different basic types, including `int`, `float`, `char`, and `bool`. === "Python" ```python title="" - # Initialize arrays using various basic data types + # Initialize arrays with different basic data types numbers: list[int] = [0] * 5 decimals: list[float] = [0.0] * 5 - # In Python, characters are actually strings of length 1 + # A Python character is a string of length 1 characters: list[str] = ['0'] * 5 bools: list[bool] = [False] * 5 - # Python lists can freely store various basic data types and object references + # Python lists can hold different basic data types and object references data = [0, 0.0, 'a', False, ListNode(0)] ``` === "C++" ```cpp title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types int numbers[5]; float decimals[5]; char characters[5]; @@ -70,7 +70,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "Java" ```java title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types int[] numbers = new int[5]; float[] decimals = new float[5]; char[] characters = new char[5]; @@ -80,7 +80,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "C#" ```csharp title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types int[] numbers = new int[5]; float[] decimals = new float[5]; char[] characters = new char[5]; @@ -90,7 +90,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "Go" ```go title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types var numbers = [5]int{} var decimals = [5]float64{} var characters = [5]byte{} @@ -100,7 +100,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "Swift" ```swift title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types let numbers = Array(repeating: 0, count: 5) let decimals = Array(repeating: 0.0, count: 5) let characters: [Character] = Array(repeating: "a", count: 5) @@ -110,14 +110,14 @@ In other words, **basic data types provide the "content type" of data, while dat === "JS" ```javascript title="" - // JavaScript arrays can freely store various basic data types and objects + // JavaScript arrays can hold different basic data types and objects const array = [0, 0.0, 'a', false]; ``` === "TS" ```typescript title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types const numbers: number[] = []; const characters: string[] = []; const bools: boolean[] = []; @@ -126,7 +126,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "Dart" ```dart title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types List numbers = List.filled(5, 0); List decimals = List.filled(5, 0.0); List characters = List.filled(5, 'a'); @@ -136,7 +136,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "Rust" ```rust title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types let numbers: Vec = vec![0; 5]; let decimals: Vec = vec![0.0; 5]; let characters: Vec = vec!['0'; 5]; @@ -146,7 +146,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "C" ```c title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types int numbers[10]; float decimals[10]; char characters[10]; @@ -156,7 +156,7 @@ In other words, **basic data types provide the "content type" of data, while dat === "Kotlin" ```kotlin title="" - // Initialize arrays using various basic data types + // Initialize arrays with different basic data types val numbers = IntArray(5) val decinals = FloatArray(5) val characters = CharArray(5) @@ -166,10 +166,10 @@ In other words, **basic data types provide the "content type" of data, while dat === "Ruby" ```ruby title="" - # Ruby lists can freely store various basic data types and object references + # Ruby arrays can hold different basic data types and object references data = [0, 0.0, 'a', false, ListNode(0)] ``` -??? pythontutor "Visualized Execution" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%90%8E%E7%BB%A7%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%A7%8D%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9D%A5%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%0A%20%20%20%20numbers%20%3D%20%5B0%5D%20*%205%0A%20%20%20%20decimals%20%3D%20%5B0.0%5D%20*%205%0A%20%20%20%20%23%20Python%20%E7%9A%84%E5%AD%97%E7%AC%A6%E5%AE%9E%E9%99%85%E4%B8%8A%E6%98%AF%E9%95%BF%E5%BA%A6%E4%B8%BA%201%20%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%0A%20%20%20%20characters%20%3D%20%5B'0'%5D%20*%205%0A%20%20%20%20bools%20%3D%20%5BFalse%5D%20*%205%0A%20%20%20%20%23%20Python%20%E7%9A%84%E5%88%97%E8%A1%A8%E5%8F%AF%E4%BB%A5%E8%87%AA%E7%94%B1%E5%AD%98%E5%82%A8%E5%90%84%E7%A7%8D%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%92%8C%E5%AF%B9%E8%B1%A1%E5%BC%95%E7%94%A8%0A%20%20%20%20data%20%3D%20%5B0,%200.0,%20'a',%20False,%20ListNode%280%29%5D&cumulative=false&curInstr=12&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked%20list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20successor%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20arrays%20with%20different%20basic%20data%20types%0A%20%20%20%20numbers%20%3D%20%5B0%5D%20%2A%205%0A%20%20%20%20decimals%20%3D%20%5B0.0%5D%20%2A%205%0A%20%20%20%20%23%20A%20Python%20character%20is%20a%20string%20of%20length%201%0A%20%20%20%20characters%20%3D%20%5B%270%27%5D%20%2A%205%0A%20%20%20%20bools%20%3D%20%5BFalse%5D%20%2A%205%0A%20%20%20%20%23%20Python%20lists%20can%20hold%20different%20basic%20data%20types%20and%20object%20references%0A%20%20%20%20data%20%3D%20%5B0%2C%200.0%2C%20%27a%27%2C%20False%2C%20ListNode%280%29%5D&cumulative=false&curInstr=12&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/docs/chapter_data_structure/character_encoding.md b/en/docs/chapter_data_structure/character_encoding.md index 80e56582c..633f95ba2 100644 --- a/en/docs/chapter_data_structure/character_encoding.md +++ b/en/docs/chapter_data_structure/character_encoding.md @@ -1,85 +1,85 @@ -# Character Encoding * +# Character encoding * -In computers, all data is stored in binary form, and character `char` is no exception. To represent characters, we need to establish a "character set" that defines a one-to-one correspondence between each character and binary numbers. With a character set, computers can convert binary numbers to characters by looking up the table. +All computer data is stored as binary numbers, including characters of type `char`. To represent characters, we need a character set that assigns each character a unique binary number. The computer can then convert numbers to characters by looking them up. -## ASCII Character Set +## ASCII character set -ASCII code is the earliest character set, with the full name American Standard Code for Information Interchange. It uses 7 binary bits (the lower 7 bits of one byte) to represent a character, and can represent a maximum of 128 different characters. As shown in the figure below, ASCII code includes uppercase and lowercase English letters, numbers 0 ~ 9, some punctuation marks, and some control characters (such as newline and tab). +ASCII, short for American Standard Code for Information Interchange, was the earliest character set. It uses 7 bits (the lower 7 bits of a byte) per character, allowing up to 128 characters. As shown in the figure below, ASCII includes uppercase and lowercase English letters, digits 0 ~ 9, punctuation, and control characters such as newlines and tabs. -![ASCII code](character_encoding.assets/ascii_table.png) +![ASCII codes](character_encoding.assets/ascii_table.png) -However, **ASCII code can only represent English**. With the globalization of computers, a character set called EASCII that can represent more languages emerged. It expands from the 7-bit basis of ASCII to 8 bits, and can represent 256 different characters. +However, **ASCII can represent only English text**. As computers spread worldwide, EASCII character sets emerged to support more languages. Extending ASCII from 7 bits to 8 allowed 256 distinct characters. -Worldwide, a batch of EASCII character sets suitable for different regions have appeared successively. The first 128 characters of these character sets are unified as ASCII code, and the last 128 characters are defined differently to adapt to the needs of different languages. +Different regions developed their own EASCII character sets. The first 128 characters remained identical to ASCII, while the remaining 128 varied to meet different languages' needs. -## GBK Character Set +## GBK character set -Later, people found that **EASCII still could not provide enough characters for many languages**. For example, there are nearly one hundred thousand Chinese characters, and several thousand are used in everyday life. In 1980, the China National Standardization Administration released the GB2312 character set, which included 6,763 Chinese characters, basically meeting the needs of computer processing for Chinese. +Eventually, **even EASCII proved too small for many languages**. Chinese, for example, has nearly 100,000 characters, with thousands used daily. In 1980, China's National Bureau of Standards released GB2312, containing 6,763 Chinese characters and meeting most basic needs for processing Chinese text. -However, GB2312 cannot handle some rare characters and traditional Chinese characters. The GBK character set is an extension based on GB2312, which includes a total of 21,886 Chinese characters. In the GBK encoding scheme, ASCII characters are represented using one byte, and Chinese characters are represented using two bytes. +GB2312 could not handle some rare and traditional characters. GBK extended it to 21,886 Chinese characters. In GBK encoding, ASCII characters use one byte, and Chinese characters use two. -## Unicode Character Set +## Unicode character set -With the vigorous development of computer technology, character sets and encoding standards flourished, which brought many problems. On the one hand, these character sets generally only define characters for specific languages and cannot work normally in multilingual environments. On the other hand, multiple character set standards exist for the same language, and if two computers use different encoding standards, garbled characters will appear during information transmission. +As computing grew, character sets and encoding standards proliferated, creating problems. Most covered only particular languages and did not work well in multilingual settings. Multiple standards also existed for the same language, so exchanging text between computers using different encodings could produce garbled text. -Researchers of that era thought: **If a sufficiently complete character set were released to include all languages and symbols in the world, wouldn't that solve problems in cross-language environments and eliminate garbled text**? Driven by this idea, a large and comprehensive character set, Unicode, was born. +Researchers asked: **could a sufficiently comprehensive character set, containing all the world's languages and symbols, solve both multilingual compatibility and garbled-text problems**? This idea led to Unicode. -Unicode, or Unified Code, can theoretically accommodate over one million characters. It is committed to including characters from around the world into a unified character set, providing a universal character set to handle and display various language texts, reducing garbled character problems caused by different encoding standards. Since its release in 1991, Unicode has continuously expanded to include new languages and characters. As of September 2022, Unicode has included 149,186 characters, including characters, symbols, and even emojis from various languages. +Unicode, whose Chinese name means "unified code", can theoretically accommodate over a million characters. It aims to bring the world's characters into one universal set for processing and displaying different languages, reducing garbled text caused by incompatible standards. Since its release in 1991, it has continued adding languages and characters. As of September 2022, Unicode contained 149,186 characters, including letters, symbols, and emoji. -As a universal character set, Unicode essentially assigns each character a unique "code point" (character identifier), whose range is U+0000 to U+10FFFF, forming a unified character numbering space. However, **Unicode does not specify how to store these character code points in computers**. We can't help but ask: when Unicode code points of multiple lengths appear simultaneously in a text, how does the system parse the characters? For example, given an encoding with a length of 2 bytes, how does the system determine whether it is one 2-byte character or two 1-byte characters? +As a universal character set, Unicode assigns each character a unique code point, or character number, from U+0000 to U+10FFFF. **It does not, by itself, specify how these code points are stored in a computer**. How can a system parse text containing code points of different lengths? For example, how does it distinguish one two-byte character from two one-byte characters? -For the above problem, **a straightforward solution is to store all characters as equal-length encodings**. As shown in the figure below, each character in "Hello" occupies 1 byte, and each character in "算法" (algorithm) occupies 2 bytes. We can encode all characters in "Hello 算法" as 2 bytes in length by padding the high bits with 0. In this way, the system can parse one character every 2 bytes and restore the content of this phrase. +**One straightforward solution is to store every character in a fixed-width encoding**. In the figure below, each character in "Hello" occupies 1 byte, while each in "算法" occupies 2 bytes. Padding the high bits with zeros lets us encode every character in "Hello 算法" with 2 bytes. The system can then read one character every 2 bytes to recover the phrase. -![Unicode encoding example](character_encoding.assets/unicode_hello_algo.png) +![Example of Unicode encoding](character_encoding.assets/unicode_hello_algo.png) -However, ASCII code has already proven to us that encoding English only requires 1 byte. If the above scheme is adopted, the size of English text will be twice that under ASCII encoding, which is very wasteful of memory space. Therefore, we need a more efficient Unicode encoding method. +ASCII has already shown that English text needs only 1 byte per character. This scheme would double its storage compared with ASCII, wasting memory. We therefore need a more efficient Unicode encoding. -## UTF-8 Encoding +## UTF-8 encoding -Currently, UTF-8 has become the most widely used Unicode encoding method internationally. **It is a variable-length encoding** that uses 1 to 4 bytes to represent a character, depending on the complexity of the character. ASCII characters only require 1 byte, Latin and Greek letters require 2 bytes, commonly used Chinese characters require 3 bytes, and some other rare characters require 4 bytes. +UTF-8 is now the most widely used Unicode encoding worldwide. **It is variable-length**, using 1 to 4 bytes per character depending on the character. ASCII characters need 1 byte, Latin and Greek letters need 2, common Chinese characters need 3, and some less common characters need 4. -The encoding rules of UTF-8 are not complicated and can be divided into the following two cases. +UTF-8's rules have two cases. -- For 1-byte characters, set the highest bit to $0$, and set the remaining 7 bits to the Unicode code point. It is worth noting that ASCII characters occupy the first 128 code points in the Unicode character set. That is to say, **UTF-8 encoding is backward compatible with ASCII code**. This means we can use UTF-8 to parse very old ASCII code text. -- For characters with a length of $n$ bytes (where $n > 1$), set the highest $n$ bits of the first byte to $1$, and set the $(n + 1)$-th bit to $0$; starting from the second byte, set the highest 2 bits of each byte to $10$; use all remaining bits to fill in the Unicode code point of the character. +- For a one-byte character, set the highest bit to $0$ and the remaining 7 bits to its Unicode code point. ASCII occupies the first 128 Unicode code points, so **UTF-8 is backward-compatible with ASCII**. UTF-8 can therefore decode old ASCII text. +- For an $n$-byte character, where $n > 1$, set the first byte's leading $n$ bits to $1$ and the next bit, $n + 1$, to $0$. Starting with the second byte, set the leading 2 bits of every byte to $10$. Fill the remaining bits with the character's Unicode code point. -The figure below shows the UTF-8 encoding corresponding to "Hello 算法". It can be observed that since the highest $n$ bits are all set to $1$, the system can determine that the character length is $n$ by counting the leading $1$ bits. +The figure below shows UTF-8 for "Hello算法". Since the leading $n$ bits are $1$, the system can determine the character length $n$ by counting the leading $1$ bits. -But why set the highest 2 bits of all other bytes to $10$? In fact, this $10$ can serve as a check symbol. Assuming the system starts parsing text from an incorrect byte, the $10$ at the beginning of the byte can help the system quickly determine an anomaly. +Why do all remaining bytes begin with $10$? This $10$ acts as a check marker. If decoding starts at the wrong byte, the leading $10$ helps the system quickly detect the problem. -The reason for using $10$ as a check symbol is that under UTF-8 encoding rules, it is impossible for a character's highest two bits to be $10$. This conclusion can be proven by contradiction: assuming the highest two bits of a character are $10$, it means the length of the character is $1$, corresponding to ASCII code. However, the highest bit of ASCII code should be $0$, which contradicts the assumption. +The prefix $10$ works as a marker because no character's first byte can begin with $10$ under UTF-8's rules. To see this by contradiction, suppose it did begin with $10$. The single leading one would imply a length of $1$ byte, corresponding to ASCII, but an ASCII byte must begin with $0$, a contradiction. -![UTF-8 encoding example](character_encoding.assets/utf-8_hello_algo.png) +![Example of UTF-8 encoding](character_encoding.assets/utf-8_hello_algo.png) -In addition to UTF-8, common encoding methods also include the following two. +Two other common encodings are: -- **UTF-16 encoding**: Uses 2 or 4 bytes to represent a character. All ASCII characters and commonly used non-English characters are represented with 2 bytes; a few characters need to use 4 bytes. For 2-byte characters, UTF-16 encoding is equal to the Unicode code point. -- **UTF-32 encoding**: Every character uses 4 bytes. This means that UTF-32 takes up more space than UTF-8 and UTF-16, especially for text with a high proportion of ASCII characters. +- **UTF-16**: Uses 2 or 4 bytes per character. ASCII and common non-English characters use 2 bytes; some characters require 4. For a two-byte character, its UTF-16 encoding equals its Unicode code point. +- **UTF-32**: Uses 4 bytes for every character. It therefore takes more space than UTF-8 or UTF-16, especially for text containing mostly ASCII characters. -From the perspective of storage space occupation, using UTF-8 to represent English characters is very efficient because it only requires 1 byte; using UTF-16 encoding for some non-English characters (such as Chinese) will be more efficient because it only requires 2 bytes, while UTF-8 may require 3 bytes. +In terms of storage, UTF-8 is efficient for English characters because each uses only 1 byte. UTF-16 is more efficient for some non-English characters, such as Chinese, using 2 bytes where UTF-8 may require 3. -From a compatibility perspective, UTF-8 has the best universality, and many tools and libraries support UTF-8 first. +In terms of compatibility, UTF-8 is the most widely supported, and many tools and libraries prioritize it. -## Character Encoding in Programming Languages +## Character encoding in programming languages -For many programming languages in the past, strings during program execution used internal encodings such as UTF-16 or UTF-32. Under these representations, we can often treat strings like arrays during processing, and this approach has the following advantages. +Historically, most programming languages have stored strings at runtime using fixed-width encodings such as UTF-16 or UTF-32. Fixed-width encoding lets us treat a string as an array, with the following advantages. -- **Random access**: UTF-16 encoded strings can be easily accessed randomly. UTF-8 is a variable-length encoding. To find the $i$-th character, we need to traverse from the beginning of the string to the $i$-th character, which requires $O(n)$ time. -- **Character counting**: Similar to random access, calculating the length of a UTF-16 encoded string is also an $O(1)$ operation. However, calculating the length of a UTF-8 encoded string requires traversing the entire string. -- **String operations**: Many string operations (such as splitting, joining, inserting, deleting, etc.) on UTF-16 encoded strings are easier to perform. Performing these operations on UTF-8 encoded strings usually requires additional calculations to ensure that invalid UTF-8 encoding is not generated. +- **Random access**: UTF-16 strings support straightforward random access. With variable-length UTF-8, finding the $i$-th character requires traversing from the beginning to the $i$-th character, taking $O(n)$ time. +- **Character counting**: Similarly, finding the length of a UTF-16 string is an $O(1)$ operation, while finding the character count of a UTF-8 string requires traversing the whole string. +- **String operations**: Operations such as splitting, concatenation, insertion, and deletion are easier on UTF-16 strings. With UTF-8, they usually require extra work to avoid producing invalid encodings. -In fact, the design of character encoding schemes for programming languages is a very interesting topic involving many factors. +The design of string representations is an interesting subject involving many factors. -- Java's `String` type uses UTF-16 encoding, with each character occupying 2 bytes. This is because at the beginning of Java language design, people believed that 16 bits were sufficient to represent all possible characters. However, this was an incorrect judgment. Later, the Unicode specification expanded beyond 16 bits, so characters in Java may now be represented by a pair of 16-bit values (called "surrogate pairs"). -- The strings of JavaScript and TypeScript use UTF-16 encoding for reasons similar to Java. When Netscape first introduced the JavaScript language in 1995, Unicode was still in its early stages of development, and at that time, using 16-bit encoding was sufficient to represent all Unicode characters. -- C# uses UTF-16 encoding mainly because the .NET platform was designed by Microsoft, and many of Microsoft's technologies (including the Windows operating system) extensively use UTF-16 encoding. +- Java's `String` uses UTF-16, with 2 bytes per character. When Java was designed, 16 bits were thought sufficient for every character. This turned out to be incorrect: Unicode later expanded beyond 16 bits, so a Java character may now require a pair of 16-bit values called a surrogate pair. +- JavaScript and TypeScript use UTF-16 for similar historical reasons. When Netscape introduced JavaScript in 1995, Unicode was still young, and 16 bits could represent all its characters at the time. +- C# uses UTF-16 largely because Microsoft designed .NET, and many Microsoft technologies, including Windows, use UTF-16 extensively. -Due to the underestimation of character quantities by the above programming languages, they had to adopt the "surrogate pair" method to represent Unicode characters with lengths exceeding 16 bits. This is a reluctant compromise. On the one hand, in strings containing surrogate pairs, one character may occupy 2 bytes or 4 bytes, thus losing the advantage of fixed-length encoding. On the other hand, handling surrogate pairs requires additional code, which increases the complexity and difficulty of debugging in programming. +Having underestimated the number of characters, these languages had to adopt surrogate pairs for Unicode characters beyond 16 bits. This was a compromise. A character in such a string may occupy 2 or 4 bytes, losing the benefit of fixed width, and handling surrogate pairs requires additional code, making programming and debugging more complex. -For the above reasons, some programming languages have proposed different encoding schemes. +For these reasons, some languages use different representations. -- Python's `str` uses Unicode encoding and adopts a flexible string representation where the stored character length depends on the largest Unicode code point in the string. If all characters in the string are ASCII characters, each character occupies 1 byte; if there are characters exceeding the ASCII range but all within the Basic Multilingual Plane (BMP), each character occupies 2 bytes; if there are characters exceeding the BMP, each character occupies 4 bytes. -- Go language's `string` type uses UTF-8 encoding internally. Go language also provides the `rune` type, which is used to represent a single Unicode code point. -- Rust language's `str` and `String` types use UTF-8 encoding internally. Rust also provides the `char` type for representing a single Unicode code point. +- Python's `str` uses Unicode with a flexible representation whose character width depends on the largest code point in the string. If all characters are ASCII, each uses 1 byte. If some exceed ASCII but all lie within the Basic Multilingual Plane (BMP), each uses 2 bytes. If any lie outside the BMP, each uses 4 bytes. +- Go's `string` uses UTF-8 internally. Go also provides `rune` to represent a single Unicode code point. +- Rust's `str` and `String` use UTF-8 internally, while `char` represents a single Unicode code point. -It should be noted that the above discussion is about how strings are stored in programming languages, **which is different from how strings are stored in files or transmitted over networks**. In file storage or network transmission, we usually encode strings into UTF-8 format to achieve optimal compatibility and space efficiency. +These descriptions concern string storage within programming languages. **How strings are stored in files or transmitted over a network is a separate question**. For file storage and network transmission, strings are usually encoded as UTF-8 for compatibility and space efficiency. diff --git a/en/docs/chapter_data_structure/classification_of_data_structure.md b/en/docs/chapter_data_structure/classification_of_data_structure.md index 5a050683b..bc09fae14 100644 --- a/en/docs/chapter_data_structure/classification_of_data_structure.md +++ b/en/docs/chapter_data_structure/classification_of_data_structure.md @@ -1,48 +1,48 @@ -# Classification of Data Structures +# Classifying data structures -Common data structures include arrays, linked lists, stacks, queues, hash tables, trees, heaps, and graphs. They can be classified from two dimensions: "logical structure" and "physical structure". +Common data structures include arrays, linked lists, stacks, queues, hash tables, trees, heaps, and graphs. They can be classified by their logical structure and physical structure. -## Logical Structure: Linear and Non-Linear +## Logical structure: linear and nonlinear -**Logical structure reveals the logical relationships between data elements**. In arrays and linked lists, data is arranged in a certain order, embodying linear relationships between elements; while in trees, data is arranged hierarchically from top to bottom, showing parent-descendant relationships; graphs are composed of nodes and edges, reflecting complex network relationships. +**Logical structure describes the relationships between data elements**. Arrays and linked lists arrange data in sequence, expressing linear relationships. Trees arrange data hierarchically from top to bottom, expressing ancestry and descent. Graphs consist of vertices and edges that express complex network relationships. -As shown in the figure below, logical structures can be divided into two major categories: "linear" and "non-linear". Linear structures are more intuitive, indicating that data is linearly arranged in logical relationships; non-linear structures are the opposite, arranged non-linearly. +As shown in the figure below, logical structures fall into two broad categories: linear and nonlinear. A linear structure arranges data in a logical sequence; a nonlinear structure does not. -- **Linear data structures**: Arrays, linked lists, stacks, queues, hash tables, where elements have a one-to-one sequential relationship. -- **Non-linear data structures**: Trees, heaps, graphs, hash tables. +- **Linear data structures**: Arrays, linked lists, stacks, queues, and hash tables. Elements have one-to-one sequential relationships. +- **Nonlinear data structures**: Trees, heaps, graphs, and hash tables. -Non-linear data structures can be further divided into tree structures and network structures. +Nonlinear data structures can be divided further into tree and network structures. -- **Tree structures**: Trees, heaps, hash tables, where elements have a one-to-many relationship. -- **Network structures**: Graphs, where elements have a many-to-many relationship. +- **Tree structures**: Trees, heaps, and hash tables, with one-to-many relationships. +- **Network structures**: Graphs, with many-to-many relationships. -![Linear and non-linear data structures](classification_of_data_structure.assets/classification_logic_structure.png) +![Linear and nonlinear data structures](classification_of_data_structure.assets/classification_logic_structure.png) -## Physical Structure: Contiguous and Dispersed +## Physical structure: contiguous and scattered storage -**When an algorithm program runs, the data being processed is mainly stored in memory**. The figure below shows a computer memory stick, where each black square contains a memory space. We can imagine memory as a huge Excel spreadsheet, where each cell can store a certain amount of data. +**While an algorithm runs, the data it processes is mainly stored in memory**. The figure below shows a memory module, with each black chip containing storage space. We can imagine memory as a huge Excel spreadsheet, where each cell holds a certain amount of data. -**The system accesses data at the target location through memory addresses**. As shown in the figure below, the computer assigns a number to each cell in the spreadsheet according to specific rules, ensuring that each memory space has a unique memory address. With these addresses, the program can access data in memory. +**The system uses memory addresses to access data at particular locations**. As shown in the figure below, the computer assigns each cell a number according to specific rules, giving every memory location a unique address. Programs use these addresses to access data. -![Memory stick, memory space, memory address](classification_of_data_structure.assets/computer_memory_location.png) +![Memory module, storage space, and memory addresses](classification_of_data_structure.assets/computer_memory_location.png) !!! tip - It should be noted that comparing memory to an Excel spreadsheet is only a simplified analogy. The actual workings of memory are much more complex, involving concepts such as address space, memory management, cache mechanisms, virtual memory, and physical memory. + The Excel spreadsheet analogy is a simplification. Real memory is more complex and involves address spaces, memory management, caching, virtual memory, and physical memory. -Memory is a shared resource for all programs. When a block of memory is occupied by a program, it usually cannot be used by other programs at the same time. **Therefore, in the design of data structures and algorithms, memory resources are an important consideration**. For example, the peak memory occupied by an algorithm should not exceed the remaining free memory of the system; if there is a lack of contiguous large memory blocks, then the data structure chosen must be able to be stored in dispersed memory spaces. +Memory is a resource used by all programs. When a program occupies a region of memory, other programs usually cannot use that region at the same time. **Memory resources are therefore an important consideration when designing data structures and algorithms**. For example, an algorithm's peak memory usage should not exceed the system's available memory. If no large contiguous region is available, the chosen data structure must support scattered storage. -As shown in the figure below, **physical structure reflects the way data is stored in computer memory**. It can be divided into contiguous-space storage (arrays) and dispersed-space storage (linked lists). At a low level, physical structure determines how data is accessed, updated, inserted, and deleted. These two physical structures exhibit complementary characteristics in terms of time efficiency and space efficiency. +As shown in the figure below, **physical structure describes how data is stored in computer memory**: either contiguously (arrays) or in scattered locations (linked lists). It determines how data can be accessed, updated, inserted, and deleted. The two storage forms have complementary strengths in time and space efficiency. -![Contiguous space storage and dispersed space storage](classification_of_data_structure.assets/classification_phisical_structure.png) +![Contiguous and scattered storage](classification_of_data_structure.assets/classification_phisical_structure.png) -It is worth noting that **all data structures are implemented based on arrays, linked lists, or a combination of both**. For example, stacks and queues can be implemented using either arrays or linked lists; while the implementation of hash tables may include both arrays and linked lists. +**All data structures are implemented using arrays, linked lists, or a combination of the two**. For example, stacks and queues can use either arrays or linked lists, while hash tables may use both. -- **Can be implemented based on arrays**: Stacks, queues, hash tables, trees, heaps, graphs, matrices, tensors (arrays with dimensions $\geq 3$), etc. -- **Can be implemented based on linked lists**: Stacks, queues, hash tables, trees, heaps, graphs, etc. +- **Array-based implementations**: Stacks, queues, hash tables, trees, heaps, graphs, matrices, tensors (arrays with dimension $\geq 3$), and more. +- **Linked-list-based implementations**: Stacks, queues, hash tables, trees, heaps, graphs, and more. -After initialization, linked lists can still adjust their length during program execution, so they are also called "dynamic data structures". After initialization, the length of arrays cannot be changed, so they are also called "static data structures". It is worth noting that arrays can change length by reallocating memory, thus retaining a limited degree of flexibility. +A linked list can change length during execution after initialization, so it is called a dynamic data structure. An array's length is fixed after initialization, so it is called a static data structure. However, reallocating memory can change an array's length, providing a degree of dynamic behavior. !!! tip - If you find it difficult to understand physical structure, it is recommended to read the next chapter first, and then review this section. + If physical structure feels difficult to understand, read the next chapter first and then return to this section. diff --git a/en/docs/chapter_data_structure/exercises.md b/en/docs/chapter_data_structure/exercises.md index 5e611cd53..5ce3abe22 100644 --- a/en/docs/chapter_data_structure/exercises.md +++ b/en/docs/chapter_data_structure/exercises.md @@ -1,86 +1,86 @@ # Exercises -## Concept Review +## Check your understanding -### Data Relationships in Everyday Situations +### Relationships between data in everyday situations -Based on the relationships among the data, choose one of "linear structure," "tree structure," and "network structure" for each situation below, and explain why: +For each situation below, choose a linear, tree, or network structure based on the relationships between the data, and explain your choice: -1. Students stand in a line; we consider only each student's immediate neighbors in front of and behind them. -2. A school is organized in levels: "school → grade → class." -3. City roads connect many intersections. One intersection may lead to several others, and the roads may form cycles. +1. Students stand in a line, each concerned only with the people immediately in front of and behind them. +2. A school is organized in a hierarchy: school → grade → class. +3. City roads connect intersections. An intersection can lead to several others, and the roads may form cycles. -??? success "Answer" +??? success "Reference answer" - 1. This is a linear structure. Except for the first and last students, each person is adjacent to exactly one person in front and one behind, so the relationships extend along a single line. + 1. A linear structure. Apart from the first and last people, each person has exactly one neighbor in front and one behind, forming a line of relationships. - 2. This is a tree structure. Each class belongs to one grade, and each grade belongs to the school, so the relationships form levels from top to bottom. + 2. A tree structure. Each class belongs to a grade, and each grade belongs to the school, forming a hierarchy from top to bottom. - 3. This is a network structure. An intersection can connect to several other intersections, and the routes can form cycles, so they cannot be arranged in a single order or a strict hierarchy. + 3. A network structure. An intersection can connect to several others, and routes can form cycles. These relationships cannot be arranged in a single sequence or a strict hierarchy. - To identify a structure, first examine the relationships among the elements rather than how much space it uses in memory. + To identify a structure, first examine the relationships between its elements, rather than how much memory it occupies. -### Storing a Logical Order in Memory +### Storing a logical sequence in memory -Consider two simplified memory layouts for storing the logical order `A → B → C`: +Consider two simplified memory layouts for storing the logical sequence `A → B → C`: - Layout A: `A, B, C` are stored in memory cells numbered `20, 21, 22`, respectively. -- Layout B: `A, B, C` are stored in memory cells numbered `20, 7, 31`, respectively. `A` records the location of `B`, and `B` records the location of `C`. +- Layout B: `A, B, C` are stored in cells numbered `20, 7, 31`, respectively. `A` records the location of `B`, and `B` records the location of `C`. -1. Which layout uses contiguous-space storage, and which uses dispersed-space storage? +1. Which layout uses contiguous storage, and which uses noncontiguous storage? 2. Which layout is more like an array, and which is more like a linked list? -3. The memory-cell numbers in Layout B are not in increasing order. Why can it still represent the logical order `A → B → C`? +3. The cell numbers in layout B are not in ascending order. Why can it still represent the logical sequence `A → B → C`? -??? success "Answer" +??? success "Reference answer" - 1. Layout A uses consecutive memory cells, so it uses contiguous-space storage. The nodes in Layout B are scattered across different locations, so it uses dispersed-space storage. + 1. Layout A uses consecutive memory cells, so its storage is contiguous. In layout B, nodes occupy separate locations, so its storage is noncontiguous. - 2. Layout A is more like an array, and Layout B is more like a linked list. + 2. Layout A is more like an array; layout B is more like a linked list. - 3. The logical order is determined by the links recorded between nodes, not by the numerical order of the memory-cell numbers. - The location stored by `A` leads to `B`, and the location stored by `B` then leads to `C`, so `A, B, C` can still be visited in order. + 3. The logical order is determined by the links recorded between nodes, not by the numerical order of their memory cell numbers. + The location stored in `A` leads to `B`, and the location stored in `B` leads to `C`, so we can still visit `A, B, C` in sequence. - This also shows that logical structure and physical structure are two different ways of viewing the same data. + This also shows that logical and physical structure are two ways of looking at the same data. -### Data Types and Structures in Homework Records +### Data types and structures in homework records -A study group records, in seating order, whether each of four students submitted their homework: +A study group records whether four students have submitted their homework, in seating order: `[true, false, true, true]` 1. Which basic data type is suitable for each element? -2. The four elements are arranged in a line by seating order. What logical structure does this use? -3. Suppose the group later records each student's homework score as `[90, 0, 85, 100]`. Does this change the data's "content type" or its "organization"? Explain why. +2. These four elements form a sequence in seating order. What logical structure does this use? +3. If the group later records homework scores instead, as `[90, 0, 85, 100]`, has the type of content changed, or the way it is organized? Explain. -??? success "Answer" +??? success "Reference answer" - 1. Each element represents only "yes" or "no," so the Boolean type `bool` is suitable. + 1. Each element represents only yes or no, so the Boolean type `bool` is appropriate. - 2. The elements are arranged in seating order, forming a linear structure that can be stored in an array. + 2. The elements form a linear structure in seating order and can be stored in an array. - 3. The content type changes: the elements change from Boolean values to integers. The organization does not change. - The data is still arranged in a line by seating order and can still be stored in an array, which is a linear structure. + 3. The content type has changed: the elements are now integers instead of Boolean values. Their organization has not changed. + The data still forms a sequence in seating order and can still use the linear structure of an array. - A basic data type describes "what is stored," while a data structure describes "how the data is organized." + A basic data type describes what is stored; a data structure describes how the data is organized. -## Programming Exercises +## Programming exercises -### Count the 1s in a Binary Representation +### Counting ones in a binary representation -Given a non-negative integer `n`, count the number of 1s in its binary representation. +Given a nonnegative integer `n`, count the ones in its binary representation. -Use bitwise operations. Do not convert the binary representation to a string or use a built-in function that directly counts 1s. +Use bitwise operations. Do not convert the binary representation to a string or use a built-in function that counts the ones directly. ??? tip "Hints" - 1. n & 1 extracts the rightmost bit of n, which tells you whether that bit is 1 - 2. Shifting right by one discards the current rightmost bit; most languages use the operator >> - 3. After implementing the method that checks and shifts one bit at a time, observe that n & (n - 1) turns the rightmost 1 in n into 0 + 1. n & 1 extracts the rightmost bit of n, allowing you to check whether it is 1. + 2. Shifting right by one bit discards the current rightmost bit; most languages use the >> operator. + 3. After implementing the method that checks each bit and shifts right, observe that n & (n - 1) changes the rightmost 1 in n to 0. [LeetCode](https://leetcode.com/problems/number-of-1-bits/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_data_structure/index.md b/en/docs/chapter_data_structure/index.md index 2997f6b31..278c5215e 100644 --- a/en/docs/chapter_data_structure/index.md +++ b/en/docs/chapter_data_structure/index.md @@ -1,9 +1,9 @@ -# Data Structures +# Data structures ![Data structures](../assets/covers/chapter_data_structure.jpg) !!! abstract - Data structures are like a sturdy and diverse framework. - - It provides a blueprint for the orderly organization of data, upon which algorithms come to life. + Data structures provide a framework that is both sturdy and versatile. + + They offer a blueprint for organizing data, a foundation on which algorithms come to life. diff --git a/en/docs/chapter_data_structure/number_encoding.md b/en/docs/chapter_data_structure/number_encoding.md index 0b813c9c1..5bb229dc7 100644 --- a/en/docs/chapter_data_structure/number_encoding.md +++ b/en/docs/chapter_data_structure/number_encoding.md @@ -1,24 +1,24 @@ -# Number Encoding * +# Number encoding * !!! tip - In this book, chapters marked with an asterisk * are optional readings. If you are short on time or find them challenging, you may skip these initially and return to them after completing the essential chapters. + Sections marked with * in their titles are optional. If you are short on time or find them difficult, you can skip them initially and return after completing the required sections. -## Sign-Magnitude, 1's Complement, and 2's Complement +## Sign-magnitude, one's complement, and two's complement -In the table from the previous section, we found that all integer types can represent one more negative number than positive numbers. For example, the `byte` range is $[-128, 127]$. This phenomenon is counterintuitive, and its underlying cause lies in sign-magnitude, 1's complement, and 2's complement representations. +The previous table showed that every integer type represents one more negative value than positive value. For example, `byte` ranges over $[-128, 127]$. Understanding this counterintuitive fact requires sign-magnitude, one's complement, and two's complement. -First, it should be noted that **numbers are stored in computers in the form of "2's complement"**. Before analyzing the reasons for this, let's first define these three concepts. +First, **integers are stored in two's complement form**. Before explaining why, let's define the three representations. -- **Sign-magnitude**: We treat the highest bit of the binary representation of a number as the sign bit, where $0$ represents a positive number and $1$ represents a negative number, and the remaining bits represent the value of the number. -- **1's complement**: The 1's complement of a positive number is the same as its sign-magnitude. For a negative number, the 1's complement is obtained by inverting all bits except the sign bit of its sign-magnitude. -- **2's complement**: The 2's complement of a positive number is the same as its sign-magnitude. For a negative number, the 2's complement is obtained by adding $1$ to its 1's complement. +- **Sign-magnitude**: The highest bit is the sign: $0$ for positive and $1$ for negative. The remaining bits represent the magnitude. +- **One's complement**: A positive number has the same representation as sign-magnitude. For a negative number, invert every bit except the sign bit of its sign-magnitude representation. +- **Two's complement**: A positive number again has the same representation as sign-magnitude. For a negative number, add $1$ to its one's complement. -The figure below shows the conversion methods among sign-magnitude, 1's complement, and 2's complement. +The figure below shows how to convert among these representations. -![Conversions among sign-magnitude, 1's complement, and 2's complement](number_encoding.assets/1s_2s_complement.png) +![Converting between sign-magnitude, one's complement, and two's complement](number_encoding.assets/1s_2s_complement.png) -Sign-magnitude, although the most intuitive, has some limitations. On one hand, **the sign-magnitude of negative numbers cannot be directly used in operations**. For example, calculating $1 + (-2)$ in sign-magnitude yields $-3$, which is clearly incorrect. +Although sign-magnitude is intuitive, it has limitations. First, **negative sign-magnitude values cannot be used directly in ordinary binary arithmetic**. For example, directly adding $1 + (-2)$ in this representation gives $-3$, which is clearly wrong. $$ \begin{aligned} @@ -29,20 +29,20 @@ $$ \end{aligned} $$ -To solve this problem, computers introduced 1's complement. If we first convert sign-magnitude to 1's complement and calculate $1 + (-2)$ in 1's complement, then convert the result back to sign-magnitude, we can obtain the correct result of $-1$. +To address this, computers introduced one's complement. Convert the operands to one's complement, calculate $1 + (-2)$, and convert the result back to sign-magnitude to obtain the correct answer, $-1$. $$ \begin{aligned} & 1 + (-2) \newline -& \rightarrow 0000 \; 0001 \; \text{(Sign-magnitude)} + 1000 \; 0010 \; \text{(Sign-magnitude)} \newline -& = 0000 \; 0001 \; \text{(1's complement)} + 1111 \; 1101 \; \text{(1's complement)} \newline -& = 1111 \; 1110 \; \text{(1's complement)} \newline -& = 1000 \; 0001 \; \text{(Sign-magnitude)} \newline +& \rightarrow 0000 \; 0001 \; \text{(sign-magnitude)} + 1000 \; 0010 \; \text{(sign-magnitude)} \newline +& = 0000 \; 0001 \; \text{(one's complement)} + 1111 \; 1101 \; \text{(one's complement)} \newline +& = 1111 \; 1110 \; \text{(one's complement)} \newline +& = 1000 \; 0001 \; \text{(sign-magnitude)} \newline & \rightarrow -1 \end{aligned} $$ -On the other hand, **the sign-magnitude of the number zero has two representations, $+0$ and $-0$**. This means that the number zero corresponds to two different binary encodings, which may cause ambiguity. For example, in conditional judgments, if we don't distinguish between positive zero and negative zero, it may lead to incorrect judgment results. If we want to handle the ambiguity of positive and negative zero, we need to introduce additional judgment operations, which may reduce the computational efficiency of the computer. +Second, **sign-magnitude has two representations of zero, $+0$ and $-0$**. Two binary encodings for zero can create ambiguity. Conditional checks may be wrong if they do not handle both forms, while special handling requires extra checks that can reduce efficiency. $$ \begin{aligned} @@ -51,70 +51,70 @@ $$ \end{aligned} $$ -Like sign-magnitude, 1's complement also has the problem of positive and negative zero ambiguity. Therefore, computers further introduced 2's complement. Let's first observe the conversion process of negative zero from sign-magnitude to 1's complement to 2's complement: +One's complement also has positive and negative zero, so computers introduced two's complement. Consider converting negative zero through all three representations: $$ \begin{aligned} --0 \rightarrow \; & 1000 \; 0000 \; \text{(Sign-magnitude)} \newline -= \; & 1111 \; 1111 \; \text{(1's complement)} \newline -= 1 \; & 0000 \; 0000 \; \text{(2's complement)} \newline +-0 \rightarrow \; & 1000 \; 0000 \; \text{(sign-magnitude)} \newline += \; & 1111 \; 1111 \; \text{(one's complement)} \newline += 1 \; & 0000 \; 0000 \; \text{(two's complement)} \newline \end{aligned} $$ -Adding $1$ to the 1's complement of negative zero produces a carry, but since the `byte` type has a length of only 8 bits, the $1$ that overflows to the 9th bit is discarded. That is to say, **the 2's complement of negative zero is $0000 \; 0000$, which is the same as the 2's complement of positive zero**. This means that in 2's complement representation, there is only one zero, and the positive and negative zero ambiguity is thus resolved. +Adding $1$ to the one's complement of negative zero produces a carry. A `byte` has only 8 bits, so the $1$ carried into the ninth bit is discarded. **Negative zero's two's complement is therefore $0000 \; 0000$, the same as positive zero**. Two's complement has only one zero, eliminating the ambiguity. -One last question remains: the range of the `byte` type is $[-128, 127]$, so where does the extra negative number $-128$ come from? We notice that all integers in the interval $[-127, +127]$ have corresponding sign-magnitude, 1's complement, and 2's complement, and sign-magnitude and 2's complement can be converted to each other. +One question remains: if `byte` ranges over $[-128, 127]$, where does the extra negative value $-128$ come from? Every integer in $[-127, +127]$ has sign-magnitude, one's complement, and two's complement representations, and its sign-magnitude and two's complement forms can be converted into each other. -However, **the 2's complement $1000 \; 0000$ is an exception, and it does not have a corresponding sign-magnitude**. According to the conversion method, we get that the sign-magnitude of this 2's complement is $0000 \; 0000$. This is clearly contradictory because this sign-magnitude represents the number $0$, and its 2's complement should be itself. The computer specifies that this special 2's complement $1000 \; 0000$ represents $-128$. In fact, the result of calculating $(-1) + (-127)$ in 2's complement is $-128$. +However, **the two's complement value $1000 \; 0000$ is an exception with no corresponding sign-magnitude form**. Applying the conversion gives $0000 \; 0000$, but that represents $0$, whose two's complement should be itself. The special pattern $1000 \; 0000$ is defined to represent $-128$. Indeed, two's complement addition of $(-1) + (-127)$ gives $-128$. $$ \begin{aligned} & (-127) + (-1) \newline -& \rightarrow 1111 \; 1111 \; \text{(Sign-magnitude)} + 1000 \; 0001 \; \text{(Sign-magnitude)} \newline -& = 1000 \; 0000 \; \text{(1's complement)} + 1111 \; 1110 \; \text{(1's complement)} \newline -& = 1000 \; 0001 \; \text{(2's complement)} + 1111 \; 1111 \; \text{(2's complement)} \newline -& = 1000 \; 0000 \; \text{(2's complement)} \newline +& \rightarrow 1111 \; 1111 \; \text{(sign-magnitude)} + 1000 \; 0001 \; \text{(sign-magnitude)} \newline +& = 1000 \; 0000 \; \text{(one's complement)} + 1111 \; 1110 \; \text{(one's complement)} \newline +& = 1000 \; 0001 \; \text{(two's complement)} + 1111 \; 1111 \; \text{(two's complement)} \newline +& = 1000 \; 0000 \; \text{(two's complement)} \newline & \rightarrow -128 \end{aligned} $$ -You may have noticed that all the above calculations are addition operations. This hints at an important fact: **the hardware circuits inside computers are mainly designed based on addition operations**. This is because addition operations are simpler to implement in hardware compared to other operations (such as multiplication, division, and subtraction), easier to parallelize, and have faster operation speeds. +All the calculations above use addition. This points to an important fact: **computer arithmetic circuits are largely built around addition**. Compared with operations such as multiplication, division, and subtraction, addition is simpler to implement in hardware, easier to parallelize, and faster. -Please note that this does not mean that computers can only perform addition. **By combining addition with some basic logical operations, computers can implement various other mathematical operations**. For example, calculating the subtraction $a - b$ can be converted to calculating the addition $a + (-b)$; calculating multiplication and division can be converted to calculating multiple additions or subtractions. +This does not mean computers can only add. **Combining addition with basic logical operations makes other arithmetic possible**. For example, subtraction $a - b$ becomes addition $a + (-b)$, while multiplication and division can be expressed as repeated additions or subtractions. -We can now summarize why computers use 2's complement: with 2's complement representation, computers can use the same circuits and operations to handle the addition of positive and negative numbers, without designing special hardware circuits for subtraction or separately handling the ambiguity of positive and negative zero. This greatly simplifies hardware design and improves efficiency. +We can now summarize why computers use two's complement. It lets the same circuitry and operations handle both positive and negative addition, without dedicated subtraction circuitry or special handling for positive and negative zero. This simplifies hardware and improves efficiency. -The design of 2's complement is very ingenious. Due to space limitations, we will stop here. Interested readers are encouraged to explore further. +Two's complement is an elegant design. Space limits our discussion here, but interested readers are encouraged to explore it further. -## Floating-Point Number Encoding +## Floating-point encoding -Careful readers may have noticed: `int` and `float` have the same length, both are 4 bytes, but why does `float` have a much larger range than `int`? This is very counterintuitive because it stands to reason that `float` needs to represent decimals, so the range should be smaller. +Both `int` and `float` occupy 4 bytes, yet `float` has a much wider range. This seems surprising: since `float` also represents fractions, should its range not be smaller? -In fact, **this is because floating-point number `float` uses a different representation method**. Let's denote a 32-bit binary number as: +**The reason is that `float` uses a different representation**. Write a 32-bit binary number as: $$ b_{31} b_{30} b_{29} \ldots b_2 b_1 b_0 $$ -According to the IEEE 754 standard, a 32-bit `float` consists of the following three parts. +Under IEEE 754, a 32-bit `float` has three parts. -- Sign bit $\mathrm{S}$: occupies 1 bit, corresponding to $b_{31}$. -- Exponent bit $\mathrm{E}$: occupies 8 bits, corresponding to $b_{30} b_{29} \ldots b_{23}$. -- Fraction bit $\mathrm{N}$: occupies 23 bits, corresponding to $b_{22} b_{21} \ldots b_0$. +- Sign $\mathrm{S}$: 1 bit, corresponding to $b_{31}$. +- Exponent $\mathrm{E}$: 8 bits, corresponding to $b_{30} b_{29} \ldots b_{23}$. +- Fraction $\mathrm{N}$: 23 bits, corresponding to $b_{22} b_{21} \ldots b_0$. -The calculation method for the value corresponding to the binary `float` is: +The value represented by the binary `float` is: $$ \text {val} = (-1)^{b_{31}} \times 2^{\left(b_{30} b_{29} \ldots b_{23}\right)_2-127} \times\left(1 . b_{22} b_{21} \ldots b_0\right)_2 $$ -Converted to decimal, the calculation formula is: +In decimal notation, this becomes: $$ \text {val}=(-1)^{\mathrm{S}} \times 2^{\mathrm{E} -127} \times (1 + \mathrm{N}) $$ -The range of each component is: +The components have these ranges: $$ \begin{aligned} @@ -123,28 +123,28 @@ $$ \end{aligned} $$ -![Calculation example of float under IEEE 754 standard](number_encoding.assets/ieee_754_float.png) +![Example of an IEEE 754 float calculation](number_encoding.assets/ieee_754_float.png) -Observing the figure above, given example data $\mathrm{S} = 0$, $\mathrm{E} = 124$, $\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$, we have: +In the figure above, $\mathrm{S} = 0$, $\mathrm{E} = 124$, and $\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$, giving: $$ \text { val } = (-1)^0 \times 2^{124 - 127} \times (1 + 0.375) = 0.171875 $$ -Now we can answer the initial question: **the representation of `float` includes an exponent bit, resulting in a range far greater than `int`**. According to the above calculation, the maximum positive number that `float` can represent is $2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}$, and the minimum negative number can be obtained by switching the sign bit. +We can now answer the original question: **the exponent gives `float` a much wider range than `int`**. The largest positive `float` is $2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}$; changing the sign bit gives the most negative value. -**Although floating-point number `float` expands the range, its side effect is sacrificing precision**. The integer type `int` uses all 32 bits to represent numbers, and the numbers are evenly distributed; however, due to the existence of the exponent bit, the larger the value of floating-point number `float`, the larger the difference between two adjacent numbers tends to be. +**The wider range of `float` comes at the cost of precision**. An `int` uses all 32 bits to represent evenly spaced integer values. Because `float` includes an exponent, the gap between adjacent representable values tends to grow as their magnitude increases. -As shown in the table below, exponent bits $\mathrm{E} = 0$ and $\mathrm{E} = 255$ have special meanings, **used to represent zero, infinity, $\mathrm{NaN}$, etc.** +As shown in the table below, exponent values $\mathrm{E} = 0$ and $\mathrm{E} = 255$ have special meanings, **representing zero, infinity, and $\mathrm{NaN}$, among other values**. -

Table   Meaning of exponent bits

+

Table   Meanings of exponent values

-| Exponent Bit E | Fraction Bit $\mathrm{N} = 0$ | Fraction Bit $\mathrm{N} \ne 0$ | Calculation Formula | -| ------------------ | ----------------------------- | ------------------------------- | ---------------------------------------------------------------------- | -| $0$ | $\pm 0$ | Subnormal Number | $(-1)^{\mathrm{S}} \times 2^{-126} \times (0.\mathrm{N})$ | -| $1, 2, \dots, 254$ | Normal Number | Normal Number | $(-1)^{\mathrm{S}} \times 2^{(\mathrm{E} -127)} \times (1.\mathrm{N})$ | -| $255$ | $\pm \infty$ | $\mathrm{NaN}$ | | +| Exponent E | Fraction $\mathrm{N} = 0$ | Fraction $\mathrm{N} \ne 0$ | Formula | +| ------------------ | ----------------------- | ------------------------- | ---------------------------------------------------------------------- | +| $0$ | $\pm 0$ | Subnormal number | $(-1)^{\mathrm{S}} \times 2^{-126} \times (0.\mathrm{N})$ | +| $1, 2, \dots, 254$ | Normal number | Normal number | $(-1)^{\mathrm{S}} \times 2^{(\mathrm{E} -127)} \times (1.\mathrm{N})$ | +| $255$ | $\pm \infty$ | $\mathrm{NaN}$ | | -It is worth noting that subnormal numbers significantly improve the precision of floating-point numbers. The smallest positive normal number is $2^{-126}$, and the smallest positive subnormal number is $2^{-126} \times 2^{-23}$. +Subnormal numbers substantially improve precision near zero. The smallest positive normal number is $2^{-126}$, and the smallest positive subnormal number is $2^{-126} \times 2^{-23}$. -Double-precision `double` also uses a representation method similar to `float`, which will not be elaborated here. +Double-precision `double` uses a similar representation, which we will not detail here. diff --git a/en/docs/chapter_data_structure/summary.md b/en/docs/chapter_data_structure/summary.md index 7e0a82c99..282dadf20 100644 --- a/en/docs/chapter_data_structure/summary.md +++ b/en/docs/chapter_data_structure/summary.md @@ -1,66 +1,66 @@ # Summary -### Key Review +### Key takeaways -- Data structures can be classified from two perspectives: logical structure and physical structure. Logical structure describes the logical relationships between data elements, while physical structure describes how data is stored in computer memory. -- Common logical structures include linear, tree-like, and network structures. We typically classify data structures as linear (arrays, linked lists, stacks, queues) and non-linear (trees, graphs, heaps) based on their logical structure. The implementation of hash tables may involve both linear and non-linear data structures. -- When a program runs, data is stored in computer memory. Each memory space has a corresponding memory address, and the program accesses data through these memory addresses. -- Physical structures are primarily divided into contiguous space storage (arrays) and dispersed space storage (linked lists). All data structures are implemented using arrays, linked lists, or a combination of both. -- Basic data types in computers include integers `byte`, `short`, `int`, `long`, floating-point numbers `float`, `double`, characters `char`, and booleans `bool`. Their value ranges depend on the size of space they occupy and their representation method. -- Sign-magnitude, 1's complement, and 2's complement are three methods for encoding numbers in computers, and they can be converted into each other. The most significant bit of sign-magnitude is the sign bit, and the remaining bits represent the value of the number. -- Integers are stored in computers in 2's complement form. Under 2's complement representation, computers can treat the addition of positive and negative numbers uniformly, without needing to design special hardware circuits for subtraction, and there is no ambiguity of positive and negative zero. -- The encoding of floating-point numbers consists of 1 sign bit, 8 exponent bits, and 23 fraction bits. Due to the exponent bits, the range of floating-point numbers is much larger than that of integers, at the cost of sacrificing precision. -- ASCII is the earliest English character set, with a length of 1 byte, containing a total of 128 characters. GBK is a commonly used Chinese character set, containing over 20,000 Chinese characters. Unicode is committed to providing a complete character set standard, collecting characters from various languages around the world, thereby solving the garbled text problem caused by inconsistent character encoding methods. -- UTF-8 is the most popular Unicode encoding method and has excellent compatibility. It is a variable-length encoding method with good scalability, effectively improving storage space efficiency. UTF-16 and UTF-32 are common Unicode encoding methods. When encoding Chinese characters, UTF-16 occupies less space than UTF-8. Programming languages such as Java and C# use UTF-16 encoding by default. +- Data structures can be classified by logical and physical structure. Logical structure describes relationships between data elements, while physical structure describes how data is stored in memory. +- Common logical structures are linear, tree, and network structures. We usually classify data structures as linear (arrays, linked lists, stacks, queues) or nonlinear (trees, graphs, heaps). A hash table implementation may contain both linear and nonlinear structures. +- During execution, data resides in computer memory. Each location has a memory address, which programs use to access its data. +- The two main physical structures are contiguous storage (arrays) and scattered storage (linked lists). All data structures are implemented using arrays, linked lists, or both. +- Basic data types include integers (`byte`, `short`, `int`, `long`), floating-point numbers (`float`, `double`), characters (`char`), and Booleans (`bool`). Their value ranges depend on storage size and representation. +- Sign-magnitude, one's complement, and two's complement are three ways to encode numbers, and can be converted into one another. In sign-magnitude representation, the highest bit is the sign and the remaining bits give the magnitude. +- Integers are stored in two's complement. This lets the computer handle positive and negative addition uniformly, avoids separate hardware circuitry for subtraction, and eliminates the ambiguity of positive and negative zero. +- A floating-point encoding consists of 1 sign bit, 8 exponent bits, and 23 fraction bits. The exponent provides a much wider range than integers, at the cost of precision. +- ASCII was the earliest English character set. It uses 1 byte and contains 128 characters. GBK is a common Chinese character set with over 20,000 Chinese characters. Unicode aims to provide a comprehensive character standard for the world's languages, avoiding garbled text caused by incompatible encodings. +- UTF-8 is the most popular Unicode encoding and is widely compatible. Its variable length makes it extensible and space-efficient. UTF-16 and UTF-32 use fixed lengths. For Chinese text, UTF-16 uses less space than UTF-8. Languages such as Java and C# use UTF-16 by default. ### Q & A -**Q**: Why do hash tables contain both linear and non-linear data structures? +**Q**: Why can a hash table contain both linear and nonlinear data structures? -The underlying structure of a hash table is an array. To resolve hash collisions, we may use "chaining" (discussed in the subsequent "Hash Collision" section): each bucket in the array points to a linked list, which may be converted to a tree (usually a red-black tree) when the list length exceeds a certain threshold. +A hash table is built on an array. To handle hash collisions, it may use separate chaining, discussed later in the Hash collisions section: each array bucket points to a linked list, which may be converted to a tree (usually a red-black tree) if it grows beyond a threshold. -From a storage perspective, the underlying structure of a hash table is an array, where each bucket slot may contain a value, a linked list, or a tree. Therefore, hash tables may contain both linear data structures (arrays, linked lists) and non-linear data structures (trees). +From a storage perspective, each bucket in the underlying array may contain a value, a linked list, or a tree. A hash table may therefore contain both linear structures (arrays and linked lists) and nonlinear structures (trees). -**Q**: Is the length of the `char` type 1 byte? +**Q**: Does `char` occupy 1 byte? -The length of the `char` type is determined by the encoding method used by the programming language. For example, Java, JavaScript, TypeScript, and C# all use UTF-16 encoding (to store Unicode code points), so the `char` type has a length of 2 bytes. +The size of `char` depends on the programming language's encoding. For example, Java, JavaScript, TypeScript, and C# use UTF-16 to store Unicode code points, so their `char` type occupies 2 bytes. -**Q**: Is there ambiguity in referring to array-based data structures as "static data structures"? Stacks can also perform "dynamic" operations such as push and pop. +**Q**: Is calling array-based structures static misleading? A stack supports dynamic operations such as pushing and popping. -Stacks can indeed implement dynamic data operations, but the data structure is still "static" (fixed length). Although array-based data structures can dynamically add or remove elements, their capacity is fixed. If the data volume exceeds the pre-allocated size, a new larger array needs to be created, and the contents of the old array must be copied to the new array. +A stack does support dynamic data operations, but its underlying structure remains static in length. Although array-based structures can add and remove elements dynamically, their capacity is fixed. When the data exceeds the allocated capacity, a larger array must be created and the old contents copied into it. -**Q**: When constructing a stack (queue), its size is not specified. Why are they "static data structures"? +**Q**: When we create a stack or queue without specifying its size, why is it still called a static data structure? -In high-level programming languages, we do not need to manually specify the initial capacity of a stack (queue); the class handles this automatically. For example, the initial capacity of Java's `ArrayList` is typically 10. Additionally, the expansion operation is also automatically implemented. See the subsequent "List" section for details. +In high-level languages, the class automatically chooses an initial capacity for a stack or queue. For example, Java's `ArrayList` typically starts with a capacity of 10. Resizing is also automatic; see the later section on lists. -**Q**: The method of converting sign-magnitude to 2's complement is "first negate then add 1". So converting 2's complement to sign-magnitude should be the inverse operation "first subtract 1 then negate". However, 2's complement can also be converted to sign-magnitude through "first negate then add 1". Why is this? +**Q**: Converting sign-magnitude to two's complement uses "invert, then add 1". Its inverse should be "subtract 1, then invert", so why does "invert, then add 1" also work in the reverse direction? -This is because the mutual conversion between sign-magnitude and 2's complement is actually the process of computing the "complement". Let us first define the complement: assuming $a + b = c$, then we say that $a$ is the complement of $b$ to $c$, and conversely, $b$ is the complement of $a$ to $c$. +These conversions are really computations of a complement. If $a + b = c$, we call $a$ the complement of $b$ with respect to $c$, and conversely $b$ the complement of $a$ with respect to $c$. -Given an $n = 4$ bit binary number $0010$, if we treat this number as sign-magnitude (ignoring the sign bit), then its 2's complement can be obtained through "first negate then add 1": +Consider the $n = 4$-bit binary number $0010$. Treating it as sign-magnitude and disregarding the sign bit, we obtain its two's complement by inverting and adding 1: $$ 0010 \rightarrow 1101 \rightarrow 1110 $$ -We find that the sum of sign-magnitude and 2's complement is $0010 + 1110 = 10000$, which means the 2's complement $1110$ is the "complement" of sign-magnitude $0010$ to $10000$. **This means the above "first negate then add 1" is actually the process of computing the complement to $10000$**. +The two representations sum to $0010 + 1110 = 10000$. Thus, $1110$ is the complement of $0010$ with respect to $10000$. **The "invert, then add 1" operation computes the complement with respect to $10000$**. -So, what is the "complement" of 2's complement $1110$ to $10000$? We can still use "first negate then add 1" to obtain it: +What is the complement of $1110$ with respect to $10000$? We can again invert and add 1: $$ 1110 \rightarrow 0001 \rightarrow 0010 $$ -In other words, sign-magnitude and 2's complement are each other's "complement" to $10000$, so "sign-magnitude to 2's complement" and "2's complement to sign-magnitude" can be implemented using the same operation (first negate then add 1). +The two representations are complements of each other with respect to $10000$, so conversion in either direction can use the same operation: invert, then add 1. -Of course, we can also use the inverse operation to find the sign-magnitude of 2's complement $1110$, that is, "first subtract 1 then negate": +Of course, we can also recover the sign-magnitude form of $1110$ by the inverse operation: subtract 1, then invert. $$ 1110 \rightarrow 1101 \rightarrow 0010 $$ -In summary, both "first negate then add 1" and "first subtract 1 then negate" are computing the complement to $10000$, and they are equivalent. +Both "invert, then add 1" and "subtract 1, then invert" compute the complement with respect to $10000$, so they are equivalent. -Essentially, the "negate" operation is actually finding the complement to $1111$ (because "sign-magnitude + 1's complement = 1111" always holds); and adding 1 to the 1's complement yields the 2's complement, which is the complement to $10000$. +In essence, inverting computes the complement with respect to $1111$, since `sign-magnitude + one's complement = 1111`. Adding 1 to the one's complement then gives the complement with respect to $10000$. -The above uses $n = 4$ as an example, and it can be generalized to binary numbers of any number of bits. +The example uses $n = 4$, but the reasoning extends to binary numbers of any bit width. diff --git a/en/docs/chapter_divide_and_conquer/binary_search_recur.md b/en/docs/chapter_divide_and_conquer/binary_search_recur.md index 969a2d6be..adfb65c37 100644 --- a/en/docs/chapter_divide_and_conquer/binary_search_recur.md +++ b/en/docs/chapter_divide_and_conquer/binary_search_recur.md @@ -1,44 +1,44 @@ -# Divide and Conquer Search Strategy +# Divide-and-conquer search -We have already learned that search algorithms are divided into two major categories. +We have learned that search algorithms fall into two broad categories. -- **Brute-force search**: Implemented by traversing the data structure, with a time complexity of $O(n)$. -- **Adaptive search**: Leverages specific data organization or prior information, with time complexity reaching $O(\log n)$ or even $O(1)$. +- **Brute-force search**: Traverses a data structure, taking $O(n)$ time. +- **Adaptive search**: Uses a particular organization of data or prior information to achieve $O(\log n)$ or even $O(1)$ time. -In fact, **search algorithms with time complexity of $O(\log n)$ are typically implemented based on the divide and conquer strategy**, such as binary search and trees. +In fact, **search algorithms that run in $O(\log n)$ time are usually based on divide and conquer**, as seen in binary search and trees. -- Each step of binary search divides the problem (searching for a target element in an array) into a smaller problem (searching for the target element in half of the array), continuing until the array is empty or the target element is found. -- Trees are representative of the divide and conquer idea. In data structures such as binary search trees, AVL trees, and heaps, the time complexity of various operations is $O(\log n)$. +- Each step of binary search reduces the problem of finding a target in an array to a smaller problem of finding it in half of the array. This continues until the array is empty or the target is found. +- Trees exemplify divide and conquer. In data structures such as binary search trees, AVL trees, and heaps, various operations take $O(\log n)$ time. -The divide and conquer strategy of binary search is as follows. +Binary search uses divide and conquer in the following way. -- **The problem can be decomposed**: Binary search recursively decomposes the original problem (searching in an array) into subproblems (searching in half of the array), achieved by comparing the middle element with the target element. -- **Subproblems are independent**: In binary search, each round only processes one subproblem, which is not affected by other subproblems. -- **Solutions of subproblems do not need to be merged**: Binary search aims to find a specific element, so there is no need to merge the solutions of subproblems. When a subproblem is solved, the original problem is also solved. +- **The problem can be divided**: By comparing the middle element with the target, binary search recursively reduces the original problem of searching an array to the subproblem of searching half of that array. +- **The subproblems are independent**: Binary search handles only one subproblem at each step, unaffected by other subproblems. +- **The subproblem solutions need not be combined**: Binary search seeks a particular element, so there is no need to combine solutions. Solving the subproblem also solves the original problem. -Divide and conquer can improve search efficiency because brute-force search can only eliminate one option per round, **while divide and conquer search can eliminate half of the options per round**. +Divide and conquer makes searching faster because brute-force search eliminates only one option per step, **whereas divide-and-conquer search can eliminate half the options**. -### Implementing Binary Search Based on Divide and Conquer +### Implementing binary search with divide and conquer -In previous sections, binary search was implemented based on iteration. Now we implement it based on divide and conquer (recursion). +Earlier chapters implemented binary search iteratively. We will now implement it recursively using divide and conquer. !!! question - Given a sorted array `nums` of length $n$, where all elements are unique, find `target`. + Given a sorted array `nums` of length $n$ with distinct elements, find the element `target`. -From a divide and conquer perspective, we denote the subproblem corresponding to the search interval $[i, j]$ as $f(i, j)$. +From a divide-and-conquer perspective, let $f(i, j)$ denote the subproblem for the search interval $[i, j]$. -Starting from the original problem $f(0, n-1)$, perform binary search through the following steps. +Starting with the original problem $f(0, n-1)$, perform binary search as follows. -1. Calculate the midpoint $m$ of the search interval $[i, j]$, and use it to eliminate half of the search interval. -2. Recursively solve the subproblem reduced by half in size, which could be $f(i, m-1)$ or $f(m+1, j)$. -3. Repeat steps `1.` and `2.` until `target` is found, or return when the interval is empty. +1. Calculate the midpoint $m$ of the search interval $[i, j]$ and use it to eliminate half the interval. +2. Recursively solve the subproblem of half the size, either $f(i, m-1)$ or $f(m+1, j)$. +3. Repeat steps `1.` and `2.`, returning when `target` is found or the interval is empty. -The figure below shows the divide and conquer process of binary search for element $6$ in an array. +The figure below shows the divide-and-conquer process for finding $6$ in an array using binary search. -![Divide and conquer process of binary search](binary_search_recur.assets/binary_search_recur.png) +![Divide-and-conquer process for binary search](binary_search_recur.assets/binary_search_recur.png) -In the implementation code, we declare a recursive function `dfs()` to solve the problem $f(i, j)$: +In the implementation, we define a recursive function `dfs()` to solve $f(i, j)$: ```src [file]{binary_search_recur}-[class]{}-[func]{binary_search} diff --git a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index 89beab368..5c5200760 100644 --- a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -1,70 +1,70 @@ -# Building a Binary Tree Problem +# Building a binary tree !!! question - Given the preorder traversal `preorder` and inorder traversal `inorder` of a binary tree, construct the binary tree and return the root node of the binary tree. Assume there are no duplicate node values in the binary tree (as shown in the figure below). + Given a binary tree's preorder traversal `preorder` and inorder traversal `inorder`, reconstruct the tree and return its root node. Assume that all node values are distinct, as shown in the figure below. -![Example data for building a binary tree](build_binary_tree_problem.assets/build_tree_example.png) +![Example input for reconstructing a binary tree](build_binary_tree_problem.assets/build_tree_example.png) -### Determining If It Is a Divide and Conquer Problem +### Identifying a divide-and-conquer problem -The original problem is defined as constructing a binary tree from `preorder` and `inorder`, which is a typical divide and conquer problem. +Reconstructing a binary tree from `preorder` and `inorder` is a typical divide-and-conquer problem. -- **The problem can be decomposed**: From a divide and conquer perspective, we can divide the original problem into two subproblems: constructing the left subtree and constructing the right subtree, plus one operation: initializing the root node. For each subtree (subproblem), we can still reuse the above division method, dividing it into smaller subtrees (subproblems) until the smallest subproblem (empty subtree) is reached. -- **Subproblems are independent**: The left and right subtrees are independent of each other; there is no overlap between them. When constructing the left subtree, we only need to focus on the parts of the inorder and preorder traversals corresponding to the left subtree. The same applies to the right subtree. -- **Solutions of subproblems can be merged**: Once we have the left and right subtrees (solutions of subproblems), we can link them to the root node to obtain the solution to the original problem. +- **The problem can be divided**: We can split the original problem into two subproblems, building the left subtree and building the right subtree, plus one operation: initializing the root node. Each subtree can be divided in the same way into smaller subtrees, until we reach the smallest subproblem, an empty subtree. +- **The subproblems are independent**: The left and right subtrees are independent and do not overlap. To build the left subtree, we need only the portions of the preorder and inorder traversals corresponding to it. The same applies to the right subtree. +- **The subproblem solutions can be combined**: Once we have built the left and right subtrees, we can attach them to the root to obtain the solution to the original problem. -### How to Divide Subtrees +### Dividing the subtrees -Based on the above analysis, this problem can be solved using divide and conquer, **but how do we divide the left and right subtrees through the preorder traversal `preorder` and inorder traversal `inorder`**? +The analysis above shows that divide and conquer applies, **but how do we use `preorder` and `inorder` to separate the left and right subtrees**? -According to the definition, both `preorder` and `inorder` can be divided into three parts. +By definition, both `preorder` and `inorder` consist of three parts. -- Preorder traversal: `[ Root Node | Left Subtree | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 3 | 9 | 2 1 7 ]`. -- Inorder traversal: `[ Left Subtree | Root Node | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 9 | 3 | 1 2 7 ]`. +- Preorder: `[ root | left subtree | right subtree ]`. For the tree above, this is `[ 3 | 9 | 2 1 7 ]`. +- Inorder: `[ left subtree | root | right subtree ]`. For the tree above, this is `[ 9 | 3 | 1 2 7 ]`. -Using the data from the figure above as an example, we can obtain the division results through the steps shown in the figure below. +Using the example above, we can divide the traversals with the steps shown in the figure below. -1. The first element 3 in the preorder traversal is the value of the root node. -2. Find the index of root node 3 in `inorder`, and use this index to divide `inorder` into `[ 9 | 3 | 1 2 7 ]`. -3. Based on the division result of `inorder`, it is easy to determine that the left and right subtrees have 1 and 3 nodes respectively, allowing us to divide `preorder` into `[ 3 | 9 | 2 1 7 ]`. +1. The first element in the preorder traversal, 3, is the root value. +2. Find the index of root value 3 in `inorder`, then use it to divide `inorder` into `[ 9 | 3 | 1 2 7 ]`. +3. This division of `inorder` tells us that the left and right subtrees have 1 and 3 nodes, respectively, allowing us to divide `preorder` into `[ 3 | 9 | 2 1 7 ]`. -![Dividing subtrees in preorder and inorder traversals](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) +![Separating subtrees in preorder and inorder traversals](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) -### Describing Subtree Intervals Based on Variables +### Describing subtree intervals with variables -Based on the above division method, **we have obtained the index intervals of the root node, left subtree, and right subtree in `preorder` and `inorder`**. To describe these index intervals, we need to use several index variables. +Using this method, **we have identified the index intervals for the root, left subtree, and right subtree in `preorder` and `inorder`**. We need a few pointer variables to describe these intervals. -- Denote the index of the current tree's root node in `preorder` as $i$. -- Denote the index of the current tree's root node in `inorder` as $m$. -- Denote the index interval of the current tree in `inorder` as $[l, r]$. +- Let $i$ be the index of the current tree's root in `preorder`. +- Let $m$ be the index of the current tree's root in `inorder`. +- Let $[l, r]$ be the interval occupied by the current tree in `inorder`. -As shown in the table below, through these variables we can represent the index of the root node in `preorder` and the index intervals of the subtrees in `inorder`. +As shown in the table below, these variables describe the root index in `preorder` and each subtree's interval in `inorder`. -

Table   Indices of root node and subtrees in preorder and inorder traversals

+

Table   Root and subtree indices in preorder and inorder traversals

-| | Root node index in `preorder` | Subtree index interval in `inorder` | -| ------------ | ----------------------------- | ----------------------------------- | -| Current tree | $i$ | $[l, r]$ | -| Left subtree | $i + 1$ | $[l, m-1]$ | -| Right subtree| $i + 1 + (m - l)$ | $[m+1, r]$ | +| | Root index in `preorder` | Subtree interval in `inorder` | +| ------ | ---------------------------- | ----------------------------- | +| Current tree | $i$ | $[l, r]$ | +| Left subtree | $i + 1$ | $[l, m-1]$ | +| Right subtree | $i + 1 + (m - l)$ | $[m+1, r]$ | -Please note that $(m-l)$ in the right subtree root node index means "the number of nodes in the left subtree". It is recommended to understand this in conjunction with the figure below. +The term $(m-l)$ in the right subtree's root index is the number of nodes in the left subtree. The figure below helps illustrate this. -![Index interval representation of root node and left and right subtrees](build_binary_tree_problem.assets/build_tree_division_pointers.png) +![Index intervals for the root and the left and right subtrees](build_binary_tree_problem.assets/build_tree_division_pointers.png) -### Code Implementation +### Code implementation -To improve the efficiency of querying $m$, we use a hash table `hmap` to store the mapping from elements in the `inorder` array to their indices: +To find $m$ efficiently, we use a hash table `hmap` mapping each element of `inorder` to its index: ```src [file]{build_tree}-[class]{}-[func]{build_tree} ``` -The figure below shows the recursive process of building the binary tree. Each node is established during the downward "recursion" process, while each edge (reference) is established during the upward "return" process. +The figure below shows the recursive reconstruction process. Nodes are created during recursive descent, while edges (references) are established as the recursive calls return. === "<1>" - ![Recursive process of building a binary tree](build_binary_tree_problem.assets/built_tree_step1.png) + ![Recursive process for building a binary tree](build_binary_tree_problem.assets/built_tree_step1.png) === "<2>" ![built_tree_step2](build_binary_tree_problem.assets/built_tree_step2.png) @@ -90,10 +90,10 @@ The figure below shows the recursive process of building the binary tree. Each n === "<9>" ![built_tree_step9](build_binary_tree_problem.assets/built_tree_step9.png) -The division results of the preorder traversal `preorder` and inorder traversal `inorder` within each recursive function are shown in the figure below. +The figure below shows how `preorder` and `inorder` are divided in each recursive call. -![Division results in each recursive function](build_binary_tree_problem.assets/built_tree_overall.png) +![Division within each recursive call](build_binary_tree_problem.assets/built_tree_overall.png) -Let the number of nodes in the tree be $n$. Initializing each node (executing one recursive function `dfs()`) takes $O(1)$ time. **Therefore, the overall time complexity is $O(n)$**. +For a tree with $n$ nodes, initializing each node (executing one call to `dfs()`) takes $O(1)$ time. **The overall time complexity is therefore $O(n)$**. -The hash table stores the mapping from `inorder` elements to their indices, with a space complexity of $O(n)$. In the worst case, when the binary tree degenerates into a linked list, the recursion depth reaches $n$, using $O(n)$ stack frame space. **Therefore, the overall space complexity is $O(n)$**. +The hash table maps `inorder` elements to indices, requiring $O(n)$ space. In the worst case, the binary tree degenerates into a linked list, and the recursion depth reaches $n$, using $O(n)$ space for stack frames. **The overall space complexity is therefore $O(n)$**. diff --git a/en/docs/chapter_divide_and_conquer/divide_and_conquer.md b/en/docs/chapter_divide_and_conquer/divide_and_conquer.md index c52a16514..83d3d4475 100644 --- a/en/docs/chapter_divide_and_conquer/divide_and_conquer.md +++ b/en/docs/chapter_divide_and_conquer/divide_and_conquer.md @@ -1,48 +1,48 @@ -# Divide and Conquer Algorithms +# Divide and conquer -Divide and conquer is a very important and common algorithmic strategy. Divide and conquer is typically implemented based on recursion, consisting of two steps: "divide" and "conquer". +Divide and conquer is a fundamental and widely used algorithmic strategy. Usually implemented recursively, it has two stages: dividing the problem and combining its solutions. -1. **Divide (partition phase)**: Recursively divide the original problem into two or more subproblems until the smallest subproblem is reached. -2. **Conquer (merge phase)**: Starting from the smallest subproblems with known solutions, merge the solutions of subproblems from bottom to top to construct the solution to the original problem. +1. **Divide (the partitioning stage)**: Recursively split the original problem into two or more subproblems until the smallest subproblems are reached. +2. **Conquer (the combining stage)**: Starting with the known solutions to the smallest subproblems, combine solutions from the bottom up to construct the solution to the original problem. -As shown in the figure below, "merge sort" is one of the typical applications of the divide and conquer strategy. +As shown in the figure below, merge sort is a classic application of divide and conquer. -1. **Divide**: Recursively divide the original array (original problem) into two subarrays (subproblems) until the subarray has only one element (smallest subproblem). -2. **Conquer**: Merge the sorted subarrays (solutions to subproblems) from bottom to top to obtain a sorted original array (solution to the original problem). +1. **Divide**: Recursively split the original array (the original problem) into two subarrays (subproblems) until each subarray contains just one element (the smallest subproblem). +2. **Conquer**: Merge the sorted subarrays (subproblem solutions) from the bottom up to obtain the sorted original array (the solution to the original problem). -![Divide and conquer strategy of merge sort](divide_and_conquer.assets/divide_and_conquer_merge_sort.png) +![Divide-and-conquer strategy in merge sort](divide_and_conquer.assets/divide_and_conquer_merge_sort.png) -## How to Determine Divide and Conquer Problems +## Recognizing divide-and-conquer problems -Whether a problem is suitable for solving with divide and conquer can usually be determined based on the following criteria. +The following criteria usually help determine whether divide and conquer is suitable for a problem. -1. **The problem can be decomposed**: The original problem can be divided into smaller, similar subproblems, and can be recursively divided in the same way. -2. **Subproblems are independent**: There is no overlap between subproblems, they are independent of each other and can be solved independently. -3. **Solutions of subproblems can be merged**: The solution to the original problem is obtained by merging the solutions of subproblems. +1. **The problem can be divided**: The original problem can be split into smaller, similar subproblems, which can in turn be divided recursively in the same way. +2. **The subproblems are independent**: They neither overlap nor depend on one another, so they can be solved independently. +3. **The subproblem solutions can be combined**: Combining them yields the solution to the original problem. -Clearly, merge sort satisfies these three criteria. +Merge sort clearly satisfies all three criteria. -1. **The problem can be decomposed**: Recursively divide the array (original problem) into two subarrays (subproblems). -2. **Subproblems are independent**: Each subarray can be sorted independently (subproblems can be solved independently). -3. **Solutions of subproblems can be merged**: Two sorted subarrays (solutions of subproblems) can be merged into one sorted array (solution of the original problem). +1. **The problem can be divided**: Recursively split an array (the original problem) into two subarrays (subproblems). +2. **The subproblems are independent**: Each subarray can be sorted independently. +3. **The subproblem solutions can be combined**: Merge the two sorted subarrays (subproblem solutions) into a single sorted array (the solution to the original problem). -## Improving Efficiency Through Divide and Conquer +## Improving efficiency with divide and conquer -**Divide and conquer can not only effectively solve algorithmic problems, but can often also improve algorithmic efficiency**. In sorting algorithms, quick sort, merge sort, and heap sort are faster than selection, bubble, and insertion sort because they apply the divide and conquer strategy. +**Divide and conquer not only solves algorithmic problems effectively, but often improves efficiency as well**. Quick sort, merge sort, and heap sort outperform selection sort, bubble sort, and insertion sort because they use this strategy. -This raises the question: **Why can divide and conquer improve algorithm efficiency, and what is the underlying logic**? In other words, why is dividing a large problem into multiple subproblems, solving the subproblems, and merging their solutions more efficient than directly solving the original problem? This question can be discussed from two aspects: operation count and parallel computation. +This raises a question: **why does divide and conquer improve efficiency, and what is the underlying reason**? Why is dividing a large problem, solving its subproblems, and combining their solutions faster than solving it directly? We can examine this from two perspectives: the number of operations and parallel computation. -### Operation Count Optimization +### Reducing the number of operations -Taking "bubble sort" as an example, processing an array of length $n$ requires $O(n^2)$ time. Suppose we divide the array at the midpoint into two subarrays, as shown in the figure below. The division requires $O(n)$ time, sorting each subarray requires $O((n / 2)^2)$ time, and merging the two subarrays requires $O(n)$ time, resulting in an overall time complexity of: +Consider bubble sort, which takes $O(n^2)$ time to sort an array of length $n$. Suppose we split the array at its midpoint, as shown in the figure below. Splitting takes $O(n)$ time, sorting each half takes $O((n / 2)^2)$ time, and merging the halves takes $O(n)$ time. The total time complexity is: $$ O(n + (\frac{n}{2})^2 \times 2 + n) = O(\frac{n^2}{2} + 2n) $$ -![Bubble sort before and after array division](divide_and_conquer.assets/divide_and_conquer_bubble_sort.png) +![Bubble sort before and after dividing the array](divide_and_conquer.assets/divide_and_conquer_bubble_sort.png) -Next, we compute the following inequality, where the left and right sides represent the total number of operations before and after division, respectively: +Now consider the following inequality, whose left and right sides represent the total operation counts before and after division: $$ \begin{aligned} @@ -52,40 +52,40 @@ n(n - 4) & > 0 \end{aligned} $$ -**This means that when $n > 4$, the number of operations after division is smaller, and sorting efficiency should be higher**. Note that the time complexity after division is still quadratic $O(n^2)$, but the constant term in the complexity has become smaller. +**When $n > 4$, dividing the array reduces the number of operations and should make sorting faster**. The time complexity is still quadratic, $O(n^2)$; only the constant factor has decreased. -Going further, **what if we continuously divide the subarrays from their midpoints into two subarrays** until the subarrays have only one element? This approach is actually "merge sort", with a time complexity of $O(n \log n)$. +What if **we keep splitting each subarray into two at its midpoint** until every subarray contains just one element? This is precisely the idea behind merge sort, which takes $O(n \log n)$ time. -Thinking further, **what if we set multiple division points** and evenly divide the original array into $k$ subarrays? This situation is very similar to "bucket sort", which is well-suited for sorting massive amounts of data, with a theoretical time complexity of $O(n + k)$. +What if **we introduce more split points**, dividing the original array evenly into $k$ subarrays? This resembles bucket sort, which is well suited to sorting large amounts of data and can theoretically achieve $O(n + k)$ time. -### Parallel Computation Optimization +### Using parallel computation -We know that the subproblems generated by divide and conquer are independent of each other, **so they can typically be solved in parallel**. This means divide and conquer can not only reduce the time complexity of algorithms, **but is also amenable to parallel optimization by the operating system**. +The subproblems produced by divide and conquer are independent, **so they can usually be solved in parallel**. Thus, divide and conquer can both reduce time complexity and **help the operating system parallelize the work**. -Parallel optimization is particularly effective in multi-core or multi-processor environments, as the system can simultaneously handle multiple subproblems, making fuller use of computing resources and significantly reducing overall runtime. +Parallelization is especially effective on multicore or multiprocessor systems. Multiple subproblems can be processed simultaneously, making fuller use of computing resources and substantially reducing total runtime. -For example, in the "bucket sort" shown in the figure below, we evenly distribute massive data into various buckets, and the sorting tasks for all buckets can be distributed to various computing units. After completion, the results are merged. +For example, in the bucket sort shown in the figure below, a large dataset is evenly distributed among buckets. The sorting tasks for different buckets can then be assigned to different computing units, and their results combined afterward. ![Parallel computation in bucket sort](divide_and_conquer.assets/divide_and_conquer_parallel_computing.png) -## Common Applications of Divide and Conquer +## Common applications of divide and conquer -On the one hand, divide and conquer can be used to solve many classic algorithmic problems. +Divide and conquer solves many classic algorithmic problems. -- **Finding the closest pair of points**: This algorithm first divides the point set into two parts, then finds the closest pair of points in each part separately, and finally finds the closest pair of points that spans both parts. -- **Large integer multiplication**: For example, the Karatsuba algorithm, which decomposes large integer multiplication into several smaller integer multiplications and additions. -- **Matrix multiplication**: For example, the Strassen algorithm, which decomposes large matrix multiplication into multiple small matrix multiplications and additions. -- **Tower of Hanoi problem**: The Tower of Hanoi problem can be solved through recursion, which is a typical application of the divide and conquer strategy. -- **Solving inversion pairs**: In a sequence, if a preceding number is greater than a following number, these two numbers form an inversion pair. Solving the inversion pair problem can utilize the divide and conquer approach with the help of merge sort. +- **Closest pair of points**: Divide a set of points into two parts, find the closest pair within each part, then find the closest pair spanning the two parts. +- **Large integer multiplication**: Karatsuba's algorithm, for example, breaks multiplication of large integers into several smaller integer multiplications and additions. +- **Matrix multiplication**: Strassen's algorithm, for example, breaks multiplication of large matrices into multiple smaller matrix multiplications and additions. +- **Tower of Hanoi**: This problem can be solved recursively and is a classic application of divide and conquer. +- **Counting inversions**: In a sequence, an earlier number and a later number form an inversion if the earlier one is larger. Inversions can be counted using divide and conquer with merge sort. -On the other hand, divide and conquer is widely applied in the design of algorithms and data structures. +Divide and conquer is also widely used in designing algorithms and data structures. -- **Binary search**: Binary search divides a sorted array into two parts from the midpoint index, then decides which half to eliminate based on the comparison result between the target value and the middle element value, and performs the same binary-search step on the remaining interval. -- **Merge sort**: Already introduced at the beginning of this section, no further elaboration needed. -- **Quick sort**: Quick sort selects a pivot value, then divides the array into two subarrays, one with elements smaller than the pivot and the other with elements larger than the pivot, then performs the same division operation on these two parts until the subarrays have only one element. -- **Bucket sort**: The basic idea of bucket sort is to scatter data into multiple buckets, then sort the elements within each bucket, and finally extract the elements from each bucket in sequence to obtain a sorted array. -- **Trees**: For example, binary search trees, AVL trees, red-black trees, B-trees, B+ trees, etc. Their search, insertion, and deletion operations can all be viewed as applications of the divide and conquer strategy. -- **Heaps**: A heap is a special complete binary tree, and its various operations, such as insertion, deletion, and heapify, actually imply the divide and conquer idea. -- **Hash tables**: Although hash tables do not directly apply divide and conquer, some methods for resolving hash collisions indirectly apply the divide and conquer strategy. For example, long linked lists in chaining may be converted to red-black trees to improve lookup efficiency. +- **Binary search**: Split a sorted array at its middle index, compare the middle element with the target to decide which half to discard, then repeat the same operation on the remaining interval. +- **Merge sort**: This was described at the start of the section. +- **Quick sort**: Choose a pivot and divide the array into two subarrays, one containing elements smaller than the pivot and the other containing larger elements. Repeat the division on both parts until each subarray contains just one element. +- **Bucket sort**: Distribute data among multiple buckets, sort the elements in each bucket, and then take the elements from the buckets in order to obtain a sorted array. +- **Trees**: Operations such as search, insertion, and deletion in binary search trees, AVL trees, red-black trees, B-trees, and B+ trees can be viewed as applications of divide and conquer. +- **Heaps**: A heap is a special kind of complete binary tree. Its operations, such as insertion, deletion, and heapification, implicitly use divide and conquer. +- **Hash tables**: Although hash tables do not directly use divide and conquer, some collision-handling strategies use it indirectly. For example, long linked lists in separate chaining may be converted into red-black trees to speed up lookups. -It can be seen that **divide and conquer is a "quietly pervasive" algorithmic idea**, embedded in various algorithms and data structures. +As we can see, **divide and conquer quietly underlies many algorithms and data structures**, often without being explicitly named. diff --git a/en/docs/chapter_divide_and_conquer/exercises.md b/en/docs/chapter_divide_and_conquer/exercises.md index 1cdab1fa5..771753d63 100644 --- a/en/docs/chapter_divide_and_conquer/exercises.md +++ b/en/docs/chapter_divide_and_conquer/exercises.md @@ -1,90 +1,90 @@ # Exercises -## Concept Review +## Check your understanding -### Which Tasks Are Suitable for Divide and Conquer? +### Which tasks suit divide and conquer? -A student wants to solve each task below by "dividing it into two halves, solving each half separately, and then combining the results." -Classify each task as "suitable for divide and conquer," "can use divide and conquer, but it will not reduce the total work," or "the two halves cannot be solved independently," and explain why. +A student wants to solve the following tasks by splitting each in half, solving the halves separately, and combining the results. +For each task, decide whether divide and conquer is suitable, is possible but does not reduce the total work, or is unsuitable because the halves cannot be solved independently. Explain. 1. Sort an unsorted array. 2. Find the maximum value in an array. -3. Execute a sequence of `push(x)` and `pop()` stack operations in order, and output the element returned by each `pop()`. +3. Execute a sequence of stack operations `push(x)` and `pop()` in order, outputting the element returned by each `pop()`. -??? success "Answer" +??? success "Reference answer" - 1. Suitable: divide the array in half, sort each half independently, and merge them in $O(n)$ time. This is exactly merge sort. - 2. Divide and conquer can be used, but it does not reduce the total work. The two halves still require examining all $n$ elements in total, - so the time complexity remains $O(n)$, just like a direct scan. - 3. The two halves cannot be solved independently. The stack's contents at the beginning of the second half depend on the result of executing the first half, - so the two halves cannot be completed without knowing each other's results. + 1. Suitable: split in half, sort the halves independently, and merge in $O(n)$ time. This is exactly merge sort. + 2. Possible, but without reducing the total work: the two halves must still examine all $n$ elements between them, + taking $O(n)$ time, just like a direct scan. + 3. The halves cannot be solved independently: the stack contents at the start of the second half depend on the results of the first half. + The second half therefore cannot run independently of the first half's result. -### How Exponentiation by Squaring Reduces Computation +### How fast exponentiation reduces computation -The recursive function below uses divide and conquer to calculate $x^n$: +The following recursive function uses divide and conquer to compute $x^n$: ```src [file]{fast_power}-[class]{}-[func]{fast_pow} ``` -Set `x = 3` and `n = 5`, and use this function to calculate the result: +Run it with `x = 3` and `n = 5`: -1. As the recursive calls proceed, which values does the argument `n` take in order? -2. Starting from the deepest call, what value does each level return? -3. Why should the recursive result be stored in `half` instead of calling the same subproblem once on each side of the multiplication? +1. What values does `n` take in successive recursive calls? +2. Starting with the deepest call, what value does each call return? +3. Why should the recursive result be saved in `half` instead of making the same recursive call on each side of the multiplication? -??? success "Answer" +??? success "Reference answer" - 1. The argument takes the values `5 → 2 → 1 → 0`. The exponent is halved at each step until the base case is reached. + 1. The parameter follows `5 → 2 → 1 → 0`, halving the exponent each time until the base case is reached. - 2. When `n = 0`, the function returns 1. When `n = 1`, it returns $1×1×3=3$. - When `n = 2`, it returns $3×3=9$. When `n = 5`, it returns $9×9×3=243$. + 2. When `n = 0`, the return value is 1. When `n = 1`, it is $1×1×3=3$. + When `n = 2`, it is $3×3=9$; when `n = 5`, it is $9×9×3=243$. - 3. If the same subproblem were called once on each side of the multiplication, the two recursive calls would perform exactly the same calculation. - Storing the result in `half` means that each level makes only one recursive call, so the recursion depth is about $\log n$. - Making two calls would cause a great deal of repeated computation. + 3. Calling the same subproblem on both sides of the multiplication repeats exactly the same computation. + Saving the result in `half` means each level makes only one recursive call, with recursion depth approximately $\log n$. + Making two calls would cause substantial repeated computation. -### Split Traversal Sequences into Left and Right Subtrees +### Splitting traversal sequences into left and right subtrees -A binary tree has no duplicate nodes. Its preorder and inorder traversals are: +A binary tree with distinct node values has these preorder and inorder traversals: - Preorder: `[A, B, D, E, C]` - Inorder: `[D, B, E, A, C]` -Split the sequences only once, at the root. You do not need to continue recursively or draw the whole tree: +Split only at the root level; there is no need to recurse further or draw the entire tree. -1. Which node is the root? -2. Which subsequences of the inorder traversal correspond to the left and right subtrees? -3. Which subsequences of the preorder traversal correspond to the left and right subtrees? Which nodes are the root's direct children? +1. What is the root node? +2. Which parts of the inorder sequence belong to the left and right subtrees? +3. Which parts of the preorder sequence belong to the left and right subtrees? What are the root's immediate children? -??? success "Answer" +??? success "Reference answer" - 1. The first node in a preorder traversal is the root, so the root is `A`. + 1. The first node in preorder is the root, so the root is `A`. - 2. `A` divides the inorder traversal into two parts: `[D, B, E]` for the left subtree and `[C]` for the right subtree. + 2. `A` splits the inorder sequence into two parts: `[D, B, E]` for the left subtree and `[C]` for the right subtree. - 3. The left subtree contains 3 nodes, so the 3 preorder elements after the root `A` belong to the left subtree, - giving `[B, D, E]`. The remaining `[C]` belongs to the right subtree. - Therefore, the root's left child is `B`, and its right child is `C`. + 3. The left subtree has three nodes, so the three preorder elements after root `A` belong to the left subtree: + `[B, D, E]`. The remaining `[C]` belongs to the right subtree. + Thus, the root's left child is `B`, and its right child is `C`. -## Programming Exercises +## Programming exercises -### Exponentiation by Squaring +### Fast exponentiation -Given a real number `x` and an integer `n`, calculate $x^n$ without calling the language's built-in power function. -Use recursive divide and conquer: halve the exponent at each step and reuse the result of the subproblem already calculated. -This exercise defines $x^0=1$, including when `x = 0`. When `n < 0`, `x != 0` is guaranteed, and the answer can be transformed into $(1/x)^{-n}$. +Given a real number `x` and an integer `n`, compute $x^n$ without calling a built-in power function. +Use recursive divide and conquer: halve the exponent at each step and reuse the computed subproblem result. +For this problem, $x^0=1$, including when `x = 0`. When `n < 0`, `x != 0` is guaranteed, and the answer can be expressed as $(1/x)^{-n}$. ??? tip "Hints" - 1. When n is 0, the answer is 1 - 2. After calculating x to the power n // 2, store the result in half rather than making the recursive call a second time - 3. When n < 0, first change x to 1 / x and then change n to -n; in C++ or Java, first convert n to a 64-bit integer to avoid overflow when negating the smallest 32-bit integer + 1. When n is 0, the answer is 1. + 2. After computing x to the power n // 2, save it in half instead of making the recursive call a second time. + 3. When n < 0, first replace x with 1 / x and n with -n. In C++ or Java, you can convert n to a 64-bit integer first to avoid overflow when negating the smallest 32-bit integer. [LeetCode](https://leetcode.com/problems/powx-n/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_divide_and_conquer/hanota_problem.md b/en/docs/chapter_divide_and_conquer/hanota_problem.md index c7e9e7200..898b60eac 100644 --- a/en/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/en/docs/chapter_divide_and_conquer/hanota_problem.md @@ -1,22 +1,22 @@ -# Tower of Hanoi Problem +# Tower of Hanoi -In merge sort and building binary trees, we decompose the original problem into two subproblems, each half the size of the original problem. However, for the Tower of Hanoi problem, we adopt a different decomposition strategy. +In merge sort and binary tree reconstruction, we divided the original problem into two subproblems, each half its size. The Tower of Hanoi uses a different division strategy. !!! question - Given three pillars, denoted as `A`, `B`, and `C`. Initially, pillar `A` has $n$ discs stacked on it, arranged from top to bottom in ascending order of size. Our task is to move these $n$ discs to pillar `C` while maintaining their original order (as shown in the figure below). The following rules must be followed when moving the discs. + Given three rods called `A`, `B`, and `C`, initially rod `A` holds $n$ disks arranged from smallest at the top to largest at the bottom. Move all $n$ disks to rod `C` while preserving their order, as shown in the figure below. The following rules apply. + + 1. A disk can only be removed from the top of one rod and placed on top of another. + 2. Only one disk may be moved at a time. + 3. A smaller disk must always be above a larger disk. - 1. A disc can only be taken from the top of one pillar and placed on top of another pillar. - 2. Only one disc can be moved at a time. - 3. A smaller disc must always be on top of a larger disc. +![Example of the Tower of Hanoi](hanota_problem.assets/hanota_example.png) -![Example of the Tower of Hanoi problem](hanota_problem.assets/hanota_example.png) +**Let $f(i)$ denote a Tower of Hanoi problem of size $i$**. For example, $f(3)$ means moving $3$ disks from `A` to `C`. -**We denote the Tower of Hanoi problem of size $i$ as $f(i)$**. For example, $f(3)$ represents moving $3$ discs from `A` to `C`. +### Considering the base cases -### Considering the Base Cases - -As shown in the figure below, for problem $f(1)$, when there is only one disc, we can move it directly from `A` to `C`. +For $f(1)$, with just one disk, simply move it directly from `A` to `C`, as shown in the figure below. === "<1>" ![Solution for a problem of size 1](hanota_problem.assets/hanota_f1_step1.png) @@ -24,11 +24,11 @@ As shown in the figure below, for problem $f(1)$, when there is only one disc, w === "<2>" ![hanota_f1_step2](hanota_problem.assets/hanota_f1_step2.png) -As shown in the figure below, for problem $f(2)$, when there are two discs, **since we must always keep the smaller disc on top of the larger disc, we need to use `B` to assist in the move**. +For $f(2)$, with two disks, **we need to use `B` because the smaller disk must remain above the larger one**, as shown in the figure below. -1. First, move the smaller disc from `A` to `B`. -2. Then move the larger disc from `A` to `C`. -3. Finally, move the smaller disc from `B` to `C`. +1. Move the smaller top disk from `A` to `B`. +2. Move the larger disk from `A` to `C`. +3. Move the smaller disk from `B` to `C`. === "<1>" ![Solution for a problem of size 2](hanota_problem.assets/hanota_f2_step1.png) @@ -42,17 +42,17 @@ As shown in the figure below, for problem $f(2)$, when there are two discs, **si === "<4>" ![hanota_f2_step4](hanota_problem.assets/hanota_f2_step4.png) -The process of solving problem $f(2)$ can be summarized as: **moving two discs from `A` to `C` with the help of `B`**. Here, `C` is called the target pillar, and `B` is called the buffer pillar. +We can summarize the solution to $f(2)$ as **moving two disks from `A` to `C` using `B`**. Here, `C` is the destination rod and `B` is the auxiliary rod. -### Subproblem Decomposition +### Dividing the problem -For problem $f(3)$, when there are three discs, the situation becomes slightly more complex. +For $f(3)$, with three disks, the situation is a little more complicated. -Since we already know the solutions to $f(1)$ and $f(2)$, we can think from a divide and conquer perspective, **treating the top two discs on `A` as a whole**, and execute the steps shown in the figure below. This successfully moves the three discs from `A` to `C`. +Since we know how to solve $f(1)$ and $f(2)$, we can use divide and conquer: **treat the top two disks on `A` as a group** and follow the steps in the figure below. This moves all three disks from `A` to `C`. -1. Let `B` be the target pillar and `C` be the buffer pillar, and move two discs from `A` to `B`. -2. Move the remaining disc from `A` directly to `C`. -3. Let `C` be the target pillar and `A` be the buffer pillar, and move two discs from `B` to `C`. +1. Use `B` as the destination and `C` as the auxiliary rod to move two disks from `A` to `B`. +2. Move the remaining disk directly from `A` to `C`. +3. Use `C` as the destination and `A` as the auxiliary rod to move two disks from `B` to `C`. === "<1>" ![Solution for a problem of size 3](hanota_problem.assets/hanota_f3_step1.png) @@ -66,32 +66,32 @@ Since we already know the solutions to $f(1)$ and $f(2)$, we can think from a di === "<4>" ![hanota_f3_step4](hanota_problem.assets/hanota_f3_step4.png) -Essentially, **we divide problem $f(3)$ into two subproblems $f(2)$ and one subproblem $f(1)$**. By solving these three subproblems in order, the original problem is solved. This shows that the subproblems are independent and their solutions can be merged. +In essence, **we have divided $f(3)$ into two $f(2)$ subproblems and one $f(1)$ subproblem**. Solving these three subproblems in order solves the original problem. This shows that the subproblems are independent and their solutions can be combined. -From this, we can summarize the divide and conquer strategy for solving the Tower of Hanoi problem shown in the figure below: divide the original problem $f(n)$ into two subproblems $f(n-1)$ and one subproblem $f(1)$, and solve these three subproblems in the following order. +We can now state the divide-and-conquer strategy for the Tower of Hanoi, illustrated in the figure below: divide $f(n)$ into two subproblems $f(n-1)$ and one subproblem $f(1)$, then solve them in this order. -1. Move $n-1$ discs from `A` to `B` with the help of `C`. -2. Move the remaining $1$ disc directly from `A` to `C`. -3. Move $n-1$ discs from `B` to `C` with the help of `A`. +1. Move $n-1$ disks from `A` to `B` using `C`. +2. Move the remaining $1$ disk directly from `A` to `C`. +3. Move $n-1$ disks from `B` to `C` using `A`. -For these two subproblems $f(n-1)$, **we can recursively divide them in the same way** until reaching the smallest subproblem $f(1)$. The solution to $f(1)$ is known and requires only one move operation. +Each of the two $f(n-1)$ subproblems **can be divided recursively in the same way** until we reach the smallest subproblem, $f(1)$. The solution to $f(1)$ is a single move. -![Divide and conquer strategy for solving the Tower of Hanoi problem](hanota_problem.assets/hanota_divide_and_conquer.png) +![Divide-and-conquer strategy for the Tower of Hanoi](hanota_problem.assets/hanota_divide_and_conquer.png) -### Code Implementation +### Code implementation -In the code, we declare a recursive function `dfs(i, src, buf, tar)`, whose purpose is to move the top $i$ discs from pillar `src` to target pillar `tar` with the help of buffer pillar `buf`: +We define a recursive function `dfs(i, src, buf, tar)` that moves the top $i$ disks from rod `src` to destination rod `tar` using auxiliary rod `buf`: ```src [file]{hanota}-[class]{}-[func]{solve_hanota} ``` -As shown in the figure below, the Tower of Hanoi problem forms a recursion tree of height $n$, where each node represents a subproblem corresponding to an invocation of the `dfs()` function, **therefore the time complexity is $O(2^n)$ and the space complexity is $O(n)$**. +As shown in the figure below, the Tower of Hanoi forms a recursion tree of height $n$. Each node represents a subproblem and corresponds to a call to `dfs()`. **The time complexity is therefore $O(2^n)$, and the space complexity is $O(n)$**. -![Recursion tree of the Tower of Hanoi problem](hanota_problem.assets/hanota_recursive_tree.png) +![Recursion tree for the Tower of Hanoi](hanota_problem.assets/hanota_recursive_tree.png) !!! quote - The Tower of Hanoi problem originates from an ancient legend. In a temple in ancient India, monks had three tall diamond pillars and $64$ golden discs of different sizes. The monks continuously moved the discs, believing that when the last disc was correctly placed, the world would come to an end. + The Tower of Hanoi comes from an ancient legend. In a temple in ancient India, monks had three tall diamond rods and $64$ gold disks of different sizes. They continually moved the disks, believing that the world would end when the last disk was placed in its correct position. - However, even if the monks moved one disc per second, it would take approximately $2^{64} \approx 1.84×10^{19}$ seconds, which is about $585$ billion years, far exceeding current estimates of the age of the universe. Therefore, if this legend is true, we should not need to worry about the end of the world. + Even at one move per second, however, this would take about $2^{64} \approx 1.84×10^{19}$ seconds, or roughly $585$ billion years, far longer than current estimates of the age of the universe. If the legend is true, we should have little reason to worry about the end of the world. diff --git a/en/docs/chapter_divide_and_conquer/index.md b/en/docs/chapter_divide_and_conquer/index.md index 0c7b6c72a..2debccc93 100644 --- a/en/docs/chapter_divide_and_conquer/index.md +++ b/en/docs/chapter_divide_and_conquer/index.md @@ -1,9 +1,9 @@ -# Divide and Conquer +# Divide and conquer ![Divide and conquer](../assets/covers/chapter_divide_and_conquer.jpg) !!! abstract - Difficult problems are decomposed layer by layer, with each decomposition making them simpler. - - Divide and conquer reveals an important truth: start with what is simple, and nothing remains complex. + A difficult problem is broken down layer by layer, becoming simpler with each division. + + Divide and conquer reveals an important truth: start with the simple parts, and complexity becomes manageable. diff --git a/en/docs/chapter_divide_and_conquer/summary.md b/en/docs/chapter_divide_and_conquer/summary.md index e017d31d1..0d9b91fc8 100644 --- a/en/docs/chapter_divide_and_conquer/summary.md +++ b/en/docs/chapter_divide_and_conquer/summary.md @@ -1,13 +1,13 @@ # Summary -### Key Review +### Key takeaways -- Divide and conquer is a common algorithm design strategy consisting of two phases, divide (partition) and conquer (merge), and is typically implemented recursively. -- The criteria for determining whether a problem is a divide and conquer problem include: whether the problem can be decomposed, whether subproblems are independent, and whether subproblems can be merged. -- Merge sort is a typical application of the divide and conquer strategy. It recursively divides an array into two equal-length subarrays until only one element remains, then merges them layer by layer to complete the sorting. -- Introducing the divide and conquer strategy can often improve algorithm efficiency. On one hand, it reduces the number of operations; on the other hand, it makes parallel optimization by the system easier. -- Divide and conquer can solve many algorithmic problems and is also widely used in data structures and algorithm design, making it ubiquitous. -- Compared to brute-force search, adaptive search is more efficient. Search algorithms with time complexity of $O(\log n)$ are typically implemented based on the divide and conquer strategy. -- Binary search is another typical application of divide and conquer. It does not include the step of merging solutions of subproblems. We can implement binary search through recursive divide and conquer. -- In the problem of building a binary tree, building the tree (original problem) can be divided into building the left subtree and right subtree (subproblems), which can be achieved by dividing the index intervals of the preorder and inorder traversals. -- In the Tower of Hanoi problem, a problem of size $n$ can be divided into two subproblems of size $n-1$ and one subproblem of size $1$. After solving these three subproblems in order, the original problem is solved. +- Divide and conquer is a common algorithm design strategy with two stages: dividing the problem and combining solutions. It is usually implemented recursively. +- To recognize a divide-and-conquer problem, ask whether it can be divided, whether the subproblems are independent, and whether their solutions can be combined. +- Merge sort is a classic example. It recursively divides an array into two equally sized subarrays until each contains one element, then merges them level by level to complete the sort. +- Divide and conquer often improves efficiency by reducing the number of operations and making it easier to parallelize the work. +- Divide and conquer solves many algorithmic problems and appears widely in the design of data structures and algorithms. +- Adaptive search is more efficient than brute-force search. Search algorithms with $O(\log n)$ time complexity are usually based on divide and conquer. +- Binary search is another classic example, with no step for combining subproblem solutions. It can be implemented through recursive divide and conquer. +- Reconstructing a binary tree can be divided into building its left and right subtrees. This is achieved by partitioning index intervals in the preorder and inorder traversals. +- A Tower of Hanoi problem of size $n$ can be divided into two subproblems of size $n-1$ and one of size $1$. Solving these three subproblems in order solves the original problem. diff --git a/en/docs/chapter_dynamic_programming/dp_problem_features.md b/en/docs/chapter_dynamic_programming/dp_problem_features.md index 8c5619671..3a48b2102 100644 --- a/en/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/en/docs/chapter_dynamic_programming/dp_problem_features.md @@ -1,79 +1,79 @@ -# Characteristics of Dynamic Programming Problems +# Properties of dynamic programming problems -In the previous section, we learned how dynamic programming solves the original problem by decomposing it into subproblems. In fact, subproblem decomposition is a general algorithmic approach, with different emphases in divide and conquer, dynamic programming, and backtracking. +The previous section showed how dynamic programming solves a problem by decomposing it into subproblems. Decomposition is a general algorithmic idea, but divide and conquer, dynamic programming, and backtracking use it differently. -- Divide and conquer algorithms recursively divide the original problem into multiple independent subproblems until the smallest subproblems are reached, and merge the solutions to the subproblems during backtracking to ultimately obtain the solution to the original problem. -- Dynamic programming also recursively decomposes problems, but the main difference from divide and conquer algorithms is that subproblems in dynamic programming are interdependent, and many overlapping subproblems appear during the decomposition process. -- Backtracking algorithms enumerate all possible solutions through trial and error, and avoid unnecessary search branches through pruning. The solution to the original problem consists of a series of decision steps, and we can regard the subsequence before each decision step as a subproblem. +- Divide and conquer recursively splits a problem into independent subproblems until the smallest ones are reached, then combines their solutions as the recursion returns to solve the original problem. +- Dynamic programming also decomposes problems recursively. Its main difference from divide and conquer is that its subproblems depend on one another, and many overlapping subproblems appear during decomposition. +- Backtracking enumerates possible solutions by trying choices and undoing them, using pruning to avoid unnecessary branches. A solution consists of a sequence of decisions, and the prefix before each decision can be viewed as a subproblem. -In fact, dynamic programming is commonly used to solve optimization problems, which not only contain overlapping subproblems but also have two other major characteristics: optimal substructure and no aftereffects. +Dynamic programming is often used for optimization problems. Besides overlapping subproblems, these problems have two key properties: optimal substructure and the no-aftereffect property. -## Optimal Substructure +## Optimal substructure -We make a slight modification to the stair climbing problem to make it more suitable for demonstrating the concept of optimal substructure. +Let's modify the climbing stairs problem slightly to illustrate optimal substructure more clearly. -!!! question "Climbing stairs with minimum cost" +!!! question "Minimum cost to climb stairs" - Given a staircase, you can climb $1$ or $2$ steps at a time, and each step is labeled with a non-negative integer representing the cost of stepping on it. Given a non-negative integer array $cost$, where $cost[i]$ represents the cost of the $i$-th step and $cost[0]$ is the ground (starting point), what is the minimum cost required to reach the top? + Given a staircase, you can climb $1$ or $2$ steps at a time. Each step has a nonnegative integer indicating the cost paid on that step. A nonnegative integer array $cost$ gives the costs: $cost[i]$ is the cost of step $i$, and $cost[0]$ refers to the ground (the starting point). What is the minimum cost of reaching the top? -As shown in the figure below, if the costs of the $1$st, $2$nd, and $3$rd steps are $1$, $10$, and $1$ respectively, then climbing from the ground to the $3$rd step requires a minimum cost of $2$. +As shown in the figure below, if steps $1$, $2$, and $3$ cost $1$, $10$, and $1$, respectively, the minimum cost of reaching step $3$ from the ground is $2$. -![Minimum cost to climb to the 3rd step](dp_problem_features.assets/min_cost_cs_example.png) +![Minimum cost of reaching step 3](dp_problem_features.assets/min_cost_cs_example.png) -Let $dp[i]$ be the accumulated cost of climbing to the $i$-th step. Since the $i$-th step can only come from the $i-1$-th or $i-2$-th step, $dp[i]$ can only equal $dp[i-1] + cost[i]$ or $dp[i-2] + cost[i]$. To minimize the cost, we should choose the smaller of the two: +Let $dp[i]$ be the total cost of reaching step $i$. Since step $i$ can only be reached from step $i - 1$ or $i - 2$, $dp[i]$ must be either $dp[i - 1] + cost[i]$ or $dp[i - 2] + cost[i]$. To minimize the cost, choose the smaller of the two: $$ dp[i] = \min(dp[i-1], dp[i-2]) + cost[i] $$ -This leads us to the meaning of optimal substructure: **the optimal solution to the original problem is constructed from the optimal solutions to the subproblems**. +This introduces the meaning of optimal substructure: **an optimal solution to the original problem is constructed from optimal solutions to its subproblems**. -This problem clearly has optimal substructure: we select the better one from the optimal solutions to the two subproblems $dp[i-1]$ and $dp[i-2]$, and use it to construct the optimal solution to the original problem $dp[i]$. +This problem clearly has optimal substructure: choose the better of $dp[i-1]$ and $dp[i-2]$ and use it to construct the optimal solution $dp[i]$. -So, does the stair climbing problem from the previous section have optimal substructure? Its goal is to find the number of ways, which seems to be a counting problem, but if we change the question: "Find the maximum number of ways". We surprisingly discover that **although the problem before and after modification are equivalent, the optimal substructure has emerged**: the maximum number of ways for the $n$-th step equals the sum of the maximum number of ways for the $n-1$-th and $n-2$-th steps. Therefore, the interpretation of optimal substructure is quite flexible and will have different meanings in different problems. +Does the climbing stairs problem from the previous section also have optimal substructure? Its goal is to count solutions, so it seems to be a counting problem. But rephrase the goal as finding the maximum number of solutions, and **although the problem remains equivalent, optimal substructure becomes apparent**: the maximum number of ways to reach step $n$ is the sum of the maximum numbers for steps $n-1$ and $n-2$. The interpretation of optimal substructure is therefore flexible and can vary by problem. -According to the state transition equation and the initial states $dp[1] = cost[1]$ and $dp[2] = cost[2]$, we can obtain the dynamic programming code: +The state transition equation, together with the initial states $dp[1] = cost[1]$ and $dp[2] = cost[2]$, gives the following dynamic programming code: ```src [file]{min_cost_climbing_stairs_dp}-[class]{}-[func]{min_cost_climbing_stairs_dp} ``` -The figure below shows the dynamic programming process for the above code. +The figure below illustrates this dynamic programming process. -![Dynamic programming process for climbing stairs with minimum cost](dp_problem_features.assets/min_cost_cs_dp.png) +![Dynamic programming for the minimum climbing cost](dp_problem_features.assets/min_cost_cs_dp.png) -This problem can also be space-optimized, compressing from one dimension to zero, reducing the space complexity from $O(n)$ to $O(1)$: +We can also reduce the state storage from a one-dimensional array to scalar variables, lowering the space complexity from $O(n)$ to $O(1)$: ```src [file]{min_cost_climbing_stairs_dp}-[class]{}-[func]{min_cost_climbing_stairs_dp_comp} ``` -## No Aftereffects +## No-aftereffect property -No aftereffects is one of the important characteristics that enable dynamic programming to solve problems effectively. Its definition is: **given a certain state, its future development is only related to the current state and has nothing to do with all past states**. +The no-aftereffect property is central to effective dynamic programming: **given a fully specified state, its future depends only on that state, not on the sequence of states that led to it**. -Taking the stair climbing problem as an example, given state $i$, it will develop into states $i+1$ and $i+2$, corresponding to jumping $1$ step and jumping $2$ steps, respectively. When making these two choices, we do not need to consider the states before state $i$, as they have no effect on the future of state $i$. +In the climbing stairs problem, state $i$ can lead to $i+1$ or $i+2$, corresponding to moves of $1$ or $2$ steps. Neither choice requires us to consider states before $i$: they do not affect the future of state $i$. -However, if we add a constraint to the stair climbing problem, the situation changes. +Adding a constraint changes the situation. -!!! question "Climbing stairs with constraint" +!!! question "Climbing stairs with a constraint" - Given a staircase with $n$ steps, where you can climb $1$ or $2$ steps at a time, **but you cannot jump $1$ step in two consecutive rounds**. How many ways are there to climb to the top? + Given a staircase with $n$ steps, you may climb $1$ or $2$ steps at a time, **but may not make two consecutive moves of $1$ step**. How many ways are there to reach the top? -As shown in the figure below, there are only $2$ feasible ways to climb to the $3$rd step. The path with three consecutive $1$-step jumps does not satisfy the constraint and is therefore discarded. +As shown in the figure below, only $2$ ways remain to reach step $3$: taking $1$ step three times in a row violates the constraint and is discarded. -![Number of ways to climb to the 3rd step with constraint](dp_problem_features.assets/climbing_stairs_constraint_example.png) +![Number of ways to reach step 3 with the constraint](dp_problem_features.assets/climbing_stairs_constraint_example.png) -In this problem, if the previous round was a jump of $1$ step, then the next round must jump $2$ steps. This means that **the next choice cannot be determined solely by the current state (current stair step number), but also depends on the previous state (the stair step number from the previous round)**. +If the previous move climbed $1$ step, the next must climb $2$ steps. Thus, **the next choice cannot be determined by the current state (the current step number) alone; it also depends on the preceding state (the previous step number)**. -It is not difficult to see that this problem no longer satisfies no aftereffects, and the state transition equation $dp[i] = dp[i-1] + dp[i-2]$ also fails, because $dp[i-1]$ represents jumping $1$ step in this round, but it includes many solutions where "the previous round was a jump of $1$ step", which cannot be directly counted in $dp[i]$ to satisfy the constraint. +The no-aftereffect property no longer holds with this state definition, and $dp[i] = dp[i-1] + dp[i-2]$ is no longer valid. Using $dp[i-1]$ means taking $1$ step now, but it includes many ways in which the preceding move also took $1$ step. We cannot simply add all of $dp[i-1]$ to $dp[i]$ without violating the constraint. -For this reason, we need to expand the state definition: **state $[i, j]$ represents being on the $i$-th step with the previous round having jumped $j$ steps**, where $j \in \{1, 2\}$. This state definition effectively distinguishes whether the previous round was a jump of $1$ step or $2$ steps, allowing us to determine where the current state came from. +We therefore expand the state definition: **state $[i, j]$ means we are on step $i$ and the previous move climbed $j$ steps**, where $j \in \{1, 2\}$. This distinguishes whether the preceding move climbed $1$ or $2$ steps, allowing us to determine how the current state could have been reached. -- When the previous round jumped $1$ step, the round before that could only choose to jump $2$ steps, i.e., $dp[i, 1]$ can only transition from $dp[i-1, 2]$. -- When the previous round jumped $2$ steps, the round before that could choose to jump $1$ step or $2$ steps, i.e., $dp[i, 2]$ can transition from $dp[i-2, 1]$ or $dp[i-2, 2]$. +- If the previous move climbed $1$ step, the move before it must have climbed $2$ steps. Thus, $dp[i, 1]$ can only come from $dp[i-1, 2]$. +- If the previous move climbed $2$ steps, the move before it could have climbed $1$ or $2$ steps. Thus, $dp[i, 2]$ can come from $dp[i-2, 1]$ or $dp[i-2, 2]$. -As shown in the figure below, under this definition, $dp[i, j]$ represents the number of ways for state $[i, j]$. The state transition equation is then: +As shown in the figure below, $dp[i, j]$ now counts the ways to reach state $[i, j]$. The state transition equations are: $$ \begin{cases} @@ -82,20 +82,20 @@ dp[i, 2] = dp[i-2, 1] + dp[i-2, 2] \end{cases} $$ -![Recurrence relation considering constraints](dp_problem_features.assets/climbing_stairs_constraint_state_transfer.png) +![Recurrence with the constraint](dp_problem_features.assets/climbing_stairs_constraint_state_transfer.png) -Finally, return $dp[n, 1] + dp[n, 2]$, where the sum of the two represents the total number of ways to climb to the $n$-th step: +Finally, return $dp[n, 1] + dp[n, 2]$, the total number of ways to reach step $n$: ```src [file]{climbing_stairs_constraint_dp}-[class]{}-[func]{climbing_stairs_constraint_dp} ``` -In the above case, since we only need to consider one more preceding state, we can still make the problem satisfy no aftereffects by expanding the state definition. However, some problems have very severe "aftereffects". +In this example, only one preceding state mattered, so expanding the state definition restored the no-aftereffect property. Some problems, however, depend much more strongly on their history. -!!! question "Climbing stairs with obstacle generation" +!!! question "Climbing stairs that generate obstacles" - Given a staircase with $n$ steps, where you can climb $1$ or $2$ steps at a time. **Whenever you reach the $i$-th step, the system automatically places an obstacle on the $2i$-th step, and no subsequent round is allowed to jump to the $2i$-th step**. For example, if the first two rounds jump to the $2$nd and $3$rd steps, then afterwards you cannot jump to the $4$th and $6$th steps. How many ways are there to climb to the top? + Given a staircase with $n$ steps, you may climb $1$ or $2$ steps at a time. **Whenever you reach step $i$, the system places an obstacle on step $2i$, and you may never land on step $2i$ afterward**. For example, if your first two moves land on steps $2$ and $3$, then steps $4$ and $6$ are blocked for all later moves. How many ways are there to reach the top? -In this problem, the next jump depends on all past states, because each jump places obstacles on higher steps, affecting future jumps. For such problems, dynamic programming is often difficult to solve. +Here, each next move depends on all previous states, because every move places an obstacle higher up and affects future moves. Such problems are often difficult to solve with dynamic programming. -In fact, many complex combinatorial optimization problems (such as the traveling salesman problem) do not satisfy no aftereffects. For such problems, we usually use other methods, such as heuristic search, genetic algorithms, and reinforcement learning, to obtain usable locally optimal solutions within a limited time. +Many complex combinatorial optimization problems, such as the traveling salesperson problem, do not satisfy the no-aftereffect property. For such problems, we often choose other methods, such as heuristic search, genetic algorithms, or reinforcement learning, to obtain useful locally optimal solutions within a limited time. diff --git a/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md index d00b51e5f..47a1eabd6 100644 --- a/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -1,65 +1,65 @@ -# Dynamic Programming Problem-Solving Approach +# Solving dynamic programming problems -The previous two sections introduced the main characteristics of dynamic programming problems. Next, let us explore two more practical issues together. +The previous two sections introduced the main properties of dynamic programming problems. We will now explore two practical questions. -1. How to determine whether a problem is a dynamic programming problem? -2. What is the complete process for solving a dynamic programming problem, and where should we start? +1. How can we recognize a dynamic programming problem? +2. Where should we start, and what are the steps for solving one? -## Problem Identification +## Recognizing the problem -Generally speaking, if a problem contains overlapping subproblems, optimal substructure, and satisfies no aftereffects, then it is usually suitable for solving with dynamic programming. However, it is difficult to directly extract these characteristics from the problem description. Therefore, we usually relax the conditions and **first observe whether the problem is suitable for solving with backtracking (exhaustive search)**. +In general, a problem is suitable for dynamic programming if it has overlapping subproblems, optimal substructure, and the no-aftereffect property. These can be difficult to identify directly from a problem statement, so we usually start with a broader test: **could backtracking (exhaustive search) solve it**? -**Problems suitable for solving with backtracking usually satisfy the "decision tree model"**, which means the problem can be described using a tree structure, where each node represents a decision and each path represents a sequence of decisions. +**Problems suited to backtracking usually fit a decision tree model**. They can be represented as a tree in which each node is a decision and each path is a sequence of decisions. -In other words, if a problem contains an explicit concept of decisions, and the solution is generated through a series of decisions, then it satisfies the decision tree model and can usually be solved using backtracking. +In other words, if a problem has clear choices and a solution is produced through a sequence of them, it fits the decision tree model and can usually be solved by backtracking. -On this basis, dynamic programming problems also have some positive indicators. +Beyond this, some signs make dynamic programming more promising. -- The problem contains descriptions such as maximum (minimum) or most (least), indicating optimization. -- The problem's state can be represented using a list, multi-dimensional matrix, or tree, and a state has a recurrence relation with its surrounding states. +- The problem asks for an optimum, such as a maximum, minimum, greatest number, or smallest number. +- States can be represented by a list, a multidimensional matrix, or a tree, and a recurrence relates each state to nearby states. -Correspondingly, there are also some negative indicators. +Other signs make it less promising. -- The goal of the problem is to find all possible solutions, rather than finding the optimal solution. -- The problem description has obvious permutation and combination characteristics, requiring the return of specific multiple solutions. +- The goal is to find all possible solutions rather than an optimal one. +- The problem explicitly involves permutations or combinations and asks for multiple concrete arrangements. -If a problem satisfies the decision tree model and has relatively obvious positive indicators, we can assume it is a dynamic programming problem and verify that assumption during the solving process. +If a problem fits the decision tree model and has clear positive signs, we can tentatively treat it as a dynamic programming problem and verify that assumption while solving it. -## Problem-Solving Steps +## Steps for solving the problem -The problem-solving process for dynamic programming varies depending on the nature and difficulty of the problem, but generally follows these steps: describe decisions, define states, establish the $dp$ table, derive state transition equations, determine boundary conditions, etc. +The process varies with the problem's nature and difficulty, but usually includes describing the decisions, defining states, creating a $dp$ table, deriving a state transition equation, and identifying boundary conditions. -To illustrate the problem-solving steps more vividly, we use a classic problem "minimum path sum" as an example. +To make these steps concrete, we will use the classic minimum path sum problem. !!! question - Given an $n \times m$ two-dimensional grid `grid` in which each cell contains a non-negative integer representing its cost, a robot starts from the top-left cell and can only move down or right at each step until reaching the bottom-right cell. Return the minimum path sum from the top-left to the bottom-right. + Given an $n \times m$ grid `grid`, each cell contains a nonnegative integer representing its cost. A robot starts at the top-left cell and can move one cell down or right at a time until it reaches the bottom-right cell. Return the minimum path sum from the top-left to the bottom-right. -The figure below shows an example where the minimum path sum for the given grid is $13$. +In the example shown in the figure below, the minimum path sum is $13$. -![Minimum path sum example data](dp_solution_pipeline.assets/min_path_sum_example.png) +![Example input for minimum path sum](dp_solution_pipeline.assets/min_path_sum_example.png) -**Step 1: Think about the decisions in each round, define the state, and thus obtain the $dp$ table** +**Step 1: Identify each decision, define the state, and establish the $dp$ table** -The decision in each round of this problem is to move one step down or right from the current cell. Let the row and column indices of the current cell be $[i, j]$. After moving down or right, the indices become $[i+1, j]$ or $[i, j+1]$. Therefore, the state should include two variables, the row index and column index, denoted as $[i, j]$. +Each decision moves one cell down or right. If the current cell has indices $[i, j]$, the next indices are $[i+1, j]$ or $[i, j+1]$. The state therefore contains two variables, the row and column indices, and is denoted by $[i, j]$. -State $[i, j]$ corresponds to the subproblem: **the minimum path sum from the starting point $[0, 0]$ to $[i, j]$**, denoted as $dp[i, j]$. +State $[i, j]$ corresponds to the subproblem of finding the minimum path sum from the starting point $[0, 0]$ to $[i, j]$. Its solution is $dp[i, j]$. -From this, we obtain the two-dimensional $dp$ matrix shown in the figure below, whose size is the same as the input grid $grid$. +This gives the two-dimensional $dp$ matrix shown in the figure below, with the same dimensions as the input $grid$. ![State definition and dp table](dp_solution_pipeline.assets/min_path_sum_solution_state_definition.png) !!! note - The dynamic programming and backtracking processes can be described as a sequence of decisions, and the state consists of all decision variables. It should contain all variables describing the progress of problem-solving, and should contain sufficient information to derive the next state. + Dynamic programming and backtracking can be described as sequences of decisions. A state consists of the decision variables and should contain every variable needed to describe progress, with enough information to derive the next state. + + Each state corresponds to a subproblem. A $dp$ table stores the solutions to all subproblems, with one dimension of the $dp$ table for each independent state variable. In essence, the $dp$ table maps states to subproblem solutions. - Each state corresponds to a subproblem, and we define a $dp$ table to store the solutions to all subproblems. Each independent variable of the state is a dimension of the $dp$ table. Essentially, the $dp$ table is a mapping between states and solutions to subproblems. +**Step 2: Identify optimal substructure and derive the state transition equation** -**Step 2: Identify the optimal substructure, and then derive the state transition equation** +State $[i, j]$ can only be reached from the cell above, $[i-1, j]$, or the cell to the left, $[i, j-1]$. The optimal substructure is therefore that the minimum path sum to $[i, j]$ is determined by the smaller of the minimum path sums to $[i, j-1]$ and $[i-1, j]$. -For state $[i, j]$, it can only transition from the cell above $[i-1, j]$ or the cell to the left $[i, j-1]$. Therefore, the optimal substructure is: the minimum path sum to reach $[i, j]$ is determined by the smaller of the minimum path sums of $[i, j-1]$ and $[i-1, j]$. - -Based on the above analysis, the state transition equation shown in the figure below can be derived: +This gives the state transition equation illustrated in the figure below: $$ dp[i, j] = \min(dp[i-1, j], dp[i, j-1]) + grid[i, j] @@ -69,75 +69,75 @@ $$ !!! note - Based on the defined $dp$ table, think about the relationship between the original problem and subproblems, and find the method to construct the optimal solution to the original problem from the optimal solutions to the subproblems, which is the optimal substructure. + Using the defined $dp$ table, examine the relationship between the original problem and its subproblems. Find how to construct an optimal solution from optimal subproblem solutions: this is the optimal substructure. - Once we identify the optimal substructure, we can use it to construct the state transition equation. + Once we identify optimal substructure, we can use it to build the state transition equation. -**Step 3: Determine boundary conditions and state transition order** +**Step 3: Determine the boundary conditions and evaluation order** -In this problem, states in the first row can only come from the state to their left, and states in the first column can only come from the state above them. Therefore, the first row $i = 0$ and first column $j = 0$ are boundary conditions. +A state in the first row can only come from its left, and a state in the first column can only come from above. Thus, the first row $i = 0$ and first column $j = 0$ define the boundary conditions. -As shown in the figure below, since each cell transitions from the cell to its left and the cell above it, we use loops to traverse the matrix, with the outer loop traversing rows and the inner loop traversing columns. +As shown in the figure below, each cell depends on the cells to its left and above. We therefore traverse the matrix with an outer loop over rows and an inner loop over columns. -![Boundary conditions and state transition order](dp_solution_pipeline.assets/min_path_sum_solution_initial_state.png) +![Boundary conditions and evaluation order](dp_solution_pipeline.assets/min_path_sum_solution_initial_state.png) !!! note - Boundary conditions in dynamic programming are used to initialize the $dp$ table, while in search they are used for pruning. + Boundary conditions initialize the $dp$ table in dynamic programming and support pruning in search. + + The key to choosing an evaluation order is ensuring that all smaller subproblems needed by the current state have already been solved correctly. - The core of state transition order is to ensure that when computing the solution to the current problem, all the smaller subproblems it depends on have already been computed correctly. +We could now write the dynamic programming solution directly. However, decomposing a problem is a top-down way of thinking, so implementing "brute-force search $\rightarrow$ memoized search $\rightarrow$ dynamic programming" often follows our reasoning more naturally. -Based on the above analysis, we can directly write the dynamic programming code. However, subproblem decomposition is a top-down approach, so implementing in the order "brute force search $\rightarrow$ memoization $\rightarrow$ dynamic programming" is more aligned with thinking habits. +### Method 1: Brute-force search -### Method 1: Brute Force Search +Starting from $[i, j]$, recursively decompose the problem into smaller states $[i-1, j]$ and $[i, j-1]$. The recursive function has the following elements. -Starting from state $[i, j]$, we continuously decompose it into smaller states $[i-1, j]$ and $[i, j-1]$. The recursive function includes the following elements. +- **Parameters**: The state $[i, j]$. +- **Return value**: The minimum path sum $dp[i, j]$ from $[0, 0]$ to $[i, j]$. +- **Base case**: When $i = 0$ and $j = 0$, return $grid[0, 0]$. +- **Pruning**: If $i < 0$ or $j < 0$, the indices are out of bounds. Return cost $+\infty$ to mark the path as infeasible. -- **Recursive parameters**: state $[i, j]$. -- **Return value**: minimum path sum from $[0, 0]$ to $[i, j]$, which is $dp[i, j]$. -- **Termination condition**: when $i = 0$ and $j = 0$, return cost $grid[0, 0]$. -- **Pruning**: when $i < 0$ or $j < 0$, the index is out of bounds, return cost $+\infty$, representing infeasibility. - -The implementation code is as follows: +The implementation is as follows: ```src [file]{min_path_sum}-[class]{}-[func]{min_path_sum_dfs} ``` -The figure below shows the recursion tree rooted at $dp[2, 1]$, which includes some overlapping subproblems whose number will increase sharply as the size of grid `grid` grows. +The figure below shows the recursion tree rooted at $dp[2, 1]$. It includes overlapping subproblems, whose number grows rapidly with the size of `grid`. -Essentially, the reason for overlapping subproblems is: **there are multiple paths from the top-left corner to reach a certain cell**. +The underlying reason for overlapping subproblems is that **multiple paths can reach the same cell from the top-left corner**. -![Brute force search recursion tree](dp_solution_pipeline.assets/min_path_sum_dfs.png) +![Recursion tree for brute-force search](dp_solution_pipeline.assets/min_path_sum_dfs.png) -Each state has two choices, down and right, so the total number of steps from the top-left corner to the bottom-right corner is $m + n - 2$, giving a worst-case time complexity of $O(2^{m + n})$, where $n$ and $m$ are the number of rows and columns of the grid, respectively. Note that this calculation does not account for situations near the grid boundaries, where only one choice remains when reaching the grid boundary, so the actual number of paths will be somewhat less. +Each state offers two choices, down and right, and reaching the bottom-right corner takes $m + n - 2$ moves. The worst-case time complexity is therefore $O(2^{m + n})$, where $n$ and $m$ are the numbers of rows and columns. This estimate ignores the grid boundaries, where only one choice remains, so the actual number of paths is smaller. -### Method 2: Memoization +### Method 2: Memoized search -We introduce a memo list `mem` of the same size as grid `grid` to record the solutions to subproblems and prune overlapping subproblems: +Introduce a memoization table `mem` with the same dimensions as `grid` to store subproblem solutions and prune repeated subproblems: ```src [file]{min_path_sum}-[class]{}-[func]{min_path_sum_dfs_mem} ``` -As shown in the figure below, after introducing memoization, all subproblem solutions only need to be computed once, so the time complexity depends on the total number of states, which is the grid size $O(nm)$. +As shown in the figure below, memoization ensures that every subproblem is solved only once. The time complexity is therefore determined by the number of states, the grid size $O(nm)$. -![Memoization recursion tree](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png) +![Recursion tree for memoized search](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png) -### Method 3: Dynamic Programming +### Method 3: Dynamic programming -Implement the dynamic programming solution based on iteration, as shown in the code below: +The following code implements dynamic programming iteratively: ```src [file]{min_path_sum}-[class]{}-[func]{min_path_sum_dp} ``` -The figure below shows the state transition process for minimum path sum, which traverses the entire grid, **thus the time complexity is $O(nm)$**. +The figure below shows the state transitions for minimum path sum. They visit the entire grid, **so the time complexity is $O(nm)$**. -The array `dp` has size $n \times m$, **thus the space complexity is $O(nm)$**. +The array `dp` has dimensions $n \times m$, **so the space complexity is $O(nm)$**. === "<1>" - ![Dynamic programming process for minimum path sum](dp_solution_pipeline.assets/min_path_sum_dp_step1.png) + ![Dynamic programming for minimum path sum](dp_solution_pipeline.assets/min_path_sum_dp_step1.png) === "<2>" ![min_path_sum_dp_step2](dp_solution_pipeline.assets/min_path_sum_dp_step2.png) @@ -172,11 +172,11 @@ The array `dp` has size $n \times m$, **thus the space complexity is $O(nm)$**. === "<12>" ![min_path_sum_dp_step12](dp_solution_pipeline.assets/min_path_sum_dp_step12.png) -### Space Optimization +### Space optimization -Since each cell is only related to the cell to its left and the cell above it, we can use a single-row array to implement the $dp$ table. +Each cell depends only on the cells to its left and above, so a single-row array is enough to represent the $dp$ table. -Note that since the array `dp` can only represent the state of one row, we cannot initialize the first column state in advance, but rather update it when traversing each row: +Because `dp` stores only one row of states, we cannot initialize the entire first column in advance. Instead, update its entry when processing each row: ```src [file]{min_path_sum}-[class]{}-[func]{min_path_sum_dp_comp} diff --git a/en/docs/chapter_dynamic_programming/edit_distance_problem.md b/en/docs/chapter_dynamic_programming/edit_distance_problem.md index bfbce88cb..e46fc5520 100644 --- a/en/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/en/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -1,80 +1,80 @@ -# Edit Distance Problem +# Edit distance problem -Edit distance, also known as Levenshtein distance, refers to the minimum number of edits required to transform one string into another, commonly used in information retrieval and natural language processing to measure the similarity between two sequences. +Edit distance, also called Levenshtein distance, is the minimum number of edits needed to transform one string into another. It is commonly used in information retrieval and natural language processing to measure similarity between sequences. !!! question - Given two strings $s$ and $t$, return the minimum number of edits required to transform $s$ into $t$. + Given two strings $s$ and $t$, return the minimum number of edits needed to transform $s$ into $t$. + + Three edits are allowed: insert a character, delete a character, or replace a character with any other character. - You can perform three types of edit operations on a string: insert a character, delete a character, or replace a character with any other character. +As shown in the figure below, transforming `kitten` into `sitting` takes 3 edits: 2 replacements and 1 insertion. Transforming `hello` into `algo` also takes 3 edits: 2 replacements and 1 deletion. -As shown in the figure below, transforming `kitten` into `sitting` requires 3 edits, including 2 replacements and 1 insertion; transforming `hello` into `algo` requires 3 steps, including 2 replacements and 1 deletion. +![Examples of edit distance](edit_distance_problem.assets/edit_distance_example.png) -![Example data for edit distance](edit_distance_problem.assets/edit_distance_example.png) +**Edit distance fits naturally into a decision tree model**. Strings are nodes, and each decision (an edit) is an edge. -**The edit distance problem can be naturally explained using the decision tree model**. Strings correspond to tree nodes, and each edit operation corresponds to an edge in the tree. +As shown in the figure below, without restrictions on the edits, each node can have many outgoing edges, one for each possible operation. Thus, many paths can transform `hello` into `algo`. -As shown in the figure below, without restricting operations, each node can branch into many edges, with each edge corresponding to one operation, meaning there are many possible paths to transform `hello` into `algo`. +From the decision tree perspective, the goal is to find the shortest path between nodes `hello` and `algo`. -From the perspective of the decision tree, the goal of this problem is to find the shortest path between node `hello` and node `algo`. +![Edit distance as a decision tree](edit_distance_problem.assets/edit_distance_decision_tree.png) -![Representing edit distance problem based on decision tree model](edit_distance_problem.assets/edit_distance_decision_tree.png) +### Dynamic programming approach -### Dynamic Programming Approach +**Step 1: Identify each decision, define the state, and establish the $dp$ table** -**Step 1: Think about the decisions in each round, define the state, and thus obtain the $dp$ table** +Each decision applies one edit to string $s$. -Each round of decision involves performing one edit operation on string $s$. +We want each edit to reduce the problem's size so that we can form subproblems. Let the lengths of $s$ and $t$ be $n$ and $m$. First consider their last characters, $s[n-1]$ and $t[m-1]$. -We want the problem size to gradually decrease during the editing process so that we can construct subproblems. Let the lengths of strings $s$ and $t$ be $n$ and $m$ respectively. We first consider the tail characters of the two strings, $s[n-1]$ and $t[m-1]$. +- If $s[n-1]$ and $t[m-1]$ match, skip them and consider $s[n-2]$ and $t[m-2]$. +- If $s[n-1]$ and $t[m-1]$ differ, apply an edit to $s$ (insertion, deletion, or replacement) to match the endings, allowing us to move on to a smaller problem. -- If $s[n-1]$ and $t[m-1]$ are the same, we can skip them and directly consider $s[n-2]$ and $t[m-2]$. -- If $s[n-1]$ and $t[m-1]$ are different, we need to perform one edit on $s$ (insert, delete, or replace) to make the tail characters of the two strings the same, allowing us to skip them and consider a smaller-scale problem. +Each decision (edit) in $s$ changes the characters still to be matched in $s$ and $t$. The state therefore specifies the $i$-th and $j$-th characters currently under consideration in $s$ and $t$, denoted by $[i, j]$. -In other words, each round of decision (edit operation) we make on string $s$ will change the remaining characters to be matched in $s$ and $t$. Therefore, the state is the $i$-th and $j$-th characters currently being considered in $s$ and $t$, denoted as $[i, j]$. +State $[i, j]$ represents the subproblem of finding **the minimum number of edits needed to transform the first $i$ characters of $s$ into the first $j$ characters of $t$**. -State $[i, j]$ corresponds to the subproblem: **the minimum number of edits required to change the first $i$ characters of $s$ into the first $j$ characters of $t$**. +This gives a two-dimensional $dp$ table of size $(n+1) \times (m+1)$. -From this, we obtain a two-dimensional $dp$ table of size $(n+1) \times (m+1)$. +**Step 2: Identify optimal substructure and derive the state transition equation** -**Step 2: Identify the optimal substructure, and then derive the state transition equation** +For subproblem $dp[i, j]$, the final characters are $s[i-1]$ and $t[j-1]$. The three possible edits give the cases shown in the figure below. -Consider subproblem $dp[i, j]$, where the tail characters of the corresponding two strings are $s[i-1]$ and $t[j-1]$, which can be divided into the three cases shown in the figure below based on different edit operations. +1. Insert $t[j-1]$ after $s[i-1]$, leaving subproblem $dp[i, j-1]$. +2. Delete $s[i-1]$, leaving subproblem $dp[i-1, j]$. +3. Replace $s[i-1]$ with $t[j-1]$, leaving subproblem $dp[i-1, j-1]$. -1. Insert $t[j-1]$ after $s[i-1]$, then the remaining subproblem is $dp[i, j-1]$. -2. Delete $s[i-1]$, then the remaining subproblem is $dp[i-1, j]$. -3. Replace $s[i-1]$ with $t[j-1]$, then the remaining subproblem is $dp[i-1, j-1]$. +![State transitions for edit distance](edit_distance_problem.assets/edit_distance_state_transfer.png) -![State transition for edit distance](edit_distance_problem.assets/edit_distance_state_transfer.png) - -Based on the above analysis, we obtain the optimal substructure: the minimum number of edits for $dp[i, j]$ equals the minimum of $dp[i, j-1]$, $dp[i-1, j]$, and $dp[i-1, j-1]$, plus the current edit cost of $1$. The corresponding state transition equation is: +The optimal substructure follows: the minimum edit count $dp[i, j]$ is the smallest of $dp[i, j-1]$, $dp[i-1, j]$, and $dp[i-1, j-1]$, plus $1$ for the current edit. The state transition equation is: $$ dp[i, j] = \min(dp[i, j-1], dp[i-1, j], dp[i-1, j-1]) + 1 $$ -Please note that **when $s[i-1]$ and $t[j-1]$ are the same, no edit is required for the current character**, in which case the state transition equation is: +When **$s[i-1]$ and $t[j-1]$ match, no edit is needed for the current characters**, and the state transition equation becomes: $$ dp[i, j] = dp[i-1, j-1] $$ -**Step 3: Determine boundary conditions and state transition order** +**Step 3: Determine the boundary conditions and evaluation order** -When both strings are empty, the number of edit steps is $0$, i.e., $dp[0, 0] = 0$. When $s$ is empty but $t$ is not, the minimum number of edit steps equals the length of $t$, i.e., the first row $dp[0, j] = j$. When $s$ is not empty but $t$ is empty, the minimum number of edit steps equals the length of $s$, i.e., the first column $dp[i, 0] = i$. +If both strings are empty, $0$ edits are needed, so $dp[0, 0] = 0$. If $s$ is empty but $t$ is not, the minimum edit count is the length of $t$, giving the first row $dp[0, j] = j$. If $s$ is nonempty but $t$ is empty, the count is the length of $s$, giving the first column $dp[i, 0] = i$. -Observing the state transition equation, the solution $dp[i, j]$ depends on solutions to the left, above, and upper-left, so the entire $dp$ table can be traversed in order through two nested loops. +The solution $dp[i, j]$ depends on entries to its left, above, and upper left, so two nested loops can traverse the $dp$ table in increasing index order. -### Code Implementation +### Code implementation ```src [file]{edit_distance}-[class]{}-[func]{edit_distance_dp} ``` -As shown in the figure below, the state transition process for the edit distance problem is very similar to that of the knapsack problem; both can be viewed as the process of filling a two-dimensional grid. +As shown in the figure below, the state transition process resembles that of the knapsack problem: both fill a two-dimensional grid. === "<1>" - ![Dynamic programming process for edit distance](edit_distance_problem.assets/edit_distance_dp_step1.png) + ![Dynamic programming for edit distance](edit_distance_problem.assets/edit_distance_dp_step1.png) === "<2>" ![edit_distance_dp_step2](edit_distance_problem.assets/edit_distance_dp_step2.png) @@ -118,11 +118,11 @@ As shown in the figure below, the state transition process for the edit distance === "<15>" ![edit_distance_dp_step15](edit_distance_problem.assets/edit_distance_dp_step15.png) -### Space Optimization +### Space optimization -Since $dp[i, j]$ depends on the states above $dp[i-1, j]$, to the left $dp[i, j-1]$, and at the upper-left $dp[i-1, j-1]$, forward traversal will lose the upper-left state $dp[i-1, j-1]$, while reverse traversal cannot construct $dp[i, j-1]$ in advance, so neither traversal order is suitable. +The state $dp[i,j]$ depends on $dp[i-1, j]$ above, $dp[i, j-1]$ to the left, and $dp[i-1, j-1]$ at the upper left. Forward iteration loses $dp[i-1, j-1]$, while backward iteration cannot compute $dp[i, j-1]$ in advance. Neither order alone is sufficient. -For this reason, we can use a variable `leftup` to temporarily store the upper-left solution $dp[i-1, j-1]$, so we only need to consider the solutions to the left and above. This situation is the same as in the unbounded knapsack problem, so we can use forward traversal. The code is as follows: +We can use a variable `leftup` to preserve the upper-left solution $dp[i-1, j-1]$, leaving only the left and upper entries to consider. The situation then matches unbounded knapsack, and forward iteration works. The code is as follows: ```src [file]{edit_distance}-[class]{}-[func]{edit_distance_dp_comp} diff --git a/en/docs/chapter_dynamic_programming/exercises.md b/en/docs/chapter_dynamic_programming/exercises.md index 079943d2c..cd50f79ec 100644 --- a/en/docs/chapter_dynamic_programming/exercises.md +++ b/en/docs/chapter_dynamic_programming/exercises.md @@ -1,111 +1,111 @@ # Exercises -## Concept Review +## Check your understanding -### When Is Dynamic Programming Appropriate? +### When is dynamic programming a good fit? -A student says, "Whenever a recurrence can be written, dynamic programming should be used." -For each task below, decide whether dynamic programming, backtracking, or a loop or mathematical formula without a `dp` table is more appropriate. Give one key reason. +A student says, “Whenever you can write a recurrence relation, you should use dynamic programming.” +For each task below, decide whether dynamic programming, backtracking, or a loop or mathematical formula without a `dp` table is the best fit. Give one key reason for each choice. -1. Using coin denominations `[1, 3, 4]`, make an amount of 6 with the fewest coins. Each denomination may be used repeatedly. +1. Find the minimum number of coins needed to make an amount of 6 using denominations `[1, 3, 4]`. Each denomination can be used any number of times. 2. Output all permutations of `[1, 2, 3]`. 3. Calculate $1 + 2 + \dots + n$. - For the task you consider suitable for dynamic programming, also state what `dp[i]` represents. + For the task you consider suitable for dynamic programming, also explain what `dp[i]` represents. -??? success "Answer" +??? success "Reference answer" - 1. Dynamic programming is suitable. Let `dp[i]` be the minimum number of coins needed to make amount `i`. - For every coin `c` that does not exceed `i`, `dp[i-c] + 1` is a candidate answer, - and the minimum of these candidates is chosen. Different choices repeatedly encounter the same amounts, and an optimal solution for a larger amount can be built from optimal solutions for smaller amounts. - The answer for amount 6 is 2, using `3 + 3`. + 1. Dynamic programming is a good fit. Let `dp[i]` be the minimum number of coins needed to make an amount of `i`. + For each coin `c` whose denomination does not exceed `i`, consider `dp[i-c] + 1` as a candidate answer, + then take the minimum of these candidates. Different choices repeatedly lead to the same amounts, and an optimal solution for a larger amount can be built from optimal solutions for smaller amounts. + The answer for an amount of 6 is 2: `3 + 3`. - 2. Backtracking is suitable. The task requires generating all 6 permutations one by one. Backtracking can systematically make a choice, continue searching, - undo the choice, and then try another branch. Regardless of the method, actually outputting every permutation requires enumerating them. + 2. Use backtracking. The task requires generating all 6 permutations individually. Backtracking systematically makes a choice, continues the search, + then undoes the choice and explores other branches. Whatever method you use, outputting all the permutations requires enumerating them. - 3. A loop or the arithmetic-series formula is sufficient. Although the recurrence `S(i) = S(i-1) + i` can be written, calculating `S(i)` depends on only one smaller value, `S(i-1)`. - Each partial sum needs to be calculated only once, so there are no repeated subproblems and no need for a `dp` table. "A recurrence can be written" does not mean "dynamic programming is needed." + 3. A loop or the formula for an arithmetic series is sufficient. Although you can write `S(i) = S(i-1) + i`, calculating `S(i)` depends on just one smaller value, `S(i-1)`. + Each partial sum needs to be calculated only once, so there are no overlapping subproblems and no need for a `dp` table. Being able to write a recurrence does not mean you need dynamic programming. -### Calculating One Cell in a Knapsack Table +### How do you calculate a cell in the knapsack table? -Consider this 0-1 knapsack problem: item weights `wgt = [1, 2, 3]`, values `val = [5, 11, 15]`, and knapsack capacity 4. -`dp[i][c]` is the maximum value obtainable using only the first $i$ items with a knapsack capacity limit of $c$; -the knapsack does not have to be filled exactly. +Consider a 0-1 knapsack problem with item weights `wgt = [1, 2, 3]`, values `val = [5, 11, 15]`, and a knapsack capacity of 4. +Here, `dp[i][c]` is the maximum value obtainable using only the first $i$ items with a capacity limit of $c$. +The knapsack does not have to be filled exactly. -Calculate only the state `dp[3][4]`. You are given `dp[2][4] = 16` and `dp[2][1] = 5`: +Calculate just the state `dp[3][4]`. You are given `dp[2][4] = 16` and `dp[2][1] = 5`. -1. If the third item is not selected, what is the candidate value? -2. If the third item is selected, how much capacity remains, and what is the candidate value? -3. What should `dp[3][4]` be? Which items does this value correspond to selecting? +1. What is the candidate value if you do not take item 3? +2. If you take item 3, how much capacity remains, and what is the candidate value? +3. What should `dp[3][4]` be? Which items does this solution take? -??? success "Answer" +??? success "Reference answer" - 1. If the third item is not selected, keep the result from the first two items. The candidate value is `dp[2][4] = 16`. + 1. If you do not take item 3, use the result for the first two items. The candidate value is `dp[2][4] = 16`. - 2. The third item has weight 3, so capacity $4-3=1$ remains after it is placed in the knapsack. The candidate value is + 2. Item 3 weighs 3, so taking it leaves a capacity of $4-3=1$. The candidate value is `dp[2][1] + 15 = 5 + 15 = 20`. - 3. Comparing 16 and 20 gives `dp[3][4] = 20`. This corresponds to selecting the first and third items, - whose total weight is $1+3=4$ and total value is $5+15=20$. + 3. Comparing 16 and 20 gives `dp[3][4] = 20`. This solution takes items 1 and 3, + with a total weight of $1+3=4$ and a total value of $5+15=20$. - This state calculation demonstrates one "select or do not select" comparison in the 0-1 knapsack problem. + Calculating this state illustrates a single “take it or leave it” comparison in the 0-1 knapsack problem. -### In Which Order Should Knapsack Capacities Be Updated? +### In what order should knapsack capacities be updated? -A 0-1 knapsack problem has only one item, with weight 2 and value 5, and the knapsack has capacity 4. -The item can be selected at most once. The initial one-dimensional array is `dp = [0, 0, 0, 0, 0]`. +A 0-1 knapsack problem has just one item, with weight 2 and value 5. The knapsack capacity is 4. +Each item can be taken at most once, and the initial one-dimensional array is `dp = [0, 0, 0, 0, 0]`. -A student processes the item by updating capacities from 2 to 4: +A student processes this item by updating capacities from 2 to 4: -- After updating `dp[2]`, its value is 5. -- After updating `dp[3]`, its value is also 5. -- When updating `dp[4]`, the student uses the newly obtained `dp[2]`, producing `dp[4] = 10`. +- Updating `dp[2]` gives 5. +- Updating `dp[3]` also gives 5. +- When updating `dp[4]`, the student uses the newly computed `dp[2]`, obtaining `dp[4] = 10`. 1. Is `dp[4] = 10` correct? Why or why not? -2. Given that each item may be selected at most once, what should `dp[4]` be? -3. When processing each item, should capacities be updated from largest to smallest or from smallest to largest? What problem does this avoid? +2. Given that each item can be taken at most once, what should `dp[4]` be? +3. When processing each item, should capacities be updated from largest to smallest or from smallest to largest? What problem does this prevent? -??? success "Answer" +??? success "Reference answer" - 1. The result is incorrect. A value of 10 is equivalent to placing the item with value 5 into the knapsack twice, - violating the condition that each item may be selected at most once. + 1. The result is incorrect. A value of 10 means taking the item worth 5 twice, + which violates the condition that each item can be taken at most once. - 2. The knapsack can contain at most this one item, so the correct value of `dp[4]` is 5. + 2. The knapsack can contain only this one item, so the correct value of `dp[4]` is 5. - 3. Capacities should be updated from largest to smallest, in the order 4, 3, 2. - Then, when calculating `dp[c]`, the value read from `dp[c-2]` still comes from before the current item was processed, - preventing the current item from being reused during the same round. + 3. Update capacities from largest to smallest: 4, then 3, then 2. + This ensures that when calculating `dp[c]`, the value read from `dp[c-2]` is still the result from before the current item was processed. + The current item therefore cannot be used more than once in the same round of updates. -## Programming Exercises +## Programming exercises -### Number of Ways to Climb Stairs +### Counting ways to climb stairs -A staircase has `n` steps. Each move climbs either 1 or 2 steps, and you must land exactly on step `n`. -Calculate the number of distinct ways to reach the top. Assume `n >= 1`; ways are distinguished only by their sequence of 1-step and 2-step moves. -Use a one-dimensional dynamic programming array. For now, do not use the space optimization that keeps only two states. +A staircase has `n` steps. You can climb either 1 or 2 steps at a time and must land exactly on step `n`. +Calculate the number of different ways to climb the staircase. Assume `n >= 1`; two ways differ only in their sequence of 1-step and 2-step moves. +Use a one-dimensional dynamic programming array. For now, do not use the space optimization that retains only two states. ??? tip "Hints" - 1. The last move to step i can cover only 1 or 2 steps - 2. Therefore, dp[i] = dp[i-1] + dp[i-2] - 3. Handle the cases where n is 1 or 2 first, then fill the table starting from step 3 + 1. The final move to step i must be either a 1-step or a 2-step move. + 2. This gives dp[i] = dp[i-1] + dp[i-2]. + 3. Handle n = 1 and n = 2 first, then fill the table starting at step 3. [LeetCode](https://leetcode.com/problems/climbing-stairs/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } -### 0-1 Knapsack +### 0-1 knapsack -You are given equal-length arrays `wgt` and `val`. Item `i` has positive integer weight `wgt[i]` and non-negative integer value `val[i]`. -The knapsack capacity `cap` is a non-negative integer. Each item may be selected at most once. Find the maximum total value that can be placed in the knapsack -without exceeding `cap`. Use one-dimensional dynamic programming. +You are given arrays `wgt` and `val` of equal length. Item `i` has a positive integer weight `wgt[i]` and a nonnegative integer value `val[i]`. +The knapsack capacity `cap` is a nonnegative integer. Each item can be taken at most once. Find the maximum total value +that fits in the knapsack without exceeding `cap` in total weight. Use one-dimensional dynamic programming. ??? tip "Hints" - 1. Initialize an array dp of length cap + 1, where dp[c] is the maximum value for a capacity limit of c - 2. When processing item i, compare dp[c], which does not select it, with dp[c-wgt[i]] + val[i], which does - 3. Update capacities from largest to smallest to avoid selecting the current item repeatedly in the same round + 1. Initialize an array dp of length cap + 1, where dp[c] is the maximum value obtainable with a capacity limit of c. + 2. When processing item i, compare dp[c] for leaving it out with dp[c-wgt[i]] + val[i] for taking it. + 3. Update capacities from largest to smallest to avoid taking the current item more than once in the same round. diff --git a/en/docs/chapter_dynamic_programming/index.md b/en/docs/chapter_dynamic_programming/index.md index 1e62e80f2..2781754df 100644 --- a/en/docs/chapter_dynamic_programming/index.md +++ b/en/docs/chapter_dynamic_programming/index.md @@ -1,9 +1,9 @@ -# Dynamic Programming +# Dynamic programming ![Dynamic programming](../assets/covers/chapter_dynamic_programming.jpg) !!! abstract - Streams flow into rivers, rivers flow into the sea. - - Dynamic programming combines solutions to small problems into the answer to a large problem, leading us step by step to the other shore of problem-solving. + Streams flow into rivers, and rivers flow into the sea. + + Dynamic programming brings together solutions to small problems to answer a larger one, guiding us step by step to the other shore. diff --git a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png index a5da3474b..f8299e9b3 100644 Binary files a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png and b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png differ diff --git a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index fc855daac..21100328c 100644 --- a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -1,67 +1,67 @@ -# Introduction to Dynamic Programming +# Introduction to dynamic programming -Dynamic programming is an important algorithmic paradigm that decomposes a problem into a series of smaller subproblems and avoids redundant computation by storing the solutions to subproblems, thereby significantly improving time efficiency. +Dynamic programming is an important algorithmic paradigm. It breaks a problem into smaller subproblems and stores their solutions to avoid repeated computation, greatly improving time efficiency. -In this section, we start with a classic example, first presenting its brute force backtracking solution, observing the overlapping subproblems within it, and then gradually deriving a more efficient dynamic programming solution. +We will start with a classic example, give a brute-force backtracking solution, identify its overlapping subproblems, and gradually derive a more efficient dynamic programming solution. !!! question "Climbing stairs" - Given a staircase with $n$ steps, where you can climb $1$ or $2$ steps at a time, how many different ways are there to reach the top? + Given a staircase with $n$ steps, you may climb $1$ or $2$ steps at a time. How many ways are there to reach the top? -As shown in the figure below, for a $3$-step staircase, there are $3$ different ways to reach the top. +As shown in the figure below, a staircase with $3$ steps has $3$ possible ways to reach the top. -![Number of ways to reach the 3rd step](intro_to_dynamic_programming.assets/climbing_stairs_example.png) +![Number of ways to reach step 3](intro_to_dynamic_programming.assets/climbing_stairs_example.png) -The goal of this problem is to determine the number of ways, so **we can consider using backtracking to enumerate all possibilities**. Specifically, imagine climbing stairs as a multi-round selection process: starting from the ground, choosing to go up $1$ or $2$ steps in each round, incrementing the count by $1$ whenever the top of the stairs is reached, and pruning when exceeding the top. The code is as follows: +The goal is to count solutions, so **we can use backtracking to enumerate every possibility**. View climbing as a sequence of choices: starting from the ground, choose to climb $1$ or $2$ steps each time. Add $1$ to the count whenever we reach the top, and prune any branch that goes beyond it. The code is as follows: ```src [file]{climbing_stairs_backtrack}-[class]{}-[func]{climbing_stairs_backtrack} ``` -## Method 1: Brute Force Search +## Method 1: Brute-force search -Backtracking algorithms typically do not explicitly decompose problems, but rather treat solving the problem as a series of decision steps, searching for all possible solutions through trial and pruning. +Backtracking usually does not explicitly decompose the problem. Instead, it treats a solution as a sequence of decisions and searches all possible solutions through trial and pruning. -We can try to analyze this problem from the perspective of problem decomposition. Let the number of ways to climb to the $i$-th step be $dp[i]$, then $dp[i]$ is the original problem, and its subproblems include: +Let's analyze this problem through decomposition. Let $dp[i]$ be the number of ways to reach step $i$. The original problem is $dp[i]$, and its subproblems include: $$ dp[i-1], dp[i-2], \dots, dp[2], dp[1] $$ -Since we can only go up $1$ or $2$ steps in each round, when we stand on the $i$-th step, we could only have been on the $i-1$-th or $i-2$-th step in the previous round. In other words, we can only reach the $i$-th step from the $i-1$-th or $i-2$-th step. +Since each move climbs $1$ or $2$ steps, when we stand on step $i$, we must previously have been on step $i - 1$ or $i - 2$. In other words, step $i$ can only be reached from step $i -1$ or $i - 2$. -This leads to an important conclusion: **the number of ways to climb to the $i-1$-th step plus the number of ways to climb to the $i-2$-th step equals the number of ways to climb to the $i$-th step**. The formula is as follows: +This gives an important result: **the number of ways to reach step $i - 1$ plus the number of ways to reach step $i - 2$ equals the number of ways to reach step $i$**. In symbols: $$ dp[i] = dp[i-1] + dp[i-2] $$ -This means that in the stair climbing problem, there exists a recurrence relation among the subproblems, and **the solution to the original problem can be constructed from the solutions to the subproblems**. The figure below illustrates this recurrence relation. +Thus, the subproblems are linked by a recurrence: **the solution to the original problem can be constructed from subproblem solutions**. The figure below illustrates this relationship. -![Recurrence relation for the number of ways](intro_to_dynamic_programming.assets/climbing_stairs_state_transfer.png) +![Recurrence for the number of ways](intro_to_dynamic_programming.assets/climbing_stairs_state_transfer.png) -We can obtain a brute force search solution based on the recurrence formula. Starting from $dp[n]$, **recursively decompose a larger problem into the sum of two smaller problems**, until reaching the smallest subproblems $dp[1]$ and $dp[2]$ and returning. Among them, the solutions to the smallest subproblems are known, namely $dp[1] = 1$ and $dp[2] = 2$, representing $1$ and $2$ ways to climb to the $1$st and $2$nd steps, respectively. +The recurrence gives a brute-force search solution. Starting at $dp[n]$, **recursively express a larger problem as the sum of two smaller ones** until reaching $dp[1]$ or $dp[2]$. Their solutions are known: $dp[1] = 1$ and $dp[2] = 2$, meaning steps $1$ and $2$ can be reached in $1$ and $2$ ways, respectively. -Observe the following code: like standard backtracking code, it also uses depth-first search but is more concise: +Like the standard backtracking code, the following code performs depth-first search, but is more concise: ```src [file]{climbing_stairs_dfs}-[class]{}-[func]{climbing_stairs_dfs} ``` -The figure below shows the recursion tree formed by brute force search. For the problem $dp[n]$, the depth of its recursion tree is $n$, with a time complexity of $O(2^n)$. Exponential growth is explosive; if we input a relatively large $n$, the wait can be very long. +The figure below shows the recursion tree for brute-force search. For $dp[n]$, its depth is $n$, and the time complexity is $O(2^n)$. Exponential growth is explosive, so a relatively large $n$ leads to a very long wait. ![Recursion tree for climbing stairs](intro_to_dynamic_programming.assets/climbing_stairs_dfs_tree.png) -Observing the above figure, **the exponential time complexity is caused by "overlapping subproblems"**. For example, $dp[9]$ is decomposed into $dp[8]$ and $dp[7]$, and $dp[8]$ is decomposed into $dp[7]$ and $dp[6]$, both of which contain the subproblem $dp[7]$. +As the figure above shows, **overlapping subproblems cause the exponential time complexity**. For example, $dp[9]$ splits into $dp[8]$ and $dp[7]$, while $dp[8]$ splits into $dp[7]$ and $dp[6]$. Both contain $dp[7]$. -And so on, subproblems contain smaller overlapping subproblems, ad infinitum. The vast majority of computational resources are wasted on these overlapping subproblems. +These subproblems in turn contain smaller overlapping subproblems, and the pattern repeats at deeper levels of the recursion tree. Most of the computation is wasted on solving the same subproblems repeatedly. -## Method 2: Memoization +## Method 2: Memoized search -To improve algorithm efficiency, **we want all overlapping subproblems to be computed only once**. For this purpose, we declare an array `mem` to record the solution to each subproblem and prune overlapping subproblems during the search process. +To improve efficiency, **we want every overlapping subproblem to be solved only once**. Introduce an array `mem` to record each solution and prune repeated subproblems during the search. -1. When computing $dp[i]$ for the first time, we record it in `mem[i]` for later use. -2. When we need to compute $dp[i]$ again, we can directly retrieve the result from `mem[i]`, thereby avoiding redundant computation of that subproblem. +1. The first time we calculate $dp[i]$, store it in `mem[i]` for later use. +2. When $dp[i]$ is needed again, retrieve it directly from `mem[i]` instead of recomputing it. The code is as follows: @@ -69,42 +69,42 @@ The code is as follows: [file]{climbing_stairs_dfs_mem}-[class]{}-[func]{climbing_stairs_dfs_mem} ``` -Observe the figure below: **after memoization, all overlapping subproblems need to be computed only once, reducing the time complexity to $O(n)$**, which is a tremendous leap. +As shown in the figure below, **memoization makes every overlapping subproblem require only one computation, reducing the time complexity to $O(n)$**: a major improvement. -![Recursion tree with memoization](intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png) +![Recursion tree for memoized search](intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png) -## Method 3: Dynamic Programming +## Method 3: Dynamic programming -**Memoization is a "top-down" method**: we start from the original problem (root node), recursively decompose larger subproblems into smaller ones, until reaching the smallest known subproblems (leaf nodes). Afterward, by backtracking, we collect the solutions to the subproblems layer by layer to construct the solution to the original problem. +**Memoized search is a top-down approach**: start with the original problem (the root), recursively divide larger subproblems into smaller ones until reaching the smallest subproblems with known solutions (the leaves), then collect their solutions as recursion returns to construct the solution to the original problem. -In contrast, **dynamic programming is a "bottom-up" method**: starting from the solutions to the smallest subproblems, iteratively constructing solutions to larger subproblems until obtaining the solution to the original problem. +In contrast, **dynamic programming is a bottom-up approach**: start with the smallest subproblem solutions and iteratively build solutions to larger subproblems until the original problem is solved. -Since dynamic programming does not include a backtracking process, it only requires loop iteration for implementation and does not need recursion. In the following code, we initialize an array `dp` to store the solutions to subproblems, which serves the same recording function as the array `mem` in memoization: +Because this dynamic programming process does not backtrack, it needs only iterative loops, not recursion. In the following code, the array `dp` stores subproblem solutions, serving the same role as `mem` in memoized search: ```src [file]{climbing_stairs_dp}-[class]{}-[func]{climbing_stairs_dp} ``` -The figure below simulates the execution process of the above code. +The figure below traces the code's execution. -![Dynamic programming process for climbing stairs](intro_to_dynamic_programming.assets/climbing_stairs_dp.png) +![Dynamic programming for climbing stairs](intro_to_dynamic_programming.assets/climbing_stairs_dp.png) -Like backtracking algorithms, dynamic programming also uses the "state" concept to represent specific stages of problem solving, with each state corresponding to a subproblem and its corresponding local optimal solution. For example, the state in the stair climbing problem is defined as the current stair step number $i$. +Like backtracking, dynamic programming uses states to represent stages of solving a problem. Each state corresponds to a subproblem and its locally optimal solution. For climbing stairs, the state is the current step number $i$. -Based on the above content, we can summarize the commonly used terminology in dynamic programming. +We can now summarize some common dynamic programming terminology. -- The array `dp` is called the dp table, where $dp[i]$ represents the solution to the subproblem corresponding to state $i$. -- The states corresponding to the smallest subproblems (the $1$st and $2$nd steps) are called initial states. -- The recurrence formula $dp[i] = dp[i-1] + dp[i-2]$ is called the state transition equation. +- The array `dp` is called the dp table, and $dp[i]$ stores the solution to the subproblem for state $i$. +- The states for the smallest subproblems (steps $1$ and $2$) are called the initial states. +- The recurrence $dp[i] = dp[i-1] + dp[i-2]$ is called the state transition equation. -## Space Optimization +## Space optimization -Observant readers may have noticed that **since $dp[i]$ is only related to $dp[i-1]$ and $dp[i-2]$, we do not need to use an array `dp` to store the solutions to all subproblems**, and can instead use two variables that roll forward. The code is as follows: +You may have noticed that **$dp[i]$ depends only on $dp[i-1]$ and $dp[i-2]$, so we do not need an entire `dp` array storing every subproblem solution**. Two variables updated as we proceed are enough. The code is as follows: ```src [file]{climbing_stairs_dp}-[class]{}-[func]{climbing_stairs_dp_comp} ``` -As the above code shows, by eliminating the space occupied by the array `dp`, the space complexity is reduced from $O(n)$ to $O(1)$. +Removing the `dp` array reduces space complexity from $O(n)$ to $O(1)$. -In dynamic programming problems, the current state often depends only on a limited number of preceding states, allowing us to retain only the necessary states and save memory space through "dimension reduction". **This space optimization technique is called "rolling variable" or "rolling array"**. +In dynamic programming, a state often depends on only a limited number of preceding states. We can keep just those needed, reducing the storage dimensions to save memory. **This technique is called using rolling variables or a rolling array**. diff --git a/en/docs/chapter_dynamic_programming/knapsack_problem.assets/knapsack_dp_step14.png b/en/docs/chapter_dynamic_programming/knapsack_problem.assets/knapsack_dp_step14.png index 2bafd6a17..ba6d6bec4 100644 Binary files a/en/docs/chapter_dynamic_programming/knapsack_problem.assets/knapsack_dp_step14.png and b/en/docs/chapter_dynamic_programming/knapsack_problem.assets/knapsack_dp_step14.png differ diff --git a/en/docs/chapter_dynamic_programming/knapsack_problem.md b/en/docs/chapter_dynamic_programming/knapsack_problem.md index 6599d02ed..fedbae277 100644 --- a/en/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/en/docs/chapter_dynamic_programming/knapsack_problem.md @@ -1,97 +1,97 @@ -# 0-1 Knapsack Problem +# 0-1 knapsack problem -The knapsack problem is an excellent introductory problem for dynamic programming and is one of the most common problem forms in dynamic programming. It has many variants, such as the 0-1 knapsack problem, the unbounded knapsack problem, and the multiple knapsack problem. +The knapsack problem is an excellent introduction to dynamic programming and one of its most common problem families. Variants include 0-1 knapsack, unbounded knapsack, and bounded knapsack. -In this section, we will first solve the most common 0-1 knapsack problem. +We will begin with the most common variant, 0-1 knapsack. !!! question - Given $n$ items and a knapsack with capacity $cap$, where the weight and value of the $i$-th item are $wgt[i-1]$ and $val[i-1]$, respectively. Each item can be selected at most once. What is the maximum value that can fit in the knapsack under the capacity limit? + Given $n$ items, where item $i$ has weight $wgt[i-1]$ and value $val[i-1]$, and a knapsack of capacity $cap$, each item may be chosen only once. What is the maximum total value that fits within the capacity? -Observe the figure below. Since item number $i$ starts counting from $1$ and array indices start from $0$, item $i$ corresponds to weight $wgt[i-1]$ and value $val[i-1]$. +As shown in the figure below, item numbers $i$ start at $1$, while array indices start at $0$. Item $i$ therefore has weight $wgt[i-1]$ and value $val[i-1]$. -![Example data for 0-1 knapsack](knapsack_problem.assets/knapsack_example.png) +![Example input for 0-1 knapsack](knapsack_problem.assets/knapsack_example.png) -We can view the 0-1 knapsack problem as a process consisting of $n$ rounds of decisions, where for each item there are two decisions: not putting it in and putting it in, thus the problem satisfies the decision tree model. +We can view 0-1 knapsack as $n$ rounds of decisions, with two choices for each item: leave it out or put it in. It therefore fits the decision tree model. -The goal of this problem is to find "the maximum value that can be placed in the knapsack within the capacity limit", so it is more likely to be a dynamic programming problem. +The objective is to maximize value within a capacity limit, making dynamic programming a promising approach. -**Step 1: Think about the decisions in each round, define the state, and thus obtain the $dp$ table** +**Step 1: Identify each decision, define the state, and establish the $dp$ table** -For each item, if not placed in the knapsack, the knapsack capacity remains unchanged; if placed in, the knapsack capacity decreases. From this, we can derive the state definition: current item number $i$ and knapsack capacity $c$, denoted as $[i, c]$. +Leaving an item out keeps the available capacity unchanged; putting it in reduces the capacity. The state therefore consists of the current item number $i$ and capacity $c$, denoted by $[i, c]$. -State $[i, c]$ corresponds to the subproblem: **the maximum value among the first $i$ items in a knapsack of capacity $c$**, denoted as $dp[i, c]$. +State $[i, c]$ represents the subproblem of finding **the maximum value obtainable from the first $i$ items with capacity $c$**, denoted by $dp[i, c]$. -What we need to find is $dp[n, cap]$, so we need a two-dimensional $dp$ table of size $(n+1) \times (cap+1)$. +We seek $dp[n, cap]$, so we need a two-dimensional $dp$ table of size $(n+1) \times (cap+1)$. -**Step 2: Identify the optimal substructure, and then derive the state transition equation** +**Step 2: Identify optimal substructure and derive the state transition equation** -After making the decision for item $i$, what remains is the subproblem of the first $i-1$ items, which can be divided into the following two cases. +After deciding what to do with item $i$, the remaining subproblem concerns the first $i-1$ items. There are two cases. -- **Not putting item $i$**: The knapsack capacity remains unchanged, and the state changes to $[i-1, c]$. -- **Putting item $i$**: The knapsack capacity decreases by $wgt[i-1]$, the value increases by $val[i-1]$, and the state changes to $[i-1, c-wgt[i-1]]$. +- **Leave item $i$ out**: Capacity stays unchanged, and the remaining state is $[i-1, c]$. +- **Put item $i$ in**: Capacity decreases by $wgt[i-1]$, value increases by $val[i-1]$, and the remaining state is $[i-1, c-wgt[i-1]]$. -The above analysis reveals the optimal substructure of this problem: **the maximum value $dp[i, c]$ equals the greater of the values obtained by not putting item $i$ into the knapsack and by putting it into the knapsack**. From this, the state transition equation can be derived: +This reveals the optimal substructure: **the maximum value $dp[i, c]$ is the larger value obtained by leaving item $i$ out or putting item $i$ in**. The state transition equation is: $$ dp[i, c] = \max(dp[i-1, c], dp[i-1, c - wgt[i-1]] + val[i-1]) $$ -Note that if the weight of the current item $wgt[i - 1]$ exceeds the remaining knapsack capacity $c$, then the only option is not to put it in the knapsack. +If the current item's weight $wgt[i - 1]$ exceeds the remaining capacity $c$, the only option is to leave it out. -**Step 3: Determine boundary conditions and state transition order** +**Step 3: Determine the boundary conditions and evaluation order** -When there are no items or the knapsack capacity is $0$, the maximum value is $0$, i.e., the first column $dp[i, 0]$ and the first row $dp[0, c]$ are both equal to $0$. +With no items or capacity $0$, the maximum value is $0$. Thus, the first column $dp[i, 0]$ and first row $dp[0, c]$ are both $0$. -The current state $[i, c]$ transitions from the state above $[i-1, c]$ and the upper-left state $[i-1, c-wgt[i-1]]$, so we can traverse the entire $dp$ table in forward order using two nested loops. +The current state $[i, c]$ depends on $[i-1, c]$ above and $[i-1, c-wgt[i-1]]$ to the upper left, so two nested loops can traverse the $dp$ table in increasing index order. -Based on the above analysis, we will next implement the brute force search, memoization, and dynamic programming solutions in order. +We will now implement brute-force search, memoized search, and dynamic programming in that order. -### Method 1: Brute Force Search +### Method 1: Brute-force search -The search code includes the following elements. +The search has the following elements. -- **Recursive parameters**: state $[i, c]$. -- **Return value**: solution to the subproblem $dp[i, c]$. -- **Termination condition**: when there are no items left ($i = 0$) or the remaining knapsack capacity is $0$, terminate the recursion and return value $0$. -- **Pruning**: if the weight of the current item exceeds the remaining knapsack capacity, only the option of not putting it in is available. +- **Parameters**: The state $[i, c]$. +- **Return value**: The subproblem solution $dp[i, c]$. +- **Base case**: If there are no items left, $i = 0$, or the remaining capacity is $0$, stop recursion and return value $0$. +- **Pruning**: If the current item is heavier than the remaining capacity, only the choice to leave it out is allowed. ```src [file]{knapsack}-[class]{}-[func]{knapsack_dfs} ``` -As shown in the figure below, since each item generates two search branches, excluding it and including it, the time complexity is $O(2^n)$. +As shown in the figure below, each item creates two branches, excluding it or including it, so the time complexity is $O(2^n)$. -Observing the recursion tree, it is easy to see overlapping subproblems, such as $dp[1, 10]$. When there are many items, large knapsack capacity, and especially many items with the same weight, the number of overlapping subproblems will increase significantly. +The recursion tree contains overlapping subproblems, such as $dp[1, 10]$. Their number grows substantially with more items and larger capacity, especially when many items share the same weight. -![Brute force search recursion tree for 0-1 knapsack problem](knapsack_problem.assets/knapsack_dfs.png) +![Brute-force recursion tree for 0-1 knapsack](knapsack_problem.assets/knapsack_dfs.png) -### Method 2: Memoization +### Method 2: Memoized search -To ensure that overlapping subproblems are only computed once, we use a memo list `mem` to record the solutions to subproblems, where `mem[i][c]` corresponds to $dp[i, c]$. +To solve each overlapping subproblem only once, use a memoization table `mem`, where `mem[i][c]` stores $dp[i, c]$. -After introducing memoization, **the time complexity depends on the number of subproblems**, which is $O(n \times cap)$. The implementation code is as follows: +With memoization, **the time complexity depends on the number of subproblems**, giving $O(n \times cap)$. The code is as follows: ```src [file]{knapsack}-[class]{}-[func]{knapsack_dfs_mem} ``` -The figure below shows the search branches pruned in memoization. +The figure below shows the branches pruned by memoized search. -![Memoization recursion tree for 0-1 knapsack problem](knapsack_problem.assets/knapsack_dfs_mem.png) +![Memoized recursion tree for 0-1 knapsack](knapsack_problem.assets/knapsack_dfs_mem.png) -### Method 3: Dynamic Programming +### Method 3: Dynamic programming -Dynamic programming is essentially the process of filling the $dp$ table during state transitions. The code is as follows: +Dynamic programming essentially fills the $dp$ table using state transitions, as shown in the code below: ```src [file]{knapsack}-[class]{}-[func]{knapsack_dp} ``` -As shown in the figure below, both time complexity and space complexity are determined by the size of the array `dp`, which is $O(n \times cap)$. +As shown in the figure below, both time and space complexity are determined by the size of `dp`: $O(n \times cap)$. === "<1>" - ![Dynamic programming process for 0-1 knapsack problem](knapsack_problem.assets/knapsack_dp_step1.png) + ![Dynamic programming for 0-1 knapsack](knapsack_problem.assets/knapsack_dp_step1.png) === "<2>" ![knapsack_dp_step2](knapsack_problem.assets/knapsack_dp_step2.png) @@ -132,19 +132,19 @@ As shown in the figure below, both time complexity and space complexity are dete === "<14>" ![knapsack_dp_step14](knapsack_problem.assets/knapsack_dp_step14.png) -### Space Optimization +### Space optimization -Since each state is only related to the state in the row above it, we can use two arrays rolling forward to reduce the space complexity from $O(n \times cap)$ to $O(cap)$. +Each state depends only on the previous row, so two rolling arrays reduce space complexity from $O(n \times cap)$ to $O(cap)$. -Further thinking, can we achieve space optimization using just one array? Observing, we can see that each state is transferred from the cell directly above or the cell in the upper-left. If there is only one array, when we start traversing row $i$, that array still stores the state of row $i-1$. +Can we go further and use just one array? Each state depends on entries directly above or to the upper left. With one array, when we begin processing row $i$, it still holds the states from row $i-1$. -- If using forward traversal, then when traversing to $dp[i, j]$, the values in the upper-left $dp[i-1, 1]$ ~ $dp[i-1, j-1]$ may have already been overwritten, thus preventing correct state transition. -- If using reverse traversal, there will be no overwriting issue, and state transition can proceed correctly. +- With forward iteration, by the time we reach $dp[i, j]$, the upper-left values $dp[i-1, 1]$ ~ $dp[i-1, j-1]$ may have been overwritten, making the transition incorrect. +- With backward iteration, these values are not overwritten before they are needed, so transitions remain correct. -The figure below shows the transition process from row $i = 1$ to row $i = 2$ using a single array. Please consider the difference between forward and reverse traversal. +The figure below shows one array changing from row $i = 1$ to row $i = 2$. Consider how forward and backward iteration differ. === "<1>" - ![Space-optimized dynamic programming process for 0-1 knapsack](knapsack_problem.assets/knapsack_dp_comp_step1.png) + ![Space-optimized dynamic programming for 0-1 knapsack](knapsack_problem.assets/knapsack_dp_comp_step1.png) === "<2>" ![knapsack_dp_comp_step2](knapsack_problem.assets/knapsack_dp_comp_step2.png) @@ -161,7 +161,7 @@ The figure below shows the transition process from row $i = 1$ to row $i = 2$ us === "<6>" ![knapsack_dp_comp_step6](knapsack_problem.assets/knapsack_dp_comp_step6.png) -In the code implementation, we simply need to delete the first dimension $i$ of the array `dp` and change the inner loop to reverse traversal: +In the implementation, simply remove the first dimension $i$ from `dp` and reverse the inner loop: ```src [file]{knapsack}-[class]{}-[func]{knapsack_dp_comp} diff --git a/en/docs/chapter_dynamic_programming/summary.md b/en/docs/chapter_dynamic_programming/summary.md index 3fc0f18c9..26912cbaa 100644 --- a/en/docs/chapter_dynamic_programming/summary.md +++ b/en/docs/chapter_dynamic_programming/summary.md @@ -1,25 +1,25 @@ # Summary -### Key Points +### Key takeaways -- Dynamic programming decomposes problems and avoids redundant computation by storing the solutions to subproblems, thereby significantly improving computational efficiency. -- Without considering time constraints, all dynamic programming problems can be solved using backtracking (brute force search), but the recursion tree contains a large number of overlapping subproblems, resulting in extremely low efficiency. By introducing a memo list, we can store the solutions to all computed subproblems, ensuring that overlapping subproblems are only computed once. -- Memoization is a top-down recursive solution, while the corresponding dynamic programming is a bottom-up iterative solution, similar to "filling in a table". Since the current state only depends on certain local states, we can eliminate one dimension of the $dp$ table to reduce space complexity. -- Subproblem decomposition is a general algorithmic approach, with different properties in divide and conquer, dynamic programming, and backtracking. -- Dynamic programming problems have three major characteristics: overlapping subproblems, optimal substructure, and no aftereffects. -- If the optimal solution to the original problem can be constructed from the optimal solutions to the subproblems, then it has optimal substructure. -- No aftereffects means that for a given state, its future development is only related to that state and has nothing to do with all past states. Many combinatorial optimization problems do not satisfy this property and cannot be solved efficiently using dynamic programming. +- Dynamic programming decomposes problems and stores subproblem solutions to avoid repeated computation and improve efficiency. +- Without a time limit, every dynamic programming problem can be solved by backtracking (brute-force search). However, the recursion tree contains many overlapping subproblems, making it very inefficient. Memoization stores computed solutions so that each overlapping subproblem is solved only once. +- Memoized search is a top-down recursive approach, while the corresponding dynamic programming solution works bottom up, like filling a table. Since the current state depends only on certain nearby states, removing a dimension from the $dp$ table can reduce space complexity. +- Decomposing a problem into subproblems is a general algorithmic idea with different characteristics in divide and conquer, dynamic programming, and backtracking. +- Dynamic programming problems have three key properties: overlapping subproblems, optimal substructure, and the no-aftereffect property. +- A problem has optimal substructure if its optimal solution can be constructed from optimal solutions to subproblems. +- The no-aftereffect property means a state's future depends only on that state, not on the history leading to it. Many combinatorial optimization problems lack this property and cannot be solved quickly with dynamic programming. -**Knapsack problem** +**Knapsack problems** -- The knapsack problem is one of the most typical dynamic programming problems, with variants such as the 0-1 knapsack, unbounded knapsack, and multiple knapsack. -- The state definition for the 0-1 knapsack is the maximum value achievable using the first $i$ items with a knapsack capacity of $c$. Based on the two decisions of not putting an item in the knapsack and putting it in, the optimal substructure can be identified and the state transition equation constructed. In space optimization, since each state depends on the state directly above and to the upper-left, the list needs to be traversed in reverse order to avoid overwriting the upper-left state. -- The unbounded knapsack problem has no limit on the selection quantity of each type of item, so the state transition for choosing to put in an item differs from the 0-1 knapsack problem. Since the state depends on the state directly above and directly to the left, space optimization should use forward traversal. -- The coin change problem is a variant of the unbounded knapsack problem. It changes from seeking the "maximum" value to seeking the "minimum" number of coins, so $\max()$ in the state transition equation should be changed to $\min()$. It changes from seeking "not exceeding" the knapsack capacity to seeking "exactly" making up the target amount, so $amt + 1$ is used to represent the invalid solution of "unable to make up the target amount". -- Coin change problem II changes from seeking the "minimum number of coins" to seeking the "number of coin combinations", so the state transition equation correspondingly changes from $\min()$ to a summation operator. +- Knapsack is one of the most typical dynamic programming problem families, with variants including 0-1, unbounded, and bounded knapsack. +- In 0-1 knapsack, the state value is the maximum value obtainable from the first $i$ items within capacity $c$. The choices to exclude or include an item reveal optimal substructure and give the state transition equation. After space optimization, backward iteration is necessary because each state depends on entries above and to the upper left, which must not be overwritten prematurely. +- Unbounded knapsack allows unlimited copies of each item, changing the transition for including an item. Because a state depends on entries directly above and to the left, space optimization requires forward iteration. +- Coin change is a variant of unbounded knapsack. Minimizing the number of coins instead of maximizing value changes $\max()$ to $\min()$ in the transition. Requiring the target amount exactly, rather than staying within a capacity, calls for using $amt + 1$ to represent an infeasible solution. +- Coin change II counts combinations instead of minimizing coin count, so the transition replaces $\min()$ with addition. -**Edit distance problem** +**Edit distance** -- Edit distance (Levenshtein distance) is used to measure the similarity between two strings, defined as the minimum number of edit steps from one string to another, with edit operations including insert, delete, and replace. -- The state definition for the edit distance problem is the minimum number of edit steps required to change the first $i$ characters of $s$ into the first $j$ characters of $t$. When $s[i-1] \ne t[j-1]$, there are three decisions: insert, delete, replace, each with corresponding remaining subproblems. From this, the optimal substructure can be identified and the state transition equation constructed. When $s[i-1] = t[j-1]$, no edit is required for the current character. -- In edit distance, the state depends on the state directly above, directly to the left, and to the upper-left, so after space optimization, neither forward nor reverse traversal can correctly perform state transitions. For this reason, we use a variable to temporarily store the upper-left state, thus transforming to a situation equivalent to the unbounded knapsack problem, allowing for forward traversal after space optimization. +- Edit distance (Levenshtein distance) measures similarity between strings. It is the minimum number of edits needed to turn one string into another, allowing insertion, deletion, and replacement. +- The edit distance state is the minimum number of edits needed to transform the first $i$ characters of $s$ into the first $j$ characters of $t$. When $s[i-1] \ne t[j-1]$, insertion, deletion, and replacement each leave a corresponding subproblem, revealing optimal substructure and the state transition equation. When $s[i-1] = t[j-1]$, no edit is needed for those characters. +- Edit distance depends on states above, to the left, and to the upper left, so neither forward nor backward iteration alone works after space optimization. A temporary variable preserves the upper-left state, reducing the situation to one like unbounded knapsack and allowing forward iteration. diff --git a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.assets/coin_change_ii_example.png b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.assets/coin_change_ii_example.png index d703fed87..28684c0c3 100644 Binary files a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.assets/coin_change_ii_example.png and b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.assets/coin_change_ii_example.png differ diff --git a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index 1243fe9da..d8e1416b0 100644 --- a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -1,49 +1,49 @@ -# Unbounded Knapsack Problem +# Unbounded knapsack problem -In this section, we first solve another common knapsack problem: the unbounded knapsack, and then explore a special case of it: the coin change problem. +We will now solve another common knapsack variant, unbounded knapsack, and then examine a special case: coin change. -## Unbounded Knapsack Problem +## Unbounded knapsack !!! question - Given $n$ items, where the weight of the $i$-th item is $wgt[i-1]$ and its value is $val[i-1]$, and a knapsack with capacity $cap$. **Each item can be selected multiple times**. What is the maximum value that can be placed in the knapsack within the capacity limit? An example is shown in the figure below. + Given $n$ items, where item $i$ has weight $wgt[i-1]$ and value $val[i-1]$, and a knapsack of capacity $cap$, **each item may be chosen repeatedly**. What is the maximum value that fits within the capacity? An example is shown in the figure below. -![Example data for unbounded knapsack problem](unbounded_knapsack_problem.assets/unbounded_knapsack_example.png) +![Example input for unbounded knapsack](unbounded_knapsack_problem.assets/unbounded_knapsack_example.png) -### Dynamic Programming Approach +### Dynamic programming approach -The unbounded knapsack problem is very similar to the 0-1 knapsack problem, **differing only in that there is no limit on the number of times an item can be selected**. +Unbounded knapsack is very similar to 0-1 knapsack. **The only difference is that there is no limit on how many times an item may be chosen**. -- In the 0-1 knapsack problem, there is only one of each type of item, so after placing item $i$ in the knapsack, we can only choose from the first $i-1$ items. -- In the unbounded knapsack problem, the quantity of each type of item is unlimited, so after placing item $i$ in the knapsack, **we can still choose from the first $i$ items**. +- In 0-1 knapsack, there is only one of each item, so after including item $i$, we can choose only from the first $i-1$ items. +- In unbounded knapsack, each item has an unlimited supply, so after including item $i$, **we can still choose from the first $i$ items**. -Under the rules of the unbounded knapsack problem, the changes in state $[i, c]$ are divided into two cases. +For unbounded knapsack, state $[i, c]$ has two cases. -- **Not putting item $i$**: Same as the 0-1 knapsack problem, transfer to $[i-1, c]$. -- **Putting item $i$**: Different from the 0-1 knapsack problem, transfer to $[i, c-wgt[i-1]]$. +- **Leave item $i$ out**: As in 0-1 knapsack, the remaining state is $[i-1, c]$. +- **Put item $i$ in**: Unlike 0-1 knapsack, the remaining state is $[i, c-wgt[i-1]]$. -Thus, the state transition equation becomes: +The state transition equation becomes: $$ dp[i, c] = \max(dp[i-1, c], dp[i, c - wgt[i-1]] + val[i-1]) $$ -### Code Implementation +### Code implementation -Comparing the code for the two problems, there is one change in state transition from $i-1$ to $i$, with everything else identical: +Compared with 0-1 knapsack, only one $i-1$ in the transition changes to $i$; the rest of the code is identical: ```src [file]{unbounded_knapsack}-[class]{}-[func]{unbounded_knapsack_dp} ``` -### Space Optimization +### Space optimization -Since the current state is transferred from states on the left and above, **after space optimization, each row in the $dp$ table should be traversed in forward order**. +The current state depends on states to its left and above, **so after space optimization, each row of the $dp$ table must be processed in forward order**. -This traversal order is exactly opposite to the 0-1 knapsack. Please refer to the figure below to understand the difference between the two. +This is the opposite of 0-1 knapsack. Use the figure below to understand the difference. === "<1>" - ![Space-optimized dynamic programming process for unbounded knapsack problem](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step1.png) + ![Space-optimized dynamic programming for unbounded knapsack](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step1.png) === "<2>" ![unbounded_knapsack_dp_comp_step2](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step2.png) @@ -60,67 +60,67 @@ This traversal order is exactly opposite to the 0-1 knapsack. Please refer to th === "<6>" ![unbounded_knapsack_dp_comp_step6](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step6.png) -The code implementation is relatively simple, just delete the first dimension of the array `dp`: +The implementation simply removes the first dimension of `dp`: ```src [file]{unbounded_knapsack}-[class]{}-[func]{unbounded_knapsack_dp_comp} ``` -## Coin Change Problem +## Coin change problem -The knapsack problem represents a large class of dynamic programming problems and has many variants, such as the coin change problem. +Knapsack represents a broad family of dynamic programming problems, with many variants such as coin change. !!! question - Given $n$ types of coins, where the denomination of the $i$-th type of coin is $coins[i - 1]$, and the target amount is $amt$. **Each type of coin can be selected multiple times**. What is the minimum number of coins needed to make up the target amount? If it is impossible to make up the target amount, return $-1$. An example is shown in the figure below. + Given $n$ coin denominations, where denomination $i$ has value $coins[i - 1]$, and a target amount $amt$, **each denomination may be used repeatedly**. What is the minimum number of coins needed to make the target? Return $-1$ if it cannot be made. An example is shown in the figure below. -![Example data for coin change problem](unbounded_knapsack_problem.assets/coin_change_example.png) +![Example input for coin change](unbounded_knapsack_problem.assets/coin_change_example.png) -### Dynamic Programming Approach +### Dynamic programming approach -**The coin change problem can be viewed as a special case of the unbounded knapsack problem**, with the following connections and differences. +**Coin change can be viewed as a special case of unbounded knapsack**, with the following connections and differences. -- The two problems can be converted to each other: "item" corresponds to "coin", "item weight" corresponds to "coin denomination", and "knapsack capacity" corresponds to "target amount". -- The optimization goals are opposite: the unbounded knapsack problem aims to maximize item value, while the coin change problem aims to minimize the number of coins. -- The unbounded knapsack problem seeks solutions "not exceeding" the knapsack capacity, while the coin change problem seeks solutions that "exactly" make up the target amount. +- The two problems can be translated into one another: items correspond to coins, item weights to coin denominations, and knapsack capacity to the target amount. +- The optimization objectives are opposite: unbounded knapsack maximizes item value, while coin change minimizes the number of coins. +- Unbounded knapsack must stay within a capacity limit, whereas coin change must reach the target amount exactly. -**Step 1: Think about the decisions in each round, define the state, and thus obtain the $dp$ table** +**Step 1: Identify each decision, define the state, and establish the $dp$ table** -State $[i, a]$ corresponds to the subproblem: **the minimum number of coins among the first $i$ types of coins that can make up amount $a$**, denoted as $dp[i, a]$. +State $[i, a]$ represents the subproblem of finding **the minimum number of coins needed to make amount $a$ using the first $i$ denominations**, denoted by $dp[i, a]$. The two-dimensional $dp$ table has size $(n+1) \times (amt+1)$. -**Step 2: Identify the optimal substructure, and then derive the state transition equation** +**Step 2: Identify optimal substructure and derive the state transition equation** -This problem differs from the unbounded knapsack problem in the following two aspects regarding the state transition equation. +The state transition differs from unbounded knapsack in two ways. -- This problem seeks the minimum value, so the operator $\max()$ needs to be changed to $\min()$. -- The optimization target is the number of coins rather than item value, so when a coin is selected, simply add $1$. +- We want a minimum, so replace $\max()$ with $\min()$. +- We optimize the coin count rather than item value, so choosing a coin adds $+1$. $$ dp[i, a] = \min(dp[i-1, a], dp[i, a - coins[i-1]] + 1) $$ -**Step 3: Determine boundary conditions and state transition order** +**Step 3: Determine the boundary conditions and evaluation order** -When the target amount is $0$, the minimum number of coins needed to make it up is $0$, so all $dp[i, 0]$ in the first column equal $0$. +A target amount of $0$ requires $0$ coins, so every entry $dp[i, 0]$ in the first column is $0$. -When there are no coins, **it is impossible to make up any amount $> 0$**, which is an invalid solution. To enable the $\min()$ function in the state transition equation to identify and filter out invalid solutions, we consider using $+ \infty$ to represent them, i.e., set all $dp[0, a]$ in the first row to $+ \infty$. +With no coins, **any target amount $> 0$ is impossible**. To let $\min()$ recognize and discard these infeasible solutions, represent them by $+ \infty$: set the first-row entries $dp[0, a]$ to $+ \infty$. -### Code Implementation +### Code implementation -Most programming languages do not provide a $+ \infty$ variable, and can only use the maximum value of integer type `int` as a substitute. However, this can lead to integer overflow: the $+ 1$ operation in the state transition equation may cause overflow. +Most programming languages do not provide an integer value for $+ \infty$, so the maximum `int` value is used instead. However, the $+ 1$ in the transition may then overflow. -For this reason, we use the number $amt + 1$ to represent invalid solutions, because the maximum number of coins needed to make up $amt$ is at most $amt$. Before returning, check whether $dp[n, amt]$ equals $amt + 1$; if so, return $-1$, indicating that the target amount cannot be made up. The code is as follows: +Instead, use $amt + 1$ to represent an infeasible solution, since making $amt$ requires at most $amt$ coins. Before returning, check whether $dp[n, amt]$ equals $amt + 1$. If so, return $-1$ to indicate that the target cannot be made. The code is as follows: ```src [file]{coin_change}-[class]{}-[func]{coin_change_dp} ``` -The figure below shows the dynamic programming process for coin change, which is very similar to the unbounded knapsack problem. +The figure below shows dynamic programming for coin change, which closely resembles unbounded knapsack. === "<1>" - ![Dynamic programming process for coin change problem](unbounded_knapsack_problem.assets/coin_change_dp_step1.png) + ![Dynamic programming for coin change](unbounded_knapsack_problem.assets/coin_change_dp_step1.png) === "<2>" ![coin_change_dp_step2](unbounded_knapsack_problem.assets/coin_change_dp_step2.png) @@ -164,43 +164,43 @@ The figure below shows the dynamic programming process for coin change, which is === "<15>" ![coin_change_dp_step15](unbounded_knapsack_problem.assets/coin_change_dp_step15.png) -### Space Optimization +### Space optimization -The space optimization for the coin change problem is handled in the same way as the unbounded knapsack problem: +Space optimization is the same as for unbounded knapsack: ```src [file]{coin_change}-[class]{}-[func]{coin_change_dp_comp} ``` -## Coin Change Problem II +## Coin change problem II !!! question - Given $n$ types of coins, where the denomination of the $i$-th type of coin is $coins[i - 1]$, and the target amount is $amt$. Each type of coin can be selected multiple times. **What is the number of coin combinations that can make up the target amount?** An example is shown in the figure below. + Given $n$ coin denominations, where denomination $i$ has value $coins[i - 1]$, and a target amount $amt$, each denomination may be used repeatedly. **How many combinations of coins make the target amount**? An example is shown in the figure below. -![Example data for coin change problem II](unbounded_knapsack_problem.assets/coin_change_ii_example.png) +![Example input for coin change II](unbounded_knapsack_problem.assets/coin_change_ii_example.png) -### Dynamic Programming Approach +### Dynamic programming approach -Compared to the previous problem, this problem's goal is to find the number of combinations, so the subproblem becomes: **the number of combinations among the first $i$ types of coins that can make up amount $a$**. The $dp$ table remains a two-dimensional matrix of size $(n+1) \times (amt + 1)$. +This time, we count combinations, so the subproblem becomes **the number of combinations that make amount $a$ using the first $i$ denominations**. The $dp$ table remains a two-dimensional matrix of size $(n+1) \times (amt + 1)$. -The number of combinations for the current state equals the sum of the combinations from not selecting the current coin and selecting the current coin. The state transition equation is: +The number of combinations for the current state is the sum of those that exclude the current coin and those that include it. The state transition equation is: $$ dp[i, a] = dp[i-1, a] + dp[i, a - coins[i-1]] $$ -When the target amount is $0$, no coins need to be selected to make up the target amount, so all $dp[i, 0]$ in the first column should be initialized to $1$. When there are no coins, it is impossible to make up any amount $>0$, so all $dp[0, a]$ in the first row equal $0$. +A target of $0$ can be made by choosing no coins, so initialize every first-column entry $dp[i, 0]$ to $1$. With no coins, any target $>0$ is impossible, so the first-row entries $dp[0, a]$ are $0$. -### Code Implementation +### Code implementation ```src [file]{coin_change_ii}-[class]{}-[func]{coin_change_ii_dp} ``` -### Space Optimization +### Space optimization -The space optimization is handled in the same way, just delete the coin dimension: +The space optimization is the same: remove the coin dimension: ```src [file]{coin_change_ii}-[class]{}-[func]{coin_change_ii_dp_comp} diff --git a/en/docs/chapter_graph/exercises.md b/en/docs/chapter_graph/exercises.md index 8a443b2c5..9d400bf6b 100644 --- a/en/docs/chapter_graph/exercises.md +++ b/en/docs/chapter_graph/exercises.md @@ -1,20 +1,20 @@ # Exercises -## Concept Review +## Check your understanding -### Represent the Same Graph in Two Ways +### Representing the same graph in two ways -An undirected graph has four vertices, `A, B, C, D`, and the edges +An undirected graph has four vertices `A, B, C, D` and edges `A-B, A-C, B-C, C-D`. 1. Write its adjacency list. -2. Fill in its adjacency matrix using only 0s and 1s. -3. To determine whether `A` and `D` are directly connected, which graph representation requires checking only one stored entry? +2. Fill in its adjacency matrix using only 0 and 1. +3. Which representation lets us determine whether `A` and `D` are directly connected by inspecting just one stored entry? 4. If a graph has many vertices but few edges, which representation usually uses less space? -??? success "Answer" +??? success "Reference answer" 1. The adjacency list is: @@ -34,70 +34,70 @@ An undirected graph has four vertices, `A, B, C, D`, and the edges | C | 1 | 1 | 0 | 1 | | D | 0 | 0 | 1 | 0 | - 3. In an adjacency matrix, you can directly check row `A`, column `D`, making it well suited to determining whether any two vertices are directly connected. + 3. In the adjacency matrix, we can inspect row `A`, column `D` directly. This makes it well suited to checking whether any two vertices are directly connected. - 4. When a graph has many vertices but few edges, an adjacency list records only the edges that actually exist. It usually uses less space than an adjacency matrix, which reserves a position for every pair of vertices. + 4. When there are many vertices but few edges, an adjacency list records only existing edges. It usually uses less space than an adjacency matrix, which reserves an entry for every pair of vertices. -### Breadth-First and Depth-First Traversal Orders +### Breadth-first and depth-first traversal orders An undirected graph has vertices `A, B, C, D, E` and edges `A-B, A-C, B-D, C-D, D-E`. -Start at A. Whenever there are several unvisited adjacent vertices, choose them in alphabetical order: +Start at A. Whenever several unvisited neighbors are available, choose them in alphabetical order: -1. Write the visit order of breadth-first traversal (BFS). -2. Write the visit order of recursive depth-first traversal (DFS). -3. Why must both traversals record which vertices have already been visited? +1. Write the breadth-first traversal (BFS) order. +2. Write the recursive depth-first traversal (DFS) order. +3. Why must both traversals keep track of vertices already visited? -??? success "Answer" +??? success "Reference answer" - 1. The BFS visit order is `A, B, C, D, E`. It first visits B and C, which are one edge away from A, - and then visits the more distant D and E. + 1. The BFS order is `A, B, C, D, E`. It first visits B and C, which are one edge away from A, + and then the more distant D and E. - 2. The DFS visit order is `A, B, D, C, E`. It repeatedly enters an unvisited adjacent vertex, - first following `A → B → D → C`. When C has no new adjacent vertex, it returns to D and then visits E. + 2. The DFS order is `A, B, D, C, E`. It proceeds into an unvisited neighbor of the current vertex, + first following `A → B → D → C`. When C has no new neighbors to visit, it returns to D and then visits E. - 3. The graph contains a cycle, such as `A-B-D-C-A`. Without recording visited vertices, - a traversal could repeatedly visit the same vertices around the cycle and fail to terminate normally. + 3. The graph contains cycles, such as `A-B-D-C-A`. Without recording visited vertices, + traversal could repeatedly follow a cycle through the same vertices and fail to terminate. -### Can One BFS Visit the Entire Graph? +### Can one BFS visit the entire graph? An undirected graph has vertices `A, B, C, D, E, F` and only the edges `A-B, B-C, D-E`. -1. Which vertices can one BFS starting at A visit? -2. Based on Question 1, has this BFS visited every vertex in the graph? Why or why not? -3. Suppose you scan all vertices in alphabetical order and start a new BFS whenever you reach an unvisited vertex. - What is the starting vertex of each BFS? Into how many mutually disconnected parts (connected components) is the graph divided? +1. Which vertices can a single BFS starting from A visit? +2. Based on your answer, has this BFS visited every vertex in the graph? Why? +3. Suppose we scan all vertices alphabetically and start a new BFS whenever we encounter an unvisited vertex. + What are the starting vertices of these searches? How many separate connected parts, or connected components, does the graph have? -??? success "Answer" +??? success "Reference answer" - 1. Starting from A, the traversal can visit only `A, B, C`. + 1. Starting from A reaches only `A, B, C`. - 2. It has not visited every vertex. `D, E` form another connected part, while F is an isolated vertex. - None of them has a path to A, so they cannot be reached from A. + 2. Not every vertex is visited. `D, E` form another connected part, and F is an isolated vertex. + None has a path to A, so they cannot be reached from A. - 3. The three BFS traversals start at `A, D, F`, and visit - `{A, B, C}`, `{D, E}`, and `{F}`, respectively. Therefore, the graph has 3 connected components. + 3. The three BFS starting vertices are `A, D, F`, in order, visiting + `{A, B, C}`, `{D, E}`, and `{F}`, respectively. Thus, the graph has three connected components. -## Programming Exercises +## Programming exercises -### Determine Whether a Path Exists in an Undirected Graph +### Checking for a path in an undirected graph You are given an undirected graph with $n$ vertices numbered from $0$ to $n-1$. Each entry `[u, v]` in the array `edges` represents an undirected edge between vertices `u` and `v`. -You are also given a starting vertex `source` and a destination vertex `destination`. First build an adjacency list from `edges`, then use BFS or DFS -to determine whether a path exists from `source` to `destination`. Return `true` if one exists and `false` otherwise. +You are also given a starting vertex `source` and an ending vertex `destination`. First build an adjacency list from `edges`, then use BFS or DFS +to determine whether a path exists from `source` to `destination`. Return `true` if it does, or `false` otherwise. The graph may contain cycles and may be disconnected. ??? tip "Hints" - 1. Add every undirected edge in both directions - 2. The graph may contain cycles, so you must record which vertices have already been visited - 3. Starting from source, return true if you encounter destination; if the traversal ends without reaching it, return false + 1. Add each undirected edge in both directions. + 2. The graph may contain cycles, so keep track of vertices already visited. + 3. Starting from source, return true if you reach destination. Return false if traversal ends without reaching it. [LeetCode](https://leetcode.com/problems/find-if-path-exists-in-graph/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_graph/graph.md b/en/docs/chapter_graph/graph.md index a305f2dc5..6c307f156 100644 --- a/en/docs/chapter_graph/graph.md +++ b/en/docs/chapter_graph/graph.md @@ -1,6 +1,6 @@ -# Graph +# Graphs -A graph is a nonlinear data structure consisting of vertices and edges. We can abstractly represent a graph $G$ as a set of vertices $V$ and a set of edges $E$. The following example shows a graph containing 5 vertices and 7 edges. +A graph is a nonlinear data structure made up of vertices and edges. We can represent a graph $G$ abstractly using a set of vertices $V$ and a set of edges $E$. The following example describes a graph with 5 vertices and 7 edges. $$ \begin{aligned} @@ -10,74 +10,74 @@ G & = \{ V, E \} \newline \end{aligned} $$ -If we view vertices as nodes and edges as references (pointers) connecting them, we can regard a graph as an extension of the linked list data structure. As shown in the figure below, **compared to linear relationships (linked lists) and divide-and-conquer relationships (trees), network relationships (graphs) have a higher degree of freedom and are therefore more complex**. +If we think of vertices as nodes and edges as references (pointers) connecting those nodes, a graph can be viewed as an extension of a linked list. As the figure below shows, **network relationships (graphs) allow more freedom than linear relationships (linked lists) or divide-and-conquer relationships (trees)**, making them more complex. -![Relationships among linked lists, trees, and graphs](graph.assets/linkedlist_tree_graph.png) +![Relationships between linked lists, trees, and graphs](graph.assets/linkedlist_tree_graph.png) -## Common Types and Terminology of Graphs +## Common graph types and terminology -Graphs can be divided into undirected graphs and directed graphs based on whether edges have direction, as shown in the figure below. +Depending on whether their edges have a direction, graphs are classified as undirected graphs or directed graphs, as shown in the figure below. -- In undirected graphs, edges represent a "bidirectional" connection between two vertices, such as friendships on WeChat or QQ. -- In directed graphs, edges have directionality, meaning edges $A \rightarrow B$ and $A \leftarrow B$ are independent of each other, such as following and follower relationships on Weibo or TikTok. +- In an undirected graph, an edge represents a two-way connection between two vertices, such as a friendship on WeChat or QQ. +- In a directed graph, edges have a direction: the edges $A \rightarrow B$ and $A \leftarrow B$ are independent of each other. Examples include following and being followed on Weibo or Douyin. ![Directed and undirected graphs](graph.assets/directed_graph.png) -Graphs can be divided into connected graphs and disconnected graphs based on whether all vertices are connected, as shown in the figure below. +Depending on whether all vertices are connected, graphs are classified as connected graphs or disconnected graphs, as shown in the figure below. -- For connected graphs, starting from any vertex, all other vertices can be reached. -- For disconnected graphs, starting from a certain vertex, at least one vertex cannot be reached. +- In a connected graph, every other vertex can be reached from any starting vertex. +- In a disconnected graph, at least one vertex cannot be reached from a given starting vertex. ![Connected and disconnected graphs](graph.assets/connected_graph.png) -We can also add a "weight" variable to edges, resulting in weighted graphs as shown in the figure below. For example, in mobile games like "Honor of Kings", the system calculates the "intimacy" between players based on how long they have played together, and such intimacy networks can be represented using weighted graphs. +We can also assign a weight to each edge to obtain a weighted graph, as shown in the figure below. For example, mobile games such as Honor of Kings calculate an affinity score between players based on the time they spend playing together. A weighted graph can represent this network of affinities. ![Weighted and unweighted graphs](graph.assets/weighted_graph.png) -Graph data structures include the following commonly used terms. +The following terms are commonly used when discussing graphs. -- Adjacency: When two vertices are connected by an edge, these two vertices are said to be "adjacent". In the figure above, the adjacent vertices of vertex 1 are vertices 2, 3, and 5. -- Path: The sequence of edges from vertex A to vertex B is called a "path" from A to B. In the figure above, the edge sequence 1-5-2-4 is a path from vertex 1 to vertex 4. -- Degree: The number of edges a vertex has. For directed graphs, in-degree indicates how many edges point to the vertex, and out-degree indicates how many edges leave the vertex. +- Adjacency: Two vertices are adjacent if an edge connects them. In the figure above, the vertices adjacent to vertex 1 are 2, 3, and 5. +- Path: A sequence of edges followed from vertex A to vertex B is called a path from A to B. In the figure above, the edge sequence 1-5-2-4 forms a path from vertex 1 to vertex 4. +- Degree: The number of edges incident to a vertex. In a directed graph, the in-degree is the number of edges pointing to the vertex, and the out-degree is the number of edges pointing away from it. -## Representation of Graphs +## Representing a graph -Common representations of graphs include "adjacency matrices" and "adjacency lists". The following uses undirected graphs as examples. +Two common ways to represent a graph are an adjacency matrix and an adjacency list. We will use undirected graphs in the following examples. -### Adjacency Matrix +### Adjacency matrix -Given a graph with $n$ vertices, an adjacency matrix uses an $n \times n$ matrix to represent the graph, where each row (column) represents a vertex, and matrix elements represent edges, using $1$ or $0$ to indicate whether an edge exists between two vertices. +For a graph with $n$ vertices, an adjacency matrix represents the graph using an $n \times n$ matrix. Each row (or column) represents a vertex, and the matrix entries represent edges: $1$ or $0$ indicates whether an edge exists between two vertices. -As shown in the figure below, let the adjacency matrix be $M$ and the vertex list be $V$. Then matrix element $M[i, j] = 1$ indicates that an edge exists between vertex $V[i]$ and vertex $V[j]$, whereas $M[i, j] = 0$ indicates no edge between the two vertices. +As shown in the figure below, let $M$ be the adjacency matrix and $V$ the vertex list. The entry $M[i, j] = 1$ means that an edge connects vertices $V[i]$ and $V[j]$, whereas $M[i, j] = 0$ means that no edge connects them. -![Adjacency matrix representation of a graph](graph.assets/adjacency_matrix.png) +![Representing a graph with an adjacency matrix](graph.assets/adjacency_matrix.png) -Adjacency matrices have the following properties. +An adjacency matrix has the following properties. -- In simple graphs, vertices cannot connect to themselves, so the elements on the main diagonal of the adjacency matrix are meaningless. -- For undirected graphs, edges in both directions are equivalent, so the adjacency matrix is symmetric about the main diagonal. -- Replacing the $1$ and $0$ entries in the adjacency matrix with weights allows it to represent weighted graphs. +- In a simple graph, a vertex cannot connect to itself, so the entries on the main diagonal do not represent meaningful connections. +- In an undirected graph, edges in the two directions are equivalent, so the adjacency matrix is symmetric about its main diagonal. +- Replacing the $1$ and $0$ entries with weights allows the matrix to represent a weighted graph. -When using adjacency matrices to represent graphs, we can directly access matrix elements to obtain edges, resulting in highly efficient addition, deletion, lookup, and modification operations, all with a time complexity of $O(1)$. However, the space complexity of the matrix is $O(n^2)$, which consumes significant memory. +With an adjacency matrix, we can access a matrix entry directly to retrieve an edge. Adding, removing, looking up, and modifying edges are therefore efficient, each taking $O(1)$ time. However, the matrix requires $O(n^2)$ space and uses a substantial amount of memory. -### Adjacency List +### Adjacency list -An adjacency list uses $n$ linked lists to represent a graph, with linked list nodes representing vertices. The $i$-th linked list corresponds to vertex $i$ and stores all adjacent vertices of that vertex (vertices connected to that vertex). The figure below shows an example of a graph stored using an adjacency list. +An adjacency list represents a graph using $n$ linked lists whose nodes represent vertices. The $i$-th linked list corresponds to vertex $i$ and stores all of its adjacent vertices (the vertices connected to it). The figure below shows an example of a graph stored as an adjacency list. -![Adjacency list representation of a graph](graph.assets/adjacency_list.png) +![Representing a graph with an adjacency list](graph.assets/adjacency_list.png) -Adjacency lists only store edges that actually exist, and the total number of edges is typically much less than $n^2$, making them more space-efficient. However, finding edges in an adjacency list requires traversing the linked list, so it is less time-efficient than an adjacency matrix. +An adjacency list stores only edges that actually exist. Since the total number of edges is usually much smaller than $n^2$, it saves space. However, finding an edge requires traversing a linked list, so lookups are slower than with an adjacency matrix. -As shown in the figure above, **the structure of adjacency lists is very similar to separate chaining in hash tables, so we can use similar methods to improve efficiency**. For example, when a linked list becomes long, it can be converted into an AVL tree or red-black tree, improving the time complexity from $O(n)$ to $O(\log n)$; it can also be converted into a hash table, reducing the time complexity to $O(1)$. +As the figure above shows, **an adjacency list closely resembles separate chaining in a hash table, so similar techniques can improve its efficiency**. For example, a long linked list can be replaced with an AVL tree or a red-black tree to improve lookup time from $O(n)$ to $O(\log n)$. Replacing it with a hash table can further reduce the time complexity to $O(1)$. -## Common Applications of Graphs +## Common applications of graphs -As shown in the table below, many real-world systems can be modeled using graphs, and corresponding problems can be reduced to graph computation problems. +As shown in the table below, many real-world systems can be modeled as graphs, and their associated problems can be reduced to graph computation problems. -

Table   Common graphs in real life

+

Table   Common graphs in everyday life

-| | Vertices | Edges | Graph Computation Problem | -| -------------- | --------------- | -------------------------------------- | ----------------------------- | -| Social network | Users | Friend relationships | Potential friend recommendation | -| Subway lines | Stations | Connectivity between stations | Shortest route recommendation | -| Solar system | Celestial bodies | Gravitational forces between celestial bodies | Planetary orbit calculation | +| | Vertices | Edges | Graph computation problem | +| -------- | ---- | -------------------- | ------------ | +| Social network | Users | Friendships | Recommending potential friends | +| Subway network | Stations | Connections between stations | Recommending the shortest route | +| Solar system | Celestial bodies | Gravitational interactions between bodies | Calculating planetary orbits | diff --git a/en/docs/chapter_graph/graph_operations.md b/en/docs/chapter_graph/graph_operations.md index ee6615909..4fd898e89 100644 --- a/en/docs/chapter_graph/graph_operations.md +++ b/en/docs/chapter_graph/graph_operations.md @@ -1,18 +1,18 @@ -# Basic Operations on Graphs +# Basic graph operations -Basic operations on graphs can be divided into operations on "edges" and operations on "vertices". Their implementations differ depending on whether the graph is represented as an "adjacency matrix" or an "adjacency list". +Basic graph operations fall into two categories: operations on edges and operations on vertices. Their implementations differ depending on whether the graph is represented by an adjacency matrix or an adjacency list. -## Implementation Based on Adjacency Matrix +## Adjacency matrix implementation -Given an undirected graph with $n$ vertices, the various operations are implemented as shown in the figure below. +For an undirected graph with $n$ vertices, the operations are implemented as shown in the figure below. -- **Adding or removing an edge**: Directly modify the specified edge in the adjacency matrix, using $O(1)$ time. Since it is an undirected graph, both directions of the edge need to be updated simultaneously. -- **Adding a vertex**: Add a row and a column at the end of the adjacency matrix and fill them all with $0$s, using $O(n)$ time. -- **Removing a vertex**: Delete a row and a column in the adjacency matrix. The worst case occurs when removing the first row and column, requiring $(n-1)^2$ elements to be "moved up and to the left", thus using $O(n^2)$ time. -- **Initialization**: Given $n$ vertices, initialize a vertex list `vertices` of length $n$, using $O(n)$ time; initialize an adjacency matrix `adjMat` of size $n \times n$, using $O(n^2)$ time. +- **Add or remove an edge**: Modify the corresponding entry in the adjacency matrix directly, taking $O(1)$ time. Since the graph is undirected, update the edge in both directions. +- **Add a vertex**: Append one row and one column to the adjacency matrix and fill them with $0$, taking $O(n)$ time. +- **Remove a vertex**: Delete the corresponding row and column from the adjacency matrix. The worst case occurs when deleting the first row and column: $(n-1)^2$ entries must be shifted up and to the left, taking $O(n^2)$ time. +- **Initialize the graph**: Given $n$ vertices, initialize the vertex list `vertices` of length $n$ in $O(n)$ time, then initialize the $n \times n$ adjacency matrix `adjMat` in $O(n^2)$ time. === "<1>" - ![Initialization, adding and removing edges, adding and removing vertices in adjacency matrix](graph_operations.assets/adjacency_matrix_step1_initialization.png) + ![Initializing an adjacency matrix and adding or removing edges and vertices](graph_operations.assets/adjacency_matrix_step1_initialization.png) === "<2>" ![adjacency_matrix_add_edge](graph_operations.assets/adjacency_matrix_step2_add_edge.png) @@ -26,24 +26,24 @@ Given an undirected graph with $n$ vertices, the various operations are implemen === "<5>" ![adjacency_matrix_remove_vertex](graph_operations.assets/adjacency_matrix_step5_remove_vertex.png) -The following is the implementation code for graphs represented using an adjacency matrix: +The following code implements a graph using an adjacency matrix: ```src [file]{graph_adjacency_matrix}-[class]{graph_adj_mat}-[func]{} ``` -## Implementation Based on Adjacency List +## Adjacency list implementation -Given an undirected graph with a total of $n$ vertices and $m$ edges, the various operations can be implemented as shown in the figure below. +For an undirected graph with $n$ vertices and $m$ edges, the operations can be implemented as shown in the figure below. -- **Adding an edge**: Add the edge at the end of the corresponding vertex's linked list, using $O(1)$ time. Since it is an undirected graph, edges in both directions need to be added simultaneously. -- **Removing an edge**: Find and remove the specified edge in the corresponding vertex's linked list, using $O(m)$ time. In an undirected graph, edges in both directions need to be removed simultaneously. -- **Adding a vertex**: Add a linked list to the adjacency list, with the new vertex as the head node, using $O(1)$ time. -- **Removing a vertex**: Traverse the entire adjacency list and remove all edges containing the specified vertex, using $O(n + m)$ time. -- **Initialization**: Create $n$ vertices and $2m$ edges in the adjacency list, using $O(n + m)$ time. +- **Add an edge**: Append the edge to the linked list of the corresponding vertex, taking $O(1)$ time. Since the graph is undirected, add the edge in both directions. +- **Remove an edge**: Find and remove the specified edge in the corresponding vertex's linked list, taking $O(m)$ time. For an undirected graph, remove the edge in both directions. +- **Add a vertex**: Add a linked list to the adjacency list with the new vertex as its head node, taking $O(1)$ time. +- **Remove a vertex**: Traverse the entire adjacency list and remove every edge involving the specified vertex, taking $O(n + m)$ time. +- **Initialize the graph**: Create $n$ vertices and $2m$ edge entries in the adjacency list, taking $O(n + m)$ time. === "<1>" - ![Initialization, adding and removing edges, adding and removing vertices in adjacency list](graph_operations.assets/adjacency_list_step1_initialization.png) + ![Initializing an adjacency list and adding or removing edges and vertices](graph_operations.assets/adjacency_list_step1_initialization.png) === "<2>" ![adjacency_list_add_edge](graph_operations.assets/adjacency_list_step2_add_edge.png) @@ -57,30 +57,30 @@ Given an undirected graph with a total of $n$ vertices and $m$ edges, the variou === "<5>" ![adjacency_list_remove_vertex](graph_operations.assets/adjacency_list_step5_remove_vertex.png) -The following code shows the adjacency list implementation. Compared with the figure above, the actual code differs in the following ways. +The following code implements an adjacency list, with these differences from the figure above. -- For convenience in adding and removing vertices, and to simplify the code, we use lists (dynamic arrays) instead of linked lists. -- A hash table is used to store the adjacency list, where `key` is the vertex instance and `value` is the list (linked list) of adjacent vertices for that vertex. +- To make adding and removing vertices easier and simplify the code, we use lists (dynamic arrays) in place of linked lists. +- A hash table stores the adjacency list. Each `key` is a vertex instance, and its `value` is the list (linked list) of adjacent vertices. -Additionally, we use the `Vertex` class to represent vertices in the adjacency list for the following reason: if we used list indices to distinguish different vertices, as with adjacency matrices, then to delete the vertex at index $i$, we would need to traverse the entire adjacency list and decrement all indices greater than $i$ by $1$, which is very inefficient. However, if each vertex is a unique `Vertex` instance, deleting one vertex does not require modifying the others. +We also use the `Vertex` class to represent vertices in the adjacency list. If we identified vertices by list indices, as in the adjacency matrix, deleting the vertex at index $i$ would require traversing the entire adjacency list and subtracting $1$ from every index greater than $i$. This would be inefficient. When each vertex is a unique `Vertex` instance, deleting one vertex requires no changes to the other vertices. ```src [file]{graph_adjacency_list}-[class]{graph_adj_list}-[func]{} ``` -## Efficiency Comparison +## Comparing efficiency -Assuming the graph has $n$ vertices and $m$ edges, the table below compares the time efficiency and space efficiency of adjacency matrices and adjacency lists. Note that the adjacency list (linked list) corresponds to the implementation used in this section, while the adjacency list (hash table) refers specifically to the implementation where all linked lists are replaced with hash tables. +For a graph with $n$ vertices and $m$ edges, the table below compares the time and space efficiency of adjacency matrices and adjacency lists. Here, "adjacency list (linked lists)" refers to the implementation in this section, while "adjacency list (hash tables)" specifically refers to an implementation in which every linked list is replaced by a hash table. -

Table   Comparison of adjacency matrix and adjacency list

+

Table   Comparing adjacency matrices and adjacency lists

-| | Adjacency matrix | Adjacency list (linked list) | Adjacency list (hash table) | -| ---------------------- | ---------------- | ---------------------------- | --------------------------- | -| Determine adjacency | $O(1)$ | $O(n)$ | $O(1)$ | -| Add an edge | $O(1)$ | $O(1)$ | $O(1)$ | -| Remove an edge | $O(1)$ | $O(n)$ | $O(1)$ | -| Add a vertex | $O(n)$ | $O(1)$ | $O(1)$ | -| Remove a vertex | $O(n^2)$ | $O(n + m)$ | $O(n)$ | -| Memory space usage | $O(n^2)$ | $O(n + m)$ | $O(n + m)$ | +| | Adjacency matrix | Adjacency list (linked lists) | Adjacency list (hash tables) | +| ------------ | -------- | -------------- | ---------------- | +| Check adjacency | $O(1)$ | $O(n)$ | $O(1)$ | +| Add an edge | $O(1)$ | $O(1)$ | $O(1)$ | +| Remove an edge | $O(1)$ | $O(n)$ | $O(1)$ | +| Add a vertex | $O(n)$ | $O(1)$ | $O(1)$ | +| Remove a vertex | $O(n^2)$ | $O(n + m)$ | $O(n)$ | +| Memory usage | $O(n^2)$ | $O(n + m)$ | $O(n + m)$ | -Observing the table above, it appears that the adjacency list (hash table) has the best time efficiency and space efficiency. However, in practice, operating on edges in the adjacency matrix is more efficient, requiring only a single array access or assignment operation. Overall, adjacency matrices embody the principle of "trading space for time", while adjacency lists embody "trading time for space". +The table above suggests that an adjacency list using hash tables is the most efficient in both time and space. In practice, however, edge operations in an adjacency matrix are faster: each requires only a single array access or assignment. Overall, an adjacency matrix trades space for time, while an adjacency list trades time for space. diff --git a/en/docs/chapter_graph/graph_traversal.assets/graph_dfs_step11.png b/en/docs/chapter_graph/graph_traversal.assets/graph_dfs_step11.png index dbfc38675..c69d148d2 100644 Binary files a/en/docs/chapter_graph/graph_traversal.assets/graph_dfs_step11.png and b/en/docs/chapter_graph/graph_traversal.assets/graph_dfs_step11.png differ diff --git a/en/docs/chapter_graph/graph_traversal.md b/en/docs/chapter_graph/graph_traversal.md index 1e89caf96..79de8d2eb 100644 --- a/en/docs/chapter_graph/graph_traversal.md +++ b/en/docs/chapter_graph/graph_traversal.md @@ -1,37 +1,37 @@ -# Graph Traversal +# Graph traversal -Trees represent "one-to-many" relationships, while graphs have a higher degree of freedom and can represent any "many-to-many" relationships. Therefore, we can view trees as a special case of graphs. Clearly, **tree traversal operations are also a special case of graph traversal operations**. +A tree represents one-to-many relationships, whereas a graph allows more freedom and can represent arbitrary many-to-many relationships. We can therefore view a tree as a special case of a graph. Accordingly, **tree traversal is also a special case of graph traversal**. -Both graphs and trees require the application of search algorithms to implement traversal operations. Graph traversal methods can also be divided into two types: breadth-first traversal and depth-first traversal. +Both graphs and trees use search algorithms for traversal. Graph traversal can likewise be divided into two types: breadth-first traversal and depth-first traversal. -## Breadth-First Search +## Breadth-first traversal -**Breadth-first search proceeds from near to far: starting from a given node, it always visits the nearest vertices first and expands outward layer by layer**. As shown in the figure below, starting from the top-left vertex, first traverse all adjacent vertices of that vertex, then traverse all adjacent vertices of the next vertex, and so on, until all vertices have been visited. +**Breadth-first traversal visits vertices from near to far. Starting from a vertex, it always visits the nearest vertices first and expands outward layer by layer**. In the figure below, we start at the top-left vertex and visit all its adjacent vertices, then visit all the adjacent vertices of the next vertex, and so on until every vertex has been visited. -![Breadth-first search of a graph](graph_traversal.assets/graph_bfs.png) +![Breadth-first traversal of a graph](graph_traversal.assets/graph_bfs.png) -### Algorithm Implementation +### Algorithm implementation -BFS is typically implemented with the help of a queue, as shown in the code below. The queue has a "first in, first out" property, which aligns with the BFS idea of "near to far". +BFS is usually implemented with a queue, as shown in the code below. A queue's first-in, first-out behavior fits the near-to-far order of BFS. -1. Add the starting vertex `startVet` to the queue and begin the loop. -2. In each iteration of the loop, pop the vertex at the front of the queue and record it as visited, then add all adjacent vertices of that vertex to the back of the queue. -3. Repeat step `2.` until all vertices have been visited. +1. Enqueue the starting vertex `startVet` and begin the loop. +2. In each iteration, dequeue the vertex at the front and record the visit, then enqueue all its adjacent vertices at the rear. +3. Repeat step `2.` until every vertex has been visited. -To prevent revisiting vertices, we use a hash set `visited` to record which nodes have been visited. +To avoid visiting a vertex more than once, we use a hash set `visited` to track which vertices have already been visited. !!! tip - A hash set can be viewed as a hash table that stores only `key` without storing `value`. It supports insertion, deletion, lookup, and update operations on `key` in $O(1)$ time. Based on the uniqueness of `key`, hash sets are typically used for data deduplication and similar scenarios. + A hash set can be viewed as a hash table that stores only `key` entries, with no `value` entries. Adding, removing, looking up, and modifying a `key` take $O(1)$ time. Because every `key` is unique, hash sets are commonly used to remove duplicate data. ```src [file]{graph_bfs}-[class]{}-[func]{graph_bfs} ``` -The code is relatively abstract; it is recommended to refer to the figure below to deepen understanding. +The code is fairly abstract; compare it with the figure below to build your understanding. === "<1>" - ![Steps of breadth-first search of a graph](graph_traversal.assets/graph_bfs_step1.png) + ![Steps in breadth-first traversal of a graph](graph_traversal.assets/graph_bfs_step1.png) === "<2>" ![graph_bfs_step2](graph_traversal.assets/graph_bfs_step2.png) @@ -63,39 +63,39 @@ The code is relatively abstract; it is recommended to refer to the figure below === "<11>" ![graph_bfs_step11](graph_traversal.assets/graph_bfs_step11.png) -!!! question "Is the breadth-first traversal sequence unique?" +!!! question "Is the breadth-first traversal order unique?" - Not unique. Breadth-first search only requires traversing in a "near to far" order, **and the traversal order of vertices at the same distance can be arbitrarily shuffled**. Taking the figure above as an example, the visit order of vertices $1$ and $3$ can be swapped, as can the visit order of vertices $2$, $4$, and $6$. + No. Breadth-first traversal only requires vertices to be visited from near to far; **vertices at the same distance may be visited in any order**. In the figure above, vertices $1$ and $3$ can be visited in either order, and vertices $2$, $4$, and $6$ can also be visited in any order. -### Complexity Analysis +### Complexity analysis -**Time complexity**: All vertices will be enqueued and dequeued once, using $O(|V|)$ time; in the process of traversing adjacent vertices, since it is an undirected graph, all edges will be visited $2$ times, using $O(2|E|)$ time; overall using $O(|V| + |E|)$ time. +**Time complexity**: Every vertex is enqueued and dequeued once, taking $O(|V|)$ time. While traversing adjacent vertices, every edge is visited $2$ times because the graph is undirected, taking $O(2|E|)$ time. The total time is $O(|V| + |E|)$. -**Space complexity**: The list `res`, hash set `visited`, and queue `que` can contain at most $|V|$ vertices, using $O(|V|)$ space. +**Space complexity**: The list `res`, hash set `visited`, and queue `que` each contain at most $|V|$ vertices, requiring $O(|V|)$ space. -## Depth-First Search +## Depth-first traversal -**Depth-first search is a traversal method that prioritizes going as far as possible, then backtracks when no path remains**. As shown in the figure below, starting from the top-left vertex, visit an adjacent vertex of the current vertex, continuing until reaching a dead end, then return and continue going as far as possible before returning again, and so on, until all vertices have been traversed. +**Depth-first traversal follows a path as far as possible and returns when it can go no farther**. In the figure below, we start at the top-left vertex and move to an adjacent vertex of the current vertex, continuing until we reach a dead end. We then return and explore another path as far as possible, repeating this process until every vertex has been visited. -![Depth-first search of a graph](graph_traversal.assets/graph_dfs.png) +![Depth-first traversal of a graph](graph_traversal.assets/graph_dfs.png) -### Algorithm Implementation +### Algorithm implementation -This "go as far as possible then return" algorithm paradigm is typically implemented using recursion. Similar to breadth-first search, in depth-first search we also need a hash set `visited` to record visited vertices and avoid revisiting. +This pattern of going as far as possible and then returning is usually implemented with recursion. As with breadth-first traversal, we use a hash set `visited` to track visited vertices and avoid visiting them again. ```src [file]{graph_dfs}-[class]{}-[func]{graph_dfs} ``` -The algorithm flow of depth-first search is shown in the figure below. +The figure below shows the steps of depth-first traversal. -- **Straight dashed lines represent downward recursion**, indicating that a new recursive method has been initiated to visit a new vertex. -- **Curved dashed lines represent upward backtracking**, indicating that this recursive call has returned to the point where it was made. +- **Straight dashed lines show recursive descent**, where a new recursive call begins to visit another vertex. +- **Curved dashed lines show backtracking**, where a recursive call has returned to the point from which it was made. -To deepen understanding, it is recommended to combine the figure below with the code to mentally simulate (or draw out) the entire DFS process, including when each recursive call begins and when it returns. +To deepen your understanding, follow the figure below alongside the code and mentally simulate (or draw on paper) the entire DFS process, including when each recursive call begins and returns. === "<1>" - ![Steps of depth-first search of a graph](graph_traversal.assets/graph_dfs_step1.png) + ![Steps in depth-first traversal of a graph](graph_traversal.assets/graph_dfs_step1.png) === "<2>" ![graph_dfs_step2](graph_traversal.assets/graph_dfs_step2.png) @@ -127,14 +127,14 @@ To deepen understanding, it is recommended to combine the figure below with the === "<11>" ![graph_dfs_step11](graph_traversal.assets/graph_dfs_step11.png) -!!! question "Is the depth-first traversal sequence unique?" +!!! question "Is the depth-first traversal order unique?" - Similar to breadth-first search, depth-first traversal sequences are also not unique. Given a vertex, any exploration direction may be chosen first; that is, the order of adjacent vertices can be arbitrarily rearranged and still constitute depth-first search. + Like breadth-first traversal, depth-first traversal does not have a unique order. From any vertex, we can explore in any direction first: rearranging the order of adjacent vertices still produces a depth-first traversal. + + For example, in tree traversal, "root $\rightarrow$ left $\rightarrow$ right", "left $\rightarrow$ root $\rightarrow$ right", and "left $\rightarrow$ right $\rightarrow$ root" correspond to preorder, inorder, and postorder traversal. They use three different visiting priorities, yet all are depth-first traversals. - Taking tree traversal as an example, "root $\rightarrow$ left $\rightarrow$ right", "left $\rightarrow$ root $\rightarrow$ right", and "left $\rightarrow$ right $\rightarrow$ root" correspond to pre-order, in-order, and post-order traversals, respectively. They represent three different traversal priorities, yet all three belong to depth-first search. +### Complexity analysis -### Complexity Analysis +**Time complexity**: Every vertex is visited $1$ time, taking $O(|V|)$ time, and every edge is visited $2$ times, taking $O(2|E|)$ time. The total time is $O(|V| + |E|)$. -**Time complexity**: All vertices will be visited $1$ time, using $O(|V|)$ time; all edges will be visited $2$ times, using $O(2|E|)$ time; overall using $O(|V| + |E|)$ time. - -**Space complexity**: The list `res` and hash set `visited` can contain at most $|V|$ vertices, and the maximum recursion depth is $|V|$, therefore using $O(|V|)$ space. +**Space complexity**: The list `res` and hash set `visited` each contain at most $|V|$ vertices, and the maximum recursion depth is $|V|$, so the space complexity is $O(|V|)$. diff --git a/en/docs/chapter_graph/index.md b/en/docs/chapter_graph/index.md index dd16be4dc..078607e72 100644 --- a/en/docs/chapter_graph/index.md +++ b/en/docs/chapter_graph/index.md @@ -1,9 +1,9 @@ -# Graph +# Graphs -![Graph](../assets/covers/chapter_graph.jpg) +![Graphs](../assets/covers/chapter_graph.jpg) !!! abstract - In the journey of life, we are like nodes, connected by countless invisible edges. - - Each encounter and parting leaves a unique mark on this vast network graph. + On the journey of life, we are like nodes joined by countless invisible edges. + + Every meeting and parting leaves a unique mark on this vast network. diff --git a/en/docs/chapter_graph/summary.md b/en/docs/chapter_graph/summary.md index 980b9fb48..481b5bed1 100644 --- a/en/docs/chapter_graph/summary.md +++ b/en/docs/chapter_graph/summary.md @@ -1,31 +1,31 @@ # Summary -### Key Review +### Key takeaways -- Graphs consist of vertices and edges and can be represented as a set of vertices and a set of edges. -- Compared with the linear relationships modeled by linked lists and the divide-and-conquer relationships modeled by trees, the network relationships modeled by graphs offer much greater flexibility and are therefore more complex. -- In directed graphs, edges have direction; in connected graphs, every vertex is reachable from any other vertex; and in weighted graphs, each edge carries a weight. -- Adjacency matrices use matrices to represent graphs, where each row (column) represents a vertex, and matrix elements represent edges, using $1$ or $0$ to indicate whether two vertices have an edge or not. Adjacency matrices are highly efficient for addition, deletion, lookup, and modification operations, but consume significant space. -- Adjacency lists use multiple linked lists to represent a graph: the $i$-th linked list corresponds to vertex $i$ and stores all vertices adjacent to it. Compared with adjacency matrices, adjacency lists use less space, but edge lookups are less efficient because the linked list must be traversed. -- When linked lists in adjacency lists become too long, they can be converted to red-black trees or hash tables, thereby improving lookup efficiency. -- From an algorithmic perspective, adjacency matrices embody "trading space for time", while adjacency lists embody "trading time for space". -- Graphs can be used to model various real-world systems, such as social networks and subway lines. -- Trees are a special case of graphs, and tree traversal is a special case of graph traversal. -- Breadth-first search in graphs explores from near to far, expanding layer by layer, and is typically implemented with a queue. -- Depth-first search in graphs follows a path as deep as possible and backtracks when it can go no farther, and is commonly implemented with recursion. +- A graph consists of vertices and edges and can be represented using a vertex set and an edge set. +- Network relationships (graphs) allow more freedom than linear relationships (linked lists) or divide-and-conquer relationships (trees), making them more complex. +- Edges in a directed graph have a direction; all vertices in a connected graph are reachable; and each edge in a weighted graph has a weight. +- An adjacency matrix represents a graph with a matrix. Each row (or column) represents a vertex, and entries represent edges, with $1$ or $0$ indicating whether an edge exists between two vertices. Adding, removing, looking up, and modifying edges are efficient, but the matrix uses substantial space. +- An adjacency list represents a graph with multiple linked lists. The $i$-th linked list corresponds to vertex $i$ and stores all of its adjacent vertices. It uses less space than an adjacency matrix, but finding an edge requires traversing a linked list, making lookups slower. +- A long linked list in an adjacency list can be replaced with a red-black tree or a hash table to speed up lookups. +- In terms of algorithm design, an adjacency matrix trades space for time, while an adjacency list trades time for space. +- Graphs can model many real-world systems, such as social networks and subway networks. +- A tree is a special case of a graph, and tree traversal is a special case of graph traversal. +- Breadth-first graph traversal searches from near to far, expanding layer by layer. It is usually implemented with a queue. +- Depth-first graph traversal follows a path as far as possible and backtracks when it can go no farther. It is often implemented with recursion. ### Q & A **Q**: Is a path defined as a sequence of vertices or a sequence of edges? -The definitions in different language versions of Wikipedia are inconsistent: the English version states "a path is a sequence of edges", while the Chinese version states "a path is a sequence of vertices". The following is the original English text: In graph theory, a path in a graph is a finite or infinite sequence of edges which joins a sequence of vertices. +Wikipedia's language editions give different definitions: the English edition defines a path as a sequence of edges, while the Chinese edition defines it as a sequence of vertices. The English wording is: In graph theory, a path in a graph is a finite or infinite sequence of edges which joins a sequence of vertices. -In this text, a path is viewed as a sequence of edges, not a sequence of vertices. This is because there may be multiple edges connecting two vertices, in which case each edge corresponds to a path. +In this book, a path is treated as a sequence of edges rather than vertices. This is because multiple edges can connect the same two vertices, in which case each edge corresponds to a path. -**Q**: In a disconnected graph, will there be unreachable vertices? +**Q**: Can some vertices in a disconnected graph remain unvisited? -In a disconnected graph, if you start from one vertex, at least one other vertex will be unreachable. To traverse a disconnected graph, you need multiple starting points so that all connected components are covered. +In a disconnected graph, at least one vertex cannot be reached from a given starting vertex. Traversing the entire graph requires multiple starting points so that all connected components are visited. -**Q**: In an adjacency list, is there any required ordering for the vertices adjacent to a given vertex? +**Q**: Must the adjacent vertices in an adjacency list appear in a particular order? -They can appear in any order. In practice, however, they may need to be sorted according to specific rules, such as the order in which vertices were added or the order of vertex values, which helps when quickly finding a vertex with some extreme value. +They may appear in any order. In practice, however, it can be useful to sort them by a specific rule, such as the order in which vertices were added or their values. This helps us quickly find a vertex with a particular extreme value. diff --git a/en/docs/chapter_greedy/exercises.md b/en/docs/chapter_greedy/exercises.md index 661c89f62..27afece8e 100644 --- a/en/docs/chapter_greedy/exercises.md +++ b/en/docs/chapter_greedy/exercises.md @@ -1,83 +1,83 @@ # Exercises -## Concept Review +## Check your understanding -### Is Choosing the Largest Coin Always Best? +### Is choosing the largest coin always best? The coin denominations are `[1, 7, 10]`, and the target amount is 14. -1. Follow the rule "always choose the largest denomination that does not exceed the remaining amount," and write the coins selected. -2. Is there a solution using fewer coins? If so, give one; otherwise, explain why not. -3. Does this example show that the greedy strategy is correct for every set of coin denominations? +1. Follow the rule “always take the largest denomination that does not exceed the remaining amount,” and list the coins chosen. +2. Is there a way to use fewer coins? If so, give one; if not, explain why. +3. Can you conclude that this greedy strategy is correct for any set of coin denominations? -??? success "Answer" +??? success "Reference answer" - 1. The greedy strategy selects `10 + 1 + 1 + 1 + 1`, using 5 coins in total. + 1. The greedy strategy chooses `10 + 1 + 1 + 1 + 1`, using 5 coins in total. - 2. There is a solution using fewer coins: `7 + 7`, which uses only 2 coins. + 2. There is a solution with fewer coins: `7 + 7`, which uses only 2. - 3. No. This counterexample shows that, for arbitrary coin denominations, repeatedly choosing the largest currently available denomination does not necessarily minimize the number of coins. - The largest immediate choice may prevent a better combination later. + 3. No. This counterexample shows that choosing the largest available denomination at each step does not always minimize the number of coins for arbitrary denominations. + The largest immediate choice can rule out a better combination later. -### Which Item Should Go into the Knapsack First? +### Which item should go into the knapsack first? -A knapsack with a capacity of 4 kilograms can hold the following items. A fraction of an item may be taken, -and the value obtained is proportional to its weight: +A knapsack with a capacity of 4 kg can hold the following items. You may take part of an item, +and the value obtained is proportional to the weight taken. -- Item A: weight 4 kilograms, value 20. -- Item B: weight 3 kilograms, value 18. +- Item A weighs 4 kg and has a value of 20. +- Item B weighs 3 kg and has a value of 18. -1. What is the value per kilogram of each item? Which item should be placed in the knapsack first? -2. Fill the knapsack using the greedy strategy for the fractional knapsack problem. What is the final value? -3. When items can be divided and the knapsack limits total weight, should items be compared by total value or by value per kilogram? Why? +1. What is each item's value per kilogram? Which item should you take first? +2. If you fill the knapsack using the greedy strategy for fractional knapsack, what is the final value? +3. When items can be divided and the knapsack limits total weight, should you compare total value or value per kilogram? Why? -??? success "Answer" +??? success "Reference answer" - 1. A has value `20 ÷ 4 = 5` per kilogram, while B has value `18 ÷ 3 = 6` per kilogram, - so B, with the higher value per unit weight, should be placed in the knapsack first. + 1. A's value per kilogram is `20 ÷ 4 = 5`, and B's is `18 ÷ 3 = 6`. + Take B first because it has the higher value per unit of weight. - 2. First take all of B, using 3 kilograms of capacity and gaining a value of 18. With 1 kilogram of capacity remaining, - take 1 kilogram of A, gaining a value of 5. The final value is `18 + 5 = 23`. + 2. Take all of B first, using 3 kg of capacity and gaining a value of 18. With 1 kg of capacity left, + take 1 kg of A for a value of 5. The final value is `18 + 5 = 23`. - 3. The knapsack limits total weight, and items can be divided, so they should be compared by value per unit weight. - Although A has a higher total value, its value per kilogram is lower than B's. Filling the knapsack with A first would yield only a value of 20. + 3. The knapsack limits total weight, and items can be divided, so compare value per unit of weight. + Although A has a higher total value, its value per kilogram is lower than B's. Taking all of A first would give a value of only 20. -### Which Pointer Should Move Next? +### Which pointer should move next? -The partition heights are `[1, 8, 6, 2, 5]`. Use two pointers, one at each end, to find the maximum capacity. -The capacity equals "the height of the shorter partition × the distance between the partitions' indices." +The wall heights are `[1, 8, 6, 2, 5]`. Use two pointers, initially at opposite ends, to find the maximum container capacity. +Capacity equals the height of the shorter of the two walls multiplied by the difference between their indices. 1. Initially, the left pointer is at index 0 and the right pointer is at index 4. What is the current capacity? Which pointer should move next? -2. After making the move chosen in Question 1, at which indices are the two pointers? What is the capacity now? Which pointer should move next? -3. For the current pair of partitions, you could move either the pointer at the shorter partition or the pointer at the taller partition. Which move could still produce a greater capacity, and why? +2. After making the move you chose in question 1, what are the two pointer indices? What is the capacity now, and which pointer should move next? +3. For the current pair of walls, you could move the pointer at the shorter wall or the pointer at the taller wall. Which move still has a chance of producing a larger capacity? Why? -??? success "Answer" +??? success "Reference answer" - 1. The current capacity is `min(1, 5) × (4 - 0) = 4`. The left partition is shorter, so move the left pointer. + 1. The current capacity is `min(1, 5) × (4 - 0) = 4`. The left wall is shorter, so move the left pointer. - 2. After the left pointer moves, the pointers are at indices 1 and 4. The current capacity is - `min(8, 5) × (4 - 1) = 15`. The right partition is shorter, so move the right pointer next. + 2. After moving the left pointer, the pointers are at indices 1 and 4. The current capacity is + `min(8, 5) × (4 - 1) = 15`. The right wall is shorter, so move the right pointer next. - 3. Moving the pointer at the shorter partition is the only move that could still produce a greater capacity. If the taller partition's pointer moves, the distance certainly decreases while the height remains limited by the unmoved shorter partition, - so the capacity can only stay the same or decrease. Only by moving the shorter partition can a taller partition possibly be found. + 3. Only moving the pointer at the shorter wall can still produce a larger capacity. Moving the pointer at the taller wall reduces the distance between the walls, while the container's height remains limited by the shorter wall that has not moved. + That height can only stay the same or decrease, so the capacity cannot exceed its previous value. Moving the pointer at the shorter wall is the only way to have a chance of finding a taller wall. -## Programming Exercises +## Programming exercises -### Fractional Knapsack +### Fractional knapsack -You are given equal-length arrays `wgt` and `val`, where `wgt[i] > 0` and `val[i] >= 0`. The knapsack has capacity `cap >= 0`. -There is only one of each item, but any fraction of an item may be placed in the knapsack. -The value obtained is proportional to the fraction of the item's total weight that is included. Use a greedy algorithm -and return the maximum total value the knapsack can hold as a real number. +You are given arrays `wgt` and `val` of equal length, where `wgt[i] > 0` and `val[i] >= 0`, and a knapsack capacity `cap >= 0`. +There is one of each item, but you may take just part of it. +The value obtained is proportional to the fraction of the item's total weight that you take. Use a greedy algorithm +to return the maximum total value the knapsack can hold, as a real number. ??? tip "Hints" - 1. First calculate each item's value per unit weight as val[i] / wgt[i], keeping the fractional part of the division - 2. Place items with higher value per unit weight into the knapsack first - 3. If the remaining capacity is less than the current item's weight, take exactly the fraction that fills the knapsack and stop + 1. First calculate each item's value per unit of weight, val[i] / wgt[i]. Preserve the fractional part of the division result. + 2. Take items with a higher value per unit of weight first. + 3. If the remaining capacity is less than the current item's weight, take just enough of the item to fill the knapsack, then stop. diff --git a/en/docs/chapter_greedy/fractional_knapsack_problem.md b/en/docs/chapter_greedy/fractional_knapsack_problem.md index 5f267f3f8..44b8c7463 100644 --- a/en/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/en/docs/chapter_greedy/fractional_knapsack_problem.md @@ -1,52 +1,52 @@ -# Fractional Knapsack Problem +# Fractional knapsack problem !!! question - Given $n$ items, where the weight of the $i$-th item is $wgt[i-1]$ and its value is $val[i-1]$, and a knapsack with capacity $cap$. Each item can be selected only once, **but a fraction of an item may be selected, with its value proportional to the selected weight**. What is the maximum total value that can be placed in the knapsack under the capacity constraint? An example is shown in the figure below. + Given $n$ items, where item $i$ has weight $wgt[i-1]$ and value $val[i-1]$, and a knapsack with capacity $cap$, each item may be selected only once. **However, you may take a fraction of an item, with its value proportional to the weight taken**. What is the maximum total value that fits within the knapsack's capacity? An example is shown in the figure below. -![Example data for the fractional knapsack problem](fractional_knapsack_problem.assets/fractional_knapsack_example.png) +![Example input for the fractional knapsack problem](fractional_knapsack_problem.assets/fractional_knapsack_example.png) -The fractional knapsack problem is very similar overall to the 0-1 knapsack problem, with states including the current item $i$ and capacity $c$, and the goal being to maximize value under the limited knapsack capacity. +The fractional knapsack problem is very similar to the 0-1 knapsack problem. The state consists of the current item $i$ and capacity $c$, and the goal is to maximize value within the knapsack's capacity. -The difference is that this problem allows selecting only a fraction of an item. As shown in the figure below, **we can split an item arbitrarily and compute its value in proportion to the selected weight**. +The difference is that we may take only part of an item. As shown in the figure below, **we can divide items into arbitrary fractions and calculate their values in proportion to their weights**. -1. For item $i$, its value per unit weight is $val[i-1] / wgt[i-1]$, referred to as unit value. -2. Suppose we put a portion of item $i$ with weight $w$ into the knapsack, then the value added to the knapsack is $w \times val[i-1] / wgt[i-1]$. +1. For item $i$, the value per unit weight is $val[i-1] / wgt[i-1]$, which we call its unit value. +2. If we take a portion of item $i$ weighing $w$, the value added to the knapsack is $w \times val[i-1] / wgt[i-1]$. -![Value of items per unit weight](fractional_knapsack_problem.assets/fractional_knapsack_unit_value.png) +![Value per unit weight of each item](fractional_knapsack_problem.assets/fractional_knapsack_unit_value.png) -### Greedy Strategy Determination +### Choosing a greedy strategy -Maximizing the total value in the knapsack **essentially means prioritizing items with higher value per unit weight**. From this observation, we can derive the greedy strategy shown in the figure below. +Maximizing the total value in the knapsack **essentially means maximizing the value obtained per unit weight**. This leads to the greedy strategy shown in the figure below. -1. Sort items by unit value from high to low. -2. Iterate through all items, **greedily selecting the item with the highest unit value in each round**. -3. If the remaining knapsack capacity is insufficient, use a portion of the current item to fill the knapsack. +1. Sort the items by unit value in descending order. +2. Iterate through the items, **greedily choosing the item with the highest unit value at each step**. +3. If the remaining capacity is insufficient, take just enough of the current item to fill the knapsack. ![Greedy strategy for the fractional knapsack problem](fractional_knapsack_problem.assets/fractional_knapsack_greedy_strategy.png) -### Code Implementation +### Code implementation -We define an `Item` class so that items can be sorted by unit value. We then iterate through the sorted items greedily, stopping once the knapsack is full and returning the result: +We define an `Item` class to make it easier to sort items by unit value. We repeatedly make greedy choices, stopping and returning the result when the knapsack is full: ```src [file]{fractional_knapsack}-[class]{}-[func]{fractional_knapsack} ``` -Built-in sorting algorithms usually take $O(n \log n)$ time, and their space complexity is usually $O(\log n)$ or $O(n)$, depending on the specific implementation of the programming language. +Built-in sorting algorithms typically take $O(n \log n)$ time and $O(\log n)$ or $O(n)$ space, depending on the programming language's implementation. -Apart from sorting, in the worst case the entire item list needs to be traversed, **therefore the time complexity is $O(n)$**, where $n$ is the number of items. +Apart from sorting, the worst case requires traversing the entire item list, **so the time complexity is $O(n)$**, where $n$ is the number of items. -Since an `Item` object list is initialized, **the space complexity is $O(n)$**. +Initializing a list of `Item` objects requires **$O(n)$ space**. -### Correctness Proof +### Proof of correctness -We use proof by contradiction. Suppose item $x$ has the highest unit value, and some algorithm produces an optimal value `res`, but the resulting solution does not include item $x$. +We use proof by contradiction. Suppose item $x$ has the highest unit value, and an algorithm produces a maximum value of `res`, but its solution does not include item $x$. -Now remove one unit of weight from any item in the knapsack and replace it with one unit of weight from item $x$. Since item $x$ has the highest unit value, the total value after the replacement must be greater than `res`. **This contradicts the assumption that `res` is optimal, proving that any optimal solution must include item $x$**. +Now remove one unit of weight of any item from the knapsack and replace it with one unit of weight of item $x$. Since $x$ has the highest unit value, the total value after replacement must exceed `res`. **This contradicts the assumption that `res` is optimal, showing that an optimal solution must include item $x$**. -We can construct the same contradiction for the other items in the solution as well. In summary, **items with higher unit value are always the better choice**, which proves that the greedy strategy is effective. +We can construct the same contradiction for other items in the solution. Thus, **an item with a higher unit value is always a better choice**, which establishes the greedy strategy. -As shown in the figure below, if we treat item weight and unit value as the horizontal and vertical axes of a two-dimensional chart, then the fractional knapsack problem can be viewed as "finding the maximum area enclosed within a bounded interval on the horizontal axis." This analogy helps explain the effectiveness of the greedy strategy from a geometric perspective. +As shown in the figure below, if we plot item weight on the horizontal axis and unit value on the vertical axis, the fractional knapsack problem becomes a problem of maximizing the area within a limited horizontal interval. This analogy provides a geometric view of why the greedy strategy works. ![Geometric representation of the fractional knapsack problem](fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png) diff --git a/en/docs/chapter_greedy/greedy_algorithm.md b/en/docs/chapter_greedy/greedy_algorithm.md index f3b1711e0..3c7d132bb 100644 --- a/en/docs/chapter_greedy/greedy_algorithm.md +++ b/en/docs/chapter_greedy/greedy_algorithm.md @@ -1,94 +1,94 @@ -# Greedy Algorithm +# Greedy algorithms -Greedy algorithm is a common approach to solving optimization problems. Its basic idea is to choose the option that appears best at each decision stage, that is, to greedily make locally optimal decisions in the hope of obtaining a globally optimal solution. Greedy algorithms are simple and efficient, and are widely used in many practical problems. +A greedy algorithm is a common approach to optimization problems. At each decision point, it chooses the option that looks best at that moment: it makes a locally optimal choice in the hope of reaching a globally optimal solution. Greedy algorithms are simple and efficient, with many practical applications. -Greedy algorithms and dynamic programming are both commonly used to solve optimization problems. They share some similarities, such as both relying on the optimal substructure property, but they work differently. +Both greedy algorithms and dynamic programming are commonly used for optimization. They share some features, such as relying on optimal substructure, but work differently. -- Dynamic programming considers all previous decisions when making the current decision, and uses solutions to past subproblems to construct the solution to the current subproblem. -- Greedy algorithms do not consider past decisions, but instead make greedy choices moving forward, continually reducing the problem size until the problem is solved. +- Dynamic programming considers the current decision in light of all decisions from earlier stages, using solutions to earlier subproblems to build the solution to the current subproblem. +- A greedy algorithm does not reconsider past decisions. It keeps moving forward, making greedy choices and shrinking the problem until it is solved. -We will first understand how greedy algorithms work through the example problem "coin change." This problem was already introduced in the "Complete Knapsack Problem" chapter, so it should already be familiar to you. +Let's explore how greedy algorithms work through the coin change problem, which you have already encountered in the section on the unbounded knapsack problem. !!! question - Given $n$ types of coins, where the denomination of the $i$-th type is $coins[i - 1]$, a target amount $amt$, and an unlimited number of coins of each type, what is the minimum number of coins needed to make up the target amount? If the target amount cannot be made up, return $-1$. + Given $n$ coin denominations, where denomination $i$ has value $coins[i - 1]$, and a target amount $amt$, you may use each denomination any number of times. What is the minimum number of coins needed to make the target amount? Return $-1$ if it cannot be made. -The greedy strategy for this problem is shown in the figure below. Given a target amount, **we greedily choose the coin that does not exceed it and is closest to it**, repeating this step until the target amount is made up. +The greedy strategy is shown in the figure below. Given the remaining target amount, **we greedily choose the largest coin that does not exceed it**, repeating this step until we have made the target amount. ![Greedy strategy for coin change](greedy_algorithm.assets/coin_change_greedy_strategy.png) -The implementation code is as follows: +The implementation is as follows: ```src [file]{coin_change_greedy}-[class]{}-[func]{coin_change_greedy} ``` -You may find yourself exclaiming, "So clean!" The greedy algorithm solves the coin change problem in only about ten lines of code. +You might find yourself thinking, "So clean!" The greedy algorithm solves coin change in only about ten lines of code. -## Advantages and Limitations of Greedy Algorithms +## Strengths and limitations of greedy algorithms -**Greedy algorithms are not only straightforward to apply and easy to implement, but are also usually very efficient**. In the code above, if the smallest coin denomination is $\min(coins)$, the greedy selection loop runs at most $amt / \min(coins)$ times, giving a time complexity of $O(amt / \min(coins))$. This is an order of magnitude lower than the time complexity of the dynamic programming solution, $O(n \times amt)$. +**Greedy algorithms are straightforward, easy to implement, and usually efficient**. In the code above, let $\min(coins)$ be the smallest coin denomination. At most $amt / \min(coins)$ greedy choices are made, giving a time complexity of $O(amt / \min(coins))$. This is an order of magnitude lower than the dynamic programming solution's $O(n \times amt)$ time complexity. -However, **for some coin denomination sets, greedy algorithms cannot find the optimal solution**. The figure below shows two examples. +However, **for some sets of coin denominations, a greedy algorithm cannot find the optimal solution**. The figure below illustrates two examples. -- **Positive example $coins = [1, 5, 10, 20, 50, 100]$**: With this coin set, the greedy algorithm can find the optimal solution for any $amt$. -- **Counterexample $coins = [1, 20, 50]$**: Suppose $amt = 60$. The greedy algorithm can only find the combination $50 + 1 \times 10$, using $11$ coins in total, whereas dynamic programming can find the optimal solution $20 + 20 + 20$ using only $3$ coins. -- **Counterexample $coins = [1, 49, 50]$**: Suppose $amt = 98$. The greedy algorithm can only find the combination $50 + 1 \times 48$, using $49$ coins in total, whereas dynamic programming can find the optimal solution $49 + 49$ using only $2$ coins. +- **Successful case: $coins = [1, 5, 10, 20, 50, 100]$**. For these denominations, the greedy algorithm finds an optimal solution for any $amt$. +- **Counterexample: $coins = [1, 20, 50]$**. For $amt = 60$, the greedy algorithm produces $50 + 1 \times 10$, using $11$ coins. Dynamic programming finds the optimal solution $20 + 20 + 20$, using only $3$ coins. +- **Counterexample: $coins = [1, 49, 50]$**. For $amt = 98$, the greedy algorithm produces $50 + 1 \times 48$, using $49$ coins. Dynamic programming finds the optimal solution $49 + 49$, using only $2$ coins. -![Examples where greedy algorithms cannot find the optimal solution](greedy_algorithm.assets/coin_change_greedy_vs_dp.png) +![Examples where the greedy algorithm fails to find the optimal solution](greedy_algorithm.assets/coin_change_greedy_vs_dp.png) -In other words, for the coin change problem, greedy algorithms cannot guarantee a globally optimal solution and may even produce very poor results. This problem is better solved with dynamic programming. +In other words, a greedy algorithm cannot guarantee a globally optimal solution to coin change and may produce a very poor solution. Dynamic programming is better suited to this problem. -In general, greedy algorithms are applicable in the following two situations. +In general, greedy algorithms are useful in two situations. -1. **The optimal solution can be guaranteed**: In this case, greedy algorithms are often the best choice because they tend to be more efficient than backtracking and dynamic programming. -2. **An approximately optimal solution can be found**: Greedy algorithms are also useful in this case. For many complex problems, finding the global optimal solution is very difficult, so efficiently finding a suboptimal solution is already a very good outcome. +1. **An optimal solution is guaranteed**: A greedy algorithm is often the best choice because it is usually more efficient than backtracking or dynamic programming. +2. **A near-optimal solution can be found**: A greedy algorithm can still be useful. For many complex problems, finding a globally optimal solution is very difficult, so finding a good suboptimal solution efficiently is valuable. -## Characteristics of Greedy Algorithms +## Properties required by greedy algorithms -So the question arises: what kind of problems are suitable for solving with greedy algorithms? Or in other words, under what conditions can greedy algorithms guarantee finding the optimal solution? +What kinds of problems are suitable for greedy algorithms? More specifically, when can a greedy algorithm guarantee an optimal solution? -Compared to dynamic programming, the conditions for using greedy algorithms are stricter, mainly focusing on two properties of the problem. +Greedy algorithms have stricter requirements than dynamic programming. Two properties are particularly important. -- **Greedy choice property**: Only when locally optimal choices can always lead to a globally optimal solution can greedy algorithms guarantee obtaining the optimal solution. -- **Optimal substructure**: The optimal solution to the original problem contains the optimal solutions to subproblems. +- **Greedy-choice property**: A greedy algorithm can guarantee an optimal solution only when locally optimal choices always lead to a globally optimal solution. +- **Optimal substructure**: An optimal solution to the original problem contains optimal solutions to its subproblems. -Optimal substructure has already been introduced in the "Dynamic Programming" chapter, so we won't elaborate on it here. It's worth noting that the optimal substructure of some problems is not obvious, but they can still be solved using greedy algorithms. +Optimal substructure was covered in the dynamic programming chapter, so we will not repeat that discussion here. Some problems have optimal substructure that is not immediately apparent, yet can still be solved greedily. -We mainly explore methods for determining the greedy choice property. Although its description seems relatively simple, **in practice, for many problems, proving the greedy choice property is not easy**. +We will focus on how to determine whether the greedy-choice property holds. Its description may sound simple, **but proving it is difficult for many problems**. -For example, in the coin change problem, although we can easily provide counterexamples to disprove the greedy choice property, proving that it holds is much harder. If asked, **under what conditions can a coin set be solved using a greedy algorithm**? We often can only rely on intuition or examples to give a vague answer, and it is difficult to provide a rigorous mathematical proof. +For coin change, for example, it is easy to give counterexamples that disprove the greedy-choice property, but proving it is harder. If asked **which conditions on the coin denominations allow a greedy solution**, we may be able to give only a vague answer based on intuition or examples, rather than a rigorous mathematical proof. !!! quote - There is a paper that presents an $O(n^3)$ algorithm for determining whether a coin set can be solved optimally by a greedy algorithm for any amount. + One paper gives an $O(n^3)$-time algorithm for determining whether a set of coin denominations allows a greedy algorithm to find an optimal solution for every amount. Pearson, D. A polynomial-time algorithm for the change-making problem[J]. Operations Research Letters, 2005, 33(3): 231-234. -## Steps for Solving Problems with Greedy Algorithms +## Steps for solving a problem greedily -The general process for solving greedy problems can be divided into the following three steps. +Solving a greedy problem generally involves three steps. -1. **Problem analysis**: Sort out and understand the characteristics of the problem, including state definitions, optimization objectives, and constraints. This step also appears in backtracking and dynamic programming. -2. **Determine the greedy strategy**: Decide how to make a greedy choice at each step. This strategy should reduce the problem size step by step and ultimately solve the entire problem. -3. **Correctness proof**: It is usually necessary to prove that the problem has both greedy choice property and optimal substructure. This step may require mathematical tools such as induction or proof by contradiction. +1. **Analyze the problem**: Understand its properties, including the state definition, optimization objective, and constraints. This step also appears in backtracking and dynamic programming. +2. **Choose a greedy strategy**: Decide how to make a greedy choice at each step. Each choice should reduce the problem's size, eventually solving the whole problem. +3. **Prove correctness**: Usually, we must establish the greedy-choice property and optimal substructure. This may require a mathematical proof, such as induction or proof by contradiction. -Determining the greedy strategy is the core step in solving such problems, but it may not be easy in practice, mainly for the following reasons. +Choosing the greedy strategy is the central step, but it can be difficult for the following reasons. -- **Greedy strategies vary greatly from problem to problem**. For many problems, the greedy strategy is fairly intuitive and can be derived through rough reasoning and experimentation. For some complex problems, however, the greedy strategy may be deeply hidden, which strongly tests one's problem-solving experience and algorithmic ability. -- **Some greedy strategies are highly deceptive**. We may confidently design a greedy strategy, write the solution code, and submit it, only to find that some test cases fail. This is because the designed greedy strategy is only "partially correct," as exemplified by the coin change problem discussed above. +- **Greedy strategies vary greatly from problem to problem**. For many problems, the strategy is straightforward and can be found with a little thought and experimentation. For more complex problems, it may be well hidden, testing our problem-solving experience and algorithmic skills. +- **Some greedy strategies are misleading**. We may confidently devise a strategy, implement it, and submit the code, only to find that it fails some test cases. The strategy is only partially correct; the coin change problem above is a typical example. -To ensure correctness, we should give a rigorous mathematical proof of the greedy strategy, **usually using proof by contradiction or mathematical induction**. +To ensure correctness, we should give the greedy strategy a rigorous mathematical proof, **usually using contradiction or mathematical induction**. -However, correctness proofs can also be difficult. If we have no clear direction, we usually resort to debugging against test cases, revising and validating the greedy strategy step by step. +However, such a proof can itself be difficult. When we do not know where to start, we often debug against test cases, gradually refining and checking the greedy strategy. -## Typical Problems Solved by Greedy Algorithms +## Typical greedy problems -Greedy algorithms are often applied to optimization problems that satisfy greedy choice property and optimal substructure. Below are some typical greedy algorithm problems. +Greedy algorithms often solve optimization problems with the greedy-choice property and optimal substructure. Some typical examples follow. -- **Coin change problem**: With certain coin combinations, greedy algorithms can always obtain the optimal solution. -- **Interval scheduling problem**: Suppose you have some tasks, each taking place during a period of time, and your goal is to complete as many tasks as possible. If you always choose the task that ends earliest, then the greedy algorithm can obtain the optimal solution. -- **Fractional knapsack problem**: Given a set of items and a carrying capacity, your goal is to select a set of items such that the total weight does not exceed the carrying capacity and the total value is maximized. If you always choose the item with the highest value-to-weight ratio (value / weight), then the greedy algorithm can obtain the optimal solution in some cases. -- **Stock trading problem**: Given a set of historical stock prices, you can make multiple trades, but if you already hold stocks, you cannot buy again before selling, and the goal is to obtain the maximum profit. -- **Huffman coding**: Huffman coding is a greedy algorithm used for lossless data compression. By constructing a Huffman tree and always merging the two nodes with the lowest frequency, the resulting Huffman tree has the minimum weighted path length (encoding length). -- **Dijkstra's algorithm**: For graphs with non-negative edge weights, it is a greedy algorithm for solving the shortest path problem from a given source vertex to all other vertices. +- **Coin change**: For certain sets of coin denominations, a greedy algorithm always finds an optimal solution. +- **Interval scheduling**: Given tasks that each occupy a time interval, the goal is to complete as many tasks as possible. Always choosing the available task that finishes earliest yields an optimal solution. +- **Fractional knapsack**: Given items and a weight limit, choose items to maximize total value without exceeding the limit. Choosing the item with the highest value-to-weight ratio at each step can yield an optimal solution in some settings. +- **Stock trading**: Given historical stock prices, you may buy and sell multiple times, but cannot buy again while holding stock until you sell it. The goal is to maximize profit. +- **Huffman coding**: This greedy algorithm is used for lossless data compression. It builds a Huffman tree by repeatedly merging the two nodes with the lowest frequencies. The resulting tree has the minimum weighted path length (encoding length). +- **Dijkstra's algorithm**: This greedy algorithm finds shortest paths from a given source vertex to every other vertex in a graph whose edge weights are all nonnegative. diff --git a/en/docs/chapter_greedy/index.md b/en/docs/chapter_greedy/index.md index 5bc36c372..8cc172fb5 100644 --- a/en/docs/chapter_greedy/index.md +++ b/en/docs/chapter_greedy/index.md @@ -1,9 +1,9 @@ -# Greedy +# Greedy algorithms -![Greedy](../assets/covers/chapter_greedy.jpg) +![Greedy algorithms](../assets/covers/chapter_greedy.jpg) !!! abstract - Sunflowers turn toward the sun, always seeking the fullest growth possible. + A sunflower turns toward the sun, always seeking the greatest opportunity to grow. - Through successive simple choices, greedy strategies gradually lead to the optimal solution. + Through a series of simple choices, a greedy strategy leads us step by step toward the best answer. diff --git a/en/docs/chapter_greedy/max_capacity_problem.md b/en/docs/chapter_greedy/max_capacity_problem.md index ceac5d127..867dfd306 100644 --- a/en/docs/chapter_greedy/max_capacity_problem.md +++ b/en/docs/chapter_greedy/max_capacity_problem.md @@ -1,52 +1,52 @@ -# Max Capacity Problem +# Maximum capacity problem !!! question - Given an array $ht$, where each element represents the height of a vertical partition. Any two partitions in the array, together with the space between them, can form a container. + Given an array $ht$, each element represents the height of a vertical wall. Any two walls, together with the space between them, form a container. + + The container's capacity is its height multiplied by its width (an area). Its height is determined by the shorter wall, and its width is the difference between the two walls' array indices. + + Choose two walls from the array to maximize the container's capacity, and return that maximum capacity. An example is shown in the figure below. - The capacity of the container equals the product of its height and width (that is, its area), where the height is determined by the shorter partition and the width is the difference between the array indices of the two partitions. +![Example input for the maximum capacity problem](max_capacity_problem.assets/max_capacity_example.png) - Select two partitions in the array such that the capacity of the resulting container is maximized, and return that maximum capacity. An example is shown in the figure below. +A container is formed by two walls, **so the state of this problem is the pair of wall indices, denoted by $[i, j]$**. -![Example data for the max capacity problem](max_capacity_problem.assets/max_capacity_example.png) - -The container is formed by any two partitions, **so the state of this problem is the indices of the two partitions, denoted by $[i, j]$**. - -According to the problem statement, capacity equals height multiplied by width, where the height is determined by the shorter partition and the width is the difference between the array indices of the two partitions. Let the capacity be $cap[i, j]$; then we obtain the following formula: +Capacity is height times width, with the height determined by the shorter wall and the width equal to the difference between the wall indices. Writing the capacity as $cap[i, j]$, we have: $$ cap[i, j] = \min(ht[i], ht[j]) \times (j - i) $$ -Let the array length be $n$. Then the number of ways to choose two partitions (that is, the total number of states) is $C_n^2 = \frac{n(n - 1)}{2}$. The most straightforward approach is to **exhaustively enumerate all states** to find the maximum capacity, which has a time complexity of $O(n^2)$. +For an array of length $n$, there are $C_n^2 = \frac{n(n - 1)}{2}$ pairs of walls (possible states). The most direct approach is to **enumerate all states** and find the maximum capacity, taking $O(n^2)$ time. -### Greedy Strategy Determination +### Choosing a greedy strategy -This problem has a more efficient solution. As shown in the figure below, consider a state $[i, j]$ where $i < j$ and $ht[i] < ht[j]$. In this case, $i$ is the shorter partition and $j$ is the taller partition. +A more efficient solution is possible. Consider the state $[i, j]$ in the figure below, where $i < j$ and $ht[i] < ht[j]$: wall $i$ is shorter and wall $j$ is taller. ![Initial state](max_capacity_problem.assets/max_capacity_initial_state.png) -As shown in the figure below, **if we now move the taller partition $j$ inward toward the shorter partition $i$, the capacity will definitely decrease**. +As shown in the figure below, **moving the taller wall $j$ inward toward the shorter wall $i$ must reduce the capacity**. -This is because after moving the taller partition $j$, the width $j-i$ definitely decreases. Since the height is determined by the shorter partition, the height can only stay the same ($i$ remains the shorter partition) or decrease ($j$ becomes the shorter partition after being moved). +Moving wall $j$ inward always reduces the width $j-i$. Since the shorter wall determines the height, the height can only stay the same (if $i$ remains shorter) or decrease (if the new wall $j$ is shorter). -![State after moving the long partition inward](max_capacity_problem.assets/max_capacity_moving_long_board.png) +![State after moving the taller wall inward](max_capacity_problem.assets/max_capacity_moving_long_board.png) -Conversely, **only by moving the shorter partition $i$ inward can the capacity possibly increase**. Although the width will definitely decrease, **the height may increase** (the moved partition at $i$ may be taller). For example, in the figure below, the area increases after moving the shorter partition. +Conversely, **only moving the shorter wall $i$ inward can potentially increase the capacity**. Although the width must decrease, **the height may increase** if the new wall $i$ is taller. The figure below shows an example where moving the shorter wall increases the area. -![State after moving the short partition inward](max_capacity_problem.assets/max_capacity_moving_short_board.png) +![State after moving the shorter wall inward](max_capacity_problem.assets/max_capacity_moving_short_board.png) -From this, we can derive the greedy strategy for this problem: initialize two pointers at the two ends, and in each round move the pointer corresponding to the shorter partition inward until the two pointers meet. +This gives us a greedy strategy: initialize two pointers at opposite ends of the container. At each step, move the pointer at the shorter wall inward until the pointers meet. -The figure below shows the execution process of the greedy strategy. +The figure below illustrates the greedy process. -1. In the initial state, pointers $i$ and $j$ are at both ends of the array. -2. Calculate the capacity of the current state $cap[i, j]$, and update the maximum capacity. -3. Compare the heights of partitions $i$ and $j$, and move the pointer corresponding to the shorter partition inward by one position. +1. Initially, pointers $i$ and $j$ are at opposite ends of the array. +2. Calculate the current capacity $cap[i, j]$ and update the maximum capacity. +3. Compare the heights of walls $i$ and $j$, and move the shorter wall inward by one position. 4. Repeat steps `2.` and `3.` until $i$ and $j$ meet. === "<1>" - ![Greedy process for the max capacity problem](max_capacity_problem.assets/max_capacity_greedy_step1.png) + ![Greedy steps for the maximum capacity problem](max_capacity_problem.assets/max_capacity_greedy_step1.png) === "<2>" ![max_capacity_greedy_step2](max_capacity_problem.assets/max_capacity_greedy_step2.png) @@ -72,28 +72,28 @@ The figure below shows the execution process of the greedy strategy. === "<9>" ![max_capacity_greedy_step9](max_capacity_problem.assets/max_capacity_greedy_step9.png) -### Code Implementation +### Code implementation -The code runs for at most $n$ rounds, **so the time complexity is $O(n)$**. +The loop runs at most $n$ times, **so the time complexity is $O(n)$**. -Variables $i$, $j$, and $res$ use only a constant amount of extra space, **so the space complexity is $O(1)$**. +The variables $i$, $j$, and $res$ require constant extra space, **so the space complexity is $O(1)$**. ```src [file]{max_capacity}-[class]{}-[func]{max_capacity} ``` -### Correctness Proof +### Proof of correctness -The reason greedy is faster than exhaustive enumeration is that each round of greedy selection "skips" some states. +The greedy algorithm is faster than exhaustive search because each greedy choice skips some states. -For example, in state $cap[i, j]$, suppose $i$ is the shorter partition and $j$ is the taller partition. If we greedily move the shorter partition $i$ inward by one position, the states shown in the figure below will be "skipped." **This means that their capacities can no longer be checked later**. +For example, in state $cap[i, j]$, suppose $i$ is the shorter wall and $j$ the taller one. Moving the shorter wall $i$ inward by one position skips the states shown in the figure below. **Their capacities will never be checked later**. $$ cap[i, i+1], cap[i, i+2], \dots, cap[i, j-2], cap[i, j-1] $$ -![States skipped by moving the short partition](max_capacity_problem.assets/max_capacity_skipped_states.png) +![States skipped by moving the shorter wall](max_capacity_problem.assets/max_capacity_skipped_states.png) -A closer look shows that **these skipped states are exactly the states obtained by moving the taller partition $j$ inward**. We have already proven that moving the taller partition inward will definitely decrease the capacity. Therefore, none of the skipped states can be the optimal solution, **so skipping them does not cause us to miss the optimum**. +Observe that **the skipped states are precisely those obtained by moving the taller wall $j$ inward**. We have already shown that moving the taller wall inward must reduce capacity. None of these skipped states can therefore be optimal, and **skipping them cannot cause us to miss the optimal solution**. -The above analysis shows that moving the shorter partition is a "safe" operation, and that the greedy strategy is effective. +This shows that moving the shorter wall is safe, and that the greedy strategy is valid. diff --git a/en/docs/chapter_greedy/max_product_cutting_problem.md b/en/docs/chapter_greedy/max_product_cutting_problem.md index 771ad8c69..dcd542535 100644 --- a/en/docs/chapter_greedy/max_product_cutting_problem.md +++ b/en/docs/chapter_greedy/max_product_cutting_problem.md @@ -1,28 +1,28 @@ -# Maximum Product Cutting Problem +# Maximum product cutting problem !!! question - Given a positive integer $n$, split it into the sum of at least two positive integers and find the maximum product of the resulting integers, as shown in the figure below. + Given a positive integer $n$, split it into a sum of at least two positive integers. What is the largest possible product of those integers? The problem is illustrated in the figure below. -![Problem definition of max product cutting](max_product_cutting_problem.assets/max_product_cutting_definition.png) +![Definition of the maximum product cutting problem](max_product_cutting_problem.assets/max_product_cutting_definition.png) -Suppose we split $n$ into $m$ integer factors, where the $i$-th factor is denoted as $n_i$, that is +Suppose we split $n$ into $m$ integers that will be factors in the product. Denote the $i$-th factor by $n_i$, so that $$ n = \sum_{i=1}^{m}n_i $$ -The goal of this problem is to find the maximum product of all integer factors, namely +Our goal is to maximize the product of all these integer factors: $$ \max(\prod_{i=1}^{m}n_i) $$ -We need to determine how many parts $m$ there should be and what each $n_i$ should be. +We need to determine how many parts $m$ to create and what each $n_i$ should be. -### Determining the Greedy Strategy +### Choosing a greedy strategy -As a rule of thumb, the product of two integers is often greater than their sum. Suppose we split off a factor of $2$ from $n$; the resulting product is $2(n-2)$. We compare this product with $n$: +The product of two integers is often greater than their sum. Suppose we split off a factor of $2$ from $n$, giving the product $2(n-2)$. Comparing this product with $n$ gives: $$ \begin{aligned} @@ -32,54 +32,54 @@ n & \geq 4 \end{aligned} $$ -As shown in the figure below, when $n \geq 4$, splitting out a $2$ will increase the product, **which indicates that integers greater than or equal to $4$ should all be split**. +As shown in the figure below, when $n \geq 4$, splitting off a $2$ increases the product. **This suggests that every integer greater than or equal to $4$ should be split further**. -**Greedy strategy one**: If the splitting scheme contains a factor $\geq 4$, it should be split further. The final splitting scheme should contain only the factors $1$, $2$, and $3$. +**Greedy strategy 1**: If a factor $\geq 4$ appears in a split, divide it further. The final split should contain only the factors $1$, $2$, and $3$. -![Splitting causes product to increase](max_product_cutting_problem.assets/max_product_cutting_greedy_infer1.png) +![Splitting increases the product](max_product_cutting_problem.assets/max_product_cutting_greedy_infer1.png) -Next, consider which factor is optimal. Among the three factors $1$, $2$, and $3$, clearly $1$ is the worst, because $1 \times (n-1) < n$ always holds, meaning splitting out $1$ will actually decrease the product. +Next, consider which factor is best. Of $1$, $2$, and $3$, clearly $1$ is the worst: $1 \times (n-1) < n$ always holds, so splitting off a $1$ reduces the product. -As shown in the figure below, when $n = 6$, we have $3 \times 3 > 2 \times 2 \times 2$. **This means that splitting out $3$ is better than splitting out $2$**. +As shown in the figure below, when $n = 6$, we have $3 \times 3 > 2 \times 2 \times 2$. **This means that splitting into factors of $3$ is better than splitting into factors of $2$**. -**Greedy strategy two**: In the splitting scheme, there should be at most two $2$s, because three $2$s can always be replaced by two $3$s to obtain a larger product. +**Greedy strategy 2**: A split should contain at most two factors of $2$, because three $2$s can always be replaced with two $3$s to obtain a larger product. -![Optimal splitting factor](max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png) +![Optimal factors](max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png) -In summary, the following greedy strategies can be derived. +These observations lead to the following greedy strategy. -1. Input integer $n$, continuously split out factor $3$ until the remainder is $0$, $1$, or $2$. -2. When the remainder is $0$, it means $n$ is a multiple of $3$, so no further action is needed. -3. When the remainder is $2$, do not split it further; keep it as is. -4. When the remainder is $1$, since $2 \times 2 > 1 \times 3$, replace the final $3$ and the remaining $1$ with two $2$s. +1. Starting with $n$, keep splitting off factors of $3$ until the remainder is $0$, $1$, or $2$. +2. If the remainder is $0$, then $n$ is a multiple of $3$, and no further action is needed. +3. If the remainder is $2$, keep it without splitting it further. +4. If the remainder is $1$, replace the last $3$ and the remaining $1$ with two $2$s, since $2 \times 2 > 1 \times 3$. -### Code Implementation +### Code implementation -As shown in the figure below, we do not need loops to split the integer. Instead, we use integer division to obtain the number of $3$s, denoted by $a$, and the modulo operation to obtain the remainder $b$, giving: +As shown in the figure below, we do not need a loop to split the integer. Floor division gives the number of $3$s, denoted by $a$, and the modulo operation gives the remainder $b$. Thus: $$ n = 3 a + b $$ -Please note that for the edge case of $n \leq 3$, a $1$ must be split out, with product $1 \times (n - 1)$. +For the boundary cases $n \leq 3$, we must split off a $1$, giving a product of $1 \times (n - 1)$. ```src [file]{max_product_cutting}-[class]{}-[func]{max_product_cutting} ``` -![Calculation method for max product cutting](max_product_cutting_problem.assets/max_product_cutting_greedy_calculation.png) +![Calculating the maximum product](max_product_cutting_problem.assets/max_product_cutting_greedy_calculation.png) -**The time complexity depends on how exponentiation is implemented in the programming language**. Taking Python as an example, there are three commonly used ways to compute powers. +**The time complexity depends on how the programming language implements exponentiation**. Python, for example, offers three common ways to compute powers. -- Both the operator `**` and the function `pow()` have time complexity $O(\log⁡ a)$. -- The function `math.pow()` internally calls the C library's `pow()` function, which performs floating-point exponentiation, with time complexity $O(1)$. +- The `**` operator and the `pow()` function both have a time complexity of $O(\log⁡ a)$. +- The `math.pow()` function calls the C library's `pow()` function internally, performing floating-point exponentiation in $O(1)$ time. -Variables $a$ and $b$ use a constant amount of extra space, **therefore the space complexity is $O(1)$**. +The variables $a$ and $b$ use constant extra space, **so the space complexity is $O(1)$**. -### Correctness Proof +### Proof of correctness -We use proof by contradiction and consider only the case where $n \geq 4$. +We use proof by contradiction, considering only $n \geq 4$. -1. **All factors $\leq 3$**: Suppose the optimal splitting scheme includes a factor $x \geq 4$. Then it can be further split into $2(x-2)$ to obtain a larger (or equal) product. This contradicts the assumption. -2. **The splitting scheme does not contain $1$**: Suppose the optimal splitting scheme includes a factor of $1$. Then it can be merged into another factor to obtain a larger product. This contradicts the assumption. -3. **The splitting scheme contains at most two $2$s**: Suppose the optimal splitting scheme includes three $2$s. Then they can be replaced by two $3$s, yielding a larger product. This contradicts the assumption. +1. **All factors are $\leq 3$**: Suppose an optimal split contains a factor $x$ that is $\geq 4$. We can split it further to obtain $2(x-2)$, giving a greater (or equal) product. This contradicts the assumption. +2. **The split contains no $1$**: Suppose an optimal split contains a factor of $1$. We can merge it into another factor to obtain a larger product, contradicting the assumption. +3. **The split contains at most two $2$s**: Suppose an optimal split contains three $2$s. Replacing them with two $3$s gives a larger product, contradicting the assumption. diff --git a/en/docs/chapter_greedy/summary.md b/en/docs/chapter_greedy/summary.md index 7d21b338b..1bb97a0ec 100644 --- a/en/docs/chapter_greedy/summary.md +++ b/en/docs/chapter_greedy/summary.md @@ -1,14 +1,14 @@ # Summary -### Key Review +### Key takeaways -- Greedy algorithms are typically used to solve optimization problems. The principle is to make locally optimal decisions at each decision stage in hopes of obtaining a globally optimal solution. -- Greedy algorithms iteratively make one greedy choice after another, transforming the problem into a smaller subproblem in each round, until the problem is solved. -- Greedy algorithms are not only simple to implement, but also have high problem-solving efficiency. Compared to dynamic programming, greedy algorithms typically have lower time complexity. -- In the coin change problem, for certain coin combinations, greedy algorithms can guarantee finding the optimal solution; for other coin combinations, however, greedy algorithms may find very poor solutions. -- Problems suitable for solving with greedy algorithms have two major properties: greedy choice property and optimal substructure. The greedy choice property represents the effectiveness of the greedy strategy. -- For some complex problems, proving the greedy choice property is not simple. Relatively speaking, disproving it is easier, such as in the coin change problem. -- Solving greedy problems mainly consists of three steps: problem analysis, determining the greedy strategy, and correctness proof. Among these, determining the greedy strategy is the core step, and correctness proof is often the main difficulty. -- The fractional knapsack problem, based on the 0-1 knapsack problem, allows selecting fractions of items, and therefore can be solved using greedy algorithms. The correctness of the greedy strategy can be proven using proof by contradiction. -- The max capacity problem can be solved using exhaustive enumeration with time complexity $O(n^2)$. By designing a greedy strategy to move the shorter side inward in each round, the time complexity can be optimized to $O(n)$. -- In the max product cutting problem, we successively derive two greedy strategies: integers $\geq 4$ should all continue to be split, and the optimal splitting factor is $3$. The code includes exponentiation operations, and the time complexity depends on the implementation method of exponentiation, typically being $O(1)$ or $O(\log n)$. +- Greedy algorithms are commonly used for optimization. At each decision point, they make a locally optimal choice in the hope of reaching a globally optimal solution. +- A greedy algorithm repeatedly makes greedy choices, turning the problem into a smaller subproblem at each step until it is solved. +- Greedy algorithms are easy to implement and efficient. Their time complexity is usually lower than that of dynamic programming. +- For some coin denominations, a greedy algorithm is guaranteed to find an optimal coin change solution. For others, it may produce a very poor solution. +- Problems suited to greedy algorithms have two properties: the greedy-choice property and optimal substructure. The greedy-choice property establishes that the strategy works. +- Proving the greedy-choice property can be difficult for complex problems. Disproving it is often easier, as coin change illustrates. +- Solving a greedy problem involves three main steps: analyze the problem, choose a greedy strategy, and prove correctness. Choosing the strategy is the central step, while proving correctness is often the hardest. +- The fractional knapsack problem extends 0-1 knapsack by allowing fractions of items, making a greedy solution possible. The strategy can be proved correct by contradiction. +- The maximum capacity problem can be solved by exhaustive search in $O(n^2)$ time. A greedy strategy that moves the shorter wall inward at each step reduces this to $O(n)$. +- For the maximum product cutting problem, we derived two greedy strategies: continue splitting integers $\geq 4$, and favor factors of $3$. The implementation uses exponentiation, so its time complexity depends on the exponentiation method, usually $O(1)$ or $O(\log n)$. diff --git a/en/docs/chapter_hashing/exercises.md b/en/docs/chapter_hashing/exercises.md index 78c6fdb65..1a80f2474 100644 --- a/en/docs/chapter_hashing/exercises.md +++ b/en/docs/chapter_hashing/exercises.md @@ -1,21 +1,21 @@ # Exercises -## Concept Review +## Check your understanding -### Searching After a Hash Collision +### Searching after a hash collision -A hash table has 5 buckets and uses the hash function $h(x)=x \bmod 5$. When a collision occurs, elements are placed one after another in a list within that bucket. -Insert `[1, 6, 11, 7]` in order: +A hash table has five buckets and uses the hash function $h(x)=x \bmod 5$. When elements collide, they are added in order to the list in that bucket. +Insert `[1, 6, 11, 7]` in sequence: -1. Write the contents of buckets 0–4. -2. When searching for 6, which bucket is checked first, and which elements are examined in order? -3. Based on the bucket contents from Question 1, do later insertions overwrite earlier ones? Explain using this collision-resolution method. +1. Write out the contents of buckets 0 through 4. +2. When searching for 6, which bucket is visited first, and which elements are checked in order? +3. Based on the bucket contents from question 1, did later elements overwrite earlier ones? Explain using this collision-handling method. -??? success "Answer" +??? success "Reference answer" - 1. Because $1\bmod5=6\bmod5=11\bmod5=1$, while $7\bmod5=2$, the buckets are: + 1. Since $1\bmod5=6\bmod5=11\bmod5=1$ and $7\bmod5=2$, the buckets contain: ```text 0: [] @@ -25,25 +25,25 @@ Insert `[1, 6, 11, 7]` in order: 4: [] ``` - 2. A search for 6 first goes to bucket 1, then compares 1 and 6 in order. The target is found on the second comparison. + 2. Searching for 6 first visits bucket 1 and compares against 1 and then 6, finding the target on the second comparison. - 3. Equal hash values mean only that the elements go into the same bucket, not that the elements are equal. Separate chaining keeps all colliding elements in the bucket - and compares them one at a time during a search, so 1, 6, and 11 do not overwrite one another. + 3. Equal hash values mean only that the elements land in the same bucket, not that the elements themselves are equal. Separate chaining keeps all colliding elements in the bucket + and compares them one by one during a search, so 1, 6, and 11 do not overwrite one another. -### Where Do Elements Go After a Hash Table Expands? +### Where do elements go when a hash table grows? -A hash table using separate chaining originally has 5 buckets and the hash function $h(x)=x\bmod5$. +A hash table using separate chaining initially has five buckets and the hash function $h(x)=x\bmod5$. The keys `[1, 6, 11]` are all in bucket 1. -The table is now expanded to 7 buckets, and the hash function becomes $h(x)=x\bmod7$: +The table is now expanded to seven buckets, and the hash function becomes $h(x)=x\bmod7$: -1. Calculate the new bucket number for 1, 6, and 11. +1. Calculate the new bucket number for each of 1, 6, and 11. 2. Which buckets contain elements after the expansion? -3. During the expansion, can the list from the old bucket 1 simply be copied into the new bucket 1? Explain using the results from Questions 1 and 2. +3. Can the list in the old bucket 1 simply be copied unchanged into the new bucket 1? Explain using your results from questions 1 and 2. -??? success "Answer" +??? success "Reference answer" 1. The new bucket numbers are: @@ -51,51 +51,51 @@ The table is now expanded to 7 buckets, and the hash function becomes $h(x)=x\bm - $6\bmod7=6$; - $11\bmod7=4$. - 2. Bucket 1 stores 1, bucket 4 stores 11, and bucket 6 stores 6. The three keys are no longer crowded into the same bucket. + 2. Bucket 1 holds 1, bucket 4 holds 11, and bucket 6 holds 6. The three keys no longer share one bucket. - 3. The list cannot be copied as is. A bucket number is calculated by taking the key modulo the number of buckets. When the bucket count changes from 5 to 7, a key's bucket number may change, - so the position of every key must be recalculated. If the old bucket 1 were copied unchanged, later searches using the new formula would go to bucket 6 for 6 and bucket 4 for 11 - and would fail to find them. + 3. The list cannot simply be copied unchanged. A bucket number is the remainder of the key divided by the number of buckets. When that number changes from 5 to 7, a key's bucket may change, + so every key's position must be recalculated. If we copied the old bucket 1 unchanged, subsequent searches for 6 and 11 using the new formula + would visit buckets 6 and 4, respectively, and fail to find them. -### Can 11 Still Be Found After Deleting 6? +### Can we still find 11 after deleting 6? -A hash table has 5 positions with indices `0–4` and uses the hash function $h(x)=x\bmod5$. -When a collision occurs, it searches to the right from the index produced by the hash function for the first empty position. +A hash table has five positions with indices `0-4` and uses the hash function $h(x)=x\bmod5$. +When a collision occurs, it starts at the computed index and searches to the right for the first empty position. Insert `[1, 6, 11]` in order: 1. At which index is each number ultimately stored? -2. When searching for 11, which indices are examined in order? -3. Suppose deleting 6 changes its position directly to an "unused empty position," and a search stops whenever it reaches an empty position. - What happens when searching for 11 afterward? Is this search result correct? If there is a problem, how can it be avoided? +2. Which indices are checked, in order, when searching for 11? +3. Suppose deleting 6 marks its position as an empty slot that has never been used, and a search stops on reaching an empty slot. + What happens when we search for 11 again? Is the result correct? If not, how can we avoid the problem? -??? success "Answer" +??? success "Reference answer" 1. The number 1 is stored at index 1. The number 6 also maps to index 1, so after the collision it is stored at index 2. - The number 11 also starts at index 1, skips the occupied indices 1 and 2, and is ultimately stored at index 3. + The number 11 likewise starts at index 1, skips occupied indices 1 and 2, and is finally stored at index 3. - 2. A search for 11 examines indices `1, 2, 3` in order and finds it at index 3. + 2. Searching for 11 checks indices `1, 2, 3` in order and finds it at index 3. - 3. If index 2 is changed to mean "never used," a search for 11 checks index 1 and then stops at index 2, - incorrectly concluding that 11 is absent. Deletion should leave a "deleted" marker. - A search that reaches this marker continues to the next index (wrapping from index 4 to index 0), while a later insertion may still reuse the position. + 3. If index 2 is marked as an empty slot that has never been used, a search for 11 checks index 1 and then stops at index 2, + incorrectly concluding that 11 is absent. Deletion should leave a deleted marker instead: + a search continues to the next index when it encounters this marker, wrapping from index 4 to index 0. Later insertions can still reuse the position. -## Programming Exercises +## Programming exercises -### Compare the Character Counts of Two Strings +### Comparing the characters in two strings Given two strings `s` and `t` containing only lowercase English letters, -you may rearrange the characters in `s` in any order, but you may not add, remove, or replace characters. +you may rearrange the characters in `s` freely, but may not add, delete, or replace any characters. -Determine whether the rearranged string can form `t`. Return `true` if it can and `false` otherwise. -Use a hash table to record how many times each letter occurs. Do not sort the characters in the strings. +Determine whether the rearrangement can produce `t`. Return `true` if it can, or `false` otherwise. +Use a hash table to record the frequency of each letter; do not sort the characters in either string. ??? tip "Hints" - 1. If the two strings have different lengths, they cannot contain each character the same number of times - 2. Use a hash table to record the count of each letter; increment the corresponding count while scanning s - 3. Decrement the corresponding count while scanning t; the strings contain each character the same number of times only if every count is 0 at the end + 1. Strings of different lengths cannot contain the same characters with the same frequencies. + 2. Use a hash table to record each letter's count; increment the appropriate count by 1 while scanning s. + 3. Decrement the appropriate count by 1 while scanning t. The character frequencies match only if all counts end at 0. [LeetCode](https://leetcode.com/problems/valid-anagram/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_hashing/hash_algorithm.md b/en/docs/chapter_hashing/hash_algorithm.md index 7f0c9767e..cc2c75dc1 100644 --- a/en/docs/chapter_hashing/hash_algorithm.md +++ b/en/docs/chapter_hashing/hash_algorithm.md @@ -1,60 +1,60 @@ -# Hash Algorithm +# Hash algorithms -The previous two sections introduced the working principle of hash tables and the methods to handle hash collisions. However, both open addressing and separate chaining **can only ensure that the hash table functions normally when hash collisions occur, but cannot reduce the frequency of hash collisions**. +The previous two sections explained how hash tables work and how collisions are resolved. Whether we use open addressing or separate chaining, **these techniques keep the table working when collisions occur, but do not reduce how often collisions happen**. -If hash collisions occur too frequently, the performance of the hash table will deteriorate drastically. As shown in the figure below, for a separate chaining hash table, in the ideal case, the key-value pairs are evenly distributed across the buckets, achieving optimal query efficiency; in the worst case, all key-value pairs are stored in the same bucket, degrading the time complexity to $O(n)$. +Frequent collisions can severely degrade performance. In the separate-chaining table shown below, the ideal case distributes pairs evenly among buckets for the best lookup efficiency. In the worst case, all pairs occupy the same bucket, degrading the time complexity to $O(n)$. -![Ideal and worst cases of hash collisions](hash_algorithm.assets/hash_collision_best_worst_condition.png) +![Best and worst cases for hash collisions](hash_algorithm.assets/hash_collision_best_worst_condition.png) -**The distribution of key-value pairs is determined by the hash function**. Recall the steps of the hash function: first compute the hash value, then take it modulo the array length: +**The hash function determines how key-value pairs are distributed**. Recall its two steps: calculate a hash value, then take it modulo the array length: ```shell index = hash(key) % capacity ``` -Observing the above formula, when the hash table capacity `capacity` is fixed, **the hash algorithm `hash()` determines the output value**, thereby determining the distribution of key-value pairs in the hash table. +For a fixed `capacity`, **the hash algorithm `hash()` determines the output**, and therefore the distribution of pairs in the table. -This means that, to reduce the probability of hash collisions, we should focus on the design of the hash algorithm `hash()`. +To lower the likelihood of collisions, we should therefore focus on designing `hash()`. -## Goals of Hash Algorithms +## Goals of a hash algorithm -To build a hash table that is both fast and robust, a hash algorithm should have the following properties: +To build a hash table that is both fast and reliable, its hash algorithm should have these properties. -- **Determinism**: For the same input, the hash algorithm should always produce the same output. Only then can the hash table be reliable. -- **High efficiency**: The process of computing the hash value should be fast enough. The smaller the computational overhead, the more practical the hash table. -- **Uniform distribution**: The hash algorithm should ensure that key-value pairs are evenly distributed in the hash table. The more uniform the distribution, the lower the probability of hash collisions. +- **Determinism**: The same input must always produce the same output, ensuring reliable lookup. +- **Efficiency**: Calculating a hash should be fast. Lower computational overhead makes the table more practical. +- **Uniform distribution**: The algorithm should distribute pairs evenly across the table. More uniform distribution means fewer collisions. -In fact, hash algorithms are not only used to implement hash tables but are also widely applied in other fields. +Hash algorithms also have many applications beyond hash tables. -- **Password storage**: To protect the security of user passwords, systems usually do not store the plaintext passwords but rather the hash values of the passwords. When a user enters a password, the system calculates the hash value of the input and compares it with the stored hash value. If they match, the password is considered correct. -- **Data integrity check**: The data sender can calculate the hash value of the data and send it along; the receiver can recalculate the hash value of the received data and compare it with the received hash value. If they match, the data is considered intact. +- **Password storage**: To protect passwords, systems usually store password hashes rather than plaintext passwords. When a user enters a password, the system hashes it and compares the result with the stored hash. A match means the password is accepted. +- **Data-integrity checking**: A sender can calculate a hash of the data and send it alongside the data. The receiver recomputes the hash and compares it with the received hash. A match indicates that the data is intact. -For cryptographic applications, hash algorithms need stronger security properties to prevent reverse engineering, such as inferring the original password from a hash value. +Cryptographic applications require stronger security properties to prevent reverse engineering, such as recovering an original password from its hash. -- **Unidirectionality**: It should be impossible to deduce any information about the input data from the hash value. -- **Collision resistance**: It should be extremely difficult to find two different inputs that produce the same hash value. -- **Avalanche effect**: Minor changes in the input should lead to significant and unpredictable changes in the output. +- **One-wayness**: The hash should not reveal any information about the input data. +- **Collision resistance**: Finding two different inputs with the same hash should be extremely difficult. +- **Avalanche effect**: A small input change should produce a large, unpredictable output change. -Note that **"uniform distribution" and "collision resistance" are two independent concepts**. Satisfying uniform distribution does not necessarily mean collision resistance. For example, under random input `key`, the hash function `key % 100` can produce a uniformly distributed output. However, this hash algorithm is too simple, and all `key` with the same last two digits will have the same output, making it easy to deduce a usable `key` from the hash value, thereby cracking the password. +**Uniform distribution and collision resistance are independent concepts**. Uniform output does not guarantee collision resistance. For example, with random keys, `key % 100` can produce uniform outputs. But this algorithm is too simple: all keys with the same last two digits give the same output. It is therefore easy to infer a usable key from a hash and crack a password. -## Design of Hash Algorithms +## Designing a hash algorithm -The design of hash algorithms is a complex issue that requires consideration of many factors. However, for some less demanding scenarios, we can also design some simple hash algorithms. +Designing a hash algorithm is complex and involves many factors. For less demanding uses, however, we can devise simple algorithms. -- **Additive hash**: Add up the ASCII codes of each character in the input and use the total sum as the hash value. -- **Multiplicative hash**: Leverage the low correlation introduced by multiplication: multiply by a constant at each step and accumulate the ASCII codes of the characters into the hash value. -- **XOR hash**: Accumulate the hash value by XORing each element of the input data. -- **Rotating hash**: Accumulate the ASCII code of each character into a hash value, performing a rotation operation on the hash value before each accumulation. +- **Additive hashing**: Add the ASCII codes of all input characters and use the sum as the hash. +- **Multiplicative hashing**: Use multiplication to mix values, multiplying by a constant each round and accumulating character ASCII codes into the hash. +- **XOR hashing**: Accumulate each input element into a hash using exclusive OR. +- **Rotational hashing**: Accumulate each character's ASCII code into a hash, rotating the hash before each accumulation. ```src [file]{simple_hash}-[class]{}-[func]{rot_hash} ``` -We can observe that the final step of each hash algorithm is to take the result modulo the large prime $1000000007$, ensuring that the hash value stays within a suitable range. This naturally raises a question: why emphasize using a prime modulus, and what are the drawbacks of using a composite modulus? +Each algorithm ends by taking the result modulo the large prime $1000000007$ to keep it within a suitable range. Why emphasize a prime modulus? What goes wrong with a composite modulus? This is an interesting question. -In short: **using a large prime as the modulus helps maximize the uniformity of hash values**. Because a prime shares no common factors with other numbers, it can reduce periodic patterns introduced by the modulo operation and thus mitigate hash collisions. +The conclusion is that **using a large prime modulus helps make hash values as uniformly distributed as possible**. Because a prime shares no nontrivial factors with other numbers, it reduces periodic patterns introduced by the modulo operation and thus avoids collisions. -For example, suppose we choose the composite number $9$ as the modulus, which can be divided by $3$, then all `key` divisible by $3$ will be mapped to hash values $0$, $3$, $6$. +For example, choose the composite modulus $9$, which is divisible by $3$. Every key divisible by $3$ then maps to one of just three hash values: $0$, $3$, or $6$. $$ \begin{aligned} @@ -64,7 +64,7 @@ $$ \end{aligned} $$ -If the input `key` values happen to follow this kind of arithmetic progression, the hash values will cluster, worsening hash collisions. Now suppose we replace `modulus` with the prime number $13$. Because `key` and `modulus` share no common factors, the output hash values become much more evenly distributed. +If the input keys follow this arithmetic progression, the hash values cluster, increasing collisions. Now replace the modulus with the prime $13$. With no common factor between the key pattern and the modulus, the output becomes noticeably more uniform. $$ \begin{aligned} @@ -74,71 +74,71 @@ $$ \end{aligned} $$ -It is worth noting that if the `key` is guaranteed to be randomly and uniformly distributed, then choosing a prime number or a composite number as the modulus can both produce uniformly distributed hash values. However, when the distribution of `key` has some periodicity, modulo a composite number is more likely to result in clustering. +If the keys are already uniformly random, either a prime or a composite modulus can produce uniformly distributed hashes. When the keys have a periodic pattern, however, a composite modulus is more likely to cause clustering. -In summary, we usually choose a prime number as the modulus, and this prime number should be large enough to eliminate periodic patterns as much as possible, enhancing the robustness of the hash algorithm. +We therefore usually choose a prime modulus, preferably a large one, to suppress periodic patterns as much as possible and make the algorithm more robust. -## Common Hash Algorithms +## Common hash algorithms -It is easy to see that the simple hash algorithms introduced above are fairly "fragile" and fall far short of the design goals of hash algorithms. For example, because addition and XOR are commutative, additive hash and XOR hash cannot distinguish strings with the same characters in a different order, which may worsen hash collisions and introduce security risks. +The simple algorithms above are fragile and fall well short of our design goals. Addition and XOR are commutative, for example, so additive and XOR hashing cannot distinguish strings containing the same characters in different orders. This can increase collisions and create security problems. -In practice, we usually use some standard hash algorithms, such as MD5, SHA-1, SHA-2, and SHA-3. They can map input data of any length to a fixed-length hash value. +In practice, standard algorithms such as MD5, SHA-1, SHA-2, and SHA-3 are commonly used. They map inputs of arbitrary length to fixed-length hash values. -Over the past century, hash algorithms have been in a continuous process of upgrading and optimization. Some researchers strive to improve the performance of hash algorithms, while others, including hackers, are dedicated to finding security issues in hash algorithms. The table below shows hash algorithms commonly used in practical applications. +Over nearly a century, hash algorithms have continually evolved. Some researchers work to improve their performance, while others, along with hackers, search for security weaknesses. The table below lists commonly used algorithms. -- MD5 and SHA-1 have been successfully attacked multiple times and are thus abandoned in various security applications. -- SHA-2 series, especially SHA-256, is one of the most secure hash algorithms to date, with no successful attacks reported, hence commonly used in various security applications and protocols. -- SHA-3 has lower implementation costs and higher computational efficiency compared to SHA-2, but its current usage coverage is not as extensive as the SHA-2 series. +- MD5 and SHA-1 have been successfully attacked repeatedly and have been retired from security applications. +- SHA-256, part of the SHA-2 family, is among the most secure hash algorithms. No successful attack has been reported, so it is widely used in security applications and protocols. +- SHA-3 has lower implementation overhead and higher computational efficiency than SHA-2, but is not yet as widely used.

Table   Common hash algorithms

-| | MD5 | SHA-1 | SHA-2 | SHA-3 | -| --------------- | ----------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------- | ---------------------------- | -| Release Year | 1992 | 1995 | 2002 | 2008 | -| Output Length | 128 bit | 160 bit | 256/512 bit | 224/256/384/512 bit | -| Hash Collisions | Frequent | Frequent | Rare | Rare | -| Security Level | Low, has been successfully attacked | Low, has been successfully attacked | High | High | -| Applications | Abandoned, still used for data integrity checks | Abandoned | Cryptocurrency transaction verification, digital signatures, etc. | Can be used to replace SHA-2 | +| | MD5 | SHA-1 | SHA-2 | SHA-3 | +| -------- | ------------------------------ | ---------------- | ---------------------------- | ------------------- | +| Introduced | 1992 | 1995 | 2002 | 2008 | +| Output length | 128 bit | 160 bit | 256/512 bit | 224/256/384/512 bit | +| Hash collisions | More frequent | More frequent | Rare | Rare | +| Security | Low; successfully attacked | Low; successfully attacked | High | High | +| Applications | Deprecated; still used for integrity checks | Deprecated | Cryptocurrency transaction verification, digital signatures, and more | Can replace SHA-2 | -## Hash Values in Data Structures +## Hash values of data structures -We know that hash table keys can be integers, floating-point numbers, strings, and other data types. Programming languages usually provide built-in hash algorithms for these types to compute bucket indices in a hash table. Taking Python as an example, we can call the `hash()` function to compute hash values for various data types. +Hash-table keys can be integers, floating-point numbers, strings, and other types. Programming languages usually provide built-in hashing for these types to calculate bucket indices. In Python, for example, `hash()` calculates hash values for various types. -- The hash values of integers and booleans are their own values. -- The calculation of hash values for floating-point numbers and strings is more complex, and interested readers are encouraged to study this on their own. -- The hash value of a tuple is obtained by hashing each of its elements and combining those results into a single hash value. -- An object's hash value is typically generated from its memory address. By overriding the object's hash method, it can instead be generated from the object's contents. +- Integers and Booleans have hash values equal to their own values. +- Hashing floating-point numbers and strings is more complex; interested readers can explore the details. +- A tuple's hash combines the hashes of its individual elements into one value. +- An object's hash is based on its memory address. Overriding its hash method allows hashing by content instead. !!! tip - Be aware that the definition and methods of the built-in hash value calculation functions in different programming languages vary. + Built-in hash functions differ in their definitions and implementations across programming languages. === "Python" ```python title="built_in_hash.py" num = 3 hash_num = hash(num) - # Hash value of integer 3 is 3 + # The hash of the integer 3 is 3 bol = True hash_bol = hash(bol) - # Hash value of boolean True is 1 + # The hash of the Boolean True is 1 dec = 3.14159 hash_dec = hash(dec) - # Hash value of decimal 3.14159 is 326484311674566659 + # The hash of the floating-point number 3.14159 is 326484311674566659 str = "Hello 算法" hash_str = hash(str) - # Hash value of string "Hello 算法" is 4617003410720528961 + # The hash of the string “Hello 算法” is 4617003410720528961 tup = (12836, "小哈") hash_tup = hash(tup) - # Hash value of tuple (12836, '小哈') is 1029005403108185979 + # The hash of the tuple (12836, '小哈') is 1029005403108185979 obj = ListNode(0) hash_obj = hash(obj) - # Hash value of ListNode object at 0x1058fd810 is 274267521 + # The hash of the node object is 274267521 ``` === "C++" @@ -146,22 +146,22 @@ We know that hash table keys can be integers, floating-point numbers, strings, a ```cpp title="built_in_hash.cpp" int num = 3; size_t hashNum = hash()(num); - // Hash value of integer 3 is 3 + // The hash of the integer 3 is 3 bool bol = true; size_t hashBol = hash()(bol); - // Hash value of boolean 1 is 1 + // The hash of the Boolean 1 is 1 double dec = 3.14159; size_t hashDec = hash()(dec); - // Hash value of decimal 3.14159 is 4614256650576692846 + // The hash of the floating-point number 3.14159 is 4614256650576692846 string str = "Hello 算法"; size_t hashStr = hash()(str); - // Hash value of string "Hello 算法" is 15466937326284535026 + // The hash of the string “Hello 算法” is 15466937326284535026 - // In C++, built-in std::hash() only provides hash values for basic data types - // Hash values for arrays and objects need to be implemented separately + // In C++, built-in std::hash() provides hashing only for basic data types + // Hashing for arrays and objects must be implemented separately ``` === "Java" @@ -169,27 +169,27 @@ We know that hash table keys can be integers, floating-point numbers, strings, a ```java title="built_in_hash.java" int num = 3; int hashNum = Integer.hashCode(num); - // Hash value of integer 3 is 3 + // The hash of the integer 3 is 3 boolean bol = true; int hashBol = Boolean.hashCode(bol); - // Hash value of boolean true is 1231 + // The hash of the Boolean true is 1231 double dec = 3.14159; int hashDec = Double.hashCode(dec); - // Hash value of decimal 3.14159 is -1340954729 + // The hash of the floating-point number 3.14159 is -1340954729 String str = "Hello 算法"; int hashStr = str.hashCode(); - // Hash value of string "Hello 算法" is -727081396 + // The hash of the string “Hello 算法” is -727081396 Object[] arr = { 12836, "小哈" }; int hashTup = Arrays.hashCode(arr); - // Hash value of array [12836, 小哈] is 1151158 + // The hash of the array [12836, 小哈] is 1151158 ListNode obj = new ListNode(0); int hashObj = obj.hashCode(); - // Hash value of ListNode object utils.ListNode@7dc5e7b4 is 2110121908 + // The hash of the node object utils.ListNode@7dc5e7b4 is 2110121908 ``` === "C#" @@ -197,33 +197,33 @@ We know that hash table keys can be integers, floating-point numbers, strings, a ```csharp title="built_in_hash.cs" int num = 3; int hashNum = num.GetHashCode(); - // Hash value of integer 3 is 3; + // The hash of the integer 3 is 3; bool bol = true; int hashBol = bol.GetHashCode(); - // Hash value of boolean true is 1; + // The hash of the Boolean true is 1; double dec = 3.14159; int hashDec = dec.GetHashCode(); - // Hash value of decimal 3.14159 is -1340954729; + // The hash of the floating-point number 3.14159 is -1340954729; string str = "Hello 算法"; int hashStr = str.GetHashCode(); - // Hash value of string "Hello 算法" is -586107568; + // The hash of the string “Hello 算法” is -586107568; object[] arr = [12836, "小哈"]; int hashTup = arr.GetHashCode(); - // Hash value of array [12836, 小哈] is 42931033; + // The hash of the array [12836, 小哈] is 42931033; ListNode obj = new(0); int hashObj = obj.GetHashCode(); - // Hash value of ListNode object 0 is 39053774; + // The hash of the node object 0 is 39053774; ``` === "Go" ```go title="built_in_hash.go" - // Go does not provide built-in hash code functions + // Go provides no built-in hash-code function ``` === "Swift" @@ -231,39 +231,39 @@ We know that hash table keys can be integers, floating-point numbers, strings, a ```swift title="built_in_hash.swift" let num = 3 let hashNum = num.hashValue - // Hash value of integer 3 is 9047044699613009734 + // The hash of the integer 3 is 9047044699613009734 let bol = true let hashBol = bol.hashValue - // Hash value of boolean true is -4431640247352757451 + // The hash of the Boolean true is -4431640247352757451 let dec = 3.14159 let hashDec = dec.hashValue - // Hash value of decimal 3.14159 is -2465384235396674631 + // The hash of the floating-point number 3.14159 is -2465384235396674631 let str = "Hello 算法" let hashStr = str.hashValue - // Hash value of string "Hello 算法" is -7850626797806988787 + // The hash of the string “Hello 算法” is -7850626797806988787 let arr = [AnyHashable(12836), AnyHashable("小哈")] let hashTup = arr.hashValue - // Hash value of array [AnyHashable(12836), AnyHashable("小哈")] is -2308633508154532996 + // The hash of the array [AnyHashable(12836), AnyHashable("小哈")] is -2308633508154532996 let obj = ListNode(x: 0) let hashObj = obj.hashValue - // Hash value of ListNode object utils.ListNode is -2434780518035996159 + // The hash of the node object utils.ListNode is -2434780518035996159 ``` === "JS" ```javascript title="built_in_hash.js" - // JavaScript does not provide built-in hash code functions + // JavaScript provides no built-in hash-code function ``` === "TS" ```typescript title="built_in_hash.ts" - // TypeScript does not provide built-in hash code functions + // TypeScript provides no built-in hash-code function ``` === "Dart" @@ -271,27 +271,27 @@ We know that hash table keys can be integers, floating-point numbers, strings, a ```dart title="built_in_hash.dart" int num = 3; int hashNum = num.hashCode; - // Hash value of integer 3 is 34803 + // The hash of the integer 3 is 34803 bool bol = true; int hashBol = bol.hashCode; - // Hash value of boolean true is 1231 + // The hash of the Boolean true is 1231 double dec = 3.14159; int hashDec = dec.hashCode; - // Hash value of decimal 3.14159 is 2570631074981783 + // The hash of the floating-point number 3.14159 is 2570631074981783 String str = "Hello 算法"; int hashStr = str.hashCode; - // Hash value of string "Hello 算法" is 468167534 + // The hash of the string “Hello 算法” is 468167534 List arr = [12836, "小哈"]; int hashArr = arr.hashCode; - // Hash value of array [12836, 小哈] is 976512528 + // The hash of the array [12836, 小哈] is 976512528 ListNode obj = new ListNode(0); int hashObj = obj.hashCode; - // Hash value of ListNode object Instance of 'ListNode' is 1033450432 + // The hash of the node object Instance of 'ListNode' is 1033450432 ``` === "Rust" @@ -304,43 +304,43 @@ We know that hash table keys can be integers, floating-point numbers, strings, a let mut num_hasher = DefaultHasher::new(); num.hash(&mut num_hasher); let hash_num = num_hasher.finish(); - // Hash value of integer 3 is 568126464209439262 + // The hash of the integer 3 is 568126464209439262 let bol = true; let mut bol_hasher = DefaultHasher::new(); bol.hash(&mut bol_hasher); let hash_bol = bol_hasher.finish(); - // Hash value of boolean true is 4952851536318644461 + // The hash of the Boolean true is 4952851536318644461 let dec: f32 = 3.14159; let mut dec_hasher = DefaultHasher::new(); dec.to_bits().hash(&mut dec_hasher); let hash_dec = dec_hasher.finish(); - // Hash value of decimal 3.14159 is 2566941990314602357 + // The hash of the floating-point number 3.14159 is 2566941990314602357 let str = "Hello 算法"; let mut str_hasher = DefaultHasher::new(); str.hash(&mut str_hasher); let hash_str = str_hasher.finish(); - // Hash value of string "Hello 算法" is 16092673739211250988 + // The hash of the string “Hello 算法” is 16092673739211250988 let arr = (&12836, &"小哈"); let mut tup_hasher = DefaultHasher::new(); arr.hash(&mut tup_hasher); let hash_tup = tup_hasher.finish(); - // Hash value of tuple (12836, "小哈") is 1885128010422702749 + // The hash of the tuple (12836, "小哈") is 1885128010422702749 let node = ListNode::new(42); let mut hasher = DefaultHasher::new(); node.borrow().val.hash(&mut hasher); let hash = hasher.finish(); - // Hash value of ListNode object RefCell { value: ListNode { val: 42, next: None } } is 15387811073369036852 + // The hash of the node object RefCell { value: ListNode { val: 42, next: None } } is 15387811073369036852 ``` === "C" ```c title="built_in_hash.c" - // C does not provide built-in hash code functions + // C provides no built-in hash-code function ``` === "Kotlin" @@ -348,27 +348,27 @@ We know that hash table keys can be integers, floating-point numbers, strings, a ```kotlin title="built_in_hash.kt" val num = 3 val hashNum = num.hashCode() - // Hash value of integer 3 is 3 + // The hash of the integer 3 is 3 val bol = true val hashBol = bol.hashCode() - // Hash value of boolean true is 1231 + // The hash of the Boolean true is 1231 val dec = 3.14159 val hashDec = dec.hashCode() - // Hash value of decimal 3.14159 is -1340954729 + // The hash of the floating-point number 3.14159 is -1340954729 val str = "Hello 算法" val hashStr = str.hashCode() - // Hash value of string "Hello 算法" is -727081396 + // The hash of the string “Hello 算法” is -727081396 val arr = arrayOf(12836, "小哈") val hashTup = arr.hashCode() - // Hash value of array [12836, 小哈] is 189568618 + // The hash of the array [12836, 小哈] is 189568618 val obj = ListNode(0) val hashObj = obj.hashCode() - // Hash value of ListNode object utils.ListNode@1d81eb93 is 495053715 + // The hash of the node object utils.ListNode@1d81eb93 is 495053715 ``` === "Ruby" @@ -376,35 +376,35 @@ We know that hash table keys can be integers, floating-point numbers, strings, a ```ruby title="built_in_hash.rb" num = 3 hash_num = num.hash - # Hash value of integer 3 is -4385856518450339636 + # The hash of the integer 3 is -4385856518450339636 bol = true hash_bol = bol.hash - # Hash value of boolean true is -1617938112149317027 + # The hash of the Boolean true is -1617938112149317027 dec = 3.14159 hash_dec = dec.hash - # Hash value of decimal 3.14159 is -1479186995943067893 + # The hash of the floating-point number 3.14159 is -1479186995943067893 str = "Hello 算法" hash_str = str.hash - # Hash value of string "Hello 算法" is -4075943250025831763 + # The hash of the string “Hello 算法” is -4075943250025831763 tup = [12836, '小哈'] hash_tup = tup.hash - # Hash value of tuple (12836, '小哈') is 1999544809202288822 + # The hash of the tuple (12836, '小哈') is 1999544809202288822 obj = ListNode.new(0) hash_obj = obj.hash - # Hash value of ListNode object # is 4302940560806366381 + # The hash of the node object # is 4302940560806366381 ``` -??? pythontutor "Visualized Execution" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%90%8E%E7%BB%A7%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20num%20%3D%203%0A%20%20%20%20hash_num%20%3D%20hash%28num%29%0A%20%20%20%20%23%20%E6%95%B4%E6%95%B0%203%20%E7%9A%84%E5%93%88%E5%B8%8C%E5%80%BC%E4%B8%BA%203%0A%0A%20%20%20%20bol%20%3D%20True%0A%20%20%20%20hash_bol%20%3D%20hash%28bol%29%0A%20%20%20%20%23%20%E5%B8%83%E5%B0%94%E9%87%8F%20True%20%E7%9A%84%E5%93%88%E5%B8%8C%E5%80%BC%E4%B8%BA%201%0A%0A%20%20%20%20dec%20%3D%203.14159%0A%20%20%20%20hash_dec%20%3D%20hash%28dec%29%0A%20%20%20%20%23%20%E5%B0%8F%E6%95%B0%203.14159%20%E7%9A%84%E5%93%88%E5%B8%8C%E5%80%BC%E4%B8%BA%20326484311674566659%0A%0A%20%20%20%20str%20%3D%20%22Hello%20%E7%AE%97%E6%B3%95%22%0A%20%20%20%20hash_str%20%3D%20hash%28str%29%0A%20%20%20%20%23%20%E5%AD%97%E7%AC%A6%E4%B8%B2%E2%80%9CHello%20%E7%AE%97%E6%B3%95%E2%80%9D%E7%9A%84%E5%93%88%E5%B8%8C%E5%80%BC%E4%B8%BA%204617003410720528961%0A%0A%20%20%20%20tup%20%3D%20%2812836,%20%22%E5%B0%8F%E5%93%88%22%29%0A%20%20%20%20hash_tup%20%3D%20hash%28tup%29%0A%20%20%20%20%23%20%E5%85%83%E7%BB%84%20%2812836,%20'%E5%B0%8F%E5%93%88'%29%20%E7%9A%84%E5%93%88%E5%B8%8C%E5%80%BC%E4%B8%BA%201029005403108185979%0A%0A%20%20%20%20obj%20%3D%20ListNode%280%29%0A%20%20%20%20hash_obj%20%3D%20hash%28obj%29%0A%20%20%20%20%23%20%E8%8A%82%E7%82%B9%E5%AF%B9%E8%B1%A1%20%3CListNode%20object%20at%200x1058fd810%3E%20%E7%9A%84%E5%93%88%E5%B8%8C%E5%80%BC%E4%B8%BA%20274267521&cumulative=false&curInstr=19&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22Linked-list%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20Reference%20to%20the%20next%20node%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20num%20%3D%203%0A%20%20%20%20hash_num%20%3D%20hash%28num%29%0A%20%20%20%20%23%20The%20hash%20of%20the%20integer%203%20is%203%0A%0A%20%20%20%20bol%20%3D%20True%0A%20%20%20%20hash_bol%20%3D%20hash%28bol%29%0A%20%20%20%20%23%20The%20hash%20of%20the%20Boolean%20True%20is%201%0A%0A%20%20%20%20dec%20%3D%203.14159%0A%20%20%20%20hash_dec%20%3D%20hash%28dec%29%0A%20%20%20%20%23%20The%20hash%20of%20the%20floating-point%20number%203.14159%20is%20326484311674566659%0A%0A%20%20%20%20str%20%3D%20%22Hello%20%E7%AE%97%E6%B3%95%22%0A%20%20%20%20hash_str%20%3D%20hash%28str%29%0A%20%20%20%20%23%20The%20hash%20of%20the%20string%20%E2%80%9CHello%20%E7%AE%97%E6%B3%95%E2%80%9D%20is%204617003410720528961%0A%0A%20%20%20%20tup%20%3D%20%2812836%2C%20%22%E5%B0%8F%E5%93%88%22%29%0A%20%20%20%20hash_tup%20%3D%20hash%28tup%29%0A%20%20%20%20%23%20The%20hash%20of%20the%20tuple%20%2812836%2C%20%27%E5%B0%8F%E5%93%88%27%29%20is%201029005403108185979%0A%0A%20%20%20%20obj%20%3D%20ListNode%280%29%0A%20%20%20%20hash_obj%20%3D%20hash%28obj%29%0A%20%20%20%20%23%20The%20hash%20of%20the%20node%20object%20%3CListNode%20object%20at%200x1058fd810%3E%20is%20274267521&cumulative=false&curInstr=19&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -In many programming languages, **only immutable objects can serve as the `key` in a hash table**. If we use a list (dynamic array) as a `key`, when the contents of the list change, its hash value also changes, and we would no longer be able to find the original `value` in the hash table. +In many programming languages, **only immutable objects can serve as hash-table keys**. If a list, a dynamic array, were used as a key, changing its contents would change its hash, making the original value impossible to retrieve from the table. -Although the member variables of a custom object (such as a linked list node) are mutable, it is hashable. **This is because the hash value of an object is usually generated based on its memory address**, and even if the contents of the object change, the memory address remains the same, so the hash value remains unchanged. +Custom objects, such as linked-list nodes, can be hashable even though their member variables are mutable. **Their hash is usually based on their memory address**, which stays the same even when their contents change. -You might have noticed that the hash values output in different consoles are different. **This is because the Python interpreter adds a random salt to the string hash function each time it starts up**. This approach effectively prevents HashDoS attacks and enhances the security of the hash algorithm. +You may notice different hash values when running the program in different consoles. **Each time the Python interpreter starts, it adds a random salt to its string hash function**. This helps prevent HashDoS attacks and improves security. diff --git a/en/docs/chapter_hashing/hash_collision.md b/en/docs/chapter_hashing/hash_collision.md index e2661fb7a..76822df2c 100644 --- a/en/docs/chapter_hashing/hash_collision.md +++ b/en/docs/chapter_hashing/hash_collision.md @@ -1,108 +1,108 @@ -# Hash Collision +# Hash collisions -The previous section mentioned that, **in most cases, the input space of a hash function is much larger than the output space**, so theoretically, hash collisions are inevitable. For example, if the input space is all integers and the output space is the array capacity size, then multiple integers will inevitably be mapped to the same bucket index. +As discussed in the previous section, **a hash function's input space is usually much larger than its output space**, so collisions are theoretically unavoidable. For example, if the input space is all integers and the output space is limited to the array's capacity, multiple integers must map to the same bucket index. -Hash collisions can lead to incorrect query results, severely impacting the usability of the hash table. To address this issue, whenever a hash collision occurs, we can perform hash table expansion until the collision disappears. This approach is simple, straightforward, and effective, but it is very inefficient because hash table expansion involves a large amount of data migration and hash value recalculation. To improve efficiency, we can adopt the following strategies: +Hash collisions can produce incorrect lookup results and make a hash table unusable. One solution is to expand the table whenever a collision occurs until the collision disappears. This simple, brute-force approach works, but is inefficient because expansion requires substantial data movement and hash computation. We can improve efficiency with the following strategies. -1. Improve the hash table data structure so that **the hash table can function normally when hash collisions occur**. -2. Only expand when necessary, that is, only when hash collisions are severe. +1. Improve the data structure **so that it works correctly even when collisions occur**. +2. Expand only when necessary, when collisions become sufficiently frequent. -The main approaches to improving a hash table's structure are separate chaining and open addressing. +The main structural approaches are “separate chaining” and “open addressing.” -## Separate Chaining +## Separate chaining -In the original hash table, each bucket can store only one key-value pair. Separate chaining replaces the single element in each bucket with a linked list, treating each key-value pair as a node and storing all colliding key-value pairs in the same list. The figure below shows an example of a separate chaining hash table. +In the original hash table, each bucket holds only one key-value pair. Separate chaining replaces this single element with a linked list. Each pair becomes a node, and all colliding pairs are stored in the same list. An example is shown below. -![Separate chaining hash table](hash_collision.assets/hash_table_chaining.png) +![A hash table using separate chaining](hash_collision.assets/hash_table_chaining.png) -In a hash table implemented with separate chaining, the basic operations work as follows: +The operations change as follows. -- **Querying elements**: Input `key`, compute the bucket index using the hash function, access the head of the corresponding linked list, and traverse the list while comparing keys until the target key-value pair is found. -- **Adding elements**: First use the hash function to locate the corresponding linked list, then insert the node (key-value pair) into the list. -- **Deleting elements**: Use the hash function to locate the corresponding linked list, then traverse it to find and delete the target node. +- **Lookup**: Hash the input `key` to obtain a bucket index, access the list's head, and traverse the list comparing keys to find the target pair. +- **Insertion**: Use the hash function to access the list's head, then add the node containing the key-value pair to the list. +- **Deletion**: Use the hash function to locate the list, traverse it to find the target node, and remove that node. -Separate chaining has the following limitations: +Separate chaining has the following limitations. -- **Increased Space Usage**: The linked list contains node pointers, which consume more memory space than arrays. -- **Reduced Query Efficiency**: This is because linear traversal of the linked list is required to find the corresponding element. +- **More space**: Linked lists store node pointers and consume more memory than arrays. +- **Slower lookup**: Finding an element requires a linear scan of the list. -The code below provides a simple implementation of a separate chaining hash table, with two things to note: +The code below gives a simple implementation. Keep two points in mind. -- Lists (dynamic arrays) are used instead of linked lists to simplify the code. In this setup, the hash table (array) contains multiple buckets, each of which is a list. -- This implementation includes a hash table expansion method. When the load factor exceeds $\frac{2}{3}$, we expand the hash table to $2$ times its original size. +- Lists, implemented as dynamic arrays, replace linked lists to simplify the code. The hash table is an array of buckets, each of which is a list. +- The implementation includes expansion: when the load factor exceeds $\frac{2}{3}$, the table's capacity is doubled to $2$ times its original size. ```src [file]{hash_map_chaining}-[class]{hash_map_chaining}-[func]{} ``` -It's worth noting that when the linked list becomes very long, the query time $O(n)$ is poor. **In this case, the linked list can be converted into an AVL tree or a red-black tree**, reducing the time complexity of lookups to $O(\log n)$. +When a list becomes long, its $O(n)$ lookup time is poor. **Replacing it with an AVL tree or red-black tree** improves lookup time to $O(\log n)$. -## Open Addressing +## Open addressing -Open addressing does not introduce additional data structures. Instead, it handles hash collisions through repeated probing. Common probing strategies include linear probing, quadratic probing, and multiple hashing. +Open addressing handles collisions by probing multiple locations without introducing another data structure. Common strategies include linear probing, quadratic probing, and multiple hashing. -Let's use linear probing as an example to introduce the mechanism of open addressing hash tables. +We use linear probing to explain how an open-addressing hash table works. -### Linear Probing +### Linear probing -Linear probing uses a fixed step size to probe sequentially, so its operations differ somewhat from those of an ordinary hash table. +Linear probing searches with a fixed step size. Its operations differ from those of a basic hash table. -- **Inserting elements**: Compute the bucket index using the hash function. If the bucket is already occupied, continue probing forward from the collision position with a fixed step size (usually $1$) until an empty bucket is found, then insert the element there. -- **Searching for elements**: If a collision occurs, continue probing forward with the same step size until the corresponding element is found and return its `value`; if an empty bucket is encountered, the target element is not in the hash table, so return `None`. +- **Insertion**: Calculate the bucket index using the hash function. If the bucket is occupied, scan forward from that position with a fixed step size, usually $1$, until an empty bucket is found, then insert the element. +- **Lookup**: If a collision is encountered, scan forward with the same step size until the target is found, then return its `value`. An empty bucket means the target is absent, so return `None`. -The figure below shows the distribution of key-value pairs in an open-addressing hash table that uses linear probing. Under this hash function, keys with the same last two digits are mapped to the same bucket. Linear probing then places them in that bucket and the subsequent buckets. +The figure below shows key-value pairs in an open-addressing table using linear probing. With this hash function, keys sharing the same last two digits map to the same bucket. Linear probing stores them successively in that bucket and the following buckets. -![Distribution of key-value pairs in open addressing (linear probing) hash table](hash_collision.assets/hash_table_linear_probing.png) +![Distribution of key-value pairs with linear probing](hash_collision.assets/hash_table_linear_probing.png) -However, **linear probing is prone to clustering**. Specifically, the longer a contiguous occupied region in the array becomes, the more likely new collisions are to occur within that region. This in turn makes the cluster grow even further, creating a vicious cycle that gradually degrades the efficiency of insertion, deletion, lookup, and update operations. +However, **linear probing is prone to clustering**. The longer a run of occupied positions becomes, the more likely a collision is to occur within it, causing the cluster to grow further. This feedback loop eventually degrades insertion, deletion, lookup, and update performance. -It's important to note that **we cannot directly delete elements from an open-addressing hash table**. Deleting an element creates an empty bucket `None` in the array. During lookup, once linear probing reaches that empty bucket, it stops, which means any elements stored farther along the probe sequence become unreachable. As a result, the program may incorrectly conclude that those elements do not exist, as shown in the figure below. +**We cannot simply delete an element from an open-addressing table**. Deletion would leave an empty bucket, `None`. A lookup stops when linear probing reaches such a bucket, making later elements in the probe sequence inaccessible and potentially reporting them as absent, as shown below. -![Query issues caused by deletion in open addressing](hash_collision.assets/hash_table_open_addressing_deletion.png) +![Lookup problems caused by deletion in open addressing](hash_collision.assets/hash_table_open_addressing_deletion.png) -To solve this problem, we can adopt lazy deletion: instead of directly removing an element from the hash table, **use a constant `TOMBSTONE` to mark the bucket**. Under this mechanism, both `None` and `TOMBSTONE` denote buckets that can accept key-value pairs. The difference is that when linear probing encounters `TOMBSTONE`, it must continue probing, because key-value pairs may still exist farther along the sequence. +To solve this, use lazy deletion: rather than simply removing the entry, **mark its bucket with the constant `TOMBSTONE`**. Both `None` and `TOMBSTONE` represent available buckets that can hold a new pair. Unlike `None`, however, a `TOMBSTONE` does not stop a search, because further pairs may lie beyond it. -However, **lazy deletion may accelerate hash-table performance degradation**. Each deletion leaves behind a marker, and as the number of `TOMBSTONE` entries grows, search time increases as well, because linear probing may need to skip over multiple tombstones before finding the target element. +However, **lazy deletion can accelerate performance degradation**. Each deletion leaves a marker. As `TOMBSTONE` entries accumulate, searches may need to skip many of them before reaching the target, increasing search time. -To address this, we can record the index of the first `TOMBSTONE` encountered during linear probing and swap the found target element into that position. The benefit is that each query or insertion can move elements closer to their ideal positions, that is, closer to where probing begins, which improves lookup efficiency. +To address this, record the first `TOMBSTONE` encountered during probing and swap the target entry with it when found. On each lookup or insertion, this moves the entry closer to its ideal location—the start of the probe sequence—and improves lookup efficiency. -The code below implements an open addressing (linear probing) hash table with lazy deletion. To make better use of the hash table space, we treat the hash table as a "circular array". When going beyond the end of the array, we return to the beginning and continue traversing. +The code below implements linear probing with lazy deletion. To use the table's space more fully, treat the array as circular: after reaching the end, wrap around to the beginning. ```src [file]{hash_map_open_addressing}-[class]{hash_map_open_addressing}-[func]{} ``` -### Quadratic Probing +### Quadratic probing -Quadratic probing is similar to linear probing and is one of the common strategies for open addressing. When a collision occurs, quadratic probing does not simply skip a fixed number of steps but skips a number of steps equal to the "square of the number of probes", i.e., $1, 4, 9, \dots$ steps. +Like linear probing, quadratic probing is a common open-addressing strategy. On a collision, it probes at distances determined by the square of the probe number—$1, 4, 9, \dots$—rather than a fixed step size. -Quadratic probing has the following advantages: +Its main advantages are as follows. -- Quadratic probing attempts to alleviate the clustering effect of linear probing by skipping distances equal to the square of the probe count. -- Quadratic probing skips larger distances to find empty positions, which helps to distribute data more evenly. +- Squared probe distances aim to reduce the clustering caused by linear probing. +- Larger jumps help distribute entries more evenly while searching for empty positions. -However, quadratic probing is not perfect: +However, quadratic probing is not perfect. -- Clustering still exists, i.e., some positions are more likely to be occupied than others. -- Due to the growth of squares, quadratic probing may not probe the entire hash table, meaning that even if there are empty buckets in the hash table, quadratic probing may not be able to access them. +- Clustering still occurs: some positions are more likely to be occupied than others. +- The growing squared distances may prevent probing the entire table, so an empty bucket may remain unreachable. -### Multiple Hashing +### Multiple hashing -As the name suggests, multiple hashing uses multiple hash functions $f_1(x)$, $f_2(x)$, $f_3(x)$, $\dots$ for probing. +As its name suggests, multiple hashing probes using several hash functions, $f_1(x)$, $f_2(x)$, $f_3(x)$, $\dots$. -- **Inserting elements**: If hash function $f_1(x)$ encounters a conflict, try $f_2(x)$, and so on, until an empty position is found and the element is inserted. -- **Searching for elements**: Search in the same order of hash functions until the target element is found and return it; if an empty position is encountered or all hash functions have been tried, it indicates the element is not in the hash table, then return `None`. +- **Insertion**: If $f_1(x)$ produces a collision, try $f_2(x)$, and so on, inserting the element when an empty position is found. +- **Lookup**: Try the hash functions in the same order and return when the target is found. If an empty position is reached or all functions have been tried, the target is absent; return `None`. -Compared with linear probing, multiple hashing is less prone to clustering, but using multiple hash functions introduces additional computational overhead. +Multiple hashing is less prone to clustering than linear probing, but computing several hash functions adds overhead. !!! tip - Please note that hash tables based on open addressing, including linear probing, quadratic probing, and multiple hashing, all have the problem that elements cannot be deleted directly. + All open-addressing strategies—linear probing, quadratic probing, and multiple hashing—share the restriction that elements cannot simply be deleted. -## Choice of Programming Languages +## Choices made by programming languages -Different programming languages adopt different hash table implementation strategies. Here are a few examples: +Programming languages use different hash-table implementations. Here are a few examples. -- Python uses open addressing. The `dict` dictionary uses pseudo-random numbers for probing. -- Java uses separate chaining. Since JDK 1.8, when the array length in `HashMap` reaches 64 and the length of a linked list reaches 8, the linked list is converted to a red-black tree to improve search performance. -- Starting with Go 1.24, the built-in `map` uses an open-addressed implementation based on Swiss Tables by default. Go 1.23 and earlier use separate chaining, with up to 8 key-value pairs per bucket and overflow buckets linked when that capacity is exceeded. +- Python uses open addressing. Its `dict` probes using pseudorandom values. +- Java uses separate chaining. Since JDK 1.8, when a `HashMap`'s underlying array has length at least 64 and a chain reaches length 8, the chain is converted to a red-black tree to improve lookup performance. +- Since Go 1.24, the built-in `map` uses open addressing based on Swiss Tables by default. Go 1.23 and earlier use separate chaining, with up to 8 key-value pairs per bucket and linked overflow buckets when that capacity is exceeded. diff --git a/en/docs/chapter_hashing/hash_map.md b/en/docs/chapter_hashing/hash_map.md index 3f2a0a696..c5a81b6b8 100755 --- a/en/docs/chapter_hashing/hash_map.md +++ b/en/docs/chapter_hashing/hash_map.md @@ -1,238 +1,238 @@ -# Hash Table +# Hash tables -A hash table, also known as a hash map, stores mappings from keys `key` to values `value`, enabling efficient lookups. Specifically, given a key `key`, we can retrieve the corresponding value `value` from a hash table in $O(1)$ time. +A hash table, also called a hash map, supports efficient lookup by mapping a `key` to a `value`. Given a key, it can retrieve the corresponding value in $O(1)$ time. -As shown below, suppose we have $n$ students, each with two pieces of information: a name and a student ID. If we want to support the query "given a student ID, return the corresponding name," we can use the hash table shown below. +Suppose we have $n$ students, each with a name and a student ID. To support the query “given a student ID, return the corresponding name,” use the hash table shown below. -![Abstract representation of a hash table](hash_map.assets/hash_table_lookup.png) +![An abstract view of hash-table lookup](hash_map.assets/hash_table_lookup.png) -In addition to hash tables, arrays and linked lists can also implement query functionality. Their efficiency comparison is shown in the following table. +Arrays and linked lists also support lookup. The table below compares their efficiency. -- **Adding elements**: Simply add elements to the end of the array (linked list), using $O(1)$ time. -- **Querying elements**: Since the array (linked list) is unordered, all elements need to be traversed, using $O(n)$ time. -- **Deleting elements**: The element must first be located, then deleted from the array (linked list), using $O(n)$ time. +- **Insertion**: append an element to the end of the array or linked list in $O(1)$ time. +- **Lookup**: because the array or list is unsorted, searching may require visiting every element, taking $O(n)$ time. +- **Deletion**: first find the element, then remove it from the array or list, taking $O(n)$ time. -

Table   Comparison of element query efficiency

+

Table   Comparison of element-lookup efficiency

-| | Array | Linked List | Hash Table | -| --------------- | ------ | ----------- | ---------- | -| Find element | $O(n)$ | $O(n)$ | $O(1)$ | -| Add element | $O(1)$ | $O(1)$ | $O(1)$ | -| Delete element | $O(n)$ | $O(n)$ | $O(1)$ | +| | Array | Linked list | Hash table | +| -------- | ------ | ------ | ------ | +| Find an element | $O(n)$ | $O(n)$ | $O(1)$ | +| Insert an element | $O(1)$ | $O(1)$ | $O(1)$ | +| Delete an element | $O(n)$ | $O(n)$ | $O(1)$ | -As we can see, **insertion, deletion, lookup, and update operations in a hash table all have time complexity $O(1)$**, making hash tables highly efficient. +**Hash-table insertion, deletion, lookup, and updating all take $O(1)$ time**, making them highly efficient. -## Common Hash Table Operations +## Common hash-table operations -Common operations on hash tables include: initialization, query operations, adding key-value pairs, and deleting key-value pairs. Example code is as follows: +Common operations include initialization, lookup, adding key-value pairs, and deleting pairs. Examples follow: === "Python" ```python title="hash_map.py" - # Initialize hash table + # Initialize a hash table hmap: dict = {} - # Add operation - # Add key-value pair (key, value) to hash table - hmap[12836] = "XiaoHa" - hmap[15937] = "XiaoLuo" - hmap[16750] = "XiaoSuan" - hmap[13276] = "XiaoFa" - hmap[10583] = "XiaoYa" + # Insertion + # Add key-value pairs (key, value) to the hash table + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" - # Query operation - # Input key into hash table to get value + # Lookup + # Look up key in the hash table to obtain value name: str = hmap[15937] - # Delete operation - # Delete key-value pair (key, value) from hash table + # Deletion + # Remove a key-value pair (key, value) from the hash table hmap.pop(10583) ``` === "C++" ```cpp title="hash_map.cpp" - /* Initialize hash table */ + /* Initialize a hash table */ unordered_map map; - /* Add operation */ - // Add key-value pair (key, value) to hash table - map[12836] = "XiaoHa"; - map[15937] = "XiaoLuo"; - map[16750] = "XiaoSuan"; - map[13276] = "XiaoFa"; - map[10583] = "XiaoYa"; + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map[12836] = "小哈"; + map[15937] = "小啰"; + map[16750] = "小算"; + map[13276] = "小法"; + map[10583] = "小鸭"; - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value string name = map[15937]; - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.erase(10583); ``` === "Java" ```java title="hash_map.java" - /* Initialize hash table */ + /* Initialize a hash table */ Map map = new HashMap<>(); - /* Add operation */ - // Add key-value pair (key, value) to hash table - map.put(12836, "XiaoHa"); - map.put(15937, "XiaoLuo"); - map.put(16750, "XiaoSuan"); - map.put(13276, "XiaoFa"); - map.put(10583, "XiaoYa"); + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value String name = map.get(15937); - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.remove(10583); ``` === "C#" ```csharp title="hash_map.cs" - /* Initialize hash table */ + /* Initialize a hash table */ Dictionary map = new() { - /* Add operation */ - // Add key-value pair (key, value) to hash table - { 12836, "XiaoHa" }, - { 15937, "XiaoLuo" }, - { 16750, "XiaoSuan" }, - { 13276, "XiaoFa" }, - { 10583, "XiaoYa" } + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + { 12836, "小哈" }, + { 15937, "小啰" }, + { 16750, "小算" }, + { 13276, "小法" }, + { 10583, "小鸭" } }; - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value string name = map[15937]; - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.Remove(10583); ``` === "Go" ```go title="hash_map_test.go" - /* Initialize hash table */ + /* Initialize a hash table */ hmap := make(map[int]string) - /* Add operation */ - // Add key-value pair (key, value) to hash table - hmap[12836] = "XiaoHa" - hmap[15937] = "XiaoLuo" - hmap[16750] = "XiaoSuan" - hmap[13276] = "XiaoFa" - hmap[10583] = "XiaoYa" + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value name := hmap[15937] - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table delete(hmap, 10583) ``` === "Swift" ```swift title="hash_map.swift" - /* Initialize hash table */ + /* Initialize a hash table */ var map: [Int: String] = [:] - /* Add operation */ - // Add key-value pair (key, value) to hash table - map[12836] = "XiaoHa" - map[15937] = "XiaoLuo" - map[16750] = "XiaoSuan" - map[13276] = "XiaoFa" - map[10583] = "XiaoYa" + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map[12836] = "小哈" + map[15937] = "小啰" + map[16750] = "小算" + map[13276] = "小法" + map[10583] = "小鸭" - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value let name = map[15937]! - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.removeValue(forKey: 10583) ``` === "JS" ```javascript title="hash_map.js" - /* Initialize hash table */ + /* Initialize a hash table */ const map = new Map(); - /* Add operation */ - // Add key-value pair (key, value) to hash table - map.set(12836, 'XiaoHa'); - map.set(15937, 'XiaoLuo'); - map.set(16750, 'XiaoSuan'); - map.set(13276, 'XiaoFa'); - map.set(10583, 'XiaoYa'); + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map.set(12836, '小哈'); + map.set(15937, '小啰'); + map.set(16750, '小算'); + map.set(13276, '小法'); + map.set(10583, '小鸭'); - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value let name = map.get(15937); - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.delete(10583); ``` === "TS" ```typescript title="hash_map.ts" - /* Initialize hash table */ + /* Initialize a hash table */ const map = new Map(); - /* Add operation */ - // Add key-value pair (key, value) to hash table - map.set(12836, 'XiaoHa'); - map.set(15937, 'XiaoLuo'); - map.set(16750, 'XiaoSuan'); - map.set(13276, 'XiaoFa'); - map.set(10583, 'XiaoYa'); - console.info('\nAfter adding, hash table is\nKey -> Value'); + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map.set(12836, '小哈'); + map.set(15937, '小啰'); + map.set(16750, '小算'); + map.set(13276, '小法'); + map.set(10583, '小鸭'); + console.info('\nAfter insertion, the hash table is\nKey -> Value'); console.info(map); - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value let name = map.get(15937); - console.info('\nInput student ID 15937, queried name ' + name); + console.info('\nLooking up student ID 15937 gives the name ' + name); - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.delete(10583); - console.info('\nAfter deleting 10583, hash table is\nKey -> Value'); + console.info('\nAfter deleting 10583, the hash table is\nKey -> Value'); console.info(map); ``` === "Dart" ```dart title="hash_map.dart" - /* Initialize hash table */ + /* Initialize a hash table */ Map map = {}; - /* Add operation */ - // Add key-value pair (key, value) to hash table - map[12836] = "XiaoHa"; - map[15937] = "XiaoLuo"; - map[16750] = "XiaoSuan"; - map[13276] = "XiaoFa"; - map[10583] = "XiaoYa"; + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map[12836] = "小哈"; + map[15937] = "小啰"; + map[16750] = "小算"; + map[13276] = "小法"; + map[10583] = "小鸭"; - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value String name = map[15937]; - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.remove(10583); ``` @@ -241,95 +241,95 @@ Common operations on hash tables include: initialization, query operations, addi ```rust title="hash_map.rs" use std::collections::HashMap; - /* Initialize hash table */ + /* Initialize a hash table */ let mut map: HashMap = HashMap::new(); - /* Add operation */ - // Add key-value pair (key, value) to hash table - map.insert(12836, "XiaoHa".to_string()); - map.insert(15937, "XiaoLuo".to_string()); - map.insert(16750, "XiaoSuan".to_string()); - map.insert(13276, "XiaoFa".to_string()); - map.insert(10583, "XiaoYa".to_string()); + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map.insert(12836, "小哈".to_string()); + map.insert(15937, "小啰".to_string()); + map.insert(16750, "小算".to_string()); + map.insert(13279, "小法".to_string()); + map.insert(10583, "小鸭".to_string()); - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value let _name: Option<&String> = map.get(&15937); - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table let _removed_value: Option = map.remove(&10583); ``` === "C" ```c title="hash_map.c" - // C does not provide a built-in hash table + // C provides no built-in hash table ``` === "Kotlin" ```kotlin title="hash_map.kt" - /* Initialize hash table */ + /* Initialize a hash table */ val map = HashMap() - /* Add operation */ - // Add key-value pair (key, value) to hash table - map[12836] = "XiaoHa" - map[15937] = "XiaoLuo" - map[16750] = "XiaoSuan" - map[13276] = "XiaoFa" - map[10583] = "XiaoYa" + /* Insertion */ + // Add key-value pairs (key, value) to the hash table + map[12836] = "小哈" + map[15937] = "小啰" + map[16750] = "小算" + map[13276] = "小法" + map[10583] = "小鸭" - /* Query operation */ - // Input key into hash table to get value + /* Lookup */ + // Look up key in the hash table to obtain value val name = map[15937] - /* Delete operation */ - // Delete key-value pair (key, value) from hash table + /* Deletion */ + // Remove a key-value pair (key, value) from the hash table map.remove(10583) ``` === "Ruby" ```ruby title="hash_map.rb" - # Initialize hash table + # Initialize a hash table hmap = {} - # Add operation - # Add key-value pair (key, value) to hash table - hmap[12836] = "XiaoHa" - hmap[15937] = "XiaoLuo" - hmap[16750] = "XiaoSuan" - hmap[13276] = "XiaoFa" - hmap[10583] = "XiaoYa" + # Insertion + # Add key-value pairs (key, value) to the hash table + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" - # Query operation - # Input key into hash table to get value + # Lookup + # Look up key in the hash table to obtain value name = hmap[15937] - # Delete operation - # Delete key-value pair (key, value) from hash table + # Deletion + # Remove a key-value pair (key, value) from the hash table hmap.delete(10583) ``` -??? pythontutor "Visualized Execution" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B7%BB%E5%8A%A0%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E6%B7%BB%E5%8A%A0%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%9F%A5%E8%AF%A2%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%90%91%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E8%BE%93%E5%85%A5%E9%94%AE%20key%20%EF%BC%8C%E5%BE%97%E5%88%B0%E5%80%BC%20value%0A%20%20%20%20name%20%3D%20hmap%5B15937%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E5%88%A0%E9%99%A4%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap.pop%2810583%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20hash%20table%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20Insertion%0A%20%20%20%20%23%20Add%20key-value%20pairs%20%28key%2C%20value%29%20to%20the%20hash%20table%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20Lookup%0A%20%20%20%20%23%20Look%20up%20key%20in%20the%20hash%20table%20to%20obtain%20value%0A%20%20%20%20name%20%3D%20hmap%5B15937%5D%0A%20%20%20%20%0A%20%20%20%20%23%20Deletion%0A%20%20%20%20%23%20Remove%20a%20key-value%20pair%20%28key%2C%20value%29%20from%20the%20hash%20table%0A%20%20%20%20hmap.pop%2810583%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -There are three common ways to traverse a hash table: traversing key-value pairs, traversing keys, and traversing values. Example code is as follows: +There are three common ways to traverse a hash table: visit key-value pairs, keys only, or values only. Examples follow: === "Python" ```python title="hash_map.py" - # Traverse hash table - # Traverse key-value pairs key->value + # Traverse the hash table + # Traverse key-value pairs: key->value for key, value in hmap.items(): print(key, "->", value) - # Traverse keys only + # Traverse keys only: key for key in hmap.keys(): print(key) - # Traverse values only + # Traverse values only: value for value in hmap.values(): print(value) ``` @@ -337,12 +337,12 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "C++" ```cpp title="hash_map.cpp" - /* Traverse hash table */ - // Traverse key-value pairs key->value + /* Traverse the hash table */ + // Traverse key-value pairs: key->value for (auto kv: map) { cout << kv.first << " -> " << kv.second << endl; } - // Traverse using iterator key->value + // Use an iterator to traverse key->value pairs for (auto iter = map.begin(); iter != map.end(); iter++) { cout << iter->first << "->" << iter->second << endl; } @@ -351,16 +351,16 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "Java" ```java title="hash_map.java" - /* Traverse hash table */ - // Traverse key-value pairs key->value - for (Map.Entry kv: map.entrySet()) { + /* Traverse the hash table */ + // Traverse key-value pairs: key->value + for (Map.Entry kv: map.entrySet()) { System.out.println(kv.getKey() + " -> " + kv.getValue()); } - // Traverse keys only + // Traverse keys only: key for (int key: map.keySet()) { System.out.println(key); } - // Traverse values only + // Traverse values only: value for (String val: map.values()) { System.out.println(val); } @@ -369,16 +369,16 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "C#" ```csharp title="hash_map.cs" - /* Traverse hash table */ - // Traverse key-value pairs Key->Value + /* Traverse the hash table */ + // Traverse key-value pairs: Key->Value foreach (var kv in map) { Console.WriteLine(kv.Key + " -> " + kv.Value); } - // Traverse keys only + // Traverse keys only: key foreach (int key in map.Keys) { Console.WriteLine(key); } - // Traverse values only + // Traverse values only: value foreach (string val in map.Values) { Console.WriteLine(val); } @@ -387,16 +387,16 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "Go" ```go title="hash_map_test.go" - /* Traverse hash table */ - // Traverse key-value pairs key->value + /* Traverse the hash table */ + // Traverse key-value pairs: key->value for key, value := range hmap { fmt.Println(key, "->", value) } - // Traverse keys only + // Traverse keys only: key for key := range hmap { fmt.Println(key) } - // Traverse values only + // Traverse values only: value for _, value := range hmap { fmt.Println(value) } @@ -405,16 +405,16 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "Swift" ```swift title="hash_map.swift" - /* Traverse hash table */ - // Traverse key-value pairs Key->Value + /* Traverse the hash table */ + // Traverse key-value pairs: Key->Value for (key, value) in map { print("\(key) -> \(value)") } - // Traverse keys only + // Traverse keys only: Key for key in map.keys { print(key) } - // Traverse values only + // Traverse values only: Value for value in map.values { print(value) } @@ -423,16 +423,16 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "JS" ```javascript title="hash_map.js" - /* Traverse hash table */ - console.info('\nTraverse key-value pairs Key->Value'); + /* Traverse the hash table */ + console.info('\nTraverse key-value pairs: Key->Value'); for (const [k, v] of map.entries()) { console.info(k + ' -> ' + v); } - console.info('\nTraverse keys only Key'); + console.info('\nTraverse keys only: Key'); for (const k of map.keys()) { console.info(k); } - console.info('\nTraverse values only Value'); + console.info('\nTraverse values only: Value'); for (const v of map.values()) { console.info(v); } @@ -441,16 +441,16 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "TS" ```typescript title="hash_map.ts" - /* Traverse hash table */ - console.info('\nTraverse key-value pairs Key->Value'); + /* Traverse the hash table */ + console.info('\nTraverse key-value pairs: Key->Value'); for (const [k, v] of map.entries()) { console.info(k + ' -> ' + v); } - console.info('\nTraverse keys only Key'); + console.info('\nTraverse keys only: Key'); for (const k of map.keys()) { console.info(k); } - console.info('\nTraverse values only Value'); + console.info('\nTraverse values only: Value'); for (const v of map.values()) { console.info(v); } @@ -459,18 +459,18 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "Dart" ```dart title="hash_map.dart" - /* Traverse hash table */ - // Traverse key-value pairs Key->Value + /* Traverse the hash table */ + // Traverse key-value pairs: Key->Value map.forEach((key, value) { print('$key -> $value'); }); - // Traverse keys only + // Traverse keys only: Key map.keys.forEach((key) { print(key); }); - // Traverse values only + // Traverse values only: Value map.values.forEach((value) { print(value); }); @@ -479,18 +479,18 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "Rust" ```rust title="hash_map.rs" - /* Traverse hash table */ - // Traverse key-value pairs Key->Value + /* Traverse the hash table */ + // Traverse key-value pairs: Key->Value for (key, value) in &map { println!("{key} -> {value}"); } - // Traverse keys only + // Traverse keys only: Key for key in map.keys() { println!("{key}"); } - // Traverse values only + // Traverse values only: Value for value in map.values() { println!("{value}"); } @@ -499,22 +499,22 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "C" ```c title="hash_map.c" - // C does not provide a built-in hash table + // C provides no built-in hash table ``` === "Kotlin" ```kotlin title="hash_map.kt" - /* Traverse hash table */ - // Traverse key-value pairs key->value + /* Traverse the hash table */ + // Traverse key-value pairs: key->value for ((key, value) in map) { println("$key -> $value") } - // Traverse keys only + // Traverse keys only: key for (key in map.keys) { println(key) } - // Traverse values only + // Traverse values only: value for (_val in map.values) { println(_val) } @@ -523,69 +523,69 @@ There are three common ways to traverse a hash table: traversing key-value pairs === "Ruby" ```ruby title="hash_map.rb" - # Traverse hash table - # Traverse key-value pairs key->value + # Traverse the hash table + # Traverse key-value pairs: key->value hmap.entries.each { |key, value| puts "#{key} -> #{value}" } - # Traverse keys only + # Traverse keys only: key hmap.keys.each { |key| puts key } - # Traverse values only + # Traverse values only: value hmap.values.each { |val| puts val } ``` -??? pythontutor "Visualized Execution" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B7%BB%E5%8A%A0%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E6%B7%BB%E5%8A%A0%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E9%94%AE%E5%80%BC%E5%AF%B9%20key-%3Evalue%0A%20%20%20%20for%20key,%20value%20in%20hmap.items%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key,%20%22-%3E%22,%20value%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E9%94%AE%20key%0A%20%20%20%20for%20key%20in%20hmap.keys%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E5%80%BC%20value%0A%20%20%20%20for%20value%20in%20hmap.values%28%29%3A%0A%20%20%20%20%20%20%20%20print%28value%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20hash%20table%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20Insertion%0A%20%20%20%20%23%20Add%20key-value%20pairs%20%28key%2C%20value%29%20to%20the%20hash%20table%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20Traverse%20the%20hash%20table%0A%20%20%20%20%23%20Traverse%20key-value%20pairs%3A%20key-%3Evalue%0A%20%20%20%20for%20key%2C%20value%20in%20hmap.items%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%2C%20%22-%3E%22%2C%20value%29%0A%20%20%20%20%23%20Traverse%20keys%20only%3A%20key%0A%20%20%20%20for%20key%20in%20hmap.keys%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%29%0A%20%20%20%20%23%20Traverse%20values%20only%3A%20value%0A%20%20%20%20for%20value%20in%20hmap.values%28%29%3A%0A%20%20%20%20%20%20%20%20print%28value%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## Simple Hash Table Implementation +## A simple implementation -Let's start with the simplest case: **implementing a hash table with just an array**. In a hash table, each empty slot in the array is called a bucket, and each bucket can store one key-value pair. A lookup therefore consists of finding the bucket for `key` and reading the `value` stored there. +Start with the simplest case: **implement a hash table using just one array**. Each slot is called a bucket and holds one key-value pair. A lookup finds the bucket corresponding to `key` and retrieves its `value`. -So how do we find the right bucket for a given `key`? We do this with a hash function. A hash function maps a larger input space to a smaller output space. In a hash table, the input space is the set of all `key`s, and the output space is the set of all buckets (array indices). In other words, given a `key`, **the hash function tells us where the corresponding key-value pair should be stored in the array**. +How do we locate the bucket for a key? A hash function maps a larger input space into a smaller output space. Here, the inputs are all possible keys, and the outputs are bucket indices. **Given a key, the hash function tells us where its key-value pair is stored in the array**. -Given a `key`, computing the bucket index involves the following two steps: +For an input `key`, the calculation has two steps. -1. Use a hash algorithm `hash()` to compute a hash value. -2. Take that hash value modulo the number of buckets (array length), `capacity`, to obtain the bucket (array index) `index` corresponding to the `key`. +1. Use a hash algorithm, `hash()`, to calculate a hash value. +2. Take that value modulo the number of buckets, the array length `capacity`, to obtain the bucket index `index`. ```shell index = hash(key) % capacity ``` -We can then use `index` to access the corresponding bucket in the hash table and retrieve the `value`. +Use `index` to access the bucket and retrieve `value`. -Suppose the array length is `capacity = 100` and the hash algorithm is `hash(key) = key`. Then the hash function is `key % 100`. The figure below illustrates how this hash function works, using student ID as `key` and name as `value`. +If `capacity = 100` and `hash(key) = key`, the resulting hash function is `key % 100`. The figure below illustrates it with student IDs as keys and names as values. -![Working principle of hash function](hash_map.assets/hash_function.png) +![How a hash function works](hash_map.assets/hash_function.png) -The following code implements a simple hash table. Here, we encapsulate `key` and `value` into a class `Pair` to represent a key-value pair. +The code below implements a simple hash table. The `Pair` class groups a `key` and `value` into a key-value pair. ```src [file]{array_hash_map}-[class]{array_hash_map}-[func]{} ``` -## Hash Collision and Resizing +## Collisions and expansion -Fundamentally, a hash function maps the input space consisting of all `key`s to the output space consisting of all array indices, and the input space is often much larger than the output space. Therefore, **in theory, different inputs must sometimes map to the same output**. +A hash function maps the input space of all possible keys into the output space of array indices. The input space is usually far larger, **so multiple inputs must theoretically map to the same output**. -For the hash function in the above example, when the input `key`s have the same last two digits, the hash function produces the same output. For example, when querying two students with IDs 12836 and 20336, we get: +For the function above, keys with the same last two digits produce the same output. Looking up student IDs 12836 and 20336 gives: ```shell 12836 % 100 = 36 20336 % 100 = 36 ``` -As shown below, two student IDs now point to the same name, which is clearly incorrect. We call this situation, where multiple inputs map to the same output, a hash collision. +As shown below, both IDs point to the same name, which is clearly incorrect. This situation, where different inputs produce the same output, is called a hash collision. -![Hash collision example](hash_map.assets/hash_collision.png) +![An example of a hash collision](hash_map.assets/hash_collision.png) -It's easy to see that the larger the hash table capacity $n$, the lower the probability that multiple `key`s will be assigned to the same bucket, and the fewer collisions. Therefore, **we can reduce hash collisions by expanding the hash table**. +A larger table capacity $n$ makes it less likely that several keys share a bucket, reducing collisions. **Expanding the hash table can therefore reduce hash collisions**. -As shown in the figure below, before expansion, the key-value pairs `(136, A)` and `(236, D)` collided, but after expansion, the collision disappears. +In the example below, `(136, A)` and `(236, D)` collide before expansion but no longer collide afterward. -![Hash table resizing](hash_map.assets/hash_table_reshash.png) +![Expanding a hash table](hash_map.assets/hash_table_reshash.png) -Like resizing an array, resizing a hash table requires migrating all key-value pairs from the original table to the new table, which is expensive. In addition, because the hash table capacity `capacity` changes, we must recompute the storage location of every key-value pair using the hash function, which further increases the cost of resizing. For this reason, programming languages typically reserve a sufficiently large hash table capacity to avoid frequent resizing. +As with array expansion, expanding a hash table requires moving every pair into a new table, which is time-consuming. Because `capacity` changes, the hash function must also recalculate every pair's position, adding further computation. Programming languages therefore usually reserve enough capacity to avoid frequent expansion. -The load factor is an important concept in hash tables. It is defined as the number of elements in the hash table divided by the number of buckets and is used to measure the severity of hash collisions. **It is also commonly used as a threshold for triggering hash table resizing**. For example, in Java, when the load factor exceeds $0.75$, the system expands the hash table to twice its original size. +The load factor is the number of elements divided by the number of buckets. It measures how crowded the table is and **often serves as the trigger for expansion**. For example, in Java, a load factor above $0.75$ causes the capacity to grow to $2$ times its previous size. diff --git a/en/docs/chapter_hashing/index.md b/en/docs/chapter_hashing/index.md index 793b3ad05..308355285 100644 --- a/en/docs/chapter_hashing/index.md +++ b/en/docs/chapter_hashing/index.md @@ -1,9 +1,9 @@ -# Hashing +# Hash tables -![Hashing](../assets/covers/chapter_hashing.jpg) +![Hash tables](../assets/covers/chapter_hashing.jpg) !!! abstract - In the world of computing, a hash table is like a clever librarian. - - It knows how to compute call numbers, allowing it to quickly locate the desired book. + In the world of computers, a hash table is like a knowledgeable librarian. + + Knowing how to calculate a call number lets the librarian find the desired book quickly. diff --git a/en/docs/chapter_hashing/summary.md b/en/docs/chapter_hashing/summary.md index 4891ec570..9aff2e969 100644 --- a/en/docs/chapter_hashing/summary.md +++ b/en/docs/chapter_hashing/summary.md @@ -1,51 +1,51 @@ # Summary -### Key Review +### Key takeaways -- Given an input `key`, a hash table can retrieve the corresponding `value` in $O(1)$ time, which is highly efficient. -- Common hash table operations include querying, adding key-value pairs, deleting key-value pairs, and traversing the hash table. -- The hash function maps a `key` to an array index, allowing access to the corresponding bucket and retrieval of the `value`. -- Two different keys may end up with the same array index after hashing, leading to erroneous query results. This phenomenon is known as hash collision. -- The larger the capacity of the hash table, the lower the probability of hash collisions. Therefore, hash table expansion can mitigate hash collisions. Similar to array expansion, hash table expansion is costly. -- The load factor, defined as the number of elements divided by the number of buckets, reflects the severity of hash collisions and is often used as a condition to trigger hash table expansion. -- Separate chaining addresses hash collisions by storing all colliding elements in the same linked list. However, excessively long linked lists can reduce query efficiency, which can be improved by further converting the linked lists into red-black trees. -- Open addressing handles hash collisions through multiple probing. Linear probing uses a fixed step size but cannot delete elements and is prone to clustering. Double hashing uses multiple hash functions for probing, which reduces clustering compared to linear probing but increases computational overhead. -- Different programming languages adopt various hash table implementations. For example, Java's `HashMap` uses separate chaining, while Python's `dict` employs open addressing. -- In hash tables, we desire hash algorithms with determinism, high efficiency, and uniform distribution. In cryptography, hash algorithms should also possess collision resistance and the avalanche effect. -- Hash algorithms typically use large prime numbers as moduli to maximize the uniform distribution of hash values and reduce hash collisions. -- Common hash algorithms include MD5, SHA-1, SHA-2, and SHA-3. MD5 is often used for file integrity checks, while SHA-2 is commonly used in secure applications and protocols. -- Programming languages usually provide built-in hash algorithms for data types to calculate bucket indices in hash tables. Generally, only immutable objects are hashable. +- Given a `key`, a hash table can retrieve its `value` in $O(1)$ time, making lookup highly efficient. +- Common operations include lookup, adding and deleting key-value pairs, and traversing the table. +- A hash function maps a `key` to an array index, allowing access to the corresponding bucket and its `value`. +- Two different keys may map to the same array index and cause incorrect lookup results. This is called a hash collision. +- A larger capacity lowers the likelihood of collisions, so expanding a hash table can alleviate them. Like array expansion, hash-table expansion is expensive. +- The load factor is the number of elements divided by the number of buckets. It indicates how crowded the table is and is commonly used to trigger expansion. +- Separate chaining replaces a single entry with a linked list and stores all colliding entries in that list. Long lists slow lookup; converting them to red-black trees can improve efficiency. +- Open addressing resolves collisions through repeated probing. Linear probing uses a fixed step size, is prone to clustering, and does not allow straightforward deletion. Multiple hashing uses several hash functions, reducing clustering but adding computational cost. +- Programming languages use different implementations: Java's `HashMap` uses separate chaining, while Python's `dict` uses open addressing. +- For hash tables, we want deterministic, efficient hashing with uniformly distributed outputs. Cryptographic hashing also requires collision resistance and an avalanche effect. +- Hash algorithms often use a large prime modulus to promote uniform hash values and reduce collisions. +- Common hash algorithms include MD5, SHA-1, SHA-2, and SHA-3. MD5 is often used for file-integrity checks, while SHA-2 is widely used in security applications and protocols. +- Programming languages usually provide built-in hashing for data types to calculate bucket indices. In general, only immutable objects are hashable. ### Q & A -**Q**: When does the time complexity of a hash table degrade to $O(n)$? +**Q**: When does a hash table have $O(n)$ time complexity? -The time complexity of a hash table can degrade to $O(n)$ when hash collisions are severe. When the hash function is well-designed, the capacity is set appropriately, and collisions are evenly distributed, the time complexity is $O(1)$. We usually consider the time complexity to be $O(1)$ when using built-in hash tables in programming languages. +When collisions are severe, its time complexity degrades to $O(n)$. With a well-designed hash function, suitable capacity, and evenly distributed collisions, it is $O(1)$. We usually assume $O(1)$ time when using a language's built-in hash table. -**Q**: Why not use the hash function $f(x) = x$? This would eliminate collisions. +**Q**: Why not use the hash function $f(x) = x$? Wouldn't that avoid collisions? -Under the hash function $f(x) = x$, each element corresponds to a unique bucket index, which is equivalent to an array. However, the input space is usually much larger than the output space (array length), so the last step of a hash function is often to take the modulo of the array length. In other words, the goal of a hash table is to map a larger state space to a smaller one while providing $O(1)$ query efficiency. +With $f(x) = x$, each element has a unique bucket index, making the structure equivalent to an array. But the input space is usually far larger than the output space, the array's length, so the final hashing step usually takes a remainder modulo that length. A hash table maps a larger state space into a smaller one while providing $O(1)$ lookup. -**Q**: Why can hash tables be more efficient than arrays, linked lists, or binary trees, even though hash tables are implemented using these structures? +**Q**: Hash tables are implemented with arrays, linked lists, and binary trees. How can they be faster than those structures? -Firstly, hash tables have higher time efficiency but lower space efficiency. A significant portion of memory in hash tables remains unused. +First, greater time efficiency comes at the cost of space efficiency: a significant portion of a hash table's memory is unused. -Secondly, hash tables are only more time-efficient in specific use cases. If a feature can be implemented with the same time complexity using an array or a linked list, it's usually faster than using a hash table. This is because the computation of the hash function incurs overhead, making the constant factor in the time complexity larger. +Second, the improvement applies only to particular operations. If an array or linked list can perform an operation with the same time complexity, it is usually faster because computing a hash adds overhead and increases the constant factor. -Lastly, the time complexity of hash tables can degrade. For example, in separate chaining, we perform search operations in a linked list or red-black tree, which still risks degrading to $O(n)$ time. +Finally, hash-table performance can degrade. For example, separate chaining searches within linked lists or red-black trees, and there is still a risk of degeneration to $O(n)$ time. -**Q**: Does double hashing also have the flaw of not being able to delete elements directly? Can space marked as deleted be reused? +**Q**: Does multiple hashing also prevent direct deletion? Can a deleted slot be reused? -Double hashing is a form of open addressing, and all open addressing methods have the drawback of not being able to delete elements directly; they require marking elements as deleted. Marked spaces can be reused. When inserting new elements into the hash table, and the hash function points to a position marked as deleted, that position can be used by the new element. This maintains the probing sequence of the hash table while ensuring efficient use of space. +Multiple hashing is a form of open addressing, and all open-addressing methods require deletion markers rather than simply emptying a slot. A marked slot can be reused. When inserting a new element, if the hash functions locate such a slot, the new element can occupy it. This preserves the probe sequence while making effective use of space. -**Q**: Why do hash collisions occur during the search process in linear probing? +**Q**: Why can a collision occur during a lookup with linear probing? -During the search process, the hash function points to the corresponding bucket and key-value pair. If the `key` doesn't match, it indicates a hash collision. Therefore, linear probing will search downward at a predetermined step size until the correct key-value pair is found or the search fails. +The hash function leads to a bucket and a key-value pair, but the stored `key` may not match the requested one. That indicates a collision. Linear probing then continues with its predefined step size until it finds the correct pair or determines that none exists. -**Q**: Why can expanding a hash table alleviate hash collisions? +**Q**: Why does expanding a hash table reduce collisions? -The last step of a hash function often involves taking the modulo of the array length $n$, to keep the output within the array index range. When expanding, the array length $n$ changes, and the indices corresponding to the keys may also change. Keys that were previously mapped to the same bucket might be distributed across multiple buckets after expansion, thereby mitigating hash collisions. +The final hashing step often takes the remainder modulo the array length $n$ to produce a valid index. After expansion, $n$ changes, so the index of a given `key` may change too. Keys that previously shared a bucket may be redistributed among several buckets, reducing collisions. -**Q**: If the goal is efficient access, why not just use an array directly? +**Q**: If fast access is the goal, why not just use an array? -When the `key` values are continuous integers within a small range, an array is indeed a simple and efficient choice. But when the `key` is of another type, such as a string, we need a hash function to map the `key` to an array index and then store the element in a bucket array. That structure is precisely what a hash table is. +When keys are consecutive integers in a small range, an array is simple and efficient. But for other key types, such as strings, we need a hash function to map keys to array indices and a bucket array to store entries. That structure is a hash table. diff --git a/en/docs/chapter_heap/build_heap.md b/en/docs/chapter_heap/build_heap.md index 8cc206d31..1491f3b0f 100644 --- a/en/docs/chapter_heap/build_heap.md +++ b/en/docs/chapter_heap/build_heap.md @@ -1,52 +1,52 @@ -# Heap Construction Operation +# Building a heap -In some cases, we want to build a heap using all elements of a list, and this process is called "heap construction operation." +Sometimes we want to construct a heap from all the elements of a list. This process is called “building a heap.” -## Implementing with Element Insertion +## Building through insertion -We first create an empty heap, then iterate through the list, performing the "element insertion operation" on each element in sequence. This means appending the element to the end of the heap and then performing "bottom-to-top" heapify on that element. +First create an empty heap, then traverse the list and insert each element: append it to the end of the heap, then sift it upward. -Each time an element is inserted into the heap, the heap's length increases by one. Since nodes are added to the binary tree sequentially from top to bottom, the heap is constructed "from top to bottom." +Each insertion increases the heap's length by one. Since nodes are added to the binary tree from the top down, this constructs the heap “top down.” -Given $n$ elements, each element's insertion operation takes $O(\log{n})$ time, so the time complexity of this heap construction method is $O(n \log n)$. +For $n$ elements, each insertion takes $O(\log{n})$ time, so this method takes $O(n \log n)$ overall. -## Implementing Through Heapify Traversal +## Building by sifting nodes down -In fact, we can implement a more efficient heap construction method in two steps. +We can build a heap more efficiently in two steps. -1. Add all elements of the list as-is to the heap, at which point the heap property is not yet satisfied. -2. Traverse the heap in reverse order (reverse of level-order traversal), performing "top-to-bottom heapify" on each non-leaf node in sequence. +1. Copy all list elements into the heap without changing their order. The heap property may not yet hold. +2. Traverse the heap in reverse level order, sifting down each non-leaf node. -**After heapifying a node, the subtree rooted at that node becomes a valid sub-heap**. Since we traverse in reverse order, the heap is constructed "from bottom to top." +**After a node is sifted down, the subtree rooted at that node is a valid heap**. Since the traversal is reversed, the heap is built “bottom up.” -The reason for choosing reverse-order traversal is that it ensures the subtrees beneath the current node are already valid sub-heaps, so heapifying the current node is effective. +Reverse traversal ensures that the subtrees below the current node are already valid heaps, which is necessary for sifting down the current node to work correctly. -It's worth noting that **since leaf nodes have no children, they are naturally valid sub-heaps and do not require heapification**. As shown in the code below, the last non-leaf node is the parent of the last node; we start from that node and heapify while traversing in reverse order: +**Leaf nodes have no children and are already valid heaps, so they need no sifting**. As the code below shows, the last non-leaf node is the parent of the last node. Start there and traverse backward, sifting each node down: ```src [file]{my_heap}-[class]{max_heap}-[func]{__init__} ``` -## Complexity Analysis +## Complexity analysis -Next, let's attempt to derive the time complexity of this second heap construction method. +Let us derive the time complexity of the second method. -- Assuming the complete binary tree has $n$ nodes, then the number of leaf nodes is $(n + 1) / 2$, where $/$ is floor division. Therefore, the number of nodes that need heapification is $n / 2$. -- In the top-to-bottom heapify process, each node can sink at most to a leaf node, so the maximum number of iterations is the height of the binary tree, $\log n$. +- A complete binary tree with $n$ nodes has $(n + 1) / 2$ leaves, where $/$ denotes integer division rounded down. Therefore, $n / 2$ nodes need sifting. +- A node can move at most as far as a leaf during sifting, so the maximum number of iterations is the tree height, $\log n$. -Multiplying these two together, we get a time complexity of $O(n \log n)$ for the heap construction process. **However, this estimate is not accurate because it doesn't account for the property that binary trees have far more nodes at lower levels than at upper levels**. +Multiplying these gives a time complexity of $O(n \log n)$. **However, this estimate is not tight, because it ignores the fact that there are far more nodes near the bottom of the tree than near the top**. -Let's perform a more accurate calculation. To simplify the analysis, assume a "perfect binary tree" with $n$ nodes and height $h$; this assumption does not affect the correctness of the result. +For a more accurate calculation, assume a perfect binary tree with $n$ nodes and height $h$. This simplifies the calculation without affecting the validity of the result. -![Node count at each level of a perfect binary tree](build_heap.assets/heapify_operations_count.png) +![Number of nodes at each level of a perfect binary tree](build_heap.assets/heapify_operations_count.png) -As shown in the figure above, the maximum number of iterations for a node's "top-to-bottom heapify" equals the distance from that node to a leaf node, which is precisely the node's height. Therefore, we can sum the "number of nodes $\times$ node height" at each level to **obtain the total number of heapify iterations for all nodes**. +As shown above, the maximum number of sift-down iterations for a node equals its distance to a leaf, which is its height. Summing “number of nodes $\times$ node height” over all levels therefore gives **the total number of sifting iterations across all nodes**. $$ T(h) = 2^0h + 2^1(h-1) + 2^2(h-2) + \dots + 2^{(h-1)}\times1 $$ -Simplifying the expression above requires some high-school sequence algebra. First, multiply $T(h)$ by $2$ to get: +To simplify this expression, use a standard series technique. First multiply $T(h)$ by $2$: $$ \begin{aligned} @@ -55,13 +55,13 @@ T(h) & = 2^0h + 2^1(h-1) + 2^2(h-2) + \dots + 2^{h-1}\times1 \newline \end{aligned} $$ -Using subtraction of shifted sums, subtract the first equation $T(h)$ from the second equation $2 T(h)$ to get: +Subtract the first expression, $T(h)$, from the shifted expression, $2 T(h)$: $$ 2T(h) - T(h) = T(h) = -2^0h + 2^1 + 2^2 + \dots + 2^{h-1} + 2^h $$ -Observing the above expression, we find that $T(h)$ is a geometric series, which can be calculated directly using the sum formula, yielding a time complexity of: +The powers of two in $T(h)$ form a geometric series. Applying the sum formula gives: $$ \begin{aligned} @@ -71,4 +71,4 @@ T(h) & = 2 \frac{1 - 2^h}{1 - 2} - h \newline \end{aligned} $$ -Furthermore, a perfect binary tree with height $h$ has $n = 2^{h+1} - 1$ nodes, so the complexity is $O(2^h) = O(n)$. This derivation shows that **the time complexity of building a heap from an input list is $O(n)$, which is highly efficient**. +A perfect binary tree of height $h$ has $n = 2^{h+1} - 1$ nodes, so $O(2^h) = O(n)$. This shows that **building a heap from an input list takes $O(n)$ time and is highly efficient**. diff --git a/en/docs/chapter_heap/exercises.md b/en/docs/chapter_heap/exercises.md index 8f7376b3a..718444a22 100644 --- a/en/docs/chapter_heap/exercises.md +++ b/en/docs/chapter_heap/exercises.md @@ -1,64 +1,64 @@ # Exercises -## Concept Review +## Check your understanding -### How Does the Heap Change After Inserting 10? +### Restoring the heap after inserting 10 -The array `[9, 7, 8, 3, 5]` represents a max heap. Now insert the number 10. +The array `[9, 7, 8, 3, 5]` represents a max heap. Insert the number 10. -1. First append 10 to the end of the array. What is the value of its parent node? -2. Starting from the new node, perform bottom-to-top heapify and write the array after each swap. -3. What is the final top element of the heap? How many swaps occur in total? +1. After appending 10 to the array, what is its parent's value? +2. Sift up from the new node. Write the array after each swap. +3. What is the final root element? How many swaps were made? -??? success "Answer" +??? success "Reference answer" - 1. After 10 is appended, its index is 5, so its parent's index is - $\lfloor(5-1)/2\rfloor=2$. The parent node's value is 8. + 1. The appended 10 has index 5, so its parent's index is + $\lfloor(5-1)/2\rfloor=2$, and its parent's value is 8. - 2. Since 10 is greater than 8, the array after the first swap is `[9, 7, 10, 3, 5, 8]`. - Since 10 is also greater than its parent 9, the array after the second swap is `[10, 7, 9, 3, 5, 8]`. - The value 10 has now reached the root node, so heapification is complete. + 2. Since 10 is greater than 8, the first swap produces `[9, 7, 10, 3, 5, 8]`. + Since 10 is also greater than its new parent, 9, the second swap produces `[10, 7, 9, 3, 5, 8]`. + The value 10 has now reached the root, so sifting stops. - 3. The final top element is 10, and 2 swaps occur in total. + 3. The final root is 10, and there were two swaps. -### Check Parent–Child Relationships in a Min Heap +### Checking parent-child relationships in a min heap -The array `[1, 4, 3, 7, 6, 2]` represents a complete binary tree. In a min heap, every parent node must be no greater than its children. +The array `[1, 4, 3, 7, 6, 2]` represents a complete binary tree. A min heap requires each parent to be no greater than its children. For index $i$, the left and right child indices are $2i+1$ and $2i+2$, respectively. 1. What are the indices and values of the children of index 2? -2. The node at index 2 has value 3. Does it violate the min-heap rule with its child? If so, which two elements should be swapped? -3. Based on your answer to Question 2, write the array after the swap if the rule is violated; otherwise, explain why no swap is needed. Finally, check the remaining parent–child relationships. +2. The node at index 2 has value 3. Does its relationship with its children violate the min-heap property? If so, which two elements should be swapped? +3. Based on your answer to question 2, write the array after the swap if one is needed, or explain why no swap is necessary. Finally, check the other parent-child relationships. -??? success "Answer" +??? success "Reference answer" - 1. The left child of index 2 is at index 5 and has value 2. The right child's index is 6, but the array has length 6, so the right child does not exist. + 1. The left child of index 2 is at index 5 and has value 2. Its right child would be at index 6, but the array has length 6, so that child does not exist. - 2. The parent value 3 is greater than the child value 2, violating the min-heap rule. The elements at indices 2 and 5 should be swapped. + 2. The parent value 3 exceeds the child value 2, violating the min-heap property. Swap the elements at indices 2 and 5. - 3. After the swap, the array is `[1, 4, 2, 7, 6, 3]`. Check each relationship: - `1 ≤ 4`, `1 ≤ 2`; `4 ≤ 7`, `4 ≤ 6`; and `2 ≤ 3`. - Every parent is now no greater than its children, so the min-heap rule is satisfied. + 3. The resulting array is `[1, 4, 2, 7, 6, 3]`. Checking each relationship gives: + `1 ≤ 4`, `1 ≤ 2`; `4 ≤ 7`, `4 ≤ 6`; `2 ≤ 3`. + Every parent is no greater than its children, so the min-heap property now holds. -### Keep the Three Largest Numbers with a Min Heap +### Keeping the three largest numbers with a min heap -To keep the 3 largest numbers from the data stream `[4, 1, 7, 3, 8]`, maintain a min heap containing no more than 3 elements. +To keep the three largest numbers from the stream `[4, 1, 7, 3, 8]`, maintain a min heap of at most three elements. -First insert the first 3 numbers into the min heap in order. Once the heap is full, for each new number: -if it is greater than the top element, remove the top and insert the new number; otherwise, leave the heap unchanged. +Insert the first three numbers in order. Once the heap is full, for each new number: +if it is greater than the root, remove the root and insert the new number; otherwise, leave the heap unchanged. -After each number is read, write the numbers kept in the heap and the top element. -Write the kept numbers as a set; you do not need to give their order in the heap array. +After reading each number, write the numbers retained in the heap and the root value. +You need only give the retained numbers as a set, not their arrangement in the heap array. -??? success "Answer" +??? success "Reference answer" - The result after each number is read is: + The results after each number are: - | Number read | Numbers kept | Top | + | Number read | Retained numbers | Root | | --- | --- | --- | | 4 | `{4}` | 4 | | 1 | `{1, 4}` | 1 | @@ -66,21 +66,21 @@ Write the kept numbers as a set; you do not need to give their order in the heap | 3 | `{3, 4, 7}` | 3 | | 8 | `{4, 7, 8}` | 4 | - Once the heap is full, its top is the smallest of the numbers currently kept. A new number replaces the top only when it is greater than the top. - The final set `{4, 7, 8}` contains exactly the 3 largest numbers. + Once the heap is full, its root is the smallest retained number. Replace the root with the new number + only when the new number is larger. The final set `{4, 7, 8}` contains exactly the three largest numbers. -## Programming Exercises +## Programming exercises -### Find the Kth Largest Element in an Array +### Finding the kth largest element in an array -Given an integer array `nums` and an integer $k$, where $1 \le k \le n$ and $n$ is the array's length, return the element that would appear at position $k$ if the array were arranged from largest to smallest. +Given an integer array `nums` and an integer $k$, where $1 \le k \le n$ and $n$ is the array length, return the element in position $k$ when the array is ordered from largest to smallest. -Count duplicate elements separately. For example, the second-largest element of `[5, 5, 2]` is still 5. Use a min heap containing no more than $k$ elements. +Count duplicate elements separately. For example, the second largest element of `[5, 5, 2]` is still 5. Use a min heap of at most $k$ elements. ??? tip "Hints" - 1. The kth largest element is the smallest among the k largest numbers - 2. Insert each number into the min heap, and remove the smallest value whenever the heap's size exceeds k - 3. After the traversal, the heap contains the k largest numbers, and its top is the answer + 1. The kth largest element is the smallest of the k largest numbers. + 2. Insert each number into the min heap; if its size exceeds k, remove the minimum. + 3. When the traversal ends, the heap retains the k largest numbers, and its root is the answer. [LeetCode](https://leetcode.com/problems/kth-largest-element-in-an-array/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_heap/heap.md b/en/docs/chapter_heap/heap.md index 8c615f8c4..8f1d5f5e3 100644 --- a/en/docs/chapter_heap/heap.md +++ b/en/docs/chapter_heap/heap.md @@ -1,39 +1,39 @@ -# Heap +# Heaps -A heap is a complete binary tree that satisfies specific conditions and can be mainly categorized into two types, as shown in the figure below. +A heap is a complete binary tree that satisfies a particular ordering condition. There are two main types, shown below. -- min heap: The value of any node $\leq$ the values of its child nodes. -- max heap: The value of any node $\geq$ the values of its child nodes. +- Min heap: every node's value is $\leq$ the values of its children. +- Max heap: every node's value is $\geq$ the values of its children. -![Min heap and max heap](heap.assets/min_heap_and_max_heap.png) +![A min heap and a max heap](heap.assets/min_heap_and_max_heap.png) -As a special case of a complete binary tree, heaps have the following characteristics. +As a special kind of complete binary tree, a heap has the following properties. -- The bottom layer nodes are filled from left to right, and nodes in other layers are fully filled. -- We call the root node of the binary tree the "heap top" and the bottom-rightmost node the "heap bottom." -- For max heaps (min heaps), the value of the heap top element (root node) is the largest (smallest). +- The lowest level is filled from left to right, and all other levels are full. +- The root is the “top” of the heap, while the rightmost node on the lowest level is its “last” node. +- In a max heap or min heap, the root holds the largest or smallest value, respectively. -## Common Heap Operations +## Common heap operations -It should be noted that many programming languages provide a priority queue, an abstract data structure defined as a queue whose elements are ordered by priority. +Many programming languages provide a priority queue, an abstract data structure whose elements are removed according to priority. -In fact, **heaps are typically used to implement priority queues, with max heaps corresponding to priority queues where elements are dequeued in descending order**. From a usage perspective, we can regard "priority queue" and "heap" as equivalent data structures. Therefore, this book does not make a special distinction between the two and uniformly refers to them as "heap." +**Heaps are commonly used to implement priority queues. A max heap corresponds to a priority queue that removes elements from largest to smallest**. From the user's perspective, the two can be treated as equivalent. This book therefore does not distinguish them strictly and uses the term “heap” for both. -Common heap operations are shown in the table below, and method names need to be determined based on the programming language. +The table below lists common operations. Method names depend on the programming language. -

Table   Efficiency of Heap Operations

+

Table   Efficiency of heap operations

-| Method name | Description | Time complexity | -| ----------- | ----------------------------------------------------------------- | --------------- | -| `push()` | Insert an element into the heap | $O(\log n)$ | -| `pop()` | Remove the heap top element | $O(\log n)$ | -| `peek()` | Access the heap top element (max/min value for max/min heap) | $O(1)$ | -| `size()` | Get the number of elements in the heap | $O(1)$ | -| `isEmpty()` | Check if the heap is empty | $O(1)$ | +| Method | Description | Time complexity | +| ----------- | ------------------------------------------------ | ----------- | +| `push()` | Insert an element | $O(\log n)$ | +| `pop()` | Remove the root element | $O(\log n)$ | +| `peek()` | Access the root: the maximum in a max heap or minimum in a min heap | $O(1)$ | +| `size()` | Get the number of elements | $O(1)$ | +| `isEmpty()` | Check whether the heap is empty | $O(1)$ | -In practical applications, we can directly use the heap class (or priority queue class) provided by programming languages. +In practice, use the heap or priority-queue class provided by the language. -Similar to "ascending order" and "descending order" in sorting algorithms, we can implement conversion between "min heap" and "max heap" by setting a `flag` or modifying the `Comparator`. The code is as follows: +Just as a sorting algorithm can order values in ascending or descending order, we can switch between min heaps and max heaps by setting a `flag` or changing the `Comparator`: === "Python" @@ -44,21 +44,21 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca max_heap, flag = [], -1 # Python's heapq module implements a min heap by default - # Consider negating elements before pushing them to the heap, which inverts the size relationship and thus implements a max heap - # In this example, flag = 1 corresponds to a min heap, flag = -1 corresponds to a max heap + # Negate elements before insertion to reverse their order and implement a max heap + # Here, flag = 1 gives a min heap and flag = -1 gives a max heap - # Push elements into the heap + # Insert elements into the heap heapq.heappush(max_heap, flag * 1) heapq.heappush(max_heap, flag * 3) heapq.heappush(max_heap, flag * 2) heapq.heappush(max_heap, flag * 5) heapq.heappush(max_heap, flag * 4) - # Get the heap top element + # Access the root element peek: int = flag * max_heap[0] # 5 - # Remove the heap top element - # The removed elements will form a descending sequence + # Remove root elements + # Removed elements form a descending sequence val = flag * heapq.heappop(max_heap) # 5 val = flag * heapq.heappop(max_heap) # 4 val = flag * heapq.heappop(max_heap) # 3 @@ -68,10 +68,10 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca # Get the heap size size: int = len(max_heap) - # Check if the heap is empty + # Check whether the heap is empty is_empty: bool = not max_heap - # Build a heap from an input list + # Build a heap from a list min_heap: list[int] = [1, 3, 2, 5, 4] heapq.heapify(min_heap) ``` @@ -85,18 +85,18 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca // Initialize a max heap priority_queue, less> maxHeap; - /* Push elements into the heap */ + /* Insert elements into the heap */ maxHeap.push(1); maxHeap.push(3); maxHeap.push(2); maxHeap.push(5); maxHeap.push(4); - /* Get the heap top element */ + /* Access the root element */ int peek = maxHeap.top(); // 5 - /* Remove the heap top element */ - // The removed elements will form a descending sequence + /* Remove root elements */ + // Removed elements form a descending sequence maxHeap.pop(); // 5 maxHeap.pop(); // 4 maxHeap.pop(); // 3 @@ -106,10 +106,10 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Get the heap size */ int size = maxHeap.size(); - /* Check if the heap is empty */ + /* Check whether the heap is empty */ bool isEmpty = maxHeap.empty(); - /* Build a heap from an input list */ + /* Build a heap from a list */ vector input{1, 3, 2, 5, 4}; priority_queue, greater> minHeap(input.begin(), input.end()); ``` @@ -120,21 +120,21 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Initialize a heap */ // Initialize a min heap Queue minHeap = new PriorityQueue<>(); - // Initialize a max heap (use lambda expression to modify Comparator) + // Initialize a max heap by changing the Comparator with a lambda expression Queue maxHeap = new PriorityQueue<>((a, b) -> b - a); - /* Push elements into the heap */ + /* Insert elements into the heap */ maxHeap.offer(1); maxHeap.offer(3); maxHeap.offer(2); maxHeap.offer(5); maxHeap.offer(4); - /* Get the heap top element */ + /* Access the root element */ int peek = maxHeap.peek(); // 5 - /* Remove the heap top element */ - // The removed elements will form a descending sequence + /* Remove root elements */ + // Removed elements form a descending sequence peek = maxHeap.poll(); // 5 peek = maxHeap.poll(); // 4 peek = maxHeap.poll(); // 3 @@ -144,10 +144,10 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Get the heap size */ int size = maxHeap.size(); - /* Check if the heap is empty */ + /* Check whether the heap is empty */ boolean isEmpty = maxHeap.isEmpty(); - /* Build a heap from an input list */ + /* Build a heap from a list */ minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4)); ``` @@ -157,21 +157,21 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Initialize a heap */ // Initialize a min heap PriorityQueue minHeap = new(); - // Initialize a max heap (use lambda expression to modify Comparer) + // Initialize a max heap by changing the Comparer with a lambda expression PriorityQueue maxHeap = new(Comparer.Create((x, y) => y.CompareTo(x))); - /* Push elements into the heap */ + /* Insert elements into the heap */ maxHeap.Enqueue(1, 1); maxHeap.Enqueue(3, 3); maxHeap.Enqueue(2, 2); maxHeap.Enqueue(5, 5); maxHeap.Enqueue(4, 4); - /* Get the heap top element */ + /* Access the root element */ int peek = maxHeap.Peek();//5 - /* Remove the heap top element */ - // The removed elements will form a descending sequence + /* Remove root elements */ + // Removed elements form a descending sequence peek = maxHeap.Dequeue(); // 5 peek = maxHeap.Dequeue(); // 4 peek = maxHeap.Dequeue(); // 3 @@ -181,52 +181,52 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Get the heap size */ int size = maxHeap.Count; - /* Check if the heap is empty */ + /* Check whether the heap is empty */ bool isEmpty = maxHeap.Count == 0; - /* Build a heap from an input list */ + /* Build a heap from a list */ minHeap = new PriorityQueue([(1, 1), (3, 3), (2, 2), (5, 5), (4, 4)]); ``` === "Go" ```go title="heap.go" - // In Go, we can construct a max heap of integers by implementing heap.Interface + // In Go, implement heap.Interface to build an integer max heap // Implementing heap.Interface also requires implementing sort.Interface type intHeap []any - // Push implements the heap.Interface method for pushing an element into the heap + // Push implements the heap.Interface method for inserting an element func (h *intHeap) Push(x any) { - // Push and Pop use pointer receiver as parameters - // because they not only adjust the slice contents but also modify the slice length + // Push and Pop use pointer receivers + // because they change the slice length as well as its contents. *h = append(*h, x.(int)) } - // Pop implements the heap.Interface method for popping the heap top element + // Pop implements the heap.Interface method used to remove the root element func (h *intHeap) Pop() any { - // The element to be removed is stored at the end + // The element to remove has been placed at the end last := (*h)[len(*h)-1] *h = (*h)[:len(*h)-1] return last } - // Len is a sort.Interface method + // Len implements a sort.Interface method func (h *intHeap) Len() int { return len(*h) } - // Less is a sort.Interface method + // Less implements a sort.Interface method func (h *intHeap) Less(i, j int) bool { - // To implement a min heap, change this to a less-than sign + // For a min heap, change this to a less-than comparison return (*h)[i].(int) > (*h)[j].(int) } - // Swap is a sort.Interface method + // Swap implements a sort.Interface method func (h *intHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } - // Top gets the heap top element + // Top returns the root element func (h *intHeap) Top() any { return (*h)[0] } @@ -237,20 +237,20 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca // Initialize a max heap maxHeap := &intHeap{} heap.Init(maxHeap) - /* Push elements into the heap */ - // Call heap.Interface methods to add elements + /* Insert elements into the heap */ + // Call the heap.Interface methods to add elements heap.Push(maxHeap, 1) heap.Push(maxHeap, 3) heap.Push(maxHeap, 2) heap.Push(maxHeap, 4) heap.Push(maxHeap, 5) - /* Get the heap top element */ + /* Access the root element */ top := maxHeap.Top() - fmt.Printf("Heap top element is %d\n", top) + fmt.Printf("The root element is %d\n", top) - /* Remove the heap top element */ - // Call heap.Interface methods to remove elements + /* Remove root elements */ + // Call the heap.Interface methods to remove elements heap.Pop(maxHeap) // 5 heap.Pop(maxHeap) // 4 heap.Pop(maxHeap) // 3 @@ -259,11 +259,11 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Get the heap size */ size := len(*maxHeap) - fmt.Printf("Number of heap elements is %d\n", size) + fmt.Printf("The heap contains %d elements\n", size) - /* Check if the heap is empty */ + /* Check whether the heap is empty */ isEmpty := len(*maxHeap) == 0 - fmt.Printf("Is the heap empty? %t\n", isEmpty) + fmt.Printf("Is the heap empty: %t\n", isEmpty) } ``` @@ -271,20 +271,20 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca ```swift title="heap.swift" /* Initialize a heap */ - // Swift's Heap type supports both max heaps and min heaps, and requires importing swift-collections + // Swift's Heap type supports both minimum and maximum operations and requires swift-collections var heap = Heap() - /* Push elements into the heap */ + /* Insert elements into the heap */ heap.insert(1) heap.insert(3) heap.insert(2) heap.insert(5) heap.insert(4) - /* Get the heap top element */ + /* Access the root element */ var peek = heap.max()! - /* Remove the heap top element */ + /* Remove root elements */ peek = heap.removeMax() // 5 peek = heap.removeMax() // 4 peek = heap.removeMax() // 3 @@ -294,29 +294,29 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Get the heap size */ let size = heap.count - /* Check if the heap is empty */ + /* Check whether the heap is empty */ let isEmpty = heap.isEmpty - /* Build a heap from an input list */ + /* Build a heap from a list */ let heap2 = Heap([1, 3, 2, 5, 4]) ``` === "JS" ```javascript title="heap.js" - // JavaScript does not provide a built-in Heap class + // JavaScript provides no built-in Heap class ``` === "TS" ```typescript title="heap.ts" - // TypeScript does not provide a built-in Heap class + // TypeScript provides no built-in Heap class ``` === "Dart" ```dart title="heap.dart" - // Dart does not provide a built-in Heap class + // Dart provides no built-in Heap class ``` === "Rust" @@ -331,18 +331,18 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca // Initialize a max heap let mut max_heap = BinaryHeap::new(); - /* Push elements into the heap */ + /* Insert elements into the heap */ max_heap.push(1); max_heap.push(3); max_heap.push(2); max_heap.push(5); max_heap.push(4); - /* Get the heap top element */ + /* Access the root element */ let peek = max_heap.peek().unwrap(); // 5 - /* Remove the heap top element */ - // The removed elements will form a descending sequence + /* Remove root elements */ + // Removed elements form a descending sequence let peek = max_heap.pop().unwrap(); // 5 let peek = max_heap.pop().unwrap(); // 4 let peek = max_heap.pop().unwrap(); // 3 @@ -352,17 +352,17 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Get the heap size */ let size = max_heap.len(); - /* Check if the heap is empty */ + /* Check whether the heap is empty */ let is_empty = max_heap.is_empty(); - /* Build a heap from an input list */ + /* Build a heap from a list */ let min_heap = BinaryHeap::from(vec![Reverse(1), Reverse(3), Reverse(2), Reverse(5), Reverse(4)]); ``` === "C" ```c title="heap.c" - // C does not provide a built-in Heap class + // C provides no built-in Heap class ``` === "Kotlin" @@ -371,21 +371,21 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Initialize a heap */ // Initialize a min heap var minHeap = PriorityQueue() - // Initialize a max heap (use lambda expression to modify Comparator) + // Initialize a max heap by changing the Comparator with a lambda expression val maxHeap = PriorityQueue { a: Int, b: Int -> b - a } - /* Push elements into the heap */ + /* Insert elements into the heap */ maxHeap.offer(1) maxHeap.offer(3) maxHeap.offer(2) maxHeap.offer(5) maxHeap.offer(4) - /* Get the heap top element */ + /* Access the root element */ var peek = maxHeap.peek() // 5 - /* Remove the heap top element */ - // The removed elements will form a descending sequence + /* Remove root elements */ + // Removed elements form a descending sequence peek = maxHeap.poll() // 5 peek = maxHeap.poll() // 4 peek = maxHeap.poll() // 3 @@ -395,59 +395,59 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca /* Get the heap size */ val size = maxHeap.size - /* Check if the heap is empty */ + /* Check whether the heap is empty */ val isEmpty = maxHeap.isEmpty() - /* Build a heap from an input list */ + /* Build a heap from a list */ minHeap = PriorityQueue(mutableListOf(1, 3, 2, 5, 4)) ``` === "Ruby" ```ruby title="heap.rb" - # Ruby does not provide a built-in Heap class + # Ruby provides no built-in Heap class ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=import%20heapq%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B0%8F%E9%A1%B6%E5%A0%86%0A%20%20%20%20min_heap,%20flag%20%3D%20%5B%5D,%201%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%A4%A7%E9%A1%B6%E5%A0%86%0A%20%20%20%20max_heap,%20flag%20%3D%20%5B%5D,%20-1%0A%20%20%20%20%0A%20%20%20%20%23%20Python%20%E7%9A%84%20heapq%20%E6%A8%A1%E5%9D%97%E9%BB%98%E8%AE%A4%E5%AE%9E%E7%8E%B0%E5%B0%8F%E9%A1%B6%E5%A0%86%0A%20%20%20%20%23%20%E8%80%83%E8%99%91%E5%B0%86%E2%80%9C%E5%85%83%E7%B4%A0%E5%8F%96%E8%B4%9F%E2%80%9D%E5%90%8E%E5%86%8D%E5%85%A5%E5%A0%86%EF%BC%8C%E8%BF%99%E6%A0%B7%E5%B0%B1%E5%8F%AF%E4%BB%A5%E5%B0%86%E5%A4%A7%E5%B0%8F%E5%85%B3%E7%B3%BB%E9%A2%A0%E5%80%92%EF%BC%8C%E4%BB%8E%E8%80%8C%E5%AE%9E%E7%8E%B0%E5%A4%A7%E9%A1%B6%E5%A0%86%0A%20%20%20%20%23%20%E5%9C%A8%E6%9C%AC%E7%A4%BA%E4%BE%8B%E4%B8%AD%EF%BC%8Cflag%20%3D%201%20%E6%97%B6%E5%AF%B9%E5%BA%94%E5%B0%8F%E9%A1%B6%E5%A0%86%EF%BC%8Cflag%20%3D%20-1%20%E6%97%B6%E5%AF%B9%E5%BA%94%E5%A4%A7%E9%A1%B6%E5%A0%86%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E5%A0%86%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%201%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%203%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%202%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%205%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%204%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%A0%86%E9%A1%B6%E5%85%83%E7%B4%A0%0A%20%20%20%20peek%20%3D%20flag%20*%20max_heap%5B0%5D%20%23%205%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%A0%86%E9%A1%B6%E5%85%83%E7%B4%A0%E5%87%BA%E5%A0%86%0A%20%20%20%20%23%20%E5%87%BA%E5%A0%86%E5%85%83%E7%B4%A0%E4%BC%9A%E5%BD%A2%E6%88%90%E4%B8%80%E4%B8%AA%E4%BB%8E%E5%A4%A7%E5%88%B0%E5%B0%8F%E7%9A%84%E5%BA%8F%E5%88%97%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%205%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%204%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%203%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%202%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%201%0A%20%20%20%20%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%A0%86%E5%A4%A7%E5%B0%8F%0A%20%20%20%20size%20%3D%20len%28max_heap%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E5%A0%86%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20not%20max_heap%0A%20%20%20%20%0A%20%20%20%20%23%20%E8%BE%93%E5%85%A5%E5%88%97%E8%A1%A8%E5%B9%B6%E5%BB%BA%E5%A0%86%0A%20%20%20%20min_heap%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20heapq.heapify%28min_heap%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=import%20heapq%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20min%20heap%0A%20%20%20%20min_heap%2C%20flag%20%3D%20%5B%5D%2C%201%0A%20%20%20%20%23%20Initialize%20a%20max%20heap%0A%20%20%20%20max_heap%2C%20flag%20%3D%20%5B%5D%2C%20-1%0A%20%20%20%20%0A%20%20%20%20%23%20Python%27s%20heapq%20module%20implements%20a%20min%20heap%20by%20default%0A%20%20%20%20%23%20Negate%20elements%20before%20insertion%20to%20reverse%20their%20order%20and%20implement%20a%20max%20heap%0A%20%20%20%20%23%20Here%2C%20flag%20%3D%201%20gives%20a%20min%20heap%20and%20flag%20%3D%20-1%20gives%20a%20max%20heap%0A%20%20%20%20%0A%20%20%20%20%23%20Insert%20elements%20into%20the%20heap%0A%20%20%20%20heapq.heappush%28max_heap%2C%20flag%20%2A%201%29%0A%20%20%20%20heapq.heappush%28max_heap%2C%20flag%20%2A%203%29%0A%20%20%20%20heapq.heappush%28max_heap%2C%20flag%20%2A%202%29%0A%20%20%20%20heapq.heappush%28max_heap%2C%20flag%20%2A%205%29%0A%20%20%20%20heapq.heappush%28max_heap%2C%20flag%20%2A%204%29%0A%20%20%20%20%0A%20%20%20%20%23%20Access%20the%20root%20element%0A%20%20%20%20peek%20%3D%20flag%20%2A%20max_heap%5B0%5D%20%23%205%0A%20%20%20%20%0A%20%20%20%20%23%20Remove%20root%20elements%0A%20%20%20%20%23%20Removed%20elements%20form%20a%20descending%20sequence%0A%20%20%20%20val%20%3D%20flag%20%2A%20heapq.heappop%28max_heap%29%20%23%205%0A%20%20%20%20val%20%3D%20flag%20%2A%20heapq.heappop%28max_heap%29%20%23%204%0A%20%20%20%20val%20%3D%20flag%20%2A%20heapq.heappop%28max_heap%29%20%23%203%0A%20%20%20%20val%20%3D%20flag%20%2A%20heapq.heappop%28max_heap%29%20%23%202%0A%20%20%20%20val%20%3D%20flag%20%2A%20heapq.heappop%28max_heap%29%20%23%201%0A%20%20%20%20%0A%20%20%20%20%23%20Get%20the%20heap%20size%0A%20%20%20%20size%20%3D%20len%28max_heap%29%0A%20%20%20%20%0A%20%20%20%20%23%20Check%20whether%20the%20heap%20is%20empty%0A%20%20%20%20is_empty%20%3D%20not%20max_heap%0A%20%20%20%20%0A%20%20%20%20%23%20Build%20a%20heap%20from%20a%20list%0A%20%20%20%20min_heap%20%3D%20%5B1%2C%203%2C%202%2C%205%2C%204%5D%0A%20%20%20%20heapq.heapify%28min_heap%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## Implementation of the Heap +## Implementing a heap -The following implementation is for a max heap. To convert it to a min heap, simply reverse all comparison logic related to ordering (for example, replace $\geq$ with $\leq$). Interested readers are encouraged to implement this on their own. +The following implementation is a max heap. To turn it into a min heap, reverse every ordering comparison, for example replacing $\geq$ with $\leq$. Interested readers can try this themselves. -### Heap Storage and Representation +### Storage and representation -As mentioned in the "Binary Tree" chapter, complete binary trees are well-suited for array representation. Since heaps are a type of complete binary tree, **we will use arrays to store heaps**. +The binary-tree chapter showed that complete binary trees are well suited to array representation. Since a heap is a complete binary tree, **we will store it in an array**. -When representing a binary tree with an array, elements represent node values, and indexes represent node positions in the binary tree. **Parent-child relationships are represented through index-mapping formulas**. +In an array representation, elements store node values and indices identify positions in the tree. **Index-mapping formulas take the place of node pointers**. -As shown in the figure below, given an index $i$, the index of its left child is $2i + 1$, the index of its right child is $2i + 2$, and the index of its parent is $(i - 1) / 2$ (floor division). When an index is out of bounds, it indicates a null node or that the node does not exist. +As shown below, a node at index $i$ has its left child at $2i + 1$, its right child at $2i + 2$, and its parent at $(i - 1) / 2$, rounded down. An out-of-bounds index represents a missing node. -![Representation and storage of heaps](heap.assets/representation_of_heap.png) +![Representing and storing a heap](heap.assets/representation_of_heap.png) -We can encapsulate the index mapping formula into functions for convenient subsequent use: +Wrap the index formulas in functions for convenient use: ```src [file]{my_heap}-[class]{max_heap}-[func]{parent} ``` -### Accessing the Heap Top Element +### Accessing the root -The heap top element is the root node of the binary tree, which is also the first element of the list: +The heap's root is the first element of the list: ```src [file]{my_heap}-[class]{max_heap}-[func]{peek} ``` -### Inserting an Element Into the Heap +### Inserting an element -Given an element `val`, we first add it to the bottom of the heap. After insertion, because `val` may be larger than other elements in the heap, the heap property may be violated. **Therefore, we need to restore the heap property along the path from the inserted node to the root**. This operation is called heapify. +To insert `val`, first append it at the end of the heap. Since it may be larger than other elements, this can violate the heap property. **We must restore the property along the path from the inserted node to the root**, an operation called heapifying. -Starting from the inserted node, **perform heapify from bottom to top**. As shown in the figure below, we compare the inserted node with its parent, and if the inserted node is larger, we swap them. We continue this process from bottom to top until we move past the root or reach a node that no longer needs to be swapped. +Start at the inserted node and **sift upward**. As shown below, compare it with its parent and swap them if the inserted value is larger. Repeat upward until the root is reached or no swap is needed. === "<1>" - ![Steps of inserting an element into the heap](heap.assets/heap_push_step1.png) + ![Steps for inserting into a heap](heap.assets/heap_push_step1.png) === "<2>" ![heap_push_step2](heap.assets/heap_push_step2.png) @@ -473,24 +473,24 @@ Starting from the inserted node, **perform heapify from bottom to top**. As show === "<9>" ![heap_push_step9](heap.assets/heap_push_step9.png) -Given a total of $n$ nodes, the tree height is $O(\log n)$. Thus, the number of loop iterations in the heapify operation is at most $O(\log n)$, **making the time complexity of the element insertion operation $O(\log n)$**. The code is as follows: +With $n$ nodes, the tree height is $O(\log n)$, so sifting takes at most $O(\log n)$ iterations. **Insertion therefore takes $O(\log n)$ time**. The code is as follows: ```src [file]{my_heap}-[class]{max_heap}-[func]{sift_up} ``` -### Removing the Heap Top Element +### Removing the root -The heap top element is the root node of the binary tree, which is the first element of the list. If we directly remove the first element from the list, all node indexes in the binary tree would change, making subsequent repair with heapify difficult. To minimize changes in element indexes, we use the following steps. +The root is the first element of the list. Removing it directly would change every remaining index, making it difficult to restore the heap property. To minimize index changes, use the following steps. -1. Swap the heap top element with the heap bottom element (swap the root node with the rightmost leaf node). -2. After swapping, remove the heap bottom from the list (note that since we've swapped, we're actually removing the original heap top element). -3. Starting from the root node, **perform heapify from top to bottom**. +1. Swap the root with the last element, the rightmost leaf. +2. Remove the last element from the list. Because of the swap, this is the original root. +3. Starting at the root, **sift downward**. -As shown in the figure below, **the direction of "top-to-bottom heapify" is opposite to "bottom-to-top heapify"**. We compare the root node's value with its two children and swap it with the largest child. Then loop this operation until we pass a leaf node or encounter a node that doesn't need swapping. +As shown below, **sifting down proceeds in the opposite direction from sifting up**. Compare the root with its two children and swap it with the larger child when needed. Continue downward until a leaf is reached or no swap is necessary. === "<1>" - ![Steps of removing the heap top element](heap.assets/heap_pop_step1.png) + ![Steps for removing the root of a heap](heap.assets/heap_pop_step1.png) === "<2>" ![heap_pop_step2](heap.assets/heap_pop_step2.png) @@ -519,14 +519,14 @@ As shown in the figure below, **the direction of "top-to-bottom heapify" is oppo === "<10>" ![heap_pop_step10](heap.assets/heap_pop_step10.png) -Similar to the element insertion operation, the time complexity of the heap top element removal operation is also $O(\log n)$. The code is as follows: +Like insertion, removing the root takes $O(\log n)$ time. The code is as follows: ```src [file]{my_heap}-[class]{max_heap}-[func]{sift_down} ``` -## Common Applications of Heaps +## Common applications -- **Priority queue**: Heaps are typically the preferred data structure for implementing priority queues. The time complexity of both enqueue and dequeue operations is $O(\log n)$, and heap construction has a time complexity of $O(n)$, making these operations highly efficient. -- **Heap sort**: Given a set of data, we can build a heap with them and then continuously perform element removal operations to obtain sorted data. However, we usually use a more elegant approach to implement heap sort, as detailed in the "Heap Sort" chapter. -- **Getting the largest $k$ elements**: This is a classic algorithm problem and also a typical application, such as selecting the top 10 trending news items for Weibo Hot Search or the top 10 best-selling products. +- **Priority queues**: Heaps are usually the first choice for implementing priority queues. Enqueueing and dequeueing both take $O(\log n)$ time, and building a heap takes $O(n)$. +- **Heap sort**: Build a heap from a dataset, then repeatedly remove its root to obtain sorted data. In practice, a more elegant implementation is usually used; see the heap-sort section. +- **Finding the largest $k$ elements**: This is both a classic algorithm problem and a common application, such as selecting the 10 hottest news stories for Weibo's trending list or the 10 best-selling products. diff --git a/en/docs/chapter_heap/index.md b/en/docs/chapter_heap/index.md index 9dfaf0525..992d52413 100644 --- a/en/docs/chapter_heap/index.md +++ b/en/docs/chapter_heap/index.md @@ -1,9 +1,9 @@ -# Heap +# Heaps -![Heap](../assets/covers/chapter_heap.jpg) +![Heaps](../assets/covers/chapter_heap.jpg) !!! abstract - Heaps are like mountain peaks, rising layer upon layer, each with a distinct shape. - - The peaks rise and fall at varying heights, yet the tallest peak always catches the eye first. + Heaps resemble mountain ranges, rising in layers of varied shapes. + + The peaks stand at different heights, yet the highest always catches the eye first. diff --git a/en/docs/chapter_heap/summary.md b/en/docs/chapter_heap/summary.md index 61e69a239..0c6c8ab69 100644 --- a/en/docs/chapter_heap/summary.md +++ b/en/docs/chapter_heap/summary.md @@ -1,17 +1,17 @@ # Summary -### Key Review +### Key takeaways -- A heap is a complete binary tree. Depending on the property it satisfies, it can be classified as either a max heap or a min heap. The top element of a max heap (min heap) is the largest (smallest) element. -- A priority queue is a queue in which elements are dequeued according to priority, and it is typically implemented using a heap. -- Common heap operations and their corresponding time complexities include inserting an element $O(\log n)$, removing the top element $O(\log n)$, and accessing the top element $O(1)$. -- Complete binary trees are well-suited for array representation, so we typically use arrays to store heaps. -- Heapify operations are used to maintain the heap property and are employed in both element insertion and removal operations. -- Building a heap from $n$ input elements can be optimized to $O(n)$, which is highly efficient. -- Top-k is a classic algorithmic problem that can be solved efficiently using a heap, with a time complexity of $O(n \log k)$. +- A heap is a complete binary tree. Depending on its ordering condition, it is either a max heap or a min heap. The root holds the largest or smallest element, respectively. +- A priority queue removes elements according to their priority and is usually implemented with a heap. +- Common heap operations and their time complexities include insertion in $O(\log n)$, removing the root in $O(\log n)$, and accessing the root in $O(1)$. +- Complete binary trees are well suited to array representation, so heaps are usually stored in arrays. +- Sifting restores the heap property and is used during both insertion and removal. +- Building a heap from $n$ input elements can be optimized to $O(n)$ time. +- Top-k is a classic algorithm problem that a heap can solve efficiently in $O(n \log k)$ time. ### Q & A -**Q**: Does the term "heap" in data structures mean the same thing as "heap" in memory management? +**Q**: Is the heap data structure the same concept as the heap in memory management? -They are not the same concept; they simply share the same name. In computer systems, the heap is part of dynamic memory allocation, and programs can use it to store data at runtime. A program can request a certain amount of heap memory to store complex structures such as objects and arrays. When the data is no longer needed, the program must release that memory to prevent memory leaks. Compared with stack memory, heap memory requires more careful management and use; improper handling can lead to problems such as memory leaks and dangling pointers. +No; they simply share a name. In computer memory, the heap is used for dynamic memory allocation, allowing a running program to store data. A program can request heap memory for structures such as objects and arrays. When that data is no longer needed, the memory must be released to prevent leaks. Compared with stack memory, heap memory requires more careful management; misuse can lead to memory leaks, dangling pointers, and other problems. diff --git a/en/docs/chapter_heap/top_k.md b/en/docs/chapter_heap/top_k.md index 08ace4d22..cc7713455 100644 --- a/en/docs/chapter_heap/top_k.md +++ b/en/docs/chapter_heap/top_k.md @@ -1,42 +1,42 @@ -# Top-k Problem +# The top-k problem !!! question - Given an unordered array `nums` of length $n$, return the largest $k$ elements in the array. + Given an unsorted array `nums` of length $n$, return its largest $k$ elements. -For this problem, we will first introduce two relatively straightforward solutions, followed by a more efficient heap-based solution. +We first introduce two straightforward solutions, then a more efficient solution using a heap. -## Method 1: Iterative Selection +## Method 1: Repeated selection -We can perform $k$ rounds of traversal as shown in the figure below, extracting the $1^{st}$, $2^{nd}$, $\dots$, $k^{th}$ largest elements in each round, with a time complexity of $O(nk)$. +As shown below, scan the array $k$ times, selecting the $1$st, $2$nd, $\dots$, and $k$th largest elements in successive rounds. This takes $O(nk)$ time. -This method is only suitable when $k \ll n$, because when $k$ is close to $n$, the time complexity approaches $O(n^2)$, making it very inefficient. +This method is suitable only when $k \ll n$. As $k$ approaches $n$, its time complexity approaches $O(n^2)$, making it very slow. -![Traversing to find the largest k elements](top_k.assets/top_k_traversal.png) +![Finding the largest k elements by repeated scans](top_k.assets/top_k_traversal.png) !!! tip - When $k = n$, we can obtain a complete sorted sequence, which is equivalent to the "selection sort" algorithm. + When $k = n$, this produces a fully sorted sequence and is equivalent to selection sort. ## Method 2: Sorting -As shown in the figure below, we can first sort the array `nums`, then return the rightmost $k$ elements, with a time complexity of $O(n \log n)$. +As shown below, sort `nums` and return its rightmost $k$ elements. This takes $O(n \log n)$ time. -Clearly, this method does more work than necessary, because we only need to find the largest $k$ elements rather than sort the other elements. +This method does more work than necessary: we need only the largest $k$ elements, not a sorted order for all the others. -![Sorting to find the largest k elements](top_k.assets/top_k_sorting.png) +![Finding the largest k elements by sorting](top_k.assets/top_k_sorting.png) -## Method 3: Heap +## Method 3: A heap -We can solve the Top-k problem more efficiently with a heap, as shown in the figure below. +A heap solves the top-k problem more efficiently, following the steps below. -1. Initialize a min heap, where the heap top element is the smallest. -2. First, insert the first $k$ elements of the array into the heap in sequence. -3. Starting from the $(k + 1)^{th}$ element, if the current element is greater than the heap top element, remove the heap top element and insert the current element into the heap. -4. After traversal is complete, the heap contains the largest $k$ elements. +1. Initialize a min heap, whose root holds its smallest element. +2. Insert the first $k$ array elements into the heap one by one. +3. Starting with element $k + 1$, if the current element is larger than the root, remove the root and insert the current element. +4. After the traversal, the heap contains the largest $k$ elements. === "<1>" - ![Finding the largest k elements using a heap](top_k.assets/top_k_heap_step1.png) + ![Finding the largest k elements with a heap](top_k.assets/top_k_heap_step1.png) === "<2>" ![top_k_heap_step2](top_k.assets/top_k_heap_step2.png) @@ -62,12 +62,12 @@ We can solve the Top-k problem more efficiently with a heap, as shown in the fig === "<9>" ![top_k_heap_step9](top_k.assets/top_k_heap_step9.png) -Example code is as follows: +An implementation follows: ```src [file]{top_k}-[class]{}-[func]{top_k_heap} ``` -A total of $n$ rounds of heap insertions and removals are performed, with the heap's maximum length being $k$, so the time complexity is $O(n \log k)$. This method is very efficient; when $k$ is small, the time complexity approaches $O(n)$; when $k$ is large, the time complexity does not exceed $O(n \log n)$. +There are $n$ rounds of heap insertion and removal to consider, and the heap has at most $k$ elements, so the time complexity is $O(n \log k)$. This is highly efficient: when $k$ is small, the complexity approaches $O(n)$; when $k$ is large, it does not exceed $O(n \log n)$. -Additionally, this method is well suited to dynamic data streams. As new data arrives, we can continuously maintain the elements in the heap, enabling dynamic updates to the largest $k$ elements. +This approach also suits dynamic data streams. As new data arrives, we can maintain the heap to update the largest $k$ elements continuously. diff --git a/en/docs/chapter_hello_algo/index.md b/en/docs/chapter_hello_algo/index.md index e86f62e1a..a3cc77b11 100644 --- a/en/docs/chapter_hello_algo/index.md +++ b/en/docs/chapter_hello_algo/index.md @@ -3,28 +3,28 @@ comments: true icon: material/rocket-launch-outline --- -# Preface +# Foreword -A few years ago, I shared the "Sword for Offer" problem solutions on LeetCode, receiving encouragement and support from many readers. During interactions with readers, the most frequently asked question I encountered was "how to get started with algorithms." Gradually, I developed a keen interest in this question. +A few years ago, I shared a series of solutions to the “Coding Interviews” problems on LeetCode and received encouragement and support from many readers. In our conversations, one question came up more than any other: “How do I get started with algorithms?” Over time, I became deeply interested in this question. -Diving straight into problem-solving seems to be the most popular approach—it's simple, direct, and effective. However, problem-solving is like playing Minesweeper: those with strong self-learning abilities can successfully defuse the mines one by one, while those with insufficient foundations may end up bruised and battered, retreating step by step in frustration. Reading through textbooks is also a common practice, but for job seekers, graduation theses, resume submissions, and preparations for written tests and interviews have already consumed most of their energy, making working through thick books an arduous challenge. +Jumping into practice problems with little preparation seems to be the most popular approach: simple, direct, and effective. Yet solving problems is rather like playing Minesweeper. Strong independent learners can clear the mines one by one, while those with a weak foundation may run into one setback after another and gradually retreat in frustration. Reading a textbook from cover to cover is another common approach. For job seekers, however, theses, applications, written tests, and interview preparation already consume most of their energy, making a thick textbook a formidable challenge. -If you're facing similar struggles, then it's fortunate that this book has "found" you. This book is my answer to this question—even if it may not be the optimal solution, it is at least a positive attempt. While this book alone won't directly land you a job offer, it will guide you through the "landscape" of data structures and algorithms, help you understand the shapes, sizes, and distributions of different "mines," and enable you to master various "mine-clearing methods." With these skills, I believe you can tackle problems and read technical literature more confidently, gradually building a complete knowledge system. +If you face similar difficulties, I am glad this book has found you. It is my answer to that question—perhaps not the optimal solution, but at least a constructive attempt. Although this book alone will not secure a job offer, it will guide you through the “knowledge map” of data structures and algorithms. It will show you the shapes, sizes, and locations of different “mines” and teach you ways to clear them. With these skills, I believe you can approach practice problems and technical reading more confidently and gradually build a complete body of knowledge. -I deeply agree with Professor Feynman's words: "Knowledge isn't free. You have to pay attention." In this sense, this book is not entirely "free." In order to live up to the precious "attention" you invest in this book, I will do my utmost and devote my greatest "attention" to completing this work. +I wholeheartedly agree with Professor Feynman's words: “Knowledge isn't free. You have to pay attention.” In this sense, the book is not entirely “free.” To honor the precious attention you invest in it, I will devote as much of my own attention as I can to writing it. -I'm keenly aware of the limits of my knowledge and experience. Although the content of this book has been refined over a period of time, there are certainly still many errors, and I sincerely welcome critiques and corrections from teachers and fellow students. +I am aware of the limits of my knowledge. Although the content has undergone considerable refinement, many errors surely remain. I sincerely welcome corrections and suggestions from teachers and fellow learners. -![Hello Algorithms](../assets/covers/chapter_hello_algo.jpg){ class="cover-image" } +![Hello Algo](../assets/covers/chapter_hello_algo.jpg){ class="cover-image" }
-

Hello, Algorithms!

+

Hello, algorithms!

-The advent of computers has brought tremendous changes to the world. With their high-speed computing capabilities and excellent programmability, they have become the ideal medium for executing algorithms and processing data. Whether it's the realistic graphics in video games, the intelligent decision-making in autonomous driving, AlphaGo's brilliant Go matches, or ChatGPT's natural interactions, these applications are all striking demonstrations of algorithms at work on computers. +Computers have transformed the world. Their speed and remarkable programmability make them an ideal medium for executing algorithms and processing data. From the lifelike graphics of video games and the intelligent decisions of self-driving cars to AlphaGo's brilliant games and ChatGPT's natural interactions, these applications are all compelling expressions of algorithms running on computers. -In fact, before the advent of computers, algorithms and data structures already existed in every corner of the world. Early algorithms were relatively simple, such as ancient counting methods and tool-making procedures. As civilization progressed, algorithms gradually became more refined and complex. From the ingenious craftsmanship of master artisans, to industrial products that liberate productive forces, to the scientific laws governing the operation of the universe, behind almost every ordinary or astonishing thing lies ingenious algorithmic thinking. +In fact, algorithms and data structures existed throughout the world long before computers. Early algorithms were relatively simple, such as ancient counting methods and steps for making tools. As civilization advanced, algorithms became more refined and complex. From exquisite craftsmanship and industrial products that boosted productivity to the scientific laws governing the universe, ingenious algorithmic ideas lie behind almost everything, whether ordinary or astonishing. -Similarly, data structures are everywhere: from large-scale social networks to small subway systems, many systems can be modeled as "graphs"; from a nation to a family, the primary organizational forms of society exhibit characteristics of "trees"; winter clothing is like a "stack," where the first item put on is the last to be taken off; a badminton tube is like a "queue," with items inserted at one end and retrieved from the other; a dictionary is like a "hash table," enabling quick lookup of target entries. +Data structures are equally widespread. Systems ranging from social networks to subway routes can be modeled as “graphs.” Social organizations, from nations to families, often have the characteristics of “trees.” Winter clothing behaves like a “stack”: what goes on first comes off last. A tube of shuttlecocks is like a “queue,” with items inserted at one end and removed at the other. A dictionary resembles a “hash table,” allowing quick lookup of a desired entry. -This book aims to help readers understand the core concepts of algorithms and data structures through clear and accessible animated illustrations and runnable code examples, and to implement them in code. Building on this foundation, the book endeavors to reveal the vivid manifestations of algorithms in the complex world and showcase the beauty of algorithms. I hope this book can be of help to you! +Through clear animated illustrations and runnable code examples, this book aims to help readers understand the core concepts of algorithms and data structures and implement them through programming. Building on that foundation, it seeks to reveal how algorithms take shape in our complex world and to show their beauty. I hope you find it helpful! diff --git a/en/docs/chapter_introduction/algorithms_are_everywhere.md b/en/docs/chapter_introduction/algorithms_are_everywhere.md index 659d760e6..b048993e1 100644 --- a/en/docs/chapter_introduction/algorithms_are_everywhere.md +++ b/en/docs/chapter_introduction/algorithms_are_everywhere.md @@ -1,56 +1,56 @@ -# Algorithms Are Everywhere +# Algorithms are everywhere -When we hear the term "algorithm," we naturally think of mathematics. However, many algorithms do not involve complex mathematics but rely more on basic logic, which can be seen everywhere in our daily lives. +When we hear the word “algorithm,” we naturally think of mathematics. In fact, many algorithms do not involve complex mathematics. Instead, they rely on basic logic that we encounter throughout everyday life. -Before we formally explore algorithms, here's an interesting fact worth sharing: **you have already learned many algorithms without realizing it, and you are used to applying them in daily life**. Let me give a few specific examples to illustrate this point. +Before we formally explore algorithms, here is an interesting fact: **you have already learned many algorithms without realizing it, and you routinely use them in everyday life**. The following examples illustrate this point. -**Example 1: Looking Up a Dictionary**. In an English dictionary, words are listed alphabetically. Assuming we're searching for a word that starts with the letter $r$, this is typically done in the following way: +**Example 1: Looking up a word in a dictionary**. In a Chinese dictionary, each character has a corresponding pinyin pronunciation, and entries are arranged alphabetically by pinyin. Suppose we want to find a character whose pinyin begins with $r$. We would usually follow the process shown in the figure below. -1. Open the dictionary to about halfway and check the first word on that page; suppose it starts with the letter $m$. -2. Since $r$ comes after $m$ in the alphabet, the first half can be ignored and the search space is narrowed down to the second half. -3. Repeat steps `1.` and `2.` until you find the page where the word starts with $r$. +1. Open the dictionary roughly halfway through and check the initial letter on that page. Suppose it is $m$. +2. Since $r$ comes after $m$ in the pinyin alphabet, rule out the first half of the dictionary and narrow the search to the second half. +3. Repeat steps `1.` and `2.` until you find the pages whose pinyin entries begin with $r$. === "<1>" - ![Process of looking up a dictionary](algorithms_are_everywhere.assets/binary_search_dictionary_step1.png) + ![Steps for looking up a word in a dictionary](algorithms_are_everywhere.assets/binary_search_dictionary_step1.png) === "<2>" - ![Binary search in dictionary step 2](algorithms_are_everywhere.assets/binary_search_dictionary_step2.png) + ![binary_search_dictionary_step2](algorithms_are_everywhere.assets/binary_search_dictionary_step2.png) === "<3>" - ![Binary search in dictionary step 3](algorithms_are_everywhere.assets/binary_search_dictionary_step3.png) + ![binary_search_dictionary_step3](algorithms_are_everywhere.assets/binary_search_dictionary_step3.png) === "<4>" - ![Binary search in dictionary step 4](algorithms_are_everywhere.assets/binary_search_dictionary_step4.png) + ![binary_search_dictionary_step4](algorithms_are_everywhere.assets/binary_search_dictionary_step4.png) === "<5>" - ![Binary search in dictionary step 5](algorithms_are_everywhere.assets/binary_search_dictionary_step5.png) + ![binary_search_dictionary_step5](algorithms_are_everywhere.assets/binary_search_dictionary_step5.png) -Looking up a dictionary, an essential skill for elementary school students is actually the famous "Binary Search" algorithm. From a data structure perspective, we can consider the dictionary as a sorted "array"; from an algorithmic perspective, the series of actions taken to look up a word in the dictionary can be viewed as the algorithm "Binary Search." +This dictionary skill, learned in elementary school, is actually the well-known “binary search” algorithm. From a data structure perspective, we can view the dictionary as a sorted “array”; from an algorithm perspective, the sequence of lookup operations above is “binary search.” -**Example 2: Organizing Playing Cards**. When playing cards, we need to arrange the cards in our hands in ascending order, as shown in the following process. +**Example 2: Arranging playing cards**. When playing cards, we need to arrange our hand from smallest to largest each round. The process is shown in the figure below. -1. Divide the playing cards into "ordered" and "unordered" sections, assuming initially the leftmost card is already in order. -2. Take out a card from the unordered section and insert it into the correct position in the ordered section; after this, the leftmost two cards are in order. -3. Repeat step `2` until all cards are in order. +1. Divide the cards into “sorted” and “unsorted” sections, initially treating the single leftmost card as sorted. +2. Take a card from the unsorted section and insert it into the correct position in the sorted section. The two leftmost cards are now sorted. +3. Repeat step `2.`, inserting one card from the unsorted section into the sorted section each round, until all the cards are sorted. -![Process of sorting a deck of cards](algorithms_are_everywhere.assets/playing_cards_sorting.png) +![Steps for sorting playing cards](algorithms_are_everywhere.assets/playing_cards_sorting.png) -The above method of organizing playing cards is essentially the "Insertion Sort" algorithm, which is very efficient for small datasets. Many programming languages' built-in sorting implementations use insertion sort internally. +This method of arranging playing cards is essentially “insertion sort,” an algorithm that is very efficient for small datasets. Insertion sort appears in the library sorting functions of many programming languages. -**Example 3: Making Change**. Assume making a purchase of $69$ at a supermarket. If you give the cashier $100$, they will need to provide you with $31$ in change. This process can be clearly understood as illustrated in the figure below. +**Example 3: Making change**. Suppose we buy goods costing $69$ yuan at a supermarket and pay the cashier $100$ yuan. The cashier needs to give us $31$ yuan in change and naturally reasons through the process shown in the figure below. -1. The available denominations smaller than $31$ are $1$, $5$, $10$, and $20$. -2. Take out the largest $20$ from the options, leaving $31 - 20 = 11$. -3. Take out the largest $10$ from the remaining options, leaving $11 - 10 = 1$. -4. Take out the largest $1$ from the remaining options, leaving $1 - 1 = 0$. -5. Complete change-making, the solution is $20 + 10 + 1 = 31$. +1. The available denominations smaller than $31$ yuan are $1$, $5$, $10$, and $20$ yuan. +2. Take the largest available denomination, $20$ yuan, leaving $31 - 20 = 11$ yuan. +3. Take the largest denomination that fits the remaining amount, $10$ yuan, leaving $11 - 10 = 1$ yuan. +4. Take the largest denomination that fits the remaining amount, $1$ yuan, leaving $1 - 1 = 0$ yuan. +5. The change is complete: $20 + 10 + 1 = 31$ yuan. -![Process of making change](algorithms_are_everywhere.assets/greedy_change.png) +![The process of making change](algorithms_are_everywhere.assets/greedy_change.png) -In the steps above, we choose what seems to be the best option at each stage by using the largest denomination available, which leads to an effective way to make change. From a data structures and algorithms perspective, this approach is known as a "Greedy" algorithm. +At each step above, we make the choice that looks best at the moment—using the largest possible denomination—and eventually obtain a valid way to make change. In terms of data structures and algorithms, this is a “greedy” algorithm. -From cooking a meal to interstellar travel, almost all problem-solving involves algorithms. The advent of computers allows us to store data structures in memory and write code to call the CPU and GPU to execute algorithms. In this way, we can transfer real-life problems to computers and solve various complex issues in a more efficient way. +From cooking a meal to traveling between stars, solving almost any problem involves algorithms. Computers allow us to store data structures in memory through programming and write code that calls on CPUs and GPUs to execute algorithms. We can therefore bring real-world problems to computers and solve complex problems more efficiently. !!! tip - If concepts such as data structures, algorithms, arrays, and binary search still feel only half-familiar, keep reading. This book will guide you into the world of data structures and algorithms. + If concepts such as data structures, algorithms, arrays, and binary search still seem unclear, keep reading. This book will guide you into the world of data structures and algorithms. diff --git a/en/docs/chapter_introduction/index.md b/en/docs/chapter_introduction/index.md index a663d0c39..055f40b4f 100644 --- a/en/docs/chapter_introduction/index.md +++ b/en/docs/chapter_introduction/index.md @@ -1,9 +1,9 @@ -# Encounter with Algorithms +# Getting to know algorithms -![Encounter with Algorithms](../assets/covers/chapter_introduction.jpg) +![Getting to know algorithms](../assets/covers/chapter_introduction.jpg) !!! abstract - A young girl dances gracefully, intertwined with data, her skirt flowing with the melody of algorithms. - - She invites you to dance with her. Follow her steps closely and enter the world of algorithms, full of logic and beauty. + A young woman dances gracefully among the data, the melody of algorithms rippling through her skirt. + + She invites you to join her. Follow her steps into a world of algorithms filled with logic and beauty. diff --git a/en/docs/chapter_introduction/summary.md b/en/docs/chapter_introduction/summary.md index 8cdc2efbc..5a5b099b0 100644 --- a/en/docs/chapter_introduction/summary.md +++ b/en/docs/chapter_introduction/summary.md @@ -1,32 +1,32 @@ # Summary -### Key Review +### Key takeaways -- Algorithms are ubiquitous in daily life and are not some distant, esoteric body of knowledge. In fact, we have already learned many algorithms unconsciously and use them to solve problems big and small in life. -- The principle of looking up a dictionary is consistent with the binary search algorithm. Binary search embodies the important algorithmic idea of divide and conquer. -- The process of organizing playing cards is very similar to the insertion sort algorithm. Insertion sort is suitable for sorting small datasets. -- The steps of making change are essentially a greedy algorithm, where the best choice is made at each step based on the current situation. -- An algorithm is a set of instructions or operational steps that solves a specific problem within a finite amount of time, while a data structure is a way of organizing and storing data in a computer. -- Data structures and algorithms are closely connected. Data structures are the foundation of algorithms, and algorithms breathe life into data structures. -- We can compare data structures and algorithms to assembling building blocks. The blocks represent data, the way they are shaped and connected represents the data structure, and the steps used to assemble them correspond to the algorithm. +- Algorithms are everywhere in daily life; they are not remote, inaccessible knowledge. In fact, we have already learned many algorithms without realizing it and use them to solve problems large and small. +- Looking up a word in a dictionary follows the same principle as binary search. Binary search illustrates the important algorithmic idea of divide and conquer. +- Arranging playing cards closely resembles insertion sort. Insertion sort is well suited to small datasets. +- Making change is essentially a greedy algorithm: at each step, make the choice that looks best at the moment. +- An algorithm is a set of instructions or steps that solves a specific problem within a finite time. A data structure is a way of organizing and storing data in a computer. +- Data structures and algorithms are closely connected. Data structures provide the foundation for algorithms, while algorithms bring data structures to life. +- We can compare data structures and algorithms to assembling building blocks: the blocks represent data; their shapes and connections represent data structures; and the assembly steps represent algorithms. ### Q & A -**Q**: As a programmer, I have never used algorithms to solve problems in my daily work. Common algorithms are already encapsulated by programming languages and can be used directly. Does this mean that the problems in our work have not yet reached the level where algorithms are needed? +**Q**: As a programmer, I have never used algorithms to solve problems in my daily work. Common algorithms are already built into programming languages, so I can simply use them. Does this mean the problems we encounter at work are not difficult enough to require algorithms? -If we compare specific work skills to "techniques" in martial arts, then fundamental subjects should be more like "internal skills". +If specific job skills are like the techniques of a martial art, foundational subjects are more like the underlying strength that makes those techniques effective. -I believe the significance of learning algorithms (and other fundamental subjects) is not that you will need to implement them from scratch at work, but that the knowledge you gain enables you to make sound professional judgments when solving problems, thereby improving the overall quality of your work. Here is a simple example. Every programming language has a built-in sorting function: +I believe the purpose of learning algorithms—and other foundational subjects—is not to implement them from scratch at work. It is to use what we have learned to respond and make judgments professionally when solving problems, improving the overall quality of our work. For a simple example, every programming language has a built-in sorting function: -- If we have not studied data structures and algorithms, we might simply feed any given data to this sorting function. It runs smoothly with good performance, and there doesn't seem to be any problem. -- But if we have studied algorithms, we would know that the time complexity of the built-in sorting function is $O(n \log n)$. However, if the given data consists of integers with a fixed number of digits (such as student IDs), we can use the more efficient "radix sort", reducing the time complexity to $O(nk)$, where $k$ is the number of digits. When the data volume is very large, the saved running time can create significant value (reduced costs, improved experience, etc.). +- Without studying data structures and algorithms, we might pass any dataset to this sorting function. It runs smoothly and performs reasonably well, so nothing seems wrong. +- After studying algorithms, however, we know that the built-in sorting function has a time complexity of $O(n \log n)$. If the data consists of integers with a fixed number of digits, such as student IDs, we can use the more efficient “radix sort” to reduce the time complexity to $O(nk)$, where $k$ is the number of digits. For large datasets, the time saved can create considerable value through lower costs, a better user experience, and so on. -In engineering, many problems are difficult to solve optimally, and many others are only solved "well enough." The difficulty of a problem depends, on the one hand, on the nature of the problem itself and, on the other hand, on the knowledge of the person examining it. The more complete a person's knowledge and the more experience they have, the deeper their analysis will be, and the more elegantly the problem can be solved. +In engineering, optimal solutions are difficult to reach for many problems, and many others are solved only “well enough.” How difficult a problem appears depends both on its nature and on the knowledge of the person examining it. The more knowledge and experience we have, the more deeply we can analyze a problem and the more elegantly we can solve it. -**Q**: Now that we let AI agents write almost all our code, do we still need to learn algorithms? +**Q**: Now that AI agents write almost all our code, do we still need to learn algorithms? -To quote Mu Li: “An agent is an amplifier of ability—it multiplies your ability by 10, rather than adding 10 to it.” +To quote Mu Li: “An agent is an ability multiplier: it multiplies your ability by 10. It is not an adder that adds 10 to your ability.” -Since AI amplifies our abilities, the abilities being multiplied matter too. I think two abilities are especially worth developing. The first is communication, particularly the ability to work with AI: making goals clear, providing the necessary context, and correcting course in time. Explaining a problem to AI also helps us think it through ourselves. The second is foundational knowledge. AI can fill in the details, but we need to understand the basic concepts ourselves to ask the right questions, judge whether answers are correct, and turn its output into useful results. +If AI multiplies our ability, then that starting ability matters too. I think two kinds of ability are especially worth developing. The first is communication, particularly collaboration with AI: state the goal clearly, supply the necessary context, and correct the course promptly. Explaining a problem to AI also helps us clarify it for ourselves. The second is foundational knowledge. AI can fill in the details, but we need to understand the basic concepts ourselves to ask relevant questions, judge what is correct, and turn its output into useful results. -One distinctive thing about AI is that, unlike earlier tools, it does not have clearly defined functional boundaries. It is a little like water, taking different shapes in different people's hands. The greater a person's abilities and the better their taste, the more pronounced AI's amplifying effect becomes. +One distinctive feature of AI is that, unlike earlier tools, it has no clear boundaries to its functionality. It is a little like water, taking different shapes in different people's hands. The stronger a person's abilities and the better their taste, the more pronounced AI's multiplying effect becomes. diff --git a/en/docs/chapter_introduction/what_is_dsa.md b/en/docs/chapter_introduction/what_is_dsa.md index dbd43ab51..19ea3a47e 100644 --- a/en/docs/chapter_introduction/what_is_dsa.md +++ b/en/docs/chapter_introduction/what_is_dsa.md @@ -1,53 +1,53 @@ -# What Is an Algorithm +# What is an algorithm? -## Algorithm Definition +## Definition of an algorithm -An algorithm is a set of instructions or operational steps that solves a specific problem within a finite amount of time. It has the following characteristics. +An algorithm is a set of instructions or steps that solves a specific problem within a finite time. It has the following properties. -- The problem is well-defined, with clear input and output definitions. -- It is feasible and can be completed with finite steps, time, and memory. -- Each step has a definite meaning, and under the same input and operating conditions, the output is always the same. +- The problem is well defined, with clear definitions of its inputs and outputs. +- The algorithm is feasible: it can finish within a finite number of steps, using finite time and memory. +- Every step has a definite meaning. Given the same inputs and execution conditions, the output is always the same. -## Data Structure Definition +## Definition of a data structure -A data structure is a way of organizing and storing data, including the data itself, the relationships between data elements, and the methods used to operate on them. It has the following design objectives. +A data structure is a way of organizing and storing data. It encompasses the data itself, the relationships between data items, and the methods used to operate on them. Its design aims to achieve the following goals. -- Occupy as little space as possible to save computer memory. -- Data operations should be as fast as possible, covering data access, addition, deletion, update, etc. -- Provide a concise data representation and logical information so that algorithms can run efficiently. +- Use as little space as possible to conserve computer memory. +- Make data operations as fast as possible, including access, insertion, deletion, and updating. +- Provide a concise representation of data and its logical relationships so that algorithms can run efficiently. -**Data structure design is a process full of trade-offs**. If we want to achieve improvements in one aspect, we often need to make compromises in another aspect. Here are two examples. +**Designing a data structure involves tradeoffs**. Improving one aspect often requires compromising another. Here are two examples. -- Compared to arrays, linked lists are more convenient for data addition and deletion operations but sacrifice data access speed. -- Compared to linked lists, graphs provide richer logical information but require larger memory space. +- Compared with arrays, linked lists make insertion and deletion easier, at the expense of data access speed. +- Compared with linked lists, graphs provide richer information about logical relationships but require more memory. -## The Relationship Between Data Structures and Algorithms +## The relationship between data structures and algorithms -As shown in the figure below, data structures and algorithms are highly related and tightly coupled, specifically manifested in the following three aspects. +As the figure below shows, data structures and algorithms are closely intertwined in three ways. -- Data structures are the foundation of algorithms. Data structures provide algorithms with structured storage of data and methods for operating on data. -- Algorithms breathe life into data structures. Data structures themselves only store data information; combined with algorithms, they can solve specific problems. -- Algorithms can usually be implemented based on different data structures, but execution efficiency may vary greatly. Choosing the appropriate data structure is key. +- Data structures are the foundation of algorithms. They provide algorithms with data in a structured form and methods for operating on that data. +- Algorithms bring data structures to life. A data structure by itself only stores information; it must be combined with an algorithm to solve a specific problem. +- An algorithm can often be implemented using different data structures, but its efficiency may vary greatly. Choosing the right data structure is crucial. ![The relationship between data structures and algorithms](what_is_dsa.assets/relationship_between_data_structure_and_algorithm.png) -Data structures and algorithms are like assembling building blocks as shown in the figure below. A set of building blocks, in addition to containing many parts, also comes with detailed assembly instructions. By following the instructions step by step, we can assemble an exquisite building block model. +Data structures and algorithms are like assembling the building blocks shown in the figure below. A set of building blocks includes not only many pieces but also detailed assembly instructions. By following those instructions step by step, we can build an intricate model. -![Assembling blocks](what_is_dsa.assets/assembling_blocks.png) +![Assembling building blocks](what_is_dsa.assets/assembling_blocks.png) -The detailed correspondence between the two is shown in the table below. +The table below describes this analogy in detail. -

Table   Comparing data structures and algorithms to assembling building blocks

+

Table   Comparing data structures and algorithms to building blocks

-| Data structures and algorithms | Assembling building blocks | -| ------------------------------ | ------------------------------------------------------------------ | -| Input data | Unassembled building blocks | -| Data structure | Organization form of building blocks, including shape, size, connection method, etc. | -| Algorithm | A series of operational steps to assemble the blocks into the target form | -| Output data | Building block model | +| Data structures and algorithms | Building blocks | +| -------------- | ---------------------------------------- | +| Input data | Unassembled blocks | +| Data structure | How the blocks are organized, including their shapes, sizes, and connections | +| Algorithm | A sequence of steps for assembling the blocks into the desired form | +| Output data | The finished model | -It is worth noting that data structures and algorithms are independent of programming languages. That is why this book can provide implementations in multiple programming languages. +Data structures and algorithms are independent of programming languages. This is why the book can provide implementations in multiple languages. -!!! tip "Conventional abbreviation" +!!! tip "A common shorthand" - In actual discussions, we usually abbreviate "data structures and algorithms" as "algorithms". For example, the well-known LeetCode algorithm problems actually examine knowledge of both data structures and algorithms. + In everyday discussions, we often shorten “data structures and algorithms” to “algorithms.” For example, the familiar algorithm problems on LeetCode actually test knowledge of both data structures and algorithms. diff --git a/en/docs/chapter_paperbook/index.assets/book_jd_link.jpg b/en/docs/chapter_paperbook/index.assets/book_jd_link.jpg new file mode 100644 index 000000000..3b8d8a58d Binary files /dev/null and b/en/docs/chapter_paperbook/index.assets/book_jd_link.jpg differ diff --git a/en/docs/chapter_paperbook/index.assets/paper_book_avl_tree.jpg b/en/docs/chapter_paperbook/index.assets/paper_book_avl_tree.jpg new file mode 100644 index 000000000..c559a76be Binary files /dev/null and b/en/docs/chapter_paperbook/index.assets/paper_book_avl_tree.jpg differ diff --git a/en/docs/chapter_paperbook/index.assets/paper_book_chapter_heap.jpg b/en/docs/chapter_paperbook/index.assets/paper_book_chapter_heap.jpg new file mode 100644 index 000000000..9789e45b1 Binary files /dev/null and b/en/docs/chapter_paperbook/index.assets/paper_book_chapter_heap.jpg differ diff --git a/en/docs/chapter_paperbook/index.assets/paper_book_overview.jpg b/en/docs/chapter_paperbook/index.assets/paper_book_overview.jpg new file mode 100644 index 000000000..400e37f55 Binary files /dev/null and b/en/docs/chapter_paperbook/index.assets/paper_book_overview.jpg differ diff --git a/en/docs/chapter_paperbook/index.md b/en/docs/chapter_paperbook/index.md new file mode 100644 index 000000000..884b9c31a --- /dev/null +++ b/en/docs/chapter_paperbook/index.md @@ -0,0 +1,68 @@ +--- +comments: true +icon: material/book-open-page-variant +status: new +--- + +# Print edition (Chinese) + +After a long period of refinement, the Simplified Chinese print edition of *Hello Algo* is finally here! A line of poetry captures how I feel: + +

Chase the wind and moon without pause; beyond the open plains lie the mountains of spring.

+ +![](index.assets/paper_book_overview.jpg){ class="animation-figure" } + +The following video introduces the print edition and shares some of my thoughts on: + +- The importance of learning data structures and algorithms. +- Why I chose Python for the print edition. +- What sharing knowledge means to me. + +> I am new to making videos. Please show your support with a like, a coin, and a favorite—thank you! + +
+ +
+ +Here are some snapshots of the print edition: + +![](index.assets/paper_book_chapter_heap.jpg){ class="animation-figure" } + +![](index.assets/paper_book_avl_tree.jpg){ class="animation-figure" } + +## Strengths and limitations + +Here are a few things about the print edition that may pleasantly surprise you: + +- Full-color printing makes the most of the book's animation-style illustrations. +- Carefully selected paper reproduces colors faithfully while preserving the distinctive feel of a printed book. +- Formatting is more standardized than in the web edition; for example, formulas in figures are italicized. +- A fold-out mind map and bookmarks are included at no extra cost. +- The print, web, and PDF editions have synchronized content, so you can switch freely between them. + +!!! tip + + Synchronizing the print and web editions is difficult, so there may be minor differences. Thank you for your understanding! + +Of course, there are also a few points to consider before buying: + +- The book uses Python, which may not be your main language. You can treat Python as pseudocode and focus on understanding the ideas. +- Full-color printing greatly improves the reading experience for illustrations and code, but costs somewhat more than black-and-white printing. + +!!! tip + + “Print quality” and “price” are like “time efficiency” and “space efficiency” in algorithms: it is difficult to optimize both. In my view, print quality corresponds to time efficiency and deserves greater priority. + +## Where to buy + +If you are interested in the Simplified Chinese print edition, consider getting a copy. We have arranged a 50% discount on the new book. Follow [this link](https://3.cn/1X-qmTD3) or scan the QR code below: + +![](index.assets/book_jd_link.jpg){ class="animation-figure" } + +## Afterword + +At first, I underestimated the work involved in publishing a print edition. I assumed that as long as the open-source project was well maintained, some form of automation could generate the printed book. Experience showed that producing a printed book differs greatly from updating an open-source project, and moving between the two requires considerable extra work. + +There is still a long road from a first draft to a manuscript ready for publication. It takes close collaboration and sustained refinement by the author and the publisher's planning, editorial, design, and marketing teams. My thanks go to Turing's commissioning editor Junhua Wang and everyone at Posts & Telecom Press and Turing Community who took part in publishing this book! + +I hope this book will help you! diff --git a/en/docs/chapter_preface/about_the_book.md b/en/docs/chapter_preface/about_the_book.md index 76520a92c..6fbeb8794 100644 --- a/en/docs/chapter_preface/about_the_book.md +++ b/en/docs/chapter_preface/about_the_book.md @@ -1,54 +1,54 @@ -# About This Book +# About this book -This project aims to create an open-source, free, beginner-friendly introductory tutorial on data structures and algorithms. +This project aims to create an open-source, free, beginner-friendly introduction to data structures and algorithms. -- The entire book uses animated illustrations, with clear and easy-to-understand content and a smooth learning curve, guiding beginners through the landscape of data structures and algorithms. -- The source code can be run with one click, helping readers improve their programming skills through practice and understand how algorithms work and the underlying implementation of data structures. -- We encourage readers to learn from each other, and everyone is welcome to ask questions and share insights in the comments section, making progress together through discussion and exchange. +- Animated illustrations throughout the book make concepts clear and accessible, with a gentle learning curve that guides beginners through the landscape of data structures and algorithms. +- Source code runs with one click, helping readers improve their programming skills through practice and understand how algorithms work and how data structures are implemented. +- We encourage readers to learn together. Ask questions and share insights in the comments, and make progress through discussion. -## Target Audience +## Who this book is for -If you are an algorithm beginner who has never studied algorithms, or if you already have some problem-solving experience but only a hazy understanding of data structures and algorithms, then this book is tailor-made for you! +If you are new to algorithms, have never studied them, or have some experience with practice problems but only a hazy understanding of data structures and algorithms—sometimes feeling you understand, sometimes not—this book is written for you! -If you have already accumulated a certain amount of problem-solving experience and are familiar with most question types, this book can help you review and organize your algorithm knowledge system, and the repository's source code can be used as a "problem-solving toolkit" or "algorithm dictionary." +If you have already solved a substantial number of problems and know most common problem types, this book can help you review and organize your knowledge. The repository's source code can serve as a “problem-solving toolkit” or an “algorithm dictionary.” -If you are an algorithm "expert," we look forward to receiving your valuable suggestions, or [joining us as a contributor](https://www.hello-algo.com/chapter_appendix/contribution/). +If you are an expert in algorithms, we welcome your valuable suggestions and invite you to [contribute to the book](../chapter_appendix/contribution.md). !!! success "Prerequisites" - You need basic programming knowledge in at least one language and the ability to read and write simple code. + You should have basic programming skills in at least one language and be able to read and write simple code. -## Content Structure +## Organization -The main content of this book is shown in the figure below. +The figure below shows the book's main topics. -- **Complexity analysis**: Evaluation dimensions and methods for data structures and algorithms. Methods for calculating time complexity and space complexity, common types, examples, etc. -- **Data structures**: Classification methods for basic data types and data structures. Definitions, advantages and disadvantages, common operations, common types, typical applications, implementation methods, and more for data structures such as arrays, linked lists, stacks, queues, hash tables, trees, heaps, and graphs. -- **Algorithms**: The definition, advantages and disadvantages, efficiency, application scenarios, problem-solving steps, and example problems of algorithms such as searching, sorting, divide and conquer, backtracking, dynamic programming, and greedy algorithms. +- **Complexity analysis**: Criteria and methods for evaluating data structures and algorithms, including how to derive time and space complexity, common complexity classes, and examples. +- **Data structures**: Basic data types and ways to classify data structures; definitions, strengths, weaknesses, common operations, variants, typical applications, and implementations of arrays, linked lists, stacks, queues, hash tables, trees, heaps, graphs, and more. +- **Algorithms**: Definitions, strengths, weaknesses, efficiency, applications, solution steps, and example problems for searching, sorting, divide and conquer, backtracking, dynamic programming, greedy algorithms, and more. -![Main content of this book](about_the_book.assets/hello_algo_mindmap.png) +![The book's main topics](about_the_book.assets/hello_algo_mindmap.png) -## Acknowledgements +## Acknowledgments -This book has been continuously improved through the joint efforts of many contributors in the open-source community. Thanks to every contributor who invested time and effort, they are (in the order automatically generated by GitHub): krahets, coderonion, Gonglja, nuomi1, Reanon, justin-tse, hpstory, danielsss, curtishd, night-cruise, S-N-O-R-L-A-X, rongyi, msk397, gvenusleo, khoaxuantu, rivertwilight, K3v123, gyt95, zhuoqinyue, yuelinxin, Zuoxun, mingXta, Phoenix0415, FangYuan33, GN-Yu, longsizhuo, pengchzn, QiLOL, Cathay-Chen, guowei-gong, xBLACKICEx, IsChristina, JoseHung, qualifier1024, hello-ikun, magentaqin, Guanngxu, thomasq0, sunshinesDL, L-Super, Transmigration-zhou, WSL0809, Slone123c, lhxsm, yuan0221, what-is-me, theNefelibatas, Shyam-Chen, sangxiaai, longranger2, codeberg-user, xiongsp, JeffersonHuang, prinpal, seven1240, Wonderdch, malone6, xiaomiusa87, gaofer, bluebean-cloud, a16su, SamJin98, hongyun-robot, nanlei, XiaChuerwu, yd-j, iron-irax, mgisr, steventimes, junminhong, heshuyue, danny900714, Nigh, Dr-XYZ, MolDuM, XC-Zero, reeswell, PXG-XPG, NI-SW, Horbin-Magician, Enlightenus, YangXuanyi, xjr7670, beatrix-chan, DullSword, qq909244296, iStig, boloboloda, hts0000, gledfish, fbigm, echo1937, jiaxianhua, wenjianmin, keshida, kilikilikid, lclc6, lwbaptx, linyejoe2, liuxjerry, szu17dmy, dshlstarr, Yucao-cy, coderlef, czruby, bongbongbakudan, beintentional, ZongYangL, ZhongYuuu, ZhongGuanbin, hezhizhen, linzeyan, ZJKung, JTCPOWI, KawaiiAsh, luluxia, xb534, ztkuaikuai, yw-1021, ElaBosak233, baagod, zhouLion, yishangzhang, yi427, yanedie, yabo083, weibk, wangwang105, th1nk3r-ing, tao363, 4yDX3906, syd168, sslmj2020, smilelsb, siqyka, selear, sdshaoda, Xi-Row, popozhu, nuquist19, noobcodemaker, XiaoK29, chadyi, lyl625760, lucaswangdev, llql1211, 0130w, shanghai-Jerry, EJackYang, Javesun99, eltociear, lipusheng, KNChiu, BlindTerran, ShiMaRing, lovelock, FreddieLi, FloranceYeh, fanchenggang, gltianwen, goerll, nedchu, curly210102, CuB3y0nd, KraHsu, CarrotDLaw, youshaoXG, bubble9um, Asashishi, Asa0oo0o0o, fanenr, eagleanurag, akshiterate, 52coder, foursevenlove, KorsChen, hopkings2008, yang-le, realwujing, Evilrabbit520, Umer-Jahangir, Turing-1024-Lee, Suremotoo, paoxiaomooo, Chieko-Seren, Senrian, Allen-Scai, 19santosh99, ymmmas, Risuntsy, Richard-Zhang1019, RafaelCaso, qingpeng9802, primexiao, Urbaner3, codetypess, nidhoggfgg, MwumLi, CreatorMetaSky, martinx, ZnYang2018, hugtyftg, logan-qiu, psychelzh, Kunchen-Luo, Keynman, and KeiichiKasai. +This book continues to improve through the collective efforts of many contributors in the open-source community. Thank you to everyone who has invested time and energy in writing it. In the order automatically generated by GitHub, they are: krahets, coderonion, Gonglja, nuomi1, Reanon, justin-tse, hpstory, danielsss, curtishd, night-cruise, S-N-O-R-L-A-X, rongyi, msk397, gvenusleo, khoaxuantu, rivertwilight, K3v123, gyt95, zhuoqinyue, yuelinxin, Zuoxun, mingXta, Phoenix0415, FangYuan33, GN-Yu, longsizhuo, pengchzn, QiLOL, Cathay-Chen, guowei-gong, xBLACKICEx, IsChristina, JoseHung, qualifier1024, hello-ikun, magentaqin, Guanngxu, thomasq0, sunshinesDL, L-Super, Transmigration-zhou, WSL0809, Slone123c, lhxsm, yuan0221, what-is-me, theNefelibatas, Shyam-Chen, sangxiaai, longranger2, codeberg-user, xiongsp, JeffersonHuang, prinpal, seven1240, Wonderdch, malone6, xiaomiusa87, gaofer, bluebean-cloud, a16su, SamJin98, hongyun-robot, nanlei, XiaChuerwu, yd-j, iron-irax, mgisr, steventimes, junminhong, heshuyue, danny900714, Nigh, Dr-XYZ, MolDuM, XC-Zero, reeswell, PXG-XPG, NI-SW, Horbin-Magician, Enlightenus, YangXuanyi, xjr7670, beatrix-chan, DullSword, qq909244296, iStig, boloboloda, hts0000, gledfish, fbigm, echo1937, jiaxianhua, wenjianmin, keshida, kilikilikid, lclc6, lwbaptx, linyejoe2, liuxjerry, szu17dmy, dshlstarr, Yucao-cy, coderlef, czruby, bongbongbakudan, beintentional, ZongYangL, ZhongYuuu, ZhongGuanbin, hezhizhen, linzeyan, ZJKung, JTCPOWI, KawaiiAsh, luluxia, xb534, ztkuaikuai, yw-1021, ElaBosak233, baagod, zhouLion, yishangzhang, yi427, yanedie, yabo083, weibk, wangwang105, th1nk3r-ing, tao363, 4yDX3906, syd168, sslmj2020, smilelsb, siqyka, selear, sdshaoda, Xi-Row, popozhu, nuquist19, noobcodemaker, XiaoK29, chadyi, lyl625760, lucaswangdev, llql1211, 0130w, shanghai-Jerry, EJackYang, Javesun99, eltociear, lipusheng, KNChiu, BlindTerran, ShiMaRing, lovelock, FreddieLi, FloranceYeh, fanchenggang, gltianwen, goerll, nedchu, curly210102, CuB3y0nd, KraHsu, CarrotDLaw, youshaoXG, bubble9um, Asashishi, Asa0oo0o0o, fanenr, eagleanurag, akshiterate, 52coder, foursevenlove, KorsChen, hopkings2008, yang-le, realwujing, Evilrabbit520, Umer-Jahangir, Turing-1024-Lee, Suremotoo, paoxiaomooo, Chieko-Seren, Senrian, Allen-Scai, 19santosh99, ymmmas, Risuntsy, Richard-Zhang1019, RafaelCaso, qingpeng9802, primexiao, Urbaner3, codetypess, nidhoggfgg, MwumLi, CreatorMetaSky, martinx, ZnYang2018, hugtyftg, logan-qiu, psychelzh, Kunchen-Luo, Keynman and KeiichiKasai. -The code review work for this book was completed by coderonion, curtishd, Gonglja, gvenusleo, hpstory, justin-tse, khoaxuantu, krahets, night-cruise, nuomi1, Reanon and rongyi (in alphabetical order). Thanks to them for the time and effort they put in; they helped keep the code consistent and standardized across the different language versions. +The code was reviewed by coderonion, curtishd, Gonglja, gvenusleo, hpstory, justin-tse, khoaxuantu, krahets, night-cruise, nuomi1, Reanon, and rongyi, listed alphabetically. Thank you for the time and effort you devoted to ensuring consistent, well-written code across languages. -The English version of this book was reviewed by yuelinxin, K3v123, magentaqin, QiLOL, Phoenix0415, SamJin98, yanedie, RafaelCaso, pengchzn and thomasq0; the Japanese version was reviewed by eltociear; the Russian version was reviewed by И. А. Шевкун and Yuyan Huang; and the Traditional Chinese version was reviewed by Shyam-Chen and Dr-XYZ. Thanks to their contributions, this book is able to serve a broader readership, and we are deeply grateful to them. +The English edition was reviewed by yuelinxin, K3v123, magentaqin, QiLOL, Phoenix0415, SamJin98, yanedie, RafaelCaso, pengchzn, and thomasq0; the Japanese edition by eltociear; the Russian edition by И. А. Шевкун and Yuyan Huang; and the Traditional Chinese edition by Shyam-Chen and Dr-XYZ. Their contributions have made the book accessible to a wider readership. Thank you all. -The ePub ebook generation tool for this book was developed by zhongfq. We thank him for his contribution, which provides readers with a more flexible way to read. +The tool for generating the book's ePub edition was developed by zhongfq. Thank you for giving readers a more flexible way to read. -During the creation of this book, I received help from many people. +Many people helped me while I was writing this book. -- Thanks to my mentor at the company, Dr. Li Xi, who encouraged me to "take action quickly" during a conversation, strengthening my determination to write this book; -- Thanks to my girlfriend Bubble, the first reader of this book, who provided many valuable suggestions from the perspective of an algorithm beginner, making this book more approachable for beginners; -- Thanks to Tengbao, Qibao, and Feibao for coming up with a creative name for this book, evoking everyone's fond memories of writing their first line of code "Hello World!"; -- Thanks to Xiaoquan for providing professional help in intellectual property rights, which played an important role in the improvement of this open-source book; -- Thanks to Sutong for designing the beautiful cover and logo for this book, and for patiently revising them many times at my perfectionist insistence; -- Thanks to @squidfunk for the typesetting suggestions, as well as for developing the open-source documentation theme [Material-for-MkDocs](https://github.com/squidfunk/mkdocs-material). +- Thank you to my mentor at work, Dr. Xi Li. During a long conversation, your encouragement to “get started” strengthened my resolve to write this book; +- Thank you to my girlfriend Paopao, the book's first reader. Your many suggestions from a beginner's perspective have made it more accessible to newcomers; +- Thank you to Tengbao, Qibao, and Feibao for giving the book a creative name that brings back fond memories of writing our first line of code, “Hello World!”; +- Thank you to Xiaoquan for your professional help with intellectual property, which played an important role in improving this open-source book; +- Thank you to Sutong for designing the beautiful cover and logo, and for patiently revising them many times to satisfy my perfectionism; +- Thank you to @squidfunk for your typography advice and for developing the open-source documentation theme [Material-for-MkDocs](https://github.com/squidfunk/mkdocs-material). -During the writing process, I read many textbooks and articles on data structures and algorithms. These works served as excellent models for this book and helped ensure the accuracy and quality of its content. I would like to thank all the teachers and predecessors for their outstanding contributions! +While writing, I read many textbooks and articles on data structures and algorithms. These works provided excellent models and helped ensure the accuracy and quality of this book. My thanks go to all the teachers and predecessors whose outstanding contributions made this possible! -This book advocates a hands-on approach to learning, and in this respect I was deeply inspired by [Dive into Deep Learning](https://github.com/d2l-ai/d2l-zh). I highly recommend this excellent work to all readers. +The book advocates learning through both thought and hands-on practice, an approach deeply inspired by [*Dive into Deep Learning*](https://github.com/d2l-ai/d2l-zh). I warmly recommend this excellent book to every reader. -**Heartfelt thanks to my parents. It is your support and encouragement that gave me the opportunity to pursue this enjoyable project**. +**My heartfelt thanks to my parents. Your constant support and encouragement have given me the opportunity to do this wonderfully engaging work**. diff --git a/en/docs/chapter_preface/index.md b/en/docs/chapter_preface/index.md index 971809b55..eb43b3309 100644 --- a/en/docs/chapter_preface/index.md +++ b/en/docs/chapter_preface/index.md @@ -4,6 +4,6 @@ !!! abstract - Algorithms are like a beautiful symphony, each line of code flows like a melody. - - May this book gently resonate in your mind, leaving a unique and profound melody. + Algorithms are like a beautiful symphony, with each line of code flowing to its rhythm. + + May this book play softly in your mind, leaving a melody that is both distinctive and lasting. diff --git a/en/docs/chapter_preface/suggestions.assets/code_md_to_repo.png b/en/docs/chapter_preface/suggestions.assets/code_md_to_repo.png index 6495e053a..e099b1529 100644 Binary files a/en/docs/chapter_preface/suggestions.assets/code_md_to_repo.png and b/en/docs/chapter_preface/suggestions.assets/code_md_to_repo.png differ diff --git a/en/docs/chapter_preface/suggestions.assets/download_code.png b/en/docs/chapter_preface/suggestions.assets/download_code.png index b37e0d67a..90c8f2613 100644 Binary files a/en/docs/chapter_preface/suggestions.assets/download_code.png and b/en/docs/chapter_preface/suggestions.assets/download_code.png differ diff --git a/en/docs/chapter_preface/suggestions.assets/pythontutor_example.png b/en/docs/chapter_preface/suggestions.assets/pythontutor_example.png index c76de89f0..9686ef994 100644 Binary files a/en/docs/chapter_preface/suggestions.assets/pythontutor_example.png and b/en/docs/chapter_preface/suggestions.assets/pythontutor_example.png differ diff --git a/en/docs/chapter_preface/suggestions.md b/en/docs/chapter_preface/suggestions.md index 10e9d82b8..4c843dbc1 100644 --- a/en/docs/chapter_preface/suggestions.md +++ b/en/docs/chapter_preface/suggestions.md @@ -1,27 +1,27 @@ -# How to Use This Book +# How to use this book !!! tip - For the best reading experience, it is recommended that you read through this section. + For the best reading experience, we recommend reading this section in full. -## Writing Style Conventions +## Writing conventions -- Sections marked with `*` after the title are optional and somewhat more challenging. If you're short on time, you can skip them on your first pass. -- Technical terms are shown in bold (in the print and PDF editions) or underlined (in the web edition), such as array. They are worth remembering, as they will help when reading technical literature. -- Key content and summary statements will be **bolded**, and such text deserves special attention. -- Words and phrases with specific meanings will be marked with "quotation marks" to avoid ambiguity. -- When terminology differs across programming languages, this book follows Python conventions; for example, it uses `None` to represent "null". -- This book partially relaxes conventional programming-language comment styles in favor of a more compact layout. Comments are mainly divided into three types: title comments, content comments, and multi-line comments. +- Sections with `*` after the title are optional and cover more difficult material. If your time is limited, you can skip them at first. +- Technical terms appear in bold in the print and PDF editions or are underlined in the web edition, such as array. Remembering these terms will help you read technical literature. +- Key points and summary statements appear in **bold** and deserve particular attention. +- Words and phrases used in a specific sense appear in “quotation marks” to avoid ambiguity. +- When terminology differs between programming languages, this book follows Python; for example, it uses `None` to represent a null value. +- For a more compact layout, this book sometimes departs from a language's usual commenting conventions. Comments fall into three main categories: heading comments, explanatory comments, and multiline comments. === "Python" ```python title="" - """Title comment, used to label functions, classes, test cases, etc.""" - - # Content comment, used to explain code in detail - + """Heading comment identifying a function, class, test case, and so on""" + + # Explanatory comment describing the code in detail + """ - Multi-line + Multiline comment """ ``` @@ -29,12 +29,12 @@ === "C++" ```cpp title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -42,12 +42,12 @@ === "Java" ```java title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -55,12 +55,12 @@ === "C#" ```csharp title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -68,12 +68,12 @@ === "Go" ```go title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -81,12 +81,12 @@ === "Swift" ```swift title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -94,12 +94,12 @@ === "JS" ```javascript title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -107,12 +107,12 @@ === "TS" ```typescript title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -120,12 +120,12 @@ === "Dart" ```dart title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -133,23 +133,23 @@ === "Rust" ```rust title="" - /* Title comment, used to label functions, classes, test cases, etc. */ + /* Heading comment identifying a function, class, test case, and so on */ - // Content comment, used to explain code in detail - - // Multi-line + // Explanatory comment describing the code in detail + + // Multiline // comment ``` === "C" ```c title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -157,12 +157,12 @@ === "Kotlin" ```kotlin title="" - /* Title comment, used to label functions, classes, test cases, etc. */ - - // Content comment, used to explain code in detail - + /* Heading comment identifying a function, class, test case, and so on */ + + // Explanatory comment describing the code in detail + /** - * Multi-line + * Multiline * comment */ ``` @@ -170,70 +170,70 @@ === "Ruby" ```ruby title="" - ### Title comment, used to label functions, classes, test cases, etc. ### + ### Heading comment identifying a function, class, test case, and so on ### - # Content comment, used to explain code in detail - - # Multi-line + # Explanatory comment describing the code in detail + + # Multiline # comment ``` -## Learning Efficiently with Animated Illustrations +## Learn efficiently with animated illustrations -Compared with plain text, videos and images have higher information density and a clearer structure, making them easier to understand. In this book, **key concepts and challenging topics are presented mainly through animated illustrations**, with text serving as explanation and supplement. +Compared with text, videos and images convey information in a denser, more structured form that is easier to understand. In this book, **key and difficult concepts are presented mainly through animated illustrations**, with text providing explanations and additional detail. -If, while reading this book, you encounter an animated illustration like the one shown below, **treat the illustration as primary and the text as supplementary**, and use both together to understand the content. +When a passage includes an animated illustration like the one below, **focus on the illustration and use the text to support it**, drawing on both to understand the material. -![Example of animated illustrations](../index.assets/animation.gif) +![An example of an animated illustration](../index.assets/animation.gif) -## Deepening Understanding Through Code Practice +## Deepen your understanding through coding -The accompanying code for this book is hosted in the [GitHub repository](https://github.com/krahets/hello-algo). As shown in the figure below, **the source code comes with test cases and can be run with one click**. +The accompanying code is hosted in the [GitHub repository](https://github.com/krahets/hello-algo). As the figure below shows, **the source code includes test cases and can be run with one click**. -If time permits, **it is recommended that you type out the code yourself**. If you have limited study time, please at least read through and run all the code. +If time permits, **we recommend typing out the code yourself while referring to the examples**. If your study time is limited, at least read and run all the code. -Compared with simply reading code, writing it yourself often brings greater rewards. **Hands-on practice is where real learning happens**. +Writing code often teaches us more than reading it. **Hands-on practice is where real learning happens**. -![Example of running code](../index.assets/running_code.gif) +![An example of running code](../index.assets/running_code.gif) -Getting the code running mainly involves three preliminary steps. +Getting ready to run the code involves three main steps. -**Step 1: Install the local programming environment**. Please follow the [tutorial](https://www.hello-algo.com/chapter_appendix/installation/) in the appendix. If it is already installed, you can skip this step. +**Step 1: Set up a local programming environment**. Follow the [tutorial](../chapter_appendix/installation.md) in the appendix. If your environment is already set up, skip this step. -**Step 2: Clone or download the code repository**. Visit the [GitHub repository](https://github.com/krahets/hello-algo). If you have already installed [Git](https://git-scm.com/downloads), you can clone this repository with the following command: +**Step 2: Clone or download the repository**. Visit the [GitHub repository](https://github.com/krahets/hello-algo). If you have [Git](https://git-scm.com/downloads) installed, clone the repository with the following command: ```shell git clone https://github.com/krahets/hello-algo.git ``` -Alternatively, you can click the "Download ZIP" button shown below to download a ZIP archive of the repository directly and then extract it locally. +Alternatively, click “Download ZIP” in the location shown below to download the code as an archive, then extract it on your computer. -![Clone repository and download code](suggestions.assets/download_code.png) +![Cloning the repository and downloading the code](suggestions.assets/download_code.png) -**Step 3: Run the source code**. As shown in the figure below, for code blocks with file names at the top, we can find the corresponding source code files in the `codes` folder of the repository. The source code files can be run with one click, which will help you save unnecessary debugging time and allow you to focus on learning content. +**Step 3: Run the source code**. As shown below, when a code block has a filename at the top, you can find the corresponding source file in the repository's `en/codes` folder. These files can be run with one click, saving unnecessary debugging time so you can focus on learning. -![Code blocks and corresponding source code files](suggestions.assets/code_md_to_repo.png) +![A code block labeled with its source filename](suggestions.assets/code_md_to_repo.png) -In addition to running code locally, **the web version also supports visual execution of Python code** (implemented based on [pythontutor](https://pythontutor.com/)). As shown in the figure below, you can click "Visual Run" below the code block to expand the view and observe the execution process of the algorithm code; you can also click "Full Screen View" for a better viewing experience. +In addition to running code locally, **the web edition supports visual execution of Python code** through [pythontutor](https://pythontutor.com/). Click “Visualize execution” beneath a code block to expand the view and watch the algorithm run. You can also click “View full screen” for a larger display, as shown in the figure below. -![Visual running of Python code](suggestions.assets/pythontutor_example.png) +![Python code visualized in PythonTutor](suggestions.assets/pythontutor_example.png) -## Growing Together Through Questions and Discussions +## Learn together through questions and discussion -When reading this book, please do not skip over points that you still do not fully understand. **Feel free to ask your questions in the comments section**, and my friends and I will do our best to answer them, usually within two days. +As you read, do not simply skip concepts you have not understood. **You are welcome to ask questions in the comments**. My fellow contributors and I will do our best to answer, usually within two days. -As shown in the figure below, the web version has a comments section at the bottom of each chapter. I encourage you to pay close attention to the discussions there. On the one hand, you can learn about the problems that others encounter, thereby filling gaps in your own understanding and prompting deeper thought. On the other hand, I hope you will generously answer other readers' questions, share your insights, and help others improve. +As shown below, every section in the web edition has a comments area at the bottom. I hope you will read it regularly. Other readers' questions can reveal gaps in your own understanding and encourage deeper thought. I also hope you will generously answer their questions, share your insights, and help them make progress. -![Example of comments section](../index.assets/comment.gif) +![An example of the comments area](../index.assets/comment.gif) -## Algorithm Learning Roadmap +## A learning path for algorithms -Overall, we can divide the process of learning data structures and algorithms into three stages. +Broadly speaking, learning data structures and algorithms can be divided into three stages. -1. **Stage 1: Algorithm introduction**. We need to familiarize ourselves with the characteristics and usage of various data structures, and learn the principles, processes, uses, and efficiency of different algorithms. -2. **Stage 2: Practice algorithm problems**. It is recommended to start with popular problems and solve at least 100 of them first, so that you become familiar with mainstream algorithm questions. When you first begin practicing problems, "knowledge forgetting" may feel like a challenge, but rest assured, this is very normal. We can review problems according to the "Ebbinghaus forgetting curve", and after 3-5 rounds of repetition, they usually stick firmly in memory. For recommended problem lists and practice plans, please see this [GitHub repository](https://github.com/krahets/LeetCode-Book). -3. **Stage 3: Building a knowledge system**. In terms of learning, we can read algorithm column articles, problem-solving frameworks, and algorithm textbooks to continuously enrich our knowledge system. In terms of practicing problems, we can try advanced problem-solving strategies, such as categorization by topic, one problem multiple solutions, one solution multiple problems, etc. Related problem-solving insights can be found in various communities. +1. **Stage 1: Learn the fundamentals**. Become familiar with the properties and uses of data structures, and study how different algorithms work, their steps, applications, and efficiency. +2. **Stage 2: Solve practice problems**. We recommend starting with popular problems and solving at least 100 to become familiar with common algorithmic problems. When you first begin, forgetting what you have learned may be a challenge, but this is normal. Review problems according to the “Ebbinghaus forgetting curve”; after 3–5 rounds of repetition, they will usually be firmly in memory. See this [GitHub repository](https://github.com/krahets/LeetCode-Book) for recommended problem lists and practice plans. +3. **Stage 3: Build a systematic understanding**. Read algorithm articles, problem-solving frameworks, and textbooks to keep expanding your knowledge. In your practice, try more advanced strategies: group problems by topic, solve one problem in several ways, or apply one solution to several problems. You can find advice on these strategies in various communities. -As shown in the figure below, the content of this book mainly covers "Stage 1", aiming to help you more efficiently carry out Stage 2 and Stage 3 learning. +As the figure below shows, this book mainly covers “Stage 1,” helping you approach Stages 2 and 3 more efficiently. -![Algorithm learning roadmap](suggestions.assets/learning_route.png) +![A learning path for algorithms](suggestions.assets/learning_route.png) diff --git a/en/docs/chapter_preface/summary.md b/en/docs/chapter_preface/summary.md index f3f756955..d2192267a 100644 --- a/en/docs/chapter_preface/summary.md +++ b/en/docs/chapter_preface/summary.md @@ -1,10 +1,10 @@ # Summary -### Key Review +### Key takeaways -- The main audience of this book is algorithm beginners. If you already have some background, this book can help you systematically review algorithm knowledge, and the source code in the book can also be used as a "problem-solving toolkit." -- The content of the book mainly includes three parts: complexity analysis, data structures, and algorithms, covering most topics in this field. -- For algorithm novices, reading an introductory book during the initial learning stage is crucial, as it can help you avoid many detours. -- The animated illustrations in the book are usually used to introduce key concepts and challenging topics. When reading this book, you should pay more attention to these topics. -- Practice is the best way to learn programming. It is strongly recommended to run the source code and type the code yourself. -- The web version of this book has a comments section for each chapter, where you are welcome to share your questions and insights at any time. +- This book is primarily for beginners in algorithms. If you already have some background, it can help you review your knowledge systematically, and its source code can serve as a toolkit for solving practice problems. +- The book has three main parts—complexity analysis, data structures, and algorithms—covering most topics in the field. +- For beginners, reading an introductory book early on is essential and can help avoid many detours. +- The animated illustrations usually explain key or challenging concepts. Pay particular attention to them as you read. +- Practice is the best way to learn programming. We strongly recommend running the source code and writing it out yourself. +- Every section in the web edition has a comments area. You are welcome to share your questions and insights at any time. diff --git a/en/docs/chapter_reference/index.md b/en/docs/chapter_reference/index.md index 1f9dfc05f..b70107e9c 100644 --- a/en/docs/chapter_reference/index.md +++ b/en/docs/chapter_reference/index.md @@ -10,15 +10,15 @@ icon: material/bookshelf [3] Robert Sedgewick, et al. Algorithms (4th Edition). -[4] Yan Weimin. Data Structures (C Language Version). +[4] Weimin Yan. Data Structures (C Edition). -[5] Deng Junhui. Data Structures (C++ Language Version, Third Edition). +[5] Junhui Deng. Data Structures (C++ Edition, 3rd Edition). -[6] Mark Allen Weiss, translated by Chen Yue. Data Structures and Algorithm Analysis in Java (Third Edition). +[6] Mark Allen Weiss; translated by Yue Chen. Data Structures and Algorithm Analysis in Java (3rd Edition). -[7] Cheng Jie. Data Structures in Plain Language. +[7] Jie Cheng. A Friendly Guide to Data Structures. -[8] Wang Zheng. The Beauty of Data Structures and Algorithms. +[8] Zheng Wang. The Beauty of Data Structures and Algorithms. [9] Gayle Laakmann McDowell. Cracking the Coding Interview: 189 Programming Questions and Solutions (6th Edition). diff --git a/en/docs/chapter_searching/binary_search.md b/en/docs/chapter_searching/binary_search.md index 4276793cc..575ef688e 100644 --- a/en/docs/chapter_searching/binary_search.md +++ b/en/docs/chapter_searching/binary_search.md @@ -1,27 +1,27 @@ -# Binary Search +# Binary search -Binary search is an efficient search algorithm based on the divide-and-conquer strategy. It leverages the sorted order of the data to reduce the search range by half in each round until the target element is found or the search interval becomes empty. +Binary search is an efficient search algorithm based on divide and conquer. It uses the sorted order of the data to halve the search range each round, continuing until it finds the target element or the search interval becomes empty. !!! question - Given an array `nums` of length $n$ with elements arranged in ascending order and no duplicates, search for and return the index of element `target` in the array. If the array does not contain the element, return $-1$. An example is shown in the figure below. + Given an array `nums` of length $n$, with distinct elements arranged in ascending order, find and return the index of `target`. If the array does not contain this element, return $-1$. An example is shown below. -![Binary search example data](binary_search.assets/binary_search_example.png) +![Example data for binary search](binary_search.assets/binary_search_example.png) -As shown in the figure below, we first initialize pointers $i = 0$ and $j = n - 1$, pointing to the first and last elements of the array respectively, representing the search interval $[0, n - 1]$. Note that square brackets denote a closed interval, which includes the boundary values themselves. +As shown below, initialize pointers $i = 0$ and $j = n - 1$ to the first and last elements, respectively. They represent the search interval $[0, n - 1]$. Square brackets denote a closed interval, which includes its endpoints. -Next, perform the following two steps in a loop: +Next, repeat the following two steps. -1. Calculate the midpoint index $m = \lfloor {(i + j) / 2} \rfloor$, where $\lfloor \: \rfloor$ denotes the floor operation. -2. Compare `nums[m]` and `target`, which results in three cases: - 1. When `nums[m] < target`, it indicates that `target` is in the interval $[m + 1, j]$, so execute $i = m + 1$. - 2. When `nums[m] > target`, it indicates that `target` is in the interval $[i, m - 1]$, so execute $j = m - 1$. - 3. When `nums[m] = target`, it indicates that `target` has been found, so return index $m$. +1. Calculate the midpoint index $m = \lfloor {(i + j) / 2} \rfloor$, where $\lfloor \: \rfloor$ denotes rounding down. +2. Compare `nums[m]` with `target`. There are three cases. + 1. If `nums[m] < target`, then `target` lies in $[m + 1, j]$, so set $i = m + 1$. + 2. If `nums[m] > target`, then `target` lies in $[i, m - 1]$, so set $j = m - 1$. + 3. If `nums[m] = target`, we have found `target`, so return index $m$. -If the array does not contain the target element, the search interval will eventually become empty. In this case, return $-1$. +If the array does not contain the target, the search interval eventually becomes empty. Return $-1$ in this case. === "<1>" - ![Binary search process](binary_search.assets/binary_search_step1.png) + ![The binary search process](binary_search.assets/binary_search_step1.png) === "<2>" ![binary_search_step2](binary_search.assets/binary_search_step2.png) @@ -41,43 +41,43 @@ If the array does not contain the target element, the search interval will event === "<7>" ![binary_search_step7](binary_search.assets/binary_search_step7.png) -It's worth noting that since both $i$ and $j$ are of `int` type, **$i + j$ may exceed the range of the `int` type**. To avoid integer overflow, we typically use the formula $m = \lfloor {i + (j - i) / 2} \rfloor$ to calculate the midpoint. +Because $i$ and $j$ are both of type `int`, **$i + j$ may exceed the range of `int`**. To avoid integer overflow, we usually calculate the midpoint using $m = \lfloor {i + (j - i) / 2} \rfloor$. -The code is shown below: +The implementation is shown below: ```src [file]{binary_search}-[class]{}-[func]{binary_search} ``` -**Time complexity is $O(\log n)$**: In the binary search loop, the interval is reduced by half each round, so the number of iterations is $\log_2 n$. +**Time complexity is $O(\log n)$**: the search interval is halved each round, so the loop runs $\log_2 n$ times. -**Space complexity is $O(1)$**: Pointers $i$ and $j$ use constant-size space. +**Space complexity is $O(1)$**: pointers $i$ and $j$ use constant space. -## Interval Representation Methods +## Ways to represent the interval -In addition to the closed interval mentioned above, another common interval representation is the "left-closed right-open" interval, defined as $[0, n)$, meaning that the left boundary is inclusive while the right boundary is exclusive. Under this representation, the interval $[i, j)$ is empty when $i = j$. +Besides the closed interval above, another common representation is a “left-closed, right-open” interval, written $[0, n)$. It includes the left endpoint but excludes the right endpoint. With this representation, $[i, j)$ is empty when $i = j$. -We can implement a binary search algorithm with the same functionality based on this representation: +We can implement an equivalent binary search algorithm using this representation: ```src [file]{binary_search}-[class]{}-[func]{binary_search_lcro} ``` -As shown in the figure below, under the two interval representations, the initialization, loop condition, and interval narrowing operations of the binary search algorithm are all different. +As shown below, the two interval representations require different initialization, loop conditions, and operations for narrowing the interval. -Since both the left and right boundaries in the "closed interval" representation are defined as closed, the operations to narrow the interval through pointers $i$ and $j$ are also symmetric. This makes it less error-prone, **so the "closed interval" approach is generally recommended**. +Since a closed interval includes both endpoints, the operations that narrow the interval through pointers $i$ and $j$ are symmetric. This makes mistakes less likely, **so we generally recommend the closed-interval version**. -![Two interval definitions](binary_search.assets/binary_search_ranges.png) +![Two ways to define the interval](binary_search.assets/binary_search_ranges.png) -## Advantages and Limitations +## Strengths and limitations -Binary search offers good performance in both time and space. +Binary search performs well in both time and space. -- Binary search has high time efficiency. With large data volumes, the logarithmic time complexity has significant advantages. For example, when the data size $n = 2^{20}$, linear search requires $2^{20} = 1048576$ iterations, while binary search only needs $\log_2 2^{20} = 20$ iterations. -- Binary search requires no extra space. Compared to searching algorithms that require additional space (such as hash-based search), binary search is more space-efficient. +- Binary search is time-efficient. Its logarithmic time complexity offers a significant advantage for large datasets. For example, when $n = 2^{20}$, linear search requires $2^{20} = 1048576$ iterations, whereas binary search needs only $\log_2 2^{20} = 20$. +- Binary search requires no extra storage. It uses less space than search algorithms that rely on additional storage, such as hash lookup. -However, binary search is not suitable for all situations, mainly for the following reasons: +However, binary search is not suitable in every situation, mainly for the following reasons. -- Binary search is only applicable to sorted data. If the input data is unsorted, sorting specifically to use binary search would be counterproductive, as sorting algorithms typically have a time complexity of $O(n \log n)$, which is higher than both linear search and binary search. For scenarios with frequent element insertions, keeping the array sorted requires inserting elements at specific positions with a time complexity of $O(n)$, which is also very expensive. -- Binary search is only applicable to arrays. Binary search requires non-contiguous, jump-style access to elements, and this kind of access is inefficient in linked lists, making it unsuitable for linked lists or linked-list-based data structures. -- For small data volumes, linear search performs better. In linear search, each round requires only 1 comparison operation; while in binary search, it requires 1 addition, 1 division, 1-3 comparison operations, and 1 addition (subtraction), totaling 4-6 unit operations. Therefore, when the data volume $n$ is small, linear search is actually faster than binary search. +- Binary search requires sorted data. Sorting unsorted input solely to perform a binary search is not worthwhile, because sorting usually takes $O(n \log n)$ time, more than either linear or binary search. When insertions are frequent, preserving the array's order requires inserting each element at a specific position, an expensive $O(n)$ operation. +- Binary search is suitable only for arrays. It needs to jump between nonadjacent elements, which is inefficient in a linked list. It is therefore unsuitable for linked lists and data structures based on them. +- Linear search performs better on small datasets. Each iteration of linear search needs only one comparison. Binary search needs one addition, one division, 1–3 comparisons, and one more addition or subtraction, totaling 4–6 elementary operations. When $n$ is small, linear search can therefore be faster. diff --git a/en/docs/chapter_searching/binary_search_edge.md b/en/docs/chapter_searching/binary_search_edge.md index 8dc32d019..13029c2f5 100644 --- a/en/docs/chapter_searching/binary_search_edge.md +++ b/en/docs/chapter_searching/binary_search_edge.md @@ -1,56 +1,56 @@ -# Binary Search Boundaries +# Finding boundaries with binary search -## Finding the Left Boundary +## Finding the left boundary !!! question - Given a sorted array `nums` of length $n$ that may contain duplicate elements, return the index of the leftmost occurrence of `target`. If the array does not contain `target`, return $-1$. + Given a sorted array `nums` of length $n$ that may contain duplicate elements, return the index of the leftmost occurrence of `target`. If the array does not contain this element, return $-1$. -Recall the method for finding the insertion point with binary search. After the search completes, $i$ points to the leftmost `target`, **so finding the insertion point is essentially finding the index of the leftmost `target`**. +Recall the method for finding an insertion point with binary search: when the search finishes, $i$ points to the leftmost `target`. **Finding the insertion point therefore amounts to finding the index of the leftmost `target`**. -Consider implementing the left boundary search using the insertion point finding function. Note that the array may not contain `target`, which could result in the following two cases: +We can use the insertion-point function to find the left boundary. However, `target` may be absent from the array, leading to either of the following outcomes. -- The insertion point index $i$ is out of bounds. -- The element `nums[i]` is not equal to `target`. +- The insertion index $i$ is out of bounds. +- `nums[i]` is not equal to `target`. -When either of these situations occurs, simply return $-1$. The code is shown below: +In either case, simply return $-1$. The code is as follows: ```src [file]{binary_search_edge}-[class]{}-[func]{binary_search_left_edge} ``` -## Finding the Right Boundary +## Finding the right boundary -So how do we find the rightmost `target`? The most direct approach is to modify the code and replace the pointer shrinking operation in the `nums[m] == target` case. The code is omitted here; interested readers can implement it themselves. +How can we find the rightmost `target`? The most direct approach is to modify the pointer update for `nums[m] == target`. We omit the code here; interested readers can implement it themselves. -Below we introduce two more clever methods. +Here are two shortcuts. -### Reusing Left Boundary Search +### Reusing the left-boundary search -In fact, we can use the function for finding the leftmost `target` to find the rightmost `target`. The specific method is: **convert finding the rightmost `target` into finding the leftmost `target + 1`**. +We can use the function for finding the leftmost element to find the rightmost one: **turn the search for the rightmost `target` into a search for the leftmost `target + 1`**. -As shown in the figure below, after the search completes, the pointer $i$ points to the leftmost `target + 1` (if it exists), while $j$ points to the rightmost `target`, **so we can return $j$**. +As shown below, when the search finishes, pointer $i$ points to the leftmost `target + 1`, if it exists, while $j$ points to the rightmost `target`. **We therefore return $j$**. -![Converting right boundary search to left boundary search](binary_search_edge.assets/binary_search_right_edge_by_left_edge.png) +![Turning a right-boundary search into a left-boundary search](binary_search_edge.assets/binary_search_right_edge_by_left_edge.png) -Note that the returned insertion point is $i$, so we need to subtract $1$ from it to obtain $j$: +The returned insertion point is $i$, so subtract $1$ to obtain $j$: ```src [file]{binary_search_edge}-[class]{}-[func]{binary_search_right_edge} ``` -### Converting to Element Search +### Turning a boundary search into an element search -We know that when the array does not contain `target`, $i$ and $j$ will eventually point to the first element greater than `target` and the rightmost element less than `target`, respectively. +When the array does not contain `target`, $i$ ultimately points to the first element greater than `target`, while $j$ points to the rightmost element smaller than `target`. -Therefore, as shown in the figure below, we can construct an element that does not exist in the array to find the left and right boundaries. +As shown below, we can therefore construct a value absent from the array and use it to find the left and right boundaries. -- Finding the leftmost `target`: This can be converted to finding `target - 0.5` and returning the pointer $i$. -- Finding the rightmost `target`: This can be converted to finding `target + 0.5` and returning the pointer $j$. +- To find the leftmost `target`, search for `target - 0.5` and return pointer $i$. +- To find the rightmost `target`, search for `target + 0.5` and return pointer $j$. -![Converting boundary search to element search](binary_search_edge.assets/binary_search_edge_by_element.png) +![Turning a boundary search into an element search](binary_search_edge.assets/binary_search_edge_by_element.png) -The code is omitted here, but the following two points are worth noting: +The code is omitted here, but keep two points in mind. -- Since the given array does not contain decimal values, we do not need to worry about how to handle equality. -- Because this method introduces decimals, the variable `target` in the function needs to be changed to a floating-point type (Python does not require this change). +- The given array contains no fractional values, so we do not need to handle equality. +- Since this method introduces fractional values, change the function's `target` variable to a floating-point type. No change is needed in Python. diff --git a/en/docs/chapter_searching/binary_search_insertion.md b/en/docs/chapter_searching/binary_search_insertion.md index 384f0fd6b..f5283d154 100644 --- a/en/docs/chapter_searching/binary_search_insertion.md +++ b/en/docs/chapter_searching/binary_search_insertion.md @@ -1,57 +1,57 @@ -# Binary Search Insertion Point +# Finding an insertion point with binary search -Binary search can be used not only to search for target elements, but also to solve many variant problems, such as finding the insertion position of a target element. +Binary search can do more than find a target element. It also solves many variations of the problem, such as finding where to insert a target. -## Case Without Duplicate Elements +## When there are no duplicates !!! question - Given a sorted array `nums` of length $n$ and an element `target`, where the array contains no duplicate elements, insert `target` into `nums` while maintaining its sorted order. If `target` already exists in the array, insert it to its left. Return the index of `target` after insertion. An example is shown below. + Given a sorted array `nums` of length $n$ with no duplicate elements and a value `target`, insert `target` into `nums` while preserving its order. If `target` is already present, insert the new value to its left. Return the index of the newly inserted `target`, as illustrated below. -![Binary search insertion point example data](binary_search_insertion.assets/binary_search_insertion_example.png) +![Example data for finding an insertion point](binary_search_insertion.assets/binary_search_insertion_example.png) -If we want to reuse the binary search code from the previous section, we need to answer the following two questions. +To reuse the binary search code from the previous section, we need to answer two questions. -**Question 1**: When the array contains `target`, is the insertion point index the same as that element's index? +**Question 1**: If the array contains `target`, is the insertion index the index of that element? -The problem requires inserting `target` to the left of equal elements, which means the newly inserted `target` replaces the position of the original `target`. In other words, **when the array contains `target`, the insertion point index is the index of that `target`**. +The problem asks us to insert `target` to the left of the equal element, so the new `target` takes the original one's position. Thus, **if the array contains `target`, the insertion index is the index of that `target`**. -**Question 2**: When the array does not contain `target`, what is the insertion point index? +**Question 2**: If the array does not contain `target`, which index is the insertion point? -To analyze this further, consider the binary search process: when `nums[m] < target`, $i$ moves, meaning that pointer $i$ is approaching elements greater than or equal to `target`. Similarly, pointer $j$ is always approaching elements less than or equal to `target`. +Consider the search process more closely. Pointer $i$ moves when `nums[m] < target`, so $i$ approaches elements greater than or equal to `target`. Similarly, pointer $j$ keeps approaching elements less than or equal to `target`. -Therefore, when the binary search ends, $i$ must point to the first element greater than `target`, and $j$ must point to the rightmost element less than `target`. **It follows that when the array does not contain `target`, the insertion index is $i$**. The code is shown below: +When the search ends, $i$ points to the first element greater than `target`, and $j$ points to the rightmost element smaller than `target`. **Thus, when `target` is absent, the insertion index is $i$**. The code is as follows: ```src [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple} ``` -## Case with Duplicate Elements +## When duplicates are present !!! question - Based on the previous problem, assume the array may contain duplicate elements, with everything else remaining the same. + Modify the previous problem to allow duplicate elements in the array. All other requirements remain the same. -Suppose there are multiple `target` elements in the array. Ordinary binary search can only return the index of one `target`, **and cannot determine how many `target` elements are to the left and right of that element**. +If the array contains multiple occurrences of `target`, ordinary binary search can return the index of one occurrence, **but cannot determine how many more occurrences lie to its left or right**. -The problem requires inserting the target element at the leftmost position, **so we need to find the index of the leftmost `target` in the array**. A straightforward initial approach is to follow the steps shown in the figure below: +The target must be inserted as far left as possible, **so we need the index of the leftmost `target`**. One initial approach follows the steps below. -1. Perform binary search to obtain the index of any `target`, denoted as $k$. -2. Starting from index $k$, perform linear traversal to the left, and return when the leftmost `target` is found. +1. Run binary search to find the index of any `target`, denoted by $k$. +2. Starting at index $k$, scan leftward and return when the leftmost `target` is reached. -![Linear search for insertion point of duplicate elements](binary_search_insertion.assets/binary_search_insertion_naive.png) +![Finding the insertion point among duplicates with a linear scan](binary_search_insertion.assets/binary_search_insertion_naive.png) -Although this method works, it includes linear search, resulting in a time complexity of $O(n)$. When the array contains many duplicate `target` elements, this method is very inefficient. +Although this works, it includes a linear search and therefore takes $O(n)$ time. It is inefficient when the array contains many occurrences of `target`. -Now consider extending the binary search code. As shown in the figure below, the overall process remains unchanged: in each iteration, we first compute the midpoint index $m$, then compare `target` with `nums[m]`, leading to the following cases: +Now consider extending binary search itself. As shown below, the overall process remains the same: each round calculates midpoint index $m$ and compares `target` with `nums[m]`. The cases are as follows. -- When `nums[m] < target` or `nums[m] > target`, it means `target` has not been found yet, so use the standard interval-shrinking operation of binary search to **move pointers $i$ and $j$ closer to `target`**. -- When `nums[m] == target`, it means elements less than `target` are in the interval $[i, m - 1]$, so use $j = m - 1$ to shrink the interval, thereby **moving pointer $j$ closer to elements less than `target`**. +- If `nums[m] < target` or `nums[m] > target`, we have not found `target`. Narrow the interval as in ordinary binary search, **bringing pointers $i$ and $j$ closer to `target`**. +- If `nums[m] == target`, elements smaller than `target` lie in $[i, m - 1]$. Narrow the interval by setting $j = m - 1$, **bringing pointer $j$ closer to elements smaller than `target`**. -After the loop completes, $i$ points to the leftmost `target`, and $j$ points to the rightmost element less than `target`, **so index $i$ is the insertion point**. +When the loop finishes, $i$ points to the leftmost `target`, and $j$ points to the rightmost element smaller than `target`. **Index $i$ is therefore the insertion point**. === "<1>" - ![Steps for binary search insertion point of duplicate elements](binary_search_insertion.assets/binary_search_insertion_step1.png) + ![Steps for finding an insertion point among duplicates](binary_search_insertion.assets/binary_search_insertion_step1.png) === "<2>" ![binary_search_insertion_step2](binary_search_insertion.assets/binary_search_insertion_step2.png) @@ -74,9 +74,9 @@ After the loop completes, $i$ points to the leftmost `target`, and $j$ points to === "<8>" ![binary_search_insertion_step8](binary_search_insertion.assets/binary_search_insertion_step8.png) -Observe the following code: the branches `nums[m] > target` and `nums[m] == target` perform the same operation, so they can be merged. +In the code below, the branches for `nums[m] > target` and `nums[m] == target` perform the same operation, so they can be combined. -Even so, we can still keep the conditional branches expanded, as the logic is clearer and more readable. +Even so, we can leave them separate to make the logic clearer and easier to read. ```src [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion} @@ -84,8 +84,8 @@ Even so, we can still keep the conditional branches expanded, as the logic is cl !!! tip - The code in this section uses the "closed interval" approach throughout. Interested readers can implement the "left-closed, right-open" approach themselves. + All the code in this section uses closed intervals. Interested readers can implement versions using left-closed, right-open intervals. -Overall, binary search is simply a matter of setting separate search targets for pointers $i$ and $j$. The target may be a specific element (such as `target`) or a range of elements (such as elements less than `target`). +Viewed more broadly, binary search simply assigns a search goal to each of pointers $i$ and $j$. The goal may be a particular element, such as `target`, or a range of elements, such as those smaller than `target`. -With each iteration of binary search, pointers $i$ and $j$ gradually approach their preset targets. Ultimately, they either find the answer or stop after crossing the boundary. +As the interval is repeatedly halved, pointers $i$ and $j$ approach their assigned goals. Eventually they either find the answer or stop after crossing a boundary. diff --git a/en/docs/chapter_searching/exercises.md b/en/docs/chapter_searching/exercises.md index d0e0b4635..55b9775ff 100644 --- a/en/docs/chapter_searching/exercises.md +++ b/en/docs/chapter_searching/exercises.md @@ -1,96 +1,95 @@ # Exercises -## Concept Review +## Check your understanding -### How Binary Search Narrows the Search Interval +### How binary search narrows the search interval Search for 16 in the sorted array `[2, 5, 8, 12, 16, 23, 38]`. -Use the closed interval `[i, j]` and calculate the midpoint as +Use a closed interval `[i, j]` and the midpoint $m=i+(j-i)/2$, rounded down. -For each round, write `(i, j, m)`, the middle element, and how the interval is narrowed next, +For each iteration, write `(i, j, m)`, the midpoint element, and how the interval is narrowed next, until the target is found. -??? success "Answer" +??? success "Reference answer" The search proceeds as follows: - | Round | `(i, j, m)` | Middle element | Next step | + | Iteration | `(i, j, m)` | Midpoint element | Next step | | --- | --- | --- | --- | - | 1 | `(0, 6, 3)` | 12 | `12 < 16`, set `i = 4` | - | 2 | `(4, 6, 5)` | 23 | `23 > 16`, set `j = 4` | + | 1 | `(0, 6, 3)` | 12 | `12 < 16`, so set `i = 4` | + | 2 | `(4, 6, 5)` | 23 | `23 > 16`, so set `j = 4` | | 3 | `(4, 4, 4)` | 16 | Target found; return index 4 | - Because the array is sorted, when the middle value is smaller than the target, the middle and everything to its left can be excluded. - When the middle value is greater than the target, the middle and everything to its right can be excluded. + Since the array is sorted, when the midpoint value is below the target, we can exclude the midpoint and everything to its left. + When it is above the target, we can exclude the midpoint and everything to its right. -### Left and Right Boundaries of Duplicate Elements +### Left and right boundaries of repeated elements -Search for the number 2 in the array `[1, 2, 2, 2, 4, 6]`. -A student uses binary search, returns immediately after finding the target at index 2, and says, -"Index 2 is the left boundary of the number 2." +Search for 2 in the array `[1, 2, 2, 2, 4, 6]`. +A student uses binary search, finds the target at index 2, and immediately returns, saying: +“Index 2 is the left boundary of the number 2.” -1. Is the student's statement correct? What are the left and right boundaries of 2? Explain why. -2. When searching for the left boundary, if the middle element equals the target, which side should be searched next? -3. When searching for the right boundary, which side should be searched next? State only the direction; you do not need to write the complete search process. +1. Is the student correct? What are the left and right boundaries of 2? Explain. +2. When finding the left boundary, which side should we continue searching if the midpoint element equals the target? +3. Which side should we continue searching when finding the right boundary? State only the direction; you do not need to give the full search process. -??? success "Answer" +??? success "Reference answer" - 1. The student's statement is incorrect. Returning as soon as a 2 is found guarantees only that some occurrence of 2 was found, not the leftmost or rightmost one. + 1. The student is incorrect. Returning immediately upon finding a 2 guarantees only that some occurrence has been found, not the leftmost or rightmost one. Here, the left boundary is index 1 and the right boundary is index 3. - 2. When searching for the left boundary, continue searching on the left even if the middle element equals 2. + 2. When finding the left boundary, continue searching to the left even if the midpoint element equals 2. For example, with a closed interval, set `j = m - 1`. - 3. When searching for the right boundary, continue searching on the right after the middle element equals 2. + 3. When finding the right boundary, continue searching to the right after the midpoint element equals 2. For example, set `i = m + 1`. -### Choosing a Search Method for Different Data +### Choosing a search method for different data -For each situation below, choose an appropriate method from "linear search," "binary search," and "hash table," and explain why: +Choose a suitable method from linear search, binary search, and a hash table for each situation, and explain: -1. Repeatedly search among $10^7$ integers that are already sorted and never change, without building any additional data structure. -2. Repeatedly test whether a key exists in a collection with frequent insertions and deletions. The collection need not remain sorted, and no range searches are needed. -3. Search an unsorted array for a value only once. +1. Repeatedly searching $10^7$ integers that are already sorted and will not change, without building an additional data structure. +2. Repeatedly checking whether a key exists in a data set with frequent insertions and deletions, with no need to preserve order or perform range queries. +3. Searching for a value only once in an unsorted array. -??? success "Answer" +??? success "Reference answer" - 1. Binary search: the data is sorted and static, so searching takes $O(\log n)$ time without extra space. - 2. Hash table: when the hash function distributes keys fairly evenly among the buckets, insertion, deletion, and lookup by key all take $O(1)$ time on average. - 3. Scan directly from beginning to end. When searching only once, sorting the array or building a hash table still requires processing the entire array first, - so neither reduces the total work for this single task. + 1. Binary search: the data is sorted and static, giving $O(\log n)$ time without an additional data structure. + 2. A hash table: when the hash function distributes keys fairly evenly among buckets, insertion, deletion, and key lookup all have average time complexity $O(1)$. + 3. Scan directly from beginning to end. For a single search, sorting or building a hash table would first require processing the entire array, + without reducing the total work for this task. - The choice depends on whether the data is sorted, whether an additional structure is allowed, how many searches are needed, and which operations must be supported. + The choice depends on whether the data is sorted, whether additional structures are allowed, how many queries there are, and which operations are needed. -## Programming Exercises +## Programming exercises -### Binary Search in a Sorted Array +### Binary search in a sorted array -Given an integer array `nums` in strictly increasing order and a target value `target`, use binary search to find `target`. If it exists, return its array index; otherwise, return -1. +Given an integer array `nums` in strictly increasing order and a target value `target`, find `target` using binary search. Return its array index if it exists, or -1 otherwise. ??? tip "Hints" - 1. The initial interval is left = 0 and right = n - 1; it is nonempty while left <= right - 2. Calculate the midpoint with mid = left + (right - left) // 2 - 3. If `nums[mid]` is less than `target`, move the left boundary to `mid + 1`; if `nums[mid]` is greater than `target`, move the right boundary to `mid - 1`; if they are equal, return immediately + 1. The initial interval has left = 0 and right = n - 1; it is nonempty while left <= right. + 2. Calculate the midpoint with mid = left + (right - left) // 2. + 3. If `nums[mid]` is less than `target`, move the left boundary to `mid + 1`; if `nums[mid]` is greater than `target`, move the right boundary to `mid - 1`; return immediately if they are equal. [LeetCode](https://leetcode.com/problems/binary-search/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } -### Insertion Point in a Sorted Array +### Finding an insertion position in a sorted array You are given an integer array `nums` in strictly increasing order and a target value `target`. -Return the index of `target` if it is already in the array. Otherwise, return the insertion point at which `target` can be inserted while keeping the array in strictly increasing order. -The insertion point may be 0 or may equal the array's length. Use binary search. +If `target` is already in the array, return its index. Otherwise, return the position at which `target` can be inserted while preserving strictly increasing order. The answer may be 0 or the array length. Use binary search. ??? tip "Hints" - 1. The answer may be 0 or the array length n - 2. With a closed interval, if `nums[mid]` is greater than or equal to `target`, set `right = mid - 1` and continue checking farther left; otherwise, set `left = mid + 1` - 3. When the loop ends, left is the insertion point + 1. The answer may be 0 or the array length n. + 2. With a closed interval, if `nums[mid]` is greater than or equal to `target`, set `right = mid - 1` to keep checking farther left; otherwise, set `left = mid + 1`. + 3. When the loop ends, left is the insertion position. [LeetCode](https://leetcode.com/problems/search-insert-position/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_searching/index.md b/en/docs/chapter_searching/index.md index 89abf6061..24c37b8a2 100644 --- a/en/docs/chapter_searching/index.md +++ b/en/docs/chapter_searching/index.md @@ -4,6 +4,6 @@ !!! abstract - Searching is an adventure into the unknown, where we may need to traverse every corner of the mysterious space, or we may be able to quickly lock onto the target. - - In this journey of discovery, each exploration may yield an unexpected answer. + Searching is an adventure into the unknown. We may need to explore every corner of a mysterious space, or we may be able to pinpoint our target quickly. + + On this journey, every exploration may reveal an unexpected answer. diff --git a/en/docs/chapter_searching/replace_linear_by_hashing.md b/en/docs/chapter_searching/replace_linear_by_hashing.md index 82edbc291..e121fc90e 100644 --- a/en/docs/chapter_searching/replace_linear_by_hashing.md +++ b/en/docs/chapter_searching/replace_linear_by_hashing.md @@ -1,34 +1,34 @@ -# Hash Optimization Strategy +# Optimizing with hashing -In algorithm problems, **we often reduce the time complexity of algorithms by replacing linear search with hash-based search**. Let's use an algorithm problem to deepen our understanding. +In algorithm problems, **we often reduce time complexity by replacing linear search with hash lookup**. Let us explore this strategy through a problem. !!! question - Given an integer array `nums` and a target value `target`, find two elements in the array whose sum is `target`, and return their indices. Any solution will do. + Given an integer array `nums` and a target value `target`, find two elements whose sum equals `target` and return their array indices. Any valid solution is acceptable. -## Linear Search: Trading Time for Space +## Linear search: trading time for space -Consider directly traversing all possible combinations. As shown in the figure below, we use nested loops and check in each iteration whether the sum of two integers is `target`. If so, return their indices. +Consider examining every possible pair directly. As shown below, use two nested loops and check whether the two integers sum to `target` each time. If they do, return their indices. -![Linear search solution for two sum](replace_linear_by_hashing.assets/two_sum_brute_force.png) +![Solving two sum with linear search](replace_linear_by_hashing.assets/two_sum_brute_force.png) -The code is shown below: +The code is as follows: ```src [file]{two_sum}-[class]{}-[func]{two_sum_brute_force} ``` -This method has a time complexity of $O(n^2)$ and a space complexity of $O(1)$, making it very time-consuming on large inputs. +This approach takes $O(n^2)$ time and $O(1)$ space, making it very slow for large datasets. -## Hash-Based Search: Trading Space for Time +## Hash lookup: trading space for time -Consider using a hash table whose keys are array elements and whose values are their indices. Traverse the array and perform the steps shown in the figure below in each iteration: +Now use a hash table whose keys are array elements and whose values are their indices. Iterate through the array, performing the steps below each round. -1. Check if the number `target - nums[i]` is in the hash table. If so, directly return the indices of these two elements. -2. Add the key-value pair `nums[i]` and index `i` to the hash table. +1. Check whether `target - nums[i]` is in the hash table. If it is, return the indices of the two elements. +2. Add the key-value pair consisting of `nums[i]` and index `i` to the hash table. === "<1>" - ![Hash table solution for two sum](replace_linear_by_hashing.assets/two_sum_hashtable_step1.png) + ![Solving two sum with an auxiliary hash table](replace_linear_by_hashing.assets/two_sum_hashtable_step1.png) === "<2>" ![two_sum_hashtable_step2](replace_linear_by_hashing.assets/two_sum_hashtable_step2.png) @@ -36,12 +36,12 @@ Consider using a hash table whose keys are array elements and whose values are t === "<3>" ![two_sum_hashtable_step3](replace_linear_by_hashing.assets/two_sum_hashtable_step3.png) -The implementation is shown below and requires only a single loop: +The implementation below requires only one loop: ```src [file]{two_sum}-[class]{}-[func]{two_sum_hash_table} ``` -This method reduces the time complexity from $O(n^2)$ to $O(n)$ through hash-based search, greatly improving runtime efficiency. +Hash lookup reduces the time complexity from $O(n^2)$ to $O(n)$, greatly improving efficiency. -Since an additional hash table needs to be maintained, the space complexity is $O(n)$. **Nevertheless, this method offers a more balanced overall time-space trade-off, making it the optimal solution to this problem**. +Maintaining an additional hash table requires $O(n)$ space. **Even so, this approach offers a better balance of time and space efficiency, making it the optimal solution to this problem**. diff --git a/en/docs/chapter_searching/searching_algorithm_revisited.md b/en/docs/chapter_searching/searching_algorithm_revisited.md index 914c351e6..4efa206b6 100644 --- a/en/docs/chapter_searching/searching_algorithm_revisited.md +++ b/en/docs/chapter_searching/searching_algorithm_revisited.md @@ -1,84 +1,84 @@ -# Searching Algorithms Revisited +# Search algorithms revisited -Searching algorithms are used to search for one or a group of elements that meet specific conditions in data structures (such as arrays, linked lists, trees, or graphs). +A search algorithm finds one or more elements that satisfy specified conditions in a data structure, such as an array, linked list, tree, or graph. -Searching algorithms can be divided into the following two categories based on their implementation approach: +Search algorithms fall into two categories according to their approach. -- **Locating target elements by traversing the data structure**, such as traversing arrays, linked lists, trees, and graphs. -- **Achieving efficient element lookup by leveraging the way data is organized or prior information about the data**, such as binary search, hash-based search, and binary search tree search. +- **Locate the target by traversing the data structure**, as in traversals of arrays, linked lists, trees, and graphs. +- **Find elements efficiently by using the organization of the data or prior information about it**, as in binary search, hash lookup, and binary search tree lookup. -As these topics have already been introduced in earlier chapters, searching algorithms should already be familiar to us. In this section, we revisit them from a more systematic perspective. +These ideas have all appeared in earlier chapters, so search algorithms are already familiar to us. In this section, we revisit them from a more systematic perspective. -## Brute-Force Search +## Brute-force search -Brute-force search locates target elements by traversing each element of the data structure. +Brute-force search locates a target by visiting the elements of a data structure one by one. -- "Linear search" is applicable to linear data structures such as arrays and linked lists. It starts from one end of the data structure and accesses elements one by one until the target element is found or the other end is reached without finding the target element. -- "Breadth-first search" and "depth-first search" are two traversal strategies for graphs and trees. Breadth-first search starts from the initial node and searches layer by layer, visiting nodes from near to far. Depth-first search starts from the initial node, follows a path to the end, then backtracks and tries other paths until the entire data structure is traversed. +- “Linear search” applies to linear data structures such as arrays and linked lists. Starting at one end, it visits elements in sequence until it finds the target or reaches the other end without finding it. +- “Breadth-first search” and “depth-first search” are two strategies for traversing graphs and trees. Breadth-first search starts at an initial node and searches level by level, visiting nearby nodes before more distant ones. Depth-first search follows a path as far as possible, then backtracks and tries other paths until the whole structure has been traversed. -The advantage of brute-force search is that it is simple and has good generality, **requiring no data preprocessing or additional data structures**. +Brute-force search is simple and broadly applicable. **It requires neither preprocessing the data nor building an additional data structure**. -However, **the time complexity of such algorithms is $O(n)$**, where $n$ is the number of elements, so performance is poor when dealing with large amounts of data. +However, **these algorithms take $O(n)$ time**, where $n$ is the number of elements, so they perform poorly on large datasets. -## Adaptive Search +## Adaptive search -Adaptive search leverages properties of the data itself (such as sorted order) to optimize the search process and locate target elements more efficiently. +Adaptive search uses properties of the data, such as its ordering, to optimize the search process and locate the target more efficiently. -- "Binary search" uses the orderliness of data to achieve efficient searching, applicable only to arrays. -- "Hash-based search" uses hash tables to store searchable data as key-value pairs, thereby enabling efficient queries. -- "Tree search" operates on specific tree structures (such as binary search trees), quickly ruling out nodes by comparing node values to locate the target element. +- “Binary search” uses sorted data to search efficiently and applies only to arrays. +- “Hash lookup” uses a hash table to map lookup keys to target data as key-value pairs. +- “Tree search” uses comparisons of node values in a particular tree structure, such as a binary search tree, to rule out nodes quickly and locate the target. -The advantage of such algorithms is high efficiency, **with time complexity reaching $O(\log n)$ or even $O(1)$**. +These algorithms are efficient, **achieving time complexities of $O(\log n)$ or even $O(1)$**. -However, **using these algorithms often requires data preprocessing**. For example, binary search requires pre-sorting the array, while hash-based search and tree search both require additional data structures, and maintaining these data structures also requires extra time and space overhead. +However, **they often require preprocessing the data**. Binary search requires sorting the array in advance; hash lookup and tree search require additional data structures, whose maintenance also costs time and space. !!! tip - Adaptive search algorithms are often called lookup algorithms, **mainly used to quickly retrieve target elements in specific data structures**. + Adaptive search algorithms are often called lookup algorithms. **Their main purpose is to retrieve target elements quickly from specific data structures**. -## Search Method Selection +## Choosing a search method -Given a dataset of size $n$, we can use linear search, binary search, tree search, hash-based search, and other methods to search for the target element. The working principles of each method are shown in the figure below. +Given a dataset of size $n$, we can find a target using linear search, binary search, tree search, hash lookup, and other methods. The figure below illustrates how these methods work. -![Multiple search strategies](searching_algorithm_revisited.assets/searching_algorithms.png) +![Different search strategies](searching_algorithm_revisited.assets/searching_algorithms.png) -The efficiency and characteristics of these methods are summarized in the table below. +The table below compares their efficiency and properties. -

Table   Comparison of search algorithm efficiency

+

Table   Efficiency of lookup algorithms

-| | Linear search | Binary search | Tree search | Hash-based search | -| ------------------ | ------------- | --------------------- | --------------------------- | -------------------------- | -| Search element | $O(n)$ | $O(\log n)$ | $O(\log n)$ | $O(1)$ | -| Insert element | $O(1)$ | $O(n)$ | $O(\log n)$ | $O(1)$ | -| Delete element | $O(n)$ | $O(n)$ | $O(\log n)$ | $O(1)$ | -| Extra space | $O(1)$ | $O(1)$ | $O(n)$ | $O(n)$ | -| Data preprocessing | / | Sorting $O(n \log n)$ | Tree building $O(n \log n)$ | Hash table building $O(n)$ | -| Data ordered | Unordered | Ordered | Ordered | Unordered | +| | Linear search | Binary search | Tree search | Hash lookup | +| ------------ | -------- | ------------------ | ------------------ | --------------- | +| Find an element | $O(n)$ | $O(\log n)$ | $O(\log n)$ | $O(1)$ | +| Insert an element | $O(1)$ | $O(n)$ | $O(\log n)$ | $O(1)$ | +| Delete an element | $O(n)$ | $O(n)$ | $O(\log n)$ | $O(1)$ | +| Extra space | $O(1)$ | $O(1)$ | $O(n)$ | $O(n)$ | +| Preprocessing | / | Sort $O(n \log n)$ | Build a tree $O(n \log n)$ | Build a hash table $O(n)$ | +| Data ordering | Unsorted | Sorted | Sorted | Unsorted | -The choice of search algorithm also depends on data volume, search performance requirements, data query and update frequency, etc. +The choice also depends on dataset size, required search performance, and how frequently the data is queried and updated. **Linear search** -- Good generality, requiring no data preprocessing operations. If we need to query the data only once, the preprocessing required by the other three methods can take longer than the linear search itself. -- Suitable for small data volumes, where time complexity has less impact on efficiency. -- Suitable for scenarios with high data update frequency, as this method does not require any additional data maintenance. +- It is broadly applicable and requires no preprocessing. If we need only one lookup, preprocessing for the other three methods takes longer than a linear search itself. +- It suits small datasets, where time complexity has less influence on actual performance. +- It suits frequently updated data because no additional data structure needs to be maintained. **Binary search** -- Suitable for large datasets, with stable performance and a worst-case time complexity of $O(\log n)$. -- Data volume cannot be too large, as storing arrays requires contiguous memory space. -- Not suitable for scenarios with frequent data insertion and deletion, as maintaining a sorted array has high overhead. +- It suits large datasets, with predictable performance and a worst-case time complexity of $O(\log n)$. +- The dataset cannot be excessively large, because an array requires contiguous memory. +- It is unsuitable for frequent insertions and deletions, since maintaining a sorted array is expensive. -**Hash-based search** +**Hash lookup** -- Suitable for scenarios with high query performance requirements, with an average time complexity of $O(1)$. -- Not suitable for scenarios requiring ordered data or range searches, as hash tables cannot maintain the data in sorted order. -- High dependence on hash functions and hash collision handling strategies, with significant risk of performance degradation. -- Not suitable for excessively large data volumes, as hash tables require extra space to minimize collisions and thus provide good query performance. +- It suits situations demanding very fast queries, with an average time complexity of $O(1)$. +- It is unsuitable when sorted data or range queries are needed, because a hash table does not preserve order. +- It depends heavily on the hash function and collision-resolution strategy, creating a greater risk of performance degradation. +- It is unsuitable for excessively large datasets, because a hash table needs extra space to minimize collisions and maintain good query performance. **Tree search** -- Suitable for massive datasets, as tree nodes are stored non-contiguously in memory. -- Suitable for scenarios that require maintaining ordered data or performing range searches. -- During continuous node insertion and deletion, binary search trees may become skewed, degrading time complexity to $O(n)$. -- If AVL trees or red-black trees are used, all operations can consistently run in $O(\log n)$ time, though maintaining tree balance adds extra overhead. +- It suits very large datasets, because tree nodes are stored at separate locations in memory. +- It suits situations requiring sorted data or range queries. +- Repeated insertions and deletions can make a binary search tree skewed, degrading its time complexity to $O(n)$. +- An AVL tree or red-black tree keeps operations at $O(\log n)$, but maintaining balance adds overhead. diff --git a/en/docs/chapter_searching/summary.md b/en/docs/chapter_searching/summary.md index c3f8ae2db..1dcb084b7 100644 --- a/en/docs/chapter_searching/summary.md +++ b/en/docs/chapter_searching/summary.md @@ -1,10 +1,10 @@ # Summary -### Key Review +### Key takeaways -- Binary search relies on ordered data and searches by repeatedly halving the search interval. It requires the input data to be sorted and applies only to arrays or array-based data structures. -- Brute-force search locates data by traversing the data structure. Linear search applies to arrays and linked lists, while breadth-first search and depth-first search apply to graphs and trees. These algorithms are broadly applicable and require no data preprocessing, but their relatively high time complexity is $O(n)$. -- Hash-based search, tree search, and binary search are efficient search methods that can quickly locate target elements in specific data structures. Such algorithms are highly efficient with time complexity reaching $O(\log n)$ or even $O(1)$, but typically require additional data structures. -- In practice, we need to analyze factors such as data scale, search performance requirements, and data query and update frequency to choose the appropriate search method. -- Linear search is suitable for small datasets or data that is updated frequently; binary search is suitable for large sorted datasets; hash-based search is suitable when high query efficiency is required and range queries are unnecessary; tree search is suitable for large dynamic datasets that must maintain order and support range queries. -- Replacing linear search with hash-based search is a commonly used strategy to optimize runtime, reducing time complexity from $O(n)$ to $O(1)$. +- Binary search relies on sorted data and repeatedly halves the search interval. It requires ordered input and applies only to arrays or data structures based on arrays. +- Brute-force search locates data by traversing a data structure. Linear search suits arrays and linked lists; breadth-first and depth-first search suit graphs and trees. These algorithms are broadly applicable and require no preprocessing, but their $O(n)$ time complexity is relatively high. +- Hash lookup, tree search, and binary search efficiently locate targets in particular data structures. Their time complexities can be $O(\log n)$ or even $O(1)$, but they usually rely on additional data structures. +- In practice, choose a search method by considering dataset size, required search performance, query frequency, update frequency, and related factors. +- Linear search suits small or frequently updated datasets; binary search suits large, sorted datasets; hash lookup suits fast queries without range-query requirements; tree search suits large, dynamic datasets that need ordering and range queries. +- Replacing linear search with hash lookup is a common optimization that reduces lookup time from $O(n)$ to $O(1)$. diff --git a/en/docs/chapter_sorting/bubble_sort.md b/en/docs/chapter_sorting/bubble_sort.md index 17a560260..699222d63 100644 --- a/en/docs/chapter_sorting/bubble_sort.md +++ b/en/docs/chapter_sorting/bubble_sort.md @@ -1,11 +1,11 @@ -# Bubble Sort +# Bubble sort -Bubble sort sorts an array by continuously comparing and swapping adjacent elements. This process resembles bubbles rising from the bottom to the top, hence the name bubble sort. +Bubble sort sorts by repeatedly comparing and swapping adjacent elements. The process resembles bubbles rising from the bottom to the top, which gives the algorithm its name. -As shown in the figure below, the bubbling process can be simulated using element swaps: starting from the leftmost end of the array and traversing to the right, compare each pair of adjacent elements, and if "left element > right element", swap them. After the traversal is complete, the largest element is moved to the rightmost end of the array. +As shown below, we can simulate bubbling through swaps. Scan from the left end of the array to the right, comparing each pair of adjacent elements. If the left element is greater than the right, swap them. After the scan, the largest element has moved to the far right. === "<1>" - ![Simulating bubble sort using element swaps](bubble_sort.assets/bubble_operation_step1.png) + ![Simulating bubbling through element swaps](bubble_sort.assets/bubble_operation_step1.png) === "<2>" ![bubble_operation_step2](bubble_sort.assets/bubble_operation_step2.png) @@ -25,35 +25,35 @@ As shown in the figure below, the bubbling process can be simulated using elemen === "<7>" ![bubble_operation_step7](bubble_sort.assets/bubble_operation_step7.png) -## Algorithm Flow +## Algorithm steps -Assume the array has length $n$. The steps of bubble sort are shown in the figure below. +For an array of length $n$, bubble sort proceeds as shown below. -1. First, perform "bubbling" on $n$ elements, **swapping the largest element of the array to its correct position**. -2. Next, perform "bubbling" on the remaining $n - 1$ elements, **swapping the second largest element to its correct position**. -3. And so on. After $n - 1$ rounds of "bubbling", **the largest $n - 1$ elements have all been swapped to their correct positions**. -4. The only remaining element must be the smallest element, requiring no sorting, so the array sorting is complete. +1. First, perform a bubbling pass over all $n$ elements, **moving the largest element to its correct position**. +2. Next, perform a pass over the remaining $n - 1$ elements, **moving the second-largest element to its correct position**. +3. Continue in this way. After $n - 1$ passes, **the largest $n - 1$ elements are all in their correct positions**. +4. The one remaining element must be the smallest and needs no further sorting. The array is now sorted. -![Bubble sort flow](bubble_sort.assets/bubble_sort_overview.png) +![The bubble sort process](bubble_sort.assets/bubble_sort_overview.png) -Example code is as follows: +An implementation follows: ```src [file]{bubble_sort}-[class]{}-[func]{bubble_sort} ``` -## Efficiency Optimization +## Improving efficiency -We can observe that if no swaps occur during a round of "bubbling", the array is already sorted and the algorithm can return immediately. Therefore, we can add a flag `flag` to detect this situation and terminate as soon as it occurs. +If a bubbling pass performs no swaps, the array is already sorted and we can return immediately. We can therefore add a variable `flag` to detect this situation and stop as soon as it occurs. -After this optimization, the worst-case and average-case time complexities of bubble sort remain $O(n^2)$; however, when the input array is already sorted, the best-case time complexity becomes $O(n)$. +With this optimization, the worst-case and average time complexities remain $O(n^2)$. However, a fully sorted input achieves the best-case time complexity of $O(n)$. ```src [file]{bubble_sort}-[class]{}-[func]{bubble_sort_with_flag} ``` -## Algorithm Characteristics +## Algorithm properties -- **Time complexity is $O(n^2)$; adaptive**: In successive rounds of "bubbling", the traversed portion of the array has lengths $n - 1$, $n - 2$, $\dots$, $2$, $1$, for a total of $(n - 1) n / 2$. After introducing the `flag` optimization, the best-case time complexity can reach $O(n)$. -- **Space complexity of $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space. -- **Stable sorting**: Equal elements are not swapped during "bubbling". +- **Time complexity: $O(n^2)$; adaptive**. Successive passes traverse $n - 1$, $n - 2$, $\dots$, $2$, and $1$ positions, totaling $(n - 1) n / 2$. With the `flag` optimization, the best-case time complexity is $O(n)$. +- **Space complexity: $O(1)$; in-place**. Pointers $i$ and $j$ use constant extra space. +- **Stable**. Equal elements are not swapped during a bubbling pass. diff --git a/en/docs/chapter_sorting/bucket_sort.md b/en/docs/chapter_sorting/bucket_sort.md index 625cfb85e..14e26db18 100644 --- a/en/docs/chapter_sorting/bucket_sort.md +++ b/en/docs/chapter_sorting/bucket_sort.md @@ -1,18 +1,18 @@ -# Bucket Sort +# Bucket sort -The sorting algorithms discussed earlier are all comparison-based sorting algorithms, which sort by comparing the relative order of elements. The worst-case time complexity of such algorithms has a lower bound of $\Omega(n \log n)$. Next, we will explore several non-comparison sorting algorithms, whose time complexity can be linear. +The sorting algorithms discussed so far are “comparison-based”: they sort by comparing elements. Their worst-case time complexity has a lower bound of $\Omega(n \log n)$. We now turn to several “non-comparison” sorting algorithms, which can achieve linear time complexity. -Bucket sort is a typical application of the divide-and-conquer strategy. It works by creating a sequence of ordered buckets, each corresponding to a data range, and distributing the data evenly among them. The elements within each bucket are then sorted separately. Finally, all buckets are merged in order. +Bucket sort is a classic application of divide and conquer. It creates ordered buckets, each corresponding to a range of values, and distributes the data evenly among them. It then sorts each bucket separately and combines the results in bucket order. -## Algorithm Flow +## Algorithm steps -Consider an array of length $n$, whose elements are floating-point numbers in the range $[0, 1)$. The flow of bucket sort is shown in the figure below. +Consider an array of length $n$ containing floating-point values in $[0, 1)$. Bucket sort proceeds as shown below. -1. Initialize $k$ buckets and distribute the $n$ elements into the $k$ buckets. -2. Sort each bucket separately (here we use the built-in sorting function of the programming language). -3. Merge the results in order from smallest to largest bucket. +1. Initialize $k$ buckets and distribute the $n$ elements among the $k$ buckets. +2. Sort each bucket separately, using the programming language's built-in sorting function here. +3. Combine the results in ascending bucket order. -![Bucket sort algorithm flow](bucket_sort.assets/bucket_sort_overview.png) +![The bucket sort process](bucket_sort.assets/bucket_sort_overview.png) The code is as follows: @@ -20,26 +20,26 @@ The code is as follows: [file]{bucket_sort}-[class]{}-[func]{bucket_sort} ``` -## Algorithm Characteristics +## Algorithm properties -Bucket sort is suitable for processing very large datasets. For example, suppose the input contains 1 million elements, and limited memory prevents the system from loading all of them at once. In that case, the data can be divided into 1000 buckets, each bucket can be sorted separately, and the results can then be merged. +Bucket sort is useful for very large datasets. For example, suppose the input contains one million elements and memory limitations prevent loading all of them at once. We can divide them into 1,000 buckets, sort the buckets separately, and combine the results. -- **Time complexity is $O(n + k)$**: Assuming the elements are evenly distributed across the buckets, each bucket contains $\frac{n}{k}$ elements. If sorting a single bucket takes $O(\frac{n}{k} \log\frac{n}{k})$ time, then sorting all buckets takes $O(n \log\frac{n}{k})$ time. **When the number of buckets $k$ is relatively large, the time complexity approaches $O(n)$**. Merging the results requires traversing all buckets and elements, which takes $O(n + k)$ time. In the worst case, all data is placed into a single bucket, and sorting that bucket takes $O(n^2)$ time. -- **Space complexity is $O(n + k)$, and bucket sort is not in-place**: It requires extra space for $k$ buckets and a total of $n$ elements. -- Whether bucket sort is stable depends on whether the algorithm for sorting elements within buckets is stable. +- **Time complexity: $O(n + k)$**. If the elements are evenly distributed, each bucket contains $\frac{n}{k}$ elements. If sorting one bucket takes $O(\frac{n}{k} \log\frac{n}{k})$ time, sorting all buckets takes $O(n \log\frac{n}{k})$. **When the number of buckets $k$ is relatively large, this approaches $O(n)$**. Combining the results visits all buckets and elements, taking $O(n + k)$ time. In the worst case, all data falls into one bucket, and sorting it takes $O(n^2)$ time. +- **Space complexity: $O(n + k)$; not in-place**. Extra space is needed for $k$ buckets and a total of $n$ elements. +- Whether bucket sort is stable depends on whether the algorithm used to sort within each bucket is stable. -## How to Achieve Even Distribution +## Distributing elements evenly -In theory, bucket sort can achieve $O(n)$ time complexity. **The key is to distribute the elements evenly across the buckets**, because real-world data is often not uniformly distributed. For example, suppose we want to divide all products on Taobao evenly into 10 buckets by price range, but the price distribution is uneven: there are many products priced below 100 yuan and very few priced above 1000 yuan. If the price range is divided evenly into 10 intervals, the numbers of products in the buckets will differ greatly. +Bucket sort can theoretically achieve $O(n)$ time. **The key is distributing elements evenly among the buckets**, since real data is often unevenly distributed. Suppose we want to divide all products on Taobao evenly into 10 buckets by price. There are many products below 100 yuan but few above 1,000 yuan. Splitting the price range into 10 equal intervals would leave very different numbers of products in the buckets. -To achieve a more even distribution, we can first choose a rough boundary and partition the data into 3 buckets. **After that, buckets containing more products can be further divided into 3 buckets until the numbers of elements in all buckets are roughly equal**. +To obtain an even distribution, we can first choose rough boundaries and divide the data into 3 buckets. **Then split the more crowded buckets into 3 smaller buckets, repeating until all buckets contain roughly equal numbers of elements**. -As shown in the figure below, this method essentially builds a recursion tree whose goal is to make the leaf nodes as balanced as possible. Of course, the data does not have to be split into 3 buckets in every round; the specific partitioning strategy can be chosen flexibly based on the characteristics of the data. +As shown below, this effectively creates a recursion tree whose leaf values should be as balanced as possible. We need not use 3 buckets at every step; the subdivision can be chosen to suit the data. -![Recursively dividing buckets](bucket_sort.assets/scatter_in_buckets_recursively.png) +![Recursively subdividing buckets](bucket_sort.assets/scatter_in_buckets_recursively.png) -If we know the probability distribution of product prices in advance, **we can set the price boundaries for each bucket according to that distribution**. Notably, the data distribution does not need to be measured exactly; it can also be approximated with a probability model chosen to fit the characteristics of the data. +If we know the probability distribution of product prices in advance, **we can choose bucket boundaries according to that distribution**. We do not necessarily need to measure the distribution explicitly; we can approximate it with a probability model suited to the data. -As shown in the figure below, we assume that product prices follow a normal distribution, which allows us to reasonably set price intervals to evenly distribute products to each bucket. +In the figure below, we assume prices follow a normal distribution. This lets us choose suitable price intervals and distribute products evenly among the buckets. -![Dividing buckets based on probability distribution](bucket_sort.assets/scatter_in_buckets_distribution.png) +![Defining buckets from a probability distribution](bucket_sort.assets/scatter_in_buckets_distribution.png) diff --git a/en/docs/chapter_sorting/counting_sort.md b/en/docs/chapter_sorting/counting_sort.md index 800c44f33..df9e98e27 100644 --- a/en/docs/chapter_sorting/counting_sort.md +++ b/en/docs/chapter_sorting/counting_sort.md @@ -1,16 +1,16 @@ -# Counting Sort +# Counting sort -Counting sort sorts by counting the occurrences of elements and is typically applied to integer arrays. +Counting sort sorts by counting how often each element occurs. It is typically used for integer arrays. -## Simple Implementation +## A simple implementation -Let's start with a simple example. Given an array `nums` of length $n$, where the elements are all "non-negative integers", the overall flow of counting sort is shown in the figure below. +Start with a simple example: an array `nums` of length $n$ containing nonnegative integers. The overall process is shown below. -1. Traverse the array to find the largest number, denoted as $m$, and then create an auxiliary array `counter` of length $m + 1$. -2. **Use `counter` to count how many times each number appears in `nums`**, where `counter[num]` stores the number of occurrences of `num`. This is simple: traverse `nums` (denote the current number by `num`) and increment `counter[num]` by $1$ each time. -3. **Because the indices of `counter` are naturally ordered, the numbers are effectively already sorted**. Next, traverse `counter` and write the numbers back into `nums` in ascending order according to their occurrence counts. +1. Traverse the array to find its maximum value, denoted by $m$, then create an auxiliary array `counter` of length $m + 1$. +2. **Use `counter` to count occurrences of each number in `nums`**. Here, `counter[num]` is the number of occurrences of `num`. Simply traverse `nums` and increment `counter[num]` by $1$ for each current value `num`. +3. **Because the indices of `counter` are inherently ordered, the numbers are effectively already sorted**. Traverse `counter` and write each number into `nums` in ascending order, repeating it according to its count. -![Counting sort flow](counting_sort.assets/counting_sort_overview.png) +![The counting sort process](counting_sort.assets/counting_sort_overview.png) The code is as follows: @@ -18,29 +18,29 @@ The code is as follows: [file]{counting_sort}-[class]{}-[func]{counting_sort_naive} ``` -!!! note "Connection between counting sort and bucket sort" +!!! note "How counting sort relates to bucket sort" - From the perspective of bucket sort, each index of the counting array `counter` can be viewed as a bucket, and the counting process can be seen as distributing elements into their corresponding buckets. Essentially, counting sort is a special case of bucket sort for integer data. + From a bucket-sort perspective, each index of `counter` is a bucket, and counting occurrences is equivalent to distributing elements into their corresponding buckets. Counting sort is essentially a special case of bucket sort for integer data. -## Complete Implementation +## A complete implementation -Observant readers may have noticed that **if the input consists of objects, step `3.` above no longer works**. Suppose the input consists of product objects and we want to sort them by price (a member variable of the class); the above algorithm can only produce the sorted order of the prices themselves. +You may have noticed that **step `3.` above fails when the input consists of objects**. Suppose the inputs are product objects and we want to sort them by price, a member variable of the class. The algorithm above can produce only the sorted prices. -So how can we obtain the sorted order of the original data? We first compute the prefix sums of `counter`. As the name suggests, the prefix sum at index `i`, `prefix[i]`, equals the sum of the elements from index `0` through `i`: +How can we obtain the sorted original data? First, calculate the “prefix sums” of `counter`. The prefix sum `prefix[i]` is the sum of the array entries up to index `i`: $$ \text{prefix}[i] = \sum_{j=0}^i \text{counter[j]} $$ -**The prefix sum has a clear interpretation: `prefix[num] - 1` gives the index of the last occurrence of element `num` in the result array `res`**. This information is crucial because it tells us where each element should be placed in the result array. Next, we traverse the original array `nums` in reverse, and for each element `num`, perform the following two steps. +**The prefix sum has a precise meaning: `prefix[num] - 1` is the index of the last occurrence of `num` in the result array `res`**. This tells us where each element belongs. Next, traverse the original array `nums` in reverse and perform two steps for each element `num`. -1. Place `num` at index `prefix[num] - 1` of the array `res`. -2. Decrease the prefix sum `prefix[num]` by $1$ to get the index for the next placement of `num`. +1. Place `num` at index `prefix[num] - 1` in `res`. +2. Decrease `prefix[num]` by $1$ to obtain the index for placing the next occurrence of `num`. -After the traversal is complete, the array `res` contains the sorted result, and finally `res` is used to overwrite the original array `nums`. The complete counting sort flow is shown in the figure below. +After the traversal, `res` contains the sorted result. Finally, copy `res` over the original array `nums`. The figure below shows the complete process. === "<1>" - ![Counting sort steps](counting_sort.assets/counting_sort_step1.png) + ![Steps of counting sort](counting_sort.assets/counting_sort_step1.png) === "<2>" ![counting_sort_step2](counting_sort.assets/counting_sort_step2.png) @@ -63,22 +63,22 @@ After the traversal is complete, the array `res` contains the sorted result, and === "<8>" ![counting_sort_step8](counting_sort.assets/counting_sort_step8.png) -The counting sort implementation is shown below: +The implementation is as follows: ```src [file]{counting_sort}-[class]{}-[func]{counting_sort} ``` -## Algorithm Characteristics +## Algorithm properties -- **Time complexity is $O(n + m)$, and counting sort is non-adaptive**: Traversing `nums` and `counter` both takes linear time. In general, when $n \gg m$, the time complexity approaches $O(n)$. -- **Space complexity of $O(n + m)$, non-in-place sorting**: Uses arrays `res` and `counter` of lengths $n$ and $m$ respectively. -- **Stable sorting**: Since elements are filled into `res` in a "right-to-left" order, traversing `nums` in reverse can avoid changing the relative positions of equal elements, thereby achieving stable sorting. In fact, traversing `nums` in forward order can also yield correct sorting results, but the result would be unstable. +- **Time complexity: $O(n + m)$; not adaptive**. Traversing `nums` and `counter` each takes linear time. Usually $n \gg m$, so the complexity approaches $O(n)$. +- **Space complexity: $O(n + m)$; not in-place**. The algorithm uses arrays `res` and `counter` of lengths $n$ and $m$, respectively. +- **Stable**. Since elements are placed into `res` from right to left, traversing `nums` in reverse preserves the relative order of equal elements. A forward traversal also produces a correctly sorted result, but it is not stable. ## Limitations -At this point, you might think counting sort is quite ingenious because it achieves efficient sorting simply by counting occurrences. However, the prerequisites for using counting sort are fairly restrictive. +Counting sort may seem ingenious: it sorts efficiently simply by counting occurrences. However, its prerequisites are relatively restrictive. -**Counting sort is only applicable to non-negative integers**. To apply it to other types of data, you must ensure that they can be converted to non-negative integers without changing the relative ordering of the elements. For example, for an integer array containing negative numbers, you can first add a constant to every number to shift them into the non-negative range, and then shift them back after sorting. +**Counting sort applies only to nonnegative integers**. For other data types, the values must be convertible to nonnegative integers without changing their relative order. For example, if an integer array contains negative numbers, add a constant to every element to make them all positive, sort, and then reverse the transformation. -**Counting sort is well suited to cases with many elements but a small value range**. For example, in the above scenario, $m$ cannot be too large; otherwise, it consumes too much space. And when $n \ll m$, counting sort takes $O(m)$ time, which may be slower than sorting algorithms with $O(n \log n)$ time complexity. +**Counting sort suits large datasets with a relatively small value range**. In the example above, $m$ must not be too large, or the algorithm will consume excessive space. When $n \ll m$, counting sort takes $O(m)$ time and may be slower than an $O(n \log n)$ sorting algorithm. diff --git a/en/docs/chapter_sorting/exercises.md b/en/docs/chapter_sorting/exercises.md index 5702b1551..e22e94b7c 100644 --- a/en/docs/chapter_sorting/exercises.md +++ b/en/docs/chapter_sorting/exercises.md @@ -1,94 +1,94 @@ # Exercises -## Concept Review +## Check your understanding -### The First Few Rounds of Selection Sort and Bubble Sort +### The first passes of selection sort and bubble sort -Given the array `[4, 2, 5, 1, 3]`, sort it in ascending order in both parts below. +Given the array `[4, 2, 5, 1, 3]`, sort in ascending order throughout this exercise. -1. Simulate the first two rounds of selection sort. Write the array after each round and identify which positions are now fixed. -2. Simulate the first round of bubble sort. Write the resulting array and the number of swaps, and identify which position is now fixed. +1. Simulate the first two passes of selection sort. Write the array after each pass and identify the positions whose final values are now fixed. +2. Simulate the first pass of bubble sort. Write the array and the number of swaps, and identify the position whose final value is now fixed. -??? success "Answer" +??? success "Reference answer" - 1. The first two rounds are: + 1. The first two passes are: - | Round | Array | Explanation | + | Pass | Array | Explanation | | --- | --- | --- | - | 1 | `[1, 2, 5, 4, 3]` | The smallest element, 1, is swapped with the first element | + | 1 | `[1, 2, 5, 4, 3]` | Swap the minimum element, 1, with the first element | | 2 | `[1, 2, 5, 4, 3]` | The value 2 is already at index 1, so no swap is needed | - The first two positions are now fixed. Later rounds need to find the smallest element only within `[5, 4, 3]`. + The first two positions are now fixed. Subsequent passes need only continue selecting the minimum from `[5, 4, 3]`. - 2. Compare adjacent elements in order: swap 4 and 2; do not swap 4 and 5; swap 5 and 1; then swap 5 and 3. - The result is `[2, 4, 1, 3, 5]`, after 3 swaps. The largest element, 5, has moved to the end of the array, so the last position is now fixed. + 2. Compare adjacent elements in order: swap 4 and 2, leave 4 and 5 unchanged, swap 5 and 1, then swap 5 and 3. + The result is `[2, 4, 1, 3, 5]`, with three swaps. The maximum element, 5, has moved to the end, fixing the last position. -### Can Equal Elements Change Their Relative Order? +### Does the order of equal elements change? -In the array $[2_a, 2_b, 1]$, $2_a$ and $2_b$ have equal values, but their subscripts mark their original order. +In the array $[2_a, 2_b, 1]$, $2_a$ and $2_b$ have equal values; the subscripts mark their original order. -1. Write the array after the first round of selection sort. Has the relative order of $2_a$ and $2_b$ changed? -2. Write the array after the first round of bubble sort. Has the relative order of $2_a$ and $2_b$ changed? -3. Based on the first two questions, explain how the two sorting algorithms differ in preserving the original order of equal elements. +1. Write the array after the first pass of selection sort. Has the relative order of $2_a$ and $2_b$ changed? +2. Write the array after the first pass of bubble sort. Has the relative order of $2_a$ and $2_b$ changed? +3. Based on your answers, explain how the two algorithms differ in preserving the original order of equal elements. -??? success "Answer" +??? success "Reference answer" - 1. In its first round, selection sort selects the smallest element, 1, and swaps it with the first element, $2_a$, producing - $[1, 2_b, 2_a]$. The relative order has changed because $2_a$ has moved behind $2_b$. + 1. The first pass of selection sort selects the minimum element, 1, and swaps it with the first element, $2_a$, producing + $[1, 2_b, 2_a]$. The swap moves $2_a$ after $2_b$, changing their relative order. - 2. Bubble sort first compares $2_a$ and $2_b$. Because they are equal, it does not swap them. It then compares $2_b$ and 1 and swaps them, - producing $[2_a, 1, 2_b]$ after the first round. $2_a$ is still before $2_b$, so their relative order has not changed. + 2. Bubble sort first compares $2_a$ with $2_b$ and does not swap them because they are equal. It then compares and swaps $2_b$ and 1. + After the first pass, the array is $[2_a, 1, 2_b]$. Since $2_a$ still precedes $2_b$, their relative order is unchanged. - 3. In this example, selection sort changes the original order of equal elements. Bubble sort swaps adjacent elements only when the left element is greater than the right one. - Equal elements are not swapped, so their original relative order is preserved. + 3. In this example, selection sort changes the original order of equal elements. Bubble sort swaps adjacent elements only when the left element + is greater than the right one. Equal elements are never swapped with each other, so their original order is preserved. -### Compare Counting Sort and Radix Sort +### Comparing counting sort and radix sort -A school needs to sort many student ID numbers, each exactly 8 digits long. Answer the following questions: +A school needs to sort many student IDs, each exactly eight digits long. Answer the following: -1. How many rounds does radix sort need when it starts with the least significant digit? -2. If the student IDs are treated directly as integers for counting sort, why would the count array need many entries that are never used? -3. Based on the first two questions, which would you choose for sorting many fixed-length 8-digit student IDs: counting sort or radix sort? Why? +1. How many passes does radix sort need when starting with the least significant digit? +2. If counting sort treats each student ID directly as an integer, why would many counting entries go unused? +3. Based on your answers, which would you choose for sorting many eight-digit student IDs: counting sort or radix sort? Why? -??? success "Answer" +??? success "Reference answer" - 1. A student ID has 8 digits, so 8 rounds are needed from the least significant to the most significant digit. Each round groups values only by 0–9. + 1. Each ID has eight digits, so there are eight passes, from the least to the most significant digit. Each pass groups digits only by values 0 through 9. - 2. Direct counting would require an entry for every possible 8-digit value, but only a small fraction of those values are actually assigned to students. - Most entries in the count array would remain 0. + 2. Direct counting reserves an entry for every possible eight-digit value, but actual students use only a small fraction of them. + Most counts would remain 0. - 3. Radix sort is the better choice. It uses the facts that the length is fixed and each digit has only 10 possible values, requiring only 8 rounds of stable grouping. - Counting sort, if it used the entire 8-digit ID as an integer index, would require count-array entries for many values that never occur. + 3. Choose radix sort. It uses the fixed number of digits and the ten possible values per digit, requiring only eight stable grouping passes. + Counting sort using each entire eight-digit ID as an integer index would reserve entries for many values that never occur. -## Programming Exercises +## Programming exercises -### Sort an Array with Merge Sort +### Sorting an array with merge sort -Given an integer array `nums`, implement merge sort yourself, arrange its elements in nondecreasing order, and return the result. Do not call the language's built-in sorting function. +Given an integer array `nums`, implement merge sort yourself to arrange its elements in nondecreasing order and return the result. Do not call the language's built-in sorting function. ??? tip "Hints" - 1. An interval of length at most 1 is already sorted - 2. Divide the interval in half at its midpoint and recursively sort both halves - 3. Use two pointers to merge the two sorted halves, then write the result back into the original array + 1. An interval of length at most 1 is already sorted. + 2. Split the interval in half at the midpoint and recursively sort each half. + 3. Use two pointers to merge the sorted halves, then write the result back to the original array. [LeetCode](https://leetcode.com/problems/sort-an-array/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } -### Sort an Integer Array with Counting Sort +### Sorting an integer array with counting sort -You are given an integer array `nums` and a non-negative integer $K$. Every element of the array is between $0$ and $K$. +You are given an integer array `nums` and a nonnegative integer $K$. Every element lies between $0$ and $K$. -Implement counting sort, write the result back into `nums` in nondecreasing order, and return `nums`. -Do not determine the order by comparing elements, and do not call the language's built-in sorting function. +Implement counting sort, write the elements back into `nums` in nondecreasing order, and return `nums`. +Do not determine the order by comparing elements with one another, and do not use the language's built-in sorting function. ??? tip "Hints" - 1. Because every element is between 0 and K, use each element's value directly as an index in the counting array - 2. Scan nums once and increment the count at the corresponding position - 3. Then scan the counting array from 0 to K; if the value x occurs a certain number of times, write x into nums that many consecutive times + 1. Since every element is between 0 and K, its value can directly index the counting array. + 2. First scan nums, incrementing the count at the corresponding position by 1. + 3. Then scan the counting array from 0 through K. If x occurs a given number of times, write that many consecutive copies of x into nums. diff --git a/en/docs/chapter_sorting/heap_sort.md b/en/docs/chapter_sorting/heap_sort.md index 44e49fb46..6f5212d58 100644 --- a/en/docs/chapter_sorting/heap_sort.md +++ b/en/docs/chapter_sorting/heap_sort.md @@ -1,31 +1,31 @@ -# Heap Sort +# Heap sort !!! tip - Before reading this section, please ensure you have completed the "Heap" chapter. + Before reading this section, make sure you have completed the chapter on heaps. -Heap sort is an efficient sorting algorithm based on the heap data structure. We can implement heap sort using the heap construction and element removal operations introduced earlier. +Heap sort is an efficient sorting algorithm built on the heap data structure. We can implement it with the heap-building and removal operations we have already learned. -1. Input the array and build a min-heap, at which point the smallest element is at the heap top. -2. Continuously perform element removal operations and record the removed elements in order to obtain a sequence sorted in ascending order. +1. Build a min heap from the input array. The smallest element is now at the root. +2. Repeatedly remove the root and record the removed elements to obtain an ascending sequence. -Although the above method is feasible, it requires an additional array to save the popped elements, which is quite wasteful of space. In practice, we usually use a more elegant implementation method. +Although this works, it requires an extra array to store the removed elements, wasting space. In practice, we usually use a more elegant implementation. -## Algorithm Flow +## Algorithm steps -Assume the array length is $n$. The flow of heap sort is shown in the figure below. +For an array of length $n$, heap sort proceeds as shown below. -1. Input the array and build a max-heap. After completion, the largest element is at the heap top. -2. Swap the heap top element (first element) with the heap bottom element (last element). After the swap is complete, reduce the heap length by $1$ and increase the count of sorted elements by $1$. -3. Starting from the heap top element, perform a top-to-bottom heapify operation (sift down). After heapify is complete, the heap property is restored. -4. Repeat steps `2.` and `3.` After $n - 1$ rounds, the array is sorted. +1. Build a max heap from the input array. The largest element is now at the root. +2. Swap the root, the first element, with the last element of the heap. Reduce the heap's length by $1$ and increase the number of sorted elements by $1$. +3. Starting at the root, sift down to restore the heap property. +4. Repeat steps `2.` and `3.`. After $n - 1$ rounds, the array is sorted. !!! tip - In fact, the element removal operation also includes steps `2.` and `3.`, with the additional step of removing the element. + In fact, removing the root of a heap also includes steps `2.` and `3.`, with the additional step of removing an element from the array. === "<1>" - ![Heap sort steps](heap_sort.assets/heap_sort_step1.png) + ![Steps of heap sort](heap_sort.assets/heap_sort_step1.png) === "<2>" ![heap_sort_step2](heap_sort.assets/heap_sort_step2.png) @@ -60,14 +60,14 @@ Assume the array length is $n$. The flow of heap sort is shown in the figure bel === "<12>" ![heap_sort_step12](heap_sort.assets/heap_sort_step12.png) -In the code below, we use the same `sift_down()` function for top-to-bottom heapify as in the "Heap" chapter. It is worth noting that since the heap length decreases as the largest element is extracted, we need to add a length parameter $n$ to `sift_down()` to specify the current effective length of the heap. The code is as follows: +The implementation uses the same `sift_down()` function introduced in the heap chapter. Because the heap shrinks as maximum elements are extracted, add a length parameter $n$ to specify its current valid length. The code is as follows: ```src [file]{heap_sort}-[class]{}-[func]{heap_sort} ``` -## Algorithm Characteristics +## Algorithm properties -- **Time complexity is $O(n \log n)$; heap sort is non-adaptive**: Heap construction takes $O(n)$ time. Extracting the largest element from the heap takes $O(\log n)$ time, and this is repeated for a total of $n - 1$ rounds. -- **Space complexity is $O(1)$; heap sort is in-place**: A few pointer variables use $O(1)$ space. Element swapping and heapify are both performed on the original array. -- **Unstable sorting**: When swapping the heap top element and heap bottom element, the relative positions of equal elements may change. +- **Time complexity: $O(n \log n)$; not adaptive**. Building the heap takes $O(n)$ time. Extracting its maximum takes $O(\log n)$ time and is repeated $n - 1$ times. +- **Space complexity: $O(1)$; in-place**. A few pointer variables use $O(1)$ space. Swapping and sifting take place within the original array. +- **Not stable**. Swapping the root with the last element of the heap may change the relative order of equal elements. diff --git a/en/docs/chapter_sorting/index.md b/en/docs/chapter_sorting/index.md index 980965aef..8d4c585b3 100644 --- a/en/docs/chapter_sorting/index.md +++ b/en/docs/chapter_sorting/index.md @@ -4,6 +4,6 @@ !!! abstract - Sorting is like a magic key that transforms chaos into order, enabling us to understand and process data more efficiently. + Sorting is like a magic key that turns chaos into order, helping us understand and process data more efficiently. - From simple ascending order to more complex classification schemes, sorting reveals the harmonious beauty of data. + From a simple ascending sequence to a complex arrangement by category, sorting reveals the harmonious beauty of data. diff --git a/en/docs/chapter_sorting/insertion_sort.md b/en/docs/chapter_sorting/insertion_sort.md index 3d1150329..a3a6dfe32 100644 --- a/en/docs/chapter_sorting/insertion_sort.md +++ b/en/docs/chapter_sorting/insertion_sort.md @@ -1,46 +1,46 @@ -# Insertion Sort +# Insertion sort -Insertion sort is a simple sorting algorithm that works very similarly to the process of manually sorting a deck of cards. +Insertion sort is a simple sorting algorithm that works much like arranging a hand of playing cards. -Specifically, we select a base element from the unsorted portion, compare it one by one with the elements in the sorted portion to its left, and insert it into the correct position. +Choose a base element from the unsorted section, compare it with the elements in the sorted section to its left, and insert it into the correct position. -The figure below illustrates how an element is inserted into an array. Let the base element be `base`. We need to shift all elements between the target index and `base` one position to the right, and then assign `base` to the target index. +The figure below shows how to insert an element into an array. Let the base element be `base`. Shift all elements between the target index and `base` one position to the right, then assign `base` to the target index. -![Single insertion operation](insertion_sort.assets/insertion_operation.png) +![A single insertion operation](insertion_sort.assets/insertion_operation.png) -## Algorithm Flow +## Algorithm steps -The overall flow of insertion sort is shown in the figure below. +The overall insertion sort process is shown below. -1. Initially, the first element of the array is already sorted. -2. Select the second element of the array as `base`, and after inserting it into the correct position, **the first 2 elements of the array are sorted**. -3. Select the third element as `base`, and after inserting it into the correct position, **the first 3 elements of the array are sorted**. -4. And so on. In the last round, select the last element as `base`, and after inserting it into the correct position, **all elements are sorted**. +1. Initially, the first element of the array is treated as sorted. +2. Select the second element as `base` and insert it into the correct position. **The first two elements are now sorted**. +3. Select the third element as `base` and insert it into the correct position. **The first three elements are now sorted**. +4. Continue in this way. In the final round, select the last element as `base` and insert it into the correct position. **All elements are now sorted**. -![Insertion sort flow](insertion_sort.assets/insertion_sort_overview.png) +![The insertion sort process](insertion_sort.assets/insertion_sort_overview.png) -Example code is as follows: +An implementation follows: ```src [file]{insertion_sort}-[class]{}-[func]{insertion_sort} ``` -## Algorithm Characteristics +## Algorithm properties -- **Time complexity of $O(n^2)$, adaptive sorting**: In the worst case, the insertion operations require $n - 1$, $n-2$, $\dots$, $2$, and $1$ iterations, respectively, summing to $(n - 1) n / 2$, so the time complexity is $O(n^2)$. When the data is already sorted, each insertion operation terminates early. When the input array is completely sorted, insertion sort achieves its best-case time complexity of $O(n)$. -- **Space complexity of $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space. -- **Stable sorting**: During insertion, we place elements to the right of equal elements, so their relative order is unchanged. +- **Time complexity: $O(n^2)$; adaptive**. In the worst case, the insertion operations require $n - 1$, $n-2$, $\dots$, $2$, and $1$ iterations, totaling $(n - 1) n / 2$, so the time complexity is $O(n^2)$. For ordered data, an insertion can stop early. A fully sorted input achieves the best-case time complexity of $O(n)$. +- **Space complexity: $O(1)$; in-place**. Pointers $i$ and $j$ use constant extra space. +- **Stable**. Each element is inserted to the right of equal elements, preserving their order. -## Advantages of Insertion Sort +## Why use insertion sort? -The time complexity of insertion sort is $O(n^2)$, while the time complexity of quick sort, which we will learn about next, is $O(n \log n)$. Although insertion sort has a higher time complexity, **it is usually faster on small datasets**. +Insertion sort takes $O(n^2)$ time, while quick sort, which we will study shortly, takes $O(n \log n)$. Despite its higher time complexity, **insertion sort is usually faster for small datasets**. -This conclusion is similar to the one about when linear search and binary search are applicable. Algorithms such as quick sort, with $O(n \log n)$ complexity, are divide-and-conquer sorting algorithms and often involve more primitive operations. When the dataset is small, the values of $n^2$ and $n \log n$ are relatively close, so asymptotic complexity does not dominate; instead, the number of primitive operations per round becomes the deciding factor. +This resembles the comparison between linear and binary search. Algorithms such as quick sort, with $O(n \log n)$ time complexity, use divide and conquer and tend to involve more elementary operations. When the dataset is small, $n^2$ and $n \log n$ are relatively close, so asymptotic complexity is not the dominant factor; the number of elementary operations per round matters more. -In fact, the built-in sorting functions of many programming languages (such as Java) use insertion sort. The general idea is: for large arrays, use divide-and-conquer sorting algorithms such as quick sort; for short arrays, use insertion sort directly. +In fact, the built-in sorting functions of many programming languages, such as Java, use insertion sort. The general approach is to use a divide-and-conquer algorithm such as quick sort for long arrays and insertion sort directly for short ones. -Although bubble sort, selection sort, and insertion sort all have a time complexity of $O(n^2)$, in actual situations, **insertion sort is used significantly more frequently than bubble sort and selection sort**, mainly for the following reasons. +Although bubble sort, selection sort, and insertion sort all take $O(n^2)$ time, **insertion sort is used much more often in practice**, mainly for the following reasons. -- Bubble sort is implemented through element swaps, which require a temporary variable and involve 3 primitive operations; insertion sort is implemented through element assignment and requires only 1 primitive operation. Therefore, **bubble sort usually has higher computational overhead than insertion sort**. -- Selection sort has a time complexity of $O(n^2)$ in any case. **If given a set of partially ordered data, insertion sort is usually more efficient than selection sort**. -- Selection sort is unstable and cannot be applied to multi-level sorting. +- Bubble sort swaps elements, using a temporary variable and three elementary operations. Insertion sort shifts elements by assignment, requiring just one operation. **Bubble sort therefore usually has higher computational overhead**. +- Selection sort takes $O(n^2)$ time in every case. **For partially sorted data, insertion sort is usually more efficient than selection sort**. +- Selection sort is not stable and therefore cannot be used for multilevel sorting. diff --git a/en/docs/chapter_sorting/merge_sort.md b/en/docs/chapter_sorting/merge_sort.md index 075371c31..597950fbc 100644 --- a/en/docs/chapter_sorting/merge_sort.md +++ b/en/docs/chapter_sorting/merge_sort.md @@ -1,23 +1,23 @@ -# Merge Sort +# Merge sort -Merge sort is a sorting algorithm based on a divide-and-conquer strategy, consisting of the "divide" and "merge" phases shown in the figure below. +Merge sort is a divide-and-conquer sorting algorithm with the “division” and “merging” phases shown below. -1. **Divide phase**: Recursively split the array at the midpoint, reducing the problem of sorting a long array to the problem of sorting shorter arrays. -2. **Merge phase**: When a sub-array has length 1, stop dividing and start merging, continuously combining the shorter sorted sub-arrays on the left and right into a longer sorted array until the process is complete. +1. **Division**: recursively split the array at its midpoint, turning the problem of sorting a long array into problems of sorting shorter arrays. +2. **Merging**: stop dividing when a subarray has length 1, then repeatedly merge two shorter sorted arrays into one longer sorted array until the process is complete. -![Divide and merge phases of merge sort](merge_sort.assets/merge_sort_overview.png) +![The division and merging phases of merge sort](merge_sort.assets/merge_sort_overview.png) -## Algorithm Flow +## Algorithm steps -As shown in the figure below, the "divide phase" recursively splits the array from the midpoint into two sub-arrays from top to bottom. +As shown below, the division phase works from the top down, recursively splitting each array into two subarrays at its midpoint. -1. Calculate the array midpoint `mid`, recursively divide the left sub-array (interval `[left, mid]`) and right sub-array (interval `[mid + 1, right]`). -2. Repeat step `1.` recursively until a sub-array has length 1. +1. Calculate the midpoint `mid`, then recursively divide the left subarray, `[left, mid]`, and the right subarray, `[mid + 1, right]`. +2. Repeat step `1.` recursively until each subarray has length 1. -The "merge phase" merges the left and right sub-arrays into a sorted array from bottom to top. Note that merging starts from sub-arrays of length 1, so every sub-array involved in this phase is already sorted. +The merging phase works from the bottom up, combining the left and right subarrays into a sorted array. Merging begins with subarrays of length 1, so every subarray involved in this phase is already sorted. === "<1>" - ![Merge sort steps](merge_sort.assets/merge_sort_step1.png) + ![Steps of merge sort](merge_sort.assets/merge_sort_step1.png) === "<2>" ![merge_sort_step2](merge_sort.assets/merge_sort_step2.png) @@ -46,28 +46,28 @@ The "merge phase" merges the left and right sub-arrays into a sorted array from === "<10>" ![merge_sort_step10](merge_sort.assets/merge_sort_step10.png) -The recursive order of merge sort is consistent with the post-order traversal of a binary tree. +Merge sort follows the same recursive order as a postorder traversal of a binary tree. -- **Post-order traversal**: First recursively traverse the left subtree, then recursively traverse the right subtree, and finally process the root node. -- **Merge sort**: First recursively process the left sub-array, then recursively process the right sub-array, and finally perform the merge. +- **Postorder traversal**: recurse into the left subtree, then the right subtree, and finally process the root. +- **Merge sort**: recurse into the left subarray, then the right subarray, and finally merge them. -The implementation of merge sort is shown in the code below. Note that the interval to be merged in `nums` is `[left, right]`, while the corresponding interval in `tmp` is `[0, right - left]`. +The implementation follows. The interval to merge in `nums` is `[left, right]`, while its corresponding interval in `tmp` is `[0, right - left]`. ```src [file]{merge_sort}-[class]{}-[func]{merge_sort} ``` -## Algorithm Characteristics +## Algorithm properties -- **Time complexity is $O(n \log n)$; merge sort is non-adaptive**: The divide phase produces a recursion tree of height $\log n$, and the total number of operations performed during merging at each level is $n$, so the overall time complexity is $O(n \log n)$. -- **Space complexity is $O(n)$; merge sort is not in-place**: The recursion depth is $\log n$, which uses $O(\log n)$ stack-frame space. The merge operation requires an auxiliary array, which uses $O(n)$ additional space. -- **Stable sort**: During merging, the relative order of equal elements remains unchanged. +- **Time complexity: $O(n \log n)$; not adaptive**. Division produces a recursion tree of height $\log n$. Merging performs a total of $n$ operations at each level, giving $O(n \log n)$ overall. +- **Space complexity: $O(n)$; not in-place**. The recursion depth is $\log n$, requiring $O(\log n)$ space for stack frames. Merging uses an auxiliary array requiring $O(n)$ extra space. +- **Stable**. Merging preserves the order of equal elements. -## Linked List Sorting +## Sorting linked lists -For linked lists, merge sort has significant advantages over other sorting algorithms, **and it can reduce the space complexity of the sorting task to $O(1)$**. +Merge sort has a significant advantage over other sorting algorithms for linked lists: **it can reduce the space complexity of sorting a linked list to $O(1)$**. -- **Divide phase**: Iteration can be used instead of recursion to split the linked list, thereby eliminating the stack-frame space used by recursion. -- **Merge phase**: In linked lists, node insertion and deletion require only pointer updates, so the merge phase (merging two short sorted linked lists into one longer sorted linked list) does not require creating an additional linked list. +- **Division**: use iteration instead of recursion to divide the list, eliminating the space needed for recursive stack frames. +- **Merging**: inserting and deleting linked-list nodes requires only changing references or pointers. Merging two short sorted lists into a longer one therefore needs no additional list. -The specific implementation details are quite complex, and interested readers can consult related materials for learning. +The implementation is fairly involved. Interested readers can consult further resources for details. diff --git a/en/docs/chapter_sorting/quick_sort.md b/en/docs/chapter_sorting/quick_sort.md index da7f8e707..3044814d2 100644 --- a/en/docs/chapter_sorting/quick_sort.md +++ b/en/docs/chapter_sorting/quick_sort.md @@ -1,15 +1,15 @@ -# Quick Sort +# Quick sort -Quick sort is an efficient and widely used sorting algorithm based on the divide-and-conquer strategy. +Quick sort is an efficient, widely used sorting algorithm based on divide and conquer. -The core operation of quick sort is "sentinel partitioning", whose goal is to select an element as the "pivot", move all elements smaller than the pivot to its left, and move all elements larger than the pivot to its right. Specifically, the process is shown in the figure below. +Its core operation is “partitioning”: choose an array element as the “pivot,” move all smaller elements to its left, and move all larger elements to its right. The partitioning process is shown below. -1. Select the leftmost element as the pivot, and initialize two pointers `i` and `j` at the two ends of the array. -2. Enter a loop. In each round, use `i` (`j`) to find the first element larger (smaller) than the pivot, and then swap the two elements. -3. Repeat step `2.` until `i` and `j` meet, then swap the pivot into the boundary position between the two sub-arrays. +1. Choose the leftmost element as the pivot and initialize pointers `i` and `j` at the two ends of the array. +2. In a loop, use `i` to find the first element greater than the pivot and `j` to find the first element smaller than it, then swap those elements. +3. Repeat step `2.` until `i` and `j` meet. Finally, swap the pivot into the boundary between the two subarrays. === "<1>" - ![Sentinel partitioning steps](quick_sort.assets/pivot_division_step1.png) + ![Steps of partitioning](quick_sort.assets/pivot_division_step1.png) === "<2>" ![pivot_division_step2](quick_sort.assets/pivot_division_step2.png) @@ -35,65 +35,65 @@ The core operation of quick sort is "sentinel partitioning", whose goal is to se === "<9>" ![pivot_division_step9](quick_sort.assets/pivot_division_step9.png) -After sentinel partitioning, the original array is divided into three parts: the left sub-array, the pivot, and the right sub-array, such that "any element in the left sub-array $\leq$ the pivot $\leq$ any element in the right sub-array". Therefore, we only need to sort the two sub-arrays next. +After partitioning, the original array has three parts: the left subarray, the pivot, and the right subarray. Every element in the left subarray is $\leq$ the pivot, which is $\leq$ every element in the right subarray. We now need only sort the two subarrays. -!!! note "Divide-and-conquer strategy of quick sort" +!!! note "Quick sort's divide-and-conquer strategy" - The essence of sentinel partitioning is to simplify the sorting problem of a longer array into the sorting problems of two shorter arrays. + Partitioning reduces the problem of sorting one long array to sorting two shorter arrays. ```src [file]{quick_sort}-[class]{quick_sort}-[func]{partition} ``` -## Algorithm Flow +## Algorithm steps -The overall flow of quick sort is shown in the figure below. +The overall quick sort process is shown below. -1. First, perform one "sentinel partitioning" on the original array to obtain the unsorted left sub-array and right sub-array. -2. Then, recursively perform "sentinel partitioning" on the left sub-array and right sub-array respectively. -3. Continue recursively until the sub-array length is 1, at which point sorting of the entire array is complete. +1. Partition the original array to obtain unsorted left and right subarrays. +2. Recursively partition the left and right subarrays. +3. Continue until each subarray has length 1, completing the sort. -![Quick sort flow](quick_sort.assets/quick_sort_overview.png) +![The quick sort process](quick_sort.assets/quick_sort_overview.png) ```src [file]{quick_sort}-[class]{quick_sort}-[func]{quick_sort} ``` -## Algorithm Characteristics +## Algorithm properties -- **Time complexity of $O(n \log n)$, non-adaptive sorting**: On average, sentinel partitioning produces $\log n$ recursive levels, and the total number of loop iterations across each level is $n$, so the overall time complexity is $O(n \log n)$. In the worst case, each round of sentinel partitioning splits an array of length $n$ into sub-arrays of lengths $0$ and $n - 1$. The recursion depth then reaches $n$, with $n$ loop iterations at each level, yielding an overall time complexity of $O(n^2)$. -- **Space complexity of $O(n)$, in-place sorting**: In the case where the input array is completely reversed, the worst recursive depth reaches $n$, using $O(n)$ stack frame space. The sorting operation is performed on the original array without the aid of an additional array. -- **Unstable sorting**: In the last step of sentinel partitioning, the pivot may be swapped to the right of an equal element. +- **Time complexity: $O(n \log n)$; not adaptive**. On average, partitioning produces $\log n$ recursive levels, with $n$ loop iterations in total at each level, giving $O(n \log n)$ time. In the worst case, each partition splits an array of length $n$ into subarrays of lengths $0$ and $n - 1$. The recursion reaches $n$ levels, each involving up to $n$ iterations, for $O(n^2)$ time overall. +- **Space complexity: $O(n)$; in-place**. A fully reverse-sorted input reaches the worst-case recursion depth of $n$, requiring $O(n)$ space for stack frames. Sorting takes place in the original array without an auxiliary array. +- **Not stable**. In the final partitioning step, the pivot may be swapped to the right of an equal element. -## Why Is Quick Sort Fast +## Why is quick sort quick? -As the name suggests, quick sort has clear efficiency advantages. Although its average time complexity is the same as that of "merge sort" and "heap sort", quick sort is usually faster in practice for the following reasons. +As its name suggests, quick sort has an efficiency advantage. Although its average time complexity matches that of merge sort and heap sort, it is usually faster, mainly for the following reasons. -- **The worst case is unlikely to occur**: Although the worst-case time complexity of quick sort is $O(n^2)$ and its performance is less predictable than that of merge sort, quick sort runs in $O(n \log n)$ time in the vast majority of cases. -- **High cache efficiency**: During sentinel partitioning, the system can load the entire sub-array into cache, so accessing elements is relatively efficient. By contrast, algorithms such as "heap sort" require non-contiguous access to elements and therefore do not enjoy this advantage. -- **Small constant factors**: Among the three algorithms above, quick sort performs the fewest comparisons, assignments, and swaps in total. This is similar to why "insertion sort" is faster than "bubble sort". +- **The worst case is unlikely**. Quick sort's worst-case time complexity is $O(n^2)$, making its performance less predictable than merge sort's, but it runs in $O(n \log n)$ time in the vast majority of cases. +- **Efficient cache use**. During partitioning, the system can load the whole subarray into cache, making element access efficient. Algorithms such as heap sort jump between elements and do not share this advantage. +- **Small constant factors**. Of these three algorithms, quick sort performs the fewest comparisons, assignments, swaps, and similar operations overall. This resembles the reason insertion sort is faster than bubble sort. -## Pivot Optimization +## Improving pivot selection -**Quick sort can become less time-efficient for certain inputs**. Consider an extreme example in which the input array is in completely descending order. Because we choose the leftmost element as the pivot, once sentinel partitioning is complete, the pivot is swapped to the far right of the array, leaving a left sub-array of length $n - 1$ and a right sub-array of length $0$. If this continues recursively, each round of sentinel partitioning produces one sub-array of length $0$, the divide-and-conquer strategy breaks down, and quick sort degenerates into an approximation of "bubble sort". +**Quick sort can become slower on certain inputs**. Consider a fully reverse-sorted array. Because the leftmost element is chosen as the pivot, partitioning moves it to the far right, leaving subarrays of lengths $n - 1$ and $0$. Continuing recursively produces a subarray of length $0$ after every partition. Divide and conquer becomes ineffective, and quick sort degenerates into a process resembling bubble sort. -To reduce the chance of this happening, **we can optimize the pivot selection strategy used in sentinel partitioning**. For example, we can choose a pivot at random. However, if we are unlucky and repeatedly pick poor pivots, performance can still be unsatisfactory. +To reduce this risk, **we can improve how the pivot is selected**. For example, choose a random element as the pivot. With bad luck, however, every pivot may still be poor, leading to disappointing performance. -It should be noted that programming languages usually generate "pseudo-random numbers". If we construct a specific test case against a pseudo-random sequence, quick sort can still suffer degraded performance. +Programming languages generally produce pseudorandom numbers. A test case constructed specifically for a pseudorandom sequence can still degrade quick sort's performance. -To improve further, we can choose three candidate elements from the array, usually the first, last, and middle elements, **and use the median of the three as the pivot**. This greatly increases the chance that the pivot is "neither too small nor too large". We can also choose more candidate elements to further improve the robustness of the algorithm. With this method, the probability that the time complexity degrades to $O(n^2)$ is significantly reduced. +For further improvement, choose three candidate elements, usually the first, last, and middle elements, **and use their median as the pivot**. This greatly increases the chance that the pivot is neither too small nor too large. We can use more candidates to improve robustness further. This approach greatly reduces the likelihood of degradation to $O(n^2)$. -Example code is as follows: +An implementation follows: ```src [file]{quick_sort}-[class]{quick_sort_median}-[func]{partition} ``` -## Recursive Depth Optimization +## Reducing recursion depth -**Quick sort may also use more space for certain inputs**. Consider a fully sorted input array. Let the length of the current sub-array in the recursion be $m$. Each round of sentinel partitioning produces a left sub-array of length $0$ and a right sub-array of length $m - 1$, which means each recursive call reduces the problem size by only one element. The recursion tree can therefore reach a height of $n - 1$, requiring $O(n)$ stack-frame space. +**Quick sort may use considerable space on certain inputs**. For a fully sorted input, let the current subarray have length $m$. Each partition produces a left subarray of length $0$ and a right subarray of length $m - 1$. Each recursive call therefore reduces the problem size by only one element, producing a recursion tree of height $n - 1$ and requiring $O(n)$ space for stack frames. -To prevent stack frames from accumulating, we can compare the lengths of the two sub-arrays after each round of sentinel partitioning, **and recurse only on the shorter one**. Because the shorter sub-array has length at most $n / 2$, this method ensures that the recursion depth does not exceed $\log n$, reducing the worst-case space complexity to $O(\log n)$. The code is shown below: +To prevent stack frames from accumulating, compare the two subarray lengths after each partition and **recurse only on the shorter one**. Since that subarray has length at most $n / 2$, the recursion depth is at most $\log n$, reducing the worst-case space complexity to $O(\log n)$. The code is as follows: ```src [file]{quick_sort}-[class]{quick_sort_tail_call}-[func]{quick_sort} diff --git a/en/docs/chapter_sorting/radix_sort.md b/en/docs/chapter_sorting/radix_sort.md index d6a322fc7..20e5c0309 100644 --- a/en/docs/chapter_sorting/radix_sort.md +++ b/en/docs/chapter_sorting/radix_sort.md @@ -1,41 +1,41 @@ -# Radix Sort +# Radix sort -The previous section introduced counting sort, which is suitable when the number of items $n$ is large but the value range $m$ is small. Suppose we need to sort $n = 10^6$ student IDs, each of which is an 8-digit number. Then the value range $m = 10^8$ is very large. Using counting sort would require a large amount of memory, whereas radix sort avoids this problem. +The previous section introduced counting sort, which suits a large number of elements $n$ with a small value range $m$. Suppose we need to sort $n = 10^6$ student IDs, each an $8$-digit number. The value range $m = 10^8$ is very large, so counting sort would require substantial memory. Radix sort avoids this problem. -Radix sort is based on the same core idea as counting sort: it also sorts by counting occurrences. Building on this, radix sort exploits the positional relationship among digits and sorts them one digit at a time to obtain the final result. +Radix sort shares counting sort's core idea of sorting by counting occurrences. It also uses the relative significance of digit positions, sorting one digit at a time to obtain the final order. -## Algorithm Flow +## Algorithm steps -Taking student ID data as an example, assume the lowest digit is the $1$st digit and the highest digit is the $8$th digit. The flow of radix sort is shown in the figure below. +For the student IDs, number the least significant digit as $1$ and the most significant as $8$. Radix sort proceeds as shown below. -1. Initialize the digit $k = 1$. -2. Perform "counting sort" on the $k$th digit of the student IDs. After completion, the data will be sorted from smallest to largest according to the $k$th digit. -3. Increase $k$ by $1$, then return to step `2.` and continue iterating until all digits are sorted, at which point the process ends. +1. Initialize the digit position $k = 1$. +2. Apply counting sort to the $k$th digit of each student ID. The data is now sorted in ascending order by its $k$th digit. +3. Increase the digit position $k$ by $1$ and return to step `2.`. Continue until all digit positions have been processed. -![Radix sort algorithm flow](radix_sort.assets/radix_sort_overview.png) +![The radix sort process](radix_sort.assets/radix_sort_overview.png) -Next, let us look at the code. For a number $x$ in base $d$, its $k$th digit $x_k$ can be obtained with the following formula: +Now consider the implementation. For a number $x$ in base $d$, its $k$th digit $x_k$ can be obtained with the following formula: $$ x_k = \lfloor\frac{x}{d^{k-1}}\rfloor \bmod d $$ -Here, $\lfloor a \rfloor$ denotes rounding the floating-point number $a$ down, and $\bmod \: d$ denotes taking the remainder modulo $d$. For student ID data, $d = 10$ and $k \in [1, 8]$. +Here, $\lfloor a \rfloor$ rounds the floating-point value $a$ down, and $\bmod \: d$ takes the remainder modulo $d$. For the student IDs, $d = 10$ and $k \in [1, 8]$. -Additionally, we need to slightly modify the counting sort code to make it sort based on the $k$th digit of the number: +We also need to modify counting sort slightly so that it sorts by the $k$th digit: ```src [file]{radix_sort}-[class]{}-[func]{radix_sort} ``` -!!! question "Why start sorting from the lowest digit?" +!!! question "Why start with the least significant digit?" - In successive sorting passes, a later pass overrides the result of an earlier one. For example, if the first pass yields $a < b$ but the second yields $a > b$, then the result of the second pass prevails. Because higher-order digits have higher priority than lower-order digits, we should sort the lower digits first and then the higher digits. + In successive rounds, a later sort takes precedence over an earlier one. For example, if the first round orders two elements as $a < b$ but the second orders them as $a > b$, the second result replaces the first. Higher digits have greater priority, so lower digits must be sorted first. -## Algorithm Characteristics +## Algorithm properties -Compared with counting sort, radix sort is suitable for larger value ranges, **but only when the data can be represented with a fixed number of digits and that digit count is not too large**. For example, floating-point numbers are not well suited to radix sort because the digit count $k$ can be too large, potentially leading to time complexity $O(nk) \gg O(n^2)$. +Compared with counting sort, radix sort handles a larger range of values, **provided the data can be represented with a fixed number of digits and that number is not too large**. For example, floating-point numbers are unsuitable because their digit count $k$ may be so large that $O(nk) \gg O(n^2)$. -- **Time complexity of $O(nk)$, non-adaptive sorting**: Let the number of items be $n$, let the values be represented in base $d$, and let the maximum number of digits be $k$. Counting sort on one digit takes $O(n + d)$ time, so sorting all $k$ digits takes $O((n + d)k)$ time. In practice, $d$ and $k$ are usually relatively small, so the overall time complexity approaches $O(n)$. -- **Space complexity of $O(n + d)$, non-in-place sorting**: Same as counting sort, radix sort requires auxiliary arrays `res` and `counter` of lengths $n$ and $d$. -- **Stable sort**: When counting sort is stable, radix sort is also stable; when counting sort is unstable, radix sort cannot guarantee correct sorting results. +- **Time complexity: $O(nk)$; not adaptive**. For $n$ elements represented in base $d$ with at most $k$ digits, counting sort on one digit takes $O(n + d)$ time. Sorting all $k$ digits takes $O((n + d)k)$. Usually $d$ and $k$ are relatively small, so the complexity approaches $O(n)$. +- **Space complexity: $O(n + d)$; not in-place**. Like counting sort, radix sort requires arrays `res` and `counter` of lengths $n$ and $d$. +- **Stable**. Radix sort is stable when the counting sort used within it is stable. If counting sort is not stable, radix sort cannot guarantee a correctly sorted result. diff --git a/en/docs/chapter_sorting/selection_sort.md b/en/docs/chapter_sorting/selection_sort.md index d37644bcd..48087d570 100644 --- a/en/docs/chapter_sorting/selection_sort.md +++ b/en/docs/chapter_sorting/selection_sort.md @@ -1,17 +1,17 @@ -# Selection Sort +# Selection sort -Selection sort works very simply: in each round, it selects the smallest element from the unsorted interval and places it at the end of the sorted interval. +Selection sort works very simply: in each round, select the smallest element from the unsorted section and place it at the end of the sorted section. -Assume the array has length $n$. The procedure of selection sort is shown in the figure below. +For an array of length $n$, the algorithm proceeds as shown below. -1. Initially, all elements are unsorted, i.e., the unsorted (index) interval is $[0, n-1]$. -2. Select the smallest element in the interval $[0, n-1]$ and swap it with the element at index $0$. After completion, the first element of the array is sorted. -3. Select the smallest element in the interval $[1, n-1]$ and swap it with the element at index $1$. After completion, the first 2 elements of the array are sorted. -4. And so on. After $n - 1$ rounds of selection and swapping, the first $n - 1$ elements of the array are sorted. -5. The only remaining element must be the largest, so no further sorting is needed and the array is sorted. +1. Initially, all elements are unsorted, so the unsorted index interval is $[0, n-1]$. +2. Select the smallest element in $[0, n-1]$ and swap it with the element at index $0$. The first element is now sorted. +3. Select the smallest element in $[1, n-1]$ and swap it with the element at index $1$. The first two elements are now sorted. +4. Continue in this way. After $n - 1$ rounds of selection and swapping, the first $n - 1$ elements are sorted. +5. The remaining element must be the largest and needs no sorting. The array is now sorted. === "<1>" - ![Selection sort steps](selection_sort.assets/selection_sort_step1.png) + ![Steps of selection sort](selection_sort.assets/selection_sort_step1.png) === "<2>" ![selection_sort_step2](selection_sort.assets/selection_sort_step2.png) @@ -43,16 +43,16 @@ Assume the array has length $n$. The procedure of selection sort is shown in the === "<11>" ![selection_sort_step11](selection_sort.assets/selection_sort_step11.png) -In the code, we use $k$ to track the smallest element within the unsorted interval: +In the code, $k$ tracks the smallest element in the unsorted section: ```src [file]{selection_sort}-[class]{}-[func]{selection_sort} ``` -## Algorithm Characteristics +## Algorithm properties -- **Time complexity $O(n^2)$, non-adaptive sorting**: The outer loop has $n - 1$ rounds in total. The inner loop runs $n - 1$ times in the first round and $1$ time in the last round. Thus, it runs $n - 1$, $n - 2$, $\dots$, $2$, and $1$ times across the rounds, summing to $\frac{n(n - 1)}{2}$. -- **Space complexity $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space. -- **Unstable sorting**: As shown in the figure below, element `nums[i]` may be swapped to the right of an element equal to it, causing a change in their relative order. +- **Time complexity: $O(n^2)$; not adaptive**. The outer loop runs $n - 1$ times. The inner loop runs $n - 1$ times in the first round and $1$ time in the last, giving successive counts of $n - 1$, $n - 2$, $\dots$, $2$, and $1$, which sum to $\frac{n(n - 1)}{2}$. +- **Space complexity: $O(1)$; in-place**. Pointers $i$ and $j$ use constant extra space. +- **Not stable**. As shown below, `nums[i]` may be swapped to the right of an equal element, changing their relative order. -![Selection sort non-stability example](selection_sort.assets/selection_sort_instability.png) +![An example showing that selection sort is not stable](selection_sort.assets/selection_sort_instability.png) diff --git a/en/docs/chapter_sorting/sorting_algorithm.md b/en/docs/chapter_sorting/sorting_algorithm.md index df15d1295..358df9571 100644 --- a/en/docs/chapter_sorting/sorting_algorithm.md +++ b/en/docs/chapter_sorting/sorting_algorithm.md @@ -1,23 +1,23 @@ -# Sorting Algorithm +# Sorting algorithms -A sorting algorithm arranges a set of data in a specific order. Sorting algorithms have extensive applications because ordered data can usually be searched, analyzed, and processed more efficiently. +A sorting algorithm arranges a dataset in a specified order. Sorting is widely used because ordered data is often easier to search, analyze, and process efficiently. -As shown in the figure below, the data being sorted can be integers, floating-point numbers, characters, strings, and so on. The sorting rule can be defined as needed, such as numerical order, ASCII order, or a custom rule. +As shown below, the data may consist of integers, floating-point numbers, characters, strings, and other types. The ordering rule can be chosen as needed: numerical magnitude, ASCII character order, or a custom rule. -![Data type and criterion examples](sorting_algorithm.assets/sorting_examples.png) +![Examples of data types and ordering rules](sorting_algorithm.assets/sorting_examples.png) -## Evaluation Dimensions +## Evaluation criteria -**Execution efficiency**: We expect the time complexity of sorting algorithms to be as low as possible, with a smaller total number of operations (reducing the constant factor in time complexity). For large data volumes, execution efficiency is particularly important. +**Efficiency**: We want low time complexity and few operations overall, corresponding to smaller constant factors in the complexity. Efficiency is especially important for large datasets. -**In-place property**: As the name implies, in-place sorting achieves sorting by operating directly on the original array without requiring additional auxiliary arrays, thus saving memory. Typically, in-place sorting involves fewer data movement operations and runs faster. +**In-place operation**: An in-place sort works directly on the original array without an auxiliary array, saving memory. It usually moves data fewer times and runs faster as well. -**Stability**: Stable sorting ensures that the relative order of equal elements in the array does not change after sorting is completed. +**Stability**: A stable sort preserves the relative order of equal elements in the array. -Stable sorting is a necessary condition for multi-level sorting scenarios. Suppose we have a table storing student information, where column 1 and column 2 are name and age, respectively. In this case, unstable sorting may cause the ordered nature of the input data to be lost: +Stable sorting is necessary for multilevel sorting. Suppose a table stores student names in its first column and ages in its second. An unstable sort may destroy the input's existing order: ```shell -# The input data is sorted by name +# The input is already sorted by name # (name, age) ('A', 19) ('B', 18) @@ -25,9 +25,9 @@ Stable sorting is a necessary condition for multi-level sorting scenarios. Suppo ('D', 19) ('E', 23) -# Suppose we use an unstable sorting algorithm to sort the list by age. -# In the result, the relative positions of ('D', 19) and ('A', 19) change, -# so the property that the input data is sorted by name is lost. +# Suppose an unstable algorithm sorts the list by age. +# The relative positions of ('D', 19) and ('A', 19) change, +# so the original ordering by name is lost ('B', 18) ('D', 19) ('A', 19) @@ -35,12 +35,12 @@ Stable sorting is a necessary condition for multi-level sorting scenarios. Suppo ('E', 23) ``` -**Adaptability**: Adaptive sorting can utilize the existing order information in the input data to reduce the amount of computation, achieving better time efficiency. The best-case time complexity of adaptive sorting algorithms is typically better than the average time complexity. +**Adaptiveness**: An adaptive sort uses ordering already present in the input to reduce computation and improve efficiency. Its best-case time complexity is usually better than its average complexity. -**Comparison-based or non-comparison**: Comparison-based sorting relies on comparison operators ($<$, $=$, $>$) to determine the relative order of elements, thereby sorting the entire array. Its worst-case time complexity has a lower bound of $\Omega(n \log n)$. Non-comparison sorting does not use comparison operators and can achieve a time complexity of $O(n)$, but its versatility is relatively limited. +**Comparison-based or not**: A comparison-based sort uses comparison operators ($<$, $=$, $>$) to determine the relative order of elements and sort the array. Its worst-case time complexity has a lower bound of $\Omega(n \log n)$. A non-comparison sort does not use comparison operators and can achieve $O(n)$ time, but is less broadly applicable. -## Ideal Sorting Algorithm +## The ideal sorting algorithm -**Fast, in-place, stable, adaptive, and broadly applicable**. Clearly, no sorting algorithm has been discovered to date that combines all of these characteristics. Therefore, when selecting a sorting algorithm, it is necessary to decide based on the specific characteristics of the data and the requirements of the problem. +**Fast, in-place, stable, adaptive, and broadly applicable**. No sorting algorithm with all these properties has yet been found. We must therefore choose according to the characteristics of the data and the requirements of the problem. -Next, we will examine various sorting algorithms and analyze their advantages and disadvantages based on the evaluation dimensions above. +Next, we will study different sorting algorithms and analyze their strengths and weaknesses using these criteria. diff --git a/en/docs/chapter_sorting/summary.md b/en/docs/chapter_sorting/summary.md index f57f7a5f0..0fccbf3f1 100644 --- a/en/docs/chapter_sorting/summary.md +++ b/en/docs/chapter_sorting/summary.md @@ -1,47 +1,47 @@ # Summary -### Key Review +### Key takeaways -- Bubble sort achieves sorting by swapping adjacent elements. By adding a flag to enable early return, we can optimize the best-case time complexity of bubble sort to $O(n)$. -- In each round, insertion sort inserts an element from the unsorted portion into its correct position in the sorted portion. Although insertion sort has a time complexity of $O(n^2)$, it remains very popular for small sorting tasks because each operation is relatively lightweight. -- Quick sort relies on sentinel partitioning. In sentinel partitioning, repeatedly choosing the worst possible pivot can degrade the time complexity to $O(n^2)$. Choosing a median-based pivot or a random pivot can reduce the probability of this degradation. By recursing on the shorter subarray first, we can effectively reduce the recursion depth and optimize the space complexity to $O(\log n)$. -- Merge sort includes two phases: divide and merge, which typically embody the divide-and-conquer strategy. In merge sort, sorting an array requires creating auxiliary arrays, with a space complexity of $O(n)$; however, the space complexity of sorting a linked list can be optimized to $O(1)$. -- Bucket sort consists of three steps: distributing data into buckets, sorting within buckets, and merging results. It also embodies the divide-and-conquer strategy and is suitable for very large data volumes. The key to bucket sort is distributing data evenly. -- Counting sort is a special case of bucket sort, which achieves sorting by counting the number of occurrences of data. Counting sort is suitable for situations where the data volume is large but the data range is limited, and requires that data can be converted to positive integers. -- Radix sort achieves data sorting by sorting digit by digit, requiring that data can be represented as fixed-digit numbers. -- Overall, we hope to find a sorting algorithm that is efficient, stable, in-place, and adaptive. However, as with other data structures and algorithms, no sorting algorithm can satisfy all of these criteria at the same time. In practice, we need to choose the appropriate sorting algorithm based on the characteristics of the data. -- The figure below compares mainstream sorting algorithms in terms of efficiency, stability, in-place property, and adaptability. +- Bubble sort sorts by swapping adjacent elements. Adding a flag to stop early improves its best-case time complexity to $O(n)$. +- Insertion sort inserts one element from the unsorted section into its correct position in the sorted section each round. Although its time complexity is $O(n^2)$, its low operation count makes it popular for small datasets. +- Quick sort sorts through partitioning. Repeatedly choosing the worst pivot can degrade its time complexity to $O(n^2)$. Median or random pivot selection reduces this risk. Recursing on the shorter subarray reduces recursion depth and improves space complexity to $O(\log n)$. +- Merge sort has division and merging phases, exemplifying divide and conquer. Sorting an array requires an auxiliary array and $O(n)$ space, while sorting a linked list can be optimized to use $O(1)$ space. +- Bucket sort has three steps: distribute data into buckets, sort within each bucket, and combine the results. It also uses divide and conquer and suits very large datasets. The key is distributing data evenly. +- Counting sort is a special case of bucket sort that sorts by counting occurrences. It suits large datasets with a limited value range and requires data that can be converted to positive integers. +- Radix sort sorts one digit at a time and requires data that can be represented as numbers with a fixed number of digits. +- Ideally, a sorting algorithm would be efficient, stable, in-place, and adaptive. As with other data structures and algorithms, however, no single sorting algorithm meets all these requirements. In practice, choose one according to the data's properties. +- The figure below compares common sorting algorithms in efficiency, stability, in-place operation, and adaptiveness. -![Sorting algorithm comparison](summary.assets/sorting_algorithms_comparison.png) +![Comparison of sorting algorithms](summary.assets/sorting_algorithms_comparison.png) ### Q & A -**Q**: In what situations is the stability of sorting algorithms necessary? +**Q**: When is stability necessary in a sorting algorithm? -In reality, we may sort based on a certain attribute of objects. For example, students have two attributes: name and height. We want to implement multi-level sorting: first sort by name to get `(A, 180) (B, 185) (C, 170) (D, 170)`; then sort by height. Because the sorting algorithm is unstable, we may get `(D, 170) (C, 170) (A, 180) (B, 185)`. +We often sort objects by one of their attributes. For example, students have names and heights, and we want a multilevel sort. First sort by name to obtain `(A, 180) (B, 185) (C, 170) (D, 170)`, then sort by height. An unstable algorithm may produce `(D, 170) (C, 170) (A, 180) (B, 185)`. -We can see that students D and C have swapped positions, destroying the ordering by name, which is not what we want. +Students D and C have switched positions, breaking the ordering by name. This is not what we want. -**Q**: Can the order of "searching from right to left" and "searching from left to right" in sentinel partitioning be swapped? +**Q**: Can the right-to-left and left-to-right scans in partitioning be reversed? -No. When we use the leftmost element as the pivot, we must first "search from right to left" and then "search from left to right". This conclusion is somewhat counterintuitive; let's analyze the reason. +No. When the leftmost element is the pivot, the right-to-left scan must come first. This may seem counterintuitive, so let us examine why. -The last step of sentinel partitioning `partition()` is to swap `nums[left]` and `nums[i]`. After the swap is complete, the elements to the left of the pivot are all `<=` the pivot, **which requires that `nums[left] >= nums[i]` must hold before the last swap**. Suppose we first "search from left to right", then if we cannot find an element larger than the pivot, **we will exit the loop when `i == j`, at which point it may be that `nums[j] == nums[i] > nums[left]`**. In other words, the last swap operation will swap an element larger than the pivot to the leftmost end of the array, causing sentinel partitioning to fail. +The final step of `partition()` swaps `nums[left]` and `nums[i]`. Afterward, every element to the left of the pivot must be `<=` the pivot, **so `nums[left] >= nums[i]` must hold before that swap**. If we scan left to right first and fail to find a larger element, **the loop stops at `i == j`, where it is possible that `nums[j] == nums[i] > nums[left]`**. The final swap would then move an element larger than the pivot to the far left, causing partitioning to fail. -For example, given the array `[0, 0, 0, 0, 1]`, if we first "search from left to right", the array after sentinel partitioning is `[1, 0, 0, 0, 0]`, which is incorrect. +For example, given `[0, 0, 0, 0, 1]`, scanning left to right first produces `[1, 0, 0, 0, 0]` after partitioning, which is incorrect. -By the same reasoning, if we select `nums[right]` as the pivot, the order is reversed: we must first "search from left to right". +Conversely, if we choose `nums[right]` as the pivot, we must scan left to right first. -**Q**: Regarding the optimization of recursion depth in quick sort, why can selecting the shorter array ensure that the recursion depth does not exceed $\log n$? +**Q**: In the recursion-depth optimization for quick sort, why does choosing the shorter subarray keep the depth at most $\log n$? -Recursion depth is the number of recursive calls that have not yet returned. Each round of sentinel partitioning divides the original array into two sub-arrays. After this optimization, the sub-array selected for further recursion is at most half the length of the original array. In the worst case, if it is always half as long, the final recursion depth is $\log n$. +Recursion depth is the number of recursive calls that have not yet returned. Each partition divides the array into two subarrays. After the optimization, the subarray chosen for recursion is at most half the original length. Even in the worst case, where it is always exactly half, the depth is $\log n$. -Reviewing the original quick sort, we may continuously recurse on the longer array. In the worst case, it would be $n$, $n - 1$, $\dots$, $2$, $1$, with a recursion depth of $n$. Recursion depth optimization can avoid this situation. +In the original quick sort, we may repeatedly recurse on the larger subarray, with lengths $n$, $n - 1$, $\dots$, $2$, and $1$ in the worst case, giving depth $n$. The optimization prevents this. -**Q**: When all elements in the array are equal, is the time complexity of quick sort $O(n^2)$? How should this degenerate case be handled? +**Q**: If all array elements are equal, does quick sort take $O(n^2)$ time? How can we handle this degeneration? -Yes. In this case, the array can be partitioned into three parts through sentinel partitioning: less than, equal to, and greater than the pivot. We then recurse only on the less-than and greater-than parts. With this approach, an array whose elements are all equal can be sorted in just one round of sentinel partitioning. +Yes. In this case, consider partitioning into three sections: elements smaller than, equal to, and greater than the pivot. Recurse only on the smaller and greater sections. If all elements are equal, a single partition then completes the sort. -**Q**: Why is the worst-case time complexity of bucket sort $O(n^2)$? +**Q**: Why is bucket sort's worst-case time complexity $O(n^2)$? -In the worst case, all elements are distributed into the same bucket. If we use an $O(n^2)$ algorithm to sort these elements, the time complexity will be $O(n^2)$. +In the worst case, all elements fall into one bucket. If an $O(n^2)$ algorithm is used to sort that bucket, the overall time complexity is $O(n^2)$. diff --git a/en/docs/chapter_stack_and_queue/deque.md b/en/docs/chapter_stack_and_queue/deque.md index d948438c1..cc8dbc836 100644 --- a/en/docs/chapter_stack_and_queue/deque.md +++ b/en/docs/chapter_stack_and_queue/deque.md @@ -1,39 +1,39 @@ -# Deque +# Deques -In a queue, we can only remove elements from the front or add elements at the rear. As shown in the figure below, a double-ended queue (deque) provides greater flexibility, allowing elements to be added or removed at both the front and the rear. +A queue allows removal only at the front and insertion only at the rear. As shown below, a double-ended queue, or deque, is more flexible: elements can be added or removed at either end. -![Operations of deque](deque.assets/deque_operations.png) +![Deque operations](deque.assets/deque_operations.png) -## Common Deque Operations +## Common deque operations -The common operations on a deque are shown in the table below. The specific method names depend on the programming language used. +The table below lists common operations. Actual method names depend on the programming language. -

Table   Efficiency of Deque Operations

+

Table   Efficiency of deque operations

-| Method | Description | Time Complexity | -| -------------- | ------------------------- | --------------- | -| `push_first()` | Add element to front | $O(1)$ | -| `push_last()` | Add element to rear | $O(1)$ | -| `pop_first()` | Remove front element | $O(1)$ | -| `pop_last()` | Remove rear element | $O(1)$ | -| `peek_first()` | Access front element | $O(1)$ | -| `peek_last()` | Access rear element | $O(1)$ | +| Method | Description | Time complexity | +| -------------- | ---------------- | ---------- | +| `push_first()` | Add an element at the front | $O(1)$ | +| `push_last()` | Add an element at the rear | $O(1)$ | +| `pop_first()` | Remove the front element | $O(1)$ | +| `pop_last()` | Remove the rear element | $O(1)$ | +| `peek_first()` | Access the front element | $O(1)$ | +| `peek_last()` | Access the rear element | $O(1)$ | -Similarly, we can directly use the deque classes provided by the programming language: +Again, we can use a deque class already provided by the programming language: === "Python" ```python title="deque.py" from collections import deque - # Initialize deque + # Initialize a deque deq: deque[int] = deque() # Enqueue elements - deq.append(2) # Add to rear + deq.append(2) # Add at the rear deq.append(5) deq.append(4) - deq.appendleft(3) # Add to front + deq.appendleft(3) # Add at the front deq.appendleft(1) # Access elements @@ -41,27 +41,27 @@ Similarly, we can directly use the deque classes provided by the programming lan rear: int = deq[-1] # Rear element # Dequeue elements - pop_front: int = deq.popleft() # Front element dequeue - pop_rear: int = deq.pop() # Rear element dequeue + pop_front: int = deq.popleft() # Remove the front element + pop_rear: int = deq.pop() # Remove the rear element - # Get deque length + # Get the deque size size: int = len(deq) - # Check if deque is empty + # Check whether the deque is empty is_empty: bool = len(deq) == 0 ``` === "C++" ```cpp title="deque.cpp" - /* Initialize deque */ + /* Initialize a deque */ deque deque; /* Enqueue elements */ - deque.push_back(2); // Add to rear + deque.push_back(2); // Add at the rear deque.push_back(5); deque.push_back(4); - deque.push_front(3); // Add to front + deque.push_front(3); // Add at the front deque.push_front(1); /* Access elements */ @@ -69,27 +69,27 @@ Similarly, we can directly use the deque classes provided by the programming lan int back = deque.back(); // Rear element /* Dequeue elements */ - deque.pop_front(); // Front element dequeue - deque.pop_back(); // Rear element dequeue + deque.pop_front(); // Remove the front element + deque.pop_back(); // Remove the rear element - /* Get deque length */ + /* Get the deque size */ int size = deque.size(); - /* Check if deque is empty */ + /* Check whether the deque is empty */ bool empty = deque.empty(); ``` === "Java" ```java title="deque.java" - /* Initialize deque */ + /* Initialize a deque */ Deque deque = new LinkedList<>(); /* Enqueue elements */ - deque.offerLast(2); // Add to rear + deque.offerLast(2); // Add at the rear deque.offerLast(5); deque.offerLast(4); - deque.offerFirst(3); // Add to front + deque.offerFirst(3); // Add at the front deque.offerFirst(1); /* Access elements */ @@ -97,28 +97,28 @@ Similarly, we can directly use the deque classes provided by the programming lan int peekLast = deque.peekLast(); // Rear element /* Dequeue elements */ - int popFirst = deque.pollFirst(); // Front element dequeue - int popLast = deque.pollLast(); // Rear element dequeue + int popFirst = deque.pollFirst(); // Remove the front element + int popLast = deque.pollLast(); // Remove the rear element - /* Get deque length */ + /* Get the deque size */ int size = deque.size(); - /* Check if deque is empty */ + /* Check whether the deque is empty */ boolean isEmpty = deque.isEmpty(); ``` === "C#" ```csharp title="deque.cs" - /* Initialize deque */ - // In C#, use LinkedList as a deque + /* Initialize a deque */ + // In C#, use a LinkedList as a deque LinkedList deque = new(); /* Enqueue elements */ - deque.AddLast(2); // Add to rear + deque.AddLast(2); // Add at the rear deque.AddLast(5); deque.AddLast(4); - deque.AddFirst(3); // Add to front + deque.AddFirst(3); // Add at the front deque.AddFirst(1); /* Access elements */ @@ -126,28 +126,28 @@ Similarly, we can directly use the deque classes provided by the programming lan int peekLast = deque.Last.Value; // Rear element /* Dequeue elements */ - deque.RemoveFirst(); // Front element dequeue - deque.RemoveLast(); // Rear element dequeue + deque.RemoveFirst(); // Remove the front element + deque.RemoveLast(); // Remove the rear element - /* Get deque length */ + /* Get the deque size */ int size = deque.Count; - /* Check if deque is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque.Count == 0; ``` === "Go" ```go title="deque_test.go" - /* Initialize deque */ - // In Go, use list as a deque + /* Initialize a deque */ + // In Go, use a list as a deque deque := list.New() /* Enqueue elements */ - deque.PushBack(2) // Add to rear + deque.PushBack(2) // Add at the rear deque.PushBack(5) deque.PushBack(4) - deque.PushFront(3) // Add to front + deque.PushFront(3) // Add at the front deque.PushFront(1) /* Access elements */ @@ -155,28 +155,28 @@ Similarly, we can directly use the deque classes provided by the programming lan rear := deque.Back() // Rear element /* Dequeue elements */ - deque.Remove(front) // Front element dequeue - deque.Remove(rear) // Rear element dequeue + deque.Remove(front) // Remove the front element + deque.Remove(rear) // Remove the rear element - /* Get deque length */ + /* Get the deque size */ size := deque.Len() - /* Check if deque is empty */ + /* Check whether the deque is empty */ isEmpty := deque.Len() == 0 ``` === "Swift" ```swift title="deque.swift" - /* Initialize deque */ - // Swift does not have a built-in deque class, can use Array as a deque + /* Initialize a deque */ + // Swift has no built-in deque class; use an Array as a deque var deque: [Int] = [] /* Enqueue elements */ - deque.append(2) // Add to rear + deque.append(2) // Add at the rear deque.append(5) deque.append(4) - deque.insert(3, at: 0) // Add to front + deque.insert(3, at: 0) // Add at the front deque.insert(1, at: 0) /* Access elements */ @@ -184,29 +184,29 @@ Similarly, we can directly use the deque classes provided by the programming lan let peekLast = deque.last! // Rear element /* Dequeue elements */ - // When using Array simulation, popFirst has O(n) complexity - let popFirst = deque.removeFirst() // Front element dequeue - let popLast = deque.removeLast() // Rear element dequeue + // When using an Array, popFirst takes O(n) time + let popFirst = deque.removeFirst() // Remove the front element + let popLast = deque.removeLast() // Remove the rear element - /* Get deque length */ + /* Get the deque size */ let size = deque.count - /* Check if deque is empty */ + /* Check whether the deque is empty */ let isEmpty = deque.isEmpty ``` === "JS" ```javascript title="deque.js" - /* Initialize deque */ - // JavaScript does not have a built-in deque, can only use Array as a deque + /* Initialize a deque */ + // JavaScript has no built-in deque; use an Array instead const deque = []; /* Enqueue elements */ deque.push(2); deque.push(5); deque.push(4); - // Please note that since it's an array, unshift() has O(n) time complexity + // Note that this is an array, so unshift() takes O(n) time deque.unshift(3); deque.unshift(1); @@ -215,29 +215,29 @@ Similarly, we can directly use the deque classes provided by the programming lan const peekLast = deque[deque.length - 1]; /* Dequeue elements */ - // Please note that since it's an array, shift() has O(n) time complexity + // Note that this is an array, so shift() takes O(n) time const popFront = deque.shift(); const popBack = deque.pop(); - /* Get deque length */ + /* Get the deque size */ const size = deque.length; - /* Check if deque is empty */ + /* Check whether the deque is empty */ const isEmpty = size === 0; ``` === "TS" ```typescript title="deque.ts" - /* Initialize deque */ - // TypeScript does not have a built-in deque, can only use Array as a deque + /* Initialize a deque */ + // TypeScript has no built-in deque; use an Array instead const deque: number[] = []; /* Enqueue elements */ deque.push(2); deque.push(5); deque.push(4); - // Please note that since it's an array, unshift() has O(n) time complexity + // Note that this is an array, so unshift() takes O(n) time deque.unshift(3); deque.unshift(1); @@ -246,29 +246,29 @@ Similarly, we can directly use the deque classes provided by the programming lan const peekLast: number = deque[deque.length - 1]; /* Dequeue elements */ - // Please note that since it's an array, shift() has O(n) time complexity + // Note that this is an array, so shift() takes O(n) time const popFront: number = deque.shift() as number; const popBack: number = deque.pop() as number; - /* Get deque length */ + /* Get the deque size */ const size: number = deque.length; - /* Check if deque is empty */ + /* Check whether the deque is empty */ const isEmpty: boolean = size === 0; ``` === "Dart" ```dart title="deque.dart" - /* Initialize deque */ + /* Initialize a deque */ // In Dart, Queue is defined as a deque Queue deque = Queue(); /* Enqueue elements */ - deque.addLast(2); // Add to rear + deque.addLast(2); // Add at the rear deque.addLast(5); deque.addLast(4); - deque.addFirst(3); // Add to front + deque.addFirst(3); // Add at the front deque.addFirst(1); /* Access elements */ @@ -276,27 +276,27 @@ Similarly, we can directly use the deque classes provided by the programming lan int peekLast = deque.last; // Rear element /* Dequeue elements */ - int popFirst = deque.removeFirst(); // Front element dequeue - int popLast = deque.removeLast(); // Rear element dequeue + int popFirst = deque.removeFirst(); // Remove the front element + int popLast = deque.removeLast(); // Remove the rear element - /* Get deque length */ + /* Get the deque size */ int size = deque.length; - /* Check if deque is empty */ + /* Check whether the deque is empty */ bool isEmpty = deque.isEmpty; ``` === "Rust" ```rust title="deque.rs" - /* Initialize deque */ + /* Initialize a deque */ let mut deque: VecDeque = VecDeque::new(); /* Enqueue elements */ - deque.push_back(2); // Add to rear + deque.push_back(2); // Add at the rear deque.push_back(5); deque.push_back(4); - deque.push_front(3); // Add to front + deque.push_front(3); // Add at the front deque.push_front(1); /* Access elements */ @@ -306,35 +306,35 @@ Similarly, we can directly use the deque classes provided by the programming lan } /* Dequeue elements */ - if let Some(pop_front) = deque.pop_front() { // Front element dequeue + if let Some(pop_front) = deque.pop_front() { // Remove the front element } - if let Some(pop_rear) = deque.pop_back() { // Rear element dequeue + if let Some(pop_rear) = deque.pop_back() { // Remove the rear element } - /* Get deque length */ + /* Get the deque size */ let size = deque.len(); - /* Check if deque is empty */ + /* Check whether the deque is empty */ let is_empty = deque.is_empty(); ``` === "C" ```c title="deque.c" - // C does not provide a built-in deque + // C provides no built-in deque ``` === "Kotlin" ```kotlin title="deque.kt" - /* Initialize deque */ + /* Initialize a deque */ val deque = LinkedList() /* Enqueue elements */ - deque.offerLast(2) // Add to rear + deque.offerLast(2) // Add at the rear deque.offerLast(5) deque.offerLast(4) - deque.offerFirst(3) // Add to front + deque.offerFirst(3) // Add at the front deque.offerFirst(1) /* Access elements */ @@ -342,28 +342,28 @@ Similarly, we can directly use the deque classes provided by the programming lan val peekLast = deque.peekLast() // Rear element /* Dequeue elements */ - val popFirst = deque.pollFirst() // Front element dequeue - val popLast = deque.pollLast() // Rear element dequeue + val popFirst = deque.pollFirst() // Remove the front element + val popLast = deque.pollLast() // Remove the rear element - /* Get deque length */ + /* Get the deque size */ val size = deque.size - /* Check if deque is empty */ + /* Check whether the deque is empty */ val isEmpty = deque.isEmpty() ``` === "Ruby" ```ruby title="deque.rb" - # Initialize deque - # Ruby does not have a built-in deque, can only use Array as a deque + # Initialize a deque + # Ruby has no built-in deque; use an Array instead deque = [] # Enqueue elements deque << 2 deque << 5 deque << 4 - # Please note that since it's an array, Array#unshift has O(n) time complexity + # Note that this is an array, so Array#unshift takes O(n) time deque.unshift(3) deque.unshift(1) @@ -372,35 +372,35 @@ Similarly, we can directly use the deque classes provided by the programming lan peek_last = deque.last # Dequeue elements - # Please note that since it's an array, Array#shift has O(n) time complexity + # Note that this is an array, so Array#shift takes O(n) time pop_front = deque.shift pop_back = deque.pop - # Get deque length + # Get the deque size size = deque.length - # Check if deque is empty + # Check whether the deque is empty is_empty = size.zero? ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%0A%20%20%20%20deq%20%3D%20deque%28%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E9%98%9F%0A%20%20%20%20deq.append%282%29%20%20%23%20%E6%B7%BB%E5%8A%A0%E8%87%B3%E9%98%9F%E5%B0%BE%0A%20%20%20%20deq.append%285%29%0A%20%20%20%20deq.append%284%29%0A%20%20%20%20deq.appendleft%283%29%20%20%23%20%E6%B7%BB%E5%8A%A0%E8%87%B3%E9%98%9F%E9%A6%96%0A%20%20%20%20deq.appendleft%281%29%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%20deque%20%3D%22,%20deq%29%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E5%85%83%E7%B4%A0%0A%20%20%20%20front%20%3D%20deq%5B0%5D%20%20%23%20%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%20front%20%3D%22,%20front%29%0A%20%20%20%20rear%20%3D%20deq%5B-1%5D%20%20%23%20%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%20rear%20%3D%22,%20rear%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20pop_front%20%3D%20deq.popleft%28%29%20%20%23%20%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20%20pop_front%20%3D%22,%20pop_front%29%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%87%BA%E9%98%9F%E5%90%8E%20deque%20%3D%22,%20deq%29%0A%20%20%20%20pop_rear%20%3D%20deq.pop%28%29%20%20%23%20%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20%20pop_rear%20%3D%22,%20pop_rear%29%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%87%BA%E9%98%9F%E5%90%8E%20deque%20%3D%22,%20deq%29%0A%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E7%9A%84%E9%95%BF%E5%BA%A6%0A%20%20%20%20size%20%3D%20len%28deq%29%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E9%95%BF%E5%BA%A6%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20len%28deq%29%20%3D%3D%200%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%20%3D%22,%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20deque%0A%20%20%20%20deq%20%3D%20deque%28%29%0A%0A%20%20%20%20%23%20Enqueue%20elements%0A%20%20%20%20deq.append%282%29%20%20%23%20Add%20at%20the%20rear%0A%20%20%20%20deq.append%285%29%0A%20%20%20%20deq.append%284%29%0A%20%20%20%20deq.appendleft%283%29%20%20%23%20Add%20at%20the%20front%0A%20%20%20%20deq.appendleft%281%29%0A%20%20%20%20print%28%22Deque%3A%20deque%20%3D%22%2C%20deq%29%0A%0A%20%20%20%20%23%20Access%20elements%0A%20%20%20%20front%20%3D%20deq%5B0%5D%20%20%23%20Front%20element%0A%20%20%20%20print%28%22Front%20element%3A%20front%20%3D%22%2C%20front%29%0A%20%20%20%20rear%20%3D%20deq%5B-1%5D%20%20%23%20Rear%20element%0A%20%20%20%20print%28%22Rear%20element%3A%20rear%20%3D%22%2C%20rear%29%0A%0A%20%20%20%20%23%20Dequeue%20elements%0A%20%20%20%20pop_front%20%3D%20deq.popleft%28%29%20%20%23%20Remove%20the%20front%20element%0A%20%20%20%20print%28%22Element%20removed%20from%20the%20front%3A%20pop_front%20%3D%22%2C%20pop_front%29%0A%20%20%20%20print%28%22After%20removing%20from%20the%20front%3A%20deque%20%3D%22%2C%20deq%29%0A%20%20%20%20pop_rear%20%3D%20deq.pop%28%29%20%20%23%20Remove%20the%20rear%20element%0A%20%20%20%20print%28%22Element%20removed%20from%20the%20rear%3A%20pop_rear%20%3D%22%2C%20pop_rear%29%0A%20%20%20%20print%28%22After%20removing%20from%20the%20rear%3A%20deque%20%3D%22%2C%20deq%29%0A%0A%20%20%20%20%23%20Get%20the%20deque%20size%0A%20%20%20%20size%20%3D%20len%28deq%29%0A%20%20%20%20print%28%22Deque%20size%3A%20size%20%3D%22%2C%20size%29%0A%0A%20%20%20%20%23%20Check%20whether%20the%20deque%20is%20empty%0A%20%20%20%20is_empty%20%3D%20len%28deq%29%20%3D%3D%200%0A%20%20%20%20print%28%22Is%20the%20deque%20empty%20%3D%22%2C%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## Deque Implementation * +## Implementing a deque * -The implementation of a deque is similar to that of a queue. You can choose either a linked list or an array as the underlying data structure. +A deque is implemented much like a queue, using either a linked list or an array as the underlying structure. -### Doubly Linked List Implementation +### Implementation with a doubly linked list -Reviewing the previous section, we used a regular singly linked list to implement a queue because it conveniently allows deleting the head node (corresponding to dequeue) and adding new nodes after the tail node (corresponding to enqueue). +In the previous section, we used a singly linked list for a queue because it easily supports removing the head for dequeueing and adding after the tail for enqueueing. -For a deque, both the front and rear can perform enqueue and dequeue operations. In other words, a deque needs to implement operations in the opposite direction as well. For this reason, we use a "doubly linked list" as the underlying data structure for the deque. +A deque supports both insertion and removal at both ends. It therefore needs the symmetric operations in the other direction as well. For this reason, we use a doubly linked list. -As shown in the figure below, we treat the head and tail nodes of the doubly linked list as the front and rear of the deque, implementing functionality to add and remove nodes at both ends. +As shown below, treat the list's head and tail as the deque's front and rear, and support adding and removing nodes at both ends. === "<1>" - ![Enqueue and dequeue operations in linked list implementation of deque](deque.assets/linkedlist_deque_step1.png) + ![Insertion and removal in a linked-list deque](deque.assets/linkedlist_deque_step1.png) === "<2>" ![linkedlist_deque_push_last](deque.assets/linkedlist_deque_step2_push_last.png) @@ -414,18 +414,18 @@ As shown in the figure below, we treat the head and tail nodes of the doubly lin === "<5>" ![linkedlist_deque_pop_first](deque.assets/linkedlist_deque_step5_pop_first.png) -The implementation code is shown below: +The implementation is as follows: ```src [file]{linkedlist_deque}-[class]{linked_list_deque}-[func]{} ``` -### Array Implementation +### Implementation with an array -As shown in the figure below, similar to implementing a queue based on an array, we can also use a circular array to implement a deque. +As with an array-based queue, we can use a circular array to implement a deque, as shown below. === "<1>" - ![Enqueue and dequeue operations in array implementation of deque](deque.assets/array_deque_step1.png) + ![Insertion and removal in an array-based deque](deque.assets/array_deque_step1.png) === "<2>" ![array_deque_push_last](deque.assets/array_deque_step2_push_last.png) @@ -439,14 +439,14 @@ As shown in the figure below, similar to implementing a queue based on an array, === "<5>" ![array_deque_pop_first](deque.assets/array_deque_step5_pop_first.png) -Based on the queue implementation, we only need to add methods for "enqueue at front" and "dequeue from rear": +Starting with the queue implementation, we need only add methods for insertion at the front and removal at the rear: ```src [file]{array_deque}-[class]{array_deque}-[func]{} ``` -## Deque Applications +## Applications -A deque combines the logic of both stacks and queues. **Therefore, it can implement all application scenarios of both, while providing greater flexibility**. +A deque combines stack and queue behavior, **so it supports all their applications while offering greater flexibility**. -We know that the "undo" function in software is typically implemented using a stack: the system pushes each change operation onto the stack and then implements undo through pop. However, considering system resource limitations, software usually limits the number of undo steps (for example, only allowing 50 steps to be saved). When the stack length exceeds 50, the software needs to perform a deletion operation at the bottom of the stack (front of the queue). **But a stack cannot implement this functionality, so a deque is needed to replace the stack**. Note that the core logic of "undo" still follows the LIFO principle of a stack; it's just that the deque can more flexibly implement some additional logic. +Software undo is usually implemented with a stack: the system uses `push` to record each change and `pop` to undo it. Because resources are limited, software often restricts the history to a fixed number of steps, such as $50$. When the stack grows beyond $50$, the oldest entry must be removed from the bottom, the front of the deque. **A stack cannot provide this operation, so a deque is needed instead**. The core undo behavior still follows last-in, first-out order; the deque simply provides flexibility for the additional logic. diff --git a/en/docs/chapter_stack_and_queue/exercises.md b/en/docs/chapter_stack_and_queue/exercises.md index 79cc72f05..555e18fe8 100644 --- a/en/docs/chapter_stack_and_queue/exercises.md +++ b/en/docs/chapter_stack_and_queue/exercises.md @@ -1,10 +1,10 @@ # Exercises -## Concept Review +## Check your understanding -### Which Element Leaves a Stack or Queue First? +### Which element comes out first in a stack or queue? -Prepare an empty stack `S` and an empty queue `Q`. Perform the same sequence of operations on each one: +Start with an empty stack `S` and an empty queue `Q`. Perform the same operations on each: Step 1: Add `A`. Step 2: Add `B`. @@ -12,46 +12,46 @@ Step 3: Remove and record one element. Step 4: Add `C`. Step 5: Keep removing and recording elements until the container is empty. -Write the order in which elements are removed from `S` and from `Q`. Explain the difference using "last-in-first-out" or "first-in-first-out." +Write the order in which elements are removed from `S` and from `Q`. Explain the difference using last in, first out or first in, first out. -??? success "Answer" +??? success "Reference answer" - The removal order for stack `S` is `B, C, A`. After the elements `A, B` are added, the most recently added element, `B`, is popped first. After `C` is added, - `C, A` are popped in that order. This is "last-in-first-out." + The removal order for stack `S` is `B, C, A`. After adding `A, B`, we first pop `B`, the most recently added element, and then add `C`. + We then pop `C, A` in order. This is last in, first out. - The removal order for queue `Q` is `A, B, C`. After the elements `A, B` are added, the earliest added element, `A`, is removed first. - After `C` is added, `B, C` are removed in that order. This is "first-in-first-out." + The removal order for queue `Q` is `A, B, C`. After adding `A, B`, we first remove `A`, the earliest element added. + We then add `C` and remove `B, C` in order. This is first in, first out. -### What Happens When the Rear Passes the End of the Array? +### What happens when the rear passes the end of the array? -A queue is implemented with a circular array of length 5, whose indices are `0–4`. -Currently, `front = 3` and `size = 2`; `A, B` are stored at indices 3 and 4, respectively. +A queue uses a circular array of length 5, with indices `0-4`. +Currently, `front = 3` and `size = 2`, with queue elements `A, B` at indices 3 and 4, respectively. -1. When `C` is enqueued, at which index should `C` be stored? What is `size` after the enqueue? -2. Next, dequeue once. Which element is removed? What are the new values of `front` and `size`? -3. What is the logical order from the front to the rear now? Does dequeuing require moving the other elements in the array? Why or why not? +1. When enqueueing `C`, at which index should `C` be placed? What is `size` afterward? +2. If we then dequeue once, which element is removed? What are the new values of `front` and `size`? +3. What is the logical order from front to rear now? Does dequeuing require moving the other array elements? Why? -??? success "Answer" +??? success "Reference answer" 1. The new element's position is `(front + size) % 5 = (3 + 2) % 5 = 0`, - so `C` is stored at index 0. After the enqueue, `size = 3`. + so `C` goes at index 0. After enqueueing, `size = 3`. - 2. Dequeuing removes the current front element, `A`. The new front index is + 2. Dequeueing removes the current front element, `A`. The new front index is `(3 + 1) % 5 = 4`, so `front = 4` and `size = 2`. - 3. The logical order of valid elements is `B, C`, with `B` at index 4 and `C` at index 0. - Dequeuing requires only changing `front` and `size`. The circular array uses the remainder operation to wrap the index back to the beginning, - so there is no need to shift all the other elements forward. + 3. The valid elements are logically ordered as `B, C`, with `B` at index 4 and `C` at index 0. + Dequeueing only advances `front` and updates `size`. The circular array uses the remainder operation to wrap indices back to the beginning, + so the other elements do not need to shift forward together. -### Operations at Both Ends of a Deque +### Operations at both ends of a deque -Here, `push_first` adds an element at the front, `push_last` adds one at the rear, -`pop_first` removes one from the front, and `pop_last` removes one from the rear. +Here, `push_first` adds at the front, `push_last` adds at the rear, +`pop_first` removes from the front, and `pop_last` removes from the rear. -Perform the following operations on an empty deque `deq`: +Starting with an empty deque `deq`, perform: 1. `push_last(A)` 2. `push_last(B)` @@ -62,38 +62,38 @@ Perform the following operations on an empty deque `deq`: -1. Which elements are returned by the two pop operations? -2. After all operations are complete, which elements remain from front to rear? -3. Examine the six operations. Can a queue that allows insertion only at the rear and removal only at the front perform all of them? If not, identify the operations it cannot perform. Then state whether a deque can perform them and explain why. +1. Which elements are removed by the two pop operations? +2. Which elements remain, from front to rear, after all the operations? +3. Examine these six steps. Can a queue that allows additions only at the rear and removals only at the front perform them all? If not, identify the unsupported operations. Then explain whether a deque can perform them all, and why. -??? success "Answer" +??? success "Reference answer" - After the first three steps, the deque from front to rear is `[C, A, B]`. + After the first three steps, the deque contains `[C, A, B]` from front to rear. - 1. `pop_last()` removes `B`. After `D` is added, the deque is `[C, A, D]`, - and `pop_first()` then removes `C`. + 1. `pop_last()` removes `B`. After adding `D`, the deque is `[C, A, D]`. + Then `pop_first()` removes `C`. - 2. `[A, D]` remains. + 2. The remaining elements are `[A, D]`. - 3. A queue that permits insertion only at the rear and removal only at the front cannot perform all the operations. - Step 3, `push_first(C)`, requires insertion at the front, and Step 4, `pop_last()`, requires removal at the rear. Both are outside the operations supported by such a queue. - A deque permits insertion and removal at both ends, so it can perform all six operations. + 3. A queue that allows additions only at the rear and removals only at the front cannot perform every operation: + step 3, `push_first(C)`, adds at the front, and step 4, `pop_last()`, removes from the rear. Neither operation is supported by that queue. + A deque supports additions and removals at both ends, so it can perform all six steps. -## Programming Exercises +## Programming exercises -### Check a Bracket Sequence +### Checking a bracket sequence -Given a string `s` containing only the three types of brackets `()`, `[]`, and `{}`, use a stack to determine whether the string is valid. +Given a string `s` containing only the three types of brackets `()`, `[]`, and `{}`, use a stack to determine whether it is valid. -A valid sequence must satisfy both conditions: every closing bracket matches the type of the most recent unmatched opening bracket, -and no unmatched opening bracket remains after the traversal. Return a Boolean value for the result. +A valid sequence must satisfy both conditions: each closing bracket matches the type of the most recent unmatched opening bracket, +and no unmatched opening brackets remain at the end. Return a Boolean result. ??? tip "Hints" - 1. You can create a mapping from each closing bracket to its matching opening bracket - 2. When you encounter a closing bracket, first check whether the stack is empty, and then check whether the top matches - 3. The stack must also be empty after the traversal + 1. You can map each closing bracket to its matching opening bracket. + 2. When you encounter a closing bracket, first check whether the stack is empty, then check whether its top matches. + 3. The stack must also be empty when the scan finishes. [LeetCode](https://leetcode.com/problems/valid-parentheses/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_stack_and_queue/index.md b/en/docs/chapter_stack_and_queue/index.md index a59e20b6c..fc387820e 100644 --- a/en/docs/chapter_stack_and_queue/index.md +++ b/en/docs/chapter_stack_and_queue/index.md @@ -1,9 +1,9 @@ -# Stacks and Queues +# Stacks and queues -![Stacks and Queues](../assets/covers/chapter_stack_and_queue.jpg) +![Stacks and queues](../assets/covers/chapter_stack_and_queue.jpg) !!! abstract - A stack is like cats piled on top of one another, while a queue is like cats lining up. - - They represent the logical relationships of LIFO (Last In, First Out) and FIFO (First In, First Out), respectively. + A stack is like cats piled on top of one another; a queue is like cats standing in line. + + They represent last-in, first-out and first-in, first-out order, respectively. diff --git a/en/docs/chapter_stack_and_queue/queue.md b/en/docs/chapter_stack_and_queue/queue.md index adcc4ca71..2b7167e8e 100755 --- a/en/docs/chapter_stack_and_queue/queue.md +++ b/en/docs/chapter_stack_and_queue/queue.md @@ -1,33 +1,33 @@ -# Queue +# Queues -A queue is a linear data structure that follows the First In, First Out (FIFO) rule. As the name suggests, it models people lining up: newcomers continuously join the rear of the queue, while the people at the front leave one by one. +A queue is a linear data structure that follows a first-in, first-out rule. It models a line of people: newcomers join at the back, while those at the front leave one by one. -As shown in the figure below, we call the front of the queue the "front" and the end the "rear." The operation of adding an element to the rear is called "enqueue," and the operation of removing the front element is called "dequeue." +As shown below, the two ends are called the “front” and the “rear.” Adding an element at the rear is called “enqueueing,” and removing an element from the front is called “dequeueing.” -![FIFO rule of queue](queue.assets/queue_operations.png) +![The first-in, first-out rule of a queue](queue.assets/queue_operations.png) -## Common Queue Operations +## Common queue operations -The common operations on a queue are shown in the table below. Note that method names may vary across programming languages. Here, we use the same naming convention as for stacks. +The table below lists common operations. Method names vary between programming languages; here we use the same names as for stacks. -

Table   Efficiency of Queue Operations

+

Table   Efficiency of queue operations

-| Method | Description | Time Complexity | -| -------- | ------------------------------------------ | --------------- | -| `push()` | Enqueue element, add element to rear | $O(1)$ | -| `pop()` | Dequeue front element | $O(1)$ | -| `peek()` | Access front element | $O(1)$ | +| Method | Description | Time complexity | +| -------- | ---------------------------- | ---------- | +| `push()` | Enqueue an element at the rear | $O(1)$ | +| `pop()` | Dequeue the front element | $O(1)$ | +| `peek()` | Access the front element | $O(1)$ | -We can directly use the queue classes provided by the programming language: +We can use a queue class already provided by the programming language: === "Python" ```python title="queue.py" from collections import deque - # Initialize queue - # In Python, we generally use the deque class as a queue - # Although queue.Queue() is a pure queue class, it is not very user-friendly, so it is not recommended + # Initialize a queue + # In Python, we usually use the deque class as a queue + # Although queue.Queue() is a dedicated queue class, it is less convenient and is not recommended here que: deque[int] = deque() # Enqueue elements @@ -37,23 +37,23 @@ We can directly use the queue classes provided by the programming language: que.append(5) que.append(4) - # Access front element + # Access the front element front: int = que[0] - # Dequeue element + # Dequeue an element pop: int = que.popleft() - # Get queue length + # Get the queue size size: int = len(que) - # Check if queue is empty + # Check whether the queue is empty is_empty: bool = len(que) == 0 ``` === "C++" ```cpp title="queue.cpp" - /* Initialize queue */ + /* Initialize a queue */ queue queue; /* Enqueue elements */ @@ -63,23 +63,23 @@ We can directly use the queue classes provided by the programming language: queue.push(5); queue.push(4); - /* Access front element */ + /* Access the front element */ int front = queue.front(); - /* Dequeue element */ + /* Dequeue an element */ queue.pop(); - /* Get queue length */ + /* Get the queue size */ int size = queue.size(); - /* Check if queue is empty */ + /* Check whether the queue is empty */ bool empty = queue.empty(); ``` === "Java" ```java title="queue.java" - /* Initialize queue */ + /* Initialize a queue */ Queue queue = new LinkedList<>(); /* Enqueue elements */ @@ -89,23 +89,23 @@ We can directly use the queue classes provided by the programming language: queue.offer(5); queue.offer(4); - /* Access front element */ + /* Access the front element */ int peek = queue.peek(); - /* Dequeue element */ + /* Dequeue an element */ int pop = queue.poll(); - /* Get queue length */ + /* Get the queue size */ int size = queue.size(); - /* Check if queue is empty */ + /* Check whether the queue is empty */ boolean isEmpty = queue.isEmpty(); ``` === "C#" ```csharp title="queue.cs" - /* Initialize queue */ + /* Initialize a queue */ Queue queue = new(); /* Enqueue elements */ @@ -115,24 +115,24 @@ We can directly use the queue classes provided by the programming language: queue.Enqueue(5); queue.Enqueue(4); - /* Access front element */ + /* Access the front element */ int peek = queue.Peek(); - /* Dequeue element */ + /* Dequeue an element */ int pop = queue.Dequeue(); - /* Get queue length */ + /* Get the queue size */ int size = queue.Count; - /* Check if queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = queue.Count == 0; ``` === "Go" ```go title="queue_test.go" - /* Initialize queue */ - // In Go, use list as a queue + /* Initialize a queue */ + // In Go, use a list as a queue queue := list.New() /* Enqueue elements */ @@ -142,25 +142,25 @@ We can directly use the queue classes provided by the programming language: queue.PushBack(5) queue.PushBack(4) - /* Access front element */ + /* Access the front element */ peek := queue.Front() - /* Dequeue element */ + /* Dequeue an element */ pop := queue.Front() queue.Remove(pop) - /* Get queue length */ + /* Get the queue size */ size := queue.Len() - /* Check if queue is empty */ + /* Check whether the queue is empty */ isEmpty := queue.Len() == 0 ``` === "Swift" ```swift title="queue.swift" - /* Initialize queue */ - // Swift does not have a built-in queue class, can use Array as a queue + /* Initialize a queue */ + // Swift has no built-in queue class; use an Array as a queue var queue: [Int] = [] /* Enqueue elements */ @@ -170,25 +170,25 @@ We can directly use the queue classes provided by the programming language: queue.append(5) queue.append(4) - /* Access front element */ + /* Access the front element */ let peek = queue.first! - /* Dequeue element */ - // Since it's an array, removeFirst has O(n) complexity + /* Dequeue an element */ + // Because this is an array, removeFirst takes O(n) time let pop = queue.removeFirst() - /* Get queue length */ + /* Get the queue size */ let size = queue.count - /* Check if queue is empty */ + /* Check whether the queue is empty */ let isEmpty = queue.isEmpty ``` === "JS" ```javascript title="queue.js" - /* Initialize queue */ - // JavaScript does not have a built-in queue, can use Array as a queue + /* Initialize a queue */ + // JavaScript has no built-in queue; use an Array as a queue const queue = []; /* Enqueue elements */ @@ -198,25 +198,25 @@ We can directly use the queue classes provided by the programming language: queue.push(5); queue.push(4); - /* Access front element */ + /* Access the front element */ const peek = queue[0]; - /* Dequeue element */ - // The underlying structure is an array, so shift() has O(n) time complexity + /* Dequeue an element */ + // The underlying structure is an array, so shift() takes O(n) time const pop = queue.shift(); - /* Get queue length */ + /* Get the queue size */ const size = queue.length; - /* Check if queue is empty */ + /* Check whether the queue is empty */ const empty = queue.length === 0; ``` === "TS" ```typescript title="queue.ts" - /* Initialize queue */ - // TypeScript does not have a built-in queue, can use Array as a queue + /* Initialize a queue */ + // TypeScript has no built-in queue; use an Array as a queue const queue: number[] = []; /* Enqueue elements */ @@ -226,25 +226,25 @@ We can directly use the queue classes provided by the programming language: queue.push(5); queue.push(4); - /* Access front element */ + /* Access the front element */ const peek = queue[0]; - /* Dequeue element */ - // The underlying structure is an array, so shift() has O(n) time complexity + /* Dequeue an element */ + // The underlying structure is an array, so shift() takes O(n) time const pop = queue.shift(); - /* Get queue length */ + /* Get the queue size */ const size = queue.length; - /* Check if queue is empty */ + /* Check whether the queue is empty */ const empty = queue.length === 0; ``` === "Dart" ```dart title="queue.dart" - /* Initialize queue */ - // In Dart, the Queue class is a deque and can also be used as a queue + /* Initialize a queue */ + // In Dart, Queue is a deque and can also be used as a queue Queue queue = Queue(); /* Enqueue elements */ @@ -254,24 +254,24 @@ We can directly use the queue classes provided by the programming language: queue.add(5); queue.add(4); - /* Access front element */ + /* Access the front element */ int peek = queue.first; - /* Dequeue element */ + /* Dequeue an element */ int pop = queue.removeFirst(); - /* Get queue length */ + /* Get the queue size */ int size = queue.length; - /* Check if queue is empty */ + /* Check whether the queue is empty */ bool isEmpty = queue.isEmpty; ``` === "Rust" ```rust title="queue.rs" - /* Initialize deque */ - // In Rust, use deque as a regular queue + /* Initialize a deque */ + // In Rust, use a deque as an ordinary queue let mut deque: VecDeque = VecDeque::new(); /* Enqueue elements */ @@ -281,31 +281,31 @@ We can directly use the queue classes provided by the programming language: deque.push_back(5); deque.push_back(4); - /* Access front element */ + /* Access the front element */ if let Some(front) = deque.front() { } - /* Dequeue element */ + /* Dequeue an element */ if let Some(pop) = deque.pop_front() { } - /* Get queue length */ + /* Get the queue size */ let size = deque.len(); - /* Check if queue is empty */ + /* Check whether the queue is empty */ let is_empty = deque.is_empty(); ``` === "C" ```c title="queue.c" - // C does not provide a built-in queue + // C provides no built-in queue ``` === "Kotlin" ```kotlin title="queue.kt" - /* Initialize queue */ + /* Initialize a queue */ val queue = LinkedList() /* Enqueue elements */ @@ -315,24 +315,24 @@ We can directly use the queue classes provided by the programming language: queue.offer(5) queue.offer(4) - /* Access front element */ + /* Access the front element */ val peek = queue.peek() - /* Dequeue element */ + /* Dequeue an element */ val pop = queue.poll() - /* Get queue length */ + /* Get the queue size */ val size = queue.size - /* Check if queue is empty */ + /* Check whether the queue is empty */ val isEmpty = queue.isEmpty() ``` === "Ruby" ```ruby title="queue.rb" - # Initialize queue - # Ruby's built-in queue (Thread::Queue) does not have peek and traversal methods, can use Array as a queue + # Initialize a queue + # Ruby's built-in Thread::Queue has no peek or traversal methods; use an Array as a queue queue = [] # Enqueue elements @@ -342,34 +342,34 @@ We can directly use the queue classes provided by the programming language: queue.push(5) queue.push(4) - # Access front element + # Access the front element of the queue peek = queue.first - # Dequeue element - # Please note that since it's an array, Array#shift has O(n) time complexity + # Dequeue an element + # Note that this is an array, so Array#shift takes O(n) time pop = queue.shift - # Get queue length + # Get the queue size size = queue.length - # Check if queue is empty + # Check whether the queue is empty is_empty = queue.empty? ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E9%98%9F%E5%88%97%0A%20%20%20%20%23%20%E5%9C%A8%20Python%20%E4%B8%AD%EF%BC%8C%E6%88%91%E4%BB%AC%E4%B8%80%E8%88%AC%E5%B0%86%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E7%B1%BB%20deque%20%E7%9C%8B%E4%BD%9C%E9%98%9F%E5%88%97%E4%BD%BF%E7%94%A8%0A%20%20%20%20%23%20%E8%99%BD%E7%84%B6%20queue.Queue%28%29%20%E6%98%AF%E7%BA%AF%E6%AD%A3%E7%9A%84%E9%98%9F%E5%88%97%E7%B1%BB%EF%BC%8C%E4%BD%86%E4%B8%8D%E5%A4%AA%E5%A5%BD%E7%94%A8%0A%20%20%20%20que%20%3D%20deque%28%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E9%98%9F%0A%20%20%20%20que.append%281%29%0A%20%20%20%20que.append%283%29%0A%20%20%20%20que.append%282%29%0A%20%20%20%20que.append%285%29%0A%20%20%20%20que.append%284%29%0A%20%20%20%20print%28%22%E9%98%9F%E5%88%97%20que%20%3D%22,%20que%29%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%0A%20%20%20%20front%20%3D%20que%5B0%5D%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%20front%20%3D%22,%20front%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20pop%20%3D%20que.popleft%28%29%0A%20%20%20%20print%28%22%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20pop%20%3D%22,%20pop%29%0A%20%20%20%20print%28%22%E5%87%BA%E9%98%9F%E5%90%8E%20que%20%3D%22,%20que%29%0A%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E9%98%9F%E5%88%97%E7%9A%84%E9%95%BF%E5%BA%A6%0A%20%20%20%20size%20%3D%20len%28que%29%0A%20%20%20%20print%28%22%E9%98%9F%E5%88%97%E9%95%BF%E5%BA%A6%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20len%28que%29%20%3D%3D%200%0A%20%20%20%20print%28%22%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%20%3D%22,%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20queue%0A%20%20%20%20%23%20In%20Python%2C%20we%20usually%20use%20the%20deque%20class%20as%20a%20queue%0A%20%20%20%20%23%20Although%20queue.Queue%28%29%20is%20a%20dedicated%20queue%20class%2C%20it%20is%20less%20convenient%0A%20%20%20%20que%20%3D%20deque%28%29%0A%0A%20%20%20%20%23%20Enqueue%20elements%0A%20%20%20%20que.append%281%29%0A%20%20%20%20que.append%283%29%0A%20%20%20%20que.append%282%29%0A%20%20%20%20que.append%285%29%0A%20%20%20%20que.append%284%29%0A%20%20%20%20print%28%22Queue%3A%20que%20%3D%22%2C%20que%29%0A%0A%20%20%20%20%23%20Access%20the%20front%20element%0A%20%20%20%20front%20%3D%20que%5B0%5D%0A%20%20%20%20print%28%22Front%20element%3A%20front%20%3D%22%2C%20front%29%0A%0A%20%20%20%20%23%20Dequeue%20an%20element%0A%20%20%20%20pop%20%3D%20que.popleft%28%29%0A%20%20%20%20print%28%22Dequeued%20element%3A%20pop%20%3D%22%2C%20pop%29%0A%20%20%20%20print%28%22After%20dequeuing%3A%20que%20%3D%22%2C%20que%29%0A%0A%20%20%20%20%23%20Get%20the%20queue%20size%0A%20%20%20%20size%20%3D%20len%28que%29%0A%20%20%20%20print%28%22Queue%20size%3A%20size%20%3D%22%2C%20size%29%0A%0A%20%20%20%20%23%20Check%20whether%20the%20queue%20is%20empty%0A%20%20%20%20is_empty%20%3D%20len%28que%29%20%3D%3D%200%0A%20%20%20%20print%28%22Is%20the%20queue%20empty%20%3D%22%2C%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## Queue Implementation +## Implementing a queue -To implement a queue, we need a data structure that allows adding elements at one end and removing elements at the other end. Both linked lists and arrays meet this requirement. +A queue needs a data structure that allows insertion at one end and deletion at the other. Both linked lists and arrays can meet this requirement. -### Linked List Implementation +### Implementation with a linked list -As shown in the figure below, we can treat the "head node" and "tail node" of a linked list as the "front" and "rear" of the queue, respectively, with the rule that nodes can only be added at the rear and removed from the front. +As shown below, treat the linked list's head and tail as the queue's front and rear. Nodes may be added only at the rear and removed only from the front. === "<1>" - ![Enqueue and dequeue operations in linked list implementation of queue](queue.assets/linkedlist_queue_step1.png) + ![Enqueue and dequeue in a linked-list queue](queue.assets/linkedlist_queue_step1.png) === "<2>" ![linkedlist_queue_push](queue.assets/linkedlist_queue_step2_push.png) @@ -377,27 +377,27 @@ As shown in the figure below, we can treat the "head node" and "tail node" of a === "<3>" ![linkedlist_queue_pop](queue.assets/linkedlist_queue_step3_pop.png) -Below is the code for implementing a queue using a linked list: +Here is an implementation using a linked list: ```src [file]{linkedlist_queue}-[class]{linked_list_queue}-[func]{} ``` -### Array Implementation +### Implementation with an array -Deleting the first element in an array has a time complexity of $O(n)$, which would make the dequeue operation inefficient. However, we can use the following clever method to avoid this problem. +Removing the first element of an array takes $O(n)$ time, which would make dequeueing inefficient. A useful technique avoids this problem. -We can use a variable `front` to point to the index of the front element and maintain a variable `size` to record the queue length. We define `rear = front + size`, which calculates the position right after the rear element. +Use a variable `front` for the index of the front element and a variable `size` for the queue's length. Define `rear = front + size`, so `rear` points to the position immediately after the last element. -Based on this design, **the valid interval containing elements in the array is `[front, rear - 1]`**. The implementation methods for various operations are shown in the figure below: +With this design, **the occupied interval is `[front, rear - 1]`**. Operations work as shown below. -- Enqueue operation: Assign the input element to the `rear` index and increase `size` by 1. -- Dequeue operation: Simply increase `front` by 1 and decrease `size` by 1. +- Enqueue: assign the new element to index `rear` and increase `size` by 1. +- Dequeue: increase `front` by 1 and decrease `size` by 1. -As you can see, both enqueue and dequeue operations require only one operation, with a time complexity of $O(1)$. +Both operations require only a constant amount of work and take $O(1)$ time. === "<1>" - ![Enqueue and dequeue operations in array implementation of queue](queue.assets/array_queue_step1.png) + ![Enqueue and dequeue in an array-based queue](queue.assets/array_queue_step1.png) === "<2>" ![array_queue_push](queue.assets/array_queue_step2_push.png) @@ -405,19 +405,19 @@ As you can see, both enqueue and dequeue operations require only one operation, === "<3>" ![array_queue_pop](queue.assets/array_queue_step3_pop.png) -You may notice a problem: as we continuously enqueue and dequeue, both `front` and `rear` move to the right. **When they reach the end of the array, they cannot continue moving**. To solve this problem, we can treat the array as a "circular array" with head and tail connected. +There is a problem: repeated enqueueing and dequeueing move both `front` and `rear` to the right, **and they cannot continue once they reach the end of the array**. To solve this, treat the array as circular, with its end connected to its beginning. -For a circular array, we need to let `front` or `rear` wrap around to the beginning of the array when they cross the end. This periodic pattern can be implemented using the "modulo operation," as shown in the code below: +When `front` or `rear` passes the array's end, wrap it back to the beginning. The remainder operation implements this cyclic behavior: ```src [file]{array_queue}-[class]{array_queue}-[func]{} ``` -The queue implemented above still has limitations: its length is immutable. However, this problem is not difficult to solve. We can replace the array with a dynamic array to introduce an expansion mechanism. Interested readers can try to implement this themselves. +This queue still has a limitation: its capacity is fixed. We can address it by replacing the array with a dynamic array and adding resizing. Interested readers can try implementing this themselves. -The comparison conclusions for the two implementations are consistent with those for stacks and will not be repeated here. +The comparison between these two implementations is the same as for stacks, so we will not repeat it here. -## Typical Applications of Queue +## Typical applications -- **Taobao orders**. After shoppers place orders, the orders are added to a queue, and the system subsequently processes the orders in the queue according to their sequence. During Double Eleven, massive orders are generated in a short time, and high concurrency becomes a key challenge that engineers need to tackle. -- **Various to-do tasks**. Any scenario that needs to implement "first come, first served" functionality, such as a printer's task queue or a restaurant's order queue, can effectively maintain the processing order using queues. +- **Taobao orders**. When a shopper places an order, it enters a queue, and the system processes orders in sequence. During the Singles' Day shopping festival, enormous numbers of orders arrive in a short time, making high concurrency a major engineering challenge. +- **Tasks awaiting processing**. Queues preserve processing order wherever first-come, first-served behavior is needed, such as printer jobs or restaurant orders. diff --git a/en/docs/chapter_stack_and_queue/stack.md b/en/docs/chapter_stack_and_queue/stack.md index b06c0dc27..b5e72c143 100755 --- a/en/docs/chapter_stack_and_queue/stack.md +++ b/en/docs/chapter_stack_and_queue/stack.md @@ -1,32 +1,32 @@ -# Stack +# Stacks -A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. +A stack is a linear data structure that follows a last-in, first-out rule. -We can compare a stack to a pile of plates on a table. If we specify that only one plate can be moved at a time, then to get the bottom plate, we must first remove the plates above it one by one. If we replace the plates with various types of elements (such as integers, characters, objects, etc.), we get the stack data structure. +Think of a stack of plates on a table. If we can move only one plate at a time, reaching the bottom plate requires removing all the plates above it first. Replace the plates with elements such as integers, characters, or objects, and we have a stack. -As shown in the figure below, we call the top of the stacked elements the "top" and the bottom the "bottom." The operation of adding an element to the top is called "push," and the operation of removing the top element is called "pop." +As shown below, the upper end is the “top” of the stack and the lower end is the “bottom.” Adding an element to the top is called “pushing,” and removing the top element is called “popping.” -![LIFO rule of stack](stack.assets/stack_operations.png) +![The last-in, first-out rule of a stack](stack.assets/stack_operations.png) -## Common Stack Operations +## Common stack operations -The common operations on a stack are shown in the table below. The specific method names depend on the programming language used. Here, we use the common naming convention of `push()`, `pop()`, and `peek()`. +The table below lists common operations. Actual method names depend on the programming language; here we use the familiar names `push()`, `pop()`, and `peek()`. -

Table   Efficiency of Stack Operations

+

Table   Efficiency of stack operations

-| Method | Description | Time Complexity | -| -------- | ---------------------------------------------- | --------------- | -| `push()` | Push element onto stack (add to top) | $O(1)$ | -| `pop()` | Pop top element from stack | $O(1)$ | -| `peek()` | Access top element | $O(1)$ | +| Method | Description | Time complexity | +| -------- | ---------------------- | ---------- | +| `push()` | Push an element onto the top | $O(1)$ | +| `pop()` | Remove the top element | $O(1)$ | +| `peek()` | Access the top element | $O(1)$ | -Typically, we can directly use the built-in stack class provided by the programming language. However, some languages may not provide a dedicated stack class. In such cases, we can use the language's "array" or "linked list" as a stack and simply avoid using operations unrelated to stack behavior. +We can usually use a language's built-in stack class. If no dedicated class is provided, use an array or linked list as a stack and avoid operations unrelated to stack behavior. === "Python" ```python title="stack.py" - # Initialize stack - # Python does not have a built-in stack class, can use list as a stack + # Initialize a stack + # Python has no built-in stack class; use a list as a stack stack: list[int] = [] # Push elements @@ -36,23 +36,23 @@ Typically, we can directly use the built-in stack class provided by the programm stack.append(5) stack.append(4) - # Access top element + # Access the top element peek: int = stack[-1] - # Pop element + # Pop an element pop: int = stack.pop() - # Get stack length + # Get the stack size size: int = len(stack) - # Check if empty + # Check whether the stack is empty is_empty: bool = len(stack) == 0 ``` === "C++" ```cpp title="stack.cpp" - /* Initialize stack */ + /* Initialize a stack */ stack stack; /* Push elements */ @@ -62,23 +62,23 @@ Typically, we can directly use the built-in stack class provided by the programm stack.push(5); stack.push(4); - /* Access top element */ + /* Access the top element */ int top = stack.top(); - /* Pop element */ + /* Pop an element */ stack.pop(); // No return value - /* Get stack length */ + /* Get the stack size */ int size = stack.size(); - /* Check if empty */ + /* Check whether the stack is empty */ bool empty = stack.empty(); ``` === "Java" ```java title="stack.java" - /* Initialize stack */ + /* Initialize a stack */ Stack stack = new Stack<>(); /* Push elements */ @@ -88,23 +88,23 @@ Typically, we can directly use the built-in stack class provided by the programm stack.push(5); stack.push(4); - /* Access top element */ + /* Access the top element */ int peek = stack.peek(); - /* Pop element */ + /* Pop an element */ int pop = stack.pop(); - /* Get stack length */ + /* Get the stack size */ int size = stack.size(); - /* Check if empty */ + /* Check whether the stack is empty */ boolean isEmpty = stack.isEmpty(); ``` === "C#" ```csharp title="stack.cs" - /* Initialize stack */ + /* Initialize a stack */ Stack stack = new(); /* Push elements */ @@ -114,24 +114,24 @@ Typically, we can directly use the built-in stack class provided by the programm stack.Push(5); stack.Push(4); - /* Access top element */ + /* Access the top element */ int peek = stack.Peek(); - /* Pop element */ + /* Pop an element */ int pop = stack.Pop(); - /* Get stack length */ + /* Get the stack size */ int size = stack.Count; - /* Check if empty */ + /* Check whether the stack is empty */ bool isEmpty = stack.Count == 0; ``` === "Go" ```go title="stack_test.go" - /* Initialize stack */ - // In Go, it is recommended to use Slice as a stack + /* Initialize a stack */ + // In Go, a slice is recommended for use as a stack var stack []int /* Push elements */ @@ -141,25 +141,25 @@ Typically, we can directly use the built-in stack class provided by the programm stack = append(stack, 5) stack = append(stack, 4) - /* Access top element */ + /* Access the top element */ peek := stack[len(stack)-1] - /* Pop element */ + /* Pop an element */ pop := stack[len(stack)-1] stack = stack[:len(stack)-1] - /* Get stack length */ + /* Get the stack size */ size := len(stack) - /* Check if empty */ + /* Check whether the stack is empty */ isEmpty := len(stack) == 0 ``` === "Swift" ```swift title="stack.swift" - /* Initialize stack */ - // Swift does not have a built-in stack class, can use Array as a stack + /* Initialize a stack */ + // Swift has no built-in stack class; use an Array as a stack var stack: [Int] = [] /* Push elements */ @@ -169,24 +169,24 @@ Typically, we can directly use the built-in stack class provided by the programm stack.append(5) stack.append(4) - /* Access top element */ + /* Access the top element */ let peek = stack.last! - /* Pop element */ + /* Pop an element */ let pop = stack.removeLast() - /* Get stack length */ + /* Get the stack size */ let size = stack.count - /* Check if empty */ + /* Check whether the stack is empty */ let isEmpty = stack.isEmpty ``` === "JS" ```javascript title="stack.js" - /* Initialize stack */ - // JavaScript does not have a built-in stack class, can use Array as a stack + /* Initialize a stack */ + // JavaScript has no built-in stack class; use an Array as a stack const stack = []; /* Push elements */ @@ -196,24 +196,24 @@ Typically, we can directly use the built-in stack class provided by the programm stack.push(5); stack.push(4); - /* Access top element */ + /* Access the top element */ const peek = stack[stack.length-1]; - /* Pop element */ + /* Pop an element */ const pop = stack.pop(); - /* Get stack length */ + /* Get the stack size */ const size = stack.length; - /* Check if empty */ + /* Check whether the stack is empty */ const is_empty = stack.length === 0; ``` === "TS" ```typescript title="stack.ts" - /* Initialize stack */ - // TypeScript does not have a built-in stack class, can use Array as a stack + /* Initialize a stack */ + // TypeScript has no built-in stack class; use an Array as a stack const stack: number[] = []; /* Push elements */ @@ -223,24 +223,24 @@ Typically, we can directly use the built-in stack class provided by the programm stack.push(5); stack.push(4); - /* Access top element */ + /* Access the top element */ const peek = stack[stack.length - 1]; - /* Pop element */ + /* Pop an element */ const pop = stack.pop(); - /* Get stack length */ + /* Get the stack size */ const size = stack.length; - /* Check if empty */ + /* Check whether the stack is empty */ const is_empty = stack.length === 0; ``` === "Dart" ```dart title="stack.dart" - /* Initialize stack */ - // Dart does not have a built-in stack class, can use List as a stack + /* Initialize a stack */ + // Dart has no built-in stack class; use a List as a stack List stack = []; /* Push elements */ @@ -250,24 +250,24 @@ Typically, we can directly use the built-in stack class provided by the programm stack.add(5); stack.add(4); - /* Access top element */ + /* Access the top element */ int peek = stack.last; - /* Pop element */ + /* Pop an element */ int pop = stack.removeLast(); - /* Get stack length */ + /* Get the stack size */ int size = stack.length; - /* Check if empty */ + /* Check whether the stack is empty */ bool isEmpty = stack.isEmpty; ``` === "Rust" ```rust title="stack.rs" - /* Initialize stack */ - // Use Vec as a stack + /* Initialize a stack */ + // Use a Vec as a stack let mut stack: Vec = Vec::new(); /* Push elements */ @@ -277,29 +277,29 @@ Typically, we can directly use the built-in stack class provided by the programm stack.push(5); stack.push(4); - /* Access top element */ + /* Access the top element */ let top = stack.last().unwrap(); - /* Pop element */ + /* Pop an element */ let pop = stack.pop().unwrap(); - /* Get stack length */ + /* Get the stack size */ let size = stack.len(); - /* Check if empty */ + /* Check whether the stack is empty */ let is_empty = stack.is_empty(); ``` === "C" ```c title="stack.c" - // C does not provide a built-in stack + // C provides no built-in stack ``` === "Kotlin" ```kotlin title="stack.kt" - /* Initialize stack */ + /* Initialize a stack */ val stack = Stack() /* Push elements */ @@ -309,24 +309,24 @@ Typically, we can directly use the built-in stack class provided by the programm stack.push(5) stack.push(4) - /* Access top element */ + /* Access the top element */ val peek = stack.peek() - /* Pop element */ + /* Pop an element */ val pop = stack.pop() - /* Get stack length */ + /* Get the stack size */ val size = stack.size - /* Check if empty */ + /* Check whether the stack is empty */ val isEmpty = stack.isEmpty() ``` === "Ruby" ```ruby title="stack.rb" - # Initialize stack - # Ruby does not have a built-in stack class, can use Array as a stack + # Initialize a stack + # Ruby has no built-in stack class; use an Array as a stack stack = [] # Push elements @@ -336,37 +336,37 @@ Typically, we can directly use the built-in stack class provided by the programm stack << 5 stack << 4 - # Access top element + # Access the top element peek = stack.last - # Pop element + # Pop an element pop = stack.pop - # Get stack length + # Get the stack size size = stack.length - # Check if empty + # Check whether the stack is empty is_empty = stack.empty? ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%A0%88%0A%20%20%20%20%23%20Python%20%E6%B2%A1%E6%9C%89%E5%86%85%E7%BD%AE%E7%9A%84%E6%A0%88%E7%B1%BB%EF%BC%8C%E5%8F%AF%E4%BB%A5%E6%8A%8A%20list%20%E5%BD%93%E4%BD%9C%E6%A0%88%E6%9D%A5%E4%BD%BF%E7%94%A8%0A%20%20%20%20stack%20%3D%20%5B%5D%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E6%A0%88%0A%20%20%20%20stack.append%281%29%0A%20%20%20%20stack.append%283%29%0A%20%20%20%20stack.append%282%29%0A%20%20%20%20stack.append%285%29%0A%20%20%20%20stack.append%284%29%0A%20%20%20%20print%28%22%E6%A0%88%20stack%20%3D%22,%20stack%29%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E6%A0%88%E9%A1%B6%E5%85%83%E7%B4%A0%0A%20%20%20%20peek%20%3D%20stack%5B-1%5D%0A%20%20%20%20print%28%22%E6%A0%88%E9%A1%B6%E5%85%83%E7%B4%A0%20peek%20%3D%22,%20peek%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%87%BA%E6%A0%88%0A%20%20%20%20pop%20%3D%20stack.pop%28%29%0A%20%20%20%20print%28%22%E5%87%BA%E6%A0%88%E5%85%83%E7%B4%A0%20pop%20%3D%22,%20pop%29%0A%20%20%20%20print%28%22%E5%87%BA%E6%A0%88%E5%90%8E%20stack%20%3D%22,%20stack%29%0A%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E6%A0%88%E7%9A%84%E9%95%BF%E5%BA%A6%0A%20%20%20%20size%20%3D%20len%28stack%29%0A%20%20%20%20print%28%22%E6%A0%88%E7%9A%84%E9%95%BF%E5%BA%A6%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20len%28stack%29%20%3D%3D%200%0A%20%20%20%20print%28%22%E6%A0%88%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%20%3D%22,%20is_empty%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20stack%0A%20%20%20%20%23%20Python%20has%20no%20built-in%20stack%20class%3B%20use%20a%20list%20as%20a%20stack%0A%20%20%20%20stack%20%3D%20%5B%5D%0A%0A%20%20%20%20%23%20Push%20elements%0A%20%20%20%20stack.append%281%29%0A%20%20%20%20stack.append%283%29%0A%20%20%20%20stack.append%282%29%0A%20%20%20%20stack.append%285%29%0A%20%20%20%20stack.append%284%29%0A%20%20%20%20print%28%22Stack%3A%20stack%20%3D%22%2C%20stack%29%0A%0A%20%20%20%20%23%20Access%20the%20top%20element%0A%20%20%20%20peek%20%3D%20stack%5B-1%5D%0A%20%20%20%20print%28%22Top%20element%3A%20peek%20%3D%22%2C%20peek%29%0A%0A%20%20%20%20%23%20Pop%20an%20element%0A%20%20%20%20pop%20%3D%20stack.pop%28%29%0A%20%20%20%20print%28%22Popped%20element%3A%20pop%20%3D%22%2C%20pop%29%0A%20%20%20%20print%28%22After%20popping%3A%20stack%20%3D%22%2C%20stack%29%0A%0A%20%20%20%20%23%20Get%20the%20stack%20size%0A%20%20%20%20size%20%3D%20len%28stack%29%0A%20%20%20%20print%28%22Stack%20size%3A%20size%20%3D%22%2C%20size%29%0A%0A%20%20%20%20%23%20Check%20whether%20the%20stack%20is%20empty%0A%20%20%20%20is_empty%20%3D%20len%28stack%29%20%3D%3D%200%0A%20%20%20%20print%28%22Is%20the%20stack%20empty%20%3D%22%2C%20is_empty%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## Stack Implementation +## Implementing a stack -To gain a deeper understanding of how a stack operates, let's try implementing a stack class ourselves. +To understand how a stack works more deeply, let us implement a stack class ourselves. -A stack follows the LIFO principle, so we can only add or remove elements at the top. However, both arrays and linked lists allow adding and removing elements at any position. **Therefore, a stack can be viewed as a restricted array or linked list**. In other words, we can "shield" some irrelevant operations of arrays or linked lists so that their external logic conforms to the characteristics of a stack. +A stack follows last-in, first-out order, so elements can be added or removed only at the top. Arrays and linked lists allow insertion and deletion at arbitrary positions, **so a stack can be viewed as a restricted array or linked list**. By hiding irrelevant operations, we make its public behavior conform to the stack's rules. -### Linked List Implementation +### Implementation with a linked list -When implementing a stack using a linked list, we can treat the head node of the linked list as the top of the stack and the tail node as the base. +When implementing a stack with a linked list, treat the head as the top and the tail as the bottom. -As shown in the figure below, for the push operation, we simply insert an element at the head of the linked list. This node insertion method is called the "head insertion method." For the pop operation, we just need to remove the head node from the linked list. +As shown below, pushing simply inserts a node at the head, an operation called “head insertion.” Popping removes the head node. === "<1>" - ![Push and pop operations in linked list implementation of stack](stack.assets/linkedlist_stack_step1.png) + ![Push and pop in a linked-list stack](stack.assets/linkedlist_stack_step1.png) === "<2>" ![linkedlist_stack_push](stack.assets/linkedlist_stack_step2_push.png) @@ -374,18 +374,18 @@ As shown in the figure below, for the push operation, we simply insert an elemen === "<3>" ![linkedlist_stack_pop](stack.assets/linkedlist_stack_step3_pop.png) -Below is sample code for implementing a stack based on a linked list: +Here is an implementation using a linked list: ```src [file]{linkedlist_stack}-[class]{linked_list_stack}-[func]{} ``` -### Array Implementation +### Implementation with an array -When implementing a stack using an array, we can treat the end of the array as the top of the stack. As shown in the figure below, push and pop operations correspond to adding and removing elements at the end of the array, both with a time complexity of $O(1)$. +For an array-based stack, use the end of the array as the top. As shown below, pushing and popping correspond to adding and removing the last element, both taking $O(1)$ time. === "<1>" - ![Push and pop operations in array implementation of stack](stack.assets/array_stack_step1.png) + ![Push and pop in an array-based stack](stack.assets/array_stack_step1.png) === "<2>" ![array_stack_push](stack.assets/array_stack_step2_push.png) @@ -393,38 +393,38 @@ When implementing a stack using an array, we can treat the end of the array as t === "<3>" ![array_stack_pop](stack.assets/array_stack_step3_pop.png) -Since elements pushed onto the stack may increase continuously, we can use a dynamic array, which eliminates the need to handle array expansion ourselves. Here is the sample code: +Since elements may keep arriving, use a dynamic array so that resizing is handled for us. An implementation follows: ```src [file]{array_stack}-[class]{array_stack}-[func]{} ``` -## Comparison of the Two Implementations +## Comparing the implementations -**Supported Operations** +**Supported operations** -Both implementations support all operations defined by the stack. The array implementation additionally supports random access, but this goes beyond the stack definition and is generally not used. +Both implementations support all stack operations. An array also allows random access, but that is outside the stack abstraction and is generally not used. -**Time Efficiency** +**Time efficiency** -In the array-based implementation, both push and pop operations occur in pre-allocated contiguous memory, which has good cache locality and is therefore more efficient. However, if pushing exceeds the array capacity, it triggers an expansion mechanism, causing the time complexity of that particular push operation to become $O(n)$. +In an array-based stack, pushes and pops operate in preallocated contiguous memory, giving good cache locality and high efficiency. However, a push that exceeds the array's capacity triggers expansion, making that particular push an $O(n)$ operation. -In the linked list-based implementation, list expansion is very flexible, and there is no issue of reduced efficiency due to array expansion. However, the push operation requires initializing a node object and modifying pointers, so it is relatively less efficient. Nevertheless, if the pushed elements are already node objects, the initialization step can be omitted, thereby improving efficiency. +A linked list grows flexibly and avoids the slowdown associated with array expansion. However, pushing requires creating a node object and updating pointers, so it is relatively slower. If the element is already a node object, the creation step can be skipped to improve efficiency. -In summary, when the elements pushed and popped are basic data types such as `int` or `double`, we can draw the following conclusions: +For elements of primitive types such as `int` or `double`, we can therefore draw the following conclusions. -- The array-based stack implementation has reduced efficiency when expansion is triggered, but since expansion is an infrequent operation, the average efficiency is higher. -- The linked list-based stack implementation can provide more stable efficiency performance. +- An array-based stack slows down when expansion is triggered, but expansion is infrequent, so average efficiency is higher. +- A linked-list stack provides more consistent performance. -**Space Efficiency** +**Space efficiency** -When initializing a list, the system allocates an "initial capacity" that may exceed the actual need. Additionally, the expansion mechanism typically expands at a specific ratio (e.g., 2x), and the capacity after expansion may also exceed actual needs. Therefore, **the array-based stack implementation may cause some space wastage**. +When a dynamic array is initialized, its initial capacity may exceed actual needs. Expansion typically multiplies the capacity by a fixed factor, such as 2, which may again leave excess space. **An array-based stack can therefore waste some space**. -However, since linked list nodes need to store additional pointers, **the space occupied by linked list nodes is relatively large**. +However, linked-list nodes also store pointers, **so each node occupies relatively more space**. -In summary, we cannot simply determine which implementation is more memory-efficient and need to analyze the specific situation. +We cannot say universally which implementation uses less memory; the answer depends on the situation. -## Typical Applications of Stack +## Typical applications -- **Back and forward in browsers, undo and redo in software**. Every time we open a new webpage, the browser pushes the previous page onto the stack, allowing us to return to the previous page via the back operation. The back operation is essentially performing a pop. To support both back and forward, two stacks are needed to work together. -- **Program memory management**. Each time a function is called, the system adds a stack frame to the top of the stack to record the function's context information. During recursion, the downward recursive phase continuously performs push operations, while the upward backtracking phase continuously performs pop operations. +- **Browser back and forward navigation, and software undo and redo**. When a new web page is opened, the browser pushes the previous page onto a stack. Going back pops that page. Supporting both back and forward requires two stacks working together. +- **Program memory management**. Each function call adds a stack frame containing the function's context. During recursion, frames are pushed as calls descend and popped as they return. diff --git a/en/docs/chapter_stack_and_queue/summary.md b/en/docs/chapter_stack_and_queue/summary.md index 8ab9b6e9b..e3f6f78b2 100644 --- a/en/docs/chapter_stack_and_queue/summary.md +++ b/en/docs/chapter_stack_and_queue/summary.md @@ -1,31 +1,31 @@ # Summary -### Key Review +### Key takeaways -- A stack is a data structure that follows the LIFO principle and can be implemented using arrays or linked lists. -- In terms of time efficiency, the array implementation of a stack has higher average efficiency, but during expansion, the time complexity of a single push operation degrades to $O(n)$. In contrast, the linked-list implementation of a stack offers more stable performance. -- In terms of space efficiency, the array implementation of a stack may lead to some degree of space wastage. However, it should be noted that the memory space occupied by linked list nodes is larger than that of array elements. -- A queue is a data structure that follows the FIFO principle and can also be implemented using arrays or linked lists. The conclusions regarding time efficiency and space efficiency comparisons for queues are similar to those for stacks mentioned above. -- A deque is a queue with greater flexibility that allows adding and removing elements at both ends. +- A stack follows last-in, first-out order and can be implemented with an array or linked list. +- An array-based stack offers higher average time efficiency, but a push that triggers expansion takes $O(n)$ time. A linked-list stack provides more consistent performance. +- An array-based stack may waste some space. However, linked-list nodes require more memory than individual array elements. +- A queue follows first-in, first-out order and can also be implemented with an array or linked list. Their time and space tradeoffs resemble those of stack implementations. +- A deque is a more flexible queue that allows insertion and removal at both ends. ### Q & A -**Q**: Is the browser's forward and backward functionality implemented with a doubly linked list? +**Q**: Are browser back and forward navigation implemented with a doubly linked list? -The browser's forward and backward behavior is essentially an application of a "stack." When a user visits a new page, that page is added to the top of the stack; when the user clicks the back button, that page is popped from the top of the stack. A deque can conveniently support some additional operations, as mentioned in the "Deque" section. +Browser navigation fundamentally reflects stack behavior. Visiting a new page adds it to the top of a stack; clicking Back removes it. A deque conveniently supports additional operations, as discussed in the deque section. -**Q**: After popping from the stack, do we need to free the memory of the popped node? +**Q**: After popping a node, do we need to free its memory? -If the popped node will still be needed later, then memory does not need to be freed. If it won't be used afterward, languages like Java and Python have automatic garbage collection, so manual memory deallocation is not required; in C and C++, manual memory deallocation is necessary. +If the node will still be used, do not free it. If it is no longer needed, languages such as `Java` and `Python` use automatic garbage collection, so manual deallocation is unnecessary. In `C` and `C++`, memory must be released manually. -**Q**: A deque seems like two stacks joined together. What is its purpose? +**Q**: A deque looks like two stacks joined together. What is it useful for? -A deque is like a combination of a stack and a queue, or two stacks joined together. It combines the logic of both, so it can support all applications of stacks and queues while offering greater flexibility. +A deque resembles a combination of a stack and a queue, or two stacks joined together. It combines stack and queue behavior, supporting all their applications with greater flexibility. -**Q**: How are undo and redo specifically implemented? +**Q**: How exactly do undo and redo work? -Use two stacks: stack `A` for undo and stack `B` for redo. +Use two stacks: `A` for undo and `B` for redo. -1. Whenever the user performs an operation, push this operation onto stack `A` and clear stack `B`. -2. When the user performs "undo," pop the most recent operation from stack `A` and push it onto stack `B`. -3. When the user performs "redo," pop the most recent operation from stack `B` and push it onto stack `A`. +1. Whenever the user performs an action, push it onto `A` and clear `B`. +2. To undo, pop the most recent action from `A` and push it onto `B`. +3. To redo, pop the most recent action from `B` and push it onto `A`. diff --git a/en/docs/chapter_tree/array_representation_of_tree.md b/en/docs/chapter_tree/array_representation_of_tree.md index f31a718ca..217620df0 100644 --- a/en/docs/chapter_tree/array_representation_of_tree.md +++ b/en/docs/chapter_tree/array_representation_of_tree.md @@ -1,34 +1,34 @@ -# Array Representation of Binary Trees +# Array representation of a binary tree -In the linked-list representation, the storage unit of a binary tree is a node `TreeNode`, and nodes are connected by pointers. The previous section introduced the basic operations of binary trees in this representation. +In a linked representation, a binary tree stores data in `TreeNode` nodes connected by pointers. The previous section introduced basic operations on binary trees in this form. -So, can we use an array to represent a binary tree? The answer is yes. +Can we represent a binary tree with an array instead? Yes. -## Representing Perfect Binary Trees +## Representing a perfect binary tree -Let's analyze a simple case first. Given a perfect binary tree, we store all nodes in an array according to the order of level-order traversal, where each node corresponds to a unique array index. +Start with a simple case. Store every node of a perfect binary tree in an array in level order. Each node then corresponds to a unique array index. -Based on the characteristics of level-order traversal, we can derive a "mapping formula" between parent node index and child node indices: **If a node's index is $i$, then its left child index is $2i + 1$ and its right child index is $2i + 2$**. The figure below shows the mapping relationships between various node indices. +Level-order traversal gives us a mapping between parent and child indices: **if a node has index $i$, its left child has index $2i + 1$ and its right child has index $2i + 2$**. The figure below illustrates this relationship. ![Array representation of a perfect binary tree](array_representation_of_tree.assets/array_representation_binary_tree.png) -**The mapping formula plays a role similar to the node references (pointers) in linked lists**. Given any node in the array, we can access its left (right) child node using the mapping formula. +**The mapping formulas serve the same role as node references or pointers in a linked representation**. Given any node in the array, we can use them to access its left or right child. -## Representing Any Binary Tree +## Representing an arbitrary binary tree -Perfect binary trees are a special case; in the middle levels of a binary tree, there are typically many `None` values. Since the level-order traversal sequence does not include these `None` values, we cannot infer the number and distribution of `None` values based on this sequence alone. **This means multiple binary tree structures can correspond to the same level-order traversal sequence**. +A perfect binary tree is a special case. Other binary trees often contain many missing nodes, `None`, at intermediate levels. Ordinary level-order traversal omits these positions, so its sequence alone cannot tell us their number or locations. **Multiple tree structures can therefore produce the same level-order sequence**. -As shown in the figure below, given a non-perfect binary tree, the above method of array representation fails. +As shown below, the array representation above no longer works for a non-perfect binary tree. -![Level-order traversal sequence corresponds to multiple binary tree possibilities](array_representation_of_tree.assets/array_representation_without_empty.png) +![Multiple binary trees can share a level-order sequence](array_representation_of_tree.assets/array_representation_without_empty.png) -To solve this problem, **we can explicitly write out all `None` values in the level-order traversal sequence**. As shown in the figure below, once we do this, the level-order traversal sequence can uniquely represent a binary tree. Example code is as follows: +To solve this, **include every `None` position explicitly in the level-order sequence**. The sequence then represents the tree uniquely, as shown below. Example code follows: === "Python" ```python title="" # Array representation of a binary tree - # Using None to represent empty slots + # Use None for empty positions tree = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] ``` @@ -36,7 +36,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```cpp title="" /* Array representation of a binary tree */ - // Using the maximum integer value INT_MAX to mark empty slots + // Use INT_MAX, the maximum int value, for empty positions vector tree = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; ``` @@ -44,7 +44,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```java title="" /* Array representation of a binary tree */ - // Using the Integer wrapper class allows for using null to mark empty slots + // Use the wrapper class Integer so that null can mark empty positions Integer[] tree = { 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 }; ``` @@ -52,7 +52,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```csharp title="" /* Array representation of a binary tree */ - // Using nullable int (int?) allows for using null to mark empty slots + // Use the nullable type int? so that null can mark empty positions int?[] tree = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15]; ``` @@ -60,7 +60,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```go title="" /* Array representation of a binary tree */ - // Using an any type slice, allowing for nil to mark empty slots + // Use a slice of any so that nil can mark empty positions tree := []any{1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15} ``` @@ -68,7 +68,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```swift title="" /* Array representation of a binary tree */ - // Using optional Int (Int?) allows for using nil to mark empty slots + // Use the optional type Int? so that nil can mark empty positions let tree: [Int?] = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] ``` @@ -76,7 +76,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```javascript title="" /* Array representation of a binary tree */ - // Using null to represent empty slots + // Use null for empty positions let tree = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15]; ``` @@ -84,7 +84,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```typescript title="" /* Array representation of a binary tree */ - // Using null to represent empty slots + // Use null for empty positions let tree: (number | null)[] = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15]; ``` @@ -92,7 +92,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```dart title="" /* Array representation of a binary tree */ - // Using nullable int (int?) allows for using null to mark empty slots + // Use the nullable type int? so that null can mark empty positions List tree = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15]; ``` @@ -100,7 +100,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```rust title="" /* Array representation of a binary tree */ - // Using None to mark empty slots + // Use None for empty positions let tree = [Some(1), Some(2), Some(3), Some(4), None, Some(6), Some(7), Some(8), Some(9), None, None, Some(12), None, None, Some(15)]; ``` @@ -108,7 +108,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```c title="" /* Array representation of a binary tree */ - // Using the maximum int value to mark empty slots, therefore, node values must not be INT_MAX + // Use the maximum int value for empty positions, so node values must not equal INT_MAX int tree[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; ``` @@ -116,7 +116,7 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```kotlin title="" /* Array representation of a binary tree */ - // Using null to represent empty slots + // Use null for empty positions val tree = arrayOf( 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 ) ``` @@ -124,37 +124,37 @@ To solve this problem, **we can explicitly write out all `None` values in the le ```ruby title="" ### Array representation of a binary tree ### - # Using nil to represent empty slots + # Use nil for empty positions tree = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] ``` ![Array representation of an arbitrary binary tree](array_representation_of_tree.assets/array_representation_with_empty.png) -It's worth noting that **complete binary trees are very well-suited for array representation**. Recalling the definition of a complete binary tree, `None` only appears at the bottom level and towards the right, **meaning all `None` values must appear at the end of the level-order traversal sequence**. +**Complete binary trees are particularly well suited to array representation**. By definition, missing positions occur only at the right end of the lowest level, **so all `None` entries appear at the end of the level-order sequence**. -This means that when using an array to represent a complete binary tree, it's possible to omit storing all `None` values, which is very convenient. The figure below gives an example. +We can therefore omit all `None` entries when storing a complete binary tree in an array. An example is shown below. ![Array representation of a complete binary tree](array_representation_of_tree.assets/array_representation_complete_binary_tree.png) -The following code implements a binary tree using an array representation, including the following operations: +The following code implements an array-based binary tree with these operations. -- Given a node, obtain its value, left (right) child node, and parent node. -- Obtain the preorder, inorder, postorder, and level-order traversal sequences. +- Given a node, get its value, left or right child, and parent. +- Obtain preorder, inorder, postorder, and level-order traversal sequences. ```src [file]{array_binary_tree}-[class]{array_binary_tree}-[func]{} ``` -## Advantages and Limitations +## Strengths and limitations -The array representation of binary trees has the following advantages: +Array representation offers the following advantages. -- Arrays are stored in contiguous memory space, which is cache-friendly, allowing faster access and traversal. -- It does not require storing pointers, which saves space. -- It allows random access to nodes. +- Contiguous memory is cache-friendly, making access and traversal fast. +- No pointers need to be stored, saving space. +- Nodes can be accessed randomly by index. -However, the array representation also has some limitations: +However, it also has limitations. -- Array storage requires contiguous memory space, so it is not suitable for storing trees with a large amount of data. -- Adding or removing nodes requires array insertion and deletion operations, which have lower efficiency. -- When there are many `None` values in the binary tree, the proportion of node data contained in the array is low, leading to lower space utilization. +- Arrays require contiguous memory, making them unsuitable for extremely large trees. +- Adding and removing nodes requires array insertion and deletion operations, which are inefficient. +- If the tree has many missing nodes, actual node data occupies only a small fraction of the array, wasting space. diff --git a/en/docs/chapter_tree/avl_tree.md b/en/docs/chapter_tree/avl_tree.md index 620628054..b590250cc 100644 --- a/en/docs/chapter_tree/avl_tree.md +++ b/en/docs/chapter_tree/avl_tree.md @@ -1,30 +1,30 @@ -# AVL Tree * +# AVL trees * -In the "Binary Search Tree" section, we mentioned that after multiple insertion and removal operations, a binary search tree may degenerate into a linked list. In this case, the time complexity of all operations degrades from $O(\log n)$ to $O(n)$. +The binary-search-tree section explained that repeated insertions and deletions can make a tree degenerate into a linked list. All operations then degrade from $O(\log n)$ to $O(n)$ time. -As shown in the figure below, after two node removal operations, this binary search tree will degrade into a linked list. +In the example below, just two deletions turn the binary search tree into a linked list. -![Degradation of an AVL tree after removing nodes](avl_tree.assets/avltree_degradation_from_removing_node.png) +![An AVL-tree example degenerating after deletions](avl_tree.assets/avltree_degradation_from_removing_node.png) -For example, in the perfect binary tree shown in the figure below, after inserting two nodes, the tree will lean heavily to the left, and the time complexity of search operations will also degrade. +Likewise, inserting two nodes into the perfect binary tree below makes it heavily skewed to the left, degrading lookup performance. -![Degradation of an AVL tree after inserting nodes](avl_tree.assets/avltree_degradation_from_inserting_node.png) +![An AVL-tree example degenerating after insertions](avl_tree.assets/avltree_degradation_from_inserting_node.png) -In 1962, G. M. Adelson-Velsky and E. M. Landis proposed the AVL tree in their paper "An algorithm for the organization of information". The paper describes a series of operations that prevent an AVL tree from degenerating as nodes are inserted and removed, thereby keeping the time complexity of various operations at $O(\log n)$. In other words, in scenarios that require frequent insertion, deletion, lookup, and update operations, AVL trees can maintain consistently efficient performance and therefore have strong practical value. +In 1962, G. M. Adelson-Velsky and E. M. Landis introduced the AVL tree in their paper “An algorithm for the organization of information.” They described operations that prevent degeneration through repeated insertions and deletions, keeping the time complexity of operations at $O(\log n)$. AVL trees therefore maintain efficient data operations when insertions, deletions, lookups, and updates are frequent, making them valuable in practice. -## Common Terminology in AVL Trees +## AVL tree terminology -An AVL tree is both a binary search tree and a balanced binary tree, simultaneously satisfying all the properties of these two types of binary trees, hence it is a balanced binary search tree. +An AVL tree is both a binary search tree and a balanced binary tree, satisfying the properties of both. It is therefore a balanced binary search tree. -### Node Height +### Node height -Since the operations related to AVL trees require obtaining node heights, we need to add a `height` variable to the node class: +AVL operations need node heights, so add a `height` variable to the node class: === "Python" ```python title="" class TreeNode: - """AVL tree node""" + """AVL tree node class""" def __init__(self, val: int): self.val: int = val # Node value self.height: int = 0 # Node height @@ -35,7 +35,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "C++" ```cpp title="" - /* AVL tree node */ + /* AVL tree node class */ struct TreeNode { int val{}; // Node value int height = 0; // Node height @@ -49,7 +49,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "Java" ```java title="" - /* AVL tree node */ + /* AVL tree node class */ class TreeNode { public int val; // Node value public int height; // Node height @@ -62,7 +62,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "C#" ```csharp title="" - /* AVL tree node */ + /* AVL tree node class */ class TreeNode(int? x) { public int? val = x; // Node value public int height; // Node height @@ -74,7 +74,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "Go" ```go title="" - /* AVL tree node */ + /* AVL tree node struct */ type TreeNode struct { Val int // Node value Height int // Node height @@ -86,7 +86,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "Swift" ```swift title="" - /* AVL tree node */ + /* AVL tree node class */ class TreeNode { var val: Int // Node value var height: Int // Node height @@ -103,10 +103,10 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "JS" ```javascript title="" - /* AVL tree node */ + /* AVL tree node class */ class TreeNode { val; // Node value - height; // Node height + height; //Node height left; // Left child pointer right; // Right child pointer constructor(val, left, right, height) { @@ -121,7 +121,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "TS" ```typescript title="" - /* AVL tree node */ + /* AVL tree node class */ class TreeNode { val: number; // Node value height: number; // Node height @@ -129,9 +129,9 @@ Since the operations related to AVL trees require obtaining node heights, we nee right: TreeNode | null; // Right child pointer constructor(val?: number, height?: number, left?: TreeNode | null, right?: TreeNode | null) { this.val = val === undefined ? 0 : val; - this.height = height === undefined ? 0 : height; - this.left = left === undefined ? null : left; - this.right = right === undefined ? null : right; + this.height = height === undefined ? 0 : height; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; } } ``` @@ -139,7 +139,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "Dart" ```dart title="" - /* AVL tree node */ + /* AVL tree node class */ class TreeNode { int val; // Node value int height; // Node height @@ -155,7 +155,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee use std::rc::Rc; use std::cell::RefCell; - /* AVL tree node */ + /* AVL tree node struct */ struct TreeNode { val: i32, // Node value height: i32, // Node height @@ -179,7 +179,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "C" ```c title="" - /* AVL tree node */ + /* AVL tree node struct */ typedef struct TreeNode { int val; int height; @@ -203,7 +203,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee === "Kotlin" ```kotlin title="" - /* AVL tree node */ + /* AVL tree node class */ class TreeNode(val _val: Int) { // Node value var height: Int = 0 // Node height var left: TreeNode? = null // Left child @@ -228,15 +228,15 @@ Since the operations related to AVL trees require obtaining node heights, we nee end ``` -The "node height" refers to the distance from that node to its farthest leaf node, i.e., the number of edges on the path. It is important to note that the height of a leaf node is $0$, and the height of a null node is $-1$. We will create two utility functions for getting and updating the height of a node: +A node's height is the distance to its farthest leaf, measured in edges. A leaf has height $0$, while a missing node has height $-1$. We create two helper functions to retrieve and update a node's height: ```src [file]{avl_tree}-[class]{avl_tree}-[func]{update_height} ``` -### Node Balance Factor +### Balance factor -The balance factor of a node is defined as the height of the node's left subtree minus the height of its right subtree, and the balance factor of a null node is defined as $0$. We also encapsulate the function to obtain the node's balance factor for convenient subsequent use: +A node's balance factor is its left subtree's height minus its right subtree's height. A missing node has balance factor $0$. Wrap this calculation in a function for convenient use: ```src [file]{avl_tree}-[class]{avl_tree}-[func]{balance_factor} @@ -244,20 +244,20 @@ The balance factor of a node is defined as the height of the node's left !!! tip - Let the balance factor be $f$, then the balance factor of any node in an AVL tree satisfies $-1 \le f \le 1$. + If a node's balance factor is $f$, every node in an AVL tree satisfies $-1 \le f \le 1$. -## Rotations in AVL Trees +## AVL tree rotations -The characteristic of AVL trees lies in the "rotation" operation, which can restore balance to unbalanced nodes without affecting the inorder traversal sequence of the binary tree. In other words, **rotation operations can both maintain the property of a "binary search tree" and make the tree return to a "balanced binary tree"**. +The defining operation of an AVL tree is rotation, which restores balance without changing the inorder traversal sequence. **A rotation preserves the binary-search-tree property while restoring the balanced-tree property**. -We call nodes with a balance factor absolute value $> 1$ "unbalanced nodes". Depending on the imbalance situation, rotation operations are divided into four types: right rotation, left rotation, right rotation then left rotation, and left rotation then right rotation. Below we describe these rotation operations in detail. +A node whose balance factor has absolute value $> 1$ is “unbalanced.” Depending on the imbalance, we use one of four rotations: right, left, right-then-left, or left-then-right. Let us examine them in detail. -### Right Rotation +### Right rotation -As shown in the figure below, the value below the node is the balance factor. From bottom to top, the first unbalanced node in the binary tree is "node 3". We focus on the subtree with this unbalanced node as the root, denoting the node as `node` and its left child as `child`, and perform a "right rotation" operation. After the right rotation is completed, the subtree regains balance and still maintains the properties of a binary search tree. +In the figure below, balance factors appear beneath the nodes. Viewed from the bottom up, node 3 is the first unbalanced node. Focus on the subtree rooted there, naming its root `node` and its left child `child`. A right rotation restores balance while preserving the binary-search-tree property. === "<1>" - ![Steps of right rotation](avl_tree.assets/avltree_right_rotate_step1.png) + ![Steps of a right rotation](avl_tree.assets/avltree_right_rotate_step1.png) === "<2>" ![avltree_right_rotate_step2](avl_tree.assets/avltree_right_rotate_step2.png) @@ -268,91 +268,91 @@ As shown in the figure below, the value below the node is the balance factor. Fr === "<4>" ![avltree_right_rotate_step4](avl_tree.assets/avltree_right_rotate_step4.png) -As shown in the figure below, when the `child` node has a right child (denoted as `grand_child`), a step needs to be added in the right rotation: set `grand_child` as the left child of `node`. +If `child` has a right child, denoted by `grand_child`, add one step to the rotation: make `grand_child` the left child of `node`, as shown below. -![Right rotation with grand_child](avl_tree.assets/avltree_right_rotate_with_grandchild.png) +![A right rotation with grand_child](avl_tree.assets/avltree_right_rotate_with_grandchild.png) -"Right rotation" is a figurative term; in practice, it is achieved by modifying node pointers, as shown in the following code: +“Rotating right” is a visual description; the implementation updates node pointers: ```src [file]{avl_tree}-[class]{avl_tree}-[func]{right_rotate} ``` -### Left Rotation +### Left rotation -Correspondingly, if considering the "mirror" of the above unbalanced binary tree, the "left rotation" operation shown in the figure below needs to be performed. +For the mirror image of the unbalanced tree above, perform a left rotation, as shown below. -![Left rotation operation](avl_tree.assets/avltree_left_rotate.png) +![A left rotation](avl_tree.assets/avltree_left_rotate.png) -Similarly, as shown in the figure below, when the `child` node has a left child (denoted as `grand_child`), a step needs to be added in the left rotation: set `grand_child` as the right child of `node`. +Similarly, if `child` has a left child, denoted by `grand_child`, make it the right child of `node` during the rotation. -![Left rotation with grand_child](avl_tree.assets/avltree_left_rotate_with_grandchild.png) +![A left rotation with grand_child](avl_tree.assets/avltree_left_rotate_with_grandchild.png) -It can be observed that **right rotation and left rotation operations are mirror symmetric in logic, and the two imbalance cases they solve are also symmetric**. Based on symmetry, we only need to replace all `left` in the right rotation implementation code with `right`, and all `right` with `left`, to obtain the left rotation implementation code: +**Right and left rotations are mirror images, as are the imbalances they resolve**. To obtain left-rotation code, exchange every `left` and `right` in the right-rotation implementation: ```src [file]{avl_tree}-[class]{avl_tree}-[func]{left_rotate} ``` -### Left Rotation Then Right Rotation +### Left-then-right rotation -For the unbalanced node 3 in the figure below, using either left rotation or right rotation alone cannot restore the subtree to balance. In this case, a "left rotation" needs to be performed on `child` first, followed by a "right rotation" on `node`. +For unbalanced node 3 below, neither a left nor a right rotation alone restores balance. First rotate `child` to the left, then rotate `node` to the right. -![Left-right rotation](avl_tree.assets/avltree_left_right_rotate.png) +![A left-then-right rotation](avl_tree.assets/avltree_left_right_rotate.png) -### Right Rotation Then Left Rotation +### Right-then-left rotation -As shown in the figure below, for the mirror case of the above unbalanced binary tree, a "right rotation" needs to be performed on `child` first, then a "left rotation" on `node`. +For the mirror-image case below, first rotate `child` to the right, then rotate `node` to the left. -![Right-left rotation](avl_tree.assets/avltree_right_left_rotate.png) +![A right-then-left rotation](avl_tree.assets/avltree_right_left_rotate.png) -### Choice of Rotation +### Choosing a rotation -The four imbalances shown in the figure below correspond one-to-one with the above cases, requiring right rotation, left rotation then right rotation, right rotation then left rotation, and left rotation operations respectively. +The four cases below correspond to the examples above and require, respectively, right, left-then-right, right-then-left, and left rotations. -![The four rotation cases of AVL tree](avl_tree.assets/avltree_rotation_cases.png) +![The four AVL rotation cases](avl_tree.assets/avltree_rotation_cases.png) -As shown in the table below, we determine which case the unbalanced node belongs to by judging the signs of the balance factor of the unbalanced node and the balance factor of its taller-side child node. +As the table below shows, the signs of the unbalanced node's balance factor and that of the child on its taller side determine the case. -

Table   Conditions for Choosing Among the Four Rotation Cases

+

Table   Conditions for choosing a rotation

-| Balance factor of the unbalanced node | Balance factor of the child node | Rotation method to apply | -| -------------------------------------- | --------------------------------- | --------------------------------- | -| $> 1$ (left-leaning tree) | $\geq 0$ | Right rotation | -| $> 1$ (left-leaning tree) | $<0$ | Left rotation then right rotation | -| $< -1$ (right-leaning tree) | $\leq 0$ | Left rotation | -| $< -1$ (right-leaning tree) | $>0$ | Right rotation then left rotation | +| Unbalanced node's balance factor | Child's balance factor | Rotation | +| ------------------ | ---------------- | ---------------- | +| $> 1$ (left-heavy) | $\geq 0$ | Right | +| $> 1$ (left-heavy) | $<0$ | Left, then right | +| $< -1$ (right-heavy) | $\leq 0$ | Left | +| $< -1$ (right-heavy) | $>0$ | Right, then left | -For ease of use, we encapsulate the rotation operations into a function. **With this function, we can perform rotations for various imbalance situations, restoring balance to unbalanced nodes**. The code is as follows: +Wrap the rotation logic in one function. **It can then handle every imbalance and restore the affected node's balance**. The code is as follows: ```src [file]{avl_tree}-[class]{avl_tree}-[func]{rotate} ``` -## Common Operations in AVL Trees +## Common AVL tree operations -### Node Insertion +### Inserting a node -The node insertion operation in AVL trees is similar in principle to that in binary search trees. The only difference is that after inserting a node in an AVL tree, a series of unbalanced nodes may appear on the path from that node to the root. Therefore, **we need to start from this node and perform rotation operations from bottom to top, restoring balance to all unbalanced nodes**. The code is as follows: +Insertion largely follows the binary-search-tree procedure. The difference is that inserting a node may unbalance nodes along the path to the root. **Starting at the inserted node, work upward and perform rotations to restore balance at every affected node**: ```src [file]{avl_tree}-[class]{avl_tree}-[func]{insert_helper} ``` -### Node Removal +### Deleting a node -Similarly, on the basis of the binary search tree's node removal method, rotation operations need to be performed from bottom to top to restore balance to all unbalanced nodes. The code is as follows: +Similarly, extend binary-search-tree deletion with rotations from the bottom up to restore balance at every affected node: ```src [file]{avl_tree}-[class]{avl_tree}-[func]{remove_helper} ``` -### Node Search +### Finding a node -The node search operation in AVL trees is consistent with that in binary search trees, and will not be elaborated here. +Lookup is identical to binary-search-tree lookup, so we will not repeat it here. -## Typical Applications of AVL Trees +## Typical applications -- Organizing and storing large-scale data, suitable for scenarios with high-frequency searches and low-frequency insertions and deletions. -- Used to build index systems in databases. -- Red-black trees are also a common type of balanced binary search tree. Compared to AVL trees, red-black trees have more relaxed balance conditions, require fewer rotation operations for node insertion and deletion, and have higher average efficiency for node addition and deletion operations. +- Organizing and storing large datasets, particularly when lookups are frequent and insertions and deletions are less frequent. +- Building database indexing systems. +- Red-black trees are another common kind of balanced binary search tree. Their balance requirements are looser than those of AVL trees, so insertion and deletion need fewer rotations and are more efficient on average. diff --git a/en/docs/chapter_tree/binary_search_tree.md b/en/docs/chapter_tree/binary_search_tree.md index eddc63d64..aaecc4f2b 100755 --- a/en/docs/chapter_tree/binary_search_tree.md +++ b/en/docs/chapter_tree/binary_search_tree.md @@ -1,26 +1,26 @@ -# Binary Search Tree +# Binary search trees -As shown in the figure below, a binary search tree satisfies the following conditions. +A binary search tree satisfies the following conditions, as shown below. -1. For the root node, the value of all nodes in the left subtree $<$ the value of the root node $<$ the value of all nodes in the right subtree. -2. The left and right subtrees of any node are also binary search trees, i.e., they satisfy condition `1.` as well. +1. Every value in the left subtree is $<$ the root value, which is $<$ every value in the right subtree. +2. The left and right subtrees of every node are also binary search trees and satisfy condition `1.`. -![Binary search tree](binary_search_tree.assets/binary_search_tree.png) +![A binary search tree](binary_search_tree.assets/binary_search_tree.png) -## Operations on a Binary Search Tree +## Binary search tree operations -We encapsulate the binary search tree as a class `BinarySearchTree` and declare a member variable `root` pointing to the tree's root node. +We encapsulate the tree in a `BinarySearchTree` class with a member variable `root` pointing to its root node. -### Searching for a Node +### Finding a node -Given a target node value `num`, we can search according to the properties of the binary search tree. As shown in the figure below, we declare a node `cur` and start from the binary search tree's root node `root`, looping to compare `cur.val` with `num`. +To find a target value `num`, use the tree's ordering property. As shown below, initialize a node reference `cur` at `root` and repeatedly compare `cur.val` with `num`. -- If `cur.val < num`, it means the target node is in `cur`'s right subtree, thus execute `cur = cur.right`. -- If `cur.val > num`, it means the target node is in `cur`'s left subtree, thus execute `cur = cur.left`. -- If `cur.val = num`, it means the target node is found, exit the loop, and return the node. +- If `cur.val < num`, the target lies in the right subtree, so set `cur = cur.right`. +- If `cur.val > num`, the target lies in the left subtree, so set `cur = cur.left`. +- If `cur.val = num`, the target has been found; exit the loop and return the node. === "<1>" - ![Example of searching for a node in a binary search tree](binary_search_tree.assets/bst_search_step1.png) + ![Finding a node in a binary search tree](binary_search_tree.assets/bst_search_step1.png) === "<2>" ![bst_search_step2](binary_search_tree.assets/bst_search_step2.png) @@ -31,53 +31,53 @@ Given a target node value `num`, we can search according to the properties of th === "<4>" ![bst_search_step4](binary_search_tree.assets/bst_search_step4.png) -The search operation in a binary search tree follows the same principle as binary search: each round rules out half of the remaining cases. The number of loop iterations is at most the height of the tree. When the tree is balanced, the search takes $O(\log n)$ time. The example code is as follows: +Searching a binary search tree follows the same principle as binary search: each round eliminates half the possibilities. The number of iterations is at most the tree's height, giving $O(\log n)$ time when the tree is balanced. An implementation follows: ```src [file]{binary_search_tree}-[class]{binary_search_tree}-[func]{search} ``` -### Inserting a Node +### Inserting a node -Given an element `num` to be inserted, in order to maintain the property of the binary search tree "left subtree < root node < right subtree," the insertion process is as shown in the figure below. +To insert `num` while preserving the ordering “left subtree < root < right subtree,” follow the process below. -1. **Finding the insertion position**: Similar to the search operation, start from the root node and loop downward searching according to the size relationship between the current node value and `num`, until passing the leaf node (traversing to `None`) and then exit the loop. -2. **Insert the node at that position**: Create a node for `num` and place it at the `None` position. +1. **Find the insertion position**: as in a search, start at the root and move down according to comparisons with `num`. Stop when the traversal passes a leaf and reaches `None`. +2. **Insert at that position**: create a node containing `num` and place it at the `None` position. ![Inserting a node into a binary search tree](binary_search_tree.assets/bst_insert.png) -In the code implementation, note the following two points: +Keep two details in mind when implementing this. -- Binary search trees do not allow duplicate nodes; otherwise, the tree would no longer satisfy its definition. Therefore, if the node to be inserted already exists in the tree, the insertion is skipped and the function returns directly. -- To implement the node insertion, we need to use node `pre` to save the node from the previous loop iteration. This way, when traversing to `None`, we can obtain its parent node, thereby completing the node insertion operation. +- Duplicate values are not allowed under this definition. If the value already exists, return without inserting it. +- Keep a reference `pre` to the node from the previous iteration. When the traversal reaches `None`, `pre` gives its parent so that the new node can be attached. ```src [file]{binary_search_tree}-[class]{binary_search_tree}-[func]{insert} ``` -Similar to searching for a node, inserting a node uses $O(\log n)$ time. +Like lookup, insertion takes $O(\log n)$ time. -### Removing a Node +### Deleting a node -First, find the target node in the binary search tree, then remove it. Similar to node insertion, we need to ensure that after the removal operation is completed, the binary search tree's property of "left subtree $<$ root node $<$ right subtree" is still maintained. Therefore, depending on the number of child nodes the target node has, we consider three cases: degree $0$, degree $1$, and degree $2$, and perform the corresponding removal operation. +First locate the target node, then remove it while preserving “left subtree < root < right subtree.” The procedure depends on whether the target has 0, 1, or 2 children. -As shown in the figure below, when the degree of the node to be removed is $0$, it means the node is a leaf node and can be directly removed. +As shown below, a node of degree $0$ is a leaf and can be removed directly. -![Removing a node in a binary search tree (degree 0)](binary_search_tree.assets/bst_remove_case1.png) +![Deleting a node of degree 0](binary_search_tree.assets/bst_remove_case1.png) -As shown in the figure below, when the degree of the node to be removed is $1$, replacing the node to be removed with its child node is sufficient. +As shown below, replace a node of degree $1$ with its only child. -![Removing a node in a binary search tree (degree 1)](binary_search_tree.assets/bst_remove_case2.png) +![Deleting a node of degree 1](binary_search_tree.assets/bst_remove_case2.png) -When the degree of the node to be removed is $2$, we cannot directly remove it; instead, we need to use a node to replace it. To maintain the binary search tree's property of "left subtree $<$ root node $<$ right subtree," **this node can be either the smallest node in the right subtree or the largest node in the left subtree**. +A node of degree $2$ cannot simply be removed; another node must replace it. To preserve “left subtree $<$ root $<$ right subtree,” **use either the smallest node in the right subtree or the largest node in the left subtree**. -Assuming we choose the smallest node in the right subtree, that is, the inorder successor, the removal process is as shown in the figure below. +Suppose we choose the smallest node in the right subtree, the next node in inorder traversal. The process is shown below. -1. Find the next node of the node to be removed in the "inorder traversal sequence," denoted as `tmp`. -2. Replace the value of the node to be removed with the value of `tmp`, and recursively remove node `tmp` in the tree. +1. Find the target node's successor in inorder traversal and call it `tmp`. +2. Replace the target node's value with `tmp`'s value, then recursively delete `tmp` from the tree. === "<1>" - ![Removing a node in a binary search tree (degree 2)](binary_search_tree.assets/bst_remove_case3_step1.png) + ![Deleting a node of degree 2](binary_search_tree.assets/bst_remove_case3_step1.png) === "<2>" ![bst_remove_case3_step2](binary_search_tree.assets/bst_remove_case3_step2.png) @@ -88,42 +88,42 @@ Assuming we choose the smallest node in the right subtree, that is, the inorder === "<4>" ![bst_remove_case3_step4](binary_search_tree.assets/bst_remove_case3_step4.png) -The node removal operation also uses $O(\log n)$ time, where finding the node to be removed requires $O(\log n)$ time, and obtaining the inorder successor node requires $O(\log n)$ time. Example code is as follows: +Deletion also takes $O(\log n)$ time: locating the target takes $O(\log n)$, and finding its inorder successor takes $O(\log n)$. An implementation follows: ```src [file]{binary_search_tree}-[class]{binary_search_tree}-[func]{remove} ``` -### Inorder Traversal Is Ordered +### Inorder traversal produces sorted data -As shown in the figure below, the inorder traversal of a binary tree follows the "left $\rightarrow$ root $\rightarrow$ right" traversal order, while the binary search tree satisfies the "left child node $<$ root node $<$ right child node" size relationship. +As shown below, inorder traversal visits “left $\rightarrow$ root $\rightarrow$ right,” while a binary search tree obeys “left child $<$ root $<$ right child.” -This means that when performing an inorder traversal in a binary search tree, the next smallest node is always traversed first, thus yielding an important property: **The inorder traversal sequence of a binary search tree is ascending**. +Thus, inorder traversal always visits the next smallest node, giving an important property: **the inorder traversal of a binary search tree is in ascending order**. -Using the property of inorder traversal being ascending, we can obtain ordered data in a binary search tree in only $O(n)$ time, without the need for additional sorting operations, which is very efficient. +This lets us retrieve sorted data from the tree in just $O(n)$ time, with no additional sorting. -![Inorder traversal sequence of a binary search tree](binary_search_tree.assets/bst_inorder_traversal.png) +![Inorder traversal of a binary search tree](binary_search_tree.assets/bst_inorder_traversal.png) -## Efficiency of Binary Search Trees +## Efficiency -Given a set of data, we consider using an array or a binary search tree for storage. Observing the table below, all operations in a binary search tree have logarithmic time complexity, providing stable and efficient performance. Arrays are more efficient than binary search trees only in scenarios with high-frequency additions and low-frequency searches and deletions. +Suppose we want to store a dataset in an array or a binary search tree. As the table below shows, the tree's operations take logarithmic time, offering consistent and efficient performance. An array is faster only when insertions are frequent but lookups and deletions are infrequent. -

Table   Efficiency comparison between arrays and search trees

+

Table   Efficiency of arrays and search trees

-| | Unsorted array | Binary search tree | -| -------------- | -------------- | ------------------ | -| Search element | $O(n)$ | $O(\log n)$ | -| Insert element | $O(1)$ | $O(\log n)$ | -| Remove element | $O(n)$ | $O(\log n)$ | +| | Unsorted array | Binary search tree | +| -------- | -------- | ----------- | +| Find an element | $O(n)$ | $O(\log n)$ | +| Insert an element | $O(1)$ | $O(\log n)$ | +| Delete an element | $O(n)$ | $O(\log n)$ | -In the ideal case, a binary search tree is balanced, so any node can be found within $O(\log n)$ loop iterations. +Ideally, the tree is balanced, allowing any node to be found within $\log n$ iterations. -However, if we continuously insert and remove nodes in a binary search tree, it may degenerate into a linked list as shown in the figure below, where the time complexity of various operations also degrades to $O(n)$. +However, repeated insertions and deletions may cause it to degenerate into a linked list, as shown below. The time complexity of its operations then degrades to $O(n)$. -![Degradation of a binary search tree](binary_search_tree.assets/bst_degradation.png) +![Degeneration of a binary search tree](binary_search_tree.assets/bst_degradation.png) -## Common Applications of Binary Search Trees +## Common applications -- Used as multi-level indexes in systems to implement efficient search, insertion, and removal operations. -- Serves as the underlying data structure for certain search algorithms. -- Used to store data streams to maintain their ordered state. +- Multilevel indexes in systems, supporting efficient lookup, insertion, and deletion. +- The underlying data structure for certain search algorithms. +- Storing a data stream while maintaining its order. diff --git a/en/docs/chapter_tree/binary_tree.md b/en/docs/chapter_tree/binary_tree.md index c883777be..2af8a67ea 100644 --- a/en/docs/chapter_tree/binary_tree.md +++ b/en/docs/chapter_tree/binary_tree.md @@ -1,26 +1,26 @@ -# Binary Tree +# Binary trees -A binary tree is a non-linear data structure that models the hierarchical relationship between "ancestors" and "descendants" and embodies a divide-and-conquer pattern in which each split branches into two. Similar to a linked list, the basic unit of a binary tree is a node, and each node contains a value, a reference to its left child node, and a reference to its right child node. +A binary tree is a nonlinear data structure representing relationships between ancestors and descendants. It embodies the divide-and-conquer idea of splitting in two. Like a linked list, it is built from nodes, each containing a value and references to its left and right children. === "Python" ```python title="" class TreeNode: - """Binary tree node""" + """Binary tree node class""" def __init__(self, val: int): self.val: int = val # Node value - self.left: TreeNode | None = None # Reference to left child node - self.right: TreeNode | None = None # Reference to right child node + self.left: TreeNode | None = None # Left child reference + self.right: TreeNode | None = None # Right child reference ``` === "C++" ```cpp title="" - /* Binary tree node */ + /* Binary tree node struct */ struct TreeNode { int val; // Node value - TreeNode *left; // Pointer to left child node - TreeNode *right; // Pointer to right child node + TreeNode *left; // Left child pointer + TreeNode *right; // Right child pointer TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; ``` @@ -28,11 +28,11 @@ A binary tree is a non-linear data structure that models the hierarchical === "Java" ```java title="" - /* Binary tree node */ + /* Binary tree node class */ class TreeNode { int val; // Node value - TreeNode left; // Reference to left child node - TreeNode right; // Reference to right child node + TreeNode left; // Left child reference + TreeNode right; // Right child reference TreeNode(int x) { val = x; } } ``` @@ -40,18 +40,18 @@ A binary tree is a non-linear data structure that models the hierarchical === "C#" ```csharp title="" - /* Binary tree node */ + /* Binary tree node class */ class TreeNode(int? x) { public int? val = x; // Node value - public TreeNode? left; // Reference to left child node - public TreeNode? right; // Reference to right child node + public TreeNode? left; // Left child reference + public TreeNode? right; // Right child reference } ``` === "Go" ```go title="" - /* Binary tree node */ + /* Binary tree node struct */ type TreeNode struct { Val int Left *TreeNode @@ -60,8 +60,8 @@ A binary tree is a non-linear data structure that models the hierarchical /* Constructor */ func NewTreeNode(v int) *TreeNode { return &TreeNode{ - Left: nil, // Pointer to left child node - Right: nil, // Pointer to right child node + Left: nil, // Left child pointer + Right: nil, // Right child pointer Val: v, // Node value } } @@ -70,11 +70,11 @@ A binary tree is a non-linear data structure that models the hierarchical === "Swift" ```swift title="" - /* Binary tree node */ + /* Binary tree node class */ class TreeNode { var val: Int // Node value - var left: TreeNode? // Reference to left child node - var right: TreeNode? // Reference to right child node + var left: TreeNode? // Left child reference + var right: TreeNode? // Right child reference init(x: Int) { val = x @@ -85,11 +85,11 @@ A binary tree is a non-linear data structure that models the hierarchical === "JS" ```javascript title="" - /* Binary tree node */ + /* Binary tree node class */ class TreeNode { val; // Node value - left; // Pointer to left child node - right; // Pointer to right child node + left; // Left child pointer + right; // Right child pointer constructor(val, left, right) { this.val = val === undefined ? 0 : val; this.left = left === undefined ? null : left; @@ -101,7 +101,7 @@ A binary tree is a non-linear data structure that models the hierarchical === "TS" ```typescript title="" - /* Binary tree node */ + /* Binary tree node class */ class TreeNode { val: number; left: TreeNode | null; @@ -109,8 +109,8 @@ A binary tree is a non-linear data structure that models the hierarchical constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { this.val = val === undefined ? 0 : val; // Node value - this.left = left === undefined ? null : left; // Reference to left child node - this.right = right === undefined ? null : right; // Reference to right child node + this.left = left === undefined ? null : left; // Left child reference + this.right = right === undefined ? null : right; // Right child reference } } ``` @@ -118,11 +118,11 @@ A binary tree is a non-linear data structure that models the hierarchical === "Dart" ```dart title="" - /* Binary tree node */ + /* Binary tree node class */ class TreeNode { int val; // Node value - TreeNode? left; // Reference to left child node - TreeNode? right; // Reference to right child node + TreeNode? left; // Left child reference + TreeNode? right; // Right child reference TreeNode(this.val, [this.left, this.right]); } ``` @@ -133,11 +133,11 @@ A binary tree is a non-linear data structure that models the hierarchical use std::rc::Rc; use std::cell::RefCell; - /* Binary tree node */ + /* Binary tree node struct */ struct TreeNode { val: i32, // Node value - left: Option>>, // Reference to left child node - right: Option>>, // Reference to right child node + left: Option>>, // Left child reference + right: Option>>, // Right child reference } impl TreeNode { @@ -155,12 +155,12 @@ A binary tree is a non-linear data structure that models the hierarchical === "C" ```c title="" - /* Binary tree node */ + /* Binary tree node struct */ typedef struct TreeNode { int val; // Node value int height; // Node height - struct TreeNode *left; // Pointer to left child node - struct TreeNode *right; // Pointer to right child node + struct TreeNode *left; // Left child pointer + struct TreeNode *right; // Right child pointer } TreeNode; /* Constructor */ @@ -179,10 +179,10 @@ A binary tree is a non-linear data structure that models the hierarchical === "Kotlin" ```kotlin title="" - /* Binary tree node */ + /* Binary tree node class */ class TreeNode(val _val: Int) { // Node value - var left: TreeNode? = null // Reference to left child node - var right: TreeNode? = null // Reference to right child node + var left: TreeNode? = null // Left child reference + var right: TreeNode? = null // Right child reference } ``` @@ -192,8 +192,8 @@ A binary tree is a non-linear data structure that models the hierarchical ### Binary tree node class ### class TreeNode attr_accessor :val # Node value - attr_accessor :left # Reference to left child node - attr_accessor :right # Reference to right child node + attr_accessor :left # Left child reference + attr_accessor :right # Right child reference def initialize(val) @val = val @@ -201,48 +201,48 @@ A binary tree is a non-linear data structure that models the hierarchical end ``` -Each node has two references (pointers), pointing respectively to the left-child node and right-child node. This node is called the parent node of these two child nodes. When given a node of a binary tree, we call the tree formed by this node's left child and all nodes below it the left subtree of this node. Similarly, the right subtree can be defined. +Each node has two references or pointers, one to its left child and one to its right child. The node is their parent. Its left child and all nodes below that child form its left subtree; the right subtree is defined similarly. -**In a binary tree, every non-leaf node has child nodes and therefore non-empty subtrees.** As shown in the figure below, if "Node 2" is regarded as a parent node, its left and right child nodes are "Node 4" and "Node 5" respectively. The left subtree is formed by "Node 4" and all nodes beneath it, while the right subtree is formed by "Node 5" and all nodes beneath it. +**Every non-leaf node has children and at least one nonempty subtree**. In the figure below, if node 2 is the parent, its left and right children are nodes 4 and 5. Its left subtree consists of node 4 and everything below it, and its right subtree consists of node 5 and everything below it. -![Parent Node, child Node, subtree](binary_tree.assets/binary_tree_definition.png) +![Parents, children, and subtrees](binary_tree.assets/binary_tree_definition.png) -## Common Terminology of Binary Trees +## Common terminology -The commonly used terminology of binary trees is shown in the figure below. +The figure below illustrates common binary-tree terminology. -- Root node: The node at the top level of a binary tree, which does not have a parent node. -- Leaf node: A node that does not have any child nodes, with both of its pointers pointing to `None`. -- Edge: A line segment that connects two nodes, representing a reference (pointer) between the nodes. -- The level of a node: It increases from top to bottom, with the root node being at level 1. -- The degree of a node: The number of child nodes that a node has. In a binary tree, the degree can be 0, 1, or 2. -- The height of a binary tree: The number of edges from the root node to the farthest leaf node. -- The depth of a node: The number of edges from the root node to the node. -- The height of a node: The number of edges from the farthest leaf node to the node. +- Root: the topmost node, which has no parent. +- Leaf: a node with no children; both of its child references point to `None`. +- Edge: a connection between two nodes, represented by a reference or pointer. +- A node's level: levels increase from top to bottom, with the root at level 1. +- A node's degree: its number of children, which is 0, 1, or 2 in a binary tree. +- A tree's height: the number of edges from the root to its farthest leaf. +- A node's depth: the number of edges from the root to that node. +- A node's height: the number of edges from that node to its farthest leaf. -![Common Terminology of Binary Trees](binary_tree.assets/binary_tree_terminology.png) +![Common binary-tree terminology](binary_tree.assets/binary_tree_terminology.png) !!! tip - We usually define "height" and "depth" as the number of edges traversed, but some textbooks and problem statements define them as the number of nodes on the path. In that case, both values are larger by 1. + Height and depth are usually measured in edges, but some problems or textbooks count nodes instead. Under that convention, add 1 to both height and depth. -## Basic Operations of Binary Trees +## Basic operations -### Initializing a Binary Tree +### Initializing a binary tree -Similar to a linked list, the initialization of a binary tree involves first creating the nodes and then establishing the references (pointers) between them. +As with a linked list, first create the nodes, then connect them with references or pointers. === "Python" ```python title="binary_tree.py" - # Initializing a binary tree - # Initializing nodes + # Initialize a binary tree + # Initialize nodes n1 = TreeNode(val=1) n2 = TreeNode(val=2) n3 = TreeNode(val=3) n4 = TreeNode(val=4) n5 = TreeNode(val=5) - # Linking references (pointers) between nodes + # Connect nodes with references (pointers) n1.left = n2 n1.right = n3 n2.left = n4 @@ -252,14 +252,14 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "C++" ```cpp title="binary_tree.cpp" - /* Initializing a binary tree */ - // Initializing nodes + /* Initialize a binary tree */ + // Initialize nodes TreeNode* n1 = new TreeNode(1); TreeNode* n2 = new TreeNode(2); TreeNode* n3 = new TreeNode(3); TreeNode* n4 = new TreeNode(4); TreeNode* n5 = new TreeNode(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1->left = n2; n1->right = n3; n2->left = n4; @@ -269,13 +269,13 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "Java" ```java title="binary_tree.java" - // Initializing nodes + // Initialize nodes TreeNode n1 = new TreeNode(1); TreeNode n2 = new TreeNode(2); TreeNode n3 = new TreeNode(3); TreeNode n4 = new TreeNode(4); TreeNode n5 = new TreeNode(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.left = n2; n1.right = n3; n2.left = n4; @@ -285,14 +285,14 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "C#" ```csharp title="binary_tree.cs" - /* Initializing a binary tree */ - // Initializing nodes + /* Initialize a binary tree */ + // Initialize nodes TreeNode n1 = new(1); TreeNode n2 = new(2); TreeNode n3 = new(3); TreeNode n4 = new(4); TreeNode n5 = new(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.left = n2; n1.right = n3; n2.left = n4; @@ -302,14 +302,14 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "Go" ```go title="binary_tree.go" - /* Initializing a binary tree */ - // Initializing nodes + /* Initialize a binary tree */ + // Initialize nodes n1 := NewTreeNode(1) n2 := NewTreeNode(2) n3 := NewTreeNode(3) n4 := NewTreeNode(4) n5 := NewTreeNode(5) - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.Left = n2 n1.Right = n3 n2.Left = n4 @@ -319,13 +319,13 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "Swift" ```swift title="binary_tree.swift" - // Initializing nodes + // Initialize nodes let n1 = TreeNode(x: 1) let n2 = TreeNode(x: 2) let n3 = TreeNode(x: 3) let n4 = TreeNode(x: 4) let n5 = TreeNode(x: 5) - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.left = n2 n1.right = n3 n2.left = n4 @@ -335,14 +335,14 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "JS" ```javascript title="binary_tree.js" - /* Initializing a binary tree */ - // Initializing nodes + /* Initialize a binary tree */ + // Initialize nodes let n1 = new TreeNode(1), n2 = new TreeNode(2), n3 = new TreeNode(3), n4 = new TreeNode(4), n5 = new TreeNode(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.left = n2; n1.right = n3; n2.left = n4; @@ -352,14 +352,14 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "TS" ```typescript title="binary_tree.ts" - /* Initializing a binary tree */ - // Initializing nodes + /* Initialize a binary tree */ + // Initialize nodes let n1 = new TreeNode(1), n2 = new TreeNode(2), n3 = new TreeNode(3), n4 = new TreeNode(4), n5 = new TreeNode(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.left = n2; n1.right = n3; n2.left = n4; @@ -369,14 +369,14 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "Dart" ```dart title="binary_tree.dart" - /* Initializing a binary tree */ - // Initializing nodes + /* Initialize a binary tree */ + // Initialize nodes TreeNode n1 = new TreeNode(1); TreeNode n2 = new TreeNode(2); TreeNode n3 = new TreeNode(3); TreeNode n4 = new TreeNode(4); TreeNode n5 = new TreeNode(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.left = n2; n1.right = n3; n2.left = n4; @@ -386,13 +386,13 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "Rust" ```rust title="binary_tree.rs" - // Initializing nodes + // Initialize nodes let n1 = TreeNode::new(1); let n2 = TreeNode::new(2); let n3 = TreeNode::new(3); let n4 = TreeNode::new(4); let n5 = TreeNode::new(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.borrow_mut().left = Some(n2.clone()); n1.borrow_mut().right = Some(n3); n2.borrow_mut().left = Some(n4); @@ -402,14 +402,14 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "C" ```c title="binary_tree.c" - /* Initializing a binary tree */ - // Initializing nodes + /* Initialize a binary tree */ + // Initialize nodes TreeNode *n1 = newTreeNode(1); TreeNode *n2 = newTreeNode(2); TreeNode *n3 = newTreeNode(3); TreeNode *n4 = newTreeNode(4); TreeNode *n5 = newTreeNode(5); - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1->left = n2; n1->right = n3; n2->left = n4; @@ -419,13 +419,13 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "Kotlin" ```kotlin title="binary_tree.kt" - // Initializing nodes + // Initialize nodes val n1 = TreeNode(1) val n2 = TreeNode(2) val n3 = TreeNode(3) val n4 = TreeNode(4) val n5 = TreeNode(5) - // Linking references (pointers) between nodes + // Connect nodes with references (pointers) n1.left = n2 n1.right = n3 n2.left = n4 @@ -435,86 +435,88 @@ Similar to a linked list, the initialization of a binary tree involves first cre === "Ruby" ```ruby title="binary_tree.rb" - # Initializing a binary tree - # Initializing nodes + # Initialize a binary tree + # Initialize nodes n1 = TreeNode.new(1) n2 = TreeNode.new(2) n3 = TreeNode.new(3) n4 = TreeNode.new(4) n5 = TreeNode.new(5) - # Linking references (pointers) between nodes + # Connect nodes with references (pointers) n1.left = n2 n1.right = n3 n2.left = n4 n2.right = n5 ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%E4%BA%8C%E5%8F%89%E6%A0%91%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%B7%A6%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%E5%8F%B3%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%8C%E5%8F%89%E6%A0%91%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8A%82%E7%82%B9%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%EF%BC%88%E6%8C%87%E9%92%88%EF%BC%89%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Left%20child%20reference%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20Right%20child%20reference%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20binary%20tree%0A%20%20%20%20%23%20Initialize%20nodes%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20Connect%20nodes%20with%20references%20%28pointers%29%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Inserting and Removing Nodes +### Inserting and deleting nodes -Similar to a linked list, inserting and removing nodes in a binary tree can be achieved by modifying pointers. The figure below provides an example. +As with linked lists, insertion and deletion can be implemented by changing pointers. An example is shown below. -![Inserting and removing nodes in a binary tree](binary_tree.assets/binary_tree_add_remove.png) +![Inserting and deleting a binary-tree node](binary_tree.assets/binary_tree_add_remove.png) === "Python" ```python title="binary_tree.py" - # Inserting and removing nodes + # Insert and delete a node p = TreeNode(0) - # Inserting node P between n1 -> n2 + # Insert node P between n1 and n2 n1.left = p p.left = n2 - # Removing node P + # Delete node P n1.left = n2 ``` === "C++" ```cpp title="binary_tree.cpp" - /* Inserting and removing nodes */ + /* Insert and delete a node */ TreeNode* P = new TreeNode(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1->left = P; P->left = n2; - // Removing node P + // Delete node P n1->left = n2; + // Free the memory + delete P; ``` === "Java" ```java title="binary_tree.java" TreeNode P = new TreeNode(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.left = P; P.left = n2; - // Removing node P + // Delete node P n1.left = n2; ``` === "C#" ```csharp title="binary_tree.cs" - /* Inserting and removing nodes */ + /* Insert and delete a node */ TreeNode P = new(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.left = P; P.left = n2; - // Removing node P + // Delete node P n1.left = n2; ``` === "Go" ```go title="binary_tree.go" - /* Inserting and removing nodes */ - // Inserting node P between n1 and n2 + /* Insert and delete a node */ + // Insert node P between n1 and n2 p := NewTreeNode(0) n1.Left = p p.Left = n2 - // Removing node P + // Delete node P n1.Left = n2 ``` @@ -522,46 +524,46 @@ Similar to a linked list, inserting and removing nodes in a binary tree can be a ```swift title="binary_tree.swift" let P = TreeNode(x: 0) - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.left = P P.left = n2 - // Removing node P + // Delete node P n1.left = n2 ``` === "JS" ```javascript title="binary_tree.js" - /* Inserting and removing nodes */ + /* Insert and delete a node */ let P = new TreeNode(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.left = P; P.left = n2; - // Removing node P + // Delete node P n1.left = n2; ``` === "TS" ```typescript title="binary_tree.ts" - /* Inserting and removing nodes */ + /* Insert and delete a node */ const P = new TreeNode(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.left = P; P.left = n2; - // Removing node P + // Delete node P n1.left = n2; ``` === "Dart" ```dart title="binary_tree.dart" - /* Inserting and removing nodes */ + /* Insert and delete a node */ TreeNode P = new TreeNode(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.left = P; P.left = n2; - // Removing node P + // Delete node P n1.left = n2; ``` @@ -569,102 +571,104 @@ Similar to a linked list, inserting and removing nodes in a binary tree can be a ```rust title="binary_tree.rs" let p = TreeNode::new(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.borrow_mut().left = Some(p.clone()); p.borrow_mut().left = Some(n2.clone()); - // Removing node P + // Delete node p n1.borrow_mut().left = Some(n2); ``` === "C" ```c title="binary_tree.c" - /* Inserting and removing nodes */ + /* Insert and delete a node */ TreeNode *P = newTreeNode(0); - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1->left = P; P->left = n2; - // Removing node P + // Delete node P n1->left = n2; + // Free the memory + free(P); ``` === "Kotlin" ```kotlin title="binary_tree.kt" val P = TreeNode(0) - // Inserting node P between n1 and n2 + // Insert node P between n1 and n2 n1.left = P P.left = n2 - // Removing node P + // Delete node P n1.left = n2 ``` === "Ruby" ```ruby title="binary_tree.rb" - # Inserting and removing nodes + # Insert and delete a node _p = TreeNode.new(0) - # Inserting node _p between n1 and n2 + # Insert node _p between n1 and n2 n1.left = _p _p.left = n2 - # Removing node _p + # Delete the node n1.left = n2 ``` -??? pythontutor "Code Visualization" +??? pythontutor "Visualize execution" - https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%E4%BA%8C%E5%8F%89%E6%A0%91%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%B7%A6%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%E5%8F%B3%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%8C%E5%8F%89%E6%A0%91%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8A%82%E7%82%B9%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%EF%BC%88%E6%8C%87%E9%92%88%EF%BC%89%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5%0A%0A%20%20%20%20%23%20%E6%8F%92%E5%85%A5%E4%B8%8E%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%0A%20%20%20%20p%20%3D%20TreeNode%280%29%0A%20%20%20%20%23%20%E5%9C%A8%20n1%20-%3E%20n2%20%E4%B8%AD%E9%97%B4%E6%8F%92%E5%85%A5%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20p%0A%20%20%20%20p.left%20%3D%20n2%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20n2&cumulative=false&curInstr=37&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false + https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22Binary%20tree%20node%20class%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Node%20value%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20Left%20child%20reference%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20Right%20child%20reference%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20Initialize%20a%20binary%20tree%0A%20%20%20%20%23%20Initialize%20nodes%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20Connect%20nodes%20with%20references%20%28pointers%29%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5%0A%0A%20%20%20%20%23%20Insert%20and%20delete%20a%20node%0A%20%20%20%20p%20%3D%20TreeNode%280%29%0A%20%20%20%20%23%20Insert%20node%20P%20between%20n1%20and%20n2%0A%20%20%20%20n1.left%20%3D%20p%0A%20%20%20%20p.left%20%3D%20n2%0A%20%20%20%20%23%20Delete%20node%20P%0A%20%20%20%20n1.left%20%3D%20n2&cumulative=false&curInstr=37&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false !!! tip - Keep in mind that inserting a node can alter the original logical structure of a binary tree, while deleting a node usually entails removing that node together with its entire subtree. In practice, insertion and deletion in binary trees are therefore typically implemented as coordinated sequences of operations to achieve a meaningful result. + Inserting a node may change the tree's logical structure, while deleting a node generally means deleting it and all its subtrees. Meaningful insertion and deletion operations therefore usually require several coordinated steps. -## Common Types of Binary Trees +## Common types of binary trees -### Perfect Binary Tree +### Perfect binary trees -As shown in the figure below, a perfect binary tree has every level completely filled. In a perfect binary tree, leaf nodes have a degree of $0$, while all other nodes have a degree of $2$. If the tree height is $h$, the total number of nodes is $2^{h+1} - 1$, following a standard exponential pattern that mirrors the common phenomenon of cell division in nature. +In a perfect binary tree, every level is completely filled, as shown below. Leaves have degree $0$, and all other nodes have degree $2$. A tree of height $h$ has $2^{h+1} - 1$ nodes, an exponential relationship reminiscent of cell division in nature. !!! tip - Please note that in the Chinese community, a perfect binary tree is often referred to as a full binary tree. + In Chinese-language resources, a perfect binary tree is often called 满二叉树. -![Perfect binary tree](binary_tree.assets/perfect_binary_tree.png) +![A perfect binary tree](binary_tree.assets/perfect_binary_tree.png) -### Complete Binary Tree +### Complete binary trees -As shown in the figure below, a complete binary tree only allows the bottom level to be incompletely filled, and the nodes at the bottom level must be filled continuously from left to right. Note that a perfect binary tree is also a complete binary tree. +Only the lowest level of a complete binary tree may be incomplete, and its nodes must fill that level consecutively from left to right, as shown below. Every perfect binary tree is also complete. -![Complete binary tree](binary_tree.assets/complete_binary_tree.png) +![A complete binary tree](binary_tree.assets/complete_binary_tree.png) -### Full Binary Tree +### Full binary trees -As shown in the figure below, in a full binary tree, all nodes except leaf nodes have two child nodes. +In a full binary tree, every node other than a leaf has exactly two children, as shown below. -![Full binary tree](binary_tree.assets/full_binary_tree.png) +![A full binary tree](binary_tree.assets/full_binary_tree.png) -### Balanced Binary Tree +### Balanced binary trees -As shown in the figure below, in a balanced binary tree, the absolute difference between the height of the left and right subtrees of any node does not exceed 1. +In a balanced binary tree, the heights of the left and right subtrees of every node differ by at most 1, as shown below. -![Balanced binary tree](binary_tree.assets/balanced_binary_tree.png) +![A balanced binary tree](binary_tree.assets/balanced_binary_tree.png) -## Degeneration of Binary Trees +## Degeneration of a binary tree -The figure below contrasts the ideal and degenerate structures of binary trees. When every level is filled, the tree becomes a "perfect binary tree"; when all nodes skew to one side, the binary tree degenerates into a "linked list". +The figure below shows ideal and degenerate tree structures. When every level is full, the tree is perfect. When every node lies along one side, the tree degenerates into a linked list. -- A perfect binary tree is the ideal case, fully leveraging the divide-and-conquer advantages of binary trees. -- A linked list represents the other extreme, where all operations become linear operations with time complexity degrading to $O(n)$. +- A perfect binary tree is the ideal case, making full use of divide and conquer. +- A linked list is the opposite extreme: operations become linear, with time complexity degrading to $O(n)$. -![The Best and Worst Structures of Binary Trees](binary_tree.assets/binary_tree_best_worst_cases.png) +![Best and worst binary-tree structures](binary_tree.assets/binary_tree_best_worst_cases.png) -As shown in the table below, in the best and worst structures, the binary tree achieves either maximum or minimum values for leaf node count, total number of nodes, and height. +As the table below shows, quantities such as leaf count, total node count, and height reach their maximum or minimum values in these best and worst structures. -

Table   The Best and Worst Structures of Binary Trees

+

Table   Best and worst binary-tree structures

-| | Perfect binary tree | Linked list | -| ----------------------------------------------- | ------------------- | ----------- | -| Number of nodes at level $i$ | $2^{i-1}$ | $1$ | -| Number of leaf nodes in a tree with height $h$ | $2^h$ | $1$ | -| Total number of nodes in a tree with height $h$ | $2^{h+1} - 1$ | $h + 1$ | -| Height of a tree with $n$ total nodes | $\log_2 (n+1) - 1$ | $n - 1$ | +| | Perfect binary tree | Linked list | +| --------------------------- | ------------------ | ------- | +| Nodes at level $i$ | $2^{i-1}$ | $1$ | +| Leaves in a tree of height $h$ | $2^h$ | $1$ | +| Total nodes in a tree of height $h$ | $2^{h+1} - 1$ | $h + 1$ | +| Height of a tree with $n$ nodes | $\log_2 (n+1) - 1$ | $n - 1$ | diff --git a/en/docs/chapter_tree/binary_tree_traversal.md b/en/docs/chapter_tree/binary_tree_traversal.md index 08d946e40..845cb7533 100755 --- a/en/docs/chapter_tree/binary_tree_traversal.md +++ b/en/docs/chapter_tree/binary_tree_traversal.md @@ -1,41 +1,41 @@ -# Binary Tree Traversal +# Traversing a binary tree -From a physical structure perspective, a tree is a data structure based on linked lists. Hence, its traversal method involves accessing nodes one by one through pointers. However, a tree is a non-linear data structure, which makes traversing a tree more complex than traversing a linked list, requiring the assistance of search algorithms. +In its physical representation, a tree is a linked data structure, so traversal follows pointers from node to node. Unlike a linked list, however, a tree is nonlinear. Traversing it is more complex and requires a search algorithm. -The common traversal methods for binary trees include level-order traversal, pre-order traversal, in-order traversal, and post-order traversal. +Common binary-tree traversals include level-order, preorder, inorder, and postorder traversal. -## Level-Order Traversal +## Level-order traversal -As shown in the figure below, level-order traversal traverses the binary tree from top to bottom, layer by layer. Within each level, it visits nodes from left to right. +As shown below, level-order traversal visits the tree level by level from top to bottom, moving from left to right within each level. -Level-order traversal is essentially breadth-first traversal, also known as breadth-first search (BFS), which proceeds outward level by level. +Level-order traversal is a form of breadth-first traversal, also called breadth-first search (BFS). It explores outward one layer at a time. ![Level-order traversal of a binary tree](binary_tree_traversal.assets/binary_tree_bfs.png) -### Code Implementation +### Implementation -Breadth-first traversal is typically implemented with the help of a "queue". The queue follows the "first in, first out" rule, while breadth-first traversal follows the "layer-by-layer progression" rule; the underlying ideas of the two are consistent. The implementation code is as follows: +Breadth-first traversal usually uses a queue. A queue's first-in, first-out rule matches the traversal's level-by-level progression. The implementation follows: ```src [file]{binary_tree_bfs}-[class]{}-[func]{level_order} ``` -### Complexity Analysis +### Complexity analysis -- **Time complexity is $O(n)$**: All nodes are visited once, using $O(n)$ time, where $n$ is the number of nodes. -- **Space complexity is $O(n)$**: In the worst case, i.e., a full binary tree, before traversing to the bottom level, the queue contains at most $(n + 1) / 2$ nodes simultaneously, occupying $O(n)$ space. +- **Time complexity: $O(n)$**. Each of the $n$ nodes is visited once, taking $O(n)$ time. +- **Space complexity: $O(n)$**. In the worst case, a perfect binary tree, the queue holds up to $(n + 1) / 2$ nodes just before the lowest level is visited, requiring $O(n)$ space. -## Preorder, Inorder, and Postorder Traversal +## Preorder, inorder, and postorder traversal -Correspondingly, preorder, inorder, and postorder traversals all belong to depth-first traversal, also known as depth-first search (DFS), which goes as deep as possible before backtracking. +Preorder, inorder, and postorder traversal are forms of depth-first traversal, also called depth-first search (DFS). They follow a path to its end, then backtrack and continue. -The figure below shows how depth-first traversal works on a binary tree. **Depth-first traversal is like "walking" around the perimeter of the entire binary tree**, encountering three positions at each node, corresponding to preorder, inorder, and postorder traversal. +The figure below illustrates depth-first traversal. **Imagine walking once around the outside of the entire binary tree**. At each node, you encounter three positions corresponding to preorder, inorder, and postorder visits. -![Preorder, inorder, and postorder traversal of a binary tree](binary_tree_traversal.assets/binary_tree_dfs.png) +![Preorder, inorder, and postorder traversal of a binary search tree](binary_tree_traversal.assets/binary_tree_dfs.png) -### Code Implementation +### Implementation -Depth-first search is usually implemented based on recursion: +Depth-first search is usually implemented recursively: ```src [file]{binary_tree_dfs}-[class]{}-[func]{post_order} @@ -43,12 +43,12 @@ Depth-first search is usually implemented based on recursion: !!! tip - Depth-first search can also be implemented iteratively, and interested readers can explore this on their own. + Depth-first search can also be implemented iteratively. Interested readers can explore this approach themselves. -The figure below shows the recursive process of preorder traversal of a binary tree, which can be divided into two opposite phases: "descending" and "returning". +The figure below shows the recursive process for preorder traversal, with two opposing phases: descending into calls and returning from them. -1. "Descending" means making a new recursive call, during which the program visits the next node. -2. "Returning" means the function call returns, indicating that the current node has been fully processed. +1. Descending starts a new function call and visits the next node. +2. Returning ends a function call, indicating that the current node has been fully processed. === "<1>" ![The recursive process of preorder traversal](binary_tree_traversal.assets/preorder_step1.png) @@ -83,7 +83,7 @@ The figure below shows the recursive process of preorder traversal of a binary t === "<11>" ![preorder_step11](binary_tree_traversal.assets/preorder_step11.png) -### Complexity Analysis +### Complexity analysis -- **Time complexity is $O(n)$**: All nodes are visited once, using $O(n)$ time. -- **Space complexity is $O(n)$**: In the worst case, i.e., the tree degenerates into a linked list, the recursion depth reaches $n$, and the system occupies $O(n)$ stack frame space. +- **Time complexity: $O(n)$**. Every node is visited once, taking $O(n)$ time. +- **Space complexity: $O(n)$**. In the worst case, the tree degenerates into a linked list, recursion reaches depth $n$, and stack frames require $O(n)$ space. diff --git a/en/docs/chapter_tree/exercises.md b/en/docs/chapter_tree/exercises.md index 004fc05c6..f7e81344a 100644 --- a/en/docs/chapter_tree/exercises.md +++ b/en/docs/chapter_tree/exercises.md @@ -1,10 +1,10 @@ # Exercises -## Concept Review +## Check your understanding -### Complete, Full, and Perfect Binary Trees +### Complete, full, and perfect binary trees -The following two arrays represent binary trees in level order, where `None` marks an empty position: +The following arrays represent binary trees in level order, with `None` marking an empty position: - Tree A: `[1, 2, 3, 4, 5, 6]` - Tree B: `[1, 2, 3, None, None, 6, 7]` @@ -12,30 +12,30 @@ The following two arrays represent binary trees in level order, where `None` mar 1. Which tree is a complete binary tree? -2. Which tree is a full binary tree, meaning every non-leaf node has two children? -3. Is either tree a perfect binary tree? Explain the reason for each tree. +2. Which is a full binary tree, meaning that every non-leaf node has two children? +3. Is either tree a perfect binary tree? Explain for each tree. -??? success "Answer" +??? success "Reference answer" - 1. Tree A is a complete binary tree. Only its lowest level is not full, and the nodes on that level occupy consecutive positions from left to right. - Tree B is not complete because there are empty positions on the left of the lowest level while nodes still appear on the right. + 1. Tree A is complete. Only its bottom level is not full, and the nodes occupy consecutive positions from left to right. + Tree B is not complete: its bottom level has empty positions on the left but still has nodes farther to the right. - 2. Tree B is a full binary tree: nodes 1 and 3 each have two children, and all other nodes are leaves. - Tree A is not full because node 3 has only one child, its left child 6. + 2. Tree B is full: nodes 1 and 3 each have two children, and all the other nodes are leaves. + Tree A is not full because node 3 has only its left child, 6. - 3. Neither tree is perfect because the lowest level of each tree is not completely filled. + 3. Neither tree is perfect, because neither has a completely filled bottom level. -### Three Traversal Orders for the Same Tree +### Three traversal orders for the same tree -Store the array `[1, 2, 3, 4, 5, 6, 7]` in level order in a complete binary tree. +Store the array `[1, 2, 3, 4, 5, 6, 7]` in a complete binary tree in level order. 1. Draw the tree. 2. Write its preorder, inorder, and postorder traversal sequences. -3. In the inorder sequence, which parts of the tree correspond to the subsequences to the left and right of root node 1? +3. In the inorder sequence, which parts of the tree do the sequences to the left and right of root node 1 correspond to? -??? success "Answer" +??? success "Reference answer" 1. The tree is: @@ -47,14 +47,14 @@ Store the array `[1, 2, 3, 4, 5, 6, 7]` in level order in a complete binary tree 4 5 6 7 ``` - 2. The preorder traversal is `1, 2, 4, 5, 3, 6, 7`; - the inorder traversal is `4, 2, 5, 1, 6, 3, 7`; - the postorder traversal is `4, 5, 2, 6, 7, 3, 1`. + 2. Preorder: `1, 2, 4, 5, 3, 6, 7`; + inorder: `4, 2, 5, 1, 6, 3, 7`; + postorder: `4, 5, 2, 6, 7, 3, 1`. - 3. The sequence `4, 2, 5` to the left of root node 1 is the inorder traversal of the left subtree; - the sequence `6, 3, 7` to its right is the inorder traversal of the right subtree. + 3. The sequence `4, 2, 5` to the left of root node 1 is the inorder traversal of the left subtree. + The sequence `6, 3, 7` to its right is the inorder traversal of the right subtree. -### Compare Two Binary Search Trees +### Comparing two binary search trees Insert each of the following sequences from left to right into an empty binary search tree: @@ -63,64 +63,64 @@ Insert each of the following sequences from left to right into an empty binary s -1. For each tree, write the nodes visited when searching for 7. -2. If height is measured by the number of edges from the root node to the farthest leaf node, what is the height of each tree? -3. Based on the first two questions, is searching for 7 equally efficient in the two trees? Explain using the trees' shapes and search paths. +1. Write the nodes visited when searching for 7 in each tree. +2. If height is measured by the number of edges from the root to the farthest leaf, what is each tree's height? +3. Based on your answers, do the two trees search for 7 equally efficiently? Explain using their shapes and search paths. -??? success "Answer" +??? success "Reference answer" - 1. In the tree built from Sequence A, the search path is `4 → 6 → 7`. - In the tree built from Sequence B, the search path is `1 → 2 → 3 → 4 → 5 → 6 → 7`. + 1. In the tree built from sequence A, the search path is `4 → 6 → 7`. + In the tree built from sequence B, it is `1 → 2 → 3 → 4 → 5 → 6 → 7`. - 2. Every level of the first tree is full, and its height is 2. The second tree has only right children, and its height is 6. + 2. The first tree has every level filled and a height of 2. The second tree has only right children and a height of 6. - 3. Searching for 7 is not equally efficient in the two trees. The insertion order changes the shape and height of a binary search tree. The search visits only 3 nodes in the first tree - but all 7 nodes in the second. The taller the tree, the more nodes may need to be compared along a path in the worst case. + 3. The searches for 7 are not equally efficient. Insertion order changes the shape and height of a binary search tree. Searching for 7 visits only three nodes in the first tree, + but must visit seven in sequence in the second. The taller the tree, the more nodes may need to be compared along a path in the worst case. -## Programming Exercises +## Programming exercises -### Maximum Depth of a Binary Tree +### Maximum depth of a binary tree You are given the root node `root` of a binary tree. Each node contains an integer value and references to its left and right children. -The maximum depth is the **number of nodes** on the path from the root node to the farthest leaf node. Return the maximum depth of the tree; the maximum depth of an empty tree is 0. +The maximum depth is the **number of nodes** on the path from the root to the farthest leaf. Return the maximum depth of the tree; an empty tree has maximum depth 0. Use recursion. ??? tip "Hints" - 1. Depth is measured by the number of nodes in this exercise, so a tree containing only a root node has a maximum depth of 1 - 2. Let the recursive function return the maximum depth of the subtree rooted at the current node - 3. Return 0 for an empty node; for a nonempty node, return max(depth(left), depth(right)) + 1 + 1. Depth is counted in nodes here. A tree containing only a root has maximum depth 1. + 2. Have the recursive function return the maximum depth of the subtree rooted at the current node. + 3. Return 0 for an empty node; otherwise, return max(depth(left), depth(right)) + 1. [LeetCode](https://leetcode.com/problems/maximum-depth-of-binary-tree/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } -### Traverse a Binary Tree Level by Level +### Traversing a binary tree level by level -Given the root node `root` of a binary tree, use a queue to visit all nodes level by level from top to bottom and from left to right within each level. +Given the root node `root` of a binary tree, use a queue to visit all nodes level by level from top to bottom, visiting each level from left to right. -Return a two-dimensional array: the first subarray stores the values at the root's level, the second stores the values at the next level, and so on. -If the tree is empty, return an empty array. +Return a two-dimensional array: the first subarray contains the values on the root's level, the second contains those on the next level, and so on. +Return an empty array if the tree is empty. ??? tip "Hints" - 1. Level-order traversal visits earlier enqueued nodes first, so use a queue - 2. At the beginning of each round, all nodes currently in the queue belong to the same level - 3. First record the queue's length, then remove exactly that many nodes and enqueue their children + 1. Level-order traversal visits earlier enqueued nodes first, so use a queue. + 2. At the start of each round, the nodes in the queue belong to exactly one level. + 3. Record the queue's length first, then dequeue that many nodes and enqueue their children. [LeetCode](https://leetcode.com/problems/binary-tree-level-order-traversal/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } -### Kth Smallest Element in a Binary Search Tree +### The kth smallest element in a binary search tree A binary search tree contains `n` nodes with distinct values. -If all node values are arranged from smallest to largest, their positions are numbered starting from 1. +If all node values are sorted in ascending order, their positions are numbered starting from 1. -Given the root node `root` and an integer `k` satisfying `1 <= k <= n`, return the value at position `k`. -Find the answer directly during an inorder traversal rather than collecting all node values first. +Given the root node `root` and an integer `k` satisfying `1 <= k <= n`, return the node value in position `k`. +Find the answer directly during inorder traversal, without first collecting all node values. ??? tip "Hints" - 1. An inorder traversal of a binary search tree visits node values from smallest to largest - 2. Inorder traversal processes the left subtree, the current node, and then the right subtree; increment the count when visiting the current node - 3. When the count first equals k, the current node's value is the answer, so no further traversal is needed + 1. An inorder traversal of a binary search tree visits node values in ascending order. + 2. Inorder traversal processes the left subtree, the current node, and then the right subtree. Increment a counter by 1 when visiting the current node. + 3. When the counter first reaches k, the current node's value is the answer; there is no need to continue traversing. [LeetCode](https://leetcode.com/problems/kth-smallest-element-in-a-bst/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } diff --git a/en/docs/chapter_tree/index.md b/en/docs/chapter_tree/index.md index 96098cd4d..e696ef10e 100644 --- a/en/docs/chapter_tree/index.md +++ b/en/docs/chapter_tree/index.md @@ -1,9 +1,9 @@ -# Tree +# Trees -![Tree](../assets/covers/chapter_tree.jpg) +![Trees](../assets/covers/chapter_tree.jpg) !!! abstract - Towering trees are full of vitality, with deep roots, lush foliage, and sprawling branches. - - They offer a vivid illustration of divide-and-conquer in data structures. + A towering tree brims with life, its roots deep, its leaves lush, and its branches spreading wide. + + It gives vivid form to the idea of dividing data to conquer a problem. diff --git a/en/docs/chapter_tree/summary.md b/en/docs/chapter_tree/summary.md index 54f9b1ed4..da98e7664 100644 --- a/en/docs/chapter_tree/summary.md +++ b/en/docs/chapter_tree/summary.md @@ -1,54 +1,54 @@ # Summary -### Key Review +### Key takeaways -- A binary tree is a non-linear data structure that embodies the divide-and-conquer logic of splitting into two. Each binary tree node contains a value and two pointers, which point to its left and right child nodes. -- For a certain node in a binary tree, the tree formed by its left (right) child node and all nodes below is called the left (right) subtree of that node. -- Related terminology of binary trees includes root node, leaf node, level, degree, edge, height, and depth. -- The initialization, node insertion, and node removal operations of binary trees are similar to those of linked lists. -- Common types of binary trees include perfect binary trees, complete binary trees, full binary trees, and balanced binary trees. A perfect binary tree is the ideal form, while a linked list represents the worst degenerate case. -- A binary tree can be represented using an array by arranging node values and empty slots in level-order traversal sequence, and implementing pointers based on the index mapping relationship between parent and child nodes. -- Level-order traversal of a binary tree is a breadth-first search method that proceeds level by level, typically implemented using a queue. -- Preorder, inorder, and postorder traversals all belong to depth-first search, which proceeds by going as deep as possible before backtracking, typically using recursion. -- A binary search tree is an efficient data structure for element searching, with search, insertion, and removal operations all having time complexity of $O(\log n)$. When a binary search tree degenerates into a linked list, all time complexities degrade to $O(n)$. -- An AVL tree, also known as a balanced binary search tree, ensures the tree remains balanced after continuous node insertions and removals through rotation operations. -- Rotation operations in AVL trees include right rotation, left rotation, right rotation followed by left rotation, and left rotation followed by right rotation. After inserting or removing nodes, AVL trees perform rotations from bottom to top to restore balance. +- A binary tree is a nonlinear data structure that embodies the divide-and-conquer idea of splitting in two. Each node contains a value and two pointers, one to each child. +- A node's left or right child, together with everything below it, forms its left or right subtree. +- Tree terminology includes root, leaf, level, degree, edge, height, and depth. +- Initializing a binary tree and inserting or deleting nodes resemble linked-list operations. +- Common types include perfect, complete, full, and balanced binary trees. A perfect tree is the ideal form; a linked list is the worst degenerate form. +- A binary tree can be represented by an array of node values and empty positions in level order. Index mappings between parents and children replace pointers. +- Level-order traversal is breadth-first search: it expands outward one layer at a time and usually uses a queue. +- Preorder, inorder, and postorder traversal are depth-first search: they follow paths to their ends before backtracking and usually use recursion. +- A binary search tree supports efficient lookup, insertion, and deletion in $O(\log n)$ time. If it degenerates into a linked list, all these operations degrade to $O(n)$. +- An AVL tree is a balanced binary search tree that uses rotations to maintain balance through insertions and deletions. +- AVL rotations include right rotation, left rotation, right-then-left rotation, and left-then-right rotation. After an insertion or deletion, rotations are performed from the bottom up to restore balance. ### Q & A -**Q**: For a binary tree with only one node, are both the height of the tree and the depth of the root node $0$? +**Q**: For a binary tree with only one node, are both the tree's height and the root's depth $0$? -Yes, because height and depth are typically defined as the number of edges on the path. +Yes. Height and depth are usually defined by the number of edges traversed. -**Q**: The insertion and removal in a binary tree are generally accomplished by a set of operations. What does "a set of operations" refer to here? Does it imply releasing the resources of the child nodes? +**Q**: The text says binary-tree insertion and deletion usually involve a sequence of coordinated operations. What does that mean? Does it refer to releasing resources held by child nodes? -Taking the binary search tree as an example, the operation of removing a node needs to be handled in three different scenarios, each requiring multiple steps of node operations. +For example, deleting a node from a binary search tree has three cases, and each case requires several steps involving nodes. -**Q**: Why does DFS traversal of binary trees have three orders: preorder, inorder, and postorder, and what are their uses? +**Q**: Why does depth-first traversal have preorder, inorder, and postorder variants? What is each used for? -Similar to forward and reverse traversal of arrays, preorder, inorder, and postorder traversals are three methods of binary tree traversal that allow us to obtain a traversal result in a specific order. For example, in a binary search tree, since nodes satisfy the relationship `left child node value < root node value < right child node value`, we only need to traverse the tree with the priority of "left $\rightarrow$ root $\rightarrow$ right" to obtain an ordered node sequence. +Just as an array can be traversed forward or backward, these are three ways to visit a binary tree and obtain a particular ordering. In a binary search tree, values satisfy `left child value < root value < right child value`, so visiting “left $\rightarrow$ root $\rightarrow$ right” produces a sorted node sequence. -**Q**: In a right rotation operation handling the relationship between unbalanced nodes `node`, `child`, and `grand_child`, doesn't the connection between `node` and its parent node get lost after the right rotation? +**Q**: A right rotation adjusts `node`, `child`, and `grand_child`. What happens to the link between `node` and its parent? Doesn't the rotation break it? -We need to view this problem from a recursive perspective. The right rotation operation `right_rotate(root)` passes in the root node of the subtree and eventually returns the root node of the subtree after rotation with `return child`. The connection between the subtree's root node and its parent node is completed after the function returns, which is not within the maintenance scope of the right rotation operation. +View the operation recursively. `right_rotate(root)` receives a subtree's root and ends with `return child`, returning the new root of that subtree. The connection to its parent is updated after the function returns; it is outside the rotation function's responsibility. -**Q**: In C++, functions are divided into `private` and `public` sections. What considerations are there for this? Why are the `height()` function and the `updateHeight()` function placed in `public` and `private`, respectively? +**Q**: In C++, how do you decide which methods are `private` and which are `public`? Why is `height()` public but `updateHeight()` private? -It mainly depends on the method's usage scope. If a method is only used within the class, then it is designed as `private`. For example, calling `updateHeight()` alone by the user makes no sense, as it is only a step in insertion or removal operations. However, `height()` is used to access node height, similar to `vector.size()`, so it is set to `public` for ease of use. +The main consideration is where the method is used. A method used only within the class should be `private`. Calling `updateHeight()` alone is not meaningful to a user; it is just one step of insertion or deletion. In contrast, `height()` retrieves a node's height, much like `vector.size()`, so it is `public` for convenient access. -**Q**: How do you build a binary search tree from a set of input data? Is the choice of root node very important? +**Q**: How do you build a binary search tree from input data? Is choosing the root important? -Yes, the method for building a tree is provided in the `build_tree()` method in the binary search tree code. As for the choice of root node, we typically sort the input data, then select the middle element as the root node, and recursively build the left and right subtrees. This approach maximizes the tree's balance. +Yes. The construction is provided by the `build_tree()` method in the binary-search-tree code. Usually, we sort the input, choose its middle element as the root, and recursively construct the left and right subtrees. This keeps the tree as balanced as possible. -**Q**: In Java, do you always have to use the `equals()` method for string comparison? +**Q**: Must strings in Java always be compared with `equals()`? -In Java, for primitive data types, `==` is used to compare whether the values of two variables are equal. For reference types, the working principles of the two symbols are different. +For primitive types in Java, `==` compares variable values. For reference types, the two forms work differently. -- `==`: Used to compare whether two variables point to the same object, i.e., whether their positions in memory are the same. -- `equals()`: Used to compare whether the values of two objects are equal. +- `==` compares whether two variables refer to the same object, meaning the same location in memory. +- `equals()` compares whether two objects have equal values. -Therefore, if we want to compare values, we should use `equals()`. However, strings initialized via `String a = "hi"; String b = "hi";` are stored in the string constant pool and point to the same object, so `a == b` can also be used to compare the contents of the two strings. +Use `equals()` when comparing values. However, strings initialized with `String a = "hi"; String b = "hi";` are stored in the string pool and refer to the same object, so `a == b` also works for these strings. -**Q**: Before reaching the bottom level, is the number of nodes in the queue $2^h$ in breadth-first traversal? +**Q**: Just before breadth-first traversal reaches the lowest level, does the queue contain $2^h$ nodes? -Yes, for example, a full binary tree with height $h = 2$ has a total of $n = 7$ nodes, then the bottom level has $4 = 2^h = (n + 1) / 2$ nodes. +Yes. For example, a perfect binary tree of height $h = 2$ has $n = 7$ nodes, with $4 = 2^h = (n + 1) / 2$ nodes on its lowest level. diff --git a/en/docs/index.assets/animation.gif b/en/docs/index.assets/animation.gif index 641e677ef..af3d4ecca 100644 Binary files a/en/docs/index.assets/animation.gif and b/en/docs/index.assets/animation.gif differ diff --git a/en/docs/index.assets/animation.mp4 b/en/docs/index.assets/animation.mp4 index cf3838e79..5a94f5a04 100644 Binary files a/en/docs/index.assets/animation.mp4 and b/en/docs/index.assets/animation.mp4 differ diff --git a/en/docs/index.assets/animation_dark.gif b/en/docs/index.assets/animation_dark.gif index a05aedd70..1f2c30e99 100644 Binary files a/en/docs/index.assets/animation_dark.gif and b/en/docs/index.assets/animation_dark.gif differ diff --git a/en/docs/index.assets/comment.gif b/en/docs/index.assets/comment.gif index a6c5b8444..653a5ddc9 100644 Binary files a/en/docs/index.assets/comment.gif and b/en/docs/index.assets/comment.gif differ diff --git a/en/docs/index.assets/comment.mp4 b/en/docs/index.assets/comment.mp4 index 3f9ea340c..2b54d13b5 100644 Binary files a/en/docs/index.assets/comment.mp4 and b/en/docs/index.assets/comment.mp4 differ diff --git a/en/docs/index.assets/hello_algo_mindmap_tp.png b/en/docs/index.assets/hello_algo_mindmap_tp.png deleted file mode 100644 index b3aea43a5..000000000 Binary files a/en/docs/index.assets/hello_algo_mindmap_tp.png and /dev/null differ diff --git a/en/docs/index.assets/running_code.gif b/en/docs/index.assets/running_code.gif index 5c77187f7..089f7dd6c 100644 Binary files a/en/docs/index.assets/running_code.gif and b/en/docs/index.assets/running_code.gif differ diff --git a/en/docs/index.assets/running_code.mp4 b/en/docs/index.assets/running_code.mp4 index c946fbd68..9390473cf 100644 Binary files a/en/docs/index.assets/running_code.mp4 and b/en/docs/index.assets/running_code.mp4 differ diff --git a/en/docs/index.assets/running_code_dark.gif b/en/docs/index.assets/running_code_dark.gif index 5d56a0184..8fbaeb92c 100644 Binary files a/en/docs/index.assets/running_code_dark.gif and b/en/docs/index.assets/running_code_dark.gif differ diff --git a/en/docs/index.html b/en/docs/index.html index b417150ac..ca77dcd55 100644 --- a/en/docs/index.html +++ b/en/docs/index.html @@ -6,78 +6,78 @@
- - + + - - Encounter with Algos + + Getting to know algorithms - - Complexity Analysis + + Complexity - - Array and Linked List + + Arrays and linked lists - - Stack and Queue + + Stacks and queues - - Hashing + + Hash tables - - Tree + + Trees - - Heap + + Heaps - - Graph + + Graphs - + Searching - - Sorting + + Sorting - - Divide and Conquer + + Divide and conquer - - Backtracking + + Backtracking - - Dynamic Programming + + Dynamic programming - - Greedy + + Greedy algorithms
- +

- Data structures and algorithms tutorial with animated illustrations and ready-to-run code + A data structures and algorithms tutorial with animated illustrations and code you can run with one click

- Get Started + Start reading @@ -105,13 +105,25 @@ +
- + The English web edition
- Read Online + Read online @@ -120,7 +132,7 @@
-

500 animated illustrations, 14 programming languages, and 3,000 community Q&As to help you get started with data structures and algorithms.

+

Get started with data structures and algorithms through 500 animated illustrations, code in 14 programming languages, and 3,000 community questions and answers

@@ -140,12 +152,12 @@

Endorsements

-

“An easy-to-understand introduction to data structures and algorithms that encourages readers to engage both mind and hands. Highly recommended for beginners!”

-

—— Junhui Deng, Professor, Department of Computer Science and Technology, Tsinghua University

+

“An accessible introduction to data structures and algorithms that encourages readers to learn by thinking and doing. Highly recommended for anyone new to algorithms.”

+

— Junhui Deng, Professor, Department of Computer Science, Tsinghua University

-

“If I had 'Hello Algo' when I was learning data structures and algorithms, it would have been 10 times easier!”

-

—— Mu Li, Senior Principal Scientist, Amazon

+

“If I had had Hello Algo when I was learning data structures and algorithms, it would have been ten times easier!”

+

— Mu Li, Senior Principal Scientist, Amazon

@@ -161,9 +173,9 @@ -

Animated Illustrations

+

Animated illustrations

-

It’s crafted for a smooth learning experience.

+

Clear explanations and a gradual learning curve

"A picture is worth a thousand words."

@@ -178,9 +190,9 @@ -

One-Click Runnable Code

+

Run code with one click

-

Run code in multiple languages with one click.

+

Code in more than ten programming languages, with execution visualizations

"Talk is cheap. Show me the code."

@@ -193,9 +205,9 @@ -

Learn Together

+

Learn together

-

Ask questions, discuss ideas, and learn with others.

+

Ask questions, join discussions, and learn with fellow readers

"Learning by teaching."

@@ -214,166 +226,98 @@
- Author: yudongjin -
Yudong Jin(@krahets) + Author: yudongjin +
Yudong Jin (@krahets)
- +
-

Translators

-

The English version of this book was reviewed by the following contributors. We thank them for their time and effort.

-
- - - - - - - - - - -
-
- - -
-

Code Reviewers

-

The multilingual code versions of this book were made possible by the following developers. We appreciate their efforts and contributions!

+

Code reviewers

+

The developers below worked together on the code in different programming languages. Thank you for your efforts and contributions!

- Reviewer: rongyi + Reviewer: rongyi
rongyi
Rust
@@ -384,7 +328,7 @@

Contributors

-

This book has been refined by the efforts of over 200 contributors. We sincerely thank them for their invaluable time and contributions!

+

More than 200 open-source contributors have helped improve this book. Thank you for your time and effort!

Contributors diff --git a/en/docs/index.md b/en/docs/index.md index 36db754bc..8a10f7556 100644 --- a/en/docs/index.md +++ b/en/docs/index.md @@ -1,5 +1,5 @@ # Hello Algo -Data structures and algorithms tutorial with animated illustrations and ready-to-run code. +An introduction to data structures and algorithms, with animated illustrations and code you can run with one click. -[Hello Algo](chapter_hello_algo/) +[Start reading](chapter_hello_algo/) diff --git a/en/mkdocs.yml b/en/mkdocs.yml index b8a030ed1..a14ed41c2 100644 --- a/en/mkdocs.yml +++ b/en/mkdocs.yml @@ -1,193 +1,320 @@ -# Config inheritance -INHERIT: ../mkdocs.yml - # Project information site_name: Hello Algo site_url: https://www.hello-algo.com/en/ -site_description: "Data structures and algorithms tutorial with animated illustrations and ready-to-run code" +site_author: krahets +site_description: A data structures and algorithms tutorial with animated illustrations and code you can run with one click docs_dir: ../build/en/docs site_dir: ../site/en # Repository +repo_name: krahets/hello-algo +repo_url: https://github.com/krahets/hello-algo edit_uri: tree/main/en/docs version: 1.3.0 +# Copyright +copyright: Copyright © 2026 krahets
The website content is licensed under CC BY-NC-SA 4.0 + # Configuration theme: + name: material custom_dir: ../build/overrides language: en font: - text: Inter + text: Noto Sans + code: JetBrains Mono + features: + # - announce.dismiss + - content.action.edit + # - content.action.view + - content.code.annotate + - content.code.copy + - content.tabs.link + - content.tooltips + # - header.autohide + # - navigation.expand + - navigation.indexes + # - navigation.instant + # - navigation.prune + # - navigation.sections + # - navigation.tabs + # - navigation.tabs.sticky + - navigation.top + - navigation.footer + - navigation.tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + # - toc.integrate palette: - scheme: default primary: white accent: teal toggle: icon: material/theme-light-dark - name: Dark mode + name: Switch to dark mode - scheme: slate primary: black accent: teal toggle: icon: material/theme-light-dark - name: Light mode + name: Switch to light mode + favicon: assets/images/favicon.png + logo: assets/images/logo.svg + icon: + logo: logo + repo: fontawesome/brands/github + edit: fontawesome/regular/pen-to-square extra: + alternate: + - name: 简体中文 + link: / + lang: zh + - name: 繁體中文 + link: /zh-hant/ + lang: zh-Hant + - name: English + link: /en/ + lang: en + - name: 日本語 + link: /ja/ + lang: ja + - name: Русский + link: /ru/ + lang: ru + - name: 한국어 + link: /ko/ + lang: ko + social: + - icon: fontawesome/brands/github + link: https://github.com/krahets + - icon: fontawesome/brands/x-twitter + link: https://twitter.com/krahets + - icon: fontawesome/solid/code + link: https://leetcode.cn/u/jyd/ + generator: false status: - new: Recently Added + new: Recently added + +# Plugins +plugins: + - search + - glightbox: + touchNavigation: true + loop: false + effect: zoom + slide_effect: none + width: 100% + height: auto + zoomable: true + draggable: false + auto_caption: false + caption_position: bottom + +# Extensions +markdown_extensions: + abbr: {} + admonition: {} + attr_list: {} + def_list: {} + footnotes: {} + md_in_html: {} + toc: + permalink: true + pymdownx.arithmatex: + generic: true + pymdownx.betterem: + smart_enable: underscore + pymdownx.caret: {} + pymdownx.details: {} + # pymdownx.emoji: + # emoji_index: !!python/name:materialx.emoji.twemoji + # emoji_generator: !!python/name:materialx.emoji.to_svg + pymdownx.highlight: + anchor_linenums: true + pymdownx.inlinehilite: {} + pymdownx.snippets: {} + pymdownx.superfences: {} + pymdownx.keys: {} + # pymdownx.magiclink: + # repo_url_shorthand: true + # user: squidfunk + # repo: mkdocs-material + pymdownx.mark: {} + pymdownx.smartsymbols: {} + pymdownx.tabbed: + alternate_style: true + pymdownx.tasklist: + custom_checkbox: true + pymdownx.tilde: {} + +extra_javascript: + - javascripts/mathjax.js + - https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.min.js + # - javascripts/katex.js + # - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.js + # - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/contrib/auto-render.min.js + +extra_css: + - stylesheets/extra.css + # - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css # Page tree nav: - - Before Starting: + - Foreword: - chapter_hello_algo/index.md - - Chapter 0. Preface: + - Chapter 0.   Preface: # [icon: material/book-open-outline] - chapter_preface/index.md - - 0.1 About This Book: chapter_preface/about_the_book.md - - 0.2 How to Use This Book: chapter_preface/suggestions.md - - 0.3 Summary: chapter_preface/summary.md - - Chapter 1. Encounter with Algorithms: + - 0.1   About this book: chapter_preface/about_the_book.md + - 0.2   How to use this book: chapter_preface/suggestions.md + - 0.3   Summary: chapter_preface/summary.md + - Chapter 1.   Getting to know algorithms: # [icon: material/calculator-variant-outline] - chapter_introduction/index.md - - 1.1 Algorithms Are Everywhere: chapter_introduction/algorithms_are_everywhere.md - - 1.2 What Is an Algorithm: chapter_introduction/what_is_dsa.md - - 1.3 Summary: chapter_introduction/summary.md - - Chapter 2. Complexity Analysis: + - 1.1   Algorithms are everywhere: chapter_introduction/algorithms_are_everywhere.md + - 1.2   What is an algorithm?: chapter_introduction/what_is_dsa.md + - 1.3   Summary: chapter_introduction/summary.md + - Chapter 2.   Complexity analysis: # [icon: material/timer-sand] - chapter_computational_complexity/index.md - - 2.1 Algorithm Efficiency Evaluation: chapter_computational_complexity/performance_evaluation.md - - 2.2 Iteration and Recursion: chapter_computational_complexity/iteration_and_recursion.md - - 2.3 Time Complexity: chapter_computational_complexity/time_complexity.md - - 2.4 Space Complexity: chapter_computational_complexity/space_complexity.md - - 2.5 Summary: chapter_computational_complexity/summary.md - - 2.6 Exercises: chapter_computational_complexity/exercises.md - - Chapter 3. Data Structures: + - 2.1   Evaluating algorithm efficiency: chapter_computational_complexity/performance_evaluation.md + - 2.2   Iteration and recursion: chapter_computational_complexity/iteration_and_recursion.md + - 2.3   Time complexity: chapter_computational_complexity/time_complexity.md + - 2.4   Space complexity: chapter_computational_complexity/space_complexity.md + - 2.5   Summary: chapter_computational_complexity/summary.md + - 2.6   Exercises: chapter_computational_complexity/exercises.md + - Chapter 3.   Data structures: # [icon: material/shape-outline] - chapter_data_structure/index.md - - 3.1 Classification of Data Structures: chapter_data_structure/classification_of_data_structure.md - - 3.2 Basic Data Types: chapter_data_structure/basic_data_types.md - - 3.3 Number Encoding *: chapter_data_structure/number_encoding.md - - 3.4 Character Encoding *: chapter_data_structure/character_encoding.md - - 3.5 Summary: chapter_data_structure/summary.md - - 3.6 Exercises: chapter_data_structure/exercises.md - - Chapter 4. Arrays and Linked Lists: + - 3.1   Classifying data structures: chapter_data_structure/classification_of_data_structure.md + - 3.2   Basic data types: chapter_data_structure/basic_data_types.md + - 3.3   Number encoding *: chapter_data_structure/number_encoding.md + - 3.4   Character encoding *: chapter_data_structure/character_encoding.md + - 3.5   Summary: chapter_data_structure/summary.md + - 3.6   Exercises: chapter_data_structure/exercises.md + - Chapter 4.   Arrays and linked lists: # [icon: material/view-list-outline] - chapter_array_and_linkedlist/index.md - - 4.1 Array: chapter_array_and_linkedlist/array.md - - 4.2 Linked List: chapter_array_and_linkedlist/linked_list.md - - 4.3 List: chapter_array_and_linkedlist/list.md - - 4.4 Random-Access Memory and Cache *: chapter_array_and_linkedlist/ram_and_cache.md - - 4.5 Summary: chapter_array_and_linkedlist/summary.md - - 4.6 Exercises: chapter_array_and_linkedlist/exercises.md - - Chapter 5. Stacks and Queues: + - 4.1   Arrays: chapter_array_and_linkedlist/array.md + - 4.2   Linked lists: chapter_array_and_linkedlist/linked_list.md + - 4.3   Lists: chapter_array_and_linkedlist/list.md + - 4.4   Memory and cache *: chapter_array_and_linkedlist/ram_and_cache.md + - 4.5   Summary: chapter_array_and_linkedlist/summary.md + - 4.6   Exercises: chapter_array_and_linkedlist/exercises.md + - Chapter 5.   Stacks and queues: # [icon: material/stack-overflow] - chapter_stack_and_queue/index.md - - 5.1 Stack: chapter_stack_and_queue/stack.md - - 5.2 Queue: chapter_stack_and_queue/queue.md - - 5.3 Deque: chapter_stack_and_queue/deque.md - - 5.4 Summary: chapter_stack_and_queue/summary.md - - 5.5 Exercises: chapter_stack_and_queue/exercises.md - - Chapter 6. Hashing: + - 5.1   Stacks: chapter_stack_and_queue/stack.md + - 5.2   Queues: chapter_stack_and_queue/queue.md + - 5.3   Deques: chapter_stack_and_queue/deque.md + - 5.4   Summary: chapter_stack_and_queue/summary.md + - 5.5   Exercises: chapter_stack_and_queue/exercises.md + - Chapter 6.   Hash tables: # [icon: material/table-search] - chapter_hashing/index.md - - 6.1 Hash Table: chapter_hashing/hash_map.md - - 6.2 Hash Collision: chapter_hashing/hash_collision.md - - 6.3 Hash Algorithm: chapter_hashing/hash_algorithm.md - - 6.4 Summary: chapter_hashing/summary.md - - 6.5 Exercises: chapter_hashing/exercises.md - - Chapter 7. Tree: + - 6.1   Hash tables: chapter_hashing/hash_map.md + - 6.2   Hash collisions: chapter_hashing/hash_collision.md + - 6.3   Hash algorithms: chapter_hashing/hash_algorithm.md + - 6.4   Summary: chapter_hashing/summary.md + - 6.5   Exercises: chapter_hashing/exercises.md + - Chapter 7.   Trees: # [icon: material/graph-outline] - chapter_tree/index.md - - 7.1 Binary Tree: chapter_tree/binary_tree.md - - 7.2 Binary Tree Traversal: chapter_tree/binary_tree_traversal.md - - 7.3 Array Representation of Binary Trees: chapter_tree/array_representation_of_tree.md - - 7.4 Binary Search Tree: chapter_tree/binary_search_tree.md - - 7.5 AVL Tree *: chapter_tree/avl_tree.md - - 7.6 Summary: chapter_tree/summary.md - - 7.7 Exercises: chapter_tree/exercises.md - - Chapter 8. Heap: + - 7.1   Binary trees: chapter_tree/binary_tree.md + - 7.2   Traversing a binary tree: chapter_tree/binary_tree_traversal.md + - 7.3   Array representation of a binary tree: chapter_tree/array_representation_of_tree.md + - 7.4   Binary search trees: chapter_tree/binary_search_tree.md + - 7.5   AVL trees *: chapter_tree/avl_tree.md + - 7.6   Summary: chapter_tree/summary.md + - 7.7   Exercises: chapter_tree/exercises.md + - Chapter 8.   Heaps: # [icon: material/family-tree] - chapter_heap/index.md - - 8.1 Heap: chapter_heap/heap.md - - 8.2 Heap Construction Operation: chapter_heap/build_heap.md - - 8.3 Top-k Problem: chapter_heap/top_k.md - - 8.4 Summary: chapter_heap/summary.md - - 8.5 Exercises: chapter_heap/exercises.md - - Chapter 9. Graph: + - 8.1   Heaps: chapter_heap/heap.md + - 8.2   Building a heap: chapter_heap/build_heap.md + - 8.3   The top-k problem: chapter_heap/top_k.md + - 8.4   Summary: chapter_heap/summary.md + - 8.5   Exercises: chapter_heap/exercises.md + - Chapter 9.   Graphs: # [icon: material/graphql] - chapter_graph/index.md - - 9.1 Graph: chapter_graph/graph.md - - 9.2 Basic Operations on Graphs: chapter_graph/graph_operations.md - - 9.3 Graph Traversal: chapter_graph/graph_traversal.md - - 9.4 Summary: chapter_graph/summary.md - - 9.5 Exercises: chapter_graph/exercises.md - - Chapter 10. Searching: + - 9.1   Graphs: chapter_graph/graph.md + - 9.2   Basic graph operations: chapter_graph/graph_operations.md + - 9.3   Graph traversal: chapter_graph/graph_traversal.md + - 9.4   Summary: chapter_graph/summary.md + - 9.5   Exercises: chapter_graph/exercises.md + - Chapter 10.   Searching: # [icon: material/text-search] - chapter_searching/index.md - - 10.1 Binary Search: chapter_searching/binary_search.md - - 10.2 Binary Search Insertion Point: chapter_searching/binary_search_insertion.md - - 10.3 Binary Search Boundaries: chapter_searching/binary_search_edge.md - - 10.4 Hash Optimization Strategy: chapter_searching/replace_linear_by_hashing.md - - 10.5 Searching Algorithms Revisited: chapter_searching/searching_algorithm_revisited.md - - 10.6 Summary: chapter_searching/summary.md - - 10.7 Exercises: chapter_searching/exercises.md - - Chapter 11. Sorting: + - 10.1   Binary search: chapter_searching/binary_search.md + - 10.2   Finding an insertion point with binary search: chapter_searching/binary_search_insertion.md + - 10.3   Finding boundaries with binary search: chapter_searching/binary_search_edge.md + - 10.4   Optimizing with hashing: chapter_searching/replace_linear_by_hashing.md + - 10.5   Search algorithms revisited: chapter_searching/searching_algorithm_revisited.md + - 10.6   Summary: chapter_searching/summary.md + - 10.7   Exercises: chapter_searching/exercises.md + - Chapter 11.   Sorting: # [icon: material/sort-ascending] - chapter_sorting/index.md - - 11.1 Sorting Algorithm: chapter_sorting/sorting_algorithm.md - - 11.2 Selection Sort: chapter_sorting/selection_sort.md - - 11.3 Bubble Sort: chapter_sorting/bubble_sort.md - - 11.4 Insertion Sort: chapter_sorting/insertion_sort.md - - 11.5 Quick Sort: chapter_sorting/quick_sort.md - - 11.6 Merge Sort: chapter_sorting/merge_sort.md - - 11.7 Heap Sort: chapter_sorting/heap_sort.md - - 11.8 Bucket Sort: chapter_sorting/bucket_sort.md - - 11.9 Counting Sort: chapter_sorting/counting_sort.md - - 11.10 Radix Sort: chapter_sorting/radix_sort.md - - 11.11 Summary: chapter_sorting/summary.md - - 11.12 Exercises: chapter_sorting/exercises.md - - Chapter 12. Divide and Conquer: + - 11.1   Sorting algorithms: chapter_sorting/sorting_algorithm.md + - 11.2   Selection sort: chapter_sorting/selection_sort.md + - 11.3   Bubble sort: chapter_sorting/bubble_sort.md + - 11.4   Insertion sort: chapter_sorting/insertion_sort.md + - 11.5   Quick sort: chapter_sorting/quick_sort.md + - 11.6   Merge sort: chapter_sorting/merge_sort.md + - 11.7   Heap sort: chapter_sorting/heap_sort.md + - 11.8   Bucket sort: chapter_sorting/bucket_sort.md + - 11.9   Counting sort: chapter_sorting/counting_sort.md + - 11.10   Radix sort: chapter_sorting/radix_sort.md + - 11.11   Summary: chapter_sorting/summary.md + - 11.12   Exercises: chapter_sorting/exercises.md + - Chapter 12.   Divide and conquer: # [icon: material/set-split] - chapter_divide_and_conquer/index.md - - 12.1 Divide and Conquer Algorithms: chapter_divide_and_conquer/divide_and_conquer.md - - 12.2 Divide and Conquer Search Strategy: chapter_divide_and_conquer/binary_search_recur.md - - 12.3 Building a Binary Tree Problem: chapter_divide_and_conquer/build_binary_tree_problem.md - - 12.4 Tower of Hanoi Problem: chapter_divide_and_conquer/hanota_problem.md - - 12.5 Summary: chapter_divide_and_conquer/summary.md - - 12.6 Exercises: chapter_divide_and_conquer/exercises.md - - Chapter 13. Backtracking: + - 12.1   Divide and conquer: chapter_divide_and_conquer/divide_and_conquer.md + - 12.2   Divide-and-conquer search: chapter_divide_and_conquer/binary_search_recur.md + - 12.3   Building a binary tree: chapter_divide_and_conquer/build_binary_tree_problem.md + - 12.4   Tower of Hanoi: chapter_divide_and_conquer/hanota_problem.md + - 12.5   Summary: chapter_divide_and_conquer/summary.md + - 12.6   Exercises: chapter_divide_and_conquer/exercises.md + - Chapter 13.   Backtracking: # [icon: material/map-marker-path] - chapter_backtracking/index.md - - 13.1 Backtracking Algorithm: chapter_backtracking/backtracking_algorithm.md - - 13.2 Permutations Problem: chapter_backtracking/permutations_problem.md - - 13.3 Subset-Sum Problem: chapter_backtracking/subset_sum_problem.md - - 13.4 N-Queens Problem: chapter_backtracking/n_queens_problem.md - - 13.5 Summary: chapter_backtracking/summary.md - - 13.6 Exercises: chapter_backtracking/exercises.md - - Chapter 14. Dynamic Programming: + - 13.1   Backtracking algorithms: chapter_backtracking/backtracking_algorithm.md + - 13.2   Permutations problem: chapter_backtracking/permutations_problem.md + - 13.3   Subset sum problem: chapter_backtracking/subset_sum_problem.md + - 13.4   N-queens problem: chapter_backtracking/n_queens_problem.md + - 13.5   Summary: chapter_backtracking/summary.md + - 13.6   Exercises: chapter_backtracking/exercises.md + - Chapter 14.   Dynamic programming: # [icon: material/table-pivot] - chapter_dynamic_programming/index.md - - 14.1 Introduction to Dynamic Programming: chapter_dynamic_programming/intro_to_dynamic_programming.md - - 14.2 Characteristics of Dynamic Programming Problems: chapter_dynamic_programming/dp_problem_features.md - - 14.3 Dynamic Programming Problem-Solving Approach: chapter_dynamic_programming/dp_solution_pipeline.md - - 14.4 0-1 Knapsack Problem: chapter_dynamic_programming/knapsack_problem.md - - 14.5 Unbounded Knapsack Problem: chapter_dynamic_programming/unbounded_knapsack_problem.md - - 14.6 Edit Distance Problem: chapter_dynamic_programming/edit_distance_problem.md - - 14.7 Summary: chapter_dynamic_programming/summary.md - - 14.8 Exercises: chapter_dynamic_programming/exercises.md - - Chapter 15. Greedy: + - 14.1   Introduction to dynamic programming: chapter_dynamic_programming/intro_to_dynamic_programming.md + - 14.2   Properties of dynamic programming problems: chapter_dynamic_programming/dp_problem_features.md + - 14.3   Solving dynamic programming problems: chapter_dynamic_programming/dp_solution_pipeline.md + - 14.4   0-1 knapsack problem: chapter_dynamic_programming/knapsack_problem.md + - 14.5   Unbounded knapsack problem: chapter_dynamic_programming/unbounded_knapsack_problem.md + - 14.6   Edit distance problem: chapter_dynamic_programming/edit_distance_problem.md + - 14.7   Summary: chapter_dynamic_programming/summary.md + - 14.8   Exercises: chapter_dynamic_programming/exercises.md + - Chapter 15.   Greedy algorithms: # [icon: material/head-heart-outline] - chapter_greedy/index.md - - 15.1 Greedy Algorithm: chapter_greedy/greedy_algorithm.md - - 15.2 Fractional Knapsack Problem: chapter_greedy/fractional_knapsack_problem.md - - 15.3 Maximum Capacity Problem: chapter_greedy/max_capacity_problem.md - - 15.4 Maximum Product Cutting Problem: chapter_greedy/max_product_cutting_problem.md - - 15.5 Summary: chapter_greedy/summary.md - - 15.6 Exercises: chapter_greedy/exercises.md - - Chapter 16. Appendix: + - 15.1   Greedy algorithms: chapter_greedy/greedy_algorithm.md + - 15.2   Fractional knapsack problem: chapter_greedy/fractional_knapsack_problem.md + - 15.3   Maximum capacity problem: chapter_greedy/max_capacity_problem.md + - 15.4   Maximum product cutting problem: chapter_greedy/max_product_cutting_problem.md + - 15.5   Summary: chapter_greedy/summary.md + - 15.6   Exercises: chapter_greedy/exercises.md + - Chapter 16.   Appendix: # [icon: material/help-circle-outline] - chapter_appendix/index.md - - 16.1 Programming Environment Installation: chapter_appendix/installation.md - - 16.2 Contributing Together: chapter_appendix/contribution.md - - 16.3 Glossary: chapter_appendix/terminology.md + - 16.1   Setting up a programming environment: chapter_appendix/installation.md + - 16.2   Contribute to the book: chapter_appendix/contribution.md + - 16.3   Glossary: chapter_appendix/terminology.md - References: - chapter_reference/index.md + - Print edition (Chinese): + - chapter_paperbook/index.md diff --git a/overrides/partials/i18n.html b/overrides/partials/i18n.html index b15d455b8..795c38217 100644 --- a/overrides/partials/i18n.html +++ b/overrides/partials/i18n.html @@ -15,11 +15,11 @@ "announcement": "紙質書(簡體中文版)已發行,詳情請見這裡" }, "en": { - "not_found": "Not found", + "not_found": "Page not found", "comments_title": "Comments", - "comments_prompt": "Feel free to drop your insights, questions or suggestions", + "comments_prompt": "Share your insights, questions, or suggestions in the comments", "giscus_language": "en", - "announcement": "Welcome to contribute to Chinese-to-English translation! For more details, please refer to CONTRIBUTING.md." + "announcement": "Help improve the English edition. See our contribution guidelines." }, "ja": { "not_found": "ページが見つかりません", diff --git a/overrides/stylesheets/extra.css b/overrides/stylesheets/extra.css index ce837e00d..b0a47ec86 100644 --- a/overrides/stylesheets/extra.css +++ b/overrides/stylesheets/extra.css @@ -396,10 +396,75 @@ a:hover .text-button span { font-weight: normal; } +/* Keep long English chapter labels inside the hero at tablet/desktop widths. */ .hero-div--en { + width: min(112vh, 76vw); + height: min(84vh, 57vw); font-size: min(1.65vh, 2.3vw); } +.hero-div--en > a > span { + z-index: 1; +} + +/* The illustrated chapter map becomes readable touch targets on small screens. */ +@media screen and (max-width: 600px) { + .home-div:has(> .hero-div--en) { + display: block; + height: auto !important; + margin-top: 0 !important; + padding: 0.5rem 0 1rem !important; + } + + .home-div > .hero-div--en { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.35rem 0.5rem; + width: calc(100% - 1.5rem); + height: auto; + margin: 0.5rem auto 1rem; + } + + .home-div > .hero-div--en > img { + display: none; + } + + .home-div > .hero-div--en > a { + display: flex; + align-items: center; + gap: 0.4rem; + min-width: 0; + min-height: 48px; + padding: 0.3rem 0.15rem; + text-align: left; + } + + .home-div > .hero-div--en > a > img { + position: static; + flex-shrink: 0; + width: 32px !important; + height: 32px !important; + object-fit: contain; + } + + .home-div > .hero-div--en > a > span { + position: static; + min-width: 0; + transform: none; + white-space: normal; + font-size: 13px; + line-height: 1.3; + } + + .home-div > .hero-div--en + .heading-div { + position: relative; + left: auto; + bottom: auto; + transform: none; + padding: 0 0.75rem; + } +} + .hero-div--ru { font-size: min(1.45vh, 2vw); } diff --git a/overrides/zensical/stylesheets/extra.css b/overrides/zensical/stylesheets/extra.css index c45c8042e..b50db98be 100644 --- a/overrides/zensical/stylesheets/extra.css +++ b/overrides/zensical/stylesheets/extra.css @@ -477,10 +477,75 @@ html:has(body[data-md-color-scheme="default"]) { font-weight: normal; } +/* Keep long English chapter labels inside the hero at tablet/desktop widths. */ .hero-div--en { + width: min(112vh, 76vw); + height: min(84vh, 57vw); font-size: min(1.65vh, 2.3vw); } +.hero-div--en > a > span { + z-index: 1; +} + +/* The illustrated chapter map becomes readable touch targets on small screens. */ +@media screen and (max-width: 600px) { + .home-div:has(> .hero-div--en) { + display: block; + height: auto !important; + margin-top: 0 !important; + padding: 0.5rem 0 1rem !important; + } + + .home-div > .hero-div--en { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.35rem 0.5rem; + width: calc(100% - 1.5rem); + height: auto; + margin: 0.5rem auto 1rem; + } + + .home-div > .hero-div--en > img { + display: none; + } + + .home-div > .hero-div--en > a { + display: flex; + align-items: center; + gap: 0.4rem; + min-width: 0; + min-height: 48px; + padding: 0.3rem 0.15rem; + text-align: left; + } + + .home-div > .hero-div--en > a > img { + position: static; + flex-shrink: 0; + width: 32px !important; + height: 32px !important; + object-fit: contain; + } + + .home-div > .hero-div--en > a > span { + position: static; + min-width: 0; + transform: none; + white-space: normal; + font-size: 13px; + line-height: 1.3; + } + + .home-div > .hero-div--en + .heading-div { + position: relative; + left: auto; + bottom: auto; + transform: none; + padding: 0 0.75rem; + } +} + .hero-div--ru { font-size: min(1.45vh, 2vw); }