Translate all code to English (#1836)

* Review the EN heading format.

* Fix pythontutor headings.

* Fix pythontutor headings.

* bug fixes

* Fix headings in **/summary.md

* Revisit the CN-to-EN translation for Python code using Claude-4.5

* Revisit the CN-to-EN translation for Java code using Claude-4.5

* Revisit the CN-to-EN translation for Cpp code using Claude-4.5.

* Fix the dictionary.

* Fix cpp code translation for the multipart strings.

* Translate Go code to English.

* Update workflows to test EN code.

* Add EN translation for C.

* Add EN translation for CSharp.

* Add EN translation for Swift.

* Trigger the CI check.

* Revert.

* Update en/hash_map.md

* Add the EN version of Dart code.

* Add the EN version of Kotlin code.

* Add missing code files.

* Add the EN version of JavaScript code.

* Add the EN version of TypeScript code.

* Fix the workflows.

* Add the EN version of Ruby code.

* Add the EN version of Rust code.

* Update the CI check for the English version  code.

* Update Python CI check.

* Fix cmakelists for en/C code.

* Fix Ruby comments
This commit is contained in:
Yudong Jin
2025-12-31 07:44:52 +08:00
committed by GitHub
parent 45e1295241
commit 2778a6f9c7
1284 changed files with 71557 additions and 3275 deletions

View File

@@ -8,39 +8,39 @@ import random
def random_access(nums: list[int]) -> int:
"""Random access to elements"""
"""Random access to element"""
# Randomly select a number from the interval [0, len(nums)-1]
random_index = random.randint(0, len(nums) - 1)
# Retrieve and return a random element
# Retrieve and return the random element
random_num = nums[random_index]
return random_num
# 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
# Please note that Python's list is a dynamic array and can be extended directly
# For learning purposes, this function treats the list as an array with immutable length
def extend(nums: list[int], enlarge: int) -> list[int]:
"""Extend array length"""
# Initialize an extended length array
# Initialize an array with extended length
res = [0] * (len(nums) + enlarge)
# Copy all elements from the original array to the new array
for i in range(len(nums)):
res[i] = nums[i]
# Return the new array after expansion
# Return the extended new array
return res
def insert(nums: list[int], num: int, index: int):
"""Insert element num at `index`"""
# Move all elements after `index` one position backward
"""Insert element num at index index in the array"""
# Move all elements at and after index index backward by one position
for i in range(len(nums) - 1, index, -1):
nums[i] = nums[i - 1]
# Assign num to the element at index
# Assign num to the element at index index
nums[index] = num
def remove(nums: list[int], index: int):
"""Remove the element at `index`"""
# Move all elements after `index` one position forward
"""Remove the element at index index"""
# Move all elements after index index forward by one position
for i in range(index, len(nums) - 1):
nums[i] = nums[i + 1]
@@ -51,17 +51,17 @@ def traverse(nums: list[int]):
# Traverse array by index
for i in range(len(nums)):
count += nums[i]
# Traverse array elements
# Direct traversal of array elements
for num in nums:
count += num
# Traverse both data index and elements
# Traverse simultaneously 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"""
"""Find the specified element in the array"""
for i in range(len(nums)):
if nums[i] == target:
return i
@@ -70,7 +70,7 @@ def find(nums: list[int], target: int) -> int:
"""Driver Code"""
if __name__ == "__main__":
# Initialize an array
# Initialize array
arr = [0] * 5
print("Array arr =", arr)
nums = [1, 3, 2, 5, 4]
@@ -78,23 +78,23 @@ if __name__ == "__main__":
# Random access
random_num: int = random_access(nums)
print("Retrieve a random element in nums", random_num)
print("Get random element from nums", random_num)
# Length extension
nums: list[int] = extend(nums, 3)
print("Extend the array length to 8, resulting in nums =", nums)
print("Extend the array length to 8, get nums =", nums)
# Insert element
insert(nums, 6, 3)
print("Insert number 6 at index 3, resulting in nums =", nums)
print("Insert number 6 at index 3, get nums =", nums)
# Remove element
remove(nums, 2)
print("Remove the element at index 2, resulting in nums =", nums)
print("Remove the element at index 2, get nums =", nums)
# Traverse array
traverse(nums)
# Search for elements
# Find element
index: int = find(nums, 3)
print("Search for element 3 in nums, resulting in index =", index)
print("Search for element 3 in nums, get index =", index)

View File

@@ -29,7 +29,7 @@ def remove(n0: ListNode):
def access(head: ListNode, index: int) -> ListNode | None:
"""Access the node at `index` in the linked list"""
"""Access the node at index index in the linked list"""
for _ in range(index):
if not head:
return None
@@ -38,7 +38,7 @@ def access(head: ListNode, index: int) -> ListNode | None:
def find(head: ListNode, target: int) -> int:
"""Search for the first node with value target in the linked list"""
"""Find the first node with value target in the linked list"""
index = 0
while head:
if head.val == target:
@@ -68,18 +68,18 @@ if __name__ == "__main__":
# Insert node
p = ListNode(0)
insert(n0, p)
print("Linked list after inserting the node is")
print("The linked list after inserting a node is")
print_linked_list(n0)
# Remove node
remove(n0)
print("Linked list after removing the node is")
print("The linked list after removing a 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
# Find node
index: int = find(n0, 2)
print("The index of the node with value 2 in the linked list = {}".format(index))

View File

@@ -12,45 +12,45 @@ if __name__ == "__main__":
# Access element
x: int = nums[1]
print("\nAccess the element at index 1, resulting in x =", x)
print("\nAccess the element at index 1, get x =", x)
# Update element
nums[1] = 0
print("\nUpdate the element at index 1 to 0, resulting in nums =", nums)
print("\nUpdate the element at index 1 to 0, get nums =", nums)
# Clear list
nums.clear()
print("\nAfter clearing the list, nums =", nums)
print("\nAfter clearing the list nums =", nums)
# Add element at the end
# Add elements 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)
print("\nAfter adding elements nums =", nums)
# Insert element in the middle
nums.insert(3, 6)
print("\nInsert number 6 at index 3, resulting in nums =", nums)
print("\nInsert number 6 at index 3, get nums =", nums)
# Remove element
nums.pop(3)
print("\nRemove the element at index 3, resulting in nums =", nums)
print("\nRemove the element at index 3, get nums =", nums)
# Traverse the list by index
# Traverse list by index
count = 0
for i in range(len(nums)):
count += nums[i]
# Traverse the list elements
# Direct traversal of 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)
print("\nConcatenate list nums1 to nums, get nums =", nums)
# Sort list
nums.sort()
print("\nAfter sorting the list, nums =", nums)
print("\nAfter sorting the list nums =", nums)

View File

@@ -13,7 +13,7 @@ class MyList:
self._capacity: int = 10 # List capacity
self._arr: list[int] = [0] * self._capacity # Array (stores list elements)
self._size: int = 0 # List length (current number of elements)
self._extend_ratio: int = 2 # Multiple for each list expansion
self._extend_ratio: int = 2 # Multiple by which the list capacity is extended each time
def size(self) -> int:
"""Get list length (current number of elements)"""
@@ -38,7 +38,7 @@ class MyList:
def add(self, num: int):
"""Add element at the end"""
# When the number of elements exceeds capacity, trigger the expansion mechanism
# When the number of elements exceeds capacity, trigger the extension mechanism
if self.size() == self.capacity():
self.extend_capacity()
self._arr[self._size] = num
@@ -48,10 +48,10 @@ class MyList:
"""Insert element in the middle"""
if index < 0 or index >= self._size:
raise IndexError("Index out of bounds")
# When the number of elements exceeds capacity, trigger the expansion mechanism
# When the number of elements exceeds capacity, trigger the extension mechanism
if self._size == self.capacity():
self.extend_capacity()
# Move all elements after `index` one position backward
# Move all elements at and after index index backward by one position
for j in range(self._size - 1, index - 1, -1):
self._arr[j + 1] = self._arr[j]
self._arr[index] = num
@@ -63,7 +63,7 @@ class MyList:
if index < 0 or index >= self._size:
raise IndexError("Index out of bounds")
num = self._arr[index]
# Move all elements after `index` one position forward
# Move all elements after index index forward by one position
for j in range(index, self._size - 1):
self._arr[j] = self._arr[j + 1]
# Update the number of elements
@@ -72,14 +72,14 @@ class MyList:
return num
def extend_capacity(self):
"""Extend list"""
# Create a new array of _extend_ratio times the length of the original array and copy the original array to the new array
"""Extend list capacity"""
# Create a new array with length _extend_ratio times the original array, and copy the original array to the new array
self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1)
# Update list capacity
self._capacity = len(self._arr)
def to_array(self) -> list[int]:
"""Return a list of valid lengths"""
"""Return list with valid length"""
return self._arr[: self._size]
@@ -87,32 +87,32 @@ class MyList:
if __name__ == "__main__":
# Initialize list
nums = MyList()
# Add element at the end
# Add elements 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()}")
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())
print("Insert number 6 at index 3, get nums =", nums.to_array())
# Remove element
nums.remove(3)
print("Remove the element at index 3, resulting in nums =", nums.to_array())
print("Remove the element at index 3, get nums =", nums.to_array())
# Access element
num = nums.get(1)
print("Access the element at index 1, resulting in num =", num)
print("Access the element at index 1, get num =", num)
# Update element
nums.set(0, 1)
print("Update the element at index 1 to 0, resulting in nums =", nums.to_array())
print("Update the element at index 1 to 0, get nums =", nums.to_array())
# Test expansion mechanism
# Test extension mechanism
for i in range(10):
# At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism at this time
# At i = 5, the list length will exceed the list capacity, triggering the extension mechanism at this point
nums.add(i)
print(f"After expansion, the list {nums.to_array()} capacity = {nums.capacity()} length = {nums.size()}")
print(f"List after extension {nums.to_array()}, capacity = {nums.capacity()}, length = {nums.size()}")

View File

@@ -14,35 +14,35 @@ def backtrack(
diags1: list[bool],
diags2: list[bool],
):
"""Backtracking algorithm: n queens"""
"""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
# Calculate the main diagonal and anti-diagonal corresponding to this 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
# Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
if not cols[col] and not diags1[diag1] and not diags2[diag2]:
# Attempt: place the queen in the cell
# Attempt: place the queen in this 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
# Backtrack: restore this cell to an empty cell
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
"""Solve N queens"""
# Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
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
cols = [False] * n # Record whether there is a queen in the column
diags1 = [False] * (2 * n - 1) # Record whether there is a queen on the main diagonal
diags2 = [False] * (2 * n - 1) # Record whether there is a queen on the anti-diagonal
res = []
backtrack(0, n, state, res, cols, diags1, diags2)
@@ -54,8 +54,8 @@ 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)}")
print(f"Input chessboard size is {n}")
print(f"There are {len(res)} queen placement solutions")
for state in res:
print("--------------------")
for row in state:

View File

@@ -8,7 +8,7 @@ Author: krahets (krahets@163.com)
def backtrack(
state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
):
"""Backtracking algorithm: Permutation I"""
"""Backtracking algorithm: Permutations I"""
# When the state length equals the number of elements, record the solution
if len(state) == len(choices):
res.append(list(state))
@@ -17,18 +17,18 @@ def backtrack(
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
# Attempt: make choice, update 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
# Backtrack: undo choice, restore to previous state
selected[i] = False
state.pop()
def permutations_i(nums: list[int]) -> list[list[int]]:
"""Permutation I"""
"""Permutations I"""
res = []
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
return res

View File

@@ -8,7 +8,7 @@ Author: krahets (krahets@163.com)
def backtrack(
state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
):
"""Backtracking algorithm: Permutation II"""
"""Backtracking algorithm: Permutations II"""
# When the state length equals the number of elements, record the solution
if len(state) == len(choices):
res.append(list(state))
@@ -18,19 +18,19 @@ def backtrack(
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
# Attempt: make choice, update state
duplicated.add(choice) # Record the selected element value
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
# Backtrack: undo choice, restore to previous state
selected[i] = False
state.pop()
def permutations_ii(nums: list[int]) -> list[list[int]]:
"""Permutation II"""
"""Permutations II"""
res = []
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
return res

View File

@@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree
def pre_order(root: TreeNode):
"""Pre-order traversal: Example one"""
"""Preorder traversal: Example 1"""
if root is None:
return
if root.val == 7:
@@ -28,7 +28,7 @@ if __name__ == "__main__":
print("\nInitialize binary tree")
print_tree(root)
# Pre-order traversal
# Preorder traversal
res = list[TreeNode]()
pre_order(root)

View File

@@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree
def pre_order(root: TreeNode):
"""Pre-order traversal: Example two"""
"""Preorder traversal: Example 2"""
if root is None:
return
# Attempt
@@ -22,7 +22,7 @@ def pre_order(root: TreeNode):
res.append(list(path))
pre_order(root.left)
pre_order(root.right)
# Retract
# Backtrack
path.pop()
@@ -32,11 +32,11 @@ if __name__ == "__main__":
print("\nInitialize binary tree")
print_tree(root)
# Pre-order traversal
# Preorder traversal
path = list[TreeNode]()
res = list[list[TreeNode]]()
pre_order(root)
print("\nOutput all root-to-node 7 paths")
print("\nOutput all paths from root node to node 7")
for path in res:
print([node.val for node in path])

View File

@@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree
def pre_order(root: TreeNode):
"""Pre-order traversal: Example three"""
"""Preorder traversal: Example 3"""
# Pruning
if root is None or root.val == 3:
return
@@ -23,7 +23,7 @@ def pre_order(root: TreeNode):
res.append(list(path))
pre_order(root.left)
pre_order(root.right)
# Retract
# Backtrack
path.pop()
@@ -33,11 +33,11 @@ if __name__ == "__main__":
print("\nInitialize binary tree")
print_tree(root)
# Pre-order traversal
# Preorder 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")
print("\nOutput all paths from root node to node 7, excluding paths with nodes of value 3")
for path in res:
print([node.val for node in path])

View File

@@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree
def is_solution(state: list[TreeNode]) -> bool:
"""Determine if the current state is a solution"""
"""Check if the current state is a solution"""
return state and state[-1].val == 7
@@ -22,7 +22,7 @@ def record_solution(state: list[TreeNode], res: list[list[TreeNode]]):
def is_valid(state: list[TreeNode], choice: TreeNode) -> bool:
"""Determine if the choice is legal under the current state"""
"""Check if the choice is valid under the current state"""
return choice is not None and choice.val != 3
@@ -39,20 +39,20 @@ def undo_choice(state: list[TreeNode], choice: TreeNode):
def backtrack(
state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]]
):
"""Backtracking algorithm: Example three"""
# Check if it's a solution
"""Backtracking algorithm: Example 3"""
# Check if it is a solution
if is_solution(state):
# Record solution
record_solution(state, res)
# Traverse all choices
for choice in choices:
# Pruning: check if the choice is legal
# Pruning: check if the choice is valid
if is_valid(state, choice):
# Attempt: make a choice, update the state
# Attempt: make choice, update state
make_choice(state, choice)
# Proceed to the next round of selection
backtrack(state, [choice.left, choice.right], res)
# Retract: undo the choice, restore to the previous state
# Backtrack: undo choice, restore to previous state
undo_choice(state, choice)
@@ -66,6 +66,6 @@ if __name__ == "__main__":
res = []
backtrack(state=[], choices=[root], res=res)
print("\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3")
print("\nOutput all paths from root node to node 7, excluding paths with nodes of value 3")
for path in res:
print([node.val for node in path])

View File

@@ -8,28 +8,28 @@ 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"""
"""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
# Pruning 2: 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
# Pruning 1: if the subset sum exceeds target, end the loop directly
# This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target
if target - choices[i] < 0:
break
# Attempt: make a choice, update target, start
# Attempt: make 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
# Backtrack: undo choice, restore to previous state
state.pop()
def subset_sum_i(nums: list[int], target: int) -> list[list[int]]:
"""Solve Subset Sum I"""
"""Solve subset sum I"""
state = [] # State (subset)
nums.sort() # Sort nums
start = 0 # Start point for traversal
@@ -45,4 +45,4 @@ if __name__ == "__main__":
res = subset_sum_i(nums, target)
print(f"Input array nums = {nums}, target = {target}")
print(f"All subsets equal to {target} res = {res}")
print(f"All subsets with sum equal to {target} res = {res}")

View File

@@ -12,26 +12,26 @@ def backtrack(
choices: list[int],
res: list[list[int]],
):
"""Backtracking algorithm: Subset Sum I"""
"""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
# Pruning: if the subset sum exceeds target, skip this choice
if total + choices[i] > target:
continue
# Attempt: make a choice, update elements and total
# Attempt: make choice, update element sum 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
# Backtrack: undo choice, restore to previous state
state.pop()
def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]:
"""Solve Subset Sum I (including duplicate subsets)"""
"""Solve subset sum I (including duplicate subsets)"""
state = [] # State (subset)
total = 0 # Subset sum
res = [] # Result list (subset list)
@@ -46,5 +46,5 @@ if __name__ == "__main__":
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"The result of this method includes duplicate sets")
print(f"All subsets with sum equal to {target} res = {res}")
print(f"Please note that the result output by this method contains duplicate sets")

View File

@@ -8,32 +8,32 @@ 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"""
"""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
# Pruning 2: start traversing from start to avoid generating duplicate subsets
# Pruning 3: 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
# Pruning 1: if the subset sum exceeds target, end the loop directly
# This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target
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
# Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly
if i > start and choices[i] == choices[i - 1]:
continue
# Attempt: make a choice, update target, start
# Attempt: make 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
# Backtrack: undo choice, restore to previous state
state.pop()
def subset_sum_ii(nums: list[int], target: int) -> list[list[int]]:
"""Solve Subset Sum II"""
"""Solve subset sum II"""
state = [] # State (subset)
nums.sort() # Sort nums
start = 0 # Start point for traversal
@@ -49,4 +49,4 @@ if __name__ == "__main__":
res = subset_sum_ii(nums, target)
print(f"Input array nums = {nums}, target = {target}")
print(f"All subsets equal to {target} res = {res}")
print(f"All subsets with sum equal to {target} res = {res}")

View File

@@ -8,7 +8,7 @@ Author: krahets (krahets@163.com)
def for_loop(n: int) -> int:
"""for loop"""
res = 0
# Loop sum 1, 2, ..., n-1, n
# Sum 1, 2, ..., n-1, n
for i in range(1, n + 1):
res += i
return res
@@ -18,7 +18,7 @@ def while_loop(n: int) -> int:
"""while loop"""
res = 0
i = 1 # Initialize condition variable
# Loop sum 1, 2, ..., n-1, n
# Sum 1, 2, ..., n-1, n
while i <= n:
res += i
i += 1 # Update condition variable
@@ -29,7 +29,7 @@ def while_loop_ii(n: int) -> int:
"""while loop (two updates)"""
res = 0
i = 1 # Initialize condition variable
# Loop sum 1, 4, 10, ...
# Sum 1, 4, 10, ...
while i <= n:
res += i
# Update condition variable
@@ -39,7 +39,7 @@ def while_loop_ii(n: int) -> int:
def nested_for_loop(n: int) -> str:
"""Double for loop"""
"""Nested for loop"""
res = ""
# Loop i = 1, 2, ..., n-1, n
for i in range(1, n + 1):
@@ -53,13 +53,13 @@ def nested_for_loop(n: int) -> str:
if __name__ == "__main__":
n = 5
res = for_loop(n)
print(f"\nfor loop sum result res = {res}")
print(f"\nSum result of for loop res = {res}")
res = while_loop(n)
print(f"\nwhile loop sum result res = {res}")
print(f"\nSum result of while loop res = {res}")
res = while_loop_ii(n)
print(f"\nwhile loop (two updates) sum result res = {res}")
print(f"\nSum result of while loop (two updates) res = {res}")
res = nested_for_loop(n)
print(f"\nDouble for loop traversal result {res}")
print(f"\nTraversal result of nested for loop {res}")

View File

@@ -10,24 +10,24 @@ def recur(n: int) -> int:
# Termination condition
if n == 1:
return 1
# Recursive: recursive call
# Recurse: recursive call
res = recur(n - 1)
# Return: return result
return n + res
def for_loop_recur(n: int) -> int:
"""Simulate recursion with iteration"""
"""Simulate recursion using iteration"""
# Use an explicit stack to simulate the system call stack
stack = []
res = 0
# Recursive: recursive call
# Recurse: recursive call
for i in range(n, 0, -1):
# Simulate "recursive" by "pushing onto the stack"
# Simulate "recurse" with "push"
stack.append(i)
# Return: return result
while stack:
# Simulate "return" by "popping from the stack"
# Simulate "return" with "pop"
res += stack.pop()
# res = 1+2+3+...+n
return res
@@ -43,7 +43,7 @@ def tail_recur(n, res):
def fib(n: int) -> int:
"""Fibonacci sequence: Recursion"""
"""Fibonacci sequence: recursion"""
# Termination condition f(1) = 0, f(2) = 1
if n == 1 or n == 2:
return n - 1
@@ -57,13 +57,13 @@ def fib(n: int) -> int:
if __name__ == "__main__":
n = 5
res = recur(n)
print(f"\nRecursive function sum result res = {res}")
print(f"\nSum result of recursive function res = {res}")
res = for_loop_recur(n)
print(f"\nSimulate recursion with iteration sum result res = {res}")
print(f"\nSum result of simulating recursion using iteration res = {res}")
res = tail_recur(n, 0)
print(f"\nTail recursive function sum result res = {res}")
print(f"\nSum result of tail recursive function res = {res}")
res = fib(n)
print(f"\nThe n th term of the Fibonacci sequence is {res}")
print(f"\nThe {n}th term of the Fibonacci sequence is {res}")

View File

@@ -18,21 +18,21 @@ def function() -> int:
def constant(n: int):
"""Constant complexity"""
"""Constant order"""
# Constants, variables, objects occupy O(1) space
a = 0
nums = [0] * 10000
node = ListNode(0)
# Variables in a loop occupy O(1) space
# Variables in the loop occupy O(1) space
for _ in range(n):
c = 0
# Functions in a loop occupy O(1) space
# Functions in the loop occupy O(1) space
for _ in range(n):
function()
def linear(n: int):
"""Linear complexity"""
"""Linear order"""
# A list of length n occupies O(n) space
nums = [0] * n
# A hash table of length n occupies O(n) space
@@ -42,30 +42,30 @@ def linear(n: int):
def linear_recur(n: int):
"""Linear complexity (recursive implementation)"""
print("Recursive n =", n)
"""Linear order (recursive implementation)"""
print("Recursion 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
"""Quadratic order"""
# A 2D list occupies O(n^2) space
num_matrix = [[0] * n for _ in range(n)]
def quadratic_recur(n: int) -> int:
"""Quadratic complexity (recursive implementation)"""
"""Quadratic order (recursive implementation)"""
if n <= 0:
return 0
# Array nums length = n, n-1, ..., 2, 1
# Array nums length is 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)"""
"""Exponential order (build full binary tree)"""
if n == 0:
return None
root = TreeNode(0)
@@ -77,14 +77,14 @@ def build_tree(n: int) -> TreeNode | None:
"""Driver Code"""
if __name__ == "__main__":
n = 5
# Constant complexity
# Constant order
constant(n)
# Linear complexity
# Linear order
linear(n)
linear_recur(n)
# Quadratic complexity
# Quadratic order
quadratic(n)
quadratic_recur(n)
# Exponential complexity
# Exponential order
root = build_tree(n)
print_tree(root)

View File

@@ -6,7 +6,7 @@ Author: krahets (krahets@163.com)
def constant(n: int) -> int:
"""Constant complexity"""
"""Constant order"""
count = 0
size = 100000
for _ in range(size):
@@ -15,7 +15,7 @@ def constant(n: int) -> int:
def linear(n: int) -> int:
"""Linear complexity"""
"""Linear order"""
count = 0
for _ in range(n):
count += 1
@@ -23,18 +23,18 @@ def linear(n: int) -> int:
def array_traversal(nums: list[int]) -> int:
"""Linear complexity (traversing an array)"""
"""Linear order (traversing array)"""
count = 0
# Loop count is proportional to the length of the array
# Number of iterations is proportional to the array length
for num in nums:
count += 1
return count
def quadratic(n: int) -> int:
"""Quadratic complexity"""
"""Quadratic order"""
count = 0
# Loop count is squared in relation to the data size n
# Number of iterations is quadratically related to the data size n
for i in range(n):
for j in range(n):
count += 1
@@ -42,26 +42,26 @@ def quadratic(n: int) -> int:
def bubble_sort(nums: list[int]) -> int:
"""Quadratic complexity (bubble sort)"""
"""Quadratic order (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
# Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that 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
count += 3 # Element swap includes 3 unit operations
return count
def exponential(n: int) -> int:
"""Exponential complexity (loop implementation)"""
"""Exponential order (loop implementation)"""
count = 0
base = 1
# Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1)
# Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1)
for _ in range(n):
for _ in range(base):
count += 1
@@ -71,14 +71,14 @@ def exponential(n: int) -> int:
def exp_recur(n: int) -> int:
"""Exponential complexity (recursive implementation)"""
"""Exponential order (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)"""
"""Logarithmic order (loop implementation)"""
count = 0
while n > 1:
n = n / 2
@@ -87,28 +87,30 @@ def logarithmic(n: int) -> int:
def log_recur(n: int) -> int:
"""Logarithmic complexity (recursive implementation)"""
"""Logarithmic order (recursive implementation)"""
if n <= 1:
return 0
return log_recur(n / 2) + 1
def linear_log_recur(n: int) -> int:
"""Linear logarithmic complexity"""
"""Linearithmic order"""
if n <= 1:
return 1
count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2)
# Divide into two, the scale of subproblems is reduced by half
count = linear_log_recur(n // 2) + linear_log_recur(n // 2)
# Current subproblem contains n operations
for _ in range(n):
count += 1
return count
def factorial_recur(n: int) -> int:
"""Factorial complexity (recursive implementation)"""
"""Factorial order (recursive implementation)"""
if n == 0:
return 1
count = 0
# From 1 split into n
# Split from 1 into n
for _ in range(n):
count += factorial_recur(n - 1)
return count
@@ -116,36 +118,36 @@ def factorial_recur(n: int) -> int:
"""Driver Code"""
if __name__ == "__main__":
# Can modify n to experience the trend of operation count changes under various complexities
# You can modify n to run and observe the trend of the number of operations for various complexities
n = 8
print("Input data size n =", n)
count: int = constant(n)
print("Constant complexity operation count =", count)
count = constant(n)
print("Number of operations of constant order =", 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 = linear(n)
print("Number of operations of linear order =", count)
count = array_traversal([0] * n)
print("Number of operations of linear order (traversing array) =", count)
count: int = quadratic(n)
print("Quadratic complexity operation count =", count)
count = quadratic(n)
print("Number of operations of quadratic order =", 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 = bubble_sort(nums)
print("Number of operations of quadratic order (bubble sort) =", 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 = exponential(n)
print("Number of operations of exponential order (loop implementation) =", count)
count = exp_recur(n)
print("Number of operations of exponential order (recursive implementation) =", 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 = logarithmic(n)
print("Number of operations of logarithmic order (loop implementation) =", count)
count = log_recur(n)
print("Number of operations of logarithmic order (recursive implementation) =", count)
count: int = linear_log_recur(n)
print("Linear logarithmic complexity (recursive implementation) operation count =", count)
count = linear_log_recur(n)
print("Number of operations of linearithmic order (recursive implementation) =", count)
count: int = factorial_recur(n)
print("Factorial complexity (recursive implementation) operation count =", count)
count = factorial_recur(n)
print("Number of operations of factorial order (recursive implementation) =", count)

View File

@@ -8,7 +8,7 @@ import random
def random_numbers(n: int) -> list[int]:
"""Generate an array with elements: 1, 2, ..., n, order shuffled"""
"""Generate an array with elements: 1, 2, ..., n, shuffled in order"""
# Generate array nums =: 1, 2, 3, ..., n
nums = [i for i in range(1, n + 1)]
# Randomly shuffle array elements
@@ -19,8 +19,8 @@ def random_numbers(n: int) -> list[int]:
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)
# When element 1 is at the head of the array, best time complexity O(1) is achieved
# When element 1 is at the tail of the array, worst time complexity O(n) is achieved
if nums[i] == 1:
return i
return -1
@@ -32,5 +32,5 @@ if __name__ == "__main__":
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)
print("\nArray [ 1, 2, ..., n ] after being shuffled =", nums)
print("The index of number 1 is", index)

View File

@@ -7,26 +7,26 @@ 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 the interval is empty, it means there is no target element, return -1
if i > j:
return -1
# Calculate midpoint index m
# Calculate the midpoint index m
m = (i + j) // 2
if nums[m] < target:
# Recursive subproblem f(m+1, j)
# Recursion subproblem f(m+1, j)
return dfs(nums, target, m + 1, j)
elif nums[m] > target:
# Recursive subproblem f(i, m-1)
# Recursion subproblem f(i, m-1)
return dfs(nums, target, i, m - 1)
else:
# Found the target element, thus return its index
# Found the target element, 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)
# Solve the problem f(0, n-1)
return dfs(nums, target, 0, n - 1)
@@ -35,6 +35,6 @@ if __name__ == "__main__":
target = 6
nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
# Binary search (double closed interval)
# Binary search (closed interval on both sides)
index = binary_search(nums, target)
print("Index of target element 6 =", index)
print("Index of target element 6 = ", index)

View File

@@ -18,25 +18,25 @@ def dfs(
l: int,
r: int,
) -> TreeNode | None:
"""Build binary tree: Divide and conquer"""
# Terminate when subtree interval is empty
"""Build binary tree: divide and conquer"""
# Terminate when the subtree interval is empty
if r - l < 0:
return None
# Initialize root node
# Initialize the root node
root = TreeNode(preorder[i])
# Query m to divide left and right subtrees
# Query m to divide the left and right subtrees
m = inorder_map[preorder[i]]
# Subproblem: build left subtree
# Subproblem: build the left subtree
root.left = dfs(preorder, inorder_map, i + 1, l, m - 1)
# Subproblem: build right subtree
# Subproblem: build the right subtree
root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r)
# Return root node
# Return the 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
# Initialize hash map, storing the mapping from inorder elements to indices
inorder_map = {val: i for i, val in enumerate(inorder)}
root = dfs(preorder, inorder_map, 0, 0, len(inorder) - 1)
return root
@@ -46,8 +46,8 @@ def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None:
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}")
print(f"Preorder traversal = {preorder}")
print(f"Inorder traversal = {inorder}")
root = build_tree(preorder, inorder)
print("The built binary tree is:")

View File

@@ -6,37 +6,37 @@ 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
"""Move a disk"""
# Take out a disk from the top of src
pan = src.pop()
# Place the disc on top of tar
# Place the disk 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 there is only one disk left in src, move it directly 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
# Subproblem f(i-1): move the top i-1 disks from src to buf using tar
dfs(i - 1, src, tar, buf)
# Subproblem f(1): move the remaining one disc from src to tar
# Subproblem f(1): move the remaining disk 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
# Subproblem f(i-1): move the top i-1 disks from buf to tar using src
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
# Move the top n disks from A to C using B
dfs(n, A, B, C)
"""Driver Code"""
if __name__ == "__main__":
# The tail of the list is the top of the pillar
# The tail of the list is the top of the rod
A = [5, 4, 3, 2, 1]
B = []
C = []
@@ -47,7 +47,7 @@ if __name__ == "__main__":
solve_hanota(A, B, C)
print("After the discs are moved:")
print("After moving the disks:")
print(f"A = {A}")
print(f"B = {B}")
print(f"C = {C}")

View File

@@ -7,24 +7,24 @@ 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
# When climbing to the n-th stair, add 1 to the solution count
if state == n:
res[0] += 1
# Traverse all choices
for choice in choices:
# Pruning: do not allow climbing beyond the nth step
# Pruning: not allowed to go beyond the n-th stair
if state + choice > n:
continue
# Attempt: make a choice, update the state
# Attempt: make a choice, update state
backtrack(choices, state + choice, n, res)
# Retract
# Backtrack
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
choices = [1, 2] # Can choose to climb up 1 or 2 stairs
state = 0 # Start climbing from the 0-th stair
res = [0] # Use res[0] to record the solution count
backtrack(choices, state, n, res)
return res[0]
@@ -34,4 +34,4 @@ if __name__ == "__main__":
n = 9
res = climbing_stairs_backtrack(n)
print(f"Climb {n} steps, there are {res} solutions in total")
print(f"Climbing {n} stairs has {res} solutions")

View File

@@ -6,12 +6,12 @@ Author: krahets (krahets@163.com)
def climbing_stairs_constraint_dp(n: int) -> int:
"""Constrained climbing stairs: Dynamic programming"""
"""Climbing stairs with constraint: Dynamic programming"""
if n == 1 or n == 2:
return 1
# Initialize dp table, used to store subproblem solutions
# Initialize dp table, used to store solutions to subproblems
dp = [[0] * 3 for _ in range(n + 1)]
# Initial state: preset the smallest subproblem solution
# Initial state: preset the solution to the smallest subproblem
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
@@ -26,4 +26,4 @@ if __name__ == "__main__":
n = 9
res = climbing_stairs_constraint_dp(n)
print(f"Climb {n} steps, there are {res} solutions in total")
print(f"Climbing {n} stairs has {res} solutions")

View File

@@ -25,4 +25,4 @@ if __name__ == "__main__":
n = 9
res = climbing_stairs_dfs(n)
print(f"Climb {n} steps, there are {res} solutions in total")
print(f"Climbing {n} stairs has {res} solutions")

View File

@@ -6,11 +6,11 @@ Author: krahets (krahets@163.com)
def dfs(i: int, mem: list[int]) -> int:
"""Memoized search"""
"""Memoization 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 record dp[i] exists, return it directly
if mem[i] != -1:
return mem[i]
# dp[i] = dp[i-1] + dp[i-2]
@@ -21,8 +21,8 @@ def dfs(i: int, mem: list[int]) -> int:
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
"""Climbing stairs: Memoization search"""
# mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record
mem = [-1] * (n + 1)
return dfs(n, mem)
@@ -32,4 +32,4 @@ if __name__ == "__main__":
n = 9
res = climbing_stairs_dfs_mem(n)
print(f"Climb {n} steps, there are {res} solutions in total")
print(f"Climbing {n} stairs has {res} solutions")

View File

@@ -9,9 +9,9 @@ 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
# Initialize dp table, used to store solutions to subproblems
dp = [0] * (n + 1)
# Initial state: preset the smallest subproblem solution
# Initial state: preset the solution to the smallest subproblem
dp[1], dp[2] = 1, 2
# State transition: gradually solve larger subproblems from smaller ones
for i in range(3, n + 1):
@@ -34,7 +34,7 @@ if __name__ == "__main__":
n = 9
res = climbing_stairs_dp(n)
print(f"Climb {n} steps, there are {res} solutions in total")
print(f"Climbing {n} stairs has {res} solutions")
res = climbing_stairs_dp_comp(n)
print(f"Climb {n} steps, there are {res} solutions in total")
print(f"Climbing {n} stairs has {res} solutions")

View File

@@ -14,14 +14,14 @@ def coin_change_dp(coins: list[int], amt: int) -> int:
# 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
# State transition: 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
# If exceeds target amount, don't select coin i
dp[i][a] = dp[i - 1][a]
else:
# The smaller value between not choosing and choosing coin i
# The smaller value between not selecting and selecting 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
@@ -35,13 +35,13 @@ def coin_change_dp_comp(coins: list[int], amt: int) -> int:
dp[0] = 0
# State transition
for i in range(1, n + 1):
# Traverse in order
# Traverse in forward order
for a in range(1, amt + 1):
if coins[i - 1] > a:
# If exceeding the target amount, do not choose coin i
# If exceeds target amount, don't select coin i
dp[a] = dp[a]
else:
# The smaller value between not choosing and choosing coin i
# The smaller value between not selecting and selecting coin i
dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1)
return dp[amt] if dp[amt] != MAX else -1
@@ -53,8 +53,8 @@ if __name__ == "__main__":
# Dynamic programming
res = coin_change_dp(coins, amt)
print(f"Minimum number of coins required to reach the target amount = {res}")
print(f"The minimum number of coins needed to make up the target amount is {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}")
print(f"The minimum number of coins needed to make up the target amount is {res}")

View File

@@ -17,10 +17,10 @@ def coin_change_ii_dp(coins: list[int], amt: int) -> int:
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
# If exceeds target amount, don't select coin i
dp[i][a] = dp[i - 1][a]
else:
# The sum of the two options of not choosing and choosing coin i
# Sum of the two options: not selecting and selecting coin i
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]
return dp[n][amt]
@@ -33,13 +33,13 @@ def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int:
dp[0] = 1
# State transition
for i in range(1, n + 1):
# Traverse in order
# Traverse in forward order
for a in range(1, amt + 1):
if coins[i - 1] > a:
# If exceeding the target amount, do not choose coin i
# If exceeds target amount, don't select coin i
dp[a] = dp[a]
else:
# The sum of the two options of not choosing and choosing coin i
# Sum of the two options: not selecting and selecting coin i
dp[a] = dp[a] + dp[a - coins[i - 1]]
return dp[amt]

View File

@@ -6,49 +6,49 @@ Author: krahets (krahets@163.com)
def edit_distance_dfs(s: str, t: str, i: int, j: int) -> int:
"""Edit distance: Brute force search"""
"""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 s is empty, return length of t
if i == 0:
return j
# If t is empty, return the length of s
# If t is empty, return length of s
if j == 0:
return i
# If the two characters are equal, skip these two characters
# If two characters are equal, skip both 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
# Minimum edit steps = minimum edit steps of insert, delete, 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 minimum edit steps
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"""
"""Edit distance: Memoization 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 s is empty, return length of t
if i == 0:
return j
# If t is empty, return the length of s
# If t is empty, return length of s
if j == 0:
return i
# If there is a record, return it
# If there's a record, return it directly
if mem[i][j] != -1:
return mem[i][j]
# If the two characters are equal, skip these two characters
# If two characters are equal, skip both 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
# Minimum edit steps = minimum edit steps of insert, delete, 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
# Record and return minimum edit steps
mem[i][j] = min(insert, delete, replace) + 1
return mem[i][j]
@@ -62,14 +62,14 @@ def edit_distance_dp(s: str, t: str) -> int:
dp[i][0] = i
for j in range(1, m + 1):
dp[0][j] = j
# State transition: the rest of the rows and columns
# State transition: 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
# If two characters are equal, skip both 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
# Minimum edit steps = minimum edit steps of insert, delete, replace + 1
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1
return dp[n][m]
@@ -81,21 +81,21 @@ def edit_distance_dp_comp(s: str, t: str) -> int:
# State transition: first row
for j in range(1, m + 1):
dp[j] = j
# State transition: the rest of the rows
# State transition: 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
# State transition: 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
# If two characters are equal, skip both characters
dp[j] = leftup
else:
# The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1
# Minimum edit steps = minimum edit steps of insert, delete, 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]
leftup = temp # Update for next round's dp[i-1, j-1]
return dp[m]
@@ -105,19 +105,19 @@ if __name__ == "__main__":
t = "pack"
n, m = len(s), len(t)
# Brute force search
# 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}")
print(f"Changing {s} to {t} requires a minimum of {res} edits")
# Memoized search
# Memoization 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}")
print(f"Changing {s} to {t} requires a minimum of {res} edits")
# Dynamic programming
res = edit_distance_dp(s, t)
print(f"To change {s} to {t}, the minimum number of edits required is {res}")
print(f"Changing {s} to {t} requires a minimum of {res} edits")
# 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}")
print(f"Changing {s} to {t} requires a minimum of {res} edits")

View File

@@ -6,43 +6,43 @@ 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
"""0-1 knapsack: Brute-force search"""
# If all items have been selected or 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 exceeds knapsack capacity, can only choose not to put it in
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 the larger 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
"""0-1 knapsack: Memoization search"""
# If all items have been selected or knapsack has no remaining capacity, return value 0
if i == 0 or c == 0:
return 0
# If there is a record, return it
# If there's a record, return it directly
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 exceeds knapsack capacity, can only choose not to put it in
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
# Record and return the larger 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"""
"""0-1 knapsack: Dynamic programming"""
n = len(wgt)
# Initialize dp table
dp = [[0] * (cap + 1) for _ in range(n + 1)]
@@ -50,16 +50,16 @@ def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int:
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
# If exceeds knapsack capacity, don't select item i
dp[i][c] = dp[i - 1][c]
else:
# The greater value between not choosing and choosing item i
# The larger value between not selecting and selecting item i
dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1])
return dp[n][cap]
def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int:
"""0-1 Knapsack: Space-optimized dynamic programming"""
"""0-1 knapsack: Space-optimized dynamic programming"""
n = len(wgt)
# Initialize dp table
dp = [0] * (cap + 1)
@@ -68,10 +68,10 @@ def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int:
# 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
# If exceeds knapsack capacity, don't select item i
dp[c] = dp[c]
else:
# The greater value between not choosing and choosing item i
# The larger value between not selecting and selecting item i
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1])
return dp[cap]
@@ -83,19 +83,19 @@ if __name__ == "__main__":
cap = 50
n = len(wgt)
# Brute force search
# Brute-force search
res = knapsack_dfs(wgt, val, n, cap)
print(f"The maximum item value without exceeding knapsack capacity is {res}")
print(f"The maximum item value not exceeding knapsack capacity is {res}")
# Memoized search
# Memoization 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}")
print(f"The maximum item value not 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}")
print(f"The maximum item value not 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}")
print(f"The maximum item value not exceeding knapsack capacity is {res}")

View File

