diff --git a/en/codes/cpp/.gitignore b/en/codes/cpp/.gitignore new file mode 100644 index 000000000..dc1ffacf4 --- /dev/null +++ b/en/codes/cpp/.gitignore @@ -0,0 +1,10 @@ +# Ignore all +* +# Unignore all with extensions +!*.* +# Unignore all dirs +!*/ + +*.dSYM/ + +build/ diff --git a/en/codes/cpp/CMakeLists.txt b/en/codes/cpp/CMakeLists.txt new file mode 100644 index 000000000..1e80bc4d7 --- /dev/null +++ b/en/codes/cpp/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) +project(hello_algo CXX) + +set(CMAKE_CXX_STANDARD 11) + +include_directories(./include) + +add_subdirectory(chapter_computational_complexity) +add_subdirectory(chapter_array_and_linkedlist) +add_subdirectory(chapter_stack_and_queue) +add_subdirectory(chapter_hashing) +add_subdirectory(chapter_tree) +add_subdirectory(chapter_heap) +add_subdirectory(chapter_graph) +add_subdirectory(chapter_searching) +add_subdirectory(chapter_sorting) +add_subdirectory(chapter_divide_and_conquer) +add_subdirectory(chapter_backtracking) +add_subdirectory(chapter_dynamic_programming) +add_subdirectory(chapter_greedy) diff --git a/en/codes/cpp/chapter_array_and_linkedlist/CMakeLists.txt b/en/codes/cpp/chapter_array_and_linkedlist/CMakeLists.txt new file mode 100644 index 000000000..2e933e016 --- /dev/null +++ b/en/codes/cpp/chapter_array_and_linkedlist/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(array array.cpp) +add_executable(linked_list linked_list.cpp) +add_executable(list list.cpp) +add_executable(my_list my_list.cpp) diff --git a/en/codes/cpp/chapter_array_and_linkedlist/array.cpp b/en/codes/cpp/chapter_array_and_linkedlist/array.cpp new file mode 100644 index 000000000..b555d9db3 --- /dev/null +++ b/en/codes/cpp/chapter_array_and_linkedlist/array.cpp @@ -0,0 +1,113 @@ +/** + * File: array.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Random access to elements */ +int randomAccess(int *nums, int size) { + // Randomly select a number in the range [0, size) + int randomIndex = rand() % size; + // Retrieve and return a random element + int randomNum = nums[randomIndex]; + return randomNum; +} + +/* Extend array length */ +int *extend(int *nums, int size, int enlarge) { + // Initialize an extended length array + int *res = new 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]; + } + // Free memory + delete[] nums; + // Return the new array after expansion + return res; +} + +/* Insert element num at `index` */ +void insert(int *nums, int size, int num, int index) { + // Move all elements after `index` one position backward + for (int i = size - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index + nums[index] = num; +} + +/* Remove the element at `index` */ +void remove(int *nums, int size, int index) { + // Move all elements after `index` one position forward + for (int i = index; i < size - 1; i++) { + nums[i] = nums[i + 1]; + } +} + +/* Traverse array */ +void traverse(int *nums, int size) { + int count = 0; + // Traverse array by index + for (int i = 0; i < size; i++) { + count += nums[i]; + } +} + +/* Search for 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) + return i; + } + return -1; +} + +/* Driver Code */ +int main() { + /* Initialize an array */ + int size = 5; + int *arr = new int[size]; + cout << "Array arr = "; + printArray(arr, size); + + int *nums = new int[size]{1, 3, 2, 5, 4}; + cout << "Array nums = "; + printArray(nums, size); + + /* Random access */ + int randomNum = randomAccess(nums, size); + cout << "Get a random element from nums = " << randomNum << endl; + + /* Length extension */ + int enlarge = 3; + nums = extend(nums, size, enlarge); + size += enlarge; + cout << "Extend the array length to 8, resulting in nums = "; + printArray(nums, size); + + /* Insert element */ + insert(nums, size, 6, 3); + cout << "Insert the number 6 at index 3, resulting in nums = "; + printArray(nums, size); + + /* Remove element */ + remove(nums, size, 2); + cout << "Remove the element at index 2, resulting in nums = "; + printArray(nums, size); + + /* Traverse array */ + traverse(nums, size); + + /* Search for elements */ + int index = find(nums, size, 3); + cout << "Find element 3 in nums, index = " << index << endl; + + // Free memory + delete[] arr; + delete[] nums; + + return 0; +} diff --git a/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp new file mode 100644 index 000000000..2767807f8 --- /dev/null +++ b/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp @@ -0,0 +1,89 @@ +/** + * File: linked_list.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Insert node P after node n0 in the linked list */ +void insert(ListNode *n0, ListNode *P) { + ListNode *n1 = n0->next; + P->next = n1; + n0->next = P; +} + +/* Remove the first node after node n0 in the linked list */ +void remove(ListNode *n0) { + if (n0->next == nullptr) + return; + // n0 -> P -> n1 + ListNode *P = n0->next; + ListNode *n1 = P->next; + n0->next = n1; + // Free memory + delete P; +} + +/* Access the node at `index` in the linked list */ +ListNode *access(ListNode *head, int index) { + for (int i = 0; i < index; i++) { + if (head == nullptr) + return nullptr; + head = head->next; + } + return head; +} + +/* Search for the first node with value target in the linked list */ +int find(ListNode *head, int target) { + int index = 0; + while (head != nullptr) { + if (head->val == target) + return index; + head = head->next; + index++; + } + return -1; +} + +/* Driver Code */ +int main() { + /* Initialize linked list */ + // Initialize each node + 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 + n0->next = n1; + n1->next = n2; + n2->next = n3; + n3->next = n4; + cout << "The initialized linked list is" << endl; + printLinkedList(n0); + + /* Insert node */ + insert(n0, new ListNode(0)); + cout << "Linked list after inserting the node is" << endl; + printLinkedList(n0); + + /* Remove node */ + remove(n0); + cout << "Linked list after removing the node is" << endl; + printLinkedList(n0); + + /* Access node */ + ListNode *node = access(n0, 3); + cout << "The value of the node at index 3 in the linked list = " << node->val << endl; + + /* Search node */ + int index = find(n0, 2); + cout << "The index of the node with value 2 in the linked list = " << index << endl; + + // Free memory + freeMemoryLinkedList(n0); + + return 0; +} diff --git a/en/codes/cpp/chapter_array_and_linkedlist/list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/list.cpp new file mode 100644 index 000000000..7c5d6e287 --- /dev/null +++ b/en/codes/cpp/chapter_array_and_linkedlist/list.cpp @@ -0,0 +1,72 @@ +/** + * File: list.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Driver Code */ +int main() { + /* Initialize list */ + vector nums = {1, 3, 2, 5, 4}; + cout << "List nums = "; + printVector(nums); + + /* Access element */ + int num = nums[1]; + cout << "Access the element at index 1, obtained num = " << num << endl; + + /* Update element */ + nums[1] = 0; + cout << "Update the element at index 1 to 0, resulting in nums = "; + printVector(nums); + + /* Clear list */ + nums.clear(); + cout << "After clearing the list, nums = "; + printVector(nums); + + /* Add element at the end */ + nums.push_back(1); + nums.push_back(3); + nums.push_back(2); + nums.push_back(5); + nums.push_back(4); + cout << "After adding elements, nums = "; + printVector(nums); + + /* Insert element in the middle */ + nums.insert(nums.begin() + 3, 6); + cout << "Insert the number 6 at index 3, resulting in nums = "; + printVector(nums); + + /* Remove element */ + nums.erase(nums.begin() + 3); + cout << "Remove the element at index 3, resulting in nums = "; + printVector(nums); + + /* Traverse the list by index */ + int count = 0; + for (int i = 0; i < nums.size(); i++) { + count += nums[i]; + } + /* Traverse the list elements */ + count = 0; + for (int x : nums) { + count += x; + } + + /* 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 = "; + printVector(nums); + + /* Sort list */ + sort(nums.begin(), nums.end()); + 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 new file mode 100644 index 000000000..8af4c6f1d --- /dev/null +++ b/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp @@ -0,0 +1,171 @@ +/** + * File: my_list.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* List class */ +class MyList { + private: + int *arr; // Array (stores list elements) + int arrCapacity = 10; // List capacity + int arrSize = 0; // List length (current number of elements) + int extendRatio = 2; // Multiple for each list expansion + + public: + /* Constructor */ + MyList() { + arr = new int[arrCapacity]; + } + + /* Destructor */ + ~MyList() { + delete[] arr; + } + + /* Get list length (current number of elements)*/ + int size() { + return arrSize; + } + + /* Get list capacity */ + int capacity() { + return arrCapacity; + } + + /* Access element */ + int get(int index) { + // If the index is out of bounds, throw an exception, as below + if (index < 0 || index >= size()) + throw out_of_range("Index out of bounds"); + return arr[index]; + } + + /* Update element */ + void set(int index, int num) { + if (index < 0 || index >= size()) + throw out_of_range("Index out of bounds"); + arr[index] = num; + } + + /* Add element at the end */ + void add(int num) { + // When the number of elements exceeds capacity, trigger the expansion mechanism + if (size() == capacity()) + extendCapacity(); + arr[size()] = num; + // Update the number of elements + arrSize++; + } + + /* Insert 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 expansion mechanism + if (size() == capacity()) + extendCapacity(); + // Move all elements after `index` one position backward + for (int j = size() - 1; j >= index; j--) { + arr[j + 1] = arr[j]; + } + arr[index] = num; + // Update the number of elements + arrSize++; + } + + /* Remove element */ + int remove(int index) { + if (index < 0 || index >= size()) + throw out_of_range("Index out of bounds"); + int num = arr[index]; + // Move all elements after `index` one position forward + for (int j = index; j < size() - 1; j++) { + arr[j] = arr[j + 1]; + } + // Update the number of elements + arrSize--; + // Return the removed element + return num; + } + + /* Extend list */ + void extendCapacity() { + // Create a new array with a length multiple of the original array by extendRatio + int newCapacity = capacity() * extendRatio; + int *tmp = arr; + arr = new int[newCapacity]; + // Copy all elements from the original array to the new array + for (int i = 0; i < size(); i++) { + arr[i] = tmp[i]; + } + // Free memory + delete[] tmp; + arrCapacity = newCapacity; + } + + /* Convert the list to a Vector for printing */ + vector toVector() { + // Only convert elements within valid length range + vector vec(size()); + for (int i = 0; i < size(); i++) { + vec[i] = arr[i]; + } + return vec; + } +}; + +/* Driver Code */ +int main() { + /* Initialize list */ + MyList *nums = new MyList(); + /* Add element at the end */ + nums->add(1); + nums->add(3); + nums->add(2); + nums->add(5); + nums->add(4); + cout << "List nums = "; + vector vec = nums->toVector(); + printVector(vec); + cout << "Capacity = " << nums->capacity() << ", length = " << nums->size() << endl; + + /* Insert element in the middle */ + nums->insert(3, 6); + cout << "Insert the number 6 at index 3, resulting in nums = "; + vec = nums->toVector(); + printVector(vec); + + /* Remove element */ + nums->remove(3); + cout << "Remove the element at index 3, resulting in nums = "; + vec = nums->toVector(); + printVector(vec); + + /* Access element */ + int num = nums->get(1); + cout << "Access the element at index 1, obtained num = " << num << endl; + + /* Update element */ + nums->set(1, 0); + cout << "Update the element at index 1 to 0, resulting in nums = "; + vec = nums->toVector(); + printVector(vec); + + /* Test expansion mechanism */ + for (int i = 0; i < 10; i++) { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism at this time + nums->add(i); + } + cout << "After extending, list nums = "; + vec = nums->toVector(); + printVector(vec); + cout << "Capacity = " << nums->capacity() << ", length = " << nums->size() << endl; + + // Free memory + delete nums; + + return 0; +} diff --git a/en/codes/cpp/chapter_backtracking/CMakeLists.txt b/en/codes/cpp/chapter_backtracking/CMakeLists.txt new file mode 100644 index 000000000..6c271e330 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(preorder_traversal_i_compact preorder_traversal_i_compact.cpp) +add_executable(preorder_traversal_ii_compact preorder_traversal_ii_compact.cpp) +add_executable(preorder_traversal_iii_compact preorder_traversal_iii_compact.cpp) +add_executable(preorder_traversal_iii_template preorder_traversal_iii_template.cpp) +add_executable(permutations_i permutations_i.cpp) +add_executable(permutations_ii permutations_ii.cpp) +add_executable(n_queens n_queens.cpp) +add_executable(subset_sum_i_naive subset_sum_i_naive.cpp) +add_executable(subset_sum_i subset_sum_i.cpp) +add_executable(subset_sum_ii subset_sum_ii.cpp) diff --git a/en/codes/cpp/chapter_backtracking/n_queens.cpp b/en/codes/cpp/chapter_backtracking/n_queens.cpp new file mode 100644 index 000000000..6fbbacd8e --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/n_queens.cpp @@ -0,0 +1,65 @@ +/** + * File: n_queens.cpp + * Created Time: 2023-05-04 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Backtracking algorithm: 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 + if (row == n) { + res.push_back(state); + return; + } + // Traverse all columns + for (int col = 0; col < n; col++) { + // Calculate the main and minor diagonals corresponding to the cell + int diag1 = row - col + n - 1; + int diag2 = row + col; + // Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell + if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { + // Attempt: place the queen in the cell + state[row][col] = "Q"; + cols[col] = diags1[diag1] = diags2[diag2] = true; + // Place the next row + backtrack(row + 1, n, state, res, cols, diags1, diags2); + // Retract: restore the cell to an empty spot + state[row][col] = "#"; + cols[col] = diags1[diag1] = diags2[diag2] = false; + } + } +} + +/* Solve n queens */ +vector>> nQueens(int n) { + // Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot + vector> state(n, vector(n, "#")); + vector cols(n, false); // Record columns with queens + vector diags1(2 * n - 1, false); // Record main diagonals with queens + vector diags2(2 * n - 1, false); // Record minor diagonals with queens + vector>> res; + + backtrack(0, n, state, res, cols, diags1, diags2); + + return res; +} + +/* Driver Code */ +int main() { + int n = 4; + vector>> res = nQueens(n); + + cout << "Input the dimensions of the chessboard as " << n << endl; + cout << "Total number of queen placement solutions = " << res.size() << endl; + for (const vector> &state : res) { + cout << "--------------------" << endl; + for (const vector &row : state) { + printVector(row); + } + } + + return 0; +} diff --git a/en/codes/cpp/chapter_backtracking/permutations_i.cpp b/en/codes/cpp/chapter_backtracking/permutations_i.cpp new file mode 100644 index 000000000..be9907ab6 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/permutations_i.cpp @@ -0,0 +1,54 @@ +/** + * File: permutations_i.cpp + * Created Time: 2023-04-24 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Backtracking algorithm: Permutation I */ +void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { + // When the state length equals the number of elements, record the solution + if (state.size() == choices.size()) { + res.push_back(state); + return; + } + // Traverse all choices + for (int i = 0; i < choices.size(); i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements + if (!selected[i]) { + // Attempt: make a choice, update the state + selected[i] = true; + state.push_back(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Retract: undo the choice, restore to the previous state + selected[i] = false; + state.pop_back(); + } + } +} + +/* Permutation I */ +vector> permutationsI(vector nums) { + vector state; + vector selected(nums.size(), false); + vector> res; + backtrack(state, nums, selected, res); + return res; +} + +/* Driver Code */ +int main() { + vector nums = {1, 2, 3}; + + vector> res = permutationsI(nums); + + cout << "Input array nums = "; + printVector(nums); + cout << "All permutations res = "; + printVectorMatrix(res); + + return 0; +} diff --git a/en/codes/cpp/chapter_backtracking/permutations_ii.cpp b/en/codes/cpp/chapter_backtracking/permutations_ii.cpp new file mode 100644 index 000000000..4cdbe8faa --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/permutations_ii.cpp @@ -0,0 +1,56 @@ +/** + * File: permutations_ii.cpp + * Created Time: 2023-04-24 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Backtracking algorithm: Permutation II */ +void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { + // When the state length equals the number of elements, record the solution + if (state.size() == choices.size()) { + res.push_back(state); + return; + } + // Traverse 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 + if (!selected[i] && duplicated.find(choice) == duplicated.end()) { + // Attempt: make a choice, update the state + duplicated.emplace(choice); // Record selected element values + selected[i] = true; + state.push_back(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Retract: undo the choice, restore to the previous state + selected[i] = false; + state.pop_back(); + } + } +} + +/* Permutation II */ +vector> permutationsII(vector nums) { + vector state; + vector selected(nums.size(), false); + vector> res; + backtrack(state, nums, selected, res); + return res; +} + +/* Driver Code */ +int main() { + vector nums = {1, 1, 2}; + + vector> res = permutationsII(nums); + + cout << "Input array nums = "; + printVector(nums); + cout << "All permutations res = "; + printVectorMatrix(res); + + return 0; +} diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp new file mode 100644 index 000000000..b92cf43d7 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp @@ -0,0 +1,39 @@ +/** + * File: preorder_traversal_i_compact.cpp + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +vector res; + +/* Pre-order traversal: Example one */ +void preOrder(TreeNode *root) { + if (root == nullptr) { + return; + } + if (root->val == 7) { + // Record solution + res.push_back(root); + } + preOrder(root->left); + preOrder(root->right); +} + +/* Driver Code */ +int main() { + TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); + cout << "\nInitialize binary tree" << endl; + printTree(root); + + // Pre-order traversal + preOrder(root); + + cout << "\nOutput all nodes with value 7" << endl; + vector vals; + for (TreeNode *node : res) { + vals.push_back(node->val); + } + printVector(vals); +} diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp new file mode 100644 index 000000000..08905abc2 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp @@ -0,0 +1,46 @@ +/** + * File: preorder_traversal_ii_compact.cpp + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +vector path; +vector> res; + +/* Pre-order traversal: Example two */ +void preOrder(TreeNode *root) { + if (root == nullptr) { + return; + } + // Attempt + path.push_back(root); + if (root->val == 7) { + // Record solution + res.push_back(path); + } + preOrder(root->left); + preOrder(root->right); + // Retract + path.pop_back(); +} + +/* Driver Code */ +int main() { + TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); + cout << "\nInitialize binary tree" << endl; + printTree(root); + + // Pre-order traversal + preOrder(root); + + cout << "\nOutput all root-to-node 7 paths" << endl; + for (vector &path : res) { + vector vals; + for (TreeNode *node : path) { + vals.push_back(node->val); + } + printVector(vals); + } +} diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp new file mode 100644 index 000000000..2e9f0a7c9 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp @@ -0,0 +1,47 @@ +/** + * File: preorder_traversal_iii_compact.cpp + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +vector path; +vector> res; + +/* Pre-order traversal: Example three */ +void preOrder(TreeNode *root) { + // Pruning + if (root == nullptr || root->val == 3) { + return; + } + // Attempt + path.push_back(root); + if (root->val == 7) { + // Record solution + res.push_back(path); + } + preOrder(root->left); + preOrder(root->right); + // Retract + path.pop_back(); +} + +/* Driver Code */ +int main() { + TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); + cout << "\nInitialize binary tree" << endl; + printTree(root); + + // Pre-order traversal + preOrder(root); + + cout << "\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3" << endl; + for (vector &path : res) { + vector vals; + for (TreeNode *node : path) { + vals.push_back(node->val); + } + printVector(vals); + } +} diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp new file mode 100644 index 000000000..a1f06fa73 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp @@ -0,0 +1,76 @@ +/** + * File: preorder_traversal_iii_template.cpp + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Determine if the current state is a solution */ +bool isSolution(vector &state) { + return !state.empty() && state.back()->val == 7; +} + +/* Record solution */ +void recordSolution(vector &state, vector> &res) { + res.push_back(state); +} + +/* Determine if the choice is legal under the current state */ +bool isValid(vector &state, TreeNode *choice) { + return choice != nullptr && choice->val != 3; +} + +/* Update state */ +void makeChoice(vector &state, TreeNode *choice) { + state.push_back(choice); +} + +/* Restore state */ +void undoChoice(vector &state, TreeNode *choice) { + state.pop_back(); +} + +/* Backtracking algorithm: Example three */ +void backtrack(vector &state, vector &choices, vector> &res) { + // Check if it's a solution + if (isSolution(state)) { + // Record solution + recordSolution(state, res); + } + // Traverse all choices + for (TreeNode *choice : choices) { + // Pruning: check if the choice is legal + if (isValid(state, choice)) { + // Attempt: make a choice, update the state + makeChoice(state, choice); + // Proceed to the next round of selection + vector nextChoices{choice->left, choice->right}; + backtrack(state, nextChoices, res); + // Retract: undo the choice, restore to the previous state + undoChoice(state, choice); + } + } +} + +/* Driver Code */ +int main() { + TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7}); + cout << "\nInitialize binary tree" << endl; + printTree(root); + + // Backtracking algorithm + vector state; + vector choices = {root}; + vector> res; + backtrack(state, choices, res); + + cout << "\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3" << endl; + for (vector &path : res) { + vector vals; + for (TreeNode *node : path) { + vals.push_back(node->val); + } + printVector(vals); + } +} diff --git a/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp b/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp new file mode 100644 index 000000000..d5971e903 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp @@ -0,0 +1,57 @@ +/** + * File: subset_sum_i.cpp + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Backtracking algorithm: Subset Sum I */ +void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.push_back(state); + return; + } + // Traverse all choices + // Pruning two: start traversing from start to avoid generating duplicate subsets + for (int i = start; i < choices.size(); i++) { + // Pruning one: if the subset sum exceeds target, end the loop immediately + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Attempt: make a choice, update target, start + state.push_back(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i, res); + // Retract: undo the choice, restore to the previous state + state.pop_back(); + } +} + +/* Solve Subset Sum I */ +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) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +int main() { + vector nums = {3, 4, 5}; + int target = 9; + + vector> res = subsetSumI(nums, target); + + cout << "Input array nums = "; + printVector(nums); + cout << "target = " << target << endl; + cout << "All subsets summing to " << target << "is" << 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 new file mode 100644 index 000000000..e06e6d0a4 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp @@ -0,0 +1,54 @@ +/** + * File: subset_sum_i_naive.cpp + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Backtracking algorithm: Subset Sum I */ +void backtrack(vector &state, int target, int total, vector &choices, vector> &res) { + // When the subset sum equals target, record the solution + if (total == target) { + res.push_back(state); + return; + } + // Traverse all choices + for (size_t i = 0; i < choices.size(); i++) { + // Pruning: if the subset sum exceeds target, skip that choice + if (total + choices[i] > target) { + continue; + } + // Attempt: make a choice, update elements and total + state.push_back(choices[i]); + // Proceed to the next round of selection + backtrack(state, target, total + choices[i], choices, res); + // Retract: undo the choice, restore to the previous state + state.pop_back(); + } +} + +/* Solve Subset Sum I (including duplicate subsets) */ +vector> subsetSumINaive(vector &nums, int target) { + vector state; // State (subset) + int total = 0; // Subset sum + vector> res; // Result list (subset list) + backtrack(state, target, total, nums, res); + return res; +} + +/* Driver Code */ +int main() { + vector nums = {3, 4, 5}; + int target = 9; + + vector> res = subsetSumINaive(nums, target); + + cout << "Input array nums = "; + printVector(nums); + cout << "target = " << target << endl; + cout << "All subsets summing to " << target << "is" << 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 new file mode 100644 index 000000000..49589cad0 --- /dev/null +++ b/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp @@ -0,0 +1,62 @@ +/** + * File: subset_sum_ii.cpp + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Backtracking algorithm: Subset Sum II */ +void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.push_back(state); + return; + } + // Traverse all choices + // Pruning two: start traversing from start to avoid generating duplicate subsets + // Pruning three: start traversing from start to avoid repeatedly selecting the same element + for (int i = start; i < choices.size(); i++) { + // Pruning one: if the subset sum exceeds target, end the loop immediately + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Pruning four: if the element equals the left element, it indicates that the search branch is repeated, skip it + if (i > start && choices[i] == choices[i - 1]) { + continue; + } + // Attempt: make a choice, update target, start + state.push_back(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i + 1, res); + // Retract: undo the choice, restore to the previous state + state.pop_back(); + } +} + +/* Solve Subset Sum II */ +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) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +int main() { + vector nums = {4, 4, 5}; + int target = 9; + + vector> res = subsetSumII(nums, target); + + cout << "Input array nums = "; + printVector(nums); + cout << "target = " << target << endl; + cout << "All subsets summing to " << target << "is" << endl; + printVectorMatrix(res); + + return 0; +} diff --git a/en/codes/cpp/chapter_computational_complexity/CMakeLists.txt b/en/codes/cpp/chapter_computational_complexity/CMakeLists.txt new file mode 100644 index 000000000..ea2845b75 --- /dev/null +++ b/en/codes/cpp/chapter_computational_complexity/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(iteration iteration.cpp) +add_executable(recursion recursion.cpp) +add_executable(space_complexity space_complexity.cpp) +add_executable(time_complexity time_complexity.cpp) +add_executable(worst_best_time_complexity worst_best_time_complexity.cpp) \ No newline at end of file diff --git a/en/codes/cpp/chapter_computational_complexity/iteration.cpp b/en/codes/cpp/chapter_computational_complexity/iteration.cpp new file mode 100644 index 000000000..d8858ebbd --- /dev/null +++ b/en/codes/cpp/chapter_computational_complexity/iteration.cpp @@ -0,0 +1,76 @@ +/** + * File: iteration.cpp + * Created Time: 2023-08-24 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* for loop */ +int forLoop(int n) { + int res = 0; + // Loop sum 1, 2, ..., n-1, n + for (int i = 1; i <= n; ++i) { + res += i; + } + return res; +} + +/* while loop */ +int whileLoop(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Loop sum 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // Update condition variable + } + return res; +} + +/* while loop (two updates) */ +int whileLoopII(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Loop sum 1, 4, 10, ... + while (i <= n) { + res += i; + // Update condition variable + i++; + i *= 2; + } + return res; +} + +/* Double for loop */ +string nestedForLoop(int n) { + ostringstream res; + // Loop i = 1, 2, ..., n-1, n + for (int i = 1; i <= n; ++i) { + // Loop j = 1, 2, ..., n-1, n + for (int j = 1; j <= n; ++j) { + res << "(" << i << ", " << j << "), "; + } + } + return res.str(); +} + +/* Driver Code */ +int main() { + int n = 5; + int res; + + res = forLoop(n); + cout << "\nSum result of the for loop res = " << res << endl; + + res = whileLoop(n); + cout << "\nSum result of the while loop res = " << res << endl; + + res = whileLoopII(n); + cout << "\nSum result of the while loop (with two updates) res = " << res << endl; + + string resStr = nestedForLoop(n); + cout << "\nResult of the double for loop traversal = " << resStr << endl; + + return 0; +} diff --git a/en/codes/cpp/chapter_computational_complexity/recursion.cpp b/en/codes/cpp/chapter_computational_complexity/recursion.cpp new file mode 100644 index 000000000..686691b5c --- /dev/null +++ b/en/codes/cpp/chapter_computational_complexity/recursion.cpp @@ -0,0 +1,78 @@ +/** + * File: recursion.cpp + * Created Time: 2023-08-24 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Recursion */ +int recur(int n) { + // Termination condition + if (n == 1) + return 1; + // Recursive: recursive call + int res = recur(n - 1); + // Return: return result + return n + res; +} + +/* Simulate recursion with iteration */ +int forLoopRecur(int n) { + // Use an explicit stack to simulate the system call stack + stack stack; + int res = 0; + // Recursive: recursive call + for (int i = n; i > 0; i--) { + // Simulate "recursive" by "pushing onto the stack" + stack.push(i); + } + // Return: return result + while (!stack.empty()) { + // Simulate "return" by "popping from the stack" + res += stack.top(); + stack.pop(); + } + // res = 1+2+3+...+n + return res; +} + +/* Tail recursion */ +int tailRecur(int n, int res) { + // Termination condition + if (n == 0) + return res; + // Tail recursive call + return tailRecur(n - 1, res + n); +} + +/* Fibonacci sequence: Recursion */ +int fib(int n) { + // Termination condition f(1) = 0, f(2) = 1 + if (n == 1 || n == 2) + return n - 1; + // Recursive call f(n) = f(n-1) + f(n-2) + int res = fib(n - 1) + fib(n - 2); + // Return result f(n) + return res; +} + +/* Driver Code */ +int main() { + int n = 5; + int res; + + res = recur(n); + cout << "\nSum result of the recursive function res = " << res << endl; + + res = forLoopRecur(n); + cout << "\nSum result using iteration to simulate recursion res = " << res << endl; + + res = tailRecur(n, 0); + cout << "\nSum result of the tail-recursive function res = " << res << endl; + + res = fib(n); + cout << "The " << n << "th number in the Fibonacci sequence 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 new file mode 100644 index 000000000..bcc0c1c55 --- /dev/null +++ b/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp @@ -0,0 +1,107 @@ +/** + * File: space_complexity.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Function */ +int func() { + // Perform some operations + return 0; +} + +/* Constant complexity */ +void constant(int n) { + // Constants, variables, objects occupy O(1) space + const int a = 0; + int b = 0; + vector nums(10000); + ListNode node(0); + // Variables in a loop occupy O(1) space + for (int i = 0; i < n; i++) { + int c = 0; + } + // Functions in a loop occupy O(1) space + for (int i = 0; i < n; i++) { + func(); + } +} + +/* Linear complexity */ +void linear(int n) { + // Array of length n occupies 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 + unordered_map map; + for (int i = 0; i < n; i++) { + map[i] = to_string(i); + } +} + +/* Linear complexity (recursive implementation) */ +void linearRecur(int n) { + cout << "Recursion n = " << n << endl; + if (n == 1) + return; + linearRecur(n - 1); +} + +/* Quadratic complexity */ +void quadratic(int n) { + // A two-dimensional list occupies O(n^2) space + vector> numMatrix; + for (int i = 0; i < n; i++) { + vector tmp; + for (int j = 0; j < n; j++) { + tmp.push_back(0); + } + numMatrix.push_back(tmp); + } +} + +/* Quadratic complexity (recursive implementation) */ +int quadraticRecur(int n) { + if (n <= 0) + return 0; + vector nums(n); + cout << "Recursive n = " << n << ", length of nums = " << nums.size() << endl; + return quadraticRecur(n - 1); +} + +/* Exponential complexity (building a full binary tree) */ +TreeNode *buildTree(int n) { + if (n == 0) + return nullptr; + TreeNode *root = new TreeNode(0); + root->left = buildTree(n - 1); + root->right = buildTree(n - 1); + return root; +} + +/* Driver Code */ +int main() { + int n = 5; + // Constant complexity + constant(n); + // Linear complexity + linear(n); + linearRecur(n); + // Quadratic complexity + quadratic(n); + quadraticRecur(n); + // Exponential complexity + TreeNode *root = buildTree(n); + printTree(root); + + // Free memory + freeMemoryTree(root); + + return 0; +} diff --git a/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp b/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp new file mode 100644 index 000000000..3035e75d5 --- /dev/null +++ b/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp @@ -0,0 +1,168 @@ +/** + * File: time_complexity.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Constant complexity */ +int constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; +} + +/* Linear complexity */ +int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; +} + +/* Linear complexity (traversing an array) */ +int arrayTraversal(vector &nums) { + int count = 0; + // Loop count is proportional to the length of the array + for (int num : nums) { + count++; + } + return count; +} + +/* Quadratic complexity */ +int quadratic(int n) { + int count = 0; + // Loop count is squared in relation to the data size n + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; +} + +/* Quadratic complexity (bubble sort) */ +int bubbleSort(vector &nums) { + int count = 0; // Counter + // Outer loop: 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 right end of the range + 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 individual operations + } + } + } + return count; +} + +/* Exponential complexity (loop implementation) */ +int exponential(int n) { + int count = 0, base = 1; + // Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) { + for (int j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +/* Exponential complexity (recursive implementation) */ +int expRecur(int n) { + if (n == 1) + return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +/* Logarithmic complexity (loop implementation) */ +int logarithmic(int n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; +} + +/* Logarithmic complexity (recursive implementation) */ +int logRecur(int n) { + if (n <= 1) + return 0; + return logRecur(n / 2) + 1; +} + +/* Linear logarithmic complexity */ +int linearLogRecur(int n) { + if (n <= 1) + return 1; + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Factorial complexity (recursive implementation) */ +int factorialRecur(int n) { + if (n == 0) + return 1; + int count = 0; + // From 1 split into n + for (int i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + +/* Driver Code */ +int main() { + // Can modify n to experience the trend of operation count changes under various complexities + int n = 8; + cout << "Input data size n = " << n << endl; + + int count = constant(n); + cout << "Number of constant complexity operations = " << count << endl; + + count = linear(n); + cout << "Number of linear complexity operations = " << count << endl; + vector arr(n); + count = arrayTraversal(arr); + cout << "Number of linear complexity operations (traversing the array) = " << count << endl; + + count = quadratic(n); + cout << "Number of quadratic order operations = " << 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 << "Number of quadratic order operations (bubble sort) = " << count << endl; + + count = exponential(n); + cout << "Number of exponential complexity operations (implemented by loop) = " << count << endl; + count = expRecur(n); + cout << "Number of exponential complexity operations (implemented by recursion) = " << count << endl; + + count = logarithmic(n); + cout << "Number of logarithmic complexity operations (implemented by loop) = " << count << endl; + count = logRecur(n); + cout << "Number of logarithmic complexity operations (implemented by recursion) = " << count << endl; + + count = linearLogRecur(n); + cout << "Number of linear logarithmic complexity operations (implemented by recursion) = " << count << endl; + + count = factorialRecur(n); + cout << "Number of factorial complexity operations (implemented by recursion) = " << 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 new file mode 100644 index 000000000..10bb2a8f2 --- /dev/null +++ b/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp @@ -0,0 +1,45 @@ +/** + * File: worst_best_time_complexity.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Generate an array with elements {1, 2, ..., n} in a randomly shuffled order */ +vector randomNumbers(int n) { + vector nums(n); + // Generate array nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + // Generate a random seed using system time + unsigned seed = chrono::system_clock::now().time_since_epoch().count(); + // Randomly shuffle array elements + shuffle(nums.begin(), nums.end(), default_random_engine(seed)); + return nums; +} + +/* Find the index of number 1 in array nums */ +int findOne(vector &nums) { + for (int i = 0; i < nums.size(); i++) { + // When element 1 is at the start of the array, achieve best time complexity O(1) + // When element 1 is at the end of the array, achieve worst time complexity O(n) + if (nums[i] == 1) + return i; + } + return -1; +} + +/* Driver Code */ +int main() { + for (int i = 0; i < 1000; i++) { + int n = 100; + vector nums = randomNumbers(n); + int index = findOne(nums); + cout << "\nThe array [ 1, 2, ..., n ] after being shuffled = "; + printVector(nums); + cout << "The index of number 1 is " << index << endl; + } + return 0; +} diff --git a/en/codes/cpp/chapter_divide_and_conquer/CMakeLists.txt b/en/codes/cpp/chapter_divide_and_conquer/CMakeLists.txt new file mode 100644 index 000000000..38dfff710 --- /dev/null +++ b/en/codes/cpp/chapter_divide_and_conquer/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(binary_search_recur binary_search_recur.cpp) +add_executable(build_tree build_tree.cpp) +add_executable(hanota hanota.cpp) \ No newline at end of file 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 new file mode 100644 index 000000000..4a5b67f47 --- /dev/null +++ b/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp @@ -0,0 +1,46 @@ +/** + * File: binary_search_recur.cpp + * Created Time: 2023-07-17 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Binary search: problem f(i, j) */ +int dfs(vector &nums, int target, int i, int j) { + // If the interval is empty, indicating no target element, return -1 + if (i > j) { + return -1; + } + // Calculate midpoint index m + int m = (i + j) / 2; + if (nums[m] < target) { + // Recursive subproblem f(m+1, j) + return dfs(nums, target, m + 1, j); + } else if (nums[m] > target) { + // Recursive subproblem f(i, m-1) + return dfs(nums, target, i, m - 1); + } else { + // Found the target element, thus return its index + return m; + } +} + +/* Binary search */ +int binarySearch(vector &nums, int target) { + int n = nums.size(); + // Solve problem f(0, n-1) + return dfs(nums, target, 0, n - 1); +} + +/* Driver Code */ +int main() { + int target = 6; + vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; + + // Binary search (double closed interval) + int index = binarySearch(nums, target); + cout << "Index of target element 6 =" << index << endl; + + return 0; +} \ No newline at end of file diff --git a/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp b/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp new file mode 100644 index 000000000..636d0db7f --- /dev/null +++ b/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp @@ -0,0 +1,51 @@ +/** + * File: build_tree.cpp + * Created Time: 2023-07-17 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Build binary tree: Divide and conquer */ +TreeNode *dfs(vector &preorder, unordered_map &inorderMap, int i, int l, int r) { + // Terminate when subtree interval is empty + if (r - l < 0) + return NULL; + // Initialize root node + TreeNode *root = new TreeNode(preorder[i]); + // Query m to divide left and right subtrees + int m = inorderMap[preorder[i]]; + // Subproblem: build left subtree + root->left = dfs(preorder, inorderMap, i + 1, l, m - 1); + // Subproblem: build right subtree + root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r); + // Return root node + return root; +} + +/* Build binary tree */ +TreeNode *buildTree(vector &preorder, vector &inorder) { + // Initialize hash table, storing in-order elements to indices mapping + unordered_map inorderMap; + for (int i = 0; i < inorder.size(); i++) { + inorderMap[inorder[i]] = i; + } + TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorder.size() - 1); + return root; +} + +/* Driver Code */ +int main() { + vector preorder = {3, 9, 2, 1, 7}; + vector inorder = {9, 3, 1, 2, 7}; + cout << "Pre-order traversal = "; + printVector(preorder); + cout << "In-order traversal = "; + printVector(inorder); + + TreeNode *root = buildTree(preorder, inorder); + cout << "The constructed binary tree is:\n"; + printTree(root); + + return 0; +} diff --git a/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp b/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp new file mode 100644 index 000000000..c98f6fd86 --- /dev/null +++ b/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp @@ -0,0 +1,66 @@ +/** + * File: hanota.cpp + * Created Time: 2023-07-17 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Move a disc */ +void move(vector &src, vector &tar) { + // Take out a disc from the top of src + int pan = src.back(); + src.pop_back(); + // Place the disc on top of tar + tar.push_back(pan); +} + +/* Solve the Tower of Hanoi problem f(i) */ +void dfs(int i, vector &src, vector &buf, vector &tar) { + // If only one disc remains on src, move it to tar + if (i == 1) { + move(src, tar); + return; + } + // Subproblem f(i-1): move the top i-1 discs from src with the help of tar to buf + dfs(i - 1, src, tar, buf); + // Subproblem f(1): move the remaining one disc from src to tar + move(src, tar); + // Subproblem f(i-1): move the top i-1 discs from buf with the help of src to tar + dfs(i - 1, buf, src, tar); +} + +/* Solve the Tower of Hanoi problem */ +void solveHanota(vector &A, vector &B, vector &C) { + int n = A.size(); + // Move the top n discs from A with the help of B to C + dfs(n, A, B, C); +} + +/* Driver Code */ +int main() { + // The tail of the list is the top of the pillar + vector A = {5, 4, 3, 2, 1}; + vector B = {}; + vector C = {}; + + cout << "Initial state:\n"; + cout << "A ="; + printVector(A); + cout << "B ="; + printVector(B); + cout << "C ="; + printVector(C); + + solveHanota(A, B, C); + + cout << "After disk movement:\n"; + cout << "A ="; + printVector(A); + cout << "B ="; + printVector(B); + cout << "C ="; + printVector(C); + + return 0; +} diff --git a/en/codes/cpp/chapter_dynamic_programming/CMakeLists.txt b/en/codes/cpp/chapter_dynamic_programming/CMakeLists.txt new file mode 100644 index 000000000..ed185458a --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(climbing_stairs_backtrack climbing_stairs_backtrack.cpp) +add_executable(climbing_stairs_dfs climbing_stairs_dfs.cpp) +add_executable(climbing_stairs_dfs_mem climbing_stairs_dfs_mem.cpp) +add_executable(climbing_stairs_dp climbing_stairs_dp.cpp) +add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.cpp) +add_executable(min_path_sum min_path_sum.cpp) +add_executable(unbounded_knapsack unbounded_knapsack.cpp) +add_executable(coin_change coin_change.cpp) +add_executable(coin_change_ii coin_change_ii.cpp) +add_executable(edit_distance edit_distance.cpp) \ No newline at end of file diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp new file mode 100644 index 000000000..5477b9aea --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp @@ -0,0 +1,43 @@ + +/** + * File: climbing_stairs_backtrack.cpp + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Backtracking */ +void backtrack(vector &choices, int state, int n, vector &res) { + // When climbing to the nth step, add 1 to the number of solutions + if (state == n) + res[0]++; + // Traverse all choices + for (auto &choice : choices) { + // Pruning: do not allow climbing beyond the nth step + if (state + choice > n) + continue; + // Attempt: make a choice, update the state + backtrack(choices, state + choice, n, res); + // Retract + } +} + +/* Climbing stairs: Backtracking */ +int climbingStairsBacktrack(int n) { + vector choices = {1, 2}; // Can choose to climb up 1 step or 2 steps + int state = 0; // Start climbing from the 0th step + vector res = {0}; // Use res[0] to record the number of solutions + backtrack(choices, state, n, res); + return res[0]; +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsBacktrack(n); + cout << "There are " << res << " solutions to climb " << n << " stairs" << 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 new file mode 100644 index 000000000..454d53338 --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp @@ -0,0 +1,37 @@ +/** + * File: climbing_stairs_constraint_dp.cpp + * Created Time: 2023-07-01 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Constrained climbing stairs: Dynamic programming */ +int climbingStairsConstraintDP(int n) { + if (n == 1 || n == 2) { + return 1; + } + // Initialize dp table, used to store subproblem solutions + vector> dp(n + 1, vector(3, 0)); + // Initial state: preset the smallest subproblem solution + 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 + 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]; + } + return dp[n][1] + dp[n][2]; +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsConstraintDP(n); + cout << "There are " << res << " solutions to climb " << n << " stairs" << 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 new file mode 100644 index 000000000..3072f956e --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp @@ -0,0 +1,32 @@ +/** + * File: climbing_stairs_dfs.cpp + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Search */ +int dfs(int i) { + // Known dp[1] and dp[2], 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 */ +int climbingStairsDFS(int n) { + return dfs(n); +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsDFS(n); + cout << "There are " << res << " solutions to climb " << n << " stairs" << 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 new file mode 100644 index 000000000..a1b2ef24f --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp @@ -0,0 +1,39 @@ +/** + * File: climbing_stairs_dfs_mem.cpp + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Memoized search */ +int dfs(int i, vector &mem) { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) + return i; + // If there is a record for dp[i], return it + 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); + // Record dp[i] + mem[i] = count; + return count; +} + +/* Climbing stairs: Memoized search */ +int climbingStairsDFSMem(int n) { + // mem[i] records the total number of solutions for climbing to the ith step, -1 means no record + vector mem(n + 1, -1); + return dfs(n, mem); +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsDFSMem(n); + cout << "There are " << res << " solutions to climb " << n << " stairs" << 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 new file mode 100644 index 000000000..f8974fb4c --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp @@ -0,0 +1,49 @@ +/** + * File: climbing_stairs_dp.cpp + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Climbing stairs: Dynamic programming */ +int climbingStairsDP(int n) { + if (n == 1 || n == 2) + return n; + // Initialize dp table, used to store subproblem solutions + vector dp(n + 1); + // Initial state: preset the smallest subproblem solution + dp[1] = 1; + dp[2] = 2; + // State transition: gradually solve larger subproblems from 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 */ +int climbingStairsDPComp(int n) { + if (n == 1 || n == 2) + return n; + int a = 1, b = 2; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = a + b; + a = tmp; + } + return b; +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsDP(n); + cout << "There are " << res << " solutions to climb " << n << " stairs" << endl; + + res = climbingStairsDPComp(n); + cout << "There are " << res << " solutions to climb " << n << " stairs" << 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 new file mode 100644 index 000000000..80c974f9b --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp @@ -0,0 +1,70 @@ +/** + * File: coin_change.cpp + * Created Time: 2023-07-11 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Coin change: Dynamic programming */ +int coinChangeDP(vector &coins, int amt) { + int n = coins.size(); + int MAX = amt + 1; + // Initialize dp table + vector> dp(n + 1, vector(amt + 1, 0)); + // State transition: first row and first column + for (int a = 1; a <= amt; a++) { + dp[0][a] = MAX; + } + // State transition: the rest of the rows and columns + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeding the target amount, do not choose coin i + dp[i][a] = dp[i - 1][a]; + } else { + // The smaller value between 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] != MAX ? dp[n][amt] : -1; +} + +/* Coin change: Space-optimized dynamic programming */ +int coinChangeDPComp(vector &coins, int amt) { + int n = coins.size(); + int MAX = amt + 1; + // Initialize dp table + vector dp(amt + 1, MAX); + dp[0] = 0; + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeding the target amount, do not choose coin i + dp[a] = dp[a]; + } else { + // The smaller value between not choosing and choosing coin i + dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1); + } + } + } + return dp[amt] != MAX ? dp[amt] : -1; +} + +/* Driver code */ +int main() { + vector coins = {1, 2, 5}; + int amt = 4; + + // Dynamic programming + int res = coinChangeDP(coins, amt); + cout << "The minimum number of coins required to make up the target amount is " << res << endl; + + // Space-optimized dynamic programming + res = coinChangeDPComp(coins, amt); + cout << "The minimum number of coins required to make up the target amount is " << 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 new file mode 100644 index 000000000..4a99a5e5a --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp @@ -0,0 +1,68 @@ +/** + * File: coin_change_ii.cpp + * Created Time: 2023-07-11 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Coin change II: Dynamic programming */ +int coinChangeIIDP(vector &coins, int amt) { + int n = coins.size(); + // Initialize dp table + vector> dp(n + 1, vector(amt + 1, 0)); + // Initialize first column + for (int i = 0; i <= n; i++) { + dp[i][0] = 1; + } + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeding the target amount, do not choose coin i + dp[i][a] = dp[i - 1][a]; + } else { + // The sum of the two options of not choosing and choosing coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; + } + } + } + return dp[n][amt]; +} + +/* Coin change II: Space-optimized dynamic programming */ +int coinChangeIIDPComp(vector &coins, int amt) { + int n = coins.size(); + // Initialize dp table + vector dp(amt + 1, 0); + dp[0] = 1; + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeding the target amount, do not choose coin i + dp[a] = dp[a]; + } else { + // The sum of the two options of not choosing and choosing coin i + dp[a] = dp[a] + dp[a - coins[i - 1]]; + } + } + } + return dp[amt]; +} + +/* Driver code */ +int main() { + vector coins = {1, 2, 5}; + int amt = 5; + + // Dynamic programming + int res = coinChangeIIDP(coins, amt); + cout << "The number of coin combinations to make up the target amount is " << res << endl; + + // Space-optimized dynamic programming + res = coinChangeIIDPComp(coins, amt); + cout << "The number of coin combinations to make up the target amount is " << 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 new file mode 100644 index 000000000..08062503c --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp @@ -0,0 +1,136 @@ +/** + * File: edit_distance.cpp + * Created Time: 2023-07-13 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* 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 (i == 0 && j == 0) + return 0; + // If s is empty, return the length of t + if (i == 0) + return j; + // If t is empty, return the length of s + if (j == 0) + return i; + // If the two characters are equal, skip these two characters + if (s[i - 1] == t[j - 1]) + return editDistanceDFS(s, t, i - 1, j - 1); + // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 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 the minimum number of edits + return min(min(insert, del), replace) + 1; +} + +/* 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 (i == 0 && j == 0) + return 0; + // If s is empty, return the length of t + if (i == 0) + return j; + // If t is empty, return the length of s + if (j == 0) + return i; + // If there is a record, return it + if (mem[i][j] != -1) + return mem[i][j]; + // If the two characters are equal, skip these two characters + if (s[i - 1] == t[j - 1]) + return editDistanceDFSMem(s, t, mem, i - 1, j - 1); + // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 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 the minimum number of edits + mem[i][j] = min(min(insert, del), replace) + 1; + return mem[i][j]; +} + +/* 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 + for (int i = 1; i <= n; i++) { + dp[i][0] = i; + } + for (int j = 1; j <= m; j++) { + dp[0][j] = j; + } + // State transition: the rest of the 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 the two characters are equal, skip these two characters + dp[i][j] = dp[i - 1][j - 1]; + } else { + // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 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 */ +int editDistanceDPComp(string s, string t) { + int n = s.length(), m = t.length(); + vector dp(m + 1, 0); + // State transition: first row + for (int j = 1; j <= m; j++) { + dp[j] = j; + } + // State transition: the rest of the rows + for (int i = 1; i <= n; i++) { + // State transition: first column + int leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = i; + // State transition: the rest of the columns + for (int j = 1; j <= m; j++) { + int temp = dp[j]; + if (s[i - 1] == t[j - 1]) { + // If the two characters are equal, skip these two characters + dp[j] = leftup; + } else { + // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // Update for the next round of dp[i-1, j-1] + } + } + return dp[m]; +} + +/* Driver Code */ +int main() { + string s = "bag"; + string t = "pack"; + int n = s.length(), m = t.length(); + + // Brute force search + int res = editDistanceDFS(s, t, n, m); + cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits.\n"; + + // 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.\n"; + + // Dynamic programming + res = editDistanceDP(s, t); + cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits.\n"; + + // Space-optimized dynamic programming + res = editDistanceDPComp(s, t); + cout << "Changing " << s << " to " << t << " requires a minimum of " << 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 new file mode 100644 index 000000000..ec03fc641 --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp @@ -0,0 +1,109 @@ +#include +#include +#include + +using namespace std; + +/* 0-1 Knapsack: Brute force search */ +int knapsackDFS(vector &wgt, vector &val, int i, int c) { + // If all items have been chosen or the knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If exceeding the knapsack capacity, can only choose not to put it in the knapsack + 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 + int no = knapsackDFS(wgt, val, i - 1, c); + int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Return the greater value of the two options + return max(no, yes); +} + +/* 0-1 Knapsack: Memoized search */ +int knapsackDFSMem(vector &wgt, vector &val, vector> &mem, int i, int c) { + // If all items have been chosen or the knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If there is a record, return it + if (mem[i][c] != -1) { + return mem[i][c]; + } + // If exceeding the knapsack capacity, can only choose not to put it in the knapsack + 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 + 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 greater value of the two options + mem[i][c] = max(no, yes); + return mem[i][c]; +} + +/* 0-1 Knapsack: Dynamic programming */ +int knapsackDP(vector &wgt, vector &val, int cap) { + int n = wgt.size(); + // Initialize dp table + vector> dp(n + 1, vector(cap + 1, 0)); + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeding the knapsack capacity, do not choose item i + dp[i][c] = dp[i - 1][c]; + } else { + // The greater value between not choosing and choosing item i + dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +/* 0-1 Knapsack: Space-optimized dynamic programming */ +int knapsackDPComp(vector &wgt, vector &val, int cap) { + int n = wgt.size(); + // Initialize dp table + vector dp(cap + 1, 0); + // State transition + for (int i = 1; i <= n; i++) { + // Traverse in reverse order + for (int c = cap; c >= 1; c--) { + if (wgt[i - 1] <= c) { + // The greater value between not choosing and choosing item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +int main() { + vector wgt = {10, 20, 30, 40, 50}; + vector val = {50, 120, 150, 210, 240}; + int cap = 50; + int n = wgt.size(); + + // Brute force search + int res = knapsackDFS(wgt, val, n, cap); + cout << "The maximum value within the bag capacity is " << res << endl; + + // Memoized search + vector> mem(n + 1, vector(cap + 1, -1)); + res = knapsackDFSMem(wgt, val, mem, n, cap); + cout << "The maximum value within the bag capacity is " << res << endl; + + // Dynamic programming + res = knapsackDP(wgt, val, cap); + cout << "The maximum value within the bag capacity is " << res << endl; + + // Space-optimized dynamic programming + res = knapsackDPComp(wgt, val, cap); + cout << "The maximum value within the bag capacity is " << 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 new file mode 100644 index 000000000..86f0a6efe --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp @@ -0,0 +1,53 @@ +/** + * File: min_cost_climbing_stairs_dp.cpp + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Climbing stairs with minimum cost: 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 subproblem solutions + vector dp(n + 1); + // Initial state: preset the smallest subproblem solution + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return dp[n]; +} + +/* Climbing stairs with minimum cost: Space-optimized dynamic programming */ +int minCostClimbingStairsDPComp(vector &cost) { + int n = cost.size() - 1; + if (n == 1 || n == 2) + return cost[n]; + int a = cost[1], b = cost[2]; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = min(a, tmp) + cost[i]; + a = tmp; + } + return b; +} + +/* Driver Code */ +int main() { + vector cost = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1}; + cout << "Input the cost list for stairs"; + printVector(cost); + + int res = minCostClimbingStairsDP(cost); + cout << "Minimum cost to climb the stairs " << res << endl; + + res = minCostClimbingStairsDPComp(cost); + 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 new file mode 100644 index 000000000..c81c3a54e --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp @@ -0,0 +1,116 @@ +/** + * File: min_path_sum.cpp + * Created Time: 2023-07-10 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Minimum path sum: Brute force search */ +int minPathSumDFS(vector> &grid, int i, int j) { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If the row or column index is out of bounds, return a +∞ cost + if (i < 0 || j < 0) { + return INT_MAX; + } + // Calculate the minimum path cost from the top-left 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 the top-left to (i, j) + return min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; +} + +/* 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 + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If the row or column index is out of bounds, return a +∞ cost + if (i < 0 || j < 0) { + return INT_MAX; + } + // If there is a record, return it + if (mem[i][j] != -1) { + return mem[i][j]; + } + // The minimum path cost from the left and top cells + int up = minPathSumDFSMem(grid, mem, i - 1, j); + int left = minPathSumDFSMem(grid, mem, i, j - 1); + // Record and return the minimum path cost from the top-left 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 */ +int minPathSumDP(vector> &grid) { + int n = grid.size(), m = grid[0].size(); + // Initialize dp table + vector> dp(n, vector(m)); + dp[0][0] = grid[0][0]; + // State transition: first row + for (int j = 1; j < m; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // State transition: first column + for (int i = 1; i < n; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // State transition: the rest of the 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]; + } + } + return dp[n - 1][m - 1]; +} + +/* Minimum path sum: Space-optimized dynamic programming */ +int minPathSumDPComp(vector> &grid) { + int n = grid.size(), m = grid[0].size(); + // Initialize dp table + vector dp(m); + // State transition: first row + dp[0] = grid[0][0]; + for (int j = 1; j < m; j++) { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transition: the rest of the rows + for (int i = 1; i < n; i++) { + // State transition: first column + dp[0] = dp[0] + grid[i][0]; + // State transition: the rest of the columns + for (int j = 1; j < m; j++) { + dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; +} + +/* 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 << "The minimum path sum from the top left corner to the bottom right corner is " << res << endl; + + // Memoized search + vector> mem(n, vector(m, -1)); + res = minPathSumDFSMem(grid, mem, n - 1, m - 1); + cout << "The minimum path sum from the top left corner to the bottom right corner is " << res << endl; + + // Dynamic programming + res = minPathSumDP(grid); + cout << "The minimum path sum from the top left corner to the bottom right corner is " << res << endl; + + // Space-optimized dynamic programming + res = minPathSumDPComp(grid); + cout << "The minimum path sum from the top left corner to the bottom right corner is " << 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 new file mode 100644 index 000000000..4ed94f07a --- /dev/null +++ b/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp @@ -0,0 +1,64 @@ +/** + * File: unbounded_knapsack.cpp + * Created Time: 2023-07-11 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Complete knapsack: Dynamic programming */ +int unboundedKnapsackDP(vector &wgt, vector &val, int cap) { + int n = wgt.size(); + // Initialize dp table + vector> dp(n + 1, vector(cap + 1, 0)); + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeding the knapsack capacity, do not choose item i + dp[i][c] = dp[i - 1][c]; + } else { + // The greater value between not choosing and choosing item i + dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +/* Complete knapsack: Space-optimized dynamic programming */ +int unboundedKnapsackDPComp(vector &wgt, vector &val, int cap) { + int n = wgt.size(); + // Initialize dp table + vector dp(cap + 1, 0); + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeding the knapsack capacity, do not choose item i + dp[c] = dp[c]; + } else { + // The greater value between not choosing and choosing item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver code */ +int main() { + vector wgt = {1, 2, 3}; + vector val = {5, 11, 15}; + int cap = 4; + + // Dynamic programming + int res = unboundedKnapsackDP(wgt, val, cap); + cout << "The maximum value within the bag capacity is " << res << endl; + + // Space-optimized dynamic programming + res = unboundedKnapsackDPComp(wgt, val, cap); + cout << "The maximum value within the bag capacity is " << res << endl; + + return 0; +} diff --git a/en/codes/cpp/chapter_graph/CMakeLists.txt b/en/codes/cpp/chapter_graph/CMakeLists.txt new file mode 100644 index 000000000..4a56ce35b --- /dev/null +++ b/en/codes/cpp/chapter_graph/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(graph_bfs graph_bfs.cpp) +add_executable(graph_dfs graph_dfs.cpp) +# add_executable(graph_adjacency_list graph_adjacency_list.cpp) +add_executable(graph_adjacency_list_test graph_adjacency_list_test.cpp) +add_executable(graph_adjacency_matrix graph_adjacency_matrix.cpp) diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp new file mode 100644 index 000000000..1fa9489c4 --- /dev/null +++ b/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp @@ -0,0 +1,90 @@ +/** + * File: graph_adjacency_list.cpp + * Created Time: 2023-02-09 + * Author: what-is-me (whatisme@outlook.jp), krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Undirected graph class based on adjacency list */ +class GraphAdjList { + public: + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + unordered_map> adjList; + + /* Remove a specified node from vector */ + void remove(vector &vec, Vertex *vet) { + for (int i = 0; i < vec.size(); i++) { + if (vec[i] == vet) { + vec.erase(vec.begin() + i); + break; + } + } + } + + /* Constructor */ + GraphAdjList(const vector> &edges) { + // Add all vertices and edges + for (const vector &edge : edges) { + addVertex(edge[0]); + addVertex(edge[1]); + addEdge(edge[0], edge[1]); + } + } + + /* Get the number of vertices */ + int size() { + return adjList.size(); + } + + /* Add edge */ + void addEdge(Vertex *vet1, Vertex *vet2) { + if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) + throw invalid_argument("Vertex does not exist"); + // Add edge vet1 - vet2 + adjList[vet1].push_back(vet2); + adjList[vet2].push_back(vet1); + } + + /* Remove edge */ + void removeEdge(Vertex *vet1, Vertex *vet2) { + if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) + throw invalid_argument("Vertex does not exist"); + // Remove edge vet1 - vet2 + remove(adjList[vet1], vet2); + remove(adjList[vet2], vet1); + } + + /* Add vertex */ + void addVertex(Vertex *vet) { + if (adjList.count(vet)) + return; + // Add a new linked list to the adjacency list + adjList[vet] = vector(); + } + + /* Remove vertex */ + void removeVertex(Vertex *vet) { + if (!adjList.count(vet)) + throw invalid_argument("Vertex does not exist"); + // Remove the vertex vet's corresponding linked list from the adjacency list + adjList.erase(vet); + // Traverse other vertices' linked lists, removing all edges containing vet + for (auto &adj : adjList) { + remove(adj.second, vet); + } + } + + /* Print the adjacency list */ + void print() { + cout << "Adjacency list =" << endl; + for (auto &adj : adjList) { + const auto &key = adj.first; + const auto &vec = adj.second; + cout << key->val << ": "; + printVector(vetsToVals(vec)); + } + } +}; + +// See test case in graph_adjacency_list_test.cpp diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp new file mode 100644 index 000000000..92d96effa --- /dev/null +++ b/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp @@ -0,0 +1,49 @@ +/** + * File: graph_adjacency_list_test.cpp + * Created Time: 2023-02-09 + * Author: what-is-me (whatisme@outlook.jp), krahets (krahets@163.com) + */ + +#include "./graph_adjacency_list.cpp" + +/* Driver Code */ +int main() { + /* Initialize 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, the graph is" << endl; + graph.print(); + + /* Add edge */ + // Vertices 1, 2 i.e., v[0], v[2] + graph.addEdge(v[0], v[2]); + cout << "\nAfter adding edge 1-2, the graph is" << endl; + graph.print(); + + /* Remove edge */ + // Vertices 1, 3 i.e., v[0], v[1] + graph.removeEdge(v[0], v[1]); + cout << "\nAfter removing edge 1-3, the graph is" << endl; + graph.print(); + + /* Add vertex */ + Vertex *v5 = new Vertex(6); + graph.addVertex(v5); + cout << "\nAfter adding vertex 6, the graph is" << endl; + graph.print(); + + /* Remove vertex */ + // Vertex 3 i.e., v[1] + graph.removeVertex(v[1]); + cout << "\nAfter removing vertex 3, the graph is" << endl; + graph.print(); + + // Free memory + for (Vertex *vet : v) { + delete vet; + } + + return 0; +} diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp new file mode 100644 index 000000000..245ac42f2 --- /dev/null +++ b/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp @@ -0,0 +1,127 @@ +/** + * File: graph_adjacency_matrix.cpp + * Created Time: 2023-02-09 + * Author: what-is-me (whatisme@outlook.jp) + */ + +#include "../utils/common.hpp" + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat { + vector vertices; // Vertex list, elements represent "vertex value", index represents "vertex index" + vector> adjMat; // Adjacency matrix, row and column indices correspond to "vertex index" + + public: + /* Constructor */ + GraphAdjMat(const vector &vertices, const vector> &edges) { + // Add vertex + for (int val : vertices) { + addVertex(val); + } + // Add edge + // Edges elements represent vertex indices + for (const vector &edge : edges) { + addEdge(edge[0], edge[1]); + } + } + + /* Get the number of vertices */ + int size() const { + return vertices.size(); + } + + /* Add vertex */ + void addVertex(int val) { + int n = size(); + // Add new vertex value to the vertex list + vertices.push_back(val); + // Add a row to the adjacency matrix + adjMat.emplace_back(vector(n, 0)); + // Add a column to the adjacency matrix + for (vector &row : adjMat) { + row.push_back(0); + } + } + + /* Remove vertex */ + void removeVertex(int index) { + if (index >= size()) { + throw out_of_range("Vertex does not exist"); + } + // Remove vertex at `index` from the vertex list + vertices.erase(vertices.begin() + index); + // Remove the row at `index` from the adjacency matrix + adjMat.erase(adjMat.begin() + index); + // Remove the column at `index` from the adjacency matrix + for (vector &row : adjMat) { + row.erase(row.begin() + index); + } + } + + /* Add edge */ + // Parameters i, j correspond to vertices element indices + void addEdge(int i, int j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { + throw out_of_range("Vertex does not exist"); + } + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) + adjMat[i][j] = 1; + adjMat[j][i] = 1; + } + + /* Remove edge */ + // Parameters i, j correspond to vertices element indices + void removeEdge(int i, int j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { + throw out_of_range("Vertex does not exist"); + } + adjMat[i][j] = 0; + adjMat[j][i] = 0; + } + + /* Print adjacency matrix */ + void print() { + cout << "Vertex list = "; + printVector(vertices); + cout << "Adjacency matrix =" << endl; + printVectorMatrix(adjMat); + } +}; + +/* Driver Code */ +int main() { + /* Initialize undirected graph */ + // Edges elements represent vertex indices + 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, the graph is" << endl; + graph.print(); + + /* Add edge */ + // Indices of vertices 1, 2 are 0, 2 respectively + graph.addEdge(0, 2); + cout << "\nAfter adding edge 1-2, the graph is" << endl; + graph.print(); + + /* Remove edge */ + // Indices of vertices 1, 3 are 0, 1 respectively + graph.removeEdge(0, 1); + cout << "\nAfter removing edge 1-3, the graph is" << endl; + graph.print(); + + /* Add vertex */ + graph.addVertex(6); + cout << "\nAfter adding vertex 6, the graph is" << endl; + graph.print(); + + /* Remove vertex */ + // Index of vertex 3 is 1 + graph.removeVertex(1); + cout << "\nAfter removing vertex 3, the graph is" << 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 new file mode 100644 index 000000000..93ab145ba --- /dev/null +++ b/en/codes/cpp/chapter_graph/graph_bfs.cpp @@ -0,0 +1,59 @@ +/** + * File: graph_bfs.cpp + * Created Time: 2023-03-02 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" +#include "./graph_adjacency_list.cpp" + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex +vector graphBFS(GraphAdjList &graph, Vertex *startVet) { + // Vertex traversal sequence + vector res; + // Hash set, used to record visited vertices + unordered_set visited = {startVet}; + // Queue used to implement BFS + queue que; + que.push(startVet); + // Starting from vertex vet, loop until all vertices are visited + while (!que.empty()) { + Vertex *vet = que.front(); + que.pop(); // Dequeue the vertex at the head of the queue + res.push_back(vet); // Record visited vertex + // Traverse all adjacent vertices of that vertex + for (auto adjVet : graph.adjList[vet]) { + if (visited.count(adjVet)) + continue; // Skip already visited vertices + que.push(adjVet); // Only enqueue unvisited vertices + visited.emplace(adjVet); // Mark the vertex as visited + } + } + // Return the vertex traversal sequence + return res; +} + +/* Driver Code */ +int main() { + /* Initialize 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, the graph is\n"; + graph.print(); + + /* Breadth-first traversal */ + vector res = graphBFS(graph, v[0]); + cout << "\nBreadth-first traversal (BFS) vertex sequence is" << endl; + printVector(vetsToVals(res)); + + // Free memory + for (Vertex *vet : v) { + delete vet; + } + + return 0; +} diff --git a/en/codes/cpp/chapter_graph/graph_dfs.cpp b/en/codes/cpp/chapter_graph/graph_dfs.cpp new file mode 100644 index 000000000..79e1e4356 --- /dev/null +++ b/en/codes/cpp/chapter_graph/graph_dfs.cpp @@ -0,0 +1,55 @@ +/** + * File: graph_dfs.cpp + * Created Time: 2023-03-02 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" +#include "./graph_adjacency_list.cpp" + +/* Depth-first traversal helper function */ +void dfs(GraphAdjList &graph, unordered_set &visited, vector &res, Vertex *vet) { + res.push_back(vet); // Record visited vertex + visited.emplace(vet); // Mark the vertex as visited + // Traverse all adjacent vertices of that vertex + for (Vertex *adjVet : graph.adjList[vet]) { + if (visited.count(adjVet)) + continue; // Skip already visited vertices + // Recursively visit adjacent vertices + dfs(graph, visited, res, adjVet); + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex +vector graphDFS(GraphAdjList &graph, Vertex *startVet) { + // Vertex traversal sequence + vector res; + // Hash set, used to record visited vertices + unordered_set visited; + dfs(graph, visited, res, startVet); + return res; +} + +/* Driver Code */ +int main() { + /* Initialize 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, the graph is" << endl; + graph.print(); + + /* Depth-first traversal */ + vector res = graphDFS(graph, v[0]); + cout << "\nDepth-first traversal (DFS) vertex sequence is" << endl; + printVector(vetsToVals(res)); + + // Free memory + for (Vertex *vet : v) { + delete vet; + } + + return 0; +} diff --git a/en/codes/cpp/chapter_greedy/CMakeLists.txt b/en/codes/cpp/chapter_greedy/CMakeLists.txt new file mode 100644 index 000000000..91788668d --- /dev/null +++ b/en/codes/cpp/chapter_greedy/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(coin_change_greedy coin_change_greedy.cpp) +add_executable(fractional_knapsack fractional_knapsack.cpp) +add_executable(max_capacity max_capacity.cpp) \ No newline at end of file diff --git a/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp b/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp new file mode 100644 index 000000000..c7d1135b9 --- /dev/null +++ b/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp @@ -0,0 +1,60 @@ +/** + * File: coin_change_greedy.cpp + * Created Time: 2023-07-20 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Coin change: Greedy */ +int coinChangeGreedy(vector &coins, int amt) { + // Assume coins list is ordered + int i = coins.size() - 1; + int count = 0; + // Loop for greedy selection until no remaining amount + while (amt > 0) { + // Find the smallest coin close to and less than the remaining amount + while (i > 0 && coins[i] > amt) { + i--; + } + // Choose coins[i] + amt -= coins[i]; + count++; + } + // If no feasible solution is found, return -1 + return amt == 0 ? count : -1; +} + +/* Driver Code */ +int main() { + // Greedy: can ensure finding a global 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 << "The minimum number of coins required to make up " << amt << " is " << res << endl; + + // Greedy: cannot ensure finding a global optimal solution + coins = {1, 20, 50}; + amt = 60; + res = coinChangeGreedy(coins, amt); + cout << "\ncoins = "; + printVector(coins); + cout << "amt = " << amt << endl; + cout << "The minimum number of coins required to make up " << amt << " is " << res << endl; + cout << "In reality, the minimum number needed is 3, i.e., 20 + 20 + 20" << endl; + + // Greedy: cannot ensure finding a global optimal solution + coins = {1, 49, 50}; + amt = 98; + res = coinChangeGreedy(coins, amt); + cout << "\ncoins = "; + printVector(coins); + cout << "amt = " << amt << endl; + cout << "The minimum number of coins required to make up " << amt << " is " << res << endl; + cout << "In reality, the minimum number needed is 2, i.e., 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 new file mode 100644 index 000000000..1781e865c --- /dev/null +++ b/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp @@ -0,0 +1,56 @@ +/** + * File: fractional_knapsack.cpp + * Created Time: 2023-07-20 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Item */ +class Item { + public: + int w; // Item weight + int v; // Item value + + Item(int w, int v) : w(w), v(v) { + } +}; + +/* Fractional knapsack: Greedy */ +double fractionalKnapsack(vector &wgt, vector &val, int cap) { + // Create an item list, containing two properties: weight, 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(items.begin(), items.end(), [](Item &a, Item &b) { return (double)a.v / a.w > (double)b.v / b.w; }); + // Loop for greedy selection + double res = 0; + for (auto &item : items) { + if (item.w <= cap) { + // If the remaining capacity is sufficient, put the entire item into the knapsack + res += item.v; + cap -= item.w; + } else { + // If the remaining capacity is insufficient, put part of the item into the knapsack + res += (double)item.v / item.w * cap; + // No remaining capacity left, thus break the loop + break; + } + } + return res; +} + +/* Driver Code */ +int main() { + vector wgt = {10, 20, 30, 40, 50}; + vector val = {50, 120, 150, 210, 240}; + int cap = 50; + + // Greedy algorithm + double res = fractionalKnapsack(wgt, val, cap); + cout << "The maximum value within the bag capacity is " << res << endl; + + return 0; +} diff --git a/en/codes/cpp/chapter_greedy/max_capacity.cpp b/en/codes/cpp/chapter_greedy/max_capacity.cpp new file mode 100644 index 000000000..d573d1e5c --- /dev/null +++ b/en/codes/cpp/chapter_greedy/max_capacity.cpp @@ -0,0 +1,39 @@ +/** + * File: max_capacity.cpp + * Created Time: 2023-07-21 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Maximum capacity: Greedy */ +int maxCapacity(vector &ht) { + // Initialize i, j, making them split the array at both ends + int i = 0, j = ht.size() - 1; + // Initial maximum capacity is 0 + int res = 0; + // Loop for greedy selection until the two boards meet + while (i < j) { + // Update maximum capacity + int cap = min(ht[i], ht[j]) * (j - i); + res = max(res, cap); + // Move the shorter board inward + if (ht[i] < ht[j]) { + i++; + } else { + j--; + } + } + return res; +} + +/* Driver Code */ +int main() { + vector ht = {3, 8, 5, 2, 7, 7, 3, 4}; + + // Greedy algorithm + int res = maxCapacity(ht); + cout << "The maximum capacity is " << 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 new file mode 100644 index 000000000..f81ce63cd --- /dev/null +++ b/en/codes/cpp/chapter_greedy/max_product_cutting.cpp @@ -0,0 +1,39 @@ +/** + * File: max_product_cutting.cpp + * Created Time: 2023-07-21 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Maximum product of cutting: Greedy */ +int maxProductCutting(int n) { + // When n <= 3, must cut out a 1 + if (n <= 3) { + return 1 * (n - 1); + } + // Greedy cut out 3s, a is the number of 3s, 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 into 2 * 2 + return (int)pow(3, a - 1) * 2 * 2; + } + if (b == 2) { + // When the remainder is 2, do nothing + return (int)pow(3, a) * 2; + } + // When the remainder is 0, do nothing + return (int)pow(3, a); +} + +/* Driver Code */ +int main() { + int n = 58; + + // Greedy algorithm + int res = maxProductCutting(n); + cout << "Maximum cutting product is" << res << endl; + + return 0; +} diff --git a/en/codes/cpp/chapter_hashing/CMakeLists.txt b/en/codes/cpp/chapter_hashing/CMakeLists.txt new file mode 100644 index 000000000..6b583ef55 --- /dev/null +++ b/en/codes/cpp/chapter_hashing/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(hash_map hash_map.cpp) +add_executable(array_hash_map_test array_hash_map_test.cpp) +add_executable(hash_map_chaining hash_map_chaining.cpp) +add_executable(hash_map_open_addressing hash_map_open_addressing.cpp) +add_executable(simple_hash simple_hash.cpp) +add_executable(built_in_hash built_in_hash.cpp) \ No newline at end of file diff --git a/en/codes/cpp/chapter_hashing/array_hash_map.cpp b/en/codes/cpp/chapter_hashing/array_hash_map.cpp new file mode 100644 index 000000000..bb4955303 --- /dev/null +++ b/en/codes/cpp/chapter_hashing/array_hash_map.cpp @@ -0,0 +1,110 @@ +/** + * File: array_hash_map.cpp + * Created Time: 2022-12-14 + * Author: msk397 (machangxinq@gmail.com) + */ + +#include "../utils/common.hpp" + +/* Key-value pair */ +struct Pair { + public: + int key; + string val; + Pair(int key, string val) { + this->key = key; + this->val = val; + } +}; + +/* Hash table based on array implementation */ +class ArrayHashMap { + private: + vector buckets; + + public: + ArrayHashMap() { + // Initialize an array, containing 100 buckets + buckets = vector(100); + } + + ~ArrayHashMap() { + // Free memory + for (const auto &bucket : buckets) { + delete bucket; + } + buckets.clear(); + } + + /* Hash function */ + int hashFunc(int key) { + int index = key % 100; + return index; + } + + /* Query operation */ + string get(int key) { + int index = hashFunc(key); + Pair *pair = buckets[index]; + if (pair == nullptr) + return ""; + return pair->val; + } + + /* Add operation */ + void put(int key, string val) { + Pair *pair = new Pair(key, val); + int index = hashFunc(key); + buckets[index] = pair; + } + + /* Remove operation */ + void remove(int key) { + int index = hashFunc(key); + // Free memory and set to nullptr + delete buckets[index]; + buckets[index] = nullptr; + } + + /* Get all key-value pairs */ + vector pairSet() { + vector pairSet; + for (Pair *pair : buckets) { + if (pair != nullptr) { + pairSet.push_back(pair); + } + } + return pairSet; + } + + /* Get all keys */ + vector keySet() { + vector keySet; + for (Pair *pair : buckets) { + if (pair != nullptr) { + keySet.push_back(pair->key); + } + } + return keySet; + } + + /* Get all values */ + vector valueSet() { + vector valueSet; + for (Pair *pair : buckets) { + if (pair != nullptr) { + valueSet.push_back(pair->val); + } + } + return valueSet; + } + + /* Print hash table */ + void print() { + for (Pair *kv : pairSet()) { + cout << kv->key << " -> " << kv->val << endl; + } + } +}; + +// See test case in array_hash_map_test.cpp diff --git a/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp b/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp new file mode 100644 index 000000000..6ad35ec82 --- /dev/null +++ b/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp @@ -0,0 +1,52 @@ +/** + * File: array_hash_map_test.cpp + * Created Time: 2022-12-14 + * Author: msk397 (machangxinq@gmail.com) + */ + +#include "./array_hash_map.cpp" + +/* Driver Code */ +int main() { + /* Initialize hash table */ + ArrayHashMap map = ArrayHashMap(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Ha"); + map.put(15937, "Luo"); + map.put(16750, "Suan"); + map.put(13276, "Fa"); + map.put(10583, "Ya"); + cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + map.print(); + + /* Query operation */ + // Enter key to the hash table, get value + string name = map.get(15937); + cout << "\nEnter student ID 15937, found name " << name << endl; + + /* Remove operation */ + // Remove key-value pair (key, value) from the hash table + map.remove(10583); + cout << "\nAfter removing 10583, the hash table is\nKey -> Value" << endl; + map.print(); + + /* Traverse hash table */ + cout << "\nTraverse key-value pairs Key->Value" << endl; + for (auto kv : map.pairSet()) { + cout << kv->key << " -> " << kv->val << endl; + } + + cout << "\nIndividually traverse keys Key" << endl; + for (auto key : map.keySet()) { + cout << key << endl; + } + + cout << "\nIndividually traverse values Value" << endl; + for (auto val : map.valueSet()) { + cout << val << endl; + } + + return 0; +} diff --git a/en/codes/cpp/chapter_hashing/built_in_hash.cpp b/en/codes/cpp/chapter_hashing/built_in_hash.cpp new file mode 100644 index 000000000..8a3d3c51d --- /dev/null +++ b/en/codes/cpp/chapter_hashing/built_in_hash.cpp @@ -0,0 +1,29 @@ +/** + * File: built_in_hash.cpp + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Driver Code */ +int main() { + int num = 3; + size_t hashNum = hash()(num); + cout << "The hash value of integer " << num << " is " << hashNum << "\n"; + + bool bol = true; + size_t hashBol = hash()(bol); + cout << "The hash value of boolean " << bol << " is " << hashBol << "\n"; + + double dec = 3.14159; + size_t hashDec = hash()(dec); + cout << "The hash value of decimal " << dec << " is " << hashDec << "\n"; + + string str = "Hello algorithm"; + size_t hashStr = hash()(str); + cout << "The hash value of string " << str << " is " << hashStr << "\n"; + + // In C++, the built-in std:hash() only provides hash values for basic data types + // Hash value calculation for arrays and objects must be implemented manually +} diff --git a/en/codes/cpp/chapter_hashing/hash_map.cpp b/en/codes/cpp/chapter_hashing/hash_map.cpp new file mode 100644 index 000000000..53c149be8 --- /dev/null +++ b/en/codes/cpp/chapter_hashing/hash_map.cpp @@ -0,0 +1,46 @@ +/** + * File: hash_map.cpp + * Created Time: 2022-12-14 + * Author: msk397 (machangxinq@gmail.com) + */ + +#include "../utils/common.hpp" + +/* Driver Code */ +int main() { + /* Initialize hash table */ + unordered_map map; + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map[12836] = "Ha"; + map[15937] = "Luo"; + map[16750] = "Suan"; + map[13276] = "Fa"; + map[10583] = "Ya"; + cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + printHashMap(map); + + /* Query operation */ + // Enter key to the hash table, get value + string name = map[15937]; + cout << "\nEnter student ID 15937, found name " << name << endl; + + /* Remove operation */ + // Remove key-value pair (key, value) from the hash table + map.erase(10583); + cout << "\nAfter removing 10583, the hash table is\nKey -> Value" << endl; + printHashMap(map); + + /* Traverse hash table */ + cout << "\nTraverse key-value pairs Key->Value" << endl; + for (auto kv : map) { + cout << kv.first << " -> " << kv.second << endl; + } + cout << "\nIterate through Key->Value using an iterator" << endl; + for (auto iter = map.begin(); iter != map.end(); iter++) { + cout << iter->first << "->" << iter->second << endl; + } + + return 0; +} diff --git a/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp b/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp new file mode 100644 index 000000000..7a903958a --- /dev/null +++ b/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp @@ -0,0 +1,150 @@ +/** + * File: hash_map_chaining.cpp + * Created Time: 2023-06-13 + * Author: krahets (krahets@163.com) + */ + +#include "./array_hash_map.cpp" + +/* Chained address hash table */ +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 + vector> buckets; // Bucket array + + public: + /* Constructor */ + HashMapChaining() : size(0), capacity(4), loadThres(2.0 / 3.0), extendRatio(2) { + buckets.resize(capacity); + } + + /* Destructor */ + ~HashMapChaining() { + for (auto &bucket : buckets) { + for (Pair *pair : bucket) { + // Free memory + delete pair; + } + } + } + + /* Hash function */ + int hashFunc(int key) { + return key % capacity; + } + + /* Load factor */ + double loadFactor() { + return (double)size / (double)capacity; + } + + /* Query operation */ + string get(int key) { + int index = hashFunc(key); + // Traverse the bucket, if the key is found, return the corresponding val + for (Pair *pair : buckets[index]) { + if (pair->key == key) { + return pair->val; + } + } + // If key not found, return an empty string + return ""; + } + + /* Add operation */ + void put(int key, string val) { + // When the load factor exceeds the threshold, perform expansion + if (loadFactor() > loadThres) { + extend(); + } + int index = hashFunc(key); + // Traverse the bucket, if the specified key is encountered, update the corresponding val and return + for (Pair *pair : buckets[index]) { + if (pair->key == key) { + pair->val = val; + return; + } + } + // If the key is not found, add the key-value pair to the end + buckets[index].push_back(new Pair(key, val)); + size++; + } + + /* Remove operation */ + void remove(int key) { + int index = hashFunc(key); + auto &bucket = buckets[index]; + // Traverse the bucket, remove the key-value pair from it + 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 + delete tmp; // Free memory + size--; + return; + } + } + } + + /* Extend hash table */ + void extend() { + // Temporarily store the original hash table + vector> bucketsTmp = buckets; + // Initialize the extended new hash table + capacity *= extendRatio; + buckets.clear(); + buckets.resize(capacity); + size = 0; + // Move key-value pairs from the original hash table to the new hash table + for (auto &bucket : bucketsTmp) { + for (Pair *pair : bucket) { + put(pair->key, pair->val); + // Free memory + delete pair; + } + } + } + + /* Print hash table */ + void print() { + for (auto &bucket : buckets) { + cout << "["; + for (Pair *pair : bucket) { + cout << pair->key << " -> " << pair->val << ", "; + } + cout << "]\n"; + } + } +}; + +/* Driver Code */ +int main() { + /* Initialize hash table */ + HashMapChaining map = HashMapChaining(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Ha"); + map.put(15937, "Luo"); + map.put(16750, "Suan"); + map.put(13276, "Fa"); + map.put(10583, "Ya"); + cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + map.print(); + + /* Query operation */ + // Enter key to the hash table, get value + string name = map.get(13276); + cout << "\nEnter student ID 13276, found name " << name << endl; + + /* Remove operation */ + // Remove key-value pair (key, value) from the hash table + map.remove(12836); + cout << "\nAfter removing 12836, the hash table is\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 new file mode 100644 index 000000000..a66233ad9 --- /dev/null +++ b/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp @@ -0,0 +1,171 @@ +/** + * File: hash_map_open_addressing.cpp + * Created Time: 2023-06-13 + * Author: krahets (krahets@163.com) + */ + +#include "./array_hash_map.cpp" + +/* Open addressing hash table */ +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 + vector buckets; // Bucket array + Pair *TOMBSTONE = new Pair(-1, "-1"); // Removal mark + + public: + /* Constructor */ + HashMapOpenAddressing() : size(0), buckets(capacity, nullptr) { + } + + /* Destructor */ + ~HashMapOpenAddressing() { + for (Pair *pair : buckets) { + if (pair != nullptr && pair != TOMBSTONE) { + delete pair; + } + } + delete TOMBSTONE; + } + + /* Hash function */ + int hashFunc(int key) { + return key % capacity; + } + + /* Load factor */ + double loadFactor() { + return (double)size / capacity; + } + + /* Search for the bucket index corresponding to key */ + int findBucket(int key) { + int index = hashFunc(key); + int firstTombstone = -1; + // Linear probing, break when encountering an empty bucket + while (buckets[index] != nullptr) { + // If the key is encountered, return the corresponding bucket index + if (buckets[index]->key == key) { + // If a removal mark 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 index; // Return bucket index + } + // Record the first encountered removal mark + if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { + firstTombstone = index; + } + // Calculate the bucket index, return to the head if exceeding the tail + index = (index + 1) % capacity; + } + // If the key does not exist, return the index of the insertion point + return firstTombstone == -1 ? index : firstTombstone; + } + + /* Query operation */ + string get(int key) { + // Search for the bucket index corresponding to key + int index = findBucket(key); + // If the key-value pair is found, return the corresponding val + if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { + return buckets[index]->val; + } + // If key-value pair does not exist, return an empty string + return ""; + } + + /* Add operation */ + void put(int key, string val) { + // When the load factor exceeds the threshold, perform expansion + if (loadFactor() > loadThres) { + extend(); + } + // Search for the bucket index corresponding to key + int index = findBucket(key); + // If the key-value pair is found, overwrite val and return + if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { + buckets[index]->val = val; + return; + } + // If the key-value pair does not exist, add the key-value pair + buckets[index] = new Pair(key, val); + size++; + } + + /* Remove operation */ + void remove(int key) { + // Search for the bucket index corresponding to key + int index = findBucket(key); + // If the key-value pair is found, cover it with a removal mark + if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { + delete buckets[index]; + buckets[index] = TOMBSTONE; + size--; + } + } + + /* Extend hash table */ + void extend() { + // Temporarily store the original hash table + vector bucketsTmp = buckets; + // Initialize the extended new hash table + capacity *= extendRatio; + buckets = vector(capacity, nullptr); + size = 0; + // Move key-value pairs from the original hash table to the new hash table + for (Pair *pair : bucketsTmp) { + if (pair != nullptr && pair != TOMBSTONE) { + put(pair->key, pair->val); + delete pair; + } + } + } + + /* Print hash table */ + void print() { + for (Pair *pair : buckets) { + if (pair == nullptr) { + cout << "nullptr" << endl; + } else if (pair == TOMBSTONE) { + cout << "TOMBSTONE" << endl; + } else { + cout << pair->key << " -> " << pair->val << endl; + } + } + } +}; + +/* Driver Code */ +int main() { + // Initialize hash table + HashMapOpenAddressing hashmap; + + // Add operation + // Add key-value pair (key, val) to the hash table + hashmap.put(12836, "Ha"); + hashmap.put(15937, "Luo"); + hashmap.put(16750, "Suan"); + hashmap.put(13276, "Fa"); + hashmap.put(10583, "Ya"); + cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + hashmap.print(); + + // Query operation + // Enter key to the hash table, get value val + string name = hashmap.get(13276); + cout << "\nEnter student ID 13276, found name " << name << endl; + + // Remove operation + // Remove key-value pair (key, val) from the hash table + hashmap.remove(16750); + cout << "\nAfter removing 16750, the hash table is\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 new file mode 100644 index 000000000..1cfe03fab --- /dev/null +++ b/en/codes/cpp/chapter_hashing/simple_hash.cpp @@ -0,0 +1,66 @@ +/** + * File: simple_hash.cpp + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Additive hash */ +int addHash(string key) { + long long hash = 0; + const int MODULUS = 1000000007; + for (unsigned char c : key) { + hash = (hash + (int)c) % MODULUS; + } + return (int)hash; +} + +/* Multiplicative hash */ +int mulHash(string key) { + long long hash = 0; + const int MODULUS = 1000000007; + for (unsigned char c : key) { + hash = (31 * hash + (int)c) % MODULUS; + } + return (int)hash; +} + +/* XOR hash */ +int xorHash(string key) { + int hash = 0; + const int MODULUS = 1000000007; + for (unsigned char c : key) { + hash ^= (int)c; + } + return hash & MODULUS; +} + +/* Rotational hash */ +int rotHash(string key) { + long long hash = 0; + const int MODULUS = 1000000007; + for (unsigned char c : key) { + hash = ((hash << 4) ^ (hash >> 28) ^ (int)c) % MODULUS; + } + return (int)hash; +} + +/* Driver Code */ +int main() { + string key = "Hello algorithm"; + + int hash = addHash(key); + cout << "Additive hash value is " << hash << endl; + + hash = mulHash(key); + cout << "Multiplicative hash value is " << hash << endl; + + hash = xorHash(key); + cout << "XOR hash value is " << hash << endl; + + hash = rotHash(key); + cout << "Rotational hash value is " << hash << endl; + + return 0; +} diff --git a/en/codes/cpp/chapter_heap/CMakeLists.txt b/en/codes/cpp/chapter_heap/CMakeLists.txt new file mode 100644 index 000000000..1ac33a44f --- /dev/null +++ b/en/codes/cpp/chapter_heap/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(heap heap.cpp) +add_executable(my_heap my_heap.cpp) +add_executable(top_k top_k.cpp) diff --git a/en/codes/cpp/chapter_heap/heap.cpp b/en/codes/cpp/chapter_heap/heap.cpp new file mode 100644 index 000000000..a87d39e4a --- /dev/null +++ b/en/codes/cpp/chapter_heap/heap.cpp @@ -0,0 +1,66 @@ +/** + * File: heap.cpp + * Created Time: 2023-01-19 + * Author: LoneRanger(836253168@qq.com) + */ + +#include "../utils/common.hpp" + +void testPush(priority_queue &heap, int val) { + heap.push(val); // Push the element into heap + cout << "\nAfter element " << val << " is added to the heap" << endl; + printHeap(heap); +} + +void testPop(priority_queue &heap) { + int val = heap.top(); + heap.pop(); + cout << "\nAfter the top element " << val << " is removed from the heap" << endl; + printHeap(heap); +} + +/* Driver Code */ +int main() { + /* Initialize the heap */ + // Initialize min-heap + // priority_queue, greater> minHeap; + // Initialize max-heap + priority_queue, less> maxHeap; + + cout << "\nThe following test case is for max-heap" << endl; + + /* Push the element into heap */ + testPush(maxHeap, 1); + testPush(maxHeap, 3); + testPush(maxHeap, 2); + testPush(maxHeap, 5); + testPush(maxHeap, 4); + + /* Access heap top element */ + int peek = maxHeap.top(); + cout << "\nTop element of the heap is " << peek << endl; + + /* Pop the element at the heap top */ + testPop(maxHeap); + testPop(maxHeap); + testPop(maxHeap); + testPop(maxHeap); + testPop(maxHeap); + + /* Get heap size */ + int size = maxHeap.size(); + cout << "\nNumber of elements in the heap is " << size << endl; + + /* Determine if heap is empty */ + bool isEmpty = maxHeap.empty(); + cout << "\nIs the heap empty " << isEmpty << endl; + + /* Enter list and build heap */ + // 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 inputting the list and building a min-heap" << 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 new file mode 100644 index 000000000..92ee6e1ca --- /dev/null +++ b/en/codes/cpp/chapter_heap/my_heap.cpp @@ -0,0 +1,155 @@ +/** + * File: my_heap.cpp + * Created Time: 2023-02-04 + * Author: LoneRanger (836253168@qq.com), what-is-me (whatisme@outlook.jp) + */ + +#include "../utils/common.hpp" + +/* Max-heap */ +class MaxHeap { + private: + // Using a dynamic array to avoid the need for resizing + vector maxHeap; + + /* Get index of left child node */ + int left(int i) { + return 2 * i + 1; + } + + /* Get index of right child node */ + int right(int i) { + return 2 * i + 2; + } + + /* Get index of parent node */ + int parent(int i) { + return (i - 1) / 2; // Integer division down + } + + /* Start heapifying node i, from bottom to top */ + void siftUp(int i) { + while (true) { + // Get parent node of node i + int p = parent(i); + // When "crossing the root node" or "node does not need repair", end heapification + if (p < 0 || maxHeap[i] <= maxHeap[p]) + break; + // Swap two nodes + swap(maxHeap[i], maxHeap[p]); + // Loop upwards heapification + i = p; + } + } + + /* Start heapifying node i, from top to bottom */ + void siftDown(int i) { + while (true) { + // Determine the largest node among i, l, r, noted as 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 the largest or indices l, r are out of bounds, no further heapification needed, break + if (ma == i) + break; + swap(maxHeap[i], maxHeap[ma]); + // Loop downwards heapification + i = ma; + } + } + + public: + /* Constructor, build heap based on input list */ + MaxHeap(vector nums) { + // Add all list elements into the heap + maxHeap = nums; + // Heapify all nodes except leaves + for (int i = parent(size() - 1); i >= 0; i--) { + siftDown(i); + } + } + + /* Get heap size */ + int size() { + return maxHeap.size(); + } + + /* Determine if heap is empty */ + bool isEmpty() { + return size() == 0; + } + + /* Access heap top element */ + int peek() { + return maxHeap[0]; + } + + /* Push the element into heap */ + void push(int val) { + // Add node + maxHeap.push_back(val); + // Heapify from bottom to top + siftUp(size() - 1); + } + + /* Element exits heap */ + void pop() { + // Empty handling + if (isEmpty()) { + throw out_of_range("Heap is empty"); + } + // Swap the root node with the rightmost leaf node (swap the first element with the last element) + swap(maxHeap[0], maxHeap[size() - 1]); + // Remove node + maxHeap.pop_back(); + // Heapify from top to bottom + siftDown(0); + } + + /* Print heap (binary tree)*/ + void print() { + cout << "Array representation of the heap:"; + printVector(maxHeap); + cout << "Tree representation of the heap:" << endl; + TreeNode *root = vectorToTree(maxHeap); + printTree(root); + freeMemoryTree(root); + } +}; + +/* Driver Code */ +int main() { + /* Initialize max-heap */ + vector vec{9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; + MaxHeap maxHeap(vec); + cout << "\nEnter list and build heap" << endl; + maxHeap.print(); + + /* Access heap top element */ + int peek = maxHeap.peek(); + cout << "\nTop element of the heap is " << peek << endl; + + /* Push the element into heap */ + int val = 7; + maxHeap.push(val); + cout << "\nAfter element " << val << " is added to the heap" << endl; + maxHeap.print(); + + /* Pop the element at the heap top */ + peek = maxHeap.peek(); + maxHeap.pop(); + cout << "\nAfter the top element " << peek << " is removed from the heap" << endl; + maxHeap.print(); + + /* Get heap size */ + int size = maxHeap.size(); + cout << "\nNumber of elements in the heap is " << size << endl; + + /* Determine if heap is empty */ + bool isEmpty = maxHeap.isEmpty(); + 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 new file mode 100644 index 000000000..a95de446a --- /dev/null +++ b/en/codes/cpp/chapter_heap/top_k.cpp @@ -0,0 +1,38 @@ +/** + * File: top_k.cpp + * Created Time: 2023-06-12 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Using heap to find the largest k elements in an array */ +priority_queue, greater> topKHeap(vector &nums, int k) { + // Initialize min-heap + priority_queue, greater> heap; + // Enter the first k elements of the array into the heap + for (int i = 0; i < k; i++) { + heap.push(nums[i]); + } + // From the k+1th element, keep the heap length as k + for (int i = k; i < nums.size(); i++) { + // If the current element is larger than the heap top element, remove the heap top element and enter the current element into the heap + if (nums[i] > heap.top()) { + heap.pop(); + heap.push(nums[i]); + } + } + return heap; +} + +// Driver Code +int main() { + vector nums = {1, 7, 6, 3, 2}; + int k = 3; + + priority_queue, greater> res = topKHeap(nums, k); + cout << "The largest " << k << " elements are:"; + printHeap(res); + + return 0; +} diff --git a/en/codes/cpp/chapter_searching/CMakeLists.txt b/en/codes/cpp/chapter_searching/CMakeLists.txt new file mode 100644 index 000000000..60a223d83 --- /dev/null +++ b/en/codes/cpp/chapter_searching/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(binary_search binary_search.cpp) +add_executable(binary_search_insertion binary_search_insertion.cpp) +add_executable(binary_search_edge binary_search_edge.cpp) +add_executable(two_sum two_sum.cpp) diff --git a/en/codes/cpp/chapter_searching/binary_search.cpp b/en/codes/cpp/chapter_searching/binary_search.cpp new file mode 100644 index 000000000..cfa143eb0 --- /dev/null +++ b/en/codes/cpp/chapter_searching/binary_search.cpp @@ -0,0 +1,59 @@ +/** + * File: binary_search.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Binary search (double closed interval) */ +int binarySearch(vector &nums, int target) { + // Initialize double closed interval [0, n-1], i.e., i, j point to the first element and last element of the array respectively + int i = 0, j = nums.size() - 1; + // Loop until the search interval is empty (when i > j, it is empty) + while (i <= j) { + int m = i + (j - i) / 2; // Calculate midpoint index m + if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j] + i = m + 1; + else if (nums[m] > target) // This situation indicates that target is in the interval [i, m-1] + j = m - 1; + else // Found the target element, thus return its index + return m; + } + // Did not find the target element, thus return -1 + return -1; +} + +/* 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 the last element +1 of the array respectively + int i = 0, j = nums.size(); + // Loop until the search interval is empty (when i = j, it is empty) + while (i < j) { + int m = i + (j - i) / 2; // Calculate midpoint index m + if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j) + i = m + 1; + else if (nums[m] > target) // This situation indicates that target is in the interval [i, m) + j = m; + else // Found the target element, thus return its index + return m; + } + // Did not find the target element, thus return -1 + return -1; +} + +/* Driver Code */ +int main() { + int target = 6; + vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; + + /* Binary search (double closed interval) */ + int index = binarySearch(nums, target); + cout << "Index of target element 6 =" << index << endl; + + /* Binary search (left closed right open interval) */ + index = binarySearchLCRO(nums, target); + cout << "Index of target element 6 =" << index << endl; + + return 0; +} diff --git a/en/codes/cpp/chapter_searching/binary_search_edge.cpp b/en/codes/cpp/chapter_searching/binary_search_edge.cpp new file mode 100644 index 000000000..1960fda2d --- /dev/null +++ b/en/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -0,0 +1,66 @@ +/** + * File: binary_search_edge.cpp + * Created Time: 2023-08-04 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Binary search for insertion point (with duplicate elements) */ +int binarySearchInsertion(const vector &nums, int target) { + int i = 0, j = nums.size() - 1; // Initialize double closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate midpoint index m + if (nums[m] < target) { + i = m + 1; // Target is in interval [m+1, j] + } else { + j = m - 1; // First element less than target is in interval [i, m-1] + } + } + // Return insertion point i + return i; +} + +/* Binary search for the leftmost target */ +int binarySearchLeftEdge(vector &nums, int target) { + // Equivalent to finding the insertion point of target + int i = binarySearchInsertion(nums, target); + // Did not find target, thus return -1 + if (i == nums.size() || nums[i] != target) { + return -1; + } + // Found target, return index i + return i; +} + +/* Binary search for the rightmost target */ +int binarySearchRightEdge(vector &nums, int target) { + // Convert to finding the leftmost target + 1 + int i = binarySearchInsertion(nums, target + 1); + // j points to the rightmost target, i points to the first element greater than target + int j = i - 1; + // Did not find target, thus return -1 + if (j == -1 || nums[j] != target) { + return -1; + } + // Found target, return index j + return j; +} + +/* Driver Code */ +int main() { + // Array with duplicate elements + vector nums = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; + cout << "\nArray nums = "; + printVector(nums); + + // Binary search for left and right boundaries + for (int target : {6, 7}) { + int index = binarySearchLeftEdge(nums, target); + cout << "The leftmost index of element " << target << " is " << index << endl; + index = binarySearchRightEdge(nums, target); + cout << "The rightmost index of element " << target << " is " << 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 new file mode 100644 index 000000000..b8b56e8a3 --- /dev/null +++ b/en/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -0,0 +1,66 @@ +/** + * File: binary_search_insertion.cpp + * Created Time: 2023-08-04 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Binary search for insertion point (no duplicate elements) */ +int binarySearchInsertionSimple(vector &nums, int target) { + int i = 0, j = nums.size() - 1; // Initialize double closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate midpoint index m + if (nums[m] < target) { + i = m + 1; // Target is in interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // Target is in interval [i, m-1] + } else { + return m; // Found target, return insertion point m + } + } + // Did not find target, return insertion point i + return i; +} + +/* Binary search for insertion point (with duplicate elements) */ +int binarySearchInsertion(vector &nums, int target) { + int i = 0, j = nums.size() - 1; // Initialize double closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate midpoint index m + if (nums[m] < target) { + i = m + 1; // Target is in interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // Target is in interval [i, m-1] + } else { + j = m - 1; // First element less than target is in interval [i, m-1] + } + } + // Return insertion point i + return i; +} + +/* Driver Code */ +int main() { + // Array without duplicate elements + vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; + cout << "\nArray nums = "; + printVector(nums); + // Binary search for insertion point + for (int target : {6, 9}) { + int index = binarySearchInsertionSimple(nums, target); + cout << "The insertion point index for element " << target << " is " << index << endl; + } + + // Array with duplicate elements + nums = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; + cout << "\nArray nums = "; + printVector(nums); + // Binary search for insertion point + for (int target : {2, 6, 20}) { + int index = binarySearchInsertion(nums, target); + cout << "The insertion point index for element " << target << " is " << index << endl; + } + + return 0; +} diff --git a/en/codes/cpp/chapter_searching/hashing_search.cpp b/en/codes/cpp/chapter_searching/hashing_search.cpp new file mode 100644 index 000000000..ad2a9fcf7 --- /dev/null +++ b/en/codes/cpp/chapter_searching/hashing_search.cpp @@ -0,0 +1,53 @@ +/** + * File: hashing_search.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Hash search (array) */ +int hashingSearchArray(unordered_map map, int target) { + // Hash table's key: target element, value: index + // If the hash table does not contain this key, return -1 + if (map.find(target) == map.end()) + return -1; + return map[target]; +} + +/* Hash search (linked list) */ +ListNode *hashingSearchLinkedList(unordered_map map, int target) { + // Hash table key: target node value, value: node object + // If the key is not in the hash table, return nullptr + if (map.find(target) == map.end()) + return nullptr; + return map[target]; +} + +/* Driver Code */ +int main() { + int target = 3; + + /* Hash search (array) */ + vector nums = {1, 5, 3, 2, 4, 7, 5, 9, 10, 8}; + // Initialize hash table + unordered_map map; + for (int i = 0; i < nums.size(); i++) { + map[nums[i]] = i; // key: element, value: index + } + int index = hashingSearchArray(map, target); + cout << "The index of target element 3 is " << index << endl; + + /* Hash search (linked list) */ + ListNode *head = vecToLinkedList(nums); + // Initialize hash table + unordered_map map1; + while (head != nullptr) { + map1[head->val] = head; // key: node value, value: node + head = head->next; + } + ListNode *node = hashingSearchLinkedList(map1, target); + cout << "The corresponding node object for target node 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 new file mode 100644 index 000000000..fca7d2588 --- /dev/null +++ b/en/codes/cpp/chapter_searching/linear_search.cpp @@ -0,0 +1,49 @@ +/** + * File: linear_search.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Linear search (array) */ +int linearSearchArray(vector &nums, int target) { + // Traverse array + for (int i = 0; i < nums.size(); i++) { + // Found the target element, thus return its index + if (nums[i] == target) + return i; + } + // Did not find the target element, thus return -1 + return -1; +} + +/* Linear search (linked list) */ +ListNode *linearSearchLinkedList(ListNode *head, int target) { + // Traverse the list + while (head != nullptr) { + // Found the target node, return it + if (head->val == target) + return head; + head = head->next; + } + // If the target node is not found, return nullptr + return nullptr; +} + +/* Driver Code */ +int main() { + int target = 3; + + /* Perform linear search in array */ + vector nums = {1, 5, 3, 2, 4, 7, 5, 9, 10, 8}; + int index = linearSearchArray(nums, target); + cout << "The index of target element 3 is " << index << endl; + + /* Perform linear search in linked list */ + ListNode *head = vecToLinkedList(nums); + ListNode *node = linearSearchLinkedList(head, target); + cout << "The corresponding node object for target node 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 new file mode 100644 index 000000000..8aa22edc9 --- /dev/null +++ b/en/codes/cpp/chapter_searching/two_sum.cpp @@ -0,0 +1,54 @@ +/** + * File: two_sum.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Method one: Brute force enumeration */ +vector twoSumBruteForce(vector &nums, int target) { + int size = nums.size(); + // Two-layer loop, time complexity is 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) + return {i, j}; + } + } + return {}; +} + +/* Method two: Auxiliary hash table */ +vector twoSumHashTable(vector &nums, int target) { + int size = nums.size(); + // Auxiliary hash table, space complexity is O(n) + unordered_map dic; + // Single-layer loop, time complexity is O(n) + for (int i = 0; i < size; i++) { + if (dic.find(target - nums[i]) != dic.end()) { + return {dic[target - nums[i]], i}; + } + dic.emplace(nums[i], i); + } + return {}; +} + +/* Driver Code */ +int main() { + // ======= Test Case ======= + vector nums = {2, 7, 11, 15}; + int target = 13; + + // ====== Driver Code ====== + // Method one + vector res = twoSumBruteForce(nums, target); + cout << "Method one res = "; + printVector(res); + // Method two + res = twoSumHashTable(nums, target); + cout << "Method two res = "; + printVector(res); + + return 0; +} diff --git a/en/codes/cpp/chapter_sorting/CMakeLists.txt b/en/codes/cpp/chapter_sorting/CMakeLists.txt new file mode 100644 index 000000000..e6347cf9f --- /dev/null +++ b/en/codes/cpp/chapter_sorting/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(selection_sort selection_sort.cpp) +add_executable(bubble_sort bubble_sort.cpp) +add_executable(insertion_sort insertion_sort.cpp) +add_executable(merge_sort merge_sort.cpp) +add_executable(quick_sort quick_sort.cpp) +add_executable(heap_sort heap_sort.cpp) \ No newline at end of file diff --git a/en/codes/cpp/chapter_sorting/bubble_sort.cpp b/en/codes/cpp/chapter_sorting/bubble_sort.cpp new file mode 100644 index 000000000..490774b36 --- /dev/null +++ b/en/codes/cpp/chapter_sorting/bubble_sort.cpp @@ -0,0 +1,56 @@ +/** + * File: bubble_sort.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Bubble sort */ +void bubbleSort(vector &nums) { + // Outer loop: 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 right end of the range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + // Here, the std + swap(nums[j], nums[j + 1]); + } + } + } +} + +/* Bubble sort (optimized with flag)*/ +void bubbleSortWithFlag(vector &nums) { + // Outer loop: 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 right end of the range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + // Here, the std + swap(nums[j], nums[j + 1]); + flag = true; // Record swapped elements + } + } + if (!flag) + break; // If no elements were swapped in this round of "bubbling", exit + } +} + +/* Driver Code */ +int main() { + vector nums = {4, 1, 3, 1, 5, 2}; + bubbleSort(nums); + cout << "After bubble sort, nums = "; + printVector(nums); + + vector nums1 = {4, 1, 3, 1, 5, 2}; + bubbleSortWithFlag(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 new file mode 100644 index 000000000..eb1713ddf --- /dev/null +++ b/en/codes/cpp/chapter_sorting/bucket_sort.cpp @@ -0,0 +1,44 @@ +/** + * File: bucket_sort.cpp + * Created Time: 2023-03-30 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Bucket sort */ +void bucketSort(vector &nums) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + int k = nums.size() / 2; + vector> buckets(k); + // 1. Distribute array elements into various buckets + for (float num : nums) { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + int i = num * k; + // Add number to bucket_idx + 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 + sort(bucket.begin(), bucket.end()); + } + // 3. Traverse buckets to merge results + int i = 0; + for (vector &bucket : buckets) { + for (float num : bucket) { + nums[i++] = num; + } + } +} + +/* Driver Code */ +int main() { + // Assume input data is floating point, range [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, 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 new file mode 100644 index 000000000..e538f28d0 --- /dev/null +++ b/en/codes/cpp/chapter_sorting/counting_sort.cpp @@ -0,0 +1,77 @@ +/** + * File: counting_sort.cpp + * Created Time: 2023-03-17 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +void countingSortNaive(vector &nums) { + // 1. Count the maximum element m in the array + int m = 0; + for (int num : nums) { + m = max(m, num); + } + // 2. Count the occurrence of each digit + // counter[num] represents the occurrence 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 + int i = 0; + for (int num = 0; num < m + 1; num++) { + for (int j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +void countingSort(vector &nums) { + // 1. Count the maximum element m in the array + int m = 0; + for (int num : nums) { + m = max(m, num); + } + // 2. Count the occurrence of each digit + // counter[num] represents the occurrence 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 + 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 + 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 + } + // Use result array res to overwrite the original array nums + nums = res; +} + +/* Driver Code */ +int main() { + vector nums = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; + countingSortNaive(nums); + cout << "After count sort (unable to sort objects), nums = "; + printVector(nums); + + vector nums1 = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; + countingSort(nums1); + cout << "After count 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 new file mode 100644 index 000000000..d2527db81 --- /dev/null +++ b/en/codes/cpp/chapter_sorting/heap_sort.cpp @@ -0,0 +1,54 @@ +/** + * File: heap_sort.cpp + * Created Time: 2023-05-26 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Heap length is n, start heapifying node i, from top to bottom */ +void siftDown(vector &nums, int n, int i) { + while (true) { + // Determine the largest node among i, l, r, noted as 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; + // If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + if (ma == i) { + break; + } + // Swap two nodes + swap(nums[i], nums[ma]); + // Loop downwards heapification + i = ma; + } +} + +/* Heap sort */ +void heapSort(vector &nums) { + // Build heap operation: heapify all nodes except leaves + 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 + for (int i = nums.size() - 1; i > 0; --i) { + // Swap the root node with the rightmost leaf node (swap the first element with the last element) + swap(nums[0], nums[i]); + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0); + } +} + +/* Driver Code */ +int main() { + vector nums = {4, 1, 3, 1, 5, 2}; + heapSort(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 new file mode 100644 index 000000000..2ad2fe1ab --- /dev/null +++ b/en/codes/cpp/chapter_sorting/insertion_sort.cpp @@ -0,0 +1,31 @@ +/** + * File: insertion_sort.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Insertion sort */ +void insertionSort(vector &nums) { + // Outer loop: 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 range [0, i-1] + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + j--; + } + nums[j + 1] = base; // Assign base to the correct position + } +} + +/* Driver Code */ +int main() { + vector nums = {4, 1, 3, 1, 5, 2}; + insertionSort(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 new file mode 100644 index 000000000..dbd45dc8a --- /dev/null +++ b/en/codes/cpp/chapter_sorting/merge_sort.cpp @@ -0,0 +1,58 @@ +/** + * File: merge_sort.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Merge left subarray and right subarray */ +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 + vector tmp(right - left + 1); + // Initialize the start 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 (i <= mid && j <= right) { + if (nums[i] <= nums[j]) + tmp[k++] = nums[i++]; + else + tmp[k++] = nums[j++]; + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while (i <= mid) { + tmp[k++] = nums[i++]; + } + 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 + for (k = 0; k < tmp.size(); k++) { + nums[left + k] = tmp[k]; + } +} + +/* Merge sort */ +void mergeSort(vector &nums, int left, int right) { + // Termination condition + if (left >= right) + return; // Terminate recursion when subarray length is 1 + // Partition stage + int mid = (left + right) / 2; // Calculate midpoint + mergeSort(nums, left, mid); // Recursively process the left subarray + mergeSort(nums, mid + 1, right); // Recursively process the right subarray + // Merge stage + merge(nums, left, mid, right); +} + +/* 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, 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 new file mode 100644 index 000000000..0a7be7ec8 --- /dev/null +++ b/en/codes/cpp/chapter_sorting/quick_sort.cpp @@ -0,0 +1,166 @@ +/** + * File: quick_sort.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Quick sort class */ +class QuickSort { + private: + /* Swap elements */ + static void swap(vector &nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Partition */ + 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 + while (i < j && nums[i] <= nums[left]) + i++; // Search from left to right for the first element greater 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 + } + + public: + /* Quick sort */ + static void quickSort(vector &nums, int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) + return; + // Partition + int pivot = partition(nums, left, right); + // Recursively process the left subarray and right subarray + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +/* Quick sort class (median pivot optimization) */ +class QuickSortMedian { + private: + /* Swap elements */ + static void swap(vector &nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Select the median of three candidate elements */ + 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 + if ((m <= l && l <= r) || (r <= l && l <= m)) + return left; // l is between m and r + return right; + } + + /* Partition (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(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 + 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 + } + + public: + /* Quick sort */ + static void quickSort(vector &nums, int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) + return; + // Partition + int pivot = partition(nums, left, right); + // Recursively process the left subarray and right subarray + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +/* Quick sort class (tail recursion optimization) */ +class QuickSortTailCall { + private: + /* Swap elements */ + static void swap(vector &nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Partition */ + 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 + while (i < j && nums[i] <= nums[left]) + i++; // Search from left to right for the first element greater 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 + } + + public: + /* Quick sort (tail recursion optimization) */ + static void quickSort(vector &nums, int left, int right) { + // Terminate when subarray length is 1 + while (left < right) { + // Partition operation + int pivot = partition(nums, left, right); + // Perform quick sort on 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] + } else { + quickSort(nums, pivot + 1, right); // Recursively sort the right subarray + right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + } + } + } +}; + +/* 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, nums = "; + printVector(nums); + + /* Quick sort (median pivot optimization) */ + vector nums1 = {2, 4, 1, 0, 3, 5}; + QuickSortMedian::quickSort(nums1, 0, nums1.size() - 1); + cout << "Quick sort (median pivot optimization) completed, nums = "; + printVector(nums1); + + /* Quick sort (tail recursion optimization) */ + vector nums2 = {2, 4, 1, 0, 3, 5}; + QuickSortTailCall::quickSort(nums2, 0, nums2.size() - 1); + cout << "Quick sort (tail recursion optimization) completed, nums = "; + printVector(nums2); + + return 0; +} diff --git a/en/codes/cpp/chapter_sorting/radix_sort.cpp b/en/codes/cpp/chapter_sorting/radix_sort.cpp new file mode 100644 index 000000000..e388f36f1 --- /dev/null +++ b/en/codes/cpp/chapter_sorting/radix_sort.cpp @@ -0,0 +1,65 @@ +/** + * File: radix_sort.cpp + * Created Time: 2023-03-26 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +int digit(int num, int exp) { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return (num / exp) % 10; +} + +/* Counting sort (based on nums k-th digit) */ +void countingSortDigit(vector &nums, int exp) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + vector counter(10, 0); + int n = nums.size(); + // Count the occurrence of digits 0~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 + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for (int i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // Traverse in reverse, based on bucket statistics, place each element into res + 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 + res[j] = nums[i]; // Place the current element at index j + counter[d]--; // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + 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 + int m = *max_element(nums.begin(), nums.end()); + // Traverse from the lowest to the highest digit + for (int exp = 1; exp <= m; exp *= 10) + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums, exp); +} + +/* Driver Code */ +int main() { + // Radix sort + vector nums = {10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996}; + radixSort(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 new file mode 100644 index 000000000..2504e3a25 --- /dev/null +++ b/en/codes/cpp/chapter_sorting/selection_sort.cpp @@ -0,0 +1,34 @@ +/** + * File: selection_sort.cpp + * Created Time: 2023-05-23 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Selection sort */ +void selectionSort(vector &nums) { + int n = nums.size(); + // Outer loop: unsorted range is [i, n-1] + for (int i = 0; i < n - 1; i++) { + // Inner loop: find the smallest element within 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 range + swap(nums[i], nums[k]); + } +} + +/* Driver Code */ +int main() { + vector nums = {4, 1, 3, 1, 5, 2}; + selectionSort(nums); + + cout << "After selection sort, nums = "; + printVector(nums); + + return 0; +} diff --git a/en/codes/cpp/chapter_stack_and_queue/CMakeLists.txt b/en/codes/cpp/chapter_stack_and_queue/CMakeLists.txt new file mode 100644 index 000000000..b55878a17 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(array_deque array_deque.cpp) +add_executable(array_queue array_queue.cpp) +add_executable(array_stack array_stack.cpp) +add_executable(deque deque.cpp) +add_executable(linkedlist_deque linkedlist_deque.cpp) +add_executable(linkedlist_queue linkedlist_queue.cpp) +add_executable(linkedlist_stack linkedlist_stack.cpp) +add_executable(queue queue.cpp) +add_executable(stack stack.cpp) diff --git a/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp b/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp new file mode 100644 index 000000000..922cbe842 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp @@ -0,0 +1,156 @@ +/** + * File: array_deque.cpp + * Created Time: 2023-03-02 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Double-ended queue class based on circular array */ +class ArrayDeque { + private: + vector nums; // Array used to store elements of the double-ended queue + int front; // Front pointer, pointing to the front element + int queSize; // Length of the double-ended queue + + public: + /* Constructor */ + ArrayDeque(int capacity) { + nums.resize(capacity); + front = queSize = 0; + } + + /* Get the capacity of the double-ended queue */ + int capacity() { + return nums.size(); + } + + /* Get the length of the double-ended queue */ + int size() { + return queSize; + } + + /* Determine if the double-ended queue is empty */ + bool isEmpty() { + return queSize == 0; + } + + /* Calculate circular array index */ + int index(int i) { + // Implement circular array by modulo operation + // When i exceeds the tail of the array, return to the head + // When i exceeds the head of the array, return to the tail + return (i + capacity()) % capacity(); + } + + /* Front enqueue */ + void pushFirst(int num) { + if (queSize == capacity()) { + cout << "Double-ended queue is full" << endl; + return; + } + // Move the front pointer one position to the left + // Implement front crossing the head of the array to return to the tail by modulo operation + front = index(front - 1); + // Add num to the front + nums[front] = num; + queSize++; + } + + /* Rear enqueue */ + void pushLast(int num) { + if (queSize == capacity()) { + cout << "Double-ended queue is full" << endl; + return; + } + // Calculate rear pointer, pointing to rear index + 1 + int rear = index(front + queSize); + // Add num to the rear + nums[rear] = num; + queSize++; + } + + /* Front dequeue */ + int popFirst() { + int num = peekFirst(); + // Move front pointer one position backward + front = index(front + 1); + queSize--; + return num; + } + + /* Rear dequeue */ + int popLast() { + int num = peekLast(); + queSize--; + return num; + } + + /* Access front element */ + int peekFirst() { + if (isEmpty()) + throw out_of_range("Double-ended queue is empty"); + return nums[front]; + } + + /* Access rear element */ + int peekLast() { + if (isEmpty()) + throw out_of_range("Double-ended queue is empty"); + // Calculate rear element index + int last = index(front + queSize - 1); + return nums[last]; + } + + /* Return array for printing */ + vector toVector() { + // Only convert elements within valid length range + vector res(queSize); + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[index(j)]; + } + return res; + } +}; + +/* Driver Code */ +int main() { + /* Initialize double-ended queue */ + ArrayDeque *deque = new ArrayDeque(10); + deque->pushLast(3); + deque->pushLast(2); + deque->pushLast(5); + cout << "Double-ended queue deque = "; + printVector(deque->toVector()); + + /* Access element */ + int peekFirst = deque->peekFirst(); + cout << "Front element peekFirst = " << peekFirst << endl; + int peekLast = deque->peekLast(); + cout << "Back element peekLast = " << peekLast << endl; + + /* Element enqueue */ + deque->pushLast(4); + cout << "Element 4 enqueued at the tail, deque = "; + printVector(deque->toVector()); + deque->pushFirst(1); + cout << "Element 1 enqueued at the head, deque = "; + printVector(deque->toVector()); + + /* Element dequeue */ + int popLast = deque->popLast(); + cout << "Deque tail element = " << popLast << ", after dequeuing from the tail"; + printVector(deque->toVector()); + int popFirst = deque->popFirst(); + cout << "Deque front element = " << popFirst << ", after dequeuing from the front"; + printVector(deque->toVector()); + + /* Get the length of the double-ended queue */ + int size = deque->size(); + cout << "Length of the double-ended queue size = " << size << endl; + + /* Determine if the double-ended queue is empty */ + bool isEmpty = deque->isEmpty(); + cout << "Is the double-ended queue 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 new file mode 100644 index 000000000..d43abbb53 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -0,0 +1,129 @@ +/** + * File: array_queue.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Queue class based on circular array */ +class ArrayQueue { + private: + int *nums; // Array for storing queue elements + int front; // Front pointer, pointing to the front element + int queSize; // Queue length + int queCapacity; // Queue capacity + + public: + ArrayQueue(int capacity) { + // Initialize an array + nums = new int[capacity]; + queCapacity = capacity; + front = queSize = 0; + } + + ~ArrayQueue() { + delete[] nums; + } + + /* Get the capacity of the queue */ + int capacity() { + return queCapacity; + } + + /* Get the length of the queue */ + int size() { + return queSize; + } + + /* Determine if the queue is empty */ + bool isEmpty() { + return size() == 0; + } + + /* Enqueue */ + void push(int num) { + if (queSize == queCapacity) { + cout << "Queue is full" << endl; + return; + } + // Calculate rear pointer, pointing to rear index + 1 + // Use modulo operation to wrap the rear pointer from the end of the array back to the start + int rear = (front + queSize) % queCapacity; + // Add num to the rear + nums[rear] = num; + queSize++; + } + + /* Dequeue */ + int pop() { + int num = peek(); + // Move front pointer one position backward, returning to the head of the array if it exceeds the tail + front = (front + 1) % queCapacity; + queSize--; + return num; + } + + /* Access front element */ + int peek() { + if (isEmpty()) + throw out_of_range("Queue is empty"); + return nums[front]; + } + + /* Convert array to Vector and return */ + vector toVector() { + // Only convert elements within valid length range + vector arr(queSize); + for (int i = 0, j = front; i < queSize; i++, j++) { + arr[i] = nums[j % queCapacity]; + } + return arr; + } +}; + +/* Driver Code */ +int main() { + /* Initialize queue */ + int capacity = 10; + ArrayQueue *queue = new ArrayQueue(capacity); + + /* Element enqueue */ + queue->push(1); + queue->push(3); + queue->push(2); + queue->push(5); + queue->push(4); + cout << "Queue queue = "; + printVector(queue->toVector()); + + /* Access front element */ + int peek = queue->peek(); + cout << "Front element peek = " << peek << endl; + + /* Element dequeue */ + peek = queue->pop(); + cout << "Element dequeued = " << peek << ", after dequeuing"; + printVector(queue->toVector()); + + /* Get the length of the queue */ + int size = queue->size(); + cout << "Length of the queue size = " << size << endl; + + /* Determine if the queue is empty */ + bool empty = queue->isEmpty(); + cout << "Is the queue empty = " << empty << endl; + + /* Test circular array */ + for (int i = 0; i < 10; i++) { + queue->push(i); + queue->pop(); + cout << "After the " << i << "th round of enqueueing + dequeuing, queue = "; + printVector(queue->toVector()); + } + + // Free memory + delete queue; + + return 0; +} diff --git a/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp b/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp new file mode 100644 index 000000000..4f5af36d2 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp @@ -0,0 +1,85 @@ +/** + * File: array_stack.cpp + * Created Time: 2022-11-28 + * Author: qualifier1024 (2539244001@qq.com) + */ + +#include "../utils/common.hpp" + +/* Stack class based on array */ +class ArrayStack { + private: + vector stack; + + public: + /* Get the length of the stack */ + int size() { + return stack.size(); + } + + /* Determine if the stack is empty */ + bool isEmpty() { + return stack.size() == 0; + } + + /* Push */ + void push(int num) { + stack.push_back(num); + } + + /* Pop */ + int pop() { + int num = top(); + stack.pop_back(); + return num; + } + + /* Access stack top element */ + int top() { + if (isEmpty()) + throw out_of_range("Stack is empty"); + return stack.back(); + } + + /* Return Vector */ + vector toVector() { + return stack; + } +}; + +/* Driver Code */ +int main() { + /* Initialize stack */ + ArrayStack *stack = new ArrayStack(); + + /* Element push */ + stack->push(1); + stack->push(3); + stack->push(2); + stack->push(5); + stack->push(4); + cout << "Stack stack = "; + printVector(stack->toVector()); + + /* Access stack top element */ + int top = stack->top(); + cout << "Top element of the stack top = " << top << endl; + + /* Element pop */ + top = stack->pop(); + cout << "Element popped from the stack = " << top << ", after popping"; + printVector(stack->toVector()); + + /* Get the length of the stack */ + int size = stack->size(); + cout << "Length of the stack size = " << size << endl; + + /* Determine if it's empty */ + bool empty = stack->isEmpty(); + cout << "Is the stack empty = " << empty << endl; + + // Free memory + delete stack; + + return 0; +} diff --git a/en/codes/cpp/chapter_stack_and_queue/deque.cpp b/en/codes/cpp/chapter_stack_and_queue/deque.cpp new file mode 100644 index 000000000..f24332ee1 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/deque.cpp @@ -0,0 +1,46 @@ +/** + * File: deque.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Driver Code */ +int main() { + /* Initialize double-ended queue */ + deque deque; + + /* Element enqueue */ + 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 = "; + printDeque(deque); + + /* Access element */ + int front = deque.front(); + cout << "Front element of the queue front = " << front << endl; + int back = deque.back(); + cout << "Back element of the queue back = " << back << endl; + + /* Element dequeue */ + deque.pop_front(); + cout << "Front element dequeued = " << front << ", after dequeuing from the front"; + printDeque(deque); + deque.pop_back(); + cout << "Back element dequeued = " << back << ", after dequeuing from the back"; + printDeque(deque); + + /* Get the length of the double-ended queue */ + int size = deque.size(); + cout << "Length of the double-ended queue size = " << size << endl; + + /* Determine if the double-ended queue is empty */ + bool empty = deque.empty(); + cout << "Is the double-ended queue 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 new file mode 100644 index 000000000..1bcf74e79 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp @@ -0,0 +1,194 @@ +/** + * File: linkedlist_deque.cpp + * Created Time: 2023-03-02 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Double-linked list node */ +struct DoublyListNode { + int val; // Node value + DoublyListNode *next; // Pointer to successor node + DoublyListNode *prev; // Pointer to predecessor node + DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) { + } +}; + +/* Double-ended queue class based on double-linked list */ +class LinkedListDeque { + private: + DoublyListNode *front, *rear; // Front node front, back node rear + int queSize = 0; // Length of the double-ended queue + + public: + /* Constructor */ + LinkedListDeque() : front(nullptr), rear(nullptr) { + } + + /* Destructor */ + ~LinkedListDeque() { + // Traverse the linked list, remove nodes, free memory + DoublyListNode *pre, *cur = front; + while (cur != nullptr) { + pre = cur; + cur = cur->next; + delete pre; + } + } + + /* Get the length of the double-ended queue */ + int size() { + return queSize; + } + + /* Determine if the double-ended queue is empty */ + bool isEmpty() { + return size() == 0; + } + + /* Enqueue operation */ + void push(int num, bool isFront) { + DoublyListNode *node = new DoublyListNode(num); + // If the list is empty, make front and rear both point to node + if (isEmpty()) + front = rear = node; + // Front enqueue operation + else if (isFront) { + // Add node to the head of the list + front->prev = node; + node->next = front; + front = node; // Update head node + // Rear enqueue operation + } else { + // Add node to the tail of the list + rear->next = node; + node->prev = rear; + rear = node; // Update tail node + } + queSize++; // Update queue length + } + + /* Front enqueue */ + void pushFirst(int num) { + push(num, true); + } + + /* Rear enqueue */ + void pushLast(int num) { + push(num, false); + } + + /* Dequeue operation */ + int pop(bool isFront) { + if (isEmpty()) + throw out_of_range("Queue is empty"); + int val; + // Front dequeue operation + if (isFront) { + val = front->val; // Temporarily store the head node value + // Remove head node + DoublyListNode *fNext = front->next; + if (fNext != nullptr) { + fNext->prev = nullptr; + front->next = nullptr; + } + delete front; + front = fNext; // Update head node + // Rear dequeue operation + } else { + val = rear->val; // Temporarily store the tail node value + // Remove tail node + DoublyListNode *rPrev = rear->prev; + if (rPrev != nullptr) { + rPrev->next = nullptr; + rear->prev = nullptr; + } + delete rear; + rear = rPrev; // Update tail node + } + queSize--; // Update queue length + return val; + } + + /* Front dequeue */ + int popFirst() { + return pop(true); + } + + /* Rear dequeue */ + int popLast() { + return pop(false); + } + + /* Access front element */ + int peekFirst() { + if (isEmpty()) + throw out_of_range("Double-ended queue is empty"); + return front->val; + } + + /* Access rear element */ + int peekLast() { + if (isEmpty()) + throw out_of_range("Double-ended queue is empty"); + return rear->val; + } + + /* Return array for printing */ + vector toVector() { + DoublyListNode *node = front; + vector res(size()); + for (int i = 0; i < res.size(); i++) { + res[i] = node->val; + node = node->next; + } + return res; + } +}; + +/* Driver Code */ +int main() { + /* Initialize double-ended queue */ + LinkedListDeque *deque = new LinkedListDeque(); + deque->pushLast(3); + deque->pushLast(2); + deque->pushLast(5); + cout << "Double-ended queue deque = "; + printVector(deque->toVector()); + + /* Access element */ + int peekFirst = deque->peekFirst(); + cout << "Front element peekFirst = " << peekFirst << endl; + int peekLast = deque->peekLast(); + cout << "Back element peekLast = " << peekLast << endl; + + /* Element enqueue */ + deque->pushLast(4); + cout << "Element 4 rear enqueued, deque ="; + printVector(deque->toVector()); + deque->pushFirst(1); + cout << "Element 1 enqueued at the head, deque = "; + printVector(deque->toVector()); + + /* Element dequeue */ + int popLast = deque->popLast(); + cout << "Deque tail element = " << popLast << ", after dequeuing from the tail"; + printVector(deque->toVector()); + int popFirst = deque->popFirst(); + cout << "Deque front element = " << popFirst << ", after dequeuing from the front"; + printVector(deque->toVector()); + + /* Get the length of the double-ended queue */ + int size = deque->size(); + cout << "Length of the double-ended queue size = " << size << endl; + + /* Determine if the double-ended queue is empty */ + bool isEmpty = deque->isEmpty(); + cout << "Is the double-ended queue empty = " << boolalpha << isEmpty << endl; + + // Free memory + delete deque; + + return 0; +} diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp new file mode 100644 index 000000000..7326a9dc4 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -0,0 +1,120 @@ +/** + * File: linkedlist_queue.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Queue class based on linked list */ +class LinkedListQueue { + private: + ListNode *front, *rear; // Front node front, back node rear + int queSize; + + public: + LinkedListQueue() { + front = nullptr; + rear = nullptr; + queSize = 0; + } + + ~LinkedListQueue() { + // Traverse the linked list, remove nodes, free memory + freeMemoryLinkedList(front); + } + + /* Get the length of the queue */ + int size() { + return queSize; + } + + /* Determine if the queue is empty */ + bool isEmpty() { + return queSize == 0; + } + + /* Enqueue */ + void push(int num) { + // Add num behind the tail node + ListNode *node = new ListNode(num); + // If the queue is empty, make the head and tail nodes both point to that node + if (front == nullptr) { + front = node; + rear = node; + } + // If the queue is not empty, add that node behind the tail node + else { + rear->next = node; + rear = node; + } + queSize++; + } + + /* Dequeue */ + int pop() { + int num = peek(); + // Remove head node + ListNode *tmp = front; + front = front->next; + // Free memory + delete tmp; + queSize--; + return num; + } + + /* Access front element */ + int peek() { + if (size() == 0) + throw out_of_range("Queue is empty"); + return front->val; + } + + /* Convert the linked list to Vector and return */ + vector toVector() { + ListNode *node = front; + vector res(size()); + for (int i = 0; i < res.size(); i++) { + res[i] = node->val; + node = node->next; + } + return res; + } +}; + +/* Driver Code */ +int main() { + /* Initialize queue */ + LinkedListQueue *queue = new LinkedListQueue(); + + /* Element enqueue */ + queue->push(1); + queue->push(3); + queue->push(2); + queue->push(5); + queue->push(4); + cout << "Queue queue = "; + printVector(queue->toVector()); + + /* Access front element */ + int peek = queue->peek(); + cout << "Front element peek = " << peek << endl; + + /* Element dequeue */ + peek = queue->pop(); + cout << "Element dequeued = " << peek << ", after dequeuing"; + printVector(queue->toVector()); + + /* Get the length of the queue */ + int size = queue->size(); + cout << "Length of the queue size = " << size << endl; + + /* Determine if the queue is empty */ + bool empty = queue->isEmpty(); + cout << "Is the queue empty = " << empty << endl; + + // Free memory + delete queue; + + return 0; +} diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp new file mode 100644 index 000000000..655f9f02b --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp @@ -0,0 +1,109 @@ +/** + * File: linkedlist_stack.cpp + * Created Time: 2022-11-28 + * Author: qualifier1024 (2539244001@qq.com) + */ + +#include "../utils/common.hpp" + +/* Stack class based on linked list */ +class LinkedListStack { + private: + ListNode *stackTop; // Use the head node as the top of the stack + int stkSize; // Length of the stack + + public: + LinkedListStack() { + stackTop = nullptr; + stkSize = 0; + } + + ~LinkedListStack() { + // Traverse the linked list, remove nodes, free memory + freeMemoryLinkedList(stackTop); + } + + /* Get the length of the stack */ + int size() { + return stkSize; + } + + /* Determine if the stack is empty */ + bool isEmpty() { + return size() == 0; + } + + /* Push */ + void push(int num) { + ListNode *node = new ListNode(num); + node->next = stackTop; + stackTop = node; + stkSize++; + } + + /* Pop */ + int pop() { + int num = top(); + ListNode *tmp = stackTop; + stackTop = stackTop->next; + // Free memory + delete tmp; + stkSize--; + return num; + } + + /* Access stack top element */ + int top() { + if (isEmpty()) + throw out_of_range("Stack is empty"); + return stackTop->val; + } + + /* Convert the List to Array and return */ + vector toVector() { + ListNode *node = stackTop; + vector res(size()); + for (int i = res.size() - 1; i >= 0; i--) { + res[i] = node->val; + node = node->next; + } + return res; + } +}; + +/* Driver Code */ +int main() { + /* Initialize stack */ + LinkedListStack *stack = new LinkedListStack(); + + /* Element push */ + stack->push(1); + stack->push(3); + stack->push(2); + stack->push(5); + stack->push(4); + cout << "Stack stack = "; + printVector(stack->toVector()); + + /* Access stack top element */ + int top = stack->top(); + cout << "Top element of the stack top = " << top << endl; + + /* Element pop */ + top = stack->pop(); + cout << "Element popped from the stack = " << top << ", after popping"; + printVector(stack->toVector()); + + /* Get the length of the stack */ + int size = stack->size(); + cout << "Length of the stack size = " << size << endl; + + /* Determine if it's empty */ + bool empty = stack->isEmpty(); + cout << "Is the stack empty = " << empty << endl; + + // Free memory + delete stack; + + return 0; +} diff --git a/en/codes/cpp/chapter_stack_and_queue/queue.cpp b/en/codes/cpp/chapter_stack_and_queue/queue.cpp new file mode 100644 index 000000000..6414ef036 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/queue.cpp @@ -0,0 +1,41 @@ +/** + * File: queue.cpp + * Created Time: 2022-11-28 + * Author: qualifier1024 (2539244001@qq.com) + */ + +#include "../utils/common.hpp" + +/* Driver Code */ +int main() { + /* Initialize queue */ + queue queue; + + /* Element enqueue */ + queue.push(1); + queue.push(3); + queue.push(2); + queue.push(5); + queue.push(4); + cout << "Queue queue = "; + printQueue(queue); + + /* Access front element */ + int front = queue.front(); + cout << "Front element of the queue front = " << front << endl; + + /* Element dequeue */ + queue.pop(); + cout << "Element dequeued = " << front << ", after dequeuing"; + printQueue(queue); + + /* Get the length of the queue */ + int size = queue.size(); + cout << "Length of the queue size = " << size << endl; + + /* Determine if the queue is empty */ + bool empty = queue.empty(); + 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 new file mode 100644 index 000000000..6b78e18d7 --- /dev/null +++ b/en/codes/cpp/chapter_stack_and_queue/stack.cpp @@ -0,0 +1,41 @@ +/** + * File: stack.cpp + * Created Time: 2022-11-28 + * Author: qualifier1024 (2539244001@qq.com) + */ + +#include "../utils/common.hpp" + +/* Driver Code */ +int main() { + /* Initialize stack */ + stack stack; + + /* Element push */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + cout << "Stack stack = "; + printStack(stack); + + /* Access stack top element */ + int top = stack.top(); + cout << "Top element of the stack top = " << top << endl; + + /* Element pop */ + stack.pop(); // No return value + cout << "Element popped from the stack = " << top << ", after popping"; + printStack(stack); + + /* Get the length of the stack */ + int size = stack.size(); + cout << "Length of the stack size = " << size << endl; + + /* Determine if it's empty */ + bool empty = stack.empty(); + cout << "Is the stack empty = " << empty << endl; + + return 0; +} diff --git a/en/codes/cpp/chapter_tree/CMakeLists.txt b/en/codes/cpp/chapter_tree/CMakeLists.txt new file mode 100644 index 000000000..fa7009bcb --- /dev/null +++ b/en/codes/cpp/chapter_tree/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(avl_tree avl_tree.cpp) +add_executable(binary_search_tree binary_search_tree.cpp) +add_executable(binary_tree binary_tree.cpp) +add_executable(binary_tree_bfs binary_tree_bfs.cpp) +add_executable(binary_tree_dfs binary_tree_dfs.cpp) +add_executable(array_binary_tree array_binary_tree.cpp) \ No newline at end of file diff --git a/en/codes/cpp/chapter_tree/array_binary_tree.cpp b/en/codes/cpp/chapter_tree/array_binary_tree.cpp new file mode 100644 index 000000000..458fa2062 --- /dev/null +++ b/en/codes/cpp/chapter_tree/array_binary_tree.cpp @@ -0,0 +1,137 @@ +/** + * File: array_binary_tree.cpp + * Created Time: 2023-07-19 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Array-based binary tree class */ +class ArrayBinaryTree { + public: + /* Constructor */ + ArrayBinaryTree(vector arr) { + tree = arr; + } + + /* List capacity */ + int size() { + return tree.size(); + } + + /* Get the value of the node at index i */ + int val(int i) { + // If index is out of bounds, return INT_MAX, representing a null + if (i < 0 || i >= size()) + return INT_MAX; + return tree[i]; + } + + /* Get the index of the left child of the node at index i */ + int left(int i) { + return 2 * i + 1; + } + + /* Get the index of the right child of the node at index i */ + int right(int i) { + return 2 * i + 2; + } + + /* Get the index of the parent of the node at index i */ + int parent(int i) { + return (i - 1) / 2; + } + + /* Level-order traversal */ + vector levelOrder() { + vector res; + // Traverse array + for (int i = 0; i < size(); i++) { + if (val(i) != INT_MAX) + res.push_back(val(i)); + } + return res; + } + + /* Pre-order traversal */ + vector preOrder() { + vector res; + dfs(0, "pre", res); + return res; + } + + /* In-order traversal */ + vector inOrder() { + vector res; + dfs(0, "in", res); + return res; + } + + /* Post-order traversal */ + vector postOrder() { + vector res; + dfs(0, "post", res); + return res; + } + + private: + vector tree; + + /* Depth-first traversal */ + void dfs(int i, string order, vector &res) { + // If it is an empty spot, return + if (val(i) == INT_MAX) + return; + // Pre-order traversal + if (order == "pre") + res.push_back(val(i)); + dfs(left(i), order, res); + // In-order traversal + if (order == "in") + res.push_back(val(i)); + dfs(right(i), order, res); + // Post-order traversal + if (order == "post") + res.push_back(val(i)); + } +}; + +/* Driver Code */ +int main() { + // Initialize binary tree + // Use INT_MAX to represent an empty spot 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 << "Binary tree in array representation:\n"; + printVector(arr); + cout << "Binary tree in linked list representation:\n"; + printTree(root); + + // Array-based binary tree class + ArrayBinaryTree abt(arr); + + // Access node + int i = 1; + int l = abt.left(i), r = abt.right(i), p = abt.parent(i); + cout << "\nCurrent node's index is " << i << ", value = " << abt.val(i) << "\n"; + cout << "Its left child's index is " << l << ", value = " << (l != INT_MAX ? to_string(abt.val(l)) : "nullptr") << "\n"; + cout << "Its right child's index is " << r << ", value = " << (r != INT_MAX ? to_string(abt.val(r)) : "nullptr") << "\n"; + cout << "Its parent's index is " << p << ", value = " << (p != INT_MAX ? to_string(abt.val(p)) : "nullptr") << "\n"; + + // Traverse tree + vector res = abt.levelOrder(); + cout << "\nLevel-order traversal is:"; + printVector(res); + res = abt.preOrder(); + cout << "Pre-order traversal is:"; + printVector(res); + res = abt.inOrder(); + cout << "In-order traversal is:"; + printVector(res); + res = abt.postOrder(); + cout << "Post-order traversal is:"; + printVector(res); + + return 0; +} diff --git a/en/codes/cpp/chapter_tree/avl_tree.cpp b/en/codes/cpp/chapter_tree/avl_tree.cpp new file mode 100644 index 000000000..ac3fbeec9 --- /dev/null +++ b/en/codes/cpp/chapter_tree/avl_tree.cpp @@ -0,0 +1,233 @@ +/** + * File: avl_tree.cpp + * Created Time: 2023-02-03 + * Author: what-is-me (whatisme@outlook.jp) + */ + +#include "../utils/common.hpp" + +/* AVL tree */ +class AVLTree { + private: + /* Update node height */ + void updateHeight(TreeNode *node) { + // Node height equals the height of the tallest subtree + 1 + node->height = max(height(node->left), height(node->right)) + 1; + } + + /* Right rotation operation */ + TreeNode *rightRotate(TreeNode *node) { + TreeNode *child = node->left; + TreeNode *grandChild = child->right; + // Rotate node to the right around child + child->right = node; + node->left = grandChild; + // Update node height + updateHeight(node); + updateHeight(child); + // Return the root of the subtree after rotation + return child; + } + + /* Left rotation operation */ + TreeNode *leftRotate(TreeNode *node) { + TreeNode *child = node->right; + TreeNode *grandChild = child->left; + // Rotate node to the left around child + child->left = node; + node->right = grandChild; + // Update node height + updateHeight(node); + updateHeight(child); + // Return the root of the subtree after rotation + return child; + } + + /* Perform rotation operation to restore balance to the subtree */ + TreeNode *rotate(TreeNode *node) { + // Get the balance factor of node + int _balanceFactor = balanceFactor(node); + // Left-leaning tree + if (_balanceFactor > 1) { + if (balanceFactor(node->left) >= 0) { + // Right rotation + return rightRotate(node); + } else { + // First left rotation then right rotation + node->left = leftRotate(node->left); + return rightRotate(node); + } + } + // Right-leaning tree + if (_balanceFactor < -1) { + if (balanceFactor(node->right) <= 0) { + // Left rotation + return leftRotate(node); + } else { + // First right rotation then left rotation + node->right = rightRotate(node->right); + return leftRotate(node); + } + } + // Balanced tree, no rotation needed, return + return node; + } + + /* Recursively insert node (helper method) */ + TreeNode *insertHelper(TreeNode *node, int val) { + if (node == nullptr) + return new TreeNode(val); + /* 1. Find insertion position and insert 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; // Do not insert duplicate nodes, return + updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to the subtree */ + node = rotate(node); + // Return the root node of the subtree + return node; + } + + /* Recursively remove node (helper method) */ + TreeNode *removeHelper(TreeNode *node, int val) { + if (node == nullptr) + return nullptr; + /* 1. Find and remove the node */ + if (val < node->val) + node->left = removeHelper(node->left, val); + else if (val > node->val) + node->right = removeHelper(node->right, val); + else { + if (node->left == nullptr || node->right == nullptr) { + TreeNode *child = node->left != nullptr ? node->left : node->right; + // Number of child nodes = 0, remove node and return + if (child == nullptr) { + delete node; + return nullptr; + } + // Number of child nodes = 1, remove node + else { + delete node; + node = child; + } + } else { + // Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it + TreeNode *temp = node->right; + while (temp->left != nullptr) { + temp = temp->left; + } + int tempVal = temp->val; + node->right = removeHelper(node->right, temp->val); + node->val = tempVal; + } + } + updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to the subtree */ + node = rotate(node); + // Return the root node of the subtree + return node; + } + + public: + TreeNode *root; // Root node + + /* Get node height */ + int height(TreeNode *node) { + // Empty node height is -1, leaf node height is 0 + return node == nullptr ? -1 : node->height; + } + + /* Get balance factor */ + int balanceFactor(TreeNode *node) { + // Empty node balance factor is 0 + if (node == nullptr) + return 0; + // Node balance factor = left subtree height - right subtree height + return height(node->left) - height(node->right); + } + + /* Insert node */ + void insert(int val) { + root = insertHelper(root, val); + } + + /* Remove node */ + void remove(int val) { + root = removeHelper(root, val); + } + + /* Search node */ + TreeNode *search(int val) { + TreeNode *cur = root; + // Loop find, break after passing leaf nodes + while (cur != nullptr) { + // Target node is in cur's right subtree + if (cur->val < val) + cur = cur->right; + // Target node is in cur's left subtree + else if (cur->val > val) + cur = cur->left; + // Found target node, break loop + else + break; + } + // Return target node + return cur; + } + + /*Constructor*/ + AVLTree() : root(nullptr) { + } + + /*Destructor*/ + ~AVLTree() { + freeMemoryTree(root); + } +}; + +void testInsert(AVLTree &tree, int val) { + tree.insert(val); + 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 << ", the AVL tree is" << endl; + printTree(tree.root); +} + +/* Driver Code */ +int main() { + /* Initialize empty AVL tree */ + AVLTree avlTree; + + /* Insert node */ + // Notice how the AVL tree maintains balance after inserting nodes + testInsert(avlTree, 1); + testInsert(avlTree, 2); + testInsert(avlTree, 3); + testInsert(avlTree, 4); + testInsert(avlTree, 5); + testInsert(avlTree, 8); + testInsert(avlTree, 7); + testInsert(avlTree, 9); + testInsert(avlTree, 10); + testInsert(avlTree, 6); + + /* Insert duplicate node */ + testInsert(avlTree, 7); + + /* Remove node */ + // Notice how the AVL tree maintains balance after removing nodes + testRemove(avlTree, 8); // Remove node with degree 0 + testRemove(avlTree, 5); // Remove node with degree 1 + testRemove(avlTree, 4); // Remove node with degree 2 + + /* Search node */ + TreeNode *node = avlTree.search(7); + cout << "\nThe found node object is " << 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 new file mode 100644 index 000000000..2f4d7895f --- /dev/null +++ b/en/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -0,0 +1,170 @@ +/** + * File: binary_search_tree.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Binary search tree */ +class BinarySearchTree { + private: + TreeNode *root; + + public: + /* Constructor */ + BinarySearchTree() { + // Initialize empty tree + root = nullptr; + } + + /* Destructor */ + ~BinarySearchTree() { + freeMemoryTree(root); + } + + /* Get binary tree root node */ + TreeNode *getRoot() { + return root; + } + + /* Search node */ + TreeNode *search(int num) { + TreeNode *cur = root; + // Loop find, break after passing leaf nodes + while (cur != nullptr) { + // Target node is in cur's right subtree + if (cur->val < num) + cur = cur->right; + // Target node is in cur's left subtree + else if (cur->val > num) + cur = cur->left; + // Found target node, break loop + else + break; + } + // Return target node + return cur; + } + + /* Insert node */ + void insert(int num) { + // If tree is empty, initialize root node + if (root == nullptr) { + root = new TreeNode(num); + return; + } + TreeNode *cur = root, *pre = nullptr; + // Loop find, break after passing leaf nodes + while (cur != nullptr) { + // Found duplicate node, thus return + if (cur->val == num) + return; + pre = cur; + // Insertion position is in cur's right subtree + if (cur->val < num) + cur = cur->right; + // Insertion position is in cur's left subtree + else + cur = cur->left; + } + // Insert node + TreeNode *node = new TreeNode(num); + if (pre->val < num) + pre->right = node; + else + pre->left = node; + } + + /* Remove node */ + void remove(int num) { + // If tree is empty, return + if (root == nullptr) + return; + TreeNode *cur = root, *pre = nullptr; + // Loop find, break after passing leaf nodes + while (cur != nullptr) { + // Found node to be removed, break loop + if (cur->val == num) + break; + pre = cur; + // Node to be removed is in cur's right subtree + if (cur->val < num) + cur = cur->right; + // Node to be removed is in cur's left subtree + else + cur = cur->left; + } + // If no node to be removed, return + if (cur == nullptr) + return; + // Number of child nodes = 0 or 1 + if (cur->left == nullptr || cur->right == nullptr) { + // When the number of child nodes = 0 / 1, child = nullptr / that child node + TreeNode *child = cur->left != nullptr ? cur->left : cur->right; + // Remove node cur + if (cur != root) { + if (pre->left == cur) + pre->left = child; + else + pre->right = child; + } else { + // If the removed node is the root, reassign the root + root = child; + } + // Free memory + delete cur; + } + // Number of child nodes = 2 + else { + // Get the next node in in-order traversal of cur + TreeNode *tmp = cur->right; + while (tmp->left != nullptr) { + tmp = tmp->left; + } + int tmpVal = tmp->val; + // Recursively remove node tmp + remove(tmp->val); + // Replace cur with tmp + cur->val = tmpVal; + } + } +}; + +/* Driver Code */ +int main() { + /* Initialize binary search tree */ + BinarySearchTree *bst = new BinarySearchTree(); + // Note that different insertion orders can result in various tree structures. This particular sequence creates 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 << "The initialized binary tree is\n" << endl; + printTree(bst->getRoot()); + + /* Search node */ + TreeNode *node = bst->search(7); + cout << endl << "The found node object is " << node << ", node value =" << node->val << endl; + + /* Insert node */ + bst->insert(16); + cout << endl << "After inserting node 16, the binary tree is\n" << endl; + printTree(bst->getRoot()); + + /* Remove node */ + bst->remove(1); + cout << endl << "After removing node 1, the binary tree is\n" << endl; + printTree(bst->getRoot()); + bst->remove(2); + cout << endl << "After removing node 2, the binary tree is\n" << endl; + printTree(bst->getRoot()); + bst->remove(4); + cout << endl << "After removing node 4, the binary tree is\n" << endl; + printTree(bst->getRoot()); + + // Free memory + delete bst; + + return 0; +} diff --git a/en/codes/cpp/chapter_tree/binary_tree.cpp b/en/codes/cpp/chapter_tree/binary_tree.cpp new file mode 100644 index 000000000..f1b13b230 --- /dev/null +++ b/en/codes/cpp/chapter_tree/binary_tree.cpp @@ -0,0 +1,43 @@ +/** + * File: binary_tree.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Driver Code */ +int main() { + /* Initialize binary tree */ + // Initialize node + 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); + // Construct node references (pointers) + n1->left = n2; + n1->right = n3; + n2->left = n4; + n2->right = n5; + cout << endl << "Initialize binary tree\n" << endl; + printTree(n1); + + /* Insert and remove nodes */ + TreeNode *P = new TreeNode(0); + // Insert node P between n1 -> n2 + n1->left = P; + P->left = n2; + 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; + printTree(n1); + + // Free memory + freeMemoryTree(n1); + + return 0; +} diff --git a/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp b/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp new file mode 100644 index 000000000..80e094f19 --- /dev/null +++ b/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp @@ -0,0 +1,42 @@ +/** + * File: binary_tree_bfs.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* Level-order traversal */ +vector levelOrder(TreeNode *root) { + // Initialize queue, add root node + queue queue; + queue.push(root); + // Initialize a list to store the traversal sequence + vector vec; + while (!queue.empty()) { + TreeNode *node = queue.front(); + queue.pop(); // Queue dequeues + vec.push_back(node->val); // Save node value + if (node->left != nullptr) + queue.push(node->left); // Left child node enqueues + if (node->right != nullptr) + queue.push(node->right); // Right child node enqueues + } + return vec; +} + +/* Driver Code */ +int main() { + /* Initialize binary tree */ + // Use a specific function to convert an array into a binary tree + TreeNode *root = vectorToTree(vector{1, 2, 3, 4, 5, 6, 7}); + cout << endl << "Initialize binary tree\n" << endl; + printTree(root); + + /* Level-order traversal */ + vector vec = levelOrder(root); + cout << endl << "Sequence of nodes in 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 new file mode 100644 index 000000000..c74ede2fe --- /dev/null +++ b/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp @@ -0,0 +1,69 @@ +/** + * File: binary_tree_dfs.cpp + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +// Initialize the list for storing traversal sequences +vector vec; + +/* Pre-order traversal */ +void preOrder(TreeNode *root) { + if (root == nullptr) + return; + // Visit priority: root node -> left subtree -> right subtree + vec.push_back(root->val); + preOrder(root->left); + preOrder(root->right); +} + +/* In-order traversal */ +void inOrder(TreeNode *root) { + if (root == nullptr) + return; + // Visit priority: left subtree -> root node -> right subtree + inOrder(root->left); + vec.push_back(root->val); + inOrder(root->right); +} + +/* Post-order traversal */ +void postOrder(TreeNode *root) { + if (root == nullptr) + return; + // Visit priority: left subtree -> right subtree -> root node + postOrder(root->left); + postOrder(root->right); + vec.push_back(root->val); +} + +/* Driver Code */ +int main() { + /* Initialize binary tree */ + // Use a specific function to convert an array into a binary tree + TreeNode *root = vectorToTree(vector{1, 2, 3, 4, 5, 6, 7}); + cout << endl << "Initialize binary tree\n" << endl; + printTree(root); + + /* Pre-order traversal */ + vec.clear(); + preOrder(root); + cout << endl << "Sequence of nodes in pre-order traversal = "; + printVector(vec); + + /* In-order traversal */ + vec.clear(); + inOrder(root); + cout << endl << "Sequence of nodes in in-order traversal = "; + printVector(vec); + + /* Post-order traversal */ + vec.clear(); + postOrder(root); + cout << endl << "Sequence of nodes in post-order traversal = "; + printVector(vec); + + return 0; +} diff --git a/en/codes/cpp/utils/CMakeLists.txt b/en/codes/cpp/utils/CMakeLists.txt new file mode 100644 index 000000000..775a55869 --- /dev/null +++ b/en/codes/cpp/utils/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(utils + common.hpp print_utils.hpp + list_node.hpp tree_node.hpp + vertex.hpp) \ No newline at end of file diff --git a/en/codes/cpp/utils/common.hpp b/en/codes/cpp/utils/common.hpp new file mode 100644 index 000000000..c72dabd88 --- /dev/null +++ b/en/codes/cpp/utils/common.hpp @@ -0,0 +1,28 @@ +/** + * File: common.hpp + * Created Time: 2021-12-19 + * Author: krahets (krahets@163.com) + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list_node.hpp" +#include "print_utils.hpp" +#include "tree_node.hpp" +#include "vertex.hpp" + +using namespace std; diff --git a/en/codes/cpp/utils/list_node.hpp b/en/codes/cpp/utils/list_node.hpp new file mode 100644 index 000000000..b774a5610 --- /dev/null +++ b/en/codes/cpp/utils/list_node.hpp @@ -0,0 +1,42 @@ +/** + * File: list_node.hpp + * Created Time: 2021-12-19 + * Author: krahets (krahets@163.com) + */ + +#pragma once + +#include +#include + +using namespace std; + +/* Linked list node */ +struct ListNode { + int val; + ListNode *next; + ListNode(int x) : val(x), next(nullptr) { + } +}; + +/* Deserialize a list into a linked list */ +ListNode *vecToLinkedList(vector list) { + ListNode *dum = new ListNode(0); + ListNode *head = dum; + for (int val : list) { + head->next = new ListNode(val); + head = head->next; + } + return dum->next; +} + +/* Free memory allocated to the linked list */ +void freeMemoryLinkedList(ListNode *cur) { + // Free memory + ListNode *pre; + while (cur != nullptr) { + pre = cur; + cur = cur->next; + delete pre; + } +} diff --git a/en/codes/cpp/utils/print_utils.hpp b/en/codes/cpp/utils/print_utils.hpp new file mode 100644 index 000000000..08420244f --- /dev/null +++ b/en/codes/cpp/utils/print_utils.hpp @@ -0,0 +1,228 @@ +/** + * File: print_utils.hpp + * Created Time: 2021-12-19 + * Author: krahets (krahets@163.com), msk397 (machangxinq@gmail.com), LoneRanger(836253168@qq.com) + */ + +#pragma once + +#include "list_node.hpp" +#include "tree_node.hpp" +#include +#include +#include +#include + +/* Find an element in a vector */ +template int vecFind(const vector &vec, T ele) { + int j = INT_MAX; + for (int i = 0; i < vec.size(); i++) { + if (vec[i] == ele) { + j = i; + } + } + return j; +} + +/* Concatenate a vector with a delim */ +template string strJoin(const string &delim, const T &vec) { + ostringstream s; + for (const auto &i : vec) { + if (&i != &vec[0]) { + s << delim; + } + s << i; + } + return s.str(); +} + +/* Repeat a string for n times */ +string strRepeat(string str, int n) { + ostringstream os; + for (int i = 0; i < n; i++) + os << str; + return os.str(); +} + +/* Print array */ +template void printArray(T *arr, int n) { + cout << "["; + for (int i = 0; i < n - 1; i++) { + cout << arr[i] << ", "; + } + if (n >= 1) + cout << arr[n - 1] << "]" << endl; + else + cout << "]" << endl; +} + +/* Get the Vector String object */ +template string getVectorString(vector &list) { + return "[" + strJoin(", ", list) + "]"; +} + +/* Print list */ +template void printVector(vector list) { + cout << getVectorString(list) << '\n'; +} + +/* Print matrix */ +template void printVectorMatrix(vector> &matrix) { + cout << "[" << '\n'; + for (vector &list : matrix) + cout << " " + getVectorString(list) + "," << '\n'; + cout << "]" << '\n'; +} + +/* Print linked list */ +void printLinkedList(ListNode *head) { + vector list; + while (head != nullptr) { + list.push_back(head->val); + head = head->next; + } + + cout << strJoin(" -> ", list) << '\n'; +} + +struct Trunk { + Trunk *prev; + string str; + Trunk(Trunk *prev, string str) { + this->prev = prev; + this->str = str; + } +}; + +void showTrunks(Trunk *p) { + if (p == nullptr) { + return; + } + + showTrunks(p->prev); + cout << p->str; +} + +/** + * 打印二叉树 + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ +void printTree(TreeNode *root, Trunk *prev, bool isRight) { + if (root == nullptr) { + return; + } + + string prev_str = " "; + Trunk trunk(prev, prev_str); + + printTree(root->right, &trunk, true); + + if (!prev) { + trunk.str = "———"; + } else if (isRight) { + trunk.str = "/———"; + prev_str = " |"; + } else { + trunk.str = "\\———"; + prev->str = prev_str; + } + + showTrunks(&trunk); + cout << " " << root->val << endl; + + if (prev) { + prev->str = prev_str; + } + trunk.str = " |"; + + printTree(root->left, &trunk, false); +} + +/* Print binary tree */ +void printTree(TreeNode *root) { + printTree(root, nullptr, false); +} + +/* Print stack */ +template void printStack(stack stk) { + // Reverse the input stack + stack tmp; + while (!stk.empty()) { + tmp.push(stk.top()); + stk.pop(); + } + // Generate the string to print + ostringstream s; + bool flag = true; + while (!tmp.empty()) { + if (flag) { + s << tmp.top(); + flag = false; + } else + s << ", " << tmp.top(); + tmp.pop(); + } + cout << "[" + s.str() + "]" << '\n'; +} + +/* Print queue */ +template void printQueue(queue queue) { + // Generate the string to print + ostringstream s; + bool flag = true; + while (!queue.empty()) { + if (flag) { + s << queue.front(); + flag = false; + } else + s << ", " << queue.front(); + queue.pop(); + } + cout << "[" + s.str() + "]" << '\n'; +} + +/* Print deque */ +template void printDeque(deque deque) { + // Generate the string to print + ostringstream s; + bool flag = true; + while (!deque.empty()) { + if (flag) { + s << deque.front(); + flag = false; + } else + s << ", " << deque.front(); + deque.pop_front(); + } + cout << "[" + s.str() + "]" << '\n'; +} + +/* Print hash table */ +// Define template parameters TKey and TValue to specify the types of key-value pairs +template void printHashMap(unordered_map map) { + for (auto kv : map) { + cout << kv.first << " -> " << kv.second << '\n'; + } +} + +/* Expose the underlying storage of the priority_queue container */ +template S &Container(priority_queue &pq) { + struct HackedQueue : private priority_queue { + static S &Container(priority_queue &pq) { + return pq.*&HackedQueue::c; + } + }; + return HackedQueue::Container(pq); +} + +/* Print heap (Priority queue) */ +template void printHeap(priority_queue &heap) { + vector vec = Container(heap); + cout << "Array representation of the heap:"; + printVector(vec); + 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 new file mode 100644 index 000000000..b4c2b6fc7 --- /dev/null +++ b/en/codes/cpp/utils/tree_node.hpp @@ -0,0 +1,84 @@ +/** + * File: tree_node.hpp + * Created Time: 2021-12-19 + * Author: krahets (krahets@163.com) + */ + +#pragma once + +#include +#include + +using namespace std; + +/* Binary tree node structure */ +struct TreeNode { + int val{}; + int height = 0; + TreeNode *parent{}; + TreeNode *left{}; + TreeNode *right{}; + TreeNode() = default; + explicit TreeNode(int x, TreeNode *parent = nullptr) : val(x), parent(parent) { + } +}; + +// For serialization encoding rules, refer to: +// https://www.hello-algo.com/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 the binary tree: +// /——— 15 +// /——— 7 +// /——— 3 +// | \——— 6 +// | \——— 12 +// ——— 1 +// \——— 2 +// | /——— 9 +// \——— 4 +// \——— 8 + +/* Deserialize a list into a binary tree: Recursively */ +TreeNode *vectorToTreeDFS(vector &arr, int i) { + if (i < 0 || i >= arr.size() || arr[i] == INT_MAX) { + return nullptr; + } + TreeNode *root = new TreeNode(arr[i]); + root->left = vectorToTreeDFS(arr, 2 * i + 1); + root->right = vectorToTreeDFS(arr, 2 * i + 2); + return root; +} + +/* Deserialize a list into a binary tree */ +TreeNode *vectorToTree(vector arr) { + return vectorToTreeDFS(arr, 0); +} + +/* Serialize a binary tree into a list: Recursively */ +void treeToVecorDFS(TreeNode *root, int i, vector &res) { + if (root == nullptr) + return; + while (i >= res.size()) { + res.push_back(INT_MAX); + } + res[i] = root->val; + treeToVecorDFS(root->left, 2 * i + 1, res); + treeToVecorDFS(root->right, 2 * i + 2, res); +} + +/* Serialize a binary tree into a list */ +vector treeToVecor(TreeNode *root) { + vector res; + treeToVecorDFS(root, 0, res); + return res; +} + +/* Free memory allocated to the binary tree */ +void freeMemoryTree(TreeNode *root) { + if (root == nullptr) + return; + freeMemoryTree(root->left); + freeMemoryTree(root->right); + delete root; +} diff --git a/en/codes/cpp/utils/vertex.hpp b/en/codes/cpp/utils/vertex.hpp new file mode 100644 index 000000000..63d9a8146 --- /dev/null +++ b/en/codes/cpp/utils/vertex.hpp @@ -0,0 +1,36 @@ +/** + * File: vertex.hpp + * Created Time: 2023-03-02 + * Author: krahets (krahets@163.com) + */ + +#pragma once + +#include + +using namespace std; + +/* Vertex class */ +struct Vertex { + int val; + Vertex(int x) : val(x) { + } +}; + +/* Input a list of values vals, return a list of vertices vets */ +vector valsToVets(vector vals) { + vector vets; + for (int val : vals) { + vets.push_back(new Vertex(val)); + } + return vets; +} + +/* Input a list of vertices vets, return a list of values vals */ +vector vetsToVals(vector vets) { + vector vals; + for (Vertex *vet : vets) { + vals.push_back(vet->val); + } + return vals; +} 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 15eb6767c..2fc44ca98 100644 --- a/en/codes/java/chapter_backtracking/subset_sum_i_naive.java +++ b/en/codes/java/chapter_backtracking/subset_sum_i_naive.java @@ -48,6 +48,6 @@ public class subset_sum_i_naive { System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); System.out.println("All subsets summing to " + target + " res = " + res); - System.out.println("Please note that the result of this method includes duplicate sets"); + System.out.println("The result of this method includes duplicate sets"); } } diff --git a/en/codes/java/chapter_graph/graph_adjacency_matrix.java b/en/codes/java/chapter_graph/graph_adjacency_matrix.java index 7e6bb0f4c..30d718a1f 100644 --- a/en/codes/java/chapter_graph/graph_adjacency_matrix.java +++ b/en/codes/java/chapter_graph/graph_adjacency_matrix.java @@ -23,7 +23,7 @@ class GraphAdjMat { addVertex(val); } // Add edge - // Please note, edges elements represent vertex indices, corresponding to vertices elements indices + // Edges elements represent vertex indices for (int[] e : edges) { addEdge(e[0], e[1]); } @@ -98,7 +98,7 @@ class GraphAdjMat { public class graph_adjacency_matrix { public static void main(String[] args) { /* Initialize undirected graph */ - // Please note, edges elements represent vertex indices, corresponding to vertices elements indices + // Edges elements represent vertex indices 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); 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 ccf765531..6340c9009 100644 --- a/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java @@ -11,7 +11,7 @@ import java.util.*; /* Double-linked list node */ class ListNode { int val; // Node value - ListNode next; // Reference to the next node + ListNode next; // Reference to successor node ListNode prev; // Reference to predecessor node ListNode(int val) { 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 7412bbf0f..3c471dbb5 100644 --- a/en/codes/python/chapter_backtracking/subset_sum_i_naive.py +++ b/en/codes/python/chapter_backtracking/subset_sum_i_naive.py @@ -47,4 +47,4 @@ if __name__ == "__main__": print(f"Input array nums = {nums}, target = {target}") print(f"All subsets equal to {target} res = {res}") - print(f"Please note that the result of this method includes duplicate sets") + print(f"The result of this method includes duplicate sets") diff --git a/en/codes/python/chapter_graph/graph_adjacency_matrix.py b/en/codes/python/chapter_graph/graph_adjacency_matrix.py index 80ae0a20b..b404b680d 100644 --- a/en/codes/python/chapter_graph/graph_adjacency_matrix.py +++ b/en/codes/python/chapter_graph/graph_adjacency_matrix.py @@ -24,7 +24,7 @@ class GraphAdjMat: for val in vertices: self.add_vertex(val) # Add edge - # Please note, edges elements represent vertex indices, corresponding to vertices elements indices + # Edges elements represent vertex indices for e in edges: self.add_edge(e[0], e[1]) @@ -85,7 +85,7 @@ class GraphAdjMat: """Driver Code""" if __name__ == "__main__": # Initialize undirected graph - # Please note, edges elements represent vertex indices, corresponding to vertices elements indices + # Edges elements represent vertex indices vertices = [1, 3, 2, 5, 4] edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]] graph = GraphAdjMat(vertices, edges) 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 20860f7c5..fa3a5a2fc 100644 --- a/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py @@ -11,7 +11,7 @@ class ListNode: def __init__(self, val: int): """Constructor""" self.val: int = val - self.next: ListNode | None = None # Reference to the next node + self.next: ListNode | None = None # Reference to successor node self.prev: ListNode | None = None # Reference to predecessor node diff --git a/en/codes/python/modules/list_node.py b/en/codes/python/modules/list_node.py index 03d1e1ede..1d158f7c4 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 the next node + self.next: ListNode | None = None # Reference to successor node def list_to_linked_list(arr: list[int]) -> ListNode | None: