diff --git a/en/codes/java/.gitignore b/en/codes/java/.gitignore new file mode 100644 index 0000000000..378eac25d3 --- /dev/null +++ b/en/codes/java/.gitignore @@ -0,0 +1 @@ +build diff --git a/en/codes/java/chapter_array_and_linkedlist/array.java b/en/codes/java/chapter_array_and_linkedlist/array.java new file mode 100644 index 0000000000..5e8ed875db --- /dev/null +++ b/en/codes/java/chapter_array_and_linkedlist/array.java @@ -0,0 +1,105 @@ +/** + * File: array.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_array_and_linkedlist; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +public class array { + /* Random access to elements */ + static int randomAccess(int[] nums) { + // Randomly select a number in the interval [0, nums.length) + int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length); + // Retrieve and return a random element + int randomNum = nums[randomIndex]; + return randomNum; + } + + /* Extend array length */ + static int[] extend(int[] nums, int enlarge) { + // Initialize an extended length array + int[] res = new int[nums.length + enlarge]; + // Copy all elements from the original array to the new array + for (int i = 0; i < nums.length; i++) { + res[i] = nums[i]; + } + // Return the new array after expansion + return res; + } + + /* Insert element num at `index` */ + static void insert(int[] nums, int num, int index) { + // Move all elements after `index` one position backward + for (int i = nums.length - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index + nums[index] = num; + } + + /* Remove the element at `index` */ + static void remove(int[] nums, int index) { + // Move all elements after `index` one position forward + for (int i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } + } + + /* Traverse array */ + static void traverse(int[] nums) { + int count = 0; + // Traverse array by index + for (int i = 0; i < nums.length; i++) { + count += nums[i]; + } + // Traverse array elements + for (int num : nums) { + count += num; + } + } + + /* Search for a specified element in the array */ + static int find(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + if (nums[i] == target) + return i; + } + return -1; + } + + /* Driver Code */ + public static void main(String[] args) { + /* Initialize an array */ + int[] arr = new int[5]; + System.out.println("Array arr = " + Arrays.toString(arr)); + int[] nums = { 1, 3, 2, 5, 4 }; + System.out.println("Array nums = " + Arrays.toString(nums)); + + /* Random access */ + int randomNum = randomAccess(nums); + System.out.println("Get a random element from nums = " + randomNum); + + /* Length extension */ + nums = extend(nums, 3); + System.out.println("Extend the array length to 8, resulting in nums = " + Arrays.toString(nums)); + + /* Insert element */ + insert(nums, 6, 3); + System.out.println("Insert the number 6 at index 3, resulting in nums = " + Arrays.toString(nums)); + + /* Remove element */ + remove(nums, 2); + System.out.println("Remove the element at index 2, resulting in nums = " + Arrays.toString(nums)); + + /* Traverse array */ + traverse(nums); + + /* Search for elements */ + int index = find(nums, 3); + System.out.println("Find element 3 in nums, index = " + index); + } +} diff --git a/en/codes/java/chapter_array_and_linkedlist/linked_list.java b/en/codes/java/chapter_array_and_linkedlist/linked_list.java new file mode 100644 index 0000000000..e8f6194872 --- /dev/null +++ b/en/codes/java/chapter_array_and_linkedlist/linked_list.java @@ -0,0 +1,86 @@ +/** + * File: linked_list.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_array_and_linkedlist; + +import utils.*; + +public class linked_list { + /* Insert node P after node n0 in the linked list */ + static 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 */ + static void remove(ListNode n0) { + if (n0.next == null) + return; + // n0 -> P -> n1 + ListNode P = n0.next; + ListNode n1 = P.next; + n0.next = n1; + } + + /* Access the node at `index` in the linked list */ + static ListNode access(ListNode head, int index) { + for (int i = 0; i < index; i++) { + if (head == null) + return null; + head = head.next; + } + return head; + } + + /* Search for the first node with value target in the linked list */ + static int find(ListNode head, int target) { + int index = 0; + while (head != null) { + if (head.val == target) + return index; + head = head.next; + index++; + } + return -1; + } + + /* Driver Code */ + public static void main(String[] args) { + /* 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; + System.out.println("The initialized linked list is"); + PrintUtil.printLinkedList(n0); + + /* Insert node */ + insert(n0, new ListNode(0)); + System.out.println("Linked list after inserting the node is"); + PrintUtil.printLinkedList(n0); + + /* Remove node */ + remove(n0); + System.out.println("Linked list after removing the node is"); + PrintUtil.printLinkedList(n0); + + /* Access node */ + ListNode node = access(n0, 3); + System.out.println("The value of the node at index 3 in the linked list = " + node.val); + + /* Search node */ + int index = find(n0, 2); + System.out.println("The index of the node with value 2 in the linked list = " + index); + } +} diff --git a/en/codes/java/chapter_array_and_linkedlist/list.java b/en/codes/java/chapter_array_and_linkedlist/list.java new file mode 100644 index 0000000000..ad3f7f809c --- /dev/null +++ b/en/codes/java/chapter_array_and_linkedlist/list.java @@ -0,0 +1,66 @@ +/** + * File: list.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_array_and_linkedlist; + +import java.util.*; + +public class list { + public static void main(String[] args) { + /* Initialize list */ + // The array's element type is Integer[], a wrapper class for int + Integer[] numbers = new Integer[] { 1, 3, 2, 5, 4 }; + List nums = new ArrayList<>(Arrays.asList(numbers)); + System.out.println("List nums = " + nums); + + /* Access element */ + int num = nums.get(1); + System.out.println("Access the element at index 1, obtained num = " + num); + + /* Update element */ + nums.set(1, 0); + System.out.println("Update the element at index 1 to 0, resulting in nums = " + nums); + + /* Clear list */ + nums.clear(); + System.out.println("After clearing the list, nums = " + nums); + + /* Add element at the end */ + nums.add(1); + nums.add(3); + nums.add(2); + nums.add(5); + nums.add(4); + System.out.println("After adding elements, nums = " + nums); + + /* Insert element in the middle */ + nums.add(3, 6); + System.out.println("Insert the number 6 at index 3, resulting in nums = " + nums); + + /* Remove element */ + nums.remove(3); + System.out.println("Remove the element at index 3, resulting in nums = " + nums); + + /* Traverse the list by index */ + int count = 0; + for (int i = 0; i < nums.size(); i++) { + count += nums.get(i); + } + /* Traverse the list elements */ + for (int x : nums) { + count += x; + } + + /* Concatenate two lists */ + List nums1 = new ArrayList<>(Arrays.asList(new Integer[] { 6, 8, 7, 10, 9 })); + nums.addAll(nums1); + System.out.println("Concatenate list nums1 to nums, resulting in nums = " + nums); + + /* Sort list */ + Collections.sort(nums); + System.out.println("After sorting the list, nums = " + nums); + } +} diff --git a/en/codes/java/chapter_array_and_linkedlist/my_list.java b/en/codes/java/chapter_array_and_linkedlist/my_list.java new file mode 100644 index 0000000000..98783b9415 --- /dev/null +++ b/en/codes/java/chapter_array_and_linkedlist/my_list.java @@ -0,0 +1,147 @@ +/** + * File: my_list.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_array_and_linkedlist; + +import java.util.*; + +/* List class */ +class MyList { + private int[] arr; // Array (stores list elements) + private int capacity = 10; // List capacity + private int size = 0; // List length (current number of elements) + private int extendRatio = 2; // Multiple for each list expansion + + /* Constructor */ + public MyList() { + arr = new int[capacity]; + } + + /* Get list length (current number of elements) */ + public int size() { + return size; + } + + /* Get list capacity */ + public int capacity() { + return capacity; + } + + /* Access element */ + public int get(int index) { + // If the index is out of bounds, throw an exception, as below + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index out of bounds"); + return arr[index]; + } + + /* Update element */ + public void set(int index, int num) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index out of bounds"); + arr[index] = num; + } + + /* Add element at the end */ + public 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 + size++; + } + + /* Insert element in the middle */ + public void insert(int index, int num) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index out of bounds"); + // When the number of elements exceeds capacity, trigger the 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 + size++; + } + + /* Remove element */ + public int remove(int index) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index out of bounds"); + int num = arr[index]; + // Move all elements after `index` one position forward + for (int j = index; j < size - 1; j++) { + arr[j] = arr[j + 1]; + } + // Update the number of elements + size--; + // Return the removed element + return num; + } + + /* Extend list */ + public void extendCapacity() { + // Create a new array with a length multiple of the original array by extendRatio, and copy the original array to the new array + arr = Arrays.copyOf(arr, capacity() * extendRatio); + // Update list capacity + capacity = arr.length; + } + + /* Convert the list to an array */ + public int[] toArray() { + int size = size(); + // Only convert elements within valid length range + int[] arr = new int[size]; + for (int i = 0; i < size; i++) { + arr[i] = get(i); + } + return arr; + } +} + +public class my_list { + /* Driver Code */ + public static void main(String[] args) { + /* 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); + System.out.println("List nums = " + Arrays.toString(nums.toArray()) + + ", capacity = " + nums.capacity() + ", length = " + nums.size()); + + /* Insert element in the middle */ + nums.insert(3, 6); + System.out.println("Insert the number 6 at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); + + /* Remove element */ + nums.remove(3); + System.out.println("Remove the element at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); + + /* Access element */ + int num = nums.get(1); + System.out.println("Access the element at index 1, obtained num = " + num); + + /* Update element */ + nums.set(1, 0); + System.out.println("Update the element at index 1 to 0, resulting in nums = " + Arrays.toString(nums.toArray())); + + /* 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); + } + System.out.println("After extending, list nums = " + Arrays.toString(nums.toArray()) + + ", capacity = " + nums.capacity() + ", length = " + nums.size()); + } +} diff --git a/en/codes/java/chapter_backtracking/n_queens.java b/en/codes/java/chapter_backtracking/n_queens.java new file mode 100644 index 0000000000..a533a8fedd --- /dev/null +++ b/en/codes/java/chapter_backtracking/n_queens.java @@ -0,0 +1,77 @@ +/** + * File: n_queens.java + * Created Time: 2023-05-04 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import java.util.*; + +public class n_queens { + /* Backtracking algorithm: n queens */ + public static void backtrack(int row, int n, List> state, List>> res, + boolean[] cols, boolean[] diags1, boolean[] diags2) { + // When all rows are placed, record the solution + if (row == n) { + List> copyState = new ArrayList<>(); + for (List sRow : state) { + copyState.add(new ArrayList<>(sRow)); + } + res.add(copyState); + 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.get(row).set(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.get(row).set(col, "#"); + cols[col] = diags1[diag1] = diags2[diag2] = false; + } + } + } + + /* Solve n queens */ + public static List>> nQueens(int n) { + // Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot + List> state = new ArrayList<>(); + for (int i = 0; i < n; i++) { + List row = new ArrayList<>(); + for (int j = 0; j < n; j++) { + row.add("#"); + } + state.add(row); + } + boolean[] cols = new boolean[n]; // Record columns with queens + boolean[] diags1 = new boolean[2 * n - 1]; // Record main diagonals with queens + boolean[] diags2 = new boolean[2 * n - 1]; // Record minor diagonals with queens + List>> res = new ArrayList<>(); + + backtrack(0, n, state, res, cols, diags1, diags2); + + return res; + } + + public static void main(String[] args) { + int n = 4; + List>> res = nQueens(n); + + System.out.println("Input the dimensions of the chessboard as " + n); + System.out.println("Total number of queen placement solutions = " + res.size()); + for (List> state : res) { + System.out.println("--------------------"); + for (List row : state) { + System.out.println(row); + } + } + } +} diff --git a/en/codes/java/chapter_backtracking/permutations_i.java b/en/codes/java/chapter_backtracking/permutations_i.java new file mode 100644 index 0000000000..dd9b44a245 --- /dev/null +++ b/en/codes/java/chapter_backtracking/permutations_i.java @@ -0,0 +1,51 @@ +/** + * File: permutations_i.java + * Created Time: 2023-04-24 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import java.util.*; + +public class permutations_i { + /* Backtracking algorithm: Permutation I */ + public static void backtrack(List state, int[] choices, boolean[] selected, List> res) { + // When the state length equals the number of elements, record the solution + if (state.size() == choices.length) { + res.add(new ArrayList(state)); + return; + } + // Traverse all choices + for (int i = 0; i < choices.length; 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.add(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.remove(state.size() - 1); + } + } + } + + /* Permutation I */ + static List> permutationsI(int[] nums) { + List> res = new ArrayList>(); + backtrack(new ArrayList(), nums, new boolean[nums.length], res); + return res; + } + + public static void main(String[] args) { + int[] nums = { 1, 2, 3 }; + + List> res = permutationsI(nums); + + System.out.println("Input array nums = " + Arrays.toString(nums)); + System.out.println("All permutations res = " + res); + } +} diff --git a/en/codes/java/chapter_backtracking/permutations_ii.java b/en/codes/java/chapter_backtracking/permutations_ii.java new file mode 100644 index 0000000000..b91e0d8fb1 --- /dev/null +++ b/en/codes/java/chapter_backtracking/permutations_ii.java @@ -0,0 +1,53 @@ +/** + * File: permutations_ii.java + * Created Time: 2023-04-24 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import java.util.*; + +public class permutations_ii { + /* Backtracking algorithm: Permutation II */ + static void backtrack(List state, int[] choices, boolean[] selected, List> res) { + // When the state length equals the number of elements, record the solution + if (state.size() == choices.length) { + res.add(new ArrayList(state)); + return; + } + // Traverse all choices + Set duplicated = new HashSet(); + for (int i = 0; i < choices.length; i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if (!selected[i] && !duplicated.contains(choice)) { + // Attempt: make a choice, update the state + duplicated.add(choice); // Record selected element values + selected[i] = true; + state.add(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.remove(state.size() - 1); + } + } + } + + /* Permutation II */ + static List> permutationsII(int[] nums) { + List> res = new ArrayList>(); + backtrack(new ArrayList(), nums, new boolean[nums.length], res); + return res; + } + + public static void main(String[] args) { + int[] nums = { 1, 2, 2 }; + + List> res = permutationsII(nums); + + System.out.println("Input array nums = " + Arrays.toString(nums)); + System.out.println("All permutations res = " + res); + } +} diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java new file mode 100644 index 0000000000..fc77adbea4 --- /dev/null +++ b/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java @@ -0,0 +1,44 @@ +/** + * File: preorder_traversal_i_compact.java + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import utils.*; +import java.util.*; + +public class preorder_traversal_i_compact { + static List res; + + /* Pre-order traversal: Example one */ + static void preOrder(TreeNode root) { + if (root == null) { + return; + } + if (root.val == 7) { + // Record solution + res.add(root); + } + preOrder(root.left); + preOrder(root.right); + } + + public static void main(String[] args) { + TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); + System.out.println("\nInitialize binary tree"); + PrintUtil.printTree(root); + + // Pre-order traversal + res = new ArrayList<>(); + preOrder(root); + + System.out.println("\nOutput all nodes with value 7"); + List vals = new ArrayList<>(); + for (TreeNode node : res) { + vals.add(node.val); + } + System.out.println(vals); + } +} diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java new file mode 100644 index 0000000000..685c7404d8 --- /dev/null +++ b/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java @@ -0,0 +1,52 @@ +/** + * File: preorder_traversal_ii_compact.java + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import utils.*; +import java.util.*; + +public class preorder_traversal_ii_compact { + static List path; + static List> res; + + /* Pre-order traversal: Example two */ + static void preOrder(TreeNode root) { + if (root == null) { + return; + } + // Attempt + path.add(root); + if (root.val == 7) { + // Record solution + res.add(new ArrayList<>(path)); + } + preOrder(root.left); + preOrder(root.right); + // Retract + path.remove(path.size() - 1); + } + + public static void main(String[] args) { + TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); + System.out.println("\nInitialize binary tree"); + PrintUtil.printTree(root); + + // Pre-order traversal + path = new ArrayList<>(); + res = new ArrayList<>(); + preOrder(root); + + System.out.println("\nOutput all root-to-node 7 paths"); + for (List path : res) { + List vals = new ArrayList<>(); + for (TreeNode node : path) { + vals.add(node.val); + } + System.out.println(vals); + } + } +} diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java new file mode 100644 index 0000000000..b66a84e1fe --- /dev/null +++ b/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java @@ -0,0 +1,53 @@ +/** + * File: preorder_traversal_iii_compact.java + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import utils.*; +import java.util.*; + +public class preorder_traversal_iii_compact { + static List path; + static List> res; + + /* Pre-order traversal: Example three */ + static void preOrder(TreeNode root) { + // Pruning + if (root == null || root.val == 3) { + return; + } + // Attempt + path.add(root); + if (root.val == 7) { + // Record solution + res.add(new ArrayList<>(path)); + } + preOrder(root.left); + preOrder(root.right); + // Retract + path.remove(path.size() - 1); + } + + public static void main(String[] args) { + TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); + System.out.println("\nInitialize binary tree"); + PrintUtil.printTree(root); + + // Pre-order traversal + path = new ArrayList<>(); + res = new ArrayList<>(); + preOrder(root); + + System.out.println("\nOutput all root-to-node 7 paths, not including nodes with value 3"); + for (List path : res) { + List vals = new ArrayList<>(); + for (TreeNode node : path) { + vals.add(node.val); + } + System.out.println(vals); + } + } +} diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java b/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java new file mode 100644 index 0000000000..c4dfa71c25 --- /dev/null +++ b/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java @@ -0,0 +1,77 @@ +/** + * File: preorder_traversal_iii_template.java + * Created Time: 2023-04-16 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import utils.*; +import java.util.*; + +public class preorder_traversal_iii_template { + /* Determine if the current state is a solution */ + static boolean isSolution(List state) { + return !state.isEmpty() && state.get(state.size() - 1).val == 7; + } + + /* Record solution */ + static void recordSolution(List state, List> res) { + res.add(new ArrayList<>(state)); + } + + /* Determine if the choice is legal under the current state */ + static boolean isValid(List state, TreeNode choice) { + return choice != null && choice.val != 3; + } + + /* Update state */ + static void makeChoice(List state, TreeNode choice) { + state.add(choice); + } + + /* Restore state */ + static void undoChoice(List state, TreeNode choice) { + state.remove(state.size() - 1); + } + + /* Backtracking algorithm: Example three */ + static void backtrack(List state, List choices, List> 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 + backtrack(state, Arrays.asList(choice.left, choice.right), res); + // Retract: undo the choice, restore to the previous state + undoChoice(state, choice); + } + } + } + + public static void main(String[] args) { + TreeNode root = TreeNode.listToTree(Arrays.asList(1, 7, 3, 4, 5, 6, 7)); + System.out.println("\nInitialize binary tree"); + PrintUtil.printTree(root); + + // Backtracking algorithm + List> res = new ArrayList<>(); + backtrack(new ArrayList<>(), Arrays.asList(root), res); + + System.out.println("\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3"); + for (List path : res) { + List vals = new ArrayList<>(); + for (TreeNode node : path) { + vals.add(node.val); + } + System.out.println(vals); + } + } +} diff --git a/en/codes/java/chapter_backtracking/subset_sum_i.java b/en/codes/java/chapter_backtracking/subset_sum_i.java new file mode 100644 index 0000000000..ce231bcbf6 --- /dev/null +++ b/en/codes/java/chapter_backtracking/subset_sum_i.java @@ -0,0 +1,55 @@ +/** + * File: subset_sum_i.java + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import java.util.*; + +public class subset_sum_i { + /* Backtracking algorithm: Subset Sum I */ + static void backtrack(List state, int target, int[] choices, int start, List> res) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.add(new ArrayList<>(state)); + return; + } + // Traverse all choices + // Pruning two: start traversing from start to avoid generating duplicate subsets + for (int i = start; i < choices.length; 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.add(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.remove(state.size() - 1); + } + } + + /* Solve Subset Sum I */ + static List> subsetSumI(int[] nums, int target) { + List state = new ArrayList<>(); // State (subset) + Arrays.sort(nums); // Sort nums + int start = 0; // Start point for traversal + List> res = new ArrayList<>(); // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; + } + + public static void main(String[] args) { + int[] nums = { 3, 4, 5 }; + int target = 9; + + List> res = subsetSumI(nums, target); + + System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); + System.out.println("All subsets summing to " + target + " res = " + res); + } +} diff --git a/en/codes/java/chapter_backtracking/subset_sum_i_naive.java b/en/codes/java/chapter_backtracking/subset_sum_i_naive.java new file mode 100644 index 0000000000..15eb6767c9 --- /dev/null +++ b/en/codes/java/chapter_backtracking/subset_sum_i_naive.java @@ -0,0 +1,53 @@ +/** + * File: subset_sum_i_naive.java + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import java.util.*; + +public class subset_sum_i_naive { + /* Backtracking algorithm: Subset Sum I */ + static void backtrack(List state, int target, int total, int[] choices, List> res) { + // When the subset sum equals target, record the solution + if (total == target) { + res.add(new ArrayList<>(state)); + return; + } + // Traverse all choices + for (int i = 0; i < choices.length; 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.add(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.remove(state.size() - 1); + } + } + + /* Solve Subset Sum I (including duplicate subsets) */ + static List> subsetSumINaive(int[] nums, int target) { + List state = new ArrayList<>(); // State (subset) + int total = 0; // Subset sum + List> res = new ArrayList<>(); // Result list (subset list) + backtrack(state, target, total, nums, res); + return res; + } + + public static void main(String[] args) { + int[] nums = { 3, 4, 5 }; + int target = 9; + + List> res = subsetSumINaive(nums, target); + + 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"); + } +} diff --git a/en/codes/java/chapter_backtracking/subset_sum_ii.java b/en/codes/java/chapter_backtracking/subset_sum_ii.java new file mode 100644 index 0000000000..9a503f672f --- /dev/null +++ b/en/codes/java/chapter_backtracking/subset_sum_ii.java @@ -0,0 +1,60 @@ +/** + * File: subset_sum_ii.java + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +package chapter_backtracking; + +import java.util.*; + +public class subset_sum_ii { + /* Backtracking algorithm: Subset Sum II */ + static void backtrack(List state, int target, int[] choices, int start, List> res) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.add(new ArrayList<>(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.length; 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.add(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.remove(state.size() - 1); + } + } + + /* Solve Subset Sum II */ + static List> subsetSumII(int[] nums, int target) { + List state = new ArrayList<>(); // State (subset) + Arrays.sort(nums); // Sort nums + int start = 0; // Start point for traversal + List> res = new ArrayList<>(); // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; + } + + public static void main(String[] args) { + int[] nums = { 4, 4, 5 }; + int target = 9; + + List> res = subsetSumII(nums, target); + + System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); + System.out.println("All subsets summing to " + target + " res = " + res); + } +} diff --git a/en/codes/java/chapter_computational_complexity/iteration.java b/en/codes/java/chapter_computational_complexity/iteration.java new file mode 100644 index 0000000000..ce42c9439f --- /dev/null +++ b/en/codes/java/chapter_computational_complexity/iteration.java @@ -0,0 +1,76 @@ +/** + * File: iteration.java + * Created Time: 2023-08-24 + * Author: krahets (krahets@163.com) + */ + +package chapter_computational_complexity; + +public class iteration { + /* for loop */ + static 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 */ + static 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) */ + static 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 */ + static String nestedForLoop(int n) { + StringBuilder res = new StringBuilder(); + // 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.append("(" + i + ", " + j + "), "); + } + } + return res.toString(); + } + + /* Driver Code */ + public static void main(String[] args) { + int n = 5; + int res; + + res = forLoop(n); + System.out.println("\nSum result of the for loop res = " + res); + + res = whileLoop(n); + System.out.println("\nSum result of the while loop res = " + res); + + res = whileLoopII(n); + System.out.println("\nSum result of the while loop (with two updates) res = " + res); + + String resStr = nestedForLoop(n); + System.out.println("\nResult of the double for loop traversal = " + resStr); + } +} diff --git a/en/codes/java/chapter_computational_complexity/recursion.java b/en/codes/java/chapter_computational_complexity/recursion.java new file mode 100644 index 0000000000..cb1249abc8 --- /dev/null +++ b/en/codes/java/chapter_computational_complexity/recursion.java @@ -0,0 +1,79 @@ +/** + * File: recursion.java + * Created Time: 2023-08-24 + * Author: krahets (krahets@163.com) + */ + +package chapter_computational_complexity; + +import java.util.Stack; + +public class recursion { + /* Recursion */ + static 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 */ + static int forLoopRecur(int n) { + // Use an explicit stack to simulate the system call stack + Stack stack = new 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.isEmpty()) { + // Simulate "return" by "popping from the stack" + res += stack.pop(); + } + // res = 1+2+3+...+n + return res; + } + + /* Tail recursion */ + static 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 */ + static 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 */ + public static void main(String[] args) { + int n = 5; + int res; + + res = recur(n); + System.out.println("\nSum result of the recursive function res = " + res); + + res = forLoopRecur(n); + System.out.println("\nSum result using iteration to simulate recursion res = " + res); + + res = tailRecur(n, 0); + System.out.println("\nSum result of the tail-recursive function res = " + res); + + res = fib(n); + System.out.println("\nThe " + n + "th number in the Fibonacci sequence is " + res); + } +} diff --git a/en/codes/java/chapter_computational_complexity/space_complexity.java b/en/codes/java/chapter_computational_complexity/space_complexity.java new file mode 100644 index 0000000000..f158a206ed --- /dev/null +++ b/en/codes/java/chapter_computational_complexity/space_complexity.java @@ -0,0 +1,110 @@ +/** + * File: space_complexity.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_computational_complexity; + +import utils.*; +import java.util.*; + +public class space_complexity { + /* Function */ + static int function() { + // Perform some operations + return 0; + } + + /* Constant complexity */ + static void constant(int n) { + // Constants, variables, objects occupy O(1) space + final int a = 0; + int b = 0; + int[] nums = new int[10000]; + ListNode node = new ListNode(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++) { + function(); + } + } + + /* Linear complexity */ + static void linear(int n) { + // Array of length n occupies O(n) space + int[] nums = new int[n]; + // A list of length n occupies O(n) space + List nodes = new ArrayList<>(); + for (int i = 0; i < n; i++) { + nodes.add(new ListNode(i)); + } + // A hash table of length n occupies O(n) space + Map map = new HashMap<>(); + for (int i = 0; i < n; i++) { + map.put(i, String.valueOf(i)); + } + } + + /* Linear complexity (recursive implementation) */ + static void linearRecur(int n) { + System.out.println("Recursion n = " + n); + if (n == 1) + return; + linearRecur(n - 1); + } + + /* Quadratic complexity */ + static void quadratic(int n) { + // Matrix occupies O(n^2) space + int[][] numMatrix = new int[n][n]; + // A two-dimensional list occupies O(n^2) space + List> numList = new ArrayList<>(); + for (int i = 0; i < n; i++) { + List tmp = new ArrayList<>(); + for (int j = 0; j < n; j++) { + tmp.add(0); + } + numList.add(tmp); + } + } + + /* Quadratic complexity (recursive implementation) */ + static int quadraticRecur(int n) { + if (n <= 0) + return 0; + // Array nums length = n, n-1, ..., 2, 1 + int[] nums = new int[n]; + System.out.println("Recursion n = " + n + " in the length of nums = " + nums.length); + return quadraticRecur(n - 1); + } + + /* Exponential complexity (building a full binary tree) */ + static TreeNode buildTree(int n) { + if (n == 0) + return null; + TreeNode root = new TreeNode(0); + root.left = buildTree(n - 1); + root.right = buildTree(n - 1); + return root; + } + + /* Driver Code */ + public static void main(String[] args) { + 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); + PrintUtil.printTree(root); + } +} diff --git a/en/codes/java/chapter_computational_complexity/time_complexity.java b/en/codes/java/chapter_computational_complexity/time_complexity.java new file mode 100644 index 0000000000..4036f7aa01 --- /dev/null +++ b/en/codes/java/chapter_computational_complexity/time_complexity.java @@ -0,0 +1,167 @@ +/** + * File: time_complexity.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_computational_complexity; + +public class time_complexity { + /* Constant complexity */ + static int constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; + } + + /* Linear complexity */ + static int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; + } + + /* Linear complexity (traversing an array) */ + static int arrayTraversal(int[] nums) { + int count = 0; + // Loop count is proportional to the length of the array + for (int num : nums) { + count++; + } + return count; + } + + /* Quadratic complexity */ + static 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) */ + static int bubbleSort(int[] nums) { + int count = 0; // Counter + // Outer loop: unsorted range is [0, i] + for (int i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the 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) */ + static 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) */ + static int expRecur(int n) { + if (n == 1) + return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } + + /* Logarithmic complexity (loop implementation) */ + static int logarithmic(int n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } + + /* Logarithmic complexity (recursive implementation) */ + static int logRecur(int n) { + if (n <= 1) + return 0; + return logRecur(n / 2) + 1; + } + + /* Linear logarithmic complexity */ + static 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) */ + static 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 */ + public static void main(String[] args) { + // Can modify n to experience the trend of operation count changes under various complexities + int n = 8; + System.out.println("Input data size n = " + n); + + int count = constant(n); + System.out.println("Number of constant complexity operations = " + count); + + count = linear(n); + System.out.println("Number of linear complexity operations = " + count); + count = arrayTraversal(new int[n]); + System.out.println("Number of linear complexity operations (traversing the array) = " + count); + + count = quadratic(n); + System.out.println("Number of quadratic order operations = " + count); + int[] nums = new int[n]; + for (int i = 0; i < n; i++) + nums[i] = n - i; // [n,n-1,...,2,1] + count = bubbleSort(nums); + System.out.println("Number of quadratic order operations (bubble sort) = " + count); + + count = exponential(n); + System.out.println("Number of exponential complexity operations (implemented by loop) = " + count); + count = expRecur(n); + System.out.println("Number of exponential complexity operations (implemented by recursion) = " + count); + + count = logarithmic(n); + System.out.println("Number of logarithmic complexity operations (implemented by loop) = " + count); + count = logRecur(n); + System.out.println("Number of logarithmic complexity operations (implemented by recursion) = " + count); + + count = linearLogRecur(n); + System.out.println("Number of linear logarithmic complexity operations (implemented by recursion) = " + count); + + count = factorialRecur(n); + System.out.println("Number of factorial complexity operations (implemented by recursion) = " + count); + } +} diff --git a/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java b/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java new file mode 100644 index 0000000000..386abb087e --- /dev/null +++ b/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java @@ -0,0 +1,50 @@ +/** + * File: worst_best_time_complexity.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_computational_complexity; + +import java.util.*; + +public class worst_best_time_complexity { + /* Generate an array with elements {1, 2, ..., n} in a randomly shuffled order */ + static int[] randomNumbers(int n) { + Integer[] nums = new Integer[n]; + // Generate array nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + // Randomly shuffle array elements + Collections.shuffle(Arrays.asList(nums)); + // Integer[] -> int[] + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = nums[i]; + } + return res; + } + + /* Find the index of number 1 in array nums */ + static int findOne(int[] nums) { + for (int i = 0; i < nums.length; 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 */ + public static void main(String[] args) { + for (int i = 0; i < 10; i++) { + int n = 100; + int[] nums = randomNumbers(n); + int index = findOne(nums); + System.out.println("\nThe array [ 1, 2, ..., n ] after being shuffled = " + Arrays.toString(nums)); + System.out.println("The index of number 1 is " + index); + } + } +} diff --git a/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java b/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java new file mode 100644 index 0000000000..0fbb3b80fe --- /dev/null +++ b/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java @@ -0,0 +1,45 @@ +/** + * File: binary_search_recur.java + * Created Time: 2023-07-17 + * Author: krahets (krahets@163.com) + */ + +package chapter_divide_and_conquer; + +public class binary_search_recur { + /* Binary search: problem f(i, j) */ + static int dfs(int[] nums, int target, int i, int j) { + // If the interval is empty, 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 */ + static int binarySearch(int[] nums, int target) { + int n = nums.length; + // Solve problem f(0, n-1) + return dfs(nums, target, 0, n - 1); + } + + public static void main(String[] args) { + int target = 6; + int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; + + // Binary search (double closed interval) + int index = binarySearch(nums, target); + System.out.println("Index of target element 6 =" + index); + } +} diff --git a/en/codes/java/chapter_divide_and_conquer/build_tree.java b/en/codes/java/chapter_divide_and_conquer/build_tree.java new file mode 100644 index 0000000000..678b20377c --- /dev/null +++ b/en/codes/java/chapter_divide_and_conquer/build_tree.java @@ -0,0 +1,51 @@ +/** + * File: build_tree.java + * Created Time: 2023-07-17 + * Author: krahets (krahets@163.com) + */ + +package chapter_divide_and_conquer; + +import utils.*; +import java.util.*; + +public class build_tree { + /* Build binary tree: Divide and conquer */ + static TreeNode dfs(int[] preorder, 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.get(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 */ + static TreeNode buildTree(int[] preorder, int[] inorder) { + // Initialize hash table, storing in-order elements to indices mapping + Map inorderMap = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderMap.put(inorder[i], i); + } + TreeNode root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); + return root; + } + + public static void main(String[] args) { + int[] preorder = { 3, 9, 2, 1, 7 }; + int[] inorder = { 9, 3, 1, 2, 7 }; + System.out.println("Pre-order traversal = " + Arrays.toString(preorder)); + System.out.println("In-order traversal = " + Arrays.toString(inorder)); + + TreeNode root = buildTree(preorder, inorder); + System.out.println("The built binary tree is:"); + PrintUtil.printTree(root); + } +} diff --git a/en/codes/java/chapter_divide_and_conquer/hanota.java b/en/codes/java/chapter_divide_and_conquer/hanota.java new file mode 100644 index 0000000000..8e7ec569dc --- /dev/null +++ b/en/codes/java/chapter_divide_and_conquer/hanota.java @@ -0,0 +1,59 @@ +/** + * File: hanota.java + * Created Time: 2023-07-17 + * Author: krahets (krahets@163.com) + */ + +package chapter_divide_and_conquer; + +import java.util.*; + +public class hanota { + /* Move a disc */ + static void move(List src, List tar) { + // Take out a disc from the top of src + Integer pan = src.remove(src.size() - 1); + // Place the disc on top of tar + tar.add(pan); + } + + /* Solve the Tower of Hanoi problem f(i) */ + static void dfs(int i, List src, List buf, List 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 */ + static void solveHanota(List A, List B, List C) { + int n = A.size(); + // Move the top n discs from A with the help of B to C + dfs(n, A, B, C); + } + + public static void main(String[] args) { + // The tail of the list is the top of the pillar + List A = new ArrayList<>(Arrays.asList(5, 4, 3, 2, 1)); + List B = new ArrayList<>(); + List C = new ArrayList<>(); + System.out.println("Initial state:"); + System.out.println("A = " + A); + System.out.println("B = " + B); + System.out.println("C = " + C); + + solveHanota(A, B, C); + + System.out.println("After the discs are moved:"); + System.out.println("A = " + A); + System.out.println("B = " + B); + System.out.println("C = " + C); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java new file mode 100644 index 0000000000..5d196607c4 --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java @@ -0,0 +1,44 @@ +/** + * File: climbing_stairs_backtrack.java + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.*; + +public class climbing_stairs_backtrack { + /* Backtracking */ + public static void backtrack(List choices, int state, int n, List res) { + // When climbing to the nth step, add 1 to the number of solutions + if (state == n) + res.set(0, res.get(0) + 1); + // Traverse all choices + for (Integer 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 */ + public static int climbingStairsBacktrack(int n) { + List choices = Arrays.asList(1, 2); // Can choose to climb up 1 step or 2 steps + int state = 0; // Start climbing from the 0th step + List res = new ArrayList<>(); + res.add(0); // Use res[0] to record the number of solutions + backtrack(choices, state, n, res); + return res.get(0); + } + + public static void main(String[] args) { + int n = 9; + + int res = climbingStairsBacktrack(n); + System.out.println(String.format("There are %d solutions to climb %d stairs", n, res)); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java new file mode 100644 index 0000000000..a67cb719f0 --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java @@ -0,0 +1,36 @@ +/** + * File: climbing_stairs_constraint_dp.java + * Created Time: 2023-07-01 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +public class climbing_stairs_constraint_dp { + /* Constrained climbing stairs: Dynamic programming */ + static int climbingStairsConstraintDP(int n) { + if (n == 1 || n == 2) { + return 1; + } + // Initialize dp table, used to store subproblem solutions + int[][] dp = new int[n + 1][3]; + // 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]; + } + + public static void main(String[] args) { + int n = 9; + + int res = climbingStairsConstraintDP(n); + System.out.println(String.format("There are %d solutions to climb %d stairs", n, res)); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java new file mode 100644 index 0000000000..68236b6538 --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java @@ -0,0 +1,31 @@ +/** + * File: climbing_stairs_dfs.java + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +public class climbing_stairs_dfs { + /* Search */ + public static 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 */ + public static int climbingStairsDFS(int n) { + return dfs(n); + } + + public static void main(String[] args) { + int n = 9; + + int res = climbingStairsDFS(n); + System.out.println(String.format("There are %d solutions to climb %d stairs", n, res)); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java new file mode 100644 index 0000000000..217964becc --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java @@ -0,0 +1,41 @@ +/** + * File: climbing_stairs_dfs_mem.java + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class climbing_stairs_dfs_mem { + /* Memoized search */ + public static int dfs(int i, int[] 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 */ + public static int climbingStairsDFSMem(int n) { + // mem[i] records the total number of solutions for climbing to the ith step, -1 means no record + int[] mem = new int[n + 1]; + Arrays.fill(mem, -1); + return dfs(n, mem); + } + + public static void main(String[] args) { + int n = 9; + + int res = climbingStairsDFSMem(n); + System.out.println(String.format("There are %d solutions to climb %d stairs", n, res)); + } +} \ No newline at end of file diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java new file mode 100644 index 0000000000..07aed0017a --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java @@ -0,0 +1,48 @@ +/** + * File: climbing_stairs_dp.java + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +public class climbing_stairs_dp { + /* Climbing stairs: Dynamic programming */ + public static int climbingStairsDP(int n) { + if (n == 1 || n == 2) + return n; + // Initialize dp table, used to store subproblem solutions + int[] dp = new int[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 */ + public static 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; + } + + public static void main(String[] args) { + int n = 9; + + int res = climbingStairsDP(n); + System.out.println(String.format("There are %d solutions to climb %d stairs", n, res)); + + res = climbingStairsDPComp(n); + System.out.println(String.format("There are %d solutions to climb %d stairs", n, res)); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/coin_change.java b/en/codes/java/chapter_dynamic_programming/coin_change.java new file mode 100644 index 0000000000..ae0af6cc52 --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/coin_change.java @@ -0,0 +1,72 @@ +/** + * File: coin_change.java + * Created Time: 2023-07-11 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class coin_change { + /* Coin change: Dynamic programming */ + static int coinChangeDP(int[] coins, int amt) { + int n = coins.length; + int MAX = amt + 1; + // Initialize dp table + int[][] dp = new int[n + 1][amt + 1]; + // 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] = Math.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 */ + static int coinChangeDPComp(int[] coins, int amt) { + int n = coins.length; + int MAX = amt + 1; + // Initialize dp table + int[] dp = new int[amt + 1]; + Arrays.fill(dp, 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] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); + } + } + } + return dp[amt] != MAX ? dp[amt] : -1; + } + + public static void main(String[] args) { + int[] coins = { 1, 2, 5 }; + int amt = 4; + + // Dynamic programming + int res = coinChangeDP(coins, amt); + System.out.println("The minimum number of coins required to make up the target amount is " + res); + + // Space-optimized dynamic programming + res = coinChangeDPComp(coins, amt); + System.out.println("The minimum number of coins required to make up the target amount is " + res); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/coin_change_ii.java b/en/codes/java/chapter_dynamic_programming/coin_change_ii.java new file mode 100644 index 0000000000..01987fb88c --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/coin_change_ii.java @@ -0,0 +1,67 @@ +/** + * File: coin_change_ii.java + * Created Time: 2023-07-11 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +public class coin_change_ii { + /* Coin change II: Dynamic programming */ + static int coinChangeIIDP(int[] coins, int amt) { + int n = coins.length; + // Initialize dp table + int[][] dp = new int[n + 1][amt + 1]; + // 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 */ + static int coinChangeIIDPComp(int[] coins, int amt) { + int n = coins.length; + // Initialize dp table + int[] dp = new int[amt + 1]; + 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]; + } + + public static void main(String[] args) { + int[] coins = { 1, 2, 5 }; + int amt = 5; + + // Dynamic programming + int res = coinChangeIIDP(coins, amt); + System.out.println("The number of coin combinations to make up the target amount is " + res); + + // Space-optimized dynamic programming + res = coinChangeIIDPComp(coins, amt); + System.out.println("The number of coin combinations to make up the target amount is " + res); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/edit_distance.java b/en/codes/java/chapter_dynamic_programming/edit_distance.java new file mode 100644 index 0000000000..b37c7c13d7 --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/edit_distance.java @@ -0,0 +1,139 @@ +/** + * File: edit_distance.java + * Created Time: 2023-07-13 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class edit_distance { + /* Edit distance: Brute force search */ + static int editDistanceDFS(String s, String t, int i, int j) { + // If both s and t are empty, return 0 + if (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.charAt(i - 1) == t.charAt(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 delete = editDistanceDFS(s, t, i - 1, j); + int replace = editDistanceDFS(s, t, i - 1, j - 1); + // Return the minimum number of edits + return Math.min(Math.min(insert, delete), replace) + 1; + } + + /* Edit distance: Memoized search */ + static int editDistanceDFSMem(String s, String t, int[][] mem, int i, int j) { + // If both s and t are empty, return 0 + if (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.charAt(i - 1) == t.charAt(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 delete = 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] = Math.min(Math.min(insert, delete), replace) + 1; + return mem[i][j]; + } + + /* Edit distance: Dynamic programming */ + static int editDistanceDP(String s, String t) { + int n = s.length(), m = t.length(); + int[][] dp = new int[n + 1][m + 1]; + // State transition: first row and first column + 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.charAt(i - 1) == t.charAt(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] = Math.min(Math.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 */ + static int editDistanceDPComp(String s, String t) { + int n = s.length(), m = t.length(); + int[] dp = new int[m + 1]; + // 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.charAt(i - 1) == t.charAt(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] = Math.min(Math.min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // Update for the next round of dp[i-1, j-1] + } + } + return dp[m]; + } + + public static void main(String[] args) { + String s = "bag"; + String t = "pack"; + int n = s.length(), m = t.length(); + + // Brute force search + int res = editDistanceDFS(s, t, n, m); + System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + + // Memoized search + int[][] mem = new int[n + 1][m + 1]; + for (int[] row : mem) + Arrays.fill(row, -1); + res = editDistanceDFSMem(s, t, mem, n, m); + System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + + // Dynamic programming + res = editDistanceDP(s, t); + System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + + // Space-optimized dynamic programming + res = editDistanceDPComp(s, t); + System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/knapsack.java b/en/codes/java/chapter_dynamic_programming/knapsack.java new file mode 100644 index 0000000000..a8bf6f3d59 --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/knapsack.java @@ -0,0 +1,116 @@ +/** + * File: knapsack.java + * Created Time: 2023-07-10 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class knapsack { + + /* 0-1 Knapsack: Brute force search */ + static int knapsackDFS(int[] wgt, int[] 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 Math.max(no, yes); + } + + /* 0-1 Knapsack: Memoized search */ + static int knapsackDFSMem(int[] wgt, int[] val, int[][] 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] = Math.max(no, yes); + return mem[i][c]; + } + + /* 0-1 Knapsack: Dynamic programming */ + static int knapsackDP(int[] wgt, int[] val, int cap) { + int n = wgt.length; + // Initialize dp table + int[][] dp = new int[n + 1][cap + 1]; + // 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] = Math.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 */ + static int knapsackDPComp(int[] wgt, int[] val, int cap) { + int n = wgt.length; + // Initialize dp table + int[] dp = new int[cap + 1]; + // 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] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; + } + + public static void main(String[] args) { + int[] wgt = { 10, 20, 30, 40, 50 }; + int[] val = { 50, 120, 150, 210, 240 }; + int cap = 50; + int n = wgt.length; + + // Brute force search + int res = knapsackDFS(wgt, val, n, cap); + System.out.println("The maximum value within the bag capacity is " + res); + + // Memoized search + int[][] mem = new int[n + 1][cap + 1]; + for (int[] row : mem) { + Arrays.fill(row, -1); + } + res = knapsackDFSMem(wgt, val, mem, n, cap); + System.out.println("The maximum value within the bag capacity is " + res); + + // Dynamic programming + res = knapsackDP(wgt, val, cap); + System.out.println("The maximum value within the bag capacity is " + res); + + // Space-optimized dynamic programming + res = knapsackDPComp(wgt, val, cap); + System.out.println("The maximum value within the bag capacity is " + res); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java b/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java new file mode 100644 index 0000000000..26de37cb4b --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java @@ -0,0 +1,53 @@ +/** + * File: min_cost_climbing_stairs_dp.java + * Created Time: 2023-06-30 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class min_cost_climbing_stairs_dp { + /* Climbing stairs with minimum cost: Dynamic programming */ + public static int minCostClimbingStairsDP(int[] cost) { + int n = cost.length - 1; + if (n == 1 || n == 2) + return cost[n]; + // Initialize dp table, used to store subproblem solutions + int[] dp = new int[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] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return dp[n]; + } + + /* Climbing stairs with minimum cost: Space-optimized dynamic programming */ + public static int minCostClimbingStairsDPComp(int[] cost) { + int n = cost.length - 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 = Math.min(a, tmp) + cost[i]; + a = tmp; + } + return b; + } + + public static void main(String[] args) { + int[] cost = { 0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1 }; + System.out.println(String.format("Input the cost list for stairs as %s", Arrays.toString(cost))); + + int res = minCostClimbingStairsDP(cost); + System.out.println(String.format("Minimum cost to climb the stairs %d", res)); + + res = minCostClimbingStairsDPComp(cost); + System.out.println(String.format("Minimum cost to climb the stairs %d", res)); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/min_path_sum.java b/en/codes/java/chapter_dynamic_programming/min_path_sum.java new file mode 100644 index 0000000000..6c033ab3dd --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/min_path_sum.java @@ -0,0 +1,125 @@ +/** + * File: min_path_sum.java + * Created Time: 2023-07-10 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class min_path_sum { + /* Minimum path sum: Brute force search */ + static int minPathSumDFS(int[][] grid, int i, int j) { + // If it's the top-left cell, terminate the search + 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 Integer.MAX_VALUE; + } + // 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 Math.min(left, up) + grid[i][j]; + } + + /* Minimum path sum: Memoized search */ + static int minPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { + // If it's the top-left cell, terminate the search + 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 Integer.MAX_VALUE; + } + // 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] = Math.min(left, up) + grid[i][j]; + return mem[i][j]; + } + + /* Minimum path sum: Dynamic programming */ + static int minPathSumDP(int[][] grid) { + int n = grid.length, m = grid[0].length; + // Initialize dp table + int[][] dp = new int[n][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] = Math.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 */ + static int minPathSumDPComp(int[][] grid) { + int n = grid.length, m = grid[0].length; + // Initialize dp table + int[] dp = new int[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] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; + } + + public static void main(String[] args) { + int[][] grid = { + { 1, 3, 1, 5 }, + { 2, 2, 4, 2 }, + { 5, 3, 2, 1 }, + { 4, 3, 5, 2 } + }; + int n = grid.length, m = grid[0].length; + + // Brute force search + int res = minPathSumDFS(grid, n - 1, m - 1); + System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + + // Memoized search + int[][] mem = new int[n][m]; + for (int[] row : mem) { + Arrays.fill(row, -1); + } + res = minPathSumDFSMem(grid, mem, n - 1, m - 1); + System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + + // Dynamic programming + res = minPathSumDP(grid); + System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + + // Space-optimized dynamic programming + res = minPathSumDPComp(grid); + System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + } +} diff --git a/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java b/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java new file mode 100644 index 0000000000..9e1f5111a2 --- /dev/null +++ b/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java @@ -0,0 +1,63 @@ +/** + * File: unbounded_knapsack.java + * Created Time: 2023-07-11 + * Author: krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +public class unbounded_knapsack { + /* Complete knapsack: Dynamic programming */ + static int unboundedKnapsackDP(int[] wgt, int[] val, int cap) { + int n = wgt.length; + // Initialize dp table + int[][] dp = new int[n + 1][cap + 1]; + // 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] = Math.max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; + } + + /* Complete knapsack: Space-optimized dynamic programming */ + static int unboundedKnapsackDPComp(int[] wgt, int[] val, int cap) { + int n = wgt.length; + // Initialize dp table + int[] dp = new int[cap + 1]; + // 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] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; + } + + public static void main(String[] args) { + int[] wgt = { 1, 2, 3 }; + int[] val = { 5, 11, 15 }; + int cap = 4; + + // Dynamic programming + int res = unboundedKnapsackDP(wgt, val, cap); + System.out.println("The maximum value within the bag capacity is " + res); + + // Space-optimized dynamic programming + res = unboundedKnapsackDPComp(wgt, val, cap); + System.out.println("The maximum value within the bag capacity is " + res); + } +} diff --git a/en/codes/java/chapter_graph/graph_adjacency_list.java b/en/codes/java/chapter_graph/graph_adjacency_list.java new file mode 100644 index 0000000000..c14b24b003 --- /dev/null +++ b/en/codes/java/chapter_graph/graph_adjacency_list.java @@ -0,0 +1,117 @@ +/** + * File: graph_adjacency_list.java + * Created Time: 2023-01-26 + * Author: krahets (krahets@163.com) + */ + +package chapter_graph; + +import java.util.*; +import utils.*; + +/* Undirected graph class based on adjacency list */ +class GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + Map> adjList; + + /* Constructor */ + public GraphAdjList(Vertex[][] edges) { + this.adjList = new HashMap<>(); + // Add all vertices and edges + for (Vertex[] edge : edges) { + addVertex(edge[0]); + addVertex(edge[1]); + addEdge(edge[0], edge[1]); + } + } + + /* Get the number of vertices */ + public int size() { + return adjList.size(); + } + + /* Add edge */ + public void addEdge(Vertex vet1, Vertex vet2) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw new IllegalArgumentException(); + // Add edge vet1 - vet2 + adjList.get(vet1).add(vet2); + adjList.get(vet2).add(vet1); + } + + /* Remove edge */ + public void removeEdge(Vertex vet1, Vertex vet2) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw new IllegalArgumentException(); + // Remove edge vet1 - vet2 + adjList.get(vet1).remove(vet2); + adjList.get(vet2).remove(vet1); + } + + /* Add vertex */ + public void addVertex(Vertex vet) { + if (adjList.containsKey(vet)) + return; + // Add a new linked list to the adjacency list + adjList.put(vet, new ArrayList<>()); + } + + /* Remove vertex */ + public void removeVertex(Vertex vet) { + if (!adjList.containsKey(vet)) + throw new IllegalArgumentException(); + // Remove the vertex vet's corresponding linked list from the adjacency list + adjList.remove(vet); + // Traverse other vertices' linked lists, removing all edges containing vet + for (List list : adjList.values()) { + list.remove(vet); + } + } + + /* Print the adjacency list */ + public void print() { + System.out.println("Adjacency list ="); + for (Map.Entry> pair : adjList.entrySet()) { + List tmp = new ArrayList<>(); + for (Vertex vertex : pair.getValue()) + tmp.add(vertex.val); + System.out.println(pair.getKey().val + ": " + tmp + ","); + } + } +} + +public class graph_adjacency_list { + public static void main(String[] args) { + /* Initialize undirected graph */ + Vertex[] v = Vertex.valsToVets(new int[] { 1, 3, 2, 5, 4 }); + Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, + { v[2], v[3] }, { v[2], v[4] }, { v[3], v[4] } }; + GraphAdjList graph = new GraphAdjList(edges); + System.out.println("\nAfter initialization, the graph is"); + graph.print(); + + /* Add edge */ + // Vertices 1, 2 i.e., v[0], v[2] + graph.addEdge(v[0], v[2]); + System.out.println("\nAfter adding edge 1-2, the graph is"); + graph.print(); + + /* Remove edge */ + // Vertices 1, 3 i.e., v[0], v[1] + graph.removeEdge(v[0], v[1]); + System.out.println("\nAfter removing edge 1-3, the graph is"); + graph.print(); + + /* Add vertex */ + Vertex v5 = new Vertex(6); + graph.addVertex(v5); + System.out.println("\nAfter adding vertex 6, the graph is"); + graph.print(); + + /* Remove vertex */ + // Vertex 3 i.e., v[1] + graph.removeVertex(v[1]); + System.out.println("\nAfter removing vertex 3, the graph is"); + graph.print(); + } +} diff --git a/en/codes/java/chapter_graph/graph_adjacency_matrix.java b/en/codes/java/chapter_graph/graph_adjacency_matrix.java new file mode 100644 index 0000000000..7e6bb0f4cb --- /dev/null +++ b/en/codes/java/chapter_graph/graph_adjacency_matrix.java @@ -0,0 +1,131 @@ +/** + * File: graph_adjacency_matrix.java + * Created Time: 2023-01-26 + * Author: krahets (krahets@163.com) + */ + +package chapter_graph; + +import utils.*; +import java.util.*; + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat { + List vertices; // Vertex list, elements represent "vertex value", index represents "vertex index" + List> adjMat; // Adjacency matrix, row and column indices correspond to "vertex index" + + /* Constructor */ + public GraphAdjMat(int[] vertices, int[][] edges) { + this.vertices = new ArrayList<>(); + this.adjMat = new ArrayList<>(); + // Add vertex + for (int val : vertices) { + addVertex(val); + } + // Add edge + // Please note, edges elements represent vertex indices, corresponding to vertices elements indices + for (int[] e : edges) { + addEdge(e[0], e[1]); + } + } + + /* Get the number of vertices */ + public int size() { + return vertices.size(); + } + + /* Add vertex */ + public void addVertex(int val) { + int n = size(); + // Add new vertex value to the vertex list + vertices.add(val); + // Add a row to the adjacency matrix + List newRow = new ArrayList<>(n); + for (int j = 0; j < n; j++) { + newRow.add(0); + } + adjMat.add(newRow); + // Add a column to the adjacency matrix + for (List row : adjMat) { + row.add(0); + } + } + + /* Remove vertex */ + public void removeVertex(int index) { + if (index >= size()) + throw new IndexOutOfBoundsException(); + // Remove vertex at `index` from the vertex list + vertices.remove(index); + // Remove the row at `index` from the adjacency matrix + adjMat.remove(index); + // Remove the column at `index` from the adjacency matrix + for (List row : adjMat) { + row.remove(index); + } + } + + /* Add edge */ + // Parameters i, j correspond to vertices element indices + public 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 new IndexOutOfBoundsException(); + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) + adjMat.get(i).set(j, 1); + adjMat.get(j).set(i, 1); + } + + /* Remove edge */ + // Parameters i, j correspond to vertices element indices + public 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 new IndexOutOfBoundsException(); + adjMat.get(i).set(j, 0); + adjMat.get(j).set(i, 0); + } + + /* Print adjacency matrix */ + public void print() { + System.out.print("Vertex list = "); + System.out.println(vertices); + System.out.println("Adjacency matrix ="); + PrintUtil.printMatrix(adjMat); + } +} + +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 + int[] vertices = { 1, 3, 2, 5, 4 }; + int[][] edges = { { 0, 1 }, { 0, 3 }, { 1, 2 }, { 2, 3 }, { 2, 4 }, { 3, 4 } }; + GraphAdjMat graph = new GraphAdjMat(vertices, edges); + System.out.println("\nAfter initialization, the graph is"); + graph.print(); + + /* Add edge */ + // Indices of vertices 1, 2 are 0, 2 respectively + graph.addEdge(0, 2); + System.out.println("\nAfter adding edge 1-2, the graph is"); + graph.print(); + + /* Remove edge */ + // Indices of vertices 1, 3 are 0, 1 respectively + graph.removeEdge(0, 1); + System.out.println("\nAfter removing edge 1-3, the graph is"); + graph.print(); + + /* Add vertex */ + graph.addVertex(6); + System.out.println("\nAfter adding vertex 6, the graph is"); + graph.print(); + + /* Remove vertex */ + // Index of vertex 3 is 1 + graph.removeVertex(1); + System.out.println("\nAfter removing vertex 3, the graph is"); + graph.print(); + } +} diff --git a/en/codes/java/chapter_graph/graph_bfs.java b/en/codes/java/chapter_graph/graph_bfs.java new file mode 100644 index 0000000000..ba6519930d --- /dev/null +++ b/en/codes/java/chapter_graph/graph_bfs.java @@ -0,0 +1,55 @@ +/** + * File: graph_bfs.java + * Created Time: 2023-02-12 + * Author: krahets (krahets@163.com) + */ + +package chapter_graph; + +import java.util.*; +import utils.*; + +public class graph_bfs { + /* Breadth-first traversal */ + // Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + static List graphBFS(GraphAdjList graph, Vertex startVet) { + // Vertex traversal sequence + List res = new ArrayList<>(); + // Hash set, used to record visited vertices + Set visited = new HashSet<>(); + visited.add(startVet); + // Queue used to implement BFS + Queue que = new LinkedList<>(); + que.offer(startVet); + // Starting from vertex vet, loop until all vertices are visited + while (!que.isEmpty()) { + Vertex vet = que.poll(); // Dequeue the vertex at the head of the queue + res.add(vet); // Record visited vertex + // Traverse all adjacent vertices of that vertex + for (Vertex adjVet : graph.adjList.get(vet)) { + if (visited.contains(adjVet)) + continue; // Skip already visited vertices + que.offer(adjVet); // Only enqueue unvisited vertices + visited.add(adjVet); // Mark the vertex as visited + } + } + // Return the vertex traversal sequence + return res; + } + + public static void main(String[] args) { + /* Initialize undirected graph */ + Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[1], v[4] }, + { v[2], v[5] }, { v[3], v[4] }, { v[3], v[6] }, { v[4], v[5] }, + { v[4], v[7] }, { v[5], v[8] }, { v[6], v[7] }, { v[7], v[8] } }; + GraphAdjList graph = new GraphAdjList(edges); + System.out.println("\nAfter initialization, the graph is"); + graph.print(); + + /* Breadth-first traversal */ + List res = graphBFS(graph, v[0]); + System.out.println("\nBreadth-first traversal (BFS) vertex sequence is"); + System.out.println(Vertex.vetsToVals(res)); + } +} diff --git a/en/codes/java/chapter_graph/graph_dfs.java b/en/codes/java/chapter_graph/graph_dfs.java new file mode 100644 index 0000000000..7838f566b4 --- /dev/null +++ b/en/codes/java/chapter_graph/graph_dfs.java @@ -0,0 +1,51 @@ +/** + * File: graph_dfs.java + * Created Time: 2023-02-12 + * Author: krahets (krahets@163.com) + */ + +package chapter_graph; + +import java.util.*; +import utils.*; + +public class graph_dfs { + /* Depth-first traversal helper function */ + static void dfs(GraphAdjList graph, Set visited, List res, Vertex vet) { + res.add(vet); // Record visited vertex + visited.add(vet); // Mark the vertex as visited + // Traverse all adjacent vertices of that vertex + for (Vertex adjVet : graph.adjList.get(vet)) { + if (visited.contains(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 + static List graphDFS(GraphAdjList graph, Vertex startVet) { + // Vertex traversal sequence + List res = new ArrayList<>(); + // Hash set, used to record visited vertices + Set visited = new HashSet<>(); + dfs(graph, visited, res, startVet); + return res; + } + + public static void main(String[] args) { + /* Initialize undirected graph */ + Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6 }); + Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, + { v[2], v[5] }, { v[4], v[5] }, { v[5], v[6] } }; + GraphAdjList graph = new GraphAdjList(edges); + System.out.println("\nAfter initialization, the graph is"); + graph.print(); + + /* Depth-first traversal */ + List res = graphDFS(graph, v[0]); + System.out.println("\nDepth-first traversal (DFS) vertex sequence is"); + System.out.println(Vertex.vetsToVals(res)); + } +} diff --git a/en/codes/java/chapter_greedy/coin_change_greedy.java b/en/codes/java/chapter_greedy/coin_change_greedy.java new file mode 100644 index 0000000000..b2da9e6372 --- /dev/null +++ b/en/codes/java/chapter_greedy/coin_change_greedy.java @@ -0,0 +1,55 @@ +/** + * File: coin_change_greedy.java + * Created Time: 2023-07-20 + * Author: krahets (krahets@163.com) + */ + +package chapter_greedy; + +import java.util.Arrays; + +public class coin_change_greedy { + /* Coin change: Greedy */ + static int coinChangeGreedy(int[] coins, int amt) { + // Assume coins list is ordered + int i = coins.length - 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; + } + + public static void main(String[] args) { + // Greedy: can ensure finding a global optimal solution + int[] coins = { 1, 5, 10, 20, 50, 100 }; + int amt = 186; + int res = coinChangeGreedy(coins, amt); + System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); + System.out.println("The minimum number of coins required to make up " + amt + " is " + res); + + // Greedy: cannot ensure finding a global optimal solution + coins = new int[] { 1, 20, 50 }; + amt = 60; + res = coinChangeGreedy(coins, amt); + System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); + System.out.println("The minimum number of coins required to make up " + amt + " is " + res); + System.out.println("In reality, the minimum number needed is 3, i.e., 20 + 20 + 20"); + + // Greedy: cannot ensure finding a global optimal solution + coins = new int[] { 1, 49, 50 }; + amt = 98; + res = coinChangeGreedy(coins, amt); + System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); + System.out.println("The minimum number of coins required to make up " + amt + " is " + res); + System.out.println("In reality, the minimum number needed is 2, i.e., 49 + 49"); + } +} diff --git a/en/codes/java/chapter_greedy/fractional_knapsack.java b/en/codes/java/chapter_greedy/fractional_knapsack.java new file mode 100644 index 0000000000..9bb9303bde --- /dev/null +++ b/en/codes/java/chapter_greedy/fractional_knapsack.java @@ -0,0 +1,59 @@ +/** + * File: fractional_knapsack.java + * Created Time: 2023-07-20 + * Author: krahets (krahets@163.com) + */ + +package chapter_greedy; + +import java.util.Arrays; +import java.util.Comparator; + +/* Item */ +class Item { + int w; // Item weight + int v; // Item value + + public Item(int w, int v) { + this.w = w; + this.v = v; + } +} + +public class fractional_knapsack { + /* Fractional knapsack: Greedy */ + static double fractionalKnapsack(int[] wgt, int[] val, int cap) { + // Create an item list, containing two properties: weight, value + Item[] items = new Item[wgt.length]; + for (int i = 0; i < wgt.length; i++) { + items[i] = new Item(wgt[i], val[i]); + } + // Sort by unit value item.v / item.w from high to low + Arrays.sort(items, Comparator.comparingDouble(item -> -((double) item.v / item.w))); + // Loop for greedy selection + double res = 0; + for (Item 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; + } + + public static void main(String[] args) { + int[] wgt = { 10, 20, 30, 40, 50 }; + int[] val = { 50, 120, 150, 210, 240 }; + int cap = 50; + + // Greedy algorithm + double res = fractionalKnapsack(wgt, val, cap); + System.out.println("The maximum value within the bag capacity is " + res); + } +} diff --git a/en/codes/java/chapter_greedy/max_capacity.java b/en/codes/java/chapter_greedy/max_capacity.java new file mode 100644 index 0000000000..ce14700298 --- /dev/null +++ b/en/codes/java/chapter_greedy/max_capacity.java @@ -0,0 +1,38 @@ +/** + * File: max_capacity.java + * Created Time: 2023-07-21 + * Author: krahets (krahets@163.com) + */ + +package chapter_greedy; + +public class max_capacity { + /* Maximum capacity: Greedy */ + static int maxCapacity(int[] ht) { + // Initialize i, j, making them split the array at both ends + int i = 0, j = ht.length - 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 = Math.min(ht[i], ht[j]) * (j - i); + res = Math.max(res, cap); + // Move the shorter board inward + if (ht[i] < ht[j]) { + i++; + } else { + j--; + } + } + return res; + } + + public static void main(String[] args) { + int[] ht = { 3, 8, 5, 2, 7, 7, 3, 4 }; + + // Greedy algorithm + int res = maxCapacity(ht); + System.out.println("The maximum capacity is " + res); + } +} diff --git a/en/codes/java/chapter_greedy/max_product_cutting.java b/en/codes/java/chapter_greedy/max_product_cutting.java new file mode 100644 index 0000000000..006162cece --- /dev/null +++ b/en/codes/java/chapter_greedy/max_product_cutting.java @@ -0,0 +1,40 @@ +/** + * File: max_product_cutting.java + * Created Time: 2023-07-21 + * Author: krahets (krahets@163.com) + */ + +package chapter_greedy; + +import java.lang.Math; + +public class max_product_cutting { + /* Maximum product of cutting: Greedy */ + public static 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) Math.pow(3, a - 1) * 2 * 2; + } + if (b == 2) { + // When the remainder is 2, do nothing + return (int) Math.pow(3, a) * 2; + } + // When the remainder is 0, do nothing + return (int) Math.pow(3, a); + } + + public static void main(String[] args) { + int n = 58; + + // Greedy algorithm + int res = maxProductCutting(n); + System.out.println("The maximum product of division is " + res); + } +} diff --git a/en/codes/java/chapter_hashing/array_hash_map.java b/en/codes/java/chapter_hashing/array_hash_map.java new file mode 100644 index 0000000000..014721c3e7 --- /dev/null +++ b/en/codes/java/chapter_hashing/array_hash_map.java @@ -0,0 +1,141 @@ +/** + * File: array_hash_map.java + * Created Time: 2022-12-04 + * Author: krahets (krahets@163.com) + */ + +package chapter_hashing; + +import java.util.*; + +/* Key-value pair */ +class Pair { + public int key; + public String val; + + public Pair(int key, String val) { + this.key = key; + this.val = val; + } +} + +/* Hash table based on array implementation */ +class ArrayHashMap { + private List buckets; + + public ArrayHashMap() { + // Initialize an array, containing 100 buckets + buckets = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + buckets.add(null); + } + } + + /* Hash function */ + private int hashFunc(int key) { + int index = key % 100; + return index; + } + + /* Query operation */ + public String get(int key) { + int index = hashFunc(key); + Pair pair = buckets.get(index); + if (pair == null) + return null; + return pair.val; + } + + /* Add operation */ + public void put(int key, String val) { + Pair pair = new Pair(key, val); + int index = hashFunc(key); + buckets.set(index, pair); + } + + /* Remove operation */ + public void remove(int key) { + int index = hashFunc(key); + // Set to null, indicating removal + buckets.set(index, null); + } + + /* Get all key-value pairs */ + public List pairSet() { + List pairSet = new ArrayList<>(); + for (Pair pair : buckets) { + if (pair != null) + pairSet.add(pair); + } + return pairSet; + } + + /* Get all keys */ + public List keySet() { + List keySet = new ArrayList<>(); + for (Pair pair : buckets) { + if (pair != null) + keySet.add(pair.key); + } + return keySet; + } + + /* Get all values */ + public List valueSet() { + List valueSet = new ArrayList<>(); + for (Pair pair : buckets) { + if (pair != null) + valueSet.add(pair.val); + } + return valueSet; + } + + /* Print hash table */ + public void print() { + for (Pair kv : pairSet()) { + System.out.println(kv.key + " -> " + kv.val); + } + } +} + +public class array_hash_map { + public static void main(String[] args) { + /* Initialize hash table */ + ArrayHashMap map = new 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"); + System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + map.print(); + + /* Query operation */ + // Enter key to the hash table, get value + String name = map.get(15937); + System.out.println("\nEnter student ID 15937, found name " + name); + + /* Remove operation */ + // Remove key-value pair (key, value) from the hash table + map.remove(10583); + System.out.println("\nAfter removing 10583, the hash table is\nKey -> Value"); + map.print(); + + /* Traverse hash table */ + System.out.println("\nTraverse key-value pairs Key->Value"); + for (Pair kv : map.pairSet()) { + System.out.println(kv.key + " -> " + kv.val); + } + System.out.println("\nIndividually traverse keys Key"); + for (int key : map.keySet()) { + System.out.println(key); + } + System.out.println("\nIndividually traverse values Value"); + for (String val : map.valueSet()) { + System.out.println(val); + } + } +} diff --git a/en/codes/java/chapter_hashing/built_in_hash.java b/en/codes/java/chapter_hashing/built_in_hash.java new file mode 100644 index 0000000000..3d935af7ce --- /dev/null +++ b/en/codes/java/chapter_hashing/built_in_hash.java @@ -0,0 +1,38 @@ +/** + * File: built_in_hash.java + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +package chapter_hashing; + +import utils.*; +import java.util.*; + +public class built_in_hash { + public static void main(String[] args) { + int num = 3; + int hashNum = Integer.hashCode(num); + System.out.println("The hash value of integer " + num + " is " + hashNum); + + boolean bol = true; + int hashBol = Boolean.hashCode(bol); + System.out.println("The hash value of boolean " + bol + " is " + hashBol); + + double dec = 3.14159; + int hashDec = Double.hashCode(dec); + System.out.println("The hash value of decimal " + dec + " is " + hashDec); + + String str = "Hello algorithm"; + int hashStr = str.hashCode(); + System.out.println("The hash value of string " + str + " is " + hashStr); + + Object[] arr = { 12836, "Ha" }; + int hashTup = Arrays.hashCode(arr); + System.out.println("The hash value of array " + Arrays.toString(arr) + " is " + hashTup); + + ListNode obj = new ListNode(0); + int hashObj = obj.hashCode(); + System.out.println("The hash value of node object " + obj + " is " + hashObj); + } +} diff --git a/en/codes/java/chapter_hashing/hash_map.java b/en/codes/java/chapter_hashing/hash_map.java new file mode 100644 index 0000000000..483a36150b --- /dev/null +++ b/en/codes/java/chapter_hashing/hash_map.java @@ -0,0 +1,52 @@ +/** + * File: hash_map.java + * Created Time: 2022-12-04 + * Author: krahets (krahets@163.com) + */ + +package chapter_hashing; + +import java.util.*; +import utils.*; + +public class hash_map { + public static void main(String[] args) { + /* Initialize hash table */ + Map map = new HashMap<>(); + + /* 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"); + System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + PrintUtil.printHashMap(map); + + /* Query operation */ + // Enter key to the hash table, get value + String name = map.get(15937); + System.out.println("\nEnter student ID 15937, found name " + name); + + /* Remove operation */ + // Remove key-value pair (key, value) from the hash table + map.remove(10583); + System.out.println("\nAfter removing 10583, the hash table is\nKey -> Value"); + PrintUtil.printHashMap(map); + + /* Traverse hash table */ + System.out.println("\nTraverse key-value pairs Key->Value"); + for (Map.Entry kv : map.entrySet()) { + System.out.println(kv.getKey() + " -> " + kv.getValue()); + } + System.out.println("\nIndividually traverse keys Key"); + for (int key : map.keySet()) { + System.out.println(key); + } + System.out.println("\nIndividually traverse values Value"); + for (String val : map.values()) { + System.out.println(val); + } + } +} diff --git a/en/codes/java/chapter_hashing/hash_map_chaining.java b/en/codes/java/chapter_hashing/hash_map_chaining.java new file mode 100644 index 0000000000..cc213d8120 --- /dev/null +++ b/en/codes/java/chapter_hashing/hash_map_chaining.java @@ -0,0 +1,148 @@ +/** + * File: hash_map_chaining.java + * Created Time: 2023-06-13 + * Author: krahets (krahets@163.com) + */ + +package chapter_hashing; + +import java.util.ArrayList; +import java.util.List; + +/* Chained address hash table */ +class HashMapChaining { + int size; // Number of key-value pairs + int capacity; // Hash table capacity + double loadThres; // Load factor threshold for triggering expansion + int extendRatio; // Expansion multiplier + List> buckets; // Bucket array + + /* Constructor */ + public HashMapChaining() { + size = 0; + capacity = 4; + loadThres = 2.0 / 3.0; + extendRatio = 2; + buckets = new ArrayList<>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.add(new ArrayList<>()); + } + } + + /* Hash function */ + int hashFunc(int key) { + return key % capacity; + } + + /* Load factor */ + double loadFactor() { + return (double) size / capacity; + } + + /* Query operation */ + String get(int key) { + int index = hashFunc(key); + List bucket = buckets.get(index); + // Traverse the bucket, if the key is found, return the corresponding val + for (Pair pair : bucket) { + if (pair.key == key) { + return pair.val; + } + } + // If key is not found, return null + return null; + } + + /* 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); + List bucket = buckets.get(index); + // Traverse the bucket, if the specified key is encountered, update the corresponding val and return + for (Pair pair : bucket) { + if (pair.key == key) { + pair.val = val; + return; + } + } + // If the key is not found, add the key-value pair to the end + Pair pair = new Pair(key, val); + bucket.add(pair); + size++; + } + + /* Remove operation */ + void remove(int key) { + int index = hashFunc(key); + List bucket = buckets.get(index); + // Traverse the bucket, remove the key-value pair from it + for (Pair pair : bucket) { + if (pair.key == key) { + bucket.remove(pair); + size--; + break; + } + } + } + + /* Extend hash table */ + void extend() { + // Temporarily store the original hash table + List> bucketsTmp = buckets; + // Initialize the extended new hash table + capacity *= extendRatio; + buckets = new ArrayList<>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.add(new ArrayList<>()); + } + size = 0; + // Move key-value pairs from the original hash table to the new hash table + for (List bucket : bucketsTmp) { + for (Pair pair : bucket) { + put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + void print() { + for (List bucket : buckets) { + List res = new ArrayList<>(); + for (Pair pair : bucket) { + res.add(pair.key + " -> " + pair.val); + } + System.out.println(res); + } + } +} + +public class hash_map_chaining { + public static void main(String[] args) { + /* Initialize hash table */ + HashMapChaining map = new 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"); + System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + map.print(); + + /* Query operation */ + // Enter key to the hash table, get value + String name = map.get(13276); + System.out.println("\nEnter student ID 13276, found name " + name); + + /* Remove operation */ + // Remove key-value pair (key, value) from the hash table + map.remove(12836); + System.out.println("\nAfter removing 12836, the hash table is\nKey -> Value"); + map.print(); + } +} diff --git a/en/codes/java/chapter_hashing/hash_map_open_addressing.java b/en/codes/java/chapter_hashing/hash_map_open_addressing.java new file mode 100644 index 0000000000..fc0ab4c5d2 --- /dev/null +++ b/en/codes/java/chapter_hashing/hash_map_open_addressing.java @@ -0,0 +1,158 @@ +/** + * File: hash_map_open_addressing.java + * Created Time: 2023-06-13 + * Author: krahets (krahets@163.com) + */ + +package chapter_hashing; + +/* Open addressing hash table */ +class HashMapOpenAddressing { + private int size; // Number of key-value pairs + private int capacity = 4; // Hash table capacity + private final double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion + private final int extendRatio = 2; // Expansion multiplier + private Pair[] buckets; // Bucket array + private final Pair TOMBSTONE = new Pair(-1, "-1"); // Removal mark + + /* Constructor */ + public HashMapOpenAddressing() { + size = 0; + buckets = new Pair[capacity]; + } + + /* Hash function */ + private int hashFunc(int key) { + return key % capacity; + } + + /* Load factor */ + private double loadFactor() { + return (double) size / capacity; + } + + /* Search for the bucket index corresponding to key */ + private int findBucket(int key) { + int index = hashFunc(key); + int firstTombstone = -1; + // Linear probing, break when encountering an empty bucket + while (buckets[index] != null) { + // 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 */ + public 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] != null && buckets[index] != TOMBSTONE) { + return buckets[index].val; + } + // If the key-value pair does not exist, return null + return null; + } + + /* Add operation */ + public 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] != null && 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 */ + public 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] != null && buckets[index] != TOMBSTONE) { + buckets[index] = TOMBSTONE; + size--; + } + } + + /* Extend hash table */ + private void extend() { + // Temporarily store the original hash table + Pair[] bucketsTmp = buckets; + // Initialize the extended new hash table + capacity *= extendRatio; + buckets = new Pair[capacity]; + size = 0; + // Move key-value pairs from the original hash table to the new hash table + for (Pair pair : bucketsTmp) { + if (pair != null && pair != TOMBSTONE) { + put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + public void print() { + for (Pair pair : buckets) { + if (pair == null) { + System.out.println("null"); + } else if (pair == TOMBSTONE) { + System.out.println("TOMBSTONE"); + } else { + System.out.println(pair.key + " -> " + pair.val); + } + } + } +} + +public class hash_map_open_addressing { + public static void main(String[] args) { + // Initialize hash table + HashMapOpenAddressing hashmap = new HashMapOpenAddressing(); + + // 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"); + System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + hashmap.print(); + + // Query operation + // Enter key to the hash table, get value val + String name = hashmap.get(13276); + System.out.println("\nEnter student ID 13276, found name " + name); + + // Remove operation + // Remove key-value pair (key, val) from the hash table + hashmap.remove(16750); + System.out.println("\nAfter removing 16750, the hash table is\nKey -> Value"); + hashmap.print(); + } +} diff --git a/en/codes/java/chapter_hashing/simple_hash.java b/en/codes/java/chapter_hashing/simple_hash.java new file mode 100644 index 0000000000..74f2777afc --- /dev/null +++ b/en/codes/java/chapter_hashing/simple_hash.java @@ -0,0 +1,65 @@ +/** + * File: simple_hash.java + * Created Time: 2023-06-21 + * Author: krahets (krahets@163.com) + */ + +package chapter_hashing; + +public class simple_hash { + /* Additive hash */ + static int addHash(String key) { + long hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash = (hash + (int) c) % MODULUS; + } + return (int) hash; + } + + /* Multiplicative hash */ + static int mulHash(String key) { + long hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash = (31 * hash + (int) c) % MODULUS; + } + return (int) hash; + } + + /* XOR hash */ + static int xorHash(String key) { + int hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash ^= (int) c; + } + return hash & MODULUS; + } + + /* Rotational hash */ + static int rotHash(String key) { + long hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash = ((hash << 4) ^ (hash >> 28) ^ (int) c) % MODULUS; + } + return (int) hash; + } + + public static void main(String[] args) { + String key = "Hello algorithm"; + + int hash = addHash(key); + System.out.println("Additive hash value is " + hash); + + hash = mulHash(key); + System.out.println("Multiplicative hash value is " + hash); + + hash = xorHash(key); + System.out.println("XOR hash value is " + hash); + + hash = rotHash(key); + System.out.println("Rotational hash value is " + hash); + } +} diff --git a/en/codes/java/chapter_heap/heap.java b/en/codes/java/chapter_heap/heap.java new file mode 100644 index 0000000000..2ae404bd4d --- /dev/null +++ b/en/codes/java/chapter_heap/heap.java @@ -0,0 +1,66 @@ +/** + * File: heap.java + * Created Time: 2023-01-07 + * Author: krahets (krahets@163.com) + */ + +package chapter_heap; + +import utils.*; +import java.util.*; + +public class heap { + public static void testPush(Queue heap, int val) { + heap.offer(val); // Push the element into heap + System.out.format("\nAfter element %d is added to the heap\n", val); + PrintUtil.printHeap(heap); + } + + public static void testPop(Queue heap) { + int val = heap.poll(); // Pop the element at the heap top + System.out.format("\nAfter the top element %d is removed from the heap\n", val); + PrintUtil.printHeap(heap); + } + + public static void main(String[] args) { + /* Initialize the heap */ + // Initialize min-heap + Queue minHeap = new PriorityQueue<>(); + // Initialize the max-heap (using lambda expression to modify Comparator if necessary) + Queue maxHeap = new PriorityQueue<>((a, b) -> b - a); + + System.out.println("\nThe following test case is for max-heap"); + + /* 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.peek(); + System.out.format("\nTop element of the heap is %d\n", peek); + + /* Pop the element at the heap top */ + testPop(maxHeap); + testPop(maxHeap); + testPop(maxHeap); + testPop(maxHeap); + testPop(maxHeap); + + /* Get heap size */ + int size = maxHeap.size(); + System.out.format("\nNumber of elements in the heap is %d\n", size); + + /* Determine if heap is empty */ + boolean isEmpty = maxHeap.isEmpty(); + System.out.format("\nIs the heap empty %b\n", isEmpty); + + /* Enter list and build heap */ + // Time complexity is O(n), not O(nlogn) + minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4)); + System.out.println("\nEnter list and build min-heap"); + PrintUtil.printHeap(minHeap); + } +} diff --git a/en/codes/java/chapter_heap/my_heap.java b/en/codes/java/chapter_heap/my_heap.java new file mode 100644 index 0000000000..de2b21a139 --- /dev/null +++ b/en/codes/java/chapter_heap/my_heap.java @@ -0,0 +1,159 @@ +/** + * File: my_heap.java + * Created Time: 2023-01-07 + * Author: krahets (krahets@163.com) + */ + +package chapter_heap; + +import utils.*; +import java.util.*; + +/* Max-heap */ +class MaxHeap { + // Use list instead of array to avoid the need for resizing + private List maxHeap; + + /* Constructor, build heap based on input list */ + public MaxHeap(List nums) { + // Add all list elements into the heap + maxHeap = new ArrayList<>(nums); + // Heapify all nodes except leaves + for (int i = parent(size() - 1); i >= 0; i--) { + siftDown(i); + } + } + + /* Get index of left child node */ + private int left(int i) { + return 2 * i + 1; + } + + /* Get index of right child node */ + private int right(int i) { + return 2 * i + 2; + } + + /* Get index of parent node */ + private int parent(int i) { + return (i - 1) / 2; // Integer division down + } + + /* Swap elements */ + private void swap(int i, int j) { + int tmp = maxHeap.get(i); + maxHeap.set(i, maxHeap.get(j)); + maxHeap.set(j, tmp); + } + + /* Get heap size */ + public int size() { + return maxHeap.size(); + } + + /* Determine if heap is empty */ + public boolean isEmpty() { + return size() == 0; + } + + /* Access heap top element */ + public int peek() { + return maxHeap.get(0); + } + + /* Push the element into heap */ + public void push(int val) { + // Add node + maxHeap.add(val); + // Heapify from bottom to top + siftUp(size() - 1); + } + + /* Start heapifying node i, from bottom to top */ + private 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.get(i) <= maxHeap.get(p)) + break; + // Swap two nodes + swap(i, p); + // Loop upwards heapification + i = p; + } + } + + /* Element exits heap */ + public int pop() { + // Empty handling + if (isEmpty()) + throw new IndexOutOfBoundsException(); + // Swap the root node with the rightmost leaf node (swap the first element with the last element) + swap(0, size() - 1); + // Remove node + int val = maxHeap.remove(size() - 1); + // Heapify from top to bottom + siftDown(0); + // Return heap top element + return val; + } + + /* Start heapifying node i, from top to bottom */ + private 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.get(l) > maxHeap.get(ma)) + ma = l; + if (r < size() && maxHeap.get(r) > maxHeap.get(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(i, ma); + // Loop downwards heapification + i = ma; + } + } + + /* Print heap (binary tree) */ + public void print() { + Queue queue = new PriorityQueue<>((a, b) -> { return b - a; }); + queue.addAll(maxHeap); + PrintUtil.printHeap(queue); + } +} + +public class my_heap { + public static void main(String[] args) { + /* Initialize max-heap */ + MaxHeap maxHeap = new MaxHeap(Arrays.asList(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2)); + System.out.println("\nEnter list and build heap"); + maxHeap.print(); + + /* Access heap top element */ + int peek = maxHeap.peek(); + System.out.format("\nTop element of the heap is %d\n", peek); + + /* Push the element into heap */ + int val = 7; + maxHeap.push(val); + System.out.format("\nAfter element %d is added to the heap\n", val); + maxHeap.print(); + + /* Pop the element at the heap top */ + peek = maxHeap.pop(); + System.out.format("\nAfter the top element %d is removed from the heap\n", peek); + maxHeap.print(); + + /* Get heap size */ + int size = maxHeap.size(); + System.out.format("\nNumber of elements in the heap is %d\n", size); + + /* Determine if heap is empty */ + boolean isEmpty = maxHeap.isEmpty(); + System.out.format("\nIs the heap empty %b\n", isEmpty); + } +} diff --git a/en/codes/java/chapter_heap/top_k.java b/en/codes/java/chapter_heap/top_k.java new file mode 100644 index 0000000000..afbb5be1e8 --- /dev/null +++ b/en/codes/java/chapter_heap/top_k.java @@ -0,0 +1,40 @@ +/** + * File: top_k.java + * Created Time: 2023-06-12 + * Author: krahets (krahets@163.com) + */ + +package chapter_heap; + +import utils.*; +import java.util.*; + +public class top_k { + /* Using heap to find the largest k elements in an array */ + static Queue topKHeap(int[] nums, int k) { + // Initialize min-heap + Queue heap = new PriorityQueue(); + // Enter the first k elements of the array into the heap + for (int i = 0; i < k; i++) { + heap.offer(nums[i]); + } + // From the k+1th element, keep the heap length as k + for (int i = k; i < nums.length; 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.peek()) { + heap.poll(); + heap.offer(nums[i]); + } + } + return heap; + } + + public static void main(String[] args) { + int[] nums = { 1, 7, 6, 3, 2 }; + int k = 3; + + Queue res = topKHeap(nums, k); + System.out.println("The largest " + k + " elements are"); + PrintUtil.printHeap(res); + } +} diff --git a/en/codes/java/chapter_searching/binary_search.java b/en/codes/java/chapter_searching/binary_search.java new file mode 100644 index 0000000000..93c34dd362 --- /dev/null +++ b/en/codes/java/chapter_searching/binary_search.java @@ -0,0 +1,58 @@ +/** + * File: binary_search.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_searching; + +public class binary_search { + /* Binary search (double closed interval) */ + static int binarySearch(int[] 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.length - 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) */ + static int binarySearchLCRO(int[] nums, int target) { + // Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively + int i = 0, j = nums.length; + // 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; + } + + public static void main(String[] args) { + int target = 6; + int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; + + /* Binary search (double closed interval) */ + int index = binarySearch(nums, target); + System.out.println("Index of target element 6 =" + index); + + /* Binary search (left closed right open interval) */ + index = binarySearchLCRO(nums, target); + System.out.println("Index of target element 6 =" + index); + } +} diff --git a/en/codes/java/chapter_searching/binary_search_edge.java b/en/codes/java/chapter_searching/binary_search_edge.java new file mode 100644 index 0000000000..54a668808e --- /dev/null +++ b/en/codes/java/chapter_searching/binary_search_edge.java @@ -0,0 +1,49 @@ +/** + * File: binary_search_edge.java + * Created Time: 2023-08-04 + * Author: krahets (krahets@163.com) + */ + +package chapter_searching; + +public class binary_search_edge { + /* Binary search for the leftmost target */ + static int binarySearchLeftEdge(int[] nums, int target) { + // Equivalent to finding the insertion point of target + int i = binary_search_insertion.binarySearchInsertion(nums, target); + // Did not find target, thus return -1 + if (i == nums.length || nums[i] != target) { + return -1; + } + // Found target, return index i + return i; + } + + /* Binary search for the rightmost target */ + static int binarySearchRightEdge(int[] nums, int target) { + // Convert to finding the leftmost target + 1 + int i = binary_search_insertion.binarySearchInsertion(nums, target + 1); + // j points to the rightmost target, i points to the first element greater than target + 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; + } + + public static void main(String[] args) { + // Array with duplicate elements + int[] nums = { 1, 3, 6, 6, 6, 6, 6, 10, 12, 15 }; + System.out.println("\nArray nums = " + java.util.Arrays.toString(nums)); + + // Binary search for left and right boundaries + for (int target : new int[] { 6, 7 }) { + int index = binarySearchLeftEdge(nums, target); + System.out.println("The leftmost index of element " + target + " is " + index); + index = binarySearchRightEdge(nums, target); + System.out.println("The rightmost index of element " + target + " is " + index); + } + } +} diff --git a/en/codes/java/chapter_searching/binary_search_insertion.java b/en/codes/java/chapter_searching/binary_search_insertion.java new file mode 100644 index 0000000000..0ce5ff8330 --- /dev/null +++ b/en/codes/java/chapter_searching/binary_search_insertion.java @@ -0,0 +1,63 @@ +/** + * File: binary_search_insertion.java + * Created Time: 2023-08-04 + * Author: krahets (krahets@163.com) + */ + +package chapter_searching; + +class binary_search_insertion { + /* Binary search for insertion point (no duplicate elements) */ + static int binarySearchInsertionSimple(int[] nums, int target) { + int i = 0, j = nums.length - 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) */ + static int binarySearchInsertion(int[] nums, int target) { + int i = 0, j = nums.length - 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; + } + + public static void main(String[] args) { + // Array without duplicate elements + int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; + System.out.println("\nArray nums = " + java.util.Arrays.toString(nums)); + // Binary search for insertion point + for (int target : new int[] { 6, 9 }) { + int index = binarySearchInsertionSimple(nums, target); + System.out.println("The insertion point index for element " + target + " is " + index); + } + + // Array with duplicate elements + nums = new int[] { 1, 3, 6, 6, 6, 6, 6, 10, 12, 15 }; + System.out.println("\nArray nums = " + java.util.Arrays.toString(nums)); + // Binary search for insertion point + for (int target : new int[] { 2, 6, 20 }) { + int index = binarySearchInsertion(nums, target); + System.out.println("The insertion point index for element " + target + " is " + index); + } + } +} diff --git a/en/codes/java/chapter_searching/hashing_search.java b/en/codes/java/chapter_searching/hashing_search.java new file mode 100644 index 0000000000..568984091d --- /dev/null +++ b/en/codes/java/chapter_searching/hashing_search.java @@ -0,0 +1,51 @@ +/** + * File: hashing_search.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_searching; + +import utils.*; +import java.util.*; + +public class hashing_search { + /* Hash search (array) */ + static int hashingSearchArray(Map map, int target) { + // Hash table's key: target element, value: index + // If the hash table does not contain this key, return -1 + return map.getOrDefault(target, -1); + } + + /* Hash search (linked list) */ + static ListNode hashingSearchLinkedList(Map map, int target) { + // Hash table key: target node value, value: node object + // If the key is not in the hash table, return null + return map.getOrDefault(target, null); + } + + public static void main(String[] args) { + int target = 3; + + /* Hash search (array) */ + int[] nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; + // Initialize hash table + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + map.put(nums[i], i); // key: element, value: index + } + int index = hashingSearchArray(map, target); + System.out.println("The index of target element 3 is " + index); + + /* Hash search (linked list) */ + ListNode head = ListNode.arrToLinkedList(nums); + // Initialize hash table + Map map1 = new HashMap<>(); + while (head != null) { + map1.put(head.val, head); // key: node value, value: node + head = head.next; + } + ListNode node = hashingSearchLinkedList(map1, target); + System.out.println("The corresponding node object for target node value 3 is " + node); + } +} diff --git a/en/codes/java/chapter_searching/linear_search.java b/en/codes/java/chapter_searching/linear_search.java new file mode 100644 index 0000000000..eb7a2f84af --- /dev/null +++ b/en/codes/java/chapter_searching/linear_search.java @@ -0,0 +1,50 @@ +/** + * File: linear_search.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_searching; + +import utils.*; + +public class linear_search { + /* Linear search (array) */ + static int linearSearchArray(int[] nums, int target) { + // Traverse array + for (int i = 0; i < nums.length; 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) */ + static ListNode linearSearchLinkedList(ListNode head, int target) { + // Traverse the list + while (head != null) { + // Found the target node, return it + if (head.val == target) + return head; + head = head.next; + } + // If the target node is not found, return null + return null; + } + + public static void main(String[] args) { + int target = 3; + + /* Perform linear search in array */ + int[] nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; + int index = linearSearchArray(nums, target); + System.out.println("The index of target element 3 is " + index); + + /* Perform linear search in linked list */ + ListNode head = ListNode.arrToLinkedList(nums); + ListNode node = linearSearchLinkedList(head, target); + System.out.println("The corresponding node object for target node value 3 is " + node); + } +} diff --git a/en/codes/java/chapter_searching/two_sum.java b/en/codes/java/chapter_searching/two_sum.java new file mode 100644 index 0000000000..22dbd5d831 --- /dev/null +++ b/en/codes/java/chapter_searching/two_sum.java @@ -0,0 +1,53 @@ +/** + * File: two_sum.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_searching; + +import java.util.*; + +public class two_sum { + /* Method one: Brute force enumeration */ + static int[] twoSumBruteForce(int[] nums, int target) { + int size = nums.length; + // 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 new int[] { i, j }; + } + } + return new int[0]; + } + + /* Method two: Auxiliary hash table */ + static int[] twoSumHashTable(int[] nums, int target) { + int size = nums.length; + // Auxiliary hash table, space complexity is O(n) + Map dic = new HashMap<>(); + // Single-layer loop, time complexity is O(n) + for (int i = 0; i < size; i++) { + if (dic.containsKey(target - nums[i])) { + return new int[] { dic.get(target - nums[i]), i }; + } + dic.put(nums[i], i); + } + return new int[0]; + } + + public static void main(String[] args) { + // ======= Test Case ======= + int[] nums = { 2, 7, 11, 15 }; + int target = 13; + + // ====== Driver Code ====== + // Method one + int[] res = twoSumBruteForce(nums, target); + System.out.println("Method one res = " + Arrays.toString(res)); + // Method two + res = twoSumHashTable(nums, target); + System.out.println("Method two res = " + Arrays.toString(res)); + } +} diff --git a/en/codes/java/chapter_sorting/bubble_sort.java b/en/codes/java/chapter_sorting/bubble_sort.java new file mode 100644 index 0000000000..3880789aed --- /dev/null +++ b/en/codes/java/chapter_sorting/bubble_sort.java @@ -0,0 +1,57 @@ +/** + * File: bubble_sort.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +public class bubble_sort { + /* Bubble sort */ + static void bubbleSort(int[] nums) { + // Outer loop: unsorted range is [0, i] + for (int i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the 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; + } + } + } + } + + /* Bubble sort (optimized with flag) */ + static void bubbleSortWithFlag(int[] nums) { + // Outer loop: unsorted range is [0, i] + for (int i = nums.length - 1; i > 0; i--) { + boolean flag = false; // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the 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; + flag = true; // Record swapped elements + } + } + if (!flag) + break; // If no elements were swapped in this round of "bubbling", exit + } + } + + public static void main(String[] args) { + int[] nums = { 4, 1, 3, 1, 5, 2 }; + bubbleSort(nums); + System.out.println("After bubble sort, nums = " + Arrays.toString(nums)); + + int[] nums1 = { 4, 1, 3, 1, 5, 2 }; + bubbleSortWithFlag(nums1); + System.out.println("After bubble sort, nums1 = " + Arrays.toString(nums1)); + } +} diff --git a/en/codes/java/chapter_sorting/bucket_sort.java b/en/codes/java/chapter_sorting/bucket_sort.java new file mode 100644 index 0000000000..07accafc15 --- /dev/null +++ b/en/codes/java/chapter_sorting/bucket_sort.java @@ -0,0 +1,47 @@ +/** + * File: bucket_sort.java + * Created Time: 2023-03-17 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +public class bucket_sort { + /* Bucket sort */ + static void bucketSort(float[] nums) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + int k = nums.length / 2; + List> buckets = new ArrayList<>(); + for (int i = 0; i < k; i++) { + buckets.add(new ArrayList<>()); + } + // 1. Distribute array elements into various buckets + for (float num : nums) { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + int i = (int) (num * k); + // Add num to bucket i + buckets.get(i).add(num); + } + // 2. Sort each bucket + for (List bucket : buckets) { + // Use built-in sorting function, can also replace with other sorting algorithms + Collections.sort(bucket); + } + // 3. Traverse buckets to merge results + int i = 0; + for (List bucket : buckets) { + for (float num : bucket) { + nums[i++] = num; + } + } + } + + public static void main(String[] args) { + // Assume input data is floating point, range [0, 1) + float[] nums = { 0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f }; + bucketSort(nums); + System.out.println("After bucket sort, nums = " + Arrays.toString(nums)); + } +} diff --git a/en/codes/java/chapter_sorting/counting_sort.java b/en/codes/java/chapter_sorting/counting_sort.java new file mode 100644 index 0000000000..a240791ab0 --- /dev/null +++ b/en/codes/java/chapter_sorting/counting_sort.java @@ -0,0 +1,78 @@ +/** + * File: counting_sort.java + * Created Time: 2023-03-17 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +public class counting_sort { + /* Counting sort */ + // Simple implementation, cannot be used for sorting objects + static void countingSortNaive(int[] nums) { + // 1. Count the maximum element m in the array + int m = 0; + for (int num : nums) { + m = Math.max(m, num); + } + // 2. Count the occurrence of each digit + // counter[num] represents the occurrence of num + int[] counter = new int[m + 1]; + for (int num : nums) { + counter[num]++; + } + // 3. Traverse counter, filling each element back into the original array nums + 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 + static void countingSort(int[] nums) { + // 1. Count the maximum element m in the array + int m = 0; + for (int num : nums) { + m = Math.max(m, num); + } + // 2. Count the occurrence of each digit + // counter[num] represents the occurrence of num + int[] counter = new int[m + 1]; + for (int num : nums) { + counter[num]++; + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + 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.length; + int[] res = new int[n]; + for (int i = n - 1; i >= 0; i--) { + int num = nums[i]; + res[counter[num] - 1] = num; // Place num at the corresponding index + counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + } + // Use result array res to overwrite the original array nums + for (int i = 0; i < n; i++) { + nums[i] = res[i]; + } + } + + public static void main(String[] args) { + int[] nums = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }; + countingSortNaive(nums); + System.out.println("After count sort (unable to sort objects), nums = " + Arrays.toString(nums)); + + int[] nums1 = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }; + countingSort(nums1); + System.out.println("After count sort, nums1 = " + Arrays.toString(nums1)); + } +} diff --git a/en/codes/java/chapter_sorting/heap_sort.java b/en/codes/java/chapter_sorting/heap_sort.java new file mode 100644 index 0000000000..1931ef5622 --- /dev/null +++ b/en/codes/java/chapter_sorting/heap_sort.java @@ -0,0 +1,57 @@ +/** + * File: heap_sort.java + * Created Time: 2023-05-26 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.Arrays; + +public class heap_sort { + /* Heap length is n, start heapifying node i, from top to bottom */ + public static void siftDown(int[] 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 + int temp = nums[i]; + nums[i] = nums[ma]; + nums[ma] = temp; + // Loop downwards heapification + i = ma; + } + } + + /* Heap sort */ + public static void heapSort(int[] nums) { + // Build heap operation: heapify all nodes except leaves + for (int i = nums.length / 2 - 1; i >= 0; i--) { + siftDown(nums, nums.length, i); + } + // Extract the largest element from the heap and repeat for n-1 rounds + for (int i = nums.length - 1; i > 0; i--) { + // Swap the root node with the rightmost leaf node (swap the first element with the last element) + int tmp = nums[0]; + nums[0] = nums[i]; + nums[i] = tmp; + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0); + } + } + + public static void main(String[] args) { + int[] nums = { 4, 1, 3, 1, 5, 2 }; + heapSort(nums); + System.out.println("After heap sort, nums = " + Arrays.toString(nums)); + } +} diff --git a/en/codes/java/chapter_sorting/insertion_sort.java b/en/codes/java/chapter_sorting/insertion_sort.java new file mode 100644 index 0000000000..137552d518 --- /dev/null +++ b/en/codes/java/chapter_sorting/insertion_sort.java @@ -0,0 +1,31 @@ +/** + * File: insertion_sort.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +public class insertion_sort { + /* Insertion sort */ + static void insertionSort(int[] nums) { + // Outer loop: sorted range is [0, i-1] + for (int i = 1; i < nums.length; i++) { + int base = nums[i], j = i - 1; + // Inner loop: insert base into the correct position within the sorted 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 + } + } + + public static void main(String[] args) { + int[] nums = { 4, 1, 3, 1, 5, 2 }; + insertionSort(nums); + System.out.println("After insertion sort, nums = " + Arrays.toString(nums)); + } +} diff --git a/en/codes/java/chapter_sorting/merge_sort.java b/en/codes/java/chapter_sorting/merge_sort.java new file mode 100644 index 0000000000..d4bebc0bab --- /dev/null +++ b/en/codes/java/chapter_sorting/merge_sort.java @@ -0,0 +1,58 @@ +/** + * File: merge_sort.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +public class merge_sort { + /* Merge left subarray and right subarray */ + static void merge(int[] nums, int left, int mid, int right) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + int[] tmp = new int[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.length; k++) { + nums[left + k] = tmp[k]; + } + } + + /* Merge sort */ + static void mergeSort(int[] 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); + } + + public static void main(String[] args) { + /* Merge sort */ + int[] nums = { 7, 3, 2, 6, 0, 1, 5, 4 }; + mergeSort(nums, 0, nums.length - 1); + System.out.println("After merge sort, nums = " + Arrays.toString(nums)); + } +} diff --git a/en/codes/java/chapter_sorting/quick_sort.java b/en/codes/java/chapter_sorting/quick_sort.java new file mode 100644 index 0000000000..aa037eeee9 --- /dev/null +++ b/en/codes/java/chapter_sorting/quick_sort.java @@ -0,0 +1,158 @@ +/** + * File: quick_sort.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +/* Quick sort class */ +class QuickSort { + /* Swap elements */ + static void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Partition */ + static int partition(int[] nums, int left, int right) { + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // Search from right to left for the first element smaller than the pivot + 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 + } + + /* Quick sort */ + public static void quickSort(int[] 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 { + /* Swap elements */ + static void swap(int[] 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(int[] nums, int left, int mid, int right) { + int l = nums[left], m = nums[mid], r = nums[right]; + if ((l <= m && m <= r) || (r <= m && m <= l)) + return mid; // m is between l and r + 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(int[] nums, int left, int right) { + // Select the median of three candidate elements + int med = medianThree(nums, left, (left + right) / 2, right); + // Swap the median to the array's leftmost position + swap(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 + } + + /* Quick sort */ + public static void quickSort(int[] 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 { + /* Swap elements */ + static void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Partition */ + static int partition(int[] nums, int left, int right) { + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // Search from right to left for the first element smaller than the pivot + 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 + } + + /* Quick sort (tail recursion optimization) */ + public static void quickSort(int[] 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] + } + } + } +} + +public class quick_sort { + public static void main(String[] args) { + /* Quick sort */ + int[] nums = { 2, 4, 1, 0, 3, 5 }; + QuickSort.quickSort(nums, 0, nums.length - 1); + System.out.println("After quick sort, nums = " + Arrays.toString(nums)); + + /* Quick sort (median pivot optimization) */ + int[] nums1 = { 2, 4, 1, 0, 3, 5 }; + QuickSortMedian.quickSort(nums1, 0, nums1.length - 1); + System.out.println("After quick sort with median pivot optimization, nums1 = " + Arrays.toString(nums1)); + + /* Quick sort (tail recursion optimization) */ + int[] nums2 = { 2, 4, 1, 0, 3, 5 }; + QuickSortTailCall.quickSort(nums2, 0, nums2.length - 1); + System.out.println("After quick sort with tail recursion optimization, nums2 = " + Arrays.toString(nums2)); + } +} diff --git a/en/codes/java/chapter_sorting/radix_sort.java b/en/codes/java/chapter_sorting/radix_sort.java new file mode 100644 index 0000000000..28c295f76e --- /dev/null +++ b/en/codes/java/chapter_sorting/radix_sort.java @@ -0,0 +1,69 @@ +/** + * File: radix_sort.java + * Created Time: 2023-01-17 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +public class radix_sort { + /* Get the k-th digit of element num, where exp = 10^(k-1) */ + static 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) */ + static void countingSortDigit(int[] nums, int exp) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + int[] counter = new int[10]; + int n = nums.length; + // 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 + int[] res = new int[n]; + for (int i = n - 1; i >= 0; i--) { + int d = digit(nums[i], exp); + int j = counter[d] - 1; // Get the index j for d in the array + 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 */ + static void radixSort(int[] nums) { + // Get the maximum element of the array, used to determine the maximum number of digits + int m = Integer.MIN_VALUE; + for (int num : nums) + if (num > m) + m = num; + // Traverse from the lowest to the highest digit + 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); + } + } + + public static void main(String[] args) { + // Radix sort + int[] nums = { 10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996 }; + radixSort(nums); + System.out.println("After radix sort, nums = " + Arrays.toString(nums)); + } +} diff --git a/en/codes/java/chapter_sorting/selection_sort.java b/en/codes/java/chapter_sorting/selection_sort.java new file mode 100644 index 0000000000..6389cd22c2 --- /dev/null +++ b/en/codes/java/chapter_sorting/selection_sort.java @@ -0,0 +1,35 @@ +/** + * File: selection_sort.java + * Created Time: 2023-05-23 + * Author: krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.Arrays; + +public class selection_sort { + /* Selection sort */ + public static void selectionSort(int[] nums) { + int n = nums.length; + // 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 + int temp = nums[i]; + nums[i] = nums[k]; + nums[k] = temp; + } + } + + public static void main(String[] args) { + int[] nums = { 4, 1, 3, 1, 5, 2 }; + selectionSort(nums); + System.out.println("After selection sort, nums = " + Arrays.toString(nums)); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/array_deque.java b/en/codes/java/chapter_stack_and_queue/array_deque.java new file mode 100644 index 0000000000..13cf8ac85e --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/array_deque.java @@ -0,0 +1,151 @@ +/** + * File: array_deque.java + * Created Time: 2023-02-16 + * Author: krahets (krahets@163.com), FangYuan33 (374072213@qq.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +/* Double-ended queue class based on circular array */ +class ArrayDeque { + private int[] nums; // Array used to store elements of the double-ended queue + private int front; // Front pointer, pointing to the front element + private int queSize; // Length of the double-ended queue + + /* Constructor */ + public ArrayDeque(int capacity) { + this.nums = new int[capacity]; + front = queSize = 0; + } + + /* Get the capacity of the double-ended queue */ + public int capacity() { + return nums.length; + } + + /* Get the length of the double-ended queue */ + public int size() { + return queSize; + } + + /* Determine if the double-ended queue is empty */ + public boolean isEmpty() { + return queSize == 0; + } + + /* Calculate circular array index */ + private 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 */ + public void pushFirst(int num) { + if (queSize == capacity()) { + System.out.println("Double-ended queue is full"); + 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 */ + public void pushLast(int num) { + if (queSize == capacity()) { + System.out.println("Double-ended queue is full"); + 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 */ + public int popFirst() { + int num = peekFirst(); + // Move front pointer one position backward + front = index(front + 1); + queSize--; + return num; + } + + /* Rear dequeue */ + public int popLast() { + int num = peekLast(); + queSize--; + return num; + } + + /* Access front element */ + public int peekFirst() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return nums[front]; + } + + /* Access rear element */ + public int peekLast() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + // Calculate rear element index + int last = index(front + queSize - 1); + return nums[last]; + } + + /* Return array for printing */ + public int[] toArray() { + // Only convert elements within valid length range + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[index(j)]; + } + return res; + } +} + +public class array_deque { + public static void main(String[] args) { + /* Initialize double-ended queue */ + ArrayDeque deque = new ArrayDeque(10); + deque.pushLast(3); + deque.pushLast(2); + deque.pushLast(5); + System.out.println("Double-ended queue deque = " + Arrays.toString(deque.toArray())); + + /* Access element */ + int peekFirst = deque.peekFirst(); + System.out.println("Front element peekFirst = " + peekFirst); + int peekLast = deque.peekLast(); + System.out.println("Back element peekLast = " + peekLast); + + /* Element enqueue */ + deque.pushLast(4); + System.out.println("Element 4 enqueued at the tail, deque = " + Arrays.toString(deque.toArray())); + deque.pushFirst(1); + System.out.println("Element 1 enqueued at the head, deque = " + Arrays.toString(deque.toArray())); + + /* Element dequeue */ + int popLast = deque.popLast(); + System.out.println("Deque tail element = " + popLast + ", after dequeuing from the tail" + Arrays.toString(deque.toArray())); + int popFirst = deque.popFirst(); + System.out.println("Deque front element = " + popFirst + ", after dequeuing from the front" + Arrays.toString(deque.toArray())); + + /* Get the length of the double-ended queue */ + int size = deque.size(); + System.out.println("Length of the double-ended queue size = " + size); + + /* Determine if the double-ended queue is empty */ + boolean isEmpty = deque.isEmpty(); + System.out.println("Is the double-ended queue empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/array_queue.java b/en/codes/java/chapter_stack_and_queue/array_queue.java new file mode 100644 index 0000000000..aaf0ba370c --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/array_queue.java @@ -0,0 +1,115 @@ +/** + * File: array_queue.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +/* Queue class based on circular array */ +class ArrayQueue { + private int[] nums; // Array for storing queue elements + private int front; // Front pointer, pointing to the front element + private int queSize; // Queue length + + public ArrayQueue(int capacity) { + nums = new int[capacity]; + front = queSize = 0; + } + + /* Get the capacity of the queue */ + public int capacity() { + return nums.length; + } + + /* Get the length of the queue */ + public int size() { + return queSize; + } + + /* Determine if the queue is empty */ + public boolean isEmpty() { + return queSize == 0; + } + + /* Enqueue */ + public void push(int num) { + if (queSize == capacity()) { + System.out.println("Queue is full"); + 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) % capacity(); + // Add num to the rear + nums[rear] = num; + queSize++; + } + + /* Dequeue */ + public 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) % capacity(); + queSize--; + return num; + } + + /* Access front element */ + public int peek() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return nums[front]; + } + + /* Return array */ + public int[] toArray() { + // Only convert elements within valid length range + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[j % capacity()]; + } + return res; + } +} + +public class array_queue { + public static void main(String[] args) { + /* 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); + System.out.println("Queue queue = " + Arrays.toString(queue.toArray())); + + /* Access front element */ + int peek = queue.peek(); + System.out.println("Front element peek = " + peek); + + /* Element dequeue */ + int pop = queue.pop(); + System.out.println("Dequeued element = " + pop + ", after dequeuing" + Arrays.toString(queue.toArray())); + + /* Get the length of the queue */ + int size = queue.size(); + System.out.println("Length of the queue size = " + size); + + /* Determine if the queue is empty */ + boolean isEmpty = queue.isEmpty(); + System.out.println("Is the queue empty = " + isEmpty); + + /* Test circular array */ + for (int i = 0; i < 10; i++) { + queue.push(i); + queue.pop(); + System.out.println("After the " + i + "th round of enqueueing + dequeuing, queue = " + Arrays.toString(queue.toArray())); + } + } +} diff --git a/en/codes/java/chapter_stack_and_queue/array_stack.java b/en/codes/java/chapter_stack_and_queue/array_stack.java new file mode 100644 index 0000000000..757ebf3b2b --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/array_stack.java @@ -0,0 +1,84 @@ +/** + * File: array_stack.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +/* Stack class based on array */ +class ArrayStack { + private ArrayList stack; + + public ArrayStack() { + // Initialize the list (dynamic array) + stack = new ArrayList<>(); + } + + /* Get the length of the stack */ + public int size() { + return stack.size(); + } + + /* Determine if the stack is empty */ + public boolean isEmpty() { + return size() == 0; + } + + /* Push */ + public void push(int num) { + stack.add(num); + } + + /* Pop */ + public int pop() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return stack.remove(size() - 1); + } + + /* Access stack top element */ + public int peek() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return stack.get(size() - 1); + } + + /* Convert the List to Array and return */ + public Object[] toArray() { + return stack.toArray(); + } +} + +public class array_stack { + public static void main(String[] args) { + /* Initialize stack */ + ArrayStack stack = new ArrayStack(); + + /* Element push */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + System.out.println("Stack stack = " + Arrays.toString(stack.toArray())); + + /* Access stack top element */ + int peek = stack.peek(); + System.out.println("Top element peek = " + peek); + + /* Element pop */ + int pop = stack.pop(); + System.out.println("Popped element = " + pop + ", after popping" + Arrays.toString(stack.toArray())); + + /* Get the length of the stack */ + int size = stack.size(); + System.out.println("Length of the stack size = " + size); + + /* Determine if it's empty */ + boolean isEmpty = stack.isEmpty(); + System.out.println("Is the stack empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/deque.java b/en/codes/java/chapter_stack_and_queue/deque.java new file mode 100644 index 0000000000..838f7b3fd9 --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/deque.java @@ -0,0 +1,46 @@ +/** + * File: deque.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +public class deque { + public static void main(String[] args) { + /* Initialize double-ended queue */ + Deque deque = new LinkedList<>(); + deque.offerLast(3); + deque.offerLast(2); + deque.offerLast(5); + System.out.println("Double-ended queue deque = " + deque); + + /* Access element */ + int peekFirst = deque.peekFirst(); + System.out.println("Front element peekFirst = " + peekFirst); + int peekLast = deque.peekLast(); + System.out.println("Back element peekLast = " + peekLast); + + /* Element enqueue */ + deque.offerLast(4); + System.out.println("Element 4 enqueued at the tail, deque = " + deque); + deque.offerFirst(1); + System.out.println("Element 1 enqueued at the head, deque = " + deque); + + /* Element dequeue */ + int popLast = deque.pollLast(); + System.out.println("Deque tail element = " + popLast + ", after dequeuing from the tail" + deque); + int popFirst = deque.pollFirst(); + System.out.println("Deque front element = " + popFirst + ", after dequeuing from the front" + deque); + + /* Get the length of the double-ended queue */ + int size = deque.size(); + System.out.println("Length of the double-ended queue size = " + size); + + /* Determine if the double-ended queue is empty */ + boolean isEmpty = deque.isEmpty(); + System.out.println("Is the double-ended queue empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java b/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java new file mode 100644 index 0000000000..ccf765531c --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java @@ -0,0 +1,175 @@ +/** + * File: linkedlist_deque.java + * Created Time: 2023-01-20 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +/* Double-linked list node */ +class ListNode { + int val; // Node value + ListNode next; // Reference to the next node + ListNode prev; // Reference to predecessor node + + ListNode(int val) { + this.val = val; + prev = next = null; + } +} + +/* Double-ended queue class based on double-linked list */ +class LinkedListDeque { + private ListNode front, rear; // Front node front, back node rear + private int queSize = 0; // Length of the double-ended queue + + public LinkedListDeque() { + front = rear = null; + } + + /* Get the length of the double-ended queue */ + public int size() { + return queSize; + } + + /* Determine if the double-ended queue is empty */ + public boolean isEmpty() { + return size() == 0; + } + + /* Enqueue operation */ + private void push(int num, boolean isFront) { + ListNode node = new ListNode(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 */ + public void pushFirst(int num) { + push(num, true); + } + + /* Rear enqueue */ + public void pushLast(int num) { + push(num, false); + } + + /* Dequeue operation */ + private int pop(boolean isFront) { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + int val; + // Front dequeue operation + if (isFront) { + val = front.val; // Temporarily store the head node value + // Remove head node + ListNode fNext = front.next; + if (fNext != null) { + fNext.prev = null; + front.next = null; + } + front = fNext; // Update head node + // Rear dequeue operation + } else { + val = rear.val; // Temporarily store the tail node value + // Remove tail node + ListNode rPrev = rear.prev; + if (rPrev != null) { + rPrev.next = null; + rear.prev = null; + } + rear = rPrev; // Update tail node + } + queSize--; // Update queue length + return val; + } + + /* Front dequeue */ + public int popFirst() { + return pop(true); + } + + /* Rear dequeue */ + public int popLast() { + return pop(false); + } + + /* Access front element */ + public int peekFirst() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return front.val; + } + + /* Access rear element */ + public int peekLast() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return rear.val; + } + + /* Return array for printing */ + public int[] toArray() { + ListNode node = front; + int[] res = new int[size()]; + for (int i = 0; i < res.length; i++) { + res[i] = node.val; + node = node.next; + } + return res; + } +} + +public class linkedlist_deque { + public static void main(String[] args) { + /* Initialize double-ended queue */ + LinkedListDeque deque = new LinkedListDeque(); + deque.pushLast(3); + deque.pushLast(2); + deque.pushLast(5); + System.out.println("Double-ended queue deque = " + Arrays.toString(deque.toArray())); + + /* Access element */ + int peekFirst = deque.peekFirst(); + System.out.println("Front element peekFirst = " + peekFirst); + int peekLast = deque.peekLast(); + System.out.println("Back element peekLast = " + peekLast); + + /* Element enqueue */ + deque.pushLast(4); + System.out.println("Element 4 enqueued at the tail, deque = " + Arrays.toString(deque.toArray())); + deque.pushFirst(1); + System.out.println("Element 1 enqueued at the head, deque = " + Arrays.toString(deque.toArray())); + + /* Element dequeue */ + int popLast = deque.popLast(); + System.out.println("Deque tail element = " + popLast + ", after dequeuing from the tail" + Arrays.toString(deque.toArray())); + int popFirst = deque.popFirst(); + System.out.println("Deque front element = " + popFirst + ", after dequeuing from the front" + Arrays.toString(deque.toArray())); + + /* Get the length of the double-ended queue */ + int size = deque.size(); + System.out.println("Length of the double-ended queue size = " + size); + + /* Determine if the double-ended queue is empty */ + boolean isEmpty = deque.isEmpty(); + System.out.println("Is the double-ended queue empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java b/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java new file mode 100644 index 0000000000..d60b27f4dc --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java @@ -0,0 +1,104 @@ +/** + * File: linkedlist_queue.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +/* Queue class based on linked list */ +class LinkedListQueue { + private ListNode front, rear; // Front node front, back node rear + private int queSize = 0; + + public LinkedListQueue() { + front = null; + rear = null; + } + + /* Get the length of the queue */ + public int size() { + return queSize; + } + + /* Determine if the queue is empty */ + public boolean isEmpty() { + return size() == 0; + } + + /* Enqueue */ + public 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 == null) { + 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 */ + public int pop() { + int num = peek(); + // Remove head node + front = front.next; + queSize--; + return num; + } + + /* Access front element */ + public int peek() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return front.val; + } + + /* Convert the linked list to Array and return */ + public int[] toArray() { + ListNode node = front; + int[] res = new int[size()]; + for (int i = 0; i < res.length; i++) { + res[i] = node.val; + node = node.next; + } + return res; + } +} + +public class linkedlist_queue { + public static void main(String[] args) { + /* Initialize queue */ + LinkedListQueue queue = new LinkedListQueue(); + + /* Element enqueue */ + queue.push(1); + queue.push(3); + queue.push(2); + queue.push(5); + queue.push(4); + System.out.println("Queue queue = " + Arrays.toString(queue.toArray())); + + /* Access front element */ + int peek = queue.peek(); + System.out.println("Front element peek = " + peek); + + /* Element dequeue */ + int pop = queue.pop(); + System.out.println("Dequeued element = " + pop + ", after dequeuing" + Arrays.toString(queue.toArray())); + + /* Get the length of the queue */ + int size = queue.size(); + System.out.println("Length of the queue size = " + size); + + /* Determine if the queue is empty */ + boolean isEmpty = queue.isEmpty(); + System.out.println("Is the queue empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java b/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java new file mode 100644 index 0000000000..c4438def8e --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java @@ -0,0 +1,95 @@ +/** + * File: linkedlist_stack.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; +import utils.*; + +/* Stack class based on linked list */ +class LinkedListStack { + private ListNode stackPeek; // Use the head node as the top of the stack + private int stkSize = 0; // Length of the stack + + public LinkedListStack() { + stackPeek = null; + } + + /* Get the length of the stack */ + public int size() { + return stkSize; + } + + /* Determine if the stack is empty */ + public boolean isEmpty() { + return size() == 0; + } + + /* Push */ + public void push(int num) { + ListNode node = new ListNode(num); + node.next = stackPeek; + stackPeek = node; + stkSize++; + } + + /* Pop */ + public int pop() { + int num = peek(); + stackPeek = stackPeek.next; + stkSize--; + return num; + } + + /* Access stack top element */ + public int peek() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return stackPeek.val; + } + + /* Convert the List to Array and return */ + public int[] toArray() { + ListNode node = stackPeek; + int[] res = new int[size()]; + for (int i = res.length - 1; i >= 0; i--) { + res[i] = node.val; + node = node.next; + } + return res; + } +} + +public class linkedlist_stack { + public static void main(String[] args) { + /* Initialize stack */ + LinkedListStack stack = new LinkedListStack(); + + /* Element push */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + System.out.println("Stack stack = " + Arrays.toString(stack.toArray())); + + /* Access stack top element */ + int peek = stack.peek(); + System.out.println("Top element peek = " + peek); + + /* Element pop */ + int pop = stack.pop(); + System.out.println("Popped element = " + pop + ", after popping" + Arrays.toString(stack.toArray())); + + /* Get the length of the stack */ + int size = stack.size(); + System.out.println("Length of the stack size = " + size); + + /* Determine if it's empty */ + boolean isEmpty = stack.isEmpty(); + System.out.println("Is the stack empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/queue.java b/en/codes/java/chapter_stack_and_queue/queue.java new file mode 100644 index 0000000000..7b37e41c38 --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/queue.java @@ -0,0 +1,40 @@ +/** + * File: queue.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +public class queue { + public static void main(String[] args) { + /* Initialize queue */ + Queue queue = new LinkedList<>(); + + /* Element enqueue */ + queue.offer(1); + queue.offer(3); + queue.offer(2); + queue.offer(5); + queue.offer(4); + System.out.println("Queue queue = " + queue); + + /* Access front element */ + int peek = queue.peek(); + System.out.println("Front element peek = " + peek); + + /* Element dequeue */ + int pop = queue.poll(); + System.out.println("Dequeued element = " + pop + ", after dequeuing" + queue); + + /* Get the length of the queue */ + int size = queue.size(); + System.out.println("Length of the queue size = " + size); + + /* Determine if the queue is empty */ + boolean isEmpty = queue.isEmpty(); + System.out.println("Is the queue empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_stack_and_queue/stack.java b/en/codes/java/chapter_stack_and_queue/stack.java new file mode 100644 index 0000000000..5270c018f1 --- /dev/null +++ b/en/codes/java/chapter_stack_and_queue/stack.java @@ -0,0 +1,40 @@ +/** + * File: stack.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_stack_and_queue; + +import java.util.*; + +public class stack { + public static void main(String[] args) { + /* Initialize stack */ + Stack stack = new Stack<>(); + + /* Element push */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + System.out.println("Stack stack = " + stack); + + /* Access stack top element */ + int peek = stack.peek(); + System.out.println("Top element peek = " + peek); + + /* Element pop */ + int pop = stack.pop(); + System.out.println("Popped element = " + pop + ", after popping" + stack); + + /* Get the length of the stack */ + int size = stack.size(); + System.out.println("Length of the stack size = " + size); + + /* Determine if it's empty */ + boolean isEmpty = stack.isEmpty(); + System.out.println("Is the stack empty = " + isEmpty); + } +} diff --git a/en/codes/java/chapter_tree/array_binary_tree.java b/en/codes/java/chapter_tree/array_binary_tree.java new file mode 100644 index 0000000000..9adb135976 --- /dev/null +++ b/en/codes/java/chapter_tree/array_binary_tree.java @@ -0,0 +1,136 @@ +/** + * File: array_binary_tree.java + * Created Time: 2023-07-19 + * Author: krahets (krahets@163.com) + */ + +package chapter_tree; + +import utils.*; +import java.util.*; + +/* Array-based binary tree class */ +class ArrayBinaryTree { + private List tree; + + /* Constructor */ + public ArrayBinaryTree(List arr) { + tree = new ArrayList<>(arr); + } + + /* List capacity */ + public int size() { + return tree.size(); + } + + /* Get the value of the node at index i */ + public Integer val(int i) { + // If the index is out of bounds, return null, representing an empty spot + if (i < 0 || i >= size()) + return null; + return tree.get(i); + } + + /* Get the index of the left child of the node at index i */ + public Integer left(int i) { + return 2 * i + 1; + } + + /* Get the index of the right child of the node at index i */ + public Integer right(int i) { + return 2 * i + 2; + } + + /* Get the index of the parent of the node at index i */ + public Integer parent(int i) { + return (i - 1) / 2; + } + + /* Level-order traversal */ + public List levelOrder() { + List res = new ArrayList<>(); + // Traverse array + for (int i = 0; i < size(); i++) { + if (val(i) != null) + res.add(val(i)); + } + return res; + } + + /* Depth-first traversal */ + private void dfs(Integer i, String order, List res) { + // If it is an empty spot, return + if (val(i) == null) + return; + // Pre-order traversal + if ("pre".equals(order)) + res.add(val(i)); + dfs(left(i), order, res); + // In-order traversal + if ("in".equals(order)) + res.add(val(i)); + dfs(right(i), order, res); + // Post-order traversal + if ("post".equals(order)) + res.add(val(i)); + } + + /* Pre-order traversal */ + public List preOrder() { + List res = new ArrayList<>(); + dfs(0, "pre", res); + return res; + } + + /* In-order traversal */ + public List inOrder() { + List res = new ArrayList<>(); + dfs(0, "in", res); + return res; + } + + /* Post-order traversal */ + public List postOrder() { + List res = new ArrayList<>(); + dfs(0, "post", res); + return res; + } +} + +public class array_binary_tree { + public static void main(String[] args) { + // Initialize binary tree + // Use a specific function to convert an array into a binary tree + List arr = Arrays.asList(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15); + + TreeNode root = TreeNode.listToTree(arr); + System.out.println("\nInitialize binary tree\n"); + System.out.println("Array representation of the binary tree:"); + System.out.println(arr); + System.out.println("Linked list representation of the binary tree:"); + PrintUtil.printTree(root); + + // Array-based binary tree class + ArrayBinaryTree abt = new ArrayBinaryTree(arr); + + // Access node + int i = 1; + Integer l = abt.left(i); + Integer r = abt.right(i); + Integer p = abt.parent(i); + System.out.println("\nThe current node's index is " + i + ", value = " + abt.val(i)); + System.out.println("Its left child's index is " + l + ", value = " + (l == null ? "null" : abt.val(l))); + System.out.println("Its right child's index is " + r + ", value = " + (r == null ? "null" : abt.val(r))); + System.out.println("Its parent's index is " + p + ", value = " + (p == null ? "null" : abt.val(p))); + + // Traverse tree + List res = abt.levelOrder(); + System.out.println("\nLevel-order traversal is:" + res); + res = abt.preOrder(); + System.out.println("Pre-order traversal is:" + res); + res = abt.inOrder(); + System.out.println("In-order traversal is:" + res); + res = abt.postOrder(); + System.out.println("Post-order traversal is:" + res); + } +} diff --git a/en/codes/java/chapter_tree/avl_tree.java b/en/codes/java/chapter_tree/avl_tree.java new file mode 100644 index 0000000000..6f34510473 --- /dev/null +++ b/en/codes/java/chapter_tree/avl_tree.java @@ -0,0 +1,220 @@ +/** + * File: avl_tree.java + * Created Time: 2022-12-10 + * Author: krahets (krahets@163.com) + */ + +package chapter_tree; + +import utils.*; + +/* AVL tree */ +class AVLTree { + TreeNode root; // Root node + + /* Get node height */ + public int height(TreeNode node) { + // Empty node height is -1, leaf node height is 0 + return node == null ? -1 : node.height; + } + + /* Update node height */ + private void updateHeight(TreeNode node) { + // Node height equals the height of the tallest subtree + 1 + node.height = Math.max(height(node.left), height(node.right)) + 1; + } + + /* Get balance factor */ + public int balanceFactor(TreeNode node) { + // Empty node balance factor is 0 + if (node == null) + return 0; + // Node balance factor = left subtree height - right subtree height + return height(node.left) - height(node.right); + } + + /* Right rotation operation */ + private 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 */ + private 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 */ + private 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; + } + + /* Insert node */ + public void insert(int val) { + root = insertHelper(root, val); + } + + /* Recursively insert node (helper method) */ + private TreeNode insertHelper(TreeNode node, int val) { + if (node == null) + 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; + } + + /* Remove node */ + public void remove(int val) { + root = removeHelper(root, val); + } + + /* Recursively remove node (helper method) */ + private TreeNode removeHelper(TreeNode node, int val) { + if (node == null) + return null; + /* 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 == null || node.right == null) { + TreeNode child = node.left != null ? node.left : node.right; + // Number of child nodes = 0, remove node and return + if (child == null) + return null; + // Number of child nodes = 1, remove node + else + 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 != null) { + temp = temp.left; + } + node.right = removeHelper(node.right, temp.val); + node.val = temp.val; + } + } + 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; + } + + /* Search node */ + public TreeNode search(int val) { + TreeNode cur = root; + // Loop find, break after passing leaf nodes + while (cur != null) { + // 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; + } +} + +public class avl_tree { + static void testInsert(AVLTree tree, int val) { + tree.insert(val); + System.out.println("\nAfter inserting node " + val + ", the AVL tree is "); + PrintUtil.printTree(tree.root); + } + + static void testRemove(AVLTree tree, int val) { + tree.remove(val); + System.out.println("\nAfter removing node " + val + ", the AVL tree is "); + PrintUtil.printTree(tree.root); + } + + public static void main(String[] args) { + /* Initialize empty AVL tree */ + AVLTree avlTree = new 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); + System.out.println("\nThe found node object is " + node + ", node value = " + node.val); + } +} diff --git a/en/codes/java/chapter_tree/binary_search_tree.java b/en/codes/java/chapter_tree/binary_search_tree.java new file mode 100644 index 0000000000..365290ac8b --- /dev/null +++ b/en/codes/java/chapter_tree/binary_search_tree.java @@ -0,0 +1,158 @@ +/** + * File: binary_search_tree.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_tree; + +import utils.*; + +/* Binary search tree */ +class BinarySearchTree { + private TreeNode root; + + /* Constructor */ + public BinarySearchTree() { + // Initialize empty tree + root = null; + } + + /* Get binary tree root node */ + public TreeNode getRoot() { + return root; + } + + /* Search node */ + public TreeNode search(int num) { + TreeNode cur = root; + // Loop find, break after passing leaf nodes + while (cur != null) { + // 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 */ + public void insert(int num) { + // If tree is empty, initialize root node + if (root == null) { + root = new TreeNode(num); + return; + } + TreeNode cur = root, pre = null; + // Loop find, break after passing leaf nodes + while (cur != null) { + // 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 */ + public void remove(int num) { + // If tree is empty, return + if (root == null) + return; + TreeNode cur = root, pre = null; + // Loop find, break after passing leaf nodes + while (cur != null) { + // 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 == null) + return; + // Number of child nodes = 0 or 1 + if (cur.left == null || cur.right == null) { + // When the number of child nodes = 0/1, child = null/that child node + TreeNode child = cur.left != null ? 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; + } + } + // Number of child nodes = 2 + else { + // Get the next node in in-order traversal of cur + TreeNode tmp = cur.right; + while (tmp.left != null) { + tmp = tmp.left; + } + // Recursively remove node tmp + remove(tmp.val); + // Replace cur with tmp + cur.val = tmp.val; + } + } +} + +public class binary_search_tree { + public static void main(String[] args) { + /* 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 + int[] nums = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + for (int num : nums) { + bst.insert(num); + } + System.out.println("\nInitialized binary tree is\n"); + PrintUtil.printTree(bst.getRoot()); + + /* Search node */ + TreeNode node = bst.search(7); + System.out.println("\nThe found node object is " + node + ", node value = " + node.val); + + /* Insert node */ + bst.insert(16); + System.out.println("\nAfter inserting node 16, the binary tree is\n"); + PrintUtil.printTree(bst.getRoot()); + + /* Remove node */ + bst.remove(1); + System.out.println("\nAfter removing node 1, the binary tree is\n"); + PrintUtil.printTree(bst.getRoot()); + bst.remove(2); + System.out.println("\nAfter removing node 2, the binary tree is\n"); + PrintUtil.printTree(bst.getRoot()); + bst.remove(4); + System.out.println("\nAfter removing node 4, the binary tree is\n"); + PrintUtil.printTree(bst.getRoot()); + } +} diff --git a/en/codes/java/chapter_tree/binary_tree.java b/en/codes/java/chapter_tree/binary_tree.java new file mode 100644 index 0000000000..df209d2e4c --- /dev/null +++ b/en/codes/java/chapter_tree/binary_tree.java @@ -0,0 +1,40 @@ +/** + * File: binary_tree.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_tree; + +import utils.*; + +public class binary_tree { + public static void main(String[] args) { + /* 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; + System.out.println("\nInitialize binary tree\n"); + PrintUtil.printTree(n1); + + /* Insert and remove nodes */ + TreeNode P = new TreeNode(0); + // Insert node P between n1 -> n2 + n1.left = P; + P.left = n2; + System.out.println("\nAfter inserting node P\n"); + PrintUtil.printTree(n1); + // Remove node P + n1.left = n2; + System.out.println("\nAfter removing node P\n"); + PrintUtil.printTree(n1); + } +} diff --git a/en/codes/java/chapter_tree/binary_tree_bfs.java b/en/codes/java/chapter_tree/binary_tree_bfs.java new file mode 100644 index 0000000000..3a398500ff --- /dev/null +++ b/en/codes/java/chapter_tree/binary_tree_bfs.java @@ -0,0 +1,42 @@ +/** + * File: binary_tree_bfs.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_tree; + +import utils.*; +import java.util.*; + +public class binary_tree_bfs { + /* Level-order traversal */ + static List levelOrder(TreeNode root) { + // Initialize queue, add root node + Queue queue = new LinkedList<>(); + queue.add(root); + // Initialize a list to store the traversal sequence + List list = new ArrayList<>(); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); // Queue dequeues + list.add(node.val); // Save node value + if (node.left != null) + queue.offer(node.left); // Left child node enqueues + if (node.right != null) + queue.offer(node.right); // Right child node enqueues + } + return list; + } + + public static void main(String[] args) { + /* Initialize binary tree */ + // Use a specific function to convert an array into a binary tree + TreeNode root = TreeNode.listToTree(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); + System.out.println("\nInitialize binary tree\n"); + PrintUtil.printTree(root); + + /* Level-order traversal */ + List list = levelOrder(root); + System.out.println("\nPrint sequence of nodes from level-order traversal = " + list); + } +} diff --git a/en/codes/java/chapter_tree/binary_tree_dfs.java b/en/codes/java/chapter_tree/binary_tree_dfs.java new file mode 100644 index 0000000000..2ca14ccaa1 --- /dev/null +++ b/en/codes/java/chapter_tree/binary_tree_dfs.java @@ -0,0 +1,68 @@ +/** + * File: binary_tree_dfs.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package chapter_tree; + +import utils.*; +import java.util.*; + +public class binary_tree_dfs { + // Initialize the list for storing traversal sequences + static ArrayList list = new ArrayList<>(); + + /* Pre-order traversal */ + static void preOrder(TreeNode root) { + if (root == null) + return; + // Visit priority: root node -> left subtree -> right subtree + list.add(root.val); + preOrder(root.left); + preOrder(root.right); + } + + /* In-order traversal */ + static void inOrder(TreeNode root) { + if (root == null) + return; + // Visit priority: left subtree -> root node -> right subtree + inOrder(root.left); + list.add(root.val); + inOrder(root.right); + } + + /* Post-order traversal */ + static void postOrder(TreeNode root) { + if (root == null) + return; + // Visit priority: left subtree -> right subtree -> root node + postOrder(root.left); + postOrder(root.right); + list.add(root.val); + } + + public static void main(String[] args) { + /* Initialize binary tree */ + // Use a specific function to convert an array into a binary tree + TreeNode root = TreeNode.listToTree(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); + System.out.println("\nInitialize binary tree\n"); + PrintUtil.printTree(root); + + /* Pre-order traversal */ + list.clear(); + preOrder(root); + System.out.println("\nPrint sequence of nodes from pre-order traversal = " + list); + + /* In-order traversal */ + list.clear(); + inOrder(root); + System.out.println("\nPrint sequence of nodes from in-order traversal = " + list); + + /* Post-order traversal */ + list.clear(); + postOrder(root); + System.out.println("\nPrint sequence of nodes from post-order traversal = " + list); + } +} diff --git a/en/codes/java/utils/ListNode.java b/en/codes/java/utils/ListNode.java new file mode 100644 index 0000000000..2156d68a97 --- /dev/null +++ b/en/codes/java/utils/ListNode.java @@ -0,0 +1,28 @@ +/** + * File: ListNode.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package utils; + +/* Linked list node */ +public class ListNode { + public int val; + public ListNode next; + + public ListNode(int x) { + val = x; + } + + /* Deserialize a list into a linked list */ + public static ListNode arrToLinkedList(int[] arr) { + ListNode dum = new ListNode(0); + ListNode head = dum; + for (int val : arr) { + head.next = new ListNode(val); + head = head.next; + } + return dum.next; + } +} diff --git a/en/codes/java/utils/PrintUtil.java b/en/codes/java/utils/PrintUtil.java new file mode 100644 index 0000000000..2ce4044f8c --- /dev/null +++ b/en/codes/java/utils/PrintUtil.java @@ -0,0 +1,116 @@ +/** + * File: PrintUtil.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package utils; + +import java.util.*; + +class Trunk { + Trunk prev; + String str; + + Trunk(Trunk prev, String str) { + this.prev = prev; + this.str = str; + } +}; + +public class PrintUtil { + /* Print matrix (Array) */ + public static void printMatrix(T[][] matrix) { + System.out.println("["); + for (T[] row : matrix) { + System.out.println(" " + row + ","); + } + System.out.println("]"); + } + + /* Print matrix (List) */ + public static void printMatrix(List> matrix) { + System.out.println("["); + for (List row : matrix) { + System.out.println(" " + row + ","); + } + System.out.println("]"); + } + + /* Print linked list */ + public static void printLinkedList(ListNode head) { + List list = new ArrayList<>(); + while (head != null) { + list.add(String.valueOf(head.val)); + head = head.next; + } + System.out.println(String.join(" -> ", list)); + } + + /* Print binary tree */ + public static void printTree(TreeNode root) { + printTree(root, null, false); + } + + /** + * Print binary tree + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ + public static void printTree(TreeNode root, Trunk prev, boolean isRight) { + if (root == null) { + return; + } + + String prev_str = " "; + Trunk trunk = new Trunk(prev, prev_str); + + printTree(root.right, trunk, true); + + if (prev == null) { + trunk.str = "———"; + } else if (isRight) { + trunk.str = "/———"; + prev_str = " |"; + } else { + trunk.str = "\\———"; + prev.str = prev_str; + } + + showTrunks(trunk); + System.out.println(" " + root.val); + + if (prev != null) { + prev.str = prev_str; + } + trunk.str = " |"; + + printTree(root.left, trunk, false); + } + + public static void showTrunks(Trunk p) { + if (p == null) { + return; + } + + showTrunks(p.prev); + System.out.print(p.str); + } + + /* Print hash table */ + public static void printHashMap(Map map) { + for (Map.Entry kv : map.entrySet()) { + System.out.println(kv.getKey() + " -> " + kv.getValue()); + } + } + + /* Print heap (Priority queue) */ + public static void printHeap(Queue queue) { + List list = new ArrayList<>(queue); + System.out.print("Array representation of the heap:"); + System.out.println(list); + System.out.println("Tree representation of the heap:"); + TreeNode root = TreeNode.listToTree(list); + printTree(root); + } +} diff --git a/en/codes/java/utils/TreeNode.java b/en/codes/java/utils/TreeNode.java new file mode 100644 index 0000000000..b9af43219d --- /dev/null +++ b/en/codes/java/utils/TreeNode.java @@ -0,0 +1,73 @@ +/** + * File: TreeNode.java + * Created Time: 2022-11-25 + * Author: krahets (krahets@163.com) + */ + +package utils; + +import java.util.*; + +/* Binary tree node class */ +public class TreeNode { + public int val; // Node value + public int height; // Node height + public TreeNode left; // Reference to the left child node + public TreeNode right; // Reference to the right child node + + /* Constructor */ + public TreeNode(int x) { + val = x; + } + + // 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 */ + private static TreeNode listToTreeDFS(List arr, int i) { + if (i < 0 || i >= arr.size() || arr.get(i) == null) { + return null; + } + TreeNode root = new TreeNode(arr.get(i)); + root.left = listToTreeDFS(arr, 2 * i + 1); + root.right = listToTreeDFS(arr, 2 * i + 2); + return root; + } + + /* Deserialize a list into a binary tree */ + public static TreeNode listToTree(List arr) { + return listToTreeDFS(arr, 0); + } + + /* Serialize a binary tree into a list: Recursively */ + private static void treeToListDFS(TreeNode root, int i, List res) { + if (root == null) + return; + while (i >= res.size()) { + res.add(null); + } + res.set(i, root.val); + treeToListDFS(root.left, 2 * i + 1, res); + treeToListDFS(root.right, 2 * i + 2, res); + } + + /* Serialize a binary tree into a list */ + public static List treeToList(TreeNode root) { + List res = new ArrayList<>(); + treeToListDFS(root, 0, res); + return res; + } +} diff --git a/en/codes/java/utils/Vertex.java b/en/codes/java/utils/Vertex.java new file mode 100644 index 0000000000..92e950abe2 --- /dev/null +++ b/en/codes/java/utils/Vertex.java @@ -0,0 +1,36 @@ +/** + * File: Vertex.java + * Created Time: 2023-02-15 + * Author: krahets (krahets@163.com) + */ + +package utils; + +import java.util.*; + +/* Vertex class */ +public class Vertex { + public int val; + + public Vertex(int val) { + this.val = val; + } + + /* Input a list of values vals, return a list of vertices vets */ + public static Vertex[] valsToVets(int[] vals) { + Vertex[] vets = new Vertex[vals.length]; + for (int i = 0; i < vals.length; i++) { + vets[i] = new Vertex(vals[i]); + } + return vets; + } + + /* Input a list of vertices vets, return a list of values vals */ + public static List vetsToVals(List vets) { + List vals = new ArrayList<>(); + for (Vertex vet : vets) { + vals.add(vet.val); + } + return vals; + } +} diff --git a/en/codes/python/.gitignore b/en/codes/python/.gitignore new file mode 100644 index 0000000000..bee8a64b79 --- /dev/null +++ b/en/codes/python/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/en/codes/python/chapter_array_and_linkedlist/array.py b/en/codes/python/chapter_array_and_linkedlist/array.py new file mode 100644 index 0000000000..ab7810dcfd --- /dev/null +++ b/en/codes/python/chapter_array_and_linkedlist/array.py @@ -0,0 +1,100 @@ +""" +File: array.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + +import random + + +def random_access(nums: list[int]) -> int: + """Random access to elements""" + # Randomly select a number from the interval [0, len(nums)-1] + random_index = random.randint(0, len(nums) - 1) + # Retrieve and return a random element + random_num = nums[random_index] + return random_num + + +# Note that Python's list is a dynamic array that can be extended +# For ease of learning, this function treats the list as a static array +def extend(nums: list[int], enlarge: int) -> list[int]: + """Extend array length""" + # Initialize an extended length array + res = [0] * (len(nums) + enlarge) + # Copy all elements from the original array to the new array + for i in range(len(nums)): + res[i] = nums[i] + # Return the new array after expansion + return res + + +def insert(nums: list[int], num: int, index: int): + """Insert element num at `index`""" + # Move all elements after `index` one position backward + for i in range(len(nums) - 1, index, -1): + nums[i] = nums[i - 1] + # Assign num to the element at index + nums[index] = num + + +def remove(nums: list[int], index: int): + """Remove the element at `index`""" + # Move all elements after `index` one position forward + for i in range(index, len(nums) - 1): + nums[i] = nums[i + 1] + + +def traverse(nums: list[int]): + """Traverse array""" + count = 0 + # Traverse array by index + for i in range(len(nums)): + count += nums[i] + # Traverse array elements + for num in nums: + count += num + # Traverse both data index and elements + for i, num in enumerate(nums): + count += nums[i] + count += num + + +def find(nums: list[int], target: int) -> int: + """Search for a specified element in the array""" + for i in range(len(nums)): + if nums[i] == target: + return i + return -1 + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize an array + arr = [0] * 5 + print("Array arr =", arr) + nums = [1, 3, 2, 5, 4] + print("Array nums =", nums) + + # Random access + random_num: int = random_access(nums) + print("Retrieve a random element in nums", random_num) + + # Length extension + nums: list[int] = extend(nums, 3) + print("Extend the array length to 8, resulting in nums =", nums) + + # Insert element + insert(nums, 6, 3) + print("Insert number 6 at index 3, resulting in nums =", nums) + + # Remove element + remove(nums, 2) + print("Remove the element at index 2, resulting in nums =", nums) + + # Traverse array + traverse(nums) + + # Search for elements + index: int = find(nums, 3) + print("Search for element 3 in nums, resulting in index =", index) diff --git a/en/codes/python/chapter_array_and_linkedlist/linked_list.py b/en/codes/python/chapter_array_and_linkedlist/linked_list.py new file mode 100644 index 0000000000..c203c08629 --- /dev/null +++ b/en/codes/python/chapter_array_and_linkedlist/linked_list.py @@ -0,0 +1,85 @@ +""" +File: linked_list.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import ListNode, print_linked_list + + +def insert(n0: ListNode, P: ListNode): + """Insert node P after node n0 in the linked list""" + n1 = n0.next + P.next = n1 + n0.next = P + + +def remove(n0: ListNode): + """Remove the first node after node n0 in the linked list""" + if not n0.next: + return + # n0 -> P -> n1 + P = n0.next + n1 = P.next + n0.next = n1 + + +def access(head: ListNode, index: int) -> ListNode | None: + """Access the node at `index` in the linked list""" + for _ in range(index): + if not head: + return None + head = head.next + return head + + +def find(head: ListNode, target: int) -> int: + """Search for the first node with value target in the linked list""" + index = 0 + while head: + if head.val == target: + return index + head = head.next + index += 1 + return -1 + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize linked list + # Initialize each node + n0 = ListNode(1) + n1 = ListNode(3) + n2 = ListNode(2) + n3 = ListNode(5) + n4 = ListNode(4) + # Build references between nodes + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 + print("The initialized linked list is") + print_linked_list(n0) + + # Insert node + p = ListNode(0) + insert(n0, p) + print("Linked list after inserting the node is") + print_linked_list(n0) + + # Remove node + remove(n0) + print("Linked list after removing the node is") + print_linked_list(n0) + + # Access node + node: ListNode = access(n0, 3) + print("The value of the node at index 3 in the linked list = {}".format(node.val)) + + # Search node + index: int = find(n0, 2) + print("The index of the node with value 2 in the linked list = {}".format(index)) diff --git a/en/codes/python/chapter_array_and_linkedlist/list.py b/en/codes/python/chapter_array_and_linkedlist/list.py new file mode 100644 index 0000000000..32c2c0c24b --- /dev/null +++ b/en/codes/python/chapter_array_and_linkedlist/list.py @@ -0,0 +1,56 @@ +""" +File: list.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + +"""Driver Code""" +if __name__ == "__main__": + # Initialize list + nums: list[int] = [1, 3, 2, 5, 4] + print("\nList nums =", nums) + + # Access element + x: int = nums[1] + print("\nAccess the element at index 1, resulting in x =", x) + + # Update element + nums[1] = 0 + print("\nUpdate the element at index 1 to 0, resulting in nums =", nums) + + # Clear list + nums.clear() + print("\nAfter clearing the list, nums =", nums) + + # Add element at the end + nums.append(1) + nums.append(3) + nums.append(2) + nums.append(5) + nums.append(4) + print("\nAfter adding the element, nums =", nums) + + # Insert element in the middle + nums.insert(3, 6) + print("\nInsert number 6 at index 3, resulting in nums =", nums) + + # Remove element + nums.pop(3) + print("\nRemove the element at index 3, resulting in nums =", nums) + + # Traverse the list by index + count = 0 + for i in range(len(nums)): + count += nums[i] + # Traverse the list elements + for num in nums: + count += num + + # Concatenate two lists + nums1 = [6, 8, 7, 10, 9] + nums += nums1 + print("\nConcatenate list nums1 to nums, resulting in nums =", nums) + + # Sort list + nums.sort() + print("\nAfter sorting the list, nums =", nums) diff --git a/en/codes/python/chapter_array_and_linkedlist/my_list.py b/en/codes/python/chapter_array_and_linkedlist/my_list.py new file mode 100644 index 0000000000..c1c7361a79 --- /dev/null +++ b/en/codes/python/chapter_array_and_linkedlist/my_list.py @@ -0,0 +1,118 @@ +""" +File: my_list.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + + +class MyList: + """List class""" + + def __init__(self): + """Constructor""" + self._capacity: int = 10 # List capacity + self._arr: list[int] = [0] * self._capacity # Array (stores list elements) + self._size: int = 0 # List length (current number of elements) + self._extend_ratio: int = 2 # Multiple for each list expansion + + def size(self) -> int: + """Get list length (current number of elements)""" + return self._size + + def capacity(self) -> int: + """Get list capacity""" + return self._capacity + + def get(self, index: int) -> int: + """Access element""" + # If the index is out of bounds, throw an exception, as below + if index < 0 or index >= self._size: + raise IndexError("Index out of bounds") + return self._arr[index] + + def set(self, num: int, index: int): + """Update element""" + if index < 0 or index >= self._size: + raise IndexError("Index out of bounds") + self._arr[index] = num + + def add(self, num: int): + """Add element at the end""" + # When the number of elements exceeds capacity, trigger the expansion mechanism + if self.size() == self.capacity(): + self.extend_capacity() + self._arr[self._size] = num + self._size += 1 + + def insert(self, num: int, index: int): + """Insert element in the middle""" + if index < 0 or index >= self._size: + raise IndexError("Index out of bounds") + # When the number of elements exceeds capacity, trigger the expansion mechanism + if self._size == self.capacity(): + self.extend_capacity() + # Move all elements after `index` one position backward + for j in range(self._size - 1, index - 1, -1): + self._arr[j + 1] = self._arr[j] + self._arr[index] = num + # Update the number of elements + self._size += 1 + + def remove(self, index: int) -> int: + """Remove element""" + if index < 0 or index >= self._size: + raise IndexError("Index out of bounds") + num = self._arr[index] + # Move all elements after `index` one position forward + for j in range(index, self._size - 1): + self._arr[j] = self._arr[j + 1] + # Update the number of elements + self._size -= 1 + # Return the removed element + return num + + def extend_capacity(self): + """Extend list""" + # Create a new array of _extend_ratio times the length of the original array and copy the original array to the new array + self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1) + # Update list capacity + self._capacity = len(self._arr) + + def to_array(self) -> list[int]: + """Return a list of valid lengths""" + return self._arr[: self._size] + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize list + nums = MyList() + # Add element at the end + nums.add(1) + nums.add(3) + nums.add(2) + nums.add(5) + nums.add(4) + print(f"List nums = {nums.to_array()} ,capacity = {nums.capacity()} ,length = {nums.size()}") + + # Insert element in the middle + nums.insert(6, index=3) + print("Insert number 6 at index 3, resulting in nums =", nums.to_array()) + + # Remove element + nums.remove(3) + print("Remove the element at index 3, resulting in nums =", nums.to_array()) + + # Access element + num = nums.get(1) + print("Access the element at index 1, resulting in num =", num) + + # Update element + nums.set(0, 1) + print("Update the element at index 1 to 0, resulting in nums =", nums.to_array()) + + # Test expansion mechanism + for i in range(10): + # At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism at this time + nums.add(i) + print(f"After expansion, the list {nums.to_array()} ,capacity = {nums.capacity()} ,length = {nums.size()}") diff --git a/en/codes/python/chapter_backtracking/n_queens.py b/en/codes/python/chapter_backtracking/n_queens.py new file mode 100644 index 0000000000..5bb3004487 --- /dev/null +++ b/en/codes/python/chapter_backtracking/n_queens.py @@ -0,0 +1,62 @@ +""" +File: n_queens.py +Created Time: 2023-04-26 +Author: krahets (krahets@163.com) +""" + + +def backtrack( + row: int, + n: int, + state: list[list[str]], + res: list[list[list[str]]], + cols: list[bool], + diags1: list[bool], + diags2: list[bool], +): + """Backtracking algorithm: n queens""" + # When all rows are placed, record the solution + if row == n: + res.append([list(row) for row in state]) + return + # Traverse all columns + for col in range(n): + # Calculate the main and minor diagonals corresponding to the cell + diag1 = row - col + n - 1 + diag2 = row + col + # Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell + if not cols[col] and not diags1[diag1] and not 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 + + +def n_queens(n: int) -> list[list[list[str]]]: + """Solve n queens""" + # Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot + state = [["#" for _ in range(n)] for _ in range(n)] + cols = [False] * n # Record columns with queens + diags1 = [False] * (2 * n - 1) # Record main diagonals with queens + diags2 = [False] * (2 * n - 1) # Record minor diagonals with queens + res = [] + backtrack(0, n, state, res, cols, diags1, diags2) + + return res + + +"""Driver Code""" +if __name__ == "__main__": + n = 4 + res = n_queens(n) + + print(f"Input chessboard dimensions as {n}") + print(f"The total number of queen placement solutions is {len(res)}") + for state in res: + print("--------------------") + for row in state: + print(row) diff --git a/en/codes/python/chapter_backtracking/permutations_i.py b/en/codes/python/chapter_backtracking/permutations_i.py new file mode 100644 index 0000000000..cd4f164b77 --- /dev/null +++ b/en/codes/python/chapter_backtracking/permutations_i.py @@ -0,0 +1,44 @@ +""" +File: permutations_i.py +Created Time: 2023-04-15 +Author: krahets (krahets@163.com) +""" + + +def backtrack( + state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] +): + """Backtracking algorithm: Permutation I""" + # When the state length equals the number of elements, record the solution + if len(state) == len(choices): + res.append(list(state)) + return + # Traverse all choices + for i, choice in enumerate(choices): + # Pruning: do not allow repeated selection of elements + if not selected[i]: + # Attempt: make a choice, update the state + selected[i] = True + state.append(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() + + +def permutations_i(nums: list[int]) -> list[list[int]]: + """Permutation I""" + res = [] + backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) + return res + + +"""Driver Code""" +if __name__ == "__main__": + nums = [1, 2, 3] + + res = permutations_i(nums) + + print(f"Input array nums = {nums}") + print(f"All permutations res = {res}") diff --git a/en/codes/python/chapter_backtracking/permutations_ii.py b/en/codes/python/chapter_backtracking/permutations_ii.py new file mode 100644 index 0000000000..5f18076e89 --- /dev/null +++ b/en/codes/python/chapter_backtracking/permutations_ii.py @@ -0,0 +1,46 @@ +""" +File: permutations_ii.py +Created Time: 2023-04-15 +Author: krahets (krahets@163.com) +""" + + +def backtrack( + state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] +): + """Backtracking algorithm: Permutation II""" + # When the state length equals the number of elements, record the solution + if len(state) == len(choices): + res.append(list(state)) + return + # Traverse all choices + duplicated = set[int]() + for i, choice in enumerate(choices): + # Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if not selected[i] and choice not in duplicated: + # Attempt: make a choice, update the state + duplicated.add(choice) # Record selected element values + selected[i] = True + state.append(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() + + +def permutations_ii(nums: list[int]) -> list[list[int]]: + """Permutation II""" + res = [] + backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) + return res + + +"""Driver Code""" +if __name__ == "__main__": + nums = [1, 2, 2] + + res = permutations_ii(nums) + + print(f"Input array nums = {nums}") + print(f"All permutations res = {res}") diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py new file mode 100644 index 0000000000..91a7c9cddb --- /dev/null +++ b/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py @@ -0,0 +1,36 @@ +""" +File: preorder_traversal_i_compact.py +Created Time: 2023-04-15 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree, list_to_tree + + +def pre_order(root: TreeNode): + """Pre-order traversal: Example one""" + if root is None: + return + if root.val == 7: + # Record solution + res.append(root) + pre_order(root.left) + pre_order(root.right) + + +"""Driver Code""" +if __name__ == "__main__": + root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + print_tree(root) + + # Pre-order traversal + res = list[TreeNode]() + pre_order(root) + + print("\nOutput all nodes with value 7") + print([node.val for node in res]) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py new file mode 100644 index 0000000000..d6fc3469ee --- /dev/null +++ b/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py @@ -0,0 +1,42 @@ +""" +File: preorder_traversal_ii_compact.py +Created Time: 2023-04-15 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree, list_to_tree + + +def pre_order(root: TreeNode): + """Pre-order traversal: Example two""" + if root is None: + return + # Attempt + path.append(root) + if root.val == 7: + # Record solution + res.append(list(path)) + pre_order(root.left) + pre_order(root.right) + # Retract + path.pop() + + +"""Driver Code""" +if __name__ == "__main__": + root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + print_tree(root) + + # Pre-order traversal + path = list[TreeNode]() + res = list[list[TreeNode]]() + pre_order(root) + + print("\nOutput all root-to-node 7 paths") + for path in res: + print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py new file mode 100644 index 0000000000..9c98318bc0 --- /dev/null +++ b/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py @@ -0,0 +1,43 @@ +""" +File: preorder_traversal_iii_compact.py +Created Time: 2023-04-15 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree, list_to_tree + + +def pre_order(root: TreeNode): + """Pre-order traversal: Example three""" + # Pruning + if root is None or root.val == 3: + return + # Attempt + path.append(root) + if root.val == 7: + # Record solution + res.append(list(path)) + pre_order(root.left) + pre_order(root.right) + # Retract + path.pop() + + +"""Driver Code""" +if __name__ == "__main__": + root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + print_tree(root) + + # Pre-order traversal + path = list[TreeNode]() + res = list[list[TreeNode]]() + pre_order(root) + + print("\nOutput all root-to-node 7 paths, not including nodes with value 3") + for path in res: + print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py b/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py new file mode 100644 index 0000000000..f35c5a4851 --- /dev/null +++ b/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py @@ -0,0 +1,71 @@ +""" +File: preorder_traversal_iii_template.py +Created Time: 2023-04-15 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree, list_to_tree + + +def is_solution(state: list[TreeNode]) -> bool: + """Determine if the current state is a solution""" + return state and state[-1].val == 7 + + +def record_solution(state: list[TreeNode], res: list[list[TreeNode]]): + """Record solution""" + res.append(list(state)) + + +def is_valid(state: list[TreeNode], choice: TreeNode) -> bool: + """Determine if the choice is legal under the current state""" + return choice is not None and choice.val != 3 + + +def make_choice(state: list[TreeNode], choice: TreeNode): + """Update state""" + state.append(choice) + + +def undo_choice(state: list[TreeNode], choice: TreeNode): + """Restore state""" + state.pop() + + +def backtrack( + state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]] +): + """Backtracking algorithm: Example three""" + # Check if it's a solution + if is_solution(state): + # Record solution + record_solution(state, res) + # Traverse all choices + for choice in choices: + # Pruning: check if the choice is legal + if is_valid(state, choice): + # Attempt: make a choice, update the state + make_choice(state, choice) + # Proceed to the next round of selection + backtrack(state, [choice.left, choice.right], res) + # Retract: undo the choice, restore to the previous state + undo_choice(state, choice) + + +"""Driver Code""" +if __name__ == "__main__": + root = list_to_tree([1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + print_tree(root) + + # Backtracking algorithm + res = [] + backtrack(state=[], choices=[root], res=res) + + print("\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3") + for path in res: + print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/subset_sum_i.py b/en/codes/python/chapter_backtracking/subset_sum_i.py new file mode 100644 index 0000000000..a8de835d04 --- /dev/null +++ b/en/codes/python/chapter_backtracking/subset_sum_i.py @@ -0,0 +1,48 @@ +""" +File: subset_sum_i.py +Created Time: 2023-06-17 +Author: krahets (krahets@163.com) +""" + + +def backtrack( + state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] +): + """Backtracking algorithm: Subset Sum I""" + # When the subset sum equals target, record the solution + if target == 0: + res.append(list(state)) + return + # Traverse all choices + # Pruning two: start traversing from start to avoid generating duplicate subsets + for i in range(start, len(choices)): + # 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.append(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() + + +def subset_sum_i(nums: list[int], target: int) -> list[list[int]]: + """Solve Subset Sum I""" + state = [] # State (subset) + nums.sort() # Sort nums + start = 0 # Start point for traversal + res = [] # Result list (subset list) + backtrack(state, target, nums, start, res) + return res + + +"""Driver Code""" +if __name__ == "__main__": + nums = [3, 4, 5] + target = 9 + res = subset_sum_i(nums, target) + + print(f"Input array nums = {nums}, target = {target}") + print(f"All subsets equal to {target} res = {res}") diff --git a/en/codes/python/chapter_backtracking/subset_sum_i_naive.py b/en/codes/python/chapter_backtracking/subset_sum_i_naive.py new file mode 100644 index 0000000000..7412bbf0f5 --- /dev/null +++ b/en/codes/python/chapter_backtracking/subset_sum_i_naive.py @@ -0,0 +1,50 @@ +""" +File: subset_sum_i_naive.py +Created Time: 2023-06-17 +Author: krahets (krahets@163.com) +""" + + +def backtrack( + state: list[int], + target: int, + total: int, + choices: list[int], + res: list[list[int]], +): + """Backtracking algorithm: Subset Sum I""" + # When the subset sum equals target, record the solution + if total == target: + res.append(list(state)) + return + # Traverse all choices + for i in range(len(choices)): + # 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.append(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() + + +def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]: + """Solve Subset Sum I (including duplicate subsets)""" + state = [] # State (subset) + total = 0 # Subset sum + res = [] # Result list (subset list) + backtrack(state, target, total, nums, res) + return res + + +"""Driver Code""" +if __name__ == "__main__": + nums = [3, 4, 5] + target = 9 + res = subset_sum_i_naive(nums, target) + + print(f"Input array nums = {nums}, target = {target}") + print(f"All subsets equal to {target} res = {res}") + print(f"Please note that the result of this method includes duplicate sets") diff --git a/en/codes/python/chapter_backtracking/subset_sum_ii.py b/en/codes/python/chapter_backtracking/subset_sum_ii.py new file mode 100644 index 0000000000..d404343e35 --- /dev/null +++ b/en/codes/python/chapter_backtracking/subset_sum_ii.py @@ -0,0 +1,52 @@ +""" +File: subset_sum_ii.py +Created Time: 2023-06-17 +Author: krahets (krahets@163.com) +""" + + +def backtrack( + state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] +): + """Backtracking algorithm: Subset Sum II""" + # When the subset sum equals target, record the solution + if target == 0: + res.append(list(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 i in range(start, len(choices)): + # 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 and choices[i] == choices[i - 1]: + continue + # Attempt: make a choice, update target, start + state.append(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() + + +def subset_sum_ii(nums: list[int], target: int) -> list[list[int]]: + """Solve Subset Sum II""" + state = [] # State (subset) + nums.sort() # Sort nums + start = 0 # Start point for traversal + res = [] # Result list (subset list) + backtrack(state, target, nums, start, res) + return res + + +"""Driver Code""" +if __name__ == "__main__": + nums = [4, 4, 5] + target = 9 + res = subset_sum_ii(nums, target) + + print(f"Input array nums = {nums}, target = {target}") + print(f"All subsets equal to {target} res = {res}") diff --git a/en/codes/python/chapter_computational_complexity/iteration.py b/en/codes/python/chapter_computational_complexity/iteration.py new file mode 100644 index 0000000000..0eb1984d4a --- /dev/null +++ b/en/codes/python/chapter_computational_complexity/iteration.py @@ -0,0 +1,65 @@ +""" +File: iteration.py +Created Time: 2023-08-24 +Author: krahets (krahets@163.com) +""" + + +def for_loop(n: int) -> int: + """for loop""" + res = 0 + # Loop sum 1, 2, ..., n-1, n + for i in range(1, n + 1): + res += i + return res + + +def while_loop(n: int) -> int: + """while loop""" + res = 0 + i = 1 # Initialize condition variable + # Loop sum 1, 2, ..., n-1, n + while i <= n: + res += i + i += 1 # Update condition variable + return res + + +def while_loop_ii(n: int) -> int: + """while loop (two updates)""" + res = 0 + i = 1 # Initialize condition variable + # Loop sum 1, 4, 10, ... + while i <= n: + res += i + # Update condition variable + i += 1 + i *= 2 + return res + + +def nested_for_loop(n: int) -> str: + """Double for loop""" + res = "" + # Loop i = 1, 2, ..., n-1, n + for i in range(1, n + 1): + # Loop j = 1, 2, ..., n-1, n + for j in range(1, n + 1): + res += f"({i}, {j}), " + return res + + +"""Driver Code""" +if __name__ == "__main__": + n = 5 + res = for_loop(n) + print(f"\nfor loop sum result res = {res}") + + res = while_loop(n) + print(f"\nwhile loop sum result res = {res}") + + res = while_loop_ii(n) + print(f"\nwhile loop (two updates) sum result res = {res}") + + res = nested_for_loop(n) + print(f"\nDouble for loop traversal result {res}") diff --git a/en/codes/python/chapter_computational_complexity/recursion.py b/en/codes/python/chapter_computational_complexity/recursion.py new file mode 100644 index 0000000000..33bca52215 --- /dev/null +++ b/en/codes/python/chapter_computational_complexity/recursion.py @@ -0,0 +1,69 @@ +""" +File: recursion.py +Created Time: 2023-08-24 +Author: krahets (krahets@163.com) +""" + + +def recur(n: int) -> int: + """Recursion""" + # Termination condition + if n == 1: + return 1 + # Recursive: recursive call + res = recur(n - 1) + # Return: return result + return n + res + + +def for_loop_recur(n: int) -> int: + """Simulate recursion with iteration""" + # Use an explicit stack to simulate the system call stack + stack = [] + res = 0 + # Recursive: recursive call + for i in range(n, 0, -1): + # Simulate "recursive" by "pushing onto the stack" + stack.append(i) + # Return: return result + while stack: + # Simulate "return" by "popping from the stack" + res += stack.pop() + # res = 1+2+3+...+n + return res + + +def tail_recur(n, res): + """Tail recursion""" + # Termination condition + if n == 0: + return res + # Tail recursive call + return tail_recur(n - 1, res + n) + + +def fib(n: int) -> int: + """Fibonacci sequence: Recursion""" + # Termination condition f(1) = 0, f(2) = 1 + if n == 1 or n == 2: + return n - 1 + # Recursive call f(n) = f(n-1) + f(n-2) + res = fib(n - 1) + fib(n - 2) + # Return result f(n) + return res + + +"""Driver Code""" +if __name__ == "__main__": + n = 5 + res = recur(n) + print(f"\nRecursive function sum result res = {res}") + + res = for_loop_recur(n) + print(f"\nSimulate recursion with iteration sum result res = {res}") + + res = tail_recur(n, 0) + print(f"\nTail recursive function sum result res = {res}") + + res = fib(n) + print(f"\nThe n th term of the Fibonacci sequence is {res}") diff --git a/en/codes/python/chapter_computational_complexity/space_complexity.py b/en/codes/python/chapter_computational_complexity/space_complexity.py new file mode 100644 index 0000000000..7d91a16d12 --- /dev/null +++ b/en/codes/python/chapter_computational_complexity/space_complexity.py @@ -0,0 +1,90 @@ +""" +File: space_complexity.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import ListNode, TreeNode, print_tree + + +def function() -> int: + """Function""" + # Perform some operations + return 0 + + +def constant(n: int): + """Constant complexity""" + # Constants, variables, objects occupy O(1) space + a = 0 + nums = [0] * 10000 + node = ListNode(0) + # Variables in a loop occupy O(1) space + for _ in range(n): + c = 0 + # Functions in a loop occupy O(1) space + for _ in range(n): + function() + + +def linear(n: int): + """Linear complexity""" + # A list of length n occupies O(n) space + nums = [0] * n + # A hash table of length n occupies O(n) space + hmap = dict[int, str]() + for i in range(n): + hmap[i] = str(i) + + +def linear_recur(n: int): + """Linear complexity (recursive implementation)""" + print("Recursive n =", n) + if n == 1: + return + linear_recur(n - 1) + + +def quadratic(n: int): + """Quadratic complexity""" + # A two-dimensional list occupies O(n^2) space + num_matrix = [[0] * n for _ in range(n)] + + +def quadratic_recur(n: int) -> int: + """Quadratic complexity (recursive implementation)""" + if n <= 0: + return 0 + # Array nums length = n, n-1, ..., 2, 1 + nums = [0] * n + return quadratic_recur(n - 1) + + +def build_tree(n: int) -> TreeNode | None: + """Exponential complexity (building a full binary tree)""" + if n == 0: + return None + root = TreeNode(0) + root.left = build_tree(n - 1) + root.right = build_tree(n - 1) + return root + + +"""Driver Code""" +if __name__ == "__main__": + n = 5 + # Constant complexity + constant(n) + # Linear complexity + linear(n) + linear_recur(n) + # Quadratic complexity + quadratic(n) + quadratic_recur(n) + # Exponential complexity + root = build_tree(n) + print_tree(root) diff --git a/en/codes/python/chapter_computational_complexity/time_complexity.py b/en/codes/python/chapter_computational_complexity/time_complexity.py new file mode 100644 index 0000000000..661d5dcee2 --- /dev/null +++ b/en/codes/python/chapter_computational_complexity/time_complexity.py @@ -0,0 +1,151 @@ +""" +File: time_complexity.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + + +def constant(n: int) -> int: + """Constant complexity""" + count = 0 + size = 100000 + for _ in range(size): + count += 1 + return count + + +def linear(n: int) -> int: + """Linear complexity""" + count = 0 + for _ in range(n): + count += 1 + return count + + +def array_traversal(nums: list[int]) -> int: + """Linear complexity (traversing an array)""" + count = 0 + # Loop count is proportional to the length of the array + for num in nums: + count += 1 + return count + + +def quadratic(n: int) -> int: + """Quadratic complexity""" + count = 0 + # Loop count is squared in relation to the data size n + for i in range(n): + for j in range(n): + count += 1 + return count + + +def bubble_sort(nums: list[int]) -> int: + """Quadratic complexity (bubble sort)""" + count = 0 # Counter + # Outer loop: unsorted range is [0, i] + for i in range(len(nums) - 1, 0, -1): + # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + for j in range(i): + if nums[j] > nums[j + 1]: + # Swap nums[j] and nums[j + 1] + tmp: int = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + count += 3 # Element swap includes 3 individual operations + return count + + +def exponential(n: int) -> int: + """Exponential complexity (loop implementation)""" + count = 0 + base = 1 + # Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1) + for _ in range(n): + for _ in range(base): + count += 1 + base *= 2 + # count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count + + +def exp_recur(n: int) -> int: + """Exponential complexity (recursive implementation)""" + if n == 1: + return 1 + return exp_recur(n - 1) + exp_recur(n - 1) + 1 + + +def logarithmic(n: int) -> int: + """Logarithmic complexity (loop implementation)""" + count = 0 + while n > 1: + n = n / 2 + count += 1 + return count + + +def log_recur(n: int) -> int: + """Logarithmic complexity (recursive implementation)""" + if n <= 1: + return 0 + return log_recur(n / 2) + 1 + + +def linear_log_recur(n: int) -> int: + """Linear logarithmic complexity""" + if n <= 1: + return 1 + count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2) + for _ in range(n): + count += 1 + return count + + +def factorial_recur(n: int) -> int: + """Factorial complexity (recursive implementation)""" + if n == 0: + return 1 + count = 0 + # From 1 split into n + for _ in range(n): + count += factorial_recur(n - 1) + return count + + +"""Driver Code""" +if __name__ == "__main__": + # Can modify n to experience the trend of operation count changes under various complexities + n = 8 + print("Input data size n =", n) + + count: int = constant(n) + print("Constant complexity operation count =", count) + + count: int = linear(n) + print("Linear complexity operation count =", count) + count: int = array_traversal([0] * n) + print("Linear complexity (traversing an array) operation count =", count) + + count: int = quadratic(n) + print("Quadratic complexity operation count =", count) + nums = [i for i in range(n, 0, -1)] # [n, n-1, ..., 2, 1] + count: int = bubble_sort(nums) + print("Quadratic complexity (bubble sort) operation count =", count) + + count: int = exponential(n) + print("Exponential complexity (loop implementation) operation count =", count) + count: int = exp_recur(n) + print("Exponential complexity (recursive implementation) operation count =", count) + + count: int = logarithmic(n) + print("Logarithmic complexity (loop implementation) operation count =", count) + count: int = log_recur(n) + print("Logarithmic complexity (recursive implementation) operation count =", count) + + count: int = linear_log_recur(n) + print("Linear logarithmic complexity (recursive implementation) operation count =", count) + + count: int = factorial_recur(n) + print("Factorial complexity (recursive implementation) operation count =", count) diff --git a/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py b/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py new file mode 100644 index 0000000000..82a0e3a585 --- /dev/null +++ b/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py @@ -0,0 +1,36 @@ +""" +File: worst_best_time_complexity.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + +import random + + +def random_numbers(n: int) -> list[int]: + """Generate an array with elements: 1, 2, ..., n, order shuffled""" + # Generate array nums =: 1, 2, 3, ..., n + nums = [i for i in range(1, n + 1)] + # Randomly shuffle array elements + random.shuffle(nums) + return nums + + +def find_one(nums: list[int]) -> int: + """Find the index of number 1 in array nums""" + for i in range(len(nums)): + # 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""" +if __name__ == "__main__": + for i in range(10): + n = 100 + nums: list[int] = random_numbers(n) + index: int = find_one(nums) + print("\nThe array [ 1, 2, ..., n ] after being shuffled =", nums) + print("Index of number 1 =", index) diff --git a/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py b/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py new file mode 100644 index 0000000000..2bd9e9bdc3 --- /dev/null +++ b/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py @@ -0,0 +1,40 @@ +""" +File: binary_search_recur.py +Created Time: 2023-07-17 +Author: krahets (krahets@163.com) +""" + + +def dfs(nums: list[int], target: int, i: int, j: int) -> int: + """Binary search: problem f(i, j)""" + # If the interval is empty, indicating no target element, return -1 + if i > j: + return -1 + # Calculate midpoint index m + m = (i + j) // 2 + if nums[m] < target: + # Recursive subproblem f(m+1, j) + return dfs(nums, target, m + 1, j) + elif 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 + + +def binary_search(nums: list[int], target: int) -> int: + """Binary search""" + n = len(nums) + # Solve problem f(0, n-1) + return dfs(nums, target, 0, n - 1) + + +"""Driver Code""" +if __name__ == "__main__": + target = 6 + nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + + # Binary search (double closed interval) + index = binary_search(nums, target) + print("Index of target element 6 =", index) diff --git a/en/codes/python/chapter_divide_and_conquer/build_tree.py b/en/codes/python/chapter_divide_and_conquer/build_tree.py new file mode 100644 index 0000000000..8c2e960271 --- /dev/null +++ b/en/codes/python/chapter_divide_and_conquer/build_tree.py @@ -0,0 +1,54 @@ +""" +File: build_tree.py +Created Time: 2023-07-15 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree + + +def dfs( + preorder: list[int], + inorder_map: dict[int, int], + i: int, + l: int, + r: int, +) -> TreeNode | None: + """Build binary tree: Divide and conquer""" + # Terminate when subtree interval is empty + if r - l < 0: + return None + # Initialize root node + root = TreeNode(preorder[i]) + # Query m to divide left and right subtrees + m = inorder_map[preorder[i]] + # Subproblem: build left subtree + root.left = dfs(preorder, inorder_map, i + 1, l, m - 1) + # Subproblem: build right subtree + root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r) + # Return root node + return root + + +def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None: + """Build binary tree""" + # Initialize hash table, storing in-order elements to indices mapping + inorder_map = {val: i for i, val in enumerate(inorder)} + root = dfs(preorder, inorder_map, 0, 0, len(inorder) - 1) + return root + + +"""Driver Code""" +if __name__ == "__main__": + preorder = [3, 9, 2, 1, 7] + inorder = [9, 3, 1, 2, 7] + print(f"Pre-order traversal = {preorder}") + print(f"In-order traversal = {inorder}") + + root = build_tree(preorder, inorder) + print("The built binary tree is:") + print_tree(root) diff --git a/en/codes/python/chapter_divide_and_conquer/hanota.py b/en/codes/python/chapter_divide_and_conquer/hanota.py new file mode 100644 index 0000000000..306d113129 --- /dev/null +++ b/en/codes/python/chapter_divide_and_conquer/hanota.py @@ -0,0 +1,53 @@ +""" +File: hanota.py +Created Time: 2023-07-16 +Author: krahets (krahets@163.com) +""" + + +def move(src: list[int], tar: list[int]): + """Move a disc""" + # Take out a disc from the top of src + pan = src.pop() + # Place the disc on top of tar + tar.append(pan) + + +def dfs(i: int, src: list[int], buf: list[int], tar: list[int]): + """Solve the Tower of Hanoi problem f(i)""" + # 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) + + +def solve_hanota(A: list[int], B: list[int], C: list[int]): + """Solve the Tower of Hanoi problem""" + n = len(A) + # Move the top n discs from A with the help of B to C + dfs(n, A, B, C) + + +"""Driver Code""" +if __name__ == "__main__": + # The tail of the list is the top of the pillar + A = [5, 4, 3, 2, 1] + B = [] + C = [] + print("Initial state:") + print(f"A = {A}") + print(f"B = {B}") + print(f"C = {C}") + + solve_hanota(A, B, C) + + print("After the discs are moved:") + print(f"A = {A}") + print(f"B = {B}") + print(f"C = {C}") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py new file mode 100644 index 0000000000..0b2ea84719 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py @@ -0,0 +1,37 @@ +""" +File: climbing_stairs_backtrack.py +Created Time: 2023-06-30 +Author: krahets (krahets@163.com) +""" + + +def backtrack(choices: list[int], state: int, n: int, res: list[int]) -> int: + """Backtracking""" + # When climbing to the nth step, add 1 to the number of solutions + if state == n: + res[0] += 1 + # Traverse all choices + for choice in 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 + + +def climbing_stairs_backtrack(n: int) -> int: + """Climbing stairs: Backtracking""" + choices = [1, 2] # Can choose to climb up 1 step or 2 steps + state = 0 # Start climbing from the 0th step + res = [0] # Use res[0] to record the number of solutions + backtrack(choices, state, n, res) + return res[0] + + +"""Driver Code""" +if __name__ == "__main__": + n = 9 + + res = climbing_stairs_backtrack(n) + print(f"Climb {n} steps, there are {res} solutions in total") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py new file mode 100644 index 0000000000..77b5b5030f --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py @@ -0,0 +1,29 @@ +""" +File: climbing_stairs_constraint_dp.py +Created Time: 2023-06-30 +Author: krahets (krahets@163.com) +""" + + +def climbing_stairs_constraint_dp(n: int) -> int: + """Constrained climbing stairs: Dynamic programming""" + if n == 1 or n == 2: + return 1 + # Initialize dp table, used to store subproblem solutions + dp = [[0] * 3 for _ in range(n + 1)] + # Initial state: preset the smallest subproblem solution + dp[1][1], dp[1][2] = 1, 0 + dp[2][1], dp[2][2] = 0, 1 + # State transition: gradually solve larger subproblems from smaller ones + for i in range(3, n + 1): + dp[i][1] = dp[i - 1][2] + dp[i][2] = dp[i - 2][1] + dp[i - 2][2] + return dp[n][1] + dp[n][2] + + +"""Driver Code""" +if __name__ == "__main__": + n = 9 + + res = climbing_stairs_constraint_dp(n) + print(f"Climb {n} steps, there are {res} solutions in total") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py new file mode 100644 index 0000000000..018445ea7b --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py @@ -0,0 +1,28 @@ +""" +File: climbing_stairs_dfs.py +Created Time: 2023-06-30 +Author: krahets (krahets@163.com) +""" + + +def dfs(i: int) -> int: + """Search""" + # Known dp[1] and dp[2], return them + if i == 1 or i == 2: + return i + # dp[i] = dp[i-1] + dp[i-2] + count = dfs(i - 1) + dfs(i - 2) + return count + + +def climbing_stairs_dfs(n: int) -> int: + """Climbing stairs: Search""" + return dfs(n) + + +"""Driver Code""" +if __name__ == "__main__": + n = 9 + + res = climbing_stairs_dfs(n) + print(f"Climb {n} steps, there are {res} solutions in total") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py new file mode 100644 index 0000000000..2f517a6497 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py @@ -0,0 +1,35 @@ +""" +File: climbing_stairs_dfs_mem.py +Created Time: 2023-06-30 +Author: krahets (krahets@163.com) +""" + + +def dfs(i: int, mem: list[int]) -> int: + """Memoized search""" + # Known dp[1] and dp[2], return them + if i == 1 or 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] + count = dfs(i - 1, mem) + dfs(i - 2, mem) + # Record dp[i] + mem[i] = count + return count + + +def climbing_stairs_dfs_mem(n: int) -> int: + """Climbing stairs: Memoized search""" + # mem[i] records the total number of solutions for climbing to the ith step, -1 means no record + mem = [-1] * (n + 1) + return dfs(n, mem) + + +"""Driver Code""" +if __name__ == "__main__": + n = 9 + + res = climbing_stairs_dfs_mem(n) + print(f"Climb {n} steps, there are {res} solutions in total") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py new file mode 100644 index 0000000000..39cb581743 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py @@ -0,0 +1,40 @@ +""" +File: climbing_stairs_dp.py +Created Time: 2023-06-30 +Author: krahets (krahets@163.com) +""" + + +def climbing_stairs_dp(n: int) -> int: + """Climbing stairs: Dynamic programming""" + if n == 1 or n == 2: + return n + # Initialize dp table, used to store subproblem solutions + dp = [0] * (n + 1) + # Initial state: preset the smallest subproblem solution + dp[1], dp[2] = 1, 2 + # State transition: gradually solve larger subproblems from smaller ones + for i in range(3, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + return dp[n] + + +def climbing_stairs_dp_comp(n: int) -> int: + """Climbing stairs: Space-optimized dynamic programming""" + if n == 1 or n == 2: + return n + a, b = 1, 2 + for _ in range(3, n + 1): + a, b = b, a + b + return b + + +"""Driver Code""" +if __name__ == "__main__": + n = 9 + + res = climbing_stairs_dp(n) + print(f"Climb {n} steps, there are {res} solutions in total") + + res = climbing_stairs_dp_comp(n) + print(f"Climb {n} steps, there are {res} solutions in total") diff --git a/en/codes/python/chapter_dynamic_programming/coin_change.py b/en/codes/python/chapter_dynamic_programming/coin_change.py new file mode 100644 index 0000000000..8ed789c516 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/coin_change.py @@ -0,0 +1,60 @@ +""" +File: coin_change.py +Created Time: 2023-07-10 +Author: krahets (krahets@163.com) +""" + + +def coin_change_dp(coins: list[int], amt: int) -> int: + """Coin change: Dynamic programming""" + n = len(coins) + MAX = amt + 1 + # Initialize dp table + dp = [[0] * (amt + 1) for _ in range(n + 1)] + # State transition: first row and first column + for a in range(1, amt + 1): + dp[0][a] = MAX + # State transition: the rest of the rows and columns + for i in range(1, n + 1): + for a in range(1, amt + 1): + 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] if dp[n][amt] != MAX else -1 + + +def coin_change_dp_comp(coins: list[int], amt: int) -> int: + """Coin change: Space-optimized dynamic programming""" + n = len(coins) + MAX = amt + 1 + # Initialize dp table + dp = [MAX] * (amt + 1) + dp[0] = 0 + # State transition + for i in range(1, n + 1): + # Traverse in order + for a in range(1, amt + 1): + 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] if dp[amt] != MAX else -1 + + +"""Driver Code""" +if __name__ == "__main__": + coins = [1, 2, 5] + amt = 4 + + # Dynamic programming + res = coin_change_dp(coins, amt) + print(f"Minimum number of coins required to reach the target amount = {res}") + + # Space-optimized dynamic programming + res = coin_change_dp_comp(coins, amt) + print(f"Minimum number of coins required to reach the target amount = {res}") diff --git a/en/codes/python/chapter_dynamic_programming/coin_change_ii.py b/en/codes/python/chapter_dynamic_programming/coin_change_ii.py new file mode 100644 index 0000000000..f389dd5369 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/coin_change_ii.py @@ -0,0 +1,58 @@ +""" +File: coin_change_ii.py +Created Time: 2023-07-10 +Author: krahets (krahets@163.com) +""" + + +def coin_change_ii_dp(coins: list[int], amt: int) -> int: + """Coin change II: Dynamic programming""" + n = len(coins) + # Initialize dp table + dp = [[0] * (amt + 1) for _ in range(n + 1)] + # Initialize first column + for i in range(n + 1): + dp[i][0] = 1 + # State transition + for i in range(1, n + 1): + for a in range(1, amt + 1): + 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] + + +def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int: + """Coin change II: Space-optimized dynamic programming""" + n = len(coins) + # Initialize dp table + dp = [0] * (amt + 1) + dp[0] = 1 + # State transition + for i in range(1, n + 1): + # Traverse in order + for a in range(1, amt + 1): + 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""" +if __name__ == "__main__": + coins = [1, 2, 5] + amt = 5 + + # Dynamic programming + res = coin_change_ii_dp(coins, amt) + print(f"The number of coin combinations to make up the target amount is {res}") + + # Space-optimized dynamic programming + res = coin_change_ii_dp_comp(coins, amt) + print(f"The number of coin combinations to make up the target amount is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/edit_distance.py b/en/codes/python/chapter_dynamic_programming/edit_distance.py new file mode 100644 index 0000000000..d62ad73551 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/edit_distance.py @@ -0,0 +1,123 @@ +""" +File: edit_distancde.py +Created Time: 2023-07-04 +Author: krahets (krahets@163.com) +""" + + +def edit_distance_dfs(s: str, t: str, i: int, j: int) -> int: + """Edit distance: Brute force search""" + # If both s and t are empty, return 0 + if i == 0 and 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 edit_distance_dfs(s, t, i - 1, j - 1) + # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + insert = edit_distance_dfs(s, t, i, j - 1) + delete = edit_distance_dfs(s, t, i - 1, j) + replace = edit_distance_dfs(s, t, i - 1, j - 1) + # Return the minimum number of edits + return min(insert, delete, replace) + 1 + + +def edit_distance_dfs_mem(s: str, t: str, mem: list[list[int]], i: int, j: int) -> int: + """Edit distance: Memoized search""" + # If both s and t are empty, return 0 + if i == 0 and 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 edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) + # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + insert = edit_distance_dfs_mem(s, t, mem, i, j - 1) + delete = edit_distance_dfs_mem(s, t, mem, i - 1, j) + replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) + # Record and return the minimum number of edits + mem[i][j] = min(insert, delete, replace) + 1 + return mem[i][j] + + +def edit_distance_dp(s: str, t: str) -> int: + """Edit distance: Dynamic programming""" + n, m = len(s), len(t) + dp = [[0] * (m + 1) for _ in range(n + 1)] + # State transition: first row and first column + for i in range(1, n + 1): + dp[i][0] = i + for j in range(1, m + 1): + dp[0][j] = j + # State transition: the rest of the rows and columns + for i in range(1, n + 1): + for j in range(1, m + 1): + if s[i - 1] == t[j - 1]: + # If 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(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 + return dp[n][m] + + +def edit_distance_dp_comp(s: str, t: str) -> int: + """Edit distance: Space-optimized dynamic programming""" + n, m = len(s), len(t) + dp = [0] * (m + 1) + # State transition: first row + for j in range(1, m + 1): + dp[j] = j + # State transition: the rest of the rows + for i in range(1, n + 1): + # State transition: first column + leftup = dp[0] # Temporarily store dp[i-1, j-1] + dp[0] += 1 + # State transition: the rest of the columns + for j in range(1, m + 1): + 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(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""" +if __name__ == "__main__": + s = "bag" + t = "pack" + n, m = len(s), len(t) + + # Brute force search + res = edit_distance_dfs(s, t, n, m) + print(f"To change {s} to {t}, the minimum number of edits required is {res}") + + # Memoized search + mem = [[-1] * (m + 1) for _ in range(n + 1)] + res = edit_distance_dfs_mem(s, t, mem, n, m) + print(f"To change {s} to {t}, the minimum number of edits required is {res}") + + # Dynamic programming + res = edit_distance_dp(s, t) + print(f"To change {s} to {t}, the minimum number of edits required is {res}") + + # Space-optimized dynamic programming + res = edit_distance_dp_comp(s, t) + print(f"To change {s} to {t}, the minimum number of edits required is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/knapsack.py b/en/codes/python/chapter_dynamic_programming/knapsack.py new file mode 100644 index 0000000000..8c06208f56 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/knapsack.py @@ -0,0 +1,101 @@ +""" +File: knapsack.py +Created Time: 2023-07-03 +Author: krahets (krahets@163.com) +""" + + +def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int: + """0-1 Knapsack: Brute force search""" + # If all items have been chosen or the knapsack has no remaining capacity, return value 0 + if i == 0 or 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 knapsack_dfs(wgt, val, i - 1, c) + # Calculate the maximum value of not putting in and putting in item i + no = knapsack_dfs(wgt, val, i - 1, c) + yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] + # Return the greater value of the two options + return max(no, yes) + + +def knapsack_dfs_mem( + wgt: list[int], val: list[int], mem: list[list[int]], i: int, c: int +) -> int: + """0-1 Knapsack: Memoized search""" + # If all items have been chosen or the knapsack has no remaining capacity, return value 0 + if i == 0 or 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 knapsack_dfs_mem(wgt, val, mem, i - 1, c) + # Calculate the maximum value of not putting in and putting in item i + no = knapsack_dfs_mem(wgt, val, mem, i - 1, c) + yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1] + # Record and return the greater value of the two options + mem[i][c] = max(no, yes) + return mem[i][c] + + +def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: + """0-1 Knapsack: Dynamic programming""" + n = len(wgt) + # Initialize dp table + dp = [[0] * (cap + 1) for _ in range(n + 1)] + # State transition + for i in range(1, n + 1): + for c in range(1, cap + 1): + 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] + + +def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: + """0-1 Knapsack: Space-optimized dynamic programming""" + n = len(wgt) + # Initialize dp table + dp = [0] * (cap + 1) + # State transition + for i in range(1, n + 1): + # Traverse in reverse order + for c in range(cap, 0, -1): + 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""" +if __name__ == "__main__": + wgt = [10, 20, 30, 40, 50] + val = [50, 120, 150, 210, 240] + cap = 50 + n = len(wgt) + + # Brute force search + res = knapsack_dfs(wgt, val, n, cap) + print(f"The maximum item value without exceeding knapsack capacity is {res}") + + # Memoized search + mem = [[-1] * (cap + 1) for _ in range(n + 1)] + res = knapsack_dfs_mem(wgt, val, mem, n, cap) + print(f"The maximum item value without exceeding knapsack capacity is {res}") + + # Dynamic programming + res = knapsack_dp(wgt, val, cap) + print(f"The maximum item value without exceeding knapsack capacity is {res}") + + # Space-optimized dynamic programming + res = knapsack_dp_comp(wgt, val, cap) + print(f"The maximum item value without exceeding knapsack capacity is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py b/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py new file mode 100644 index 0000000000..47730c1855 --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py @@ -0,0 +1,43 @@ +""" +File: min_cost_climbing_stairs_dp.py +Created Time: 2023-06-30 +Author: krahets (krahets@163.com) +""" + + +def min_cost_climbing_stairs_dp(cost: list[int]) -> int: + """Climbing stairs with minimum cost: Dynamic programming""" + n = len(cost) - 1 + if n == 1 or n == 2: + return cost[n] + # Initialize dp table, used to store subproblem solutions + dp = [0] * (n + 1) + # Initial state: preset the smallest subproblem solution + dp[1], dp[2] = cost[1], cost[2] + # State transition: gradually solve larger subproblems from smaller ones + for i in range(3, n + 1): + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] + return dp[n] + + +def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int: + """Climbing stairs with minimum cost: Space-optimized dynamic programming""" + n = len(cost) - 1 + if n == 1 or n == 2: + return cost[n] + a, b = cost[1], cost[2] + for i in range(3, n + 1): + a, b = b, min(a, b) + cost[i] + return b + + +"""Driver Code""" +if __name__ == "__main__": + cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1] + print(f"Enter the list of stair costs as {cost}") + + res = min_cost_climbing_stairs_dp(cost) + print(f"Minimum cost to climb the stairs {res}") + + res = min_cost_climbing_stairs_dp_comp(cost) + print(f"Minimum cost to climb the stairs {res}") diff --git a/en/codes/python/chapter_dynamic_programming/min_path_sum.py b/en/codes/python/chapter_dynamic_programming/min_path_sum.py new file mode 100644 index 0000000000..2c9a14739e --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/min_path_sum.py @@ -0,0 +1,104 @@ +""" +File: min_path_sum.py +Created Time: 2023-07-04 +Author: krahets (krahets@163.com) +""" + +from math import inf + + +def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int: + """Minimum path sum: Brute force search""" + # If it's the top-left cell, terminate the search + if i == 0 and j == 0: + return grid[0][0] + # If the row or column index is out of bounds, return a +∞ cost + if i < 0 or j < 0: + return inf + # Calculate the minimum path cost from the top-left to (i-1, j) and (i, j-1) + up = min_path_sum_dfs(grid, i - 1, j) + left = min_path_sum_dfs(grid, i, j - 1) + # Return the minimum path cost from the top-left to (i, j) + return min(left, up) + grid[i][j] + + +def min_path_sum_dfs_mem( + grid: list[list[int]], mem: list[list[int]], i: int, j: int +) -> int: + """Minimum path sum: Memoized search""" + # If it's the top-left cell, terminate the search + if i == 0 and j == 0: + return grid[0][0] + # If the row or column index is out of bounds, return a +∞ cost + if i < 0 or j < 0: + return inf + # 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 + up = min_path_sum_dfs_mem(grid, mem, i - 1, j) + left = min_path_sum_dfs_mem(grid, mem, i, j - 1) + # Record and return the minimum path cost from the top-left to (i, j) + mem[i][j] = min(left, up) + grid[i][j] + return mem[i][j] + + +def min_path_sum_dp(grid: list[list[int]]) -> int: + """Minimum path sum: Dynamic programming""" + n, m = len(grid), len(grid[0]) + # Initialize dp table + dp = [[0] * m for _ in range(n)] + dp[0][0] = grid[0][0] + # State transition: first row + for j in range(1, m): + dp[0][j] = dp[0][j - 1] + grid[0][j] + # State transition: first column + for i in range(1, n): + dp[i][0] = dp[i - 1][0] + grid[i][0] + # State transition: the rest of the rows and columns + for i in range(1, n): + for j in range(1, m): + dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] + return dp[n - 1][m - 1] + + +def min_path_sum_dp_comp(grid: list[list[int]]) -> int: + """Minimum path sum: Space-optimized dynamic programming""" + n, m = len(grid), len(grid[0]) + # Initialize dp table + dp = [0] * m + # State transition: first row + dp[0] = grid[0][0] + for j in range(1, m): + dp[j] = dp[j - 1] + grid[0][j] + # State transition: the rest of the rows + for i in range(1, n): + # State transition: first column + dp[0] = dp[0] + grid[i][0] + # State transition: the rest of the columns + for j in range(1, m): + dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] + return dp[m - 1] + + +"""Driver Code""" +if __name__ == "__main__": + grid = [[1, 3, 1, 5], [2, 2, 4, 2], [5, 3, 2, 1], [4, 3, 5, 2]] + n, m = len(grid), len(grid[0]) + + # Brute force search + res = min_path_sum_dfs(grid, n - 1, m - 1) + print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") + + # Memoized search + mem = [[-1] * m for _ in range(n)] + res = min_path_sum_dfs_mem(grid, mem, n - 1, m - 1) + print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") + + # Dynamic programming + res = min_path_sum_dp(grid) + print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") + + # Space-optimized dynamic programming + res = min_path_sum_dp_comp(grid) + print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py b/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py new file mode 100644 index 0000000000..e2ad1de7bf --- /dev/null +++ b/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py @@ -0,0 +1,55 @@ +""" +File: unbounded_knapsack.py +Created Time: 2023-07-10 +Author: krahets (krahets@163.com) +""" + + +def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: + """Complete knapsack: Dynamic programming""" + n = len(wgt) + # Initialize dp table + dp = [[0] * (cap + 1) for _ in range(n + 1)] + # State transition + for i in range(1, n + 1): + for c in range(1, cap + 1): + 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] + + +def unbounded_knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: + """Complete knapsack: Space-optimized dynamic programming""" + n = len(wgt) + # Initialize dp table + dp = [0] * (cap + 1) + # State transition + for i in range(1, n + 1): + # Traverse in order + for c in range(1, cap + 1): + 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""" +if __name__ == "__main__": + wgt = [1, 2, 3] + val = [5, 11, 15] + cap = 4 + + # Dynamic programming + res = unbounded_knapsack_dp(wgt, val, cap) + print(f"The maximum item value without exceeding knapsack capacity is {res}") + + # Space-optimized dynamic programming + res = unbounded_knapsack_dp_comp(wgt, val, cap) + print(f"The maximum item value without exceeding knapsack capacity is {res}") diff --git a/en/codes/python/chapter_graph/graph_adjacency_list.py b/en/codes/python/chapter_graph/graph_adjacency_list.py new file mode 100644 index 0000000000..ecc24eb8d0 --- /dev/null +++ b/en/codes/python/chapter_graph/graph_adjacency_list.py @@ -0,0 +1,111 @@ +""" +File: graph_adjacency_list.py +Created Time: 2023-02-23 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import Vertex, vals_to_vets + + +class GraphAdjList: + """Undirected graph class based on adjacency list""" + + def __init__(self, edges: list[list[Vertex]]): + """Constructor""" + # Adjacency list, key: vertex, value: all adjacent vertices of that vertex + self.adj_list = dict[Vertex, list[Vertex]]() + # Add all vertices and edges + for edge in edges: + self.add_vertex(edge[0]) + self.add_vertex(edge[1]) + self.add_edge(edge[0], edge[1]) + + def size(self) -> int: + """Get the number of vertices""" + return len(self.adj_list) + + def add_edge(self, vet1: Vertex, vet2: Vertex): + """Add edge""" + if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: + raise ValueError() + # Add edge vet1 - vet2 + self.adj_list[vet1].append(vet2) + self.adj_list[vet2].append(vet1) + + def remove_edge(self, vet1: Vertex, vet2: Vertex): + """Remove edge""" + if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: + raise ValueError() + # Remove edge vet1 - vet2 + self.adj_list[vet1].remove(vet2) + self.adj_list[vet2].remove(vet1) + + def add_vertex(self, vet: Vertex): + """Add vertex""" + if vet in self.adj_list: + return + # Add a new linked list to the adjacency list + self.adj_list[vet] = [] + + def remove_vertex(self, vet: Vertex): + """Remove vertex""" + if vet not in self.adj_list: + raise ValueError() + # Remove the vertex vet's corresponding linked list from the adjacency list + self.adj_list.pop(vet) + # Traverse other vertices' linked lists, removing all edges containing vet + for vertex in self.adj_list: + if vet in self.adj_list[vertex]: + self.adj_list[vertex].remove(vet) + + def print(self): + """Print the adjacency list""" + print("Adjacency list =") + for vertex in self.adj_list: + tmp = [v.val for v in self.adj_list[vertex]] + print(f"{vertex.val}: {tmp},") + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize undirected graph + v = vals_to_vets([1, 3, 2, 5, 4]) + 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]], + ] + graph = GraphAdjList(edges) + print("\nAfter initialization, the graph is") + graph.print() + + # Add edge + # Vertices 1, 2 i.e., v[0], v[2] + graph.add_edge(v[0], v[2]) + print("\nAfter adding edge 1-2, the graph is") + graph.print() + + # Remove edge + # Vertices 1, 3 i.e., v[0], v[1] + graph.remove_edge(v[0], v[1]) + print("\nAfter removing edge 1-3, the graph is") + graph.print() + + # Add vertex + v5 = Vertex(6) + graph.add_vertex(v5) + print("\nAfter adding vertex 6, the graph is") + graph.print() + + # Remove vertex + # Vertex 3 i.e., v[1] + graph.remove_vertex(v[1]) + print("\nAfter removing vertex 3, the graph is") + graph.print() diff --git a/en/codes/python/chapter_graph/graph_adjacency_matrix.py b/en/codes/python/chapter_graph/graph_adjacency_matrix.py new file mode 100644 index 0000000000..80ae0a20b0 --- /dev/null +++ b/en/codes/python/chapter_graph/graph_adjacency_matrix.py @@ -0,0 +1,116 @@ +""" +File: graph_adjacency_matrix.py +Created Time: 2023-02-23 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import Vertex, print_matrix + + +class GraphAdjMat: + """Undirected graph class based on adjacency matrix""" + + def __init__(self, vertices: list[int], edges: list[list[int]]): + """Constructor""" + # Vertex list, elements represent "vertex value", index represents "vertex index" + self.vertices: list[int] = [] + # Adjacency matrix, row and column indices correspond to "vertex index" + self.adj_mat: list[list[int]] = [] + # Add vertex + for val in vertices: + self.add_vertex(val) + # Add edge + # Please note, edges elements represent vertex indices, corresponding to vertices elements indices + for e in edges: + self.add_edge(e[0], e[1]) + + def size(self) -> int: + """Get the number of vertices""" + return len(self.vertices) + + def add_vertex(self, val: int): + """Add vertex""" + n = self.size() + # Add new vertex value to the vertex list + self.vertices.append(val) + # Add a row to the adjacency matrix + new_row = [0] * n + self.adj_mat.append(new_row) + # Add a column to the adjacency matrix + for row in self.adj_mat: + row.append(0) + + def remove_vertex(self, index: int): + """Remove vertex""" + if index >= self.size(): + raise IndexError() + # Remove vertex at `index` from the vertex list + self.vertices.pop(index) + # Remove the row at `index` from the adjacency matrix + self.adj_mat.pop(index) + # Remove the column at `index` from the adjacency matrix + for row in self.adj_mat: + row.pop(index) + + def add_edge(self, i: int, j: int): + """Add edge""" + # Parameters i, j correspond to vertices element indices + # Handle index out of bounds and equality + if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: + raise IndexError() + # In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) + self.adj_mat[i][j] = 1 + self.adj_mat[j][i] = 1 + + def remove_edge(self, i: int, j: int): + """Remove edge""" + # Parameters i, j correspond to vertices element indices + # Handle index out of bounds and equality + if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: + raise IndexError() + self.adj_mat[i][j] = 0 + self.adj_mat[j][i] = 0 + + def print(self): + """Print adjacency matrix""" + print("Vertex list =", self.vertices) + print("Adjacency matrix =") + print_matrix(self.adj_mat) + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize undirected graph + # Please note, edges elements represent vertex indices, corresponding to vertices elements indices + vertices = [1, 3, 2, 5, 4] + edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]] + graph = GraphAdjMat(vertices, edges) + print("\nAfter initialization, the graph is") + graph.print() + + # Add edge + # Indices of vertices 1, 2 are 0, 2 respectively + graph.add_edge(0, 2) + print("\nAfter adding edge 1-2, the graph is") + graph.print() + + # Remove edge + # Indices of vertices 1, 3 are 0, 1 respectively + graph.remove_edge(0, 1) + print("\nAfter removing edge 1-3, the graph is") + graph.print() + + # Add vertex + graph.add_vertex(6) + print("\nAfter adding vertex 6, the graph is") + graph.print() + + # Remove vertex + # Index of vertex 3 is 1 + graph.remove_vertex(1) + print("\nAfter removing vertex 3, the graph is") + graph.print() diff --git a/en/codes/python/chapter_graph/graph_bfs.py b/en/codes/python/chapter_graph/graph_bfs.py new file mode 100644 index 0000000000..b1e73b9d38 --- /dev/null +++ b/en/codes/python/chapter_graph/graph_bfs.py @@ -0,0 +1,64 @@ +""" +File: graph_bfs.py +Created Time: 2023-02-23 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import Vertex, vals_to_vets, vets_to_vals +from collections import deque +from graph_adjacency_list import GraphAdjList + + +def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: + """Breadth-first traversal""" + # Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + # Vertex traversal sequence + res = [] + # Hash set, used to record visited vertices + visited = set[Vertex]([start_vet]) + # Queue used to implement BFS + que = deque[Vertex]([start_vet]) + # Starting from vertex vet, loop until all vertices are visited + while len(que) > 0: + vet = que.popleft() # Dequeue the vertex at the head of the queue + res.append(vet) # Record visited vertex + # Traverse all adjacent vertices of that vertex + for adj_vet in graph.adj_list[vet]: + if adj_vet in visited: + continue # Skip already visited vertices + que.append(adj_vet) # Only enqueue unvisited vertices + visited.add(adj_vet) # Mark the vertex as visited + # Return the vertex traversal sequence + return res + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize undirected graph + v = vals_to_vets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + 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]], + ] + graph = GraphAdjList(edges) + print("\nAfter initialization, the graph is") + graph.print() + + # Breadth-first traversal + res = graph_bfs(graph, v[0]) + print("\nBreadth-first traversal (BFS) vertex sequence is") + print(vets_to_vals(res)) diff --git a/en/codes/python/chapter_graph/graph_dfs.py b/en/codes/python/chapter_graph/graph_dfs.py new file mode 100644 index 0000000000..de2b619e9a --- /dev/null +++ b/en/codes/python/chapter_graph/graph_dfs.py @@ -0,0 +1,57 @@ +""" +File: graph_dfs.py +Created Time: 2023-02-23 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import Vertex, vets_to_vals, vals_to_vets +from graph_adjacency_list import GraphAdjList + + +def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex): + """Depth-first traversal helper function""" + res.append(vet) # Record visited vertex + visited.add(vet) # Mark the vertex as visited + # Traverse all adjacent vertices of that vertex + for adjVet in graph.adj_list[vet]: + if adjVet in visited: + continue # Skip already visited vertices + # Recursively visit adjacent vertices + dfs(graph, visited, res, adjVet) + + +def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: + """Depth-first traversal""" + # Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + # Vertex traversal sequence + res = [] + # Hash set, used to record visited vertices + visited = set[Vertex]() + dfs(graph, visited, res, start_vet) + return res + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize undirected graph + v = vals_to_vets([0, 1, 2, 3, 4, 5, 6]) + 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]], + ] + graph = GraphAdjList(edges) + print("\nAfter initialization, the graph is") + graph.print() + + # Depth-first traversal + res = graph_dfs(graph, v[0]) + print("\nDepth-first traversal (DFS) vertex sequence is") + print(vets_to_vals(res)) diff --git a/en/codes/python/chapter_greedy/coin_change_greedy.py b/en/codes/python/chapter_greedy/coin_change_greedy.py new file mode 100644 index 0000000000..9f3afb31a1 --- /dev/null +++ b/en/codes/python/chapter_greedy/coin_change_greedy.py @@ -0,0 +1,48 @@ +""" +File: coin_change_greedy.py +Created Time: 2023-07-18 +Author: krahets (krahets@163.com) +""" + + +def coin_change_greedy(coins: list[int], amt: int) -> int: + """Coin change: Greedy""" + # Assume coins list is ordered + i = len(coins) - 1 + 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 and coins[i] > amt: + i -= 1 + # Choose coins[i] + amt -= coins[i] + count += 1 + # If no feasible solution is found, return -1 + return count if amt == 0 else -1 + + +"""Driver Code""" +if __name__ == "__main__": + # Greedy: can ensure finding a global optimal solution + coins = [1, 5, 10, 20, 50, 100] + amt = 186 + res = coin_change_greedy(coins, amt) + print(f"\ncoins = {coins}, amt = {amt}") + print(f"The minimum number of coins needed to make up {amt} is {res}") + + # Greedy: cannot ensure finding a global optimal solution + coins = [1, 20, 50] + amt = 60 + res = coin_change_greedy(coins, amt) + print(f"\ncoins = {coins}, amt = {amt}") + print(f"The minimum number of coins needed to make up {amt} is {res}") + print(f"In reality, the minimum number needed is 3, i.e., 20 + 20 + 20") + + # Greedy: cannot ensure finding a global optimal solution + coins = [1, 49, 50] + amt = 98 + res = coin_change_greedy(coins, amt) + print(f"\ncoins = {coins}, amt = {amt}") + print(f"The minimum number of coins needed to make up {amt} is {res}") + print(f"In reality, the minimum number needed is 2, i.e., 49 + 49") diff --git a/en/codes/python/chapter_greedy/fractional_knapsack.py b/en/codes/python/chapter_greedy/fractional_knapsack.py new file mode 100644 index 0000000000..96be870304 --- /dev/null +++ b/en/codes/python/chapter_greedy/fractional_knapsack.py @@ -0,0 +1,46 @@ +""" +File: fractional_knapsack.py +Created Time: 2023-07-19 +Author: krahets (krahets@163.com) +""" + + +class Item: + """Item""" + + def __init__(self, w: int, v: int): + self.w = w # Item weight + self.v = v # Item value + + +def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int: + """Fractional knapsack: Greedy""" + # Create an item list, containing two properties: weight, value + items = [Item(w, v) for w, v in zip(wgt, val)] + # Sort by unit value item.v / item.w from high to low + items.sort(key=lambda item: item.v / item.w, reverse=True) + # Loop for greedy selection + res = 0 + for item in 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 += (item.v / item.w) * cap + # No remaining capacity left, thus break the loop + break + return res + + +"""Driver Code""" +if __name__ == "__main__": + wgt = [10, 20, 30, 40, 50] + val = [50, 120, 150, 210, 240] + cap = 50 + n = len(wgt) + + # Greedy algorithm + res = fractional_knapsack(wgt, val, cap) + print(f"The maximum item value without exceeding knapsack capacity is {res}") diff --git a/en/codes/python/chapter_greedy/max_capacity.py b/en/codes/python/chapter_greedy/max_capacity.py new file mode 100644 index 0000000000..411d623570 --- /dev/null +++ b/en/codes/python/chapter_greedy/max_capacity.py @@ -0,0 +1,33 @@ +""" +File: max_capacity.py +Created Time: 2023-07-21 +Author: krahets (krahets@163.com) +""" + + +def max_capacity(ht: list[int]) -> int: + """Maximum capacity: Greedy""" + # Initialize i, j, making them split the array at both ends + i, j = 0, len(ht) - 1 + # Initial maximum capacity is 0 + res = 0 + # Loop for greedy selection until the two boards meet + while i < j: + # Update maximum capacity + cap = min(ht[i], ht[j]) * (j - i) + res = max(res, cap) + # Move the shorter board inward + if ht[i] < ht[j]: + i += 1 + else: + j -= 1 + return res + + +"""Driver Code""" +if __name__ == "__main__": + ht = [3, 8, 5, 2, 7, 7, 3, 4] + + # Greedy algorithm + res = max_capacity(ht) + print(f"Maximum capacity is {res}") diff --git a/en/codes/python/chapter_greedy/max_product_cutting.py b/en/codes/python/chapter_greedy/max_product_cutting.py new file mode 100644 index 0000000000..923c953505 --- /dev/null +++ b/en/codes/python/chapter_greedy/max_product_cutting.py @@ -0,0 +1,33 @@ +""" +File: max_product_cutting.py +Created Time: 2023-07-21 +Author: krahets (krahets@163.com) +""" + +import math + + +def max_product_cutting(n: int) -> int: + """Maximum product of cutting: Greedy""" + # 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 + a, b = n // 3, n % 3 + if b == 1: + # When the remainder is 1, convert a pair of 1 * 3 into 2 * 2 + return int(math.pow(3, a - 1)) * 2 * 2 + if b == 2: + # When the remainder is 2, do nothing + return int(math.pow(3, a)) * 2 + # When the remainder is 0, do nothing + return int(math.pow(3, a)) + + +"""Driver Code""" +if __name__ == "__main__": + n = 58 + + # Greedy algorithm + res = max_product_cutting(n) + print(f"Maximum product of cutting is {res}") diff --git a/en/codes/python/chapter_hashing/array_hash_map.py b/en/codes/python/chapter_hashing/array_hash_map.py new file mode 100644 index 0000000000..88daadf3aa --- /dev/null +++ b/en/codes/python/chapter_hashing/array_hash_map.py @@ -0,0 +1,117 @@ +""" +File: array_hash_map.py +Created Time: 2022-12-14 +Author: msk397 (machangxinq@gmail.com) +""" + + +class Pair: + """Key-value pair""" + + def __init__(self, key: int, val: str): + self.key = key + self.val = val + + +class ArrayHashMap: + """Hash table based on array implementation""" + + def __init__(self): + """Constructor""" + # Initialize an array, containing 100 buckets + self.buckets: list[Pair | None] = [None] * 100 + + def hash_func(self, key: int) -> int: + """Hash function""" + index = key % 100 + return index + + def get(self, key: int) -> str: + """Query operation""" + index: int = self.hash_func(key) + pair: Pair = self.buckets[index] + if pair is None: + return None + return pair.val + + def put(self, key: int, val: str): + """Add operation""" + pair = Pair(key, val) + index: int = self.hash_func(key) + self.buckets[index] = pair + + def remove(self, key: int): + """Remove operation""" + index: int = self.hash_func(key) + # Set to None, representing removal + self.buckets[index] = None + + def entry_set(self) -> list[Pair]: + """Get all key-value pairs""" + result: list[Pair] = [] + for pair in self.buckets: + if pair is not None: + result.append(pair) + return result + + def key_set(self) -> list[int]: + """Get all keys""" + result = [] + for pair in self.buckets: + if pair is not None: + result.append(pair.key) + return result + + def value_set(self) -> list[str]: + """Get all values""" + result = [] + for pair in self.buckets: + if pair is not None: + result.append(pair.val) + return result + + def print(self): + """Print hash table""" + for pair in self.buckets: + if pair is not None: + print(pair.key, "->", pair.val) + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize hash table + hmap = ArrayHashMap() + + # Add operation + # Add key-value pair (key, value) to the hash table + hmap.put(12836, "Ha") + hmap.put(15937, "Luo") + hmap.put(16750, "Suan") + hmap.put(13276, "Fa") + hmap.put(10583, "Ya") + print("\nAfter adding, the hash table is\nKey -> Value") + hmap.print() + + # Query operation + # Enter key to the hash table, get value + name = hmap.get(15937) + print("\nEnter student ID 15937, found name " + name) + + # Remove operation + # Remove key-value pair (key, value) from the hash table + hmap.remove(10583) + print("\nAfter removing 10583, the hash table is\nKey -> Value") + hmap.print() + + # Traverse hash table + print("\nTraverse key-value pairs Key->Value") + for pair in hmap.entry_set(): + print(pair.key, "->", pair.val) + + print("\nIndividually traverse keys Key") + for key in hmap.key_set(): + print(key) + + print("\nIndividually traverse values Value") + for val in hmap.value_set(): + print(val) diff --git a/en/codes/python/chapter_hashing/built_in_hash.py b/en/codes/python/chapter_hashing/built_in_hash.py new file mode 100644 index 0000000000..323b6933c4 --- /dev/null +++ b/en/codes/python/chapter_hashing/built_in_hash.py @@ -0,0 +1,37 @@ +""" +File: built_in_hash.py +Created Time: 2023-06-15 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import ListNode + +"""Driver Code""" +if __name__ == "__main__": + num = 3 + hash_num = hash(num) + print(f"Integer {num}'s hash value is {hash_num}") + + bol = True + hash_bol = hash(bol) + print(f"Boolean {bol}'s hash value is {hash_bol}") + + dec = 3.14159 + hash_dec = hash(dec) + print(f"Decimal {dec}'s hash value is {hash_dec}") + + str = "Hello algorithm" + hash_str = hash(str) + print(f"String {str}'s hash value is {hash_str}") + + tup = (12836, "Ha") + hash_tup = hash(tup) + print(f"Tuple {tup}'s hash value is {hash(hash_tup)}") + + obj = ListNode(0) + hash_obj = hash(obj) + print(f"Node object {obj}'s hash value is {hash_obj}") diff --git a/en/codes/python/chapter_hashing/hash_map.py b/en/codes/python/chapter_hashing/hash_map.py new file mode 100644 index 0000000000..e1f2b74620 --- /dev/null +++ b/en/codes/python/chapter_hashing/hash_map.py @@ -0,0 +1,50 @@ +""" +File: hash_map.py +Created Time: 2022-12-14 +Author: msk397 (machangxinq@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import print_dict + +"""Driver Code""" +if __name__ == "__main__": + # Initialize hash table + hmap = dict[int, str]() + + # Add operation + # Add key-value pair (key, value) to the hash table + hmap[12836] = "Ha" + hmap[15937] = "Luo" + hmap[16750] = "Suan" + hmap[13276] = "Fa" + hmap[10583] = "Ya" + print("\nAfter adding, the hash table is\nKey -> Value") + print_dict(hmap) + + # Query operation + # Enter key to the hash table, get value + name: str = hmap[15937] + print("\nEnter student ID 15937, found name " + name) + + # Remove operation + # Remove key-value pair (key, value) from the hash table + hmap.pop(10583) + print("\nAfter removing 10583, the hash table is\nKey -> Value") + print_dict(hmap) + + # Traverse hash table + print("\nTraverse key-value pairs Key->Value") + for key, value in hmap.items(): + print(key, "->", value) + + print("\nIndividually traverse keys Key") + for key in hmap.keys(): + print(key) + + print("\nIndividually traverse values Value") + for val in hmap.values(): + print(val) diff --git a/en/codes/python/chapter_hashing/hash_map_chaining.py b/en/codes/python/chapter_hashing/hash_map_chaining.py new file mode 100644 index 0000000000..f6e47c2eac --- /dev/null +++ b/en/codes/python/chapter_hashing/hash_map_chaining.py @@ -0,0 +1,118 @@ +""" +File: hash_map_chaining.py +Created Time: 2023-06-13 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from chapter_hashing.array_hash_map import Pair + + +class HashMapChaining: + """Chained address hash table""" + + def __init__(self): + """Constructor""" + self.size = 0 # Number of key-value pairs + self.capacity = 4 # Hash table capacity + self.load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion + self.extend_ratio = 2 # Expansion multiplier + self.buckets = [[] for _ in range(self.capacity)] # Bucket array + + def hash_func(self, key: int) -> int: + """Hash function""" + return key % self.capacity + + def load_factor(self) -> float: + """Load factor""" + return self.size / self.capacity + + def get(self, key: int) -> str | None: + """Query operation""" + index = self.hash_func(key) + bucket = self.buckets[index] + # Traverse the bucket, if the key is found, return the corresponding val + for pair in bucket: + if pair.key == key: + return pair.val + # If the key is not found, return None + return None + + def put(self, key: int, val: str): + """Add operation""" + # When the load factor exceeds the threshold, perform expansion + if self.load_factor() > self.load_thres: + self.extend() + index = self.hash_func(key) + bucket = self.buckets[index] + # Traverse the bucket, if the specified key is encountered, update the corresponding val and return + for pair in bucket: + if pair.key == key: + pair.val = val + return + # If the key is not found, add the key-value pair to the end + pair = Pair(key, val) + bucket.append(pair) + self.size += 1 + + def remove(self, key: int): + """Remove operation""" + index = self.hash_func(key) + bucket = self.buckets[index] + # Traverse the bucket, remove the key-value pair from it + for pair in bucket: + if pair.key == key: + bucket.remove(pair) + self.size -= 1 + break + + def extend(self): + """Extend hash table""" + # Temporarily store the original hash table + buckets = self.buckets + # Initialize the extended new hash table + self.capacity *= self.extend_ratio + self.buckets = [[] for _ in range(self.capacity)] + self.size = 0 + # Move key-value pairs from the original hash table to the new hash table + for bucket in buckets: + for pair in bucket: + self.put(pair.key, pair.val) + + def print(self): + """Print hash table""" + for bucket in self.buckets: + res = [] + for pair in bucket: + res.append(str(pair.key) + " -> " + pair.val) + print(res) + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize hash table + hashmap = HashMapChaining() + + # Add operation + # Add key-value pair (key, value) to the hash table + hashmap.put(12836, "Ha") + hashmap.put(15937, "Luo") + hashmap.put(16750, "Suan") + hashmap.put(13276, "Fa") + hashmap.put(10583, "Ya") + print("\nAfter adding, the hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]") + hashmap.print() + + # Query operation + # Enter key to the hash table, get value + name = hashmap.get(13276) + print("\nEnter student ID 13276, found name " + name) + + # Remove operation + # Remove key-value pair (key, value) from the hash table + hashmap.remove(12836) + print("\nAfter removing 12836, the hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]") + hashmap.print() diff --git a/en/codes/python/chapter_hashing/hash_map_open_addressing.py b/en/codes/python/chapter_hashing/hash_map_open_addressing.py new file mode 100644 index 0000000000..9c9e7d14dd --- /dev/null +++ b/en/codes/python/chapter_hashing/hash_map_open_addressing.py @@ -0,0 +1,138 @@ +""" +File: hash_map_open_addressing.py +Created Time: 2023-06-13 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from chapter_hashing.array_hash_map import Pair + + +class HashMapOpenAddressing: + """Open addressing hash table""" + + def __init__(self): + """Constructor""" + self.size = 0 # Number of key-value pairs + self.capacity = 4 # Hash table capacity + self.load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion + self.extend_ratio = 2 # Expansion multiplier + self.buckets: list[Pair | None] = [None] * self.capacity # Bucket array + self.TOMBSTONE = Pair(-1, "-1") # Removal mark + + def hash_func(self, key: int) -> int: + """Hash function""" + return key % self.capacity + + def load_factor(self) -> float: + """Load factor""" + return self.size / self.capacity + + def find_bucket(self, key: int) -> int: + """Search for the bucket index corresponding to key""" + index = self.hash_func(key) + first_tombstone = -1 + # Linear probing, break when encountering an empty bucket + while self.buckets[index] is not None: + # If the key is encountered, return the corresponding bucket index + if self.buckets[index].key == key: + # If a removal mark was encountered earlier, move the key-value pair to that index + if first_tombstone != -1: + self.buckets[first_tombstone] = self.buckets[index] + self.buckets[index] = self.TOMBSTONE + return first_tombstone # Return the moved bucket index + return index # Return bucket index + # Record the first encountered removal mark + if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE: + first_tombstone = index + # Calculate the bucket index, return to the head if exceeding the tail + index = (index + 1) % self.capacity + # If the key does not exist, return the index of the insertion point + return index if first_tombstone == -1 else first_tombstone + + def get(self, key: int) -> str: + """Query operation""" + # Search for the bucket index corresponding to key + index = self.find_bucket(key) + # If the key-value pair is found, return the corresponding val + if self.buckets[index] not in [None, self.TOMBSTONE]: + return self.buckets[index].val + # If the key-value pair does not exist, return None + return None + + def put(self, key: int, val: str): + """Add operation""" + # When the load factor exceeds the threshold, perform expansion + if self.load_factor() > self.load_thres: + self.extend() + # Search for the bucket index corresponding to key + index = self.find_bucket(key) + # If the key-value pair is found, overwrite val and return + if self.buckets[index] not in [None, self.TOMBSTONE]: + self.buckets[index].val = val + return + # If the key-value pair does not exist, add the key-value pair + self.buckets[index] = Pair(key, val) + self.size += 1 + + def remove(self, key: int): + """Remove operation""" + # Search for the bucket index corresponding to key + index = self.find_bucket(key) + # If the key-value pair is found, cover it with a removal mark + if self.buckets[index] not in [None, self.TOMBSTONE]: + self.buckets[index] = self.TOMBSTONE + self.size -= 1 + + def extend(self): + """Extend hash table""" + # Temporarily store the original hash table + buckets_tmp = self.buckets + # Initialize the extended new hash table + self.capacity *= self.extend_ratio + self.buckets = [None] * self.capacity + self.size = 0 + # Move key-value pairs from the original hash table to the new hash table + for pair in buckets_tmp: + if pair not in [None, self.TOMBSTONE]: + self.put(pair.key, pair.val) + + def print(self): + """Print hash table""" + for pair in self.buckets: + if pair is None: + print("None") + elif pair is self.TOMBSTONE: + print("TOMBSTONE") + else: + print(pair.key, "->", pair.val) + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize hash table + hashmap = HashMapOpenAddressing() + + # 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") + print("\nAfter adding, the hash table is\nKey -> Value") + hashmap.print() + + # Query operation + # Enter key to the hash table, get value val + name = hashmap.get(13276) + print("\nEnter student ID 13276, found name " + name) + + # Remove operation + # Remove key-value pair (key, val) from the hash table + hashmap.remove(16750) + print("\nAfter removing 16750, the hash table is\nKey -> Value") + hashmap.print() diff --git a/en/codes/python/chapter_hashing/simple_hash.py b/en/codes/python/chapter_hashing/simple_hash.py new file mode 100644 index 0000000000..64614f558f --- /dev/null +++ b/en/codes/python/chapter_hashing/simple_hash.py @@ -0,0 +1,58 @@ +""" +File: simple_hash.py +Created Time: 2023-06-15 +Author: krahets (krahets@163.com) +""" + + +def add_hash(key: str) -> int: + """Additive hash""" + hash = 0 + modulus = 1000000007 + for c in key: + hash += ord(c) + return hash % modulus + + +def mul_hash(key: str) -> int: + """Multiplicative hash""" + hash = 0 + modulus = 1000000007 + for c in key: + hash = 31 * hash + ord(c) + return hash % modulus + + +def xor_hash(key: str) -> int: + """XOR hash""" + hash = 0 + modulus = 1000000007 + for c in key: + hash ^= ord(c) + return hash % modulus + + +def rot_hash(key: str) -> int: + """Rotational hash""" + hash = 0 + modulus = 1000000007 + for c in key: + hash = (hash << 4) ^ (hash >> 28) ^ ord(c) + return hash % modulus + + +"""Driver Code""" +if __name__ == "__main__": + key = "Hello algorithm" + + hash = add_hash(key) + print(f"Additive hash value is {hash}") + + hash = mul_hash(key) + print(f"Multiplicative hash value is {hash}") + + hash = xor_hash(key) + print(f"XOR hash value is {hash}") + + hash = rot_hash(key) + print(f"Rotational hash value is {hash}") diff --git a/en/codes/python/chapter_heap/heap.py b/en/codes/python/chapter_heap/heap.py new file mode 100644 index 0000000000..247e3cb46b --- /dev/null +++ b/en/codes/python/chapter_heap/heap.py @@ -0,0 +1,71 @@ +""" +File: heap.py +Created Time: 2023-02-23 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import print_heap + +import heapq + + +def test_push(heap: list, val: int, flag: int = 1): + heapq.heappush(heap, flag * val) # Push the element into heap + print(f"\nElement {val} after pushed into heap") + print_heap([flag * val for val in heap]) + + +def test_pop(heap: list, flag: int = 1): + val = flag * heapq.heappop(heap) # Pop the element at the heap top + print(f"\nHeap top element {val} after exiting heap") + print_heap([flag * val for val in heap]) + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize min-heap + min_heap, flag = [], 1 + # Initialize max-heap + max_heap, flag = [], -1 + + print("\nThe following test case is for max-heap") + # Python's heapq module implements min-heap by default + # Consider "negating the elements" before entering the heap, thus reversing the comparator to implement a max-heap + # In this example, flag = 1 corresponds to min-heap, flag = -1 corresponds to max-heap + + # Push the element into heap + test_push(max_heap, 1, flag) + test_push(max_heap, 3, flag) + test_push(max_heap, 2, flag) + test_push(max_heap, 5, flag) + test_push(max_heap, 4, flag) + + # Access heap top element + peek: int = flag * max_heap[0] + print(f"\nHeap top element is {peek}") + + # Pop the element at the heap top + test_pop(max_heap, flag) + test_pop(max_heap, flag) + test_pop(max_heap, flag) + test_pop(max_heap, flag) + test_pop(max_heap, flag) + + # Get heap size + size: int = len(max_heap) + print(f"\nNumber of heap elements is {size}") + + # Determine if heap is empty + is_empty: bool = not max_heap + print(f"\nIs the heap empty {is_empty}") + + # Enter list and build heap + # Time complexity is O(n), not O(nlogn) + min_heap = [1, 3, 2, 5, 4] + heapq.heapify(min_heap) + print("\nEnter list and build min-heap") + print_heap(min_heap) diff --git a/en/codes/python/chapter_heap/my_heap.py b/en/codes/python/chapter_heap/my_heap.py new file mode 100644 index 0000000000..bb78d94937 --- /dev/null +++ b/en/codes/python/chapter_heap/my_heap.py @@ -0,0 +1,137 @@ +""" +File: my_heap.py +Created Time: 2023-02-23 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import print_heap + + +class MaxHeap: + """Max-heap""" + + def __init__(self, nums: list[int]): + """Constructor, build heap based on input list""" + # Add all list elements into the heap + self.max_heap = nums + # Heapify all nodes except leaves + for i in range(self.parent(self.size() - 1), -1, -1): + self.sift_down(i) + + def left(self, i: int) -> int: + """Get index of left child node""" + return 2 * i + 1 + + def right(self, i: int) -> int: + """Get index of right child node""" + return 2 * i + 2 + + def parent(self, i: int) -> int: + """Get index of parent node""" + return (i - 1) // 2 # Integer division down + + def swap(self, i: int, j: int): + """Swap elements""" + self.max_heap[i], self.max_heap[j] = self.max_heap[j], self.max_heap[i] + + def size(self) -> int: + """Get heap size""" + return len(self.max_heap) + + def is_empty(self) -> bool: + """Determine if heap is empty""" + return self.size() == 0 + + def peek(self) -> int: + """Access heap top element""" + return self.max_heap[0] + + def push(self, val: int): + """Push the element into heap""" + # Add node + self.max_heap.append(val) + # Heapify from bottom to top + self.sift_up(self.size() - 1) + + def sift_up(self, i: int): + """Start heapifying node i, from bottom to top""" + while True: + # Get parent node of node i + p = self.parent(i) + # When "crossing the root node" or "node does not need repair", end heapification + if p < 0 or self.max_heap[i] <= self.max_heap[p]: + break + # Swap two nodes + self.swap(i, p) + # Loop upwards heapification + i = p + + def pop(self) -> int: + """Element exits heap""" + # Empty handling + if self.is_empty(): + raise IndexError("Heap is empty") + # Swap the root node with the rightmost leaf node (swap the first element with the last element) + self.swap(0, self.size() - 1) + # Remove node + val = self.max_heap.pop() + # Heapify from top to bottom + self.sift_down(0) + # Return heap top element + return val + + def sift_down(self, i: int): + """Start heapifying node i, from top to bottom""" + while True: + # Determine the largest node among i, l, r, noted as ma + l, r, ma = self.left(i), self.right(i), i + if l < self.size() and self.max_heap[l] > self.max_heap[ma]: + ma = l + if r < self.size() and self.max_heap[r] > self.max_heap[ma]: + ma = r + # If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + if ma == i: + break + # Swap two nodes + self.swap(i, ma) + # Loop downwards heapification + i = ma + + def print(self): + """Print heap (binary tree)""" + print_heap(self.max_heap) + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize max-heap + max_heap = MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) + print("\nEnter list and build heap") + max_heap.print() + + # Access heap top element + peek = max_heap.peek() + print(f"\nHeap top element is {peek}") + + # Push the element into heap + val = 7 + max_heap.push(val) + print(f"\nElement {val} after pushed into heap") + max_heap.print() + + # Pop the element at the heap top + peek = max_heap.pop() + print(f"\nHeap top element {peek} after exiting heap") + max_heap.print() + + # Get heap size + size = max_heap.size() + print(f"\nNumber of heap elements is {size}") + + # Determine if heap is empty + is_empty = max_heap.is_empty() + print(f"\nIs the heap empty {is_empty}") diff --git a/en/codes/python/chapter_heap/top_k.py b/en/codes/python/chapter_heap/top_k.py new file mode 100644 index 0000000000..804c297513 --- /dev/null +++ b/en/codes/python/chapter_heap/top_k.py @@ -0,0 +1,39 @@ +""" +File: top_k.py +Created Time: 2023-06-10 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import print_heap + +import heapq + + +def top_k_heap(nums: list[int], k: int) -> list[int]: + """Using heap to find the largest k elements in an array""" + # Initialize min-heap + heap = [] + # Enter the first k elements of the array into the heap + for i in range(k): + heapq.heappush(heap, nums[i]) + # From the k+1th element, keep the heap length as k + for i in range(k, len(nums)): + # 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[0]: + heapq.heappop(heap) + heapq.heappush(heap, nums[i]) + return heap + + +"""Driver Code""" +if __name__ == "__main__": + nums = [1, 7, 6, 3, 2] + k = 3 + + res = top_k_heap(nums, k) + print(f"The largest {k} elements are") + print_heap(res) diff --git a/en/codes/python/chapter_searching/binary_search.py b/en/codes/python/chapter_searching/binary_search.py new file mode 100644 index 0000000000..fd0d9c078d --- /dev/null +++ b/en/codes/python/chapter_searching/binary_search.py @@ -0,0 +1,52 @@ +""" +File: binary_search.py +Created Time: 2022-11-26 +Author: timi (xisunyy@163.com) +""" + + +def binary_search(nums: list[int], target: int) -> int: + """Binary search (double closed interval)""" + # Initialize double closed interval [0, n-1], i.e., i, j point to the first element and last element of the array respectively + i, j = 0, len(nums) - 1 + # Loop until the search interval is empty (when i > j, it is empty) + while i <= j: + # Theoretically, Python's numbers can be infinitely large (depending on memory size), so there is no need to consider large number overflow + m = (i + j) // 2 # Calculate midpoint index m + if nums[m] < target: + i = m + 1 # This situation indicates that target is in the interval [m+1, j] + elif nums[m] > target: + j = m - 1 # This situation indicates that target is in the interval [i, m-1] + else: + return m # Found the target element, thus return its index + return -1 # Did not find the target element, thus return -1 + + +def binary_search_lcro(nums: list[int], target: int) -> int: + """Binary search (left closed right open interval)""" + # Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively + i, j = 0, len(nums) + # Loop until the search interval is empty (when i = j, it is empty) + while i < j: + m = (i + j) // 2 # Calculate midpoint index m + if nums[m] < target: + i = m + 1 # This situation indicates that target is in the interval [m+1, j) + elif nums[m] > target: + j = m # This situation indicates that target is in the interval [i, m) + else: + return m # Found the target element, thus return its index + return -1 # Did not find the target element, thus return -1 + + +"""Driver Code""" +if __name__ == "__main__": + target = 6 + nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + + # Binary search (double closed interval) + index = binary_search(nums, target) + print("Index of target element 6 =", index) + + # Binary search (left closed right open interval) + index = binary_search_lcro(nums, target) + print("Index of target element 6 =", index) diff --git a/en/codes/python/chapter_searching/binary_search_edge.py b/en/codes/python/chapter_searching/binary_search_edge.py new file mode 100644 index 0000000000..9edf577d1a --- /dev/null +++ b/en/codes/python/chapter_searching/binary_search_edge.py @@ -0,0 +1,49 @@ +""" +File: binary_search_edge.py +Created Time: 2023-08-04 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from binary_search_insertion import binary_search_insertion + + +def binary_search_left_edge(nums: list[int], target: int) -> int: + """Binary search for the leftmost target""" + # Equivalent to finding the insertion point of target + i = binary_search_insertion(nums, target) + # Did not find target, thus return -1 + if i == len(nums) or nums[i] != target: + return -1 + # Found target, return index i + return i + + +def binary_search_right_edge(nums: list[int], target: int) -> int: + """Binary search for the rightmost target""" + # Convert to finding the leftmost target + 1 + i = binary_search_insertion(nums, target + 1) + # j points to the rightmost target, i points to the first element greater than target + j = i - 1 + # Did not find target, thus return -1 + if j == -1 or nums[j] != target: + return -1 + # Found target, return index j + return j + + +"""Driver Code""" +if __name__ == "__main__": + # Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] + print(f"\nArray nums = {nums}") + + # Binary search for left and right boundaries + for target in [6, 7]: + index = binary_search_left_edge(nums, target) + print(f"The index of the leftmost element {target} is {index}") + index = binary_search_right_edge(nums, target) + print(f"The index of the rightmost element {target} is {index}") diff --git a/en/codes/python/chapter_searching/binary_search_insertion.py b/en/codes/python/chapter_searching/binary_search_insertion.py new file mode 100644 index 0000000000..e63f9e6c6c --- /dev/null +++ b/en/codes/python/chapter_searching/binary_search_insertion.py @@ -0,0 +1,54 @@ +""" +File: binary_search_insertion.py +Created Time: 2023-08-04 +Author: krahets (krahets@163.com) +""" + + +def binary_search_insertion_simple(nums: list[int], target: int) -> int: + """Binary search for insertion point (no duplicate elements)""" + i, j = 0, len(nums) - 1 # Initialize double closed interval [0, n-1] + while i <= j: + m = (i + j) // 2 # Calculate midpoint index m + if nums[m] < target: + i = m + 1 # Target is in interval [m+1, j] + elif 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 + + +def binary_search_insertion(nums: list[int], target: int) -> int: + """Binary search for insertion point (with duplicate elements)""" + i, j = 0, len(nums) - 1 # Initialize double closed interval [0, n-1] + while i <= j: + m = (i + j) // 2 # Calculate midpoint index m + if nums[m] < target: + i = m + 1 # Target is in interval [m+1, j] + elif 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""" +if __name__ == "__main__": + # Array without duplicate elements + nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + print(f"\nArray nums = {nums}") + # Binary search for insertion point + for target in [6, 9]: + index = binary_search_insertion_simple(nums, target) + print(f"Element {target}'s insertion point index is {index}") + + # Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] + print(f"\nArray nums = {nums}") + # Binary search for insertion point + for target in [2, 6, 20]: + index = binary_search_insertion(nums, target) + print(f"Element {target}'s insertion point index is {index}") diff --git a/en/codes/python/chapter_searching/hashing_search.py b/en/codes/python/chapter_searching/hashing_search.py new file mode 100644 index 0000000000..835240639e --- /dev/null +++ b/en/codes/python/chapter_searching/hashing_search.py @@ -0,0 +1,51 @@ +""" +File: hashing_search.py +Created Time: 2022-11-26 +Author: timi (xisunyy@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import ListNode, list_to_linked_list + + +def hashing_search_array(hmap: dict[int, int], target: int) -> int: + """Hash search (array)""" + # Hash table's key: target element, value: index + # If the hash table does not contain this key, return -1 + return hmap.get(target, -1) + + +def hashing_search_linkedlist( + hmap: dict[int, ListNode], target: int +) -> ListNode | None: + """Hash search (linked list)""" + # Hash table's key: target element, value: node object + # If the hash table does not contain this key, return None + return hmap.get(target, None) + + +"""Driver Code""" +if __name__ == "__main__": + target = 3 + + # Hash search (array) + nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] + # Initialize hash table + map0 = dict[int, int]() + for i in range(len(nums)): + map0[nums[i]] = i # key: element, value: index + index: int = hashing_search_array(map0, target) + print("Index of target element 3 =", index) + + # Hash search (linked list) + head: ListNode = list_to_linked_list(nums) + # Initialize hash table + map1 = dict[int, ListNode]() + while head: + map1[head.val] = head # key: node value, value: node + head = head.next + node: ListNode = hashing_search_linkedlist(map1, target) + print("Target node value 3's corresponding node object is", node) diff --git a/en/codes/python/chapter_searching/linear_search.py b/en/codes/python/chapter_searching/linear_search.py new file mode 100644 index 0000000000..84bd83301a --- /dev/null +++ b/en/codes/python/chapter_searching/linear_search.py @@ -0,0 +1,45 @@ +""" +File: linear_search.py +Created Time: 2022-11-26 +Author: timi (xisunyy@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import ListNode, list_to_linked_list + + +def linear_search_array(nums: list[int], target: int) -> int: + """Linear search (array)""" + # Traverse array + for i in range(len(nums)): + if nums[i] == target: # Found the target element, thus return its index + return i + return -1 # Did not find the target element, thus return -1 + + +def linear_search_linkedlist(head: ListNode, target: int) -> ListNode | None: + """Linear search (linked list)""" + # Traverse the list + while head: + if head.val == target: # Found the target node, return it + return head + head = head.next + return None # Did not find the target node, thus return None + + +"""Driver Code""" +if __name__ == "__main__": + target = 3 + + # Perform linear search in array + nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] + index: int = linear_search_array(nums, target) + print("Index of target element 3 =", index) + + # Perform linear search in linked list + head: ListNode = list_to_linked_list(nums) + node: ListNode | None = linear_search_linkedlist(head, target) + print("Target node value 3's corresponding node object is", node) diff --git a/en/codes/python/chapter_searching/two_sum.py b/en/codes/python/chapter_searching/two_sum.py new file mode 100644 index 0000000000..52b34e4608 --- /dev/null +++ b/en/codes/python/chapter_searching/two_sum.py @@ -0,0 +1,42 @@ +""" +File: two_sum.py +Created Time: 2022-11-25 +Author: krahets (krahets@163.com) +""" + + +def two_sum_brute_force(nums: list[int], target: int) -> list[int]: + """Method one: Brute force enumeration""" + # Two-layer loop, time complexity is O(n^2) + for i in range(len(nums) - 1): + for j in range(i + 1, len(nums)): + if nums[i] + nums[j] == target: + return [i, j] + return [] + + +def two_sum_hash_table(nums: list[int], target: int) -> list[int]: + """Method two: Auxiliary hash table""" + # Auxiliary hash table, space complexity is O(n) + dic = {} + # Single-layer loop, time complexity is O(n) + for i in range(len(nums)): + if target - nums[i] in dic: + return [dic[target - nums[i]], i] + dic[nums[i]] = i + return [] + + +"""Driver Code""" +if __name__ == "__main__": + # ======= Test Case ======= + nums = [2, 7, 11, 15] + target = 13 + + # ====== Driver Code ====== + # Method one + res: list[int] = two_sum_brute_force(nums, target) + print("Method one res =", res) + # Method two + res: list[int] = two_sum_hash_table(nums, target) + print("Method two res =", res) diff --git a/en/codes/python/chapter_sorting/bubble_sort.py b/en/codes/python/chapter_sorting/bubble_sort.py new file mode 100644 index 0000000000..3e2d2766c6 --- /dev/null +++ b/en/codes/python/chapter_sorting/bubble_sort.py @@ -0,0 +1,44 @@ +""" +File: bubble_sort.py +Created Time: 2022-11-25 +Author: timi (xisunyy@163.com) +""" + + +def bubble_sort(nums: list[int]): + """Bubble sort""" + n = len(nums) + # Outer loop: unsorted range is [0, i] + for i in range(n - 1, 0, -1): + # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + for j in range(i): + if nums[j] > nums[j + 1]: + # Swap nums[j] and nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] + + +def bubble_sort_with_flag(nums: list[int]): + """Bubble sort (optimized with flag)""" + n = len(nums) + # Outer loop: unsorted range is [0, i] + for i in range(n - 1, 0, -1): + flag = False # Initialize flag + # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + for j in range(i): + if nums[j] > nums[j + 1]: + # Swap nums[j] and nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] + flag = True # Record swapped elements + if not flag: + break # If no elements were swapped in this round of "bubbling", exit + + +"""Driver Code""" +if __name__ == "__main__": + nums = [4, 1, 3, 1, 5, 2] + bubble_sort(nums) + print("Bubble sort completed nums =", nums) + + nums1 = [4, 1, 3, 1, 5, 2] + bubble_sort_with_flag(nums1) + print("Bubble sort completed nums =", nums1) diff --git a/en/codes/python/chapter_sorting/bucket_sort.py b/en/codes/python/chapter_sorting/bucket_sort.py new file mode 100644 index 0000000000..6bcb71aca0 --- /dev/null +++ b/en/codes/python/chapter_sorting/bucket_sort.py @@ -0,0 +1,35 @@ +""" +File: bucket_sort.py +Created Time: 2023-03-30 +Author: krahets (krahets@163.com) +""" + + +def bucket_sort(nums: list[float]): + """Bucket sort""" + # Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + k = len(nums) // 2 + buckets = [[] for _ in range(k)] + # 1. Distribute array elements into various buckets + for num in nums: + # Input data range is [0, 1), use num * k to map to index range [0, k-1] + i = int(num * k) + # Add num to bucket i + buckets[i].append(num) + # 2. Sort each bucket + for bucket in buckets: + # Use built-in sorting function, can also replace with other sorting algorithms + bucket.sort() + # 3. Traverse buckets to merge results + i = 0 + for bucket in buckets: + for num in bucket: + nums[i] = num + i += 1 + + +if __name__ == "__main__": + # Assume input data is floating point, range [0, 1) + nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37] + bucket_sort(nums) + print("Bucket sort completed nums =", nums) diff --git a/en/codes/python/chapter_sorting/counting_sort.py b/en/codes/python/chapter_sorting/counting_sort.py new file mode 100644 index 0000000000..5302d2a79f --- /dev/null +++ b/en/codes/python/chapter_sorting/counting_sort.py @@ -0,0 +1,64 @@ +""" +File: counting_sort.py +Created Time: 2023-03-21 +Author: krahets (krahets@163.com) +""" + + +def counting_sort_naive(nums: list[int]): + """Counting sort""" + # Simple implementation, cannot be used for sorting objects + # 1. Count the maximum element m in the array + m = 0 + for num in nums: + m = max(m, num) + # 2. Count the occurrence of each digit + # counter[num] represents the occurrence of num + counter = [0] * (m + 1) + for num in nums: + counter[num] += 1 + # 3. Traverse counter, filling each element back into the original array nums + i = 0 + for num in range(m + 1): + for _ in range(counter[num]): + nums[i] = num + i += 1 + + +def counting_sort(nums: list[int]): + """Counting sort""" + # Complete implementation, can sort objects and is a stable sort + # 1. Count the maximum element m in the array + m = max(nums) + # 2. Count the occurrence of each digit + # counter[num] represents the occurrence of num + counter = [0] * (m + 1) + for num in nums: + counter[num] += 1 + # 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + # counter[num]-1 is the last index where num appears in res + for i in range(m): + counter[i + 1] += counter[i] + # 4. Traverse nums in reverse order, placing each element into the result array res + # Initialize the array res to record results + n = len(nums) + res = [0] * n + for i in range(n - 1, -1, -1): + num = nums[i] + res[counter[num] - 1] = num # Place num at the corresponding index + counter[num] -= 1 # Decrement the prefix sum by 1, getting the next index to place num + # Use result array res to overwrite the original array nums + for i in range(n): + nums[i] = res[i] + + +"""Driver Code""" +if __name__ == "__main__": + nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] + + counting_sort_naive(nums) + print(f"Counting sort (unable to sort objects) completed nums = {nums}") + + nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] + counting_sort(nums1) + print(f"Counting sort completed nums1 = {nums1}") diff --git a/en/codes/python/chapter_sorting/heap_sort.py b/en/codes/python/chapter_sorting/heap_sort.py new file mode 100644 index 0000000000..f3429785b7 --- /dev/null +++ b/en/codes/python/chapter_sorting/heap_sort.py @@ -0,0 +1,45 @@ +""" +File: heap_sort.py +Created Time: 2023-05-24 +Author: krahets (krahets@163.com) +""" + + +def sift_down(nums: list[int], n: int, i: int): + """Heap length is n, start heapifying node i, from top to bottom""" + while True: + # Determine the largest node among i, l, r, noted as ma + l = 2 * i + 1 + r = 2 * i + 2 + ma = i + if l < n and nums[l] > nums[ma]: + ma = l + if r < n and nums[r] > nums[ma]: + ma = r + # If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + if ma == i: + break + # Swap two nodes + nums[i], nums[ma] = nums[ma], nums[i] + # Loop downwards heapification + i = ma + + +def heap_sort(nums: list[int]): + """Heap sort""" + # Build heap operation: heapify all nodes except leaves + for i in range(len(nums) // 2 - 1, -1, -1): + sift_down(nums, len(nums), i) + # Extract the largest element from the heap and repeat for n-1 rounds + for i in range(len(nums) - 1, 0, -1): + # Swap the root node with the rightmost leaf node (swap the first element with the last element) + nums[0], nums[i] = nums[i], nums[0] + # Start heapifying the root node, from top to bottom + sift_down(nums, i, 0) + + +"""Driver Code""" +if __name__ == "__main__": + nums = [4, 1, 3, 1, 5, 2] + heap_sort(nums) + print("Heap sort completed nums =", nums) diff --git a/en/codes/python/chapter_sorting/insertion_sort.py b/en/codes/python/chapter_sorting/insertion_sort.py new file mode 100644 index 0000000000..6ef4ace857 --- /dev/null +++ b/en/codes/python/chapter_sorting/insertion_sort.py @@ -0,0 +1,25 @@ +""" +File: insertion_sort.py +Created Time: 2022-11-25 +Author: timi (xisunyy@163.com) +""" + + +def insertion_sort(nums: list[int]): + """Insertion sort""" + # Outer loop: sorted range is [0, i-1] + for i in range(1, len(nums)): + base = nums[i] + j = i - 1 + # Inner loop: insert base into the correct position within the sorted range [0, i-1] + while j >= 0 and nums[j] > base: + nums[j + 1] = nums[j] # Move nums[j] to the right by one position + j -= 1 + nums[j + 1] = base # Assign base to the correct position + + +"""Driver Code""" +if __name__ == "__main__": + nums = [4, 1, 3, 1, 5, 2] + insertion_sort(nums) + print("Insertion sort completed nums =", nums) diff --git a/en/codes/python/chapter_sorting/merge_sort.py b/en/codes/python/chapter_sorting/merge_sort.py new file mode 100644 index 0000000000..18a1d9ddc9 --- /dev/null +++ b/en/codes/python/chapter_sorting/merge_sort.py @@ -0,0 +1,55 @@ +""" +File: merge_sort.py +Created Time: 2022-11-25 +Author: timi (xisunyy@163.com), krahets (krahets@163.com) +""" + + +def merge(nums: list[int], left: int, mid: int, right: int): + """Merge left subarray and right subarray""" + # Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + # Create a temporary array tmp to store the merged results + tmp = [0] * (right - left + 1) + # Initialize the start indices of the left and right subarrays + i, j, k = left, mid + 1, 0 + # While both subarrays still have elements, compare and copy the smaller element into the temporary array + while i <= mid and j <= right: + if nums[i] <= nums[j]: + tmp[k] = nums[i] + i += 1 + else: + tmp[k] = nums[j] + j += 1 + k += 1 + # Copy the remaining elements of the left and right subarrays into the temporary array + while i <= mid: + tmp[k] = nums[i] + i += 1 + k += 1 + while j <= right: + tmp[k] = nums[j] + j += 1 + k += 1 + # Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for k in range(0, len(tmp)): + nums[left + k] = tmp[k] + + +def merge_sort(nums: list[int], left: int, right: int): + """Merge sort""" + # Termination condition + if left >= right: + return # Terminate recursion when subarray length is 1 + # Partition stage + mid = (left + right) // 2 # Calculate midpoint + merge_sort(nums, left, mid) # Recursively process the left subarray + merge_sort(nums, mid + 1, right) # Recursively process the right subarray + # Merge stage + merge(nums, left, mid, right) + + +"""Driver Code""" +if __name__ == "__main__": + nums = [7, 3, 2, 6, 0, 1, 5, 4] + merge_sort(nums, 0, len(nums) - 1) + print("Merge sort completed nums =", nums) diff --git a/en/codes/python/chapter_sorting/quick_sort.py b/en/codes/python/chapter_sorting/quick_sort.py new file mode 100644 index 0000000000..652fb3cba8 --- /dev/null +++ b/en/codes/python/chapter_sorting/quick_sort.py @@ -0,0 +1,129 @@ +""" +File: quick_sort.py +Created Time: 2022-11-25 +Author: timi (xisunyy@163.com) +""" + + +class QuickSort: + """Quick sort class""" + + def partition(self, nums: list[int], left: int, right: int) -> int: + """Partition""" + # Use nums[left] as the pivot + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # Search from right to left for the first element smaller than the pivot + while i < j and nums[i] <= nums[left]: + i += 1 # Search from left to right for the first element greater than the pivot + # Swap elements + nums[i], nums[j] = nums[j], nums[i] + # Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + return i # Return the index of the pivot + + def quick_sort(self, nums: list[int], left: int, right: int): + """Quick sort""" + # Terminate recursion when subarray length is 1 + if left >= right: + return + # Partition + pivot = self.partition(nums, left, right) + # Recursively process the left subarray and right subarray + self.quick_sort(nums, left, pivot - 1) + self.quick_sort(nums, pivot + 1, right) + + +class QuickSortMedian: + """Quick sort class (median pivot optimization)""" + + def median_three(self, nums: list[int], left: int, mid: int, right: int) -> int: + """Select the median of three candidate elements""" + l, m, r = nums[left], nums[mid], nums[right] + if (l <= m <= r) or (r <= m <= l): + return mid # m is between l and r + if (m <= l <= r) or (r <= l <= m): + return left # l is between m and r + return right + + def partition(self, nums: list[int], left: int, right: int) -> int: + """Partition (median of three)""" + # Use nums[left] as the pivot + med = self.median_three(nums, left, (left + right) // 2, right) + # Swap the median to the array's leftmost position + nums[left], nums[med] = nums[med], nums[left] + # Use nums[left] as the pivot + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # Search from right to left for the first element smaller than the pivot + while i < j and nums[i] <= nums[left]: + i += 1 # Search from left to right for the first element greater than the pivot + # Swap elements + nums[i], nums[j] = nums[j], nums[i] + # Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + return i # Return the index of the pivot + + def quick_sort(self, nums: list[int], left: int, right: int): + """Quick sort""" + # Terminate recursion when subarray length is 1 + if left >= right: + return + # Partition + pivot = self.partition(nums, left, right) + # Recursively process the left subarray and right subarray + self.quick_sort(nums, left, pivot - 1) + self.quick_sort(nums, pivot + 1, right) + + +class QuickSortTailCall: + """Quick sort class (tail recursion optimization)""" + + def partition(self, nums: list[int], left: int, right: int) -> int: + """Partition""" + # Use nums[left] as the pivot + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # Search from right to left for the first element smaller than the pivot + while i < j and nums[i] <= nums[left]: + i += 1 # Search from left to right for the first element greater than the pivot + # Swap elements + nums[i], nums[j] = nums[j], nums[i] + # Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + return i # Return the index of the pivot + + def quick_sort(self, nums: list[int], left: int, right: int): + """Quick sort (tail recursion optimization)""" + # Terminate when subarray length is 1 + while left < right: + # Partition operation + pivot = self.partition(nums, left, right) + # Perform quick sort on the shorter of the two subarrays + if pivot - left < right - pivot: + self.quick_sort(nums, left, pivot - 1) # Recursively sort the left subarray + left = pivot + 1 # Remaining unsorted interval is [pivot + 1, right] + else: + self.quick_sort(nums, pivot + 1, right) # Recursively sort the right subarray + right = pivot - 1 # Remaining unsorted interval is [left, pivot - 1] + + +"""Driver Code""" +if __name__ == "__main__": + # Quick sort + nums = [2, 4, 1, 0, 3, 5] + QuickSort().quick_sort(nums, 0, len(nums) - 1) + print("Quick sort completed nums =", nums) + + # Quick sort (median pivot optimization) + nums1 = [2, 4, 1, 0, 3, 5] + QuickSortMedian().quick_sort(nums1, 0, len(nums1) - 1) + print("Quick sort (median pivot optimization) completed nums =", nums1) + + # Quick sort (tail recursion optimization) + nums2 = [2, 4, 1, 0, 3, 5] + QuickSortTailCall().quick_sort(nums2, 0, len(nums2) - 1) + print("Quick sort (tail recursion optimization) completed nums =", nums2) diff --git a/en/codes/python/chapter_sorting/radix_sort.py b/en/codes/python/chapter_sorting/radix_sort.py new file mode 100644 index 0000000000..ce80caf97a --- /dev/null +++ b/en/codes/python/chapter_sorting/radix_sort.py @@ -0,0 +1,69 @@ +""" +File: radix_sort.py +Created Time: 2023-03-26 +Author: krahets (krahets@163.com) +""" + + +def digit(num: int, exp: int) -> int: + """Get the k-th digit of element num, where exp = 10^(k-1)""" + # Passing exp instead of k can avoid repeated expensive exponentiation here + return (num // exp) % 10 + + +def counting_sort_digit(nums: list[int], exp: int): + """Counting sort (based on nums k-th digit)""" + # Decimal digit range is 0~9, therefore need a bucket array of length 10 + counter = [0] * 10 + n = len(nums) + # Count the occurrence of digits 0~9 + for i in range(n): + d = digit(nums[i], exp) # Get the k-th digit of nums[i], noted as d + counter[d] += 1 # Count the occurrence of digit d + # Calculate prefix sum, converting "occurrence count" into "array index" + for i in range(1, 10): + counter[i] += counter[i - 1] + # Traverse in reverse, based on bucket statistics, place each element into res + res = [0] * n + for i in range(n - 1, -1, -1): + d = digit(nums[i], exp) + j = counter[d] - 1 # Get the index j for d in the array + res[j] = nums[i] # Place the current element at index j + counter[d] -= 1 # Decrease the count of d by 1 + # Use result to overwrite the original array nums + for i in range(n): + nums[i] = res[i] + + +def radix_sort(nums: list[int]): + """Radix sort""" + # Get the maximum element of the array, used to determine the maximum number of digits + m = max(nums) + # Traverse from the lowest to the highest digit + exp = 1 + while exp <= m: + # 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) + counting_sort_digit(nums, exp) + exp *= 10 + + +"""Driver Code""" +if __name__ == "__main__": + # Radix sort + nums = [ + 10546151, + 35663510, + 42865989, + 34862445, + 81883077, + 88906420, + 72429244, + 30524779, + 82060337, + 63832996, + ] + radix_sort(nums) + print("Radix sort completed nums =", nums) diff --git a/en/codes/python/chapter_sorting/selection_sort.py b/en/codes/python/chapter_sorting/selection_sort.py new file mode 100644 index 0000000000..e4fffa3cad --- /dev/null +++ b/en/codes/python/chapter_sorting/selection_sort.py @@ -0,0 +1,26 @@ +""" +File: selection_sort.py +Created Time: 2023-05-22 +Author: krahets (krahets@163.com) +""" + + +def selection_sort(nums: list[int]): + """Selection sort""" + n = len(nums) + # Outer loop: unsorted range is [i, n-1] + for i in range(n - 1): + # Inner loop: find the smallest element within the unsorted range + k = i + for j in range(i + 1, n): + if nums[j] < nums[k]: + k = j # Record the index of the smallest element + # Swap the smallest element with the first element of the unsorted range + nums[i], nums[k] = nums[k], nums[i] + + +"""Driver Code""" +if __name__ == "__main__": + nums = [4, 1, 3, 1, 5, 2] + selection_sort(nums) + print("Selection sort completed nums =", nums) diff --git a/en/codes/python/chapter_stack_and_queue/array_deque.py b/en/codes/python/chapter_stack_and_queue/array_deque.py new file mode 100644 index 0000000000..270a56e6d4 --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/array_deque.py @@ -0,0 +1,129 @@ +""" +File: array_deque.py +Created Time: 2023-03-01 +Author: krahets (krahets@163.com) +""" + + +class ArrayDeque: + """Double-ended queue class based on circular array""" + + def __init__(self, capacity: int): + """Constructor""" + self._nums: list[int] = [0] * capacity + self._front: int = 0 + self._size: int = 0 + + def capacity(self) -> int: + """Get the capacity of the double-ended queue""" + return len(self._nums) + + def size(self) -> int: + """Get the length of the double-ended queue""" + return self._size + + def is_empty(self) -> bool: + """Determine if the double-ended queue is empty""" + return self._size == 0 + + def index(self, i: int) -> int: + """Calculate circular array index""" + # 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 + self.capacity()) % self.capacity() + + def push_first(self, num: int): + """Front enqueue""" + if self._size == self.capacity(): + print("Double-ended queue is full") + 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 + self._front = self.index(self._front - 1) + # Add num to the front + self._nums[self._front] = num + self._size += 1 + + def push_last(self, num: int): + """Rear enqueue""" + if self._size == self.capacity(): + print("Double-ended queue is full") + return + # Calculate rear pointer, pointing to rear index + 1 + rear = self.index(self._front + self._size) + # Add num to the rear + self._nums[rear] = num + self._size += 1 + + def pop_first(self) -> int: + """Front dequeue""" + num = self.peek_first() + # Move front pointer one position backward + self._front = self.index(self._front + 1) + self._size -= 1 + return num + + def pop_last(self) -> int: + """Rear dequeue""" + num = self.peek_last() + self._size -= 1 + return num + + def peek_first(self) -> int: + """Access front element""" + if self.is_empty(): + raise IndexError("Double-ended queue is empty") + return self._nums[self._front] + + def peek_last(self) -> int: + """Access rear element""" + if self.is_empty(): + raise IndexError("Double-ended queue is empty") + # Calculate rear element index + last = self.index(self._front + self._size - 1) + return self._nums[last] + + def to_array(self) -> list[int]: + """Return array for printing""" + # Only convert elements within valid length range + res = [] + for i in range(self._size): + res.append(self._nums[self.index(self._front + i)]) + return res + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize double-ended queue + deque = ArrayDeque(10) + deque.push_last(3) + deque.push_last(2) + deque.push_last(5) + print("Double-ended queue deque =", deque.to_array()) + + # Access element + peek_first: int = deque.peek_first() + print("Front element peek_first =", peek_first) + peek_last: int = deque.peek_last() + print("Rear element peek_last =", peek_last) + + # Element enqueue + deque.push_last(4) + print("Element 4 rear enqueued, deque =", deque.to_array()) + deque.push_first(1) + print("Element 1 front enqueued, deque =", deque.to_array()) + + # Element dequeue + pop_last: int = deque.pop_last() + print("Rear dequeued element =", pop_last, ", deque after rear dequeue =", deque.to_array()) + pop_first: int = deque.pop_first() + print("Front dequeued element =", pop_first, ", deque after front dequeue =", deque.to_array()) + + # Get the length of the double-ended queue + size: int = deque.size() + print("Double-ended queue length size =", size) + + # Determine if the double-ended queue is empty + is_empty: bool = deque.is_empty() + print("Is the double-ended queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/array_queue.py b/en/codes/python/chapter_stack_and_queue/array_queue.py new file mode 100644 index 0000000000..25f6356aaf --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/array_queue.py @@ -0,0 +1,98 @@ +""" +File: array_queue.py +Created Time: 2022-12-01 +Author: Peng Chen (pengchzn@gmail.com) +""" + + +class ArrayQueue: + """Queue class based on circular array""" + + def __init__(self, size: int): + """Constructor""" + self._nums: list[int] = [0] * size # Array for storing queue elements + self._front: int = 0 # Front pointer, pointing to the front element + self._size: int = 0 # Queue length + + def capacity(self) -> int: + """Get the capacity of the queue""" + return len(self._nums) + + def size(self) -> int: + """Get the length of the queue""" + return self._size + + def is_empty(self) -> bool: + """Determine if the queue is empty""" + return self._size == 0 + + def push(self, num: int): + """Enqueue""" + if self._size == self.capacity(): + raise IndexError("Queue is full") + # Calculate rear pointer, pointing to rear index + 1 + # Use modulo operation to wrap the rear pointer from the end of the array back to the start + rear: int = (self._front + self._size) % self.capacity() + # Add num to the rear + self._nums[rear] = num + self._size += 1 + + def pop(self) -> int: + """Dequeue""" + num: int = self.peek() + # Move front pointer one position backward, returning to the head of the array if it exceeds the tail + self._front = (self._front + 1) % self.capacity() + self._size -= 1 + return num + + def peek(self) -> int: + """Access front element""" + if self.is_empty(): + raise IndexError("Queue is empty") + return self._nums[self._front] + + def to_list(self) -> list[int]: + """Return array for printing""" + res = [0] * self.size() + j: int = self._front + for i in range(self.size()): + res[i] = self._nums[(j % self.capacity())] + j += 1 + return res + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize queue + queue = ArrayQueue(10) + + # Element enqueue + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + print("Queue queue =", queue.to_list()) + + # Access front element + peek: int = queue.peek() + print("Front element peek =", peek) + + # Element dequeue + pop: int = queue.pop() + print("Dequeued element pop =", pop) + print("Queue after dequeue =", queue.to_list()) + + # Get the length of the queue + size: int = queue.size() + print("Queue length size =", size) + + # Determine if the queue is empty + is_empty: bool = queue.is_empty() + print("Is the queue empty =", is_empty) + + # Test circular array + for i in range(10): + queue.push(i) + queue.pop() + print("In the ", i, "th round of enqueue + dequeue, queue = ", queue.to_list()) diff --git a/en/codes/python/chapter_stack_and_queue/array_stack.py b/en/codes/python/chapter_stack_and_queue/array_stack.py new file mode 100644 index 0000000000..2775c2602b --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/array_stack.py @@ -0,0 +1,72 @@ +""" +File: array_stack.py +Created Time: 2022-11-29 +Author: Peng Chen (pengchzn@gmail.com) +""" + + +class ArrayStack: + """Stack class based on array""" + + def __init__(self): + """Constructor""" + self._stack: list[int] = [] + + def size(self) -> int: + """Get the length of the stack""" + return len(self._stack) + + def is_empty(self) -> bool: + """Determine if the stack is empty""" + return self.size() == 0 + + def push(self, item: int): + """Push""" + self._stack.append(item) + + def pop(self) -> int: + """Pop""" + if self.is_empty(): + raise IndexError("Stack is empty") + return self._stack.pop() + + def peek(self) -> int: + """Access stack top element""" + if self.is_empty(): + raise IndexError("Stack is empty") + return self._stack[-1] + + def to_list(self) -> list[int]: + """Return array for printing""" + return self._stack + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize stack + stack = ArrayStack() + + # Element push + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + print("Stack stack =", stack.to_list()) + + # Access stack top element + peek: int = stack.peek() + print("Stack top element peek =", peek) + + # Element pop + pop: int = stack.pop() + print("Popped element pop =", pop) + print("Stack after pop =", stack.to_list()) + + # Get the length of the stack + size: int = stack.size() + print("Stack length size =", size) + + # Determine if it's empty + is_empty: bool = stack.is_empty() + print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/deque.py b/en/codes/python/chapter_stack_and_queue/deque.py new file mode 100644 index 0000000000..08406a96bb --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/deque.py @@ -0,0 +1,42 @@ +""" +File: deque.py +Created Time: 2022-11-29 +Author: Peng Chen (pengchzn@gmail.com) +""" + +from collections import deque + +"""Driver Code""" +if __name__ == "__main__": + # Initialize double-ended queue + deq: deque[int] = deque() + + # Element enqueue + deq.append(2) # Add to rear + deq.append(5) + deq.append(4) + deq.appendleft(3) # Add to front + deq.appendleft(1) + print("Double-ended queue deque =", deq) + + # Access element + front: int = deq[0] # Front element + print("Front element front =", front) + rear: int = deq[-1] # Rear element + print("Rear element rear =", rear) + + # Element dequeue + pop_front: int = deq.popleft() # Front element dequeue + print("Front dequeued element pop_front =", pop_front) + print("Deque after front dequeue =", deq) + pop_rear: int = deq.pop() # Rear element dequeue + print("Rear dequeued element pop_rear =", pop_rear) + print("Deque after rear dequeue =", deq) + + # Get the length of the double-ended queue + size: int = len(deq) + print("Double-ended queue length size =", size) + + # Determine if the double-ended queue is empty + is_empty: bool = len(deq) == 0 + print("Is the double-ended queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py b/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py new file mode 100644 index 0000000000..20860f7c5b --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py @@ -0,0 +1,151 @@ +""" +File: linkedlist_deque.py +Created Time: 2023-03-01 +Author: krahets (krahets@163.com) +""" + + +class ListNode: + """Double-linked list node""" + + def __init__(self, val: int): + """Constructor""" + self.val: int = val + self.next: ListNode | None = None # Reference to the next node + self.prev: ListNode | None = None # Reference to predecessor node + + +class LinkedListDeque: + """Double-ended queue class based on double-linked list""" + + def __init__(self): + """Constructor""" + self._front: ListNode | None = None # Head node front + self._rear: ListNode | None = None # Tail node rear + self._size: int = 0 # Length of the double-ended queue + + def size(self) -> int: + """Get the length of the double-ended queue""" + return self._size + + def is_empty(self) -> bool: + """Determine if the double-ended queue is empty""" + return self._size == 0 + + def push(self, num: int, is_front: bool): + """Enqueue operation""" + node = ListNode(num) + # If the list is empty, make front and rear both point to node + if self.is_empty(): + self._front = self._rear = node + # Front enqueue operation + elif is_front: + # Add node to the head of the list + self._front.prev = node + node.next = self._front + self._front = node # Update head node + # Rear enqueue operation + else: + # Add node to the tail of the list + self._rear.next = node + node.prev = self._rear + self._rear = node # Update tail node + self._size += 1 # Update queue length + + def push_first(self, num: int): + """Front enqueue""" + self.push(num, True) + + def push_last(self, num: int): + """Rear enqueue""" + self.push(num, False) + + def pop(self, is_front: bool) -> int: + """Dequeue operation""" + if self.is_empty(): + raise IndexError("Double-ended queue is empty") + # Front dequeue operation + if is_front: + val: int = self._front.val # Temporarily store the head node value + # Remove head node + fnext: ListNode | None = self._front.next + if fnext != None: + fnext.prev = None + self._front.next = None + self._front = fnext # Update head node + # Rear dequeue operation + else: + val: int = self._rear.val # Temporarily store the tail node value + # Remove tail node + rprev: ListNode | None = self._rear.prev + if rprev != None: + rprev.next = None + self._rear.prev = None + self._rear = rprev # Update tail node + self._size -= 1 # Update queue length + return val + + def pop_first(self) -> int: + """Front dequeue""" + return self.pop(True) + + def pop_last(self) -> int: + """Rear dequeue""" + return self.pop(False) + + def peek_first(self) -> int: + """Access front element""" + if self.is_empty(): + raise IndexError("Double-ended queue is empty") + return self._front.val + + def peek_last(self) -> int: + """Access rear element""" + if self.is_empty(): + raise IndexError("Double-ended queue is empty") + return self._rear.val + + def to_array(self) -> list[int]: + """Return array for printing""" + node = self._front + res = [0] * self.size() + for i in range(self.size()): + res[i] = node.val + node = node.next + return res + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize double-ended queue + deque = LinkedListDeque() + deque.push_last(3) + deque.push_last(2) + deque.push_last(5) + print("Double-ended queue deque =", deque.to_array()) + + # Access element + peek_first: int = deque.peek_first() + print("Front element peek_first =", peek_first) + peek_last: int = deque.peek_last() + print("Rear element peek_last =", peek_last) + + # Element enqueue + deque.push_last(4) + print("Element 4 rear enqueued, deque =", deque.to_array()) + deque.push_first(1) + print("Element 1 front enqueued, deque =", deque.to_array()) + + # Element dequeue + pop_last: int = deque.pop_last() + print("Rear dequeued element =", pop_last, ", deque after rear dequeue =", deque.to_array()) + pop_first: int = deque.pop_first() + print("Front dequeued element =", pop_first, ", deque after front dequeue =", deque.to_array()) + + # Get the length of the double-ended queue + size: int = deque.size() + print("Double-ended queue length size =", size) + + # Determine if the double-ended queue is empty + is_empty: bool = deque.is_empty() + print("Is the double-ended queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py b/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py new file mode 100644 index 0000000000..1acee511e5 --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py @@ -0,0 +1,97 @@ +""" +File: linkedlist_queue.py +Created Time: 2022-12-01 +Author: Peng Chen (pengchzn@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import ListNode + + +class LinkedListQueue: + """Queue class based on linked list""" + + def __init__(self): + """Constructor""" + self._front: ListNode | None = None # Head node front + self._rear: ListNode | None = None # Tail node rear + self._size: int = 0 + + def size(self) -> int: + """Get the length of the queue""" + return self._size + + def is_empty(self) -> bool: + """Determine if the queue is empty""" + return self._size == 0 + + def push(self, num: int): + """Enqueue""" + # Add num behind the tail node + node = ListNode(num) + # If the queue is empty, make the head and tail nodes both point to that node + if self._front is None: + self._front = node + self._rear = node + # If the queue is not empty, add that node behind the tail node + else: + self._rear.next = node + self._rear = node + self._size += 1 + + def pop(self) -> int: + """Dequeue""" + num = self.peek() + # Remove head node + self._front = self._front.next + self._size -= 1 + return num + + def peek(self) -> int: + """Access front element""" + if self.is_empty(): + raise IndexError("Queue is empty") + return self._front.val + + def to_list(self) -> list[int]: + """Convert to a list for printing""" + queue = [] + temp = self._front + while temp: + queue.append(temp.val) + temp = temp.next + return queue + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize queue + queue = LinkedListQueue() + + # Element enqueue + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + print("Queue queue =", queue.to_list()) + + # Access front element + peek: int = queue.peek() + print("Front element front =", peek) + + # Element dequeue + pop_front: int = queue.pop() + print("Dequeued element pop =", pop_front) + print("Queue after dequeue =", queue.to_list()) + + # Get the length of the queue + size: int = queue.size() + print("Queue length size =", size) + + # Determine if the queue is empty + is_empty: bool = queue.is_empty() + print("Is the queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py b/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py new file mode 100644 index 0000000000..7efd39c7f2 --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py @@ -0,0 +1,89 @@ +""" +File: linkedlist_stack.py +Created Time: 2022-11-29 +Author: Peng Chen (pengchzn@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import ListNode + + +class LinkedListStack: + """Stack class based on linked list""" + + def __init__(self): + """Constructor""" + self._peek: ListNode | None = None + self._size: int = 0 + + def size(self) -> int: + """Get the length of the stack""" + return self._size + + def is_empty(self) -> bool: + """Determine if the stack is empty""" + return self._size == 0 + + def push(self, val: int): + """Push""" + node = ListNode(val) + node.next = self._peek + self._peek = node + self._size += 1 + + def pop(self) -> int: + """Pop""" + num = self.peek() + self._peek = self._peek.next + self._size -= 1 + return num + + def peek(self) -> int: + """Access stack top element""" + if self.is_empty(): + raise IndexError("Stack is empty") + return self._peek.val + + def to_list(self) -> list[int]: + """Convert to a list for printing""" + arr = [] + node = self._peek + while node: + arr.append(node.val) + node = node.next + arr.reverse() + return arr + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize stack + stack = LinkedListStack() + + # Element push + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + print("Stack stack =", stack.to_list()) + + # Access stack top element + peek: int = stack.peek() + print("Stack top element peek =", peek) + + # Element pop + pop: int = stack.pop() + print("Popped element pop =", pop) + print("Stack after pop =", stack.to_list()) + + # Get the length of the stack + size: int = stack.size() + print("Stack length size =", size) + + # Determine if it's empty + is_empty: bool = stack.is_empty() + print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/queue.py b/en/codes/python/chapter_stack_and_queue/queue.py new file mode 100644 index 0000000000..495aae1279 --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/queue.py @@ -0,0 +1,39 @@ +""" +File: queue.py +Created Time: 2022-11-29 +Author: Peng Chen (pengchzn@gmail.com) +""" + +from collections import deque + +"""Driver Code""" +if __name__ == "__main__": + # Initialize queue + # In Python, we generally consider the deque class as a queue + # Although queue.Queue() is a pure queue class, it's not very user-friendly + que: deque[int] = deque() + + # Element enqueue + que.append(1) + que.append(3) + que.append(2) + que.append(5) + que.append(4) + print("Queue que =", que) + + # Access front element + front: int = que[0] + print("Front element front =", front) + + # Element dequeue + pop: int = que.popleft() + print("Dequeued element pop =", pop) + print("Queue after dequeue =", que) + + # Get the length of the queue + size: int = len(que) + print("Queue length size =", size) + + # Determine if the queue is empty + is_empty: bool = len(que) == 0 + print("Is the queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/stack.py b/en/codes/python/chapter_stack_and_queue/stack.py new file mode 100644 index 0000000000..1a3447eed9 --- /dev/null +++ b/en/codes/python/chapter_stack_and_queue/stack.py @@ -0,0 +1,36 @@ +""" +File: stack.py +Created Time: 2022-11-29 +Author: Peng Chen (pengchzn@gmail.com) +""" + +"""Driver Code""" +if __name__ == "__main__": + # Initialize stack + # Python does not have a built-in stack class, but you can use a list as a stack + stack: list[int] = [] + + # Element push + stack.append(1) + stack.append(3) + stack.append(2) + stack.append(5) + stack.append(4) + print("Stack stack =", stack) + + # Access stack top element + peek: int = stack[-1] + print("Stack top element peek =", peek) + + # Element pop + pop: int = stack.pop() + print("Popped element pop =", pop) + print("Stack after pop =", stack) + + # Get the length of the stack + size: int = len(stack) + print("Stack length size =", size) + + # Determine if it's empty + is_empty: bool = len(stack) == 0 + print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_tree/array_binary_tree.py b/en/codes/python/chapter_tree/array_binary_tree.py new file mode 100644 index 0000000000..47c35d2e1e --- /dev/null +++ b/en/codes/python/chapter_tree/array_binary_tree.py @@ -0,0 +1,119 @@ +""" +File: array_binary_tree.py +Created Time: 2023-07-19 +Author: krahets (krahets@163.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, list_to_tree, print_tree + + +class ArrayBinaryTree: + """Array-based binary tree class""" + + def __init__(self, arr: list[int | None]): + """Constructor""" + self._tree = list(arr) + + def size(self): + """List capacity""" + return len(self._tree) + + def val(self, i: int) -> int | None: + """Get the value of the node at index i""" + # If the index is out of bounds, return None, representing a vacancy + if i < 0 or i >= self.size(): + return None + return self._tree[i] + + def left(self, i: int) -> int | None: + """Get the index of the left child of the node at index i""" + return 2 * i + 1 + + def right(self, i: int) -> int | None: + """Get the index of the right child of the node at index i""" + return 2 * i + 2 + + def parent(self, i: int) -> int | None: + """Get the index of the parent of the node at index i""" + return (i - 1) // 2 + + def level_order(self) -> list[int]: + """Level-order traversal""" + self.res = [] + # Traverse array + for i in range(self.size()): + if self.val(i) is not None: + self.res.append(self.val(i)) + return self.res + + def dfs(self, i: int, order: str): + """Depth-first traversal""" + if self.val(i) is None: + return + # Pre-order traversal + if order == "pre": + self.res.append(self.val(i)) + self.dfs(self.left(i), order) + # In-order traversal + if order == "in": + self.res.append(self.val(i)) + self.dfs(self.right(i), order) + # Post-order traversal + if order == "post": + self.res.append(self.val(i)) + + def pre_order(self) -> list[int]: + """Pre-order traversal""" + self.res = [] + self.dfs(0, order="pre") + return self.res + + def in_order(self) -> list[int]: + """In-order traversal""" + self.res = [] + self.dfs(0, order="in") + return self.res + + def post_order(self) -> list[int]: + """Post-order traversal""" + self.res = [] + self.dfs(0, order="post") + return self.res + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize binary tree + # Use a specific function to convert an array into a binary tree + arr = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] + root = list_to_tree(arr) + print("\nInitialize binary tree\n") + print("Array representation of the binary tree:") + print(arr) + print("Linked list representation of the binary tree:") + print_tree(root) + + # Array-based binary tree class + abt = ArrayBinaryTree(arr) + + # Access node + i = 1 + l, r, p = abt.left(i), abt.right(i), abt.parent(i) + print(f"\nCurrent node index is {i}, value is {abt.val(i)}") + print(f"Its left child node index is {l}, value is {abt.val(l)}") + print(f"Its right child node index is {r}, value is {abt.val(r)}") + print(f"Its parent node index is {p}, value is {abt.val(p)}") + + # Traverse tree + res = abt.level_order() + print("\nLevel-order traversal is:", res) + res = abt.pre_order() + print("Pre-order traversal is:", res) + res = abt.in_order() + print("In-order traversal is:", res) + res = abt.post_order() + print("Post-order traversal is:", res) diff --git a/en/codes/python/chapter_tree/avl_tree.py b/en/codes/python/chapter_tree/avl_tree.py new file mode 100644 index 0000000000..399515e864 --- /dev/null +++ b/en/codes/python/chapter_tree/avl_tree.py @@ -0,0 +1,200 @@ +""" +File: avl_tree.py +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree + + +class AVLTree: + """AVL tree""" + + def __init__(self): + """Constructor""" + self._root = None + + def get_root(self) -> TreeNode | None: + """Get binary tree root node""" + return self._root + + def height(self, node: TreeNode | None) -> int: + """Get node height""" + # Empty node height is -1, leaf node height is 0 + if node is not None: + return node.height + return -1 + + def update_height(self, node: TreeNode | None): + """Update node height""" + # Node height equals the height of the tallest subtree + 1 + node.height = max([self.height(node.left), self.height(node.right)]) + 1 + + def balance_factor(self, node: TreeNode | None) -> int: + """Get balance factor""" + # Empty node balance factor is 0 + if node is None: + return 0 + # Node balance factor = left subtree height - right subtree height + return self.height(node.left) - self.height(node.right) + + def right_rotate(self, node: TreeNode | None) -> TreeNode | None: + """Right rotation operation""" + child = node.left + grand_child = child.right + # Rotate node to the right around child + child.right = node + node.left = grand_child + # Update node height + self.update_height(node) + self.update_height(child) + # Return the root of the subtree after rotation + return child + + def left_rotate(self, node: TreeNode | None) -> TreeNode | None: + """Left rotation operation""" + child = node.right + grand_child = child.left + # Rotate node to the left around child + child.left = node + node.right = grand_child + # Update node height + self.update_height(node) + self.update_height(child) + # Return the root of the subtree after rotation + return child + + def rotate(self, node: TreeNode | None) -> TreeNode | None: + """Perform rotation operation to restore balance to the subtree""" + # Get the balance factor of node + balance_factor = self.balance_factor(node) + # Left-leaning tree + if balance_factor > 1: + if self.balance_factor(node.left) >= 0: + # Right rotation + return self.right_rotate(node) + else: + # First left rotation then right rotation + node.left = self.left_rotate(node.left) + return self.right_rotate(node) + # Right-leaning tree + elif balance_factor < -1: + if self.balance_factor(node.right) <= 0: + # Left rotation + return self.left_rotate(node) + else: + # First right rotation then left rotation + node.right = self.right_rotate(node.right) + return self.left_rotate(node) + # Balanced tree, no rotation needed, return + return node + + def insert(self, val): + """Insert node""" + self._root = self.insert_helper(self._root, val) + + def insert_helper(self, node: TreeNode | None, val: int) -> TreeNode: + """Recursively insert node (helper method)""" + if node is None: + return TreeNode(val) + # 1. Find insertion position and insert node + if val < node.val: + node.left = self.insert_helper(node.left, val) + elif val > node.val: + node.right = self.insert_helper(node.right, val) + else: + # Do not insert duplicate nodes, return + return node + # Update node height + self.update_height(node) + # 2. Perform rotation operation to restore balance to the subtree + return self.rotate(node) + + def remove(self, val: int): + """Remove node""" + self._root = self.remove_helper(self._root, val) + + def remove_helper(self, node: TreeNode | None, val: int) -> TreeNode | None: + """Recursively remove node (helper method)""" + if node is None: + return None + # 1. Find and remove the node + if val < node.val: + node.left = self.remove_helper(node.left, val) + elif val > node.val: + node.right = self.remove_helper(node.right, val) + else: + if node.left is None or node.right is None: + child = node.left or node.right + # Number of child nodes = 0, remove node and return + if child is None: + return None + # Number of child nodes = 1, remove node + else: + node = child + else: + # Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it + temp = node.right + while temp.left is not None: + temp = temp.left + node.right = self.remove_helper(node.right, temp.val) + node.val = temp.val + # Update node height + self.update_height(node) + # 2. Perform rotation operation to restore balance to the subtree + return self.rotate(node) + + def search(self, val: int) -> TreeNode | None: + """Search node""" + cur = self._root + # Loop find, break after passing leaf nodes + while cur is not None: + # Target node is in cur's right subtree + if cur.val < val: + cur = cur.right + # Target node is in cur's left subtree + elif cur.val > val: + cur = cur.left + # Found target node, break loop + else: + break + # Return target node + return cur + + +"""Driver Code""" +if __name__ == "__main__": + + def test_insert(tree: AVLTree, val: int): + tree.insert(val) + print("\nInsert node {} after, AVL tree is".format(val)) + print_tree(tree.get_root()) + + def test_remove(tree: AVLTree, val: int): + tree.remove(val) + print("\nRemove node {} after, AVL tree is".format(val)) + print_tree(tree.get_root()) + + # Initialize empty AVL tree + avl_tree = AVLTree() + + # Insert node + # Notice how the AVL tree maintains balance after inserting nodes + for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6]: + test_insert(avl_tree, val) + + # Insert duplicate node + test_insert(avl_tree, 7) + + # Remove node + # Notice how the AVL tree maintains balance after removing nodes + test_remove(avl_tree, 8) # Remove node with degree 0 + test_remove(avl_tree, 5) # Remove node with degree 1 + test_remove(avl_tree, 4) # Remove node with degree 2 + + result_node = avl_tree.search(7) + print("\nFound node object is {}, node value = {}".format(result_node, result_node.val)) diff --git a/en/codes/python/chapter_tree/binary_search_tree.py b/en/codes/python/chapter_tree/binary_search_tree.py new file mode 100644 index 0000000000..e4a6d1f3e7 --- /dev/null +++ b/en/codes/python/chapter_tree/binary_search_tree.py @@ -0,0 +1,146 @@ +""" +File: binary_search_tree.py +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree + + +class BinarySearchTree: + """Binary search tree""" + + def __init__(self): + """Constructor""" + # Initialize empty tree + self._root = None + + def get_root(self) -> TreeNode | None: + """Get binary tree root node""" + return self._root + + def search(self, num: int) -> TreeNode | None: + """Search node""" + cur = self._root + # Loop find, break after passing leaf nodes + while cur is not None: + # Target node is in cur's right subtree + if cur.val < num: + cur = cur.right + # Target node is in cur's left subtree + elif cur.val > num: + cur = cur.left + # Found target node, break loop + else: + break + return cur + + def insert(self, num: int): + """Insert node""" + # If tree is empty, initialize root node + if self._root is None: + self._root = TreeNode(num) + return + # Loop find, break after passing leaf nodes + cur, pre = self._root, None + while cur is not None: + # 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 + node = TreeNode(num) + if pre.val < num: + pre.right = node + else: + pre.left = node + + def remove(self, num: int): + """Remove node""" + # If tree is empty, return + if self._root is None: + return + # Loop find, break after passing leaf nodes + cur, pre = self._root, None + while cur is not None: + # 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 is None: + return + + # Number of child nodes = 0 or 1 + if cur.left is None or cur.right is None: + # When the number of child nodes = 0/1, child = null/that child node + child = cur.left or cur.right + # Remove node cur + if cur != self._root: + if pre.left == cur: + pre.left = child + else: + pre.right = child + else: + # If the removed node is the root, reassign the root + self._root = child + # Number of child nodes = 2 + else: + # Get the next node in in-order traversal of cur + tmp: TreeNode = cur.right + while tmp.left is not None: + tmp = tmp.left + # Recursively remove node tmp + self.remove(tmp.val) + # Replace cur with tmp + cur.val = tmp.val + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize binary search tree + bst = BinarySearchTree() + nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] + # Note that different insertion orders can result in various tree structures. This particular sequence creates a perfect binary tree + for num in nums: + bst.insert(num) + print("\nInitialized binary tree is\n") + print_tree(bst.get_root()) + + # Search node + node = bst.search(7) + print("\nFound node object is: {}, node value = {}".format(node, node.val)) + + # Insert node + bst.insert(16) + print("\nAfter inserting node 16, the binary tree is\n") + print_tree(bst.get_root()) + + # Remove node + bst.remove(1) + print("\nAfter removing node 1, the binary tree is\n") + print_tree(bst.get_root()) + + bst.remove(2) + print("\nAfter removing node 2, the binary tree is\n") + print_tree(bst.get_root()) + + bst.remove(4) + print("\nAfter removing node 4, the binary tree is\n") + print_tree(bst.get_root()) diff --git a/en/codes/python/chapter_tree/binary_tree.py b/en/codes/python/chapter_tree/binary_tree.py new file mode 100644 index 0000000000..cdfb8416d3 --- /dev/null +++ b/en/codes/python/chapter_tree/binary_tree.py @@ -0,0 +1,41 @@ +""" +File: binary_tree.py +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, print_tree + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize binary tree + # Initialize node + n1 = TreeNode(val=1) + n2 = TreeNode(val=2) + n3 = TreeNode(val=3) + n4 = TreeNode(val=4) + n5 = TreeNode(val=5) + # Construct node references (pointers) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + print("\nInitialize binary tree\n") + print_tree(n1) + + # Insert and remove nodes + P = TreeNode(0) + # Insert node P between n1 -> n2 + n1.left = P + P.left = n2 + print("\nAfter inserting node P\n") + print_tree(n1) + # Remove node + n1.left = n2 + print("\nAfter removing node P\n") + print_tree(n1) diff --git a/en/codes/python/chapter_tree/binary_tree_bfs.py b/en/codes/python/chapter_tree/binary_tree_bfs.py new file mode 100644 index 0000000000..77e1ceac79 --- /dev/null +++ b/en/codes/python/chapter_tree/binary_tree_bfs.py @@ -0,0 +1,42 @@ +""" +File: binary_tree_bfs.py +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, list_to_tree, print_tree +from collections import deque + + +def level_order(root: TreeNode | None) -> list[int]: + """Level-order traversal""" + # Initialize queue, add root node + queue: deque[TreeNode] = deque() + queue.append(root) + # Initialize a list to store the traversal sequence + res = [] + while queue: + node: TreeNode = queue.popleft() # Queue dequeues + res.append(node.val) # Save node value + if node.left is not None: + queue.append(node.left) # Left child node enqueues + if node.right is not None: + queue.append(node.right) # Right child node enqueues + return res + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize binary tree + # Use a specific function to convert an array into a binary tree + root: TreeNode = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree\n") + print_tree(root) + + # Level-order traversal + res: list[int] = level_order(root) + print("\nPrint sequence of nodes from level-order traversal = ", res) diff --git a/en/codes/python/chapter_tree/binary_tree_dfs.py b/en/codes/python/chapter_tree/binary_tree_dfs.py new file mode 100644 index 0000000000..d25afe649f --- /dev/null +++ b/en/codes/python/chapter_tree/binary_tree_dfs.py @@ -0,0 +1,65 @@ +""" +File: binary_tree_dfs.py +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) +""" + +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from modules import TreeNode, list_to_tree, print_tree + + +def pre_order(root: TreeNode | None): + """Pre-order traversal""" + if root is None: + return + # Visit priority: root node -> left subtree -> right subtree + res.append(root.val) + pre_order(root=root.left) + pre_order(root=root.right) + + +def in_order(root: TreeNode | None): + """In-order traversal""" + if root is None: + return + # Visit priority: left subtree -> root node -> right subtree + in_order(root=root.left) + res.append(root.val) + in_order(root=root.right) + + +def post_order(root: TreeNode | None): + """Post-order traversal""" + if root is None: + return + # Visit priority: left subtree -> right subtree -> root node + post_order(root=root.left) + post_order(root=root.right) + res.append(root.val) + + +"""Driver Code""" +if __name__ == "__main__": + # Initialize binary tree + # Use a specific function to convert an array into a binary tree + root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree\n") + print_tree(root) + + # Pre-order traversal + res = [] + pre_order(root) + print("\nPrint sequence of nodes from pre-order traversal = ", res) + + # In-order traversal + res.clear() + in_order(root) + print("\nPrint sequence of nodes from in-order traversal = ", res) + + # Post-order traversal + res.clear() + post_order(root) + print("\nPrint sequence of nodes from post-order traversal = ", res) diff --git a/en/codes/python/modules/__init__.py b/en/codes/python/modules/__init__.py new file mode 100644 index 0000000000..b10799e3c5 --- /dev/null +++ b/en/codes/python/modules/__init__.py @@ -0,0 +1,19 @@ +# Follow the PEP 585 - Type Hinting Generics In Standard Collections +# https://peps.python.org/pep-0585/ +from __future__ import annotations + +# Import common libs here to simplify the code by `from module import *` +from .list_node import ( + ListNode, + list_to_linked_list, + linked_list_to_list, +) +from .tree_node import TreeNode, list_to_tree, tree_to_list +from .vertex import Vertex, vals_to_vets, vets_to_vals +from .print_util import ( + print_matrix, + print_linked_list, + print_tree, + print_dict, + print_heap, +) diff --git a/en/codes/python/modules/list_node.py b/en/codes/python/modules/list_node.py new file mode 100644 index 0000000000..03d1e1edef --- /dev/null +++ b/en/codes/python/modules/list_node.py @@ -0,0 +1,32 @@ +""" +File: list_node.py +Created Time: 2021-12-11 +Author: krahets (krahets@163.com) +""" + + +class ListNode: + """LinkedList node class""" + + def __init__(self, val: int): + self.val: int = val # Node value + self.next: ListNode | None = None # Reference to the next node + + +def list_to_linked_list(arr: list[int]) -> ListNode | None: + """Deserialize a list into a linked list""" + dum = head = ListNode(0) + for a in arr: + node = ListNode(a) + head.next = node + head = head.next + return dum.next + + +def linked_list_to_list(head: ListNode | None) -> list[int]: + """Serialize a linked list into a list""" + arr: list[int] = [] + while head: + arr.append(head.val) + head = head.next + return arr diff --git a/en/codes/python/modules/print_util.py b/en/codes/python/modules/print_util.py new file mode 100644 index 0000000000..0cf92db5d1 --- /dev/null +++ b/en/codes/python/modules/print_util.py @@ -0,0 +1,81 @@ +""" +File: print_util.py +Created Time: 2021-12-11 +Author: krahets (krahets@163.com), msk397 (machangxinq@gmail.com) +""" + +from .tree_node import TreeNode, list_to_tree +from .list_node import ListNode, linked_list_to_list + + +def print_matrix(mat: list[list[int]]): + """Print matrix""" + s = [] + for arr in mat: + s.append(" " + str(arr)) + print("[\n" + ",\n".join(s) + "\n]") + + +def print_linked_list(head: ListNode | None): + """Print linked list""" + arr: list[int] = linked_list_to_list(head) + print(" -> ".join([str(a) for a in arr])) + + +class Trunk: + def __init__(self, prev, string: str | None = None): + self.prev = prev + self.str = string + + +def show_trunks(p: Trunk | None): + if p is None: + return + show_trunks(p.prev) + print(p.str, end="") + + +def print_tree( + root: TreeNode | None, prev: Trunk | None = None, is_right: bool = False +): + """ + Print binary tree + This tree printer is borrowed from TECHIE DELIGHT + https://www.techiedelight.com/c-program-print-binary-tree/ + """ + if root is None: + return + + prev_str = " " + trunk = Trunk(prev, prev_str) + print_tree(root.right, trunk, True) + + if prev is None: + trunk.str = "———" + elif is_right: + trunk.str = "/———" + prev_str = " |" + else: + trunk.str = "\———" + prev.str = prev_str + + show_trunks(trunk) + print(" " + str(root.val)) + if prev: + prev.str = prev_str + trunk.str = " |" + print_tree(root.left, trunk, False) + + +def print_dict(hmap: dict): + """Print dictionary""" + for key, value in hmap.items(): + print(key, "->", value) + + +def print_heap(heap: list[int]): + """Print heap""" + print("Array representation of the heap:", heap) + print("Tree representation of the heap:") + root: TreeNode | None = list_to_tree(heap) + print_tree(root) diff --git a/en/codes/python/modules/tree_node.py b/en/codes/python/modules/tree_node.py new file mode 100644 index 0000000000..95bfef3fb7 --- /dev/null +++ b/en/codes/python/modules/tree_node.py @@ -0,0 +1,69 @@ +""" +File: tree_node.py +Created Time: 2021-12-11 +Author: krahets (krahets@163.com) +""" + +from collections import deque + + +class TreeNode: + """Binary tree node class""" + + def __init__(self, val: int = 0): + self.val: int = val # Node value + self.height: int = 0 # Node height + self.left: TreeNode | None = None # Reference to the left child node + self.right: TreeNode | None = None # Reference to the right child node + + # 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 + + +def list_to_tree_dfs(arr: list[int], i: int) -> TreeNode | None: + """Deserialize a list into a binary tree: Recursively""" + # If the index is out of array bounds, or the corresponding element is None, return None + if i < 0 or i >= len(arr) or arr[i] is None: + return None + # Construct the current node + root = TreeNode(arr[i]) + # Recursively construct left and right subtrees + root.left = list_to_tree_dfs(arr, 2 * i + 1) + root.right = list_to_tree_dfs(arr, 2 * i + 2) + return root + + +def list_to_tree(arr: list[int]) -> TreeNode | None: + """Deserialize a list into a binary tree""" + return list_to_tree_dfs(arr, 0) + + +def tree_to_list_dfs(root: TreeNode, i: int, res: list[int]) -> list[int]: + """Serialize a binary tree into a list: Recursively""" + if root is None: + return + if i >= len(res): + res += [None] * (i - len(res) + 1) + res[i] = root.val + tree_to_list_dfs(root.left, 2 * i + 1, res) + tree_to_list_dfs(root.right, 2 * i + 2, res) + + +def tree_to_list(root: TreeNode | None) -> list[int]: + """Serialize a binary tree into a list""" + res = [] + tree_to_list_dfs(root, 0, res) + return res diff --git a/en/codes/python/modules/vertex.py b/en/codes/python/modules/vertex.py new file mode 100644 index 0000000000..40602dde92 --- /dev/null +++ b/en/codes/python/modules/vertex.py @@ -0,0 +1,20 @@ +# File: vertex.py +# Created Time: 2023-02-23 +# Author: krahets (krahets@163.com) + + +class Vertex: + """Vertex class""" + + def __init__(self, val: int): + self.val = val + + +def vals_to_vets(vals: list[int]) -> list["Vertex"]: + """Input a list of values vals, return a list of vertices vets""" + return [Vertex(val) for val in vals] + + +def vets_to_vals(vets: list["Vertex"]) -> list[int]: + """Input a list of vertices vets, return a list of values vals""" + return [vet.val for vet in vets] diff --git a/en/codes/python/test_all.py b/en/codes/python/test_all.py new file mode 100644 index 0000000000..c1d8c3b034 --- /dev/null +++ b/en/codes/python/test_all.py @@ -0,0 +1,33 @@ +import os +import glob +import subprocess + +env = os.environ.copy() +env["PYTHONIOENCODING"] = "utf-8" + +if __name__ == "__main__": + # find source code files + src_paths = sorted(glob.glob("en/codes/python/chapter_*/*.py")) + errors = [] + + # run python code + for src_path in src_paths: + process = subprocess.Popen( + ["python", src_path], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + env=env, + encoding='utf-8' + ) + # Wait for the process to complete, and get the output and error messages + stdout, stderr = process.communicate() + # Check the exit status + exit_status = process.returncode + if exit_status != 0: + errors.append(stderr) + + print(f"Tested {len(src_paths)} files") + print(f"Found exception in {len(errors)} files") + if len(errors) > 0: + raise RuntimeError("\n\n".join(errors))