@@ -6,13 +6,13 @@ Author: krahets (krahets@163.com)
def min_cost_climbing_stairs_dp(cost: list[int]) -> int:
"""Climbing stairs with minimum cost: Dynamic programming"""
"""Minimum cost climbing stairs: Dynamic programming"""
n = len(cost) - 1
if n == 1 or n == 2:
return cost[n]
# Initialize dp table, used to store subproblem solutions
# Initialize dp table, used to store solutions to subproblems
dp = [0] * (n + 1)
# Initial state: preset the smallest subproblem solution
# Initial state: preset the solution to the smallest subproblem
dp[1], dp[2] = cost[1], cost[2]
# State transition: gradually solve larger subproblems from smaller ones
for i in range(3, n + 1):
@@ -21,7 +21,7 @@ def min_cost_climbing_stairs_dp(cost: list[int]) -> int:
def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int:
"""Climbing stairs with minimum cost: Space-optimized dynamic programming"""
"""Minimum cost climbing stairs: Space-optimized dynamic programming"""
n = len(cost) - 1
if n == 1 or n == 2:
return cost[n]
@@ -34,10 +34,10 @@ def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int:
"""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}")
print(f"Input stair cost list is {cost}")
res = min_cost_climbing_stairs_dp(cost)
print(f"Minimum cost to climb the stairs {res}")
print(f"The minimum cost to finish climbing the stairs is {res}")
res = min_cost_climbing_stairs_dp_comp(cost)
print(f"Minimum cost to climb the stairs {res}")
print(f"The minimum cost to finish climbing the stairs is {res}")

View File

@@ -8,37 +8,37 @@ from math import inf
def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int:
"""Minimum path sum: Brute force search"""
"""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 row or column index is out of bounds, return +∞ 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)
# Calculate the minimum path cost from 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 the minimum path cost from 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"""
"""Minimum path sum: Memoization 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 row or column index is out of bounds, return +∞ cost
if i < 0 or j < 0:
return inf
# If there is a record, return it
# If there's a record, return it directly
if mem[i][j] != -1:
return mem[i][j]
# The minimum path cost from the left and top cells
# Minimum path cost for left and upper 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)
# Record and return the minimum path cost from top-left to (i, j)
mem[i][j] = min(left, up) + grid[i][j]
return mem[i][j]
@@ -55,7 +55,7 @@ def min_path_sum_dp(grid: list[list[int]]) -> int:
# 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
# State transition: 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]
@@ -71,11 +71,11 @@ def min_path_sum_dp_comp(grid: list[list[int]]) -> int:
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
# State transition: 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
# State transition: 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]
@@ -86,19 +86,19 @@ 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
# 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}")
print(f"The minimum path sum from top-left to bottom-right is {res}")
# Memoized search
# Memoization 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}")
print(f"The minimum path sum from top-left to bottom-right 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}")
print(f"The minimum path sum from top-left to bottom-right 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}")
print(f"The minimum path sum from top-left to bottom-right is {res}")

View File

@@ -6,7 +6,7 @@ Author: krahets (krahets@163.com)
def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int:
"""Complete knapsack: Dynamic programming"""
"""Unbounded knapsack: Dynamic programming"""
n = len(wgt)
# Initialize dp table
dp = [[0] * (cap + 1) for _ in range(n + 1)]
@@ -14,28 +14,28 @@ def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int:
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
# If exceeds knapsack capacity, don't select item i
dp[i][c] = dp[i - 1][c]
else:
# The greater value between not choosing and choosing item i
# The larger value between not selecting and selecting 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"""
"""Unbounded 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
# Traverse in forward order
for c in range(1, cap + 1):
if wgt[i - 1] > c:
# If exceeding the knapsack capacity, do not choose item i
# If exceeds knapsack capacity, don't select item i
dp[c] = dp[c]
else:
# The greater value between not choosing and choosing item i
# The larger value between not selecting and selecting item i
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1])
return dp[cap]
@@ -48,8 +48,8 @@ if __name__ == "__main__":
# Dynamic programming
res = unbounded_knapsack_dp(wgt, val, cap)
print(f"The maximum item value without exceeding knapsack capacity is {res}")
print(f"The maximum item value not 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}")
print(f"The maximum item value not exceeding knapsack capacity is {res}")

View File

@@ -48,22 +48,22 @@ class GraphAdjList:
"""Add vertex"""
if vet in self.adj_list:
return
# Add a new linked list to the adjacency list
# Add a new linked list in 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
# Remove the linked list corresponding to vertex vet in the adjacency list
self.adj_list.pop(vet)
# Traverse other vertices' linked lists, removing all edges containing vet
# Traverse the linked lists of other vertices and remove 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"""
print("Adjacency list =")
for vertex in self.adj_list:
tmp = [v.val for v in self.adj_list[vertex]]
@@ -87,13 +87,13 @@ if __name__ == "__main__":
graph.print()
# Add edge
# Vertices 1, 2 i.e., v[0], v[2]
# Vertices 1, 2 are 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]
# Vertices 1, 3 are v[0], v[1]
graph.remove_edge(v[0], v[1])
print("\nAfter removing edge 1-3, the graph is")
graph.print()
@@ -105,7 +105,7 @@ if __name__ == "__main__":
graph.print()
# Remove vertex
# Vertex 3 i.e., v[1]
# Vertex 3 is v[1]
graph.remove_vertex(v[1])
print("\nAfter removing vertex 3, the graph is")
graph.print()

View File

@@ -16,15 +16,15 @@ class GraphAdjMat:
def __init__(self, vertices: list[int], edges: list[list[int]]):
"""Constructor"""
# Vertex list, elements represent "vertex value", index represents "vertex index"
# Vertex list, where the element represents the "vertex value" and the index represents the "vertex index"
self.vertices: list[int] = []
# Adjacency matrix, row and column indices correspond to "vertex index"
# Adjacency matrix, where the row and column indices correspond to the "vertex index"
self.adj_mat: list[list[int]] = []
# Add vertex
# Add vertices
for val in vertices:
self.add_vertex(val)
# Add edge
# Edges elements represent vertex indices
# Add edges
# Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices
for e in edges:
self.add_edge(e[0], e[1])
@@ -35,7 +35,7 @@ class GraphAdjMat:
def add_vertex(self, val: int):
"""Add vertex"""
n = self.size()
# Add new vertex value to the vertex list
# Add the value of the new vertex to the vertex list
self.vertices.append(val)
# Add a row to the adjacency matrix
new_row = [0] * n
@@ -48,27 +48,27 @@ class GraphAdjMat:
"""Remove vertex"""
if index >= self.size():
raise IndexError()
# Remove vertex at `index` from the vertex list
# Remove the vertex at index from the vertex list
self.vertices.pop(index)
# Remove the row at `index` from the adjacency matrix
# Remove the row at index from the adjacency matrix
self.adj_mat.pop(index)
# Remove the column at `index` from the adjacency matrix
# 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
# Parameters i, j correspond to the 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)
# In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (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
# Parameters i, j correspond to the 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()
@@ -85,7 +85,7 @@ class GraphAdjMat:
"""Driver Code"""
if __name__ == "__main__":
# Initialize undirected graph
# Edges elements represent vertex indices
# Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices
vertices = [1, 3, 2, 5, 4]
edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]]
graph = GraphAdjMat(vertices, edges)
@@ -93,13 +93,13 @@ if __name__ == "__main__":
graph.print()
# Add edge
# Indices of vertices 1, 2 are 0, 2 respectively
# Vertices 1, 2 have indices 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
# Vertices 1, 3 have indices 0, 1 respectively
graph.remove_edge(0, 1)
print("\nAfter removing edge 1-3, the graph is")
graph.print()
@@ -110,7 +110,7 @@ if __name__ == "__main__":
graph.print()
# Remove vertex
# Index of vertex 3 is 1
# Vertex 3 has index 1
graph.remove_vertex(1)
print("\nAfter removing vertex 3, the graph is")
graph.print()

View File

@@ -15,24 +15,24 @@ 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
# Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex
# Vertex traversal sequence
res = []
# Hash set, used to record visited vertices
# Hash set for recording vertices that have been visited
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
vet = que.popleft() # Dequeue the front vertex
res.append(vet) # Record visited vertex
# Traverse all adjacent vertices of that vertex
# Traverse all adjacent vertices of this vertex
for adj_vet in graph.adj_list[vet]:
if adj_vet in visited:
continue # Skip already visited vertices
continue # Skip vertices that have been visited
que.append(adj_vet) # Only enqueue unvisited vertices
visited.add(adj_vet) # Mark the vertex as visited
# Return the vertex traversal sequence
visited.add(adj_vet) # Mark this vertex as visited
# Return vertex traversal sequence
return res

View File

@@ -15,21 +15,21 @@ 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
visited.add(vet) # Mark this vertex as visited
# Traverse all adjacent vertices of this vertex
for adjVet in graph.adj_list[vet]:
if adjVet in visited:
continue # Skip already visited vertices
continue # Skip vertices that have been visited
# 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
# Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex
# Vertex traversal sequence
res = []
# Hash set, used to record visited vertices
# Hash set for recording vertices that have been visited
visited = set[Vertex]()
dfs(graph, visited, res, start_vet)
return res

View File

@@ -6,13 +6,13 @@ Author: krahets (krahets@163.com)
def coin_change_greedy(coins: list[int], amt: int) -> int:
"""Coin change: Greedy"""
# Assume coins list is ordered
"""Coin change: Greedy algorithm"""
# Assume coins list is sorted
i = len(coins) - 1
count = 0
# Loop for greedy selection until no remaining amount
# Loop to make greedy choices until no remaining amount
while amt > 0:
# Find the smallest coin close to and less than the remaining amount
# Find the coin that is less than and closest to the remaining amount
while i > 0 and coins[i] > amt:
i -= 1
# Choose coins[i]
@@ -24,25 +24,25 @@ def coin_change_greedy(coins: list[int], amt: int) -> int:
"""Driver Code"""
if __name__ == "__main__":
# Greedy: can ensure finding a global optimal solution
# Greedy algorithm: Can guarantee finding the 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}")
print(f"The minimum number of coins needed to make {amt} is {res}")
# Greedy: cannot ensure finding a global optimal solution
# Greedy algorithm: Cannot guarantee finding the 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")
print(f"The minimum number of coins needed to make {amt} is {res}")
print(f"Actually the minimum number needed is 3, i.e., 20 + 20 + 20")
# Greedy: cannot ensure finding a global optimal solution
# Greedy algorithm: Cannot guarantee finding the 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")
print(f"The minimum number of coins needed to make {amt} is {res}")
print(f"Actually the minimum number needed is 2, i.e., 49 + 49")

View File

@@ -14,8 +14,8 @@ class Item:
def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int:
"""Fractional knapsack: Greedy"""
# Create an item list, containing two properties: weight, value
"""Fractional knapsack: Greedy algorithm"""
# Create item list with two attributes: 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)
@@ -23,13 +23,13 @@ def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int:
res = 0
for item in items:
if item.w <= cap:
# If the remaining capacity is sufficient, put the entire item into the knapsack
# If remaining capacity is sufficient, put the entire current 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
# If remaining capacity is insufficient, put part of the current item into the knapsack
res += (item.v / item.w) * cap
# No remaining capacity left, thus break the loop
# No remaining capacity, so break out of the loop
break
return res

View File

@@ -6,14 +6,14 @@ 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
"""Max capacity: Greedy algorithm"""
# Initialize i, j to be at both ends of the array
i, j = 0, len(ht) - 1
# Initial maximum capacity is 0
# Initial max capacity is 0
res = 0
# Loop for greedy selection until the two boards meet
while i < j:
# Update maximum capacity
# Update max capacity
cap = min(ht[i], ht[j]) * (j - i)
res = max(res, cap)
# Move the shorter board inward
@@ -30,4 +30,4 @@ if __name__ == "__main__":
# Greedy algorithm
res = max_capacity(ht)
print(f"Maximum capacity is {res}")
print(f"Max capacity is {res}")

View File

@@ -8,14 +8,14 @@ import math
def max_product_cutting(n: int) -> int:
"""Maximum product of cutting: Greedy"""
"""Max product cutting: Greedy algorithm"""
# 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
# Greedily cut out 3, 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
# When the remainder is 1, convert a pair of 1 * 3 to 2 * 2
return int(math.pow(3, a - 1)) * 2 * 2
if b == 2:
# When the remainder is 2, do nothing
@@ -30,4 +30,4 @@ if __name__ == "__main__":
# Greedy algorithm
res = max_product_cutting(n)
print(f"Maximum product of cutting is {res}")
print(f"Max product cutting is {res}")

View File

@@ -18,7 +18,7 @@ class ArrayHashMap:
def __init__(self):
"""Constructor"""
# Initialize an array, containing 100 buckets
# Initialize array with 100 buckets
self.buckets: list[Pair | None] = [None] * 100
def hash_func(self, key: int) -> int:
@@ -26,7 +26,7 @@ class ArrayHashMap:
index = key % 100
return index
def get(self, key: int) -> str:
def get(self, key: int) -> str | None:
"""Query operation"""
index: int = self.hash_func(key)
pair: Pair = self.buckets[index]
@@ -35,7 +35,7 @@ class ArrayHashMap:
return pair.val
def put(self, key: int, val: str):
"""Add operation"""
"""Add and update operation"""
pair = Pair(key, val)
index: int = self.hash_func(key)
self.buckets[index] = pair
@@ -43,7 +43,7 @@ class ArrayHashMap:
def remove(self, key: int):
"""Remove operation"""
index: int = self.hash_func(key)
# Set to None, representing removal
# Set to None to represent removal
self.buckets[index] = None
def entry_set(self) -> list[Pair]:
@@ -84,18 +84,18 @@ if __name__ == "__main__":
# 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")
hmap.put(12836, "Xiao Ha")
hmap.put(15937, "Xiao Luo")
hmap.put(16750, "Xiao Suan")
hmap.put(13276, "Xiao Fa")
hmap.put(10583, "Xiao Ya")
print("\nAfter adding, the hash table is\nKey -> Value")
hmap.print()
# Query operation
# Enter key to the hash table, get value
# Input key into the hash table to get value
name = hmap.get(15937)
print("\nEnter student ID 15937, found name " + name)
print("\nInput student ID 15937, found name " + name)
# Remove operation
# Remove key-value pair (key, value) from the hash table
@@ -108,10 +108,10 @@ if __name__ == "__main__":
for pair in hmap.entry_set():
print(pair.key, "->", pair.val)
print("\nIndividually traverse keys Key")
print("\nTraverse keys only Key")
for key in hmap.key_set():
print(key)
print("\nIndividually traverse values Value")
print("\nTraverse values only Value")
for val in hmap.value_set():
print(val)

View File

@@ -14,24 +14,24 @@ from modules import ListNode
if __name__ == "__main__":
num = 3
hash_num = hash(num)
print(f"Integer {num}'s hash value is {hash_num}")
print(f"Hash value of integer {num} is {hash_num}")
bol = True
hash_bol = hash(bol)
print(f"Boolean {bol}'s hash value is {hash_bol}")
print(f"Hash value of boolean {bol} is {hash_bol}")
dec = 3.14159
hash_dec = hash(dec)
print(f"Decimal {dec}'s hash value is {hash_dec}")
print(f"Hash value of decimal {dec} is {hash_dec}")
str = "Hello algorithm"
str = "Hello algo"
hash_str = hash(str)
print(f"String {str}'s hash value is {hash_str}")
print(f"Hash value of string {str} is {hash_str}")
tup = (12836, "Ha")
tup = (12836, "Xiao Ha")
hash_tup = hash(tup)
print(f"Tuple {tup}'s hash value is {hash(hash_tup)}")
print(f"Hash value of tuple {tup} is {hash(hash_tup)}")
obj = ListNode(0)
hash_obj = hash(obj)
print(f"Node object {obj}'s hash value is {hash_obj}")
print(f"Hash value of node object {obj} is {hash_obj}")

View File

@@ -17,18 +17,18 @@ if __name__ == "__main__":
# 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"
hmap[12836] = "Xiao Ha"
hmap[15937] = "Xiao Luo"
hmap[16750] = "Xiao Suan"
hmap[13276] = "Xiao Fa"
hmap[10583] = "Xiao Ya"
print("\nAfter adding, the hash table is\nKey -> Value")
print_dict(hmap)
# Query operation
# Enter key to the hash table, get value
# Input key into the hash table to get value
name: str = hmap[15937]
print("\nEnter student ID 15937, found name " + name)
print("\nInput student ID 15937, found name " + name)
# Remove operation
# Remove key-value pair (key, value) from the hash table
@@ -41,10 +41,10 @@ if __name__ == "__main__":
for key, value in hmap.items():
print(key, "->", value)
print("\nIndividually traverse keys Key")
print("\nTraverse keys only Key")
for key in hmap.keys():
print(key)
print("\nIndividually traverse values Value")
print("\nTraverse values only Value")
for val in hmap.values():
print(val)

View File

@@ -12,7 +12,7 @@ from chapter_hashing.array_hash_map import Pair
class HashMapChaining:
"""Chained address hash table"""
"""Hash table with separate chaining"""
def __init__(self):
"""Constructor"""
@@ -34,26 +34,26 @@ class HashMapChaining:
"""Query operation"""
index = self.hash_func(key)
bucket = self.buckets[index]
# Traverse the bucket, if the key is found, return the corresponding val
# Traverse bucket, if key is found, return corresponding val
for pair in bucket:
if pair.key == key:
return pair.val
# If the key is not found, return None
# If 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
# When load factor exceeds 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
# Traverse bucket, if specified key is encountered, update 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
# If key does not exist, append key-value pair to the end
pair = Pair(key, val)
bucket.append(pair)
self.size += 1
@@ -62,7 +62,7 @@ class HashMapChaining:
"""Remove operation"""
index = self.hash_func(key)
bucket = self.buckets[index]
# Traverse the bucket, remove the key-value pair from it
# Traverse bucket and remove key-value pair from it
for pair in bucket:
if pair.key == key:
bucket.remove(pair)
@@ -70,14 +70,14 @@ class HashMapChaining:
break
def extend(self):
"""Extend hash table"""
"""Expand hash table"""
# Temporarily store the original hash table
buckets = self.buckets
# Initialize the extended new hash table
# Initialize expanded 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
# Move key-value pairs from original hash table to new hash table
for bucket in buckets:
for pair in bucket:
self.put(pair.key, pair.val)
@@ -98,18 +98,18 @@ if __name__ == "__main__":
# 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")
hashmap.put(12836, "Xiao Ha")
hashmap.put(15937, "Xiao Luo")
hashmap.put(16750, "Xiao Suan")
hashmap.put(13276, "Xiao Fa")
hashmap.put(10583, "Xiao Ya")
print("\nAfter adding, the hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]")
hashmap.print()
# Query operation
# Enter key to the hash table, get value
# Input key into the hash table to get value
name = hashmap.get(13276)
print("\nEnter student ID 13276, found name " + name)
print("\nInput student ID 13276, found name " + name)
# Remove operation
# Remove key-value pair (key, value) from the hash table

View File

@@ -12,7 +12,7 @@ from chapter_hashing.array_hash_map import Pair
class HashMapOpenAddressing:
"""Open addressing hash table"""
"""Hash table with open addressing"""
def __init__(self):
"""Constructor"""
@@ -21,7 +21,7 @@ class HashMapOpenAddressing:
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
self.TOMBSTONE = Pair(-1, "-1") # Removal marker
def hash_func(self, key: int) -> int:
"""Hash function"""
@@ -32,70 +32,70 @@ class HashMapOpenAddressing:
return self.size / self.capacity
def find_bucket(self, key: int) -> int:
"""Search for the bucket index corresponding to key"""
"""Search for 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 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 a removal marker was encountered before, 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
# Record the first removal marker encountered
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
# Calculate bucket index, wrap around to the head if past the tail
index = (index + 1) % self.capacity
# If the key does not exist, return the index of the insertion point
# If key does not exist, return the index for insertion
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
# Search for bucket index corresponding to key
index = self.find_bucket(key)
# If the key-value pair is found, return the corresponding val
# If key-value pair is found, return 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
# If 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
# When load factor exceeds threshold, perform expansion
if self.load_factor() > self.load_thres:
self.extend()
# Search for the bucket index corresponding to key
# Search for bucket index corresponding to key
index = self.find_bucket(key)
# If the key-value pair is found, overwrite val and return
# If 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
# If 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
# Search for bucket index corresponding to key
index = self.find_bucket(key)
# If the key-value pair is found, cover it with a removal mark
# If key-value pair is found, overwrite it with removal marker
if self.buckets[index] not in [None, self.TOMBSTONE]:
self.buckets[index] = self.TOMBSTONE
self.size -= 1
def extend(self):
"""Extend hash table"""
"""Expand hash table"""
# Temporarily store the original hash table
buckets_tmp = self.buckets
# Initialize the extended new hash table
# Initialize expanded 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
# Move key-value pairs from original hash table to new hash table
for pair in buckets_tmp:
if pair not in [None, self.TOMBSTONE]:
self.put(pair.key, pair.val)
@@ -118,18 +118,18 @@ if __name__ == "__main__":
# 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")
hashmap.put(12836, "Xiao Ha")
hashmap.put(15937, "Xiao Luo")
hashmap.put(16750, "Xiao Suan")
hashmap.put(13276, "Xiao Fa")
hashmap.put(10583, "Xiao Ya")
print("\nAfter adding, the hash table is\nKey -> Value")
hashmap.print()
# Query operation
# Enter key to the hash table, get value val
# Input key into the hash table to get val
name = hashmap.get(13276)
print("\nEnter student ID 13276, found name " + name)
print("\nInput student ID 13276, found name " + name)
# Remove operation
# Remove key-value pair (key, val) from the hash table

View File

@@ -43,7 +43,7 @@ def rot_hash(key: str) -> int:
"""Driver Code"""
if __name__ == "__main__":
key = "Hello algorithm"
key = "Hello algo"
hash = add_hash(key)
print(f"Additive hash value is {hash}")

View File

@@ -14,41 +14,41 @@ 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")
heapq.heappush(heap, flag * val) # Element enters heap
print(f"\nAfter element {val} enters 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")
val = flag * heapq.heappop(heap) # Top element exits heap
print(f"\nAfter top element {val} exits heap")
print_heap([flag * val for val in heap])
"""Driver Code"""
if __name__ == "__main__":
# Initialize min-heap
# Initialize min heap
min_heap, flag = [], 1
# Initialize max-heap
# 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
print("\nThe following test cases are for max heap")
# Python's heapq module implements min heap by default
# Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap
# In this example, flag = 1 corresponds to min heap, flag = -1 corresponds to max heap
# Push the element into heap
# Elements enter 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
# Get top element
peek: int = flag * max_heap[0]
print(f"\nHeap top element is {peek}")
print(f"\nTop element is {peek}")
# Pop the element at the heap top
# Top element exits heap
test_pop(max_heap, flag)
test_pop(max_heap, flag)
test_pop(max_heap, flag)
@@ -59,13 +59,13 @@ if __name__ == "__main__":
size: int = len(max_heap)
print(f"\nNumber of heap elements is {size}")
# Determine if heap is empty
# Check if heap is empty
is_empty: bool = not max_heap
print(f"\nIs the heap empty {is_empty}")
print(f"\nIs heap empty {is_empty}")
# Enter list and build heap
# Input 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("\nAfter inputting list and building min heap")
print_heap(min_heap)

View File

@@ -12,13 +12,13 @@ from modules import print_heap
class MaxHeap:
"""Max-heap"""
"""Max heap"""
def __init__(self, nums: list[int]):
"""Constructor, build heap based on input list"""
# Add all list elements into the heap
# Add list elements to heap as is
self.max_heap = nums
# Heapify all nodes except leaves
# Heapify all nodes except leaf nodes
for i in range(self.parent(self.size() - 1), -1, -1):
self.sift_down(i)
@@ -32,7 +32,7 @@ class MaxHeap:
def parent(self, i: int) -> int:
"""Get index of parent node"""
return (i - 1) // 2 # Integer division down
return (i - 1) // 2 # Floor division
def swap(self, i: int, j: int):
"""Swap elements"""
@@ -43,62 +43,62 @@ class MaxHeap:
return len(self.max_heap)
def is_empty(self) -> bool:
"""Determine if heap is empty"""
"""Check if heap is empty"""
return self.size() == 0
def peek(self) -> int:
"""Access heap top element"""
"""Access top element"""
return self.max_heap[0]
def push(self, val: int):
"""Push the element into heap"""
"""Element enters 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"""
"""Starting from node i, heapify 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
# When "crossing root node" or "node needs no repair", end heapify
if p < 0 or self.max_heap[i] <= self.max_heap[p]:
break
# Swap two nodes
self.swap(i, p)
# Loop upwards heapification
# Loop upward heapify
i = p
def pop(self) -> int:
"""Element exits heap"""
# Empty handling
# Handle empty case
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)
# Swap root node with rightmost leaf node (swap first element with last element)
self.swap(0, self.size() - 1)
# Remove node
# Delete node
val = self.max_heap.pop()
# Heapify from top to bottom
self.sift_down(0)
# Return heap top element
# Return top element
return val
def sift_down(self, i: int):
"""Start heapifying node i, from top to bottom"""
"""Starting from node i, heapify from top to bottom"""
while True:
# Determine the largest node among i, l, r, noted as ma
# Find node with largest value among i, l, r, denoted 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 node i is largest or indices l, r are out of bounds, no need to continue heapify, break
if ma == i:
break
# Swap two nodes
self.swap(i, ma)
# Loop downwards heapification
# Loop downward heapify
i = ma
def print(self):
@@ -108,30 +108,30 @@ class MaxHeap:
"""Driver Code"""
if __name__ == "__main__":
# Initialize max-heap
# Initialize max heap
max_heap = MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2])
print("\nEnter list and build heap")
print("\nAfter inputting list and building heap")
max_heap.print()
# Access heap top element
# Get top element
peek = max_heap.peek()
print(f"\nHeap top element is {peek}")
print(f"\nTop element is {peek}")
# Push the element into heap
# Element enters heap
val = 7
max_heap.push(val)
print(f"\nElement {val} after pushed into heap")
print(f"\nAfter element {val} enters heap")
max_heap.print()
# Pop the element at the heap top
# Top element exits heap
peek = max_heap.pop()
print(f"\nHeap top element {peek} after exiting heap")
print(f"\nAfter top element {peek} exits heap")
max_heap.print()
# Get heap size
size = max_heap.size()
print(f"\nNumber of heap elements is {size}")
# Determine if heap is empty
# Check if heap is empty
is_empty = max_heap.is_empty()
print(f"\nIs the heap empty {is_empty}")
print(f"\nIs heap empty {is_empty}")

View File

@@ -14,15 +14,15 @@ 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
"""Find the largest k elements in array based on heap"""
# Initialize min heap
heap = []
# Enter the first k elements of the array into the heap
# Enter the first k elements of array into heap
for i in range(k):
heapq.heappush(heap, nums[i])
# From the k+1th element, keep the heap length as k
# Starting from the (k+1)th element, maintain 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 current element is greater than top element, top element exits heap, current element enters heap
if nums[i] > heap[0]:
heapq.heappop(heap)
heapq.heappush(heap, nums[i])

View File

@@ -6,36 +6,36 @@ 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
"""Binary search (closed interval)"""
# Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array
i, j = 0, len(nums) - 1
# Loop until the search interval is empty (when i > j, it is empty)
# Loop, exit when the search interval is empty (empty when i > j)
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 - i) // 2 # Calculate midpoint index m
# In theory, Python numbers can be infinitely large (depending on memory size), no need to consider large number overflow
m = (i + j) // 2 # Calculate midpoint index m
if nums[m] < target:
i = m + 1 # This situation indicates that target is in the interval [m+1, j]
i = m + 1 # This means 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]
j = m - 1 # This means 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
return m # Found the target element, return its index
return -1 # Target element not found, return -1
def binary_search_lcro(nums: list[int], target: int) -> int:
"""Binary search (left closed right open interval)"""
# Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively
"""Binary search (left-closed right-open interval)"""
# Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1
i, j = 0, len(nums)
# Loop until the search interval is empty (when i = j, it is empty)
# Loop, exit when the search interval is empty (empty when i = j)
while i < j:
m = i + (j - i) // 2 # Calculate midpoint index m
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)
i = m + 1 # This means 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)
j = m # This means 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
return m # Found the target element, return its index
return -1 # Target element not found, return -1
"""Driver Code"""
@@ -43,10 +43,10 @@ if __name__ == "__main__":
target = 6
nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
# Binary search (double closed interval)
# Binary search (closed interval)
index = binary_search(nums, target)
print("Index of target element 6 =", index)
# Binary search (left closed right open interval)
# Binary search (left-closed right-open interval)
index = binary_search_lcro(nums, target)
print("Index of target element 6 =", index)

View File

@@ -15,7 +15,7 @@ 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
# Target not found, return -1
if i == len(nums) or nums[i] != target:
return -1
# Found target, return index i
@@ -28,7 +28,7 @@ def binary_search_right_edge(nums: list[int], target: int) -> int:
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
# Target not found, return -1
if j == -1 or nums[j] != target:
return -1
# Found target, return index j
@@ -41,9 +41,9 @@ if __name__ == "__main__":
nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]
print(f"\nArray nums = {nums}")
# Binary search for left and right boundaries
# Binary search for left boundary and right boundary
for target in [6, 7]:
index = binary_search_left_edge(nums, target)
print(f"The index of the leftmost element {target} is {index}")
print(f"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}")
print(f"Index of the rightmost element {target} is {index}")

View File

@@ -7,30 +7,30 @@ 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]
i, j = 0, len(nums) - 1 # Initialize closed interval [0, n-1]
while i <= j:
m = i + (j - i) // 2 # Calculate midpoint index m
m = (i + j) // 2 # Calculate midpoint index m
if nums[m] < target:
i = m + 1 # Target is in interval [m+1, j]
i = m + 1 # target is in the interval [m+1, j]
elif nums[m] > target:
j = m - 1 # Target is in interval [i, m-1]
j = m - 1 # target is in the interval [i, m-1]
else:
return m # Found target, return insertion point m
# Did not find target, return insertion point i
# Target not found, return insertion point i
return i
def binary_search_insertion(nums: list[int], target: int) -> int:
"""Binary search for insertion point (with duplicate elements)"""
i, j = 0, len(nums) - 1 # Initialize double closed interval [0, n-1]
i, j = 0, len(nums) - 1 # Initialize closed interval [0, n-1]
while i <= j:
m = i + (j - i) // 2 # Calculate midpoint index m
m = (i + j) // 2 # Calculate midpoint index m
if nums[m] < target:
i = m + 1 # Target is in interval [m+1, j]
i = m + 1 # target is in the interval [m+1, j]
elif nums[m] > target:
j = m - 1 # Target is in interval [i, m-1]
j = m - 1 # target is in the interval [i, m-1]
else:
j = m - 1 # First element less than target is in interval [i, m-1]
j = m - 1 # The first element less than target is in the interval [i, m-1]
# Return insertion point i
return i
@@ -43,7 +43,7 @@ if __name__ == "__main__":
# 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}")
print(f"Index of insertion point for element {target} is {index}")
# Array with duplicate elements
nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]
@@ -51,4 +51,4 @@ if __name__ == "__main__":
# 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}")
print(f"Index of insertion point for element {target} is {index}")

View File

@@ -14,7 +14,7 @@ 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
# If this key does not exist in the hash table, return -1
return hmap.get(target, -1)
@@ -23,7 +23,7 @@ def hashing_search_linkedlist(
) -> 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
# If this key does not exist in the hash table, return None
return hmap.get(target, None)
@@ -48,4 +48,4 @@ if __name__ == "__main__":
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)
print("The corresponding node object for target node value 3 is", node)

View File

@@ -13,21 +13,21 @@ from modules import ListNode, list_to_linked_list
def linear_search_array(nums: list[int], target: int) -> int:
"""Linear search (array)"""
# Traverse array
# Traverse the array
for i in range(len(nums)):
if nums[i] == target: # Found the target element, thus return its index
if nums[i] == target: # Found the target element, return its index
return i
return -1 # Did not find the target element, thus return -1
return -1 # Target element not found, return -1
def linear_search_linkedlist(head: ListNode, target: int) -> ListNode | None:
"""Linear search (linked list)"""
# Traverse the list
# Traverse the linked 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
return None # Target node not found, return None
"""Driver Code"""
@@ -42,4 +42,4 @@ if __name__ == "__main__":
# 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)
print("The corresponding node object for target node value 3 is", node)

View File

@@ -6,8 +6,8 @@ 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)
"""Method 1: Brute force enumeration"""
# Two nested loops, 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:
@@ -16,10 +16,10 @@ def two_sum_brute_force(nums: list[int], target: int) -> list[int]:
def two_sum_hash_table(nums: list[int], target: int) -> list[int]:
"""Method two: Auxiliary hash table"""
"""Method 2: Auxiliary hash table"""
# Auxiliary hash table, space complexity is O(n)
dic = {}
# Single-layer loop, time complexity is O(n)
# Single loop, time complexity is O(n)
for i in range(len(nums)):
if target - nums[i] in dic:
return [dic[target - nums[i]], i]
@@ -34,9 +34,9 @@ if __name__ == "__main__":
target = 13
# ====== Driver Code ======
# Method one
# Method 1
res: list[int] = two_sum_brute_force(nums, target)
print("Method one res =", res)
# Method two
print("Method 1 res =", res)
# Method 2
res: list[int] = two_sum_hash_table(nums, target)
print("Method two res =", res)
print("Method 2 res =", res)

View File

@@ -8,9 +8,9 @@ Author: timi (xisunyy@163.com)
def bubble_sort(nums: list[int]):
"""Bubble sort"""
n = len(nums)
# Outer loop: unsorted range is [0, i]
# Outer loop: unsorted interval 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
# Inner loop: swap the largest element in the unsorted interval [0, i] to the rightmost end of the interval
for j in range(i):
if nums[j] > nums[j + 1]:
# Swap nums[j] and nums[j + 1]
@@ -18,27 +18,27 @@ def bubble_sort(nums: list[int]):
def bubble_sort_with_flag(nums: list[int]):
"""Bubble sort (optimized with flag)"""
"""Bubble sort (flag optimization)"""
n = len(nums)
# Outer loop: unsorted range is [0, i]
# Outer loop: unsorted interval 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
# Inner loop: swap the largest element in the unsorted interval [0, i] to the rightmost end of the interval
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
flag = True # Record element swap
if not flag:
break # If no elements were swapped in this round of "bubbling", exit
break # No elements were swapped in this round of "bubbling", exit directly
"""Driver Code"""
if __name__ == "__main__":
nums = [4, 1, 3, 1, 5, 2]
bubble_sort(nums)
print("Bubble sort completed nums =", nums)
print("After bubble sort, nums =", nums)
nums1 = [4, 1, 3, 1, 5, 2]
bubble_sort_with_flag(nums1)
print("Bubble sort completed nums =", nums1)
print("After bubble sort, nums =", nums1)

View File

@@ -29,7 +29,7 @@ def bucket_sort(nums: list[float]):
if __name__ == "__main__":
# Assume input data is floating point, range [0, 1)
# Assume input data is floating point, interval [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)
print("After bucket sort, nums =", nums)

View File

@@ -12,7 +12,7 @@ def counting_sort_naive(nums: list[int]):
m = 0
for num in nums:
m = max(m, num)
# 2. Count the occurrence of each digit
# 2. Count the occurrence of each number
# counter[num] represents the occurrence of num
counter = [0] * (m + 1)
for num in nums:
@@ -30,7 +30,7 @@ def counting_sort(nums: list[int]):
# 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
# 2. Count the occurrence of each number
# counter[num] represents the occurrence of num
counter = [0] * (m + 1)
for num in nums:
@@ -57,8 +57,8 @@ 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}")
print(f"After counting sort (unable to sort objects), nums = {nums}")
nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]
counting_sort(nums1)
print(f"Counting sort completed nums1 = {nums1}")
print(f"After counting sort, nums1 = {nums1}")

View File

@@ -42,4 +42,4 @@ def heap_sort(nums: list[int]):
if __name__ == "__main__":
nums = [4, 1, 3, 1, 5, 2]
heap_sort(nums)
print("Heap sort completed nums =", nums)
print("After heap sort, nums =", nums)

View File

@@ -7,11 +7,11 @@ Author: timi (xisunyy@163.com)
def insertion_sort(nums: list[int]):
"""Insertion sort"""
# Outer loop: sorted range is [0, i-1]
# Outer loop: sorted interval 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]
# Inner loop: insert base into the correct position within the sorted interval [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
@@ -22,4 +22,4 @@ def insertion_sort(nums: list[int]):
if __name__ == "__main__":
nums = [4, 1, 3, 1, 5, 2]
insertion_sort(nums)
print("Insertion sort completed nums =", nums)
print("After insertion sort, nums =", nums)

View File

@@ -40,8 +40,8 @@ def merge_sort(nums: list[int], left: int, right: int):
# Termination condition
if left >= right:
return # Terminate recursion when subarray length is 1
# Partition stage
mid = left + (right - left) // 2 # Calculate midpoint
# Divide and conquer stage
mid = (left + right) // 2 # Calculate midpoint
merge_sort(nums, left, mid) # Recursively process the left subarray
merge_sort(nums, mid + 1, right) # Recursively process the right subarray
# Merge stage
@@ -52,4 +52,4 @@ def merge_sort(nums: list[int], left: int, right: int):
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)
print("After merge sort, nums =", nums)

View File

@@ -9,7 +9,7 @@ class QuickSort:
"""Quick sort class"""
def partition(self, nums: list[int], left: int, right: int) -> int:
"""Partition"""
"""Sentinel partition"""
# Use nums[left] as the pivot
i, j = left, right
while i < j:
@@ -28,7 +28,7 @@ class QuickSort:
# Terminate recursion when subarray length is 1
if left >= right:
return
# Partition
# Sentinel partition
pivot = self.partition(nums, left, right)
# Recursively process the left subarray and right subarray
self.quick_sort(nums, left, pivot - 1)
@@ -48,7 +48,7 @@ class QuickSortMedian:
return right
def partition(self, nums: list[int], left: int, right: int) -> int:
"""Partition (median of three)"""
"""Sentinel 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
@@ -71,7 +71,7 @@ class QuickSortMedian:
# Terminate recursion when subarray length is 1
if left >= right:
return
# Partition
# Sentinel partition
pivot = self.partition(nums, left, right)
# Recursively process the left subarray and right subarray
self.quick_sort(nums, left, pivot - 1)
@@ -79,10 +79,10 @@ class QuickSortMedian:
class QuickSortTailCall:
"""Quick sort class (tail recursion optimization)"""
"""Quick sort class (recursion depth optimization)"""
def partition(self, nums: list[int], left: int, right: int) -> int:
"""Partition"""
"""Sentinel partition"""
# Use nums[left] as the pivot
i, j = left, right
while i < j:
@@ -97,10 +97,10 @@ class QuickSortTailCall:
return i # Return the index of the pivot
def quick_sort(self, nums: list[int], left: int, right: int):
"""Quick sort (tail recursion optimization)"""
"""Quick sort (recursion depth optimization)"""
# Terminate when subarray length is 1
while left < right:
# Partition operation
# Sentinel partition operation
pivot = self.partition(nums, left, right)
# Perform quick sort on the shorter of the two subarrays
if pivot - left < right - pivot:
@@ -116,14 +116,14 @@ 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)
print("After quick sort, 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)
print("After quick sort (median pivot optimization), nums =", nums1)
# Quick sort (tail recursion optimization)
# Quick sort (recursion depth 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)
print("After quick sort (recursion depth optimization), nums =", nums2)

View File

@@ -66,4 +66,4 @@ if __name__ == "__main__":
63832996,
]
radix_sort(nums)
print("Radix sort completed nums =", nums)
print("After radix sort, nums =", nums)

View File

@@ -8,14 +8,14 @@ Author: krahets (krahets@163.com)
def selection_sort(nums: list[int]):
"""Selection sort"""
n = len(nums)
# Outer loop: unsorted range is [i, n-1]
# Outer loop: unsorted interval is [i, n-1]
for i in range(n - 1):
# Inner loop: find the smallest element within the unsorted range
# Inner loop: find the smallest element within the unsorted interval
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
# Swap the smallest element with the first element of the unsorted interval
nums[i], nums[k] = nums[k], nums[i]
@@ -23,4 +23,4 @@ def selection_sort(nums: list[int]):
if __name__ == "__main__":
nums = [4, 1, 3, 1, 5, 2]
selection_sort(nums)
print("Selection sort completed nums =", nums)
print("After selection sort, nums =", nums)

View File

@@ -6,7 +6,7 @@ Author: krahets (krahets@163.com)
class ArrayDeque:
"""Double-ended queue class based on circular array"""
"""Double-ended queue based on circular array implementation"""
def __init__(self, capacity: int):
"""Constructor"""
@@ -23,70 +23,70 @@ class ArrayDeque:
return self._size
def is_empty(self) -> bool:
"""Determine if the double-ended queue is empty"""
"""Check 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
# Use modulo operation to wrap the array head and tail together
# When i passes the tail of the array, return to the head
# When i passes the head of the array, return to the tail
return (i + self.capacity()) % self.capacity()
def push_first(self, num: int):
"""Front enqueue"""
"""Front of the queue 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
# Front pointer moves one position to the left
# Use modulo operation to wrap front around to the tail after passing the head of the array
self._front = self.index(self._front - 1)
# Add num to the front
# Add num to the front of the queue
self._nums[self._front] = num
self._size += 1
def push_last(self, num: int):
"""Rear enqueue"""
"""Rear of the queue enqueue"""
if self._size == self.capacity():
print("Double-ended queue is full")
return
# Calculate rear pointer, pointing to rear index + 1
# Calculate rear pointer, points to rear index + 1
rear = self.index(self._front + self._size)
# Add num to the rear
# Add num to the rear of the queue
self._nums[rear] = num
self._size += 1
def pop_first(self) -> int:
"""Front dequeue"""
"""Front of the queue dequeue"""
num = self.peek_first()
# Move front pointer one position backward
# Front pointer moves one position backward
self._front = self.index(self._front + 1)
self._size -= 1
return num
def pop_last(self) -> int:
"""Rear dequeue"""
"""Rear of the queue dequeue"""
num = self.peek_last()
self._size -= 1
return num
def peek_first(self) -> int:
"""Access front element"""
"""Access front of the queue 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"""
"""Access rear of the queue element"""
if self.is_empty():
raise IndexError("Double-ended queue is empty")
# Calculate rear element index
# Calculate tail 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
# Only convert list elements within the valid length range
res = []
for i in range(self._size):
res.append(self._nums[self.index(self._front + i)])
@@ -100,30 +100,30 @@ if __name__ == "__main__":
deque.push_last(3)
deque.push_last(2)
deque.push_last(5)
print("Double-ended queue deque =", deque.to_array())
print("double-ended queue deque =", deque.to_array())
# Access element
# Access elements
peek_first: int = deque.peek_first()
print("Front element peek_first =", peek_first)
print("Front of the queue element peek_first =", peek_first)
peek_last: int = deque.peek_last()
print("Rear element peek_last =", peek_last)
print("Rear of the queue element peek_last =", peek_last)
# Element enqueue
# Elements enqueue
deque.push_last(4)
print("Element 4 rear enqueued, deque =", deque.to_array())
print("Element 4 rear enqueue after deque =", deque.to_array())
deque.push_first(1)
print("Element 1 front enqueued, deque =", deque.to_array())
print("Element 1 front enqueue after deque =", deque.to_array())
# Element dequeue
# Elements dequeue
pop_last: int = deque.pop_last()
print("Rear dequeued element =", pop_last, ", deque after rear dequeue =", deque.to_array())
print("Rear dequeued element =", pop_last, ", rear dequeue after deque =", deque.to_array())
pop_first: int = deque.pop_first()
print("Front dequeued element =", pop_first, ", deque after front dequeue =", deque.to_array())
print("Front dequeued element =", pop_first, ", front dequeue after deque =", deque.to_array())
# Get the length of the double-ended queue
size: int = deque.size()
print("Double-ended queue length size =", size)
print("Length of the double-ended queue size =", size)
# Determine if the double-ended queue is empty
# Check if the double-ended queue is empty
is_empty: bool = deque.is_empty()
print("Is the double-ended queue empty =", is_empty)

View File

@@ -6,12 +6,12 @@ Author: Peng Chen (pengchzn@gmail.com)
class ArrayQueue:
"""Queue class based on circular array"""
"""Queue based on circular array implementation"""
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._front: int = 0 # Front pointer, points to the front of the queue element
self._size: int = 0 # Queue length
def capacity(self) -> int:
@@ -23,36 +23,36 @@ class ArrayQueue:
return self._size
def is_empty(self) -> bool:
"""Determine if the queue is empty"""
"""Check 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
# Calculate rear pointer, points to rear index + 1
# Use modulo operation to wrap rear around to the head after passing the tail of the array
rear: int = (self._front + self._size) % self.capacity()
# Add num to the rear
# Add num to the rear of the queue
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
# Front pointer moves one position backward, if it passes the tail, return to the head of the array
self._front = (self._front + 1) % self.capacity()
self._size -= 1
return num
def peek(self) -> int:
"""Access front element"""
"""Access front of the queue 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"""
"""Return list for printing"""
res = [0] * self.size()
j: int = self._front
for i in range(self.size()):
@@ -66,28 +66,28 @@ if __name__ == "__main__":
# Initialize queue
queue = ArrayQueue(10)
# Element enqueue
# Elements enqueue
queue.push(1)
queue.push(3)
queue.push(2)
queue.push(5)
queue.push(4)
print("Queue queue =", queue.to_list())
print("queue =", queue.to_list())
# Access front element
# Access front of the queue element
peek: int = queue.peek()
print("Front element peek =", peek)
print("Front of the queue element peek =", peek)
# Element dequeue
pop: int = queue.pop()
print("Dequeued element pop =", pop)
print("Queue after dequeue =", queue.to_list())
print("After dequeue queue =", queue.to_list())
# Get the length of the queue
size: int = queue.size()
print("Queue length size =", size)
print("Length of the queue size =", size)
# Determine if the queue is empty
# Check if the queue is empty
is_empty: bool = queue.is_empty()
print("Is the queue empty =", is_empty)
@@ -95,4 +95,4 @@ if __name__ == "__main__":
for i in range(10):
queue.push(i)
queue.pop()
print("In the ", i, "th round of enqueue + dequeue, queue = ", queue.to_list())
print("Round", i, "enqueue + dequeue after queue = ", queue.to_list())

View File

@@ -6,7 +6,7 @@ Author: Peng Chen (pengchzn@gmail.com)
class ArrayStack:
"""Stack class based on array"""
"""Stack based on array implementation"""
def __init__(self):
"""Constructor"""
@@ -17,7 +17,7 @@ class ArrayStack:
return len(self._stack)
def is_empty(self) -> bool:
"""Determine if the stack is empty"""
"""Check if the stack is empty"""
return self.size() == 0
def push(self, item: int):
@@ -31,13 +31,13 @@ class ArrayStack:
return self._stack.pop()
def peek(self) -> int:
"""Access stack top element"""
"""Access top of the stack 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 list for printing"""
return self._stack
@@ -46,27 +46,27 @@ if __name__ == "__main__":
# Initialize stack
stack = ArrayStack()
# Element push
# Elements push onto stack
stack.push(1)
stack.push(3)
stack.push(2)
stack.push(5)
stack.push(4)
print("Stack stack =", stack.to_list())
print("stack =", stack.to_list())
# Access stack top element
# Access top of the stack element
peek: int = stack.peek()
print("Stack top element peek =", peek)
print("Top of the stack element peek =", peek)
# Element pop
# Element pop from stack
pop: int = stack.pop()
print("Popped element pop =", pop)
print("Stack after pop =", stack.to_list())
print("After pop stack =", stack.to_list())
# Get the length of the stack
size: int = stack.size()
print("Stack length size =", size)
print("Length of the stack size =", size)
# Determine if it's empty
# Check if it is empty
is_empty: bool = stack.is_empty()
print("Is the stack empty =", is_empty)

View File

@@ -11,32 +11,32 @@ if __name__ == "__main__":
# Initialize double-ended queue
deq: deque[int] = deque()
# Element enqueue
deq.append(2) # Add to rear
# Elements enqueue
deq.append(2) # Add to the rear of the queue
deq.append(5)
deq.append(4)
deq.appendleft(3) # Add to front
deq.appendleft(3) # Add to the front of the queue
deq.appendleft(1)
print("Double-ended queue deque =", deq)
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)
# Access elements
front: int = deq[0] # Front of the queue element
print("Front of the queue element front =", front)
rear: int = deq[-1] # Rear of the queue element
print("Rear of the queue element rear =", rear)
# Element dequeue
pop_front: int = deq.popleft() # Front element dequeue
# Elements dequeue
pop_front: int = deq.popleft() # Front of the queue element dequeues
print("Front dequeued element pop_front =", pop_front)
print("Deque after front dequeue =", deq)
pop_rear: int = deq.pop() # Rear element dequeue
print("After front dequeue deque =", deq)
pop_rear: int = deq.pop() # Rear of the queue element dequeues
print("Rear dequeued element pop_rear =", pop_rear)
print("Deque after rear dequeue =", deq)
print("After rear dequeue deque =", deq)
# Get the length of the double-ended queue
size: int = len(deq)
print("Double-ended queue length size =", size)
print("Length of the double-ended queue size =", size)
# Determine if the double-ended queue is empty
# Check if the double-ended queue is empty
is_empty: bool = len(deq) == 0
print("Is the double-ended queue empty =", is_empty)

View File

@@ -6,17 +6,17 @@ Author: krahets (krahets@163.com)
class ListNode:
"""Double-linked list node"""
"""Doubly linked list node"""
def __init__(self, val: int):
"""Constructor"""
self.val: int = val
self.next: ListNode | None = None # Reference to successor node
self.prev: ListNode | None = None # Reference to predecessor node
self.next: ListNode | None = None # Successor node reference
self.prev: ListNode | None = None # Predecessor node reference
class LinkedListDeque:
"""Double-ended queue class based on double-linked list"""
"""Double-ended queue based on doubly linked list implementation"""
def __init__(self):
"""Constructor"""
@@ -29,54 +29,54 @@ class LinkedListDeque:
return self._size
def is_empty(self) -> bool:
"""Determine if the double-ended queue is empty"""
"""Check 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 the linked list is empty, make both front and rear point to node
if self.is_empty():
self._front = self._rear = node
# Front enqueue operation
# Front of the queue enqueue operation
elif is_front:
# Add node to the head of the list
# Add node to the head of the linked list
self._front.prev = node
node.next = self._front
self._front = node # Update head node
# Rear enqueue operation
# Rear of the queue enqueue operation
else:
# Add node to the tail of the list
# Add node to the tail of the linked list
self._rear.next = node
node.prev = self._rear
self._rear = node # Update tail node
self._size += 1 # Update queue length
def push_first(self, num: int):
"""Front enqueue"""
"""Front of the queue enqueue"""
self.push(num, True)
def push_last(self, num: int):
"""Rear enqueue"""
"""Rear of the queue 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
# Front of the queue dequeue operation
if is_front:
val: int = self._front.val # Temporarily store the head node value
# Remove head node
val: int = self._front.val # Temporarily store head node value
# Delete head node
fnext: ListNode | None = self._front.next
if fnext is not None:
fnext.prev = None
self._front.next = None
self._front = fnext # Update head node
# Rear dequeue operation
# Rear of the queue dequeue operation
else:
val: int = self._rear.val # Temporarily store the tail node value
# Remove tail node
val: int = self._rear.val # Temporarily store tail node value
# Delete tail node
rprev: ListNode | None = self._rear.prev
if rprev is not None:
rprev.next = None
@@ -86,21 +86,21 @@ class LinkedListDeque:
return val
def pop_first(self) -> int:
"""Front dequeue"""
"""Front of the queue dequeue"""
return self.pop(True)
def pop_last(self) -> int:
"""Rear dequeue"""
"""Rear of the queue dequeue"""
return self.pop(False)
def peek_first(self) -> int:
"""Access front element"""
"""Access front of the queue element"""
if self.is_empty():
raise IndexError("Double-ended queue is empty")
return self._front.val
def peek_last(self) -> int:
"""Access rear element"""
"""Access rear of the queue element"""
if self.is_empty():
raise IndexError("Double-ended queue is empty")
return self._rear.val
@@ -122,30 +122,30 @@ if __name__ == "__main__":
deque.push_last(3)
deque.push_last(2)
deque.push_last(5)
print("Double-ended queue deque =", deque.to_array())
print("double-ended queue deque =", deque.to_array())
# Access element
# Access elements
peek_first: int = deque.peek_first()
print("Front element peek_first =", peek_first)
print("Front of the queue element peek_first =", peek_first)
peek_last: int = deque.peek_last()
print("Rear element peek_last =", peek_last)
print("Rear of the queue element peek_last =", peek_last)
# Element enqueue
# Elements enqueue
deque.push_last(4)
print("Element 4 rear enqueued, deque =", deque.to_array())
print("Element 4 rear enqueue after deque =", deque.to_array())
deque.push_first(1)
print("Element 1 front enqueued, deque =", deque.to_array())
print("Element 1 front enqueue after deque =", deque.to_array())
# Element dequeue
# Elements dequeue
pop_last: int = deque.pop_last()
print("Rear dequeued element =", pop_last, ", deque after rear dequeue =", deque.to_array())
print("Rear dequeued element =", pop_last, ", rear dequeue after deque =", deque.to_array())
pop_first: int = deque.pop_first()
print("Front dequeued element =", pop_first, ", deque after front dequeue =", deque.to_array())
print("Front dequeued element =", pop_first, ", front dequeue after deque =", deque.to_array())
# Get the length of the double-ended queue
size: int = deque.size()
print("Double-ended queue length size =", size)
print("Length of the double-ended queue size =", size)
# Determine if the double-ended queue is empty
# Check if the double-ended queue is empty
is_empty: bool = deque.is_empty()
print("Is the double-ended queue empty =", is_empty)

View File

@@ -12,7 +12,7 @@ from modules import ListNode
class LinkedListQueue:
"""Queue class based on linked list"""
"""Queue based on linked list implementation"""
def __init__(self):
"""Constructor"""
@@ -25,18 +25,18 @@ class LinkedListQueue:
return self._size
def is_empty(self) -> bool:
"""Determine if the queue is empty"""
"""Check if the queue is empty"""
return self._size == 0
def push(self, num: int):
"""Enqueue"""
# Add num behind the tail node
# Add num after the tail node
node = ListNode(num)
# If the queue is empty, make the head and tail nodes both point to that node
# If the queue is empty, make both front and rear point to the 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
# If the queue is not empty, add the node after the tail node
else:
self._rear.next = node
self._rear = node
@@ -45,19 +45,19 @@ class LinkedListQueue:
def pop(self) -> int:
"""Dequeue"""
num = self.peek()
# Remove head node
# Delete head node
self._front = self._front.next
self._size -= 1
return num
def peek(self) -> int:
"""Access front element"""
"""Access front of the queue 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"""
"""Convert to list for printing"""
queue = []
temp = self._front
while temp:
@@ -71,27 +71,27 @@ if __name__ == "__main__":
# Initialize queue
queue = LinkedListQueue()
# Element enqueue
# Elements enqueue
queue.push(1)
queue.push(3)
queue.push(2)
queue.push(5)
queue.push(4)
print("Queue queue =", queue.to_list())
print("queue =", queue.to_list())
# Access front element
# Access front of the queue element
peek: int = queue.peek()
print("Front element front =", peek)
print("Front of the queue element front =", peek)
# Element dequeue
pop_front: int = queue.pop()
print("Dequeued element pop =", pop_front)
print("Queue after dequeue =", queue.to_list())
print("After dequeue queue =", queue.to_list())
# Get the length of the queue
size: int = queue.size()
print("Queue length size =", size)
print("Length of the queue size =", size)
# Determine if the queue is empty
# Check if the queue is empty
is_empty: bool = queue.is_empty()
print("Is the queue empty =", is_empty)

View File

@@ -12,7 +12,7 @@ from modules import ListNode
class LinkedListStack:
"""Stack class based on linked list"""
"""Stack based on linked list implementation"""
def __init__(self):
"""Constructor"""
@@ -24,7 +24,7 @@ class LinkedListStack:
return self._size
def is_empty(self) -> bool:
"""Determine if the stack is empty"""
"""Check if the stack is empty"""
return self._size == 0
def push(self, val: int):
@@ -42,13 +42,13 @@ class LinkedListStack:
return num
def peek(self) -> int:
"""Access stack top element"""
"""Access top of the stack 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"""
"""Convert to list for printing"""
arr = []
node = self._peek
while node:
@@ -63,27 +63,27 @@ if __name__ == "__main__":
# Initialize stack
stack = LinkedListStack()
# Element push
# Elements push onto stack
stack.push(1)
stack.push(3)
stack.push(2)
stack.push(5)
stack.push(4)
print("Stack stack =", stack.to_list())
print("stack =", stack.to_list())
# Access stack top element
# Access top of the stack element
peek: int = stack.peek()
print("Stack top element peek =", peek)
print("Top of the stack element peek =", peek)
# Element pop
# Element pop from stack
pop: int = stack.pop()
print("Popped element pop =", pop)
print("Stack after pop =", stack.to_list())
print("After pop stack =", stack.to_list())
# Get the length of the stack
size: int = stack.size()
print("Stack length size =", size)
print("Length of the stack size =", size)
# Determine if it's empty
# Check if it is empty
is_empty: bool = stack.is_empty()
print("Is the stack empty =", is_empty)

View File

@@ -9,31 +9,31 @@ 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
# In Python, we generally use the double-ended queue class deque as a queue
# Although queue.Queue() is a proper queue class, it is not very convenient to use
que: deque[int] = deque()
# Element enqueue
# Elements enqueue
que.append(1)
que.append(3)
que.append(2)
que.append(5)
que.append(4)
print("Queue que =", que)
print("queue que =", que)
# Access front element
# Access front of the queue element
front: int = que[0]
print("Front element front =", front)
print("Front of the queue element front =", front)
# Element dequeue
pop: int = que.popleft()
print("Dequeued element pop =", pop)
print("Queue after dequeue =", que)
print("After dequeue que =", que)
# Get the length of the queue
size: int = len(que)
print("Queue length size =", size)
print("Length of the queue size =", size)
# Determine if the queue is empty
# Check if the queue is empty
is_empty: bool = len(que) == 0
print("Is the queue empty =", is_empty)

View File

@@ -7,30 +7,30 @@ 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
# Python does not have a built-in stack class, we can use list as a stack
stack: list[int] = []
# Element push
# Elements push onto stack
stack.append(1)
stack.append(3)
stack.append(2)
stack.append(5)
stack.append(4)
print("Stack stack =", stack)
print("stack =", stack)
# Access stack top element
# Access top of the stack element
peek: int = stack[-1]
print("Stack top element peek =", peek)
print("Top of the stack element peek =", peek)
# Element pop
# Element pop from stack
pop: int = stack.pop()
print("Popped element pop =", pop)
print("Stack after pop =", stack)
print("After pop stack =", stack)
# Get the length of the stack
size: int = len(stack)
print("Stack length size =", size)
print("Length of the stack size =", size)
# Determine if it's empty
# Check if it is empty
is_empty: bool = len(stack) == 0
print("Is the stack empty =", is_empty)

View File

@@ -12,7 +12,7 @@ from modules import TreeNode, list_to_tree, print_tree
class ArrayBinaryTree:
"""Array-based binary tree class"""
"""Binary tree class represented by array"""
def __init__(self, arr: list[int | None]):
"""Constructor"""
@@ -23,28 +23,28 @@ class ArrayBinaryTree:
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
"""Get value of node at index i"""
# If index is out of bounds, return None, representing empty position
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"""
"""Get index of left child node of 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"""
"""Get index of right child node of 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"""
"""Get index of parent node of node at index i"""
return (i - 1) // 2
def level_order(self) -> list[int]:
"""Level-order traversal"""
self.res = []
# Traverse array
# Traverse array directly
for i in range(self.size()):
if self.val(i) is not None:
self.res.append(self.val(i))
@@ -54,32 +54,32 @@ class ArrayBinaryTree:
"""Depth-first traversal"""
if self.val(i) is None:
return
# Pre-order traversal
# Preorder traversal
if order == "pre":
self.res.append(self.val(i))
self.dfs(self.left(i), order)
# In-order traversal
# Inorder traversal
if order == "in":
self.res.append(self.val(i))
self.dfs(self.right(i), order)
# Post-order traversal
# Postorder traversal
if order == "post":
self.res.append(self.val(i))
def pre_order(self) -> list[int]:
"""Pre-order traversal"""
"""Preorder traversal"""
self.res = []
self.dfs(0, order="pre")
return self.res
def in_order(self) -> list[int]:
"""In-order traversal"""
"""Inorder traversal"""
self.res = []
self.dfs(0, order="in")
return self.res
def post_order(self) -> list[int]:
"""Post-order traversal"""
"""Postorder traversal"""
self.res = []
self.dfs(0, order="post")
return self.res
@@ -88,32 +88,32 @@ class ArrayBinaryTree:
"""Driver Code"""
if __name__ == "__main__":
# Initialize binary tree
# Use a specific function to convert an array into a binary tree
# Here we use a function to generate a binary tree directly from an array
arr = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
root = list_to_tree(arr)
print("\nInitialize binary tree\n")
print("Array representation of the binary tree:")
print("Array representation of binary tree:")
print(arr)
print("Linked list representation of the binary tree:")
print("Linked list representation of binary tree:")
print_tree(root)
# Array-based binary tree class
# Binary tree class represented by array
abt = ArrayBinaryTree(arr)
# Access node
# Access nodes
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)}")
print(f"\nCurrent node's index is {i}, value is {abt.val(i)}")
print(f"Its left child node's index is {l}, value is {abt.val(l)}")
print(f"Its right child node's index is {r}, value is {abt.val(r)}")
print(f"Its parent node's index is {p}, value is {abt.val(p)}")
# Traverse tree
res = abt.level_order()
print("\nLevel-order traversal is:", res)
res = abt.pre_order()
print("Pre-order traversal is:", res)
print("Preorder traversal is:", res)
res = abt.in_order()
print("In-order traversal is:", res)
print("Inorder traversal is:", res)
res = abt.post_order()
print("Post-order traversal is:", res)
print("Postorder traversal is:", res)

View File

@@ -46,31 +46,31 @@ class AVLTree:
"""Right rotation operation"""
child = node.left
grand_child = child.right
# Rotate node to the right around child
# Using child as pivot, rotate node to the right
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 root node of 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
# Using child as pivot, rotate node to the left
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 root node of 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
"""Perform rotation operation to restore balance to this subtree"""
# Get balance factor of node
balance_factor = self.balance_factor(node)
# Left-leaning tree
if balance_factor > 1:
@@ -90,7 +90,7 @@ class AVLTree:
# First right rotation then left rotation
node.right = self.right_rotate(node.right)
return self.left_rotate(node)
# Balanced tree, no rotation needed, return
# Balanced tree, no rotation needed, return directly
return node
def insert(self, val):
@@ -107,22 +107,22 @@ class AVLTree:
elif val > node.val:
node.right = self.insert_helper(node.right, val)
else:
# Do not insert duplicate nodes, return
# Duplicate node not inserted, return directly
return node
# Update node height
self.update_height(node)
# 2. Perform rotation operation to restore balance to the subtree
# 2. Perform rotation operation to restore balance to this subtree
return self.rotate(node)
def remove(self, val: int):
"""Remove node"""
"""Delete 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)"""
"""Recursively delete node (helper method)"""
if node is None:
return None
# 1. Find and remove the node
# 1. Find node and delete
if val < node.val:
node.left = self.remove_helper(node.left, val)
elif val > node.val:
@@ -130,14 +130,14 @@ class AVLTree:
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
# Number of child nodes = 0, delete node directly and return
if child is None:
return None
# Number of child nodes = 1, remove node
# Number of child nodes = 1, delete node directly
else:
node = child
else:
# Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it
# Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it
temp = node.right
while temp.left is not None:
temp = temp.left
@@ -145,13 +145,13 @@ class AVLTree:
node.val = temp.val
# Update node height
self.update_height(node)
# 2. Perform rotation operation to restore balance to the subtree
# 2. Perform rotation operation to restore balance to this subtree
return self.rotate(node)
def search(self, val: int) -> TreeNode | None:
"""Search node"""
cur = self._root
# Loop find, break after passing leaf nodes
# Loop search, exit after passing leaf node
while cur is not None:
# Target node is in cur's right subtree
if cur.val < val:
@@ -159,7 +159,7 @@ class AVLTree:
# Target node is in cur's left subtree
elif cur.val > val:
cur = cur.left
# Found target node, break loop
# Found target node, exit loop
else:
break
# Return target node
@@ -171,30 +171,30 @@ if __name__ == "__main__":
def test_insert(tree: AVLTree, val: int):
tree.insert(val)
print("\nInsert node {} after, AVL tree is".format(val))
print("\nAfter inserting node {}, 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("\nAfter deleting node {}, 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
# Insert nodes
# Please pay attention to 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
# Delete nodes
# Please pay attention to how the AVL tree maintains balance after deleting nodes
test_remove(avl_tree, 8) # Delete node with degree 0
test_remove(avl_tree, 5) # Delete node with degree 1
test_remove(avl_tree, 4) # Delete node with degree 2
result_node = avl_tree.search(7)
print("\nFound node object is {}, node value = {}".format(result_node, result_node.val))

View File

@@ -26,7 +26,7 @@ class BinarySearchTree:
def search(self, num: int) -> TreeNode | None:
"""Search node"""
cur = self._root
# Loop find, break after passing leaf nodes
# Loop search, exit after passing leaf node
while cur is not None:
# Target node is in cur's right subtree
if cur.val < num:
@@ -34,7 +34,7 @@ class BinarySearchTree:
# Target node is in cur's left subtree
elif cur.val > num:
cur = cur.left
# Found target node, break loop
# Found target node, exit loop
else:
break
return cur
@@ -45,10 +45,10 @@ class BinarySearchTree:
if self._root is None:
self._root = TreeNode(num)
return
# Loop find, break after passing leaf nodes
# Loop search, exit after passing leaf node
cur, pre = self._root, None
while cur is not None:
# Found duplicate node, thus return
# Found duplicate node, return directly
if cur.val == num:
return
pre = cur
@@ -66,47 +66,47 @@ class BinarySearchTree:
pre.left = node
def remove(self, num: int):
"""Remove node"""
# If tree is empty, return
"""Delete node"""
# If tree is empty, return directly
if self._root is None:
return
# Loop find, break after passing leaf nodes
# Loop search, exit after passing leaf node
cur, pre = self._root, None
while cur is not None:
# Found node to be removed, break loop
# Found node to delete, exit loop
if cur.val == num:
break
pre = cur
# Node to be removed is in cur's right subtree
# Node to delete is in cur's right subtree
if cur.val < num:
cur = cur.right
# Node to be removed is in cur's left subtree
# Node to delete is in cur's left subtree
else:
cur = cur.left
# If no node to be removed, return
# If no node to delete, return directly
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
# When number of child nodes = 0 / 1, child = null / that child node
child = cur.left or cur.right
# Remove node cur
# Delete 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
# If deleted node is root node, reassign root node
self._root = child
# Number of child nodes = 2
else:
# Get the next node in in-order traversal of cur
# Get next node of cur in inorder traversal
tmp: TreeNode = cur.right
while tmp.left is not None:
tmp = tmp.left
# Recursively remove node tmp
# Recursively delete node tmp
self.remove(tmp.val)
# Replace cur with tmp
cur.val = tmp.val
@@ -117,7 +117,7 @@ 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
# Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree
for num in nums:
bst.insert(num)
print("\nInitialized binary tree is\n")
@@ -129,18 +129,18 @@ if __name__ == "__main__":
# Insert node
bst.insert(16)
print("\nAfter inserting node 16, the binary tree is\n")
print("\nAfter inserting node 16, binary tree is\n")
print_tree(bst.get_root())
# Remove node
# Delete node
bst.remove(1)
print("\nAfter removing node 1, the binary tree is\n")
print("\nAfter deleting node 1, binary tree is\n")
print_tree(bst.get_root())
bst.remove(2)
print("\nAfter removing node 2, the binary tree is\n")
print("\nAfter deleting node 2, binary tree is\n")
print_tree(bst.get_root())
bst.remove(4)
print("\nAfter removing node 4, the binary tree is\n")
print("\nAfter deleting node 4, binary tree is\n")
print_tree(bst.get_root())

View File

@@ -14,13 +14,13 @@ from modules import TreeNode, print_tree
"""Driver Code"""
if __name__ == "__main__":
# Initialize binary tree
# Initialize node
# Initialize nodes
n1 = TreeNode(val=1)
n2 = TreeNode(val=2)
n3 = TreeNode(val=3)
n4 = TreeNode(val=4)
n5 = TreeNode(val=5)
# Construct node references (pointers)
# Build references (pointers) between nodes
n1.left = n2
n1.right = n3
n2.left = n4
@@ -28,14 +28,14 @@ if __name__ == "__main__":
print("\nInitialize binary tree\n")
print_tree(n1)
# Insert and remove nodes
# Insert and delete 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
# Delete node
n1.left = n2
print("\nAfter removing node P\n")
print("\nAfter deleting node P\n")
print_tree(n1)

View File

@@ -17,26 +17,26 @@ def level_order(root: TreeNode | None) -> list[int]:
# Initialize queue, add root node
queue: deque[TreeNode] = deque()
queue.append(root)
# Initialize a list to store the traversal sequence
# Initialize a list to save the traversal sequence
res = []
while queue:
node: TreeNode = queue.popleft() # Queue dequeues
node: TreeNode = queue.popleft() # Dequeue
res.append(node.val) # Save node value
if node.left is not None:
queue.append(node.left) # Left child node enqueues
queue.append(node.left) # Left child node enqueue
if node.right is not None:
queue.append(node.right) # Right child node enqueues
queue.append(node.right) # Right child node enqueue
return res
"""Driver Code"""
if __name__ == "__main__":
# Initialize binary tree
# Use a specific function to convert an array into a binary tree
# Here we use a function to generate a binary tree directly from an array
root: TreeNode = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7])
print("\nInitialize binary tree\n")
print_tree(root)
# Level-order traversal
res: list[int] = level_order(root)
print("\nPrint sequence of nodes from level-order traversal = ", res)
print("\nLevel-order traversal node print sequence = ", res)

View File

@@ -12,7 +12,7 @@ from modules import TreeNode, list_to_tree, print_tree
def pre_order(root: TreeNode | None):
"""Pre-order traversal"""
"""Preorder traversal"""
if root is None:
return
# Visit priority: root node -> left subtree -> right subtree
@@ -22,7 +22,7 @@ def pre_order(root: TreeNode | None):
def in_order(root: TreeNode | None):
"""In-order traversal"""
"""Inorder traversal"""
if root is None:
return
# Visit priority: left subtree -> root node -> right subtree
@@ -32,7 +32,7 @@ def in_order(root: TreeNode | None):
def post_order(root: TreeNode | None):
"""Post-order traversal"""
"""Postorder traversal"""
if root is None:
return
# Visit priority: left subtree -> right subtree -> root node
@@ -44,22 +44,22 @@ def post_order(root: TreeNode | None):
"""Driver Code"""
if __name__ == "__main__":
# Initialize binary tree
# Use a specific function to convert an array into a binary tree
# Here we use a function to generate a binary tree directly from an array
root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7])
print("\nInitialize binary tree\n")
print_tree(root)
# Pre-order traversal
# Preorder traversal
res = []
pre_order(root)
print("\nPrint sequence of nodes from pre-order traversal = ", res)
print("\nPreorder traversal node print sequence = ", res)
# In-order traversal
# Inorder traversal
res.clear()
in_order(root)
print("\nPrint sequence of nodes from in-order traversal = ", res)
print("\nInorder traversal node print sequence = ", res)
# Post-order traversal
# Postorder traversal
res.clear()
post_order(root)
print("\nPrint sequence of nodes from post-order traversal = ", res)
print("\nPostorder traversal node print sequence = ", res)

View File

@@ -6,11 +6,11 @@ Author: krahets (krahets@163.com)
class ListNode:
"""LinkedList node class"""
"""Linked list node class"""
def __init__(self, val: int):
self.val: int = val # Node value
self.next: ListNode | None = None # Reference to successor node
self.next: ListNode | None = None # Reference to next node
def list_to_linked_list(arr: list[int]) -> ListNode | None:

View File

@@ -13,14 +13,14 @@ class TreeNode:
def __init__(self, val: int = 0):
self.val: int = val # Node value
self.height: int = 0 # Node height
self.left: TreeNode | None = None # Reference to the left child node
self.right: TreeNode | None = None # Reference to the right child node
self.left: TreeNode | None = None # Reference to left child node
self.right: TreeNode | None = None # Reference to right child node
# For serialization encoding rules, refer to:
# For the serialization encoding rules, please refer to:
# https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
# Array representation of the binary tree:
# Array representation of binary tree:
# [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
# Linked list representation of the binary tree:
# Linked list representation of binary tree:
# /——— 15
# /——— 7
# /——— 3
@@ -34,13 +34,13 @@ class TreeNode:
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
"""Deserialize a list into a binary tree: recursion"""
# If the index exceeds the array length, 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
# Build the current node
root = TreeNode(arr[i])
# Recursively construct left and right subtrees
# Recursively build the 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
@@ -52,7 +52,7 @@ def list_to_tree(arr: list[int]) -> TreeNode | None:
def tree_to_list_dfs(root: TreeNode, i: int, res: list[int]) -> list[int]:
"""Serialize a binary tree into a list: Recursively"""
"""Serialize a binary tree into a list: recursion"""
if root is None:
return
if i >= len(res):

View File

@@ -11,10 +11,10 @@ class Vertex:
def vals_to_vets(vals: list[int]) -> list["Vertex"]:
"""Input a list of values vals, return a list of vertices vets"""
"""Input value list vals, return vertex list 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"""
"""Input vertex list vets, return value list vals"""
return [vet.val for vet in vets]

View File

@@ -7,7 +7,7 @@ env["PYTHONIOENCODING"] = "utf-8"
if __name__ == "__main__":
# find source code files
src_paths = sorted(glob.glob("en/codes/python/chapter_*/*.py"))
src_paths = sorted(glob.glob("chapter_*/*.py"))
errors = []
# run python code